/* * Copyright (c) 2018-2020 ARM Limited. * * SPDX-License-Identifier: MIT * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "arm_compute/graph/backends/GLES/GCFunctionFactory.h" #include "arm_compute/core/utils/misc/Cast.h" #include "arm_compute/graph/Graph.h" #include "arm_compute/graph/GraphContext.h" #include "arm_compute/graph/backends/FunctionHelpers.h" #include "arm_compute/runtime/GLES_COMPUTE/GCFunctions.h" using namespace arm_compute::utils::cast; namespace arm_compute { namespace graph { namespace backends { /** Target specific information structure used to pass information to the layer templates */ struct GCTargetInfo { using TensorType = arm_compute::IGCTensor; static Target TargetType; }; Target GCTargetInfo::TargetType = Target::GC; /** Collection of GC convolution functions */ struct GCConvolutionLayerFunctions { using GenericConvolutionLayer = GCConvolutionLayer; using GEMMConvolutionLayer = GCConvolutionLayer; using DirectConvolutionLayer = GCDirectConvolutionLayer; }; /** Collection of GC depthwise convolution functions */ struct GCDepthwiseConvolutionLayerFunctions { using DepthwiseConvolutionLayer3x3 = GCDepthwiseConvolutionLayer3x3; }; /** Collection of GC element-wise functions */ struct GCEltwiseFunctions { using Addition = GCArithmeticAddition; using Multiplication = GCPixelWiseMultiplication; }; namespace detail { template <> std::unique_ptr create_convolution_layer(ConvolutionLayerNode &node, GraphContext &ctx) { validate_node(node, 3 /* expected inputs */, 1 /* expected outputs */); // Extract IO and info GCTargetInfo::TensorType *input = get_backing_tensor(node.input(0)); GCTargetInfo::TensorType *weights = get_backing_tensor(node.input(1)); GCTargetInfo::TensorType *biases = get_backing_tensor(node.input(2)); GCTargetInfo::TensorType *output = get_backing_tensor(node.output(0)); if(is_data_type_quantized_asymmetric(input->info()->data_type())) { biases->info()->set_data_type(DataType::S32); } const PadStrideInfo conv_info = node.convolution_info(); const ConvolutionMethod conv_algorithm = node.convolution_method(); const ActivationLayerInfo fused_act = node.fused_activation(); // Create and configure function (we assume that functions have been validated before creation) std::shared_ptr mm = get_memory_manager(ctx, GCTargetInfo::TargetType); std::unique_ptr func; std::string func_name; if(conv_algorithm == ConvolutionMethod::Direct) { std::tie(func, func_name) = create_named_function( std::string("DirectConvolutionLayer"), input, weights, biases, output, conv_info, fused_act); } else { std::tie(func, func_name) = create_named_memory_managed_function( std::string("ConvolutionLayer"), mm, input, weights, biases, output, conv_info, WeightsInfo(), Size2D(1U, 1U), fused_act); } // Log info ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated " << node.name() << " Type: " << func_name << " Data Type: " << input->info()->data_type() << " Input QuantInfo: " << input->info()->quantization_info() << " Weights QuantInfo: " << weights->info()->quantization_info() << " Input shape: " << input->info()->tensor_shape() << " Weights shape: " << weights->info()->tensor_shape() << " Output shape: " << output->info()->tensor_shape() << (fused_act.enabled() ? " " + to_string(fused_act.activation()) : "") << std::endl); return func; } template <> std::unique_ptr create_depthwise_convolution_layer(DepthwiseConvolutionLayerNode &node) { validate_node(node, 3 /* expected inputs */, 1 /* expected outputs */); // Extract IO and info GCTargetInfo::TensorType *input = get_backing_tensor(node.input(0)); GCTargetInfo::TensorType *weights = get_backing_tensor(node.input(1)); GCTargetInfo::TensorType *biases = get_backing_tensor(node.input(2)); GCTargetInfo::TensorType *output = get_backing_tensor(node.output(0)); if(is_data_type_quantized_asymmetric(input->info()->data_type())) { biases->info()->set_data_type(DataType::S32); } const PadStrideInfo conv_info = node.convolution_info(); const DepthwiseConvolutionMethod dwc_algorithm = node.depthwise_convolution_method(); const ActivationLayerInfo fused_act = node.fused_activation(); const int depth_multiplier = node.depth_multiplier(); // Create and configure function (we assume that functions have been validated before creation) std::unique_ptr func; std::string func_name; if(dwc_algorithm == DepthwiseConvolutionMethod::Optimized3x3) { std::tie(func, func_name) = create_named_function( std::string("DepthwiseConvolutionLayer3x3"), input, weights, biases, output, conv_info, depth_multiplier, fused_act); } else { ARM_COMPUTE_ERROR("Generic DepthwiseConvolutionLayer is not supported in GLES backend"); } // Log info ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated " << node.name() << " Type: " << func_name << " Target " << GCTargetInfo::TargetType << " Data Type: " << input->info()->data_type() << " Input QuantInfo: " << input->info()->quantization_info() << " Weights QuantInfo: " << weights->info()->quantization_info() << " Input shape: " << input->info()->tensor_shape() << " Weights shape: " << weights->info()->tensor_shape() << " Output shape: " << output->info()->tensor_shape() << " Depth multiplier: " << depth_multiplier << (fused_act.enabled() ? " " + to_string(fused_act.activation()) : "") << std::endl); return func; } template <> std::unique_ptr create_eltwise_layer(EltwiseLayerNode &node) { ARM_COMPUTE_LOG_GRAPH_VERBOSE( "Creating GC EltwiseLayer node with ID : " << node.id() << " and Name: " << node.name() << std::endl); ARM_COMPUTE_ERROR_ON(node.num_inputs() != 2); ARM_COMPUTE_ERROR_ON(node.num_outputs() != 1); // Extract IO and info GCTargetInfo::TensorType *input1 = get_backing_tensor(node.input(0)); GCTargetInfo::TensorType *input2 = get_backing_tensor(node.input(1)); GCTargetInfo::TensorType *output = get_backing_tensor(node.output(0)); const EltwiseOperation eltwise_op = node.eltwise_operation(); const ConvertPolicy convert_policy = node.convert_policy(); ARM_COMPUTE_ERROR_ON(input1 == nullptr); ARM_COMPUTE_ERROR_ON(input2 == nullptr); ARM_COMPUTE_ERROR_ON(output == nullptr); std::unique_ptr func = nullptr; std::string func_name; if(eltwise_op == EltwiseOperation::Add) { std::tie(func, func_name) = create_named_function( std::string("GCArithmeticAddition"), input1, input2, output, convert_policy); } else if(eltwise_op == EltwiseOperation::Sub) { ARM_COMPUTE_ERROR("Arithmetic subtraction is not supported in GLES backend"); } else if(eltwise_op == EltwiseOperation::Mul) { std::tie(func, func_name) = create_named_function( std::string("PixelWiseMultiplication"), input1, input2, output, 1.f); } else { ARM_COMPUTE_ERROR("Unsupported element-wise operation!"); } // Log info ARM_COMPUTE_LOG_GRAPH_INFO("Instantiated " << node.name() << " Type: " << node.type() << " Target: " << GCTargetInfo::TargetType << " Operation: " << func_name << " Data Type: " << input1->info()->data_type() << " Shape: " << input1->info()->tensor_shape() << std::endl); return func; } } //namespace detail std::unique_ptr GCFunctionFactory::create(INode *node, GraphContext &ctx) { if(node == nullptr) { return nullptr; } NodeType type = node->type(); switch(type) { case NodeType::ActivationLayer: return detail::create_activation_layer(*polymorphic_downcast(node)); case NodeType::BatchNormalizationLayer: return detail::create_batch_normalization_layer(*polymorphic_downcast(node)); case NodeType::ConvolutionLayer: return detail::create_convolution_layer(*polymorphic_downcast(node), ctx); case NodeType::ConcatenateLayer: return detail::create_concatenate_layer(*polymorphic_downcast(node)); case NodeType::DepthwiseConvolutionLayer: return detail::create_depthwise_convolution_layer(*polymorphic_downcast(node)); case NodeType::EltwiseLayer: return detail::create_eltwise_layer(*polymorphic_downcast(node)); case NodeType::FullyConnectedLayer: return detail::create_fully_connected_layer(*polymorphic_downcast(node), ctx); case NodeType::NormalizationLayer: return detail::create_normalization_layer(*polymorphic_downcast(node), ctx); case NodeType::NormalizePlanarYUVLayer: return detail::create_normalize_planar_yuv_layer(*polymorphic_downcast(node)); case NodeType::PoolingLayer: return detail::create_pooling_layer(*polymorphic_downcast(node)); case NodeType::PrintLayer: return detail::create_print_layer(*polymorphic_downcast(node)); case NodeType::ResizeLayer: return detail::create_resize_layer(*polymorphic_downcast(node)); case NodeType::SoftmaxLayer: return detail::create_softmax_layer(*polymorphic_downcast(node), ctx); default: return nullptr; } } } // namespace backends } // namespace graph } // namespace arm_compute