Project import generated by Copybara.
GitOrigin-RevId: 612e50bb8db2ec3dc1c30049372d87a80c3848db
This commit is contained in:
+1
-1
@@ -16,7 +16,7 @@ load("//mediapipe/gpu:metal.bzl", "metal_library")
|
||||
load("@build_bazel_rules_apple//apple:ios.bzl", "ios_unit_test")
|
||||
load("//mediapipe/framework/port:build_config.bzl", "mediapipe_cc_proto_library")
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
+34
-41
@@ -343,6 +343,7 @@ GlContext::~GlContext() {
|
||||
#error This file must be built with ARC.
|
||||
#endif
|
||||
#endif // __OBJC__
|
||||
|
||||
if (thread_) {
|
||||
auto status = thread_->Run([this] {
|
||||
if (profiling_helper_) {
|
||||
@@ -350,9 +351,8 @@ GlContext::~GlContext() {
|
||||
}
|
||||
return ExitContext(nullptr);
|
||||
});
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << "Failed to deactivate context on thread: " << status;
|
||||
}
|
||||
LOG_IF(ERROR, !status.ok())
|
||||
<< "Failed to deactivate context on thread: " << status;
|
||||
if (thread_->IsCurrentThread()) {
|
||||
thread_.release()->SelfDestruct();
|
||||
}
|
||||
@@ -368,40 +368,38 @@ void GlContext::SetProfilingContext(
|
||||
}
|
||||
}
|
||||
|
||||
::mediapipe::Status GlContext::SwitchContextAndRun(GlStatusFunction gl_func) {
|
||||
ContextBinding saved_context;
|
||||
MP_RETURN_IF_ERROR(EnterContext(&saved_context)) << " (entering GL context)";
|
||||
auto status = gl_func();
|
||||
LogUncheckedGlErrors(CheckForGlErrors());
|
||||
MP_RETURN_IF_ERROR(ExitContext(&saved_context)) << " (exiting GL context)";
|
||||
return status;
|
||||
}
|
||||
|
||||
::mediapipe::Status GlContext::Run(GlStatusFunction gl_func, int node_id,
|
||||
Timestamp input_timestamp) {
|
||||
::mediapipe::Status status;
|
||||
if (thread_) {
|
||||
bool had_gl_errors = false;
|
||||
status = thread_->Run(
|
||||
[this, gl_func, node_id, &input_timestamp, &had_gl_errors] {
|
||||
if (profiling_helper_) {
|
||||
profiling_helper_->MarkTimestamp(node_id, input_timestamp,
|
||||
/*is_finish=*/false);
|
||||
}
|
||||
auto status = gl_func();
|
||||
if (profiling_helper_) {
|
||||
profiling_helper_->MarkTimestamp(node_id, input_timestamp,
|
||||
/*is_finish=*/true);
|
||||
}
|
||||
had_gl_errors = CheckForGlErrors();
|
||||
return status;
|
||||
});
|
||||
LogUncheckedGlErrors(had_gl_errors);
|
||||
} else {
|
||||
ContextBinding saved_context;
|
||||
MP_RETURN_IF_ERROR(EnterContext(&saved_context));
|
||||
if (profiling_helper_) {
|
||||
if (profiling_helper_) {
|
||||
gl_func = [=] {
|
||||
profiling_helper_->MarkTimestamp(node_id, input_timestamp,
|
||||
/*is_finish=*/false);
|
||||
}
|
||||
status = gl_func();
|
||||
if (profiling_helper_) {
|
||||
auto status = gl_func();
|
||||
profiling_helper_->MarkTimestamp(node_id, input_timestamp,
|
||||
/*is_finish=*/true);
|
||||
}
|
||||
LogUncheckedGlErrors(CheckForGlErrors());
|
||||
MP_RETURN_IF_ERROR(ExitContext(&saved_context));
|
||||
return status;
|
||||
};
|
||||
}
|
||||
if (thread_) {
|
||||
bool had_gl_errors = false;
|
||||
status = thread_->Run([this, gl_func, &had_gl_errors] {
|
||||
auto status = gl_func();
|
||||
had_gl_errors = CheckForGlErrors();
|
||||
return status;
|
||||
});
|
||||
LogUncheckedGlErrors(had_gl_errors);
|
||||
} else {
|
||||
status = SwitchContextAndRun(gl_func);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -416,17 +414,12 @@ void GlContext::RunWithoutWaiting(GlVoidFunction gl_func) {
|
||||
});
|
||||
} else {
|
||||
// TODO: queue up task instead.
|
||||
ContextBinding saved_context;
|
||||
auto status = EnterContext(&saved_context);
|
||||
auto status = SwitchContextAndRun([gl_func] {
|
||||
gl_func();
|
||||
return ::mediapipe::OkStatus();
|
||||
});
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << "Failed to enter context: " << status;
|
||||
return;
|
||||
}
|
||||
gl_func();
|
||||
LogUncheckedGlErrors(CheckForGlErrors());
|
||||
status = ExitContext(&saved_context);
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << "Failed to exit context: " << status;
|
||||
LOG(ERROR) << "Error in RunWithoutWaiting: " << status;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -589,7 +582,7 @@ class GlFenceSyncPoint : public GlSyncPoint {
|
||||
|
||||
void GlMultiSyncPoint::Add(std::shared_ptr<GlSyncPoint> new_sync) {
|
||||
for (auto& sync : syncs_) {
|
||||
if (&sync->GetContext() == &new_sync->GetContext()) {
|
||||
if (sync->GetContext() == new_sync->GetContext()) {
|
||||
sync = std::move(new_sync);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ class GlSyncPoint {
|
||||
// Returns whether the sync point has been reached. Does not block.
|
||||
virtual bool IsReady() = 0;
|
||||
|
||||
const GlContext& GetContext() { return *gl_context_; }
|
||||
const std::shared_ptr<GlContext>& GetContext() { return gl_context_; }
|
||||
|
||||
protected:
|
||||
std::shared_ptr<GlContext> gl_context_;
|
||||
@@ -366,6 +366,11 @@ class GlContext : public std::enable_shared_from_this<GlContext> {
|
||||
::mediapipe::Status GetGlExtensions();
|
||||
::mediapipe::Status GetGlExtensionsCompat();
|
||||
|
||||
// Make the context current, run gl_func, and restore the previous context.
|
||||
// Internal helper only; callers should use Run or RunWithoutWaiting instead,
|
||||
// which delegates to the dedicated thread if required.
|
||||
::mediapipe::Status SwitchContextAndRun(GlStatusFunction gl_func);
|
||||
|
||||
// The following ContextBinding functions have platform-specific
|
||||
// implementations.
|
||||
|
||||
|
||||
@@ -191,6 +191,18 @@ void GlContext::DestroyContext() {
|
||||
.IgnoreError();
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
if (HasContext()) {
|
||||
// Detach the current program to work around b/166322604.
|
||||
if (eglMakeCurrent(display_, surface_, surface_, context_)) {
|
||||
glUseProgram(0);
|
||||
} else {
|
||||
LOG(ERROR) << "eglMakeCurrent() returned error " << std::showbase
|
||||
<< std::hex << eglGetError();
|
||||
}
|
||||
}
|
||||
#endif // __ANDROID__
|
||||
|
||||
// Destroy the context and surface.
|
||||
if (IsCurrent()) {
|
||||
if (!eglMakeCurrent(display_, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
||||
|
||||
@@ -72,23 +72,23 @@ GlContext::StatusOrGlContext GlContext::Create(
|
||||
// multithreading options, like the special-case combination of USE_PTHREADS
|
||||
// and OFFSCREEN_FRAMEBUFFER)
|
||||
EM_ASM(let init_once = true; if (init_once) {
|
||||
const __cachedFindCanvasEventTarget = __findCanvasEventTarget;
|
||||
const cachedFindCanvasEventTarget = findCanvasEventTarget;
|
||||
|
||||
if (typeof __cachedFindCanvasEventTarget != = 'function') {
|
||||
if (typeof cachedFindCanvasEventTarget != = 'function') {
|
||||
if (typeof console != = 'undefined') {
|
||||
console.error(
|
||||
'Expected Emscripten global function ' +
|
||||
'"__findCanvasEventTarget" not found. WebGL context creation ' +
|
||||
'"findCanvasEventTarget" not found. WebGL context creation ' +
|
||||
'may fail.');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
__findCanvasEventTarget = function(target) {
|
||||
findCanvasEventTarget = function(target) {
|
||||
if (Module && Module.canvas) {
|
||||
return Module.canvas;
|
||||
} else if (Module && Module.canvasCssSelector) {
|
||||
return __cachedFindCanvasEventTarget(Module.canvasCssSelector);
|
||||
return cachedFindCanvasEventTarget(Module.canvasCssSelector);
|
||||
} else {
|
||||
if (typeof console != = 'undefined') {
|
||||
console.warn('Module properties canvas and canvasCssSelector not ' +
|
||||
@@ -97,7 +97,7 @@ GlContext::StatusOrGlContext GlContext::Create(
|
||||
// We still go through with the find attempt, although for most use
|
||||
// cases it will not succeed, just in case the user does want to fall-
|
||||
// back.
|
||||
return __cachedFindCanvasEventTarget(target);
|
||||
return cachedFindCanvasEventTarget(target);
|
||||
}
|
||||
}; // NOLINT: Necessary semicolon.
|
||||
init_once = false;
|
||||
|
||||
@@ -23,6 +23,13 @@ std::unique_ptr<GlTextureBuffer> GlTextureBuffer::Wrap(
|
||||
deletion_callback);
|
||||
}
|
||||
|
||||
std::unique_ptr<GlTextureBuffer> GlTextureBuffer::Wrap(
|
||||
GLenum target, GLuint name, int width, int height, GpuBufferFormat format,
|
||||
std::shared_ptr<GlContext> context, DeletionCallback deletion_callback) {
|
||||
return absl::make_unique<GlTextureBuffer>(target, name, width, height, format,
|
||||
deletion_callback, context);
|
||||
}
|
||||
|
||||
std::unique_ptr<GlTextureBuffer> GlTextureBuffer::Create(int width, int height,
|
||||
GpuBufferFormat format,
|
||||
const void* data) {
|
||||
@@ -36,18 +43,22 @@ std::unique_ptr<GlTextureBuffer> GlTextureBuffer::Create(int width, int height,
|
||||
|
||||
GlTextureBuffer::GlTextureBuffer(GLenum target, GLuint name, int width,
|
||||
int height, GpuBufferFormat format,
|
||||
DeletionCallback deletion_callback)
|
||||
DeletionCallback deletion_callback,
|
||||
std::shared_ptr<GlContext> producer_context)
|
||||
: name_(name),
|
||||
width_(width),
|
||||
height_(height),
|
||||
format_(format),
|
||||
target_(target),
|
||||
deletion_callback_(deletion_callback) {}
|
||||
deletion_callback_(deletion_callback),
|
||||
producer_context_(producer_context) {}
|
||||
|
||||
bool GlTextureBuffer::CreateInternal(const void* data) {
|
||||
auto context = GlContext::GetCurrent();
|
||||
if (!context) return false;
|
||||
|
||||
producer_context_ = context; // Save creation GL context.
|
||||
|
||||
glGenTextures(1, &name_);
|
||||
if (!name_) return false;
|
||||
|
||||
@@ -106,6 +117,7 @@ void GlTextureBuffer::Updated(std::shared_ptr<GlSyncPoint> prod_token) {
|
||||
CHECK(!producer_sync_)
|
||||
<< "Updated existing texture which had not been marked for reuse!";
|
||||
producer_sync_ = std::move(prod_token);
|
||||
producer_context_ = producer_sync_->GetContext();
|
||||
}
|
||||
|
||||
void GlTextureBuffer::DidRead(std::shared_ptr<GlSyncPoint> cons_token) {
|
||||
|
||||
@@ -50,6 +50,11 @@ class GlTextureBuffer {
|
||||
GLenum target, GLuint name, int width, int height, GpuBufferFormat format,
|
||||
DeletionCallback deletion_callback);
|
||||
|
||||
// Same as Wrap above, but saves the given context for future use.
|
||||
static std::unique_ptr<GlTextureBuffer> Wrap(
|
||||
GLenum target, GLuint name, int width, int height, GpuBufferFormat format,
|
||||
std::shared_ptr<GlContext> context, DeletionCallback deletion_callback);
|
||||
|
||||
// Creates a texture of dimensions width x height and allocates space for it.
|
||||
// If data is provided, it is uploaded to the texture; otherwise, it can be
|
||||
// provided later via glTexSubImage2D.
|
||||
@@ -63,7 +68,8 @@ class GlTextureBuffer {
|
||||
// The commands producing the texture are assumed to be completed at the
|
||||
// time of this call. If not, call Updated on the result.
|
||||
GlTextureBuffer(GLenum target, GLuint name, int width, int height,
|
||||
GpuBufferFormat format, DeletionCallback deletion_callback);
|
||||
GpuBufferFormat format, DeletionCallback deletion_callback,
|
||||
std::shared_ptr<GlContext> producer_context = nullptr);
|
||||
~GlTextureBuffer();
|
||||
|
||||
// Included to support nativeGetGpuBuffer* in Java.
|
||||
@@ -111,6 +117,11 @@ class GlTextureBuffer {
|
||||
void WaitForConsumers();
|
||||
void WaitForConsumersOnGpu();
|
||||
|
||||
// Returns the GL context this buffer was created with.
|
||||
const std::shared_ptr<GlContext>& GetProducerContext() {
|
||||
return producer_context_;
|
||||
}
|
||||
|
||||
private:
|
||||
// Creates a texture of dimensions width x height and allocates space for it.
|
||||
// If data is provided, it is uploaded to the texture; otherwise, it can be
|
||||
@@ -132,6 +143,7 @@ class GlTextureBuffer {
|
||||
std::unique_ptr<GlMultiSyncPoint> consumer_multi_sync_ ABSL_GUARDED_BY(
|
||||
consumer_sync_mutex_) = absl::make_unique<GlMultiSyncPoint>();
|
||||
DeletionCallback deletion_callback_;
|
||||
std::shared_ptr<GlContext> producer_context_;
|
||||
};
|
||||
|
||||
using GlTextureBufferSharedPtr = std::shared_ptr<GlTextureBuffer>;
|
||||
|
||||
@@ -206,10 +206,11 @@ void GpuBufferMultiPool::EntryList::InsertAfter(Entry* entry, Entry* after) {
|
||||
Prepend(entry);
|
||||
}
|
||||
|
||||
void GpuBufferMultiPool::Evict() {
|
||||
void GpuBufferMultiPool::Evict(std::vector<SimplePool>* evicted) {
|
||||
// Remove excess entries.
|
||||
while (entry_list_.size() > kMaxPoolCount) {
|
||||
Entry* victim = entry_list_.tail();
|
||||
evicted->emplace_back(std::move(victim->pool));
|
||||
entry_list_.Remove(victim);
|
||||
pools_.erase(victim->spec);
|
||||
}
|
||||
@@ -230,6 +231,7 @@ void GpuBufferMultiPool::Evict() {
|
||||
entry->request_count /= 2;
|
||||
Entry* next = entry->next;
|
||||
if (entry->request_count == 0) {
|
||||
evicted->emplace_back(std::move(entry->pool));
|
||||
entry_list_.Remove(entry);
|
||||
pools_.erase(entry->spec);
|
||||
}
|
||||
@@ -240,36 +242,43 @@ void GpuBufferMultiPool::Evict() {
|
||||
|
||||
GpuBufferMultiPool::SimplePool GpuBufferMultiPool::RequestPool(
|
||||
const BufferSpec& key) {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
auto pool_it = pools_.find(key);
|
||||
Entry* entry;
|
||||
if (pool_it == pools_.end()) {
|
||||
std::tie(pool_it, std::ignore) =
|
||||
pools_.emplace(std::piecewise_construct, std::forward_as_tuple(key),
|
||||
std::forward_as_tuple(key));
|
||||
entry = &pool_it->second;
|
||||
CHECK_EQ(entry->request_count, 0);
|
||||
entry->request_count = 1;
|
||||
entry_list_.Append(entry);
|
||||
if (entry->prev != nullptr) CHECK_GE(entry->prev->request_count, 1);
|
||||
} else {
|
||||
entry = &pool_it->second;
|
||||
++entry->request_count;
|
||||
Entry* larger = entry->prev;
|
||||
while (larger != nullptr && larger->request_count < entry->request_count) {
|
||||
larger = larger->prev;
|
||||
SimplePool pool;
|
||||
std::vector<SimplePool> evicted;
|
||||
{
|
||||
absl::MutexLock lock(&mutex_);
|
||||
auto pool_it = pools_.find(key);
|
||||
Entry* entry;
|
||||
if (pool_it == pools_.end()) {
|
||||
std::tie(pool_it, std::ignore) =
|
||||
pools_.emplace(std::piecewise_construct, std::forward_as_tuple(key),
|
||||
std::forward_as_tuple(key));
|
||||
entry = &pool_it->second;
|
||||
CHECK_EQ(entry->request_count, 0);
|
||||
entry->request_count = 1;
|
||||
entry_list_.Append(entry);
|
||||
if (entry->prev != nullptr) CHECK_GE(entry->prev->request_count, 1);
|
||||
} else {
|
||||
entry = &pool_it->second;
|
||||
++entry->request_count;
|
||||
Entry* larger = entry->prev;
|
||||
while (larger != nullptr &&
|
||||
larger->request_count < entry->request_count) {
|
||||
larger = larger->prev;
|
||||
}
|
||||
if (larger != entry->prev) {
|
||||
entry_list_.Remove(entry);
|
||||
entry_list_.InsertAfter(entry, larger);
|
||||
}
|
||||
}
|
||||
if (larger != entry->prev) {
|
||||
entry_list_.Remove(entry);
|
||||
entry_list_.InsertAfter(entry, larger);
|
||||
if (!entry->pool && entry->request_count >= kMinRequestsBeforePool) {
|
||||
entry->pool = MakeSimplePool(key);
|
||||
}
|
||||
pool = entry->pool;
|
||||
++total_request_count_;
|
||||
Evict(&evicted);
|
||||
}
|
||||
if (!entry->pool && entry->request_count >= kMinRequestsBeforePool) {
|
||||
entry->pool = MakeSimplePool(key);
|
||||
}
|
||||
SimplePool pool = entry->pool;
|
||||
++total_request_count_;
|
||||
Evict();
|
||||
// Evicted pools, and their buffers, will be released without holding the
|
||||
// lock.
|
||||
return pool;
|
||||
}
|
||||
|
||||
|
||||
@@ -160,7 +160,8 @@ class GpuBufferMultiPool {
|
||||
SimplePool RequestPool(const BufferSpec& key);
|
||||
GpuBuffer GetBufferFromSimplePool(BufferSpec spec, const SimplePool& pool);
|
||||
GpuBuffer GetBufferWithoutPool(const BufferSpec& spec);
|
||||
void Evict() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
void Evict(std::vector<SimplePool>* evicted)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
absl::Mutex mutex_;
|
||||
std::unordered_map<BufferSpec, Entry, BufferSpecHash> pools_
|
||||
|
||||
Reference in New Issue
Block a user