Bug 1665280 - For RawBuffer reads, return a view into the shmem. r=lsalzman

Differential Revision: https://phabricator.services.mozilla.com/D90354
This commit is contained in:
unknown 2020-09-16 07:16:48 +00:00
parent c95f299299
commit 7f28f17f33
6 changed files with 129 additions and 73 deletions

View File

@ -141,6 +141,13 @@ class Marshaller {
}
};
template <typename T>
inline Range<T> AsRange(T* const begin, T* const end) {
const auto size = MaybeAs<size_t>(end - begin);
MOZ_RELEASE_ASSERT(size);
return {begin, *size};
}
/**
* Used to give QueueParamTraits a way to write to the Producer without
* actually altering it, in case the transaction fails.
@ -158,16 +165,22 @@ class ProducerView {
mWrite(aWrite),
mStatus(QueueStatus::kSuccess) {}
template <typename T>
QueueStatus WriteFromRange(const Range<const T>& src) {
if (!mStatus) return mStatus;
mProducer->WriteFromRange(src);
return mStatus;
}
/**
* Copy bytes from aBuffer to the producer if there is enough room.
* aBufferSize must not be 0.
*/
inline QueueStatus Write(const uint8_t* begin, const uint8_t* end);
template <typename T>
inline QueueStatus Write(const T* begin, const T* end) {
return Write(reinterpret_cast<const uint8_t*>(begin),
reinterpret_cast<const uint8_t*>(end));
MOZ_RELEASE_ASSERT(begin <= end);
if (!mStatus) return mStatus;
WriteFromRange(AsRange(begin, end));
return mStatus;
}
template <typename T>
@ -214,12 +227,31 @@ class ConsumerView {
* Read bytes from the consumer if there is enough data. aBuffer may
* be null (in which case the data is skipped)
*/
inline QueueStatus Read(uint8_t* begin, uint8_t* end);
template <typename T>
inline QueueStatus Read(T* begin, T* end) {
return Read(reinterpret_cast<uint8_t*>(begin),
reinterpret_cast<uint8_t*>(end));
inline QueueStatus Read(T* const destBegin, T* const destEnd) {
MOZ_ASSERT(destBegin);
MOZ_RELEASE_ASSERT(destBegin <= destEnd);
if (!mStatus) return mStatus;
const auto dest = AsRange(destBegin, destEnd);
const auto view = ReadRange<T>(dest.length());
if (!view) return mStatus;
const auto byteSize = ByteSize(dest);
if (byteSize) {
memcpy(dest.begin().get(), view->begin().get(), byteSize);
}
return mStatus;
}
/// Return a view wrapping the shmem.
template <typename T>
inline Maybe<Range<const T>> ReadRange(const size_t elemCount) {
if (!mStatus) return {};
const auto view = mConsumer->template ReadRange<T>(elemCount);
if (!view) {
mStatus = QueueStatus::kTooSmall;
}
return view;
}
template <typename T>
@ -249,29 +281,6 @@ class ConsumerView {
QueueStatus mStatus;
};
template <typename T>
QueueStatus ProducerView<T>::Write(const uint8_t* const begin,
const uint8_t* const end) {
MOZ_ASSERT(begin <= end);
if (!mStatus) return mStatus;
if (begin < end) {
MOZ_ASSERT(begin);
mStatus = mProducer->WriteObject(mRead, mWrite, begin, end - begin);
}
return mStatus;
}
template <typename T>
QueueStatus ConsumerView<T>::Read(uint8_t* const begin, uint8_t* const end) {
MOZ_ASSERT(begin <= end);
if (!mStatus) return mStatus;
if (begin < end) {
MOZ_ASSERT(begin);
mStatus = mConsumer->ReadObject(mRead, mWrite, begin, end - begin);
}
return mStatus;
}
// ---------------------------------------------------------------
/**

View File

@ -41,15 +41,18 @@ Maybe<Range<uint8_t>> WebGLChild::AllocPendingCmdBytes(const size_t size) {
mPendingCmdsShmem = std::move(shmem);
mPendingCmdsPos = 0;
}
const auto range = mPendingCmdsShmem.ByteRange();
const auto remaining =
Range<uint8_t>{range.begin() + mPendingCmdsPos, range.end()};
if (size > remaining.length()) {
auto itr = range.begin() + mPendingCmdsPos;
const auto offset = AlignmentOffset(kUniversalAlignment, itr.get());
mPendingCmdsPos += offset;
const auto required = mPendingCmdsPos + size;
if (required > range.length()) {
FlushPendingCmds();
return AllocPendingCmdBytes(size);
}
itr = range.begin() + mPendingCmdsPos;
const auto remaining = Range<uint8_t>{itr, range.end()};
mPendingCmdsPos += size;
return Some(Range<uint8_t>{remaining.begin(), remaining.begin() + size});
}

View File

@ -40,13 +40,30 @@ class RangeConsumerView final : public webgl::ConsumerView<RangeConsumerView> {
(void)Remaining(); // assert size non-negative
}
QueueStatus ReadObject(size_t*, size_t, void* const src, const size_t size) {
const auto remaining = Remaining();
if (size > remaining) return QueueStatus::kTooSmall;
void AlignTo(const size_t alignment) {
const auto offset = AlignmentOffset(alignment, mSrcItr.get());
if (offset > Remaining()) {
mSrcItr = mSrcEnd;
return;
}
mSrcItr += offset;
}
memcpy(src, mSrcItr.get(), size);
mSrcItr += size;
return QueueStatus::kSuccess;
template <typename T>
Maybe<Range<const T>> ReadRange(const size_t elemCount) {
AlignTo(alignof(T));
constexpr auto elemSize = sizeof(T);
const auto byteSizeChecked = CheckedInt<size_t>(elemCount) * elemSize;
MOZ_RELEASE_ASSERT(byteSizeChecked.isValid());
const auto& byteSize = byteSizeChecked.value();
const auto remaining = Remaining();
if (byteSize > remaining) return {};
const auto begin = reinterpret_cast<const T*>(mSrcItr.get());
mSrcItr += byteSize;
return Some(Range<const T>{begin, elemCount});
}
};
@ -61,9 +78,16 @@ class SizeOnlyProducerView final
public:
SizeOnlyProducerView() : ProducerView(this, 0, nullptr) {}
QueueStatus WriteObject(size_t, size_t*, const void*, const size_t size) {
mRequiredSize += size;
return QueueStatus::kSuccess;
template <typename T>
void WriteFromRange(const Range<const T>& src) {
constexpr auto alignment = alignof(T);
const size_t byteSize = ByteSize(src);
// printf_stderr("SizeOnlyProducerView: @%zu +%zu\n", alignment, byteSize);
const auto offset = AlignmentOffset(alignment, mRequiredSize);
mRequiredSize += offset;
mRequiredSize += byteSize;
}
const auto& RequiredSize() const { return mRequiredSize; }
@ -72,26 +96,35 @@ class SizeOnlyProducerView final
// -
class RangeProducerView final : public webgl::ProducerView<RangeProducerView> {
RangedPtr<uint8_t> mDestItr;
const RangedPtr<uint8_t> mDestBegin;
const RangedPtr<uint8_t> mDestEnd;
RangedPtr<uint8_t> mDestItr;
public:
auto Remaining() const { return *MaybeAs<size_t>(mDestEnd - mDestItr); }
explicit RangeProducerView(const Range<uint8_t> range)
: ProducerView(this, 0, nullptr),
mDestItr(range.begin()),
mDestEnd(range.end()) {
mDestBegin(range.begin()),
mDestEnd(range.end()),
mDestItr(mDestBegin) {
(void)Remaining(); // assert size non-negative
}
QueueStatus WriteObject(size_t, size_t*, const void* const src,
const size_t size) {
MOZ_ASSERT(size <= Remaining());
template <typename T>
void WriteFromRange(const Range<const T>& src) {
constexpr auto alignment = alignof(T);
const size_t byteSize = ByteSize(src);
// printf_stderr("RangeProducerView: @%zu +%zu\n", alignment, byteSize);
memcpy(mDestItr.get(), src, size);
mDestItr += size;
return QueueStatus::kSuccess;
const auto offset = AlignmentOffset(alignment, mDestItr.get());
mDestItr += offset;
MOZ_ASSERT(byteSize <= Remaining());
if (byteSize) {
memcpy(mDestItr.get(), src.begin().get(), byteSize);
}
mDestItr += byteSize;
}
};

View File

@ -61,6 +61,7 @@ IPCResult WebGLParent::RecvDispatchCommands(Shmem&& rawShmem,
auto view = webgl::RangeConsumerView{cmdsBytes};
while (true) {
view.AlignTo(kUniversalAlignment);
size_t id = 0;
const auto status = view.ReadParam(&id);
if (status != QueueStatus::kSuccess) break;

View File

@ -85,7 +85,7 @@ struct QueueParamTraits<RawBuffer<T>> {
if (!status) return status;
if (!hasData) return status;
status = view.Write(begin, begin + elemCount);
status = view.WriteFromRange(in.Data());
return status;
}
@ -108,16 +108,10 @@ struct QueueParamTraits<RawBuffer<T>> {
return QueueStatus::kSuccess;
}
auto buffer = UniqueBuffer::Alloc(elemCount * sizeof(T));
if (!buffer) return QueueStatus::kOOMError;
using MutT = std::remove_cv_t<T>;
const auto begin = reinterpret_cast<MutT*>(buffer.get());
const auto range = Range<MutT>{begin, elemCount};
auto temp = RawBuffer<T>{range, std::move(buffer)};
*out = std::move(temp);
return view.Read(range.begin().get(), range.end().get());
auto data = view.template ReadRange<T>(elemCount);
if (!data) return QueueStatus::kTooSmall;
*out = std::move(RawBuffer<T>{*data});
return QueueStatus::kSuccess;
}
};
@ -168,9 +162,10 @@ struct QueueParamTraits<std::string> {
template <typename U>
static QueueStatus Write(ProducerView<U>& aProducerView, const T& aArg) {
auto status = aProducerView.WriteParam(aArg.size());
const auto size = aArg.size();
auto status = aProducerView.WriteParam(size);
if (!status) return status;
status = aProducerView.Write(aArg.data(), aArg.data() + aArg.size());
status = aProducerView.WriteFromRange(Range<const char>{aArg.data(), size});
return status;
}
@ -180,12 +175,9 @@ struct QueueParamTraits<std::string> {
auto status = aConsumerView.ReadParam(&size);
if (!status) return status;
const UniqueBuffer temp = malloc(size);
const auto dest = static_cast<char*>(temp.get());
if (!dest) return QueueStatus::kFatalError;
status = aConsumerView.Read(dest, dest + size);
aArg->assign(dest, size);
const auto view = aConsumerView.template ReadRange<char>(size);
if (!view) return QueueStatus::kFatalError;
aArg->assign(view->begin().get(), size);
return status;
}
};

View File

@ -990,6 +990,24 @@ inline auto MakeRangeAbv(const T& abv)
return {abv.Data(), abv.Length()};
}
// -
constexpr auto kUniversalAlignment = alignof(std::max_align_t);
template <typename T>
inline size_t AlignmentOffset(const size_t alignment, const T posOrPtr) {
MOZ_ASSERT(alignment);
const auto begin = reinterpret_cast<uintptr_t>(posOrPtr);
const auto wholeMultiples = (begin + (alignment - 1)) / alignment;
const auto aligned = wholeMultiples * alignment;
return aligned - begin;
}
template <typename T>
inline size_t ByteSize(const Range<T>& range) {
return range.length() * sizeof(T);
}
Maybe<Range<const uint8_t>> GetRangeFromView(const dom::ArrayBufferView& view,
GLuint elemOffset,
GLuint elemCountOverride);