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
+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
@@ -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/softmax_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLOperationTest, Softmax1x1) {
auto status = Softmax1x1Test(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, Softmax1x1BigNumber) {
auto status = Softmax1x1BigNumberTest(&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/softmax_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLOperationTest, Softmax) {
auto status = SoftmaxTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, SoftmaxBigNumber) {
auto status = SoftmaxBigNumberTest(&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 <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/space_to_depth_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
// A known Qualcomm Adreno bug makes the 1 channel test fail on some Adreno
// 5xxs.
TEST_F(OpenCLOperationTest, SpaceToDepthTensorShape1x2x2x1BlockSize2) {
ASSERT_OK(SpaceToDepthTensorShape1x2x2x1BlockSize2Test(&exec_env_));
}
TEST_F(OpenCLOperationTest, SpaceToDepthTensorShape1x2x2x2BlockSize2) {
ASSERT_OK(SpaceToDepthTensorShape1x2x2x2BlockSize2Test(&exec_env_));
}
TEST_F(OpenCLOperationTest, SpaceToDepthTensorShape1x2x2x3BlockSize2) {
ASSERT_OK(SpaceToDepthTensorShape1x2x2x3BlockSize2Test(&exec_env_));
}
TEST_F(OpenCLOperationTest, SpaceToDepthTensorShape1x4x4x1BlockSize2) {
ASSERT_OK(SpaceToDepthTensorShape1x4x4x1BlockSize2Test(&exec_env_));
}
TEST_F(OpenCLOperationTest, SpaceToDepthTensorShape1x6x6x1BlockSize3) {
ASSERT_OK(SpaceToDepthTensorShape1x6x6x1BlockSize3Test(&exec_env_));
}
} // namespace
} // 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/split_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
TEST_F(OpenCLOperationTest, SplitChannels) {
auto status = SplitChannelsTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, SplitChannelsX4) {
auto status = SplitChannelsX4Test(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, SplitWidth) {
auto status = SplitWidthTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, SplitHeight) {
auto status = SplitHeightTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, SplitBatch) {
auto status = SplitBatchTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, SplitDepth) {
auto status = SplitDepthTest(&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/strided_slice_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLOperationTest, StridedSlice) {
auto status = StridedSliceTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,54 @@
/* 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/tile_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
TEST_F(OpenCLOperationTest, TileChannels) {
auto status = TileChannelsTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, TileChannelsX4) {
auto status = TileChannelsX4Test(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, TileWidth) {
auto status = TileWidthTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, TileHeight) {
auto status = TileHeightTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, TileHWC) {
auto status = TileHWCTest(&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/transpose_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
namespace {
TEST_F(OpenCLOperationTest, Transpose) {
auto status = TransposeTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,54 @@
/* 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/winograd_test_util.h"
namespace tflite {
namespace gpu {
namespace cl {
TEST_F(OpenCLOperationTest, Winograd4x4To36TileX6) {
auto status = Winograd4x4To36TileX6Test(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, Winograd36To4x4Tile4x1) {
auto status = Winograd36To4x4Tile4x1Test(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, Winograd4x4To36) {
auto status = Winograd4x4To36Test(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, Winograd4x4To36Batch) {
auto status = Winograd4x4To36BatchTest(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
TEST_F(OpenCLOperationTest, Winograd36To4x4) {
auto status = Winograd36To4x4Test(&exec_env_);
ASSERT_TRUE(status.ok()) << status.message();
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,505 @@
/* 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/opencl_wrapper.h"
#if defined(_WIN32)
#define __WINDOWS__
#endif
#ifdef __WINDOWS__
#include <windows.h>
#else
#include <dlfcn.h>
#endif
#include <string>
#include "absl/strings/str_cat.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/tools/logging.h"
namespace tflite {
namespace gpu {
namespace cl {
#ifdef __ANDROID__
#define LoadFunction(function) \
if (use_wrapper) { \
function = reinterpret_cast<PFN_##function>(loadOpenCLPointer(#function)); \
} else { \
function = reinterpret_cast<PFN_##function>(dlsym(libopencl, #function)); \
}
namespace {
// Loads a library from Android SP-HAL namespace which includes libraries from
// the path /vendor/lib[64] directly and several sub-folders in it.
// First tries using dlopen(), which should work if the process is running with
// linker namespace "sphal" (so has permissions to sphal paths).
// If it fails, for example if process is running with linker default namespace
// because it's a sub-process of the app, then tries loading the library using
// a sphal helper loader function from Vendor NDK support library.
void* AndroidDlopenSphalLibrary(const char* filename, int dlopen_flags) {
void* lib = dlopen(filename, dlopen_flags);
if (lib != nullptr) {
return lib;
}
static void* (*android_load_sphal_library)(const char*, int) = nullptr;
if (android_load_sphal_library != nullptr) {
return android_load_sphal_library(filename, dlopen_flags);
}
android_load_sphal_library =
reinterpret_cast<decltype(android_load_sphal_library)>(
dlsym(RTLD_NEXT, "android_load_sphal_library"));
if (android_load_sphal_library == nullptr) {
void* vndk = dlopen("libvndksupport.so", RTLD_NOW);
if (vndk != nullptr) {
android_load_sphal_library =
reinterpret_cast<decltype(android_load_sphal_library)>(
dlsym(vndk, "android_load_sphal_library"));
}
if (android_load_sphal_library == nullptr) {
return nullptr;
}
}
return android_load_sphal_library(filename, dlopen_flags);
}
} // namespace
#elif defined(__WINDOWS__)
#define LoadFunction(function) \
function = \
reinterpret_cast<PFN_##function>(GetProcAddress(libopencl, #function));
#else
#define LoadFunction(function) \
function = reinterpret_cast<PFN_##function>(dlsym(libopencl, #function));
#endif
#define LoadFunctionExtension(plat_id, function) \
function = reinterpret_cast<PFN_##function>( \
clGetExtensionFunctionAddressForPlatform(plat_id, #function));
#ifdef __WINDOWS__
void LoadOpenCLFunctions(HMODULE libopencl);
#else
void LoadOpenCLFunctions(void* libopencl, bool use_wrapper);
#endif
absl::Status LoadOpenCLOnce() {
#ifdef __WINDOWS__
HMODULE libopencl = LoadLibraryA("OpenCL.dll");
if (libopencl) {
LoadOpenCLFunctions(libopencl);
return absl::OkStatus();
} else {
DWORD error_code = GetLastError();
return absl::UnknownError(absl::StrCat(
"Can not open OpenCL library on this device, error code - ",
error_code));
}
#else
void* libopencl = nullptr;
#ifdef __APPLE__
static const char* kClLibName =
"/System/Library/Frameworks/OpenCL.framework/OpenCL";
#else
static const char* kClLibName = "libOpenCL.so";
#endif
#ifdef __ANDROID__
libopencl = AndroidDlopenSphalLibrary(kClLibName, RTLD_NOW | RTLD_LOCAL);
if (!libopencl) {
// Legacy Pixel phone or auto path?
libopencl =
AndroidDlopenSphalLibrary("libOpenCL-pixel.so", RTLD_NOW | RTLD_LOCAL);
if (!libopencl) {
libopencl =
AndroidDlopenSphalLibrary("libOpenCL-car.so", RTLD_NOW | RTLD_LOCAL);
}
if (libopencl) {
typedef void (*enableOpenCL_t)();
enableOpenCL_t enableOpenCL =
reinterpret_cast<enableOpenCL_t>(dlsym(libopencl, "enableOpenCL"));
if (enableOpenCL != nullptr) {
enableOpenCL();
LoadOpenCLFunctions(libopencl, true);
return absl::OkStatus();
}
}
}
#else
libopencl = dlopen(kClLibName, RTLD_NOW | RTLD_LOCAL);
#endif
if (libopencl) {
TFLITE_LOG(INFO) << "Loaded OpenCL library with dlopen.";
LoadOpenCLFunctions(libopencl, false);
return absl::OkStatus();
}
const char* dlerror_result = dlerror();
TFLITE_LOG(INFO) << "Failed to load OpenCL library with dlopen: "
<< (dlerror_result ? dlerror_result : "unknown error")
<< ". Trying ICD loader.";
// Check if OpenCL functions are found via OpenCL ICD Loader.
LoadOpenCLFunctions(libopencl, false);
if (clGetPlatformIDs != nullptr) {
cl_uint num_platforms;
cl_int status = clGetPlatformIDs(0, nullptr, &num_platforms);
if (status == CL_SUCCESS && num_platforms != 0) {
TFLITE_LOG(INFO) << "Loaded OpenCL library with ICD loader.";
return absl::OkStatus();
}
return absl::UnknownError("OpenCL is not supported.");
}
// record error
dlerror_result = dlerror();
std::string error(dlerror_result ? dlerror_result : "unknown error");
return absl::UnknownError(
absl::StrCat("Can not open OpenCL library on this device - ", error));
#endif
}
absl::Status LoadOpenCL() {
static auto* status = new absl::Status(LoadOpenCLOnce());
return *status;
}
void LoadOpenCLFunctionExtensions(cl_platform_id platform_id) {
// cl_khr_command_buffer extension
LoadFunctionExtension(platform_id, clCreateCommandBufferKHR);
LoadFunctionExtension(platform_id, clRetainCommandBufferKHR);
LoadFunctionExtension(platform_id, clReleaseCommandBufferKHR);
LoadFunctionExtension(platform_id, clFinalizeCommandBufferKHR);
LoadFunctionExtension(platform_id, clEnqueueCommandBufferKHR);
LoadFunctionExtension(platform_id, clCommandNDRangeKernelKHR);
LoadFunctionExtension(platform_id, clGetCommandBufferInfoKHR);
// cl_arm_import_memory extension
LoadFunctionExtension(platform_id, clImportMemoryARM);
// cl_khr_semaphore extension
LoadFunctionExtension(platform_id, clCreateSemaphoreWithPropertiesKHR);
LoadFunctionExtension(platform_id, clEnqueueWaitSemaphoresKHR);
LoadFunctionExtension(platform_id, clEnqueueSignalSemaphoresKHR);
}
#ifdef __WINDOWS__
void LoadOpenCLFunctions(HMODULE libopencl) {
#else
void LoadOpenCLFunctions(void* libopencl, bool use_wrapper) {
#ifdef __ANDROID__
typedef void* (*loadOpenCLPointer_t)(const char* name);
loadOpenCLPointer_t loadOpenCLPointer;
if (use_wrapper) {
loadOpenCLPointer = reinterpret_cast<loadOpenCLPointer_t>(
dlsym(libopencl, "loadOpenCLPointer"));
}
#endif
#endif
LoadFunction(clGetPlatformIDs);
LoadFunction(clGetPlatformInfo);
LoadFunction(clGetDeviceIDs);
LoadFunction(clGetDeviceInfo);
LoadFunction(clCreateSubDevices);
LoadFunction(clRetainDevice);
LoadFunction(clReleaseDevice);
LoadFunction(clCreateContext);
LoadFunction(clCreateContextFromType);
LoadFunction(clRetainContext);
LoadFunction(clReleaseContext);
LoadFunction(clGetContextInfo);
LoadFunction(clCreateCommandQueueWithProperties);
LoadFunction(clRetainCommandQueue);
LoadFunction(clReleaseCommandQueue);
LoadFunction(clGetCommandQueueInfo);
LoadFunction(clCreateBuffer);
LoadFunction(clCreateSubBuffer);
LoadFunction(clCreateImage);
LoadFunction(clCreatePipe);
LoadFunction(clRetainMemObject);
LoadFunction(clReleaseMemObject);
LoadFunction(clGetSupportedImageFormats);
LoadFunction(clGetMemObjectInfo);
LoadFunction(clGetImageInfo);
LoadFunction(clGetPipeInfo);
LoadFunction(clSetMemObjectDestructorCallback);
LoadFunction(clSVMAlloc);
LoadFunction(clSVMFree);
LoadFunction(clCreateSamplerWithProperties);
LoadFunction(clRetainSampler);
LoadFunction(clReleaseSampler);
LoadFunction(clGetSamplerInfo);
LoadFunction(clCreateProgramWithSource);
LoadFunction(clCreateProgramWithBinary);
LoadFunction(clCreateProgramWithBuiltInKernels);
LoadFunction(clRetainProgram);
LoadFunction(clReleaseProgram);
LoadFunction(clBuildProgram);
LoadFunction(clCompileProgram);
LoadFunction(clLinkProgram);
LoadFunction(clUnloadPlatformCompiler);
LoadFunction(clGetProgramInfo);
LoadFunction(clGetProgramBuildInfo);
LoadFunction(clCreateKernel);
LoadFunction(clCreateKernelsInProgram);
LoadFunction(clRetainKernel);
LoadFunction(clReleaseKernel);
LoadFunction(clSetKernelArg);
LoadFunction(clSetKernelArgSVMPointer);
LoadFunction(clSetKernelExecInfo);
LoadFunction(clGetKernelInfo);
LoadFunction(clGetKernelArgInfo);
LoadFunction(clGetKernelWorkGroupInfo);
LoadFunction(clWaitForEvents);
LoadFunction(clGetEventInfo);
LoadFunction(clCreateUserEvent);
LoadFunction(clRetainEvent);
LoadFunction(clReleaseEvent);
LoadFunction(clSetUserEventStatus);
LoadFunction(clSetEventCallback);
LoadFunction(clGetEventProfilingInfo);
LoadFunction(clFlush);
LoadFunction(clFinish);
LoadFunction(clEnqueueReadBuffer);
LoadFunction(clEnqueueReadBufferRect);
LoadFunction(clEnqueueWriteBuffer);
LoadFunction(clEnqueueWriteBufferRect);
LoadFunction(clEnqueueFillBuffer);
LoadFunction(clEnqueueCopyBuffer);
LoadFunction(clEnqueueCopyBufferRect);
LoadFunction(clEnqueueReadImage);
LoadFunction(clEnqueueWriteImage);
LoadFunction(clEnqueueFillImage);
LoadFunction(clEnqueueCopyImage);
LoadFunction(clEnqueueCopyImageToBuffer);
LoadFunction(clEnqueueCopyBufferToImage);
LoadFunction(clEnqueueMapBuffer);
LoadFunction(clEnqueueMapImage);
LoadFunction(clEnqueueUnmapMemObject);
LoadFunction(clEnqueueMigrateMemObjects);
LoadFunction(clEnqueueNDRangeKernel);
LoadFunction(clEnqueueNativeKernel);
LoadFunction(clEnqueueMarkerWithWaitList);
LoadFunction(clEnqueueBarrierWithWaitList);
LoadFunction(clEnqueueSVMFree);
LoadFunction(clEnqueueSVMMemcpy);
LoadFunction(clEnqueueSVMMemFill);
LoadFunction(clEnqueueSVMMap);
LoadFunction(clEnqueueSVMUnmap);
LoadFunction(clGetExtensionFunctionAddressForPlatform);
LoadFunction(clCreateImage2D);
LoadFunction(clCreateImage3D);
LoadFunction(clEnqueueMarker);
LoadFunction(clEnqueueWaitForEvents);
LoadFunction(clEnqueueBarrier);
LoadFunction(clUnloadCompiler);
LoadFunction(clGetExtensionFunctionAddress);
LoadFunction(clCreateCommandQueue);
LoadFunction(clCreateSampler);
LoadFunction(clEnqueueTask);
#ifndef CL_DELEGATE_NO_GL
// OpenGL sharing
LoadFunction(clCreateFromGLBuffer);
LoadFunction(clCreateFromGLTexture);
LoadFunction(clEnqueueAcquireGLObjects);
LoadFunction(clEnqueueReleaseGLObjects);
// cl_khr_egl_event extension
LoadFunction(clCreateEventFromEGLSyncKHR);
// EGL sharing
LoadFunction(clCreateFromEGLImageKHR);
LoadFunction(clEnqueueAcquireEGLObjectsKHR);
LoadFunction(clEnqueueReleaseEGLObjectsKHR);
#endif
LoadQcomExtensionFunctions();
}
// No OpenCL support, do not set function addresses
PFN_clGetPlatformIDs clGetPlatformIDs;
PFN_clGetPlatformInfo clGetPlatformInfo;
PFN_clGetDeviceIDs clGetDeviceIDs;
PFN_clGetDeviceInfo clGetDeviceInfo;
PFN_clCreateSubDevices clCreateSubDevices;
PFN_clRetainDevice clRetainDevice;
PFN_clReleaseDevice clReleaseDevice;
PFN_clCreateContext clCreateContext;
PFN_clCreateContextFromType clCreateContextFromType;
PFN_clRetainContext clRetainContext;
PFN_clReleaseContext clReleaseContext;
PFN_clGetContextInfo clGetContextInfo;
PFN_clCreateCommandQueueWithProperties clCreateCommandQueueWithProperties;
PFN_clRetainCommandQueue clRetainCommandQueue;
PFN_clReleaseCommandQueue clReleaseCommandQueue;
PFN_clGetCommandQueueInfo clGetCommandQueueInfo;
PFN_clCreateBuffer clCreateBuffer;
PFN_clCreateSubBuffer clCreateSubBuffer;
PFN_clCreateImage clCreateImage;
PFN_clCreatePipe clCreatePipe;
PFN_clRetainMemObject clRetainMemObject;
PFN_clReleaseMemObject clReleaseMemObject;
PFN_clGetSupportedImageFormats clGetSupportedImageFormats;
PFN_clGetMemObjectInfo clGetMemObjectInfo;
PFN_clGetImageInfo clGetImageInfo;
PFN_clGetPipeInfo clGetPipeInfo;
PFN_clSetMemObjectDestructorCallback clSetMemObjectDestructorCallback;
PFN_clSVMAlloc clSVMAlloc;
PFN_clSVMFree clSVMFree;
PFN_clCreateSamplerWithProperties clCreateSamplerWithProperties;
PFN_clRetainSampler clRetainSampler;
PFN_clReleaseSampler clReleaseSampler;
PFN_clGetSamplerInfo clGetSamplerInfo;
PFN_clCreateProgramWithSource clCreateProgramWithSource;
PFN_clCreateProgramWithBinary clCreateProgramWithBinary;
PFN_clCreateProgramWithBuiltInKernels clCreateProgramWithBuiltInKernels;
PFN_clRetainProgram clRetainProgram;
PFN_clReleaseProgram clReleaseProgram;
PFN_clBuildProgram clBuildProgram;
PFN_clCompileProgram clCompileProgram;
PFN_clLinkProgram clLinkProgram;
PFN_clUnloadPlatformCompiler clUnloadPlatformCompiler;
PFN_clGetProgramInfo clGetProgramInfo;
PFN_clGetProgramBuildInfo clGetProgramBuildInfo;
PFN_clCreateKernel clCreateKernel;
PFN_clCreateKernelsInProgram clCreateKernelsInProgram;
PFN_clRetainKernel clRetainKernel;
PFN_clReleaseKernel clReleaseKernel;
PFN_clSetKernelArg clSetKernelArg;
PFN_clSetKernelArgSVMPointer clSetKernelArgSVMPointer;
PFN_clSetKernelExecInfo clSetKernelExecInfo;
PFN_clGetKernelInfo clGetKernelInfo;
PFN_clGetKernelArgInfo clGetKernelArgInfo;
PFN_clGetKernelWorkGroupInfo clGetKernelWorkGroupInfo;
PFN_clWaitForEvents clWaitForEvents;
PFN_clGetEventInfo clGetEventInfo;
PFN_clCreateUserEvent clCreateUserEvent;
PFN_clRetainEvent clRetainEvent;
PFN_clReleaseEvent clReleaseEvent;
PFN_clSetUserEventStatus clSetUserEventStatus;
PFN_clSetEventCallback clSetEventCallback;
PFN_clGetEventProfilingInfo clGetEventProfilingInfo;
PFN_clFlush clFlush;
PFN_clFinish clFinish;
PFN_clEnqueueReadBuffer clEnqueueReadBuffer;
PFN_clEnqueueReadBufferRect clEnqueueReadBufferRect;
PFN_clEnqueueWriteBuffer clEnqueueWriteBuffer;
PFN_clEnqueueWriteBufferRect clEnqueueWriteBufferRect;
PFN_clEnqueueFillBuffer clEnqueueFillBuffer;
PFN_clEnqueueCopyBuffer clEnqueueCopyBuffer;
PFN_clEnqueueCopyBufferRect clEnqueueCopyBufferRect;
PFN_clEnqueueReadImage clEnqueueReadImage;
PFN_clEnqueueWriteImage clEnqueueWriteImage;
PFN_clEnqueueFillImage clEnqueueFillImage;
PFN_clEnqueueCopyImage clEnqueueCopyImage;
PFN_clEnqueueCopyImageToBuffer clEnqueueCopyImageToBuffer;
PFN_clEnqueueCopyBufferToImage clEnqueueCopyBufferToImage;
PFN_clEnqueueMapBuffer clEnqueueMapBuffer;
PFN_clEnqueueMapImage clEnqueueMapImage;
PFN_clEnqueueUnmapMemObject clEnqueueUnmapMemObject;
PFN_clEnqueueMigrateMemObjects clEnqueueMigrateMemObjects;
PFN_clEnqueueNDRangeKernel clEnqueueNDRangeKernel;
PFN_clEnqueueNativeKernel clEnqueueNativeKernel;
PFN_clEnqueueMarkerWithWaitList clEnqueueMarkerWithWaitList;
PFN_clEnqueueBarrierWithWaitList clEnqueueBarrierWithWaitList;
PFN_clEnqueueSVMFree clEnqueueSVMFree;
PFN_clEnqueueSVMMemcpy clEnqueueSVMMemcpy;
PFN_clEnqueueSVMMemFill clEnqueueSVMMemFill;
PFN_clEnqueueSVMMap clEnqueueSVMMap;
PFN_clEnqueueSVMUnmap clEnqueueSVMUnmap;
PFN_clGetExtensionFunctionAddressForPlatform
clGetExtensionFunctionAddressForPlatform;
PFN_clCreateImage2D clCreateImage2D;
PFN_clCreateImage3D clCreateImage3D;
PFN_clEnqueueMarker clEnqueueMarker;
PFN_clEnqueueWaitForEvents clEnqueueWaitForEvents;
PFN_clEnqueueBarrier clEnqueueBarrier;
PFN_clUnloadCompiler clUnloadCompiler;
PFN_clGetExtensionFunctionAddress clGetExtensionFunctionAddress;
PFN_clCreateCommandQueue clCreateCommandQueue;
PFN_clCreateSampler clCreateSampler;
PFN_clEnqueueTask clEnqueueTask;
#ifndef CL_DELEGATE_NO_GL
// OpenGL sharing
PFN_clCreateFromGLBuffer clCreateFromGLBuffer;
PFN_clCreateFromGLTexture clCreateFromGLTexture;
PFN_clEnqueueAcquireGLObjects clEnqueueAcquireGLObjects;
PFN_clEnqueueReleaseGLObjects clEnqueueReleaseGLObjects;
// cl_khr_egl_event extension
PFN_clCreateEventFromEGLSyncKHR clCreateEventFromEGLSyncKHR;
// EGL sharing
PFN_clCreateFromEGLImageKHR clCreateFromEGLImageKHR;
PFN_clEnqueueAcquireEGLObjectsKHR clEnqueueAcquireEGLObjectsKHR;
PFN_clEnqueueReleaseEGLObjectsKHR clEnqueueReleaseEGLObjectsKHR;
#endif
// cl_khr_command_buffer extension
PFN_clCreateCommandBufferKHR clCreateCommandBufferKHR;
PFN_clRetainCommandBufferKHR clRetainCommandBufferKHR;
PFN_clReleaseCommandBufferKHR clReleaseCommandBufferKHR;
PFN_clFinalizeCommandBufferKHR clFinalizeCommandBufferKHR;
PFN_clEnqueueCommandBufferKHR clEnqueueCommandBufferKHR;
PFN_clCommandNDRangeKernelKHR clCommandNDRangeKernelKHR;
PFN_clGetCommandBufferInfoKHR clGetCommandBufferInfoKHR;
// cl_arm_import_memory extension
PFN_clImportMemoryARM clImportMemoryARM;
// cl_khr_semaphore extension
PFN_clCreateSemaphoreWithPropertiesKHR clCreateSemaphoreWithPropertiesKHR;
PFN_clEnqueueWaitSemaphoresKHR clEnqueueWaitSemaphoresKHR;
PFN_clEnqueueSignalSemaphoresKHR clEnqueueSignalSemaphoresKHR;
DEFINE_QCOM_FUNCTION_PTRS
cl_mem CreateImage2DLegacy(cl_context context, cl_mem_flags flags,
const cl_image_format* image_format,
const cl_image_desc* image_desc, void* host_ptr,
cl_int* errcode_ret) {
if (clCreateImage) { // clCreateImage available since OpenCL 1.2
return clCreateImage(context, flags, image_format, image_desc, host_ptr,
errcode_ret);
} else {
return clCreateImage2D(context, flags, image_format,
image_desc->image_width, image_desc->image_height,
image_desc->image_row_pitch, host_ptr, errcode_ret);
}
}
cl_mem CreateImage3DLegacy(cl_context context, cl_mem_flags flags,
const cl_image_format* image_format,
const cl_image_desc* image_desc, void* host_ptr,
cl_int* errcode_ret) {
if (clCreateImage) { // clCreateImage available since OpenCL 1.2
return clCreateImage(context, flags, image_format, image_desc, host_ptr,
errcode_ret);
} else {
return clCreateImage3D(context, flags, image_format,
image_desc->image_width, image_desc->image_height,
image_desc->image_depth, image_desc->image_row_pitch,
image_desc->image_slice_pitch, host_ptr,
errcode_ret);
}
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,763 @@
/* 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_OPENCL_WRAPPER_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_OPENCL_WRAPPER_H_
#include <CL/cl.h>
#include <CL/cl_egl.h>
#include <CL/cl_ext.h>
#include <CL/cl_gl.h>
#include <CL/cl_platform.h>
#include "tensorflow/lite/delegates/gpu/cl/default/qcom_wrapper.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
namespace tflite {
namespace gpu {
namespace cl {
absl::Status LoadOpenCL();
void LoadOpenCLFunctionExtensions(cl_platform_id platform_id);
typedef cl_int(CL_API_CALL *PFN_clGetPlatformIDs)(
cl_uint /* num_entries */, cl_platform_id * /* platforms */,
cl_uint * /* num_platforms */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clGetPlatformInfo)(
cl_platform_id /* platform */, cl_platform_info /* param_name */,
size_t /* param_value_size */, void * /* param_value */,
size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clGetDeviceIDs)(
cl_platform_id /* platform */, cl_device_type /* device_type */,
cl_uint /* num_entries */, cl_device_id * /* devices */,
cl_uint * /* num_devices */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clGetDeviceInfo)(
cl_device_id /* device */, cl_device_info /* param_name */,
size_t /* param_value_size */, void * /* param_value */,
size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clCreateSubDevices)(
cl_device_id /* in_device */,
const cl_device_partition_property * /* properties */,
cl_uint /* num_devices */, cl_device_id * /* out_devices */,
cl_uint * /* num_devices_ret */) CL_API_SUFFIX__VERSION_1_2;
typedef cl_int(CL_API_CALL *PFN_clRetainDevice)(cl_device_id /* device */)
CL_API_SUFFIX__VERSION_1_2;
typedef cl_int(CL_API_CALL *PFN_clReleaseDevice)(cl_device_id /* device */)
CL_API_SUFFIX__VERSION_1_2;
typedef cl_context(CL_API_CALL *PFN_clCreateContext)(
const cl_context_properties * /* properties */, cl_uint /* num_devices */,
const cl_device_id * /* devices */,
void(CL_CALLBACK * /* pfn_notify */)(const char *, const void *, size_t,
void *),
void * /* user_data */,
cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_context(CL_API_CALL *PFN_clCreateContextFromType)(
const cl_context_properties * /* properties */,
cl_device_type /* device_type */,
void(CL_CALLBACK * /* pfn_notify*/)(const char *, const void *, size_t,
void *),
void * /* user_data */,
cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clRetainContext)(cl_context /* context */)
CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clReleaseContext)(cl_context /* context */)
CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clGetContextInfo)(
cl_context /* context */, cl_context_info /* param_name */,
size_t /* param_value_size */, void * /* param_value */,
size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_command_queue(CL_API_CALL *PFN_clCreateCommandQueueWithProperties)(
cl_context /* context */, cl_device_id /* device */,
const cl_queue_properties * /* properties */,
cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_2_0;
typedef cl_int(CL_API_CALL *PFN_clRetainCommandQueue)(
cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clReleaseCommandQueue)(
cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clGetCommandQueueInfo)(
cl_command_queue /* command_queue */,
cl_command_queue_info /* param_name */, size_t /* param_value_size */,
void * /* param_value */,
size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_mem(CL_API_CALL *PFN_clCreateBuffer)(
cl_context /* context */, cl_mem_flags /* flags */, size_t /* size */,
void * /* host_ptr */,
cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_mem(CL_API_CALL *PFN_clCreateSubBuffer)(
cl_mem /* buffer */, cl_mem_flags /* flags */,
cl_buffer_create_type /* buffer_create_type */,
const void * /* buffer_create_info */,
cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_1;
typedef cl_mem(CL_API_CALL *PFN_clCreateImage)(
cl_context /* context */, cl_mem_flags /* flags */,
const cl_image_format * /* image_format */,
const cl_image_desc * /* image_desc */, void * /* host_ptr */,
cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_2;
typedef cl_mem(CL_API_CALL *PFN_clCreatePipe)(
cl_context /* context */, cl_mem_flags /* flags */,
cl_uint /* pipe_packet_size */, cl_uint /* pipe_max_packets */,
const cl_pipe_properties * /* properties */,
cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_2_0;
typedef cl_int(CL_API_CALL *PFN_clRetainMemObject)(cl_mem /* memobj */)
CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clReleaseMemObject)(cl_mem /* memobj */)
CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clGetSupportedImageFormats)(
cl_context /* context */, cl_mem_flags /* flags */,
cl_mem_object_type /* image_type */, cl_uint /* num_entries */,
cl_image_format * /* image_formats */,
cl_uint * /* num_image_formats */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clGetMemObjectInfo)(
cl_mem /* memobj */, cl_mem_info /* param_name */,
size_t /* param_value_size */, void * /* param_value */,
size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clGetImageInfo)(
cl_mem /* image */, cl_image_info /* param_name */,
size_t /* param_value_size */, void * /* param_value */,
size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clGetPipeInfo)(
cl_mem /* pipe */, cl_pipe_info /* param_name */,
size_t /* param_value_size */, void * /* param_value */,
size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_2_0;
typedef cl_int(CL_API_CALL *PFN_clSetMemObjectDestructorCallback)(
cl_mem /* memobj */,
void(CL_CALLBACK * /*pfn_notify*/)(cl_mem /* memobj */,
void * /*user_data*/),
void * /*user_data */) CL_API_SUFFIX__VERSION_1_1;
typedef void *(CL_API_CALL *PFN_clSVMAlloc)(
cl_context /* context */, cl_svm_mem_flags /* flags */, size_t /* size */,
cl_uint /* alignment */)CL_API_SUFFIX__VERSION_2_0;
typedef void(CL_API_CALL *PFN_clSVMFree)(cl_context /* context */,
void * /* svm_pointer */)
CL_API_SUFFIX__VERSION_2_0;
typedef cl_sampler(CL_API_CALL *PFN_clCreateSamplerWithProperties)(
cl_context /* context */,
const cl_sampler_properties * /* normalized_coords */,
cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_2_0;
typedef cl_int(CL_API_CALL *PFN_clRetainSampler)(cl_sampler /* sampler */)
CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clReleaseSampler)(cl_sampler /* sampler */)
CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clGetSamplerInfo)(
cl_sampler /* sampler */, cl_sampler_info /* param_name */,
size_t /* param_value_size */, void * /* param_value */,
size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_program(CL_API_CALL *PFN_clCreateProgramWithSource)(
cl_context /* context */, cl_uint /* count */, const char ** /* strings */,
const size_t * /* lengths */,
cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_program(CL_API_CALL *PFN_clCreateProgramWithBinary)(
cl_context /* context */, cl_uint /* num_devices */,
const cl_device_id * /* device_list */, const size_t * /* lengths */,
const unsigned char ** /* binaries */, cl_int * /* binary_status */,
cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_program(CL_API_CALL *PFN_clCreateProgramWithBuiltInKernels)(
cl_context /* context */, cl_uint /* num_devices */,
const cl_device_id * /* device_list */, const char * /* kernel_names */,
cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_2;
typedef cl_int(CL_API_CALL *PFN_clRetainProgram)(cl_program /* program */)
CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clReleaseProgram)(cl_program /* program */)
CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clBuildProgram)(
cl_program /* program */, cl_uint /* num_devices */,
const cl_device_id * /* device_list */, const char * /* options */,
void(CL_CALLBACK * /* pfn_notify */)(cl_program /* program */,
void * /* user_data */),
void * /* user_data */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clCompileProgram)(
cl_program /* program */, cl_uint /* num_devices */,
const cl_device_id * /* device_list */, const char * /* options */,
cl_uint /* num_input_headers */, const cl_program * /* input_headers */,
const char ** /* header_include_names */,
void(CL_CALLBACK * /* pfn_notify */)(cl_program /* program */,
void * /* user_data */),
void * /* user_data */) CL_API_SUFFIX__VERSION_1_2;
typedef cl_program(CL_API_CALL *PFN_clLinkProgram)(
cl_context /* context */, cl_uint /* num_devices */,
const cl_device_id * /* device_list */, const char * /* options */,
cl_uint /* num_input_programs */, const cl_program * /* input_programs */,
void(CL_CALLBACK * /* pfn_notify */)(cl_program /* program */,
void * /* user_data */),
void * /* user_data */,
cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_2;
typedef cl_int(CL_API_CALL *PFN_clUnloadPlatformCompiler)(
cl_platform_id /* platform */) CL_API_SUFFIX__VERSION_1_2;
typedef cl_int(CL_API_CALL *PFN_clGetProgramInfo)(
cl_program /* program */, cl_program_info /* param_name */,
size_t /* param_value_size */, void * /* param_value */,
size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clGetProgramBuildInfo)(
cl_program /* program */, cl_device_id /* device */,
cl_program_build_info /* param_name */, size_t /* param_value_size */,
void * /* param_value */,
size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_kernel(CL_API_CALL *PFN_clCreateKernel)(
cl_program /* program */, const char * /* kernel_name */,
cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clCreateKernelsInProgram)(
cl_program /* program */, cl_uint /* num_kernels */,
cl_kernel * /* kernels */,
cl_uint * /* num_kernels_ret */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clRetainKernel)(cl_kernel /* kernel */)
CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clReleaseKernel)(cl_kernel /* kernel */)
CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clSetKernelArg)(
cl_kernel /* kernel */, cl_uint /* arg_index */, size_t /* arg_size */,
const void * /* arg_value */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clSetKernelArgSVMPointer)(
cl_kernel /* kernel */, cl_uint /* arg_index */,
const void * /* arg_value */) CL_API_SUFFIX__VERSION_2_0;
typedef cl_int(CL_API_CALL *PFN_clSetKernelExecInfo)(
cl_kernel /* kernel */, cl_kernel_exec_info /* param_name */,
size_t /* param_value_size */,
const void * /* param_value */) CL_API_SUFFIX__VERSION_2_0;
typedef cl_int(CL_API_CALL *PFN_clGetKernelInfo)(
cl_kernel /* kernel */, cl_kernel_info /* param_name */,
size_t /* param_value_size */, void * /* param_value */,
size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clGetKernelArgInfo)(
cl_kernel /* kernel */, cl_uint /* arg_indx */,
cl_kernel_arg_info /* param_name */, size_t /* param_value_size */,
void * /* param_value */,
size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_2;
typedef cl_int(CL_API_CALL *PFN_clGetKernelWorkGroupInfo)(
cl_kernel /* kernel */, cl_device_id /* device */,
cl_kernel_work_group_info /* param_name */, size_t /* param_value_size */,
void * /* param_value */,
size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clWaitForEvents)(
cl_uint /* num_events */,
const cl_event * /* event_list */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clGetEventInfo)(
cl_event /* event */, cl_event_info /* param_name */,
size_t /* param_value_size */, void * /* param_value */,
size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_event(CL_API_CALL *PFN_clCreateUserEvent)(cl_context /* context */,
cl_int * /* errcode_ret */)
CL_API_SUFFIX__VERSION_1_1;
typedef cl_int(CL_API_CALL *PFN_clRetainEvent)(cl_event /* event */)
CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clReleaseEvent)(cl_event /* event */)
CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clSetUserEventStatus)(
cl_event /* event */,
cl_int /* execution_status */) CL_API_SUFFIX__VERSION_1_1;
typedef cl_int(CL_API_CALL *PFN_clSetEventCallback)(
cl_event /* event */, cl_int /* command_exec_callback_type */,
void(CL_CALLBACK * /* pfn_notify */)(cl_event, cl_int, void *),
void * /* user_data */) CL_API_SUFFIX__VERSION_1_1;
typedef cl_int(CL_API_CALL *PFN_clGetEventProfilingInfo)(
cl_event /* event */, cl_profiling_info /* param_name */,
size_t /* param_value_size */, void * /* param_value */,
size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clFlush)(cl_command_queue /* command_queue */)
CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clFinish)(cl_command_queue /* command_queue */)
CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clEnqueueReadBuffer)(
cl_command_queue /* command_queue */, cl_mem /* buffer */,
cl_bool /* blocking_read */, size_t /* offset */, size_t /* size */,
void * /* ptr */, cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */,
cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clEnqueueReadBufferRect)(
cl_command_queue /* command_queue */, cl_mem /* buffer */,
cl_bool /* blocking_read */, const size_t * /* buffer_offset */,
const size_t * /* host_offset */, const size_t * /* region */,
size_t /* buffer_row_pitch */, size_t /* buffer_slice_pitch */,
size_t /* host_row_pitch */, size_t /* host_slice_pitch */,
void * /* ptr */, cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */,
cl_event * /* event */) CL_API_SUFFIX__VERSION_1_1;
typedef cl_int(CL_API_CALL *PFN_clEnqueueWriteBuffer)(
cl_command_queue /* command_queue */, cl_mem /* buffer */,
cl_bool /* blocking_write */, size_t /* offset */, size_t /* size */,
const void * /* ptr */, cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */,
cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clEnqueueWriteBufferRect)(
cl_command_queue /* command_queue */, cl_mem /* buffer */,
cl_bool /* blocking_write */, const size_t * /* buffer_offset */,
const size_t * /* host_offset */, const size_t * /* region */,
size_t /* buffer_row_pitch */, size_t /* buffer_slice_pitch */,
size_t /* host_row_pitch */, size_t /* host_slice_pitch */,
const void * /* ptr */, cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */,
cl_event * /* event */) CL_API_SUFFIX__VERSION_1_1;
typedef cl_int(CL_API_CALL *PFN_clEnqueueFillBuffer)(
cl_command_queue /* command_queue */, cl_mem /* buffer */,
const void * /* pattern */, size_t /* pattern_size */, size_t /* offset */,
size_t /* size */, cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */,
cl_event * /* event */) CL_API_SUFFIX__VERSION_1_2;
typedef cl_int(CL_API_CALL *PFN_clEnqueueCopyBuffer)(
cl_command_queue /* command_queue */, cl_mem /* src_buffer */,
cl_mem /* dst_buffer */, size_t /* src_offset */, size_t /* dst_offset */,
size_t /* size */, cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */,
cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clEnqueueCopyBufferRect)(
cl_command_queue /* command_queue */, cl_mem /* src_buffer */,
cl_mem /* dst_buffer */, const size_t * /* src_origin */,
const size_t * /* dst_origin */, const size_t * /* region */,
size_t /* src_row_pitch */, size_t /* src_slice_pitch */,
size_t /* dst_row_pitch */, size_t /* dst_slice_pitch */,
cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */,
cl_event * /* event */) CL_API_SUFFIX__VERSION_1_1;
typedef cl_int(CL_API_CALL *PFN_clEnqueueReadImage)(
cl_command_queue /* command_queue */, cl_mem /* image */,
cl_bool /* blocking_read */, const size_t * /* origin[3] */,
const size_t * /* region[3] */, size_t /* row_pitch */,
size_t /* slice_pitch */, void * /* ptr */,
cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */,
cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clEnqueueWriteImage)(
cl_command_queue /* command_queue */, cl_mem /* image */,
cl_bool /* blocking_write */, const size_t * /* origin[3] */,
const size_t * /* region[3] */, size_t /* input_row_pitch */,
size_t /* input_slice_pitch */, const void * /* ptr */,
cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */,
cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clEnqueueFillImage)(
cl_command_queue /* command_queue */, cl_mem /* image */,
const void * /* fill_color */, const size_t * /* origin[3] */,
const size_t * /* region[3] */, cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */,
cl_event * /* event */) CL_API_SUFFIX__VERSION_1_2;
typedef cl_int(CL_API_CALL *PFN_clEnqueueCopyImage)(
cl_command_queue /* command_queue */, cl_mem /* src_image */,
cl_mem /* dst_image */, const size_t * /* src_origin[3] */,
const size_t * /* dst_origin[3] */, const size_t * /* region[3] */,
cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */,
cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clEnqueueCopyImageToBuffer)(
cl_command_queue /* command_queue */, cl_mem /* src_image */,
cl_mem /* dst_buffer */, const size_t * /* src_origin[3] */,
const size_t * /* region[3] */, size_t /* dst_offset */,
cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */,
cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clEnqueueCopyBufferToImage)(
cl_command_queue /* command_queue */, cl_mem /* src_buffer */,
cl_mem /* dst_image */, size_t /* src_offset */,
const size_t * /* dst_origin[3] */, const size_t * /* region[3] */,
cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */,
cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0;
typedef void *(CL_API_CALL *PFN_clEnqueueMapBuffer)(
cl_command_queue /* command_queue */, cl_mem /* buffer */,
cl_bool /* blocking_map */, cl_map_flags /* map_flags */,
size_t /* offset */, size_t /* size */,
cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */, cl_event * /* event */,
cl_int * /* errcode_ret */)CL_API_SUFFIX__VERSION_1_0;
typedef void *(CL_API_CALL *PFN_clEnqueueMapImage)(
cl_command_queue /* command_queue */, cl_mem /* image */,
cl_bool /* blocking_map */, cl_map_flags /* map_flags */,
const size_t * /* origin[3] */, const size_t * /* region[3] */,
size_t * /* image_row_pitch */, size_t * /* image_slice_pitch */,
cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */, cl_event * /* event */,
cl_int * /* errcode_ret */)CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clEnqueueUnmapMemObject)(
cl_command_queue /* command_queue */, cl_mem /* memobj */,
void * /* mapped_ptr */, cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */,
cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clEnqueueMigrateMemObjects)(
cl_command_queue /* command_queue */, cl_uint /* num_mem_objects */,
const cl_mem * /* mem_objects */, cl_mem_migration_flags /* flags */,
cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */,
cl_event * /* event */) CL_API_SUFFIX__VERSION_1_2;
typedef cl_int(CL_API_CALL *PFN_clEnqueueNDRangeKernel)(
cl_command_queue /* command_queue */, cl_kernel /* kernel */,
cl_uint /* work_dim */, const size_t * /* global_work_offset */,
const size_t * /* global_work_size */, const size_t * /* local_work_size */,
cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */,
cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clEnqueueNativeKernel)(
cl_command_queue /* command_queue */,
void(CL_CALLBACK * /*user_func*/)(void *), void * /* args */,
size_t /* cb_args */, cl_uint /* num_mem_objects */,
const cl_mem * /* mem_list */, const void ** /* args_mem_loc */,
cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */,
cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0;
typedef cl_int(CL_API_CALL *PFN_clEnqueueMarkerWithWaitList)(
cl_command_queue /* command_queue */, cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */,
cl_event * /* event */) CL_API_SUFFIX__VERSION_1_2;
typedef cl_int(CL_API_CALL *PFN_clEnqueueBarrierWithWaitList)(
cl_command_queue /* command_queue */, cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */,
cl_event * /* event */) CL_API_SUFFIX__VERSION_1_2;
typedef cl_int(CL_API_CALL *PFN_clEnqueueSVMFree)(
cl_command_queue /* command_queue */, cl_uint /* num_svm_pointers */,
void *[] /* svm_pointers[] */,
void(CL_CALLBACK * /*pfn_free_func*/)(cl_command_queue /* queue */,
cl_uint /* num_svm_pointers */,
void *[] /* svm_pointers[] */,
void * /* user_data */),
void * /* user_data */, cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */,
cl_event * /* event */) CL_API_SUFFIX__VERSION_2_0;
typedef cl_int(CL_API_CALL *PFN_clEnqueueSVMMemcpy)(
cl_command_queue /* command_queue */, cl_bool /* blocking_copy */,
void * /* dst_ptr */, const void * /* src_ptr */, size_t /* size */,
cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */,
cl_event * /* event */) CL_API_SUFFIX__VERSION_2_0;
typedef cl_int(CL_API_CALL *PFN_clEnqueueSVMMemFill)(
cl_command_queue /* command_queue */, void * /* svm_ptr */,
const void * /* pattern */, size_t /* pattern_size */, size_t /* size */,
cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */,
cl_event * /* event */) CL_API_SUFFIX__VERSION_2_0;
typedef cl_int(CL_API_CALL *PFN_clEnqueueSVMMap)(
cl_command_queue /* command_queue */, cl_bool /* blocking_map */,
cl_map_flags /* flags */, void * /* svm_ptr */, size_t /* size */,
cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */,
cl_event * /* event */) CL_API_SUFFIX__VERSION_2_0;
typedef cl_int(CL_API_CALL *PFN_clEnqueueSVMUnmap)(
cl_command_queue /* command_queue */, void * /* svm_ptr */,
cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */,
cl_event * /* event */) CL_API_SUFFIX__VERSION_2_0;
typedef void *(CL_API_CALL *PFN_clGetExtensionFunctionAddressForPlatform)(
cl_platform_id /* platform */,
const char * /* func_name */)CL_API_SUFFIX__VERSION_1_2;
typedef cl_mem(CL_API_CALL *PFN_clCreateImage2D)(
cl_context /* context */, cl_mem_flags /* flags */,
const cl_image_format * /* image_format */, size_t /* image_width */,
size_t /* image_height */, size_t /* image_row_pitch */,
void * /* host_ptr */, cl_int * /* errcode_ret */);
typedef cl_mem(CL_API_CALL *PFN_clCreateImage3D)(
cl_context /* context */, cl_mem_flags /* flags */,
const cl_image_format * /* image_format */, size_t /* image_width */,
size_t /* image_height */, size_t /* image_depth */,
size_t /* image_row_pitch */, size_t /* image_slice_pitch */,
void * /* host_ptr */, cl_int * /* errcode_ret */);
typedef cl_int(CL_API_CALL *PFN_clEnqueueMarker)(
cl_command_queue /* command_queue */, cl_event * /* event */);
typedef cl_int(CL_API_CALL *PFN_clEnqueueWaitForEvents)(
cl_command_queue /* command_queue */, cl_uint /* num_events */,
const cl_event * /* event_list */);
typedef cl_int(CL_API_CALL *PFN_clEnqueueBarrier)(
cl_command_queue /* command_queue */);
typedef cl_int(CL_API_CALL *PFN_clUnloadCompiler)();
typedef void *(CL_API_CALL *PFN_clGetExtensionFunctionAddress)(
const char * /* func_name */);
typedef cl_command_queue(CL_API_CALL *PFN_clCreateCommandQueue)(
cl_context /* context */, cl_device_id /* device */,
cl_command_queue_properties /* properties */, cl_int * /* errcode_ret */);
typedef cl_sampler(CL_API_CALL *PFN_clCreateSampler)(
cl_context /* context */, cl_bool /* normalized_coords */,
cl_addressing_mode /* addressing_mode */, cl_filter_mode /* filter_mode */,
cl_int * /* errcode_ret */);
typedef cl_int(CL_API_CALL *PFN_clEnqueueTask)(
cl_command_queue /* command_queue */, cl_kernel /* kernel */,
cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */, cl_event * /* event */);
#ifndef CL_DELEGATE_NO_GL
// OpenGL sharing
typedef cl_mem(CL_API_CALL *PFN_clCreateFromGLBuffer)(cl_context, cl_mem_flags,
cl_GLuint, int *);
typedef cl_mem(CL_API_CALL *PFN_clCreateFromGLTexture)(
cl_context /* context */, cl_mem_flags /* flags */, cl_GLenum /* target */,
cl_GLint /* miplevel */, cl_GLuint /* texture */,
cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_2;
typedef cl_int(CL_API_CALL *PFN_clEnqueueAcquireGLObjects)(
cl_command_queue /* command_queue */, cl_uint /* num_objects */,
const cl_mem * /* mem_objects */, cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */, cl_event * /* event */);
typedef cl_int(CL_API_CALL *PFN_clEnqueueReleaseGLObjects)(
cl_command_queue /* command_queue */, cl_uint /* num_objects */,
const cl_mem * /* mem_objects */, cl_uint /* num_events_in_wait_list */,
const cl_event * /* event_wait_list */,
cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0;
// cl_khr_egl_event extension
// CLeglDisplayKHR is an opaque handle to an EGLDisplay
typedef void *CLeglDisplayKHR;
// CLeglSyncKHR is an opaque handle to an EGLSync object
typedef void *CLeglSyncKHR;
typedef cl_event(CL_API_CALL *PFN_clCreateEventFromEGLSyncKHR)(
cl_context /* context */, CLeglSyncKHR /* sync */,
CLeglDisplayKHR /* display */, cl_int * /* errcode_ret */);
// EGL sharing
typedef cl_mem(CL_API_CALL *PFN_clCreateFromEGLImageKHR)(
cl_context /*context*/, CLeglDisplayKHR /*display*/,
CLeglImageKHR /*image*/, cl_mem_flags /*flags*/,
const cl_egl_image_properties_khr * /*properties*/,
cl_int * /*errcode_ret*/);
typedef cl_int(CL_API_CALL *PFN_clEnqueueAcquireEGLObjectsKHR)(
cl_command_queue /*command_queue*/, cl_uint /*num_objects*/,
const cl_mem * /*mem_objects*/, cl_uint /*num_events_in_wait_list*/,
const cl_event * /*event_wait_list*/, cl_event * /*event*/);
typedef cl_int(CL_API_CALL *PFN_clEnqueueReleaseEGLObjectsKHR)(
cl_command_queue /*command_queue*/, cl_uint /*num_objects*/,
const cl_mem * /*mem_objects*/, cl_uint /*num_events_in_wait_list*/,
const cl_event * /*event_wait_list*/, cl_event * /*event*/);
#endif
// cl_khr_command_buffer
typedef cl_command_buffer_khr(CL_API_CALL *PFN_clCreateCommandBufferKHR)(
cl_uint /*num_queues*/, const cl_command_queue * /*queues*/,
const cl_command_buffer_properties_khr * /*properties*/,
cl_int * /*errcode_ret*/);
typedef cl_int(CL_API_CALL *PFN_clRetainCommandBufferKHR)(
cl_command_buffer_khr /*command_buffer*/);
typedef cl_int(CL_API_CALL *PFN_clReleaseCommandBufferKHR)(
cl_command_buffer_khr /*command_buffer*/);
typedef cl_int(CL_API_CALL *PFN_clFinalizeCommandBufferKHR)(
cl_command_buffer_khr /*command_buffer*/);
typedef cl_int(CL_API_CALL *PFN_clEnqueueCommandBufferKHR)(
cl_uint /*num_queues*/, cl_command_queue * /*queues*/,
cl_command_buffer_khr /*command_buffer*/,
cl_uint /*num_events_in_wait_list*/, const cl_event * /*event_wait_list*/,
cl_event * /*event*/);
#if CL_KHR_COMMAND_BUFFER_EXTENSION_VERSION >= CL_MAKE_VERSION(0, 9, 5)
typedef cl_int(CL_API_CALL *PFN_clCommandNDRangeKernelKHR)(
cl_command_buffer_khr /*command_buffer*/,
cl_command_queue /*command_queue*/,
const cl_command_properties_khr * /*properties*/, cl_kernel /*kernel*/,
cl_uint /*work_dim*/, const size_t * /*global_work_offset*/,
const size_t * /*global_work_size*/, const size_t * /*local_work_size*/,
cl_uint /*num_sync_points_in_wait_list*/,
const cl_sync_point_khr * /*sync_point_wait_list*/,
cl_sync_point_khr * /*sync_point*/,
cl_mutable_command_khr * /*mutable_handle*/);
#else
typedef cl_int(CL_API_CALL *PFN_clCommandNDRangeKernelKHR)(
cl_command_buffer_khr /*command_buffer*/,
cl_command_queue /*command_queue*/,
const cl_ndrange_kernel_command_properties_khr * /*properties*/,
cl_kernel /*kernel*/, cl_uint /*work_dim*/,
const size_t * /*global_work_offset*/, const size_t * /*global_work_size*/,
const size_t * /*local_work_size*/,
cl_uint /*num_sync_points_in_wait_list*/,
const cl_sync_point_khr * /*sync_point_wait_list*/,
cl_sync_point_khr * /*sync_point*/,
cl_mutable_command_khr * /*mutable_handle*/);
#endif
typedef cl_int(CL_API_CALL *PFN_clGetCommandBufferInfoKHR)(
cl_command_buffer_khr /*command_buffer*/,
cl_command_buffer_info_khr /*param_name*/, size_t /*param_value_size*/,
void * /*param_value*/, size_t * /*param_value_size_ret*/);
// cl_arm_import_memory extension
typedef cl_mem(CL_API_CALL *PFN_clImportMemoryARM)(
cl_context /*context*/, cl_mem_flags /*flags*/,
const cl_import_properties_arm * /*properties*/, void * /*memory*/,
size_t /*size*/, cl_int * /*errcode_ret*/);
// cl_khr_semaphore extension
typedef cl_semaphore_khr(CL_API_CALL *PFN_clCreateSemaphoreWithPropertiesKHR)(
cl_context /*context*/, const cl_semaphore_properties_khr * /*sema_props*/,
cl_int * /*errcode_ret*/);
typedef cl_int(CL_API_CALL *PFN_clEnqueueWaitSemaphoresKHR)(
cl_command_queue /*command_queue*/, cl_uint /*num_sema_objects*/,
const cl_semaphore_khr * /*sema_objects*/,
const cl_semaphore_payload_khr * /*sema_payload_list*/,
cl_uint /*num_events_in_wait_list*/, const cl_event * /*event_wait_list*/,
cl_event * /*event*/);
typedef cl_int(CL_API_CALL *PFN_clEnqueueSignalSemaphoresKHR)(
cl_command_queue /*command_queue*/, cl_uint /*num_sema_objects*/,
const cl_semaphore_khr * /*sema_objects*/,
const cl_semaphore_payload_khr * /*sema_payload_list*/,
cl_uint /*num_events_in_wait_list*/, const cl_event * /*event_wait_list*/,
cl_event * /*event*/);
extern PFN_clGetPlatformIDs clGetPlatformIDs;
extern PFN_clGetPlatformInfo clGetPlatformInfo;
extern PFN_clGetDeviceIDs clGetDeviceIDs;
extern PFN_clGetDeviceInfo clGetDeviceInfo;
extern PFN_clCreateSubDevices clCreateSubDevices;
extern PFN_clRetainDevice clRetainDevice;
extern PFN_clReleaseDevice clReleaseDevice;
extern PFN_clCreateContext clCreateContext;
extern PFN_clCreateContextFromType clCreateContextFromType;
extern PFN_clRetainContext clRetainContext;
extern PFN_clReleaseContext clReleaseContext;
extern PFN_clGetContextInfo clGetContextInfo;
extern PFN_clCreateCommandQueueWithProperties
clCreateCommandQueueWithProperties;
extern PFN_clRetainCommandQueue clRetainCommandQueue;
extern PFN_clReleaseCommandQueue clReleaseCommandQueue;
extern PFN_clGetCommandQueueInfo clGetCommandQueueInfo;
extern PFN_clCreateBuffer clCreateBuffer;
extern PFN_clCreateSubBuffer clCreateSubBuffer;
extern PFN_clCreateImage clCreateImage;
extern PFN_clCreatePipe clCreatePipe;
extern PFN_clRetainMemObject clRetainMemObject;
extern PFN_clReleaseMemObject clReleaseMemObject;
extern PFN_clGetSupportedImageFormats clGetSupportedImageFormats;
extern PFN_clGetMemObjectInfo clGetMemObjectInfo;
extern PFN_clGetImageInfo clGetImageInfo;
extern PFN_clGetPipeInfo clGetPipeInfo;
extern PFN_clSetMemObjectDestructorCallback clSetMemObjectDestructorCallback;
extern PFN_clSVMAlloc clSVMAlloc;
extern PFN_clSVMFree clSVMFree;
extern PFN_clCreateSamplerWithProperties clCreateSamplerWithProperties;
extern PFN_clRetainSampler clRetainSampler;
extern PFN_clReleaseSampler clReleaseSampler;
extern PFN_clGetSamplerInfo clGetSamplerInfo;
extern PFN_clCreateProgramWithSource clCreateProgramWithSource;
extern PFN_clCreateProgramWithBinary clCreateProgramWithBinary;
extern PFN_clCreateProgramWithBuiltInKernels clCreateProgramWithBuiltInKernels;
extern PFN_clRetainProgram clRetainProgram;
extern PFN_clReleaseProgram clReleaseProgram;
extern PFN_clBuildProgram clBuildProgram;
extern PFN_clCompileProgram clCompileProgram;
extern PFN_clLinkProgram clLinkProgram;
extern PFN_clUnloadPlatformCompiler clUnloadPlatformCompiler;
extern PFN_clGetProgramInfo clGetProgramInfo;
extern PFN_clGetProgramBuildInfo clGetProgramBuildInfo;
extern PFN_clCreateKernel clCreateKernel;
extern PFN_clCreateKernelsInProgram clCreateKernelsInProgram;
extern PFN_clRetainKernel clRetainKernel;
extern PFN_clReleaseKernel clReleaseKernel;
extern PFN_clSetKernelArg clSetKernelArg;
extern PFN_clSetKernelArgSVMPointer clSetKernelArgSVMPointer;
extern PFN_clSetKernelExecInfo clSetKernelExecInfo;
extern PFN_clGetKernelInfo clGetKernelInfo;
extern PFN_clGetKernelArgInfo clGetKernelArgInfo;
extern PFN_clGetKernelWorkGroupInfo clGetKernelWorkGroupInfo;
extern PFN_clWaitForEvents clWaitForEvents;
extern PFN_clGetEventInfo clGetEventInfo;
extern PFN_clCreateUserEvent clCreateUserEvent;
extern PFN_clRetainEvent clRetainEvent;
extern PFN_clReleaseEvent clReleaseEvent;
extern PFN_clSetUserEventStatus clSetUserEventStatus;
extern PFN_clSetEventCallback clSetEventCallback;
extern PFN_clGetEventProfilingInfo clGetEventProfilingInfo;
extern PFN_clFlush clFlush;
extern PFN_clFinish clFinish;
extern PFN_clEnqueueReadBuffer clEnqueueReadBuffer;
extern PFN_clEnqueueReadBufferRect clEnqueueReadBufferRect;
extern PFN_clEnqueueWriteBuffer clEnqueueWriteBuffer;
extern PFN_clEnqueueWriteBufferRect clEnqueueWriteBufferRect;
extern PFN_clEnqueueFillBuffer clEnqueueFillBuffer;
extern PFN_clEnqueueCopyBuffer clEnqueueCopyBuffer;
extern PFN_clEnqueueCopyBufferRect clEnqueueCopyBufferRect;
extern PFN_clEnqueueReadImage clEnqueueReadImage;
extern PFN_clEnqueueWriteImage clEnqueueWriteImage;
extern PFN_clEnqueueFillImage clEnqueueFillImage;
extern PFN_clEnqueueCopyImage clEnqueueCopyImage;
extern PFN_clEnqueueCopyImageToBuffer clEnqueueCopyImageToBuffer;
extern PFN_clEnqueueCopyBufferToImage clEnqueueCopyBufferToImage;
extern PFN_clEnqueueMapBuffer clEnqueueMapBuffer;
extern PFN_clEnqueueMapImage clEnqueueMapImage;
extern PFN_clEnqueueUnmapMemObject clEnqueueUnmapMemObject;
extern PFN_clEnqueueMigrateMemObjects clEnqueueMigrateMemObjects;
extern PFN_clEnqueueNDRangeKernel clEnqueueNDRangeKernel;
extern PFN_clEnqueueNativeKernel clEnqueueNativeKernel;
extern PFN_clEnqueueMarkerWithWaitList clEnqueueMarkerWithWaitList;
extern PFN_clEnqueueBarrierWithWaitList clEnqueueBarrierWithWaitList;
extern PFN_clEnqueueSVMFree clEnqueueSVMFree;
extern PFN_clEnqueueSVMMemcpy clEnqueueSVMMemcpy;
extern PFN_clEnqueueSVMMemFill clEnqueueSVMMemFill;
extern PFN_clEnqueueSVMMap clEnqueueSVMMap;
extern PFN_clEnqueueSVMUnmap clEnqueueSVMUnmap;
extern PFN_clGetExtensionFunctionAddressForPlatform
clGetExtensionFunctionAddressForPlatform;
extern PFN_clCreateImage2D clCreateImage2D;
extern PFN_clCreateImage3D clCreateImage3D;
extern PFN_clEnqueueMarker clEnqueueMarker;
extern PFN_clEnqueueWaitForEvents clEnqueueWaitForEvents;
extern PFN_clEnqueueBarrier clEnqueueBarrier;
extern PFN_clUnloadCompiler clUnloadCompiler;
extern PFN_clGetExtensionFunctionAddress clGetExtensionFunctionAddress;
extern PFN_clCreateCommandQueue clCreateCommandQueue;
extern PFN_clCreateSampler clCreateSampler;
extern PFN_clEnqueueTask clEnqueueTask;
#ifndef CL_DELEGATE_NO_GL
// OpenGL sharing
extern PFN_clCreateFromGLBuffer clCreateFromGLBuffer;
extern PFN_clCreateFromGLTexture clCreateFromGLTexture;
extern PFN_clEnqueueAcquireGLObjects clEnqueueAcquireGLObjects;
extern PFN_clEnqueueReleaseGLObjects clEnqueueReleaseGLObjects;
// cl_khr_egl_event extension
extern PFN_clCreateEventFromEGLSyncKHR clCreateEventFromEGLSyncKHR;
// EGL sharing
extern PFN_clCreateFromEGLImageKHR clCreateFromEGLImageKHR;
extern PFN_clEnqueueAcquireEGLObjectsKHR clEnqueueAcquireEGLObjectsKHR;
extern PFN_clEnqueueReleaseEGLObjectsKHR clEnqueueReleaseEGLObjectsKHR;
#endif
// cl_khr_command_buffer extension
extern PFN_clCreateCommandBufferKHR clCreateCommandBufferKHR;
extern PFN_clRetainCommandBufferKHR clRetainCommandBufferKHR;
extern PFN_clReleaseCommandBufferKHR clReleaseCommandBufferKHR;
extern PFN_clFinalizeCommandBufferKHR clFinalizeCommandBufferKHR;
extern PFN_clEnqueueCommandBufferKHR clEnqueueCommandBufferKHR;
extern PFN_clCommandNDRangeKernelKHR clCommandNDRangeKernelKHR;
extern PFN_clGetCommandBufferInfoKHR clGetCommandBufferInfoKHR;
// cl_arm_import_memory extension
extern PFN_clImportMemoryARM clImportMemoryARM;
// cl_khr_semaphore extension
extern PFN_clCreateSemaphoreWithPropertiesKHR
clCreateSemaphoreWithPropertiesKHR;
extern PFN_clEnqueueWaitSemaphoresKHR clEnqueueWaitSemaphoresKHR;
extern PFN_clEnqueueSignalSemaphoresKHR clEnqueueSignalSemaphoresKHR;
// For convenient image creation
// It uses clCreateImage if it available (clCreateImage available since cl 1.2)
// otherwise it will use legacy clCreateImage2D
cl_mem CreateImage2DLegacy(cl_context context, cl_mem_flags flags,
const cl_image_format *image_format,
const cl_image_desc *image_desc, void *host_ptr,
cl_int *errcode_ret);
// It uses clCreateImage if it available (clCreateImage available since cl 1.2)
// otherwise it will use legacy clCreateImage3D
cl_mem CreateImage3DLegacy(cl_context context, cl_mem_flags flags,
const cl_image_format *image_format,
const cl_image_desc *image_desc, void *host_ptr,
cl_int *errcode_ret);
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_OPENCL_WRAPPER_H_
@@ -0,0 +1,204 @@
/* 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/program_cache.h"
#include <cstdint>
#include <string>
#include <utility>
#include "flatbuffers/flatbuffers.h" // from @flatbuffers
#include "tensorflow/lite/delegates/gpu/cl/cl_program.h"
#include "tensorflow/lite/delegates/gpu/cl/compiled_program_cache_generated.h"
#include "tensorflow/lite/delegates/gpu/cl/util.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include <farmhash.h>
namespace tflite {
namespace gpu {
namespace cl {
namespace {
// Farmhash Fingerprint
inline uint64_t CombineFingerprints(uint64_t l, uint64_t h) {
// Murmur-inspired hashing.
const uint64_t kMul = 0x9ddfea08eb382d69ULL;
uint64_t a = (l ^ h) * kMul;
a ^= (a >> 47);
uint64_t b = (h ^ a) * kMul;
b ^= (b >> 44);
b *= kMul;
b ^= (b >> 41);
b *= kMul;
return b;
}
uint64_t GetProgramFingerprint(const std::string& code,
const std::string& compiler_options) {
const uint64_t code_fingerprint =
::util::Fingerprint64(code.c_str(), code.size());
const uint64_t options_fingerprint = ::util::Fingerprint64(
compiler_options.c_str(), compiler_options.size());
return CombineFingerprints(code_fingerprint, options_fingerprint);
}
std::string GetDriverVersion(const CLDevice& device) {
return device.GetPlatformVersion() + "_jet_version_0";
}
} // namespace
ProgramCache::ProgramDescriptor::ProgramDescriptor(
const std::string& code, const std::string& compiler_options)
: fingerprint(GetProgramFingerprint(code, compiler_options)) {}
ProgramCache::ProgramDescriptor::ProgramDescriptor(uint64_t fingerprints)
: fingerprint(fingerprints) {}
ProgramCache::ProgramCache(ProgramCache&& program_cache)
: programs_(std::move(program_cache.programs_)) {}
ProgramCache& ProgramCache::operator=(ProgramCache&& program_cache) {
if (this != &program_cache) {
programs_ = std::move(program_cache.programs_);
}
return *this;
}
absl::Status ProgramCache::GetOrCreateCLKernel(
const std::string& code, const std::string& function_name,
const std::vector<CompilerOptions>& compiler_options,
const CLContext& context, const CLDevice& device, CLKernel* result,
uint64_t* kernel_fingerprint) {
const std::string options =
CompilerOptionsToString(device.GetInfo(), compiler_options);
ProgramDescriptor desc(code, options);
if (kernel_fingerprint) {
*kernel_fingerprint = desc.fingerprint;
}
auto it = programs_.find(desc);
if (it != programs_.end()) {
return result->CreateFromProgram(it->second, function_name);
}
CLProgram program;
RETURN_IF_ERROR(CreateCLProgram(code, options, context, device, &program));
RETURN_IF_ERROR(result->CreateFromProgram(program, function_name));
programs_.insert(std::make_pair(std::move(desc), std::move(program)));
return absl::OkStatus();
}
absl::Status ProgramCache::GetOrCreateCLKernel(const std::string& code,
const std::string& function_name,
const CLContext& context,
const CLDevice& device,
CLKernel* result,
uint64_t* kernel_fingerprint) {
return GetOrCreateCLKernel(code, function_name, {}, context, device, result,
kernel_fingerprint);
}
absl::Status ProgramCache::GetKernel(uint64_t fingerprint,
const std::string& function_name,
CLKernel* result) const {
ProgramDescriptor desc(fingerprint);
auto it = programs_.find(desc);
if (it == programs_.end()) {
return absl::NotFoundError("No program with this fingerprint.");
}
return result->CreateFromProgram(it->second, function_name);
}
absl::Status ProgramCache::AddProgramBinary(const CLContext& context,
const CLDevice& device,
uint64_t fingerprint,
absl::Span<const uint8_t> binary) {
ProgramDescriptor desc(fingerprint);
auto it = programs_.find(desc);
if (it == programs_.end()) {
CLProgram program;
RETURN_IF_ERROR(
CreateCLProgramFromBinary(context, device, binary, &program));
programs_.insert(std::make_pair(std::move(desc), std::move(program)));
}
return absl::OkStatus();
}
absl::Status ProgramCache::GetProgramBinary(
uint64_t fingerprint, std::vector<uint8_t>* program_binary) const {
ProgramDescriptor desc(fingerprint);
auto it = programs_.find(desc);
if (it == programs_.end()) {
return absl::NotFoundError("No program with this fingerprint.");
}
return it->second.GetBinary(program_binary);
}
absl::Status ProgramCache::AddSerializedCache(
const CLContext& context, const CLDevice& device,
absl::Span<const uint8_t> serialized_cache) {
flatbuffers::Verifier verifier(serialized_cache.data(),
serialized_cache.size());
if (!data::VerifyCompiledCacheBuffer(verifier)) {
return absl::InvalidArgumentError("Serialized model is corrupted.");
}
auto model = data::GetCompiledCache(serialized_cache.data());
std::string platform_version(model->driver_version()->c_str(),
model->driver_version()->size());
if (GetDriverVersion(device) != platform_version) {
return absl::InvalidArgumentError(
"OpenCL driver changed, cache invalid, should be regenerated");
}
for (auto serialized_program : *model->programs()) {
auto binary_span = absl::MakeSpan(serialized_program->binary()->data(),
serialized_program->binary()->size());
RETURN_IF_ERROR(AddProgramBinary(
context, device, serialized_program->fingerprint(), binary_span));
}
return absl::OkStatus();
}
absl::Status ProgramCache::GetSerializedCache(
const CLDevice& device, std::vector<uint8_t>* serialized_cache) const {
::flatbuffers::FlatBufferBuilder builder;
std::vector<flatbuffers::Offset<data::Program>> serialized_programs;
for (auto& program : programs_) {
std::vector<uint8_t> binary;
RETURN_IF_ERROR(program.second.GetBinary(&binary));
auto binary_offset = builder.CreateVector(binary);
data::ProgramBuilder program_builder(builder);
program_builder.add_fingerprint(program.first.fingerprint);
program_builder.add_binary(binary_offset);
serialized_programs.push_back(program_builder.Finish());
}
auto driver_version = builder.CreateString(GetDriverVersion(device));
auto programs_s = builder.CreateVector(serialized_programs);
data::CompiledCacheBuilder cache_builder(builder);
cache_builder.add_driver_version(driver_version);
cache_builder.add_programs(programs_s);
data::FinishCompiledCacheBuffer(builder, cache_builder.Finish());
size_t next_element = serialized_cache->size();
serialized_cache->resize(serialized_cache->size() + builder.GetSize());
std::memcpy(&(*serialized_cache)[next_element], builder.GetBufferPointer(),
builder.GetSize());
return absl::OkStatus();
}
} // namespace cl
} // namespace gpu
} // namespace tflite
@@ -0,0 +1,101 @@
/* 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_PROGRAM_CACHE_H_
#define TENSORFLOW_LITE_DELEGATES_GPU_CL_PROGRAM_CACHE_H_
#include <cstdint>
#include <string>
#include <vector>
#include "absl/container/flat_hash_map.h"
#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/cl_kernel.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_program.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
namespace tflite {
namespace gpu {
namespace cl {
class ProgramCache {
public:
ProgramCache() = default;
ProgramCache(ProgramCache&& program_cache);
ProgramCache& operator=(ProgramCache&& program_cache);
ProgramCache(const ProgramCache&) = delete;
ProgramCache& operator=(const ProgramCache&) = delete;
absl::Status GetOrCreateCLKernel(
const std::string& code, const std::string& function_name,
const std::vector<CompilerOptions>& compiler_options,
const CLContext& context, const CLDevice& device, CLKernel* result,
uint64_t* kernel_fingerprint = nullptr);
absl::Status GetOrCreateCLKernel(const std::string& code,
const std::string& function_name,
const CLContext& context,
const CLDevice& device, CLKernel* result,
uint64_t* kernel_fingerprint = nullptr);
absl::Status GetKernel(uint64_t fingerprint, const std::string& function_name,
CLKernel* result) const;
absl::Status AddProgramBinary(const CLContext& context,
const CLDevice& device, uint64_t fingerprint,
absl::Span<const uint8_t> binary);
absl::Status GetProgramBinary(uint64_t fingerprint,
std::vector<uint8_t>* program_binary) const;
absl::Status AddSerializedCache(const CLContext& context,
const CLDevice& device,
absl::Span<const uint8_t> serialized_cache);
absl::Status GetSerializedCache(const CLDevice& device,
std::vector<uint8_t>* serialized_cache) const;
private:
struct ProgramDescriptor {
ProgramDescriptor() = default;
ProgramDescriptor(const std::string& code,
const std::string& compiler_options);
explicit ProgramDescriptor(uint64_t fingerprint);
uint64_t fingerprint;
};
struct ProgramDescriptorHasher {
std::size_t operator()(const ProgramDescriptor& k) const {
return std::hash<uint64_t>()(k.fingerprint);
}
};
struct ProgramDescriptorEqual {
bool operator()(const ProgramDescriptor& a,
const ProgramDescriptor& b) const {
return a.fingerprint == b.fingerprint;
}
};
absl::flat_hash_map<ProgramDescriptor, CLProgram, ProgramDescriptorHasher,
ProgramDescriptorEqual>
programs_;
};
} // namespace cl
} // namespace gpu
} // namespace tflite
#endif // TENSORFLOW_LITE_DELEGATES_GPU_CL_PROGRAM_CACHE_H_

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