chore: import upstream snapshot with attribution
cffconvert / validate (push) Has been skipped
License Check / license-check (push) Failing after 2s

This commit is contained in:
wehub-resource-sync
2026-07-13 12:14:16 +08:00
commit 8a852e4b4e
36502 changed files with 9277225 additions and 0 deletions
+388
View File
@@ -0,0 +1,388 @@
load("@bazel_skylib//lib:selects.bzl", "selects")
load("@build_bazel_rules_apple//apple:ios.bzl", "ios_static_framework")
load("@build_bazel_rules_apple//apple:macos.bzl", "macos_dylib")
load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
load("@rules_cc//cc:objc_library.bzl", "objc_library")
load(
"//tensorflow/core/platform:build_config_root.bzl",
"tf_gpu_tests_tags",
)
load("//tensorflow/lite:build_def.bzl", "CXX17_BAZEL_ONLY_COPTS", "tflite_pagesize_linkopts")
load("//tensorflow/lite:special_rules.bzl", "tflite_extra_gles_deps", "tflite_portable_test_suite")
load("//tensorflow/lite/delegates/gpu:build_defs.bzl", "gpu_delegate_linkopts")
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
default_visibility = ["//visibility:public"],
licenses = ["notice"],
)
exports_files([
"delegate.h",
"delegate_options.h",
"metal_delegate.h",
])
_DELEGATE_NO_GL_DEPS = select({
"//tensorflow:android": [
":async_buffers",
"//tensorflow/lite/core/async/c:task",
"//tensorflow/lite/core/async/interop/c:attribute_map",
"//tensorflow/lite/core/async/interop/c:constants",
"//tensorflow/lite/delegates/gpu/gl:android_sync",
"//tensorflow/lite/delegates/gpu/gl:egl_environment",
"//tensorflow/lite/delegates/utils",
"//tensorflow/lite/delegates/utils:async_type_helpers",
"//tensorflow/lite/delegates/utils:ret_macros",
"//tensorflow/lite/delegates/utils:sync_fence",
],
"//conditions:default": [],
}) + [
":android_hardware_buffer",
":api",
":delegate_options",
":tflite_profile",
#"//third_party/GL:EGL_headers",
#"//third_party/GL:GLES3_headers",
# go/keep-sorted start
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/types:span",
"//tensorflow/lite/async:backend_async_kernel_interface",
"//tensorflow/lite/core/async/interop/c:types",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/delegates/gpu/cl:util",
"//tensorflow/lite/delegates/gpu/common:data_type",
"//tensorflow/lite/delegates/gpu/common:model",
"//tensorflow/lite/delegates/gpu/common:model_builder",
"//tensorflow/lite/delegates/gpu/common:model_builder_helper",
"//tensorflow/lite/delegates/gpu/common:quantization_util",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates:serialization",
"//tensorflow/lite/kernels:kernel_util",
"//tensorflow/lite/profiling/telemetry",
"//tensorflow/lite/profiling/telemetry/c:telemetry_setting",
"//tensorflow/lite/profiling/telemetry/c:telemetry_setting_internal",
"//tensorflow/lite/profiling/telemetry:telemetry_status",
"//tensorflow/lite:kernel_api",
"//tensorflow/lite:minimal_logging",
# go/keep-sorted end
]
config_setting(
name = "tflite_gpu_binary_release",
values = {"copt": "-DTFLITE_GPU_BINARY_RELEASE"},
)
config_setting(
name = "tflite_gpu_extra_gles_deps",
constraint_values = [
"@platforms//cpu:x86_64",
"@platforms//os:linux",
],
values = {
"copt": "-DTFLITE_GPU_EXTRA_GLES_DEPS",
},
)
cc_library(
name = "gl_delegate",
srcs = ["gl_delegate.cc"],
hdrs = ["gl_delegate.h"],
linkopts = gpu_delegate_linkopts(),
deps = [
"//tensorflow/lite:kernel_api",
"//tensorflow/lite:minimal_logging",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/delegates/gpu/common:convert",
"//tensorflow/lite/delegates/gpu/common:gpu_info",
"//tensorflow/lite/delegates/gpu/common:model",
"//tensorflow/lite/delegates/gpu/common:model_builder",
"//tensorflow/lite/delegates/gpu/common:model_transformer",
"//tensorflow/lite/delegates/gpu/common:shape",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common:tensor",
"//tensorflow/lite/delegates/gpu/common/transformations:model_transformations",
"//tensorflow/lite/delegates/gpu/gl:api",
"//tensorflow/lite/delegates/gpu/gl:command_queue",
"//tensorflow/lite/delegates/gpu/gl:compiler",
"//tensorflow/lite/delegates/gpu/gl:compiler_options",
"//tensorflow/lite/delegates/gpu/gl:egl_environment",
"//tensorflow/lite/delegates/gpu/gl:gl_buffer",
"//tensorflow/lite/delegates/gpu/gl:gl_call",
"//tensorflow/lite/delegates/gpu/gl:object",
"//tensorflow/lite/delegates/gpu/gl:object_manager",
"//tensorflow/lite/delegates/gpu/gl:request_gpu_info",
"//tensorflow/lite/delegates/gpu/gl:runtime_options",
"//tensorflow/lite/delegates/gpu/gl/converters:bhwc_to_phwc4",
"//tensorflow/lite/delegates/gpu/gl/converters:phwc4_to_bhwc",
"//tensorflow/lite/delegates/gpu/gl/kernels:registry",
"//tensorflow/lite/delegates/gpu/gl/workgroups:best_effort_calculator",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/types:span",
] + select({
"//conditions:default": [
"//tensorflow/lite/delegates/gpu/gl:common_cc_fbs",
"//tensorflow/lite/delegates/gpu/gl:metadata_cc_fbs",
"//tensorflow/lite/delegates/gpu/gl:workgroups_cc_fbs",
"//tensorflow/lite/schema:schema_fbs",
"@flatbuffers",
],
":tflite_gpu_binary_release": [],
}) + tflite_extra_gles_deps(),
)
objc_library(
name = "metal_delegate",
srcs = ["metal_delegate.mm"],
hdrs = ["metal_delegate.h"],
copts = CXX17_BAZEL_ONLY_COPTS,
module_name = "TensorFlowLiteCMetal",
sdk_frameworks = ["Metal"],
deps = [
"//tensorflow/lite:kernel_api",
"//tensorflow/lite:minimal_logging",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/delegates/gpu/common:convert",
"//tensorflow/lite/delegates/gpu/common:gpu_info",
"//tensorflow/lite/delegates/gpu/common:model",
"//tensorflow/lite/delegates/gpu/common:model_builder",
"//tensorflow/lite/delegates/gpu/common:model_transformer",
"//tensorflow/lite/delegates/gpu/common:precision",
"//tensorflow/lite/delegates/gpu/common:quantization_util",
"//tensorflow/lite/delegates/gpu/common:shape",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common:types",
"//tensorflow/lite/delegates/gpu/metal:buffer_convert",
"//tensorflow/lite/delegates/gpu/metal:common",
"//tensorflow/lite/delegates/gpu/metal:inference_context",
"//tensorflow/lite/delegates/gpu/metal:metal_spatial_tensor",
"//tensorflow/lite/kernels:kernel_util",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/types:span",
],
)
objc_library(
name = "metal_delegate_internal",
hdrs = ["metal_delegate_internal.h"],
copts = CXX17_BAZEL_ONLY_COPTS,
sdk_frameworks = ["Metal"],
deps = ["//tensorflow/lite/delegates/gpu:metal_delegate"],
)
# build -c opt --config android_arm64 --copt -Os --copt -DTFLITE_GPU_BINARY_RELEASE --linkopt -s --strip always :libtensorflowlite_gpu_gl.so
cc_binary(
name = "libtensorflowlite_gpu_gl.so",
linkopts = [
"-Wl,-soname=libtensorflowlite_gpu_gl.so",
] + gpu_delegate_linkopts() + select({
"//tensorflow:windows": [],
"//conditions:default": [
"-fvisibility=hidden",
],
}) + tflite_pagesize_linkopts(),
linkshared = 1,
linkstatic = 1,
tags = [
"nobuilder",
"notap",
],
deps = [":gl_delegate"],
)
# build -c opt --config android_arm64 --copt -Os --copt -DTFLITE_GPU_BINARY_RELEASE --linkopt -s --strip always :libtensorflowlite_gpu_delegate.so
cc_binary(
name = "libtensorflowlite_gpu_delegate.so",
linkopts = [
"-Wl,-soname=libtensorflowlite_gpu_delegate.so",
] + gpu_delegate_linkopts() + select({
"//tensorflow:windows": [],
"//conditions:default": [
"-fvisibility=hidden",
],
}),
linkshared = 1,
linkstatic = 1,
tags = [
"nobuilder",
"notap",
],
deps = [":delegate"],
)
# bazel build -c opt --cpu ios_arm64 --copt -Os --copt -DTFLITE_GPU_BINARY_RELEASE --copt -fvisibility=hidden --linkopt -s --strip always --cxxopt=-std=c++14 :libtensorflowlite_gpu_metal --apple_platform_type=ios
ios_static_framework(
name = "tensorflow_lite_gpu_framework",
hdrs = [
"metal_delegate.h",
"metal_delegate_internal.h",
],
minimum_os_version = "12.0",
deps = [":metal_delegate"],
)
# Note: Support for MacOS is best-effort at the moment.
# bazel build -c opt --copt -Os --copt -DTFLITE_GPU_BINARY_RELEASE --copt -fvisibility=hidden --linkopt -s --strip always --cxxopt=-std=c++14 :tensorflow_lite_gpu_dylib --apple_platform_type=macos
macos_dylib(
name = "tensorflow_lite_gpu_dylib",
linkopts = [
"-all_load",
"-dead_strip",
],
minimum_os_version = "12.0",
tags = [
"manual",
"nobuilder",
"notap",
],
deps = [
":metal_delegate",
":metal_delegate_internal",
],
)
cc_library(
name = "api",
srcs = ["api.cc"],
hdrs = ["api.h"],
deps = [
"//tensorflow/lite/delegates/gpu/common:data_type",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common:util",
"//tensorflow/lite/delegates/gpu/gl:portable",
"@com_google_absl//absl/types:span",
"@com_google_absl//absl/types:variant",
"@opencl_headers",
"@vulkan_headers//:vulkan_headers_no_prototypes",
],
)
cc_library(
name = "spi",
hdrs = ["spi.h"],
deps = [
":api",
"//tensorflow/lite/delegates/gpu/common:access_type",
"//tensorflow/lite/delegates/gpu/common:status",
],
)
# Currently the GPU delegate needs to be built on Android (due to EGL dependency),
# or built with -DCL_DELEGATE_NO_GL (disabling OpenGL backend fallback), or both.
selects.config_setting_group(
name = "supports_gpu_delegate",
match_any = [
"//tensorflow:android",
"//tensorflow/lite/delegates/gpu/cl:opencl_delegate_no_gl",
],
)
cc_library(
name = "delegate_options",
srcs = ["delegate_options.cc"],
hdrs = ["delegate_options.h"],
deps = ["//tensorflow/lite/core/c:common"],
)
# copybara:uncomment_begin(google-only)
# cc_library(
# name = "delegate_no_gl",
# srcs = [
# # copybara:comment_begin(oss-only)
# "android_version.cc",
# # copybara:comment_end
# "delegate.cc",
# ],
# hdrs = ["delegate.h"],
# defines = ["CL_DELEGATE_NO_GL"],
# linkopts = gpu_delegate_linkopts(),
# deps = _DELEGATE_NO_GL_DEPS + [
# "//tensorflow/lite/delegates/gpu/cl:api_no_gl",
# "//tensorflow/lite/delegates/gpu/gl:api2",
# ],
# )
# copybara:uncomment_end
cc_library(
name = "delegate",
srcs = [
# copybara:comment_begin(oss-only)
"android_version.cc",
# copybara:comment_end
"delegate.cc",
],
hdrs = ["delegate.h"],
linkopts = gpu_delegate_linkopts(),
deps = select({
"//tensorflow/lite/delegates/gpu/cl:opencl_delegate_no_gl": [],
"//conditions:default": [
"//tensorflow/lite/delegates/gpu/gl:api2",
],
}) + _DELEGATE_NO_GL_DEPS + ["//tensorflow/lite/delegates/gpu/cl:api"],
)
cc_library(
name = "tflite_profile",
srcs = ["tflite_profile.cc"],
hdrs = ["tflite_profile.h"],
deps = [
"//tensorflow/lite/core/api",
"//tensorflow/lite/delegates/gpu/common/task:profiling_info",
"@com_google_absl//absl/time",
],
)
cc_library(
name = "android_hardware_buffer",
srcs = ["android_hardware_buffer.cc"],
hdrs = ["android_hardware_buffer.h"],
)
cc_test(
name = "android_hardware_buffer_test",
srcs = ["android_hardware_buffer_test.cc"],
deps = [
":android_hardware_buffer",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "async_buffers",
srcs = ["async_buffers.cc"],
hdrs = ["async_buffers.h"],
deps = [
":android_hardware_buffer",
":api",
"//tensorflow/lite/delegates/gpu/common:data_type",
"//tensorflow/lite/delegates/gpu/gl:gl_errors",
"@com_google_absl//absl/status",
],
)
cc_test(
name = "async_buffers_test",
srcs = ["async_buffers_test.cc"],
tags = tf_gpu_tests_tags() + [
"local",
"nobuilder",
"notap",
"tflite_not_portable_ios",
],
deps = [
":android_hardware_buffer",
":api",
":async_buffers",
":delegate",
"//tensorflow/lite/delegates/gpu/common:data_type",
"//tensorflow/lite/delegates/gpu/gl:egl_environment",
"@com_google_googletest//:gtest_main",
],
)
tflite_portable_test_suite()
+174
View File
@@ -0,0 +1,174 @@
# TFLite on GPU
TensorFlow Lite (TFLite) supports several hardware accelerators. This document
describes how to use the GPU backend using the TFLite delegate APIs on Android
and iOS.
GPUs are designed to have high throughput for massively parallelizable
workloads. Thus, they are well-suited for deep neural nets which consists of a
huge number of operators, each working on some input tensor(s) that can be
easily divided into smaller workloads and carried out in parallel, typically
resulting in lower latency. In the best scenario, inference on the GPU may now
run fast enough and now become suitable for real-time applications if it was not
before.
GPUs do their computation with 16-bit or 32-bit floating point numbers and do
not require quantization for optimal performance unlike the CPUs. If
quantization of your neural network was not an option due to lower accuracy
caused by lost precision, such concern can be discarded when running deep neural
net models on the GPU.
Another benefit that comes with GPU inference is its power efficiency. GPUs
carry out the computations in a very efficient and optimized way, so that they
consume less power and generate less heat than when the same task is run on the
CPUs.
TFLite on GPU supports the following ops in 16-bit and 32-bit float precision:
* `ADD v1`
* `AVERAGE_POOL_2D v1`
* `CONCATENATION v1`
* `CONV_2D v1`
* `DEPTHWISE_CONV_2D v1-2`
* `EXP v1`
* `FULLY_CONNECTED v1`
* `LOGISTIC v1`
* `LSTM v2 (Basic LSTM only)`
* `MAX_POOL_2D v1`
* `MAXIMUM v1`
* `MINIMUM v1`
* `MUL v1`
* `PAD v1`
* `PRELU v1`
* `RELU v1`
* `RELU6 v1`
* `RESHAPE v1`
* `RESIZE_BILINEAR v1-3`
* `SOFTMAX v1`
* `STRIDED_SLICE v1`
* `SUB v1`
* `TRANSPOSE_CONV v1`
## Basic Usage
**Note:** Following section describes the example usage for Android GPU delegate
with C++. For other languages and platforms, please see
[the documentation](https://www.tensorflow.org/lite/performance/gpu).
Using TFLite on GPU is as simple as getting the GPU delegate via
`TfLiteGpuDelegateV2Create()` and then passing it to
`InterpreterBuilder::AddDelegate()`:
```c++
////////
// Set up InterpreterBuilder.
auto model = FlatBufferModel::BuildFromFile(model_path);
ops::builtin::BuiltinOpResolver op_resolver;
InterpreterBuilder interpreter_builder(*model, op_resolver);
////////
// NEW: Prepare GPU delegate.
auto* delegate = TfLiteGpuDelegateV2Create(/*default options=*/nullptr);
interpreter_builder.AddDelegate(delegate);
////////
// Set up Interpreter.
std::unique_ptr<Interpreter> interpreter;
if (interpreter_builder(&interpreter) != kTfLiteOk) return;
////////
// IMPORTANT: AllocateTensors can be called only AFTER ModifyGraphWithDelegate
////////
// Run inference.
WriteToInputTensor(interpreter->typed_input_tensor<float>(0));
if (interpreter->Invoke() != kTfLiteOk) return;
ReadFromOutputTensor(interpreter->typed_output_tensor<float>(0));
////////
// Clean up.
TfLiteGpuDelegateV2Delete(delegate);
```
*IMPORTANT:* When calling `Interpreter::ModifyGraphWithDelegate()` or
`InterpreterBuilder::operator()` or
`Interpreter::Invoke()`, the caller must have a `EGLContext` in the current
thread and `Interpreter::Invoke()` must be called from the same `EGLContext`.
If such `EGLContext` does not exist, the delegate will internally create one,
but then the developer must ensure that `Interpreter::Invoke()` is always called
from the same thread `InterpreterBuilder::operator()` or
`Interpreter::ModifyGraphWithDelegate()` was called.
## Building and Runtime
TFLite GPU backend uses OpenGL ES 3.1 compute shaders or OpenCL.
```sh
bazel build --config android_arm64 //path/to/your:project
```
Metal shaders are used for iOS, which were introduced with iOS 8. Thus,
compilation flags should look like:
```sh
bazel build --config ios_fat //path/to/your:project
```
## Advanced Usage: Delegate Options
There are GPU options that can be set and passed on to
`TfLiteGpuDelegateV2Create()`. When option is set to `nullptr` as shown in the
Basic Usage, it translates to:
```c++
const TfLiteGpuDelegateOptionsV2 kDefaultOptions =
TfLiteGpuDelegateOptionsV2Default();
```
Similar for `TFLGpuDelegateCreate()`:
```c++
const TFLGpuDelegateOptions kDefaultOptions = {
.allow_precision_loss = false,
.wait_type = TFLGpuDelegateWaitTypePassive,
.enable_quantization = false,
};
```
While it is convenient to just supply `nullptr`, it is recommended to explicitly
set the options to avoid any unexpected artifacts in case default values are
changed.
*IMPORTANT:* Note that the default option may not be the fastest. For faster
execution, you may want to set `allow_precision_loss` to `true` so that the GPU
performs FP16 calculation internally, and set `wait_type` to
`TFLGpuDelegateWaitTypeAggressive` to avoid GPU sleep mode.
## Tips and Tricks
* Some operations that are trivial on CPU side may be high cost in GPU land.
One class of such operation is various forms of reshape operations (including
`BATCH_TO_SPACE`, `SPACE_TO_BATCH`, `SPACE_TO_DEPTH`, etc.). If those ops
are inserted into the network just for the network architect's logical
thinking, it is worth removing them for performance.
* On GPU, tensor data is sliced into 4-channels. Thus, a computation on a
tensor of shape `[B, H, W, 5]` will perform about the same on a tensor of
shape `[B, H, W, 8]`, but significantly worse than `[B, H, W, 4]`.
* In that sense, if the camera hardware supports image frames in RGBA, feeding
that 4-channel input is significantly faster as a memory copy (from 3-channel
RGB to 4-channel RGBX) can be avoided.
* For performance [best practices](https://www.tensorflow.org/lite/performance/best_practices), do not hesitate to re-train your classifier with
mobile-optimized network architecture. That is a significant part of
optimization for on-device inference.
## Publication
* [On-Device Neural Net Inference with Mobile GPUs](https://arxiv.org/abs/1907.01989)
* Juhyun Lee, Nikolay Chirkov, Ekaterina Ignasheva, Yury Pisarchyk, Mogan
Shieh, Fabio Riccardi, Raman Sarokin, Andrei Kulik, and Matthias
Grundmann
* CVPR Workshop
[Efficient Deep Learning for Computer Vision (ECV2019)](https://sites.google.com/corp/view/ecv2019)
@@ -0,0 +1,53 @@
/* Copyright 2023 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/android_hardware_buffer.h"
#include <dlfcn.h>
namespace tflite::gpu {
OptionalAndroidHardwareBuffer::OptionalAndroidHardwareBuffer() {
#ifdef __ANDROID__
dlopen_handle_ = dlopen("libnativewindow.so", RTLD_NOW);
if (dlopen_handle_ == nullptr) {
supported_ = false;
return;
}
allocate_ = reinterpret_cast<decltype(allocate_)>(
dlsym(dlopen_handle_, "AHardwareBuffer_allocate"));
acquire_ = reinterpret_cast<decltype(acquire_)>(
dlsym(dlopen_handle_, "AHardwareBuffer_acquire"));
release_ = reinterpret_cast<decltype(release_)>(
dlsym(dlopen_handle_, "AHardwareBuffer_release"));
describe_ = reinterpret_cast<decltype(describe_)>(
dlsym(dlopen_handle_, "AHardwareBuffer_describe"));
is_supported_ = reinterpret_cast<decltype(is_supported_)>(
dlsym(dlopen_handle_, "AHardwareBuffer_isSupported"));
supported_ =
(allocate_ != nullptr && acquire_ != nullptr && release_ != nullptr &&
describe_ != nullptr && is_supported_ != nullptr);
#else
dlopen_handle_ = nullptr;
allocate_ = nullptr;
acquire_ = nullptr;
release_ = nullptr;
describe_ = nullptr;
is_supported_ = nullptr;
supported_ = false;
#endif
}
} // namespace tflite::gpu
@@ -0,0 +1,130 @@
/* Copyright 2023 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_ANDROID_HARDWARE_BUFFER_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_ANDROID_HARDWARE_BUFFER_H_
#include <stdint.h>
#ifdef __ANDROID__
#include <android/hardware_buffer.h>
#else
extern "C" {
typedef struct AHardwareBuffer AHardwareBuffer;
// struct is a copy of the Android NDK AHardwareBuffer_Desc struct in the link
// below
// https://developer.android.com/ndk/reference/struct/a-hardware-buffer-desc
typedef struct AHardwareBuffer_Desc AHardwareBuffer_Desc;
struct AHardwareBuffer_Desc {
uint32_t width;
uint32_t height;
uint32_t layers;
uint32_t format;
uint64_t usage;
uint32_t stride;
uint32_t rfu0;
uint64_t rfu1;
};
} // extern "C"
#endif // __ANDROID__
namespace tflite::gpu {
// This header file and singleton class encapsulates the following Android NDK
// features
// - header <android/hardware_buffer.h>
// - opaque struct type AHardwareBuffer
// - struct type AHardwareBuffer_Desc
// - function AHardwareBuffer_isSupported
// - function AHardwareBuffer_allocate
// - function AHardwareBuffer_acquire
// - function AHardwareBuffer_release
// - function AHardwareBuffer_describe
// - library libnativewindow.so (for the above features)
//
// For documentation on these features, see
// <https://developer.android.com/ndk/reference/group/a-hardware-buffer>:
//
// Unlike using the native NDK functionality directly, this class only has a
// run-time dependency on API level 26, not a build-time dependency. So it can
// be used even when building with NDK min SDK level < 26, as long as you are
// very careful to check that Supported() returns true before calling any other
// methods.
class OptionalAndroidHardwareBuffer {
public:
static OptionalAndroidHardwareBuffer& Instance() {
static OptionalAndroidHardwareBuffer instance;
return instance;
}
// Returns true if the functionality in this class is supported.
bool Supported() { return supported_; }
// Like AHardwareBuffer_isSupported.
// Caller must check that Supported() returns true before calling this
// function.
int IsSupported(const AHardwareBuffer_Desc* description) {
return is_supported_(description);
}
// Like AHardwareBuffer_allocate.
// Caller must check that Supported() returns true before calling this
// function.
int Allocate(const AHardwareBuffer_Desc* description,
AHardwareBuffer** buffer) {
return allocate_(description, buffer);
}
// Like AHardwareBuffer_acquire.
// Caller must check that Supported() returns true before calling this
// function.
void Acquire(AHardwareBuffer* buffer) { return acquire_(buffer); }
// Like AHardwareBuffer_release.
// Caller must check that Supported() returns true before calling this
// function.
void Release(AHardwareBuffer* buffer) { return release_(buffer); }
// Like AHardwareBuffer_describe.
// Caller must check that Supported() returns true before calling this
// function.
void Describe(AHardwareBuffer* buffer, AHardwareBuffer_Desc* desc) {
return describe_(buffer, desc);
}
private:
void* dlopen_handle_;
int (*is_supported_)(const AHardwareBuffer_Desc* desc);
int (*allocate_)(const AHardwareBuffer_Desc* desc, AHardwareBuffer** buffer);
void (*acquire_)(AHardwareBuffer* buffer);
void (*release_)(AHardwareBuffer* buffer);
void (*describe_)(AHardwareBuffer* buffer, AHardwareBuffer_Desc* desc);
bool supported_;
OptionalAndroidHardwareBuffer();
OptionalAndroidHardwareBuffer(const OptionalAndroidHardwareBuffer&) = delete;
// Note that we deliberately do not call dlclose() in the destructor; doing
// so would complicate the code and would unnecessarily introduce additional
// failure scenarios. The object is a singleton and so is only destroyed when
// the process is about to exit, and the OS will automatically reclaim the
// resources on process exit anyway, so calling dlclose would only slow down
// process exit.
~OptionalAndroidHardwareBuffer() = default;
};
} // namespace tflite::gpu
#endif // TENSORFLOW_LITE_DELEGATES_GPU_ANDROID_HARDWARE_BUFFER_H_
@@ -0,0 +1,75 @@
/* Copyright 2023 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/android_hardware_buffer.h"
#include <gtest/gtest.h>
using tflite::gpu::OptionalAndroidHardwareBuffer;
auto Instance = OptionalAndroidHardwareBuffer::Instance;
namespace {
#ifndef __ANDROID__
TEST(OptionalAndroidHardwareBufferTest, NotSupportedOnNonAndroid) {
EXPECT_EQ(Instance().Supported(), false);
}
#else // defined(__ANDROID__)
TEST(OptionalAndroidHardwareBufferTest, SupportedOnAndroid) {
EXPECT_EQ(Instance().Supported(), true);
}
TEST(OptionalAndroidHardwareBufferTest, CanAllocateAndReleaseOnAndroid) {
EXPECT_EQ(Instance().Supported(), true);
AHardwareBuffer* buffer;
AHardwareBuffer_Desc description{};
description.width = 1600;
description.height = 1;
description.layers = 1;
description.rfu0 = 0;
description.rfu1 = 0;
description.stride = 1;
description.format = AHARDWAREBUFFER_FORMAT_BLOB;
description.usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN;
EXPECT_TRUE(Instance().IsSupported(&description));
EXPECT_EQ(Instance().Allocate(&description, &buffer), 0);
Instance().Release(buffer);
}
TEST(OptionalAndroidHardwareBufferTest, CanAcquireAndReleaseOnAndroid) {
EXPECT_EQ(Instance().Supported(), true);
AHardwareBuffer* buffer;
AHardwareBuffer_Desc description{};
description.width = 1600;
description.height = 1;
description.layers = 1;
description.rfu0 = 0;
description.rfu1 = 0;
description.stride = 1;
description.format = AHARDWAREBUFFER_FORMAT_BLOB;
description.usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN;
EXPECT_TRUE(Instance().IsSupported(&description));
EXPECT_EQ(Instance().Allocate(&description, &buffer), 0);
Instance().Acquire(buffer);
Instance().Release(buffer); // To match Acquire
Instance().Release(buffer); // To match Allocate
}
#endif // defined(__ANDROID__)
} // namespace
@@ -0,0 +1,58 @@
/* Copyright 2023 The TensorFlow Authors. All Rights Reserved.
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.
==============================================================================*/
#if defined(__ANDROID__)
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <sys/system_properties.h>
extern "C" {
static int SdkVersion;
static int IsPreRelease;
static void readSystemProperties(void) {
char buf[PROP_VALUE_MAX];
if (__system_property_get("ro.build.version.sdk", buf) == 0) {
// When the system property doesn't exist, defaults to future API level.
SdkVersion = __ANDROID_API_FUTURE__;
} else {
SdkVersion = atoi(buf); // NOLINT(runtime/deprecated_fn)
}
if (__system_property_get("ro.build.version.codename", buf) == 0) {
IsPreRelease = 1;
} else {
IsPreRelease = strcmp(buf, "REL") != 0;
}
return;
}
int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor) {
(int32_t) Minor;
(int32_t) Subminor;
static pthread_once_t once = PTHREAD_ONCE_INIT;
pthread_once(&once, readSystemProperties);
return SdkVersion >= Major ||
(IsPreRelease && Major == __ANDROID_API_FUTURE__);
}
} // extern "C"
#endif // defined(__ANDROID__)
+213
View File
@@ -0,0 +1,213 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/api.h"
#include <cstdint>
#include <limits>
#include <variant>
#include "tensorflow/lite/delegates/gpu/common/util.h"
namespace tflite {
namespace gpu {
namespace {
struct ObjectTypeGetter {
ObjectType operator()(std::monostate) const { return ObjectType::UNKNOWN; }
ObjectType operator()(OpenGlBuffer) const { return ObjectType::OPENGL_SSBO; }
ObjectType operator()(OpenGlTexture) const {
return ObjectType::OPENGL_TEXTURE;
}
ObjectType operator()(OpenClBuffer) const {
return ObjectType::OPENCL_BUFFER;
}
ObjectType operator()(OpenClTexture) const {
return ObjectType::OPENCL_TEXTURE;
}
ObjectType operator()(VulkanBuffer) const {
return ObjectType::VULKAN_BUFFER;
}
ObjectType operator()(VulkanTexture) const {
return ObjectType::VULKAN_TEXTURE;
}
ObjectType operator()(CpuMemory) const { return ObjectType::CPU_MEMORY; }
};
struct ObjectValidityChecker {
bool operator()(std::monostate) const { return false; }
bool operator()(OpenGlBuffer obj) const { return obj.id != GL_INVALID_INDEX; }
bool operator()(OpenGlTexture obj) const {
return obj.id != GL_INVALID_INDEX && obj.format != GL_INVALID_ENUM;
}
bool operator()(OpenClBuffer obj) const { return obj.memobj; }
bool operator()(OpenClTexture obj) const { return obj.memobj; }
bool operator()(VulkanBuffer obj) const { return obj.memory; }
bool operator()(VulkanTexture obj) const { return obj.memory; }
bool operator()(CpuMemory obj) const {
return obj.data != nullptr && obj.size_bytes > 0 &&
(data_type == DataType::UNKNOWN || data_type == DataType::BOOL ||
obj.size_bytes % SizeOf(data_type) == 0);
}
DataType data_type;
};
} // namespace
bool IsValid(const ObjectDef& def) {
return def.data_type != DataType::UNKNOWN &&
def.data_layout != DataLayout::UNKNOWN &&
def.object_type != ObjectType::UNKNOWN;
}
ObjectType GetType(const TensorObject& object) {
return std::visit(ObjectTypeGetter{}, object);
}
bool IsValid(const TensorObjectDef& def) {
return IsValid(def.object_def) &&
NumElements(def) <= std::numeric_limits<int32_t>::max();
}
bool IsValid(const TensorObjectDef& def, const TensorObject& object) {
return IsValid(def) && GetType(object) == def.object_def.object_type &&
std::visit(ObjectValidityChecker{def.object_def.data_type}, object);
}
bool IsObjectPresent(ObjectType type, const TensorObject& obj) {
switch (type) {
case ObjectType::CPU_MEMORY:
return std::holds_alternative<CpuMemory>(obj);
case ObjectType::OPENGL_SSBO:
return std::holds_alternative<OpenGlBuffer>(obj);
case ObjectType::OPENGL_TEXTURE:
return std::holds_alternative<OpenGlTexture>(obj);
case ObjectType::OPENCL_BUFFER:
return std::holds_alternative<OpenClBuffer>(obj);
case ObjectType::OPENCL_TEXTURE:
return std::holds_alternative<OpenClTexture>(obj);
case ObjectType::VULKAN_BUFFER:
return std::holds_alternative<VulkanBuffer>(obj);
case ObjectType::VULKAN_TEXTURE:
return std::holds_alternative<VulkanTexture>(obj);
case ObjectType::UNKNOWN:
return false;
}
}
bool IsObjectInitialized(const TensorObject& obj) {
return GetType(obj) != ObjectType::UNKNOWN;
}
int64_t NumElements(const TensorObjectDef& def) {
const auto& d = def.dimensions;
switch (def.object_def.data_layout) {
case DataLayout::BHWC:
return d.product();
case DataLayout::HWDC4:
case DataLayout::HDWC4:
case DataLayout::DHWC4:
return static_cast<int64_t>(d.b) * d.h * d.w * AlignByN(d.c, 4);
case DataLayout::UNKNOWN:
return 0;
}
return 0;
}
int GetPosition(const InferenceOptions& options, InferencePriority p) {
if (options.priority1 == p) return 1;
if (options.priority2 == p) return 2;
if (options.priority3 == p) return 3;
return 4; // least important
}
PriorityImportance GetRelativeImportance(const InferenceOptions& options,
InferencePriority p1,
InferencePriority p2) {
int p1_position = GetPosition(options, p1);
int p2_position = GetPosition(options, p2);
if (p1_position == p2_position) return PriorityImportance::UNKNOWN;
return p1_position < p2_position ? PriorityImportance::HIGHER
: PriorityImportance::LOWER;
}
bool IsValid(const InferenceOptions& options) {
if (options.usage == InferenceUsage::UNKNOWN) {
return false;
}
if (options.priority1 == InferencePriority::UNKNOWN ||
options.priority2 == InferencePriority::UNKNOWN ||
options.priority3 == InferencePriority::UNKNOWN) {
return false;
}
if (options.priority1 == InferencePriority::AUTO) {
return false;
}
if (options.priority2 == InferencePriority::AUTO &&
options.priority3 != InferencePriority::AUTO) {
return false;
}
if (options.priority1 == options.priority2 ||
options.priority1 == options.priority3) {
return false;
}
if (options.priority2 == options.priority3 &&
options.priority2 != InferencePriority::AUTO) {
return false;
}
return true;
}
// Implementation note: this resolution logic is shared between GL and CL
// backends, but they might have own logic. Thus, the function is defined
// here just for code re-use purposes.
void ResolveAutoPriority(InferenceOptions* options) {
// priority1 can not be AUTO as it would make options invalid.
if (options->priority2 == InferencePriority::AUTO) {
switch (options->priority1) {
case InferencePriority::MIN_LATENCY:
options->priority2 = InferencePriority::MIN_MEMORY_USAGE;
options->priority3 = InferencePriority::MAX_PRECISION;
return;
case InferencePriority::MIN_MEMORY_USAGE:
options->priority2 = InferencePriority::MAX_PRECISION;
options->priority3 = InferencePriority::MIN_LATENCY;
return;
case InferencePriority::MAX_PRECISION:
options->priority2 = InferencePriority::MIN_LATENCY;
options->priority3 = InferencePriority::MIN_MEMORY_USAGE;
return;
case InferencePriority::UNKNOWN:
case InferencePriority::AUTO:
// Invalid and unreachable option.
return;
}
}
if (options->priority3 == InferencePriority::AUTO) {
// Simply add missing priority
if (GetPosition(*options, InferencePriority::MIN_LATENCY) == 4) {
options->priority3 = InferencePriority::MIN_LATENCY;
} else if (GetPosition(*options, InferencePriority::MAX_PRECISION) == 4) {
options->priority3 = InferencePriority::MAX_PRECISION;
} else if (GetPosition(*options, InferencePriority::MIN_MEMORY_USAGE) ==
4) {
options->priority3 = InferencePriority::MIN_MEMORY_USAGE;
}
}
}
} // namespace gpu
} // namespace tflite
+416
View File
@@ -0,0 +1,416 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_API_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_API_H_
// Usage example:
//
// // Builder is created from a model using GPU-specific parameters.
// std::unique_ptr<InferenceBuilder> builder = ...;
//
// // input data is coming from a texture
// // output data goes to CPU
// builder->SetInputObjectDef(0, {DataType::FLOAT16, DataLayout::PHWC4,
// ObjectType::OPENGL_TEXTURE, true});
// builder->SetOutputObjectDef(0, {DataType::FLOAT32, DataLayout::BHWC,
// ObjectType::CPU_MEMORY, false});
// std::unique_ptr<InferenceRunner> runner;
// RETURN_IF_ERROR(builder->Build(&runner)); // may take significant time.
// RETURN_IF_ERROR(
// runner->SetInputObject(0, OpenGlTexture{texture_ud, texture_format}));
// RETURN_IF_ERROR(runner->Run());
#include <cstdint>
#include <memory>
#include <variant>
#include <vector>
#include "absl/types/span.h"
#include "absl/types/variant.h"
#include <CL/cl.h>
#include "tensorflow/lite/delegates/gpu/common/data_type.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/util.h"
// The `absl::Status` conflicts with the macro definition in the X11/Xlib.h,
// undefine the VK_USE_PLATFORM_XLIB_KHR to exclude the header file.
#undef VK_USE_PLATFORM_XLIB_KHR
#include "vulkan/vulkan.h" // from @vulkan_headers
#define GL_NO_PROTOTYPES
#define EGL_NO_PROTOTYPES
#include "tensorflow/lite/delegates/gpu/gl/portable_gl31.h"
#undef GL_NO_PROTOTYPES
#undef EGL_NO_PROTOTYPES
namespace tflite {
namespace gpu {
// Common abbreviations:
// B - batch
// H - height
// W - width
// C - channels
// D - depth := DivideRoundUp(C, 4)
// C4 - is the constant = 4.
enum class DataLayout {
UNKNOWN,
BHWC,
DHWC4,
HWDC4,
HDWC4,
};
enum class ObjectType {
UNKNOWN,
OPENGL_SSBO,
OPENGL_TEXTURE,
CPU_MEMORY,
OPENCL_TEXTURE,
OPENCL_BUFFER,
VULKAN_BUFFER,
VULKAN_TEXTURE
};
struct OpenGlBuffer {
OpenGlBuffer() = default;
explicit OpenGlBuffer(GLuint new_id) : id(new_id) {}
GLuint id = GL_INVALID_INDEX;
};
struct OpenGlTexture {
OpenGlTexture() = default;
OpenGlTexture(GLuint new_id, GLenum new_format)
: id(new_id), format(new_format) {}
GLuint id = GL_INVALID_INDEX;
GLenum format = GL_INVALID_ENUM;
};
struct OpenClBuffer {
OpenClBuffer() = default;
explicit OpenClBuffer(cl_mem new_memobj) : memobj(new_memobj) {}
cl_mem memobj = nullptr;
};
struct OpenClTexture {
OpenClTexture() = default;
explicit OpenClTexture(cl_mem new_memobj) : memobj(new_memobj) {}
cl_mem memobj = nullptr;
// TODO(akulik): should it specify texture format?
};
struct VulkanBuffer {
VulkanBuffer() = default;
explicit VulkanBuffer(VkBuffer buffer_, VkDeviceSize size_,
VkDeviceMemory memory_, VkDeviceSize offset_)
: buffer(buffer_), size(size_), memory(memory_), offset(offset_) {}
VkBuffer buffer;
VkDeviceSize size;
VkDeviceMemory memory;
VkDeviceSize offset;
};
struct VulkanTexture {
VulkanTexture() = default;
explicit VulkanTexture(VkDeviceMemory new_memory) : memory(new_memory) {}
VkImage image;
VkImageView image_view;
VkFormat format;
VkExtent3D extent;
VkDeviceMemory memory;
VkDeviceSize offset;
};
struct VulkanMemory {
VulkanMemory() = default;
explicit VulkanMemory(VkDeviceMemory new_memory) : memory(new_memory) {}
VkDeviceMemory memory;
VkDeviceSize size;
VkDeviceSize offset;
};
struct CpuMemory {
CpuMemory() = default;
CpuMemory(void* new_data, size_t new_size_bytes)
: data(new_data), size_bytes(new_size_bytes) {}
void* data = nullptr;
size_t size_bytes = 0;
};
template <typename T>
inline CpuMemory MakeCpuMemory(absl::Span<T> t) {
CpuMemory m;
m.data = t.data();
m.size_bytes = t.size() * sizeof(T);
return m;
}
template <typename T>
inline CpuMemory MakeReadableCpuMemory(absl::Span<const T> t) {
CpuMemory m;
m.data = const_cast<T*>(t.data());
m.size_bytes = t.size() * sizeof(T);
return m;
}
// Defines object representation.
struct ObjectDef {
DataType data_type = DataType::UNKNOWN;
DataLayout data_layout = DataLayout::UNKNOWN;
ObjectType object_type = ObjectType::UNKNOWN;
// If true, then object is managed externally and needs to be provided to
// InferenceRunner by a user before running inference.
//
// User-provided objects will not be re-used internally for any purpose to
// lower overall memory usage.
bool user_provided = false;
bool operator==(const ObjectDef& other) const {
return data_type == other.data_type && data_layout == other.data_layout &&
object_type == other.object_type &&
user_provided == other.user_provided;
}
};
bool IsValid(const ObjectDef& def);
struct Dimensions {
Dimensions() : b(1), h(1), w(1), c(1) {}
Dimensions(int32_t batch, int32_t height, int32_t width, int32_t channels)
: b(batch), h(height), w(width), c(channels) {}
int32_t d() const { return DivideRoundUp(c, 4); }
int64_t product() const { return static_cast<int64_t>(b) * h * w * c; }
bool operator==(const Dimensions& other) const {
return b == other.b && h == other.h && w == other.w && c == other.c;
}
int32_t b;
int32_t h;
int32_t w;
int32_t c;
};
// Connects tensor shape with corresponding object definition.
struct TensorObjectDef {
// Dimensions semantic is defined by corresponding DataLayout.
Dimensions dimensions;
ObjectDef object_def;
bool operator==(const TensorObjectDef& other) const {
return dimensions == other.dimensions && object_def == other.object_def;
}
};
// @return true if tensor object def is defined.
bool IsValid(const TensorObjectDef& def);
// @return the number of elements in a tensor object.
int64_t NumElements(const TensorObjectDef& def);
using TensorObject =
std::variant<std::monostate, OpenGlBuffer, OpenGlTexture, CpuMemory,
OpenClBuffer, OpenClTexture, VulkanBuffer, VulkanTexture>;
// @return true if object is set and corresponding values are defined.
bool IsValid(const TensorObjectDef& def, const TensorObject& object);
ObjectType GetType(const TensorObject& object);
// @return true if corresponding object is set for the given type
bool IsObjectPresent(ObjectType type, const TensorObject& obj);
// @return true if corresponding object has already been initialized and
// assigned with a specific ObjectType.
bool IsObjectInitialized(const TensorObject& obj);
class InferenceRunner;
// Allows to inspect and change input and output definitions before a graph is
// prepared for the inference.
class InferenceBuilder {
public:
virtual ~InferenceBuilder() {}
// Returns inference graph inputs and outputs definitions.
virtual std::vector<TensorObjectDef> inputs() const = 0;
virtual std::vector<TensorObjectDef> outputs() const = 0;
// Sets new shape for the input if underlying implementation and graph
// structure allows dynamic tensors.
virtual absl::Status SetInputShape(int index,
const Dimensions& dimensions) = 0;
// Updates object definitions for the given index. Implementation may allow
// to use different layouts and/or data type conversions between objects
// defined in a graph and given objects, for example:
// input '0' is DataType::FLOAT32, DataLayout::BHWC.
// A user, however, has an input in DataType::FLOAT16, DataLayout::PHWC4.
// An implementation may allow this transformation to happen automatically
// under the hood.
virtual absl::Status SetInputObjectDef(int index, ObjectDef def) = 0;
virtual absl::Status SetOutputObjectDef(int index, ObjectDef def) = 0;
virtual absl::Status SetAllInputObjectDefsTo(ObjectDef def) {
auto input_defs = inputs();
for (int i = 0; i < input_defs.size(); ++i) {
RETURN_IF_ERROR(SetInputObjectDef(i, def));
}
return absl::OkStatus();
}
virtual absl::Status SetAllOutputObjectDefsTo(ObjectDef def) {
auto output_defs = outputs();
for (int i = 0; i < output_defs.size(); ++i) {
RETURN_IF_ERROR(SetOutputObjectDef(i, def));
}
return absl::OkStatus();
}
// Creates new instance of the inference runner. InferenceBuilder stays valid
// and could be used to create another inference runner if needed.
//
// This method may take significant time to prepare new inference runner. For
// example, it may require to compile OpenGL shaders.
virtual absl::Status Build(std::unique_ptr<InferenceRunner>* runner) = 0;
};
// Runs prepared inference. Every object marked as external needs to be set
// prior calling Run method.
class InferenceRunner {
public:
virtual ~InferenceRunner() {}
// Returns inference graph inputs and outputs definitions.
virtual std::vector<TensorObjectDef> inputs() const = 0;
virtual std::vector<TensorObjectDef> outputs() const = 0;
// Getters provide access to underlying objects for the given index.
// Setters allow to set or change external object for the given index. Note,
// object need to match object definition set before in InferenceBuilder.
virtual absl::Status GetInputObject(int index, TensorObject* object) = 0;
virtual absl::Status GetOutputObject(int index, TensorObject* object) = 0;
virtual absl::Status SetInputObject(int index, TensorObject object) = 0;
virtual absl::Status SetOutputObject(int index, TensorObject object) = 0;
virtual absl::Status Run() = 0;
};
// Encapsulated compilation/runtime tradeoffs.
enum class InferenceUsage {
UNKNOWN,
// InferenceRunner will be used only once. Therefore, it is important to
// minimize bootstrap time as well.
FAST_SINGLE_ANSWER,
// Prefer maximizing the throughput. Same inference runner will be used
// repeatedly on different inputs.
SUSTAINED_SPEED,
// Balance init latency and throughput. This option will result in slightly
// higher init latency than FAST_SINGLE_ANSWER but should have inference
// latency closer to SUSTAINED_SPEED.
BALANCED,
};
// Defines aspects to control while instantiating a runner.
enum class InferencePriority {
UNKNOWN,
AUTO,
MIN_LATENCY,
MAX_PRECISION,
MIN_MEMORY_USAGE,
};
struct InferenceOptions {
InferenceUsage usage = InferenceUsage::SUSTAINED_SPEED;
// Ordered priorities provide better understanding of desired semantics,
// where priority(n) is more important than priority(n+1).
// AUTO priority is needed when a single priority is the most important
// factor. For example, priority1 = InferencePriority::MIN_LATENCY and leaving
// everything else to AUTO would result in configuration that achieves maximum
// performance.
//
// AUTO priority can only be used when higher priorities are fully specified.
// For example:
// VALID: priority1 = MIN_LATENCY, priority2 = AUTO, priority3 = AUTO
// VALID: priority1 = MIN_LATENCY, priority2 = MAX_PRECISION,
// priority3 = AUTO
// INVALID: priority1 = AUTO, priority2 = MIN_LATENCY, priority3 = AUTO
// INVALID: priority1 = MIN_LATENCY, priority2 = AUTO,
// priority3 = MAX_PRECISION
// Invalid priorities will result in error.
InferencePriority priority1 = InferencePriority::MAX_PRECISION;
InferencePriority priority2 = InferencePriority::AUTO;
InferencePriority priority3 = InferencePriority::AUTO;
#ifdef TFLITE_GPU_ENABLE_INVOKE_LOOP
// Number of times to invoke the inference in GPU delegate, to collect more
// accurate latency result. Default as 1, which is the original behavior.
int gpu_invoke_loop_times = 1;
#endif
};
// Returns a position number for the priority. If priority is missing,
// then it would return 'max num priorities + 1'.
int GetPosition(const InferenceOptions& options, InferencePriority p);
// Return true if options are valid.
bool IsValid(const InferenceOptions& options);
// Resolves AUTO priorities and specifies them explicitly.
// Note, no-one should assume that these mappings will not change.
// Technically this function is declared here for code re-use purposes and
// by no means it should be treated as canonical way to resolve AUTO.
void ResolveAutoPriority(InferenceOptions* options);
enum class PriorityImportance {
UNKNOWN,
HIGHER,
LOWER,
};
// If both p1 and p2 are not present in options, return UNKNOWN
// If p1 is present, but p2 is not, return HIGHER
// If p2 is present, but p1 is not, return LOWER
// If both are present, and p1 is more important, return HIGHER, otherwise,
// LOWER.
PriorityImportance GetRelativeImportance(const InferenceOptions& options,
InferencePriority p1,
InferencePriority p2);
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_API_H_
@@ -0,0 +1,105 @@
/* Copyright 2023 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/async_buffers.h"
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2ext.h>
#include <GLES3/gl31.h>
#include "absl/status/status.h"
#include "tensorflow/lite/delegates/gpu/android_hardware_buffer.h"
#include "tensorflow/lite/delegates/gpu/gl/gl_errors.h"
namespace {
PFNGLBUFFERSTORAGEEXTERNALEXTPROC glBufferStorageExternalEXT;
PFNEGLGETNATIVECLIENTBUFFERANDROIDPROC eglGetNativeClientBufferANDROID;
bool IsGlSupported() {
static const bool extensions_allowed = [] {
eglGetNativeClientBufferANDROID =
reinterpret_cast<PFNEGLGETNATIVECLIENTBUFFERANDROIDPROC>(
eglGetProcAddress("eglGetNativeClientBufferANDROID"));
glBufferStorageExternalEXT =
reinterpret_cast<PFNGLBUFFERSTORAGEEXTERNALEXTPROC>(
eglGetProcAddress("glBufferStorageExternalEXT"));
return eglGetNativeClientBufferANDROID && glBufferStorageExternalEXT;
}();
return extensions_allowed;
}
} // namespace
namespace tflite {
namespace gpu {
// Where the AHWB<->SSBO mapping occurs
absl::Status AsyncBuffer::MapAHardwareBufferToGlBuffer() {
if (!IsGlSupported()) {
return absl::UnknownError(
"No GL extension functions found to bind AHardwareBuffer and "
"OpenGL buffer");
}
EGLClientBuffer native_buffer = eglGetNativeClientBufferANDROID(ahwb_);
if (!native_buffer) {
return absl::UnknownError("Can't get native buffer");
}
// If an error is traced back to below fcn, check your ahwb usage flags.
// An example may be found in async_buffers_test.cc
glBufferStorageExternalEXT(GL_SHADER_STORAGE_BUFFER, 0, bytes_, native_buffer,
GL_MAP_READ_BIT | GL_MAP_WRITE_BIT |
GL_MAP_COHERENT_BIT_EXT |
GL_MAP_PERSISTENT_BIT_EXT);
return gl::GetOpenGlErrors();
}
// Allocate SSBO, call the AHWB<->SSBO mapping, and fail gracefully if needed.
absl::Status AsyncBuffer::AllocateOpenGlBuffer() {
if (opengl_buffer_ == GL_INVALID_INDEX) {
// Generate and bind SSBO
glGenBuffers(1, &opengl_buffer_);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, opengl_buffer_);
absl::Status status = MapAHardwareBufferToGlBuffer();
if (!status.ok()) {
// If we can't map to SSBO, clear AHWB & SSBO
if (ahwb_ != nullptr) {
if (OptionalAndroidHardwareBuffer::Instance().Supported()) {
OptionalAndroidHardwareBuffer::Instance().Release(ahwb_);
}
ahwb_ = nullptr;
}
glBufferData(GL_SHADER_STORAGE_BUFFER, bytes_, nullptr, GL_STREAM_COPY);
}
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
}
return absl::OkStatus();
}
// Public function which will map the AHWB (from class constructor) to a SSBO
// and return the associated the id by reference
absl::Status AsyncBuffer::GetOpenGlBuffer(GLuint& buffer_ref) {
if (!valid_) {
absl::Status status = AllocateOpenGlBuffer();
if (!status.ok()) {
return status;
}
}
valid_ = true;
buffer_ref = opengl_buffer_;
return absl::OkStatus();
}
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,58 @@
/* Copyright 2023 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_ASYNC_BUFFERS_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_ASYNC_BUFFERS_H_
#if defined(__ANDROID__)
#include <android/hardware_buffer.h>
#endif // __ANDROID__
#include <GLES3/gl31.h>
#include "absl/status/status.h"
#include "tensorflow/lite/delegates/gpu/api.h"
#include "tensorflow/lite/delegates/gpu/common/data_type.h"
extern "C" typedef struct AHardwareBuffer AHardwareBuffer;
namespace tflite {
namespace gpu {
class AsyncBuffer {
private:
int bytes_; // Number of bytes in the buffer
bool valid_ = false; // Have we mapped to SSBO already
GLuint opengl_buffer_ = GL_INVALID_INDEX; // SSBO buffer id
AHardwareBuffer* ahwb_ = nullptr;
// Where the AHWB<->SSBO mapping occurs
absl::Status MapAHardwareBufferToGlBuffer();
// Allocate SSBO, call the AHWB<->SSBO mapping; fail gracefully if needed.
absl::Status AllocateOpenGlBuffer();
public:
explicit AsyncBuffer(TensorObjectDef tensor_def, AHardwareBuffer* ahwb) {
bytes_ = NumElements(tensor_def) * SizeOf(tensor_def.object_def.data_type);
ahwb_ = ahwb;
}
// Map the AHWB (from class constructor) to an SSBO id
absl::Status GetOpenGlBuffer(GLuint& buffer_ref);
};
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_ASYNC_BUFFERS_H_
@@ -0,0 +1,74 @@
/* Copyright 2023 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/async_buffers.h"
#include <memory>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/android_hardware_buffer.h"
#include "tensorflow/lite/delegates/gpu/api.h"
#include "tensorflow/lite/delegates/gpu/common/data_type.h"
#include "tensorflow/lite/delegates/gpu/gl/egl_environment.h"
namespace tflite {
namespace gpu {
namespace {
TEST(AsyncBufferTest, DuplicateTest) {
if (__builtin_available(android 26, *)) {
auto Instance = OptionalAndroidHardwareBuffer::Instance;
// Create tie
TensorObjectDef* tie = new TensorObjectDef();
tie->object_def.data_type = DataType::FLOAT32;
tie->object_def.data_layout = DataLayout::BHWC;
tie->dimensions = Dimensions(2, 2, 2, 2);
// Create AHWB
AHardwareBuffer_Desc buffDesc = {};
buffDesc.width = 1000;
buffDesc.height = 1;
buffDesc.layers = 1;
buffDesc.format = AHARDWAREBUFFER_FORMAT_BLOB;
buffDesc.usage = AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER;
AHardwareBuffer* ahwb;
EXPECT_TRUE(Instance().IsSupported(&buffDesc));
EXPECT_EQ(Instance().Allocate(&buffDesc, &ahwb), 0);
// Init GL Env to properly use gl fcns
std::unique_ptr<gl::EglEnvironment> env;
EXPECT_OK(gl::EglEnvironment::NewEglEnvironment(&env));
AsyncBuffer async_buffer1 = AsyncBuffer(*tie, ahwb);
GLuint buffer1, buffer2;
EXPECT_OK(async_buffer1.GetOpenGlBuffer(buffer1));
EXPECT_GE(buffer1, 0);
EXPECT_OK(async_buffer1.GetOpenGlBuffer(buffer2));
// Check that each instance of AsyncBuffer class has only one id
EXPECT_EQ(buffer1, buffer2);
AsyncBuffer async_buffer2 = AsyncBuffer(*tie, ahwb);
EXPECT_OK(async_buffer2.GetOpenGlBuffer(buffer2));
// Check that each different instance will produce unique id
EXPECT_NE(buffer1, buffer2);
} else {
GTEST_SKIP();
}
}
} // namespace
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,38 @@
"""Additional build options needed for the GPU Delegate."""
def gpu_delegate_linkopts():
"""Additional link options needed when linking in the GPU Delegate."""
return select({
"//tensorflow:android": [
"-lEGL",
# We don't need to link libGLESv3, because if it exists,
# it is a symlink to libGLESv2.
# See Compatibility Definition Document:
# https://source.android.com/compatibility/10/android-10-cdd#7_1_4_1_opengl_es
"-lGLESv2",
],
"//conditions:default": [],
})
def tflite_angle_heapcheck_deps():
# copybara:uncomment_begin(google-only)
# return select({
# "//tensorflow/lite/delegates/gpu:tflite_gpu_angle": [
# "@com_google_googletest//:gtest_main_no_heapcheck",
# ],
# "//conditions:default": [
# "@com_google_googletest//:gtest_main",
# ],
# })
# copybara:uncomment_end
# copybara:comment_begin(oss-only)
return ["@com_google_googletest//:gtest_main"]
# copybara:comment_end
def gtest_main_no_heapcheck_deps():
# copybara:uncomment_begin(google-only)
# return ["@com_google_googletest//:gtest_main_no_heapcheck"]
# copybara:uncomment_end
# copybara:comment_begin(oss-only)
return ["@com_google_googletest//:gtest_main"]
# copybara:comment_end
+671
View File
@@ -0,0 +1,671 @@
load("@flatbuffers//:build_defs.bzl", "flatbuffer_cc_library")
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
load("//tensorflow:tensorflow.bzl", "if_google", "workspace_root")
load(
"//tensorflow/core/platform:build_config_root.bzl",
"tf_gpu_tests_tags",
)
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
default_visibility = ["//visibility:public"],
licenses = ["notice"],
)
_API_NO_GL_DEPS = [
":cl_command_queue",
":cl_errors",
":cl_event",
":environment",
":inference_context",
":opencl_wrapper",
":tensor",
":tensor_type_util",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/types:span",
"//tensorflow/lite/delegates/gpu:api",
"//tensorflow/lite/delegates/gpu:tflite_profile",
"//tensorflow/lite/delegates/gpu/cl/kernels:converter",
"//tensorflow/lite/delegates/gpu/common:data_type",
"//tensorflow/lite/delegates/gpu/common:model",
"//tensorflow/lite/delegates/gpu/common:precision",
"//tensorflow/lite/delegates/gpu/common:shape",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common:tensor",
"//tensorflow/lite/delegates/gpu/common/task:tensor_desc",
]
_GPU_API_DELEGATE_NO_GL_DEPS = [
":opencl_wrapper",
":tensor_type_util",
"@com_google_absl//absl/types:span",
"//tensorflow/lite:kernel_api",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/delegates/gpu:api",
"//tensorflow/lite/delegates/gpu/common:model",
"//tensorflow/lite/delegates/gpu/common:model_builder",
"//tensorflow/lite/delegates/gpu/common:model_transformer",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/transformations:model_transformations",
]
config_setting(
name = "opencl_delegate_no_gl",
values = {"copt": "-DCL_DELEGATE_NO_GL"},
)
# copybara:uncomment_begin(google-only)
# cc_library(
# name = "api_no_gl",
# srcs = ["api.cc"],
# hdrs = ["api.h"],
# defines = ["CL_DELEGATE_NO_GL"],
# deps = _API_NO_GL_DEPS + ["//third_party/GL:EGL_headers"],
# )
# copybara:uncomment_end
cc_library(
name = "api",
srcs = ["api.cc"],
hdrs = ["api.h"],
deps = select({
":opencl_delegate_no_gl": [],
"//conditions:default": [
":egl_sync",
":gl_interop",
],
}) + _API_NO_GL_DEPS,
)
cc_library(
name = "buffer",
srcs = ["buffer.cc"],
hdrs = ["buffer.h"],
deps = [
":cl_command_queue",
":cl_context",
":gpu_object",
":opencl_wrapper",
":util",
"//tensorflow/lite/delegates/gpu/common:data_type",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/task:buffer_desc",
"@com_google_absl//absl/status",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/types:span",
],
)
cc_test(
name = "buffer_test",
srcs = ["buffer_test.cc"],
linkstatic = True,
tags = tf_gpu_tests_tags() + [
"linux",
"local",
],
deps = [
":buffer",
":cl_test",
"//tensorflow/lite/delegates/gpu/common:status",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "cl_test",
testonly = 1,
hdrs = ["cl_test.h"],
deps = [
":environment",
":opencl_wrapper",
"@com_google_googletest//:gtest",
],
)
cc_library(
name = "cl_arguments",
srcs = ["cl_arguments.cc"],
hdrs = ["cl_arguments.h"],
deps = [
":buffer",
":cl_context",
":gpu_object",
":qcom_thin_filter",
":tensor",
"//tensorflow/lite/delegates/gpu/common:gpu_info",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common:util",
"//tensorflow/lite/delegates/gpu/common/task:arguments",
"//tensorflow/lite/delegates/gpu/common/task:util",
"@com_google_absl//absl/strings",
],
)
cc_test(
name = "cl_arguments_test",
srcs = ["cl_arguments_test.cc"],
linkstatic = True,
tags = tf_gpu_tests_tags() + [
"linux",
"local",
],
deps = [
":buffer",
":cl_arguments",
":cl_test",
":gpu_object",
"//tensorflow/lite/delegates/gpu/common:gpu_info",
"@com_google_absl//absl/strings",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "cl_command_buffer",
srcs = ["cl_command_buffer.cc"],
hdrs = ["cl_command_buffer.h"],
deps = [
":cl_command_queue",
":cl_event",
":opencl_wrapper",
":util",
"//tensorflow/lite/delegates/gpu/common:status",
"@com_google_absl//absl/strings",
],
)
cc_library(
name = "cl_command_queue",
srcs = ["cl_command_queue.cc"],
hdrs = ["cl_command_queue.h"],
deps = [
":cl_context",
":cl_device",
":cl_event",
":cl_kernel",
":opencl_wrapper",
":util",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common:types",
"//tensorflow/lite/delegates/gpu/common/task:profiling_info",
"@com_google_absl//absl/strings",
],
)
cc_library(
name = "cl_context",
srcs = ["cl_context.cc"],
hdrs = ["cl_context.h"],
deps = [
":cl_device",
":cl_image_format",
":opencl_wrapper",
":util",
"//tensorflow/lite/delegates/gpu/common:data_type",
"//tensorflow/lite/delegates/gpu/common:status",
"@com_google_absl//absl/strings",
],
)
cc_library(
name = "cl_device",
srcs = ["cl_device.cc"],
hdrs = ["cl_device.h"],
deps = [
":opencl_wrapper",
":util",
"//tensorflow/lite/delegates/gpu/common:gpu_info",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common:types",
"//tensorflow/lite/experimental/acceleration/compatibility:android_info",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
],
)
cc_test(
name = "cl_device_test",
srcs = ["cl_device_test.cc"],
deps = [
":cl_device",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "cl_errors",
hdrs = ["cl_errors.h"],
deps = [
":util",
"//tensorflow/lite/delegates/gpu/common:status",
],
)
cc_library(
name = "cl_event",
srcs = ["cl_event.cc"],
hdrs = ["cl_event.h"],
deps = [
":opencl_wrapper",
],
)
cc_library(
name = "cl_image_format",
srcs = ["cl_image_format.cc"],
hdrs = ["cl_image_format.h"],
deps = [
":opencl_wrapper",
"//tensorflow/lite/delegates/gpu/common:data_type",
],
)
cc_library(
name = "cl_kernel",
srcs = ["cl_kernel.cc"],
hdrs = ["cl_kernel.h"],
deps = [
":cl_context",
":cl_device",
":cl_program",
":opencl_wrapper",
":util",
"//tensorflow/lite/delegates/gpu/common:kernel_info",
"//tensorflow/lite/delegates/gpu/common:status",
"@com_google_absl//absl/strings",
],
)
cc_library(
name = "cl_memory",
srcs = ["cl_memory.cc"],
hdrs = ["cl_memory.h"],
deps = [
":opencl_wrapper",
"//tensorflow/lite/delegates/gpu/common:access_type",
"//tensorflow/lite/delegates/gpu/common:status",
],
)
cc_library(
name = "cl_operation",
srcs = ["cl_operation.cc"],
hdrs = ["cl_operation.h"],
deps = [
":cl_arguments",
":cl_command_queue",
":cl_context",
":cl_device",
":cl_kernel",
":program_cache",
":tensor",
"//tensorflow/lite/delegates/gpu/common/task:compiler_options",
"//tensorflow/lite/delegates/gpu/common/task:gpu_operation",
"@com_google_absl//absl/strings",
],
)
cc_library(
name = "cl_program",
srcs = ["cl_program.cc"],
hdrs = ["cl_program.h"],
deps = [
":cl_context",
":cl_device",
":opencl_wrapper",
":util",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/task:compiler_options",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/types:span",
],
)
flatbuffer_cc_library(
name = "compiled_program_cache_cc_fbs",
srcs = ["compiled_program_cache.fbs"],
flatc_args = [
"--scoped-enums",
],
)
cc_library(
name = "egl_sync",
srcs = ["egl_sync.cc"],
hdrs = ["egl_sync.h"],
defines = [
"EGL_EGLEXT_PROTOTYPES",
],
deps = [
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/gl:gl_call",
],
)
cc_library(
name = "environment",
srcs = ["environment.cc"],
hdrs = ["environment.h"],
deps = [
":cl_command_queue",
":cl_context",
":cl_device",
":program_cache",
"//tensorflow/lite/delegates/gpu/common:data_type",
"//tensorflow/lite/delegates/gpu/common:gpu_info",
"//tensorflow/lite/delegates/gpu/common:precision",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common:tensor",
"//tensorflow/lite/delegates/gpu/common/task:tensor_desc",
],
)
cc_library(
name = "gl_interop",
srcs = ["gl_interop.cc"],
hdrs = ["gl_interop.h"],
deps = [
":cl_command_queue",
":cl_context",
":cl_device",
":cl_errors",
":cl_event",
":cl_memory",
":egl_sync",
":environment",
":opencl_wrapper",
"//tensorflow/lite/delegates/gpu:spi",
"//tensorflow/lite/delegates/gpu/common:access_type",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/gl:gl_call",
"//tensorflow/lite/delegates/gpu/gl:gl_sync",
"//tensorflow/lite/delegates/gpu/gl:portable",
"@com_google_absl//absl/strings",
],
)
# copybara:uncomment_begin(google-only)
# cc_library(
# name = "gl_interop_no_gl",
# srcs = ["gl_interop.cc"],
# hdrs = ["gl_interop.h"],
# defines = ["CL_DELEGATE_NO_GL"],
# deps = [
# ":cl_command_queue",
# ":cl_device",
# ":environment",
# "//third_party/GL:EGL_headers",
# "//tensorflow/lite/delegates/gpu:spi",
# "//tensorflow/lite/delegates/gpu/common:access_type",
# "//tensorflow/lite/delegates/gpu/common:status",
# ],
# )
#
# cc_test(
# name = "gl_interop_no_gl_test",
# srcs = ["gl_interop_no_gl_test.cc"],
# tags = ["requires-gpu-nvidia"],
# deps = [
# ":gl_interop_no_gl",
# "@com_google_googletest//:gtest_main",
# ],
# )
# copybara:uncomment_end
# copybara:uncomment_begin(google-only)
# cc_library(
# name = "gpu_api_delegate_no_gl",
# srcs = ["gpu_api_delegate.cc"],
# hdrs = ["gpu_api_delegate.h"],
# defines = ["CL_DELEGATE_NO_GL"],
# linkopts = select({
# "//tensorflow:android": [
# "-lEGL",
# "-lGLESv3",
# ],
# "//conditions:default": [],
# }),
# deps = _GPU_API_DELEGATE_NO_GL_DEPS + [
# ":api_no_gl",
# "//third_party/GL:EGL_headers",
# "//third_party/GL:GLES3_headers",
# "//tensorflow/lite/delegates/gpu:delegate_no_gl",
# ],
# )
# copybara:uncomment_end
cc_library(
name = "gpu_api_delegate",
srcs = ["gpu_api_delegate.cc"],
hdrs = ["gpu_api_delegate.h"],
linkopts = select({
"//tensorflow:android": [
"-lEGL",
"-lGLESv3",
],
"//conditions:default": [],
}),
deps = _GPU_API_DELEGATE_NO_GL_DEPS + [
":api",
"//tensorflow/lite/delegates/gpu:delegate",
],
)
cc_library(
name = "gpu_object",
hdrs = ["gpu_object.h"],
deps = [
":opencl_wrapper",
"//tensorflow/lite/delegates/gpu/common:access_type",
"//tensorflow/lite/delegates/gpu/common:data_type",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/task:gpu_object_desc",
],
)
cc_library(
name = "inference_context",
srcs = [
"inference_context.cc",
],
hdrs = [
"inference_context.h",
],
deps = [
":buffer",
":cl_command_buffer",
":cl_command_queue",
":cl_device",
":cl_event",
":cl_operation",
":environment",
":gpu_object",
":opencl_wrapper",
":recordable_queue_builder",
":serialization_cc_fbs",
":tensor",
"//tensorflow/lite/delegates/gpu/common:data_type",
"//tensorflow/lite/delegates/gpu/common:gpu_model",
"//tensorflow/lite/delegates/gpu/common:gpu_model_cc_fbs",
"//tensorflow/lite/delegates/gpu/common:memory_management",
"//tensorflow/lite/delegates/gpu/common:model",
"//tensorflow/lite/delegates/gpu/common:model_hints",
"//tensorflow/lite/delegates/gpu/common:precision",
"//tensorflow/lite/delegates/gpu/common:shape",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common:tensor",
"//tensorflow/lite/delegates/gpu/common:types",
"//tensorflow/lite/delegates/gpu/common:util",
"//tensorflow/lite/delegates/gpu/common/task:gpu_operation",
"//tensorflow/lite/delegates/gpu/common/task:serialization_base",
"//tensorflow/lite/delegates/gpu/common/task:tensor_desc",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
],
)
cc_library(
name = "opencl_wrapper",
srcs = ["opencl_wrapper.cc"],
hdrs = ["opencl_wrapper.h"],
linkopts = select({
"//tensorflow:android": ["-lm"],
"//tensorflow:windows": [], # Windows does not support dlopen().
"//conditions:default": ["-ldl"], # opencl_wrapper calls dlopen()
}),
local_defines = select({
# copybara:uncomment_begin(google-only)
# "//tools/cc_target_os:linux-google": ["__LINUX_GOOGLE__"],
# copybara:uncomment_end
"//conditions:default": [],
}),
deps = [
"//tensorflow/lite/delegates/gpu/common:status",
"@com_google_absl//absl/strings",
"@opencl_headers",
"//tensorflow/lite/delegates/gpu/cl/" + if_google("google", "default") + ":qcom_wrapper",
"//tensorflow/lite/tools:logging",
] + select({
"//conditions:default": [],
}),
)
cc_library(
name = "program_cache",
srcs = ["program_cache.cc"],
hdrs = ["program_cache.h"],
deps = [
":cl_context",
":cl_device",
":cl_kernel",
":cl_program",
":compiled_program_cache_cc_fbs",
":util",
"//tensorflow/lite/delegates/gpu/common:status",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/types:span",
"@farmhash_archive//:farmhash",
"@flatbuffers",
],
)
cc_library(
name = "qcom_thin_filter",
srcs = ["qcom_thin_filter.cc"],
hdrs = ["qcom_thin_filter.h"],
deps = [
":cl_context",
":gpu_object",
":util",
"//tensorflow/lite/delegates/gpu/common/task:qcom_thin_filter_desc",
],
)
cc_library(
name = "recordable_queue",
hdrs = ["recordable_queue.h"],
deps = [
":cl_command_queue",
":cl_context",
":cl_device",
":cl_operation",
":opencl_wrapper",
"//tensorflow/lite/delegates/gpu/common:status",
],
)
cc_library(
name = "recordable_queue_builder",
hdrs = ["recordable_queue_builder.h"],
deps = [
":cl_context",
":cl_device",
":cl_operation",
":recordable_queue",
"//tensorflow/lite/delegates/gpu/cl/" + if_google("google", "default") + ":recordable_queue",
],
)
flatbuffer_cc_library(
name = "serialization_cc_fbs",
srcs = ["serialization.fbs"],
flatc_args = [
"--scoped-enums",
"-I " + workspace_root,
],
includes = [
"//tensorflow/lite/delegates/gpu/common:gpu_model_cc_fbs_includes",
"//tensorflow/lite/delegates/gpu/common/task:serialization_base_cc_fbs_includes",
],
)
cc_library(
name = "tensor",
srcs = ["tensor.cc"],
hdrs = ["tensor.h"],
deps = [
":buffer",
":cl_command_queue",
":cl_context",
":cl_device",
":cl_image_format",
":cl_memory",
":gpu_object",
":util",
"//tensorflow/lite/delegates/gpu/common:data_type",
"//tensorflow/lite/delegates/gpu/common:shape",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common:tensor",
"//tensorflow/lite/delegates/gpu/common:types",
"//tensorflow/lite/delegates/gpu/common/task:gpu_tensor",
"//tensorflow/lite/delegates/gpu/common/task:tensor_desc",
"@com_google_absl//absl/strings",
],
)
cc_test(
name = "tensor_test",
srcs = ["tensor_test.cc"],
linkstatic = True,
tags = tf_gpu_tests_tags() + [
"linux",
"local",
],
deps = [
":cl_test",
":tensor",
"//tensorflow/lite/delegates/gpu/common:data_type",
"//tensorflow/lite/delegates/gpu/common:shape",
"//tensorflow/lite/delegates/gpu/common:status",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "tensor_type_util",
srcs = ["tensor_type_util.cc"],
hdrs = ["tensor_type_util.h"],
deps = [
"//tensorflow/lite/delegates/gpu:api",
"//tensorflow/lite/delegates/gpu/common/task:tensor_desc",
],
)
cc_library(
name = "util",
srcs = ["util.cc"],
hdrs = ["util.h"],
deps = [
":opencl_wrapper",
"//tensorflow/lite/delegates/gpu/common:data_type",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common:tensor",
"//tensorflow/lite/delegates/gpu/common:util",
"@com_google_absl//absl/status",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/types:span",
"//tensorflow/lite/delegates/gpu/cl/" + if_google("google", "default") + ":util",
],
)
File diff suppressed because it is too large Load Diff
+175
View File
@@ -0,0 +1,175 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_CL_API_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_API_H_
#ifdef CL_DELEGATE_NO_GL
#define EGL_NO_PROTOTYPES
#endif
#include <EGL/egl.h>
#include <cstdint>
#include <memory>
#include "absl/types/span.h"
#include "tensorflow/lite/delegates/gpu/api.h"
#include "tensorflow/lite/delegates/gpu/common/model.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
// Usage example:
//
// std::unique_ptr<InferenceEnvironment> env;
// RETURN_IF_ERROR(NewInferenceEnvironment(option, &env));
//
// InferenceOptions options;
//
// std::unique_ptr<InferenceBuilder> builder;
// RETURN_IF_ERROR(env->NewInferenceBuilder(options, model, &builder));
// // now builder is ready to prepare inference runner.
//
// -----------------
// Supported formats
// -----------------
//
// OpenCL implementation uses 2D textures as the primary format.
// Tensor in HWDC4 layout is {TEXTURE_2D, RGBA, width := W*D, height := H}.
//
namespace tflite {
namespace gpu {
namespace cl {
struct InferenceOptions : public tflite::gpu::InferenceOptions {};
// Indicates environment
struct InferenceEnvironmentProperties {
bool is_opencl_available = false;
// GL objects (buffers and textures) could be shared with CL context.
bool is_gl_sharing_supported = false;
// Indicates whether fast GL->CL synchronization is supported.
bool is_gl_to_cl_fast_sync_supported = false;
// Indicates whether fast CL->GL synchronization is supported.
bool is_cl_to_gl_fast_sync_supported = false;
};
// Environment manages all resources that need to stay until any inference is
// running using OpenCL backend.
class InferenceEnvironment {
public:
virtual ~InferenceEnvironment() {}
// Converts GraphFloat32 into intermediate, device-specific representation.
// This serialized_model specific for device and InferenceOptions.
// serialized_model cannot be used with another device or InferenceOptions.
// Loading serialized_model is much faster than loading GraphFloat32.
// serialized_model must be used with appropriate NewInferenceBuilder
// method (see below).
// Normally BuildSerializedModel method need to be called whenever a model or
// OS GPU driver is updated.
virtual absl::Status BuildSerializedModel(
const InferenceOptions& options, GraphFloat32 model,
std::vector<uint8_t>* serialized_model) = 0;
// Serialized model can became invalid when environment changes. In this case
// this call will fail and model must be regenerated(with
// BuildSerializedModel).
virtual absl::Status NewInferenceBuilder(
const absl::Span<const uint8_t> serialized_model,
std::unique_ptr<InferenceBuilder>* builder) = 0;
virtual absl::Status NewInferenceBuilder(
const InferenceOptions& options, GraphFloat32 model,
std::unique_ptr<InferenceBuilder>* builder) = 0;
// Returns opaque binary blob that contains a collection of already compiled
// OpenCL kernels present in a cache. Returned data could be re-used later
// to speed up compilation time when new environment is created for the same
// set of models.
// Returned data is valid only if used on the same device, otherwise it will
// not be compatible and will be discarded.
virtual std::vector<uint8_t> GetSerializedBinaryCache() const = 0;
};
struct InferenceEnvironmentOptions {
// If any of these objects are set, created environment will use them instead
// of creating/choosing own instances.
cl_device_id device = nullptr;
cl_context context = nullptr;
cl_command_queue command_queue = nullptr;
// Whenever input and/or output is GL object, EGL display and context must be
// set to create GL aware OpenCL context. Do not set these variables whenever
// GL interoperability is not needed.
// It is the error to set egl_display, egl_context AND context at the same
// time. If egl_display and egl_context are set, they will be used to create
// GL-aware CL context.
EGLDisplay egl_display = EGL_NO_DISPLAY;
EGLContext egl_context = EGL_NO_CONTEXT;
// Should contain data returned from
// InferenceEnvironment::GetSerializedBinaryCache method.
// Invalid or incompatible data will be discarded. Compiled binary may become
// incompatible when GPU driver is updated.
absl::Span<const uint8_t> serialized_binary_cache;
bool IsGlAware() const {
return egl_context != EGL_NO_CONTEXT && egl_display != EGL_NO_DISPLAY;
}
};
// Creates new OpenCL environment that needs to stay around until all inference
// runners are destroyed.
absl::Status NewInferenceEnvironment(
const InferenceEnvironmentOptions& options,
std::unique_ptr<InferenceEnvironment>* environment,
InferenceEnvironmentProperties* properties /* optional */);
class CLInferenceRunner : public ::tflite::gpu::InferenceRunner {
public:
// The RunWithoutExternalBufferCopy provides a contract where the user of this
// interface does not need
// a. Inputs to be copied to the internal GPU buffer from the external CPU
// input buffer
// b. Outputs to be copied from the internal GPU buffer to the
// external CPU buffer
//
// The user of this interface is responsible for copying the inputs prior to
// running the GPU kernels and outputs post running with the other interfaces
// provided here.
virtual absl::Status RunWithoutExternalBufferCopy() = 0;
// Copies from the external input tensor (normally CPU buffer) to the internal
// OpenCL buffer. The call only guarantees a queueing of the command. The
// caller is expected to hold a copy of the queue and wait for completion if
// the external buffer is a CPU buffer.
virtual absl::Status CopyFromExternalInput(int index) = 0;
// Copies from the internal output OpenCL buffer to the external output
// tensor. The call only guarantees a queueing of the command. The caller
// is expected to hold a copy of the queue and wait for completion if the
// external buffer is a CPU buffer.
virtual absl::Status CopyToExternalOutput(int index) = 0;
};
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_API_H_
+143
View File
@@ -0,0 +1,143 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/cl/buffer.h"
#include <string>
#include "absl/status/status.h"
#include "tensorflow/lite/delegates/gpu/common/data_type.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
absl::Status CreateBuffer(size_t size_in_bytes, bool gpu_read_only,
const void* data, CLContext* context,
Buffer* result) {
cl_mem buffer;
RETURN_IF_ERROR(CreateCLBuffer(context->context(), size_in_bytes,
gpu_read_only, const_cast<void*>(data),
&buffer));
*result = Buffer(buffer, size_in_bytes);
return absl::OkStatus();
}
absl::Status CreateSubBuffer(const Buffer& parent, size_t origin_in_bytes,
size_t size_in_bytes, bool gpu_read_only,
CLContext* context, Buffer* result) {
cl_mem buffer;
if (parent.IsSubBuffer()) {
return absl::InvalidArgumentError(
"Cannot create a sub-buffer from a sub-buffer!");
}
RETURN_IF_ERROR(CreateCLSubBuffer(context->context(), parent.GetMemoryPtr(),
origin_in_bytes, size_in_bytes,
gpu_read_only, &buffer));
*result = Buffer(buffer, size_in_bytes, /*is_sub_buffer=*/true);
return absl::OkStatus();
}
} // namespace
Buffer::Buffer(cl_mem buffer, size_t size_in_bytes, bool is_sub_buffer)
: buffer_(buffer), size_(size_in_bytes), is_sub_buffer_(is_sub_buffer) {}
Buffer::Buffer(cl_mem buffer)
: buffer_(buffer), size_(0), is_sub_buffer_(false), owner_(false) {}
Buffer::Buffer(Buffer&& buffer)
: buffer_(buffer.buffer_),
size_(buffer.size_),
is_sub_buffer_(buffer.is_sub_buffer_),
owner_(buffer.owner_) {
buffer.buffer_ = nullptr;
buffer.size_ = 0;
buffer.is_sub_buffer_ = false;
}
Buffer& Buffer::operator=(Buffer&& buffer) {
if (this != &buffer) {
Release();
std::swap(size_, buffer.size_);
std::swap(buffer_, buffer.buffer_);
std::swap(is_sub_buffer_, buffer.is_sub_buffer_);
std::swap(owner_, buffer.owner_);
}
return *this;
}
void Buffer::Release() {
if (owner_ && buffer_) {
clReleaseMemObject(buffer_);
buffer_ = nullptr;
size_ = 0;
is_sub_buffer_ = false;
}
}
absl::Status Buffer::GetGPUResources(const GPUObjectDescriptor* obj_ptr,
GPUResourcesWithValue* resources) const {
const auto* buffer_desc = dynamic_cast<const BufferDescriptor*>(obj_ptr);
if (!buffer_desc) {
return absl::InvalidArgumentError("Expected BufferDescriptor on input.");
}
resources->buffers.push_back({"buffer", buffer_});
return absl::OkStatus();
}
absl::Status Buffer::CreateFromBufferDescriptor(const BufferDescriptor& desc,
CLContext* context) {
bool read_only = desc.memory_type == MemoryType::CONSTANT;
uint8_t* data_ptr = desc.data.empty()
? nullptr
: const_cast<unsigned char*>(desc.data.data());
size_ = desc.size;
return CreateCLBuffer(context->context(), desc.size, read_only, data_ptr,
&buffer_);
}
Buffer CreateBufferShared(cl_mem buffer) { return Buffer(buffer); }
absl::Status CreateReadOnlyBuffer(size_t size_in_bytes, CLContext* context,
Buffer* result) {
return CreateBuffer(size_in_bytes, true, nullptr, context, result);
}
absl::Status CreateReadOnlyBuffer(size_t size_in_bytes, const void* data,
CLContext* context, Buffer* result) {
return CreateBuffer(size_in_bytes, true, data, context, result);
}
absl::Status CreateReadWriteBuffer(size_t size_in_bytes, CLContext* context,
Buffer* result) {
return CreateBuffer(size_in_bytes, false, nullptr, context, result);
}
absl::Status CreateReadWriteSubBuffer(const Buffer& parent,
size_t origin_in_bytes,
size_t size_in_bytes, CLContext* context,
Buffer* result) {
return CreateSubBuffer(parent, origin_in_bytes, size_in_bytes,
/*gpu_read_only=*/false, context, result);
}
} // namespace cl
} // namespace gpu
} // namespace tflite
+125
View File
@@ -0,0 +1,125 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_CL_BUFFER_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_BUFFER_H_
#include "absl/strings/str_cat.h"
#include "absl/types/span.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_command_queue.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_context.h"
#include "tensorflow/lite/delegates/gpu/cl/gpu_object.h"
#include "tensorflow/lite/delegates/gpu/cl/opencl_wrapper.h"
#include "tensorflow/lite/delegates/gpu/cl/util.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/task/buffer_desc.h"
namespace tflite {
namespace gpu {
namespace cl {
// Buffer represent linear GPU data storage with arbitrary data format.
// Buffer is moveable but not copyable.
class Buffer : public GPUObject {
public:
Buffer() {} // just for using Buffer as a class members
Buffer(cl_mem buffer, size_t size_in_bytes, bool is_sub_buffer = false);
explicit Buffer(cl_mem buffer);
// Move only
Buffer(Buffer&& buffer);
Buffer& operator=(Buffer&& buffer);
Buffer(const Buffer&) = delete;
Buffer& operator=(const Buffer&) = delete;
~Buffer() override { Release(); }
// for profiling and memory statistics
uint64_t GetMemorySizeInBytes() const { return size_; }
cl_mem GetMemoryPtr() const { return buffer_; }
bool IsSubBuffer() const { return is_sub_buffer_; }
// Writes data to a buffer. Data should point to a region that
// has exact size in bytes as size_in_bytes(constructor parameter).
template <typename T>
absl::Status WriteData(CLCommandQueue* queue, const absl::Span<T> data);
// Reads data from Buffer into CPU memory.
template <typename T>
absl::Status ReadData(CLCommandQueue* queue, std::vector<T>* result) const;
absl::Status GetGPUResources(const GPUObjectDescriptor* obj_ptr,
GPUResourcesWithValue* resources) const override;
absl::Status CreateFromBufferDescriptor(const BufferDescriptor& desc,
CLContext* context);
private:
void Release();
cl_mem buffer_ = nullptr;
size_t size_ = 0;
bool is_sub_buffer_ = false;
bool owner_ = true;
};
Buffer CreateBufferShared(cl_mem buffer);
absl::Status CreateReadOnlyBuffer(size_t size_in_bytes, CLContext* context,
Buffer* result);
absl::Status CreateReadOnlyBuffer(size_t size_in_bytes, const void* data,
CLContext* context, Buffer* result);
absl::Status CreateReadWriteBuffer(size_t size_in_bytes, CLContext* context,
Buffer* result);
absl::Status CreateReadWriteSubBuffer(const Buffer& parent,
size_t origin_in_bytes,
size_t size_in_bytes, CLContext* context,
Buffer* result);
template <typename T>
absl::Status Buffer::WriteData(CLCommandQueue* queue,
const absl::Span<T> data) {
if (size_ != sizeof(T) * data.size()) {
return absl::InvalidArgumentError(absl::StrCat(
"absl::Span<T> data size is different from buffer allocated size: ",
size_, " vs ", sizeof(T) * data.size()));
}
RETURN_IF_ERROR(queue->EnqueueWriteBuffer(buffer_, size_, data.data()));
return absl::OkStatus();
}
template <typename T>
absl::Status Buffer::ReadData(CLCommandQueue* queue,
std::vector<T>* result) const {
if (size_ % sizeof(T) != 0) {
return absl::UnknownError("Wrong element size(typename T is not correct?");
}
const int elements_count = size_ / sizeof(T);
result->resize(elements_count);
return queue->EnqueueReadBuffer(buffer_, size_, result->data());
}
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_BUFFER_H_
@@ -0,0 +1,60 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/cl/buffer.h"
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
using ::testing::FloatNear;
using ::testing::Pointwise;
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLTest, BufferTestFloat) {
const std::vector<float> data = {1.0, 2.0, 3.0, -4.0, 5.1};
Buffer buffer;
ASSERT_OK(CreateReadWriteBuffer(sizeof(float) * 5, &env_.context(), &buffer));
ASSERT_OK(buffer.WriteData(env_.queue(),
absl::MakeConstSpan(data.data(), data.size())));
std::vector<float> gpu_data;
ASSERT_OK(buffer.ReadData<float>(env_.queue(), &gpu_data));
EXPECT_THAT(gpu_data, Pointwise(FloatNear(0.0f), data));
}
TEST_F(OpenCLTest, BufferTestHalf) {
const std::vector<half> data = {half(1.4), half(2.1), half(2.2)};
Buffer buffer;
ASSERT_OK(CreateReadWriteBuffer(sizeof(half) * 3, &env_.context(), &buffer));
ASSERT_OK(buffer.WriteData(env_.queue(),
absl::MakeConstSpan(data.data(), data.size())));
std::vector<half> gpu_data;
ASSERT_OK(buffer.ReadData<half>(env_.queue(), &gpu_data));
EXPECT_THAT(gpu_data, Pointwise(FloatNear(0.0f), data));
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,635 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/cl/cl_arguments.h"
#include <memory>
#include <string>
#include <utility>
#include "absl/strings/ascii.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/substitute.h"
#include "tensorflow/lite/delegates/gpu/cl/buffer.h"
#include "tensorflow/lite/delegates/gpu/cl/gpu_object.h"
#include "tensorflow/lite/delegates/gpu/cl/qcom_thin_filter.h"
#include "tensorflow/lite/delegates/gpu/cl/tensor.h"
#include "tensorflow/lite/delegates/gpu/common/task/util.h"
#include "tensorflow/lite/delegates/gpu/common/util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
bool IsWordSymbol(char symbol) {
return absl::ascii_isalnum(symbol) || symbol == '_';
}
void ReplaceAllWords(const std::string& old_word, const std::string& new_word,
std::string* str) {
size_t position = str->find(old_word);
while (position != std::string::npos) {
char prev = position == 0 ? '.' : (*str)[position - 1];
char next = position + old_word.size() < str->size()
? (*str)[position + old_word.size()]
: '.';
if (IsWordSymbol(prev) || IsWordSymbol(next)) {
position = str->find(old_word, position + 1);
continue;
}
str->replace(position, old_word.size(), new_word);
position = str->find(old_word, position + new_word.size());
}
}
void AppendArgument(const std::string& arg, std::string* args) {
if (!args->empty()) {
absl::StrAppend(args, ",\n ");
}
absl::StrAppend(args, arg);
}
std::string GetImageModifier(AccessType access) {
switch (access) {
case AccessType::READ:
return "__read_only";
case AccessType::WRITE:
return "__write_only";
case AccessType::READ_WRITE:
return "__read_write";
}
}
std::string GetDefaultSamplers(const GpuInfo& gpu_info) {
std::string result;
result +=
"__constant sampler_t smp_none = CLK_NORMALIZED_COORDS_FALSE | "
"CLK_ADDRESS_NONE | CLK_FILTER_NEAREST;\n";
if (gpu_info.IsAdreno() && gpu_info.adreno_info.IsAdreno3xx()) {
// Unfortunately, CLK_ADDRESS_CLAMP is very slow on Adreno3xx and
// we can observe huge register overhead when compared to other modes.
// While using CLK_ADDRESS_NONE with out-of-range image coordinates is
// undefined in the OpenCL specification, we have observed that
// CLK_ADDRESS_NONE works like CLK_ADDRESS_CLAMP for out-of-range image
// coordinates for RGBA F16/F32 textures on Adreno3xx devices. Using
// CLK_ADDRESS_NONE is significantly faster than CLK_ADDRESS_CLAMP on Adreno
// 3xx.
result +=
"__constant sampler_t smp_zero = CLK_NORMALIZED_COORDS_FALSE | "
"CLK_ADDRESS_NONE | CLK_FILTER_NEAREST;\n";
} else {
result +=
"__constant sampler_t smp_zero = CLK_NORMALIZED_COORDS_FALSE | "
"CLK_ADDRESS_CLAMP | CLK_FILTER_NEAREST;\n";
}
return result;
}
absl::Status CreateCLObject(GPUObjectDescriptor* desc, CLContext* context,
GPUObjectPtr* result) {
const auto* buffer_desc = dynamic_cast<const BufferDescriptor*>(desc);
if (buffer_desc) {
Buffer gpu_buffer;
RETURN_IF_ERROR(
gpu_buffer.CreateFromBufferDescriptor(*buffer_desc, context));
*result = std::make_unique<Buffer>(std::move(gpu_buffer));
return absl::OkStatus();
}
const auto* tensor_desc = dynamic_cast<const TensorDescriptor*>(desc);
if (tensor_desc) {
Tensor gpu_tensor;
RETURN_IF_ERROR(gpu_tensor.CreateFromDescriptor(*tensor_desc, context));
*result = std::make_unique<Tensor>(std::move(gpu_tensor));
return absl::OkStatus();
}
const auto* qcom_thin_filter_desc =
dynamic_cast<const QcomThinFilterDescriptor*>(desc);
if (qcom_thin_filter_desc) {
QcomThinFilter thin_filter;
RETURN_IF_ERROR(
thin_filter.CreateFromDescriptor(*qcom_thin_filter_desc, context));
*result = std::make_unique<QcomThinFilter>(std::move(thin_filter));
return absl::OkStatus();
}
return absl::InvalidArgumentError("Unknown GPU descriptor.");
}
} // namespace
// Static
constexpr char CLArguments::kArgsPrefix[];
absl::Status CLArguments::Init(const GpuInfo& gpu_info, CLContext* context,
Arguments* args, std::string* code) {
RETURN_IF_ERROR(AllocateObjects(*args, context));
RETURN_IF_ERROR(AddObjectArgs(gpu_info, *args));
args->MoveObjectRefs(&object_refs_);
const bool use_f32_for_halfs = gpu_info.IsPowerVR();
CopyArguments(*args, use_f32_for_halfs);
RETURN_IF_ERROR(SetObjectsResources(*args));
RenameArgumentsInCode(code);
args->ResolveArgsPass(code);
*code = absl::Substitute(*code, GetListOfArgs());
if (gpu_info.SupportsImages()) {
*code = GetDefaultSamplers(gpu_info) + *code;
}
return absl::OkStatus();
}
absl::Status CLArguments::Init(const GpuInfo& gpu_info, Arguments* args,
CLContext* context) {
RETURN_IF_ERROR(AllocateObjects(*args, context));
RETURN_IF_ERROR(AddObjectArgs(gpu_info, *args));
args->MoveObjectRefs(&object_refs_);
const bool use_f32_for_halfs = gpu_info.IsPowerVR();
CopyArguments(*args, use_f32_for_halfs);
RETURN_IF_ERROR(SetObjectsResources(*args));
return absl::OkStatus();
}
absl::Status CLArguments::AllocateObjects(const Arguments& args,
CLContext* context) {
objects_.resize(args.GetObjects().size());
int i = 0;
for (auto& t : args.GetObjects()) {
RETURN_IF_ERROR(CreateCLObject(t.second.get(), context, &objects_[i]));
i++;
}
return absl::OkStatus();
}
absl::Status CLArguments::AddObjectArgs(const GpuInfo& gpu_info,
const Arguments& args) {
for (const auto& t : args.GetObjects()) {
AddGPUResources(t.first, t.second->GetGPUResources(gpu_info));
}
for (const auto& t : args.GetObjectRefs()) {
AddGPUResources(t.first, t.second->GetGPUResources(gpu_info));
}
return absl::OkStatus();
}
absl::Status CLArguments::SetObjectsResources(const Arguments& args) {
int i = 0;
for (const auto& t : args.GetObjects()) {
GPUResourcesWithValue resources;
RETURN_IF_ERROR(objects_[i]->GetGPUResources(t.second.get(), &resources));
RETURN_IF_ERROR(SetGPUResources(t.first, resources));
i++;
}
return absl::OkStatus();
}
void CLArguments::CopyArguments(const Arguments& args, bool use_f32_for_halfs) {
for (const auto& fvalue : args.GetFloatValues()) {
auto& new_val = float_values_[fvalue.first];
new_val.value = fvalue.second.value;
new_val.active = fvalue.second.active;
if (fvalue.second.active) {
new_val.offset = shared_float4s_data_.size();
shared_float4s_data_.push_back(new_val.value);
}
}
for (const auto& ivalue : args.GetIntValues()) {
auto& new_val = int_values_[ivalue.first];
new_val.value = ivalue.second.value;
new_val.active = ivalue.second.active;
if (ivalue.second.active) {
new_val.offset = shared_int4s_data_.size();
shared_int4s_data_.push_back(new_val.value);
}
}
for (const auto& hfvalue : args.GetHalfValues()) {
auto& new_val = half_values_[hfvalue.first];
new_val.value = hfvalue.second.value;
new_val.active = hfvalue.second.active;
if (hfvalue.second.active) {
if (use_f32_for_halfs) {
new_val.store_as_f32 = true;
new_val.offset = shared_float4s_data_.size();
shared_float4s_data_.push_back(new_val.value);
} else {
new_val.store_as_f32 = false;
new_val.offset = shared_half4s_data_.size();
shared_half4s_data_.push_back(new_val.value);
}
}
}
int shared_int4s_aligned_size = AlignByN(shared_int4s_data_.size(), 4);
shared_int4s_data_.resize(shared_int4s_aligned_size);
int shared_float4s_aligned_size = AlignByN(shared_float4s_data_.size(), 4);
shared_float4s_data_.resize(shared_float4s_aligned_size);
int shared_half4s_aligned_size = AlignByN(shared_half4s_data_.size(), 4);
shared_half4s_data_.resize(shared_half4s_aligned_size);
}
void CLArguments::RenameArgumentsInCode(std::string* code) {
const std::string postfixes[4] = {"x", "y", "z", "w"};
for (const auto& fvalue : float_values_) {
if (fvalue.second.active) {
std::string index = std::to_string(fvalue.second.offset / 4);
std::string new_name =
"shared_float4_" + index + "." + postfixes[fvalue.second.offset % 4];
ReplaceAllWords(kArgsPrefix + fvalue.first, new_name, code);
}
}
for (const auto& ivalue : int_values_) {
if (ivalue.second.active) {
std::string index = std::to_string(ivalue.second.offset / 4);
std::string new_name =
"shared_int4_" + index + "." + postfixes[ivalue.second.offset % 4];
ReplaceAllWords(kArgsPrefix + ivalue.first, new_name, code);
}
}
for (const auto& hfvalue : half_values_) {
if (hfvalue.second.active) {
std::string index = std::to_string(hfvalue.second.offset / 4);
std::string new_name;
if (hfvalue.second.store_as_f32) {
new_name = "(half)(shared_float4_" + index + "." +
postfixes[hfvalue.second.offset % 4] + ")";
} else {
new_name = "shared_half4_" + index + "." +
postfixes[hfvalue.second.offset % 4];
}
ReplaceAllWords(kArgsPrefix + hfvalue.first, new_name, code);
}
}
}
void CLArguments::AddBuffer(const std::string& name,
const GPUBufferDescriptor& desc) {
buffers_[name].desc = desc;
}
void CLArguments::AddImage2D(const std::string& name,
const GPUImage2DDescriptor& desc) {
images2d_[name].desc = desc;
}
void CLArguments::AddImage2DArray(const std::string& name,
const GPUImage2DArrayDescriptor& desc) {
image2d_arrays_[name].desc = desc;
}
void CLArguments::AddImage3D(const std::string& name,
const GPUImage3DDescriptor& desc) {
images3d_[name].desc = desc;
}
void CLArguments::AddImageBuffer(const std::string& name,
const GPUImageBufferDescriptor& desc) {
image_buffers_[name].desc = desc;
}
void CLArguments::AddCustomMemory(const std::string& name,
const GPUCustomMemoryDescriptor& desc) {
custom_memories_[name].desc = desc;
}
void CLArguments::AddGPUResources(const std::string& name,
const GPUResources& resources) {
for (const auto& r : resources.buffers) {
AddBuffer(absl::StrCat(name, "_", r.first), r.second);
}
for (const auto& r : resources.images2d) {
AddImage2D(absl::StrCat(name, "_", r.first), r.second);
}
for (const auto& r : resources.image2d_arrays) {
AddImage2DArray(absl::StrCat(name, "_", r.first), r.second);
}
for (const auto& r : resources.images3d) {
AddImage3D(absl::StrCat(name, "_", r.first), r.second);
}
for (const auto& r : resources.image_buffers) {
AddImageBuffer(absl::StrCat(name, "_", r.first), r.second);
}
for (const auto& r : resources.custom_memories) {
AddCustomMemory(absl::StrCat(name, "_", r.first), r.second);
}
}
absl::Status CLArguments::SetInt(const std::string& name, int value) {
auto it = int_values_.find(name);
if (it == int_values_.end()) {
return absl::NotFoundError(
absl::StrCat("No int argument with name - ", name));
}
it->second.value = value;
if (it->second.active) {
shared_int4s_data_[it->second.offset] = value;
}
return absl::OkStatus();
}
absl::Status CLArguments::SetFloat(const std::string& name, float value) {
auto it = float_values_.find(name);
if (it == float_values_.end()) {
return absl::NotFoundError(
absl::StrCat("No float argument with name - ", name));
}
it->second.value = value;
if (it->second.active) {
shared_float4s_data_[it->second.offset] = value;
}
return absl::OkStatus();
}
absl::Status CLArguments::SetHalf(const std::string& name, half value) {
auto it = half_values_.find(name);
if (it == half_values_.end()) {
return absl::NotFoundError(
absl::StrCat("No half argument with name - ", name));
}
it->second.value = value;
if (it->second.active) {
if (it->second.store_as_f32) {
shared_float4s_data_[it->second.offset] = value;
} else {
shared_half4s_data_[it->second.offset] = value;
}
}
return absl::OkStatus();
}
absl::Status CLArguments::SetImage2D(const std::string& name, cl_mem memory) {
auto it = images2d_.find(name);
if (it == images2d_.end()) {
return absl::NotFoundError(
absl::StrCat("No image2D argument with name - ", name));
}
it->second.memory = memory;
return absl::OkStatus();
}
absl::Status CLArguments::SetBuffer(const std::string& name, cl_mem memory) {
auto it = buffers_.find(name);
if (it == buffers_.end()) {
return absl::NotFoundError(
absl::StrCat("No buffer argument with name - ", name));
}
it->second.memory = memory;
return absl::OkStatus();
}
absl::Status CLArguments::SetImage2DArray(const std::string& name,
cl_mem memory) {
auto it = image2d_arrays_.find(name);
if (it == image2d_arrays_.end()) {
return absl::NotFoundError(
absl::StrCat("No image2D array argument with name - ", name));
}
it->second.memory = memory;
return absl::OkStatus();
}
absl::Status CLArguments::SetImage3D(const std::string& name, cl_mem memory) {
auto it = images3d_.find(name);
if (it == images3d_.end()) {
return absl::NotFoundError(
absl::StrCat("No image3D argument with name - ", name));
}
it->second.memory = memory;
return absl::OkStatus();
}
absl::Status CLArguments::SetImageBuffer(const std::string& name,
cl_mem memory) {
auto it = image_buffers_.find(name);
if (it == image_buffers_.end()) {
return absl::NotFoundError(
absl::StrCat("No image buffer argument with name - ", name));
}
it->second.memory = memory;
return absl::OkStatus();
}
absl::Status CLArguments::SetCustomMemory(const std::string& name,
cl_mem memory) {
auto it = custom_memories_.find(name);
if (it == custom_memories_.end()) {
return absl::NotFoundError(
absl::StrCat("No custom memory argument with name - ", name));
}
it->second.memory = memory;
return absl::OkStatus();
}
absl::Status CLArguments::SetObjectRef(const std::string& name,
const GPUObject* object) {
auto it = object_refs_.find(name);
if (it == object_refs_.end()) {
return absl::NotFoundError(
absl::StrCat("No object ref with name - ", name));
}
GPUResourcesWithValue resources;
RETURN_IF_ERROR(object->GetGPUResources(it->second.get(), &resources));
return SetGPUResources(name, resources);
}
absl::Status CLArguments::SetGPUResources(
const std::string& name, const GPUResourcesWithValue& resources) {
for (const auto& r : resources.generic.ints) {
RETURN_IF_ERROR(SetInt(absl::StrCat(name, "_", r.first), r.second));
}
for (const auto& r : resources.generic.floats) {
RETURN_IF_ERROR(SetFloat(absl::StrCat(name, "_", r.first), r.second));
}
for (const auto& r : resources.buffers) {
RETURN_IF_ERROR(SetBuffer(absl::StrCat(name, "_", r.first), r.second));
}
for (const auto& r : resources.images2d) {
RETURN_IF_ERROR(SetImage2D(absl::StrCat(name, "_", r.first), r.second));
}
for (const auto& r : resources.image2d_arrays) {
RETURN_IF_ERROR(
SetImage2DArray(absl::StrCat(name, "_", r.first), r.second));
}
for (const auto& r : resources.images3d) {
RETURN_IF_ERROR(SetImage3D(absl::StrCat(name, "_", r.first), r.second));
}
for (const auto& r : resources.image_buffers) {
RETURN_IF_ERROR(SetImageBuffer(absl::StrCat(name, "_", r.first), r.second));
}
for (const auto& r : resources.custom_memories) {
RETURN_IF_ERROR(
SetCustomMemory(absl::StrCat(name, "_", r.first), r.second));
}
return absl::OkStatus();
}
std::string CLArguments::GetListOfArgs() {
std::string result;
for (auto& t : buffers_) {
const std::string type_name =
t.second.desc.data_type == DataType::FLOAT32 ? "float" : "half";
std::string attributes;
for (const auto& attr : t.second.desc.attributes) {
attributes += absl::StrCat(" __attribute__((", attr, "))");
}
std::string cl_type;
if (t.second.desc.data_type == DataType::BOOL) {
cl_type = ToCLDataType(DataType::UINT8, t.second.desc.element_size);
} else {
cl_type =
ToCLDataType(t.second.desc.data_type, t.second.desc.element_size);
}
AppendArgument(absl::StrCat(MemoryTypeToCLType(t.second.desc.memory_type),
" ", cl_type, "* ", t.first, attributes),
&result);
}
for (auto& t : image_buffers_) {
AppendArgument(absl::StrCat(GetImageModifier(t.second.desc.access_type),
" image1d_buffer_t ", t.first),
&result);
}
for (auto& t : images2d_) {
AppendArgument(absl::StrCat(GetImageModifier(t.second.desc.access_type),
" image2d_t ", t.first),
&result);
}
for (auto& t : image2d_arrays_) {
AppendArgument(absl::StrCat(GetImageModifier(t.second.desc.access_type),
" image2d_array_t ", t.first),
&result);
}
for (auto& t : images3d_) {
AppendArgument(absl::StrCat(GetImageModifier(t.second.desc.access_type),
" image3d_t ", t.first),
&result);
}
for (auto& t : custom_memories_) {
AppendArgument(absl::StrCat(t.second.desc.type_name, " ", t.first),
&result);
}
for (int i = 0; i < shared_int4s_data_.size() / 4; ++i) {
AppendArgument(absl::StrCat("int4 shared_int4_", i), &result);
}
for (int i = 0; i < shared_float4s_data_.size() / 4; ++i) {
AppendArgument(absl::StrCat("float4 shared_float4_", i), &result);
}
for (int i = 0; i < shared_half4s_data_.size() / 4; ++i) {
AppendArgument(absl::StrCat("half4 shared_half4_", i), &result);
}
return result;
}
absl::Status CLArguments::Bind(cl_kernel kernel, int offset) {
for (auto& t : buffers_) {
const int error_code =
clSetKernelArg(kernel, offset, sizeof(cl_mem), &t.second.memory);
if (error_code != CL_SUCCESS) {
return absl::UnknownError(absl::StrCat(
"Failed to set kernel arguments - ", CLErrorCodeToString(error_code),
"(at index - ", offset, ")"));
}
offset++;
}
for (auto& t : image_buffers_) {
const int error_code =
clSetKernelArg(kernel, offset, sizeof(cl_mem), &t.second.memory);
if (error_code != CL_SUCCESS) {
return absl::UnknownError(absl::StrCat(
"Failed to set kernel arguments - ", CLErrorCodeToString(error_code),
"(at index - ", offset, ")"));
}
offset++;
}
for (auto& t : images2d_) {
const int error_code =
clSetKernelArg(kernel, offset, sizeof(cl_mem), &t.second.memory);
if (error_code != CL_SUCCESS) {
return absl::UnknownError(absl::StrCat(
"Failed to set kernel arguments - ", CLErrorCodeToString(error_code),
"(at index - ", offset, ")"));
}
offset++;
}
for (auto& t : image2d_arrays_) {
const int error_code =
clSetKernelArg(kernel, offset, sizeof(cl_mem), &t.second.memory);
if (error_code != CL_SUCCESS) {
return absl::UnknownError(absl::StrCat(
"Failed to set kernel arguments - ", CLErrorCodeToString(error_code),
"(at index - ", offset, ")"));
}
offset++;
}
for (auto& t : images3d_) {
const int error_code =
clSetKernelArg(kernel, offset, sizeof(cl_mem), &t.second.memory);
if (error_code != CL_SUCCESS) {
return absl::UnknownError(absl::StrCat(
"Failed to set kernel arguments - ", CLErrorCodeToString(error_code),
"(at index - ", offset, ")"));
}
offset++;
}
for (auto& t : custom_memories_) {
const int error_code =
clSetKernelArg(kernel, offset, sizeof(cl_mem), &t.second.memory);
if (error_code != CL_SUCCESS) {
return absl::UnknownError(absl::StrCat(
"Failed to set kernel arguments - ", CLErrorCodeToString(error_code),
"(at index - ", offset, ")"));
}
offset++;
}
for (int i = 0; i < shared_int4s_data_.size() / 4; ++i) {
const int error_code = clSetKernelArg(kernel, offset, sizeof(int32_t) * 4,
&shared_int4s_data_[i * 4]);
if (error_code != CL_SUCCESS) {
return absl::UnknownError(absl::StrCat(
"Failed to set kernel arguments - ", CLErrorCodeToString(error_code),
"(at index - ", offset, ")"));
}
offset++;
}
for (int i = 0; i < shared_float4s_data_.size() / 4; ++i) {
const int error_code = clSetKernelArg(kernel, offset, sizeof(int32_t) * 4,
&shared_float4s_data_[i * 4]);
if (error_code != CL_SUCCESS) {
return absl::UnknownError(absl::StrCat(
"Failed to set kernel arguments - ", CLErrorCodeToString(error_code),
"(at index - ", offset, ")"));
}
offset++;
}
for (int i = 0; i < shared_half4s_data_.size() / 4; ++i) {
const int error_code = clSetKernelArg(kernel, offset, sizeof(int16_t) * 4,
&shared_half4s_data_[i * 4]);
if (error_code != CL_SUCCESS) {
return absl::UnknownError(absl::StrCat(
"Failed to set kernel arguments - ", CLErrorCodeToString(error_code),
"(at index - ", offset, ")"));
}
offset++;
}
return absl::OkStatus();
}
bool CLArguments::HasEqualScalarArguments(const CLArguments& other) const {
return (other.int_values_ == int_values_ &&
other.float_values_ == float_values_ &&
other.half_values_ == half_values_);
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,186 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_ARGUMENTS_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_ARGUMENTS_H_
#include <map>
#include <string>
#include <vector>
#include "tensorflow/lite/delegates/gpu/cl/cl_context.h"
#include "tensorflow/lite/delegates/gpu/cl/gpu_object.h"
#include "tensorflow/lite/delegates/gpu/common/gpu_info.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/task/arguments.h"
namespace tflite {
namespace gpu {
namespace cl {
class CLArguments : public ArgumentsBinder {
public:
CLArguments() = default;
absl::Status Init(const GpuInfo& gpu_info,
CLContext* context, Arguments* args, std::string* code);
absl::Status Init(const GpuInfo& gpu_info, Arguments* args,
CLContext* context);
// Move only
CLArguments(CLArguments&& args) = default;
CLArguments& operator=(CLArguments&& args) = default;
CLArguments(const CLArguments&) = delete;
CLArguments& operator=(const CLArguments&) = delete;
absl::Status SetInt(const std::string& name, int value) override;
absl::Status SetFloat(const std::string& name, float value) override;
absl::Status SetHalf(const std::string& name, half value) override;
absl::Status SetObjectRef(const std::string& name, const GPUObject* object);
absl::Status Bind(cl_kernel kernel, int offset = 0);
// Compares Int, Float, Half names to values mapping with the mapping in
// `other` and returns true if they are the same.
bool HasEqualScalarArguments(const CLArguments& other) const;
private:
absl::Status AllocateObjects(const Arguments& args, CLContext* context);
absl::Status AddObjectArgs(const GpuInfo& gpu_info, const Arguments& args);
void CopyArguments(const Arguments& args, bool use_f32_for_halfs);
void RenameArgumentsInCode(std::string* code);
std::string GetListOfArgs();
void AddBuffer(const std::string& name, const GPUBufferDescriptor& desc);
void AddImage2D(const std::string& name, const GPUImage2DDescriptor& desc);
void AddImage2DArray(const std::string& name,
const GPUImage2DArrayDescriptor& desc);
void AddImage3D(const std::string& name, const GPUImage3DDescriptor& desc);
void AddImageBuffer(const std::string& name,
const GPUImageBufferDescriptor& desc);
void AddCustomMemory(const std::string& name,
const GPUCustomMemoryDescriptor& desc);
void AddGPUResources(const std::string& name, const GPUResources& resources);
absl::Status SetObjectsResources(const Arguments& args);
absl::Status SetGPUResources(const std::string& name,
const GPUResourcesWithValue& resources);
absl::Status SetImage2D(const std::string& name, cl_mem memory);
absl::Status SetBuffer(const std::string& name, cl_mem memory);
absl::Status SetImage2DArray(const std::string& name, cl_mem memory);
absl::Status SetImage3D(const std::string& name, cl_mem memory);
absl::Status SetImageBuffer(const std::string& name, cl_mem memory);
absl::Status SetCustomMemory(const std::string& name, cl_mem memory);
static constexpr char kArgsPrefix[] = "args.";
struct IntValue {
int value;
// many arguments generated automatically and not used
// to reduce amount of data transferred we adding this optimization
bool active = false;
// offset to shared storage.
uint32_t offset = -1;
bool operator==(const IntValue& other) const {
return value == other.value && offset == other.offset &&
active == other.active;
}
};
std::map<std::string, IntValue> int_values_;
std::vector<int32_t> shared_int4s_data_;
struct FloatValue {
float value;
// many arguments generated automatically and not used
// to reduce amount of data transferred we adding this optimization
bool active = false;
// offset to shared storage.
uint32_t offset = -1;
bool operator==(const FloatValue& other) const {
return value == other.value && offset == other.offset &&
active == other.active;
}
};
std::map<std::string, FloatValue> float_values_;
std::vector<float> shared_float4s_data_;
struct HalfValue {
half value;
// many arguments generated automatically and not used
// to reduce amount of data transferred we adding this optimization
bool active = false;
// some devices have issues with half parameters.
bool store_as_f32 = false;
// offset to shared uniform storage.
uint32_t offset = -1;
bool operator==(const HalfValue& other) const {
return value == other.value && offset == other.offset &&
active == other.active;
}
};
std::map<std::string, HalfValue> half_values_;
std::vector<half> shared_half4s_data_;
struct CLBufferDescriptor {
GPUBufferDescriptor desc;
cl_mem memory;
};
struct CLImage2DDescriptor {
GPUImage2DDescriptor desc;
cl_mem memory;
};
struct CLImage2DArrayDescriptor {
GPUImage2DArrayDescriptor desc;
cl_mem memory;
};
struct CLImage3DDescriptor {
GPUImage3DDescriptor desc;
cl_mem memory;
};
struct CLImageBufferDescriptor {
GPUImageBufferDescriptor desc;
cl_mem memory;
};
struct CLCustomMemoryDescriptor {
GPUCustomMemoryDescriptor desc;
cl_mem memory;
};
std::map<std::string, CLBufferDescriptor> buffers_;
std::map<std::string, CLImage2DDescriptor> images2d_;
std::map<std::string, CLImage2DArrayDescriptor> image2d_arrays_;
std::map<std::string, CLImage3DDescriptor> images3d_;
std::map<std::string, CLImageBufferDescriptor> image_buffers_;
std::map<std::string, CLCustomMemoryDescriptor> custom_memories_;
std::map<std::string, GPUObjectDescriptorPtr> object_refs_;
std::vector<GPUObjectPtr> objects_;
};
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_ARGUMENTS_H_
@@ -0,0 +1,76 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/cl/cl_arguments.h"
#include <cstdint>
#include <string>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "absl/strings/match.h"
#include "tensorflow/lite/delegates/gpu/cl/buffer.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_test.h"
#include "tensorflow/lite/delegates/gpu/cl/gpu_object.h"
#include "tensorflow/lite/delegates/gpu/common/gpu_info.h"
namespace tflite {
namespace gpu {
namespace cl {
TEST(CLArgumentsTest, TestSelectorResolve) {
BufferDescriptor desc;
desc.element_type = DataType::FLOAT32;
desc.element_size = 4;
desc.memory_type = MemoryType::GLOBAL;
Arguments args;
args.AddObjectRef("weights", AccessType::READ,
std::make_unique<BufferDescriptor>(std::move(desc)));
std::string sample_code = R"(
__kernel void main_function($0) {
if (a < 3) {
value = args.weights.Read(id);
}
})";
CLArguments cl_args;
GpuInfo gpu_info;
ASSERT_OK(cl_args.Init(gpu_info, nullptr, &args, &sample_code));
EXPECT_TRUE(absl::StrContains(sample_code, "value = weights_buffer[id];"));
EXPECT_TRUE(
absl::StrContains(sample_code, "__global float4* weights_buffer"));
}
TEST(CLArgumentsTest, TestNoSelector) {
BufferDescriptor desc;
desc.element_type = DataType::FLOAT32;
desc.element_size = 4;
desc.memory_type = MemoryType::GLOBAL;
Arguments args;
args.AddObjectRef("weights", AccessType::READ,
std::make_unique<BufferDescriptor>(std::move(desc)));
std::string sample_code = R"(
if (a < 3) {
value = args.weights.UnknownSelector(id);
}
)";
CLArguments cl_args;
GpuInfo gpu_info;
EXPECT_FALSE(cl_args.Init(gpu_info, nullptr, &args, &sample_code).ok());
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,98 @@
/* Copyright 2024 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/cl/cl_command_buffer.h"
#include <utility>
#include <vector>
#include "absl/strings/str_cat.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_command_queue.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_event.h"
#include "tensorflow/lite/delegates/gpu/cl/opencl_wrapper.h"
#include "tensorflow/lite/delegates/gpu/cl/util.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
namespace tflite {
namespace gpu {
namespace cl {
CLCommandBuffer::CLCommandBuffer(CLCommandBuffer&& cb) : cb_(cb.cb_) {
cb.cb_ = nullptr;
}
CLCommandBuffer& CLCommandBuffer::operator=(CLCommandBuffer&& cb) {
if (this != &cb) {
Release();
std::swap(cb_, cb.cb_);
}
return *this;
}
void CLCommandBuffer::Release() {
if (cb_) {
clReleaseCommandBufferKHR(cb_);
cb_ = nullptr;
}
}
absl::Status CLCommandBuffer::Init(CLCommandQueue* queue,
bool simultaneous_use) {
cl_int errcode_ret = CL_SUCCESS;
std::vector<cl_command_buffer_properties_khr> properties;
if (simultaneous_use) {
properties.push_back(CL_COMMAND_BUFFER_FLAGS_KHR);
properties.push_back(CL_COMMAND_BUFFER_SIMULTANEOUS_USE_KHR);
}
properties.push_back(0);
cl_command_buffer_properties_khr* properties_ptr =
properties.size() != 1 ? properties.data() : nullptr;
cl_command_queue cmd_queue = queue->queue();
cb_ = clCreateCommandBufferKHR(1, &cmd_queue, properties_ptr, &errcode_ret);
if (errcode_ret != CL_SUCCESS) {
return absl::InternalError(absl::StrCat("Failed clCreateCommandBufferKHR.",
CLErrorCodeToString(errcode_ret)));
}
return absl::OkStatus();
}
absl::Status CLCommandBuffer::Finalize() {
cl_int errcode_ret = clFinalizeCommandBufferKHR(cb_);
if (errcode_ret != CL_SUCCESS) {
return absl::InternalError(
absl::StrCat("Failed clFinalizeCommandBufferKHR.",
CLErrorCodeToString(errcode_ret)));
}
return absl::OkStatus();
}
absl::Status CLCommandBuffer::Enqueue(CLCommandQueue* queue, CLEvent* event) {
cl_event resulting_event;
cl_command_queue cmd_queue = queue->queue();
cl_int errcode_ret = clEnqueueCommandBufferKHR(
1, &cmd_queue, cb_, 0, nullptr, event ? &resulting_event : nullptr);
if (errcode_ret != CL_SUCCESS) {
return absl::InternalError(absl::StrCat("Failed clEnqueueCommandBufferKHR.",
CLErrorCodeToString(errcode_ret)));
}
if (event) {
*event = CLEvent(resulting_event);
}
return absl::OkStatus();
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,53 @@
/* Copyright 2024 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_COMMAND_BUFFER_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_COMMAND_BUFFER_H_
#include "tensorflow/lite/delegates/gpu/cl/cl_command_queue.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_event.h"
#include "tensorflow/lite/delegates/gpu/cl/opencl_wrapper.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
namespace tflite {
namespace gpu {
namespace cl {
class CLCommandBuffer {
public:
CLCommandBuffer() = default;
// Move only
CLCommandBuffer(CLCommandBuffer&& cb);
CLCommandBuffer& operator=(CLCommandBuffer&& cb);
CLCommandBuffer(const CLCommandBuffer&) = delete;
CLCommandBuffer& operator=(const CLCommandBuffer&) = delete;
~CLCommandBuffer() { Release(); }
absl::Status Init(CLCommandQueue* queue, bool simultaneous_use = false);
absl::Status Finalize();
absl::Status Enqueue(CLCommandQueue* queue, CLEvent* event = nullptr);
cl_command_buffer_khr GetCommandBuffer() const { return cb_; }
private:
void Release();
cl_command_buffer_khr cb_ = nullptr;
};
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_COMMAND_BUFFER_H_
@@ -0,0 +1,385 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/cl/cl_command_queue.h"
#include <array>
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/str_cat.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_device.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_event.h"
#include "tensorflow/lite/delegates/gpu/cl/util.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/types.h"
namespace tflite {
namespace gpu {
namespace cl {
CLCommandQueue::CLCommandQueue(cl_command_queue queue, bool has_ownership)
: queue_(queue), has_ownership_(has_ownership) {}
CLCommandQueue::CLCommandQueue(CLCommandQueue&& queue)
: queue_(queue.queue_), has_ownership_(queue.has_ownership_) {
queue.queue_ = nullptr;
}
CLCommandQueue& CLCommandQueue::operator=(CLCommandQueue&& queue) {
if (this != &queue) {
Release();
std::swap(queue_, queue.queue_);
has_ownership_ = queue.has_ownership_;
}
return *this;
}
CLCommandQueue::~CLCommandQueue() { Release(); }
void CLCommandQueue::Release() {
if (has_ownership_ && queue_) {
clReleaseCommandQueue(queue_);
queue_ = nullptr;
}
}
absl::Status CLCommandQueue::Dispatch(const CLKernel& kernel,
const int3& work_groups_count,
const int3& work_group_size,
CLEvent* event) {
std::array<size_t, 3> local;
std::array<size_t, 3> global;
for (int i = 0; i < 3; ++i) {
local[i] = work_group_size[i];
global[i] = work_groups_count[i] * work_group_size[i];
}
cl_event resulting_event;
const int error_code = clEnqueueNDRangeKernel(
queue_, kernel.kernel(), 3, nullptr, global.data(), local.data(), 0,
nullptr, event ? &resulting_event : nullptr);
if (event) {
*event = CLEvent(resulting_event);
}
if (error_code != CL_SUCCESS) {
return absl::UnknownError(
absl::StrCat("Failed to clEnqueueNDRangeKernel - ",
CLErrorCodeToString(error_code)));
}
return absl::OkStatus();
}
absl::Status CLCommandQueue::Dispatch(const CLKernel& kernel,
const int3& work_groups_count,
const int3& work_group_size) {
return Dispatch(kernel, work_groups_count, work_group_size, nullptr);
}
absl::Status CLCommandQueue::EnqueueEvent(CLEvent* event) {
cl_event resulting_event;
const int error_code = clEnqueueMarker(queue_, &resulting_event);
*event = CLEvent(resulting_event);
if (error_code != CL_SUCCESS) {
return absl::UnknownError(absl::StrCat("Failed to clEnqueueMarker - ",
CLErrorCodeToString(error_code)));
}
return absl::OkStatus();
}
absl::Status CLCommandQueue::EnqueueWriteImage(cl_mem memory, int3 region,
const void* data, bool async) {
const size_t origin[] = {0, 0, 0};
const size_t r[] = {static_cast<size_t>(region.x),
static_cast<size_t>(region.y),
static_cast<size_t>(region.z)};
const cl_bool blocking = async ? CL_FALSE : CL_TRUE;
auto error_code = clEnqueueWriteImage(queue_, memory, blocking, origin, r, 0,
0, data, 0, nullptr, nullptr);
if (error_code != CL_SUCCESS) {
return absl::UnknownError(
absl::StrCat("Failed to upload data to GPU (clEnqueueWriteImage) - ",
CLErrorCodeToString(error_code)));
}
return absl::OkStatus();
}
absl::Status CLCommandQueue::EnqueueReadImage(cl_mem memory, int3 region,
void* data, bool async) {
const size_t origin[] = {0, 0, 0};
const size_t r[] = {static_cast<size_t>(region.x),
static_cast<size_t>(region.y),
static_cast<size_t>(region.z)};
const cl_bool blocking = async ? CL_FALSE : CL_TRUE;
auto error_code = clEnqueueReadImage(queue_, memory, blocking, origin, r, 0,
0, data, 0, nullptr, nullptr);
if (error_code != CL_SUCCESS) {
return absl::UnknownError(
absl::StrCat("Failed to read data from GPU (clEnqueueReadImage) - ",
CLErrorCodeToString(error_code)));
}
return absl::OkStatus();
}
absl::Status CLCommandQueue::EnqueueWriteBuffer(cl_mem memory,
size_t size_in_bytes,
const void* data, bool async) {
const cl_bool blocking = async ? CL_FALSE : CL_TRUE;
auto error_code = clEnqueueWriteBuffer(
queue_, memory, blocking, 0, size_in_bytes, data, 0, nullptr, nullptr);
if (error_code != CL_SUCCESS) {
return absl::UnknownError(
absl::StrCat("Failed to upload data to GPU (clEnqueueWriteBuffer) - ",
CLErrorCodeToString(error_code)));
}
return absl::OkStatus();
}
absl::Status CLCommandQueue::EnqueueReadBuffer(cl_mem memory,
size_t size_in_bytes, void* data,
bool async) {
const cl_bool blocking = async ? CL_FALSE : CL_TRUE;
auto error_code = clEnqueueReadBuffer(
queue_, memory, blocking, 0, size_in_bytes, data, 0, nullptr, nullptr);
if (error_code != CL_SUCCESS) {
return absl::UnknownError(
absl::StrCat("Failed to read data from GPU (clEnqueueReadBuffer) - ",
CLErrorCodeToString(error_code)));
}
return absl::OkStatus();
}
absl::Status CLCommandQueue::WaitForCompletion() {
auto error_code = clFinish(queue_);
if (error_code != CL_SUCCESS) {
return absl::UnknownError(
absl::StrCat("Failed to clFinish - ", CLErrorCodeToString(error_code)));
}
return absl::OkStatus();
}
ProfilingCommandQueue::ProfilingCommandQueue(cl_command_queue queue)
: CLCommandQueue(queue, true) {
events_.reserve(128);
}
ProfilingCommandQueue::ProfilingCommandQueue(ProfilingCommandQueue&& queue)
: CLCommandQueue(std::move(queue)),
events_(std::move(queue.events_)),
number_of_dispatches_(std::move(queue.number_of_dispatches_)),
current_label_(std::move(queue.current_label_)) {}
ProfilingCommandQueue& ProfilingCommandQueue::operator=(
ProfilingCommandQueue&& queue) {
if (this != &queue) {
events_ = std::move(queue.events_);
number_of_dispatches_ = std::move(queue.number_of_dispatches_);
current_label_ = std::move(queue.current_label_);
CLCommandQueue::operator=(std::move(queue));
}
return *this;
}
void ProfilingCommandQueue::SetEventsLabel(const std::string& name) {
current_label_ = name;
}
void ProfilingCommandQueue::ResetMeasurements() {
events_.clear();
number_of_dispatches_.clear();
}
absl::Status ProfilingCommandQueue::Dispatch(const CLKernel& kernel,
const int3& work_groups_count,
const int3& work_group_size) {
events_.push_back(CLEvent());
number_of_dispatches_.push_back(1);
RETURN_IF_ERROR(CLCommandQueue::Dispatch(kernel, work_groups_count,
work_group_size,
&events_[events_.size() - 1]));
events_.back().SetName(current_label_);
return absl::OkStatus();
}
absl::Status ProfilingCommandQueue::DispatchNTimes(
const CLKernel& kernel, const int3& work_groups_count,
const int3& work_group_size, int n, int flush_period) {
number_of_dispatches_.push_back(n);
if (n == 1) {
events_.push_back(CLEvent());
RETURN_IF_ERROR(CLCommandQueue::Dispatch(kernel, work_groups_count,
work_group_size,
&events_[events_.size() - 1]));
events_.back().SetName(current_label_);
} else {
events_.push_back(CLEvent());
events_.push_back(CLEvent());
RETURN_IF_ERROR(CLCommandQueue::Dispatch(kernel, work_groups_count,
work_group_size,
&events_[events_.size() - 2]));
for (int i = 1; i < n - 1; ++i) {
RETURN_IF_ERROR(
CLCommandQueue::Dispatch(kernel, work_groups_count, work_group_size));
if (flush_period && i % flush_period == 0) {
clFlush(queue_);
}
}
RETURN_IF_ERROR(CLCommandQueue::Dispatch(kernel, work_groups_count,
work_group_size,
&events_[events_.size() - 1]));
clFlush(queue_);
events_[events_.size() - 2].SetName(current_label_);
events_[events_.size() - 1].SetName(current_label_);
}
return absl::OkStatus();
}
ProfilingInfo ProfilingCommandQueue::GetProfilingInfo() const {
ProfilingInfo result;
result.dispatches.resize(number_of_dispatches_.size());
int events_counter = 0;
for (int i = 0; i < number_of_dispatches_.size(); ++i) {
result.dispatches[i].label = events_[events_counter].GetName();
if (number_of_dispatches_[i] == 1) {
result.dispatches[i].duration =
absl::Nanoseconds(events_[events_counter].GetEventTimeNs());
events_counter += 1;
} else {
result.dispatches[i].duration =
absl::Nanoseconds(events_[events_counter + 1].GetFinishedTimeNs() -
events_[events_counter].GetStartedTimeNs()) /
number_of_dispatches_[i];
events_counter += 2;
}
}
return result;
}
absl::Status ProfilingCommandQueue::GetBestWorkGroupIndex(
const CLKernel& kernel, const GpuInfo& gpu_info,
const std::vector<int3>& work_groups_count,
const std::vector<int3>& work_group_sizes, int* index) {
// Some Adreno 3xx can have wrong numbers for some events
const bool possible_bug_with_events =
gpu_info.IsAdreno() && gpu_info.adreno_info.IsAdreno3xx();
events_.resize(work_group_sizes.size());
for (int i = 0; i < work_group_sizes.size(); ++i) {
RETURN_IF_ERROR(CLCommandQueue::Dispatch(kernel, work_groups_count[i],
work_group_sizes[i], &events_[i]));
// reducing the speed of memory leak on Mali for some kernels
if (gpu_info.IsMali() && i % 8 == 7) {
events_[i - 7].Wait();
}
if (possible_bug_with_events) {
// We are trying to increase probability for correct result.
RETURN_IF_ERROR(WaitForCompletion());
}
}
RETURN_IF_ERROR(WaitForCompletion());
// To release memory of some kernel pool on Mali.
if (gpu_info.IsMali()) {
RETURN_IF_ERROR(kernel.ReInit());
}
int minimum_index = 0;
double minimum_time = std::numeric_limits<double>::max();
if (possible_bug_with_events) { // we will try to cut out suspicious results
double average_time = 0.0;
int average_samples_count = 0;
for (int i = 0; i < work_group_sizes.size(); ++i) {
if (events_[i].GetEventTimeMs() < 100 * 1000) { // 100 sec
average_time += events_[i].GetEventTimeMs();
average_samples_count++;
}
}
average_time /= average_samples_count;
for (int i = 0; i < work_group_sizes.size(); ++i) {
double time = events_[i].GetEventTimeMs();
if (time < minimum_time && time >= 0.1 * average_time) {
minimum_index = i;
minimum_time = time;
}
}
} else {
for (int i = 0; i < work_group_sizes.size(); ++i) {
double time = events_[i].GetEventTimeMs();
if (time < minimum_time) {
minimum_index = i;
minimum_time = time;
}
}
}
*index = minimum_index;
return absl::OkStatus();
}
absl::Status CreateCLCommandQueue(const CLDevice& device,
const CLContext& context,
CLCommandQueue* result) {
int error_code;
cl_command_queue queue =
clCreateCommandQueue(context.context(), device.id(), 0, &error_code);
if (!queue) {
return absl::UnknownError(
absl::StrCat("Failed to create a command queue - ",
CLErrorCodeToString(error_code)));
}
*result = CLCommandQueue(queue, true);
return absl::OkStatus();
}
double ProfilingCommandQueue::GetQueueExecutionTimeMs() const {
const uint64_t start = events_.front().GetStartedTimeNs();
const uint64_t end = events_.back().GetFinishedTimeNs();
const uint64_t time_ns = (end - start);
return static_cast<double>(time_ns) / 1000000.0;
}
double ProfilingCommandQueue::GetSumOfEventsTimeMs() const {
double sum = 0.0;
for (int i = 0; i < events_.size(); ++i) {
sum += events_[i].GetEventTimeMs();
}
return sum;
}
absl::Status CreateProfilingCommandQueue(const CLDevice& device,
const CLContext& context,
ProfilingCommandQueue* result) {
int error_code;
cl_command_queue queue = clCreateCommandQueue(
context.context(), device.id(), CL_QUEUE_PROFILING_ENABLE, &error_code);
if (!queue) {
return absl::UnknownError(
absl::StrCat("Failed to create a command queue - ",
CLErrorCodeToString(error_code)));
}
*result = ProfilingCommandQueue(queue);
return absl::OkStatus();
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,141 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_COMMAND_QUEUE_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_COMMAND_QUEUE_H_
#include <cstdint>
#include <string>
#include <vector>
#include "tensorflow/lite/delegates/gpu/cl/cl_context.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_device.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_event.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_kernel.h"
#include "tensorflow/lite/delegates/gpu/cl/opencl_wrapper.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/task/profiling_info.h"
#include "tensorflow/lite/delegates/gpu/common/types.h"
namespace tflite {
namespace gpu {
namespace cl {
// A wrapper around opencl command queue
class CLCommandQueue {
public:
CLCommandQueue() {}
CLCommandQueue(cl_command_queue queue, bool has_ownership);
// Move only
CLCommandQueue(CLCommandQueue&& queue);
CLCommandQueue& operator=(CLCommandQueue&& queue);
CLCommandQueue(const CLCommandQueue&) = delete;
CLCommandQueue& operator=(const CLCommandQueue&) = delete;
virtual ~CLCommandQueue();
cl_command_queue queue() const { return queue_; }
virtual absl::Status Dispatch(const CLKernel& kernel,
const int3& work_groups_count,
const int3& work_group_size);
absl::Status Dispatch(const CLKernel& kernel, const int3& work_groups_count,
const int3& work_group_size, CLEvent* event);
absl::Status EnqueueEvent(CLEvent* event);
absl::Status EnqueueWriteImage(cl_mem memory, int3 region, const void* data,
bool async = false);
absl::Status EnqueueReadImage(cl_mem memory, int3 region, void* data,
bool async = false);
absl::Status EnqueueWriteBuffer(cl_mem memory, size_t size_in_bytes,
const void* data, bool async = false);
absl::Status EnqueueReadBuffer(cl_mem memory, size_t size_in_bytes,
void* data, bool async = false);
absl::Status WaitForCompletion();
protected:
void Release();
cl_command_queue queue_ = nullptr;
bool has_ownership_ = false;
};
class ProfilingCommandQueue : public CLCommandQueue {
public:
ProfilingCommandQueue() {}
explicit ProfilingCommandQueue(cl_command_queue queue);
// Move only
ProfilingCommandQueue(ProfilingCommandQueue&& queue);
ProfilingCommandQueue& operator=(ProfilingCommandQueue&& queue);
ProfilingCommandQueue(const ProfilingCommandQueue&) = delete;
ProfilingCommandQueue& operator=(const ProfilingCommandQueue&) = delete;
absl::Status Dispatch(const CLKernel& kernel, const int3& work_groups_count,
const int3& work_group_size) override;
// for better profiling
absl::Status DispatchNTimes(const CLKernel& kernel,
const int3& work_groups_count,
const int3& work_group_size, int n,
int flush_period = 0);
// will write index for fastest work_group among work_group_sizes
absl::Status GetBestWorkGroupIndex(const CLKernel& kernel,
const GpuInfo& gpu_info,
const std::vector<int3>& work_groups_count,
const std::vector<int3>& work_group_sizes,
int* index);
// call ResetMeasurements() to start new seriese of measurements
void ResetMeasurements();
double GetQueueExecutionTimeMs() const;
// Difference from GetQueueExecutionTimeMs is that this number doesn't include
// time between kernels(kernels launches or preparing) on GPU. Usually, this
// time should be 5-10% better than GetQueueExecutionTimeMs, because 5-10%
// spend on something else(maybe kernels launches or preparing)
double GetSumOfEventsTimeMs() const;
// This label will be used for all subsequent dispatches.
void SetEventsLabel(const std::string& name);
ProfilingInfo GetProfilingInfo() const;
private:
std::vector<CLEvent> events_;
std::vector<int> number_of_dispatches_;
std::string current_label_;
};
absl::Status CreateCLCommandQueue(const CLDevice& device,
const CLContext& context,
CLCommandQueue* result);
absl::Status CreateProfilingCommandQueue(const CLDevice& device,
const CLContext& context,
ProfilingCommandQueue* result);
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_COMMAND_QUEUE_H_
@@ -0,0 +1,163 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/cl/cl_context.h"
#include "absl/strings/str_cat.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_image_format.h"
#include "tensorflow/lite/delegates/gpu/cl/util.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
std::vector<cl_image_format> GetSupportedImage2DFormats(cl_context context,
cl_mem_flags flags) {
cl_uint num_image_formats;
cl_int error = clGetSupportedImageFormats(
context, flags, CL_MEM_OBJECT_IMAGE2D, 0, nullptr, &num_image_formats);
if (error != CL_SUCCESS) {
return {};
}
std::vector<cl_image_format> result(num_image_formats);
error = clGetSupportedImageFormats(context, flags, CL_MEM_OBJECT_IMAGE2D,
num_image_formats, &result[0], nullptr);
if (error != CL_SUCCESS) {
return {};
}
return result;
}
bool IsEqualToImageFormat(cl_image_format image_format, DataType data_type,
int num_channels) {
return image_format.image_channel_data_type ==
DataTypeToChannelType(data_type) &&
image_format.image_channel_order == ToChannelOrder(num_channels);
}
void AddSupportedImageFormats(cl_context context, GpuInfo* info) {
auto supported_formats =
GetSupportedImage2DFormats(context, CL_MEM_READ_WRITE);
const std::vector<DataType> kPossibleDataTypes = {
DataType::FLOAT16, DataType::FLOAT32, DataType::INT8, DataType::UINT8,
DataType::INT16, DataType::UINT16, DataType::INT32, DataType::UINT32};
for (auto format : supported_formats) {
for (auto data_type : kPossibleDataTypes) {
if (IsEqualToImageFormat(format, data_type, 1)) {
info->opencl_info.supported_images_2d.r_layout.insert(data_type);
} else if (IsEqualToImageFormat(format, data_type, 2)) {
info->opencl_info.supported_images_2d.rg_layout.insert(data_type);
} else if (IsEqualToImageFormat(format, data_type, 3)) {
info->opencl_info.supported_images_2d.rgb_layout.insert(data_type);
} else if (IsEqualToImageFormat(format, data_type, 4)) {
info->opencl_info.supported_images_2d.rgba_layout.insert(data_type);
}
}
}
}
absl::Status CreateCLContext(const CLDevice& device,
cl_context_properties* properties,
CLContext* result) {
int error_code;
cl_device_id device_id = device.id();
cl_context context =
clCreateContext(properties, 1, &device_id, nullptr, nullptr, &error_code);
if (!context) {
return absl::UnknownError(
absl::StrCat("Failed to create a compute context - ",
CLErrorCodeToString(error_code)));
}
AddSupportedImageFormats(context, &device.info_);
*result = CLContext(context, true);
return absl::OkStatus();
}
} // namespace
CLContext::CLContext(cl_context context, bool has_ownership)
: context_(context), has_ownership_(has_ownership) {}
CLContext::CLContext(cl_context context, bool has_ownership, CLDevice& device)
: context_(context), has_ownership_(has_ownership) {
AddSupportedImageFormats(context, &device.info_);
}
CLContext::CLContext(CLContext&& context)
: context_(context.context_), has_ownership_(context.has_ownership_) {
context.context_ = nullptr;
}
CLContext& CLContext::operator=(CLContext&& context) {
if (this != &context) {
Release();
std::swap(context_, context.context_);
has_ownership_ = context.has_ownership_;
}
return *this;
}
CLContext::~CLContext() { Release(); }
void CLContext::Release() {
if (has_ownership_ && context_) {
clReleaseContext(context_);
context_ = nullptr;
}
}
bool CLContext::IsFloatTexture2DSupported(int num_channels, DataType data_type,
cl_mem_flags flags) const {
auto supported_formats = GetSupportedImage2DFormats(context_, flags);
for (auto format : supported_formats) {
if (format.image_channel_data_type == DataTypeToChannelType(data_type) &&
format.image_channel_order == ToChannelOrder(num_channels)) {
return true;
}
}
return false;
}
absl::Status CreateCLContext(const CLDevice& device, CLContext* result) {
return CreateCLContext(device, nullptr, result);
}
absl::Status CreateCLGLContext(const CLDevice& device,
cl_context_properties egl_context,
cl_context_properties egl_display,
CLContext* result) {
if (!device.GetInfo().SupportsExtension("cl_khr_gl_sharing")) {
return absl::UnavailableError("Device doesn't support CL-GL sharing.");
}
cl_context_properties platform =
reinterpret_cast<cl_context_properties>(device.platform());
cl_context_properties props[] = {CL_GL_CONTEXT_KHR,
egl_context,
CL_EGL_DISPLAY_KHR,
egl_display,
CL_CONTEXT_PLATFORM,
platform,
0};
return CreateCLContext(device, props, result);
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,64 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_CONTEXT_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_CONTEXT_H_
#include "tensorflow/lite/delegates/gpu/cl/cl_device.h"
#include "tensorflow/lite/delegates/gpu/cl/opencl_wrapper.h"
#include "tensorflow/lite/delegates/gpu/common/data_type.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
namespace tflite {
namespace gpu {
namespace cl {
// A RAII wrapper around opencl context
class CLContext {
public:
CLContext() {}
CLContext(cl_context context, bool has_ownership);
CLContext(cl_context context, bool has_ownership, CLDevice& device);
// Move only
CLContext(CLContext&& context);
CLContext& operator=(CLContext&& context);
CLContext(const CLContext&) = delete;
CLContext& operator=(const CLContext&) = delete;
~CLContext();
cl_context context() const { return context_; }
bool IsFloatTexture2DSupported(int num_channels, DataType data_type,
cl_mem_flags flags = CL_MEM_READ_WRITE) const;
private:
void Release();
cl_context context_ = nullptr;
bool has_ownership_ = false;
};
absl::Status CreateCLContext(const CLDevice& device, CLContext* result);
absl::Status CreateCLGLContext(const CLDevice& device,
cl_context_properties egl_context,
cl_context_properties egl_display,
CLContext* result);
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_CONTEXT_H_
@@ -0,0 +1,503 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/cl/cl_device.h"
#include <algorithm>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/ascii.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_split.h"
#include "tensorflow/lite/delegates/gpu/cl/opencl_wrapper.h"
#include "tensorflow/lite/delegates/gpu/cl/util.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/experimental/acceleration/compatibility/android_info.h"
namespace tflite {
namespace gpu {
namespace cl {
void ParseQualcommOpenClCompilerVersion(
const std::string& cl_driver_version,
AdrenoInfo::OpenClCompilerVersion* result) {
// Searching this part: "Compiler E031.**.**.**" where * is digit
const std::string start = "Compiler E031.";
size_t position = cl_driver_version.find(start);
if (position == std::string::npos) {
return;
}
const size_t main_part_length = 8; // main part is **.**.**
if (position + start.length() + main_part_length >
cl_driver_version.length()) {
return;
}
const std::string main_part =
cl_driver_version.substr(position + start.length(), main_part_length);
if (!absl::ascii_isdigit(main_part[0]) ||
!absl::ascii_isdigit(main_part[1]) || main_part[2] != '.' ||
!absl::ascii_isdigit(main_part[3]) ||
!absl::ascii_isdigit(main_part[4]) || main_part[5] != '.' ||
!absl::ascii_isdigit(main_part[6]) ||
!absl::ascii_isdigit(main_part[7])) {
return;
}
result->major = (main_part[0] - '0') * 10 + (main_part[1] - '0');
result->minor = (main_part[3] - '0') * 10 + (main_part[4] - '0');
result->patch = (main_part[6] - '0') * 10 + (main_part[7] - '0');
}
static void ParsePowerVRDriverVersion(const std::string& cl_driver_version,
PowerVRInfo::DriverVersion& result) {
size_t position = cl_driver_version.find('@');
if (position == std::string::npos) {
return;
}
// string format: "*.**@*******" where * is digit
int main = 0;
size_t curpos = 0;
while (curpos < position && absl::ascii_isdigit(cl_driver_version[curpos])) {
main = main * 10 + cl_driver_version[curpos] - '0';
++curpos;
}
++curpos;
int minor = 0;
while (curpos < position) {
minor = minor * 10 + cl_driver_version[curpos] - '0';
++curpos;
}
curpos = position + 1;
int id = 0;
while (curpos < cl_driver_version.length()) {
id = id * 10 + cl_driver_version[curpos] - '0';
++curpos;
}
result.branch_main = main;
result.branch_minor = minor;
result.id = id;
}
template <>
std::string GetDeviceInfo<std::string>(cl_device_id id, cl_device_info info) {
size_t size;
cl_int error = clGetDeviceInfo(id, info, 0, nullptr, &size);
if (error != CL_SUCCESS) {
return "";
}
std::string result(size - 1, 0);
error = clGetDeviceInfo(id, info, size, &result[0], nullptr);
if (error != CL_SUCCESS) {
return "";
}
return result;
}
namespace {
template <typename T>
T GetPlatformInfo(cl_platform_id id, cl_platform_info info) {
T result;
cl_int error = clGetPlatformInfo(id, info, sizeof(T), &result, nullptr);
if (error != CL_SUCCESS) {
return -1;
}
return result;
}
std::string GetPlatformInfo(cl_platform_id id, cl_platform_info info) {
size_t size;
cl_int error = clGetPlatformInfo(id, info, 0, nullptr, &size);
if (error != CL_SUCCESS) {
return "";
}
std::string result(size - 1, 0);
error = clGetPlatformInfo(id, info, size, &result[0], nullptr);
if (error != CL_SUCCESS) {
return "";
}
return result;
}
void GetDeviceWorkDimsSizes(cl_device_id id, int3* result) {
int dims_count =
GetDeviceInfo<cl_uint>(id, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS);
if (dims_count < 3) {
return;
}
std::vector<size_t> limits(dims_count);
cl_int error =
clGetDeviceInfo(id, CL_DEVICE_MAX_WORK_ITEM_SIZES,
sizeof(size_t) * dims_count, limits.data(), nullptr);
if (error != CL_SUCCESS) {
return;
}
// dims_count must be at least 3 according to spec
result->x = limits[0];
result->y = limits[1];
result->z = limits[2];
}
OpenClVersion ParseCLVersion(const std::string& version) {
const auto first_dot_pos = version.find_first_of('.');
if (first_dot_pos == std::string::npos) {
return OpenClVersion::kCl1_0;
}
const int major = version[first_dot_pos - 1] - '0';
const int minor = version[first_dot_pos + 1] - '0';
if (major == 1) {
if (minor == 2) {
return OpenClVersion::kCl1_2;
} else if (minor == 1) {
return OpenClVersion::kCl1_1;
} else {
return OpenClVersion::kCl1_0;
}
} else if (major == 2) {
if (minor == 2) {
return OpenClVersion::kCl2_2;
} else if (minor == 1) {
return OpenClVersion::kCl2_1;
} else {
return OpenClVersion::kCl2_0;
}
} else if (major == 3) {
return OpenClVersion::kCl3_0;
} else {
return OpenClVersion::kCl1_0;
}
}
// check that gpu_version belong to range min_version-max_version
// min_version is included and max_version is excluded.
bool IsGPUVersionInRange(int gpu_version, int min_version, int max_version) {
return gpu_version >= min_version && gpu_version < max_version;
}
GpuInfo GpuInfoFromDeviceID(cl_device_id id, cl_platform_id platform_id) {
GpuInfo info;
info.opencl_info.platform_version =
GetPlatformInfo(platform_id, CL_PLATFORM_VERSION);
info.opencl_info.device_name = GetDeviceInfo<std::string>(id, CL_DEVICE_NAME);
info.opencl_info.vendor_name =
GetDeviceInfo<std::string>(id, CL_DEVICE_VENDOR);
info.opencl_info.opencl_c_version =
GetDeviceInfo<std::string>(id, CL_DEVICE_OPENCL_C_VERSION);
info.opencl_info.driver_version =
GetDeviceInfo<std::string>(id, CL_DRIVER_VERSION);
const std::string gpu_description = absl::StrCat(
info.opencl_info.device_name, " ", info.opencl_info.vendor_name, " ",
info.opencl_info.opencl_c_version);
GetGpuInfoFromDeviceDescription(gpu_description, GpuApi::kOpenCl, &info);
info.opencl_info.cl_version =
ParseCLVersion(info.opencl_info.opencl_c_version);
info.opencl_info.extensions =
absl::StrSplit(GetDeviceInfo<std::string>(id, CL_DEVICE_EXTENSIONS), ' ');
const std::vector<std::string> unsupported_extensions =
GetUnsupportedExtensions();
for (const auto& unsupported_extension : unsupported_extensions) {
for (auto it = info.opencl_info.extensions.begin();
it != info.opencl_info.extensions.end();) {
if (*it == unsupported_extension) {
it = info.opencl_info.extensions.erase(it);
} else {
++it;
}
}
}
info.opencl_info.supports_fp16 = false;
info.opencl_info.supports_image3d_writes = false;
for (const auto& ext : info.opencl_info.extensions) {
if (ext == "cl_khr_fp16") {
info.opencl_info.supports_fp16 = true;
}
if (ext == "cl_khr_3d_image_writes") {
info.opencl_info.supports_image3d_writes = true;
}
}
info.opencl_info.supports_images =
GetDeviceInfo<cl_bool>(id, CL_DEVICE_IMAGE_SUPPORT);
cl_device_fp_config f32_config =
GetDeviceInfo<cl_device_fp_config>(id, CL_DEVICE_SINGLE_FP_CONFIG);
info.opencl_info.supports_fp32_rtn = f32_config & CL_FP_ROUND_TO_NEAREST;
if (info.opencl_info.supports_fp16) {
cl_device_fp_config f16_config;
auto status = GetDeviceInfo<cl_device_fp_config>(
id, CL_DEVICE_HALF_FP_CONFIG, &f16_config);
// AMD supports cl_khr_fp16 but CL_DEVICE_HALF_FP_CONFIG is empty.
if (status.ok() && !info.IsAMD()) {
info.opencl_info.supports_fp16_rtn = f16_config & CL_FP_ROUND_TO_NEAREST;
} else { // happens on PowerVR
f16_config = f32_config;
info.opencl_info.supports_fp16_rtn = info.opencl_info.supports_fp32_rtn;
}
} else {
info.opencl_info.supports_fp16_rtn = false;
}
if (info.IsPowerVR()) {
if (!info.powervr_info.IsBetterThan(PowerVRGpu::kRogueGm9xxx)) {
// Some GPU older than RogueGe8xxx has accuracy issue with FP16.
info.opencl_info.supports_fp16 = false;
} else if (!info.opencl_info.supports_fp16) {
// PowerVR doesn't have full support of fp16 and so doesn't list this
// extension. But it can support fp16 in MADs and as buffers/textures
// types, so we will use it.
info.opencl_info.supports_fp16 = true;
info.opencl_info.supports_fp16_rtn = info.opencl_info.supports_fp32_rtn;
}
}
if (!info.opencl_info.supports_image3d_writes &&
((info.IsAdreno() && info.adreno_info.IsAdreno4xx()) ||
info.IsNvidia())) {
// in local tests Adreno 430 can write in image 3d, at least on small sizes,
// but it doesn't have cl_khr_3d_image_writes in list of available
// extensions
// The same for NVidia
info.opencl_info.supports_image3d_writes = true;
}
info.opencl_info.compute_units_count =
GetDeviceInfo<cl_uint>(id, CL_DEVICE_MAX_COMPUTE_UNITS);
info.opencl_info.image2d_max_width =
GetDeviceInfo<size_t>(id, CL_DEVICE_IMAGE2D_MAX_WIDTH);
info.opencl_info.image2d_max_height =
GetDeviceInfo<size_t>(id, CL_DEVICE_IMAGE2D_MAX_HEIGHT);
info.opencl_info.buffer_max_size =
GetDeviceInfo<cl_ulong>(id, CL_DEVICE_MAX_MEM_ALLOC_SIZE);
info.opencl_info.max_allocation_size =
GetDeviceInfo<cl_ulong>(id, CL_DEVICE_MAX_MEM_ALLOC_SIZE);
if (info.opencl_info.cl_version >= OpenClVersion::kCl1_2) {
info.opencl_info.image_buffer_max_size =
GetDeviceInfo<size_t>(id, CL_DEVICE_IMAGE_MAX_BUFFER_SIZE);
info.opencl_info.image_array_max_layers =
GetDeviceInfo<size_t>(id, CL_DEVICE_IMAGE_MAX_ARRAY_SIZE);
}
info.opencl_info.image3d_max_width =
GetDeviceInfo<size_t>(id, CL_DEVICE_IMAGE3D_MAX_WIDTH);
info.opencl_info.image3d_max_height =
GetDeviceInfo<size_t>(id, CL_DEVICE_IMAGE2D_MAX_HEIGHT);
info.opencl_info.image3d_max_depth =
GetDeviceInfo<size_t>(id, CL_DEVICE_IMAGE3D_MAX_DEPTH);
int3 max_work_group_sizes;
GetDeviceWorkDimsSizes(id, &max_work_group_sizes);
info.opencl_info.max_work_group_size_x = max_work_group_sizes.x;
info.opencl_info.max_work_group_size_y = max_work_group_sizes.y;
info.opencl_info.max_work_group_size_z = max_work_group_sizes.z;
info.opencl_info.max_work_group_total_size =
GetDeviceInfo<size_t>(id, CL_DEVICE_MAX_WORK_GROUP_SIZE);
info.opencl_info.dedicated_local_memory =
(GetDeviceInfo<cl_device_local_mem_type>(id, CL_DEVICE_LOCAL_MEM_TYPE) ==
CL_LOCAL);
if (info.IsCL30OrHigher()) {
info.opencl_info.preferred_work_group_size_multiple =
GetDeviceInfo<size_t>(id, CL_DEVICE_PREFERRED_WORK_GROUP_SIZE_MULTIPLE);
} else {
info.opencl_info.preferred_work_group_size_multiple = 0;
}
info.opencl_info.base_addr_align_in_bits =
GetDeviceInfo<cl_uint>(id, CL_DEVICE_MEM_BASE_ADDR_ALIGN);
info.opencl_info.image_pitch_alignment = 0;
if (info.opencl_info.cl_version == OpenClVersion::kCl2_0 ||
info.opencl_info.cl_version == OpenClVersion::kCl2_1 ||
info.opencl_info.cl_version == OpenClVersion::kCl2_2) {
info.opencl_info.image_pitch_alignment =
GetDeviceInfo<cl_uint>(id, CL_DEVICE_IMAGE_PITCH_ALIGNMENT);
info.opencl_info.image_base_address_alignment =
GetDeviceInfo<cl_uint>(id, CL_DEVICE_IMAGE_BASE_ADDRESS_ALIGNMENT);
} else if (info.SupportsExtension("cl_khr_image2d_from_buffer")) {
cl_uint result = 0;
auto status =
GetDeviceInfo(id, CL_DEVICE_IMAGE_PITCH_ALIGNMENT_KHR, &result);
if (status.ok()) {
info.opencl_info.image_pitch_alignment = result;
}
result = 0;
status =
GetDeviceInfo(id, CL_DEVICE_IMAGE_BASE_ADDRESS_ALIGNMENT_KHR, &result);
if (status.ok()) {
info.opencl_info.image_base_address_alignment = result;
}
}
if (info.SupportsExtension("cl_arm_scheduling_controls")) {
auto capabilities =
GetDeviceInfo<cl_device_scheduling_controls_capabilities_arm>(
id, CL_DEVICE_SCHEDULING_CONTROLS_CAPABILITIES_ARM);
info.opencl_info.supports_register_allocation_arm =
capabilities & CL_DEVICE_SCHEDULING_REGISTER_ALLOCATION_ARM;
}
if (info.SupportsExtension("cl_intel_required_subgroup_size")) {
size_t sub_groups_ret_size;
cl_int status =
clGetDeviceInfo(id, 0x4108 /*CL_DEVICE_SUB_GROUP_SIZES_INTEL*/, 0,
nullptr, &sub_groups_ret_size);
if (status == CL_SUCCESS) {
size_t sub_groups_count = sub_groups_ret_size / sizeof(size_t);
std::vector<size_t> sub_group_sizes(sub_groups_count);
status =
clGetDeviceInfo(id, 0x4108 /*CL_DEVICE_SUB_GROUP_SIZES_INTEL*/,
sub_groups_ret_size, sub_group_sizes.data(), nullptr);
if (status == CL_SUCCESS) {
for (int i = 0; i < sub_groups_count; ++i) {
info.supported_subgroup_sizes.push_back(sub_group_sizes[i]);
}
}
}
}
if (info.IsAdreno()) {
ParseQualcommOpenClCompilerVersion(info.opencl_info.driver_version,
&info.adreno_info.cl_compiler_version);
} else if (info.IsPowerVR()) {
ParsePowerVRDriverVersion(info.opencl_info.driver_version,
info.powervr_info.driver_version);
}
return info;
}
} // namespace
CLDevice::CLDevice(cl_device_id id, cl_platform_id platform_id)
: info_(GpuInfoFromDeviceID(id, platform_id)),
id_(id),
platform_id_(platform_id) {
if (info_.IsAdreno() &&
info_.adreno_info.adreno_gpu == AdrenoGpu::kAdreno630) {
acceleration::AndroidInfo android_info;
if (acceleration::RequestAndroidInfo(&android_info).ok()) {
info_.adreno_info.compiler_bugs_in_a6xx =
android_info.android_sdk_version == "26";
}
}
}
CLDevice::CLDevice(const CLDevice& device)
: info_(device.info_), id_(device.id_), platform_id_(device.platform_id_) {}
CLDevice& CLDevice::operator=(const CLDevice& device) {
if (this != &device) {
info_ = device.info_;
id_ = device.id_;
platform_id_ = device.platform_id_;
}
return *this;
}
CLDevice::CLDevice(CLDevice&& device)
: info_(std::move(device.info_)),
id_(device.id_),
platform_id_(device.platform_id_) {
device.id_ = nullptr;
device.platform_id_ = nullptr;
}
CLDevice& CLDevice::operator=(CLDevice&& device) {
if (this != &device) {
id_ = nullptr;
platform_id_ = nullptr;
info_ = std::move(device.info_);
std::swap(id_, device.id_);
std::swap(platform_id_, device.platform_id_);
}
return *this;
}
std::string CLDevice::GetPlatformVersion() const {
return GetPlatformInfo(platform_id_, CL_PLATFORM_VERSION);
}
void CLDevice::DisableOneLayerTextureArray() {
info_.adreno_info.support_one_layer_texture_array = false;
}
absl::Status CreateDefaultGPUDevice(CLDevice* result) {
// Get num. platforms
cl_uint num_platforms;
cl_int status = clGetPlatformIDs(0, nullptr, &num_platforms);
if (status != CL_SUCCESS) {
return absl::UnknownError(
absl::StrFormat("clGetPlatformIDs returned %d", status));
}
if (num_platforms == 0) {
return absl::UnknownError("No supported OpenCL platform.");
}
// Get platforms
std::vector<cl_platform_id> platforms(num_platforms);
status = clGetPlatformIDs(num_platforms, platforms.data(), nullptr);
if (status != CL_SUCCESS) {
return absl::UnknownError(
absl::StrFormat("clGetPlatformIDs returned %d", status));
}
// Select fastest indicated GPU
cl_uint perf_indicator_fastest = 0; // Performance indicator of fastest GPU
cl_device_id device_id_fastest; // ID of fastest indicated GPU
cl_platform_id
platform_id_fastest; // ID of platform with fastest indicated GPU
for (cl_uint p = 0; p < num_platforms; ++p) {
// Get GPUs of platform
cl_uint num_devices;
status = clGetDeviceIDs(platforms[p], CL_DEVICE_TYPE_GPU, 0, nullptr,
&num_devices); // Get num. GPUs
if (status != CL_SUCCESS) continue;
std::vector<cl_device_id> devices(num_devices);
status = clGetDeviceIDs(platforms[p], CL_DEVICE_TYPE_GPU, num_devices,
devices.data(), nullptr); // Get GPU IDs
if (status != CL_SUCCESS) continue;
// Iterate over GPUs
for (cl_uint d = 0; d < num_devices; ++d) {
// Compute performance indicator
cl_uint max_comp_units;
status = clGetDeviceInfo(devices[d], CL_DEVICE_MAX_COMPUTE_UNITS,
sizeof(max_comp_units), &max_comp_units, NULL);
if (status != CL_SUCCESS) max_comp_units = 1;
cl_uint max_clock_freq = 0;
status = clGetDeviceInfo(devices[d], CL_DEVICE_MAX_CLOCK_FREQUENCY,
sizeof(max_clock_freq), &max_clock_freq, NULL);
if (status != CL_SUCCESS || max_clock_freq == 0) max_clock_freq = 1;
cl_uint perf_indicator = max_comp_units * max_clock_freq;
// Bookmark fastest indicated GPU and its platform
if (perf_indicator > perf_indicator_fastest) {
perf_indicator_fastest = perf_indicator;
device_id_fastest = devices[d];
platform_id_fastest = platforms[p];
}
}
}
if (perf_indicator_fastest == 0) {
return absl::UnknownError("No GPU detected.");
}
*result = CLDevice(device_id_fastest, platform_id_fastest);
LoadOpenCLFunctionExtensions(platform_id_fastest);
return absl::OkStatus();
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,91 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_DEVICE_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_DEVICE_H_
#include <string>
#include <vector>
#include "tensorflow/lite/delegates/gpu/cl/opencl_wrapper.h"
#include "tensorflow/lite/delegates/gpu/cl/util.h"
#include "tensorflow/lite/delegates/gpu/common/gpu_info.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/types.h"
namespace tflite {
namespace gpu {
namespace cl {
// A wrapper around opencl device id
class CLDevice {
public:
CLDevice() = default;
CLDevice(cl_device_id id, cl_platform_id platform_id);
CLDevice(CLDevice&& device);
CLDevice& operator=(CLDevice&& device);
CLDevice(const CLDevice&);
CLDevice& operator=(const CLDevice&);
~CLDevice() {}
cl_device_id id() const { return id_; }
cl_platform_id platform() const { return platform_id_; }
std::string GetPlatformVersion() const;
// To track bug on some Adreno. b/131099086
void DisableOneLayerTextureArray();
const GpuInfo& GetInfo() const { return info_; }
// We update device info during context creation, so as supported texture
// formats can be requested from context only.
mutable GpuInfo info_;
private:
cl_device_id id_ = nullptr;
cl_platform_id platform_id_ = nullptr;
};
absl::Status CreateDefaultGPUDevice(CLDevice* result);
template <typename T>
T GetDeviceInfo(cl_device_id id, cl_device_info info) {
T result;
cl_int error = clGetDeviceInfo(id, info, sizeof(T), &result, nullptr);
if (error != CL_SUCCESS) {
return -1;
}
return result;
}
template <typename T>
absl::Status GetDeviceInfo(cl_device_id id, cl_device_info info, T* result) {
cl_int error = clGetDeviceInfo(id, info, sizeof(T), result, nullptr);
if (error != CL_SUCCESS) {
return absl::InvalidArgumentError(CLErrorCodeToString(error));
}
return absl::OkStatus();
}
void ParseQualcommOpenClCompilerVersion(
const std::string& cl_driver_version,
AdrenoInfo::OpenClCompilerVersion* result);
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_DEVICE_H_
@@ -0,0 +1,82 @@
/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
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.
==============================================================================*/
// testing function from unnamed namespace
#include "tensorflow/lite/delegates/gpu/cl/cl_device.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
namespace tflite {
namespace gpu {
namespace cl {
TEST(QualcommOpenClCompilerVersionParsing, Base) {
AdrenoInfo::OpenClCompilerVersion result;
ParseQualcommOpenClCompilerVersion("random text Compiler E031.79.53.41",
&result);
EXPECT_EQ(result.major, 79);
EXPECT_EQ(result.minor, 53);
EXPECT_EQ(result.patch, 41);
}
TEST(QualcommOpenClCompilerVersionParsing, WrongFormat0) {
AdrenoInfo::OpenClCompilerVersion result;
ParseQualcommOpenClCompilerVersion("random text Assembler A337.79.53.41",
&result);
EXPECT_EQ(result.major, 0);
EXPECT_EQ(result.minor, 0);
EXPECT_EQ(result.patch, 0);
}
TEST(QualcommOpenClCompilerVersionParsing, WrongFormat1) {
AdrenoInfo::OpenClCompilerVersion result;
ParseQualcommOpenClCompilerVersion("random text Compiler E031.79.53.4",
&result);
EXPECT_EQ(result.major, 0);
EXPECT_EQ(result.minor, 0);
EXPECT_EQ(result.patch, 0);
}
TEST(QualcommOpenClCompilerVersionParsing, WrongFormat2) {
AdrenoInfo::OpenClCompilerVersion result;
ParseQualcommOpenClCompilerVersion("random text Compiler E031:79:53:41",
&result);
EXPECT_EQ(result.major, 0);
EXPECT_EQ(result.minor, 0);
EXPECT_EQ(result.patch, 0);
}
TEST(QualcommOpenClCompilerVersionParsing, WrongFormat3) {
AdrenoInfo::OpenClCompilerVersion result;
ParseQualcommOpenClCompilerVersion("random text Compiler E031.79.x53.41",
&result);
EXPECT_EQ(result.major, 0);
EXPECT_EQ(result.minor, 0);
EXPECT_EQ(result.patch, 0);
}
TEST(QualcommOpenClCompilerVersionParsing, WrongFormat4) {
AdrenoInfo::OpenClCompilerVersion result;
ParseQualcommOpenClCompilerVersion("random text Compiler E031.a9.53.41",
&result);
EXPECT_EQ(result.major, 0);
EXPECT_EQ(result.minor, 0);
EXPECT_EQ(result.patch, 0);
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,42 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_ERRORS_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_ERRORS_H_
#include <string>
#include "tensorflow/lite/delegates/gpu/cl/util.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
namespace tflite {
namespace gpu {
namespace cl {
// @return if error_code is success, then return OK status. Otherwise translates
// error code into a message.
inline absl::Status GetOpenCLError(cl_int error_code) {
if (error_code == CL_SUCCESS) {
return absl::OkStatus();
}
return absl::InternalError("OpenCL error: " +
CLErrorCodeToString(error_code));
}
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_ERRORS_H_
@@ -0,0 +1,84 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/cl/cl_event.h"
#include <string>
#include <utility>
#include "tensorflow/lite/delegates/gpu/cl/opencl_wrapper.h"
namespace tflite {
namespace gpu {
namespace cl {
CLEvent::CLEvent(cl_event event) : event_(event) {}
CLEvent::CLEvent(CLEvent&& event)
: event_(event.event_), name_(std::move(event.name_)) {
event.event_ = nullptr;
}
CLEvent& CLEvent::operator=(CLEvent&& event) {
if (this != &event) {
Release();
std::swap(event_, event.event_);
name_ = std::move(event.name_);
}
return *this;
}
uint64_t CLEvent::GetStartedTimeNs() const {
cl_ulong time_ns;
clGetEventProfilingInfo(event_, CL_PROFILING_COMMAND_START, sizeof(cl_ulong),
&time_ns, nullptr);
return time_ns;
}
uint64_t CLEvent::GetFinishedTimeNs() const {
cl_ulong time_ns;
clGetEventProfilingInfo(event_, CL_PROFILING_COMMAND_END, sizeof(cl_ulong),
&time_ns, nullptr);
return time_ns;
}
double CLEvent::GetEventTimeMs() const {
const uint64_t start = GetStartedTimeNs();
const uint64_t end = GetFinishedTimeNs();
const uint64_t time_ns = (end - start);
return static_cast<double>(time_ns) * 1e-6;
}
uint64_t CLEvent::GetEventTimeNs() const {
return GetFinishedTimeNs() - GetStartedTimeNs();
}
void CLEvent::SetName(const std::string& name) { name_ = name; }
void CLEvent::Wait() const { clWaitForEvents(1, &event_); }
CLEvent::~CLEvent() { Release(); }
void CLEvent::Release() {
if (event_) {
clReleaseEvent(event_);
event_ = nullptr;
}
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,69 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_EVENT_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_EVENT_H_
#include <cstdint>
#include <string>
#include "tensorflow/lite/delegates/gpu/cl/opencl_wrapper.h"
namespace tflite {
namespace gpu {
namespace cl {
// A RAII wrapper around opencl event
class CLEvent {
public:
CLEvent() {}
explicit CLEvent(cl_event event);
// Move only
CLEvent(CLEvent&& event);
CLEvent& operator=(CLEvent&& event);
CLEvent(const CLEvent&) = delete;
CLEvent& operator=(const CLEvent&) = delete;
~CLEvent();
uint64_t GetStartedTimeNs() const;
uint64_t GetFinishedTimeNs() const;
double GetEventTimeMs() const;
uint64_t GetEventTimeNs() const;
void Wait() const;
cl_event event() const { return event_; }
bool is_valid() const { return event_ != nullptr; }
void SetName(const std::string& name);
std::string GetName() const { return name_; }
private:
void Release();
cl_event event_ = nullptr;
std::string name_; // optional, for profiling mostly
};
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_EVENT_H_
@@ -0,0 +1,64 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/cl/cl_image_format.h"
namespace tflite {
namespace gpu {
namespace cl {
cl_channel_order ToChannelOrder(int num_channels) {
switch (num_channels) {
case 1:
return CL_R;
case 2:
return CL_RG;
case 3:
return CL_RGB;
case 4:
return CL_RGBA;
default:
return -1;
}
}
cl_channel_type DataTypeToChannelType(DataType type, bool normalized) {
switch (type) {
case DataType::FLOAT32:
return CL_FLOAT;
case DataType::FLOAT16:
return CL_HALF_FLOAT;
case DataType::INT8:
return normalized ? CL_SNORM_INT8 : CL_SIGNED_INT8;
case DataType::UINT8:
return normalized ? CL_UNORM_INT8 : CL_UNSIGNED_INT8;
case DataType::INT16:
return normalized ? CL_SNORM_INT16 : CL_SIGNED_INT16;
case DataType::UINT16:
return normalized ? CL_UNORM_INT16 : CL_UNSIGNED_INT16;
case DataType::INT32:
return CL_SIGNED_INT32;
case DataType::UINT32:
return CL_UNSIGNED_INT32;
case DataType::BOOL:
return CL_UNSIGNED_INT8;
default:
return CL_FLOAT;
}
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,34 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_IMAGE_FORMAT_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_IMAGE_FORMAT_H_
#include "tensorflow/lite/delegates/gpu/cl/opencl_wrapper.h"
#include "tensorflow/lite/delegates/gpu/common/data_type.h"
namespace tflite {
namespace gpu {
namespace cl {
cl_channel_order ToChannelOrder(int num_channels);
cl_channel_type DataTypeToChannelType(DataType type, bool normalized = false);
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_IMAGE_FORMAT_H_
@@ -0,0 +1,159 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/cl/cl_kernel.h"
#include <string>
#include <utility>
#include "absl/strings/str_cat.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_program.h"
#include "tensorflow/lite/delegates/gpu/cl/util.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
absl::Status GetKernelMaxWorkGroupSize(cl_kernel kernel, cl_device_id device_id,
int* result) {
size_t max_work_group_size;
cl_int error_code =
clGetKernelWorkGroupInfo(kernel, device_id, CL_KERNEL_WORK_GROUP_SIZE,
sizeof(size_t), &max_work_group_size, nullptr);
if (error_code != CL_SUCCESS) {
return absl::UnknownError(
absl::StrCat("Failed to get info CL_KERNEL_WORK_GROUP_SIZE ",
CLErrorCodeToString(error_code)));
}
*result = static_cast<int>(max_work_group_size);
return absl::OkStatus();
}
absl::Status GetKernelPrivateMemorySize(cl_kernel kernel,
cl_device_id device_id, int* result) {
cl_ulong private_mem_size;
cl_int error_code =
clGetKernelWorkGroupInfo(kernel, device_id, CL_KERNEL_PRIVATE_MEM_SIZE,
sizeof(cl_ulong), &private_mem_size, nullptr);
if (error_code != CL_SUCCESS) {
return absl::UnknownError(
absl::StrCat("Failed to get info CL_KERNEL_PRIVATE_MEM_SIZE ",
CLErrorCodeToString(error_code)));
}
*result = static_cast<int>(private_mem_size);
return absl::OkStatus();
}
} // namespace
CLKernel::CLKernel(CLKernel&& kernel)
: info_(kernel.info_),
binding_counter_(kernel.binding_counter_),
function_name_(std::move(kernel.function_name_)),
program_(kernel.program_),
kernel_(kernel.kernel_) {
kernel.kernel_ = nullptr;
}
CLKernel& CLKernel::operator=(CLKernel&& kernel) {
if (this != &kernel) {
Release();
std::swap(info_, kernel.info_);
std::swap(binding_counter_, kernel.binding_counter_);
function_name_ = std::move(kernel.function_name_);
std::swap(program_, kernel.program_);
std::swap(kernel_, kernel.kernel_);
}
return *this;
}
CLKernel::~CLKernel() { Release(); }
absl::Status CLKernel::ReInit() const {
clReleaseKernel(kernel_);
cl_kernel* kern_ptr = const_cast<cl_kernel*>(&kernel_);
int error_code;
*kern_ptr = clCreateKernel(program_, function_name_.c_str(), &error_code);
if (!kernel_ || error_code != CL_SUCCESS) {
*kern_ptr = nullptr;
return absl::UnknownError(absl::StrCat("Failed to create ", function_name_,
CLErrorCodeToString(error_code)));
}
return absl::OkStatus();
}
void CLKernel::Release() {
if (kernel_) {
clReleaseKernel(kernel_);
clReleaseProgram(program_);
kernel_ = nullptr;
}
}
absl::Status CLKernel::CreateFromProgram(const CLProgram& program,
const std::string& function_name) {
int error_code;
function_name_ = function_name;
kernel_ =
clCreateKernel(program.program(), function_name.c_str(), &error_code);
if (!kernel_ || error_code != CL_SUCCESS) {
kernel_ = nullptr;
return absl::UnknownError(absl::StrCat("Failed to create ", function_name,
CLErrorCodeToString(error_code)));
}
program_ = program.program();
clRetainProgram(program_);
RETURN_IF_ERROR(GetKernelPrivateMemorySize(kernel_, program.GetDeviceId(),
&info_.private_memory_size));
RETURN_IF_ERROR(GetKernelMaxWorkGroupSize(kernel_, program.GetDeviceId(),
&info_.max_work_group_size));
return absl::OkStatus();
}
absl::Status CLKernel::SetMemory(int index, cl_mem memory) {
return SetBytes(index, &memory, sizeof(cl_mem));
}
absl::Status CLKernel::SetMemoryAuto(cl_mem memory) {
return SetBytesAuto(&memory, sizeof(cl_mem));
}
absl::Status CLKernel::SetBytes(int index, const void* ptr, int length) const {
const int error_code = clSetKernelArg(kernel_, index, length, ptr);
if (error_code != CL_SUCCESS) {
return absl::UnknownError(absl::StrCat("Failed to set kernel arguments - ",
CLErrorCodeToString(error_code)));
}
return absl::OkStatus();
}
absl::Status CLKernel::SetBytesAuto(const void* ptr, int length) {
const int error_code = clSetKernelArg(kernel_, binding_counter_, length, ptr);
if (error_code != CL_SUCCESS) {
return absl::UnknownError(absl::StrCat(
"Failed to set kernel arguments - ", CLErrorCodeToString(error_code),
"(at index - ", binding_counter_, ")"));
}
binding_counter_++;
return absl::OkStatus();
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,91 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_KERNEL_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_KERNEL_H_
#include <string>
#include "tensorflow/lite/delegates/gpu/cl/cl_context.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_device.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_program.h"
#include "tensorflow/lite/delegates/gpu/cl/opencl_wrapper.h"
#include "tensorflow/lite/delegates/gpu/common/kernel_info.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
namespace tflite {
namespace gpu {
namespace cl {
// Arguments binding to CLKernel can be manual or automatic
// In manual you specify binding index explicitly
// In automatic binding, index auto-incremented with every binding call
// Also, if you use automatic mode you must call ResetBindingCounter
// before parameters binding
class CLKernel {
public:
CLKernel() {}
// Move only
CLKernel(CLKernel&& kernel);
CLKernel& operator=(CLKernel&& kernel);
CLKernel(const CLKernel&) = delete;
CLKernel& operator=(const CLKernel&) = delete;
~CLKernel();
cl_kernel kernel() const { return kernel_; }
absl::Status CreateFromProgram(const CLProgram& program,
const std::string& function_name);
absl::Status SetMemory(int index, cl_mem memory);
absl::Status SetMemoryAuto(cl_mem memory);
template <typename T>
absl::Status SetBytes(int index, const T& value) const {
return SetBytes(index, static_cast<const void*>(&value), sizeof(T));
}
template <typename T>
absl::Status SetBytesAuto(const T& value) {
return SetBytesAuto(static_cast<const void*>(&value), sizeof(T));
}
int GetBindingCounter() const { return binding_counter_; }
void ResetBindingCounter() { binding_counter_ = 0; }
// Do not use this function
// workaround for Mali memory leak
absl::Status ReInit() const;
KernelInfo info_;
private:
void Release();
absl::Status SetBytes(int index, const void* ptr, int length) const;
absl::Status SetBytesAuto(const void* ptr, int length);
int binding_counter_ = -1;
std::string function_name_;
// reference to program from which kernel was created
cl_program program_ = nullptr;
cl_kernel kernel_ = nullptr;
};
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_KERNEL_H_
@@ -0,0 +1,37 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/cl/cl_memory.h"
namespace tflite {
namespace gpu {
namespace cl {
cl_mem_flags ToClMemFlags(AccessType access_type) {
switch (access_type) {
case AccessType::READ:
return CL_MEM_READ_ONLY;
case AccessType::WRITE:
return CL_MEM_WRITE_ONLY;
case AccessType::READ_WRITE:
return CL_MEM_READ_WRITE;
}
return CL_MEM_READ_ONLY; // unreachable
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,89 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_MEMORY_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_MEMORY_H_
#include "tensorflow/lite/delegates/gpu/cl/opencl_wrapper.h"
#include "tensorflow/lite/delegates/gpu/common/access_type.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
namespace tflite {
namespace gpu {
namespace cl {
// RAII wrapper for OpenCL memory object.
//
// Image is moveable but not copyable.
class CLMemory {
public:
// Creates invalid object.
CLMemory() : CLMemory(nullptr, false) {}
CLMemory(cl_mem memory, bool has_ownership)
: memory_(memory), has_ownership_(has_ownership) {}
// Move-only
CLMemory(const CLMemory&) = delete;
CLMemory& operator=(const CLMemory&) = delete;
CLMemory(CLMemory&& image)
: memory_(image.memory_), has_ownership_(image.has_ownership_) {
image.memory_ = nullptr;
}
~CLMemory() { Invalidate(); }
CLMemory& operator=(CLMemory&& image) {
if (this != &image) {
Invalidate();
std::swap(memory_, image.memory_);
has_ownership_ = image.has_ownership_;
}
return *this;
}
cl_mem memory() const { return memory_; }
bool is_valid() const { return memory_ != nullptr; }
// @return true if this object actually owns corresponding CL memory
// and manages it's lifetime.
bool has_ownership() const { return has_ownership_; }
cl_mem Release() {
cl_mem to_return = memory_;
memory_ = nullptr;
return to_return;
}
private:
void Invalidate() {
if (memory_ && has_ownership_) {
clReleaseMemObject(memory_);
}
memory_ = nullptr;
}
cl_mem memory_ = nullptr;
bool has_ownership_ = false;
};
cl_mem_flags ToClMemFlags(AccessType access_type);
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_MEMORY_H_
@@ -0,0 +1,227 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/cl/cl_operation.h"
#include <string>
#include "tensorflow/lite/delegates/gpu/common/task/compiler_options.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
std::string GetCommonOpenCLDefines(CalculationsPrecision precision) {
std::string result;
result += "#define GLOBAL_ID_0 get_global_id(0)\n";
result += "#define GLOBAL_ID_1 get_global_id(1)\n";
result += "#define GLOBAL_ID_2 get_global_id(2)\n";
result += "#define LOCAL_ID_0 get_local_id(0)\n";
result += "#define LOCAL_ID_1 get_local_id(1)\n";
result += "#define LOCAL_ID_2 get_local_id(2)\n";
result += "#define GROUP_ID_0 get_group_id(0)\n";
result += "#define GROUP_ID_1 get_group_id(1)\n";
result += "#define GROUP_ID_2 get_group_id(2)\n";
result += "#define GROUP_SIZE_0 get_local_size(0)\n";
result += "#define GROUP_SIZE_1 get_local_size(1)\n";
result += "#define GROUP_SIZE_2 get_local_size(2)\n";
result += "#define SUB_GROUP_LOCAL_ID get_sub_group_local_id()\n";
result += "#define SUB_GROUP_BROADCAST(V, ID) sub_group_broadcast(V, ID)\n";
result += "#define SIMD_LOCAL_MEM_BARRIER barrier(CLK_LOCAL_MEM_FENCE)\n";
result += "#define LOCAL_MEM_BARRIER barrier(CLK_LOCAL_MEM_FENCE)\n";
result += "#define MAIN_FUNCTION __kernel void main_function\n";
result += "#define INIT_FLOAT(value) (float)(value)\n";
result += "#define INIT_FLOAT2(value) (float2)(value)\n";
result += "#define INIT_FLOAT2v2(v0, v1) (float2)(v0, v1)\n";
result += "#define INIT_FLOAT3(value) (float3)(value)\n";
result += "#define INIT_FLOAT3v3(v0, v1, v2) (float3)(v0, v1, v2)\n";
result += "#define INIT_FLOAT4(value) (float4)(value)\n";
result += "#define INIT_FLOAT4v4(v0, v1, v2, v3) (float4)(v0, v1, v2, v3)\n";
result += "#define INIT_INT(value) (int)(value)\n";
result += "#define INIT_INT2v2(v0, v1) (int2)(v0, v1)\n";
result += "#define INIT_INT4v4(v0, v1, v2, v3) (int4)(v0, v1, v2, v3)\n";
result += "#define CONVERT_TO_INT4(value) convert_int4(value)\n";
switch (precision) {
case CalculationsPrecision::F32:
result += "#pragma OPENCL EXTENSION cl_khr_3d_image_writes : enable\n";
result += "#define ACCUM_FLT4 float4\n";
result += "#define INIT_ACCUM_FLT4(value) (float4)(value)\n";
result += "#define FLT float\n";
result += "#define FLT2 float2\n";
result += "#define FLT3 float3\n";
result += "#define FLT4 float4\n";
result += "#define TO_FLT4 convert_float4\n";
result += "#define TO_ACCUM_TYPE convert_float4\n";
result += "#define TO_ACCUM_FLT convert_float\n";
result += "#define TO_ACCUM_FLT2 convert_float2\n";
result += "#define TO_ACCUM_FLT3 convert_float3\n";
result += "#define TO_ACCUM_FLT4 convert_float4\n";
result += "#define INIT_FLT(value) (float)(value)\n";
result += "#define INIT_FLT4(value) (float4)(value)\n";
result +=
"#define INIT_FLT4v4(v0, v1, v2, v3) (float4)(v0, v1, v2, v3)\n";
break;
case CalculationsPrecision::F16:
result += "#pragma OPENCL EXTENSION cl_khr_3d_image_writes : enable\n";
result += "#pragma OPENCL EXTENSION cl_khr_fp16 : enable\n";
result += "#define ACCUM_FLT4 half4\n";
result += "#define INIT_ACCUM_FLT4(value) (half4)(value)\n";
result += "#define FLT half\n";
result += "#define FLT2 half2\n";
result += "#define FLT3 half3\n";
result += "#define FLT4 half4\n";
result += "#define TO_FLT4 convert_half4\n";
result += "#define TO_ACCUM_TYPE convert_half4\n";
result += "#define TO_ACCUM_FLT convert_half\n";
result += "#define TO_ACCUM_FLT2 convert_half2\n";
result += "#define TO_ACCUM_FLT3 convert_half3\n";
result += "#define TO_ACCUM_FLT4 convert_half4\n";
result += "#define INIT_FLT(value) (half)(value)\n";
result += "#define INIT_FLT4(value) (half4)(value)\n";
result += "#define INIT_FLT4v4(v0, v1, v2, v3) (half4)(v0, v1, v2, v3)\n";
break;
case CalculationsPrecision::F32_F16:
result += "#pragma OPENCL EXTENSION cl_khr_3d_image_writes : enable\n";
result += "#pragma OPENCL EXTENSION cl_khr_fp16 : enable\n";
result += "#define ACCUM_FLT4 float4\n";
result += "#define INIT_ACCUM_FLT4(value) (float4)(value)\n";
result += "#define FLT half\n";
result += "#define FLT2 half2\n";
result += "#define FLT3 half3\n";
result += "#define FLT4 half4\n";
result += "#define TO_FLT4 convert_half4\n";
result += "#define TO_ACCUM_TYPE convert_float4\n";
result += "#define TO_ACCUM_FLT convert_float\n";
result += "#define TO_ACCUM_FLT2 convert_float2\n";
result += "#define TO_ACCUM_FLT3 convert_float3\n";
result += "#define TO_ACCUM_FLT4 convert_float4\n";
result += "#define INIT_FLT(value) (half)(value)\n";
result += "#define INIT_FLT4(value) (half4)(value)\n";
result += "#define INIT_FLT4v4(v0, v1, v2, v3) (half4)(v0, v1, v2, v3)\n";
break;
}
result += "#define bool2 uchar2\n";
result += "#define bool3 uchar3\n";
result += "#define bool4 uchar4\n";
const auto cl_specific_defines = GetClSpecificDefines();
for (const auto& define : cl_specific_defines) {
result += "#define " + define.first + " " + define.second + "\n";
}
return result;
}
} // namespace
absl::Status ClOperation::UpdateParams() {
for (int i = 0; i < operation_->GetSrcTensorsNames().size(); ++i) {
const auto* cl_spatial_tensor =
dynamic_cast<const Tensor*>(operation_->GetSrcTensors()[i]);
if (!cl_spatial_tensor) {
return absl::InvalidArgumentError("Expected CLSpatialTensor.");
}
RETURN_IF_ERROR(cl_args_.SetObjectRef(operation_->GetSrcTensorsNames()[i],
cl_spatial_tensor));
}
for (int i = 0; i < operation_->GetDstTensorsNames().size(); ++i) {
const auto* cl_spatial_tensor =
dynamic_cast<const Tensor*>(operation_->GetDstTensors()[i]);
if (!cl_spatial_tensor) {
return absl::InvalidArgumentError("Expected CLSpatialTensor.");
}
RETURN_IF_ERROR(cl_args_.SetObjectRef(operation_->GetDstTensorsNames()[i],
cl_spatial_tensor));
}
RETURN_IF_ERROR(operation_->BindArguments(&cl_args_));
operation_->RecalculateGridSize();
operation_->RecalculateWorkGroupsCount();
return absl::OkStatus();
}
absl::Status ClOperation::SetSrcTensor(int index, Tensor* tensor) {
operation_->SetSrc(tensor, index);
return cl_args_.SetObjectRef(operation_->GetSrcTensorsNames()[index], tensor);
}
absl::Status ClOperation::SetDstTensor(int index, Tensor* tensor) {
operation_->SetDst(tensor, index);
return cl_args_.SetObjectRef(operation_->GetDstTensorsNames()[index], tensor);
}
absl::Status ClOperation::Compile(const CreationContext& creation_context) {
operation_->code_ =
GetCommonOpenCLDefines(operation_->GetPrecision()) + operation_->code_;
RETURN_IF_ERROR(cl_args_.Init(creation_context.GetGpuInfo(),
creation_context.context, &operation_->args_,
&operation_->code_));
operation_->args_.ReleaseCPURepresentation();
if (creation_context.device->info_.opencl_info.IsCLVK()) {
operation_->compiler_options_.push_back(
CompilerOptions::kClFastRelaxedMath);
}
return creation_context.cache->GetOrCreateCLKernel(
operation_->code_, "main_function", operation_->compiler_options_,
*creation_context.context, *creation_context.device, &kernel_,
&kernel_fingerprint_);
}
absl::Status ClOperation::RestoreDeserialized(const ProgramCache& program_cache,
uint64_t fingerprint,
const GpuInfo& gpu_info,
const int3& work_group_size,
CLContext* context) {
kernel_fingerprint_ = fingerprint;
RETURN_IF_ERROR(
program_cache.GetKernel(kernel_fingerprint_, "main_function", &kernel_));
operation_->work_group_size_ = work_group_size;
operation_->RecalculateWorkGroupsCount();
RETURN_IF_ERROR(cl_args_.Init(gpu_info, &operation_->args_, context));
operation_->args_.ReleaseCPURepresentation();
return absl::OkStatus();
}
absl::Status ClOperation::Tune(TuningType tuning_type, const GpuInfo& gpu_info,
ProfilingCommandQueue* profiling_queue) {
std::vector<GPUOperation::DispatchInfo> possible_dispatches;
operation_->GetPossibleDispatches(tuning_type, gpu_info, kernel_.info_,
&possible_dispatches);
if (possible_dispatches.empty()) {
return absl::NotFoundError("No dispatch parameters to launch kernel");
}
if (possible_dispatches.size() == 1) {
operation_->work_group_size_ = possible_dispatches[0].work_group_size;
operation_->RecalculateWorkGroupsCount();
return absl::OkStatus();
} else {
std::vector<int3> work_group_sizes(possible_dispatches.size());
std::vector<int3> work_groups_counts(possible_dispatches.size());
for (int i = 0; i < possible_dispatches.size(); ++i) {
work_group_sizes[i] = possible_dispatches[i].work_group_size;
work_groups_counts[i] = possible_dispatches[i].work_groups_count;
}
RETURN_IF_ERROR(cl_args_.Bind(kernel_.kernel()));
int best_work_group_index;
RETURN_IF_ERROR(profiling_queue->GetBestWorkGroupIndex(
kernel_, gpu_info, work_groups_counts, work_group_sizes,
&best_work_group_index));
operation_->work_group_size_ = work_group_sizes[best_work_group_index];
operation_->RecalculateWorkGroupsCount();
return absl::OkStatus();
}
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,141 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_OPERATION_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_OPERATION_H_
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/str_cat.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_arguments.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_command_queue.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_context.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_device.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_kernel.h"
#include "tensorflow/lite/delegates/gpu/cl/program_cache.h"
#include "tensorflow/lite/delegates/gpu/cl/tensor.h"
#include "tensorflow/lite/delegates/gpu/common/task/gpu_operation.h"
namespace tflite {
namespace gpu {
namespace cl {
struct CreationContext {
const CLDevice* device;
CLContext* context;
CLCommandQueue* queue;
ProgramCache* cache;
const GpuInfo& GetGpuInfo() const { return device->info_; }
};
class ClOperation {
public:
ClOperation() = default;
virtual ~ClOperation() = default;
// Move only
ClOperation(ClOperation&& operation) = default;
ClOperation& operator=(ClOperation&& operation) = default;
ClOperation(const ClOperation&) = delete;
ClOperation& operator=(const ClOperation&) = delete;
void Init(std::unique_ptr<GPUOperation>&& gpu_operation) {
operation_ = std::move(gpu_operation);
}
GPUOperation& GetGpuOperation() { return *operation_; }
const GPUOperation& GetGpuOperation() const { return *operation_; }
uint64_t GetKernelFingerprint() const { return kernel_fingerprint_; }
// should be called after changes of inputs/outputs.
absl::Status UpdateParams();
absl::Status SetSrcTensor(int index, Tensor* tensor);
absl::Status SetDstTensor(int index, Tensor* tensor);
absl::Status AddToQueue(CLCommandQueue* queue) {
RETURN_IF_ERROR(cl_args_.Bind(kernel_.kernel()));
return queue->Dispatch(kernel_, operation_->GetWorkGroupsCount(),
operation_->work_group_size_);
}
absl::Status AddToCommandBuffer(cl_command_buffer_khr cb) {
RETURN_IF_ERROR(cl_args_.Bind(kernel_.kernel()));
std::array<size_t, 3> local;
std::array<size_t, 3> global;
for (int i = 0; i < 3; ++i) {
local[i] = operation_->work_group_size_[i];
global[i] =
operation_->GetWorkGroupsCount()[i] * operation_->work_group_size_[i];
}
const int error_code = clCommandNDRangeKernelKHR(
cb, nullptr, nullptr, kernel_.kernel(), 3, nullptr, global.data(),
local.data(), 0, nullptr, nullptr, nullptr);
if (error_code != CL_SUCCESS) {
return absl::UnknownError(
absl::StrCat("Failed to clCommandNDRangeKernelKHR - ",
CLErrorCodeToString(error_code)));
}
return absl::OkStatus();
}
absl::Status AddToQueue(ProfilingCommandQueue* queue, CLEvent* event) {
RETURN_IF_ERROR(cl_args_.Bind(kernel_.kernel()));
return queue->CLCommandQueue::Dispatch(kernel_,
operation_->GetWorkGroupsCount(),
operation_->work_group_size_, event);
}
// for better profiling
absl::Status AddToQueueNTimes(ProfilingCommandQueue* queue, int n,
int flush_period = 0) {
RETURN_IF_ERROR(cl_args_.Bind(kernel_.kernel()));
return queue->DispatchNTimes(kernel_, operation_->GetWorkGroupsCount(),
operation_->work_group_size_, n, flush_period);
}
absl::Status Tune(TuningType tuning_type, const GpuInfo& gpu_info,
ProfilingCommandQueue* profiling_queue);
absl::Status Compile(const CreationContext& creation_context);
absl::Status RestoreDeserialized(const ProgramCache& program_cache,
uint64_t fingerprint,
const GpuInfo& gpu_info,
const int3& work_group_size,
CLContext* context);
int3 GetWorkGroupSize() const { return operation_->work_group_size_; }
bool HasEqualScalarArguments(const ClOperation& op) const {
return cl_args_.HasEqualScalarArguments(op.cl_args_);
}
private:
std::unique_ptr<GPUOperation> operation_;
CLKernel kernel_;
uint64_t kernel_fingerprint_;
CLArguments cl_args_;
};
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_OPERATION_H_
@@ -0,0 +1,226 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/cl/cl_program.h"
#include <cstdint>
#include <cstring>
#include <string>
#include <vector>
#include "absl/strings/str_cat.h"
#include "absl/types/span.h"
#include "tensorflow/lite/delegates/gpu/cl/util.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
std::string GetProgramBuildInfo(cl_program program, cl_device_id id,
cl_program_build_info info) {
size_t size;
cl_int error_code =
clGetProgramBuildInfo(program, id, info, 0, nullptr, &size);
if (error_code != CL_SUCCESS) {
return absl::StrCat("Failed to GetProgramBuildInfo - ",
CLErrorCodeToString(error_code));
}
std::string result(size - 1, 0);
error_code =
clGetProgramBuildInfo(program, id, info, size, &result[0], nullptr);
if (error_code != CL_SUCCESS) {
return absl::StrCat("Failed to GetProgramBuildInfo - ",
CLErrorCodeToString(error_code));
}
return result;
}
absl::Status GetBinarySize(cl_program program, size_t* binary_size) {
cl_int error_code = clGetProgramInfo(program, CL_PROGRAM_BINARY_SIZES,
sizeof(size_t), binary_size, nullptr);
if (error_code != CL_SUCCESS) {
return absl::UnknownError(
absl::StrCat("Failed to get program binary size - ",
CLErrorCodeToString(error_code)));
}
return absl::OkStatus();
}
absl::Status BuildProgram(cl_program program, const CLDevice& device,
const std::string& compiler_options) {
const int error_code = clBuildProgram(
program, 0, nullptr, compiler_options.c_str(), nullptr, nullptr);
if (error_code != CL_SUCCESS) {
return absl::UnknownError(absl::StrCat(
"Failed to build program executable - ",
CLErrorCodeToString(error_code),
GetProgramBuildInfo(program, device.id(), CL_PROGRAM_BUILD_LOG)));
}
return absl::OkStatus();
}
std::string CompilerOptionToString(const GpuInfo& gpu_info,
CompilerOptions option) {
switch (option) {
case CompilerOptions::kAdrenoFullSimd:
if (gpu_info.opencl_info.IsCLVK()) {
return "";
}
if (gpu_info.IsAdreno()) {
if (gpu_info.adreno_info.IsAdreno3xx() ||
gpu_info.adreno_info.IsAdreno4xx()) {
return "-qcom-accelerate-16-bit";
} else {
return "-qcom-accelerate-16-bit=true";
}
} else {
return "unsupported";
}
case CompilerOptions::kAdrenoMoreWaves:
if (gpu_info.opencl_info.IsCLVK()) {
return "";
}
if (gpu_info.IsAdreno()) {
if (!(gpu_info.adreno_info.IsAdreno3xx() ||
gpu_info.adreno_info.IsAdreno4xx())) {
return "-qcom-accelerate-16-bit=false";
} else {
return "";
}
} else {
return "unsupported";
}
case CompilerOptions::kClFastRelaxedMath:
return "-cl-fast-relaxed-math";
case CompilerOptions::kClRegisterAllocation64:
if (gpu_info.opencl_info.supports_register_allocation_arm) {
return "-fregister-allocation=64";
} else {
return "";
}
case CompilerOptions::kClDisableOptimizations:
return "-cl-opt-disable";
case CompilerOptions::kCl20:
return "-cl-std=CL2.0";
case CompilerOptions::kCl30:
return "-cl-std=CL3.0";
}
}
} // namespace
std::string CompilerOptionsToString(
const GpuInfo& gpu_info,
const std::vector<CompilerOptions>& compiler_options) {
std::string result;
for (auto option : compiler_options) {
absl::StrAppend(&result, CompilerOptionToString(gpu_info, option), " ");
}
return result;
}
CLProgram::CLProgram(cl_program program, cl_device_id device_id)
: program_(program), device_id_(device_id) {}
CLProgram::CLProgram(CLProgram&& program)
: program_(program.program_), device_id_(program.device_id_) {
program.program_ = nullptr;
}
CLProgram& CLProgram::operator=(CLProgram&& program) {
if (this != &program) {
Release();
std::swap(program_, program.program_);
std::swap(device_id_, program.device_id_);
}
return *this;
}
CLProgram::~CLProgram() { Release(); }
void CLProgram::Release() {
if (program_) {
clReleaseProgram(program_);
program_ = nullptr;
}
}
absl::Status CLProgram::GetBinary(std::vector<uint8_t>* result) const {
size_t binary_size;
RETURN_IF_ERROR(GetBinarySize(program_, &binary_size));
result->resize(result->size() + binary_size);
uint8_t* binary_ptr = result->data() + result->size() - binary_size;
cl_int error_code =
clGetProgramInfo(program_, CL_PROGRAM_BINARIES, sizeof(unsigned char*),
&binary_ptr, nullptr);
if (error_code != CL_SUCCESS) {
return absl::UnknownError(absl::StrCat("Failed to get program binary - ",
CLErrorCodeToString(error_code)));
}
return absl::OkStatus();
}
absl::Status CreateCLProgram(const std::string& code,
const std::string& compiler_options,
const CLContext& context, const CLDevice& device,
CLProgram* result) {
int error_code;
const char* source = code.c_str();
cl_program program = clCreateProgramWithSource(context.context(), 1, &source,
nullptr, &error_code);
if (!program || error_code != CL_SUCCESS) {
return absl::UnknownError(
absl::StrCat("Failed to create compute program - ",
CLErrorCodeToString(error_code)));
}
*result = CLProgram(program, device.id());
RETURN_IF_ERROR(BuildProgram(program, device, compiler_options));
return absl::OkStatus();
}
absl::Status CreateCLProgramFromBinary(const CLContext& context,
const CLDevice& device,
absl::Span<const uint8_t> binary,
CLProgram* result) {
cl_int binary_status;
cl_int error_code;
cl_device_id devices_list[] = {device.id()};
size_t binary_size = binary.size();
const uint8_t* binary_pointer = binary.data();
cl_program program = clCreateProgramWithBinary(
context.context(), 1, devices_list, &binary_size, &binary_pointer,
&binary_status, &error_code);
if (binary_status != CL_SUCCESS) {
return absl::UnknownError(absl::StrCat(
"Something wrong with binary after clCreateProgramWithBinary - ",
binary_status));
}
if (error_code != CL_SUCCESS) {
return absl::UnknownError(absl::StrCat("Failed to create program - ",
CLErrorCodeToString(error_code)));
}
*result = CLProgram(program, device.id());
return BuildProgram(program, device, "");
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,84 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_PROGRAM_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_PROGRAM_H_
#include <cstdint>
#include <string>
#include <vector>
#include "absl/types/span.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_context.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_device.h"
#include "tensorflow/lite/delegates/gpu/cl/opencl_wrapper.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/task/compiler_options.h"
namespace tflite {
namespace gpu {
namespace cl {
std::string CompilerOptionsToString(
const GpuInfo& gpu_info,
const std::vector<CompilerOptions>& compiler_options);
class CLProgram {
public:
CLProgram() {}
CLProgram(cl_program program, cl_device_id device_id);
// Move only
CLProgram(CLProgram&& program);
CLProgram& operator=(CLProgram&& program);
CLProgram(const CLProgram&) = delete;
CLProgram& operator=(const CLProgram&) = delete;
~CLProgram();
cl_program program() const { return program_; }
// Return the cl_device_id associated with the program object.
// This can be the device associated with context on which the program object
// has been created or can be device that was specified when a program object
// was created using clCreateProgramWithBinary.
cl_device_id GetDeviceId() const { return device_id_; }
absl::Status GetBinary(std::vector<uint8_t>* result) const;
private:
void Release();
cl_program program_ = nullptr;
// reference
cl_device_id device_id_ = nullptr;
};
absl::Status CreateCLProgram(const std::string& code,
const std::string& compiler_options,
const CLContext& context, const CLDevice& device,
CLProgram* result);
absl::Status CreateCLProgramFromBinary(const CLContext& context,
const CLDevice& device,
absl::Span<const uint8_t> binary,
CLProgram* result);
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_PROGRAM_H_
@@ -0,0 +1,49 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_TEST_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_TEST_H_
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/environment.h"
#include "tensorflow/lite/delegates/gpu/cl/opencl_wrapper.h"
namespace tflite {
namespace gpu {
namespace cl {
#ifndef ASSERT_OK
#define ASSERT_OK(x) ASSERT_TRUE(x.ok());
#endif
class OpenCLTest : public ::testing::Test {
public:
void SetUp() override {
ASSERT_OK(LoadOpenCL());
ASSERT_OK(CreateEnvironment(&env_));
}
protected:
Environment env_;
};
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_CL_TEST_H_
@@ -0,0 +1,31 @@
// Copyright 2019 The TensorFlow Authors. All Rights Reserved.
//
// 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.
namespace tflite.gpu.cl.data;
file_identifier "AFCM";
file_extension "jetbin";
table Program {
fingerprint:uint64;
binary:[ubyte];
}
table CompiledCache {
driver_version:string;
programs:[Program];
}
root_type CompiledCache;
@@ -0,0 +1,223 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
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.
==============================================================================*/
// automatically generated by the FlatBuffers compiler, do not modify
#ifndef FLATBUFFERS_GENERATED_COMPILEDPROGRAMCACHE_TFLITE_GPU_CL_DATA_H_
#define FLATBUFFERS_GENERATED_COMPILEDPROGRAMCACHE_TFLITE_GPU_CL_DATA_H_
#include "flatbuffers/flatbuffers.h"
// Ensure the included flatbuffers.h is the same version as when this file was
// generated, otherwise it may not be compatible.
static_assert(FLATBUFFERS_VERSION_MAJOR == 25 &&
FLATBUFFERS_VERSION_MINOR == 9 &&
FLATBUFFERS_VERSION_REVISION == 23,
"Non-compatible flatbuffers version included");
namespace tflite {
namespace gpu {
namespace cl {
namespace data {
struct Program;
struct ProgramBuilder;
struct CompiledCache;
struct CompiledCacheBuilder;
struct Program FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef ProgramBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_FINGERPRINT = 4,
VT_BINARY = 6
};
uint64_t fingerprint() const {
return GetField<uint64_t>(VT_FINGERPRINT, 0);
}
const ::flatbuffers::Vector<uint8_t> *binary() const {
return GetPointer<const ::flatbuffers::Vector<uint8_t> *>(VT_BINARY);
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<uint64_t>(verifier, VT_FINGERPRINT, 8) &&
VerifyOffset(verifier, VT_BINARY) &&
verifier.VerifyVector(binary()) &&
verifier.EndTable();
}
};
struct ProgramBuilder {
typedef Program Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_fingerprint(uint64_t fingerprint) {
fbb_.AddElement<uint64_t>(Program::VT_FINGERPRINT, fingerprint, 0);
}
void add_binary(::flatbuffers::Offset<::flatbuffers::Vector<uint8_t>> binary) {
fbb_.AddOffset(Program::VT_BINARY, binary);
}
explicit ProgramBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<Program> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<Program>(end);
return o;
}
};
inline ::flatbuffers::Offset<Program> CreateProgram(
::flatbuffers::FlatBufferBuilder &_fbb,
uint64_t fingerprint = 0,
::flatbuffers::Offset<::flatbuffers::Vector<uint8_t>> binary = 0) {
ProgramBuilder builder_(_fbb);
builder_.add_fingerprint(fingerprint);
builder_.add_binary(binary);
return builder_.Finish();
}
inline ::flatbuffers::Offset<Program> CreateProgramDirect(
::flatbuffers::FlatBufferBuilder &_fbb,
uint64_t fingerprint = 0,
const std::vector<uint8_t> *binary = nullptr) {
auto binary__ = binary ? _fbb.CreateVector<uint8_t>(*binary) : 0;
return tflite::gpu::cl::data::CreateProgram(
_fbb,
fingerprint,
binary__);
}
struct CompiledCache FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef CompiledCacheBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_DRIVER_VERSION = 4,
VT_PROGRAMS = 6
};
const ::flatbuffers::String *driver_version() const {
return GetPointer<const ::flatbuffers::String *>(VT_DRIVER_VERSION);
}
const ::flatbuffers::Vector<::flatbuffers::Offset<tflite::gpu::cl::data::Program>> *programs() const {
return GetPointer<const ::flatbuffers::Vector<::flatbuffers::Offset<tflite::gpu::cl::data::Program>> *>(VT_PROGRAMS);
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyOffset(verifier, VT_DRIVER_VERSION) &&
verifier.VerifyString(driver_version()) &&
VerifyOffset(verifier, VT_PROGRAMS) &&
verifier.VerifyVector(programs()) &&
verifier.VerifyVectorOfTables(programs()) &&
verifier.EndTable();
}
};
struct CompiledCacheBuilder {
typedef CompiledCache Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_driver_version(::flatbuffers::Offset<::flatbuffers::String> driver_version) {
fbb_.AddOffset(CompiledCache::VT_DRIVER_VERSION, driver_version);
}
void add_programs(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<tflite::gpu::cl::data::Program>>> programs) {
fbb_.AddOffset(CompiledCache::VT_PROGRAMS, programs);
}
explicit CompiledCacheBuilder(::flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<CompiledCache> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<CompiledCache>(end);
return o;
}
};
inline ::flatbuffers::Offset<CompiledCache> CreateCompiledCache(
::flatbuffers::FlatBufferBuilder &_fbb,
::flatbuffers::Offset<::flatbuffers::String> driver_version = 0,
::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<tflite::gpu::cl::data::Program>>> programs = 0) {
CompiledCacheBuilder builder_(_fbb);
builder_.add_programs(programs);
builder_.add_driver_version(driver_version);
return builder_.Finish();
}
inline ::flatbuffers::Offset<CompiledCache> CreateCompiledCacheDirect(
::flatbuffers::FlatBufferBuilder &_fbb,
const char *driver_version = nullptr,
const std::vector<::flatbuffers::Offset<tflite::gpu::cl::data::Program>> *programs = nullptr) {
auto driver_version__ = driver_version ? _fbb.CreateString(driver_version) : 0;
auto programs__ = programs ? _fbb.CreateVector<::flatbuffers::Offset<tflite::gpu::cl::data::Program>>(*programs) : 0;
return tflite::gpu::cl::data::CreateCompiledCache(
_fbb,
driver_version__,
programs__);
}
inline const tflite::gpu::cl::data::CompiledCache *GetCompiledCache(const void *buf) {
return ::flatbuffers::GetRoot<tflite::gpu::cl::data::CompiledCache>(buf);
}
inline const tflite::gpu::cl::data::CompiledCache *GetSizePrefixedCompiledCache(const void *buf) {
return ::flatbuffers::GetSizePrefixedRoot<tflite::gpu::cl::data::CompiledCache>(buf);
}
inline const char *CompiledCacheIdentifier() {
return "AFCM";
}
inline bool CompiledCacheBufferHasIdentifier(const void *buf) {
return ::flatbuffers::BufferHasIdentifier(
buf, CompiledCacheIdentifier());
}
inline bool SizePrefixedCompiledCacheBufferHasIdentifier(const void *buf) {
return ::flatbuffers::BufferHasIdentifier(
buf, CompiledCacheIdentifier(), true);
}
inline bool VerifyCompiledCacheBuffer(
::flatbuffers::Verifier &verifier) {
return verifier.VerifyBuffer<tflite::gpu::cl::data::CompiledCache>(CompiledCacheIdentifier());
}
inline bool VerifySizePrefixedCompiledCacheBuffer(
::flatbuffers::Verifier &verifier) {
return verifier.VerifySizePrefixedBuffer<tflite::gpu::cl::data::CompiledCache>(CompiledCacheIdentifier());
}
inline const char *CompiledCacheExtension() {
return "jetbin";
}
inline void FinishCompiledCacheBuffer(
::flatbuffers::FlatBufferBuilder &fbb,
::flatbuffers::Offset<tflite::gpu::cl::data::CompiledCache> root) {
fbb.Finish(root, CompiledCacheIdentifier());
}
inline void FinishSizePrefixedCompiledCacheBuffer(
::flatbuffers::FlatBufferBuilder &fbb,
::flatbuffers::Offset<tflite::gpu::cl::data::CompiledCache> root) {
fbb.FinishSizePrefixed(root, CompiledCacheIdentifier());
}
} // namespace data
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // FLATBUFFERS_GENERATED_COMPILEDPROGRAMCACHE_TFLITE_GPU_CL_DATA_H_
@@ -0,0 +1,26 @@
load("@rules_cc//cc:cc_library.bzl", "cc_library")
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
default_visibility = ["//visibility:public"],
licenses = ["notice"],
)
cc_library(
name = "qcom_wrapper",
hdrs = ["qcom_wrapper.h"],
)
cc_library(
name = "recordable_queue",
srcs = ["recordable_queue.cc"],
deps = [
"//tensorflow/lite/delegates/gpu/cl:recordable_queue",
],
)
cc_library(
name = "util",
srcs = ["util.cc"],
deps = ["//tensorflow/lite/delegates/gpu/cl:opencl_wrapper"],
)
@@ -0,0 +1,31 @@
/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_CL_DEFAULT_QCOM_WRAPPER_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_DEFAULT_QCOM_WRAPPER_H_
namespace tflite {
namespace gpu {
namespace cl {
#define LoadQcomExtensionFunctions()
#define DEFINE_QCOM_FUNCTION_PTRS
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_DEFAULT_QCOM_WRAPPER_H_
@@ -0,0 +1,32 @@
/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/cl/recordable_queue.h"
#include <memory>
namespace tflite {
namespace gpu {
namespace cl {
std::unique_ptr<RecordableQueue> CreateRecordableQueue(
const std::vector<ClOperation*>& ops, const CLDevice& device,
const CLContext& context) {
return std::make_unique<RecordableQueue>(RecordableQueue());
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,42 @@
/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
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 <string>
#include <utility>
#include <vector>
#include "tensorflow/lite/delegates/gpu/cl/opencl_wrapper.h"
namespace tflite {
namespace gpu {
namespace cl {
absl::Status CreateQcomConvolutionFilter(cl_context context, int kernel_x,
int kernel_y, cl_mem* filter,
const void* data) {
return absl::UnavailableError("CreateQcomConvolutionFilter not available.");
}
std::vector<std::string> GetUnsupportedExtensions() {
return {"cl_qcom_accelerated_image_ops", "cl_qcom_recordable_queues"};
}
std::vector<std::pair<std::string, std::string>> GetClSpecificDefines() {
return {};
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,129 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/cl/egl_sync.h"
#include "tensorflow/lite/delegates/gpu/gl/gl_call.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
bool HasExtension(EGLDisplay display, const char* extension) {
const char* extensions = eglQueryString(display, EGL_EXTENSIONS);
return extensions && std::strstr(extensions, extension);
}
absl::Status IsEglFenceSyncSupported(EGLDisplay display) {
static bool supported = HasExtension(display, "EGL_KHR_fence_sync");
if (supported) {
return absl::OkStatus();
}
return absl::InternalError("Not supported: EGL_KHR_fence_sync");
}
absl::Status IsEglWaitSyncSupported(EGLDisplay display) {
static bool supported = HasExtension(display, "EGL_KHR_wait_sync");
if (supported) {
return absl::OkStatus();
}
return absl::InternalError("Not supported: EGL_KHR_wait_sync");
}
} // anonymous namespace
absl::Status EglSync::NewFence(EGLDisplay display, EglSync* sync) {
RETURN_IF_ERROR(IsEglFenceSyncSupported(display));
static auto* egl_create_sync_khr =
reinterpret_cast<decltype(&eglCreateSyncKHR)>(
eglGetProcAddress("eglCreateSyncKHR"));
if (egl_create_sync_khr == nullptr) {
// Needs extension: EGL_KHR_fence_sync (EGL) / GL_OES_EGL_sync (OpenGL ES).
return absl::InternalError(
"Not supported / bad EGL implementation: eglCreateSyncKHR.");
}
EGLSyncKHR egl_sync;
RETURN_IF_ERROR(TFLITE_GPU_CALL_EGL(*egl_create_sync_khr, &egl_sync, display,
EGL_SYNC_FENCE_KHR, nullptr));
if (egl_sync == EGL_NO_SYNC_KHR) {
return absl::InternalError("Returned empty KHR EGL sync");
}
*sync = EglSync(display, egl_sync);
return absl::OkStatus();
}
EglSync& EglSync::operator=(EglSync&& sync) {
if (this != &sync) {
Invalidate();
std::swap(sync_, sync.sync_);
display_ = sync.display_;
}
return *this;
}
void EglSync::Invalidate() {
if (sync_ != EGL_NO_SYNC_KHR) {
static auto* egl_destroy_sync_khr =
reinterpret_cast<decltype(&eglDestroySyncKHR)>(
eglGetProcAddress("eglDestroySyncKHR"));
// Needs extension: EGL_KHR_fence_sync (EGL) / GL_OES_EGL_sync (OpenGL ES).
if (IsEglFenceSyncSupported(display_).ok() && egl_destroy_sync_khr) {
// Note: we're doing nothing when the function pointer is nullptr, or the
// call returns EGL_FALSE.
(*egl_destroy_sync_khr)(display_, sync_);
}
sync_ = EGL_NO_SYNC_KHR;
}
}
absl::Status EglSync::ServerWait() {
RETURN_IF_ERROR(IsEglWaitSyncSupported(display_));
static auto* egl_wait_sync_khr = reinterpret_cast<decltype(&eglWaitSyncKHR)>(
eglGetProcAddress("eglWaitSyncKHR"));
if (egl_wait_sync_khr == nullptr) {
// Needs extension: EGL_KHR_wait_sync
return absl::InternalError("Not supported: eglWaitSyncKHR.");
}
EGLint result;
RETURN_IF_ERROR(
TFLITE_GPU_CALL_EGL(*egl_wait_sync_khr, &result, display_, sync_, 0));
return result == EGL_TRUE ? absl::OkStatus()
: absl::InternalError("eglWaitSync failed");
}
absl::Status EglSync::ClientWait() {
RETURN_IF_ERROR(IsEglFenceSyncSupported(display_));
static auto* egl_client_wait_sync_khr =
reinterpret_cast<decltype(&eglClientWaitSyncKHR)>(
eglGetProcAddress("eglClientWaitSyncKHR"));
if (egl_client_wait_sync_khr == nullptr) {
// Needs extension: EGL_KHR_fence_sync (EGL) / GL_OES_EGL_sync (OpenGL ES).
return absl::InternalError("Not supported: eglClientWaitSyncKHR.");
}
EGLint result;
// TODO(akulik): make it active wait for better performance
RETURN_IF_ERROR(
TFLITE_GPU_CALL_EGL(*egl_client_wait_sync_khr, &result, display_, sync_,
EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, EGL_FOREVER_KHR));
return result == EGL_CONDITION_SATISFIED_KHR
? absl::OkStatus()
: absl::InternalError("eglClientWaitSync failed");
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,79 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_CL_EGL_SYNC_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_EGL_SYNC_H_
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include "tensorflow/lite/delegates/gpu/common/status.h"
namespace tflite {
namespace gpu {
namespace cl {
// RAII wrapper for EGL sync object.
// EglSync is moveable but not copyable.
class EglSync {
public:
// Creates a fence in OpenGL command stream. This sync is enqueued and *not*
// flushed.
//
// Depends on EGL_KHR_fence_sync extension.
static absl::Status NewFence(EGLDisplay display, EglSync* sync);
// Creates invalid object.
EglSync() : EglSync(EGL_NO_DISPLAY, EGL_NO_SYNC_KHR) {}
EglSync(EGLDisplay display, EGLSyncKHR sync)
: display_(display), sync_(sync) {}
// Move-only
EglSync(EglSync&& sync);
EglSync& operator=(EglSync&& sync);
EglSync(const EglSync&) = delete;
EglSync& operator=(const EglSync&) = delete;
~EglSync() { Invalidate(); }
// Causes GPU to block and wait until this sync has been signaled.
// This call does not block and returns immediately.
absl::Status ServerWait();
// Causes CPU to block and wait until this sync has been signaled.
absl::Status ClientWait();
// Returns the EGLDisplay on which this instance was created.
EGLDisplay display() const { return display_; }
// Returns the EGLSyncKHR wrapped by this instance.
EGLSyncKHR sync() const { return sync_; }
// Returns true if this instance wraps a valid EGLSync object.
bool is_valid() const { return sync_ != nullptr; }
private:
void Invalidate();
EGLDisplay display_;
EGLSyncKHR sync_;
};
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_EGL_SYNC_H_
@@ -0,0 +1,296 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/cl/environment.h"
#include <utility>
#include <vector>
#include "tensorflow/lite/delegates/gpu/cl/cl_command_queue.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_context.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_device.h"
#include "tensorflow/lite/delegates/gpu/common/gpu_info.h"
#include "tensorflow/lite/delegates/gpu/common/precision.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/task/tensor_desc.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
absl::Status CreateEnvironment(Environment* result, bool shared,
cl_context_properties egl_context,
cl_context_properties egl_display) {
CLDevice gpu;
RETURN_IF_ERROR(CreateDefaultGPUDevice(&gpu));
CLContext context;
if (shared) {
RETURN_IF_ERROR(CreateCLGLContext(gpu, egl_context, egl_display, &context));
} else {
RETURN_IF_ERROR(CreateCLContext(gpu, &context));
}
CLCommandQueue queue;
RETURN_IF_ERROR(CreateCLCommandQueue(gpu, context, &queue));
ProfilingCommandQueue profiling_queue;
RETURN_IF_ERROR(CreateProfilingCommandQueue(gpu, context, &profiling_queue));
*result = Environment(std::move(gpu), std::move(context), std::move(queue),
std::move(profiling_queue));
return result->Init();
}
bool IsGpuSupportsStorageType(const GpuInfo& gpu_info,
TensorStorageType storage_type) {
switch (storage_type) {
case TensorStorageType::TEXTURE_2D:
return !gpu_info.IsAMD();
case TensorStorageType::BUFFER:
return true;
case TensorStorageType::TEXTURE_ARRAY:
return !gpu_info.IsAMD() && gpu_info.SupportsTextureArray();
case TensorStorageType::IMAGE_BUFFER:
return (gpu_info.IsAdreno() || gpu_info.IsAMD() || gpu_info.IsNvidia()) &&
gpu_info.SupportsImageBuffer();
case TensorStorageType::TEXTURE_3D:
return !gpu_info.IsAMD() && gpu_info.SupportsImage3D();
case TensorStorageType::SINGLE_TEXTURE_2D:
return false;
case TensorStorageType::UNKNOWN:
return false;
}
return false;
}
bool IsGpuSupportsPrecision(const GpuInfo& gpu_info,
CalculationsPrecision precision) {
switch (precision) {
case CalculationsPrecision::F32_F16:
case CalculationsPrecision::F16:
return gpu_info.SupportsFP16();
case CalculationsPrecision::F32:
return true;
}
}
} // namespace
Environment::Environment(CLDevice&& device, CLContext&& context,
CLCommandQueue&& queue,
ProfilingCommandQueue&& profiling_queue)
: device_(std::move(device)),
context_(std::move(context)),
queue_(std::move(queue)),
profiling_queue_(std::move(profiling_queue)) {}
Environment::Environment(Environment&& environment)
: device_(std::move(environment.device_)),
context_(std::move(environment.context_)),
queue_(std::move(environment.queue_)),
profiling_queue_(std::move(environment.profiling_queue_)),
program_cache_(std::move(environment.program_cache_)) {}
Environment& Environment::operator=(Environment&& environment) {
if (this != &environment) {
device_ = std::move(environment.device_);
context_ = std::move(environment.context_);
queue_ = std::move(environment.queue_);
profiling_queue_ = std::move(environment.profiling_queue_);
program_cache_ = std::move(environment.program_cache_);
}
return *this;
}
absl::Status Environment::Init() {
if (device().GetInfo().IsAdreno() &&
device().GetInfo().SupportsTextureArray()) {
const auto& adreno_info = device().info_.adreno_info;
// Some Adreno < 600 have bug with one layer texture array. b/131099086
// If we have one layer texture array and will write smt from kernel to this
// texture, we will get zeroes instead of actual values.
// The same kernel will work, if we use texture array with more than one
// layer.
if (adreno_info.IsAdreno3xx() || adreno_info.IsAdreno4xx() ||
adreno_info.IsAdreno5xx()) {
GetDevicePtr()->DisableOneLayerTextureArray();
}
}
return absl::OkStatus();
}
void Environment::SetHighPerformance() const {
// TODO(sorokin) use cl_perf_hint if available
}
void Environment::SetDefaultPerformance() const {
// TODO(sorokin) use cl_perf_hint if available
}
void Environment::SetLowPerformance() const {
// TODO(sorokin) use cl_perf_hint if available
}
std::vector<CalculationsPrecision> Environment::GetSupportedPrecisions() const {
std::vector<CalculationsPrecision> precisions;
for (CalculationsPrecision precision :
{CalculationsPrecision::F32, CalculationsPrecision::F32_F16,
CalculationsPrecision::F16}) {
if (IsSupported(precision)) {
precisions.push_back(precision);
}
}
return precisions;
}
bool Environment::IsSupported(CalculationsPrecision precision) const {
return IsGpuSupportsPrecision(device_.GetInfo(), precision);
}
std::vector<TensorStorageType> Environment::GetSupportedStorages() const {
std::vector<TensorStorageType> storage_types;
for (auto storage_type :
{TensorStorageType::TEXTURE_2D, TensorStorageType::BUFFER,
TensorStorageType::TEXTURE_ARRAY, TensorStorageType::IMAGE_BUFFER,
TensorStorageType::TEXTURE_3D}) {
if (IsSupported(storage_type)) {
storage_types.push_back(storage_type);
}
}
return storage_types;
}
std::vector<TensorStorageType>
Environment::GetSupportedStoragesWithHWZeroClampSupport() const {
std::vector<TensorStorageType> storage_types;
for (auto storage_type :
{TensorStorageType::TEXTURE_2D, TensorStorageType::TEXTURE_ARRAY,
TensorStorageType::TEXTURE_3D}) {
if (IsSupported(storage_type)) {
storage_types.push_back(storage_type);
}
}
return storage_types;
}
bool Environment::IsSupported(TensorStorageType storage_type) const {
return IsGpuSupportsStorageType(device_.GetInfo(), storage_type);
}
TensorStorageType GetFastestStorageType(const GpuInfo& gpu_info) {
if (gpu_info.IsAdreno()) {
if (gpu_info.adreno_info.IsAdreno6xxOrHigher() &&
!gpu_info.opencl_info.IsImage2dFromBufferSupported()) {
return TensorStorageType::TEXTURE_ARRAY;
} else {
return TensorStorageType::TEXTURE_2D;
}
} else if (gpu_info.IsPowerVR()) {
return TensorStorageType::TEXTURE_2D;
} else if (gpu_info.IsMali()) {
return TensorStorageType::TEXTURE_2D;
} else if (gpu_info.IsNvidia()) {
return gpu_info.SupportsImageBuffer() ? TensorStorageType::IMAGE_BUFFER
: TensorStorageType::BUFFER;
} else if (gpu_info.IsAMD()) {
return gpu_info.SupportsImageBuffer() ? TensorStorageType::IMAGE_BUFFER
: TensorStorageType::BUFFER;
} else if (gpu_info.IsIntel()) {
return TensorStorageType::BUFFER;
}
return TensorStorageType::BUFFER;
}
TensorStorageType GetStorageTypeWithMinimalMemoryConsumption(
const GpuInfo& gpu_info) {
if (gpu_info.IsAdreno()) {
if (gpu_info.adreno_info.IsAdreno3xx() ||
gpu_info.adreno_info.IsAdreno4xx()) {
return TensorStorageType::BUFFER;
} else {
if (gpu_info.opencl_info.IsImage2dFromBufferSupported()) {
return TensorStorageType::TEXTURE_2D;
} else {
return TensorStorageType::IMAGE_BUFFER;
}
}
} else if (gpu_info.IsPowerVR()) {
if (gpu_info.opencl_info.IsImage2dFromBufferSupported() &&
CanUseSubBufferForImage2d(gpu_info)) {
return TensorStorageType::TEXTURE_2D;
} else {
return TensorStorageType::BUFFER;
}
} else if (gpu_info.IsMali()) {
if (gpu_info.opencl_info.IsImage2dFromBufferSupported() &&
CanUseSubBufferForImage2d(gpu_info)) {
return TensorStorageType::TEXTURE_2D;
} else {
return TensorStorageType::BUFFER;
}
} else if (gpu_info.IsNvidia()) {
return gpu_info.SupportsImageBuffer() ? TensorStorageType::IMAGE_BUFFER
: TensorStorageType::BUFFER;
} else if (gpu_info.IsAMD()) {
return gpu_info.SupportsImageBuffer() ? TensorStorageType::IMAGE_BUFFER
: TensorStorageType::BUFFER;
} else if (gpu_info.IsIntel()) {
return TensorStorageType::BUFFER;
}
return TensorStorageType::BUFFER;
}
bool CanUseSubBufferForImage2d(const GpuInfo& gpu_info) {
if (!gpu_info.IsCL11OrHigher()) {
return false;
}
if (gpu_info.IsPowerVR() &&
gpu_info.powervr_info.driver_version.branch_main <= 23) {
// 24.2@6603887 - works.
// 1.15@6133110 - doesn't work.
// Segfaults, wrong results at model level.
return false;
}
if (gpu_info.IsNvidia()) {
return false;
}
if (gpu_info.IsMali() &&
(gpu_info.mali_info.IsBifrost() || gpu_info.mali_info.IsMidgard())) {
// Known driver issue on some G72 (Bifrost), G76 (Bifrost), T830 (Midgard),
// and T880 (Midgard) devices.
return false;
}
return true;
}
absl::Status CreateEnvironment(Environment* result) {
CLDevice gpu;
RETURN_IF_ERROR(CreateDefaultGPUDevice(&gpu));
CLContext context;
RETURN_IF_ERROR(CreateCLContext(gpu, &context));
CLCommandQueue queue;
RETURN_IF_ERROR(CreateCLCommandQueue(gpu, context, &queue));
ProfilingCommandQueue profiling_queue;
RETURN_IF_ERROR(CreateProfilingCommandQueue(gpu, context, &profiling_queue));
*result = Environment(std::move(gpu), std::move(context), std::move(queue),
std::move(profiling_queue));
return result->Init();
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,91 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_CL_ENVIRONMENT_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_ENVIRONMENT_H_
#include "tensorflow/lite/delegates/gpu/cl/cl_command_queue.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_context.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_device.h"
#include "tensorflow/lite/delegates/gpu/cl/program_cache.h"
#include "tensorflow/lite/delegates/gpu/common/data_type.h"
#include "tensorflow/lite/delegates/gpu/common/gpu_info.h"
#include "tensorflow/lite/delegates/gpu/common/precision.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/task/tensor_desc.h"
#include "tensorflow/lite/delegates/gpu/common/tensor.h"
namespace tflite {
namespace gpu {
namespace cl {
class Environment {
public:
Environment() = default;
explicit Environment(CLDevice&& device, CLContext&& context,
CLCommandQueue&& queue,
ProfilingCommandQueue&& profiling_queue);
// Move only
Environment(Environment&& environment);
Environment& operator=(Environment&& environment);
Environment(const Environment&) = delete;
Environment& operator=(const Environment&) = delete;
const CLDevice& device() const { return device_; }
CLDevice* GetDevicePtr() { return &device_; }
const CLDevice* GetDevicePtr() const { return &device_; }
CLContext& context() { return context_; }
CLCommandQueue* queue() { return &queue_; }
ProfilingCommandQueue* profiling_queue() { return &profiling_queue_; }
ProgramCache* program_cache() { return &program_cache_; }
const ProgramCache* program_cache() const { return &program_cache_; }
std::vector<CalculationsPrecision> GetSupportedPrecisions() const;
bool IsSupported(CalculationsPrecision precision) const;
std::vector<TensorStorageType> GetSupportedStorages() const;
// returns storage types that support zero clamping when reading OOB in HW
// (Height/Width) dimensions.
std::vector<TensorStorageType> GetSupportedStoragesWithHWZeroClampSupport()
const;
bool IsSupported(TensorStorageType storage_type) const;
absl::Status Init();
void SetHighPerformance() const;
void SetDefaultPerformance() const;
void SetLowPerformance() const; // for energy saving
private:
CLDevice device_;
CLContext context_;
CLCommandQueue queue_;
ProfilingCommandQueue profiling_queue_;
ProgramCache program_cache_;
};
TensorStorageType GetFastestStorageType(const GpuInfo& gpu_info);
TensorStorageType GetStorageTypeWithMinimalMemoryConsumption(
const GpuInfo& gpu_info);
// Checks if image 2D creation from sub-buffer is supported.
bool CanUseSubBufferForImage2d(const GpuInfo& gpu_info);
absl::Status CreateEnvironment(Environment* result);
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_ENVIRONMENT_H_
@@ -0,0 +1,313 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/cl/gl_interop.h"
#ifndef CL_DELEGATE_NO_GL
#include <algorithm>
#include <variant>
#include "absl/strings/str_cat.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_errors.h"
#include "tensorflow/lite/delegates/gpu/gl/gl_call.h"
#include "tensorflow/lite/delegates/gpu/gl/gl_sync.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
#ifndef EGL_VERSION_1_5
typedef void* EGLSync;
#define EGL_SYNC_CL_EVENT 0x30FE
#define EGL_CL_EVENT_HANDLE 0x309C
#define EGL_NO_SYNC 0
#endif /* EGL_VERSION_1_5 */
// TODO(b/131897059): replace with 64 version when EGL 1.5 is available.
// it should use KHR_cl_event2 extension. More details are in b/129974818.
using PFNEGLCREATESYNCPROC = EGLSync(EGLAPIENTRYP)(
EGLDisplay dpy, EGLenum type, const EGLAttrib* attrib_list);
PFNEGLCREATESYNCPROC g_eglCreateSync = nullptr;
} // namespace
absl::Status CreateEglSyncFromClEvent(cl_event event, EGLDisplay display,
EglSync* sync) {
if (!IsEglSyncFromClEventSupported()) {
return absl::UnimplementedError(
"CreateEglSyncFromClEvent is not supported");
}
EGLSync egl_sync;
const EGLAttrib attributes[] = {EGL_CL_EVENT_HANDLE,
reinterpret_cast<EGLAttrib>(event), EGL_NONE};
RETURN_IF_ERROR(TFLITE_GPU_CALL_EGL(g_eglCreateSync, &egl_sync, display,
EGL_SYNC_CL_EVENT, attributes));
if (egl_sync == EGL_NO_SYNC) {
return absl::InternalError("Returned empty EGL sync");
}
*sync = EglSync(display, egl_sync);
return absl::OkStatus();
}
bool IsEglSyncFromClEventSupported() {
// In C++11, static initializers are guaranteed to be evaluated only once.
static bool supported = []() -> bool {
// This function requires EGL 1.5 to work
g_eglCreateSync = reinterpret_cast<PFNEGLCREATESYNCPROC>(
eglGetProcAddress("eglCreateSync"));
// eglQueryString accepts EGL_NO_DISPLAY only starting EGL 1.5
if (!eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS)) {
g_eglCreateSync = nullptr;
}
return (g_eglCreateSync != nullptr);
}();
return supported;
}
absl::Status CreateClEventFromEglSync(cl_context context,
const EglSync& egl_sync, CLEvent* event) {
cl_int error_code;
cl_event new_event = clCreateEventFromEGLSyncKHR(
context, egl_sync.sync(), egl_sync.display(), &error_code);
if (error_code != CL_SUCCESS) {
return absl::InternalError(
absl::StrCat("Unable to create CL sync from EGL sync. ",
CLErrorCodeToString(error_code)));
}
*event = CLEvent(new_event);
return absl::OkStatus();
}
bool IsClEventFromEglSyncSupported(const CLDevice& device) {
return device.GetInfo().SupportsExtension("cl_khr_egl_event");
}
absl::Status CreateClMemoryFromGlBuffer(GLuint gl_ssbo_id,
AccessType access_type,
CLContext* context, CLMemory* memory) {
cl_int error_code;
auto mem = clCreateFromGLBuffer(context->context(), ToClMemFlags(access_type),
gl_ssbo_id, &error_code);
if (error_code != CL_SUCCESS) {
return absl::InternalError(
absl::StrCat("Unable to acquire CL buffer from GL buffer. ",
CLErrorCodeToString(error_code)));
}
*memory = CLMemory(mem, true);
return absl::OkStatus();
}
absl::Status CreateClMemoryFromGlTexture(GLenum texture_target,
GLuint texture_id,
AccessType access_type,
CLContext* context, CLMemory* memory) {
cl_int error_code;
auto mem =
clCreateFromGLTexture(context->context(), ToClMemFlags(access_type),
texture_target, 0, texture_id, &error_code);
if (error_code != CL_SUCCESS) {
return absl::InternalError(
absl::StrCat("Unable to create CL buffer from GL texture. ",
CLErrorCodeToString(error_code)));
}
*memory = CLMemory(mem, true);
return absl::OkStatus();
}
bool IsGlSharingSupported(const CLDevice& device) {
return clCreateFromGLBuffer && clCreateFromGLTexture &&
device.GetInfo().SupportsExtension("cl_khr_gl_sharing");
}
AcquiredGlObjects::~AcquiredGlObjects() { Release({}, nullptr).IgnoreError(); }
absl::Status AcquiredGlObjects::Acquire(
const std::vector<cl_mem>& memory, cl_command_queue queue,
const std::vector<cl_event>& wait_events, CLEvent* acquire_event,
AcquiredGlObjects* objects) {
if (!memory.empty()) {
cl_event new_event;
cl_int error_code = clEnqueueAcquireGLObjects(
queue, memory.size(), memory.data(), wait_events.size(),
wait_events.data(), acquire_event ? &new_event : nullptr);
if (error_code != CL_SUCCESS) {
return absl::InternalError(absl::StrCat("Unable to acquire GL object. ",
CLErrorCodeToString(error_code)));
}
if (acquire_event) {
*acquire_event = CLEvent(new_event);
}
clFlush(queue);
}
*objects = AcquiredGlObjects(memory, queue);
return absl::OkStatus();
}
absl::Status AcquiredGlObjects::Release(
const std::vector<cl_event>& wait_events, CLEvent* release_event) {
if (queue_ && !memory_.empty()) {
cl_event new_event;
cl_int error_code = clEnqueueReleaseGLObjects(
queue_, memory_.size(), memory_.data(), wait_events.size(),
wait_events.data(), release_event ? &new_event : nullptr);
if (error_code != CL_SUCCESS) {
return absl::InternalError(absl::StrCat("Unable to release GL object. ",
CLErrorCodeToString(error_code)));
}
if (release_event) {
*release_event = CLEvent(new_event);
}
clFlush(queue_);
queue_ = nullptr;
}
return absl::OkStatus();
}
GlInteropFabric::GlInteropFabric(EGLDisplay egl_display,
Environment* environment)
: is_egl_sync_supported_(true),
is_egl_to_cl_mapping_supported_(
IsClEventFromEglSyncSupported(environment->device())),
is_cl_to_egl_mapping_supported_(IsEglSyncFromClEventSupported()),
egl_display_(egl_display),
context_(environment->context().context()),
queue_(environment->queue()->queue()) {}
void GlInteropFabric::RegisterMemory(cl_mem memory) {
memory_.push_back(memory);
}
void GlInteropFabric::UnregisterMemory(cl_mem memory) {
auto it = std::find(memory_.begin(), memory_.end(), memory);
if (it != memory_.end()) {
memory_.erase(it);
}
}
absl::Status GlInteropFabric::Start() {
if (!is_enabled()) {
return absl::OkStatus();
}
// In GL-CL interoperability, we need to make sure GL finished processing of
// all commands that might affect GL objects. There are a few ways:
// a) glFinish
// slow, but portable
// b) EglSync + ClientWait
// faster alternative for glFinish, but still slow as it stalls GPU
// pipeline.
// c) EglSync->CLEvent or GlSync->CLEvent mapping
// Fast, as it allows to map sync to CL event and use it as a dependency
// later without stalling GPU pipeline.
CLEvent inbound_event;
std::vector<cl_event> inbound_events;
if (is_egl_sync_supported_) {
EglSync sync;
RETURN_IF_ERROR(EglSync::NewFence(egl_display_, &sync));
if (is_egl_to_cl_mapping_supported_) {
// (c) EglSync->CLEvent or GlSync->CLEvent mapping
glFlush();
RETURN_IF_ERROR(CreateClEventFromEglSync(context_, sync, &inbound_event));
inbound_events.push_back(inbound_event.event());
} else {
// (b) EglSync + ClientWait
RETURN_IF_ERROR(sync.ClientWait());
}
} else {
// (a) glFinish / GL fence sync
RETURN_IF_ERROR(gl::GlActiveSyncWait());
}
// Acquire all GL objects needed while processing.
return AcquiredGlObjects::Acquire(memory_, queue_, inbound_events, nullptr,
&gl_objects_);
}
absl::Status GlInteropFabric::Finish() {
if (!is_enabled()) {
return absl::OkStatus();
}
CLEvent outbound_event;
RETURN_IF_ERROR(gl_objects_.Release({}, &outbound_event));
// if (is_egl_sync_supported_ && is_cl_to_egl_mapping_supported_) {
// EglSync egl_outbound_sync;
// RETURN_IF_ERROR(CreateEglSyncFromClEvent(outbound_event.event(),
// egl_display_,
// &egl_outbound_sync));
// // Instruct GL pipeline to wait until corresponding CL event is signaled.
// RETURN_IF_ERROR(egl_outbound_sync.ServerWait());
// glFlush();
// } else {
// // Slower option if proper sync is not supported. It is equivalent to
// // clFinish, but, hopefully, faster.
// outbound_event.Wait();
// }
// This slow sync is the only working solution right now. We have to debug why
// above version is not working fast and reliable.
outbound_event.Wait();
return absl::OkStatus();
}
GlClBufferCopier::GlClBufferCopier(const TensorObjectDef& input_def,
const TensorObjectDef& output_def,
Environment* environment) {
queue_ = environment->queue();
size_in_bytes_ =
NumElements(input_def) * SizeOf(input_def.object_def.data_type);
}
absl::Status GlClBufferCopier::Convert(const TensorObject& input_obj,
const TensorObject& output_obj) {
if (std::holds_alternative<OpenGlBuffer>(input_obj)) {
auto ssbo = std::get_if<OpenGlBuffer>(&input_obj);
auto cl_mem = std::get_if<OpenClBuffer>(&output_obj);
RETURN_IF_ERROR(
TFLITE_GPU_CALL_GL(glBindBuffer, GL_SHADER_STORAGE_BUFFER, ssbo->id));
void* ptr;
RETURN_IF_ERROR(TFLITE_GPU_CALL_GL(glMapBufferRange, &ptr,
GL_SHADER_STORAGE_BUFFER, 0,
size_in_bytes_, GL_MAP_READ_BIT));
RETURN_IF_ERROR(
queue_->EnqueueWriteBuffer(cl_mem->memobj, size_in_bytes_, ptr));
RETURN_IF_ERROR(
TFLITE_GPU_CALL_GL(glUnmapBuffer, GL_SHADER_STORAGE_BUFFER));
} else {
auto cl_mem = std::get_if<OpenClBuffer>(&input_obj);
auto ssbo = std::get_if<OpenGlBuffer>(&output_obj);
RETURN_IF_ERROR(
TFLITE_GPU_CALL_GL(glBindBuffer, GL_SHADER_STORAGE_BUFFER, ssbo->id));
void* ptr;
RETURN_IF_ERROR(TFLITE_GPU_CALL_GL(glMapBufferRange, &ptr,
GL_SHADER_STORAGE_BUFFER, 0,
size_in_bytes_, GL_MAP_WRITE_BIT));
RETURN_IF_ERROR(
queue_->EnqueueReadBuffer(cl_mem->memobj, size_in_bytes_, ptr));
RETURN_IF_ERROR(
TFLITE_GPU_CALL_GL(glUnmapBuffer, GL_SHADER_STORAGE_BUFFER));
}
return absl::OkStatus();
}
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // CL_DELEGATE_NO_GL
@@ -0,0 +1,271 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_CL_GL_INTEROP_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_GL_INTEROP_H_
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <vector>
#include "tensorflow/lite/delegates/gpu/cl/cl_command_queue.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_device.h"
#include "tensorflow/lite/delegates/gpu/cl/environment.h"
#include "tensorflow/lite/delegates/gpu/common/access_type.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/spi.h"
#ifndef CL_DELEGATE_NO_GL
#include "tensorflow/lite/delegates/gpu/cl/cl_context.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_event.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_memory.h"
#include "tensorflow/lite/delegates/gpu/cl/egl_sync.h"
#include "tensorflow/lite/delegates/gpu/cl/opencl_wrapper.h"
#include "tensorflow/lite/delegates/gpu/gl/portable_gl31.h"
#endif
namespace tflite {
namespace gpu {
namespace cl {
#ifndef CL_DELEGATE_NO_GL
// Creates an EglSync from OpenCL event. Source event does not need to outlive
// returned sync and could be safely destroyed.
//
// Depends on EGL 1.5.
absl::Status CreateEglSyncFromClEvent(cl_event event, EGLDisplay display,
EglSync* sync);
// Returns true if 'CreateEglSyncFromClEvent' is supported.
bool IsEglSyncFromClEventSupported();
// Creates CL event from EGL sync.
// Created event could only be consumed by AcquiredGlObject::Acquire call as
// a 'wait_event'.
absl::Status CreateClEventFromEglSync(cl_context context,
const EglSync& egl_sync, CLEvent* event);
// Returns true if 'CreateClEventFromEglSync' is supported.
bool IsClEventFromEglSyncSupported(const CLDevice& device);
// Creates new CL memory object from OpenGL buffer.
absl::Status CreateClMemoryFromGlBuffer(GLuint gl_ssbo_id,
AccessType access_type,
CLContext* context, CLMemory* memory);
// Creates new CL memory object from OpenGL texture.
absl::Status CreateClMemoryFromGlTexture(GLenum texture_target,
GLuint texture_id,
AccessType access_type,
CLContext* context, CLMemory* memory);
// Returns true if GL objects could be shared with OpenCL context.
bool IsGlSharingSupported(const CLDevice& device);
// RAII-wrapper for GL objects acquired into CL context.
class AcquiredGlObjects {
public:
static bool IsSupported(const CLDevice& device);
AcquiredGlObjects() : AcquiredGlObjects({}, nullptr) {}
// Quitely releases OpenGL objects. It is recommended to call Release()
// explicitly to properly handle potential errors.
~AcquiredGlObjects();
// Acquires memory from the OpenGL context. Memory must be created by either
// CreateClMemoryFromGlBuffer or CreateClMemoryFromGlTexture calls.
// If 'acquire_event' is not nullptr, it will be signared once acquisition is
// complete.
static absl::Status Acquire(const std::vector<cl_mem>& memory,
cl_command_queue queue,
const std::vector<cl_event>& wait_events,
CLEvent* acquire_event /* optional */,
AcquiredGlObjects* objects);
// Releases OpenCL memory back to OpenGL context. If 'release_event' is not
// nullptr, it will be signalled once release is complete.
absl::Status Release(const std::vector<cl_event>& wait_events,
CLEvent* release_event /* optional */);
private:
AcquiredGlObjects(const std::vector<cl_mem>& memory, cl_command_queue queue)
: memory_(memory), queue_(queue) {}
std::vector<cl_mem> memory_;
cl_command_queue queue_;
};
// Incapsulates all complicated GL-CL synchronization. It manages life time of
// all appropriate events to ensure fast synchronization whenever possible.
class GlInteropFabric {
public:
GlInteropFabric(EGLDisplay egl_display, Environment* environment);
// Ensures proper GL->CL synchronization is in place before
// GL objects that are mapped to CL objects are used.
absl::Status Start();
// Puts appropriate CL->GL synchronization after all work is complete.
absl::Status Finish();
// Registers memory to be used from GL context. Such CL memory object must
// be created with CreateClMemoryFromGlBuffer or CreateClMemoryFromGlTexture
// call.
void RegisterMemory(cl_mem memory);
// Unregisters memory registered with RegisterMemory call.
void UnregisterMemory(cl_mem memory);
private:
bool is_enabled() const { return egl_display_ && !memory_.empty(); }
bool is_egl_sync_supported_;
bool is_egl_to_cl_mapping_supported_;
bool is_cl_to_egl_mapping_supported_;
const EGLDisplay egl_display_;
cl_context context_;
cl_command_queue queue_;
std::vector<cl_mem> memory_;
AcquiredGlObjects gl_objects_; // transient during Start/Finish calls.
};
// Copies data from(to) GL buffer to(from) CL buffer using CPU.
class GlClBufferCopier : public TensorObjectConverter {
public:
static bool IsSupported(const ObjectDef& input, const ObjectDef& output) {
return input.data_type == output.data_type &&
input.data_layout == output.data_layout &&
((input.object_type == ObjectType::OPENGL_SSBO &&
output.object_type == ObjectType::OPENCL_BUFFER) ||
(input.object_type == ObjectType::OPENCL_BUFFER &&
output.object_type == ObjectType::OPENGL_SSBO));
}
GlClBufferCopier(const TensorObjectDef& input_def,
const TensorObjectDef& output_def, Environment* environment);
absl::Status Convert(const TensorObject& input_obj,
const TensorObject& output_obj) override;
private:
size_t size_in_bytes_;
CLCommandQueue* queue_ = nullptr;
};
#else
class EglSync;
class CLEvent;
class CLContext;
class CLMemory;
inline absl::Status CreateEglSyncFromClEvent(cl_event event, EGLDisplay display,
EglSync* sync) {
return absl::UnimplementedError(
"GL interop is not supported when compiled with CL_DELEGATE_NO_GL");
}
inline bool IsEglSyncFromClEventSupported() { return false; }
inline absl::Status CreateClEventFromEglSync(cl_context context,
const EglSync& egl_sync,
CLEvent* event) {
return absl::UnimplementedError(
"GL interop is not supported when compiled with CL_DELEGATE_NO_GL");
}
inline bool IsClEventFromEglSyncSupported(const CLDevice& device) {
return false;
}
inline absl::Status CreateClMemoryFromGlBuffer(GLuint gl_ssbo_id,
AccessType access_type,
CLContext* context,
CLMemory* memory) {
return absl::UnimplementedError(
"GL interop is not supported when compiled with CL_DELEGATE_NO_GL");
}
inline absl::Status CreateClMemoryFromGlTexture(GLenum texture_target,
GLuint texture_id,
AccessType access_type,
CLContext* context,
CLMemory* memory) {
return absl::UnimplementedError(
"GL interop is not supported when compiled with CL_DELEGATE_NO_GL");
}
inline bool IsGlSharingSupported(const CLDevice& device) { return false; }
class AcquiredGlObjects {
public:
static bool IsSupported(const CLDevice& device) { return false; }
AcquiredGlObjects() = default;
~AcquiredGlObjects() = default;
static absl::Status Acquire(const std::vector<cl_mem>& memory,
cl_command_queue queue,
const std::vector<cl_event>& wait_events,
CLEvent* acquire_event /* optional */,
AcquiredGlObjects* objects) {
return absl::UnimplementedError(
"GL interop is not supported when compiled with CL_DELEGATE_NO_GL");
}
absl::Status Release(const std::vector<cl_event>& wait_events,
CLEvent* release_event /* optional */) {
return absl::OkStatus();
}
};
class GlInteropFabric {
public:
GlInteropFabric(EGLDisplay egl_display, Environment* environment) {}
absl::Status Start() { return absl::OkStatus(); }
absl::Status Finish() { return absl::OkStatus(); }
void RegisterMemory(cl_mem memory) {}
void UnregisterMemory(cl_mem memory) {}
};
class GlClBufferCopier : public TensorObjectConverter {
public:
static bool IsSupported(const ObjectDef& input, const ObjectDef& output) {
return false;
}
GlClBufferCopier(const TensorObjectDef& input_def,
const TensorObjectDef& output_def,
Environment* environment) {}
absl::Status Convert(const TensorObject& input_obj,
const TensorObject& output_obj) override {
return absl::UnimplementedError(
"GL interop is not supported when compiled with CL_DELEGATE_NO_GL");
}
};
#endif // CL_DELEGATE_NO_GL
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_GL_INTEROP_H_
@@ -0,0 +1,31 @@
/* Copyright 2026 The TensorFlow Authors. All Rights Reserved.
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 <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/gl_interop.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST(GlInteropNoGlTest, IsEglSyncFromClEventSupported) {
EXPECT_FALSE(IsEglSyncFromClEventSupported());
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,417 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/cl/gpu_api_delegate.h"
#include <cstdint>
#include "absl/types/span.h"
#include "tensorflow/lite/builtin_ops.h"
#include "tensorflow/lite/delegates/gpu/api.h"
#include "tensorflow/lite/delegates/gpu/cl/api.h"
#include "tensorflow/lite/delegates/gpu/cl/opencl_wrapper.h"
#include "tensorflow/lite/delegates/gpu/cl/tensor_type_util.h"
#include "tensorflow/lite/delegates/gpu/common/model.h"
#include "tensorflow/lite/delegates/gpu/common/model_builder.h"
#include "tensorflow/lite/delegates/gpu/common/model_transformer.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/transformations/model_transformations.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
// Forward declarations.
TfLiteStatus DelegatePrepare(TfLiteContext* context, TfLiteDelegate* delegate);
InferencePriority ToPriority(int32_t priority) {
switch (priority) {
case TfLiteGpuInferencePriority::
TFLITE_GPU_INFERENCE_PRIORITY_MAX_PRECISION:
return InferencePriority::MAX_PRECISION;
case TfLiteGpuInferencePriority::TFLITE_GPU_INFERENCE_PRIORITY_MIN_LATENCY:
return InferencePriority::MIN_LATENCY;
}
return InferencePriority::MAX_PRECISION;
}
DataType ToDataType(TfLiteType data_type) {
switch (data_type) {
case kTfLiteFloat16:
return DataType::FLOAT16;
case kTfLiteFloat32:
return DataType::FLOAT32;
default:
return DataType::UNKNOWN;
}
}
DataLayout ToDataLayoutFromTFL(TfLiteGpuDataLayout data_layout) {
switch (data_layout) {
case TFLITE_GPU_DATA_LAYOUT_BHWC:
return DataLayout::BHWC;
case TFLITE_GPU_DATA_LAYOUT_DHWC4:
return DataLayout::DHWC4;
default:
return DataLayout::UNKNOWN;
}
}
class Delegate {
public:
explicit Delegate(const TfLiteGpuDelegateOptions_New* options) {
if (options) {
options_ = *options;
} else {
// Default options.
options_.compile_options.precision_loss_allowed = 0;
options_.compile_options.inference_priority = TfLiteGpuInferencePriority::
TFLITE_GPU_INFERENCE_PRIORITY_MAX_PRECISION;
options_.egl_display = EGL_NO_DISPLAY;
options_.egl_context = EGL_NO_CONTEXT;
options_.serialized_binary_cache_data = nullptr;
options_.serialized_binary_cache_size = 0;
}
}
absl::Status Prepare(TfLiteContext* context,
const TfLiteDelegateParams* delegate_params) {
// Extract TFLite delegate execution plan from the context and convert it
// into GraphFloat32.
GraphFloat32 graph;
RETURN_IF_ERROR(BuildModel(context, delegate_params, &graph));
// Apply general transformations on the graph.
ModelTransformer transformer(&graph);
if (!ApplyModelTransformations(&transformer)) {
return absl::InternalError("Graph transformations failed");
}
InferenceEnvironmentOptions env_options;
env_options.egl_context = options_.egl_context;
env_options.egl_display = options_.egl_display;
env_options.serialized_binary_cache = {
options_.serialized_binary_cache_data,
options_.serialized_binary_cache_size};
InferenceEnvironmentProperties properties;
absl::Status status =
NewInferenceEnvironment(env_options, &environment_, &properties);
if (!properties.is_opencl_available) {
TF_LITE_KERNEL_LOG(context, "TfLiteGpuDelegate: OpenCL is not available");
}
if (!properties.is_gl_sharing_supported) {
TF_LITE_KERNEL_LOG(context,
"TfLiteGpuDelegate: GL sharing is not supported");
}
if (!properties.is_cl_to_gl_fast_sync_supported) {
TF_LITE_KERNEL_LOG(
context, "TfLiteGpuDelegate: fast CL to GL sync is not supported");
}
if (!properties.is_gl_to_cl_fast_sync_supported) {
TF_LITE_KERNEL_LOG(
context, "TfLiteGpuDelegate: fast GL to CL sync is not supported");
}
RETURN_IF_ERROR(status);
std::vector<uint32_t> input_refs;
{
const auto& inputs = graph.inputs();
input_refs.reserve(inputs.size());
for (auto input : inputs) {
input_refs.push_back(input->tensor.ref);
}
}
std::vector<uint32_t> output_refs;
{
const auto& outputs = graph.outputs();
output_refs.reserve(outputs.size());
for (auto output : outputs) {
output_refs.push_back(output->tensor.ref);
}
}
InferenceOptions options;
options.usage = InferenceUsage::FAST_SINGLE_ANSWER;
if (options_.compile_options.precision_loss_allowed == 0) {
options.priority1 = InferencePriority::MAX_PRECISION;
switch (options_.compile_options.inference_priority) {
case TfLiteGpuInferencePriority::
TFLITE_GPU_INFERENCE_PRIORITY_MAX_PRECISION:
options.priority2 = InferencePriority::MIN_MEMORY_USAGE;
options.priority3 = InferencePriority::MIN_LATENCY;
break;
case TfLiteGpuInferencePriority::
TFLITE_GPU_INFERENCE_PRIORITY_MIN_LATENCY:
options.priority2 = InferencePriority::MIN_LATENCY;
options.priority3 = InferencePriority::MIN_MEMORY_USAGE;
break;
case TfLiteGpuInferencePriority::
TFLITE_GPU_INFERENCE_PRIORITY_MIN_MEMORY_USAGE:
options.priority2 = InferencePriority::MIN_MEMORY_USAGE;
options.priority3 = InferencePriority::MIN_LATENCY;
break;
}
} else {
switch (options_.compile_options.inference_priority) {
case TfLiteGpuInferencePriority::
TFLITE_GPU_INFERENCE_PRIORITY_MAX_PRECISION:
options.priority1 = InferencePriority::MIN_LATENCY;
options.priority2 = InferencePriority::MAX_PRECISION;
options.priority3 = InferencePriority::MIN_MEMORY_USAGE;
break;
case TfLiteGpuInferencePriority::
TFLITE_GPU_INFERENCE_PRIORITY_MIN_LATENCY:
options.priority1 = InferencePriority::MIN_LATENCY;
options.priority2 = InferencePriority::MIN_MEMORY_USAGE;
options.priority3 = InferencePriority::MAX_PRECISION;
break;
case TfLiteGpuInferencePriority::
TFLITE_GPU_INFERENCE_PRIORITY_MIN_MEMORY_USAGE:
options.priority1 = InferencePriority::MIN_MEMORY_USAGE;
options.priority2 = InferencePriority::MIN_LATENCY;
options.priority3 = InferencePriority::MAX_PRECISION;
break;
}
}
std::unique_ptr<InferenceBuilder> builder;
RETURN_IF_ERROR(
environment_->NewInferenceBuilder(options, std::move(graph), &builder));
// At this point tflite didn't allocate tensors yet, therefore, collect
// indices and set all input and output tensors from tflite later.
input_indices_.reserve(input_refs.size());
for (auto tensor_index : input_refs) {
int object_index = input_indices_.size();
input_indices_.push_back(tensor_index);
RETURN_IF_ERROR(
builder->SetInputObjectDef(object_index, GetObjectDef(tensor_index)));
}
output_indices_.reserve(output_refs.size());
for (auto tensor_index : output_refs) {
int object_index = output_indices_.size();
output_indices_.push_back(tensor_index);
RETURN_IF_ERROR(builder->SetOutputObjectDef(object_index,
GetObjectDef(tensor_index)));
}
return builder->Build(&runner_);
}
absl::Status SetInputsAndOutputs(TfLiteContext* context) {
int i = 0;
for (auto index : input_indices_) {
RETURN_IF_ERROR(
runner_->SetInputObject(i++, GetTensorObject(index, context)));
}
i = 0;
for (auto index : output_indices_) {
RETURN_IF_ERROR(
runner_->SetOutputObject(i++, GetTensorObject(index, context)));
}
return absl::OkStatus();
}
absl::Status Invoke(TfLiteContext* context) {
RETURN_IF_ERROR(SetInputsAndOutputs(context));
return runner_->Run();
}
void BindGlBufferToTensor(GLuint buffer_id, int tensor_index,
DataType data_type, DataLayout data_layout) {
// At this point the delegate haven't seen a model yet. Therefore, just
// record what object gets assigned.
if (tensor_index >= tensors_.size()) {
tensors_.resize(tensor_index + 1);
}
TensorObjectDef def;
def.object_def.data_type = data_type;
def.object_def.data_layout = data_layout;
def.object_def.object_type = ObjectType::OPENGL_SSBO;
def.object_def.user_provided = true;
def.dimensions = Dimensions(0, 0, 0, 0);
OpenGlBuffer buffer;
buffer.id = buffer_id;
TensorObject obj = buffer;
tensors_[tensor_index] = std::make_pair(obj, def);
}
ObjectDef GetObjectDef(int index) const {
if (index < tensors_.size() && IsValid(tensors_[index].second)) {
return tensors_[index].second.object_def;
}
ObjectDef default_object_def;
default_object_def.data_type = DataType::FLOAT32;
default_object_def.data_layout = DataLayout::BHWC;
default_object_def.object_type = ObjectType::CPU_MEMORY;
default_object_def.user_provided = true;
return default_object_def;
}
TensorObject GetTensorObject(int index, TfLiteContext* context) const {
if (index < tensors_.size() &&
IsValid(tensors_[index].second, tensors_[index].first)) {
return tensors_[index].first;
}
auto& tensor = context->tensors[index];
return MakeCpuMemory(absl::MakeSpan(tensor.data.raw, tensor.bytes));
}
TfLiteDelegate* tflite_delegate() { return &delegate_; }
bool SupportsGlObjects() const {
return options_.egl_context != EGL_NO_CONTEXT &&
options_.egl_display != EGL_NO_DISPLAY;
}
absl::Span<const uint8_t> GetSerializedBinaryCache() {
binary_cache_ = environment_->GetSerializedBinaryCache();
return binary_cache_;
}
private:
TfLiteDelegate delegate_ = {
reinterpret_cast<void*>(this), // .data_
DelegatePrepare, // .Prepare
nullptr, // .CopyFromBufferHandle
nullptr, // .CopyToBufferHandle
nullptr, // .FreeBufferHandle
kTfLiteDelegateFlagsNone, // .flags
};
TfLiteGpuDelegateOptions_New options_;
std::unique_ptr<InferenceEnvironment> environment_;
std::unique_ptr<InferenceRunner> runner_;
std::vector<int64_t> input_indices_;
std::vector<int64_t> output_indices_;
std::vector<uint8_t> binary_cache_;
std::vector<std::pair<TensorObject, TensorObjectDef>> tensors_;
};
inline Delegate* GetDelegate(TfLiteNode* node) {
return reinterpret_cast<Delegate*>(node->user_data);
}
inline Delegate* GetDelegate(TfLiteDelegate* delegate) {
return reinterpret_cast<Delegate*>(delegate->data_);
}
TfLiteStatus DelegatePrepare(TfLiteContext* context, TfLiteDelegate* delegate) {
const TfLiteRegistration kRegistration = {
// .init
[](TfLiteContext* context, const char* buffer, size_t) -> void* {
const auto* params =
reinterpret_cast<const TfLiteDelegateParams*>(buffer);
auto* gpu_delegate = GetDelegate(params->delegate);
// Everything below should happen in prepare function call, but TFLite
// for whatever reason forbids that.
const auto status = gpu_delegate->Prepare(context, params);
if (!status.ok()) {
TF_LITE_KERNEL_LOG(context, "TfLiteGpuDelegate Init: %s",
std::string(status.message()).c_str());
return nullptr;
}
return gpu_delegate;
},
// .free
[](TfLiteContext*, void* buffer) -> void {},
// .prepare
[](TfLiteContext* context, TfLiteNode* node) -> TfLiteStatus {
if (!node->user_data) {
TF_LITE_KERNEL_LOG(
context,
"TfLiteGpuDelegate Prepare: delegate is not initialized");
return kTfLiteError;
}
// TODO(akulik): tflite tensors are not allocated here either. It would
// be good to set inputs and outputs only once here instead of setting
// them every time in .invoke.
return kTfLiteOk;
},
// .invoke
[](TfLiteContext* context, TfLiteNode* node) -> TfLiteStatus {
const auto status = GetDelegate(node)->Invoke(context);
if (!status.ok()) {
TF_LITE_KERNEL_LOG(context, "TfLiteGpuDelegate Invoke: %s",
std::string(status.message()).c_str());
return kTfLiteError;
}
return kTfLiteOk;
},
nullptr, // .profiling_string
0, // .builtin_code
"TfLiteGpuDelegate_New", // .custom_name
1, // .version
};
TfLiteIntArray* ops_to_replace = GetOpsToReplace(context);
const auto status = context->ReplaceNodeSubsetsWithDelegateKernels(
context, kRegistration, ops_to_replace, delegate);
TfLiteIntArrayFree(ops_to_replace);
return status;
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
TfLiteDelegate* TfLiteGpuDelegateCreate_New(
const TfLiteGpuDelegateOptions_New* options) {
auto* gpu_delegate = new tflite::gpu::cl::Delegate(options);
return gpu_delegate ? gpu_delegate->tflite_delegate() : nullptr;
}
void TfLiteGpuDelegateDelete_New(TfLiteDelegate* delegate) {
delete tflite::gpu::cl::GetDelegate(delegate);
}
TFL_CAPI_EXPORT TfLiteStatus TfLiteGpuDelegateBindGlBufferToTensor(
TfLiteDelegate* delegate, GLuint buffer_id, int tensor_index,
TfLiteType data_type, TfLiteGpuDataLayout data_layout) {
auto* gpu_delegate = tflite::gpu::cl::GetDelegate(delegate);
if (!gpu_delegate) {
return kTfLiteError;
}
if (!gpu_delegate->SupportsGlObjects()) {
return kTfLiteError;
}
auto type = tflite::gpu::cl::ToDataType(data_type);
if (type == tflite::gpu::DataType::UNKNOWN) {
return kTfLiteError;
}
auto layout = tflite::gpu::cl::ToDataLayoutFromTFL(data_layout);
if (layout == tflite::gpu::DataLayout::UNKNOWN) {
return kTfLiteError;
}
gpu_delegate->BindGlBufferToTensor(buffer_id, tensor_index, type, layout);
return kTfLiteOk;
}
bool TfLiteGpuDelegateGetSerializedBinaryCache(TfLiteDelegate* delegate,
size_t* size,
const uint8_t** data) {
*size = 0;
auto* gpu_delegate = tflite::gpu::cl::GetDelegate(delegate);
if (!gpu_delegate) {
return false;
}
auto cache = gpu_delegate->GetSerializedBinaryCache();
if (cache.empty()) {
return false;
}
*size = cache.size();
*data = cache.data();
return true;
}
@@ -0,0 +1,107 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_CL_GPU_API_DELEGATE_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_GPU_API_DELEGATE_H_
#define GL_NO_PROTOTYPES
#define EGL_NO_PROTOTYPES
#include <EGL/egl.h>
#include <GLES3/gl31.h>
#undef GL_NO_PROTOTYPES
#undef EGL_NO_PROTOTYPES
#include <stdint.h>
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/delegates/gpu/delegate.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// Shader compilation options.
typedef struct {
// When set to zero, computations are carried out in 32-bit floating point.
// Otherwise, the GPU may quantify tensors, downcast values, process in FP16
// (recommended).
int32_t precision_loss_allowed;
// Priority is defined in TfLiteGpuInferencePriority.
int32_t inference_priority;
} TfLiteGpuCompileOptions_New;
typedef struct {
TfLiteGpuCompileOptions_New compile_options;
// [Optional]
// Whenever EGL display and EGL context are set, corresponding OpenCL context
// will be created.
// These variables are required when using GL objects as inputs or outputs.
EGLDisplay egl_display;
EGLContext egl_context;
// [Optional]
// Contains data returned from TfLiteGpuDelegateGetSerializedBinaryCache call.
// Invalid or incompatible data will be discarded. Compiled binary may become
// incompatible when GPU driver is updated.
const uint8_t* serialized_binary_cache_data;
size_t serialized_binary_cache_size;
} TfLiteGpuDelegateOptions_New;
// Creates a new delegate instance that need to be destroyed with
// TfLiteGpuDelegateDelete_New when delegate is no longer used by TFLite.
// When `options` is set to `nullptr`, the following default values are used:
// .compile_options = {
// .precision_loss_allowed = false,
// }
// .egl_display = EGL_NO_DISPLAY;
// .egl_context = EGL_NO_CONTEXT;
TFL_CAPI_EXPORT TfLiteDelegate* TfLiteGpuDelegateCreate_New(
const TfLiteGpuDelegateOptions_New* options);
// Destroys a delegate created with `TfLiteGpuDelegateCreate_New` call.
TFL_CAPI_EXPORT void TfLiteGpuDelegateDelete_New(TfLiteDelegate* delegate);
typedef enum {
TFLITE_GPU_DATA_LAYOUT_BHWC = 0,
TFLITE_GPU_DATA_LAYOUT_DHWC4 = 1,
} TfLiteGpuDataLayout;
// Binds GL shader storage object to an input or an output tensor in the
// initialized delegate. Bound buffer should have sufficient storage to
// accommodate all elements of a tensor.
//
// Supports data of kTfliteFloat16 or kTfliteFloat32 types in BHWC or DHWC4 data
// layouts.
//
// *** Must be called *before* `Interpreter::ModifyGraphWithDelegate`. ***
TFL_CAPI_EXPORT TfLiteStatus TfLiteGpuDelegateBindGlBufferToTensor(
TfLiteDelegate* delegate, GLuint buffer_id, int tensor_index,
TfLiteType data_type, TfLiteGpuDataLayout data_layout);
// Returns opaque binary blob that contains a collection of cached OpenCL
// binaries. Returned data could be re-used later to speed up initialization
// time when new delegate is created for the same model.
// Returned data is valid only if used on the same device, otherwise it will
// not be compatible and will be discarded.
TFL_CAPI_EXPORT bool TfLiteGpuDelegateGetSerializedBinaryCache(
TfLiteDelegate* delegate, size_t* size, const uint8_t** data);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_GPU_API_DELEGATE_H_
@@ -0,0 +1,73 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_CL_GPU_OBJECT_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_GPU_OBJECT_H_
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "tensorflow/lite/delegates/gpu/cl/opencl_wrapper.h"
#include "tensorflow/lite/delegates/gpu/common/access_type.h"
#include "tensorflow/lite/delegates/gpu/common/data_type.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/task/gpu_object_desc.h"
namespace tflite {
namespace gpu {
namespace cl {
struct GPUResourcesWithValue {
GenericGPUResourcesWithValue generic;
std::vector<std::pair<std::string, cl_mem>> buffers;
std::vector<std::pair<std::string, cl_mem>> images2d;
std::vector<std::pair<std::string, cl_mem>> image2d_arrays;
std::vector<std::pair<std::string, cl_mem>> images3d;
std::vector<std::pair<std::string, cl_mem>> image_buffers;
std::vector<std::pair<std::string, cl_mem>> custom_memories;
void AddFloat(const std::string& name, float value) {
generic.AddFloat(name, value);
}
void AddInt(const std::string& name, int value) {
generic.AddInt(name, value);
}
};
class GPUObject {
public:
GPUObject() = default;
// Move only
GPUObject(GPUObject&& obj_desc) = default;
GPUObject& operator=(GPUObject&& obj_desc) = default;
GPUObject(const GPUObject&) = delete;
GPUObject& operator=(const GPUObject&) = delete;
virtual ~GPUObject() = default;
virtual absl::Status GetGPUResources(
const GPUObjectDescriptor* obj_ptr,
GPUResourcesWithValue* resources) const = 0;
};
using GPUObjectPtr = std::unique_ptr<GPUObject>;
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_GPU_OBJECT_H_
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,229 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_CL_INFERENCE_CONTEXT_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_INFERENCE_CONTEXT_H_
#include <cstdint>
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "tensorflow/lite/delegates/gpu/cl/buffer.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_command_buffer.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_command_queue.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_operation.h"
#include "tensorflow/lite/delegates/gpu/cl/environment.h"
#include "tensorflow/lite/delegates/gpu/cl/gpu_object.h"
#include "tensorflow/lite/delegates/gpu/cl/opencl_wrapper.h"
#include "tensorflow/lite/delegates/gpu/cl/recordable_queue_builder.h"
#include "tensorflow/lite/delegates/gpu/cl/serialization_generated.h"
#include "tensorflow/lite/delegates/gpu/cl/tensor.h"
#include "tensorflow/lite/delegates/gpu/common/gpu_model.h"
#include "tensorflow/lite/delegates/gpu/common/model.h"
#include "tensorflow/lite/delegates/gpu/common/model_hints.h"
#include "tensorflow/lite/delegates/gpu/common/precision.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/task/tensor_desc.h"
#include "tensorflow/lite/delegates/gpu/common/tensor.h"
namespace tflite {
namespace gpu {
namespace cl {
struct CLNode {
ClOperation cl_operation;
std::vector<ValueId> inputs;
std::vector<ValueId> outputs;
// Mostly for debug purposes.
std::string name;
CLNode() = default;
CLNode(CLNode&& node) = default;
CLNode& operator=(CLNode&& node) = default;
CLNode(const CLNode&) = delete;
CLNode& operator=(const CLNode&) = delete;
};
enum class TensorType { kVariable, kConst, kExternal, kRuntime };
class InferenceContext {
public:
absl::Status InitFromGraph(const CreateGpuModelInfo& create_info,
const GraphFloat32& graph, Environment* env,
std::vector<uint8_t>* serialized_model = nullptr);
absl::Status InitFromGpuModel(
const CreateGpuModelInfo& create_info, GpuModel* gpu_model,
Environment* env, std::vector<uint8_t>* serialized_model = nullptr,
Buffer* shared_buffer = nullptr);
absl::Status AddToCommandBuffer(cl_command_buffer_khr cb);
// Applies OpenCL-specific transformations to the graph before the
// initialization. These transformations are either impossible or useless in
// other backends.
absl::Status InitFromGraphWithTransforms(
const CreateGpuModelInfo& create_info, GraphFloat32* graph,
Environment* env, std::vector<uint8_t>* serialized_model = nullptr);
void FlushQueue(CLCommandQueue* queue);
absl::Status AddToQueue(CLCommandQueue* queue);
absl::Status Profile(ProfilingCommandQueue* queue, ProfilingInfo* result);
// for profiling and memory statistics
uint64_t GetSizeOfMemoryAllocatedForIntermediateTensors() const;
uint64_t GetConstantTensorsSize() const;
absl::Status SetInputTensor(ValueId id, const TensorFloat32& tensor,
CLCommandQueue* queue);
// It will work only with input/output tensor ids. For all other ids we don't
// have any guarantees.
Tensor* GetTensor(ValueId id);
absl::Status GetOutputTensor(ValueId id, CLCommandQueue* queue,
TensorFloat32* result);
const std::vector<ValueId>& GetInputIds() const { return input_ids_; }
const std::vector<ValueId>& GetOutputIds() const { return output_ids_; }
absl::Status RestoreDeserialized(
const absl::Span<const uint8_t> serialized_model, Environment* env,
CreateGpuModelInfo* create_info = nullptr);
// Can be used only with ids from external_mutable_tensors in create_info
// Must be called after initialization and before execution
absl::Status SetTensor(const ValueId& tensor_id, Tensor* tensor_ptr);
private:
flatbuffers::Offset<data::InferenceContext> Encode(
const CLDevice& device, const ProgramCache& program_cache,
flatbuffers::Offset<tflite::gpu::data::GpuModel> gpu_model_fb,
flatbuffers::FlatBufferBuilder* builder);
void InitFromGpuModel(const GpuInfo& gpu_info, GpuModel* gpu_model);
absl::Status AllocateMemory(const GpuModel& gpu_model,
const GpuInfo& gpu_info,
const CreateGpuModelInfo* create_info,
CLContext* context);
absl::Status AllocateConstTensors(const GpuModel& gpu_model,
CLContext* context);
absl::Status AllocateVariableTensors(const GpuModel& gpu_model,
CLContext* context);
absl::Status AllocateBufferBasedTensors(const GpuModel& gpu_model,
const GpuInfo& gpu_info,
const CreateGpuModelInfo* create_info,
CLContext* context);
absl::Status AllocateStrongShapesTensors(
const GpuModel& gpu_model, const GpuInfo& gpu_info,
const CreateGpuModelInfo* create_info, CLContext* context);
void BindMemoryToOperations();
absl::Status Compile(const CreationContext& creation_context);
absl::Status Tune(TuningType tuning_type, const GpuInfo& gpu_info,
ProfilingCommandQueue* profiling_queue);
absl::Status UpdateParams();
void PrepareExternal();
void InitRecordableQueue(Environment* env);
absl::Status ProfileTime(ProfilingCommandQueue* queue, ProfilingInfo* result);
absl::Status ClarifyTimeMultipleEnqueue(double ops_total_duration_ms,
int min_ops, int max_ops,
ProfilingCommandQueue* queue,
ProfilingInfo* result);
absl::Status ClarifyTimeWithCommandBuffer(ProfilingCommandQueue* queue,
ProfilingInfo* result);
absl::Status AddCommandBufferToQueue(CLCommandQueue* queue);
struct ExecutionHints {
bool need_flush = false;
bool flush_periodically = false;
int flush_period = 1;
// In order to reduce memory leak on Mali a pipeline needs to be
// synchronized with CPU to prevent growing internal global OpenCL kernel
// pool. One trick is to enqueue an event from a previous run. Most of the
// time is should already be executed on GPU and should not stall the
// pipeline.
bool need_manual_release = false;
CLEvent prev_enqueue_start_point;
void Init(const GpuInfo& gpu_info);
};
ExecutionHints execution_hints_;
// Directly mapped nodes from graph, but some of them "inactive" due
// to fusion (inactive = fused).
// Memory is allocated only once, in ConvertOperations, and is not modified
// anywhere.
std::vector<CLNode> nodes_;
absl::flat_hash_map<ValueId, Tensor*> external_immutable_tensors_;
absl::flat_hash_map<ValueId, Tensor*> external_mutable_tensors_;
absl::flat_hash_map<ValueId, std::vector<int>> external_tensor_to_nodes_;
std::map<ValueId, Tensor> const_tensors_;
std::map<ValueId, ValueId> variable_ids_and_refs_;
std::map<ValueId, Tensor> variable_tensors_;
std::unique_ptr<Buffer> shared_buffers_parent_;
Buffer* shared_buffers_parent_ptr_ = nullptr;
std::vector<Buffer> shared_buffers_;
std::vector<Tensor>
shared_buffer_tensors_; // use references to memory from shared_buffers_
std::map<ValueId, int> graph_ids_to_shared_buffer_tensors_;
std::map<ValueId, Tensor> strong_shape_tensors_;
std::map<ValueId, ValueId> graph_ids_to_strong_shape_tensors_;
std::vector<ValueId> input_ids_;
std::vector<ValueId> output_ids_;
std::unique_ptr<RecordableQueue> recordable_queue_ = nullptr;
bool use_command_buffer_ = false;
std::unique_ptr<CLCommandBuffer> command_buffer_ = nullptr;
GpuInfo gpu_info_;
};
absl::Status GetInOutRefs(const absl::Span<const uint8_t> serialized_model,
std::vector<int64_t>* in_refs,
std::vector<int64_t>* out_refs);
absl::Status GetTotalBufferSizeForTensors(const GpuModel& gpu_model,
const CreateGpuModelInfo& create_info,
const GpuInfo& gpu_info,
uint64_t* result);
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_INFERENCE_CONTEXT_H_
@@ -0,0 +1,803 @@
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
load(
"//tensorflow/core/platform:build_config_root.bzl",
"tf_gpu_tests_tags",
)
load("//tensorflow/lite/delegates/gpu:build_defs.bzl", "gtest_main_no_heapcheck_deps")
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
default_visibility = ["//visibility:public"],
licenses = ["notice"],
)
cc_test(
name = "add_test",
srcs = ["add_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
# TODO(b/279977471) Once b/279347631 is resolved, check for heap again
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:add_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "cast_test",
srcs = ["cast_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:cast_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_library(
name = "cl_test",
testonly = 1,
srcs = ["cl_test.cc"],
hdrs = ["cl_test.h"],
deps = [
"//tensorflow/lite/delegates/gpu/cl:cl_operation",
"//tensorflow/lite/delegates/gpu/cl:environment",
"//tensorflow/lite/delegates/gpu/cl:opencl_wrapper",
"//tensorflow/lite/delegates/gpu/cl:tensor",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/task:testing_util",
"@com_google_googletest//:gtest",
],
)
cc_test(
name = "concat_test",
srcs = ["concat_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:concat_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "conv_constants_test",
srcs = ["conv_constants_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:conv_constants_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "conv_generic_test",
srcs = ["conv_generic_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:conv_generic_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "conv_weights_converter_test",
srcs = ["conv_weights_converter_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:conv_weights_converter_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_library(
name = "converter",
srcs = ["converter.cc"],
hdrs = ["converter.h"],
deps = [
"//tensorflow/lite/delegates/gpu:spi",
"//tensorflow/lite/delegates/gpu/cl:buffer",
"//tensorflow/lite/delegates/gpu/cl:cl_arguments",
"//tensorflow/lite/delegates/gpu/cl:cl_command_queue",
"//tensorflow/lite/delegates/gpu/cl:cl_errors",
"//tensorflow/lite/delegates/gpu/cl:environment",
"//tensorflow/lite/delegates/gpu/cl:opencl_wrapper",
"//tensorflow/lite/delegates/gpu/cl:tensor",
"//tensorflow/lite/delegates/gpu/cl:tensor_type_util",
"//tensorflow/lite/delegates/gpu/common:data_type",
"//tensorflow/lite/delegates/gpu/common:types",
"//tensorflow/lite/delegates/gpu/common:util",
"//tensorflow/lite/delegates/gpu/common/task:arguments",
"//tensorflow/lite/delegates/gpu/common/task:gpu_operation",
"//tensorflow/lite/delegates/gpu/common/task:tensor_desc",
"//tensorflow/lite/delegates/gpu/common/task:util",
"//tensorflow/lite/delegates/gpu/common/task:work_group_picking",
"//tensorflow/lite/delegates/gpu/common/tasks:conversion",
"@com_google_absl//absl/status",
"@com_google_absl//absl/strings",
],
)
cc_test(
name = "convolution_transposed_test",
srcs = ["convolution_transposed_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:convolution_transposed_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "convolution_transposed_3x3_test",
srcs = ["convolution_transposed_3x3_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:convolution_transposed_3x3_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "convolution_transposed_3x3_thin_test",
srcs = ["convolution_transposed_3x3_thin_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:convolution_transposed_3x3_thin_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "convolution_transposed_4x4_test",
srcs = ["convolution_transposed_4x4_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:convolution_transposed_4x4_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "convolution_transposed_thin_test",
srcs = ["convolution_transposed_thin_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:convolution_transposed_thin_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "cumsum_test",
srcs = ["cumsum_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:cumsum_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "depthwise_conv_test",
srcs = ["depthwise_conv_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:depthwise_conv_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "depthwise_conv_3x3_test",
srcs = ["depthwise_conv_3x3_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:depthwise_conv_3x3_stride_h2_test_util",
"//tensorflow/lite/delegates/gpu/common/tasks:depthwise_conv_3x3_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "elementwise_test",
srcs = ["elementwise_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:elementwise_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "fully_connected_test",
srcs = ["fully_connected_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/cl:environment",
"//tensorflow/lite/delegates/gpu/common:data_type",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:shape",
"//tensorflow/lite/delegates/gpu/common:tensor",
"//tensorflow/lite/delegates/gpu/common/task:gpu_operation",
"//tensorflow/lite/delegates/gpu/common/tasks:fully_connected",
"//tensorflow/lite/delegates/gpu/common/tasks:fully_connected_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "gather_test",
srcs = ["gather_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:gather_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "lstm_test",
srcs = ["lstm_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:lstm_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "lstm_full_test",
srcs = ["lstm_full_test.cc"],
linkstatic = True,
tags = tf_gpu_tests_tags() + [
"linux",
"local",
],
deps = [
"//tensorflow/lite:framework",
"//tensorflow/lite/delegates/gpu:delegate",
"//tensorflow/lite/kernels:test_main",
"//tensorflow/lite/kernels:test_util",
"//tensorflow/lite/schema:schema_fbs",
"@com_google_googletest//:gtest",
"@flatbuffers",
],
)
cc_test(
name = "max_unpooling_test",
srcs = ["max_unpooling_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:max_unpooling_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "mean_stddev_normalization_test",
srcs = ["mean_stddev_normalization_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:mean_stddev_normalization_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "one_hot_test",
srcs = ["one_hot_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:one_hot_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "padding_test",
srcs = ["padding_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:padding_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "pooling_test",
srcs = ["pooling_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:pooling_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "prelu_test",
srcs = ["prelu_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:prelu_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "quantize_and_dequantize_test",
srcs = ["quantize_and_dequantize_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:quantize_and_dequantize_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "reduce_test",
srcs = ["reduce_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:reduce_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "relu_test",
srcs = ["relu_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:relu_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "resampler_test",
srcs = ["resampler_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
"//tensorflow/lite/delegates/gpu/cl/kernels:cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:resampler_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "reshape_test",
srcs = ["reshape_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:reshape_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "reshapex4_test",
srcs = ["reshapex4_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:reshape_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "select_v2_test",
srcs = ["select_v2_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:select_v2_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "softmax_test",
srcs = ["softmax_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:softmax_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "softmax1x1_test",
srcs = ["softmax1x1_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:softmax_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "space_to_depth_test",
srcs = ["space_to_depth_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:space_to_depth_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "split_test",
srcs = ["split_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:split_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "strided_slice_test",
srcs = ["strided_slice_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:strided_slice_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "tile_test",
srcs = ["tile_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:tile_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "transpose_test",
srcs = ["transpose_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:transpose_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "resize_test",
srcs = ["resize_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:resize_test_util",
] + gtest_main_no_heapcheck_deps(),
)
cc_test(
name = "winograd_test",
srcs = ["winograd_test.cc"],
tags = [
"noasan",
"nomsan",
"notsan",
"requires-gpu-nvidia",
],
deps = [
":cl_test",
"//tensorflow/lite/delegates/gpu/common:operations",
"//tensorflow/lite/delegates/gpu/common:status",
"//tensorflow/lite/delegates/gpu/common/tasks:winograd_test_util",
] + gtest_main_no_heapcheck_deps(),
)
test_suite(
name = "all_tests",
tests = [
"add_test",
"cast_test",
"concat_test",
"conv_constants_test",
"conv_generic_test",
"conv_weights_converter_test",
"convolution_transposed_3x3_test",
"convolution_transposed_3x3_thin_test",
"convolution_transposed_4x4_test",
"convolution_transposed_test",
"convolution_transposed_thin_test",
"depthwise_conv_3x3_test",
"depthwise_conv_test",
"elementwise_test",
"fully_connected_test",
"gather_test",
"lstm_test",
"max_unpooling_test",
"mean_stddev_normalization_test",
"padding_test",
"pooling_test",
"prelu_test",
"reduce_test",
"relu_test",
"reshape_test",
"reshapex4_test",
"resize_test",
"softmax1x1_test",
"softmax_test",
"split_test",
"strided_slice_test",
"tile_test",
"transpose_test",
"winograd_test",
],
)
@@ -0,0 +1,48 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/add_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLOperationTest, AddTwoEqualTensors) {
auto status = AddTwoEqualTensorsTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, AddFirstTensorHasMoreChannelsThanSecond) {
auto status = AddFirstTensorHasMoreChannelsThanSecondTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, AddFirstTensorHasLessChannelsThanSecond) {
auto status = AddFirstTensorHasLessChannelsThanSecond(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,46 @@
/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
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 <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/cast_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLOperationTest, Cast) {
auto status = CastTests(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, CastToBool) {
auto status = CastToBoolTests(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, CastFromBool) {
auto status = CastFromBoolTests(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,93 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include <memory>
#include <utility>
#include <vector>
#include "tensorflow/lite/delegates/gpu/cl/cl_operation.h"
#include "tensorflow/lite/delegates/gpu/cl/tensor.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
namespace tflite {
namespace gpu {
namespace cl {
absl::Status ClExecutionEnvironment::Init() { return CreateEnvironment(&env_); }
std::vector<CalculationsPrecision>
ClExecutionEnvironment::GetSupportedPrecisions() const {
return env_.GetSupportedPrecisions();
}
std::vector<TensorStorageType> ClExecutionEnvironment::GetSupportedStorages(
DataType data_type) const {
if (data_type == DataType::FLOAT16 &&
!env_.GetDevicePtr()->GetInfo().SupportsFP16()) {
return {};
}
return env_.GetSupportedStorages();
}
const GpuInfo& ClExecutionEnvironment::GetGpuInfo() const {
return env_.GetDevicePtr()->GetInfo();
}
absl::Status ClExecutionEnvironment::ExecuteGpuOperationInternal(
const std::vector<TensorDescriptor*>& src_cpu,
const std::vector<TensorDescriptor*>& dst_cpu,
std::unique_ptr<GPUOperation>&& operation) {
const OperationDef& op_def = operation->GetDefinition();
std::vector<Tensor> src(src_cpu.size());
for (int i = 0; i < src_cpu.size(); ++i) {
RETURN_IF_ERROR(src[i].CreateFromDescriptor(*src_cpu[i], &env_.context()));
operation->SetSrc(&src[i], i);
}
std::vector<Tensor> dst(dst_cpu.size());
for (int i = 0; i < dst_cpu.size(); ++i) {
TensorDescriptor descriptor_with_shape = op_def.dst_tensors[i];
descriptor_with_shape.SetBHWDCShape(dst_cpu[i]->GetBHWDCShape());
RETURN_IF_ERROR(
CreateTensor(env_.context(), descriptor_with_shape, &dst[i]));
operation->SetDst(&dst[i], i);
}
ClOperation cl_op;
cl_op.Init(std::move(operation));
{
CreationContext creation_context;
creation_context.device = env_.GetDevicePtr();
creation_context.context = &env_.context();
creation_context.queue = env_.queue();
creation_context.cache = env_.program_cache();
RETURN_IF_ERROR(cl_op.Compile(creation_context));
}
RETURN_IF_ERROR(cl_op.UpdateParams());
RETURN_IF_ERROR(cl_op.AddToQueue(env_.queue()));
RETURN_IF_ERROR(env_.queue()->WaitForCompletion());
for (int i = 0; i < dst_cpu.size(); ++i) {
RETURN_IF_ERROR(dst[i].ToDescriptor(dst_cpu[i], env_.queue()));
}
return absl::OkStatus();
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,73 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_CL_KERNELS_CL_TEST_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_KERNELS_CL_TEST_H_
#include <memory>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/environment.h"
#include "tensorflow/lite/delegates/gpu/cl/opencl_wrapper.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/task/testing_util.h"
namespace tflite {
namespace gpu {
namespace cl {
#ifndef ASSERT_OK
#define ASSERT_OK(x) ASSERT_TRUE(x.ok());
#endif
class ClExecutionEnvironment : public TestExecutionEnvironment {
public:
ClExecutionEnvironment() = default;
~ClExecutionEnvironment() override = default;
absl::Status Init();
std::vector<CalculationsPrecision> GetSupportedPrecisions() const override;
std::vector<TensorStorageType> GetSupportedStorages(
DataType data_type) const override;
const GpuInfo& GetGpuInfo() const override;
absl::Status ExecuteGpuOperationInternal(
const std::vector<TensorDescriptor*>& src_cpu,
const std::vector<TensorDescriptor*>& dst_cpu,
std::unique_ptr<GPUOperation>&& operation) override;
private:
Environment env_;
};
class OpenCLOperationTest : public ::testing::Test {
public:
void SetUp() override {
ASSERT_OK(LoadOpenCL());
ASSERT_OK(exec_env_.Init());
}
protected:
ClExecutionEnvironment exec_env_;
};
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_KERNELS_CL_TEST_H_
@@ -0,0 +1,53 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/concat_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLOperationTest, ConcatWidth) {
auto status = ConcatWidthTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ConcatHeight) {
auto status = ConcatHeightTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ConcatChannels) {
auto status = ConcatChannelsTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ConcatChannelsAlignedx4) {
auto status = ConcatChannelsAlignedx4Test(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,39 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/conv_constants_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
TEST_F(OpenCLOperationTest, ConvConstantsSimpleWeights) {
const auto status = ConvConstantsSimpleWeightsTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ConvConstants) {
const auto status = ConvConstantsTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,56 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/conv_generic_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
TEST_F(OpenCLOperationTest, ConvGeneric1x1SimpleWeights) {
const auto status = ConvGeneric1x1SimpleWeightsTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ConvGeneric1x1) {
const auto status = ConvGeneric1x1Test(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ConvGenericSimpleWeights) {
const auto status = ConvGenericSimpleWeightsTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ConvGeneric) {
const auto status = ConvGenericTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ConvGenericGrouped) {
const auto status = ConvGenericGroupedTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,61 @@
/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
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 <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/conv_weights_converter_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
TEST_F(OpenCLOperationTest, ConverterToConvWeights1x1OutX4) {
const auto status = ConverterToConvWeights1x1OutX4Test(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ConverterToConvWeights1x1OutX4Unaligned) {
const auto status = ConverterToConvWeights1x1OutX4UnalignedTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ConverterToConvWeights1x1OutX2) {
const auto status = ConverterToConvWeights1x1OutX2Test(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ConverterToConvWeightsOutX2) {
const auto status = ConverterToConvWeightsOutX2Test(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ConverterToConvTransposedWeights4x4) {
const auto status = ConverterToConvTransposedWeights4x4Test(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ConverterToConvWeights4xTextures) {
const auto status = ConverterToConvWeights4xTexturesTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,644 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/cl/kernels/converter.h"
#include <array>
#include <memory>
#include <string>
#include <utility>
#include <variant>
#include <vector>
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/substitute.h"
#include "tensorflow/lite/delegates/gpu/cl/buffer.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_arguments.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_command_queue.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_errors.h"
#include "tensorflow/lite/delegates/gpu/cl/opencl_wrapper.h"
#include "tensorflow/lite/delegates/gpu/cl/tensor.h"
#include "tensorflow/lite/delegates/gpu/cl/tensor_type_util.h"
#include "tensorflow/lite/delegates/gpu/common/data_type.h"
#include "tensorflow/lite/delegates/gpu/common/task/arguments.h"
#include "tensorflow/lite/delegates/gpu/common/task/gpu_operation.h"
#include "tensorflow/lite/delegates/gpu/common/task/tensor_desc.h"
#include "tensorflow/lite/delegates/gpu/common/task/util.h"
#include "tensorflow/lite/delegates/gpu/common/task/work_group_picking.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/conversion.h"
#include "tensorflow/lite/delegates/gpu/common/types.h"
#include "tensorflow/lite/delegates/gpu/common/util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
class OpenClConverterImpl : public TensorObjectConverter {
public:
virtual absl::Status Init(const TensorObjectDef& input_def,
const TensorObjectDef& output_def,
Environment* environment) = 0;
void SetGpuInfo(const GpuInfo& info) { gpu_info_ = info; }
protected:
absl::Status DispatchKernel(Buffer* buffer, Tensor* tensor) {
RETURN_IF_ERROR(cl_args_.SetObjectRef("buffer", buffer));
RETURN_IF_ERROR(cl_args_.SetObjectRef("tensor", tensor));
RETURN_IF_ERROR(cl_args_.Bind(kernel_.kernel()));
const int64_t grid_x =
static_cast<int64_t>(tensor->Width()) * tensor->Batch();
if (grid_x > std::numeric_limits<int>::max()) {
return absl::InvalidArgumentError(
"Grid x-dimension exceeds 32-bit integer maximum.");
}
const int3 grid =
int3(static_cast<int>(grid_x), tensor->Height(), tensor->Slices());
std::vector<int3> work_groups;
GetPossibleWorkGroupsConv(TuningType::kFast, gpu_info_, kernel_.info_, grid,
&work_groups);
const int3 work_group_size = work_groups[0];
const int3 work_groups_count = GetWorkGroupsCount(grid, work_group_size);
return queue_->Dispatch(kernel_, work_groups_count, work_group_size);
}
CLArguments cl_args_;
BHWC shape_;
CLKernel kernel_;
TensorDescriptor tensor_descriptor_;
GpuInfo gpu_info_;
CLCommandQueue* queue_ = nullptr;
const CLContext* context_ = nullptr;
};
bool IsSupportedDataType(DataType type) {
return type == DataType::FLOAT16 || type == DataType::FLOAT32 ||
type == DataType::INT32 || type == DataType::BOOL;
}
bool IsBHWCOpenCLBuffer(const ObjectDef& def) {
return IsSupportedDataType(def.data_type) &&
def.object_type == ObjectType::OPENCL_BUFFER &&
def.data_layout == DataLayout::BHWC;
}
bool IsOpenCLTensor(const ObjectDef& def) {
const bool is_buffer_tensor = def.object_type == ObjectType::OPENCL_BUFFER &&
def.data_layout == DataLayout::DHWC4;
const bool is_image2d_tensor =
def.object_type == ObjectType::OPENCL_TEXTURE &&
def.data_layout == DataLayout::HDWC4;
const bool is_image2d_array_tensor =
def.object_type == ObjectType::OPENCL_TEXTURE &&
def.data_layout == DataLayout::DHWC4;
const bool is_single_image_tensor =
def.object_type == ObjectType::OPENCL_TEXTURE &&
def.data_layout == DataLayout::BHWC;
return IsSupportedDataType(def.data_type) &&
(is_buffer_tensor || is_image2d_tensor || is_image2d_array_tensor ||
is_single_image_tensor);
}
absl::Status GetOpenCLMemory(const TensorObject& obj, cl_mem* memory) {
auto texture = std::get_if<OpenClTexture>(&obj);
auto buffer = std::get_if<OpenClBuffer>(&obj);
if (texture && texture->memobj) {
*memory = texture->memobj;
} else if (buffer && buffer->memobj) {
*memory = buffer->memobj;
} else {
return absl::InvalidArgumentError("Missing OpenCL object.");
}
return absl::OkStatus();
}
// Implements conversion from OpenCL tensor to another OpenCL tensor.
class TensorToTensorConverter : public OpenClConverterImpl {
public:
static bool IsSupported(const ObjectDef& input, const ObjectDef& output) {
return IsOpenCLTensor(input) && IsOpenCLTensor(output);
}
absl::Status Init(const TensorObjectDef& input_def,
const TensorObjectDef& output_def,
Environment* environment) final {
src_tensor_descriptor_ =
TensorDescriptor(input_def.object_def.data_type,
ToTensorStorageType(input_def.object_def.object_type,
input_def.object_def.data_layout),
Layout::BHWC);
dst_tensor_descriptor_ =
TensorDescriptor(output_def.object_def.data_type,
ToTensorStorageType(output_def.object_def.object_type,
output_def.object_def.data_layout),
Layout::BHWC);
GPUOperation gpu_op =
CreateTensorToTensorOp(environment->GetDevicePtr()->GetInfo(),
src_tensor_descriptor_, dst_tensor_descriptor_);
gpu_op.code_ =
"#define MAIN_FUNCTION __kernel void tensor_to_tensor\n" + gpu_op.code_;
const bool need_fp16_support =
input_def.object_def.data_type == DataType::FLOAT16 ||
output_def.object_def.data_type == DataType::FLOAT16;
if (need_fp16_support) {
gpu_op.code_ =
"#pragma OPENCL EXTENSION cl_khr_fp16 : enable\n" + gpu_op.code_;
}
queue_ = environment->queue();
context_ = &environment->context();
shape_ = BHWC(input_def.dimensions.b, input_def.dimensions.h,
input_def.dimensions.w, input_def.dimensions.c);
RETURN_IF_ERROR(gpu_op.AssembleCode(environment->device().GetInfo()));
RETURN_IF_ERROR(cl_args_.Init(environment->device().GetInfo(), nullptr,
&gpu_op.args_, &gpu_op.code_));
return environment->program_cache()->GetOrCreateCLKernel(
gpu_op.code_, "tensor_to_tensor", environment->context(),
environment->device(), &kernel_);
}
absl::Status Convert(const TensorObject& input_obj,
const TensorObject& output_obj) override {
cl_mem in_memory;
RETURN_IF_ERROR(GetOpenCLMemory(input_obj, &in_memory));
cl_mem out_memory;
RETURN_IF_ERROR(GetOpenCLMemory(output_obj, &out_memory));
Tensor src_tensor;
TensorDescriptor descriptor_with_shape = src_tensor_descriptor_;
descriptor_with_shape.SetBHWCShape(shape_);
RETURN_IF_ERROR(CreateTensorShared(*context_, in_memory,
descriptor_with_shape, &src_tensor));
Tensor dst_tensor;
descriptor_with_shape = dst_tensor_descriptor_;
descriptor_with_shape.SetBHWCShape(shape_);
RETURN_IF_ERROR(CreateTensorShared(*context_, out_memory,
descriptor_with_shape, &dst_tensor));
RETURN_IF_ERROR(cl_args_.SetObjectRef("src_tensor", &src_tensor));
RETURN_IF_ERROR(cl_args_.SetObjectRef("dst_tensor", &dst_tensor));
RETURN_IF_ERROR(cl_args_.Bind(kernel_.kernel()));
const int64_t grid_x =
static_cast<int64_t>(dst_tensor.Width()) * dst_tensor.Batch();
if (grid_x > std::numeric_limits<int>::max()) {
return absl::InvalidArgumentError(
"Grid x-dimension exceeds 32-bit integer maximum.");
}
const int3 grid = int3(static_cast<int>(grid_x), dst_tensor.Height(),
dst_tensor.Slices());
const int3 work_group_size = {16, 8, 1};
const int3 work_groups_count = GetWorkGroupsCount(grid, work_group_size);
return queue_->Dispatch(kernel_, work_groups_count, work_group_size);
}
private:
TensorDescriptor src_tensor_descriptor_;
TensorDescriptor dst_tensor_descriptor_;
};
// Implements conversion from OpenCL-specific tensor layout to BHWC OpenCL
// buffer.
class TensorToBHWCBufferConverter : public OpenClConverterImpl {
public:
static bool IsSupported(const ObjectDef& input, const ObjectDef& output) {
return IsOpenCLTensor(input) && IsBHWCOpenCLBuffer(output);
}
absl::Status Init(const TensorObjectDef& input_def,
const TensorObjectDef& output_def,
Environment* environment) final {
TensorStorageType src_tensor_type = ToTensorStorageType(
input_def.object_def.object_type, input_def.object_def.data_layout);
tensor_descriptor_ = TensorDescriptor(input_def.object_def.data_type,
src_tensor_type, Layout::BHWC);
BufferDescriptor buffer_desc;
buffer_desc.element_type = output_def.object_def.data_type;
buffer_desc.element_size = 1;
buffer_desc.memory_type = MemoryType::GLOBAL;
GPUOperation gpu_op =
CreateTensorToBhwcBufferOp(environment->GetDevicePtr()->GetInfo(),
tensor_descriptor_, buffer_desc);
gpu_op.code_ =
"#define MAIN_FUNCTION __kernel void tensor_to_bhwc\n" + gpu_op.code_;
if (output_def.object_def.data_type == DataType::BOOL ||
input_def.object_def.data_type == DataType::BOOL) {
gpu_op.code_ =
"#define convert_bool4(value) (convert_uchar4((value) != 0) & "
"(uchar4) 1)\n#define bool4 uchar4\n" +
gpu_op.code_;
}
const bool need_fp16_support =
input_def.object_def.data_type == DataType::FLOAT16 ||
output_def.object_def.data_type == DataType::FLOAT16;
if (need_fp16_support) {
gpu_op.code_ =
"#pragma OPENCL EXTENSION cl_khr_fp16 : enable\n" + gpu_op.code_;
}
queue_ = environment->queue();
context_ = &environment->context();
shape_ = BHWC(input_def.dimensions.b, input_def.dimensions.h,
input_def.dimensions.w, input_def.dimensions.c);
RETURN_IF_ERROR(gpu_op.AssembleCode(environment->device().GetInfo()));
RETURN_IF_ERROR(cl_args_.Init(environment->device().GetInfo(), nullptr,
&gpu_op.args_, &gpu_op.code_));
return environment->program_cache()->GetOrCreateCLKernel(
gpu_op.code_, "tensor_to_bhwc", environment->context(),
environment->device(), &kernel_);
}
absl::Status Convert(const TensorObject& input_obj,
const TensorObject& output_obj) override {
auto output = std::get_if<OpenClBuffer>(&output_obj);
if (!output || !output->memobj) {
return absl::InvalidArgumentError(
"Missing output in tensor_to_bhwc converter");
}
cl_mem in_memory;
RETURN_IF_ERROR(GetOpenCLMemory(input_obj, &in_memory));
Tensor tensor;
TensorDescriptor descriptor_with_shape = tensor_descriptor_;
descriptor_with_shape.SetBHWCShape(shape_);
RETURN_IF_ERROR(CreateTensorShared(*context_, in_memory,
descriptor_with_shape, &tensor));
Buffer buffer = CreateBufferShared(output->memobj);
return DispatchKernel(&buffer, &tensor);
}
};
// Implements conversion from BHWC OpenCL buffer to OpenCL-specific tensor
// layout.
class BHWCBufferToTensorConverter : public OpenClConverterImpl {
public:
static bool IsSupported(const ObjectDef& input, const ObjectDef& output) {
return IsBHWCOpenCLBuffer(input) && IsOpenCLTensor(output);
}
absl::Status Init(const TensorObjectDef& input_def,
const TensorObjectDef& output_def,
Environment* environment) final {
TensorStorageType dst_tensor_type = ToTensorStorageType(
output_def.object_def.object_type, output_def.object_def.data_layout);
tensor_descriptor_ = TensorDescriptor(output_def.object_def.data_type,
dst_tensor_type, Layout::BHWC);
BufferDescriptor buffer_desc;
buffer_desc.element_type = input_def.object_def.data_type;
buffer_desc.element_size = 1;
buffer_desc.memory_type = MemoryType::GLOBAL;
GPUOperation gpu_op =
CreateBhwcBufferToTensorOp(environment->GetDevicePtr()->GetInfo(),
buffer_desc, tensor_descriptor_);
gpu_op.code_ =
"#define MAIN_FUNCTION __kernel void bhwc_to_tensor\n" + gpu_op.code_;
if (output_def.object_def.data_type == DataType::BOOL ||
input_def.object_def.data_type == DataType::BOOL) {
gpu_op.code_ =
"#define convert_bool4(value) (convert_uchar4((value) != 0) & "
"(uchar4) 1)\n#define bool4 uchar4\n" +
gpu_op.code_;
}
const bool need_fp16_support =
input_def.object_def.data_type == DataType::FLOAT16 ||
output_def.object_def.data_type == DataType::FLOAT16;
if (need_fp16_support) {
gpu_op.code_ =
"#pragma OPENCL EXTENSION cl_khr_fp16 : enable\n" + gpu_op.code_;
}
queue_ = environment->queue();
context_ = &environment->context();
shape_ = BHWC(output_def.dimensions.b, output_def.dimensions.h,
output_def.dimensions.w, output_def.dimensions.c);
RETURN_IF_ERROR(gpu_op.AssembleCode(environment->device().GetInfo()));
RETURN_IF_ERROR(cl_args_.Init(environment->device().GetInfo(), nullptr,
&gpu_op.args_, &gpu_op.code_));
return environment->program_cache()->GetOrCreateCLKernel(
gpu_op.code_, "bhwc_to_tensor", environment->context(),
environment->device(), &kernel_);
}
absl::Status Convert(const TensorObject& input_obj,
const TensorObject& output_obj) override {
auto input = std::get_if<OpenClBuffer>(&input_obj);
if (!input || !input->memobj) {
return absl::InvalidArgumentError(
"Missing input in bhwc_to_tensor converter");
}
cl_mem out_memory;
RETURN_IF_ERROR(GetOpenCLMemory(output_obj, &out_memory));
Tensor tensor;
TensorDescriptor descriptor_with_shape = tensor_descriptor_;
descriptor_with_shape.SetBHWCShape(shape_);
RETURN_IF_ERROR(CreateTensorShared(*context_, out_memory,
descriptor_with_shape, &tensor));
Buffer buffer = CreateBufferShared(input->memobj);
return DispatchKernel(&buffer, &tensor);
}
};
std::array<size_t, 3> CalculateTextureRegion(const TensorObjectDef& def) {
const auto& dims = def.dimensions;
std::array<size_t, 3> region = {0, 0, 1};
switch (ToTensorStorageType(def.object_def.object_type,
def.object_def.data_layout)) {
case TensorStorageType::SINGLE_TEXTURE_2D:
region[0] = static_cast<size_t>(dims.w) * dims.b;
region[1] = static_cast<size_t>(dims.h);
break;
case TensorStorageType::TEXTURE_2D:
region[0] = static_cast<size_t>(dims.w) * dims.b;
region[1] = static_cast<size_t>(dims.h) * dims.d();
break;
case TensorStorageType::TEXTURE_ARRAY:
region[0] = static_cast<size_t>(dims.w) * dims.b;
region[1] = static_cast<size_t>(dims.h);
region[2] = static_cast<size_t>(dims.d());
break;
default:
break;
}
return region;
}
bool IsOpenClTextureOrBuffer(ObjectType type) {
return type == ObjectType::OPENCL_BUFFER ||
type == ObjectType::OPENCL_TEXTURE;
}
// Copies data from one object of the same type and layout to another object.
class TrivialCopier : public OpenClConverterImpl {
public:
static bool IsSupported(const ObjectDef& input, const ObjectDef& output) {
return IsOpenClTextureOrBuffer(input.object_type) &&
input.data_type == output.data_type &&
input.object_type == output.object_type &&
input.data_layout == output.data_layout;
}
absl::Status Init(const TensorObjectDef& input_def,
const TensorObjectDef& output_def,
Environment* environment) final {
shape_ = BHWC(input_def.dimensions.b, input_def.dimensions.h,
input_def.dimensions.w, input_def.dimensions.c);
data_type_ = input_def.object_def.data_type;
data_layout_ = input_def.object_def.data_layout;
queue_ = environment->queue();
region_ = CalculateTextureRegion(output_def);
return absl::OkStatus();
}
absl::Status Convert(const TensorObject& input_obj,
const TensorObject& output_obj) override {
auto texture_input = std::get_if<OpenClTexture>(&input_obj);
auto texture_output = std::get_if<OpenClTexture>(&output_obj);
if (texture_input && texture_output) {
return Copy(*texture_input, *texture_output);
}
auto buffer_input = std::get_if<OpenClBuffer>(&input_obj);
auto buffer_output = std::get_if<OpenClBuffer>(&output_obj);
if (buffer_input && buffer_output) {
return Copy(*buffer_input, *buffer_output);
}
return absl::InvalidArgumentError("Unexpected object");
}
absl::Status Copy(const OpenClBuffer& input, const OpenClBuffer& output) {
if (input.memobj == output.memobj) {
return absl::OkStatus();
}
const size_t channels =
(data_layout_ == DataLayout::BHWC) ? shape_.c : AlignByN(shape_.c, 4);
return GetOpenCLError(clEnqueueCopyBuffer(
queue_->queue(), input.memobj, output.memobj, 0, 0,
SizeOf(data_type_) * shape_.w * shape_.h * channels * shape_.b, 0,
nullptr, nullptr));
}
absl::Status Copy(const OpenClTexture& input, const OpenClTexture& output) {
if (input.memobj == output.memobj) {
return absl::OkStatus();
}
size_t origin[3] = {0, 0, 0};
return GetOpenCLError(
clEnqueueCopyImage(queue_->queue(), input.memobj, output.memobj, origin,
origin, region_.data(), 0, nullptr, nullptr));
}
private:
DataType data_type_ = DataType::UNKNOWN;
DataLayout data_layout_ = DataLayout::UNKNOWN;
std::array<size_t, 3> region_;
};
// Copies data from/to CPU into a tensor.
class CpuCopier : public OpenClConverterImpl {
public:
explicit CpuCopier(bool asynchronous = false) : async_(asynchronous) {}
static bool IsSupported(const ObjectDef& input, const ObjectDef& output) {
return input.data_type == output.data_type &&
input.data_layout == output.data_layout &&
((input.object_type == ObjectType::CPU_MEMORY &&
IsOpenClTextureOrBuffer(output.object_type)) ||
(output.object_type == ObjectType::CPU_MEMORY &&
IsOpenClTextureOrBuffer(input.object_type)));
}
absl::Status Init(const TensorObjectDef& input_def,
const TensorObjectDef& output_def,
Environment* environment) final {
const TensorObjectDef& gpu_def =
input_def.object_def.object_type == ObjectType::CPU_MEMORY ? output_def
: input_def;
region_ = CalculateTextureRegion(gpu_def);
required_bytes_ =
NumElements(gpu_def) * SizeOf(gpu_def.object_def.data_type);
input_data_type_ = input_def.object_def.data_type;
output_data_type_ = output_def.object_def.data_type;
queue_ = environment->queue();
return absl::OkStatus();
}
absl::Status Convert(const TensorObject& input_obj,
const TensorObject& output_obj) override {
auto cpu_input = std::get_if<CpuMemory>(&input_obj);
auto cpu_output = std::get_if<CpuMemory>(&output_obj);
if (cpu_input) {
if (cpu_input->size_bytes < required_bytes_) {
return absl::InvalidArgumentError(
absl::StrCat("CPU input memory size ", cpu_input->size_bytes,
" is less than required ", required_bytes_));
}
if (output_data_type_ == DataType::BOOL) {
return CopyFromBoolCpu(cpu_input, output_obj);
}
auto texture_output = std::get_if<OpenClTexture>(&output_obj);
if (texture_output) {
return queue_->EnqueueWriteImage(
texture_output->memobj, int3(region_[0], region_[1], region_[2]),
cpu_input->data, async_);
}
auto buffer_output = std::get_if<OpenClBuffer>(&output_obj);
if (buffer_output) {
return queue_->EnqueueWriteBuffer(
buffer_output->memobj, required_bytes_, cpu_input->data, async_);
}
} else if (cpu_output) {
if (cpu_output->size_bytes < required_bytes_) {
return absl::InvalidArgumentError(
absl::StrCat("CPU output memory size ", cpu_output->size_bytes,
" is less than required ", required_bytes_));
}
if (input_data_type_ == DataType::BOOL) {
return CopyToBoolCpu(input_obj, cpu_output);
}
auto texture_input = std::get_if<OpenClTexture>(&input_obj);
if (texture_input) {
return queue_->EnqueueReadImage(
texture_input->memobj, int3(region_[0], region_[1], region_[2]),
cpu_output->data, async_);
}
auto buffer_input = std::get_if<OpenClBuffer>(&input_obj);
if (buffer_input) {
return queue_->EnqueueReadBuffer(buffer_input->memobj, required_bytes_,
cpu_output->data, async_);
}
}
return absl::InvalidArgumentError("Unexpected object");
}
private:
absl::Status CopyToBoolCpu(const TensorObject& tensor_obj,
const CpuMemory* cpu_memory) {
const size_t num_elements = required_bytes_;
std::vector<uint8_t> tmp_data(num_elements);
auto texture_input = std::get_if<OpenClTexture>(&tensor_obj);
if (texture_input) {
RETURN_IF_ERROR(queue_->EnqueueReadImage(
texture_input->memobj, int3(region_[0], region_[1], region_[2]),
tmp_data.data(), false));
} else {
auto buffer_input = std::get_if<OpenClBuffer>(&tensor_obj);
if (!buffer_input) {
return absl::InvalidArgumentError("Unexpected object");
}
RETURN_IF_ERROR(queue_->EnqueueReadBuffer(
buffer_input->memobj, tmp_data.size(), tmp_data.data(), false));
}
bool* output_data = reinterpret_cast<bool*>(cpu_memory->data);
for (size_t i = 0; i < num_elements; ++i) {
output_data[i] = tmp_data[i];
}
return absl::OkStatus();
}
absl::Status CopyFromBoolCpu(const CpuMemory* cpu_memory,
const TensorObject& tensor_obj) {
const size_t num_elements = required_bytes_;
const bool* bool_data = reinterpret_cast<bool*>(cpu_memory->data);
std::vector<uint8_t> tmp_bool_data;
tmp_bool_data.reserve(num_elements);
for (size_t i = 0; i < num_elements; ++i) {
tmp_bool_data.push_back(bool_data[i]);
}
auto texture_output = std::get_if<OpenClTexture>(&tensor_obj);
if (texture_output) {
return queue_->EnqueueWriteImage(texture_output->memobj,
int3(region_[0], region_[1], region_[2]),
tmp_bool_data.data(), false);
}
auto buffer_output = std::get_if<OpenClBuffer>(&tensor_obj);
if (buffer_output) {
return queue_->EnqueueWriteBuffer(buffer_output->memobj,
tmp_bool_data.size(),
tmp_bool_data.data(), false);
}
return absl::InvalidArgumentError("Unexpected object");
}
std::array<size_t, 3> region_;
size_t required_bytes_ = 0;
bool async_;
DataType input_data_type_;
DataType output_data_type_;
};
class OpenClTensorConverterBuilder : public TensorObjectConverterBuilder {
public:
explicit OpenClTensorConverterBuilder(Environment* environment)
: environment_(environment) {}
bool IsSupported(const TensorObjectDef& input,
const TensorObjectDef& output) const final {
const auto& input_def = input.object_def;
const auto& output_def = output.object_def;
return input.dimensions == output.dimensions &&
(TrivialCopier::IsSupported(input_def, output_def) ||
TensorToTensorConverter::IsSupported(input_def, output_def) ||
CpuCopier::IsSupported(input_def, output_def) ||
TensorToBHWCBufferConverter::IsSupported(input_def, output_def) ||
BHWCBufferToTensorConverter::IsSupported(input_def, output_def));
}
absl::Status MakeConverter(
const TensorObjectDef& input, const TensorObjectDef& output,
std::unique_ptr<TensorObjectConverter>* converter) final {
std::unique_ptr<OpenClConverterImpl> impl;
const auto& input_def = input.object_def;
const auto& output_def = output.object_def;
if (TrivialCopier::IsSupported(input_def, output_def)) {
impl = std::make_unique<TrivialCopier>();
} else if (TensorToTensorConverter::IsSupported(input_def, output_def)) {
impl = std::make_unique<TensorToTensorConverter>();
} else if (CpuCopier::IsSupported(input_def, output_def)) {
impl = std::make_unique<CpuCopier>(/*asynchronous*/ true);
} else if (TensorToBHWCBufferConverter::IsSupported(input_def,
output_def)) {
impl = std::make_unique<TensorToBHWCBufferConverter>();
} else if (BHWCBufferToTensorConverter::IsSupported(input_def,
output_def)) {
impl = std::make_unique<BHWCBufferToTensorConverter>();
} else {
return absl::UnimplementedError("Unsupported conversion");
}
RETURN_IF_ERROR(impl->Init(input, output, environment_));
impl->SetGpuInfo(environment_->GetDevicePtr()->GetInfo());
*converter = std::move(impl);
return absl::OkStatus();
}
private:
Environment* environment_;
};
} // namespace
std::unique_ptr<TensorObjectConverterBuilder> NewConverterBuilder(
Environment* environment) {
return std::make_unique<OpenClTensorConverterBuilder>(environment);
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,37 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 TENSORFLOW_LITE_DELEGATES_GPU_CL_KERNELS_CONVERTER_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_KERNELS_CONVERTER_H_
#include <memory>
#include "tensorflow/lite/delegates/gpu/cl/environment.h"
#include "tensorflow/lite/delegates/gpu/spi.h"
namespace tflite {
namespace gpu {
namespace cl {
// Supports conversions from BHWC to internal OpenCL tensor representation and
// back. Also supports F16/F32.
std::unique_ptr<TensorObjectConverterBuilder> NewConverterBuilder(
Environment* environment);
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_KERNELS_CONVERTER_H_
@@ -0,0 +1,34 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
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 <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/convolution_transposed_3x3_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
TEST_F(OpenCLOperationTest, ConvolutionTransposed3x3) {
auto status = ConvolutionTransposed3x3Test(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,39 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/convolution_transposed_3x3_thin_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
TEST_F(OpenCLOperationTest, ConvolutionTransposed3x3ThinSimpleWeights) {
auto status = ConvolutionTransposed3x3ThinSimpleWeightsTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ConvolutionTransposed3x3Thin) {
auto status = ConvolutionTransposed3x3ThinTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,38 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/convolution_transposed_4x4_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLOperationTest, ConvolutionTransposed4x4SimpleWeights) {
auto status = ConvolutionTransposed4x4SimpleWeightsTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,43 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/convolution_transposed_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLOperationTest, ConvolutionTransposedSimpleWeights) {
auto status = ConvolutionTransposedSimpleWeightsTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ConvolutionTransposed) {
auto status = ConvolutionTransposedTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,39 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/convolution_transposed_thin_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
TEST_F(OpenCLOperationTest, ConvolutionTransposedThinSimpleWeights) {
auto status = ConvolutionTransposedThinSimpleWeightsTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ConvolutionTransposedThin) {
auto status = ConvolutionTransposedThinTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,37 @@
/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
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 <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/cumsum_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLOperationTest, CumsumHWCTest) {
absl::Status status = CumsumHWCTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, CumsumBHWCTest) {
absl::Status status = CumsumBHWCTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,49 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/depthwise_conv_3x3_stride_h2_test_util.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/depthwise_conv_3x3_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLOperationTest, DepthwiseConv3x3SimpleWeights) {
auto status = DepthwiseConv3x3SimpleWeightsTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, DepthwiseConv3x3) {
auto status = DepthwiseConv3x3Test(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, DepthWiseConv3x3StrideH2SimpleWeights) {
auto status = DepthWiseConv3x3StrideH2SimpleWeightsTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,48 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/depthwise_conv_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLOperationTest, DepthwiseConvSimpleWeights) {
auto status = DepthwiseConvSimpleWeightsTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, DepthwiseConvNoMultiplier) {
auto status = DepthwiseConvNoMultiplierTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, DepthwiseConvMultiplier2) {
auto status = DepthwiseConvMultiplier2Test(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,157 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/elementwise_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLOperationTest, Abs) { ASSERT_OK(AbsTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, Cos) { ASSERT_OK(CosTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, Copy) { ASSERT_OK(CopyTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, Elu) { ASSERT_OK(EluTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, Exp) { ASSERT_OK(ExpTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, Floor) { ASSERT_OK(FloorTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, FloorDiv) { ASSERT_OK(FloorDivTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, FloorMod) { ASSERT_OK(FloorModTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, Gelu) { ASSERT_OK(GeluTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, HardSwish) { ASSERT_OK(HardSwishTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, Log) { ASSERT_OK(LogTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, Neg) { ASSERT_OK(NegTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, Rsqrt) { ASSERT_OK(RsqrtTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, Sigmoid) { ASSERT_OK(SigmoidTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, Sin) { ASSERT_OK(SinTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, Sqrt) { ASSERT_OK(SqrtTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, Square) { ASSERT_OK(SquareTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, Tanh) { ASSERT_OK(TanhTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, Sub) { ASSERT_OK(SubTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, SquaredDiff) {
ASSERT_OK(SquaredDiffTest(&exec_env_));
}
TEST_F(OpenCLOperationTest, Div) { ASSERT_OK(DivTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, Pow) { ASSERT_OK(PowTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, Add) { ASSERT_OK(AddTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, Maximum) { ASSERT_OK(MaximumTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, MaximumWithScalar) {
ASSERT_OK(MaximumWithScalarTest(&exec_env_));
}
TEST_F(OpenCLOperationTest, MaximumWithConstantLinearTensor) {
ASSERT_OK(MaximumWithConstantLinearTensorTest(&exec_env_));
}
TEST_F(OpenCLOperationTest, MaximumWithConstantHWCTensor) {
ASSERT_OK(MaximumWithConstantHWCTensorTest(&exec_env_));
}
TEST_F(OpenCLOperationTest, MaximumWithConstantHWCTensorBroadcastChannels) {
ASSERT_OK(MaximumWithConstantHWCTensorBroadcastChannelsTest(&exec_env_));
}
TEST_F(OpenCLOperationTest, Minimum) { ASSERT_OK(MinimumTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, MinimumWithScalar) {
ASSERT_OK(MinimumWithScalarTest(&exec_env_));
}
TEST_F(OpenCLOperationTest, Mul) { ASSERT_OK(MulTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, MulBroadcastHW) {
ASSERT_OK(MulBroadcastHWTest(&exec_env_));
}
TEST_F(OpenCLOperationTest, MulBroadcastChannels) {
ASSERT_OK(MulBroadcastChannelsTest(&exec_env_));
}
TEST_F(OpenCLOperationTest, SubWithScalarAtFirstPosition) {
ASSERT_OK(SubWithScalarAtFirstPositionTest(&exec_env_));
}
TEST_F(OpenCLOperationTest, Less) { ASSERT_OK(LessTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, LessEqual) { ASSERT_OK(LessEqualTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, Greater) { ASSERT_OK(GreaterTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, GreaterEqual) {
ASSERT_OK(GreaterEqualTest(&exec_env_));
}
TEST_F(OpenCLOperationTest, Equal) { ASSERT_OK(EqualTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, NotEqual) { ASSERT_OK(NotEqualTest(&exec_env_)); }
TEST_F(OpenCLOperationTest, CosBroadcast) {
ASSERT_OK(CosBroadcastTest(&exec_env_));
}
TEST_F(OpenCLOperationTest, MaximumScalarBroadcastInput) {
ASSERT_OK(MaximumScalarBroadcastInputTest(&exec_env_));
}
TEST_F(OpenCLOperationTest, MulLinearBroadcastInput) {
ASSERT_OK(MulLinearBroadcastInputTest(&exec_env_));
}
TEST_F(OpenCLOperationTest, MulBroadcastBothInputs) {
ASSERT_OK(MulBroadcastBothInputsTest(&exec_env_));
}
TEST_F(OpenCLOperationTest, LogicalAndTest) {
ASSERT_OK(LogicalAndTest(&exec_env_));
}
TEST_F(OpenCLOperationTest, LogicalAndWithConstantTest) {
ASSERT_OK(LogicalAndWithConstantTest(&exec_env_));
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,136 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 "tensorflow/lite/delegates/gpu/common/tasks/fully_connected.h"
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/environment.h"
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/data_type.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/shape.h"
#include "tensorflow/lite/delegates/gpu/common/task/gpu_operation.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/fully_connected_test_util.h"
#include "tensorflow/lite/delegates/gpu/common/tensor.h"
using ::testing::ElementsAreArray;
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLOperationTest, FullyConnected) {
auto status = FullyConnectedTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, FullyConnectedLarge) {
auto status = FullyConnectedLargeTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, FullyConnectedExtraLarge) {
auto status = FullyConnectedExtraLargeTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, FullyConnectedInt8) {
auto status = FullyConnectedInt8Test(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, RearrageWeights) {
tflite::gpu::Tensor<OHWI, DataType::FLOAT32> weights;
weights.shape = OHWI(8, 1, 1, 8);
weights.data = {
0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, //
10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, //
20.0f, 21.0f, 22.0f, 23.0f, 24.0f, 25.0f, 26.0f, 27.0f, //
30.0f, 31.0f, 32.0f, 33.0f, 34.0f, 35.0f, 36.0f, 37.0f, //
40.0f, 41.0f, 42.0f, 43.0f, 44.0f, 45.0f, 46.0f, 47.0f, //
50.0f, 51.0f, 52.0f, 53.0f, 54.0f, 55.0f, 56.0f, 57.0f, //
60.0f, 61.0f, 62.0f, 63.0f, 64.0f, 65.0f, 66.0f, 67.0f, //
70.0f, 71.0f, 72.0f, 73.0f, 74.0f, 75.0f, 76.0f, 77.0f //
};
std::vector<float> expected_rearranged_data = {
// Top-left block
0.0f, 10.0f, 20.0f, 30.0f, 1.0f, 11.0f, 21.0f, 31.0f, 2.0f, 12.0f, 22.0f,
32.0f, 3.0f, 13.0f, 23.0f, 33.0f,
// Bottom-left block
40.0f, 50.0f, 60.0f, 70.0f, 41.0f, 51.0f, 61.0f, 71.0f, 42.0f, 52.0f,
62.0f, 72.0f, 43.0f, 53.0f, 63.0f, 73.0f,
// Top-right block
4.0f, 14.0f, 24.0f, 34.0f, 5.0f, 15.0f, 25.0f, 35.0f, 6.0f, 16.0f, 26.0f,
36.0f, 7.0f, 17.0f, 27.0f, 37.0f,
// Bottom-right block
44.0f, 54.0f, 64.0f, 74.0f, 45.0f, 55.0f, 65.0f, 75.0f, 46.0f, 56.0f,
66.0f, 76.0f, 47.0f, 57.0f, 67.0f, 77.0f};
std::vector<float> data(8 * 8);
RearrangeFCWeightsToIOO4I4(weights, data.data());
EXPECT_THAT(data, ElementsAreArray(expected_rearranged_data));
}
TEST_F(OpenCLOperationTest, RearrageWeightsWhenPaddingIsRequired) {
tflite::gpu::Tensor<OHWI, DataType::FLOAT32> weights;
weights.shape = OHWI(9, 1, 1, 7);
weights.data = {
0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, //
10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, //
20.0f, 21.0f, 22.0f, 23.0f, 24.0f, 25.0f, 26.0f, //
30.0f, 31.0f, 32.0f, 33.0f, 34.0f, 35.0f, 36.0f, //
40.0f, 41.0f, 42.0f, 43.0f, 44.0f, 45.0f, 46.0f, //
50.0f, 51.0f, 52.0f, 53.0f, 54.0f, 55.0f, 56.0f, //
60.0f, 61.0f, 62.0f, 63.0f, 64.0f, 65.0f, 66.0f, //
70.0f, 71.0f, 72.0f, 73.0f, 74.0f, 75.0f, 76.0f, //
80.0f, 81.0f, 82.0f, 83.0f, 84.0f, 85.0f, 86.0f, //
};
std::vector<float> expected_rearranged_data = {
// Top-left block
0.0f, 10.0f, 20.0f, 30.0f, 1.0f, 11.0f, 21.0f, 31.0f, 2.0f, 12.0f, 22.0f,
32.0f, 3.0f, 13.0f, 23.0f, 33.0f,
// Mid-left block
40.0f, 50.0f, 60.0f, 70.0f, 41.0f, 51.0f, 61.0f, 71.0f, 42.0f, 52.0f,
62.0f, 72.0f, 43.0f, 53.0f, 63.0f, 73.0f,
// Bottom-left block
80.0f, 0.0f, 0.0f, 0.0f, 81.0f, 0.0f, 0.0f, 0.0f, 82.0f, 0.0f, 0.0f, 0.0f,
83.0f, 0.0f, 0.0f, 0.0f,
// Top-right block
4.0f, 14.0f, 24.0f, 34.0f, 5.0f, 15.0f, 25.0f, 35.0f, 6.0f, 16.0f, 26.0f,
36.0f, 0.0f, 0.0f, 0.0f, 0.0f,
// Mid-left block
44.0f, 54.0f, 64.0f, 74.0f, 45.0f, 55.0f, 65.0f, 75.0f, 46.0f, 56.0f,
66.0f, 76.0f, 0.0f, 0.0f, 0.0f, 0.0f,
// Bottom-right block
84.0f, 0.0f, 0.0f, 0.0f, 85.0f, 0.0f, 0.0f, 0.0f, 86.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f};
std::vector<float> data(12 * 8);
RearrangeFCWeightsToIOO4I4(weights, data.data());
EXPECT_THAT(data, ElementsAreArray(expected_rearranged_data));
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,69 @@
/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
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 <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/gather_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
TEST_F(OpenCLOperationTest, GatherBatch) {
auto status = GatherBatchTest(&exec_env_, false);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, GatherBatchConst) {
auto status = GatherBatchTest(&exec_env_, true);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, GatherHeight) {
auto status = GatherHeightTest(&exec_env_, false);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, GatherHeightConst) {
auto status = GatherHeightTest(&exec_env_, true);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, GatherWidth) {
auto status = GatherWidthTest(&exec_env_, false);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, GatherWidthConst) {
auto status = GatherWidthTest(&exec_env_, true);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, GatherChannels) {
auto status = GatherChannelsTest(&exec_env_, false);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, GatherChannelsConst) {
auto status = GatherChannelsTest(&exec_env_, true);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace cl
} // namespace gpu
} // namespace tflite
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,38 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 <cmath>
#include <cstdlib>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/lstm_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
TEST_F(OpenCLOperationTest, LSTM) {
auto status = LstmTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,38 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/max_unpooling_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLOperationTest, MaxUnpooling) {
auto status = MaxUnpoolingTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,87 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
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 <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/mean_stddev_normalization_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
// note: 100.01 is not representable in FP16 (is in FP32), so use 101.0 instead.
TEST_F(OpenCLOperationTest, MeanStddevNormSeparateBatches) {
// zero mean, zero variance
auto status = MeanStddevNormSeparateBatchesTest(0.0f, 0.0f, 0.0f, &exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
// zero mean, small variance
status = MeanStddevNormSeparateBatchesTest(0.0f, 0.01f, 2.63e-4f, &exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
// zero mean, large variance
status =
MeanStddevNormSeparateBatchesTest(0.0f, 100.0f, 2.63e-4f, &exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
// small mean, zero variance
status = MeanStddevNormSeparateBatchesTest(0.01f, 0.0f, 0.0f, &exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
// small mean, small variance
status =
MeanStddevNormSeparateBatchesTest(0.01f, 0.01f, 3.57e-4f, &exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
// small mean, large variance
status =
MeanStddevNormSeparateBatchesTest(1.0f, 100.0f, 2.63e-4f, &exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
// large mean, zero variance
status = MeanStddevNormSeparateBatchesTest(100.0f, 0.0f, 0.0f, &exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
// large mean, small variance
status =
MeanStddevNormSeparateBatchesTest(100.0f, 1.0f, 2.63e-4f, &exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
// large mean, large variance
status =
MeanStddevNormSeparateBatchesTest(100.0f, 100.0f, 2.63e-4f, &exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, MeanStddevNormalizationAllBatches) {
auto status = MeanStddevNormalizationAllBatchesTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, MeanStddevNormalizationLargeVector) {
auto status = MeanStddevNormalizationLargeVectorTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,41 @@
/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
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 <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/one_hot_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLOperationTest, OneHot) {
auto status = OneHotTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, OneHotBatch) {
auto status = OneHotBatchTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,88 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/padding_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLOperationTest, PaddingAppendWidth) {
auto status = PaddingAppendWidthTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, PaddingAppendWidthConstValues) {
auto status = PaddingAppendWidthConstValuesTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, PaddingPrependWidth) {
auto status = PaddingPrependWidthTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, PaddingAppendHeight) {
auto status = PaddingAppendHeightTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, PaddingPrependHeight) {
auto status = PaddingPrependHeightTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, PaddingAppendChannels) {
auto status = PaddingAppendChannelsTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, PaddingPrependChannels) {
auto status = PaddingPrependChannelsTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, PaddingPrependChannelsX4) {
auto status = PaddingPrependChannelsX4Test(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, PaddingComplex) {
auto status = PaddingComplexTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, PaddingReflectWidth) {
auto status = PaddingReflectWidthTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, PaddingReflectChannels) {
auto status = PaddingReflectChannelsTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,53 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/pooling_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLOperationTest, AveragePooling) {
auto status = AveragePoolingTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, AveragePoolingNonEmptyPadding) {
auto status = AveragePoolingNonEmptyPaddingTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, MaxPooling) {
auto status = MaxPoolingTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, MaxPoolingIndices) {
auto status = MaxPoolingIndicesTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,43 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/prelu_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLOperationTest, PReLUAlpha) {
auto status = PReLUAlphaTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, PReLUHWCAlpha) {
auto status = PReLUHWCAlphaTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,53 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
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 <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/quantize_and_dequantize_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLOperationTest, QuantAndDequant_Dim2Bits8) {
auto status = QuantAndDequant_Dim2Bits8Test(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, QuantAndDequant_Dim3Bits8_NegativeRange) {
auto status = QuantAndDequant_Dim3Bits8_NegativeRangeTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, QuantAndDequant_Dim3Bits16) {
auto status = QuantAndDequant_Dim3Bits16Test(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, QuantAndDequant_Dim2Bits16_NegativeRange) {
auto status = QuantAndDequant_Dim2Bits16_NegativeRangeTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,58 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
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 <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/reduce_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLOperationTest, MeanHW) {
auto status = MeanHWTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ReduceSumChannels) {
auto status = ReduceSumChannelsTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ReduceProductChannels) {
auto status = ReduceProductChannelsTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ReduceMaxChannels) {
auto status = ReduceMaxChannelsTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ReduceMinChannels) {
auto status = ReduceMinChannelsTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,71 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/relu_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
TEST_F(OpenCLOperationTest, ReLUNoClipNoAlpha) {
auto status = ReLUNoClipNoAlphaTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ReLUClip) {
auto status = ReLUClipTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ReLUAlpha) {
auto status = ReLUAlphaTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ReLUAlphaClip) {
auto status = ReLUAlphaClipTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ReLULN1NoClipNoAlpha) {
auto status = ReLUN1NoClipNoAlphaTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status;
}
TEST_F(OpenCLOperationTest, ReLUN1Clip) {
auto status = ReLUN1ClipTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status;
}
TEST_F(OpenCLOperationTest, ReLULN1Alpha) {
auto status = ReLUN1AlphaTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status;
}
TEST_F(OpenCLOperationTest, ReLUN1AlphaClip) {
auto status = ReLUN1AlphaClipTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status;
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,42 @@
/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
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 <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/resampler_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
TEST_F(OpenCLOperationTest, ResamplerIdentity) {
auto status = ResamplerIdentityTest(BHWC(1, 2, 2, 1), &exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
status = ResamplerIdentityTest(BHWC(1, 3, 5, 3), &exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
status = ResamplerIdentityTest(BHWC(1, 6, 1, 7), &exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,38 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/reshape_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLOperationTest, Reshape) {
auto status = ReshapeTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,38 @@
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
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 <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/reshape_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLOperationTest, Reshapex4) {
auto status = Reshapex4Test(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,68 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
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 <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/resize_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLOperationTest, ResizeBilinearAligned) {
auto status = ResizeBilinearAlignedTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ResizeBilinearNonAligned) {
auto status = ResizeBilinearNonAlignedTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ResizeBilinearWithoutHalfPixel) {
auto status = ResizeBilinearWithoutHalfPixelTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ResizeBilinearWithHalfPixel) {
auto status = ResizeBilinearWithHalfPixelTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ResizeNearest) {
auto status = ResizeNearestTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ResizeNearestAlignCorners) {
auto status = ResizeNearestAlignCornersTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, ResizeNearestHalfPixelCenters) {
auto status = ResizeNearestHalfPixelCentersTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,68 @@
/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
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 <gtest/gtest.h>
#include "tensorflow/lite/delegates/gpu/cl/kernels/cl_test.h"
#include "tensorflow/lite/delegates/gpu/common/tasks/select_v2_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLOperationTest, SelectV2) {
auto status = SelectV2Test(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, SelectV2Batch) {
auto status = SelectV2BatchTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, SelectV2Channels) {
auto status = SelectV2ChannelsTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, SelectV2ChannelsBatch) {
auto status = SelectV2ChannelsBatchTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, SelectV2BroadcastTrue) {
auto status = SelectV2BroadcastTrueTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, SelectV2BroadcastFalse) {
auto status = SelectV2BroadcastFalseTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, SelectV2BroadcastBoth) {
auto status = SelectV2BroadcastBothTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, SelectV2ChannelsBroadcastFalse) {
auto status = SelectV2ChannelsBroadcastFalseTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite

Some files were not shown because too many files have changed in this diff Show More