Project import generated by Copybara.

PiperOrigin-RevId: 263889205
This commit is contained in:
MediaPipe Team
2019-08-16 18:56:48 -07:00
committed by jqtang
parent dc40414468
commit 294687295d
443 changed files with 33160 additions and 2011 deletions
+462 -36
View File
@@ -16,6 +16,8 @@ licenses(["notice"]) # Apache 2.0
package(default_visibility = ["//visibility:public"])
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")
# Disabling GPU support is sometimes useful on desktop Linux because SwiftShader can
@@ -43,28 +45,61 @@ cc_library(
deps = [":gpu_service"],
)
GL_BASE_LINK_OPTS = select({
"//conditions:default": [],
"//mediapipe:android": [
"-lGLESv2",
"-lEGL",
# Note: on Android, libGLESv3.so is normally a symlink to
# libGLESv2.so, so we don't need to link to it. In fact, we
# do not _want_ to link to it, or we would be unable to load
# on API level < 18, where the symlink is missing entirely.
# Note: if we ever find a strange version of Android where the
# GLESv3 library is not a symlink, we will have to load it at
# runtime. Weak GLESv3 symbols will still be resolved if we
# load it early enough.
],
"//mediapipe:apple": [
"-framework OpenGLES",
"-framework CoreVideo",
],
"//mediapipe:macos": [
"-framework OpenGL",
"-framework CoreVideo",
],
})
# This is @unused internally.
GL_BASE_LINK_OPTS_OSS = GL_BASE_LINK_OPTS + select({
"//conditions:default": [
# Use GLES/EGL on linux.
# Requires support from graphics card driver (nvidia,mesa,etc..)
# and libraries to be installed.
# Ex: libegl1-mesa-dev libgles2-mesa-dev, or libegl1-nvidia libgles2-nvidia, etc...
"-lGLESv2",
"-lEGL",
],
"//mediapipe:android": [],
"//mediapipe:apple": [],
"//mediapipe:macos": [],
":disable_gpu": [],
})
cc_library(
name = "gl_base",
features = ["-layering_check"],
linkopts = select({
"//conditions:default": [],
"//mediapipe:android": [
"-lGLESv2",
"-lEGL",
# Note: on Android, libGLESv3.so is normally a symlink to
# libGLESv2.so, so we don't need to link to it. In fact, we
# do not _want_ to link to it, or we would be unable to load
# on API level < 18, where the symlink is missing entirely.
# Note: if we ever find a strange version of Android where the
# GLESv3 library is not a symlink, we will have to load it at
# runtime. Weak GLESv3 symbols will still be resolved if we
# load it early enough.
defines = select({
"//mediapipe:apple": [
"GLES_SILENCE_DEPRECATION=1",
],
"//conditions:default": [],
}),
features = ["-layering_check"],
linkopts = GL_BASE_LINK_OPTS_OSS,
textual_hdrs = ["gl_base.h"],
visibility = ["//visibility:public"],
deps = [":gl_base_hdr"] + select({
"//mediapipe:android": [],
"//mediapipe:apple": [],
"//conditions:default": [
],
}),
@@ -75,9 +110,21 @@ cc_library(
hdrs = ["gl_base.h"],
features = ["-layering_check"],
# Note: need the frameworks on Apple platforms to get the headers.
linkopts = select({
"//conditions:default": [],
"//mediapipe:apple": [
"-framework OpenGLES",
"-framework CoreVideo",
],
"//mediapipe:macos": [
"-framework OpenGL",
"-framework CoreVideo",
],
}),
visibility = ["//visibility:public"],
deps = select({
"//mediapipe:android": [],
"//mediapipe:apple": [],
"//conditions:default": [
],
}),
@@ -101,15 +148,27 @@ cc_library(
"//conditions:default": [
"gl_context_egl.cc",
],
"//mediapipe:apple": [
"gl_context_eagl.cc",
],
"//mediapipe:macos": [
"gl_context_nsgl.cc",
],
}),
hdrs = ["gl_context.h"],
copts = select({
"//conditions:default": [],
"//mediapipe:apple": [
"-x objective-c++",
"-std=c++11",
"-fobjc-arc",
],
}),
visibility = ["//visibility:public"],
deps = [
":gl_base",
":gl_thread_collector",
"//mediapipe/framework:executor",
"//mediapipe/framework:mediapipe_profiling",
"//mediapipe/framework:timestamp",
"//mediapipe/framework/port:logging",
"//mediapipe/framework/port:ret_check",
"//mediapipe/framework/port:status",
@@ -119,7 +178,14 @@ cc_library(
"@com_google_absl//absl/debugging:leak_check",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/synchronization",
],
"//mediapipe/framework:mediapipe_profiling",
"//mediapipe/framework:timestamp",
] + select({
"//conditions:default": [],
"//mediapipe:apple": [
"//mediapipe/objc:CFHolder",
],
}),
)
cc_library(
@@ -150,6 +216,13 @@ cc_library(
"//conditions:default": [
":gl_texture_buffer",
],
"//mediapipe:apple": [
"//mediapipe/objc:CFHolder",
],
"//mediapipe:macos": [
"//mediapipe/objc:CFHolder",
":gl_texture_buffer",
],
}),
)
@@ -167,6 +240,58 @@ cc_library(
],
)
objc_library(
name = "pixel_buffer_pool_util",
srcs = ["pixel_buffer_pool_util.mm"],
hdrs = ["pixel_buffer_pool_util.h"],
copts = [
"-std=c++11",
"-Wno-shorten-64-to-32",
],
sdk_frameworks = [
"CoreVideo",
],
visibility = ["//visibility:public"],
)
objc_library(
name = "MPPGraphGPUData",
srcs = [
"MPPGraphGPUData.mm",
"gpu_shared_data_internal.cc",
],
hdrs = ["MPPGraphGPUData.h"],
copts = [
"-x objective-c++",
"-std=c++11",
"-Wno-shorten-64-to-32",
],
sdk_frameworks = [
"CoreVideo",
"Metal",
] + select({
"//conditions:default": [
"OpenGLES",
],
"//mediapipe:macos": [
"OpenGL",
"AppKit",
],
}),
visibility = ["//visibility:public"],
deps = [
":gl_base",
":gl_context",
":gpu_buffer_multi_pool",
":gpu_shared_data_header",
":graph_support",
"//mediapipe/framework:calculator_context",
"//mediapipe/framework/port:ret_check",
"//mediapipe/gpu:gl_context_options_cc_proto",
"@google_toolbox_for_mac//:GTM_Defines",
],
)
proto_library(
name = "gl_context_options_proto",
srcs = ["gl_context_options.proto"],
@@ -188,7 +313,12 @@ cc_library(
name = "gpu_shared_data_header",
textual_hdrs = [
"gpu_shared_data_internal.h",
],
] + select({
"//conditions:default": [],
"//mediapipe:apple": [
"MPPGraphGPUData.h",
],
}),
visibility = ["//visibility:private"],
deps = [
":gl_base",
@@ -196,23 +326,46 @@ cc_library(
],
)
cc_library(
alias(
name = "gpu_shared_data_internal",
actual = select({
"//conditions:default": ":gpu_shared_data_internal_actual",
":disable_gpu": ":gpu_shared_data_internal_stub",
}),
visibility = ["//visibility:public"],
)
cc_library(
name = "gpu_shared_data_internal_stub",
hdrs = [
"gpu_shared_data_internal.h",
],
defines = ["MEDIAPIPE_DISABLE_GPU"],
visibility = ["//visibility:private"],
deps = [
":graph_support",
"//mediapipe/framework:calculator_context",
"//mediapipe/framework:calculator_node",
"//mediapipe/framework:executor",
"//mediapipe/framework/deps:no_destructor",
"//mediapipe/framework/port:ret_check",
"//mediapipe/gpu:gl_context_options_cc_proto",
],
)
cc_library(
name = "gpu_shared_data_internal_actual",
srcs = select({
"//conditions:default": [
"gpu_shared_data_internal.cc",
],
# iOS uses an Objective-C++ version of this, built in MediaPipeGraphGPUData.
":disable_gpu": [],
# iOS uses an Objective-C++ version of this, built in MPPGraphGPUData.
"//mediapipe:apple": [],
}),
hdrs = [
"gpu_shared_data_internal.h",
],
defines = select({
"//conditions:default": [],
":disable_gpu": ["MEDIAPIPE_DISABLE_GPU"],
}),
visibility = ["//visibility:public"],
visibility = ["//visibility:private"],
deps = [
"//mediapipe/gpu:gl_context_options_cc_proto",
":graph_support",
@@ -221,17 +374,15 @@ cc_library(
"//mediapipe/framework:calculator_node",
"//mediapipe/framework/port:ret_check",
"//mediapipe/framework/deps:no_destructor",
":gl_base",
":gl_context",
":gpu_buffer_multi_pool",
":gpu_shared_data_header",
] + select({
"//conditions:default": [
":gl_base",
":gl_context",
":gpu_buffer_multi_pool",
":gpu_shared_data_header",
],
":disable_gpu": [],
}) + select({
"//conditions:default": [],
":disable_gpu": [],
"//mediapipe:apple": [
":MPPGraphGPUData",
],
}),
)
@@ -241,11 +392,24 @@ cc_library(
"//conditions:default": [
"gl_texture_buffer_pool.cc",
],
"//mediapipe:apple": [],
"//mediapipe:macos": [
"gl_texture_buffer_pool.cc",
],
}),
hdrs = ["gpu_buffer_multi_pool.h"] + select({
"//conditions:default": [
"gl_texture_buffer_pool.h",
],
"//mediapipe:apple": [
# The inclusions check does not see that this is provided by
# pixel_buffer_pool_util, so we include it here too. This is
# b/28066691.
"pixel_buffer_pool_util.h",
],
"//mediapipe:macos": [
"gl_texture_buffer_pool.h",
],
}),
visibility = ["//visibility:public"],
deps = [
@@ -261,6 +425,14 @@ cc_library(
"//conditions:default": [
":gl_texture_buffer",
],
"//mediapipe:apple": [
":pixel_buffer_pool_util",
"//mediapipe/objc:CFHolder",
],
"//mediapipe:macos": [
":pixel_buffer_pool_util",
":gl_texture_buffer",
],
}),
)
@@ -293,13 +465,37 @@ HELPER_COMMON_HDRS = [
"gl_calculator_helper_impl.h",
]
HELPER_IOS_SRCS = [
"gl_calculator_helper_impl_ios.mm",
"gl_calculator_helper_impl_common.cc",
]
HELPER_IOS_FRAMEWORKS = [
"AVFoundation",
"CoreVideo",
"CoreGraphics",
"CoreMedia",
"GLKit",
"QuartzCore",
] + select({
"//conditions:default": [
"OpenGLES",
],
"//mediapipe:macos": [
"OpenGL",
"AppKit",
],
})
cc_library(
name = "gl_calculator_helper",
srcs = select({
"//conditions:default": HELPER_COMMON_SRCS + HELPER_ANDROID_SRCS,
"//mediapipe:apple": [],
}),
hdrs = HELPER_COMMON_HDRS + select({
"//conditions:default": HELPER_ANDROID_HDRS,
"//mediapipe:apple": [],
}),
visibility = ["//visibility:public"],
deps = [
@@ -311,6 +507,7 @@ cc_library(
":gpu_service",
":graph_support",
":shader_util",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework:calculator_cc_proto",
"//mediapipe/framework:calculator_context",
"//mediapipe/framework:calculator_node",
@@ -332,9 +529,58 @@ cc_library(
] + select({
"//conditions:default": [
],
"//mediapipe:apple": [
":gl_calculator_helper_ios",
"//mediapipe/objc:util",
],
}),
)
objc_library(
name = "gl_calculator_helper_ios",
srcs = HELPER_COMMON_SRCS + HELPER_IOS_SRCS,
hdrs = HELPER_COMMON_HDRS,
copts = [
"-std=c++11",
"-Wno-shorten-64-to-32",
],
sdk_frameworks = HELPER_IOS_FRAMEWORKS,
visibility = ["//visibility:public"],
deps = [
":gl_base",
":gl_context",
":gpu_buffer",
":gpu_buffer_multi_pool",
":gpu_service",
":gpu_shared_data_internal",
":shader_util",
"//mediapipe/framework:calculator_framework",
"//mediapipe/objc:mediapipe_framework_ios",
"//mediapipe/objc:util",
],
)
objc_library(
name = "MPPMetalHelper",
srcs = ["MPPMetalHelper.mm"],
hdrs = ["MPPMetalHelper.h"],
copts = [
"-std=c++11",
"-Wno-shorten-64-to-32",
],
sdk_frameworks = [
"CoreVideo",
"Metal",
],
visibility = ["//visibility:public"],
deps = [
":gpu_shared_data_internal",
":graph_support",
"//mediapipe/objc:mediapipe_framework_ios",
"@google_toolbox_for_mac//:GTM_Defines",
],
)
proto_library(
name = "scale_mode_proto",
srcs = ["scale_mode.proto"],
@@ -400,7 +646,10 @@ cc_library(
"//mediapipe/framework/formats:image_frame",
"//mediapipe/framework/port:ret_check",
"//mediapipe/framework/port:status",
],
] + select({
"//conditions:default": [],
"//mediapipe:apple": ["//mediapipe/objc:util"],
}),
alwayslink = 1,
)
@@ -452,6 +701,7 @@ cc_library(
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/port:ret_check",
"//mediapipe/framework/port:status",
"//mediapipe/framework/tool:options_util",
"//mediapipe/gpu:gl_scaler_calculator_cc_proto",
],
alwayslink = 1,
@@ -493,3 +743,179 @@ mediapipe_cc_proto_library(
visibility = ["//visibility:public"],
deps = [":gl_surface_sink_calculator_proto"],
)
### Metal calculators
metal_library(
name = "simple_shaders_mtl",
srcs = ["simple_shaders.metal"],
hdrs = ["metal_shader_base.h"],
)
# Only needed for cc_library depending on simple_shaders_mtl.
objc_library(
name = "simple_shaders_for_cc",
hdrs = ["metal_shader_base.h"],
deps = [":simple_shaders_mtl"],
)
proto_library(
name = "copy_calculator_proto",
srcs = ["copy_calculator.proto"],
visibility = ["//visibility:public"],
deps = ["//mediapipe/framework:calculator_proto"],
)
objc_library(
name = "metal_copy_calculator",
srcs = ["MetalCopyCalculator.mm"],
copts = ["-std=c++11"],
sdk_frameworks = [
"CoreVideo",
"Metal",
],
visibility = ["//visibility:public"],
deps = [
":MPPMetalHelper",
":simple_shaders_mtl",
"//mediapipe/gpu:copy_calculator_cc_proto",
"//mediapipe/objc:mediapipe_framework_ios",
],
alwayslink = 1,
)
objc_library(
name = "metal_rgb_weight_calculator",
srcs = ["MetalRgbWeightCalculator.mm"],
copts = ["-std=c++11"],
sdk_frameworks = [
"CoreVideo",
"Metal",
],
visibility = ["//visibility:public"],
deps = [
":MPPMetalHelper",
":simple_shaders_mtl",
"//mediapipe/objc:mediapipe_framework_ios",
],
alwayslink = 1,
)
objc_library(
name = "metal_sobel_calculator",
srcs = ["MetalSobelCalculator.mm"],
copts = ["-std=c++11"],
sdk_frameworks = [
"CoreVideo",
"Metal",
],
visibility = ["//visibility:public"],
deps = [
":MPPMetalHelper",
":simple_shaders_mtl",
"//mediapipe/objc:mediapipe_framework_ios",
],
alwayslink = 1,
)
objc_library(
name = "metal_sobel_compute_calculator",
srcs = ["MetalSobelComputeCalculator.mm"],
copts = ["-std=c++11"],
sdk_frameworks = [
"CoreVideo",
"Metal",
],
visibility = ["//visibility:public"],
deps = [
":MPPMetalHelper",
":simple_shaders_mtl",
"//mediapipe/objc:mediapipe_framework_ios",
],
alwayslink = 1,
)
objc_library(
name = "mps_sobel_calculator",
srcs = ["MPSSobelCalculator.mm"],
copts = ["-std=c++11"],
sdk_frameworks = [
"CoreVideo",
"Metal",
"MetalPerformanceShaders",
],
visibility = ["//visibility:public"],
deps = [
":MPPMetalHelper",
"//mediapipe/objc:mediapipe_framework_ios",
],
alwayslink = 1,
)
### Tests
cc_library(
name = "gpu_test_base",
testonly = 1,
hdrs = ["gpu_test_base.h"],
deps = [
":gl_calculator_helper",
":gpu_shared_data_internal",
"//testing/base/public:gunit_for_library_testonly",
],
)
MIN_IOS_VERSION = "9.0" # For thread_local.
test_suite(
name = "ios",
tags = ["ios"],
)
test_suite(
name = "metal",
tags = ["metal"],
)
objc_library(
name = "gl_ios_test_lib",
testonly = 1,
srcs = [
"MPPGraphGPUDataTests.mm",
"gl_ios_test.mm",
],
copts = [
"-std=c++11",
"-Wno-shorten-64-to-32",
],
data = [
"//mediapipe/objc:testdata/googlelogo_color_272x92dp.png",
],
deps = [
":MPPGraphGPUData",
":gl_scaler_calculator",
":gpu_buffer_to_image_frame_calculator",
":gpu_shared_data_internal",
":image_frame_to_gpu_buffer_calculator",
"//mediapipe/objc:MPPGraphTestBase",
"//mediapipe/objc:mediapipe_framework_ios",
"//mediapipe/framework/tool:source",
"//mediapipe/framework/port:threadpool",
"@com_google_absl//absl/memory",
] + select({
"//mediapipe:ios_i386": [],
"//mediapipe:ios_x86_64": [],
"//conditions:default": [
":metal_rgb_weight_calculator",
],
}),
)
ios_unit_test(
name = "gl_ios_test",
minimum_os_version = MIN_IOS_VERSION,
tags = [
"ios",
],
deps = [":gl_ios_test_lib"],
)
+71
View File
@@ -0,0 +1,71 @@
// Copyright 2019 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef MEDIAPIPE_GPU_DRISHTIGRAPHGPUDATA_H_
#define MEDIAPIPE_GPU_DRISHTIGRAPHGPUDATA_H_
#import <CoreVideo/CVMetalTextureCache.h>
#import <CoreVideo/CoreVideo.h>
#import <Metal/Metal.h>
#import "mediapipe/gpu/gl_base.h"
#import "mediapipe/gpu/gl_context.h"
namespace mediapipe {
class GlContext;
class GpuBufferMultiPool;
} // namespace mediapipe
@interface MPPGraphGPUData : NSObject {
// Shared buffer pool for GPU calculators.
mediapipe::GpuBufferMultiPool* _gpuBufferPool;
mediapipe::GlContext* _glContext;
}
- (instancetype)init NS_UNAVAILABLE;
/// Initialize. The provided multipool pointer must remain valid throughout
/// this object's lifetime.
- (instancetype)initWithContext:(mediapipe::GlContext*)context
multiPool:(mediapipe::GpuBufferMultiPool*)pool NS_DESIGNATED_INITIALIZER;
/// Shared texture pool for GPU calculators.
/// For internal use by GlCalculatorHelper.
@property(readonly) mediapipe::GpuBufferMultiPool* gpuBufferPool;
/// Shared OpenGL context.
#if TARGET_OS_OSX
@property(readonly) NSOpenGLContext* glContext;
@property(readonly) NSOpenGLPixelFormat* glPixelFormat;
#else
@property(readonly) EAGLContext* glContext;
#endif // TARGET_OS_OSX
/// Shared texture cache.
#if TARGET_OS_OSX
@property(readonly) CVOpenGLTextureCacheRef textureCache;
#else
@property(readonly) CVOpenGLESTextureCacheRef textureCache;
#endif // TARGET_OS_OSX
/// Shared Metal resources.
@property(readonly) id<MTLDevice> mtlDevice;
@property(readonly) id<MTLCommandQueue> mtlCommandQueue;
#if COREVIDEO_SUPPORTS_METAL
@property(readonly) CVMetalTextureCacheRef mtlTextureCache;
#endif
@end
#endif // MEDIAPIPE_GPU_DRISHTIGRAPHGPUDATA_H_
+123
View File
@@ -0,0 +1,123 @@
// Copyright 2019 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "mediapipe/gpu/MPPGraphGPUData.h"
#import "GTMDefines.h"
#include "mediapipe/gpu/gl_context.h"
#include "mediapipe/gpu/gpu_buffer_multi_pool.h"
#if TARGET_OS_OSX
#import <AppKit/NSOpenGL.h>
#else
#import <OpenGLES/EAGL.h>
#endif // TARGET_OS_OSX
@implementation MPPGraphGPUData
@synthesize textureCache = _textureCache;
@synthesize mtlDevice = _mtlDevice;
@synthesize mtlCommandQueue = _mtlCommandQueue;
#if COREVIDEO_SUPPORTS_METAL
@synthesize mtlTextureCache = _mtlTextureCache;
#endif
#if TARGET_OS_OSX
typedef CVOpenGLTextureCacheRef CVTextureCacheType;
#else
typedef CVOpenGLESTextureCacheRef CVTextureCacheType;
#endif // TARGET_OS_OSX
- (instancetype)initWithContext:(mediapipe::GlContext*)context
multiPool:(mediapipe::GpuBufferMultiPool*)pool {
self = [super init];
if (self) {
_gpuBufferPool = pool;
_glContext = context;
}
return self;
}
- (void)dealloc {
if (_textureCache) {
_textureCache = NULL;
}
#if COREVIDEO_SUPPORTS_METAL
if (_mtlTextureCache) {
CFRelease(_mtlTextureCache);
_mtlTextureCache = NULL;
}
#endif
}
#if TARGET_OS_OSX
- (NSOpenGLContext *)glContext {
return _glContext->nsgl_context();
}
- (NSOpenGLPixelFormat *) glPixelFormat {
return _glContext->nsgl_pixel_format();
}
#else
- (EAGLContext *)glContext {
return _glContext->eagl_context();
}
#endif // TARGET_OS_OSX
- (CVTextureCacheType)textureCache {
@synchronized(self) {
if (!_textureCache) {
_textureCache = _glContext->cv_texture_cache();
}
}
return _textureCache;
}
- (mediapipe::GpuBufferMultiPool *)gpuBufferPool {
return _gpuBufferPool;
}
- (id<MTLDevice>)mtlDevice {
@synchronized(self) {
if (!_mtlDevice) {
_mtlDevice = MTLCreateSystemDefaultDevice();
}
}
return _mtlDevice;
}
- (id<MTLCommandQueue>)mtlCommandQueue {
@synchronized(self) {
if (!_mtlCommandQueue) {
_mtlCommandQueue = [self.mtlDevice newCommandQueue];
}
}
return _mtlCommandQueue;
}
#if COREVIDEO_SUPPORTS_METAL
- (CVMetalTextureCacheRef)mtlTextureCache {
@synchronized(self) {
if (!_mtlTextureCache) {
CVReturn err = CVMetalTextureCacheCreate(NULL, NULL, self.mtlDevice, NULL, &_mtlTextureCache);
NSAssert(err == kCVReturnSuccess, @"Error at CVMetalTextureCacheCreate %d", err);
// TODO: register and flush metal caches too.
}
}
return _mtlTextureCache;
}
#endif
@end
+86
View File
@@ -0,0 +1,86 @@
// Copyright 2019 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#include <memory>
#include "absl/memory/memory.h"
#include "mediapipe/framework/port/threadpool.h"
#import "mediapipe/gpu/MPPGraphGPUData.h"
#import "mediapipe/gpu/gpu_shared_data_internal.h"
@interface MPPGraphGPUDataTests : XCTestCase {
}
@end
@implementation MPPGraphGPUDataTests
// This test verifies that the internal Objective-C object is correctly
// released when the C++ wrapper is released.
- (void)testCorrectlyReleased {
__weak id gpuData = nil;
std::weak_ptr<mediapipe::GpuResources> gpuRes;
@autoreleasepool {
mediapipe::GpuSharedData gpu_shared;
gpuRes = gpu_shared.gpu_resources;
gpuData = gpu_shared.gpu_resources->ios_gpu_data();
XCTAssertNotEqual(gpuRes.lock(), nullptr);
XCTAssertNotNil(gpuData);
}
XCTAssertEqual(gpuRes.lock(), nullptr);
XCTAssertNil(gpuData);
}
// This test verifies that the lazy initialization of the glContext instance
// variable is thread-safe. All threads should read the same value.
- (void)testGlContextThreadSafeLazyInitialization {
mediapipe::GpuSharedData gpu_shared;
constexpr int kNumThreads = 10;
EAGLContext* ogl_context[kNumThreads];
auto pool = absl::make_unique<mediapipe::ThreadPool>(kNumThreads);
pool->StartWorkers();
for (int i = 0; i < kNumThreads; ++i) {
pool->Schedule([&gpu_shared, &ogl_context, i] {
ogl_context[i] = gpu_shared.gpu_resources->ios_gpu_data().glContext;
});
}
pool.reset();
for (int i = 0; i < kNumThreads - 1; ++i) {
XCTAssertEqual(ogl_context[i], ogl_context[i + 1]);
}
}
// This test verifies that the lazy initialization of the textureCache instance
// variable is thread-safe. All threads should read the same value.
- (void)testTextureCacheThreadSafeLazyInitialization {
mediapipe::GpuSharedData gpu_shared;
constexpr int kNumThreads = 10;
CFHolder<CVOpenGLESTextureCacheRef> texture_cache[kNumThreads];
auto pool = absl::make_unique<mediapipe::ThreadPool>(kNumThreads);
pool->StartWorkers();
for (int i = 0; i < kNumThreads; ++i) {
pool->Schedule([&gpu_shared, &texture_cache, i] {
texture_cache[i].reset(gpu_shared.gpu_resources->ios_gpu_data().textureCache);
});
}
pool.reset();
for (int i = 0; i < kNumThreads - 1; ++i) {
XCTAssertEqual(*texture_cache[i], *texture_cache[i + 1]);
}
}
@end
+105
View File
@@ -0,0 +1,105 @@
// Copyright 2019 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef MEDIAPIPE_GPU_MEDIAPIPE_METAL_HELPER_H_
#define MEDIAPIPE_GPU_MEDIAPIPE_METAL_HELPER_H_
#import <CoreVideo/CVMetalTextureCache.h>
#import <CoreVideo/CoreVideo.h>
#import <Metal/Metal.h>
#include "mediapipe/framework/packet.h"
#include "mediapipe/framework/packet_type.h"
#include "mediapipe/gpu/MPPGraphGPUData.h"
#include "mediapipe/gpu/gpu_shared_data_internal.h"
NS_ASSUME_NONNULL_BEGIN
@interface MPPMetalHelper : NSObject {
MPPGraphGPUData* _gpuShared;
}
- (instancetype)init NS_UNAVAILABLE;
/// Initialize. This initializer is recommended for calculators.
- (instancetype)initWithCalculatorContext:(mediapipe::CalculatorContext*)cc;
/// Initialize.
- (instancetype)initWithGpuResources:(mediapipe::GpuResources*)gpuResources
NS_DESIGNATED_INITIALIZER;
/// Configures a calculator's contract for accessing GPU resources.
/// Calculators should use this in GetContract.
+ (::mediapipe::Status)updateContract:(mediapipe::CalculatorContract*)cc;
/// Deprecated initializer.
- (instancetype)initWithSidePackets:(const mediapipe::PacketSet&)inputSidePackets;
/// Deprecated initializer.
- (instancetype)initWithGpuSharedData:(mediapipe::GpuSharedData*)gpuShared;
/// Configures a calculator's side packets for accessing GPU resources.
/// Calculators should use this in FillExpectations.
+ (::mediapipe::Status)setupInputSidePackets:(mediapipe::PacketTypeSet*)inputSidePackets;
/// Get a metal command buffer.
/// Calculators should use this method instead of getting a buffer from the
/// MTLCommandQueue directly, not just for convenience, but also because the
/// framework may want to add some custom hooks to the commandBuffers used by
/// calculators.
- (id<MTLCommandBuffer>)commandBuffer;
/// Creates a CVMetalTextureRef linked to the provided GpuBuffer.
/// Ownership follows the copy rule, so the caller is responsible for
/// releasing the CVMetalTextureRef.
- (CVMetalTextureRef)copyCVMetalTextureWithGpuBuffer:(const mediapipe::GpuBuffer&)gpuBuffer;
/// Creates a CVMetalTextureRef linked to the provided GpuBuffer given a specific plane.
/// Ownership follows the copy rule, so the caller is responsible for
/// releasing the CVMetalTextureRef.
- (CVMetalTextureRef)copyCVMetalTextureWithGpuBuffer:(const mediapipe::GpuBuffer&)gpuBuffer
plane:(size_t)plane;
/// Returns a MTLTexture linked to the provided GpuBuffer.
/// A calculator can freely use it as a rendering source, but it should not
/// use it as a rendering target if the GpuBuffer was provided as an input.
- (id<MTLTexture>)metalTextureWithGpuBuffer:(const mediapipe::GpuBuffer&)gpuBuffer;
/// Returns a MTLTexture linked to the provided GpuBuffer given a specific plane.
/// A calculator can freely use it as a rendering source, but it should not
/// use it as a rendering target if the GpuBuffer was provided as an input.
- (id<MTLTexture>)metalTextureWithGpuBuffer:(const mediapipe::GpuBuffer&)gpuBuffer
plane:(size_t)plane;
/// Obtains a new GpuBuffer to be used as an output destination.
- (mediapipe::GpuBuffer)mediapipeGpuBufferWithWidth:(int)width height:(int)height;
/// Obtains a new GpuBuffer to be used as an output destination.
- (mediapipe::GpuBuffer)mediapipeGpuBufferWithWidth:(int)width
height:(int)height
format:(mediapipe::GpuBufferFormat)format;
/// Convenience method to load a Metal library stored as a bundle resource.
- (id<MTLLibrary>)newLibraryWithResourceName:(NSString*)name error:(NSError* _Nullable*)error;
/// Shared Metal resources.
@property(readonly) id<MTLDevice> mtlDevice;
@property(readonly) id<MTLCommandQueue> mtlCommandQueue;
@property(readonly) CVMetalTextureCacheRef mtlTextureCache;
@end
NS_ASSUME_NONNULL_END
#endif // MEDIAPIPE_GPU_MEDIAPIPE_METAL_HELPER_H_
+210
View File
@@ -0,0 +1,210 @@
// Copyright 2019 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "mediapipe/gpu/MPPMetalHelper.h"
#import "mediapipe/gpu/graph_support.h"
#import "GTMDefines.h"
#include "mediapipe/framework/port/ret_check.h"
namespace mediapipe {
// Using a C++ class so it can be declared as a friend of LegacyCalculatorSupport.
class MetalHelperLegacySupport {
public:
static CalculatorContract* GetCalculatorContract() {
return LegacyCalculatorSupport::Scoped<CalculatorContract>::current();
}
static CalculatorContext* GetCalculatorContext() {
return LegacyCalculatorSupport::Scoped<CalculatorContext>::current();
}
};
} // namespace mediapipe
@implementation MPPMetalHelper
- (instancetype)initWithGpuResources:(mediapipe::GpuResources*)gpuResources {
self = [super init];
if (self) {
_gpuShared = gpuResources->ios_gpu_data();
}
return self;
}
- (instancetype)initWithGpuSharedData:(mediapipe::GpuSharedData*)gpuShared {
return [self initWithGpuResources:gpuShared->gpu_resources.get()];
}
- (instancetype)initWithCalculatorContext:(mediapipe::CalculatorContext*)cc {
if (!cc) return nil;
return [self initWithGpuResources:&cc->Service(mediapipe::kGpuService).GetObject()];
}
+ (::mediapipe::Status)updateContract:(mediapipe::CalculatorContract*)cc {
cc->UseService(mediapipe::kGpuService);
// Allow the legacy side packet to be provided, too, for backwards
// compatibility with existing graphs. It will just be ignored.
auto& input_side_packets = cc->InputSidePackets();
auto id = input_side_packets.GetId(mediapipe::kGpuSharedTagName, 0);
if (id.IsValid()) {
input_side_packets.Get(id).Set<mediapipe::GpuSharedData*>();
}
return ::mediapipe::OkStatus();
}
// Legacy support.
- (instancetype)initWithSidePackets:(const mediapipe::PacketSet&)inputSidePackets {
auto cc = mediapipe::MetalHelperLegacySupport::GetCalculatorContext();
if (cc) {
CHECK_EQ(&inputSidePackets, &cc->InputSidePackets());
return [self initWithCalculatorContext:cc];
}
// TODO: remove when we can.
LOG(WARNING)
<< "CalculatorContext not available. If this calculator uses "
"CalculatorBase, call initWithCalculatorContext instead.";
mediapipe::GpuSharedData* gpu_shared =
inputSidePackets.Tag(mediapipe::kGpuSharedTagName).Get<mediapipe::GpuSharedData*>();
return [self initWithGpuResources:gpu_shared->gpu_resources.get()];
}
// Legacy support.
+ (::mediapipe::Status)setupInputSidePackets:(mediapipe::PacketTypeSet*)inputSidePackets {
auto cc = mediapipe::MetalHelperLegacySupport::GetCalculatorContract();
if (cc) {
CHECK_EQ(inputSidePackets, &cc->InputSidePackets());
return [self updateContract:cc];
}
// TODO: remove when we can.
LOG(WARNING)
<< "CalculatorContract not available. If you're calling this "
"from a GetContract method, call updateContract instead.";
auto id = inputSidePackets->GetId(mediapipe::kGpuSharedTagName, 0);
RET_CHECK(id.IsValid())
<< "A " << mediapipe::kGpuSharedTagName
<< " input side packet is required here.";
inputSidePackets->Get(id).Set<mediapipe::GpuSharedData*>();
return ::mediapipe::OkStatus();
}
- (id<MTLDevice>)mtlDevice {
return _gpuShared.mtlDevice;
}
- (id<MTLCommandQueue>)mtlCommandQueue {
return _gpuShared.mtlCommandQueue;
}
- (CVMetalTextureCacheRef)mtlTextureCache {
return _gpuShared.mtlTextureCache;
}
- (id<MTLCommandBuffer>)commandBuffer {
return [_gpuShared.mtlCommandQueue commandBuffer];
}
- (CVMetalTextureRef)copyCVMetalTextureWithGpuBuffer:(const mediapipe::GpuBuffer&)gpuBuffer
plane:(size_t)plane {
CVPixelBufferRef pixel_buffer = gpuBuffer.GetCVPixelBufferRef();
OSType pixel_format = CVPixelBufferGetPixelFormatType(pixel_buffer);
MTLPixelFormat metalPixelFormat = MTLPixelFormatInvalid;
int width = gpuBuffer.width();
int height = gpuBuffer.height();
switch (pixel_format) {
case kCVPixelFormatType_32BGRA:
NSCAssert(plane == 0, @"Invalid plane number");
metalPixelFormat = MTLPixelFormatBGRA8Unorm;
break;
case kCVPixelFormatType_64RGBAHalf:
NSCAssert(plane == 0, @"Invalid plane number");
metalPixelFormat = MTLPixelFormatRGBA16Float;
break;
case kCVPixelFormatType_OneComponent8:
NSCAssert(plane == 0, @"Invalid plane number");
metalPixelFormat = MTLPixelFormatR8Uint;
break;
case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
if (plane == 0) {
metalPixelFormat = MTLPixelFormatR8Unorm;
} else if (plane == 1) {
metalPixelFormat = MTLPixelFormatRG8Unorm;
} else {
NSCAssert(NO, @"Invalid plane number");
}
width = CVPixelBufferGetWidthOfPlane(pixel_buffer, plane);
height = CVPixelBufferGetHeightOfPlane(pixel_buffer, plane);
break;
case kCVPixelFormatType_TwoComponent16Half:
metalPixelFormat = MTLPixelFormatRG16Float;
NSCAssert(plane == 0, @"Invalid plane number");
break;
case kCVPixelFormatType_OneComponent32Float:
metalPixelFormat = MTLPixelFormatR32Float;
NSCAssert(plane == 0, @"Invalid plane number");
break;
default:
NSCAssert(NO, @"Invalid pixel buffer format");
break;
}
CVMetalTextureRef texture;
CVReturn err = CVMetalTextureCacheCreateTextureFromImage(
NULL, _gpuShared.mtlTextureCache, gpuBuffer.GetCVPixelBufferRef(), NULL,
metalPixelFormat, width, height, plane, &texture);
CHECK_EQ(err, kCVReturnSuccess);
return texture;
}
- (CVMetalTextureRef)copyCVMetalTextureWithGpuBuffer:(const mediapipe::GpuBuffer&)gpuBuffer {
return [self copyCVMetalTextureWithGpuBuffer:gpuBuffer plane:0];
}
- (id<MTLTexture>)metalTextureWithGpuBuffer:(const mediapipe::GpuBuffer&)gpuBuffer {
return [self metalTextureWithGpuBuffer:gpuBuffer plane:0];
}
- (id<MTLTexture>)metalTextureWithGpuBuffer:(const mediapipe::GpuBuffer&)gpuBuffer
plane:(size_t)plane {
CFHolder<CVMetalTextureRef> cvTexture;
cvTexture.adopt([self copyCVMetalTextureWithGpuBuffer:gpuBuffer plane:plane]);
return CVMetalTextureGetTexture(*cvTexture);
}
- (mediapipe::GpuBuffer)mediapipeGpuBufferWithWidth:(int)width height:(int)height {
return _gpuShared.gpuBufferPool->GetBuffer(width, height);
}
- (mediapipe::GpuBuffer)mediapipeGpuBufferWithWidth:(int)width
height:(int)height
format:(mediapipe::GpuBufferFormat)format {
return _gpuShared.gpuBufferPool->GetBuffer(width, height, format);
}
- (id<MTLLibrary>)newLibraryWithResourceName:(NSString*)name error:(NSError * _Nullable *)error {
return [_gpuShared.mtlDevice newLibraryWithFile:[[NSBundle bundleForClass:[self class]]
pathForResource:name ofType:@"metallib"]
error:error];
}
@end
+3
View File
@@ -32,6 +32,9 @@ struct EglSurfaceHolder {
EGLSurface surface GUARDED_BY(mutex) = EGL_NO_SURFACE;
// True if MediaPipe created the surface and is responsible for destroying it.
bool owned GUARDED_BY(mutex) = false;
// Vertical flip of the surface, useful for conversion between coordinate
// systems with top-left v.s. bottom-left origins.
bool flip_y = false;
};
} // namespace mediapipe
+1 -1
View File
@@ -70,7 +70,7 @@
#include <GLES3/gl32.h>
// When using the Linux EGL headers, we may end up pulling a
// "#define Status int" from Xlib.h, which interferes with util::Status.
// "#define Status int" from Xlib.h, which interferes with mediapipe::Status.
#undef Status
// More crud from X
+2 -2
View File
@@ -32,7 +32,7 @@
#ifdef __APPLE__
#include <CoreVideo/CoreVideo.h>
#include "mediapipe/framework/ios/CFHolder.h"
#include "mediapipe/objc/CFHolder.h"
#endif // __APPLE__
namespace mediapipe {
@@ -40,7 +40,7 @@ namespace mediapipe {
class GlCalculatorHelperImpl;
class GlTexture;
class GpuResources;
class GpuSharedData;
struct GpuSharedData;
#ifdef __APPLE__
#if TARGET_OS_OSX
@@ -0,0 +1,192 @@
// Copyright 2019 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "mediapipe/gpu/gl_calculator_helper_impl.h"
#if TARGET_OS_OSX
#import <AppKit/NSOpenGL.h>
#else
#import <OpenGLES/EAGL.h>
#endif // TARGET_OS_OSX
#import <AVFoundation/AVFoundation.h>
#include "absl/memory/memory.h"
#include "mediapipe/gpu/gpu_buffer_multi_pool.h"
#include "mediapipe/gpu/pixel_buffer_pool_util.h"
#include "mediapipe/objc/util.h"
namespace mediapipe {
GlVersion GlCalculatorHelperImpl::GetGlVersion() {
#if TARGET_OS_OSX
return GlVersion::kGL;
#else
if (gl_context_->eagl_context().API == kEAGLRenderingAPIOpenGLES3) return GlVersion::kGLES3;
else return GlVersion::kGLES2;
#endif // TARGET_OS_OSX
}
#if MEDIAPIPE_GPU_BUFFER_USE_CV_PIXEL_BUFFER
GlTexture GlCalculatorHelperImpl::CreateSourceTexture(
const mediapipe::ImageFrame& image_frame) {
GlTexture texture;
texture.helper_impl_ = this;
texture.width_ = image_frame.Width();
texture.height_ = image_frame.Height();
auto format = GpuBufferFormatForImageFormat(image_frame.Format());
GlTextureInfo info = GlTextureInfoForGpuBufferFormat(format, 0, GetGlVersion());
glGenTextures(1, &texture.name_);
glBindTexture(GL_TEXTURE_2D, texture.name_);
glTexImage2D(GL_TEXTURE_2D, 0, info.gl_internal_format, texture.width_,
texture.height_, 0, info.gl_format, info.gl_type,
image_frame.PixelData());
SetStandardTextureParams(GL_TEXTURE_2D);
return texture;
}
GlTexture GlCalculatorHelperImpl::CreateSourceTexture(
const GpuBuffer& gpu_buffer) {
return MapGpuBuffer(gpu_buffer, 0);
}
GlTexture GlCalculatorHelperImpl::CreateSourceTexture(
const GpuBuffer& gpu_buffer, int plane) {
return MapGpuBuffer(gpu_buffer, plane);
}
GlTexture GlCalculatorHelperImpl::MapGpuBuffer(
const GpuBuffer& gpu_buffer, int plane) {
CVReturn err;
GlTexture texture;
texture.helper_impl_ = this;
texture.gpu_buffer_ = gpu_buffer;
texture.plane_ = plane;
const GlTextureInfo info =
GlTextureInfoForGpuBufferFormat(gpu_buffer.format(), plane, GetGlVersion());
// When scale is not 1, we still give the nominal size of the image.
texture.width_ = gpu_buffer.width();
texture.height_ = gpu_buffer.height();
#if TARGET_OS_OSX
CVOpenGLTextureRef cv_texture_temp;
err = CVOpenGLTextureCacheCreateTextureFromImage(
kCFAllocatorDefault, gl_context_->cv_texture_cache(), gpu_buffer.GetCVPixelBufferRef(), NULL,
&cv_texture_temp);
NSCAssert(cv_texture_temp && !err,
@"Error at CVOpenGLTextureCacheCreateTextureFromImage %d", err);
texture.cv_texture_.adopt(cv_texture_temp);
texture.target_ = CVOpenGLTextureGetTarget(*texture.cv_texture_);
texture.name_ = CVOpenGLTextureGetName(*texture.cv_texture_);
#else
CVOpenGLESTextureRef cv_texture_temp;
err = CVOpenGLESTextureCacheCreateTextureFromImage(
kCFAllocatorDefault, gl_context_->cv_texture_cache(), gpu_buffer.GetCVPixelBufferRef(), NULL,
GL_TEXTURE_2D, info.gl_internal_format, texture.width_ / info.downscale,
texture.height_ / info.downscale, info.gl_format, info.gl_type, plane,
&cv_texture_temp);
NSCAssert(cv_texture_temp && !err,
@"Error at CVOpenGLESTextureCacheCreateTextureFromImage %d", err);
texture.cv_texture_.adopt(cv_texture_temp);
texture.target_ = CVOpenGLESTextureGetTarget(*texture.cv_texture_);
texture.name_ = CVOpenGLESTextureGetName(*texture.cv_texture_);
#endif // TARGET_OS_OSX
glBindTexture(texture.target(), texture.name());
SetStandardTextureParams(texture.target());
return texture;
}
#endif // MEDIAPIPE_GPU_BUFFER_USE_CV_PIXEL_BUFFER
template<>
std::unique_ptr<ImageFrame> GlTexture::GetFrame<ImageFrame>() const {
#if MEDIAPIPE_GPU_BUFFER_USE_CV_PIXEL_BUFFER
if (gpu_buffer_.GetCVPixelBufferRef()) {
return CreateImageFrameForCVPixelBuffer(gpu_buffer_.GetCVPixelBufferRef());
}
ImageFormat::Format image_format =
ImageFormatForGpuBufferFormat(gpu_buffer_.format());
// TODO: handle gl version here.
GlTextureInfo info = GlTextureInfoForGpuBufferFormat(
gpu_buffer_.format(), plane_);
auto output = absl::make_unique<ImageFrame>(
image_format, width_, height_);
glReadPixels(0, 0, width_, height_, info.gl_format, info.gl_type,
output->MutablePixelData());
return output;
#else
CHECK(gpu_buffer_.format() == GpuBufferFormat::kBGRA32);
auto output =
absl::make_unique<ImageFrame>(ImageFormat::SRGBA, width_, height_,
ImageFrame::kGlDefaultAlignmentBoundary);
CHECK(helper_impl_);
helper_impl_->ReadTexture(*this, output->MutablePixelData(), output->PixelDataSize());
return output;
#endif // MEDIAPIPE_GPU_BUFFER_USE_CV_PIXEL_BUFFER
}
template<>
std::unique_ptr<GpuBuffer> GlTexture::GetFrame<GpuBuffer>() const {
NSCAssert(gpu_buffer_, @"gpu_buffer_ must be valid");
#if TARGET_IPHONE_SIMULATOR
CVPixelBufferRef pixel_buffer = gpu_buffer_.GetCVPixelBufferRef();
CVReturn err = CVPixelBufferLockBaseAddress(pixel_buffer, 0);
NSCAssert(err == kCVReturnSuccess, @"CVPixelBufferLockBaseAddress failed: %d", err);
OSType pixel_format = CVPixelBufferGetPixelFormatType(pixel_buffer);
if (pixel_format == kCVPixelFormatType_32BGRA) {
// TODO: restore previous framebuffer? Move this to helper so we can
// use BindFramebuffer?
glViewport(0, 0, width_, height_);
glFramebufferTexture2D(
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target_, name_, 0);
glReadPixels(0, 0, width_, height_, GL_BGRA, GL_UNSIGNED_BYTE,
CVPixelBufferGetBaseAddress(pixel_buffer));
} else {
uint32_t format_big = CFSwapInt32HostToBig(pixel_format);
NSLog(@"unsupported pixel format: %.4s", (char*)&format_big);
}
err = CVPixelBufferUnlockBaseAddress(pixel_buffer, 0);
NSCAssert(err == kCVReturnSuccess, @"CVPixelBufferUnlockBaseAddress failed: %d", err);
#endif
return absl::make_unique<GpuBuffer>(gpu_buffer_);
}
void GlTexture::Release() {
#if MEDIAPIPE_GPU_BUFFER_USE_CV_PIXEL_BUFFER
if (*cv_texture_) {
cv_texture_.reset(NULL);
} else if (name_) {
// This is only needed because of the glGenTextures in
// CreateSourceTexture(ImageFrame)... change.
glDeleteTextures(1, &name_);
}
#endif // MEDIAPIPE_GPU_BUFFER_USE_CV_PIXEL_BUFFER
helper_impl_ = nullptr;
gpu_buffer_ = nullptr;
plane_ = 0;
name_ = 0;
width_ = 0;
height_ = 0;
}
} // namespace mediapipe
+1 -1
View File
@@ -33,7 +33,7 @@
#ifdef __APPLE__
#include <CoreVideo/CoreVideo.h>
#include "mediapipe/framework/ios/CFHolder.h"
#include "mediapipe/objc/CFHolder.h"
#if TARGET_OS_OSX
+8 -1
View File
@@ -75,7 +75,14 @@ GlContext::StatusOrGlContext GlContext::Create(EAGLSharegroup* sharegroup,
return ::mediapipe::OkStatus();
}
void GlContext::DestroyContext() {}
void GlContext::DestroyContext() {
if (*texture_cache_) {
// The texture cache must be flushed on tear down, otherwise we potentially
// leak pixel buffers whose textures have pending GL operations after the
// CVOpenGLESTextureRef is released in GlTexture::Release.
CVOpenGLESTextureCacheFlush(*texture_cache_, 0);
}
}
GlContext::ContextBinding GlContext::ThisContextBinding() {
GlContext::ContextBinding result;
+10
View File
@@ -34,8 +34,18 @@ static pthread_key_t egl_release_thread_key;
static pthread_once_t egl_release_key_once = PTHREAD_ONCE_INIT;
static void EglThreadExitCallback(void* key_value) {
#if defined(__ANDROID__)
eglMakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
#else
// Some implementations have chosen to allow EGL_NO_DISPLAY as a valid display
// parameter for eglMakeCurrent. This behavior is not portable to all EGL
// implementations, and should be considered as an undocumented vendor
// extension.
// https://www.khronos.org/registry/EGL/sdk/docs/man/html/eglMakeCurrent.xhtml
eglMakeCurrent(eglGetDisplay(EGL_DEFAULT_DISPLAY), EGL_NO_SURFACE,
EGL_NO_SURFACE, EGL_NO_CONTEXT);
#endif
eglReleaseThread();
}
+8 -1
View File
@@ -102,7 +102,14 @@ GlContext::StatusOrGlContext GlContext::Create(NSOpenGLContext* share_context,
return ::mediapipe::OkStatus();
}
void GlContext::DestroyContext() {}
void GlContext::DestroyContext() {
if (*texture_cache_) {
// The texture cache must be flushed on tear down, otherwise we potentially
// leak pixel buffers whose textures have pending GL operations after the
// CVOpenGLTextureRef is released in GlTexture::Release.
CVOpenGLTextureCacheFlush(*texture_cache_, 0);
}
}
GlContext::ContextBinding GlContext::ThisContextBinding() {
GlContext::ContextBinding result;
+252
View File
@@ -0,0 +1,252 @@
// Copyright 2019 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#import "mediapipe/framework/tool/source.pb.h"
#import "mediapipe/gpu/gpu_shared_data_internal.h"
#import "mediapipe/objc/MPPGraph.h"
#import "mediapipe/objc/MPPGraphTestBase.h"
#import "mediapipe/objc/util.h"
#include "absl/memory/memory.h"
#import "mediapipe/framework/calculator_framework.h"
#import "mediapipe/gpu/gl_calculator_helper.h"
@interface GLIOSTests : MPPGraphTestBase{
UIImage* _sourceImage;
MPPGraph* _graph;
}
@end
@implementation GLIOSTests
- (void)setUp {
[super setUp];
_sourceImage = [self testImageNamed:@"googlelogo_color_272x92dp" extension:@"png"];
}
- (void)tearDown {
[super tearDown];
}
- (CVPixelBufferRef)redPixelBuffer:(CVPixelBufferRef)input {
return [self transformPixelBuffer:input
outputPixelFormat:kCVPixelFormatType_32BGRA
transformation:^(CVPixelBufferRef input,
CVPixelBufferRef output) {
vImage_Buffer vInput = vImageForCVPixelBuffer(input);
vImage_Buffer vRed = vImageForCVPixelBuffer(output);
static const int16_t matrix[16] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 256, 0,
0, 0, 0, 256,
};
vImage_Error vError = vImageMatrixMultiply_ARGB8888(
&vInput, &vRed, matrix, 256, NULL, NULL, 0);
XCTAssertEqual(vError, kvImageNoError);
}];
}
- (CVPixelBufferRef)luminancePixelBuffer:(CVPixelBufferRef)input {
return [self transformPixelBuffer:input
outputPixelFormat:kCVPixelFormatType_32BGRA
transformation:^(CVPixelBufferRef input,
CVPixelBufferRef output) {
vImage_Buffer vInput = vImageForCVPixelBuffer(input);
vImage_Buffer vLuminance = vImageForCVPixelBuffer(output);
// sRGB weights: R 0.2125, G 0.7154, B 0.0721
static const int16_t matrix[16] = {
721, 721, 721, 0,
7154, 7154, 7154, 0,
2125, 2125, 2125, 0,
0, 0, 0, 10000,
};
vImage_Error vError = vImageMatrixMultiply_ARGB8888(
&vInput, &vLuminance, matrix, 10000, NULL, NULL, 0);
XCTAssertEqual(vError, kvImageNoError);
}];
}
- (CVPixelBufferRef)grayPixelBuffer:(CVPixelBufferRef)input {
return [self transformPixelBuffer:input
outputPixelFormat:kCVPixelFormatType_OneComponent8
transformation:^(CVPixelBufferRef input,
CVPixelBufferRef output) {
vImage_Buffer vInput = vImageForCVPixelBuffer(input);
vImage_Buffer vGray = vImageForCVPixelBuffer(output);
vImage_Error vError = vImageBGRAToGray(&vInput, &vGray);
XCTAssertEqual(vError, kvImageNoError);
}];
}
- (void)testGlConverters {
CFHolder<CVPixelBufferRef> originalPixelBuffer;
::mediapipe::Status status =
CreateCVPixelBufferFromCGImage([_sourceImage CGImage], &originalPixelBuffer);
XCTAssert(status.ok());
mediapipe::CalculatorGraphConfig config;
config.add_input_stream("input_frames");
auto node = config.add_node();
node->set_calculator("GpuBufferToImageFrameCalculator");
node->add_input_stream("input_frames");
node->add_output_stream("image_frames");
auto node2 = config.add_node();
node2->set_calculator("ImageFrameToGpuBufferCalculator");
node2->add_input_stream("image_frames");
node2->add_output_stream("output_frames");
_graph = [[MPPGraph alloc] initWithGraphConfig:config];
[_graph addFrameOutputStream:"output_frames"
outputPacketType:MediaPipePacketPixelBuffer];
[self testGraph:_graph input:*originalPixelBuffer expectedOutput:*originalPixelBuffer];
}
- (void)testGlConvertersNoOpInserted {
CFHolder<CVPixelBufferRef> originalPixelBuffer;
::mediapipe::Status status =
CreateCVPixelBufferFromCGImage([_sourceImage CGImage], &originalPixelBuffer);
XCTAssert(status.ok());
mediapipe::CalculatorGraphConfig config;
config.add_input_stream("input_frames");
auto node = config.add_node();
node->set_calculator("GpuBufferToImageFrameCalculator");
node->add_input_stream("input_frames");
node->add_output_stream("image_frames");
// This node should be a no-op, since its inputs are already ImageFrames.
auto no_op_node = config.add_node();
no_op_node->set_calculator("GpuBufferToImageFrameCalculator");
no_op_node->add_input_stream("image_frames");
no_op_node->add_output_stream("still_image_frames");
auto node2 = config.add_node();
node2->set_calculator("ImageFrameToGpuBufferCalculator");
node2->add_input_stream("still_image_frames");
node2->add_output_stream("output_frames");
_graph = [[MPPGraph alloc] initWithGraphConfig:config];
[_graph addFrameOutputStream:"output_frames"
outputPacketType:MediaPipePacketPixelBuffer];
[self testGraph:_graph input:*originalPixelBuffer expectedOutput:*originalPixelBuffer];
}
- (void)testGlConvertersWithOptionalSidePackets {
CFHolder<CVPixelBufferRef> originalPixelBuffer;
::mediapipe::Status status =
CreateCVPixelBufferFromCGImage([_sourceImage CGImage], &originalPixelBuffer);
XCTAssert(status.ok());
mediapipe::CalculatorGraphConfig config;
config.add_input_stream("input_frames");
auto node = config.add_node();
node->set_calculator("GpuBufferToImageFrameCalculator");
node->add_input_stream("input_frames");
node->add_output_stream("image_frames");
auto node2 = config.add_node();
node2->set_calculator("ImageFrameToGpuBufferCalculator");
node2->add_input_stream("image_frames");
node2->add_output_stream("output_frames");
_graph = [[MPPGraph alloc] initWithGraphConfig:config];
[_graph addFrameOutputStream:"output_frames"
outputPacketType:MediaPipePacketPixelBuffer];
[self testGraph:_graph input:*originalPixelBuffer expectedOutput:*originalPixelBuffer];
}
- (void)testDestinationSizes {
mediapipe::GpuSharedData gpuData;
mediapipe::GlCalculatorHelper helper;
helper.InitializeForTest(&gpuData);
std::vector<std::pair<int, int>> sizes{
{200, 300},
{200, 299},
{196, 300},
{194, 300},
{193, 300},
};
for (const auto& width_height : sizes) {
mediapipe::GlTexture texture =
helper.CreateDestinationTexture(width_height.first, width_height.second);
XCTAssertNotEqual(texture.name(), 0);
}
}
- (void)testSimpleConversionFromFormat:(OSType)cvPixelFormat {
CFHolder<CVPixelBufferRef> originalPixelBuffer;
::mediapipe::Status status =
CreateCVPixelBufferFromCGImage([_sourceImage CGImage], &originalPixelBuffer);
XCTAssert(status.ok());
CVPixelBufferRef convertedPixelBuffer =
[self convertPixelBuffer:*originalPixelBuffer
toPixelFormat:cvPixelFormat];
CVPixelBufferRef bgraPixelBuffer =
[self convertPixelBuffer:convertedPixelBuffer
toPixelFormat:kCVPixelFormatType_32BGRA];
mediapipe::CalculatorGraphConfig config;
config.add_input_stream("input_frames");
auto node = config.add_node();
node->set_calculator("GlScalerCalculator");
node->add_input_stream("input_frames");
node->add_output_stream("output_frames");
_graph = [[MPPGraph alloc] initWithGraphConfig:config];
[_graph addFrameOutputStream:"output_frames"
outputPacketType:MediaPipePacketPixelBuffer];
[self testGraph:_graph input:convertedPixelBuffer expectedOutput:bgraPixelBuffer];
CFRelease(convertedPixelBuffer);
CFRelease(bgraPixelBuffer);
}
- (void)testOneComponent8 {
[self testSimpleConversionFromFormat:kCVPixelFormatType_OneComponent8];
}
- (void)testMetalRgbWeight {
#if TARGET_IPHONE_SIMULATOR
NSLog(@"Metal tests skipped on Simulator.");
#else
CFHolder<CVPixelBufferRef> originalPixelBuffer;
::mediapipe::Status status =
CreateCVPixelBufferFromCGImage([_sourceImage CGImage], &originalPixelBuffer);
XCTAssert(status.ok());
CVPixelBufferRef redPixelBuffer = [self redPixelBuffer:*originalPixelBuffer];
mediapipe::CalculatorGraphConfig config;
config.add_input_stream("input_frames");
auto node = config.add_node();
node->set_calculator("MetalRgbWeightCalculator");
node->add_input_stream("input_frames");
node->add_output_stream("output_frames");
node->add_input_side_packet("WEIGHTS:rgb_weights");
_graph = [[MPPGraph alloc] initWithGraphConfig:config];
[_graph addFrameOutputStream:"output_frames"
outputPacketType:MediaPipePacketPixelBuffer];
[_graph setSidePacket:(mediapipe::MakePacket<float[3]>(1.0, 0.0, 0.0))
named:"rgb_weights"];
[self testGraph:_graph input:*originalPixelBuffer expectedOutput:redPixelBuffer];
CFRelease(redPixelBuffer);
#endif // TARGET_IPHONE_SIMULATOR
}
@end
+9 -1
View File
@@ -15,6 +15,7 @@
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/port/ret_check.h"
#include "mediapipe/framework/port/status.h"
#include "mediapipe/framework/tool/options_util.h"
#include "mediapipe/gpu/gl_calculator_helper.h"
#include "mediapipe/gpu/gl_quad_renderer.h"
#include "mediapipe/gpu/gl_scaler_calculator.pb.h"
@@ -52,6 +53,8 @@ namespace mediapipe {
// both having padding of 1 pixels. So the value of output stream is 1 / 5 =
// 0.2.
// Additional input side packets:
// OPTIONS: the GlScalerCalculatorOptions to use. Will replace or merge with
// existing calculator options, depending on field merge_fields.
// OUTPUT_DIMENSIONS: the output width and height in pixels.
// ROTATION: the counterclockwise rotation angle in degrees.
// These can also be specified as options.
@@ -101,6 +104,9 @@ REGISTER_CALCULATOR(GlScalerCalculator);
}
RETURN_IF_ERROR(GlCalculatorHelper::UpdateContract(cc));
if (cc->InputSidePackets().HasTag("OPTIONS")) {
cc->InputSidePackets().Tag("OPTIONS").Set<GlScalerCalculatorOptions>();
}
if (HasTagOrIndex(&cc->InputSidePackets(), "OUTPUT_DIMENSIONS", 1)) {
TagOrIndex(&cc->InputSidePackets(), "OUTPUT_DIMENSIONS", 1)
.Set<DimensionsPacketType>();
@@ -127,7 +133,9 @@ REGISTER_CALCULATOR(GlScalerCalculator);
RETURN_IF_ERROR(helper_.Open(cc));
int rotation_ccw = 0;
const auto& options = cc->Options<GlScalerCalculatorOptions>();
const auto& options =
tool::RetrieveOptions(cc->Options<GlScalerCalculatorOptions>(),
cc->InputSidePackets(), "OPTIONS");
if (options.has_output_width()) {
dst_width_ = options.output_width();
}
+1 -1
View File
@@ -128,7 +128,7 @@ REGISTER_CALCULATOR(GlSurfaceSinkCalculator);
renderer_->GlRender(src.width(), src.height(), dst_width, dst_height,
scale_mode_, FrameRotation::kNone,
/*flip_horizontal=*/false, /*flip_vertical=*/false,
/*flip_texture=*/false));
/*flip_texture=*/surface_holder_->flip_y));
glBindTexture(src.target(), 0);
+1 -1
View File
@@ -23,7 +23,7 @@
#if defined(__APPLE__)
#include <CoreVideo/CoreVideo.h>
#include "mediapipe/framework/ios/CFHolder.h"
#include "mediapipe/objc/CFHolder.h"
#if !TARGET_OS_OSX
#define MEDIAPIPE_GPU_BUFFER_USE_CV_PIXEL_BUFFER 1
#endif // TARGET_OS_OSX
+3 -3
View File
@@ -22,7 +22,7 @@
#include "mediapipe/gpu/gpu_shared_data_internal.h"
#ifdef __APPLE__
#include "mediapipe/framework/ios/CFHolder.h"
#include "mediapipe/objc/CFHolder.h"
#endif // __APPLE__
namespace mediapipe {
@@ -129,7 +129,7 @@ GpuBufferMultiPool::~GpuBufferMultiPool() {
#ifdef __APPLE__
void GpuBufferMultiPool::RegisterTextureCache(CVTextureCacheType cache) {
MutexLock lock(&mutex_);
absl::MutexLock lock(&mutex_);
CHECK(std::find(texture_caches_.begin(), texture_caches_.end(), cache) ==
texture_caches_.end())
@@ -138,7 +138,7 @@ void GpuBufferMultiPool::RegisterTextureCache(CVTextureCacheType cache) {
}
void GpuBufferMultiPool::UnregisterTextureCache(CVTextureCacheType cache) {
MutexLock lock(&mutex_);
absl::MutexLock lock(&mutex_);
auto it = std::find(texture_caches_.begin(), texture_caches_.end(), cache);
CHECK(it != texture_caches_.end())
+1 -1
View File
@@ -39,7 +39,7 @@
namespace mediapipe {
class GpuSharedData;
struct GpuSharedData;
struct BufferSpec {
BufferSpec(int w, int h, GpuBufferFormat f)
@@ -19,7 +19,7 @@
#define HAVE_GPU_BUFFER
#ifdef __APPLE__
#include "mediapipe/framework/ios/util.h"
#include "mediapipe/objc/util.h"
#endif
#include "mediapipe/gpu/gl_calculator_helper.h"
+4 -5
View File
@@ -20,7 +20,7 @@
#include "mediapipe/gpu/graph_support.h"
#if __APPLE__
#import "mediapipe/gpu/MediaPipeGraphGPUData.h"
#import "mediapipe/gpu/MPPGraphGPUData.h"
#endif // __APPLE__
namespace mediapipe {
@@ -85,9 +85,8 @@ GpuResources::GpuResources(std::shared_ptr<GlContext> gl_context) {
std::make_shared<GlContextExecutor>(gl_context.get());
#if __APPLE__
gpu_buffer_pool().RegisterTextureCache(gl_context->cv_texture_cache());
ios_gpu_data_ =
[[MediaPipeGraphGPUData alloc] initWithContext:gl_context.get()
multiPool:&gpu_buffer_pool_];
ios_gpu_data_ = [[MPPGraphGPUData alloc] initWithContext:gl_context.get()
multiPool:&gpu_buffer_pool_];
#endif // __APPLE__
}
@@ -170,7 +169,7 @@ const std::shared_ptr<GlContext>& GpuResources::gl_context(
GpuSharedData::GpuSharedData() : GpuSharedData(kPlatformGlContextNone) {}
#if __APPLE__
MediaPipeGraphGPUData* GpuResources::ios_gpu_data() { return ios_gpu_data_; }
MPPGraphGPUData* GpuResources::ios_gpu_data() { return ios_gpu_data_; }
#endif // __APPLE__
} // namespace mediapipe
+4 -4
View File
@@ -31,9 +31,9 @@
#ifdef __APPLE__
#ifdef __OBJC__
@class MediaPipeGraphGPUData;
@class MPPGraphGPUData;
#else
struct MediaPipeGraphGPUData;
struct MPPGraphGPUData;
#endif // __OBJC__
#endif // defined(__APPLE__)
@@ -66,7 +66,7 @@ class GpuResources {
GpuBufferMultiPool& gpu_buffer_pool() { return gpu_buffer_pool_; }
#ifdef __APPLE__
MediaPipeGraphGPUData* ios_gpu_data();
MPPGraphGPUData* ios_gpu_data();
#endif // defined(__APPLE__)
void PrepareGpuNode(CalculatorNode* node);
@@ -93,7 +93,7 @@ class GpuResources {
#ifdef __APPLE__
// Note that this is an Objective-C object.
MediaPipeGraphGPUData* ios_gpu_data_;
MPPGraphGPUData* ios_gpu_data_;
#endif // defined(__APPLE__)
std::map<std::string, std::shared_ptr<Executor>> named_executors_;
@@ -18,7 +18,7 @@
#include "mediapipe/gpu/gl_calculator_helper.h"
#ifdef __APPLE__
#include "mediapipe/framework/ios/util.h"
#include "mediapipe/objc/util.h"
#endif
namespace mediapipe {
+181
View File
@@ -0,0 +1,181 @@
"""Experimental Skylark rules for Apple's Metal.
This creates a .metallib file containing compiled Metal shaders.
Note that the default behavior in Xcode is to put all metal shaders into a
single "default.metallib", which can be loaded using the method
newDefaultLibrary in MTLDevice. Meanwhile, the metal_library rule creates a
named .metallib, which can be loaded using newLibraryWithFile:error:.
Example:
metal_library(
name = "my_shaders",
srcs = ["my_shaders.metal"],
hdrs = ["my_shaders.h"],
)
This produces a "my_shaders.metallib".
The metal_library target can be added to the deps attribute of an objc_library.
The dependent objc_library can then access the headers declared by the
metal_library, if any.
The metal_library target can also be added to the resources attribute as a
simple data file, but in that case any declared headers are not visible to
dependent objc_library rules.
"""
load("@build_bazel_apple_support//lib:apple_support.bzl", "apple_support")
load("@bazel_skylib//lib:dicts.bzl", "dicts")
# This load statement is overriding the visibility of the internal implementation of rules_apple.
# This rule will be migrated to rules_apple in the future, hence the override. Please do not use
# this import anywhere else.
load(
"@build_bazel_rules_apple//apple/internal:resources.bzl",
"resources",
)
def _metal_compiler_args(ctx, src, obj, minimum_os_version, copts, diagnostics, deps_dump):
"""Returns arguments for metal compiler."""
apple_fragment = ctx.fragments.apple
platform = apple_fragment.single_arch_platform
if not minimum_os_version:
minimum_os_version = ctx.attr._xcode_config[apple_common.XcodeVersionConfig].minimum_os_for_platform_type(
platform.platform_type,
)
args = copts + [
"-arch",
"air64", # TODO: choose based on target device/cpu/platform?
"-emit-llvm",
"-c",
"-gline-tables-only",
"-isysroot",
apple_support.path_placeholders.sdkroot(),
"-ffast-math",
"-serialize-diagnostics",
diagnostics.path,
"-o",
obj.path,
"-mios-version-min=%s" % minimum_os_version,
"",
src.path,
"-MMD",
"-MT",
"dependencies",
"-MF",
deps_dump.path,
]
return args
def _metal_compiler_inputs(srcs, hdrs, deps = []):
"""Determines the list of inputs required for a compile action."""
objc_providers = [x.objc for x in deps if hasattr(x, "objc")]
objc_files = depset()
for objc in objc_providers:
objc_files += objc.header
return srcs + hdrs + objc_files.to_list()
def _metal_library_impl(ctx):
"""Implementation for metal_library Skylark rule."""
# A unique path for rule's outputs.
objs_outputs_path = "{}.objs/".format(ctx.label.name)
output_objs = []
for src in ctx.files.srcs:
basename = src.basename
obj = ctx.actions.declare_file(objs_outputs_path + basename + ".air")
output_objs.append(obj)
diagnostics = ctx.actions.declare_file(objs_outputs_path + basename + ".dia")
deps_dump = ctx.actions.declare_file(objs_outputs_path + basename + ".dat")
args = (["metal"] +
_metal_compiler_args(ctx, src, obj, ctx.attr.minimum_os_version, ctx.attr.copts, diagnostics, deps_dump))
apple_support.run(
ctx,
xcode_path_resolve_level = apple_support.xcode_path_resolve_level.args,
inputs = _metal_compiler_inputs(ctx.files.srcs, ctx.files.hdrs, ctx.attr.deps),
outputs = [obj, diagnostics, deps_dump],
mnemonic = "MetalCompile",
executable = "/usr/bin/xcrun",
arguments = args,
use_default_shell_env = False,
progress_message = ("Compiling Metal shader %s" %
(basename)),
)
output_lib = ctx.actions.declare_file(ctx.label.name + ".metallib")
args = [
"metallib",
"-split-module",
"-o",
output_lib.path,
] + [x.path for x in output_objs]
apple_support.run(
ctx,
xcode_path_resolve_level = apple_support.xcode_path_resolve_level.args,
inputs = output_objs,
outputs = (output_lib,),
mnemonic = "MetalLink",
executable = "/usr/bin/xcrun",
arguments = args,
progress_message = (
"Linking Metal library %s" % ctx.label.name
),
)
# This ridiculous circumlocution is needed because new_objc_provider rejects
# an empty depset, with the error:
# "Value for key header must be a set of File, instead found set of unknown."
# It also rejects an explicit "None".
additional_params = {}
if ctx.files.hdrs:
additional_params["header"] = depset([f for f in ctx.files.hdrs])
objc_provider = apple_common.new_objc_provider(
providers = [x.objc for x in ctx.attr.deps if hasattr(x, "objc")],
**additional_params
)
return [
DefaultInfo(
files = depset([output_lib]),
),
objc_provider,
# Return the provider for the new bundling logic of rules_apple.
resources.bucketize_typed([output_lib], "unprocessed"),
]
METAL_LIBRARY_ATTRS = dicts.add(apple_support.action_required_attrs(), {
"srcs": attr.label_list(allow_files = [".metal"], allow_empty = False),
"hdrs": attr.label_list(allow_files = [".h"]),
"deps": attr.label_list(providers = [["objc"]]),
"copts": attr.string_list(),
"minimum_os_version": attr.string(),
})
metal_library = rule(
implementation = _metal_library_impl,
attrs = METAL_LIBRARY_ATTRS,
fragments = ["apple", "objc", "swift"],
output_to_genfiles = True,
)
"""
Builds a Metal library.
Args:
srcs: Metal shader sources.
hdrs: Header files used by the shader sources.
deps: objc_library targets whose headers should be visible to the shaders.
The header files declared in this rule are also visible to any objc_library
rules that have it as a dependency, so that constants and typedefs can be
shared between Metal and Objective-C code.
"""
+73
View File
@@ -0,0 +1,73 @@
// Copyright 2019 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef MEDIAPIPE_GPU_PIXEL_BUFFER_POOL_UTIL_H_
#define MEDIAPIPE_GPU_PIXEL_BUFFER_POOL_UTIL_H_
#include <CoreVideo/CoreVideo.h>
#include <TargetConditionals.h>
#include <functional>
#ifndef __APPLE__
#error gpu_pixel_buffer_pool_util is only for use on Apple platforms.
#endif // !defined(__APPLE__)
namespace mediapipe {
#if TARGET_OS_OSX
typedef CVOpenGLTextureCacheRef CVTextureCacheType;
#else
typedef CVOpenGLESTextureCacheRef CVTextureCacheType;
#endif // TARGET_OS_OSX
// Create a CVPixelBufferPool.
CVPixelBufferPoolRef CreateCVPixelBufferPool(int width, int height,
OSType pixel_format,
int keep_count,
CFTimeInterval maxAge);
// Preallocate the given number of pixel buffers.
OSStatus PreallocateCVPixelBufferPoolBuffers(CVPixelBufferPoolRef pool,
int count,
CFDictionaryRef auxAttributes);
// Create a CVPixelBuffer using a pool.
// If the pool is full, will flush the provided texture cache before trying
// again.
CVReturn CreateCVPixelBufferWithPool(CVPixelBufferPoolRef pool,
CFDictionaryRef auxAttributes,
CVTextureCacheType textureCache,
CVPixelBufferRef* outBuffer);
// Create a CVPixelBuffer using a pool.
// If the pool is full, will call the provided function before trying again.
CVReturn CreateCVPixelBufferWithPool(CVPixelBufferPoolRef pool,
CFDictionaryRef auxAttributes,
std::function<void(void)> flush,
CVPixelBufferRef* outBuffer);
// Create an auxiliary attribute dictionary, which can be used with
// CVPixelBufferPool, specifying the given allocation threshold.
CFDictionaryRef CreateCVPixelBufferPoolAuxiliaryAttributesForThreshold(
int allocationThreshold);
// Create a CVPixelBuffer without using a pool.
CVReturn CreateCVPixelBufferWithoutPool(int width, int height,
OSType pixelFormat,
CVPixelBufferRef* outBuffer);
} // namespace mediapipe
#endif // MEDIAPIPE_GPU_PIXEL_BUFFER_POOL_UTIL_H_
+163
View File
@@ -0,0 +1,163 @@
// Copyright 2019 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "mediapipe/gpu/pixel_buffer_pool_util.h"
#import <Foundation/Foundation.h>
#if !defined(ENABLE_MEDIAPIPE_GPU_BUFFER_THRESHOLD_CHECK) && !defined(NDEBUG)
#define ENABLE_MEDIAPIPE_GPU_BUFFER_THRESHOLD_CHECK 1
#endif // defined(ENABLE_MEDIAPIPE_GPU_BUFFER_THRESHOLD_CHECK)
namespace mediapipe {
CVPixelBufferPoolRef CreateCVPixelBufferPool(
int width, int height, OSType pixelFormat, int keepCount,
CFTimeInterval maxAge) {
CVPixelBufferPoolRef pool = NULL;
NSDictionary *sourcePixelBufferOptions = @{
(id)kCVPixelBufferPixelFormatTypeKey : @(pixelFormat),
(id)kCVPixelBufferWidthKey : @(width),
(id)kCVPixelBufferHeightKey : @(height),
#if TARGET_OS_OSX
(id)kCVPixelFormatOpenGLCompatibility : @(YES),
#else
(id)kCVPixelFormatOpenGLESCompatibility : @(YES),
#endif // TARGET_OS_OSX
(id)kCVPixelBufferIOSurfacePropertiesKey : @{ /*empty dictionary*/ }
};
NSMutableDictionary *pixelBufferPoolOptions = [[NSMutableDictionary alloc] init];
pixelBufferPoolOptions[(id)kCVPixelBufferPoolMinimumBufferCountKey] = @(keepCount);
if (maxAge > 0) {
pixelBufferPoolOptions[(id)kCVPixelBufferPoolMaximumBufferAgeKey] = @(maxAge);
}
CVPixelBufferPoolCreate(
kCFAllocatorDefault, (__bridge CFDictionaryRef)pixelBufferPoolOptions,
(__bridge CFDictionaryRef)sourcePixelBufferOptions, &pool);
return pool;
}
OSStatus PreallocateCVPixelBufferPoolBuffers(
CVPixelBufferPoolRef pool, int count, CFDictionaryRef auxAttributes) {
CVReturn err = kCVReturnSuccess;
NSMutableArray *pixelBuffers = [[NSMutableArray alloc] init];
for (int i = 0; i < count && err == kCVReturnSuccess; i++) {
CVPixelBufferRef pixelBuffer = NULL;
err = CVPixelBufferPoolCreatePixelBufferWithAuxAttributes(
kCFAllocatorDefault, pool, auxAttributes, &pixelBuffer);
if (err != kCVReturnSuccess) {
break;
}
[pixelBuffers addObject:(__bridge id)pixelBuffer];
CFRelease(pixelBuffer);
}
return err;
}
CFDictionaryRef CreateCVPixelBufferPoolAuxiliaryAttributesForThreshold(int allocationThreshold) {
if (allocationThreshold > 0) {
return (CFDictionaryRef)CFBridgingRetain(
@{(id)kCVPixelBufferPoolAllocationThresholdKey: @(allocationThreshold)});
} else {
return nil;
}
}
CVReturn CreateCVPixelBufferWithPool(
CVPixelBufferPoolRef pool, CFDictionaryRef auxAttributes,
CVTextureCacheType textureCache, CVPixelBufferRef* outBuffer) {
return CreateCVPixelBufferWithPool(pool, auxAttributes, [textureCache](){
#if TARGET_OS_OSX
CVOpenGLTextureCacheFlush(textureCache, 0);
#else
CVOpenGLESTextureCacheFlush(textureCache, 0);
#endif // TARGET_OS_OSX
}, outBuffer);
}
CVReturn CreateCVPixelBufferWithPool(
CVPixelBufferPoolRef pool, CFDictionaryRef auxAttributes,
std::function<void(void)> flush, CVPixelBufferRef* outBuffer) {
CVReturn err = CVPixelBufferPoolCreatePixelBufferWithAuxAttributes(
kCFAllocatorDefault, pool, auxAttributes, outBuffer);
if (err == kCVReturnWouldExceedAllocationThreshold) {
if (flush) {
// Call the flush function to potentially release the retained buffers
// and try again to create a pixel buffer.
flush();
err = CVPixelBufferPoolCreatePixelBufferWithAuxAttributes(
kCFAllocatorDefault, pool, auxAttributes, outBuffer);
}
if (err == kCVReturnWouldExceedAllocationThreshold) {
// TODO: allow the application to set the threshold. For now, disable it by
// default, since the threshold we are using is arbitrary and some graphs routinely cross it.
#ifdef ENABLE_MEDIAPIPE_GPU_BUFFER_THRESHOLD_CHECK
NSLog(@"Using more buffers than expected! This is a debug-only warning, "
"you can ignore it if your app works fine otherwise.");
#ifdef DEBUG
NSLog(@"Pool status: %@", ((__bridge NSObject *)pool).description);
#endif // DEBUG
#endif // defined(ENABLE_MEDIAPIPE_GPU_BUFFER_THRESHOLD_CHECK)
// Try again and ignore threshold.
// TODO drop a frame instead?
err = CVPixelBufferPoolCreatePixelBufferWithAuxAttributes(
kCFAllocatorDefault, pool, NULL, outBuffer);
}
}
return err;
}
#if TARGET_IPHONE_SIMULATOR
static void FreeRefConReleaseCallback(void* refCon, const void* baseAddress) {
free(refCon);
}
#endif
CVReturn CreateCVPixelBufferWithoutPool(
int width, int height, OSType pixelFormat, CVPixelBufferRef* outBuffer) {
NSDictionary *attributes = @{
#if TARGET_OS_OSX
(id)kCVPixelFormatOpenGLCompatibility : @(YES),
#else
(id)kCVPixelFormatOpenGLESCompatibility : @(YES),
#endif // TARGET_OS_OSX
(id)kCVPixelBufferIOSurfacePropertiesKey : @{ /*empty dictionary*/ }
};
#if TARGET_IPHONE_SIMULATOR
// On the simulator, syncing the texture with the pixelbuffer does not work,
// and we have to use glReadPixels. Since GL_UNPACK_ROW_LENGTH is not
// available in OpenGL ES 2, we should create the buffer so the pixels are
// contiguous.
//
// TODO: verify if we can use kIOSurfaceBytesPerRow to force
// CoreVideo to give us contiguous data.
size_t bytes_per_row = width * 4;
void* data = malloc(bytes_per_row * height);
return CVPixelBufferCreateWithBytes(
kCFAllocatorDefault, width, height, pixelFormat, data, bytes_per_row,
FreeRefConReleaseCallback, data, (__bridge CFDictionaryRef)attributes,
outBuffer);
#else
return CVPixelBufferCreate(
kCFAllocatorDefault, width, height, pixelFormat,
(__bridge CFDictionaryRef)attributes, outBuffer);
#endif
}
} // namespace mediapipe