blob: d0621f56c12a4f2d53b525f5df7c1b0d5e7aaf77 [file] [log] [blame]
/*
* Copyright 2019 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef skgpu_AsyncReadTypes_DEFINED
#define skgpu_AsyncReadTypes_DEFINED
#include "include/core/SkData.h"
#include "include/core/SkImage.h"
#include "include/private/SkTArray.h"
#include "src/core/SkMessageBus.h"
#include <algorithm>
#include <forward_list>
namespace skgpu {
/**
* We sometimes hand clients objects that contain mapped buffers. The client may consume
* the mapped buffer on another thread. This object manages receiving messages that buffers are
* ready to be unmapped (on the owner's thread). It also handles cleaning up mapped
* buffers if the owner is destroyed before the client has finished with the buffer.
*
* Buffers are first registered using insert() before being passed the client. process() should be
* called periodically on the owner's thread to poll for messages and process them.
*/
template <typename T, typename IDType>
class TClientMappedBufferManager {
public:
/**
* The message type that internal users of this should post to unmap the buffer.
* Set fInboxID to inboxID(). fBuffer must have been previously passed to insert().
*/
struct BufferFinishedMessage {
BufferFinishedMessage(sk_sp<T> buffer,
IDType intendedRecipient)
: fBuffer(std::move(buffer)), fIntendedRecipient(intendedRecipient) {}
BufferFinishedMessage(BufferFinishedMessage&& other) {
fBuffer = std::move(other.fBuffer);
fIntendedRecipient = other.fIntendedRecipient;
other.fIntendedRecipient.makeInvalid();
}
sk_sp<T> fBuffer;
IDType fIntendedRecipient;
};
using BufferFinishedMessageBus = SkMessageBus<BufferFinishedMessage,
IDType,
false>;
TClientMappedBufferManager(IDType ownerID)
: fFinishedBufferInbox(ownerID) {}
TClientMappedBufferManager(const TClientMappedBufferManager&) = delete;
TClientMappedBufferManager(TClientMappedBufferManager&&) = delete;
~TClientMappedBufferManager() {
this->process();
if (!fAbandoned) {
// If we're going down before we got the messages we go ahead and unmap all the buffers.
// It's up to the client to ensure that they aren't being accessed on another thread
// while this is happening (or afterwards on any thread).
for (auto& b : fClientHeldBuffers) {
b->unmap();
}
}
}
TClientMappedBufferManager& operator=(const TClientMappedBufferManager&) = delete;
TClientMappedBufferManager& operator=(TClientMappedBufferManager&&) = delete;
/** Initialize BufferFinishedMessage::fIntendedRecipient to this value. It is the
* unique ID of the object that owns this buffer manager.
*/
IDType ownerID() const {
return fFinishedBufferInbox.uniqueID();
}
/**
* Let the manager know to expect a message with buffer 'b'. It's illegal for a buffer to be
* inserted again before it is unmapped by process().
*/
void insert(sk_sp<T> b) {
SkDEBUGCODE(auto end = fClientHeldBuffers.end());
SkASSERT(std::find(fClientHeldBuffers.begin(), end, b) == end);
fClientHeldBuffers.emplace_front(std::move(b));
}
/** Poll for messages and unmap any incoming buffers. */
void process() {
SkSTArray<4, BufferFinishedMessage> messages;
fFinishedBufferInbox.poll(&messages);
if (!fAbandoned) {
for (auto& m : messages) {
this->remove(m.fBuffer);
m.fBuffer->unmap();
}
}
}
/** Notifies the manager that the context has been abandoned. No more unmaps() will occur.*/
void abandon() {
fAbandoned = true;
fClientHeldBuffers.clear();
}
private:
typename BufferFinishedMessageBus::Inbox fFinishedBufferInbox;
std::forward_list<sk_sp<T>> fClientHeldBuffers;
bool fAbandoned = false;
void remove(const sk_sp<T>& b) {
// There is no convenient remove only the first element that equals a value functionality in
// std::forward_list.
auto prev = fClientHeldBuffers.before_begin();
auto end = fClientHeldBuffers.end();
SkASSERT(std::find(fClientHeldBuffers.begin(), end, b) != end);
for (auto cur = fClientHeldBuffers.begin(); cur != end; prev = cur++) {
if (*cur == b) {
fClientHeldBuffers.erase_after(prev);
break;
}
}
SkASSERT(std::find(fClientHeldBuffers.begin(), end, b) == end);
}
};
////////////////////////////////////////////////////////////////////////////////
template <typename T, typename IDType, typename TransferResultType>
class TAsyncReadResult : public SkImage::AsyncReadResult {
public:
TAsyncReadResult(IDType intendedRecipient)
: fIntendedRecipient(intendedRecipient) {
}
~TAsyncReadResult() override {
for (int i = 0; i < fPlanes.size(); ++i) {
fPlanes[i].releaseMappedBuffer(fIntendedRecipient);
}
}
int count() const override { return fPlanes.size(); }
const void* data(int i) const override { return fPlanes[i].data(); }
size_t rowBytes(int i) const override { return fPlanes[i].rowBytes(); }
bool addTransferResult(const TransferResultType& result,
SkISize dimensions,
size_t rowBytes,
TClientMappedBufferManager<T, IDType>* manager) {
SkASSERT(!result.fTransferBuffer->isMapped());
const void* mappedData = result.fTransferBuffer->map();
if (!mappedData) {
return false;
}
if (result.fPixelConverter) {
size_t size = rowBytes*dimensions.height();
sk_sp<SkData> data = SkData::MakeUninitialized(size);
result.fPixelConverter(data->writable_data(), mappedData);
this->addCpuPlane(std::move(data), rowBytes);
result.fTransferBuffer->unmap();
} else {
manager->insert(result.fTransferBuffer);
this->addMappedPlane(mappedData, rowBytes, std::move(result.fTransferBuffer));
}
return true;
}
void addCpuPlane(sk_sp<SkData> data, size_t rowBytes) {
SkASSERT(data);
SkASSERT(rowBytes > 0);
fPlanes.emplace_back(std::move(data), rowBytes);
}
private:
void addMappedPlane(const void* data, size_t rowBytes, sk_sp<T> mappedBuffer) {
SkASSERT(data);
SkASSERT(rowBytes > 0);
SkASSERT(mappedBuffer);
SkASSERT(mappedBuffer->isMapped());
fPlanes.emplace_back(std::move(mappedBuffer), rowBytes);
}
class Plane {
public:
Plane(sk_sp<T> buffer, size_t rowBytes)
: fMappedBuffer(std::move(buffer)), fRowBytes(rowBytes) {}
Plane(sk_sp<SkData> data, size_t rowBytes) : fData(std::move(data)), fRowBytes(rowBytes) {}
Plane(Plane&&) = default;
~Plane() { SkASSERT(!fMappedBuffer); }
Plane& operator=(const Plane&) = delete;
Plane& operator=(Plane&&) = default;
void releaseMappedBuffer(IDType intendedRecipient) {
if (fMappedBuffer) {
TClientMappedBufferManager<T, IDType>::BufferFinishedMessageBus::Post(
{std::move(fMappedBuffer), intendedRecipient});
}
}
const void* data() const {
if (fMappedBuffer) {
SkASSERT(!fData);
SkASSERT(fMappedBuffer->isMapped());
return fMappedBuffer->map();
}
SkASSERT(fData);
return fData->data();
}
size_t rowBytes() const { return fRowBytes; }
private:
sk_sp<SkData> fData;
sk_sp<T> fMappedBuffer;
size_t fRowBytes;
};
SkSTArray<3, Plane> fPlanes;
IDType fIntendedRecipient;
};
} // namespace skgpu
#endif // skgpu_AsyncReadTypes_DEFINED