523d16dffa
PiperOrigin-RevId: 493519590
151 lines
5.2 KiB
C++
151 lines
5.2 KiB
C++
#include "mediapipe/gpu/gpu_buffer.h"
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include "absl/functional/bind_front.h"
|
|
#include "absl/strings/str_cat.h"
|
|
#include "absl/strings/str_join.h"
|
|
#include "mediapipe/framework/port/logging.h"
|
|
|
|
#if MEDIAPIPE_GPU_BUFFER_USE_CV_PIXEL_BUFFER
|
|
#include "mediapipe/objc/util.h"
|
|
#endif // MEDIAPIPE_GPU_BUFFER_USE_CV_PIXEL_BUFFER
|
|
|
|
namespace mediapipe {
|
|
|
|
namespace {
|
|
|
|
struct StorageTypeFormatter {
|
|
void operator()(std::string* out,
|
|
const std::shared_ptr<internal::GpuBufferStorage>& s) const {
|
|
absl::StrAppend(out, s->storage_type().name());
|
|
}
|
|
};
|
|
|
|
} // namespace
|
|
|
|
std::string GpuBuffer::DebugString() const {
|
|
return holder_ ? absl::StrCat("GpuBuffer[", width(), "x", height(), " ",
|
|
format(), " as ", holder_->DebugString(), "]")
|
|
: "GpuBuffer[invalid]";
|
|
}
|
|
|
|
std::string GpuBuffer::StorageHolder::DebugString() const {
|
|
absl::MutexLock lock(&mutex_);
|
|
return absl::StrJoin(storages_, ", ", StorageTypeFormatter());
|
|
}
|
|
|
|
internal::GpuBufferStorage* GpuBuffer::StorageHolder::GetStorageForView(
|
|
TypeId view_provider_type, bool for_writing) const {
|
|
std::shared_ptr<internal::GpuBufferStorage> chosen_storage;
|
|
std::function<std::shared_ptr<internal::GpuBufferStorage>()> conversion;
|
|
|
|
{
|
|
absl::MutexLock lock(&mutex_);
|
|
// First see if any current storage supports the view.
|
|
for (const auto& s : storages_) {
|
|
if (s->can_down_cast_to(view_provider_type)) {
|
|
chosen_storage = s;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Then try to convert existing storages to one that does.
|
|
// TODO: choose best conversion.
|
|
if (!chosen_storage) {
|
|
for (const auto& s : storages_) {
|
|
if (auto converter = internal::GpuBufferStorageRegistry::Get()
|
|
.StorageConverterForViewProvider(
|
|
view_provider_type, s->storage_type())) {
|
|
conversion = absl::bind_front(converter, s);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Avoid invoking a converter or factory while holding the mutex.
|
|
// Two reasons:
|
|
// 1. Readers that don't need a conversion will not be blocked.
|
|
// 2. We use mutexes to make sure GL contexts are not used simultaneously on
|
|
// different threads, and we also rely on Mutex's deadlock detection
|
|
// heuristic, which enforces a consistent mutex acquisition order.
|
|
// This function is likely to be called within a GL context, and the
|
|
// conversion function may in turn use a GL context, and this may cause a
|
|
// false positive in the deadlock detector.
|
|
// TODO: we could use Mutex::ForgetDeadlockInfo instead.
|
|
if (conversion) {
|
|
auto new_storage = conversion();
|
|
absl::MutexLock lock(&mutex_);
|
|
// Another reader might have already completed and inserted the same
|
|
// conversion. TODO: prevent this?
|
|
for (const auto& s : storages_) {
|
|
if (s->can_down_cast_to(view_provider_type)) {
|
|
chosen_storage = s;
|
|
break;
|
|
}
|
|
}
|
|
if (!chosen_storage) {
|
|
storages_.push_back(std::move(new_storage));
|
|
chosen_storage = storages_.back();
|
|
}
|
|
}
|
|
|
|
if (for_writing) {
|
|
// This will temporarily hold storages to be released, and do so while the
|
|
// lock is not held (see above).
|
|
decltype(storages_) old_storages;
|
|
using std::swap;
|
|
if (chosen_storage) {
|
|
// Discard all other storages.
|
|
absl::MutexLock lock(&mutex_);
|
|
swap(old_storages, storages_);
|
|
storages_ = {chosen_storage};
|
|
} else {
|
|
// Allocate a new storage supporting the requested view.
|
|
if (auto factory =
|
|
internal::GpuBufferStorageRegistry::Get()
|
|
.StorageFactoryForViewProvider(view_provider_type)) {
|
|
if (auto new_storage = factory(width_, height_, format_)) {
|
|
absl::MutexLock lock(&mutex_);
|
|
swap(old_storages, storages_);
|
|
storages_ = {std::move(new_storage)};
|
|
chosen_storage = storages_.back();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// It is ok to return a non-owning storage pointer here because this object
|
|
// ensures the storage's lifetime. Overwriting a GpuBuffer while readers are
|
|
// active would violate this, but it's not allowed in MediaPipe.
|
|
return chosen_storage ? chosen_storage.get() : nullptr;
|
|
}
|
|
|
|
internal::GpuBufferStorage& GpuBuffer::GetStorageForViewOrDie(
|
|
TypeId view_provider_type, bool for_writing) const {
|
|
auto* chosen_storage =
|
|
GpuBuffer::GetStorageForView(view_provider_type, for_writing);
|
|
CHECK(chosen_storage) << "no view provider found for requested view "
|
|
<< view_provider_type.name() << "; storages available: "
|
|
<< (holder_ ? holder_->DebugString() : "invalid");
|
|
DCHECK(chosen_storage->can_down_cast_to(view_provider_type));
|
|
return *chosen_storage;
|
|
}
|
|
|
|
#if !MEDIAPIPE_DISABLE_GPU
|
|
#if MEDIAPIPE_GPU_BUFFER_USE_CV_PIXEL_BUFFER
|
|
CVPixelBufferRef GetCVPixelBufferRef(const GpuBuffer& buffer) {
|
|
if (buffer.GetStorageForView(
|
|
kTypeId<internal::ViewProvider<CVPixelBufferRef>>,
|
|
/*for_writing=*/false) != nullptr) {
|
|
return *buffer.GetReadView<CVPixelBufferRef>();
|
|
}
|
|
return nullptr;
|
|
}
|
|
#endif // MEDIAPIPE_GPU_BUFFER_USE_CV_PIXEL_BUFFER
|
|
#endif // !MEDIAPIPE_DISABLE_GPU
|
|
|
|
} // namespace mediapipe
|