Project import generated by Copybara.
PiperOrigin-RevId: 263889205
This commit is contained in:
+462
-36
@@ -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"],
|
||||
)
|
||||
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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.
|
||||
"""
|
||||
@@ -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_
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user