/* * Instant compiling a one liner OpenCL filter * This file is part of ufo-serge filter set. * Copyright (C) 2016 Serge Cohen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Serge Cohen */ #include "config.h" // The following define seems necessary on Linux to get stdio.h declare asprintf family of functions. // Should be done early enough to be sure that stdio.h is not already included withOUT _GNU_SOURCE on. #define _GNU_SOURCE #include // for asprintf at least. #ifdef __APPLE__ #include #else #include #endif #include "ufo-ocl-1liner-task.h" /* #define IN_LOG printf("=> %s : %s.%d\n", __func__, __FILE__, __LINE__); #define OUT_LOG printf("OUT %s : %s.%d\n", __func__, __FILE__, __LINE__); */ #define IN_LOG #define OUT_LOG struct _UfoOCL1LinerTaskPrivate { gchar * one_line; cl_kernel kernel; guint num_inputs; gboolean be_quiet; }; static void ufo_task_interface_init (UfoTaskIface *iface); G_DEFINE_TYPE_WITH_CODE (UfoOCL1LinerTask, ufo_ocl_1liner_task, UFO_TYPE_TASK_NODE, G_IMPLEMENT_INTERFACE (UFO_TYPE_TASK, ufo_task_interface_init)) #define UFO_OCL_1LINER_TASK_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), UFO_TYPE_OCL_1LINER_TASK, UfoOCL1LinerTaskPrivate)) enum { PROP_0, PROP_ONE_LINE, PROP_NUM_INPUTS, PROP_QUIET, N_PROPERTIES }; static GParamSpec *properties[N_PROPERTIES] = { NULL, }; UfoNode * ufo_ocl_1liner_task_new (void) { return UFO_NODE (g_object_new (UFO_TYPE_OCL_1LINER_TASK, NULL)); } static void ufo_ocl_1liner_task_setup (UfoTask *task, UfoResources *resources, GError **error) { IN_LOG UfoOCL1LinerTaskPrivate *priv; priv = UFO_OCL_1LINER_TASK_GET_PRIVATE (task); /* Preparing the kernel source to be compiled */ gchar * kernel_skel = NULL; char * kernel_src = NULL; const gchar * skel_filename = "ocl-1liner-skel.cl"; kernel_skel = ufo_resources_get_kernel_source (resources, skel_filename, error); char skel_in_macro[1024]; char skel_in[1024]; int one_in_macro_size; int one_in_size; char * skel_in_macro_next = skel_in_macro; char * skel_in_next = skel_in; for ( guint i=0; i != priv->num_inputs; ++i ) { one_in_macro_size = snprintf(skel_in_macro_next, 1024 - (skel_in_macro_next - skel_in_macro), "#define in_%d_px (in_%d[px_index])\n", i, i); one_in_size = snprintf(skel_in_next, 1024 - (skel_in_next - skel_in), "__global float *in_%d,\n", i); if ( (0 >= one_in_macro_size) || (0 >= one_in_size) ) goto exit; skel_in_macro_next += one_in_macro_size; skel_in_next += one_in_size; } asprintf(&kernel_src, kernel_skel, skel_in_macro, skel_in, priv->one_line); if ( ! priv->be_quiet ) { /* Done with the preparation of the OpenCL sources. */ fprintf(stdout, "Current version of the one-liner OpenCL source code :\n%s\n", kernel_src); } /* Copiling the kernel now : */ priv->kernel = ufo_resources_get_kernel_from_source (resources, kernel_src, "ocl_1liner", NULL, error); /* Done compiling sources into a kernel, if existing retain it */ if (priv->kernel != NULL) UFO_RESOURCES_CHECK_AND_SET (clRetainKernel (priv->kernel), error); exit: /* Releasing resources no longer used */ g_free (kernel_skel); free (kernel_src); OUT_LOG } static void ufo_ocl_1liner_task_get_requisition (UfoTask *task, UfoBuffer **inputs, UfoRequisition *requisition, GError **error) { // In the current version of the kernel all the inputs are supposed to have the same dimensions // Or more precisely the output has the same dimension as first input (indexed 0) and one work-item // is run for each pixel of input 0 (hence for each pixel of the output). ufo_buffer_get_requisition (inputs[0], requisition); } static guint ufo_ocl_1liner_task_get_num_inputs (UfoTask *task) { IN_LOG UfoOCL1LinerTaskPrivate *priv; priv = UFO_OCL_1LINER_TASK_GET_PRIVATE (task); OUT_LOG return priv->num_inputs; } static guint ufo_ocl_1liner_task_get_num_dimensions (UfoTask *task, guint input) { return 2; } static UfoTaskMode ufo_ocl_1liner_task_get_mode (UfoTask *task) { // Running as a single image stream processing and using OpenCL for GPU computation return UFO_TASK_MODE_PROCESSOR | UFO_TASK_MODE_GPU; } static gboolean ufo_ocl_1liner_task_process (UfoTask *task, UfoBuffer **inputs, UfoBuffer *output, UfoRequisition *requisition) { IN_LOG UfoOCL1LinerTaskPrivate *priv; priv = UFO_OCL_1LINER_TASK_GET_PRIVATE (task); UfoGpuNode *node; UfoProfiler *profiler; cl_command_queue cmd_queue; cl_mem in_mem; cl_mem out_mem; node = UFO_GPU_NODE (ufo_task_node_get_proc_node (UFO_TASK_NODE (task))); cmd_queue = ufo_gpu_node_get_cmd_queue (node); profiler = ufo_task_node_get_profiler (UFO_TASK_NODE (task)); // Taking care of setting the inputs : for ( guint i=0; i != priv->num_inputs; ++i ) { in_mem = ufo_buffer_get_device_array (inputs[i], cmd_queue); UFO_RESOURCES_CHECK_CLERR (clSetKernelArg (priv->kernel, i, sizeof (cl_mem), &in_mem)); } // Taking care of setting the output : out_mem = ufo_buffer_get_device_array (output, cmd_queue); UFO_RESOURCES_CHECK_CLERR (clSetKernelArg (priv->kernel, priv->num_inputs, sizeof (cl_mem), &out_mem)); // Finally launching the kernel : ufo_profiler_call (profiler, cmd_queue, priv->kernel, 2, requisition->dims, NULL); // Done, returning (TRUE): OUT_LOG return TRUE; } static void ufo_ocl_1liner_task_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IN_LOG UfoOCL1LinerTaskPrivate *priv = UFO_OCL_1LINER_TASK_GET_PRIVATE (object); switch (property_id) { case PROP_ONE_LINE: g_free (priv->one_line); priv->one_line = g_value_dup_string (value); break; case PROP_NUM_INPUTS: priv->num_inputs = g_value_get_uint (value); break; case PROP_QUIET: priv->be_quiet = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } OUT_LOG } static void ufo_ocl_1liner_task_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IN_LOG UfoOCL1LinerTaskPrivate *priv = UFO_OCL_1LINER_TASK_GET_PRIVATE (object); switch (property_id) { case PROP_ONE_LINE: g_value_set_string (value, priv->one_line); break; case PROP_NUM_INPUTS: g_value_set_uint (value, priv->num_inputs); break; case PROP_QUIET: g_value_set_boolean (value, priv->be_quiet); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } OUT_LOG } static void ufo_ocl_1liner_task_finalize (GObject *object) { IN_LOG UfoOCL1LinerTaskPrivate *priv = UFO_OCL_1LINER_TASK_GET_PRIVATE (object); g_free (priv->one_line); if ( priv->kernel ) UFO_RESOURCES_CHECK_CLERR(clReleaseKernel(priv->kernel)); G_OBJECT_CLASS (ufo_ocl_1liner_task_parent_class)->finalize (object); OUT_LOG } static void ufo_task_interface_init (UfoTaskIface *iface) { IN_LOG iface->setup = ufo_ocl_1liner_task_setup; iface->get_num_inputs = ufo_ocl_1liner_task_get_num_inputs; iface->get_num_dimensions = ufo_ocl_1liner_task_get_num_dimensions; iface->get_mode = ufo_ocl_1liner_task_get_mode; iface->get_requisition = ufo_ocl_1liner_task_get_requisition; iface->process = ufo_ocl_1liner_task_process; OUT_LOG } static void ufo_ocl_1liner_task_class_init (UfoOCL1LinerTaskClass *klass) { IN_LOG GObjectClass *oclass = G_OBJECT_CLASS (klass); oclass->set_property = ufo_ocl_1liner_task_set_property; oclass->get_property = ufo_ocl_1liner_task_get_property; oclass->finalize = ufo_ocl_1liner_task_finalize; properties[PROP_ONE_LINE] = g_param_spec_string ("one-line", "The one line C/OpenCL computation to perform", "* in0 .. inN are input array(s).\n" "* out is 1D output array.\n" "\n" "Those can be addressed using px_index which is the /current/ pixel for the workitem.\n" "\n" "One can also access a /random/ pixel value using the macro : IMG_VAL which takes three\n" "arguments : the (x, y) pixel coordinate, and the array pointer for the image.\n" "NB : this macro acn be used also for lvalue (but it is risky that a work-item\n" "modifies a pixel value in =out= that is NOT the one indexed by px_index.\n" "\n" "Examples :\n" "* using the OpenCL sqrt function :\n" "out[px_index] = sqrt(in0[px_index])" "* Using the other adressing methods and the image size to shift one pixel to the left\n", "out[px_index] = IMG_VAL((x<(sizeX-1))?x+1:(sizeX-1),y,in0)", G_PARAM_READWRITE); properties[PROP_NUM_INPUTS] = g_param_spec_uint ("num-inputs", "Number of input streams.", "Number of input streams, which will be labelled in0, in1 ... in(n-1).", 0, G_MAXUINT, 0, G_PARAM_READWRITE); properties[PROP_QUIET] = g_param_spec_boolean("quiet", "When turned to false, will display the generated kernel source to stdout", "Defaulting to 'true', minimising output", TRUE, G_PARAM_READWRITE); for (guint i = PROP_0 + 1; i < N_PROPERTIES; i++) g_object_class_install_property (oclass, i, properties[i]); g_type_class_add_private (oclass, sizeof(UfoOCL1LinerTaskPrivate)); OUT_LOG } static void ufo_ocl_1liner_task_init(UfoOCL1LinerTask *self) { IN_LOG self->priv = UFO_OCL_1LINER_TASK_GET_PRIVATE(self); self->priv->one_line = NULL; self->priv->kernel = NULL; self->priv->num_inputs=1; self->priv->be_quiet=TRUE; OUT_LOG }