Skip to content
This repository has been archived by the owner on Jul 30, 2022. It is now read-only.

Overview

Felix Krause edited this page Nov 10, 2013 · 6 revisions

Overview of the OpenCLAda binding

This page is obsolete and does not represent the current API correctly. I plan to update the documentation and load it up to http://flyx.github.io/OpenCLAda/. Meanwhile, have a look at the test code, it shows how to use the current API.

Introduction

OpenCLAda is a thick binding, that is to say, it doesn't just provide the raw C functions in Ada syntax. The major features OpenCLAda adds to the C API are:

  • Object orientation
  • A class hierarchy of the OpenCL object types
  • Automatic reference counting with Controlled types
  • Type safety (obviously, as it's Ada)
  • Getter functions for every property of an object instead of one generic function for retrieving all properties

Structure

Here's an UML diagram showing the class hierarchy and package organisation of OpenCLAda:

OpenCLAda class diagram

Features

OpenCLAda currently includes most functions of OpenCL 1.0. It does not include support for any extensions; however, I plan to implement the OpenGL interfacing extension. I currently have no plans for implementing other extensions.

Differences to the C API

Object Orientation

OpenCLAda encapsulates the OpenCL object types in an object-oriented class hierarchy (which is code-wise non-existent in the C API). The class hierarchy is depicted above.

The CL_Object class provides a "=" operator with the usual semantics. Only Platform and Device are derived directly from CL_Object because these are not user-generated run-time objects. All other objects derive from Runtime_Object. All CL_Object variables are references to OpenCL objects; that is to say, if you assign an CL_Object value from one to another variable, they both will point to the same OpenCL object.

Runtime_Object implements the Adjust and Finalize procedures from Ada.Finalization.Controlled (which is the parent of CL_Object). These procedures take care of memory management via the reference counting mechanism provided by OpenCL. While Runtime_Object provides a function Reference_Count to get the current reference count value, the user doesn't have to take care of deallocating objects he doesn't use anymore - they will be deallocated automatically as soon as the last variable referencing the object reaches its end of life.

As the user does not need to initialize a Runtime_Object at declaration, it provides an Initialized function. If this returns false, the variable does not reference an OpenCL object. Using an uninitialized Runtime_Object with the API will result in an exception.

Exceptions

OpenCL does have quite a few error codes. These are translated into exceptions in OpenCLAda. A lot of erroneous calls to the OpenCL API that result in error codes are prevented by OpenCLAda because of its higher level interface, but of course there is still a lot of stuff the user can do wrong.

Generics

Everywhere the C API uses void pointers, OpenCLAda provides generic functions or packages. Take for example this function:

extern CL_API_ENTRY cl_int CL_API_CALL
clSetKernelArg(cl_kernel    /* kernel */,
               cl_uint      /* arg_index */,
               size_t       /* arg_size */,
               const void * /* arg_value */) CL_API_SUFFIX__VERSION_1_0;

The corresponding procedure in OpenCLAda looks like this:

generic
   type Argument_Type is private;
   Argument_Index : UInt;
procedure Set_Kernel_Argument (Target : Kernel; Value : Argument_Type);

Getter Functions

The C API provides one property getter function for every OpenCL object, like this:

extern CL_API_ENTRY cl_int CL_API_CALL 
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;

OpenCLAda instead provides one getter function per property, eg:

function Profile    (Source : Platform) return String;
function Version    (Source : Platform) return String;

Enumerations and Bit Vectors

OpenCL does provide quite a lot of constants. A good part of them are used for the generic property getter functions, so they are hidden in the OpenCLAda backend in favor of specific property getters. The other C constants fall in two categories:

  • Constants used as enumeration values. Those are translated into enumeration types in OpenCLAda.
  • Constants used for bit vectors. Those are translated into record types with Boolean fields representing the corresponding bit vector.

A special case is the cl_memory_flags bit vector. It has the following structure:

type Bits58 is mod 2 ** 58;
type Memory_Flags is
   record
      Read_Write     : Boolean := False;
      Write_Only     : Boolean := False;
      Read_Only      : Boolean := False;
      Use_Host_Ptr   : Boolean := False;
      Alloc_Host_Ptr : Boolean := False;
      Copy_Host_Ptr  : Boolean := False;
      Reserved       : Bits58  := 0;
   end record;

Because quite some of these flags are mutually exclusive, the record type is not visible for the user of OpenCLAda. Instead, a type Access_Kind together with additional Boolean function parameters is used where needed:

type Access_Kind is (Read_Only, Write_Only, Read_Write);
[...]
function Create_Buffer (Context         : Contexts.Context;
                        Mode            : Access_Kind;
                        Size            : CL.Size;
                        Use_Host_Memory : Boolean := False) return Buffer;

Vector Types

OpenCLAda provides that standard vector types of OpenCL; named Typen, where Type is any of {Char, Short, Int, Long, UChar, UShort, UInt, ULong, Float} and n is any of {2, 3, 4, 8, 16}. Basic arithmetic operations on these vector types are available in the generic package CL.Vector_Operations. You have to instantiate it for each type you want to use with it, eg:

package Int2_Operations is new CL.Vector_Operations
   (CL.Int, CL."+", CL."-", CL."*", CL."/", CL."<", Range2, CL.Int2);

You do not need to explicitly specify the operations on CL.Int if they are directly visible.