Khronos OpenCL SDK

repository·main·Indexed 21 days ago

https://github.com/khronosgroup/opencl-sdk

A collection of essential components for OpenCL development, including headers, C++ bindings, an ICD loader, and utility libraries. The SDK provides an exported OpenCL Utility Library for common tasks and a non-exported OpenCL SDK Library for internal sample use. It includes code samples for C/C++, Python (via PyOpenCL), and Ruby (via opencl_ruby_ffi), as well as documentation on kernel profiling, binary loading, and data exchange techniques.

Tokens
46.4K
Snippets
102
Records
164
Agent score
70%

What's inside OpenCL SDK

  1. Understand the copybufferkernel sample

    main

    The copybufferkernel sample demonstrates how to use an OpenCL kernel to perform parallel work. In this specific implementation, each OpenCL work item is responsible for copying a single value from a source buffer to a destination buffer. Because the sample launches one work item for every element in the source buffer, the result is functionally equivalent to a standard buffer copy operation.

    A key characteristic of this sample is that the OpenCL kernel source code is embedded directly into the host code as a raw string. At runtime, the host code uses this string to create an OpenCL program and invokes the device compiler to prepare the kernel for execution.

  2. OpenCL SDK Components Overview

    main

    The OpenCL SDK provides the following core components for OpenCL development:

    • OpenCL Headers: Located in external/OpenCL-Headers/.
    • OpenCL C++ bindings: Located in external/OpenCL-CLHPP/include.
    • OpenCL Loader: Located in external/OpenCL-ICD-Loader.
    • OpenCL utility library: Located in lib/include.
    • Code samples: Located in samples/.
    • Documentation: Located in docs/.
  3. Understand OpenCL-OpenGL Interop in the NBody Sample

    main

    The Gravitational NBody sample demonstrates how to share vertex buffers between OpenCL and OpenGL. It focuses on the setup of interop contexts and shared resources to allow OpenCL to perform computations on data that is subsequently rendered by OpenGL.

    Key concepts used in this sample include:

    • Double Buffering: Used to manage gravitational interaction and time-stepping. This prevents race conditions where some particles might use updated positions while others are still calculating forces.
    • Implicit Synchronization: The sample utilizes basic and implicit interop context synchronization to coordinate between the two APIs.
  4. Understand the Reduce sample purpose and flow

    main

    The Reduce sample demonstrates how to implement a reduction algorithm (e.g., sum or minimum) by querying device extensions at runtime to select the most efficient kernel implementation.

    Application Flow

    1. Device Selection: Select an OpenCL device.
    2. Extension Querying: Check if the device supports built-in intrinsics: work_group_reduce_<op> and/or sub_group_reduce_<op>.
    3. Kernel Customization: The reduction operation (sum or minimum) is baked into the kernel source at runtime based on user input.
    4. Multi-step Reduction: The kernel reduces input to a scalar result in multiple steps using double buffering. In each step, the output size is input_size / (max_work_group_size * 2).
    5. Execution: Launch batches of kernels based on optimal work-group sizes queried from the compiled kernel.
  5. Share memory between OpenCL and Vulkan

    main

    You can share external device resources across GPU APIs, such as sharing buffers between Vulkan (for rendering) and OpenCL (for general-purpose computation). This is achieved by exporting a memory handle from Vulkan and importing it into OpenCL using specific handle types and file descriptors (on Linux) or Win32 handles (on Windows).

    Key Workflow:

    1. Vulkan Setup: Create a Vulkan instance with VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME and VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME enabled.
    2. Compatibility Check: Ensure the OpenCL device and Vulkan physical device share the same UUID (queried via CL_DEVICE_UUID_KHR in OpenCL and deviceUUID in Vulkan) and that the OpenCL device supports cl_khr_external_memory_opaque_fd (Linux) or cl_khr_external_memory_win32 (Windows).
    3. Vulkan Buffer Creation: Create buffers using VkExternalMemoryBufferCreateInfo (passed via pNext to VkBufferCreateInfo) with a non-null handleTypes field.
    4. Memory Allocation: Allocate memory using vkAllocateMemory with a VkExportMemoryAllocateInfo structure in the pNext chain.
    5. OpenCL Import: Obtain a file descriptor from Vulkan using vkGetMemoryFdKHR and initialize OpenCL buffers via clCreateBufferWithProperties using the CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_FD_KHR (or Win32 equivalent) property.
  6. Set up OpenCL-OpenGL Interop contexts and windows

    main

    To reduce the complexity of setting up interop contexts and managing windowed application flow, use the following utility functions and classes:

    • cl::util::get_interop_context: Simplifies the setup of an interop context.
    • cl::util::InteropWindow: Provides "GLUT-like" features. It manages typical windowed application control flow and ensures that API objects are created in the correct order for successful interop.

    The application lifecycle is primarily driven by cl::sdk::InteropWindow::run().

    // The application flow is dictated by the run method of the InteropWindow utility
    cl::sdk::InteropWindow window;
    // ... setup code ...
    window.run();
  7. Implement reduction using different kernel strategies

    main

    The sample provides three ways to implement the reduction logic depending on hardware support:

    1. Vanilla Work-group Reduction: A textbook tree-like reduction using async_work_group_copy to move data to local memory and barrier(CLK_LOCAL_MEM_FENCE) to synchronize steps. It uses a read_local helper to handle non-uniform input sizes.
    2. Work-group Reduction (Built-in): Uses the OpenCL intrinsic work_group_reduce_<op> (e.g., work_group_reduce_add) to perform the reduction within a work-group more efficiently than a manual loop.
    3. Sub-group Reduction (Built-in): Uses sub_group_reduce_<op> (e.g., sub_group_reduce_min). This is highly efficient on wide SIMD architectures because sub-group synchronization is often 'free' (lockstep execution). It reduces the input size by sub_group_size * 2 in each iteration, significantly reducing the number of global/local barriers required.
  8. How device fission works in OpenCL

    main

    Device fission allows you to partition a single OpenCL device into multiple sub-devices. These sub-devices are perceived as independent devices but correspond to specific physical regions of the original hardware. This is useful for task parallelism or isolating workloads.

    There are three primary partitioning strategies:

    1. Partition equally by compute units: You specify the number of compute units each sub-device should have. OpenCL creates as many sub-devices as possible. If the total number of compute units is not perfectly divisible by your specification, the remaining units are not assigned to any sub-device.
    2. Partition by counts: You specify the exact number of compute units for each sub-device. This is useful for isolating high-priority tasks from lower-priority ones.
    3. Partition by affinity domain: The device is split based on shared cache hierarchies (e.g., grouping compute units by NUMA node). This helps maximize throughput in high-throughput jobs or optimize for shared memory requirements.

    Note: Fission can be recursive; a sub-device can itself be partitioned into further sub-sub-devices.

  9. Understand the difference between the OpenCL Utility Library and the OpenCL SDK Library

    main

    The OpenCL SDK contains two distinct libraries designed for different usage scenarios:

    1. OpenCL Utility Library: An exported library designed to ease the use of OpenCL by condensing common tasks into single functions or adding missing functionality without breaking the core API. It is analogous to GLU and GLUT in the OpenGL domain. This library is intended for external use.

    2. OpenCL SDK Library: A library that builds on top of the Utility library. It handles tasks like command-line argument parsing, device selection, and logging. Because it deduplicates tasks that applications often implement uniquely, it is not exported when installing the SDK and has low promises for forward/backward compatibility. It is primarily intended for use within the SDK samples.

  10. Implement data exchange between workitems in OpenCL

    main

    To optimize memory-bound tasks like blurring, you can reduce global memory accesses by exchanging data between workitems within a workgroup. This sample demonstrates three primary techniques for data exchange:

    1. Local Memory Exchange: Workitems collectively load the required pixel data into local memory, and then each workitem reads the pixels sequentially from that local memory.
    2. Subgroup Exchange: Uses OpenCL 3.0 extensions cl_khr_subgroup_shuffle and cl_khr_subgroup_shuffle_relative to exchange data directly between workitems in a subgroup without using local memory. This requires using sub_group_shuffle, sub_group_shuffle_up, or sub_group_shuffle_down intrinsics.
    3. Vanilla Single-pass: A standard approach without specialized data exchange optimizations.
  11. Extract and load OpenCL program binaries

    main

    You can perform offline compilation by extracting a compiled program's binary and loading it later.

    1. Extraction: Use clGetProgramInfo with the CL_PROGRAM_BINARIES parameter to retrieve the binary for a specific device.
    2. Storage: Save this binary to a file.
    3. Loading: Use clCreateProgramWithBinary to construct an OpenCL program from the saved binary for the same device and context.

    This workflow allows you to avoid the overhead of runtime compilation by reusing previously compiled binaries.