From 99c20d243b8fcde31322953ae789f110079c42c7 Mon Sep 17 00:00:00 2001 From: bashbaug Date: Tue, 16 Jan 2018 10:39:13 -0800 Subject: [PATCH] First Release --- .gitignore | 28 + Android.mk | 23 + CL/cl.h | 1478 +++ CL/cl_gl.h | 173 + CL/cl_platform.h | 1395 +++ CMakeLists.txt | 261 + CODE_OF_CONDUCT.md | 76 + CONTRIBUTING.md | 32 + GL/glcorearb.h | 3597 ++++++ Kernels/builtin_kernels.cl | 131 + Kernels/precompiled_kernels.cl | 297 + LICENSE | 21 + OS/OS.h | 33 + OS/OS_linux.cpp | 36 + OS/OS_linux.h | 134 + OS/OS_linux_common.cpp | 151 + OS/OS_linux_common.h | 306 + OS/OS_mac.cpp | 36 + OS/OS_mac.h | 114 + OS/OS_mac_common.cpp | 130 + OS/OS_mac_common.h | 251 + OS/OS_mac_interpose.h | 171 + OS/OS_timer.h | 125 + OS/OS_windows.cpp | 37 + OS/OS_windows.h | 192 + OS/OS_windows_common.cpp | 41 + OS/OS_windows_common.h | 379 + README.md | 82 + Src/clIntercept.def | 140 + Src/clIntercept.map | 175 + Src/cli_ext.h | 701 ++ Src/common.h | 110 + Src/controls.h | 177 + Src/dispatch.cpp | 8612 +++++++++++++ Src/dispatch.h | 1220 ++ Src/enummap.cpp | 973 ++ Src/enummap.h | 129 + Src/git_version.cpp.in | 27 + Src/git_version.rc.in | 64 + Src/instrumentation.h | 175 + Src/intercept.cpp | 10177 ++++++++++++++++ Src/intercept.h | 2109 ++++ Src/main.cpp | 86 + Src/stubs.cpp | 1958 +++ attached_licenses/LICENSE | 25 + attached_licenses/LICENSE_1_0.txt | 23 + cmake_modules/GetGitRevisionDescription.cmake | 130 + .../GetGitRevisionDescription.cmake.in | 41 + config/CLIConfig.cpp | 1116 ++ config/CLIConfig.h | 170 + config/CLIConfig.rc | 202 + config/CLIConfig_version.rc2 | 62 + config/CMakeLists.txt | 62 + config/clintercept_logo.ico | Bin 0 -> 215470 bytes config/default.ico | Bin 0 -> 766 bytes config/disabled.ico | Bin 0 -> 766 bytes config/envVars.h | 109 + config/modified.ico | Bin 0 -> 766 bytes config/modified_default.ico | Bin 0 -> 766 bytes config/modified_nondefault.ico | Bin 0 -> 766 bytes config/nondefault.ico | Bin 0 -> 766 bytes config/resource.h | 56 + config/separator.ico | Bin 0 -> 766 bytes docs/build.md | 67 + docs/chrome_tracing.md | 67 + docs/controls.md | 620 + docs/images/chrome_tracing_detail.png | Bin 0 -> 59970 bytes docs/images/chrome_tracing_empty.png | Bin 0 -> 35429 bytes docs/images/chrome_tracing_example.png | Bin 0 -> 48859 bytes docs/images/cmake_itt.png | Bin 0 -> 37613 bytes docs/images/vtune_config.png | Bin 0 -> 352981 bytes docs/images/vtune_device_timing.png | Bin 0 -> 82449 bytes docs/images/vtune_output.png | Bin 0 -> 162076 bytes docs/injecting_programs.md | 95 + docs/install.md | 129 + docs/vtune_logging.md | 77 + resource/clIntercept.rc | Bin 0 -> 3594 bytes resource/clIntercept_resource.h | 17 + scripts/generate_controls_doc.py | 207 + 79 files changed, 39838 insertions(+) create mode 100644 .gitignore create mode 100644 Android.mk create mode 100644 CL/cl.h create mode 100644 CL/cl_gl.h create mode 100644 CL/cl_platform.h create mode 100644 CMakeLists.txt create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 GL/glcorearb.h create mode 100644 Kernels/builtin_kernels.cl create mode 100644 Kernels/precompiled_kernels.cl create mode 100644 LICENSE create mode 100644 OS/OS.h create mode 100644 OS/OS_linux.cpp create mode 100644 OS/OS_linux.h create mode 100644 OS/OS_linux_common.cpp create mode 100644 OS/OS_linux_common.h create mode 100644 OS/OS_mac.cpp create mode 100644 OS/OS_mac.h create mode 100644 OS/OS_mac_common.cpp create mode 100644 OS/OS_mac_common.h create mode 100644 OS/OS_mac_interpose.h create mode 100644 OS/OS_timer.h create mode 100644 OS/OS_windows.cpp create mode 100644 OS/OS_windows.h create mode 100644 OS/OS_windows_common.cpp create mode 100644 OS/OS_windows_common.h create mode 100644 README.md create mode 100644 Src/clIntercept.def create mode 100644 Src/clIntercept.map create mode 100644 Src/cli_ext.h create mode 100644 Src/common.h create mode 100644 Src/controls.h create mode 100644 Src/dispatch.cpp create mode 100644 Src/dispatch.h create mode 100644 Src/enummap.cpp create mode 100644 Src/enummap.h create mode 100644 Src/git_version.cpp.in create mode 100644 Src/git_version.rc.in create mode 100644 Src/instrumentation.h create mode 100644 Src/intercept.cpp create mode 100644 Src/intercept.h create mode 100644 Src/main.cpp create mode 100644 Src/stubs.cpp create mode 100644 attached_licenses/LICENSE create mode 100644 attached_licenses/LICENSE_1_0.txt create mode 100644 cmake_modules/GetGitRevisionDescription.cmake create mode 100644 cmake_modules/GetGitRevisionDescription.cmake.in create mode 100644 config/CLIConfig.cpp create mode 100644 config/CLIConfig.h create mode 100644 config/CLIConfig.rc create mode 100644 config/CLIConfig_version.rc2 create mode 100644 config/CMakeLists.txt create mode 100644 config/clintercept_logo.ico create mode 100644 config/default.ico create mode 100644 config/disabled.ico create mode 100644 config/envVars.h create mode 100644 config/modified.ico create mode 100644 config/modified_default.ico create mode 100644 config/modified_nondefault.ico create mode 100644 config/nondefault.ico create mode 100644 config/resource.h create mode 100644 config/separator.ico create mode 100644 docs/build.md create mode 100644 docs/chrome_tracing.md create mode 100644 docs/controls.md create mode 100644 docs/images/chrome_tracing_detail.png create mode 100644 docs/images/chrome_tracing_empty.png create mode 100644 docs/images/chrome_tracing_example.png create mode 100644 docs/images/cmake_itt.png create mode 100644 docs/images/vtune_config.png create mode 100644 docs/images/vtune_device_timing.png create mode 100644 docs/images/vtune_output.png create mode 100644 docs/injecting_programs.md create mode 100644 docs/install.md create mode 100644 docs/vtune_logging.md create mode 100644 resource/clIntercept.rc create mode 100644 resource/clIntercept_resource.h create mode 100644 scripts/generate_controls_doc.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..e24ff034 --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +.klocwork/ +.vs/ +.vscode/ +_bin/ +_bin32/ +_bin64/ +build/ +build32/ +build64/ +Builds/ +config/*.png +install/ +Makefile +*~ +*.aps +*.bmp +*.ncb +*.opensdf +*.pal +*.sdf +*.sln +*.suo +*.vcproj +*.vcxproj +*.vcxproj.filters +*.vcxproj.user +*.xcf + diff --git a/Android.mk b/Android.mk new file mode 100644 index 00000000..63d82a7e --- /dev/null +++ b/Android.mk @@ -0,0 +1,23 @@ +LOCAL_PATH := $(call my-dir) + +#================# + +include $(CLEAR_VARS) + +APP_STL := stlport_static + +LOCAL_MODULE := clIntercept + +#LOCAL_CPPFLAGS += -std=c++11 + +LOCAL_SHARED_LIBRARIES := libdl liblog +LOCAL_SRC_FILES += Src/dispatch.cpp Src/enummap.cpp Src/intercept.cpp Src/main.cpp Src/stubs.cpp +LOCAL_SRC_FILES += OS/OS_linux_common.cpp OS/OS_linux.cpp + +LOCAL_C_INCLUDES += $(LOCAL_PATH)/Src + + +include external/libcxx/libcxx.mk + +include $(BUILD_SHARED_LIBRARY) + diff --git a/CL/cl.h b/CL/cl.h new file mode 100644 index 00000000..f217e554 --- /dev/null +++ b/CL/cl.h @@ -0,0 +1,1478 @@ +/******************************************************************************* + * Copyright (c) 2008-2015 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and/or associated documentation files (the + * "Materials"), to deal in the Materials without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Materials, and to + * permit persons to whom the Materials are 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 Materials. + * + * MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS + * KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS + * SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT + * https://www.khronos.org/registry/ + * + * THE MATERIALS ARE 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 + * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + ******************************************************************************/ + +#ifndef __OPENCL_CL_H +#define __OPENCL_CL_H + +// Unlike the Khronos header file, we want to unconditonally include the +// CLIntercept cl_platform.h, and not the system cl_platform.h. +#if 0 +#ifdef __APPLE__ +#include +#else +#include +#endif +#else +#include "CL/cl_platform.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************************/ + +typedef struct _cl_platform_id * cl_platform_id; +typedef struct _cl_device_id * cl_device_id; +typedef struct _cl_context * cl_context; +typedef struct _cl_command_queue * cl_command_queue; +typedef struct _cl_mem * cl_mem; +typedef struct _cl_program * cl_program; +typedef struct _cl_kernel * cl_kernel; +typedef struct _cl_event * cl_event; +typedef struct _cl_sampler * cl_sampler; + +typedef cl_uint cl_bool; /* WARNING! Unlike cl_ types in cl_platform.h, cl_bool is not guaranteed to be the same size as the bool in kernels. */ +typedef cl_ulong cl_bitfield; +typedef cl_bitfield cl_device_type; +typedef cl_uint cl_platform_info; +typedef cl_uint cl_device_info; +typedef cl_bitfield cl_device_fp_config; +typedef cl_uint cl_device_mem_cache_type; +typedef cl_uint cl_device_local_mem_type; +typedef cl_bitfield cl_device_exec_capabilities; +typedef cl_bitfield cl_device_svm_capabilities; +typedef cl_bitfield cl_command_queue_properties; +typedef intptr_t cl_device_partition_property; +typedef cl_bitfield cl_device_affinity_domain; + +typedef intptr_t cl_context_properties; +typedef cl_uint cl_context_info; +typedef cl_bitfield cl_queue_properties; +typedef cl_uint cl_command_queue_info; +typedef cl_uint cl_channel_order; +typedef cl_uint cl_channel_type; +typedef cl_bitfield cl_mem_flags; +typedef cl_bitfield cl_svm_mem_flags; +typedef cl_uint cl_mem_object_type; +typedef cl_uint cl_mem_info; +typedef cl_bitfield cl_mem_migration_flags; +typedef cl_uint cl_image_info; +typedef cl_uint cl_buffer_create_type; +typedef cl_uint cl_addressing_mode; +typedef cl_uint cl_filter_mode; +typedef cl_uint cl_sampler_info; +typedef cl_bitfield cl_map_flags; +typedef intptr_t cl_pipe_properties; +typedef cl_uint cl_pipe_info; +typedef cl_uint cl_program_info; +typedef cl_uint cl_program_build_info; +typedef cl_uint cl_program_binary_type; +typedef cl_int cl_build_status; +typedef cl_uint cl_kernel_info; +typedef cl_uint cl_kernel_arg_info; +typedef cl_uint cl_kernel_arg_address_qualifier; +typedef cl_uint cl_kernel_arg_access_qualifier; +typedef cl_bitfield cl_kernel_arg_type_qualifier; +typedef cl_uint cl_kernel_work_group_info; +typedef cl_uint cl_kernel_sub_group_info; +typedef cl_uint cl_event_info; +typedef cl_uint cl_command_type; +typedef cl_uint cl_profiling_info; +typedef cl_bitfield cl_sampler_properties; +typedef cl_uint cl_kernel_exec_info; + +typedef struct _cl_image_format { + cl_channel_order image_channel_order; + cl_channel_type image_channel_data_type; +} cl_image_format; + +typedef struct _cl_image_desc { + cl_mem_object_type image_type; + size_t image_width; + size_t image_height; + size_t image_depth; + size_t image_array_size; + size_t image_row_pitch; + size_t image_slice_pitch; + cl_uint num_mip_levels; + cl_uint num_samples; + cl_mem mem_object; +} cl_image_desc; + +typedef struct _cl_buffer_region { + size_t origin; + size_t size; +} cl_buffer_region; + +/******************************************************************************/ + +/* Error Codes */ +#define CL_SUCCESS 0 +#define CL_DEVICE_NOT_FOUND -1 +#define CL_DEVICE_NOT_AVAILABLE -2 +#define CL_COMPILER_NOT_AVAILABLE -3 +#define CL_MEM_OBJECT_ALLOCATION_FAILURE -4 +#define CL_OUT_OF_RESOURCES -5 +#define CL_OUT_OF_HOST_MEMORY -6 +#define CL_PROFILING_INFO_NOT_AVAILABLE -7 +#define CL_MEM_COPY_OVERLAP -8 +#define CL_IMAGE_FORMAT_MISMATCH -9 +#define CL_IMAGE_FORMAT_NOT_SUPPORTED -10 +#define CL_BUILD_PROGRAM_FAILURE -11 +#define CL_MAP_FAILURE -12 +#define CL_MISALIGNED_SUB_BUFFER_OFFSET -13 +#define CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST -14 +#define CL_COMPILE_PROGRAM_FAILURE -15 +#define CL_LINKER_NOT_AVAILABLE -16 +#define CL_LINK_PROGRAM_FAILURE -17 +#define CL_DEVICE_PARTITION_FAILED -18 +#define CL_KERNEL_ARG_INFO_NOT_AVAILABLE -19 + +#define CL_INVALID_VALUE -30 +#define CL_INVALID_DEVICE_TYPE -31 +#define CL_INVALID_PLATFORM -32 +#define CL_INVALID_DEVICE -33 +#define CL_INVALID_CONTEXT -34 +#define CL_INVALID_QUEUE_PROPERTIES -35 +#define CL_INVALID_COMMAND_QUEUE -36 +#define CL_INVALID_HOST_PTR -37 +#define CL_INVALID_MEM_OBJECT -38 +#define CL_INVALID_IMAGE_FORMAT_DESCRIPTOR -39 +#define CL_INVALID_IMAGE_SIZE -40 +#define CL_INVALID_SAMPLER -41 +#define CL_INVALID_BINARY -42 +#define CL_INVALID_BUILD_OPTIONS -43 +#define CL_INVALID_PROGRAM -44 +#define CL_INVALID_PROGRAM_EXECUTABLE -45 +#define CL_INVALID_KERNEL_NAME -46 +#define CL_INVALID_KERNEL_DEFINITION -47 +#define CL_INVALID_KERNEL -48 +#define CL_INVALID_ARG_INDEX -49 +#define CL_INVALID_ARG_VALUE -50 +#define CL_INVALID_ARG_SIZE -51 +#define CL_INVALID_KERNEL_ARGS -52 +#define CL_INVALID_WORK_DIMENSION -53 +#define CL_INVALID_WORK_GROUP_SIZE -54 +#define CL_INVALID_WORK_ITEM_SIZE -55 +#define CL_INVALID_GLOBAL_OFFSET -56 +#define CL_INVALID_EVENT_WAIT_LIST -57 +#define CL_INVALID_EVENT -58 +#define CL_INVALID_OPERATION -59 +#define CL_INVALID_GL_OBJECT -60 +#define CL_INVALID_BUFFER_SIZE -61 +#define CL_INVALID_MIP_LEVEL -62 +#define CL_INVALID_GLOBAL_WORK_SIZE -63 +#define CL_INVALID_PROPERTY -64 +#define CL_INVALID_IMAGE_DESCRIPTOR -65 +#define CL_INVALID_COMPILER_OPTIONS -66 +#define CL_INVALID_LINKER_OPTIONS -67 +#define CL_INVALID_DEVICE_PARTITION_COUNT -68 +#define CL_INVALID_PIPE_SIZE -69 +#define CL_INVALID_DEVICE_QUEUE -70 +#define CL_INVALID_SPEC_ID -71 +#define CL_MAX_SIZE_RESTRICTION_EXCEEDED -72 + +/* OpenCL Version */ +#define CL_VERSION_1_0 1 +#define CL_VERSION_1_1 1 +#define CL_VERSION_1_2 1 +#define CL_VERSION_2_0 1 +#define CL_VERSION_2_1 1 +#define CL_VERSION_2_2 1 + +/* cl_bool */ +#define CL_FALSE 0 +#define CL_TRUE 1 +#define CL_BLOCKING CL_TRUE +#define CL_NON_BLOCKING CL_FALSE + +/* cl_platform_info */ +#define CL_PLATFORM_PROFILE 0x0900 +#define CL_PLATFORM_VERSION 0x0901 +#define CL_PLATFORM_NAME 0x0902 +#define CL_PLATFORM_VENDOR 0x0903 +#define CL_PLATFORM_EXTENSIONS 0x0904 +#define CL_PLATFORM_HOST_TIMER_RESOLUTION 0x0905 + +/* cl_device_type - bitfield */ +#define CL_DEVICE_TYPE_DEFAULT (1 << 0) +#define CL_DEVICE_TYPE_CPU (1 << 1) +#define CL_DEVICE_TYPE_GPU (1 << 2) +#define CL_DEVICE_TYPE_ACCELERATOR (1 << 3) +#define CL_DEVICE_TYPE_CUSTOM (1 << 4) +#define CL_DEVICE_TYPE_ALL 0xFFFFFFFF + +/* cl_device_info */ +#define CL_DEVICE_TYPE 0x1000 +#define CL_DEVICE_VENDOR_ID 0x1001 +#define CL_DEVICE_MAX_COMPUTE_UNITS 0x1002 +#define CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS 0x1003 +#define CL_DEVICE_MAX_WORK_GROUP_SIZE 0x1004 +#define CL_DEVICE_MAX_WORK_ITEM_SIZES 0x1005 +#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR 0x1006 +#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT 0x1007 +#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT 0x1008 +#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG 0x1009 +#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT 0x100A +#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE 0x100B +#define CL_DEVICE_MAX_CLOCK_FREQUENCY 0x100C +#define CL_DEVICE_ADDRESS_BITS 0x100D +#define CL_DEVICE_MAX_READ_IMAGE_ARGS 0x100E +#define CL_DEVICE_MAX_WRITE_IMAGE_ARGS 0x100F +#define CL_DEVICE_MAX_MEM_ALLOC_SIZE 0x1010 +#define CL_DEVICE_IMAGE2D_MAX_WIDTH 0x1011 +#define CL_DEVICE_IMAGE2D_MAX_HEIGHT 0x1012 +#define CL_DEVICE_IMAGE3D_MAX_WIDTH 0x1013 +#define CL_DEVICE_IMAGE3D_MAX_HEIGHT 0x1014 +#define CL_DEVICE_IMAGE3D_MAX_DEPTH 0x1015 +#define CL_DEVICE_IMAGE_SUPPORT 0x1016 +#define CL_DEVICE_MAX_PARAMETER_SIZE 0x1017 +#define CL_DEVICE_MAX_SAMPLERS 0x1018 +#define CL_DEVICE_MEM_BASE_ADDR_ALIGN 0x1019 +#define CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE 0x101A +#define CL_DEVICE_SINGLE_FP_CONFIG 0x101B +#define CL_DEVICE_GLOBAL_MEM_CACHE_TYPE 0x101C +#define CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE 0x101D +#define CL_DEVICE_GLOBAL_MEM_CACHE_SIZE 0x101E +#define CL_DEVICE_GLOBAL_MEM_SIZE 0x101F +#define CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE 0x1020 +#define CL_DEVICE_MAX_CONSTANT_ARGS 0x1021 +#define CL_DEVICE_LOCAL_MEM_TYPE 0x1022 +#define CL_DEVICE_LOCAL_MEM_SIZE 0x1023 +#define CL_DEVICE_ERROR_CORRECTION_SUPPORT 0x1024 +#define CL_DEVICE_PROFILING_TIMER_RESOLUTION 0x1025 +#define CL_DEVICE_ENDIAN_LITTLE 0x1026 +#define CL_DEVICE_AVAILABLE 0x1027 +#define CL_DEVICE_COMPILER_AVAILABLE 0x1028 +#define CL_DEVICE_EXECUTION_CAPABILITIES 0x1029 +#define CL_DEVICE_QUEUE_PROPERTIES 0x102A /* deprecated */ +#define CL_DEVICE_QUEUE_ON_HOST_PROPERTIES 0x102A +#define CL_DEVICE_NAME 0x102B +#define CL_DEVICE_VENDOR 0x102C +#define CL_DRIVER_VERSION 0x102D +#define CL_DEVICE_PROFILE 0x102E +#define CL_DEVICE_VERSION 0x102F +#define CL_DEVICE_EXTENSIONS 0x1030 +#define CL_DEVICE_PLATFORM 0x1031 +#define CL_DEVICE_DOUBLE_FP_CONFIG 0x1032 +#define CL_DEVICE_HALF_FP_CONFIG 0x1033 +#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF 0x1034 +#define CL_DEVICE_HOST_UNIFIED_MEMORY 0x1035 /* deprecated */ +#define CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR 0x1036 +#define CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT 0x1037 +#define CL_DEVICE_NATIVE_VECTOR_WIDTH_INT 0x1038 +#define CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG 0x1039 +#define CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT 0x103A +#define CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE 0x103B +#define CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF 0x103C +#define CL_DEVICE_OPENCL_C_VERSION 0x103D +#define CL_DEVICE_LINKER_AVAILABLE 0x103E +#define CL_DEVICE_BUILT_IN_KERNELS 0x103F +#define CL_DEVICE_IMAGE_MAX_BUFFER_SIZE 0x1040 +#define CL_DEVICE_IMAGE_MAX_ARRAY_SIZE 0x1041 +#define CL_DEVICE_PARENT_DEVICE 0x1042 +#define CL_DEVICE_PARTITION_MAX_SUB_DEVICES 0x1043 +#define CL_DEVICE_PARTITION_PROPERTIES 0x1044 +#define CL_DEVICE_PARTITION_AFFINITY_DOMAIN 0x1045 +#define CL_DEVICE_PARTITION_TYPE 0x1046 +#define CL_DEVICE_REFERENCE_COUNT 0x1047 +#define CL_DEVICE_PREFERRED_INTEROP_USER_SYNC 0x1048 +#define CL_DEVICE_PRINTF_BUFFER_SIZE 0x1049 +#define CL_DEVICE_IMAGE_PITCH_ALIGNMENT 0x104A +#define CL_DEVICE_IMAGE_BASE_ADDRESS_ALIGNMENT 0x104B +#define CL_DEVICE_MAX_READ_WRITE_IMAGE_ARGS 0x104C +#define CL_DEVICE_MAX_GLOBAL_VARIABLE_SIZE 0x104D +#define CL_DEVICE_QUEUE_ON_DEVICE_PROPERTIES 0x104E +#define CL_DEVICE_QUEUE_ON_DEVICE_PREFERRED_SIZE 0x104F +#define CL_DEVICE_QUEUE_ON_DEVICE_MAX_SIZE 0x1050 +#define CL_DEVICE_MAX_ON_DEVICE_QUEUES 0x1051 +#define CL_DEVICE_MAX_ON_DEVICE_EVENTS 0x1052 +#define CL_DEVICE_SVM_CAPABILITIES 0x1053 +#define CL_DEVICE_GLOBAL_VARIABLE_PREFERRED_TOTAL_SIZE 0x1054 +#define CL_DEVICE_MAX_PIPE_ARGS 0x1055 +#define CL_DEVICE_PIPE_MAX_ACTIVE_RESERVATIONS 0x1056 +#define CL_DEVICE_PIPE_MAX_PACKET_SIZE 0x1057 +#define CL_DEVICE_PREFERRED_PLATFORM_ATOMIC_ALIGNMENT 0x1058 +#define CL_DEVICE_PREFERRED_GLOBAL_ATOMIC_ALIGNMENT 0x1059 +#define CL_DEVICE_PREFERRED_LOCAL_ATOMIC_ALIGNMENT 0x105A +#define CL_DEVICE_IL_VERSION 0x105B +#define CL_DEVICE_MAX_NUM_SUB_GROUPS 0x105C +#define CL_DEVICE_SUB_GROUP_INDEPENDENT_FORWARD_PROGRESS 0x105D + +/* cl_device_fp_config - bitfield */ +#define CL_FP_DENORM (1 << 0) +#define CL_FP_INF_NAN (1 << 1) +#define CL_FP_ROUND_TO_NEAREST (1 << 2) +#define CL_FP_ROUND_TO_ZERO (1 << 3) +#define CL_FP_ROUND_TO_INF (1 << 4) +#define CL_FP_FMA (1 << 5) +#define CL_FP_SOFT_FLOAT (1 << 6) +#define CL_FP_CORRECTLY_ROUNDED_DIVIDE_SQRT (1 << 7) + +/* cl_device_mem_cache_type */ +#define CL_NONE 0x0 +#define CL_READ_ONLY_CACHE 0x1 +#define CL_READ_WRITE_CACHE 0x2 + +/* cl_device_local_mem_type */ +#define CL_LOCAL 0x1 +#define CL_GLOBAL 0x2 + +/* cl_device_exec_capabilities - bitfield */ +#define CL_EXEC_KERNEL (1 << 0) +#define CL_EXEC_NATIVE_KERNEL (1 << 1) + +/* cl_command_queue_properties - bitfield */ +#define CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE (1 << 0) +#define CL_QUEUE_PROFILING_ENABLE (1 << 1) +#define CL_QUEUE_ON_DEVICE (1 << 2) +#define CL_QUEUE_ON_DEVICE_DEFAULT (1 << 3) + +/* cl_context_info */ +#define CL_CONTEXT_REFERENCE_COUNT 0x1080 +#define CL_CONTEXT_DEVICES 0x1081 +#define CL_CONTEXT_PROPERTIES 0x1082 +#define CL_CONTEXT_NUM_DEVICES 0x1083 + +/* cl_context_properties */ +#define CL_CONTEXT_PLATFORM 0x1084 +#define CL_CONTEXT_INTEROP_USER_SYNC 0x1085 + +/* cl_device_partition_property */ +#define CL_DEVICE_PARTITION_EQUALLY 0x1086 +#define CL_DEVICE_PARTITION_BY_COUNTS 0x1087 +#define CL_DEVICE_PARTITION_BY_COUNTS_LIST_END 0x0 +#define CL_DEVICE_PARTITION_BY_AFFINITY_DOMAIN 0x1088 + +/* cl_device_affinity_domain */ +#define CL_DEVICE_AFFINITY_DOMAIN_NUMA (1 << 0) +#define CL_DEVICE_AFFINITY_DOMAIN_L4_CACHE (1 << 1) +#define CL_DEVICE_AFFINITY_DOMAIN_L3_CACHE (1 << 2) +#define CL_DEVICE_AFFINITY_DOMAIN_L2_CACHE (1 << 3) +#define CL_DEVICE_AFFINITY_DOMAIN_L1_CACHE (1 << 4) +#define CL_DEVICE_AFFINITY_DOMAIN_NEXT_PARTITIONABLE (1 << 5) + +/* cl_device_svm_capabilities */ +#define CL_DEVICE_SVM_COARSE_GRAIN_BUFFER (1 << 0) +#define CL_DEVICE_SVM_FINE_GRAIN_BUFFER (1 << 1) +#define CL_DEVICE_SVM_FINE_GRAIN_SYSTEM (1 << 2) +#define CL_DEVICE_SVM_ATOMICS (1 << 3) + +/* cl_command_queue_info */ +#define CL_QUEUE_CONTEXT 0x1090 +#define CL_QUEUE_DEVICE 0x1091 +#define CL_QUEUE_REFERENCE_COUNT 0x1092 +#define CL_QUEUE_PROPERTIES 0x1093 +#define CL_QUEUE_SIZE 0x1094 +#define CL_QUEUE_DEVICE_DEFAULT 0x1095 + +/* cl_mem_flags and cl_svm_mem_flags - bitfield */ +#define CL_MEM_READ_WRITE (1 << 0) +#define CL_MEM_WRITE_ONLY (1 << 1) +#define CL_MEM_READ_ONLY (1 << 2) +#define CL_MEM_USE_HOST_PTR (1 << 3) +#define CL_MEM_ALLOC_HOST_PTR (1 << 4) +#define CL_MEM_COPY_HOST_PTR (1 << 5) +/* reserved (1 << 6) */ +#define CL_MEM_HOST_WRITE_ONLY (1 << 7) +#define CL_MEM_HOST_READ_ONLY (1 << 8) +#define CL_MEM_HOST_NO_ACCESS (1 << 9) +#define CL_MEM_SVM_FINE_GRAIN_BUFFER (1 << 10) /* used by cl_svm_mem_flags only */ +#define CL_MEM_SVM_ATOMICS (1 << 11) /* used by cl_svm_mem_flags only */ +#define CL_MEM_KERNEL_READ_AND_WRITE (1 << 12) + +/* cl_mem_migration_flags - bitfield */ +#define CL_MIGRATE_MEM_OBJECT_HOST (1 << 0) +#define CL_MIGRATE_MEM_OBJECT_CONTENT_UNDEFINED (1 << 1) + +/* cl_channel_order */ +#define CL_R 0x10B0 +#define CL_A 0x10B1 +#define CL_RG 0x10B2 +#define CL_RA 0x10B3 +#define CL_RGB 0x10B4 +#define CL_RGBA 0x10B5 +#define CL_BGRA 0x10B6 +#define CL_ARGB 0x10B7 +#define CL_INTENSITY 0x10B8 +#define CL_LUMINANCE 0x10B9 +#define CL_Rx 0x10BA +#define CL_RGx 0x10BB +#define CL_RGBx 0x10BC +#define CL_DEPTH 0x10BD +#define CL_DEPTH_STENCIL 0x10BE +#define CL_sRGB 0x10BF +#define CL_sRGBx 0x10C0 +#define CL_sRGBA 0x10C1 +#define CL_sBGRA 0x10C2 +#define CL_ABGR 0x10C3 + +/* cl_channel_type */ +#define CL_SNORM_INT8 0x10D0 +#define CL_SNORM_INT16 0x10D1 +#define CL_UNORM_INT8 0x10D2 +#define CL_UNORM_INT16 0x10D3 +#define CL_UNORM_SHORT_565 0x10D4 +#define CL_UNORM_SHORT_555 0x10D5 +#define CL_UNORM_INT_101010 0x10D6 +#define CL_SIGNED_INT8 0x10D7 +#define CL_SIGNED_INT16 0x10D8 +#define CL_SIGNED_INT32 0x10D9 +#define CL_UNSIGNED_INT8 0x10DA +#define CL_UNSIGNED_INT16 0x10DB +#define CL_UNSIGNED_INT32 0x10DC +#define CL_HALF_FLOAT 0x10DD +#define CL_FLOAT 0x10DE +#define CL_UNORM_INT24 0x10DF +#define CL_UNORM_INT_101010_2 0x10E0 + +/* cl_mem_object_type */ +#define CL_MEM_OBJECT_BUFFER 0x10F0 +#define CL_MEM_OBJECT_IMAGE2D 0x10F1 +#define CL_MEM_OBJECT_IMAGE3D 0x10F2 +#define CL_MEM_OBJECT_IMAGE2D_ARRAY 0x10F3 +#define CL_MEM_OBJECT_IMAGE1D 0x10F4 +#define CL_MEM_OBJECT_IMAGE1D_ARRAY 0x10F5 +#define CL_MEM_OBJECT_IMAGE1D_BUFFER 0x10F6 +#define CL_MEM_OBJECT_PIPE 0x10F7 + +/* cl_mem_info */ +#define CL_MEM_TYPE 0x1100 +#define CL_MEM_FLAGS 0x1101 +#define CL_MEM_SIZE 0x1102 +#define CL_MEM_HOST_PTR 0x1103 +#define CL_MEM_MAP_COUNT 0x1104 +#define CL_MEM_REFERENCE_COUNT 0x1105 +#define CL_MEM_CONTEXT 0x1106 +#define CL_MEM_ASSOCIATED_MEMOBJECT 0x1107 +#define CL_MEM_OFFSET 0x1108 +#define CL_MEM_USES_SVM_POINTER 0x1109 + +/* cl_image_info */ +#define CL_IMAGE_FORMAT 0x1110 +#define CL_IMAGE_ELEMENT_SIZE 0x1111 +#define CL_IMAGE_ROW_PITCH 0x1112 +#define CL_IMAGE_SLICE_PITCH 0x1113 +#define CL_IMAGE_WIDTH 0x1114 +#define CL_IMAGE_HEIGHT 0x1115 +#define CL_IMAGE_DEPTH 0x1116 +#define CL_IMAGE_ARRAY_SIZE 0x1117 +#define CL_IMAGE_BUFFER 0x1118 +#define CL_IMAGE_NUM_MIP_LEVELS 0x1119 +#define CL_IMAGE_NUM_SAMPLES 0x111A + +/* cl_pipe_info */ +#define CL_PIPE_PACKET_SIZE 0x1120 +#define CL_PIPE_MAX_PACKETS 0x1121 + +/* cl_addressing_mode */ +#define CL_ADDRESS_NONE 0x1130 +#define CL_ADDRESS_CLAMP_TO_EDGE 0x1131 +#define CL_ADDRESS_CLAMP 0x1132 +#define CL_ADDRESS_REPEAT 0x1133 +#define CL_ADDRESS_MIRRORED_REPEAT 0x1134 + +/* cl_filter_mode */ +#define CL_FILTER_NEAREST 0x1140 +#define CL_FILTER_LINEAR 0x1141 + +/* cl_sampler_info */ +#define CL_SAMPLER_REFERENCE_COUNT 0x1150 +#define CL_SAMPLER_CONTEXT 0x1151 +#define CL_SAMPLER_NORMALIZED_COORDS 0x1152 +#define CL_SAMPLER_ADDRESSING_MODE 0x1153 +#define CL_SAMPLER_FILTER_MODE 0x1154 +#define CL_SAMPLER_MIP_FILTER_MODE 0x1155 +#define CL_SAMPLER_LOD_MIN 0x1156 +#define CL_SAMPLER_LOD_MAX 0x1157 + +/* cl_map_flags - bitfield */ +#define CL_MAP_READ (1 << 0) +#define CL_MAP_WRITE (1 << 1) +#define CL_MAP_WRITE_INVALIDATE_REGION (1 << 2) + +/* cl_program_info */ +#define CL_PROGRAM_REFERENCE_COUNT 0x1160 +#define CL_PROGRAM_CONTEXT 0x1161 +#define CL_PROGRAM_NUM_DEVICES 0x1162 +#define CL_PROGRAM_DEVICES 0x1163 +#define CL_PROGRAM_SOURCE 0x1164 +#define CL_PROGRAM_BINARY_SIZES 0x1165 +#define CL_PROGRAM_BINARIES 0x1166 +#define CL_PROGRAM_NUM_KERNELS 0x1167 +#define CL_PROGRAM_KERNEL_NAMES 0x1168 +#define CL_PROGRAM_IL 0x1169 +#define CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT 0x116A +#define CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT 0x116B + +/* cl_program_build_info */ +#define CL_PROGRAM_BUILD_STATUS 0x1181 +#define CL_PROGRAM_BUILD_OPTIONS 0x1182 +#define CL_PROGRAM_BUILD_LOG 0x1183 +#define CL_PROGRAM_BINARY_TYPE 0x1184 +#define CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE 0x1185 + +/* cl_program_binary_type */ +#define CL_PROGRAM_BINARY_TYPE_NONE 0x0 +#define CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT 0x1 +#define CL_PROGRAM_BINARY_TYPE_LIBRARY 0x2 +#define CL_PROGRAM_BINARY_TYPE_EXECUTABLE 0x4 + +/* cl_build_status */ +#define CL_BUILD_SUCCESS 0 +#define CL_BUILD_NONE -1 +#define CL_BUILD_ERROR -2 +#define CL_BUILD_IN_PROGRESS -3 + +/* cl_kernel_info */ +#define CL_KERNEL_FUNCTION_NAME 0x1190 +#define CL_KERNEL_NUM_ARGS 0x1191 +#define CL_KERNEL_REFERENCE_COUNT 0x1192 +#define CL_KERNEL_CONTEXT 0x1193 +#define CL_KERNEL_PROGRAM 0x1194 +#define CL_KERNEL_ATTRIBUTES 0x1195 +#define CL_KERNEL_MAX_NUM_SUB_GROUPS 0x11B9 +#define CL_KERNEL_COMPILE_NUM_SUB_GROUPS 0x11BA + +/* cl_kernel_arg_info */ +#define CL_KERNEL_ARG_ADDRESS_QUALIFIER 0x1196 +#define CL_KERNEL_ARG_ACCESS_QUALIFIER 0x1197 +#define CL_KERNEL_ARG_TYPE_NAME 0x1198 +#define CL_KERNEL_ARG_TYPE_QUALIFIER 0x1199 +#define CL_KERNEL_ARG_NAME 0x119A + +/* cl_kernel_arg_address_qualifier */ +#define CL_KERNEL_ARG_ADDRESS_GLOBAL 0x119B +#define CL_KERNEL_ARG_ADDRESS_LOCAL 0x119C +#define CL_KERNEL_ARG_ADDRESS_CONSTANT 0x119D +#define CL_KERNEL_ARG_ADDRESS_PRIVATE 0x119E + +/* cl_kernel_arg_access_qualifier */ +#define CL_KERNEL_ARG_ACCESS_READ_ONLY 0x11A0 +#define CL_KERNEL_ARG_ACCESS_WRITE_ONLY 0x11A1 +#define CL_KERNEL_ARG_ACCESS_READ_WRITE 0x11A2 +#define CL_KERNEL_ARG_ACCESS_NONE 0x11A3 + +/* cl_kernel_arg_type_qualifer */ +#define CL_KERNEL_ARG_TYPE_NONE 0 +#define CL_KERNEL_ARG_TYPE_CONST (1 << 0) +#define CL_KERNEL_ARG_TYPE_RESTRICT (1 << 1) +#define CL_KERNEL_ARG_TYPE_VOLATILE (1 << 2) +#define CL_KERNEL_ARG_TYPE_PIPE (1 << 3) + +/* cl_kernel_work_group_info */ +#define CL_KERNEL_WORK_GROUP_SIZE 0x11B0 +#define CL_KERNEL_COMPILE_WORK_GROUP_SIZE 0x11B1 +#define CL_KERNEL_LOCAL_MEM_SIZE 0x11B2 +#define CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE 0x11B3 +#define CL_KERNEL_PRIVATE_MEM_SIZE 0x11B4 +#define CL_KERNEL_GLOBAL_WORK_SIZE 0x11B5 + +/* cl_kernel_sub_group_info */ +#define CL_KERNEL_MAX_SUB_GROUP_SIZE_FOR_NDRANGE 0x2033 +#define CL_KERNEL_SUB_GROUP_COUNT_FOR_NDRANGE 0x2034 +#define CL_KERNEL_LOCAL_SIZE_FOR_SUB_GROUP_COUNT 0x11B8 + +/* cl_kernel_exec_info */ +#define CL_KERNEL_EXEC_INFO_SVM_PTRS 0x11B6 +#define CL_KERNEL_EXEC_INFO_SVM_FINE_GRAIN_SYSTEM 0x11B7 + +/* cl_event_info */ +#define CL_EVENT_COMMAND_QUEUE 0x11D0 +#define CL_EVENT_COMMAND_TYPE 0x11D1 +#define CL_EVENT_REFERENCE_COUNT 0x11D2 +#define CL_EVENT_COMMAND_EXECUTION_STATUS 0x11D3 +#define CL_EVENT_CONTEXT 0x11D4 + +/* cl_command_type */ +#define CL_COMMAND_NDRANGE_KERNEL 0x11F0 +#define CL_COMMAND_TASK 0x11F1 +#define CL_COMMAND_NATIVE_KERNEL 0x11F2 +#define CL_COMMAND_READ_BUFFER 0x11F3 +#define CL_COMMAND_WRITE_BUFFER 0x11F4 +#define CL_COMMAND_COPY_BUFFER 0x11F5 +#define CL_COMMAND_READ_IMAGE 0x11F6 +#define CL_COMMAND_WRITE_IMAGE 0x11F7 +#define CL_COMMAND_COPY_IMAGE 0x11F8 +#define CL_COMMAND_COPY_IMAGE_TO_BUFFER 0x11F9 +#define CL_COMMAND_COPY_BUFFER_TO_IMAGE 0x11FA +#define CL_COMMAND_MAP_BUFFER 0x11FB +#define CL_COMMAND_MAP_IMAGE 0x11FC +#define CL_COMMAND_UNMAP_MEM_OBJECT 0x11FD +#define CL_COMMAND_MARKER 0x11FE +#define CL_COMMAND_ACQUIRE_GL_OBJECTS 0x11FF +#define CL_COMMAND_RELEASE_GL_OBJECTS 0x1200 +#define CL_COMMAND_READ_BUFFER_RECT 0x1201 +#define CL_COMMAND_WRITE_BUFFER_RECT 0x1202 +#define CL_COMMAND_COPY_BUFFER_RECT 0x1203 +#define CL_COMMAND_USER 0x1204 +#define CL_COMMAND_BARRIER 0x1205 +#define CL_COMMAND_MIGRATE_MEM_OBJECTS 0x1206 +#define CL_COMMAND_FILL_BUFFER 0x1207 +#define CL_COMMAND_FILL_IMAGE 0x1208 +#define CL_COMMAND_SVM_FREE 0x1209 +#define CL_COMMAND_SVM_MEMCPY 0x120A +#define CL_COMMAND_SVM_MEMFILL 0x120B +#define CL_COMMAND_SVM_MAP 0x120C +#define CL_COMMAND_SVM_UNMAP 0x120D + +/* command execution status */ +#define CL_COMPLETE 0x0 +#define CL_RUNNING 0x1 +#define CL_SUBMITTED 0x2 +#define CL_QUEUED 0x3 + +/* cl_buffer_create_type */ +#define CL_BUFFER_CREATE_TYPE_REGION 0x1220 + +/* cl_profiling_info */ +#define CL_PROFILING_COMMAND_QUEUED 0x1280 +#define CL_PROFILING_COMMAND_SUBMIT 0x1281 +#define CL_PROFILING_COMMAND_START 0x1282 +#define CL_PROFILING_COMMAND_END 0x1283 +#define CL_PROFILING_COMMAND_COMPLETE 0x1284 + +/********************************************************************************************************/ + +/* Platform API */ +extern CL_API_ENTRY cl_int CL_API_CALL +clGetPlatformIDs(cl_uint /* num_entries */, + cl_platform_id * /* platforms */, + cl_uint * /* num_platforms */) CL_API_SUFFIX__VERSION_1_0; + +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; + +/* Device APIs */ +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +clRetainDevice(cl_device_id /* device */) CL_API_SUFFIX__VERSION_1_2; + +extern CL_API_ENTRY cl_int CL_API_CALL +clReleaseDevice(cl_device_id /* device */) CL_API_SUFFIX__VERSION_1_2; + +extern CL_API_ENTRY cl_int CL_API_CALL +clSetDefaultDeviceCommandQueue(cl_context /* context */, + cl_device_id /* device */, + cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_2_1; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetDeviceAndHostTimer(cl_device_id /* device */, + cl_ulong* /* device_timestamp */, + cl_ulong* /* host_timestamp */) CL_API_SUFFIX__VERSION_2_1; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetHostTimer(cl_device_id /* device */, + cl_ulong* /* host_timestamp */) CL_API_SUFFIX__VERSION_2_1; + +/* Context APIs */ +extern CL_API_ENTRY cl_context CL_API_CALL +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; + +extern CL_API_ENTRY cl_context CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +clRetainContext(cl_context /* context */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clReleaseContext(cl_context /* context */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +/* Command Queue APIs */ +extern CL_API_ENTRY cl_command_queue CL_API_CALL +clCreateCommandQueueWithProperties(cl_context /* context */, + cl_device_id /* device */, + const cl_queue_properties * /* properties */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_2_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clRetainCommandQueue(cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clReleaseCommandQueue(cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +/* Memory Object APIs */ +extern CL_API_ENTRY cl_mem CL_API_CALL +clCreateBuffer(cl_context /* context */, + cl_mem_flags /* flags */, + size_t /* size */, + void * /* host_ptr */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_mem CL_API_CALL +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; + +extern CL_API_ENTRY cl_mem CL_API_CALL +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; + +extern CL_API_ENTRY cl_mem CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +clRetainMemObject(cl_mem /* memobj */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clReleaseMemObject(cl_mem /* memobj */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +clSetMemObjectDestructorCallback(cl_mem /* memobj */, + void (CL_CALLBACK * /*pfn_notify*/)( cl_mem /* memobj */, void* /*user_data*/), + void * /*user_data */ ) CL_API_SUFFIX__VERSION_1_1; + +/* SVM Allocation APIs */ +extern CL_API_ENTRY void * CL_API_CALL +clSVMAlloc(cl_context /* context */, + cl_svm_mem_flags /* flags */, + size_t /* size */, + cl_uint /* alignment */) CL_API_SUFFIX__VERSION_2_0; + +extern CL_API_ENTRY void CL_API_CALL +clSVMFree(cl_context /* context */, + void * /* svm_pointer */) CL_API_SUFFIX__VERSION_2_0; + +/* Sampler APIs */ +extern CL_API_ENTRY cl_sampler CL_API_CALL +clCreateSamplerWithProperties(cl_context /* context */, + const cl_sampler_properties * /* sampler_properties */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_2_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clRetainSampler(cl_sampler /* sampler */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clReleaseSampler(cl_sampler /* sampler */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +/* Program Object APIs */ +extern CL_API_ENTRY cl_program CL_API_CALL +clCreateProgramWithSource(cl_context /* context */, + cl_uint /* count */, + const char ** /* strings */, + const size_t * /* lengths */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_program CL_API_CALL +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; + +extern CL_API_ENTRY cl_program CL_API_CALL +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; + +extern CL_API_ENTRY cl_program CL_API_CALL +clCreateProgramWithIL(cl_context /* context */, + const void* /* il */, + size_t /* length */, + cl_int* /* errcode_ret */) CL_API_SUFFIX__VERSION_2_1; + +extern CL_API_ENTRY cl_int CL_API_CALL +clRetainProgram(cl_program /* program */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clReleaseProgram(cl_program /* program */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_program CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +clSetProgramReleaseCallback(cl_program /* program */, + void (CL_CALLBACK * /* pfn_notify */)(cl_program /* program */, void * /* user_data */), + void * /* user_data */) CL_API_SUFFIX__VERSION_2_2; + +extern CL_API_ENTRY cl_int CL_API_CALL +clSetProgramSpecializationConstant(cl_program /* program */, + cl_uint /* spec_id */, + size_t /* spec_size */, + const void* /* spec_value */) CL_API_SUFFIX__VERSION_2_2; + +extern CL_API_ENTRY cl_int CL_API_CALL +clUnloadPlatformCompiler(cl_platform_id /* platform */) CL_API_SUFFIX__VERSION_1_2; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +/* Kernel Object APIs */ +extern CL_API_ENTRY cl_kernel CL_API_CALL +clCreateKernel(cl_program /* program */, + const char * /* kernel_name */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clCreateKernelsInProgram(cl_program /* program */, + cl_uint /* num_kernels */, + cl_kernel * /* kernels */, + cl_uint * /* num_kernels_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_kernel CL_API_CALL +clCloneKernel(cl_kernel /* source_kernel */, + cl_int* /* errcode_ret */) CL_API_SUFFIX__VERSION_2_1; + +extern CL_API_ENTRY cl_int CL_API_CALL +clRetainKernel(cl_kernel /* kernel */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clReleaseKernel(cl_kernel /* kernel */) CL_API_SUFFIX__VERSION_1_0; + +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +clSetKernelArgSVMPointer(cl_kernel /* kernel */, + cl_uint /* arg_index */, + const void * /* arg_value */) CL_API_SUFFIX__VERSION_2_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetKernelSubGroupInfo(cl_kernel /* kernel */, + cl_device_id /* device */, + cl_kernel_sub_group_info /* param_name */, + size_t /* input_value_size */, + const void* /*input_value */, + size_t /* param_value_size */, + void* /* param_value */, + size_t* /* param_value_size_ret */ ) CL_API_SUFFIX__VERSION_2_1; + +/* Event Object APIs */ +extern CL_API_ENTRY cl_int CL_API_CALL +clWaitForEvents(cl_uint /* num_events */, + const cl_event * /* event_list */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_event CL_API_CALL +clCreateUserEvent(cl_context /* context */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_1; + +extern CL_API_ENTRY cl_int CL_API_CALL +clRetainEvent(cl_event /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clReleaseEvent(cl_event /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clSetUserEventStatus(cl_event /* event */, + cl_int /* execution_status */) CL_API_SUFFIX__VERSION_1_1; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +/* Profiling APIs */ +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +/* Flush and Finish APIs */ +extern CL_API_ENTRY cl_int CL_API_CALL +clFlush(cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clFinish(cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_1_0; + +/* Enqueued Commands APIs */ +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY void * CL_API_CALL +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; + +extern CL_API_ENTRY void * CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueSVMMigrateMem(cl_command_queue /* command_queue */, + cl_uint /* num_svm_pointers */, + const void** /* svm_pointers */, + const size_t* /* sizes */, + 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_2_1; + +/* Extension function access + * + * Returns the extension function address for the given function name, + * or NULL if a valid function can not be found. The client must + * check to make sure the address is not NULL, before using or + * calling the returned function address. + */ +extern CL_API_ENTRY void * CL_API_CALL +clGetExtensionFunctionAddressForPlatform(cl_platform_id /* platform */, + const char * /* func_name */) CL_API_SUFFIX__VERSION_1_2; + +#ifdef CL_USE_DEPRECATED_OPENCL_1_0_APIS + /* + * WARNING: + * This API introduces mutable state into the OpenCL implementation. It has been REMOVED + * to better facilitate thread safety. The 1.0 API is not thread safe. It is not tested by the + * OpenCL 1.1 conformance test, and consequently may not work or may not work dependably. + * It is likely to be non-performant. Use of this API is not advised. Use at your own risk. + * + * Software developers previously relying on this API are instructed to set the command queue + * properties when creating the queue, instead. + */ + extern CL_API_ENTRY cl_int CL_API_CALL + clSetCommandQueueProperty(cl_command_queue /* command_queue */, + cl_command_queue_properties /* properties */, + cl_bool /* enable */, + cl_command_queue_properties * /* old_properties */) CL_EXT_SUFFIX__VERSION_1_0_DEPRECATED; +#endif /* CL_USE_DEPRECATED_OPENCL_1_0_APIS */ + +/* Deprecated OpenCL 1.1 APIs */ +extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_mem CL_API_CALL +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 */) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; + +extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_mem CL_API_CALL +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 */) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; + +extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_int CL_API_CALL +clEnqueueMarker(cl_command_queue /* command_queue */, + cl_event * /* event */) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; + +extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_int CL_API_CALL +clEnqueueWaitForEvents(cl_command_queue /* command_queue */, + cl_uint /* num_events */, + const cl_event * /* event_list */) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; + +extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_int CL_API_CALL +clEnqueueBarrier(cl_command_queue /* command_queue */) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; + +extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_int CL_API_CALL +clUnloadCompiler(void) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; + +extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_1_DEPRECATED void * CL_API_CALL +clGetExtensionFunctionAddress(const char * /* func_name */) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; + +/* Deprecated OpenCL 1.2 APIs */ +extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_2_DEPRECATED cl_command_queue CL_API_CALL +clCreateCommandQueue(cl_context /* context */, + cl_device_id /* device */, + cl_command_queue_properties /* properties */, + cl_int * /* errcode_ret */) CL_EXT_SUFFIX__VERSION_1_2_DEPRECATED; + + +extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_2_DEPRECATED cl_sampler CL_API_CALL +clCreateSampler(cl_context /* context */, + cl_bool /* normalized_coords */, + cl_addressing_mode /* addressing_mode */, + cl_filter_mode /* filter_mode */, + cl_int * /* errcode_ret */) CL_EXT_SUFFIX__VERSION_1_2_DEPRECATED; + +extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_2_DEPRECATED cl_int CL_API_CALL +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 */) CL_EXT_SUFFIX__VERSION_1_2_DEPRECATED; + +#ifdef __cplusplus +} +#endif + +#endif /* __OPENCL_CL_H */ + diff --git a/CL/cl_gl.h b/CL/cl_gl.h new file mode 100644 index 00000000..14218d0c --- /dev/null +++ b/CL/cl_gl.h @@ -0,0 +1,173 @@ +/********************************************************************************** + * Copyright (c) 2008-2015 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and/or associated documentation files (the + * "Materials"), to deal in the Materials without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Materials, and to + * permit persons to whom the Materials are 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 Materials. + * + * MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS + * KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS + * SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT + * https://www.khronos.org/registry/ + * + * THE MATERIALS ARE 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 + * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + **********************************************************************************/ + +#ifndef __OPENCL_CL_GL_H +#define __OPENCL_CL_GL_H + +// Unlike the Khronos header file, we want to unconditonally include the +// CLIntercept cl.h, and not the system cl.h. +#if 0 +#ifdef __APPLE__ +#include +#else +#include +#endif +#else +#include "CL/cl.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef cl_uint cl_gl_object_type; +typedef cl_uint cl_gl_texture_info; +typedef cl_uint cl_gl_platform_info; +typedef struct __GLsync *cl_GLsync; + +/* cl_gl_object_type = 0x2000 - 0x200F enum values are currently taken */ +#define CL_GL_OBJECT_BUFFER 0x2000 +#define CL_GL_OBJECT_TEXTURE2D 0x2001 +#define CL_GL_OBJECT_TEXTURE3D 0x2002 +#define CL_GL_OBJECT_RENDERBUFFER 0x2003 +#define CL_GL_OBJECT_TEXTURE2D_ARRAY 0x200E +#define CL_GL_OBJECT_TEXTURE1D 0x200F +#define CL_GL_OBJECT_TEXTURE1D_ARRAY 0x2010 +#define CL_GL_OBJECT_TEXTURE_BUFFER 0x2011 + +/* cl_gl_texture_info */ +#define CL_GL_TEXTURE_TARGET 0x2004 +#define CL_GL_MIPMAP_LEVEL 0x2005 +#define CL_GL_NUM_SAMPLES 0x2012 + + +extern CL_API_ENTRY cl_mem CL_API_CALL +clCreateFromGLBuffer(cl_context /* context */, + cl_mem_flags /* flags */, + cl_GLuint /* bufobj */, + int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_mem CL_API_CALL +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; + +extern CL_API_ENTRY cl_mem CL_API_CALL +clCreateFromGLRenderbuffer(cl_context /* context */, + cl_mem_flags /* flags */, + cl_GLuint /* renderbuffer */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetGLObjectInfo(cl_mem /* memobj */, + cl_gl_object_type * /* gl_object_type */, + cl_GLuint * /* gl_object_name */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetGLTextureInfo(cl_mem /* memobj */, + cl_gl_texture_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +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 */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +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; + + +/* Deprecated OpenCL 1.1 APIs */ +extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_mem CL_API_CALL +clCreateFromGLTexture2D(cl_context /* context */, + cl_mem_flags /* flags */, + cl_GLenum /* target */, + cl_GLint /* miplevel */, + cl_GLuint /* texture */, + cl_int * /* errcode_ret */) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; + +extern CL_API_ENTRY CL_EXT_PREFIX__VERSION_1_1_DEPRECATED cl_mem CL_API_CALL +clCreateFromGLTexture3D(cl_context /* context */, + cl_mem_flags /* flags */, + cl_GLenum /* target */, + cl_GLint /* miplevel */, + cl_GLuint /* texture */, + cl_int * /* errcode_ret */) CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED; + +/* cl_khr_gl_sharing extension */ + +#define cl_khr_gl_sharing 1 + +typedef cl_uint cl_gl_context_info; + +/* Additional Error Codes */ +#define CL_INVALID_GL_SHAREGROUP_REFERENCE_KHR -1000 + +/* cl_gl_context_info */ +#define CL_CURRENT_DEVICE_FOR_GL_CONTEXT_KHR 0x2006 +#define CL_DEVICES_FOR_GL_CONTEXT_KHR 0x2007 + +/* Additional cl_context_properties */ +#define CL_GL_CONTEXT_KHR 0x2008 +#define CL_EGL_DISPLAY_KHR 0x2009 +#define CL_GLX_DISPLAY_KHR 0x200A +#define CL_WGL_HDC_KHR 0x200B +#define CL_CGL_SHAREGROUP_KHR 0x200C + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetGLContextInfoKHR(const cl_context_properties * /* properties */, + cl_gl_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_API_ENTRY cl_int (CL_API_CALL *clGetGLContextInfoKHR_fn)( + const cl_context_properties * properties, + cl_gl_context_info param_name, + size_t param_value_size, + void * param_value, + size_t * param_value_size_ret); + +#ifdef __cplusplus +} +#endif + +#endif /* __OPENCL_CL_GL_H */ diff --git a/CL/cl_platform.h b/CL/cl_platform.h new file mode 100644 index 00000000..873f837e --- /dev/null +++ b/CL/cl_platform.h @@ -0,0 +1,1395 @@ +/********************************************************************************** + * Copyright (c) 2008-2015 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and/or associated documentation files (the + * "Materials"), to deal in the Materials without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Materials, and to + * permit persons to whom the Materials are 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 Materials. + * + * MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS + * KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS + * SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT + * https://www.khronos.org/registry/ + * + * THE MATERIALS ARE 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 + * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + **********************************************************************************/ + +/* $Revision: 11803 $ on $Date: 2010-06-25 10:02:12 -0700 (Fri, 25 Jun 2010) $ */ + +#ifndef __CL_PLATFORM_H +#define __CL_PLATFORM_H + +#ifdef __APPLE__ + /* Contains #defines for AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER below */ + #include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_WIN32) + #define CL_API_ENTRY + #define CL_API_CALL __stdcall + #define CL_CALLBACK __stdcall +#else + #define CL_API_ENTRY + #define CL_API_CALL + #define CL_CALLBACK +#endif + +/* + * Deprecation flags refer to the last version of the header in which the + * feature was not deprecated. + * + * E.g. VERSION_1_1_DEPRECATED means the feature is present in 1.1 without + * deprecation but is deprecated in versions later than 1.1. + */ + +#ifdef __APPLE__ + #define CL_EXTENSION_WEAK_LINK __attribute__((weak_import)) + #define CL_API_SUFFIX__VERSION_1_0 AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER + #define CL_EXT_SUFFIX__VERSION_1_0 CL_EXTENSION_WEAK_LINK AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER + #define CL_API_SUFFIX__VERSION_1_1 AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER + #define GCL_API_SUFFIX__VERSION_1_1 AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER + #define CL_EXT_SUFFIX__VERSION_1_1 CL_EXTENSION_WEAK_LINK AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER + #define CL_EXT_SUFFIX__VERSION_1_0_DEPRECATED CL_EXTENSION_WEAK_LINK AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_7 + + #ifdef AVAILABLE_MAC_OS_X_VERSION_10_8_AND_LATER + #define CL_API_SUFFIX__VERSION_1_2 AVAILABLE_MAC_OS_X_VERSION_10_8_AND_LATER + #define GCL_API_SUFFIX__VERSION_1_2 AVAILABLE_MAC_OS_X_VERSION_10_8_AND_LATER + #define CL_EXT_SUFFIX__VERSION_1_2 CL_EXTENSION_WEAK_LINK AVAILABLE_MAC_OS_X_VERSION_10_8_AND_LATER + #define CL_EXT_PREFIX__VERSION_1_1_DEPRECATED + #define CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED CL_EXTENSION_WEAK_LINK AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_8 + #else + #warning This path should never happen outside of internal operating system development. AvailabilityMacros do not function correctly here! + #define CL_API_SUFFIX__VERSION_1_2 AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER + #define GCL_API_SUFFIX__VERSION_1_2 AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER + #define CL_EXT_SUFFIX__VERSION_1_2 CL_EXTENSION_WEAK_LINK AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER + #define CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED CL_EXTENSION_WEAK_LINK AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER + #endif + + // These aren't in the Khronos header file, but are needed to avoid + // build errors on OSX. + #define CL_API_SUFFIX__VERSION_2_0 + #define CL_EXT_SUFFIX__VERSION_2_0 + #define CL_API_SUFFIX__VERSION_2_1 + #define CL_EXT_SUFFIX__VERSION_2_1 + #define CL_API_SUFFIX__VERSION_2_2 + #define CL_EXT_SUFFIX__VERSION_2_2 + #define CL_EXT_SUFFIX__VERSION_1_2_DEPRECATED + #define CL_EXT_PREFIX__VERSION_1_2_DEPRECATED + #define CL_EXT_SUFFIX__VERSION_2_0_DEPRECATED + #define CL_EXT_PREFIX__VERSION_2_0_DEPRECATED + #define CL_EXT_SUFFIX__VERSION_2_1_DEPRECATED + #define CL_EXT_PREFIX__VERSION_2_1_DEPRECATED +#else + #define CL_EXTENSION_WEAK_LINK + #define CL_API_SUFFIX__VERSION_1_0 + #define CL_EXT_SUFFIX__VERSION_1_0 + #define CL_API_SUFFIX__VERSION_1_1 + #define CL_EXT_SUFFIX__VERSION_1_1 + #define CL_API_SUFFIX__VERSION_1_2 + #define CL_EXT_SUFFIX__VERSION_1_2 + #define CL_API_SUFFIX__VERSION_2_0 + #define CL_EXT_SUFFIX__VERSION_2_0 + #define CL_API_SUFFIX__VERSION_2_1 + #define CL_EXT_SUFFIX__VERSION_2_1 + #define CL_API_SUFFIX__VERSION_2_2 + #define CL_EXT_SUFFIX__VERSION_2_2 + + #ifdef __GNUC__ + #ifdef CL_USE_DEPRECATED_OPENCL_1_0_APIS + #define CL_EXT_SUFFIX__VERSION_1_0_DEPRECATED + #define CL_EXT_PREFIX__VERSION_1_0_DEPRECATED + #else + #define CL_EXT_SUFFIX__VERSION_1_0_DEPRECATED __attribute__((deprecated)) + #define CL_EXT_PREFIX__VERSION_1_0_DEPRECATED + #endif + + #ifdef CL_USE_DEPRECATED_OPENCL_1_1_APIS + #define CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED + #define CL_EXT_PREFIX__VERSION_1_1_DEPRECATED + #else + #define CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED __attribute__((deprecated)) + #define CL_EXT_PREFIX__VERSION_1_1_DEPRECATED + #endif + + #ifdef CL_USE_DEPRECATED_OPENCL_1_2_APIS + #define CL_EXT_SUFFIX__VERSION_1_2_DEPRECATED + #define CL_EXT_PREFIX__VERSION_1_2_DEPRECATED + #else + #define CL_EXT_SUFFIX__VERSION_1_2_DEPRECATED __attribute__((deprecated)) + #define CL_EXT_PREFIX__VERSION_1_2_DEPRECATED + #endif + + #ifdef CL_USE_DEPRECATED_OPENCL_2_0_APIS + #define CL_EXT_SUFFIX__VERSION_2_0_DEPRECATED + #define CL_EXT_PREFIX__VERSION_2_0_DEPRECATED + #else + #define CL_EXT_SUFFIX__VERSION_2_0_DEPRECATED __attribute__((deprecated)) + #define CL_EXT_PREFIX__VERSION_2_0_DEPRECATED + #endif + + #ifdef CL_USE_DEPRECATED_OPENCL_2_1_APIS + #define CL_EXT_SUFFIX__VERSION_2_1_DEPRECATED + #define CL_EXT_PREFIX__VERSION_2_1_DEPRECATED + #else + #define CL_EXT_SUFFIX__VERSION_2_1_DEPRECATED __attribute__((deprecated)) + #define CL_EXT_PREFIX__VERSION_2_1_DEPRECATED + #endif + #elif defined(_WIN32) + #ifdef CL_USE_DEPRECATED_OPENCL_1_0_APIS + #define CL_EXT_SUFFIX__VERSION_1_0_DEPRECATED + #define CL_EXT_PREFIX__VERSION_1_0_DEPRECATED + #else + #define CL_EXT_SUFFIX__VERSION_1_0_DEPRECATED + #define CL_EXT_PREFIX__VERSION_1_0_DEPRECATED __declspec(deprecated) + #endif + + #ifdef CL_USE_DEPRECATED_OPENCL_1_1_APIS + #define CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED + #define CL_EXT_PREFIX__VERSION_1_1_DEPRECATED + #else + #define CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED + #define CL_EXT_PREFIX__VERSION_1_1_DEPRECATED __declspec(deprecated) + #endif + + #ifdef CL_USE_DEPRECATED_OPENCL_1_2_APIS + #define CL_EXT_SUFFIX__VERSION_1_2_DEPRECATED + #define CL_EXT_PREFIX__VERSION_1_2_DEPRECATED + #else + #define CL_EXT_SUFFIX__VERSION_1_2_DEPRECATED + #define CL_EXT_PREFIX__VERSION_1_2_DEPRECATED __declspec(deprecated) + #endif + + #ifdef CL_USE_DEPRECATED_OPENCL_2_0_APIS + #define CL_EXT_SUFFIX__VERSION_2_0_DEPRECATED + #define CL_EXT_PREFIX__VERSION_2_0_DEPRECATED + #else + #define CL_EXT_SUFFIX__VERSION_2_0_DEPRECATED + #define CL_EXT_PREFIX__VERSION_2_0_DEPRECATED __declspec(deprecated) + #endif + + #ifdef CL_USE_DEPRECATED_OPENCL_2_1_APIS + #define CL_EXT_SUFFIX__VERSION_2_1_DEPRECATED + #define CL_EXT_PREFIX__VERSION_2_1_DEPRECATED + #else + #define CL_EXT_SUFFIX__VERSION_2_1_DEPRECATED + #define CL_EXT_PREFIX__VERSION_2_1_DEPRECATED __declspec(deprecated) + #endif + #else + #define CL_EXT_SUFFIX__VERSION_1_0_DEPRECATED + #define CL_EXT_PREFIX__VERSION_1_0_DEPRECATED + + #define CL_EXT_SUFFIX__VERSION_1_1_DEPRECATED + #define CL_EXT_PREFIX__VERSION_1_1_DEPRECATED + + #define CL_EXT_SUFFIX__VERSION_1_2_DEPRECATED + #define CL_EXT_PREFIX__VERSION_1_2_DEPRECATED + + #define CL_EXT_SUFFIX__VERSION_2_0_DEPRECATED + #define CL_EXT_PREFIX__VERSION_2_0_DEPRECATED + + #define CL_EXT_SUFFIX__VERSION_2_1_DEPRECATED + #define CL_EXT_PREFIX__VERSION_2_1_DEPRECATED + #endif +#endif + +#if (defined (_WIN32) && defined(_MSC_VER)) + +/* scalar types */ +typedef signed __int8 cl_char; +typedef unsigned __int8 cl_uchar; +typedef signed __int16 cl_short; +typedef unsigned __int16 cl_ushort; +typedef signed __int32 cl_int; +typedef unsigned __int32 cl_uint; +typedef signed __int64 cl_long; +typedef unsigned __int64 cl_ulong; + +typedef unsigned __int16 cl_half; +typedef float cl_float; +typedef double cl_double; + +/* Macro names and corresponding values defined by OpenCL */ +#define CL_CHAR_BIT 8 +#define CL_SCHAR_MAX 127 +#define CL_SCHAR_MIN (-127-1) +#define CL_CHAR_MAX CL_SCHAR_MAX +#define CL_CHAR_MIN CL_SCHAR_MIN +#define CL_UCHAR_MAX 255 +#define CL_SHRT_MAX 32767 +#define CL_SHRT_MIN (-32767-1) +#define CL_USHRT_MAX 65535 +#define CL_INT_MAX 2147483647 +#define CL_INT_MIN (-2147483647-1) +#define CL_UINT_MAX 0xffffffffU +#define CL_LONG_MAX ((cl_long) 0x7FFFFFFFFFFFFFFFLL) +#define CL_LONG_MIN ((cl_long) -0x7FFFFFFFFFFFFFFFLL - 1LL) +#define CL_ULONG_MAX ((cl_ulong) 0xFFFFFFFFFFFFFFFFULL) + +#define CL_FLT_DIG 6 +#define CL_FLT_MANT_DIG 24 +#define CL_FLT_MAX_10_EXP +38 +#define CL_FLT_MAX_EXP +128 +#define CL_FLT_MIN_10_EXP -37 +#define CL_FLT_MIN_EXP -125 +#define CL_FLT_RADIX 2 +#define CL_FLT_MAX 340282346638528859811704183484516925440.0f +#define CL_FLT_MIN 1.175494350822287507969e-38f +#define CL_FLT_EPSILON 1.1920928955078125e-7f + +#define CL_HALF_DIG 3 +#define CL_HALF_MANT_DIG 11 +#define CL_HALF_MAX_10_EXP +4 +#define CL_HALF_MAX_EXP +16 +#define CL_HALF_MIN_10_EXP -4 +#define CL_HALF_MIN_EXP -13 +#define CL_HALF_RADIX 2 +#define CL_HALF_MAX 65504.0f +#define CL_HALF_MIN 6.103515625e-05f +#define CL_HALF_EPSILON 9.765625e-04f + +#define CL_DBL_DIG 15 +#define CL_DBL_MANT_DIG 53 +#define CL_DBL_MAX_10_EXP +308 +#define CL_DBL_MAX_EXP +1024 +#define CL_DBL_MIN_10_EXP -307 +#define CL_DBL_MIN_EXP -1021 +#define CL_DBL_RADIX 2 +#define CL_DBL_MAX 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0 +#define CL_DBL_MIN 2.225073858507201383090e-308 +#define CL_DBL_EPSILON 2.220446049250313080847e-16 + +#define CL_M_E 2.718281828459045090796 +#define CL_M_LOG2E 1.442695040888963387005 +#define CL_M_LOG10E 0.434294481903251816668 +#define CL_M_LN2 0.693147180559945286227 +#define CL_M_LN10 2.302585092994045901094 +#define CL_M_PI 3.141592653589793115998 +#define CL_M_PI_2 1.570796326794896557999 +#define CL_M_PI_4 0.785398163397448278999 +#define CL_M_1_PI 0.318309886183790691216 +#define CL_M_2_PI 0.636619772367581382433 +#define CL_M_2_SQRTPI 1.128379167095512558561 +#define CL_M_SQRT2 1.414213562373095145475 +#define CL_M_SQRT1_2 0.707106781186547572737 + +#define CL_M_E_F 2.71828174591064f +#define CL_M_LOG2E_F 1.44269502162933f +#define CL_M_LOG10E_F 0.43429449200630f +#define CL_M_LN2_F 0.69314718246460f +#define CL_M_LN10_F 2.30258512496948f +#define CL_M_PI_F 3.14159274101257f +#define CL_M_PI_2_F 1.57079637050629f +#define CL_M_PI_4_F 0.78539818525314f +#define CL_M_1_PI_F 0.31830987334251f +#define CL_M_2_PI_F 0.63661974668503f +#define CL_M_2_SQRTPI_F 1.12837922573090f +#define CL_M_SQRT2_F 1.41421353816986f +#define CL_M_SQRT1_2_F 0.70710676908493f + +#define CL_NAN (CL_INFINITY - CL_INFINITY) +#define CL_HUGE_VALF ((cl_float) 1e50) +#define CL_HUGE_VAL ((cl_double) 1e500) +#define CL_MAXFLOAT CL_FLT_MAX +#define CL_INFINITY CL_HUGE_VALF + +#else + +#include + +/* scalar types */ +typedef int8_t cl_char; +typedef uint8_t cl_uchar; +typedef int16_t cl_short __attribute__((aligned(2))); +typedef uint16_t cl_ushort __attribute__((aligned(2))); +typedef int32_t cl_int __attribute__((aligned(4))); +typedef uint32_t cl_uint __attribute__((aligned(4))); +typedef int64_t cl_long __attribute__((aligned(8))); +typedef uint64_t cl_ulong __attribute__((aligned(8))); + +typedef uint16_t cl_half __attribute__((aligned(2))); +typedef float cl_float __attribute__((aligned(4))); +typedef double cl_double __attribute__((aligned(8))); + +/* Macro names and corresponding values defined by OpenCL */ +#define CL_CHAR_BIT 8 +#define CL_SCHAR_MAX 127 +#define CL_SCHAR_MIN (-127-1) +#define CL_CHAR_MAX CL_SCHAR_MAX +#define CL_CHAR_MIN CL_SCHAR_MIN +#define CL_UCHAR_MAX 255 +#define CL_SHRT_MAX 32767 +#define CL_SHRT_MIN (-32767-1) +#define CL_USHRT_MAX 65535 +#define CL_INT_MAX 2147483647 +#define CL_INT_MIN (-2147483647-1) +#define CL_UINT_MAX 0xffffffffU +#define CL_LONG_MAX ((cl_long) 0x7FFFFFFFFFFFFFFFLL) +#define CL_LONG_MIN ((cl_long) -0x7FFFFFFFFFFFFFFFLL - 1LL) +#define CL_ULONG_MAX ((cl_ulong) 0xFFFFFFFFFFFFFFFFULL) + +#define CL_FLT_DIG 6 +#define CL_FLT_MANT_DIG 24 +#define CL_FLT_MAX_10_EXP +38 +#define CL_FLT_MAX_EXP +128 +#define CL_FLT_MIN_10_EXP -37 +#define CL_FLT_MIN_EXP -125 +#define CL_FLT_RADIX 2 +#define CL_FLT_MAX 340282346638528859811704183484516925440.0f +#define CL_FLT_MIN 1.175494350822287507969e-38f +#define CL_FLT_EPSILON 1.1920928955078125e-7f + +#define CL_HALF_DIG 3 +#define CL_HALF_MANT_DIG 11 +#define CL_HALF_MAX_10_EXP +4 +#define CL_HALF_MAX_EXP +16 +#define CL_HALF_MIN_10_EXP -4 +#define CL_HALF_MIN_EXP -13 +#define CL_HALF_RADIX 2 +#define CL_HALF_MAX 65504.0f +#define CL_HALF_MIN 6.103515625e-05f +#define CL_HALF_EPSILON 9.765625e-04f + +#define CL_DBL_DIG 15 +#define CL_DBL_MANT_DIG 53 +#define CL_DBL_MAX_10_EXP +308 +#define CL_DBL_MAX_EXP +1024 +#define CL_DBL_MIN_10_EXP -307 +#define CL_DBL_MIN_EXP -1021 +#define CL_DBL_RADIX 2 +#define CL_DBL_MAX 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0 +#define CL_DBL_MIN 2.225073858507201383090e-308 +#define CL_DBL_EPSILON 2.220446049250313080847e-16 + +#define CL_M_E 2.718281828459045090796 +#define CL_M_LOG2E 1.442695040888963387005 +#define CL_M_LOG10E 0.434294481903251816668 +#define CL_M_LN2 0.693147180559945286227 +#define CL_M_LN10 2.302585092994045901094 +#define CL_M_PI 3.141592653589793115998 +#define CL_M_PI_2 1.570796326794896557999 +#define CL_M_PI_4 0.785398163397448278999 +#define CL_M_1_PI 0.318309886183790691216 +#define CL_M_2_PI 0.636619772367581382433 +#define CL_M_2_SQRTPI 1.128379167095512558561 +#define CL_M_SQRT2 1.414213562373095145475 +#define CL_M_SQRT1_2 0.707106781186547572737 + +#define CL_M_E_F 2.71828174591064f +#define CL_M_LOG2E_F 1.44269502162933f +#define CL_M_LOG10E_F 0.43429449200630f +#define CL_M_LN2_F 0.69314718246460f +#define CL_M_LN10_F 2.30258512496948f +#define CL_M_PI_F 3.14159274101257f +#define CL_M_PI_2_F 1.57079637050629f +#define CL_M_PI_4_F 0.78539818525314f +#define CL_M_1_PI_F 0.31830987334251f +#define CL_M_2_PI_F 0.63661974668503f +#define CL_M_2_SQRTPI_F 1.12837922573090f +#define CL_M_SQRT2_F 1.41421353816986f +#define CL_M_SQRT1_2_F 0.70710676908493f + +#if defined( __GNUC__ ) + #define CL_HUGE_VALF __builtin_huge_valf() + #define CL_HUGE_VAL __builtin_huge_val() + #define CL_NAN __builtin_nanf( "" ) +#else + #define CL_HUGE_VALF ((cl_float) 1e50) + #define CL_HUGE_VAL ((cl_double) 1e500) + float nanf( const char * ); + #define CL_NAN nanf( "" ) +#endif +#define CL_MAXFLOAT CL_FLT_MAX +#define CL_INFINITY CL_HUGE_VALF + +#endif + +#include + +/* Mirror types to GL types. Mirror types allow us to avoid deciding which headers to load based on whether we are using GL or GLES here. */ +typedef unsigned int cl_GLuint; +typedef int cl_GLint; +typedef unsigned int cl_GLenum; + +/* + * Vector types + * + * Note: OpenCL requires that all types be naturally aligned. + * This means that vector types must be naturally aligned. + * For example, a vector of four floats must be aligned to + * a 16 byte boundary (calculated as 4 * the natural 4-byte + * alignment of the float). The alignment qualifiers here + * will only function properly if your compiler supports them + * and if you don't actively work to defeat them. For example, + * in order for a cl_float4 to be 16 byte aligned in a struct, + * the start of the struct must itself be 16-byte aligned. + * + * Maintaining proper alignment is the user's responsibility. + */ + +/* Define basic vector types */ +#if defined( __VEC__ ) + #include /* may be omitted depending on compiler. AltiVec spec provides no way to detect whether the header is required. */ + typedef vector unsigned char __cl_uchar16; + typedef vector signed char __cl_char16; + typedef vector unsigned short __cl_ushort8; + typedef vector signed short __cl_short8; + typedef vector unsigned int __cl_uint4; + typedef vector signed int __cl_int4; + typedef vector float __cl_float4; + #define __CL_UCHAR16__ 1 + #define __CL_CHAR16__ 1 + #define __CL_USHORT8__ 1 + #define __CL_SHORT8__ 1 + #define __CL_UINT4__ 1 + #define __CL_INT4__ 1 + #define __CL_FLOAT4__ 1 +#endif + +#if defined( __SSE__ ) + #if defined( __MINGW64__ ) + #include + #else + #include + #endif + #if defined( __GNUC__ ) + typedef float __cl_float4 __attribute__((vector_size(16))); + #else + typedef __m128 __cl_float4; + #endif + #define __CL_FLOAT4__ 1 +#endif + +#if defined( __SSE2__ ) + #if defined( __MINGW64__ ) + #include + #else + #include + #endif + #if defined( __GNUC__ ) + typedef cl_uchar __cl_uchar16 __attribute__((vector_size(16))); + typedef cl_char __cl_char16 __attribute__((vector_size(16))); + typedef cl_ushort __cl_ushort8 __attribute__((vector_size(16))); + typedef cl_short __cl_short8 __attribute__((vector_size(16))); + typedef cl_uint __cl_uint4 __attribute__((vector_size(16))); + typedef cl_int __cl_int4 __attribute__((vector_size(16))); + typedef cl_ulong __cl_ulong2 __attribute__((vector_size(16))); + typedef cl_long __cl_long2 __attribute__((vector_size(16))); + typedef cl_double __cl_double2 __attribute__((vector_size(16))); + #else + typedef __m128i __cl_uchar16; + typedef __m128i __cl_char16; + typedef __m128i __cl_ushort8; + typedef __m128i __cl_short8; + typedef __m128i __cl_uint4; + typedef __m128i __cl_int4; + typedef __m128i __cl_ulong2; + typedef __m128i __cl_long2; + typedef __m128d __cl_double2; + #endif + #define __CL_UCHAR16__ 1 + #define __CL_CHAR16__ 1 + #define __CL_USHORT8__ 1 + #define __CL_SHORT8__ 1 + #define __CL_INT4__ 1 + #define __CL_UINT4__ 1 + #define __CL_ULONG2__ 1 + #define __CL_LONG2__ 1 + #define __CL_DOUBLE2__ 1 +#endif + +#if defined( __MMX__ ) + #include + #if defined( __GNUC__ ) + typedef cl_uchar __cl_uchar8 __attribute__((vector_size(8))); + typedef cl_char __cl_char8 __attribute__((vector_size(8))); + typedef cl_ushort __cl_ushort4 __attribute__((vector_size(8))); + typedef cl_short __cl_short4 __attribute__((vector_size(8))); + typedef cl_uint __cl_uint2 __attribute__((vector_size(8))); + typedef cl_int __cl_int2 __attribute__((vector_size(8))); + typedef cl_ulong __cl_ulong1 __attribute__((vector_size(8))); + typedef cl_long __cl_long1 __attribute__((vector_size(8))); + typedef cl_float __cl_float2 __attribute__((vector_size(8))); + #else + typedef __m64 __cl_uchar8; + typedef __m64 __cl_char8; + typedef __m64 __cl_ushort4; + typedef __m64 __cl_short4; + typedef __m64 __cl_uint2; + typedef __m64 __cl_int2; + typedef __m64 __cl_ulong1; + typedef __m64 __cl_long1; + typedef __m64 __cl_float2; + #endif + #define __CL_UCHAR8__ 1 + #define __CL_CHAR8__ 1 + #define __CL_USHORT4__ 1 + #define __CL_SHORT4__ 1 + #define __CL_INT2__ 1 + #define __CL_UINT2__ 1 + #define __CL_ULONG1__ 1 + #define __CL_LONG1__ 1 + #define __CL_FLOAT2__ 1 +#endif + +#if defined( __AVX__ ) + #if defined( __MINGW64__ ) + #include + #else + #include + #endif + #if defined( __GNUC__ ) + typedef cl_float __cl_float8 __attribute__((vector_size(32))); + typedef cl_double __cl_double4 __attribute__((vector_size(32))); + #else + typedef __m256 __cl_float8; + typedef __m256d __cl_double4; + #endif + #define __CL_FLOAT8__ 1 + #define __CL_DOUBLE4__ 1 +#endif + +/* Define capabilities for anonymous struct members. */ +#if defined( __GNUC__) && ! defined( __STRICT_ANSI__ ) +#define __CL_HAS_ANON_STRUCT__ 1 +#define __CL_ANON_STRUCT__ __extension__ +#elif defined( _WIN32) && defined(_MSC_VER) + #if _MSC_VER >= 1500 + /* Microsoft Developer Studio 2008 supports anonymous structs, but + * complains by default. */ + #define __CL_HAS_ANON_STRUCT__ 1 + #define __CL_ANON_STRUCT__ + /* Disable warning C4201: nonstandard extension used : nameless + * struct/union */ + #pragma warning( push ) + #pragma warning( disable : 4201 ) + #endif +#else +#define __CL_HAS_ANON_STRUCT__ 0 +#define __CL_ANON_STRUCT__ +#endif + +/* Define alignment keys */ +#if defined( __GNUC__ ) + #define CL_ALIGNED(_x) __attribute__ ((aligned(_x))) +#elif defined( _WIN32) && (_MSC_VER) + /* Alignment keys neutered on windows because MSVC can't swallow function arguments with alignment requirements */ + /* http://msdn.microsoft.com/en-us/library/373ak2y1%28VS.71%29.aspx */ + /* #include */ + /* #define CL_ALIGNED(_x) _CRT_ALIGN(_x) */ + #define CL_ALIGNED(_x) +#else + #warning Need to implement some method to align data here + #define CL_ALIGNED(_x) +#endif + +/* Indicate whether .xyzw, .s0123 and .hi.lo are supported */ +#if __CL_HAS_ANON_STRUCT__ + /* .xyzw and .s0123...{f|F} are supported */ + #define CL_HAS_NAMED_VECTOR_FIELDS 1 + /* .hi and .lo are supported */ + #define CL_HAS_HI_LO_VECTOR_FIELDS 1 +#endif + +/* Define cl_vector types */ + +/* ---- cl_charn ---- */ +typedef union +{ + cl_char CL_ALIGNED(2) s[2]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_char x, y; }; + __CL_ANON_STRUCT__ struct{ cl_char s0, s1; }; + __CL_ANON_STRUCT__ struct{ cl_char lo, hi; }; +#endif +#if defined( __CL_CHAR2__) + __cl_char2 v2; +#endif +}cl_char2; + +typedef union +{ + cl_char CL_ALIGNED(4) s[4]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_char x, y, z, w; }; + __CL_ANON_STRUCT__ struct{ cl_char s0, s1, s2, s3; }; + __CL_ANON_STRUCT__ struct{ cl_char2 lo, hi; }; +#endif +#if defined( __CL_CHAR2__) + __cl_char2 v2[2]; +#endif +#if defined( __CL_CHAR4__) + __cl_char4 v4; +#endif +}cl_char4; + +/* cl_char3 is identical in size, alignment and behavior to cl_char4. See section 6.1.5. */ +typedef cl_char4 cl_char3; + +typedef union +{ + cl_char CL_ALIGNED(8) s[8]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_char x, y, z, w; }; + __CL_ANON_STRUCT__ struct{ cl_char s0, s1, s2, s3, s4, s5, s6, s7; }; + __CL_ANON_STRUCT__ struct{ cl_char4 lo, hi; }; +#endif +#if defined( __CL_CHAR2__) + __cl_char2 v2[4]; +#endif +#if defined( __CL_CHAR4__) + __cl_char4 v4[2]; +#endif +#if defined( __CL_CHAR8__ ) + __cl_char8 v8; +#endif +}cl_char8; + +typedef union +{ + cl_char CL_ALIGNED(16) s[16]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_char x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; + __CL_ANON_STRUCT__ struct{ cl_char s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; + __CL_ANON_STRUCT__ struct{ cl_char8 lo, hi; }; +#endif +#if defined( __CL_CHAR2__) + __cl_char2 v2[8]; +#endif +#if defined( __CL_CHAR4__) + __cl_char4 v4[4]; +#endif +#if defined( __CL_CHAR8__ ) + __cl_char8 v8[2]; +#endif +#if defined( __CL_CHAR16__ ) + __cl_char16 v16; +#endif +}cl_char16; + + +/* ---- cl_ucharn ---- */ +typedef union +{ + cl_uchar CL_ALIGNED(2) s[2]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_uchar x, y; }; + __CL_ANON_STRUCT__ struct{ cl_uchar s0, s1; }; + __CL_ANON_STRUCT__ struct{ cl_uchar lo, hi; }; +#endif +#if defined( __cl_uchar2__) + __cl_uchar2 v2; +#endif +}cl_uchar2; + +typedef union +{ + cl_uchar CL_ALIGNED(4) s[4]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_uchar x, y, z, w; }; + __CL_ANON_STRUCT__ struct{ cl_uchar s0, s1, s2, s3; }; + __CL_ANON_STRUCT__ struct{ cl_uchar2 lo, hi; }; +#endif +#if defined( __CL_UCHAR2__) + __cl_uchar2 v2[2]; +#endif +#if defined( __CL_UCHAR4__) + __cl_uchar4 v4; +#endif +}cl_uchar4; + +/* cl_uchar3 is identical in size, alignment and behavior to cl_uchar4. See section 6.1.5. */ +typedef cl_uchar4 cl_uchar3; + +typedef union +{ + cl_uchar CL_ALIGNED(8) s[8]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_uchar x, y, z, w; }; + __CL_ANON_STRUCT__ struct{ cl_uchar s0, s1, s2, s3, s4, s5, s6, s7; }; + __CL_ANON_STRUCT__ struct{ cl_uchar4 lo, hi; }; +#endif +#if defined( __CL_UCHAR2__) + __cl_uchar2 v2[4]; +#endif +#if defined( __CL_UCHAR4__) + __cl_uchar4 v4[2]; +#endif +#if defined( __CL_UCHAR8__ ) + __cl_uchar8 v8; +#endif +}cl_uchar8; + +typedef union +{ + cl_uchar CL_ALIGNED(16) s[16]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_uchar x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; + __CL_ANON_STRUCT__ struct{ cl_uchar s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; + __CL_ANON_STRUCT__ struct{ cl_uchar8 lo, hi; }; +#endif +#if defined( __CL_UCHAR2__) + __cl_uchar2 v2[8]; +#endif +#if defined( __CL_UCHAR4__) + __cl_uchar4 v4[4]; +#endif +#if defined( __CL_UCHAR8__ ) + __cl_uchar8 v8[2]; +#endif +#if defined( __CL_UCHAR16__ ) + __cl_uchar16 v16; +#endif +}cl_uchar16; + + +/* ---- cl_shortn ---- */ +typedef union +{ + cl_short CL_ALIGNED(4) s[2]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_short x, y; }; + __CL_ANON_STRUCT__ struct{ cl_short s0, s1; }; + __CL_ANON_STRUCT__ struct{ cl_short lo, hi; }; +#endif +#if defined( __CL_SHORT2__) + __cl_short2 v2; +#endif +}cl_short2; + +typedef union +{ + cl_short CL_ALIGNED(8) s[4]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_short x, y, z, w; }; + __CL_ANON_STRUCT__ struct{ cl_short s0, s1, s2, s3; }; + __CL_ANON_STRUCT__ struct{ cl_short2 lo, hi; }; +#endif +#if defined( __CL_SHORT2__) + __cl_short2 v2[2]; +#endif +#if defined( __CL_SHORT4__) + __cl_short4 v4; +#endif +}cl_short4; + +/* cl_short3 is identical in size, alignment and behavior to cl_short4. See section 6.1.5. */ +typedef cl_short4 cl_short3; + +typedef union +{ + cl_short CL_ALIGNED(16) s[8]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_short x, y, z, w; }; + __CL_ANON_STRUCT__ struct{ cl_short s0, s1, s2, s3, s4, s5, s6, s7; }; + __CL_ANON_STRUCT__ struct{ cl_short4 lo, hi; }; +#endif +#if defined( __CL_SHORT2__) + __cl_short2 v2[4]; +#endif +#if defined( __CL_SHORT4__) + __cl_short4 v4[2]; +#endif +#if defined( __CL_SHORT8__ ) + __cl_short8 v8; +#endif +}cl_short8; + +typedef union +{ + cl_short CL_ALIGNED(32) s[16]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_short x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; + __CL_ANON_STRUCT__ struct{ cl_short s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; + __CL_ANON_STRUCT__ struct{ cl_short8 lo, hi; }; +#endif +#if defined( __CL_SHORT2__) + __cl_short2 v2[8]; +#endif +#if defined( __CL_SHORT4__) + __cl_short4 v4[4]; +#endif +#if defined( __CL_SHORT8__ ) + __cl_short8 v8[2]; +#endif +#if defined( __CL_SHORT16__ ) + __cl_short16 v16; +#endif +}cl_short16; + + +/* ---- cl_ushortn ---- */ +typedef union +{ + cl_ushort CL_ALIGNED(4) s[2]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_ushort x, y; }; + __CL_ANON_STRUCT__ struct{ cl_ushort s0, s1; }; + __CL_ANON_STRUCT__ struct{ cl_ushort lo, hi; }; +#endif +#if defined( __CL_USHORT2__) + __cl_ushort2 v2; +#endif +}cl_ushort2; + +typedef union +{ + cl_ushort CL_ALIGNED(8) s[4]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_ushort x, y, z, w; }; + __CL_ANON_STRUCT__ struct{ cl_ushort s0, s1, s2, s3; }; + __CL_ANON_STRUCT__ struct{ cl_ushort2 lo, hi; }; +#endif +#if defined( __CL_USHORT2__) + __cl_ushort2 v2[2]; +#endif +#if defined( __CL_USHORT4__) + __cl_ushort4 v4; +#endif +}cl_ushort4; + +/* cl_ushort3 is identical in size, alignment and behavior to cl_ushort4. See section 6.1.5. */ +typedef cl_ushort4 cl_ushort3; + +typedef union +{ + cl_ushort CL_ALIGNED(16) s[8]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_ushort x, y, z, w; }; + __CL_ANON_STRUCT__ struct{ cl_ushort s0, s1, s2, s3, s4, s5, s6, s7; }; + __CL_ANON_STRUCT__ struct{ cl_ushort4 lo, hi; }; +#endif +#if defined( __CL_USHORT2__) + __cl_ushort2 v2[4]; +#endif +#if defined( __CL_USHORT4__) + __cl_ushort4 v4[2]; +#endif +#if defined( __CL_USHORT8__ ) + __cl_ushort8 v8; +#endif +}cl_ushort8; + +typedef union +{ + cl_ushort CL_ALIGNED(32) s[16]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_ushort x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; + __CL_ANON_STRUCT__ struct{ cl_ushort s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; + __CL_ANON_STRUCT__ struct{ cl_ushort8 lo, hi; }; +#endif +#if defined( __CL_USHORT2__) + __cl_ushort2 v2[8]; +#endif +#if defined( __CL_USHORT4__) + __cl_ushort4 v4[4]; +#endif +#if defined( __CL_USHORT8__ ) + __cl_ushort8 v8[2]; +#endif +#if defined( __CL_USHORT16__ ) + __cl_ushort16 v16; +#endif +}cl_ushort16; + +/* ---- cl_intn ---- */ +typedef union +{ + cl_int CL_ALIGNED(8) s[2]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_int x, y; }; + __CL_ANON_STRUCT__ struct{ cl_int s0, s1; }; + __CL_ANON_STRUCT__ struct{ cl_int lo, hi; }; +#endif +#if defined( __CL_INT2__) + __cl_int2 v2; +#endif +}cl_int2; + +typedef union +{ + cl_int CL_ALIGNED(16) s[4]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_int x, y, z, w; }; + __CL_ANON_STRUCT__ struct{ cl_int s0, s1, s2, s3; }; + __CL_ANON_STRUCT__ struct{ cl_int2 lo, hi; }; +#endif +#if defined( __CL_INT2__) + __cl_int2 v2[2]; +#endif +#if defined( __CL_INT4__) + __cl_int4 v4; +#endif +}cl_int4; + +/* cl_int3 is identical in size, alignment and behavior to cl_int4. See section 6.1.5. */ +typedef cl_int4 cl_int3; + +typedef union +{ + cl_int CL_ALIGNED(32) s[8]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_int x, y, z, w; }; + __CL_ANON_STRUCT__ struct{ cl_int s0, s1, s2, s3, s4, s5, s6, s7; }; + __CL_ANON_STRUCT__ struct{ cl_int4 lo, hi; }; +#endif +#if defined( __CL_INT2__) + __cl_int2 v2[4]; +#endif +#if defined( __CL_INT4__) + __cl_int4 v4[2]; +#endif +#if defined( __CL_INT8__ ) + __cl_int8 v8; +#endif +}cl_int8; + +typedef union +{ + cl_int CL_ALIGNED(64) s[16]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_int x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; + __CL_ANON_STRUCT__ struct{ cl_int s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; + __CL_ANON_STRUCT__ struct{ cl_int8 lo, hi; }; +#endif +#if defined( __CL_INT2__) + __cl_int2 v2[8]; +#endif +#if defined( __CL_INT4__) + __cl_int4 v4[4]; +#endif +#if defined( __CL_INT8__ ) + __cl_int8 v8[2]; +#endif +#if defined( __CL_INT16__ ) + __cl_int16 v16; +#endif +}cl_int16; + + +/* ---- cl_uintn ---- */ +typedef union +{ + cl_uint CL_ALIGNED(8) s[2]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_uint x, y; }; + __CL_ANON_STRUCT__ struct{ cl_uint s0, s1; }; + __CL_ANON_STRUCT__ struct{ cl_uint lo, hi; }; +#endif +#if defined( __CL_UINT2__) + __cl_uint2 v2; +#endif +}cl_uint2; + +typedef union +{ + cl_uint CL_ALIGNED(16) s[4]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_uint x, y, z, w; }; + __CL_ANON_STRUCT__ struct{ cl_uint s0, s1, s2, s3; }; + __CL_ANON_STRUCT__ struct{ cl_uint2 lo, hi; }; +#endif +#if defined( __CL_UINT2__) + __cl_uint2 v2[2]; +#endif +#if defined( __CL_UINT4__) + __cl_uint4 v4; +#endif +}cl_uint4; + +/* cl_uint3 is identical in size, alignment and behavior to cl_uint4. See section 6.1.5. */ +typedef cl_uint4 cl_uint3; + +typedef union +{ + cl_uint CL_ALIGNED(32) s[8]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_uint x, y, z, w; }; + __CL_ANON_STRUCT__ struct{ cl_uint s0, s1, s2, s3, s4, s5, s6, s7; }; + __CL_ANON_STRUCT__ struct{ cl_uint4 lo, hi; }; +#endif +#if defined( __CL_UINT2__) + __cl_uint2 v2[4]; +#endif +#if defined( __CL_UINT4__) + __cl_uint4 v4[2]; +#endif +#if defined( __CL_UINT8__ ) + __cl_uint8 v8; +#endif +}cl_uint8; + +typedef union +{ + cl_uint CL_ALIGNED(64) s[16]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_uint x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; + __CL_ANON_STRUCT__ struct{ cl_uint s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; + __CL_ANON_STRUCT__ struct{ cl_uint8 lo, hi; }; +#endif +#if defined( __CL_UINT2__) + __cl_uint2 v2[8]; +#endif +#if defined( __CL_UINT4__) + __cl_uint4 v4[4]; +#endif +#if defined( __CL_UINT8__ ) + __cl_uint8 v8[2]; +#endif +#if defined( __CL_UINT16__ ) + __cl_uint16 v16; +#endif +}cl_uint16; + +/* ---- cl_longn ---- */ +typedef union +{ + cl_long CL_ALIGNED(16) s[2]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_long x, y; }; + __CL_ANON_STRUCT__ struct{ cl_long s0, s1; }; + __CL_ANON_STRUCT__ struct{ cl_long lo, hi; }; +#endif +#if defined( __CL_LONG2__) + __cl_long2 v2; +#endif +}cl_long2; + +typedef union +{ + cl_long CL_ALIGNED(32) s[4]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_long x, y, z, w; }; + __CL_ANON_STRUCT__ struct{ cl_long s0, s1, s2, s3; }; + __CL_ANON_STRUCT__ struct{ cl_long2 lo, hi; }; +#endif +#if defined( __CL_LONG2__) + __cl_long2 v2[2]; +#endif +#if defined( __CL_LONG4__) + __cl_long4 v4; +#endif +}cl_long4; + +/* cl_long3 is identical in size, alignment and behavior to cl_long4. See section 6.1.5. */ +typedef cl_long4 cl_long3; + +typedef union +{ + cl_long CL_ALIGNED(64) s[8]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_long x, y, z, w; }; + __CL_ANON_STRUCT__ struct{ cl_long s0, s1, s2, s3, s4, s5, s6, s7; }; + __CL_ANON_STRUCT__ struct{ cl_long4 lo, hi; }; +#endif +#if defined( __CL_LONG2__) + __cl_long2 v2[4]; +#endif +#if defined( __CL_LONG4__) + __cl_long4 v4[2]; +#endif +#if defined( __CL_LONG8__ ) + __cl_long8 v8; +#endif +}cl_long8; + +typedef union +{ + cl_long CL_ALIGNED(128) s[16]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_long x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; + __CL_ANON_STRUCT__ struct{ cl_long s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; + __CL_ANON_STRUCT__ struct{ cl_long8 lo, hi; }; +#endif +#if defined( __CL_LONG2__) + __cl_long2 v2[8]; +#endif +#if defined( __CL_LONG4__) + __cl_long4 v4[4]; +#endif +#if defined( __CL_LONG8__ ) + __cl_long8 v8[2]; +#endif +#if defined( __CL_LONG16__ ) + __cl_long16 v16; +#endif +}cl_long16; + + +/* ---- cl_ulongn ---- */ +typedef union +{ + cl_ulong CL_ALIGNED(16) s[2]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_ulong x, y; }; + __CL_ANON_STRUCT__ struct{ cl_ulong s0, s1; }; + __CL_ANON_STRUCT__ struct{ cl_ulong lo, hi; }; +#endif +#if defined( __CL_ULONG2__) + __cl_ulong2 v2; +#endif +}cl_ulong2; + +typedef union +{ + cl_ulong CL_ALIGNED(32) s[4]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_ulong x, y, z, w; }; + __CL_ANON_STRUCT__ struct{ cl_ulong s0, s1, s2, s3; }; + __CL_ANON_STRUCT__ struct{ cl_ulong2 lo, hi; }; +#endif +#if defined( __CL_ULONG2__) + __cl_ulong2 v2[2]; +#endif +#if defined( __CL_ULONG4__) + __cl_ulong4 v4; +#endif +}cl_ulong4; + +/* cl_ulong3 is identical in size, alignment and behavior to cl_ulong4. See section 6.1.5. */ +typedef cl_ulong4 cl_ulong3; + +typedef union +{ + cl_ulong CL_ALIGNED(64) s[8]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_ulong x, y, z, w; }; + __CL_ANON_STRUCT__ struct{ cl_ulong s0, s1, s2, s3, s4, s5, s6, s7; }; + __CL_ANON_STRUCT__ struct{ cl_ulong4 lo, hi; }; +#endif +#if defined( __CL_ULONG2__) + __cl_ulong2 v2[4]; +#endif +#if defined( __CL_ULONG4__) + __cl_ulong4 v4[2]; +#endif +#if defined( __CL_ULONG8__ ) + __cl_ulong8 v8; +#endif +}cl_ulong8; + +typedef union +{ + cl_ulong CL_ALIGNED(128) s[16]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_ulong x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; + __CL_ANON_STRUCT__ struct{ cl_ulong s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; + __CL_ANON_STRUCT__ struct{ cl_ulong8 lo, hi; }; +#endif +#if defined( __CL_ULONG2__) + __cl_ulong2 v2[8]; +#endif +#if defined( __CL_ULONG4__) + __cl_ulong4 v4[4]; +#endif +#if defined( __CL_ULONG8__ ) + __cl_ulong8 v8[2]; +#endif +#if defined( __CL_ULONG16__ ) + __cl_ulong16 v16; +#endif +}cl_ulong16; + + +/* --- cl_floatn ---- */ + +typedef union +{ + cl_float CL_ALIGNED(8) s[2]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_float x, y; }; + __CL_ANON_STRUCT__ struct{ cl_float s0, s1; }; + __CL_ANON_STRUCT__ struct{ cl_float lo, hi; }; +#endif +#if defined( __CL_FLOAT2__) + __cl_float2 v2; +#endif +}cl_float2; + +typedef union +{ + cl_float CL_ALIGNED(16) s[4]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_float x, y, z, w; }; + __CL_ANON_STRUCT__ struct{ cl_float s0, s1, s2, s3; }; + __CL_ANON_STRUCT__ struct{ cl_float2 lo, hi; }; +#endif +#if defined( __CL_FLOAT2__) + __cl_float2 v2[2]; +#endif +#if defined( __CL_FLOAT4__) + __cl_float4 v4; +#endif +}cl_float4; + +/* cl_float3 is identical in size, alignment and behavior to cl_float4. See section 6.1.5. */ +typedef cl_float4 cl_float3; + +typedef union +{ + cl_float CL_ALIGNED(32) s[8]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_float x, y, z, w; }; + __CL_ANON_STRUCT__ struct{ cl_float s0, s1, s2, s3, s4, s5, s6, s7; }; + __CL_ANON_STRUCT__ struct{ cl_float4 lo, hi; }; +#endif +#if defined( __CL_FLOAT2__) + __cl_float2 v2[4]; +#endif +#if defined( __CL_FLOAT4__) + __cl_float4 v4[2]; +#endif +#if defined( __CL_FLOAT8__ ) + __cl_float8 v8; +#endif +}cl_float8; + +typedef union +{ + cl_float CL_ALIGNED(64) s[16]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_float x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; + __CL_ANON_STRUCT__ struct{ cl_float s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; + __CL_ANON_STRUCT__ struct{ cl_float8 lo, hi; }; +#endif +#if defined( __CL_FLOAT2__) + __cl_float2 v2[8]; +#endif +#if defined( __CL_FLOAT4__) + __cl_float4 v4[4]; +#endif +#if defined( __CL_FLOAT8__ ) + __cl_float8 v8[2]; +#endif +#if defined( __CL_FLOAT16__ ) + __cl_float16 v16; +#endif +}cl_float16; + +/* --- cl_doublen ---- */ + +typedef union +{ + cl_double CL_ALIGNED(16) s[2]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_double x, y; }; + __CL_ANON_STRUCT__ struct{ cl_double s0, s1; }; + __CL_ANON_STRUCT__ struct{ cl_double lo, hi; }; +#endif +#if defined( __CL_DOUBLE2__) + __cl_double2 v2; +#endif +}cl_double2; + +typedef union +{ + cl_double CL_ALIGNED(32) s[4]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_double x, y, z, w; }; + __CL_ANON_STRUCT__ struct{ cl_double s0, s1, s2, s3; }; + __CL_ANON_STRUCT__ struct{ cl_double2 lo, hi; }; +#endif +#if defined( __CL_DOUBLE2__) + __cl_double2 v2[2]; +#endif +#if defined( __CL_DOUBLE4__) + __cl_double4 v4; +#endif +}cl_double4; + +/* cl_double3 is identical in size, alignment and behavior to cl_double4. See section 6.1.5. */ +typedef cl_double4 cl_double3; + +typedef union +{ + cl_double CL_ALIGNED(64) s[8]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_double x, y, z, w; }; + __CL_ANON_STRUCT__ struct{ cl_double s0, s1, s2, s3, s4, s5, s6, s7; }; + __CL_ANON_STRUCT__ struct{ cl_double4 lo, hi; }; +#endif +#if defined( __CL_DOUBLE2__) + __cl_double2 v2[4]; +#endif +#if defined( __CL_DOUBLE4__) + __cl_double4 v4[2]; +#endif +#if defined( __CL_DOUBLE8__ ) + __cl_double8 v8; +#endif +}cl_double8; + +typedef union +{ + cl_double CL_ALIGNED(128) s[16]; +#if __CL_HAS_ANON_STRUCT__ + __CL_ANON_STRUCT__ struct{ cl_double x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; + __CL_ANON_STRUCT__ struct{ cl_double s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; + __CL_ANON_STRUCT__ struct{ cl_double8 lo, hi; }; +#endif +#if defined( __CL_DOUBLE2__) + __cl_double2 v2[8]; +#endif +#if defined( __CL_DOUBLE4__) + __cl_double4 v4[4]; +#endif +#if defined( __CL_DOUBLE8__ ) + __cl_double8 v8[2]; +#endif +#if defined( __CL_DOUBLE16__ ) + __cl_double16 v16; +#endif +}cl_double16; + +/* Macro to facilitate debugging + * Usage: + * Place CL_PROGRAM_STRING_DEBUG_INFO on the line before the first line of your source. + * The first line ends with: CL_PROGRAM_STRING_DEBUG_INFO \" + * Each line thereafter of OpenCL C source must end with: \n\ + * The last line ends in "; + * + * Example: + * + * const char *my_program = CL_PROGRAM_STRING_DEBUG_INFO "\ + * kernel void foo( int a, float * b ) \n\ + * { \n\ + * // my comment \n\ + * *b[ get_global_id(0)] = a; \n\ + * } \n\ + * "; + * + * This should correctly set up the line, (column) and file information for your source + * string so you can do source level debugging. + */ +#define __CL_STRINGIFY( _x ) # _x +#define _CL_STRINGIFY( _x ) __CL_STRINGIFY( _x ) +#define CL_PROGRAM_STRING_DEBUG_INFO "#line " _CL_STRINGIFY(__LINE__) " \"" __FILE__ "\" \n\n" + +#ifdef __cplusplus +} +#endif + +#undef __CL_HAS_ANON_STRUCT__ +#undef __CL_ANON_STRUCT__ +#if defined( _WIN32) && defined(_MSC_VER) + #if _MSC_VER >=1500 + #pragma warning( pop ) + #endif +#endif + +#endif /* __CL_PLATFORM_H */ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..cabd8b50 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,261 @@ +# Copyright (c) 2018 Intel Corporation +# +# 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. + +cmake_minimum_required(VERSION 3.1 FATAL_ERROR) + +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +set(CMAKE_CONFIGURATION_TYPES Debug Release) +set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) + +if (NOT CMAKE_BUILD_TYPE) + message(STATUS "No build type selected, default to Release") + set(CMAKE_BUILD_TYPE "Release" CACHE PATH "Build Type" FORCE) +endif() + +project(CLIntercept) + +find_package( Threads ) + +# This uses modules from: https://github.com/rpavlik/cmake-modules +# to get Git revision information and put it in the generated files: +# git_version.cpp - version information for CLIntercept log +# git_version.rc2 - DLL version information (Windows only) +add_definitions("-DCLINTERCEPT_CMAKE") +include(cmake_modules/GetGitRevisionDescription.cmake) +get_git_head_revision(GIT_REFSPEC GIT_SHA1) +git_describe(GIT_DESCRIBE) +configure_file(Src/git_version.cpp.in "${CMAKE_CURRENT_BINARY_DIR}/git_version.cpp" @ONLY) +configure_file(Src/git_version.rc.in "${CMAKE_CURRENT_BINARY_DIR}/git_version.rc2" @ONLY) + +# Build the CLIntercept config app for 32-bit Windows builds, but not for 64-bit or other OSes. +if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + if(CMAKE_SIZEOF_VOID_P EQUAL 4) + add_subdirectory(config) + endif() +endif() + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/Src +) +link_directories( +) + +set( CLINTERCEPT_OS_FILES + OS/OS.h +) +if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + list( APPEND CLINTERCEPT_OS_FILES + OS/OS_timer.h + OS/OS_windows.cpp + OS/OS_windows.h + OS/OS_windows_common.cpp + OS/OS_windows_common.h + ) +elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + list( APPEND CLINTERCEPT_OS_FILES + OS/OS_linux.cpp + OS/OS_linux.h + OS/OS_linux_common.cpp + OS/OS_linux_common.h + ) +elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") + list( APPEND CLINTERCEPT_OS_FILES + OS/OS_mac.cpp + OS/OS_mac.h + OS/OS_mac_common.cpp + OS/OS_mac_common.h + ) +endif() +source_group( OS FILES + ${CLINTERCEPT_OS_FILES} +) + +set( CLINTERCEPT_RESOURCE_FILES + Kernels/builtin_kernels.cl + Kernels/precompiled_kernels.cl + resource/clIntercept.rc + resource/clIntercept_resource.h + "${CMAKE_CURRENT_BINARY_DIR}/git_version.rc2" +) +source_group( Resources FILES + ${CLINTERCEPT_RESOURCE_FILES} +) +if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + set(CLINTERCEPT_KERNELS_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/Kernels) + #TODO: This is currently hard-coded for 64-bit Linux builds. + set(CLINTERCEPT_KERNELS_OUTPUT_FORMAT elf64-x86-64) + add_custom_command(OUTPUT ${CLINTERCEPT_KERNELS_OUTPUT_DIRECTORY}/precompiled_kernels.o + COMMAND mkdir -p ${CLINTERCEPT_KERNELS_OUTPUT_DIRECTORY} + COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && objcopy -I binary -O ${CLINTERCEPT_KERNELS_OUTPUT_FORMAT} --binary-architecture i386 + Kernels/precompiled_kernels.cl + ${CLINTERCEPT_KERNELS_OUTPUT_DIRECTORY}/precompiled_kernels.o + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/Kernels/precompiled_kernels.cl + ) + add_custom_command(OUTPUT ${CLINTERCEPT_KERNELS_OUTPUT_DIRECTORY}/builtin_kernels.o + COMMAND mkdir -p ${CLINTERCEPT_KERNELS_OUTPUT_DIRECTORY} + COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && objcopy -I binary -O ${CLINTERCEPT_KERNELS_OUTPUT_FORMAT} --binary-architecture i386 + Kernels/builtin_kernels.cl + ${CLINTERCEPT_KERNELS_OUTPUT_DIRECTORY}/builtin_kernels.o + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/Kernels/builtin_kernels.cl + ) + list( APPEND CLINTERCEPT_RESOURCE_FILES + ${CLINTERCEPT_KERNELS_OUTPUT_DIRECTORY}/precompiled_kernels.o + ${CLINTERCEPT_KERNELS_OUTPUT_DIRECTORY}/builtin_kernels.o + ) +endif() + +set( CLINTERCEPT_SOURCE_FILES + Src/clIntercept.def + Src/clIntercept.map + Src/cli_ext.h + Src/common.h + Src/controls.h + Src/dispatch.cpp + Src/dispatch.h + Src/enummap.cpp + Src/enummap.h + Src/instrumentation.h + Src/intercept.cpp + Src/intercept.h + Src/main.cpp + Src/stubs.cpp + "${CMAKE_CURRENT_BINARY_DIR}/git_version.cpp" +) +source_group( Source FILES + ${CLINTERCEPT_SOURCE_FILES} +) + +set( CLINTERCEPT_CL_HEADERS + CL/cl.h + CL/cl_gl.h + CL/cl_platform.h +) +source_group( CL FILES + ${CLINTERCEPT_CL_HEADERS} +) + +# MDAPI Support (optional) +set( ENABLE_MDAPI CACHE BOOL "Enable MDAPI Support" ) +if( ENABLE_MDAPI ) + add_definitions("-DUSE_MDAPI") + include_directories( embargo/mdapi ) + set( CLINTERCEPT_MDAPI_FILES + embargo/mdapi/DriverStorePath.h + embargo/mdapi/intercept_mdapi.cpp + embargo/mdapi/MetricsDiscoveryHelper.cpp + embargo/mdapi/MetricsDiscoveryHelper.h + embargo/mdapi/metrics_discovery_api.h + embargo/mdapi/windef4linux.h + ) + source_group( MDAPI FILES + ${CLINTERCEPT_MDAPI_FILES} + ) +endif() + +add_library( OpenCL SHARED + ${CLINTERCEPT_CL_HEADERS} + ${CLINTERCEPT_OS_FILES} + ${CLINTERCEPT_RESOURCE_FILES} + ${CLINTERCEPT_SOURCE_FILES} + ${CLINTERCEPT_MDAPI_FILES} +) +set_target_properties( OpenCL PROPERTIES VERSION "1.2" SOVERSION "1" ) +target_link_libraries( OpenCL ${CMAKE_DL_LIBS} ) + +if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc") + target_link_libraries( OpenCL SetupAPI Shlwapi ) +elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + find_package( Threads ) + set_target_properties(OpenCL PROPERTIES COMPILE_FLAGS "-g -Wall -std=gnu++11") + set_target_properties(OpenCL PROPERTIES LINK_FLAGS "-Wl,--version-script -Wl,${CMAKE_SOURCE_DIR}/Src/clIntercept.map") + target_link_libraries( OpenCL ${CMAKE_THREAD_LIBS_INIT} ) +elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") + find_package(OpenCL REQUIRED) + set_target_properties(OpenCL PROPERTIES COMPILE_FLAGS "-g -Wall -arch i386 -arch x86_64") + target_link_libraries( OpenCL OpenCL::OpenCL ) +endif() + +# ITT Support (optional) +set( ENABLE_ITT CACHE BOOL "Enable ITT (Instrumentation Tracing Technology) API Support" ) +set( PROGRAMFILES_X86 "PROGRAMFILES(X86)" ) +find_path( VTUNE_INCLUDE_DIR ittnotify.h + HINTS + /opt/intel/vtune_amplifier_xe/include + "$ENV{${PROGRAMFILES_X86}}/Intel/VTune\ Amplifier\ XE/include" ) +if (CMAKE_SIZEOF_VOID_P EQUAL 4) + #message( STATUS "Searching for 32-bit ittnotify lib..." ) + find_library( VTUNE_ITTNOTIFY_LIB NAMES ittnotify libittnotify + HINTS + /opt/intel/vtune_amplifier_xe/lib32 + "$ENV{${PROGRAMFILES_X86}}/Intel/VTune\ Amplifier\ XE/lib32" ) +else() + #message( STATUS "Searching for 64-bit ittnotify lib..." ) + find_library( VTUNE_ITTNOTIFY_LIB NAMES ittnotify libittnotify + HINTS + /opt/intel/vtune_amplifier_xe/lib64 + "$ENV{${PROGRAMFILES_X86}}/Intel/VTune\ Amplifier\ XE/lib64" ) +endif() +if( ENABLE_ITT ) + add_definitions("-DUSE_ITT") + include_directories( ${VTUNE_INCLUDE_DIR} ) + message( STATUS "VTune ITTNotify Lib is: ${VTUNE_ITTNOTIFY_LIB}" ) + target_link_libraries( OpenCL ${VTUNE_ITTNOTIFY_LIB} ${CMAKE_THREAD_LIBS_INIT} ) +endif() + +if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(CLINTERCEPT_PLATFORM_NAME "x64") + elseif (CMAKE_SIZEOF_VOID_P EQUAL 4) + set(CLINTERCEPT_PLATFORM_NAME "Win32") + else() + set(CLINTERCEPT_PLATFORM_NAME "Unknown") + endif() + + if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/Builds/${CLINTERCEPT_PLATFORM_NAME}" CACHE PATH "Install Path" FORCE) + endif() + + foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} ) + string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG_UPPER ) + + # For Windows debug builds, we want to statically link the C runtime. + # If we do not statically link the C runtime then the target machine + # must install Visual Studio, which is not desirable. + if( ${OUTPUTCONFIG_UPPER} MATCHES "DEBUG" ) + #message( STATUS "DEBUG build detected!" ) + #message( STATUS " 'CMAKE_CXX_FLAGS_${OUTPUTCONFIG_UPPER}': ${CMAKE_CXX_FLAGS_${OUTPUTCONFIG_UPPER}}" ) + #message( STATUS "->" ) + if( CMAKE_CXX_FLAGS_${OUTPUTCONFIG_UPPER} MATCHES "/MD" ) + string( REGEX REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_${OUTPUTCONFIG_UPPER} "${CMAKE_CXX_FLAGS_${OUTPUTCONFIG_UPPER}}" ) + endif() + #message( STATUS " 'CMAKE_CXX_FLAGS_${OUTPUTCONFIG_UPPER}': ${CMAKE_CXX_FLAGS_${OUTPUTCONFIG_UPPER}}" ) + endif() + install(TARGETS OpenCL DESTINATION ${OUTPUTCONFIG} CONFIGURATIONS ${OUTPUTCONFIG}) + endforeach() +else() + if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/install" CACHE PATH "Install Path" FORCE) + endif() + install(TARGETS OpenCL DESTINATION "lib") +endif() diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..8aec231a --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team via email at ben 'dot' ashbaugh 'at' +intel 'dot' com or any other Intel GitHub maintainer (see profile for email +address). All complaints will be reviewed and investigated and will result in +a response that is deemed necessary and appropriate to the circumstances. The +project team is obligated to maintain confidentiality with regard to the +reporter of an incident. Further details of specific enforcement policies may +be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct/ + +[homepage]: https://www.contributor-covenant.org + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..237d4f1c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,32 @@ +# Contributing to the Intercept Layer for OpenCL Applications + +Thanks for your time! Contributions to the Intercept Layer for OpenCL +Applications are welcomed and encouraged. Below you can find guidelines +for contributing to the Intercept Layer for OpenCL Applications. + +## How to Report an Issue or Feature Request + +Public GitHub issues are the preferred method for reporting issues and feature +requests. Private or sensitive issues may be submitted via email to +this project's maintainer (Ben Ashbaugh - ben 'dot' ashbaugh 'at' intel 'dot' +com), or to any other Intel GitHub maintainer (see profile for email address). + +## How to Fix and Issue or Add a Feature + +If you have an idea how to improve the Intercept Layer for OpenCL Applications: + +1. Please share your proposal via a GitHub issue. This lets others know what + you're working on and gives others an opportunity to provide early feedback. +1. Implement, validate, and document your fix or feature. Be sure it doesn't + break any existing functionality! +1. Submit a pull request with your changes. + +After submitting a pull request your contribution will be reviewed. Your pull +request may be accepted as-is, or additional fixes or modifications may be +required. + +--- + +\* Other names and brands may be claimed as the property of others. + +Copyright (c) 2018, Intel(R) Corporation diff --git a/GL/glcorearb.h b/GL/glcorearb.h new file mode 100644 index 00000000..71e3b069 --- /dev/null +++ b/GL/glcorearb.h @@ -0,0 +1,3597 @@ +#ifndef __glcorearb_h_ +#define __glcorearb_h_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** Copyright (c) 2013-2014 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are 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 Materials. +** +** THE MATERIALS ARE 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 +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ +/* +** This header is generated from the Khronos OpenGL / OpenGL ES XML +** API Registry. The current version of the Registry, generator scripts +** used to make the header, and the header can be found at +** http://www.opengl.org/registry/ +** +** Khronos $Revision$ on $Date$ +*/ + +#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include +#endif + +#ifndef APIENTRY +#define APIENTRY +#endif +#ifndef APIENTRYP +#define APIENTRYP APIENTRY * +#endif +#ifndef GLAPI +#define GLAPI extern +#endif + +/* glcorearb.h is for use with OpenGL core profile implementations. +** It should should be placed in the same directory as gl.h and +** included as . +** +** glcorearb.h includes only APIs in the latest OpenGL core profile +** implementation together with APIs in newer ARB extensions which +** can be supported by the core profile. It does not, and never will +** include functionality removed from the core profile, such as +** fixed-function vertex and fragment processing. +** +** Do not #include both and either of or +** in the same source file. +*/ + +/* Generated C header for: + * API: gl + * Profile: core + * Versions considered: .* + * Versions emitted: .* + * Default extensions included: glcore + * Additional extensions included: _nomatch_^ + * Extensions removed: _nomatch_^ + */ + +#ifndef GL_VERSION_1_0 +#define GL_VERSION_1_0 1 +typedef void GLvoid; +typedef unsigned int GLenum; +typedef float GLfloat; +typedef int GLint; +typedef int GLsizei; +typedef unsigned int GLbitfield; +typedef double GLdouble; +typedef unsigned int GLuint; +typedef unsigned char GLboolean; +typedef unsigned char GLubyte; +typedef void (APIENTRYP PFNGLCULLFACEPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLFRONTFACEPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLHINTPROC) (GLenum target, GLenum mode); +typedef void (APIENTRYP PFNGLLINEWIDTHPROC) (GLfloat width); +typedef void (APIENTRYP PFNGLPOINTSIZEPROC) (GLfloat size); +typedef void (APIENTRYP PFNGLPOLYGONMODEPROC) (GLenum face, GLenum mode); +typedef void (APIENTRYP PFNGLSCISSORPROC) (GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXPARAMETERFPROC) (GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLTEXPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLTEXPARAMETERIPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLTEXPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXIMAGE1DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXIMAGE2DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLDRAWBUFFERPROC) (GLenum buf); +typedef void (APIENTRYP PFNGLCLEARPROC) (GLbitfield mask); +typedef void (APIENTRYP PFNGLCLEARCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (APIENTRYP PFNGLCLEARSTENCILPROC) (GLint s); +typedef void (APIENTRYP PFNGLCLEARDEPTHPROC) (GLdouble depth); +typedef void (APIENTRYP PFNGLSTENCILMASKPROC) (GLuint mask); +typedef void (APIENTRYP PFNGLCOLORMASKPROC) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +typedef void (APIENTRYP PFNGLDEPTHMASKPROC) (GLboolean flag); +typedef void (APIENTRYP PFNGLDISABLEPROC) (GLenum cap); +typedef void (APIENTRYP PFNGLENABLEPROC) (GLenum cap); +typedef void (APIENTRYP PFNGLFINISHPROC) (void); +typedef void (APIENTRYP PFNGLFLUSHPROC) (void); +typedef void (APIENTRYP PFNGLBLENDFUNCPROC) (GLenum sfactor, GLenum dfactor); +typedef void (APIENTRYP PFNGLLOGICOPPROC) (GLenum opcode); +typedef void (APIENTRYP PFNGLSTENCILFUNCPROC) (GLenum func, GLint ref, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILOPPROC) (GLenum fail, GLenum zfail, GLenum zpass); +typedef void (APIENTRYP PFNGLDEPTHFUNCPROC) (GLenum func); +typedef void (APIENTRYP PFNGLPIXELSTOREFPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPIXELSTOREIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLREADBUFFERPROC) (GLenum src); +typedef void (APIENTRYP PFNGLREADPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels); +typedef void (APIENTRYP PFNGLGETBOOLEANVPROC) (GLenum pname, GLboolean *data); +typedef void (APIENTRYP PFNGLGETDOUBLEVPROC) (GLenum pname, GLdouble *data); +typedef GLenum (APIENTRYP PFNGLGETERRORPROC) (void); +typedef void (APIENTRYP PFNGLGETFLOATVPROC) (GLenum pname, GLfloat *data); +typedef void (APIENTRYP PFNGLGETINTEGERVPROC) (GLenum pname, GLint *data); +typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGPROC) (GLenum name); +typedef void (APIENTRYP PFNGLGETTEXIMAGEPROC) (GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERFVPROC) (GLenum target, GLint level, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERIVPROC) (GLenum target, GLint level, GLenum pname, GLint *params); +typedef GLboolean (APIENTRYP PFNGLISENABLEDPROC) (GLenum cap); +typedef void (APIENTRYP PFNGLDEPTHRANGEPROC) (GLdouble near, GLdouble far); +typedef void (APIENTRYP PFNGLVIEWPORTPROC) (GLint x, GLint y, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCullFace (GLenum mode); +GLAPI void APIENTRY glFrontFace (GLenum mode); +GLAPI void APIENTRY glHint (GLenum target, GLenum mode); +GLAPI void APIENTRY glLineWidth (GLfloat width); +GLAPI void APIENTRY glPointSize (GLfloat size); +GLAPI void APIENTRY glPolygonMode (GLenum face, GLenum mode); +GLAPI void APIENTRY glScissor (GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTexParameterf (GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glTexParameterfv (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glTexParameteri (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glTexParameteriv (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTexImage1D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glDrawBuffer (GLenum buf); +GLAPI void APIENTRY glClear (GLbitfield mask); +GLAPI void APIENTRY glClearColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +GLAPI void APIENTRY glClearStencil (GLint s); +GLAPI void APIENTRY glClearDepth (GLdouble depth); +GLAPI void APIENTRY glStencilMask (GLuint mask); +GLAPI void APIENTRY glColorMask (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +GLAPI void APIENTRY glDepthMask (GLboolean flag); +GLAPI void APIENTRY glDisable (GLenum cap); +GLAPI void APIENTRY glEnable (GLenum cap); +GLAPI void APIENTRY glFinish (void); +GLAPI void APIENTRY glFlush (void); +GLAPI void APIENTRY glBlendFunc (GLenum sfactor, GLenum dfactor); +GLAPI void APIENTRY glLogicOp (GLenum opcode); +GLAPI void APIENTRY glStencilFunc (GLenum func, GLint ref, GLuint mask); +GLAPI void APIENTRY glStencilOp (GLenum fail, GLenum zfail, GLenum zpass); +GLAPI void APIENTRY glDepthFunc (GLenum func); +GLAPI void APIENTRY glPixelStoref (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPixelStorei (GLenum pname, GLint param); +GLAPI void APIENTRY glReadBuffer (GLenum src); +GLAPI void APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels); +GLAPI void APIENTRY glGetBooleanv (GLenum pname, GLboolean *data); +GLAPI void APIENTRY glGetDoublev (GLenum pname, GLdouble *data); +GLAPI GLenum APIENTRY glGetError (void); +GLAPI void APIENTRY glGetFloatv (GLenum pname, GLfloat *data); +GLAPI void APIENTRY glGetIntegerv (GLenum pname, GLint *data); +GLAPI const GLubyte *APIENTRY glGetString (GLenum name); +GLAPI void APIENTRY glGetTexImage (GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +GLAPI void APIENTRY glGetTexParameterfv (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTexParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTexLevelParameterfv (GLenum target, GLint level, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTexLevelParameteriv (GLenum target, GLint level, GLenum pname, GLint *params); +GLAPI GLboolean APIENTRY glIsEnabled (GLenum cap); +GLAPI void APIENTRY glDepthRange (GLdouble near, GLdouble far); +GLAPI void APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height); +#endif +#endif /* GL_VERSION_1_0 */ + +#ifndef GL_VERSION_1_1 +#define GL_VERSION_1_1 1 +typedef float GLclampf; +typedef double GLclampd; +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_FALSE 0 +#define GL_TRUE 1 +#define GL_POINTS 0x0000 +#define GL_LINES 0x0001 +#define GL_LINE_LOOP 0x0002 +#define GL_LINE_STRIP 0x0003 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRIANGLE_FAN 0x0006 +#define GL_QUADS 0x0007 +#define GL_NEVER 0x0200 +#define GL_LESS 0x0201 +#define GL_EQUAL 0x0202 +#define GL_LEQUAL 0x0203 +#define GL_GREATER 0x0204 +#define GL_NOTEQUAL 0x0205 +#define GL_GEQUAL 0x0206 +#define GL_ALWAYS 0x0207 +#define GL_ZERO 0 +#define GL_ONE 1 +#define GL_SRC_COLOR 0x0300 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_SRC_ALPHA 0x0302 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_DST_ALPHA 0x0304 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 +#define GL_DST_COLOR 0x0306 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_SRC_ALPHA_SATURATE 0x0308 +#define GL_NONE 0 +#define GL_FRONT_LEFT 0x0400 +#define GL_FRONT_RIGHT 0x0401 +#define GL_BACK_LEFT 0x0402 +#define GL_BACK_RIGHT 0x0403 +#define GL_FRONT 0x0404 +#define GL_BACK 0x0405 +#define GL_LEFT 0x0406 +#define GL_RIGHT 0x0407 +#define GL_FRONT_AND_BACK 0x0408 +#define GL_NO_ERROR 0 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVALID_OPERATION 0x0502 +#define GL_OUT_OF_MEMORY 0x0505 +#define GL_CW 0x0900 +#define GL_CCW 0x0901 +#define GL_POINT_SIZE 0x0B11 +#define GL_POINT_SIZE_RANGE 0x0B12 +#define GL_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_LINE_SMOOTH 0x0B20 +#define GL_LINE_WIDTH 0x0B21 +#define GL_LINE_WIDTH_RANGE 0x0B22 +#define GL_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_POLYGON_MODE 0x0B40 +#define GL_POLYGON_SMOOTH 0x0B41 +#define GL_CULL_FACE 0x0B44 +#define GL_CULL_FACE_MODE 0x0B45 +#define GL_FRONT_FACE 0x0B46 +#define GL_DEPTH_RANGE 0x0B70 +#define GL_DEPTH_TEST 0x0B71 +#define GL_DEPTH_WRITEMASK 0x0B72 +#define GL_DEPTH_CLEAR_VALUE 0x0B73 +#define GL_DEPTH_FUNC 0x0B74 +#define GL_STENCIL_TEST 0x0B90 +#define GL_STENCIL_CLEAR_VALUE 0x0B91 +#define GL_STENCIL_FUNC 0x0B92 +#define GL_STENCIL_VALUE_MASK 0x0B93 +#define GL_STENCIL_FAIL 0x0B94 +#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 +#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 +#define GL_STENCIL_REF 0x0B97 +#define GL_STENCIL_WRITEMASK 0x0B98 +#define GL_VIEWPORT 0x0BA2 +#define GL_DITHER 0x0BD0 +#define GL_BLEND_DST 0x0BE0 +#define GL_BLEND_SRC 0x0BE1 +#define GL_BLEND 0x0BE2 +#define GL_LOGIC_OP_MODE 0x0BF0 +#define GL_COLOR_LOGIC_OP 0x0BF2 +#define GL_DRAW_BUFFER 0x0C01 +#define GL_READ_BUFFER 0x0C02 +#define GL_SCISSOR_BOX 0x0C10 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_COLOR_CLEAR_VALUE 0x0C22 +#define GL_COLOR_WRITEMASK 0x0C23 +#define GL_DOUBLEBUFFER 0x0C32 +#define GL_STEREO 0x0C33 +#define GL_LINE_SMOOTH_HINT 0x0C52 +#define GL_POLYGON_SMOOTH_HINT 0x0C53 +#define GL_UNPACK_SWAP_BYTES 0x0CF0 +#define GL_UNPACK_LSB_FIRST 0x0CF1 +#define GL_UNPACK_ROW_LENGTH 0x0CF2 +#define GL_UNPACK_SKIP_ROWS 0x0CF3 +#define GL_UNPACK_SKIP_PIXELS 0x0CF4 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_PACK_SWAP_BYTES 0x0D00 +#define GL_PACK_LSB_FIRST 0x0D01 +#define GL_PACK_ROW_LENGTH 0x0D02 +#define GL_PACK_SKIP_ROWS 0x0D03 +#define GL_PACK_SKIP_PIXELS 0x0D04 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#define GL_MAX_VIEWPORT_DIMS 0x0D3A +#define GL_SUBPIXEL_BITS 0x0D50 +#define GL_TEXTURE_1D 0x0DE0 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_POLYGON_OFFSET_UNITS 0x2A00 +#define GL_POLYGON_OFFSET_POINT 0x2A01 +#define GL_POLYGON_OFFSET_LINE 0x2A02 +#define GL_POLYGON_OFFSET_FILL 0x8037 +#define GL_POLYGON_OFFSET_FACTOR 0x8038 +#define GL_TEXTURE_BINDING_1D 0x8068 +#define GL_TEXTURE_BINDING_2D 0x8069 +#define GL_TEXTURE_WIDTH 0x1000 +#define GL_TEXTURE_HEIGHT 0x1001 +#define GL_TEXTURE_INTERNAL_FORMAT 0x1003 +#define GL_TEXTURE_BORDER_COLOR 0x1004 +#define GL_TEXTURE_RED_SIZE 0x805C +#define GL_TEXTURE_GREEN_SIZE 0x805D +#define GL_TEXTURE_BLUE_SIZE 0x805E +#define GL_TEXTURE_ALPHA_SIZE 0x805F +#define GL_DONT_CARE 0x1100 +#define GL_FASTEST 0x1101 +#define GL_NICEST 0x1102 +#define GL_BYTE 0x1400 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_SHORT 0x1402 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_INT 0x1404 +#define GL_UNSIGNED_INT 0x1405 +#define GL_FLOAT 0x1406 +#define GL_DOUBLE 0x140A +#define GL_STACK_OVERFLOW 0x0503 +#define GL_STACK_UNDERFLOW 0x0504 +#define GL_CLEAR 0x1500 +#define GL_AND 0x1501 +#define GL_AND_REVERSE 0x1502 +#define GL_COPY 0x1503 +#define GL_AND_INVERTED 0x1504 +#define GL_NOOP 0x1505 +#define GL_XOR 0x1506 +#define GL_OR 0x1507 +#define GL_NOR 0x1508 +#define GL_EQUIV 0x1509 +#define GL_INVERT 0x150A +#define GL_OR_REVERSE 0x150B +#define GL_COPY_INVERTED 0x150C +#define GL_OR_INVERTED 0x150D +#define GL_NAND 0x150E +#define GL_SET 0x150F +#define GL_TEXTURE 0x1702 +#define GL_COLOR 0x1800 +#define GL_DEPTH 0x1801 +#define GL_STENCIL 0x1802 +#define GL_STENCIL_INDEX 0x1901 +#define GL_DEPTH_COMPONENT 0x1902 +#define GL_RED 0x1903 +#define GL_GREEN 0x1904 +#define GL_BLUE 0x1905 +#define GL_ALPHA 0x1906 +#define GL_RGB 0x1907 +#define GL_RGBA 0x1908 +#define GL_POINT 0x1B00 +#define GL_LINE 0x1B01 +#define GL_FILL 0x1B02 +#define GL_KEEP 0x1E00 +#define GL_REPLACE 0x1E01 +#define GL_INCR 0x1E02 +#define GL_DECR 0x1E03 +#define GL_VENDOR 0x1F00 +#define GL_RENDERER 0x1F01 +#define GL_VERSION 0x1F02 +#define GL_EXTENSIONS 0x1F03 +#define GL_NEAREST 0x2600 +#define GL_LINEAR 0x2601 +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_PROXY_TEXTURE_1D 0x8063 +#define GL_PROXY_TEXTURE_2D 0x8064 +#define GL_REPEAT 0x2901 +#define GL_R3_G3_B2 0x2A10 +#define GL_RGB4 0x804F +#define GL_RGB5 0x8050 +#define GL_RGB8 0x8051 +#define GL_RGB10 0x8052 +#define GL_RGB12 0x8053 +#define GL_RGB16 0x8054 +#define GL_RGBA2 0x8055 +#define GL_RGBA4 0x8056 +#define GL_RGB5_A1 0x8057 +#define GL_RGBA8 0x8058 +#define GL_RGB10_A2 0x8059 +#define GL_RGBA12 0x805A +#define GL_RGBA16 0x805B +#define GL_VERTEX_ARRAY 0x8074 +typedef void (APIENTRYP PFNGLDRAWARRAYSPROC) (GLenum mode, GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLDRAWELEMENTSPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices); +typedef void (APIENTRYP PFNGLGETPOINTERVPROC) (GLenum pname, void **params); +typedef void (APIENTRYP PFNGLPOLYGONOFFSETPROC) (GLfloat factor, GLfloat units); +typedef void (APIENTRYP PFNGLCOPYTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLBINDTEXTUREPROC) (GLenum target, GLuint texture); +typedef void (APIENTRYP PFNGLDELETETEXTURESPROC) (GLsizei n, const GLuint *textures); +typedef void (APIENTRYP PFNGLGENTEXTURESPROC) (GLsizei n, GLuint *textures); +typedef GLboolean (APIENTRYP PFNGLISTEXTUREPROC) (GLuint texture); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count); +GLAPI void APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const void *indices); +GLAPI void APIENTRY glGetPointerv (GLenum pname, void **params); +GLAPI void APIENTRY glPolygonOffset (GLfloat factor, GLfloat units); +GLAPI void APIENTRY glCopyTexImage1D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +GLAPI void APIENTRY glCopyTexImage2D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GLAPI void APIENTRY glCopyTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glBindTexture (GLenum target, GLuint texture); +GLAPI void APIENTRY glDeleteTextures (GLsizei n, const GLuint *textures); +GLAPI void APIENTRY glGenTextures (GLsizei n, GLuint *textures); +GLAPI GLboolean APIENTRY glIsTexture (GLuint texture); +#endif +#endif /* GL_VERSION_1_1 */ + +#ifndef GL_VERSION_1_2 +#define GL_VERSION_1_2 1 +#define GL_UNSIGNED_BYTE_3_3_2 0x8032 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2 0x8036 +#define GL_TEXTURE_BINDING_3D 0x806A +#define GL_PACK_SKIP_IMAGES 0x806B +#define GL_PACK_IMAGE_HEIGHT 0x806C +#define GL_UNPACK_SKIP_IMAGES 0x806D +#define GL_UNPACK_IMAGE_HEIGHT 0x806E +#define GL_TEXTURE_3D 0x806F +#define GL_PROXY_TEXTURE_3D 0x8070 +#define GL_TEXTURE_DEPTH 0x8071 +#define GL_TEXTURE_WRAP_R 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE 0x8073 +#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#define GL_BGR 0x80E0 +#define GL_BGRA 0x80E1 +#define GL_MAX_ELEMENTS_VERTICES 0x80E8 +#define GL_MAX_ELEMENTS_INDICES 0x80E9 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_TEXTURE_MIN_LOD 0x813A +#define GL_TEXTURE_MAX_LOD 0x813B +#define GL_TEXTURE_BASE_LEVEL 0x813C +#define GL_TEXTURE_MAX_LEVEL 0x813D +#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 +#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 +#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); +typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawRangeElements (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); +GLAPI void APIENTRY glTexImage3D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#endif +#endif /* GL_VERSION_1_2 */ + +#ifndef GL_VERSION_1_3 +#define GL_VERSION_1_3 1 +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_MULTISAMPLE 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE 0x809F +#define GL_SAMPLE_COVERAGE 0x80A0 +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +#define GL_COMPRESSED_RGB 0x84ED +#define GL_COMPRESSED_RGBA 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0 +#define GL_TEXTURE_COMPRESSED 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 +#define GL_CLAMP_TO_BORDER 0x812D +typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC) (GLfloat value, GLboolean invert); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint level, void *img); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glActiveTexture (GLenum texture); +GLAPI void APIENTRY glSampleCoverage (GLfloat value, GLboolean invert); +GLAPI void APIENTRY glCompressedTexImage3D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexImage1D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glGetCompressedTexImage (GLenum target, GLint level, void *img); +#endif +#endif /* GL_VERSION_1_3 */ + +#ifndef GL_VERSION_1_4 +#define GL_VERSION_1_4 1 +#define GL_BLEND_DST_RGB 0x80C8 +#define GL_BLEND_SRC_RGB 0x80C9 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_COMPONENT24 0x81A6 +#define GL_DEPTH_COMPONENT32 0x81A7 +#define GL_MIRRORED_REPEAT 0x8370 +#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD +#define GL_TEXTURE_LOD_BIAS 0x8501 +#define GL_INCR_WRAP 0x8507 +#define GL_DECR_WRAP 0x8508 +#define GL_TEXTURE_DEPTH_SIZE 0x884A +#define GL_TEXTURE_COMPARE_MODE 0x884C +#define GL_TEXTURE_COMPARE_FUNC 0x884D +#define GL_FUNC_ADD 0x8006 +#define GL_FUNC_SUBTRACT 0x800A +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#define GL_MIN 0x8007 +#define GL_MAX 0x8008 +#define GL_CONSTANT_COLOR 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC) (GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLBLENDCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncSeparate (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +GLAPI void APIENTRY glMultiDrawArrays (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); +GLAPI void APIENTRY glMultiDrawElements (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount); +GLAPI void APIENTRY glPointParameterf (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPointParameterfv (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glPointParameteri (GLenum pname, GLint param); +GLAPI void APIENTRY glPointParameteriv (GLenum pname, const GLint *params); +GLAPI void APIENTRY glBlendColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +GLAPI void APIENTRY glBlendEquation (GLenum mode); +#endif +#endif /* GL_VERSION_1_4 */ + +#ifndef GL_VERSION_1_5 +#define GL_VERSION_1_5 1 +#include +typedef ptrdiff_t GLsizeiptr; +typedef ptrdiff_t GLintptr; +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_USAGE 0x8765 +#define GL_QUERY_COUNTER_BITS 0x8864 +#define GL_CURRENT_QUERY 0x8865 +#define GL_QUERY_RESULT 0x8866 +#define GL_QUERY_RESULT_AVAILABLE 0x8867 +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F +#define GL_READ_ONLY 0x88B8 +#define GL_WRITE_ONLY 0x88B9 +#define GL_READ_WRITE 0x88BA +#define GL_BUFFER_ACCESS 0x88BB +#define GL_BUFFER_MAPPED 0x88BC +#define GL_BUFFER_MAP_POINTER 0x88BD +#define GL_STREAM_DRAW 0x88E0 +#define GL_STREAM_READ 0x88E1 +#define GL_STREAM_COPY 0x88E2 +#define GL_STATIC_DRAW 0x88E4 +#define GL_STATIC_READ 0x88E5 +#define GL_STATIC_COPY 0x88E6 +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_DYNAMIC_READ 0x88E9 +#define GL_DYNAMIC_COPY 0x88EA +#define GL_SAMPLES_PASSED 0x8914 +#define GL_SRC1_ALPHA 0x8589 +typedef void (APIENTRYP PFNGLGENQUERIESPROC) (GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLDELETEQUERIESPROC) (GLsizei n, const GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISQUERYPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINQUERYPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC) (GLuint id, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer); +typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers); +typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers); +typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const void *data, GLenum usage); +typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); +typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, void *data); +typedef void *(APIENTRYP PFNGLMAPBUFFERPROC) (GLenum target, GLenum access); +typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC) (GLenum target, GLenum pname, void **params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenQueries (GLsizei n, GLuint *ids); +GLAPI void APIENTRY glDeleteQueries (GLsizei n, const GLuint *ids); +GLAPI GLboolean APIENTRY glIsQuery (GLuint id); +GLAPI void APIENTRY glBeginQuery (GLenum target, GLuint id); +GLAPI void APIENTRY glEndQuery (GLenum target); +GLAPI void APIENTRY glGetQueryiv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectiv (GLuint id, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectuiv (GLuint id, GLenum pname, GLuint *params); +GLAPI void APIENTRY glBindBuffer (GLenum target, GLuint buffer); +GLAPI void APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers); +GLAPI void APIENTRY glGenBuffers (GLsizei n, GLuint *buffers); +GLAPI GLboolean APIENTRY glIsBuffer (GLuint buffer); +GLAPI void APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void *data, GLenum usage); +GLAPI void APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); +GLAPI void APIENTRY glGetBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, void *data); +GLAPI void *APIENTRY glMapBuffer (GLenum target, GLenum access); +GLAPI GLboolean APIENTRY glUnmapBuffer (GLenum target); +GLAPI void APIENTRY glGetBufferParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetBufferPointerv (GLenum target, GLenum pname, void **params); +#endif +#endif /* GL_VERSION_1_5 */ + +#ifndef GL_VERSION_2_0 +#define GL_VERSION_2_0 1 +typedef char GLchar; +typedef short GLshort; +typedef signed char GLbyte; +typedef unsigned short GLushort; +#define GL_BLEND_EQUATION_RGB 0x8009 +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 +#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#define GL_STENCIL_BACK_FUNC 0x8800 +#define GL_STENCIL_BACK_FAIL 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#define GL_MAX_DRAW_BUFFERS 0x8824 +#define GL_DRAW_BUFFER0 0x8825 +#define GL_DRAW_BUFFER1 0x8826 +#define GL_DRAW_BUFFER2 0x8827 +#define GL_DRAW_BUFFER3 0x8828 +#define GL_DRAW_BUFFER4 0x8829 +#define GL_DRAW_BUFFER5 0x882A +#define GL_DRAW_BUFFER6 0x882B +#define GL_DRAW_BUFFER7 0x882C +#define GL_DRAW_BUFFER8 0x882D +#define GL_DRAW_BUFFER9 0x882E +#define GL_DRAW_BUFFER10 0x882F +#define GL_DRAW_BUFFER11 0x8830 +#define GL_DRAW_BUFFER12 0x8831 +#define GL_DRAW_BUFFER13 0x8832 +#define GL_DRAW_BUFFER14 0x8833 +#define GL_DRAW_BUFFER15 0x8834 +#define GL_BLEND_EQUATION_ALPHA 0x883D +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A +#define GL_MAX_VARYING_FLOATS 0x8B4B +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_SHADER_TYPE 0x8B4F +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_SAMPLER_1D 0x8B5D +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_3D 0x8B5F +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLER_1D_SHADOW 0x8B61 +#define GL_SAMPLER_2D_SHADOW 0x8B62 +#define GL_DELETE_STATUS 0x8B80 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_LINK_STATUS 0x8B82 +#define GL_VALIDATE_STATUS 0x8B83 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_ATTACHED_SHADERS 0x8B85 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#define GL_CURRENT_PROGRAM 0x8B8D +#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0 +#define GL_LOWER_LEFT 0x8CA1 +#define GL_UPPER_LEFT 0x8CA2 +#define GL_STENCIL_BACK_REF 0x8CA3 +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLDRAWBUFFERSPROC) (GLsizei n, const GLenum *bufs); +typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC) (GLenum face, GLenum func, GLint ref, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC) (GLenum face, GLuint mask); +typedef void (APIENTRYP PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader); +typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC) (GLuint program, GLuint index, const GLchar *name); +typedef void (APIENTRYP PFNGLCOMPILESHADERPROC) (GLuint shader); +typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC) (void); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC) (GLenum type); +typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLDELETESHADERPROC) (GLuint shader); +typedef void (APIENTRYP PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader); +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index); +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index); +typedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC) (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); +typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void (APIENTRYP PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); +typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETUNIFORMFVPROC) (GLuint program, GLint location, GLfloat *params); +typedef void (APIENTRYP PFNGLGETUNIFORMIVPROC) (GLuint program, GLint location, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum pname, void **pointer); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC) (GLuint program); +typedef GLboolean (APIENTRYP PFNGLISSHADERPROC) (GLuint shader); +typedef void (APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); +typedef void (APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLUNIFORM2FPROC) (GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLUNIFORM3FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLUNIFORM4FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLUNIFORM1IPROC) (GLint location, GLint v0); +typedef void (APIENTRYP PFNGLUNIFORM2IPROC) (GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLUNIFORM3IPROC) (GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLUNIFORM4IPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLUNIFORM1FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM2FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM3FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM4FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM1IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM2IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM3IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM4IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC) (GLuint index, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SPROC) (GLuint index, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC) (GLuint index, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SPROC) (GLuint index, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SPROC) (GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha); +GLAPI void APIENTRY glDrawBuffers (GLsizei n, const GLenum *bufs); +GLAPI void APIENTRY glStencilOpSeparate (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +GLAPI void APIENTRY glStencilFuncSeparate (GLenum face, GLenum func, GLint ref, GLuint mask); +GLAPI void APIENTRY glStencilMaskSeparate (GLenum face, GLuint mask); +GLAPI void APIENTRY glAttachShader (GLuint program, GLuint shader); +GLAPI void APIENTRY glBindAttribLocation (GLuint program, GLuint index, const GLchar *name); +GLAPI void APIENTRY glCompileShader (GLuint shader); +GLAPI GLuint APIENTRY glCreateProgram (void); +GLAPI GLuint APIENTRY glCreateShader (GLenum type); +GLAPI void APIENTRY glDeleteProgram (GLuint program); +GLAPI void APIENTRY glDeleteShader (GLuint shader); +GLAPI void APIENTRY glDetachShader (GLuint program, GLuint shader); +GLAPI void APIENTRY glDisableVertexAttribArray (GLuint index); +GLAPI void APIENTRY glEnableVertexAttribArray (GLuint index); +GLAPI void APIENTRY glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glGetActiveUniform (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glGetAttachedShaders (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); +GLAPI GLint APIENTRY glGetAttribLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI void APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI void APIENTRY glGetShaderSource (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); +GLAPI GLint APIENTRY glGetUniformLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGetUniformfv (GLuint program, GLint location, GLfloat *params); +GLAPI void APIENTRY glGetUniformiv (GLuint program, GLint location, GLint *params); +GLAPI void APIENTRY glGetVertexAttribdv (GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetVertexAttribfv (GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, void **pointer); +GLAPI GLboolean APIENTRY glIsProgram (GLuint program); +GLAPI GLboolean APIENTRY glIsShader (GLuint shader); +GLAPI void APIENTRY glLinkProgram (GLuint program); +GLAPI void APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); +GLAPI void APIENTRY glUseProgram (GLuint program); +GLAPI void APIENTRY glUniform1f (GLint location, GLfloat v0); +GLAPI void APIENTRY glUniform2f (GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glUniform3f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glUniform4f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glUniform1i (GLint location, GLint v0); +GLAPI void APIENTRY glUniform2i (GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glUniform3i (GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glUniform4i (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glUniform1fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform2fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform3fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform4fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform1iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform2iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform3iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform4iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniformMatrix2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glValidateProgram (GLuint program); +GLAPI void APIENTRY glVertexAttrib1d (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttrib1dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib1f (GLuint index, GLfloat x); +GLAPI void APIENTRY glVertexAttrib1fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib1s (GLuint index, GLshort x); +GLAPI void APIENTRY glVertexAttrib1sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib2d (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttrib2dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib2f (GLuint index, GLfloat x, GLfloat y); +GLAPI void APIENTRY glVertexAttrib2fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib2s (GLuint index, GLshort x, GLshort y); +GLAPI void APIENTRY glVertexAttrib2sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib3d (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttrib3dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib3f (GLuint index, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glVertexAttrib3fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib3s (GLuint index, GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glVertexAttrib3sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4Nbv (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4Niv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4Nsv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4Nub (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +GLAPI void APIENTRY glVertexAttrib4Nubv (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4Nuiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4Nusv (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttrib4bv (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttrib4dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib4f (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glVertexAttrib4fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib4iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4s (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glVertexAttrib4sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4ubv (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4usv (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +#endif +#endif /* GL_VERSION_2_0 */ + +#ifndef GL_VERSION_2_1 +#define GL_VERSION_2_1 1 +#define GL_PIXEL_PACK_BUFFER 0x88EB +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF +#define GL_FLOAT_MAT2x3 0x8B65 +#define GL_FLOAT_MAT2x4 0x8B66 +#define GL_FLOAT_MAT3x2 0x8B67 +#define GL_FLOAT_MAT3x4 0x8B68 +#define GL_FLOAT_MAT4x2 0x8B69 +#define GL_FLOAT_MAT4x3 0x8B6A +#define GL_SRGB 0x8C40 +#define GL_SRGB8 0x8C41 +#define GL_SRGB_ALPHA 0x8C42 +#define GL_SRGB8_ALPHA8 0x8C43 +#define GL_COMPRESSED_SRGB 0x8C48 +#define GL_COMPRESSED_SRGB_ALPHA 0x8C49 +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUniformMatrix2x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix2x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +#endif +#endif /* GL_VERSION_2_1 */ + +#ifndef GL_VERSION_3_0 +#define GL_VERSION_3_0 1 +typedef unsigned short GLhalf; +#define GL_COMPARE_REF_TO_TEXTURE 0x884E +#define GL_CLIP_DISTANCE0 0x3000 +#define GL_CLIP_DISTANCE1 0x3001 +#define GL_CLIP_DISTANCE2 0x3002 +#define GL_CLIP_DISTANCE3 0x3003 +#define GL_CLIP_DISTANCE4 0x3004 +#define GL_CLIP_DISTANCE5 0x3005 +#define GL_CLIP_DISTANCE6 0x3006 +#define GL_CLIP_DISTANCE7 0x3007 +#define GL_MAX_CLIP_DISTANCES 0x0D32 +#define GL_MAJOR_VERSION 0x821B +#define GL_MINOR_VERSION 0x821C +#define GL_NUM_EXTENSIONS 0x821D +#define GL_CONTEXT_FLAGS 0x821E +#define GL_COMPRESSED_RED 0x8225 +#define GL_COMPRESSED_RG 0x8226 +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 +#define GL_RGBA32F 0x8814 +#define GL_RGB32F 0x8815 +#define GL_RGBA16F 0x881A +#define GL_RGB16F 0x881B +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD +#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF +#define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905 +#define GL_CLAMP_READ_COLOR 0x891C +#define GL_FIXED_ONLY 0x891D +#define GL_MAX_VARYING_COMPONENTS 0x8B4B +#define GL_TEXTURE_1D_ARRAY 0x8C18 +#define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19 +#define GL_TEXTURE_2D_ARRAY 0x8C1A +#define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B +#define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C +#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D +#define GL_R11F_G11F_B10F 0x8C3A +#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B +#define GL_RGB9_E5 0x8C3D +#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E +#define GL_TEXTURE_SHARED_SIZE 0x8C3F +#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76 +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80 +#define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85 +#define GL_PRIMITIVES_GENERATED 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88 +#define GL_RASTERIZER_DISCARD 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B +#define GL_INTERLEAVED_ATTRIBS 0x8C8C +#define GL_SEPARATE_ATTRIBS 0x8C8D +#define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F +#define GL_RGBA32UI 0x8D70 +#define GL_RGB32UI 0x8D71 +#define GL_RGBA16UI 0x8D76 +#define GL_RGB16UI 0x8D77 +#define GL_RGBA8UI 0x8D7C +#define GL_RGB8UI 0x8D7D +#define GL_RGBA32I 0x8D82 +#define GL_RGB32I 0x8D83 +#define GL_RGBA16I 0x8D88 +#define GL_RGB16I 0x8D89 +#define GL_RGBA8I 0x8D8E +#define GL_RGB8I 0x8D8F +#define GL_RED_INTEGER 0x8D94 +#define GL_GREEN_INTEGER 0x8D95 +#define GL_BLUE_INTEGER 0x8D96 +#define GL_RGB_INTEGER 0x8D98 +#define GL_RGBA_INTEGER 0x8D99 +#define GL_BGR_INTEGER 0x8D9A +#define GL_BGRA_INTEGER 0x8D9B +#define GL_SAMPLER_1D_ARRAY 0x8DC0 +#define GL_SAMPLER_2D_ARRAY 0x8DC1 +#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3 +#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 +#define GL_SAMPLER_CUBE_SHADOW 0x8DC5 +#define GL_UNSIGNED_INT_VEC2 0x8DC6 +#define GL_UNSIGNED_INT_VEC3 0x8DC7 +#define GL_UNSIGNED_INT_VEC4 0x8DC8 +#define GL_INT_SAMPLER_1D 0x8DC9 +#define GL_INT_SAMPLER_2D 0x8DCA +#define GL_INT_SAMPLER_3D 0x8DCB +#define GL_INT_SAMPLER_CUBE 0x8DCC +#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE +#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF +#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 +#define GL_QUERY_WAIT 0x8E13 +#define GL_QUERY_NO_WAIT 0x8E14 +#define GL_QUERY_BY_REGION_WAIT 0x8E15 +#define GL_QUERY_BY_REGION_NO_WAIT 0x8E16 +#define GL_BUFFER_ACCESS_FLAGS 0x911F +#define GL_BUFFER_MAP_LENGTH 0x9120 +#define GL_BUFFER_MAP_OFFSET 0x9121 +#define GL_DEPTH_COMPONENT32F 0x8CAC +#define GL_DEPTH32F_STENCIL8 0x8CAD +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 +#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211 +#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 +#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 +#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 +#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 +#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 +#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 +#define GL_FRAMEBUFFER_DEFAULT 0x8218 +#define GL_FRAMEBUFFER_UNDEFINED 0x8219 +#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 +#define GL_DEPTH_STENCIL 0x84F9 +#define GL_UNSIGNED_INT_24_8 0x84FA +#define GL_DEPTH24_STENCIL8 0x88F0 +#define GL_TEXTURE_STENCIL_SIZE 0x88F1 +#define GL_TEXTURE_RED_TYPE 0x8C10 +#define GL_TEXTURE_GREEN_TYPE 0x8C11 +#define GL_TEXTURE_BLUE_TYPE 0x8C12 +#define GL_TEXTURE_ALPHA_TYPE 0x8C13 +#define GL_TEXTURE_DEPTH_TYPE 0x8C16 +#define GL_UNSIGNED_NORMALIZED 0x8C17 +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#define GL_READ_FRAMEBUFFER 0x8CA8 +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA +#define GL_RENDERBUFFER_SAMPLES 0x8CAB +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_COLOR_ATTACHMENT1 0x8CE1 +#define GL_COLOR_ATTACHMENT2 0x8CE2 +#define GL_COLOR_ATTACHMENT3 0x8CE3 +#define GL_COLOR_ATTACHMENT4 0x8CE4 +#define GL_COLOR_ATTACHMENT5 0x8CE5 +#define GL_COLOR_ATTACHMENT6 0x8CE6 +#define GL_COLOR_ATTACHMENT7 0x8CE7 +#define GL_COLOR_ATTACHMENT8 0x8CE8 +#define GL_COLOR_ATTACHMENT9 0x8CE9 +#define GL_COLOR_ATTACHMENT10 0x8CEA +#define GL_COLOR_ATTACHMENT11 0x8CEB +#define GL_COLOR_ATTACHMENT12 0x8CEC +#define GL_COLOR_ATTACHMENT13 0x8CED +#define GL_COLOR_ATTACHMENT14 0x8CEE +#define GL_COLOR_ATTACHMENT15 0x8CEF +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_FRAMEBUFFER 0x8D40 +#define GL_RENDERBUFFER 0x8D41 +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#define GL_STENCIL_INDEX1 0x8D46 +#define GL_STENCIL_INDEX4 0x8D47 +#define GL_STENCIL_INDEX8 0x8D48 +#define GL_STENCIL_INDEX16 0x8D49 +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 +#define GL_MAX_SAMPLES 0x8D57 +#define GL_FRAMEBUFFER_SRGB 0x8DB9 +#define GL_HALF_FLOAT 0x140B +#define GL_MAP_READ_BIT 0x0001 +#define GL_MAP_WRITE_BIT 0x0002 +#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 +#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 +#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 +#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 +#define GL_COMPRESSED_RED_RGTC1 0x8DBB +#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC +#define GL_COMPRESSED_RG_RGTC2 0x8DBD +#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE +#define GL_RG 0x8227 +#define GL_RG_INTEGER 0x8228 +#define GL_R8 0x8229 +#define GL_R16 0x822A +#define GL_RG8 0x822B +#define GL_RG16 0x822C +#define GL_R16F 0x822D +#define GL_R32F 0x822E +#define GL_RG16F 0x822F +#define GL_RG32F 0x8230 +#define GL_R8I 0x8231 +#define GL_R8UI 0x8232 +#define GL_R16I 0x8233 +#define GL_R16UI 0x8234 +#define GL_R32I 0x8235 +#define GL_R32UI 0x8236 +#define GL_RG8I 0x8237 +#define GL_RG8UI 0x8238 +#define GL_RG16I 0x8239 +#define GL_RG16UI 0x823A +#define GL_RG32I 0x823B +#define GL_RG32UI 0x823C +#define GL_VERTEX_ARRAY_BINDING 0x85B5 +typedef void (APIENTRYP PFNGLCOLORMASKIPROC) (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC) (GLenum target, GLuint index, GLboolean *data); +typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC) (GLenum target, GLuint index, GLint *data); +typedef void (APIENTRYP PFNGLENABLEIPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLDISABLEIPROC) (GLenum target, GLuint index); +typedef GLboolean (APIENTRYP PFNGLISENABLEDIPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKPROC) (GLenum primitiveMode); +typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC) (void); +typedef void (APIENTRYP PFNGLBINDBUFFERRANGEPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC) (GLenum target, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC) (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLCLAMPCOLORPROC) (GLenum target, GLenum clamp); +typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERPROC) (GLuint id, GLenum mode); +typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC) (void); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVPROC) (GLuint index, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IPROC) (GLuint index, GLint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IPROC) (GLuint index, GLint x, GLint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IPROC) (GLuint index, GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IPROC) (GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIPROC) (GLuint index, GLuint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIPROC) (GLuint index, GLuint x, GLuint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLGETUNIFORMUIVPROC) (GLuint program, GLint location, GLuint *params); +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONPROC) (GLuint program, GLuint color, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLUNIFORM1UIPROC) (GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLUNIFORM2UIPROC) (GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLUNIFORM3UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLUNIFORM4UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLUNIFORM1UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM2UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM3UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM4UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLCLEARBUFFERIVPROC) (GLenum buffer, GLint drawbuffer, const GLint *value); +typedef void (APIENTRYP PFNGLCLEARBUFFERUIVPROC) (GLenum buffer, GLint drawbuffer, const GLuint *value); +typedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC) (GLenum buffer, GLint drawbuffer, const GLfloat *value); +typedef void (APIENTRYP PFNGLCLEARBUFFERFIPROC) (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGIPROC) (GLenum name, GLuint index); +typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC) (GLuint renderbuffer); +typedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC) (GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC) (GLsizei n, const GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC) (GLuint framebuffer); +typedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC) (GLenum target, GLuint framebuffer); +typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC) (GLsizei n, const GLuint *framebuffers); +typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); +typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC) (GLenum target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC) (GLenum target); +typedef void (APIENTRYP PFNGLBLITFRAMEBUFFERPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void *(APIENTRYP PFNGLMAPBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC) (GLuint array); +typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *arrays); +typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays); +typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYPROC) (GLuint array); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorMaski (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +GLAPI void APIENTRY glGetBooleani_v (GLenum target, GLuint index, GLboolean *data); +GLAPI void APIENTRY glGetIntegeri_v (GLenum target, GLuint index, GLint *data); +GLAPI void APIENTRY glEnablei (GLenum target, GLuint index); +GLAPI void APIENTRY glDisablei (GLenum target, GLuint index); +GLAPI GLboolean APIENTRY glIsEnabledi (GLenum target, GLuint index); +GLAPI void APIENTRY glBeginTransformFeedback (GLenum primitiveMode); +GLAPI void APIENTRY glEndTransformFeedback (void); +GLAPI void APIENTRY glBindBufferRange (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glBindBufferBase (GLenum target, GLuint index, GLuint buffer); +GLAPI void APIENTRY glTransformFeedbackVaryings (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); +GLAPI void APIENTRY glGetTransformFeedbackVarying (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glClampColor (GLenum target, GLenum clamp); +GLAPI void APIENTRY glBeginConditionalRender (GLuint id, GLenum mode); +GLAPI void APIENTRY glEndConditionalRender (void); +GLAPI void APIENTRY glVertexAttribIPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glGetVertexAttribIiv (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribIuiv (GLuint index, GLenum pname, GLuint *params); +GLAPI void APIENTRY glVertexAttribI1i (GLuint index, GLint x); +GLAPI void APIENTRY glVertexAttribI2i (GLuint index, GLint x, GLint y); +GLAPI void APIENTRY glVertexAttribI3i (GLuint index, GLint x, GLint y, GLint z); +GLAPI void APIENTRY glVertexAttribI4i (GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glVertexAttribI1ui (GLuint index, GLuint x); +GLAPI void APIENTRY glVertexAttribI2ui (GLuint index, GLuint x, GLuint y); +GLAPI void APIENTRY glVertexAttribI3ui (GLuint index, GLuint x, GLuint y, GLuint z); +GLAPI void APIENTRY glVertexAttribI4ui (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glVertexAttribI1iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI2iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI3iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI4iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI1uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI2uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI3uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4bv (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttribI4sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttribI4ubv (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttribI4usv (GLuint index, const GLushort *v); +GLAPI void APIENTRY glGetUniformuiv (GLuint program, GLint location, GLuint *params); +GLAPI void APIENTRY glBindFragDataLocation (GLuint program, GLuint color, const GLchar *name); +GLAPI GLint APIENTRY glGetFragDataLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glUniform1ui (GLint location, GLuint v0); +GLAPI void APIENTRY glUniform2ui (GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glUniform3ui (GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glUniform4ui (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glUniform1uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform2uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform3uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform4uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glTexParameterIiv (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTexParameterIuiv (GLenum target, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glGetTexParameterIiv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTexParameterIuiv (GLenum target, GLenum pname, GLuint *params); +GLAPI void APIENTRY glClearBufferiv (GLenum buffer, GLint drawbuffer, const GLint *value); +GLAPI void APIENTRY glClearBufferuiv (GLenum buffer, GLint drawbuffer, const GLuint *value); +GLAPI void APIENTRY glClearBufferfv (GLenum buffer, GLint drawbuffer, const GLfloat *value); +GLAPI void APIENTRY glClearBufferfi (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +GLAPI const GLubyte *APIENTRY glGetStringi (GLenum name, GLuint index); +GLAPI GLboolean APIENTRY glIsRenderbuffer (GLuint renderbuffer); +GLAPI void APIENTRY glBindRenderbuffer (GLenum target, GLuint renderbuffer); +GLAPI void APIENTRY glDeleteRenderbuffers (GLsizei n, const GLuint *renderbuffers); +GLAPI void APIENTRY glGenRenderbuffers (GLsizei n, GLuint *renderbuffers); +GLAPI void APIENTRY glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetRenderbufferParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI GLboolean APIENTRY glIsFramebuffer (GLuint framebuffer); +GLAPI void APIENTRY glBindFramebuffer (GLenum target, GLuint framebuffer); +GLAPI void APIENTRY glDeleteFramebuffers (GLsizei n, const GLuint *framebuffers); +GLAPI void APIENTRY glGenFramebuffers (GLsizei n, GLuint *framebuffers); +GLAPI GLenum APIENTRY glCheckFramebufferStatus (GLenum target); +GLAPI void APIENTRY glFramebufferTexture1D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture3D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +GLAPI void APIENTRY glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GLAPI void APIENTRY glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, GLenum pname, GLint *params); +GLAPI void APIENTRY glGenerateMipmap (GLenum target); +GLAPI void APIENTRY glBlitFramebuffer (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +GLAPI void APIENTRY glRenderbufferStorageMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glFramebufferTextureLayer (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI void *APIENTRY glMapBufferRange (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +GLAPI void APIENTRY glFlushMappedBufferRange (GLenum target, GLintptr offset, GLsizeiptr length); +GLAPI void APIENTRY glBindVertexArray (GLuint array); +GLAPI void APIENTRY glDeleteVertexArrays (GLsizei n, const GLuint *arrays); +GLAPI void APIENTRY glGenVertexArrays (GLsizei n, GLuint *arrays); +GLAPI GLboolean APIENTRY glIsVertexArray (GLuint array); +#endif +#endif /* GL_VERSION_3_0 */ + +#ifndef GL_VERSION_3_1 +#define GL_VERSION_3_1 1 +#define GL_SAMPLER_2D_RECT 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64 +#define GL_SAMPLER_BUFFER 0x8DC2 +#define GL_INT_SAMPLER_2D_RECT 0x8DCD +#define GL_INT_SAMPLER_BUFFER 0x8DD0 +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 +#define GL_TEXTURE_BUFFER 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D +#define GL_TEXTURE_RECTANGLE 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8 +#define GL_R8_SNORM 0x8F94 +#define GL_RG8_SNORM 0x8F95 +#define GL_RGB8_SNORM 0x8F96 +#define GL_RGBA8_SNORM 0x8F97 +#define GL_R16_SNORM 0x8F98 +#define GL_RG16_SNORM 0x8F99 +#define GL_RGB16_SNORM 0x8F9A +#define GL_RGBA16_SNORM 0x8F9B +#define GL_SIGNED_NORMALIZED 0x8F9C +#define GL_PRIMITIVE_RESTART 0x8F9D +#define GL_PRIMITIVE_RESTART_INDEX 0x8F9E +#define GL_COPY_READ_BUFFER 0x8F36 +#define GL_COPY_WRITE_BUFFER 0x8F37 +#define GL_UNIFORM_BUFFER 0x8A11 +#define GL_UNIFORM_BUFFER_BINDING 0x8A28 +#define GL_UNIFORM_BUFFER_START 0x8A29 +#define GL_UNIFORM_BUFFER_SIZE 0x8A2A +#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B +#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C +#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D +#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E +#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F +#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 +#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 +#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 +#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 +#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 +#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 +#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 +#define GL_UNIFORM_TYPE 0x8A37 +#define GL_UNIFORM_SIZE 0x8A38 +#define GL_UNIFORM_NAME_LENGTH 0x8A39 +#define GL_UNIFORM_BLOCK_INDEX 0x8A3A +#define GL_UNIFORM_OFFSET 0x8A3B +#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C +#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D +#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E +#define GL_UNIFORM_BLOCK_BINDING 0x8A3F +#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 +#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 +#define GL_INVALID_INDEX 0xFFFFFFFFu +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); +typedef void (APIENTRYP PFNGLTEXBUFFERPROC) (GLenum target, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXPROC) (GLuint index); +typedef void (APIENTRYP PFNGLCOPYBUFFERSUBDATAPROC) (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC) (GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC) (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMNAMEPROC) (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); +typedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC) (GLuint program, const GLchar *uniformBlockName); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKIVPROC) (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); +typedef void (APIENTRYP PFNGLUNIFORMBLOCKBINDINGPROC) (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstanced (GLenum mode, GLint first, GLsizei count, GLsizei instancecount); +GLAPI void APIENTRY glDrawElementsInstanced (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); +GLAPI void APIENTRY glTexBuffer (GLenum target, GLenum internalformat, GLuint buffer); +GLAPI void APIENTRY glPrimitiveRestartIndex (GLuint index); +GLAPI void APIENTRY glCopyBufferSubData (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +GLAPI void APIENTRY glGetUniformIndices (GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); +GLAPI void APIENTRY glGetActiveUniformsiv (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetActiveUniformName (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); +GLAPI GLuint APIENTRY glGetUniformBlockIndex (GLuint program, const GLchar *uniformBlockName); +GLAPI void APIENTRY glGetActiveUniformBlockiv (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetActiveUniformBlockName (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); +GLAPI void APIENTRY glUniformBlockBinding (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +#endif +#endif /* GL_VERSION_3_1 */ + +#ifndef GL_VERSION_3_2 +#define GL_VERSION_3_2 1 +typedef struct __GLsync *GLsync; +#ifndef GLEXT_64_TYPES_DEFINED +/* This code block is duplicated in glxext.h, so must be protected */ +#define GLEXT_64_TYPES_DEFINED +/* Define int32_t, int64_t, and uint64_t types for UST/MSC */ +/* (as used in the GL_EXT_timer_query extension). */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#include +#elif defined(__sun__) || defined(__digital__) +#include +#if defined(__STDC__) +#if defined(__arch64__) || defined(_LP64) +typedef long int int64_t; +typedef unsigned long int uint64_t; +#else +typedef long long int int64_t; +typedef unsigned long long int uint64_t; +#endif /* __arch64__ */ +#endif /* __STDC__ */ +#elif defined( __VMS ) || defined(__sgi) +#include +#elif defined(__SCO__) || defined(__USLC__) +#include +#elif defined(__UNIXOS2__) || defined(__SOL64__) +typedef long int int32_t; +typedef long long int int64_t; +typedef unsigned long long int uint64_t; +#elif defined(_WIN32) && defined(__GNUC__) +#include +#elif defined(_WIN32) +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +/* Fallback if nothing above works */ +#include +#endif +#endif +typedef uint64_t GLuint64; +typedef int64_t GLint64; +#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 +#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 +#define GL_LINES_ADJACENCY 0x000A +#define GL_LINE_STRIP_ADJACENCY 0x000B +#define GL_TRIANGLES_ADJACENCY 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY 0x000D +#define GL_PROGRAM_POINT_SIZE 0x8642 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8 +#define GL_GEOMETRY_SHADER 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT 0x8916 +#define GL_GEOMETRY_INPUT_TYPE 0x8917 +#define GL_GEOMETRY_OUTPUT_TYPE 0x8918 +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1 +#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122 +#define GL_MAX_GEOMETRY_INPUT_COMPONENTS 0x9123 +#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124 +#define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125 +#define GL_CONTEXT_PROFILE_MASK 0x9126 +#define GL_DEPTH_CLAMP 0x864F +#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C +#define GL_FIRST_VERTEX_CONVENTION 0x8E4D +#define GL_LAST_VERTEX_CONVENTION 0x8E4E +#define GL_PROVOKING_VERTEX 0x8E4F +#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F +#define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111 +#define GL_OBJECT_TYPE 0x9112 +#define GL_SYNC_CONDITION 0x9113 +#define GL_SYNC_STATUS 0x9114 +#define GL_SYNC_FLAGS 0x9115 +#define GL_SYNC_FENCE 0x9116 +#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 +#define GL_UNSIGNALED 0x9118 +#define GL_SIGNALED 0x9119 +#define GL_ALREADY_SIGNALED 0x911A +#define GL_TIMEOUT_EXPIRED 0x911B +#define GL_CONDITION_SATISFIED 0x911C +#define GL_WAIT_FAILED 0x911D +#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull +#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 +#define GL_SAMPLE_POSITION 0x8E50 +#define GL_SAMPLE_MASK 0x8E51 +#define GL_SAMPLE_MASK_VALUE 0x8E52 +#define GL_MAX_SAMPLE_MASK_WORDS 0x8E59 +#define GL_TEXTURE_2D_MULTISAMPLE 0x9100 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101 +#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105 +#define GL_TEXTURE_SAMPLES 0x9106 +#define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107 +#define GL_SAMPLER_2D_MULTISAMPLE 0x9108 +#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A +#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B +#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D +#define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E +#define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F +#define GL_MAX_INTEGER_SAMPLES 0x9110 +typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex); +typedef void (APIENTRYP PFNGLPROVOKINGVERTEXPROC) (GLenum mode); +typedef GLsync (APIENTRYP PFNGLFENCESYNCPROC) (GLenum condition, GLbitfield flags); +typedef GLboolean (APIENTRYP PFNGLISSYNCPROC) (GLsync sync); +typedef void (APIENTRYP PFNGLDELETESYNCPROC) (GLsync sync); +typedef GLenum (APIENTRYP PFNGLCLIENTWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void (APIENTRYP PFNGLWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void (APIENTRYP PFNGLGETINTEGER64VPROC) (GLenum pname, GLint64 *data); +typedef void (APIENTRYP PFNGLGETSYNCIVPROC) (GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); +typedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC) (GLenum target, GLuint index, GLint64 *data); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERI64VPROC) (GLenum target, GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVPROC) (GLenum pname, GLuint index, GLfloat *val); +typedef void (APIENTRYP PFNGLSAMPLEMASKIPROC) (GLuint maskNumber, GLbitfield mask); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawElementsBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); +GLAPI void APIENTRY glDrawRangeElementsBaseVertex (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex); +GLAPI void APIENTRY glDrawElementsInstancedBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex); +GLAPI void APIENTRY glMultiDrawElementsBaseVertex (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex); +GLAPI void APIENTRY glProvokingVertex (GLenum mode); +GLAPI GLsync APIENTRY glFenceSync (GLenum condition, GLbitfield flags); +GLAPI GLboolean APIENTRY glIsSync (GLsync sync); +GLAPI void APIENTRY glDeleteSync (GLsync sync); +GLAPI GLenum APIENTRY glClientWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); +GLAPI void APIENTRY glWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); +GLAPI void APIENTRY glGetInteger64v (GLenum pname, GLint64 *data); +GLAPI void APIENTRY glGetSynciv (GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); +GLAPI void APIENTRY glGetInteger64i_v (GLenum target, GLuint index, GLint64 *data); +GLAPI void APIENTRY glGetBufferParameteri64v (GLenum target, GLenum pname, GLint64 *params); +GLAPI void APIENTRY glFramebufferTexture (GLenum target, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glTexImage2DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTexImage3DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glGetMultisamplefv (GLenum pname, GLuint index, GLfloat *val); +GLAPI void APIENTRY glSampleMaski (GLuint maskNumber, GLbitfield mask); +#endif +#endif /* GL_VERSION_3_2 */ + +#ifndef GL_VERSION_3_3 +#define GL_VERSION_3_3 1 +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE +#define GL_SRC1_COLOR 0x88F9 +#define GL_ONE_MINUS_SRC1_COLOR 0x88FA +#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB +#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC +#define GL_ANY_SAMPLES_PASSED 0x8C2F +#define GL_SAMPLER_BINDING 0x8919 +#define GL_RGB10_A2UI 0x906F +#define GL_TEXTURE_SWIZZLE_R 0x8E42 +#define GL_TEXTURE_SWIZZLE_G 0x8E43 +#define GL_TEXTURE_SWIZZLE_B 0x8E44 +#define GL_TEXTURE_SWIZZLE_A 0x8E45 +#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 +#define GL_TIME_ELAPSED 0x88BF +#define GL_TIMESTAMP 0x8E28 +#define GL_INT_2_10_10_10_REV 0x8D9F +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONINDEXEDPROC) (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATAINDEXPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGENSAMPLERSPROC) (GLsizei count, GLuint *samplers); +typedef void (APIENTRYP PFNGLDELETESAMPLERSPROC) (GLsizei count, const GLuint *samplers); +typedef GLboolean (APIENTRYP PFNGLISSAMPLERPROC) (GLuint sampler); +typedef void (APIENTRYP PFNGLBINDSAMPLERPROC) (GLuint unit, GLuint sampler); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIPROC) (GLuint sampler, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFPROC) (GLuint sampler, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, const GLfloat *param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, const GLuint *param); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLQUERYCOUNTERPROC) (GLuint id, GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VPROC) (GLuint id, GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VPROC) (GLuint id, GLenum pname, GLuint64 *params); +typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORPROC) (GLuint index, GLuint divisor); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindFragDataLocationIndexed (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); +GLAPI GLint APIENTRY glGetFragDataIndex (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGenSamplers (GLsizei count, GLuint *samplers); +GLAPI void APIENTRY glDeleteSamplers (GLsizei count, const GLuint *samplers); +GLAPI GLboolean APIENTRY glIsSampler (GLuint sampler); +GLAPI void APIENTRY glBindSampler (GLuint unit, GLuint sampler); +GLAPI void APIENTRY glSamplerParameteri (GLuint sampler, GLenum pname, GLint param); +GLAPI void APIENTRY glSamplerParameteriv (GLuint sampler, GLenum pname, const GLint *param); +GLAPI void APIENTRY glSamplerParameterf (GLuint sampler, GLenum pname, GLfloat param); +GLAPI void APIENTRY glSamplerParameterfv (GLuint sampler, GLenum pname, const GLfloat *param); +GLAPI void APIENTRY glSamplerParameterIiv (GLuint sampler, GLenum pname, const GLint *param); +GLAPI void APIENTRY glSamplerParameterIuiv (GLuint sampler, GLenum pname, const GLuint *param); +GLAPI void APIENTRY glGetSamplerParameteriv (GLuint sampler, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSamplerParameterIiv (GLuint sampler, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSamplerParameterfv (GLuint sampler, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetSamplerParameterIuiv (GLuint sampler, GLenum pname, GLuint *params); +GLAPI void APIENTRY glQueryCounter (GLuint id, GLenum target); +GLAPI void APIENTRY glGetQueryObjecti64v (GLuint id, GLenum pname, GLint64 *params); +GLAPI void APIENTRY glGetQueryObjectui64v (GLuint id, GLenum pname, GLuint64 *params); +GLAPI void APIENTRY glVertexAttribDivisor (GLuint index, GLuint divisor); +GLAPI void APIENTRY glVertexAttribP1ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP1uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexAttribP2ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP2uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexAttribP3ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP3uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexAttribP4ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP4uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +#endif +#endif /* GL_VERSION_3_3 */ + +#ifndef GL_VERSION_4_0 +#define GL_VERSION_4_0 1 +#define GL_SAMPLE_SHADING 0x8C36 +#define GL_MIN_SAMPLE_SHADING_VALUE 0x8C37 +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5F +#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY 0x900B +#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F +#define GL_DRAW_INDIRECT_BUFFER 0x8F3F +#define GL_DRAW_INDIRECT_BUFFER_BINDING 0x8F43 +#define GL_GEOMETRY_SHADER_INVOCATIONS 0x887F +#define GL_MAX_GEOMETRY_SHADER_INVOCATIONS 0x8E5A +#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET 0x8E5B +#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET 0x8E5C +#define GL_FRAGMENT_INTERPOLATION_OFFSET_BITS 0x8E5D +#define GL_MAX_VERTEX_STREAMS 0x8E71 +#define GL_DOUBLE_VEC2 0x8FFC +#define GL_DOUBLE_VEC3 0x8FFD +#define GL_DOUBLE_VEC4 0x8FFE +#define GL_DOUBLE_MAT2 0x8F46 +#define GL_DOUBLE_MAT3 0x8F47 +#define GL_DOUBLE_MAT4 0x8F48 +#define GL_DOUBLE_MAT2x3 0x8F49 +#define GL_DOUBLE_MAT2x4 0x8F4A +#define GL_DOUBLE_MAT3x2 0x8F4B +#define GL_DOUBLE_MAT3x4 0x8F4C +#define GL_DOUBLE_MAT4x2 0x8F4D +#define GL_DOUBLE_MAT4x3 0x8F4E +#define GL_ACTIVE_SUBROUTINES 0x8DE5 +#define GL_ACTIVE_SUBROUTINE_UNIFORMS 0x8DE6 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS 0x8E47 +#define GL_ACTIVE_SUBROUTINE_MAX_LENGTH 0x8E48 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH 0x8E49 +#define GL_MAX_SUBROUTINES 0x8DE7 +#define GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS 0x8DE8 +#define GL_NUM_COMPATIBLE_SUBROUTINES 0x8E4A +#define GL_COMPATIBLE_SUBROUTINES 0x8E4B +#define GL_PATCHES 0x000E +#define GL_PATCH_VERTICES 0x8E72 +#define GL_PATCH_DEFAULT_INNER_LEVEL 0x8E73 +#define GL_PATCH_DEFAULT_OUTER_LEVEL 0x8E74 +#define GL_TESS_CONTROL_OUTPUT_VERTICES 0x8E75 +#define GL_TESS_GEN_MODE 0x8E76 +#define GL_TESS_GEN_SPACING 0x8E77 +#define GL_TESS_GEN_VERTEX_ORDER 0x8E78 +#define GL_TESS_GEN_POINT_MODE 0x8E79 +#define GL_ISOLINES 0x8E7A +#define GL_FRACTIONAL_ODD 0x8E7B +#define GL_FRACTIONAL_EVEN 0x8E7C +#define GL_MAX_PATCH_VERTICES 0x8E7D +#define GL_MAX_TESS_GEN_LEVEL 0x8E7E +#define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E7F +#define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E80 +#define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS 0x8E81 +#define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS 0x8E82 +#define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS 0x8E83 +#define GL_MAX_TESS_PATCH_COMPONENTS 0x8E84 +#define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS 0x8E85 +#define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS 0x8E86 +#define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS 0x8E89 +#define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS 0x8E8A +#define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS 0x886C +#define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS 0x886D +#define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E1E +#define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E1F +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER 0x84F0 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER 0x84F1 +#define GL_TESS_EVALUATION_SHADER 0x8E87 +#define GL_TESS_CONTROL_SHADER 0x8E88 +#define GL_TRANSFORM_FEEDBACK 0x8E22 +#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED 0x8E23 +#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE 0x8E24 +#define GL_TRANSFORM_FEEDBACK_BINDING 0x8E25 +#define GL_MAX_TRANSFORM_FEEDBACK_BUFFERS 0x8E70 +typedef void (APIENTRYP PFNGLMINSAMPLESHADINGPROC) (GLfloat value); +typedef void (APIENTRYP PFNGLBLENDEQUATIONIPROC) (GLuint buf, GLenum mode); +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLBLENDFUNCIPROC) (GLuint buf, GLenum src, GLenum dst); +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +typedef void (APIENTRYP PFNGLDRAWARRAYSINDIRECTPROC) (GLenum mode, const void *indirect); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const void *indirect); +typedef void (APIENTRYP PFNGLUNIFORM1DPROC) (GLint location, GLdouble x); +typedef void (APIENTRYP PFNGLUNIFORM2DPROC) (GLint location, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLUNIFORM3DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLUNIFORM4DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLUNIFORM1DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORM2DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORM3DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORM4DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLGETUNIFORMDVPROC) (GLuint program, GLint location, GLdouble *params); +typedef GLint (APIENTRYP PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC) (GLuint program, GLenum shadertype, const GLchar *name); +typedef GLuint (APIENTRYP PFNGLGETSUBROUTINEINDEXPROC) (GLuint program, GLenum shadertype, const GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC) (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINENAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); +typedef void (APIENTRYP PFNGLUNIFORMSUBROUTINESUIVPROC) (GLenum shadertype, GLsizei count, const GLuint *indices); +typedef void (APIENTRYP PFNGLGETUNIFORMSUBROUTINEUIVPROC) (GLenum shadertype, GLint location, GLuint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMSTAGEIVPROC) (GLuint program, GLenum shadertype, GLenum pname, GLint *values); +typedef void (APIENTRYP PFNGLPATCHPARAMETERIPROC) (GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLPATCHPARAMETERFVPROC) (GLenum pname, const GLfloat *values); +typedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSPROC) (GLsizei n, const GLuint *ids); +typedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSPROC) (GLsizei n, GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKPROC) (GLuint id); +typedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKPROC) (void); +typedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKPROC) (void); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKPROC) (GLenum mode, GLuint id); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC) (GLenum mode, GLuint id, GLuint stream); +typedef void (APIENTRYP PFNGLBEGINQUERYINDEXEDPROC) (GLenum target, GLuint index, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYINDEXEDPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLGETQUERYINDEXEDIVPROC) (GLenum target, GLuint index, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMinSampleShading (GLfloat value); +GLAPI void APIENTRY glBlendEquationi (GLuint buf, GLenum mode); +GLAPI void APIENTRY glBlendEquationSeparatei (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +GLAPI void APIENTRY glBlendFunci (GLuint buf, GLenum src, GLenum dst); +GLAPI void APIENTRY glBlendFuncSeparatei (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +GLAPI void APIENTRY glDrawArraysIndirect (GLenum mode, const void *indirect); +GLAPI void APIENTRY glDrawElementsIndirect (GLenum mode, GLenum type, const void *indirect); +GLAPI void APIENTRY glUniform1d (GLint location, GLdouble x); +GLAPI void APIENTRY glUniform2d (GLint location, GLdouble x, GLdouble y); +GLAPI void APIENTRY glUniform3d (GLint location, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glUniform4d (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glUniform1dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniform2dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniform3dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniform4dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix2x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix2x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix3x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix3x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix4x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix4x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glGetUniformdv (GLuint program, GLint location, GLdouble *params); +GLAPI GLint APIENTRY glGetSubroutineUniformLocation (GLuint program, GLenum shadertype, const GLchar *name); +GLAPI GLuint APIENTRY glGetSubroutineIndex (GLuint program, GLenum shadertype, const GLchar *name); +GLAPI void APIENTRY glGetActiveSubroutineUniformiv (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values); +GLAPI void APIENTRY glGetActiveSubroutineUniformName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); +GLAPI void APIENTRY glGetActiveSubroutineName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); +GLAPI void APIENTRY glUniformSubroutinesuiv (GLenum shadertype, GLsizei count, const GLuint *indices); +GLAPI void APIENTRY glGetUniformSubroutineuiv (GLenum shadertype, GLint location, GLuint *params); +GLAPI void APIENTRY glGetProgramStageiv (GLuint program, GLenum shadertype, GLenum pname, GLint *values); +GLAPI void APIENTRY glPatchParameteri (GLenum pname, GLint value); +GLAPI void APIENTRY glPatchParameterfv (GLenum pname, const GLfloat *values); +GLAPI void APIENTRY glBindTransformFeedback (GLenum target, GLuint id); +GLAPI void APIENTRY glDeleteTransformFeedbacks (GLsizei n, const GLuint *ids); +GLAPI void APIENTRY glGenTransformFeedbacks (GLsizei n, GLuint *ids); +GLAPI GLboolean APIENTRY glIsTransformFeedback (GLuint id); +GLAPI void APIENTRY glPauseTransformFeedback (void); +GLAPI void APIENTRY glResumeTransformFeedback (void); +GLAPI void APIENTRY glDrawTransformFeedback (GLenum mode, GLuint id); +GLAPI void APIENTRY glDrawTransformFeedbackStream (GLenum mode, GLuint id, GLuint stream); +GLAPI void APIENTRY glBeginQueryIndexed (GLenum target, GLuint index, GLuint id); +GLAPI void APIENTRY glEndQueryIndexed (GLenum target, GLuint index); +GLAPI void APIENTRY glGetQueryIndexediv (GLenum target, GLuint index, GLenum pname, GLint *params); +#endif +#endif /* GL_VERSION_4_0 */ + +#ifndef GL_VERSION_4_1 +#define GL_VERSION_4_1 1 +#define GL_FIXED 0x140C +#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B +#define GL_LOW_FLOAT 0x8DF0 +#define GL_MEDIUM_FLOAT 0x8DF1 +#define GL_HIGH_FLOAT 0x8DF2 +#define GL_LOW_INT 0x8DF3 +#define GL_MEDIUM_INT 0x8DF4 +#define GL_HIGH_INT 0x8DF5 +#define GL_SHADER_COMPILER 0x8DFA +#define GL_SHADER_BINARY_FORMATS 0x8DF8 +#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 +#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB +#define GL_MAX_VARYING_VECTORS 0x8DFC +#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD +#define GL_RGB565 0x8D62 +#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257 +#define GL_PROGRAM_BINARY_LENGTH 0x8741 +#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE +#define GL_PROGRAM_BINARY_FORMATS 0x87FF +#define GL_VERTEX_SHADER_BIT 0x00000001 +#define GL_FRAGMENT_SHADER_BIT 0x00000002 +#define GL_GEOMETRY_SHADER_BIT 0x00000004 +#define GL_TESS_CONTROL_SHADER_BIT 0x00000008 +#define GL_TESS_EVALUATION_SHADER_BIT 0x00000010 +#define GL_ALL_SHADER_BITS 0xFFFFFFFF +#define GL_PROGRAM_SEPARABLE 0x8258 +#define GL_ACTIVE_PROGRAM 0x8259 +#define GL_PROGRAM_PIPELINE_BINDING 0x825A +#define GL_MAX_VIEWPORTS 0x825B +#define GL_VIEWPORT_SUBPIXEL_BITS 0x825C +#define GL_VIEWPORT_BOUNDS_RANGE 0x825D +#define GL_LAYER_PROVOKING_VERTEX 0x825E +#define GL_VIEWPORT_INDEX_PROVOKING_VERTEX 0x825F +#define GL_UNDEFINED_VERTEX 0x8260 +typedef void (APIENTRYP PFNGLRELEASESHADERCOMPILERPROC) (void); +typedef void (APIENTRYP PFNGLSHADERBINARYPROC) (GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length); +typedef void (APIENTRYP PFNGLGETSHADERPRECISIONFORMATPROC) (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); +typedef void (APIENTRYP PFNGLDEPTHRANGEFPROC) (GLfloat n, GLfloat f); +typedef void (APIENTRYP PFNGLCLEARDEPTHFPROC) (GLfloat d); +typedef void (APIENTRYP PFNGLGETPROGRAMBINARYPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); +typedef void (APIENTRYP PFNGLPROGRAMBINARYPROC) (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIPROC) (GLuint program, GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLUSEPROGRAMSTAGESPROC) (GLuint pipeline, GLbitfield stages, GLuint program); +typedef void (APIENTRYP PFNGLACTIVESHADERPROGRAMPROC) (GLuint pipeline, GLuint program); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMVPROC) (GLenum type, GLsizei count, const GLchar *const*strings); +typedef void (APIENTRYP PFNGLBINDPROGRAMPIPELINEPROC) (GLuint pipeline); +typedef void (APIENTRYP PFNGLDELETEPROGRAMPIPELINESPROC) (GLsizei n, const GLuint *pipelines); +typedef void (APIENTRYP PFNGLGENPROGRAMPIPELINESPROC) (GLsizei n, GLuint *pipelines); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMPIPELINEPROC) (GLuint pipeline); +typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEIVPROC) (GLuint pipeline, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IPROC) (GLuint program, GLint location, GLint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FPROC) (GLuint program, GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DPROC) (GLuint program, GLint location, GLdouble v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIPROC) (GLuint program, GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IPROC) (GLuint program, GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPIPELINEPROC) (GLuint pipeline); +typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEINFOLOGPROC) (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLDVPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLVIEWPORTARRAYVPROC) (GLuint first, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); +typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLSCISSORARRAYVPROC) (GLuint first, GLsizei count, const GLint *v); +typedef void (APIENTRYP PFNGLSCISSORINDEXEDPROC) (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLSCISSORINDEXEDVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLDEPTHRANGEARRAYVPROC) (GLuint first, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDPROC) (GLuint index, GLdouble n, GLdouble f); +typedef void (APIENTRYP PFNGLGETFLOATI_VPROC) (GLenum target, GLuint index, GLfloat *data); +typedef void (APIENTRYP PFNGLGETDOUBLEI_VPROC) (GLenum target, GLuint index, GLdouble *data); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glReleaseShaderCompiler (void); +GLAPI void APIENTRY glShaderBinary (GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length); +GLAPI void APIENTRY glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); +GLAPI void APIENTRY glDepthRangef (GLfloat n, GLfloat f); +GLAPI void APIENTRY glClearDepthf (GLfloat d); +GLAPI void APIENTRY glGetProgramBinary (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); +GLAPI void APIENTRY glProgramBinary (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); +GLAPI void APIENTRY glProgramParameteri (GLuint program, GLenum pname, GLint value); +GLAPI void APIENTRY glUseProgramStages (GLuint pipeline, GLbitfield stages, GLuint program); +GLAPI void APIENTRY glActiveShaderProgram (GLuint pipeline, GLuint program); +GLAPI GLuint APIENTRY glCreateShaderProgramv (GLenum type, GLsizei count, const GLchar *const*strings); +GLAPI void APIENTRY glBindProgramPipeline (GLuint pipeline); +GLAPI void APIENTRY glDeleteProgramPipelines (GLsizei n, const GLuint *pipelines); +GLAPI void APIENTRY glGenProgramPipelines (GLsizei n, GLuint *pipelines); +GLAPI GLboolean APIENTRY glIsProgramPipeline (GLuint pipeline); +GLAPI void APIENTRY glGetProgramPipelineiv (GLuint pipeline, GLenum pname, GLint *params); +GLAPI void APIENTRY glProgramUniform1i (GLuint program, GLint location, GLint v0); +GLAPI void APIENTRY glProgramUniform1iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform1f (GLuint program, GLint location, GLfloat v0); +GLAPI void APIENTRY glProgramUniform1fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform1d (GLuint program, GLint location, GLdouble v0); +GLAPI void APIENTRY glProgramUniform1dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform1ui (GLuint program, GLint location, GLuint v0); +GLAPI void APIENTRY glProgramUniform1uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform2i (GLuint program, GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glProgramUniform2iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform2f (GLuint program, GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glProgramUniform2fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform2d (GLuint program, GLint location, GLdouble v0, GLdouble v1); +GLAPI void APIENTRY glProgramUniform2dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform2ui (GLuint program, GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glProgramUniform2uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform3i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glProgramUniform3iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform3f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glProgramUniform3fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform3d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); +GLAPI void APIENTRY glProgramUniform3dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform3ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glProgramUniform3uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform4i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glProgramUniform4iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform4f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glProgramUniform4fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform4d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); +GLAPI void APIENTRY glProgramUniform4dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform4ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glProgramUniform4uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniformMatrix2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glValidateProgramPipeline (GLuint pipeline); +GLAPI void APIENTRY glGetProgramPipelineInfoLog (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI void APIENTRY glVertexAttribL1d (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttribL2d (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttribL3d (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttribL4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttribL1dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL2dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL3dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL4dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribLPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glGetVertexAttribLdv (GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glViewportArrayv (GLuint first, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glViewportIndexedf (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); +GLAPI void APIENTRY glViewportIndexedfv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glScissorArrayv (GLuint first, GLsizei count, const GLint *v); +GLAPI void APIENTRY glScissorIndexed (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); +GLAPI void APIENTRY glScissorIndexedv (GLuint index, const GLint *v); +GLAPI void APIENTRY glDepthRangeArrayv (GLuint first, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glDepthRangeIndexed (GLuint index, GLdouble n, GLdouble f); +GLAPI void APIENTRY glGetFloati_v (GLenum target, GLuint index, GLfloat *data); +GLAPI void APIENTRY glGetDoublei_v (GLenum target, GLuint index, GLdouble *data); +#endif +#endif /* GL_VERSION_4_1 */ + +#ifndef GL_VERSION_4_2 +#define GL_VERSION_4_2 1 +#define GL_COPY_READ_BUFFER_BINDING 0x8F36 +#define GL_COPY_WRITE_BUFFER_BINDING 0x8F37 +#define GL_TRANSFORM_FEEDBACK_ACTIVE 0x8E24 +#define GL_TRANSFORM_FEEDBACK_PAUSED 0x8E23 +#define GL_UNPACK_COMPRESSED_BLOCK_WIDTH 0x9127 +#define GL_UNPACK_COMPRESSED_BLOCK_HEIGHT 0x9128 +#define GL_UNPACK_COMPRESSED_BLOCK_DEPTH 0x9129 +#define GL_UNPACK_COMPRESSED_BLOCK_SIZE 0x912A +#define GL_PACK_COMPRESSED_BLOCK_WIDTH 0x912B +#define GL_PACK_COMPRESSED_BLOCK_HEIGHT 0x912C +#define GL_PACK_COMPRESSED_BLOCK_DEPTH 0x912D +#define GL_PACK_COMPRESSED_BLOCK_SIZE 0x912E +#define GL_NUM_SAMPLE_COUNTS 0x9380 +#define GL_MIN_MAP_BUFFER_ALIGNMENT 0x90BC +#define GL_ATOMIC_COUNTER_BUFFER 0x92C0 +#define GL_ATOMIC_COUNTER_BUFFER_BINDING 0x92C1 +#define GL_ATOMIC_COUNTER_BUFFER_START 0x92C2 +#define GL_ATOMIC_COUNTER_BUFFER_SIZE 0x92C3 +#define GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE 0x92C4 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS 0x92C5 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES 0x92C6 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER 0x92C7 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER 0x92C8 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER 0x92C9 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER 0x92CA +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER 0x92CB +#define GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS 0x92CC +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS 0x92CD +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS 0x92CE +#define GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS 0x92CF +#define GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS 0x92D0 +#define GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS 0x92D1 +#define GL_MAX_VERTEX_ATOMIC_COUNTERS 0x92D2 +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS 0x92D3 +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS 0x92D4 +#define GL_MAX_GEOMETRY_ATOMIC_COUNTERS 0x92D5 +#define GL_MAX_FRAGMENT_ATOMIC_COUNTERS 0x92D6 +#define GL_MAX_COMBINED_ATOMIC_COUNTERS 0x92D7 +#define GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE 0x92D8 +#define GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS 0x92DC +#define GL_ACTIVE_ATOMIC_COUNTER_BUFFERS 0x92D9 +#define GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX 0x92DA +#define GL_UNSIGNED_INT_ATOMIC_COUNTER 0x92DB +#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001 +#define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002 +#define GL_UNIFORM_BARRIER_BIT 0x00000004 +#define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008 +#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020 +#define GL_COMMAND_BARRIER_BIT 0x00000040 +#define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080 +#define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100 +#define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200 +#define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400 +#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT 0x00000800 +#define GL_ATOMIC_COUNTER_BARRIER_BIT 0x00001000 +#define GL_ALL_BARRIER_BITS 0xFFFFFFFF +#define GL_MAX_IMAGE_UNITS 0x8F38 +#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS 0x8F39 +#define GL_IMAGE_BINDING_NAME 0x8F3A +#define GL_IMAGE_BINDING_LEVEL 0x8F3B +#define GL_IMAGE_BINDING_LAYERED 0x8F3C +#define GL_IMAGE_BINDING_LAYER 0x8F3D +#define GL_IMAGE_BINDING_ACCESS 0x8F3E +#define GL_IMAGE_1D 0x904C +#define GL_IMAGE_2D 0x904D +#define GL_IMAGE_3D 0x904E +#define GL_IMAGE_2D_RECT 0x904F +#define GL_IMAGE_CUBE 0x9050 +#define GL_IMAGE_BUFFER 0x9051 +#define GL_IMAGE_1D_ARRAY 0x9052 +#define GL_IMAGE_2D_ARRAY 0x9053 +#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054 +#define GL_IMAGE_2D_MULTISAMPLE 0x9055 +#define GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056 +#define GL_INT_IMAGE_1D 0x9057 +#define GL_INT_IMAGE_2D 0x9058 +#define GL_INT_IMAGE_3D 0x9059 +#define GL_INT_IMAGE_2D_RECT 0x905A +#define GL_INT_IMAGE_CUBE 0x905B +#define GL_INT_IMAGE_BUFFER 0x905C +#define GL_INT_IMAGE_1D_ARRAY 0x905D +#define GL_INT_IMAGE_2D_ARRAY 0x905E +#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F +#define GL_INT_IMAGE_2D_MULTISAMPLE 0x9060 +#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061 +#define GL_UNSIGNED_INT_IMAGE_1D 0x9062 +#define GL_UNSIGNED_INT_IMAGE_2D 0x9063 +#define GL_UNSIGNED_INT_IMAGE_3D 0x9064 +#define GL_UNSIGNED_INT_IMAGE_2D_RECT 0x9065 +#define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066 +#define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067 +#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY 0x9068 +#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C +#define GL_MAX_IMAGE_SAMPLES 0x906D +#define GL_IMAGE_BINDING_FORMAT 0x906E +#define GL_IMAGE_FORMAT_COMPATIBILITY_TYPE 0x90C7 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE 0x90C8 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS 0x90C9 +#define GL_MAX_VERTEX_IMAGE_UNIFORMS 0x90CA +#define GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS 0x90CB +#define GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS 0x90CC +#define GL_MAX_GEOMETRY_IMAGE_UNIFORMS 0x90CD +#define GL_MAX_FRAGMENT_IMAGE_UNIFORMS 0x90CE +#define GL_MAX_COMBINED_IMAGE_UNIFORMS 0x90CF +#define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F +#define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); +typedef void (APIENTRYP PFNGLGETINTERNALFORMATIVPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params); +typedef void (APIENTRYP PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC) (GLuint program, GLuint bufferIndex, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLBINDIMAGETEXTUREPROC) (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); +typedef void (APIENTRYP PFNGLMEMORYBARRIERPROC) (GLbitfield barriers); +typedef void (APIENTRYP PFNGLTEXSTORAGE1DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (APIENTRYP PFNGLTEXSTORAGE2DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXSTORAGE3DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC) (GLenum mode, GLuint id, GLsizei instancecount); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC) (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstancedBaseInstance (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); +GLAPI void APIENTRY glDrawElementsInstancedBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance); +GLAPI void APIENTRY glDrawElementsInstancedBaseVertexBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); +GLAPI void APIENTRY glGetInternalformativ (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params); +GLAPI void APIENTRY glGetActiveAtomicCounterBufferiv (GLuint program, GLuint bufferIndex, GLenum pname, GLint *params); +GLAPI void APIENTRY glBindImageTexture (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); +GLAPI void APIENTRY glMemoryBarrier (GLbitfield barriers); +GLAPI void APIENTRY glTexStorage1D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +GLAPI void APIENTRY glTexStorage2D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTexStorage3D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glDrawTransformFeedbackInstanced (GLenum mode, GLuint id, GLsizei instancecount); +GLAPI void APIENTRY glDrawTransformFeedbackStreamInstanced (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); +#endif +#endif /* GL_VERSION_4_2 */ + +#ifndef GL_VERSION_4_3 +#define GL_VERSION_4_3 1 +typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +#define GL_NUM_SHADING_LANGUAGE_VERSIONS 0x82E9 +#define GL_VERTEX_ATTRIB_ARRAY_LONG 0x874E +#define GL_COMPRESSED_RGB8_ETC2 0x9274 +#define GL_COMPRESSED_SRGB8_ETC2 0x9275 +#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 +#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 +#define GL_COMPRESSED_R11_EAC 0x9270 +#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 +#define GL_COMPRESSED_RG11_EAC 0x9272 +#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 +#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69 +#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A +#define GL_MAX_ELEMENT_INDEX 0x8D6B +#define GL_COMPUTE_SHADER 0x91B9 +#define GL_MAX_COMPUTE_UNIFORM_BLOCKS 0x91BB +#define GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS 0x91BC +#define GL_MAX_COMPUTE_IMAGE_UNIFORMS 0x91BD +#define GL_MAX_COMPUTE_SHARED_MEMORY_SIZE 0x8262 +#define GL_MAX_COMPUTE_UNIFORM_COMPONENTS 0x8263 +#define GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS 0x8264 +#define GL_MAX_COMPUTE_ATOMIC_COUNTERS 0x8265 +#define GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS 0x8266 +#define GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS 0x90EB +#define GL_MAX_COMPUTE_WORK_GROUP_COUNT 0x91BE +#define GL_MAX_COMPUTE_WORK_GROUP_SIZE 0x91BF +#define GL_COMPUTE_WORK_GROUP_SIZE 0x8267 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER 0x90EC +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER 0x90ED +#define GL_DISPATCH_INDIRECT_BUFFER 0x90EE +#define GL_DISPATCH_INDIRECT_BUFFER_BINDING 0x90EF +#define GL_COMPUTE_SHADER_BIT 0x00000020 +#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 +#define GL_DEBUG_SOURCE_API 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION 0x824A +#define GL_DEBUG_SOURCE_OTHER 0x824B +#define GL_DEBUG_TYPE_ERROR 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E +#define GL_DEBUG_TYPE_PORTABILITY 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE 0x8250 +#define GL_DEBUG_TYPE_OTHER 0x8251 +#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES 0x9145 +#define GL_DEBUG_SEVERITY_HIGH 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 +#define GL_DEBUG_SEVERITY_LOW 0x9148 +#define GL_DEBUG_TYPE_MARKER 0x8268 +#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 +#define GL_DEBUG_TYPE_POP_GROUP 0x826A +#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C +#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D +#define GL_BUFFER 0x82E0 +#define GL_SHADER 0x82E1 +#define GL_PROGRAM 0x82E2 +#define GL_QUERY 0x82E3 +#define GL_PROGRAM_PIPELINE 0x82E4 +#define GL_SAMPLER 0x82E6 +#define GL_MAX_LABEL_LENGTH 0x82E8 +#define GL_DEBUG_OUTPUT 0x92E0 +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +#define GL_MAX_UNIFORM_LOCATIONS 0x826E +#define GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310 +#define GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311 +#define GL_FRAMEBUFFER_DEFAULT_LAYERS 0x9312 +#define GL_FRAMEBUFFER_DEFAULT_SAMPLES 0x9313 +#define GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS 0x9314 +#define GL_MAX_FRAMEBUFFER_WIDTH 0x9315 +#define GL_MAX_FRAMEBUFFER_HEIGHT 0x9316 +#define GL_MAX_FRAMEBUFFER_LAYERS 0x9317 +#define GL_MAX_FRAMEBUFFER_SAMPLES 0x9318 +#define GL_INTERNALFORMAT_SUPPORTED 0x826F +#define GL_INTERNALFORMAT_PREFERRED 0x8270 +#define GL_INTERNALFORMAT_RED_SIZE 0x8271 +#define GL_INTERNALFORMAT_GREEN_SIZE 0x8272 +#define GL_INTERNALFORMAT_BLUE_SIZE 0x8273 +#define GL_INTERNALFORMAT_ALPHA_SIZE 0x8274 +#define GL_INTERNALFORMAT_DEPTH_SIZE 0x8275 +#define GL_INTERNALFORMAT_STENCIL_SIZE 0x8276 +#define GL_INTERNALFORMAT_SHARED_SIZE 0x8277 +#define GL_INTERNALFORMAT_RED_TYPE 0x8278 +#define GL_INTERNALFORMAT_GREEN_TYPE 0x8279 +#define GL_INTERNALFORMAT_BLUE_TYPE 0x827A +#define GL_INTERNALFORMAT_ALPHA_TYPE 0x827B +#define GL_INTERNALFORMAT_DEPTH_TYPE 0x827C +#define GL_INTERNALFORMAT_STENCIL_TYPE 0x827D +#define GL_MAX_WIDTH 0x827E +#define GL_MAX_HEIGHT 0x827F +#define GL_MAX_DEPTH 0x8280 +#define GL_MAX_LAYERS 0x8281 +#define GL_MAX_COMBINED_DIMENSIONS 0x8282 +#define GL_COLOR_COMPONENTS 0x8283 +#define GL_DEPTH_COMPONENTS 0x8284 +#define GL_STENCIL_COMPONENTS 0x8285 +#define GL_COLOR_RENDERABLE 0x8286 +#define GL_DEPTH_RENDERABLE 0x8287 +#define GL_STENCIL_RENDERABLE 0x8288 +#define GL_FRAMEBUFFER_RENDERABLE 0x8289 +#define GL_FRAMEBUFFER_RENDERABLE_LAYERED 0x828A +#define GL_FRAMEBUFFER_BLEND 0x828B +#define GL_READ_PIXELS 0x828C +#define GL_READ_PIXELS_FORMAT 0x828D +#define GL_READ_PIXELS_TYPE 0x828E +#define GL_TEXTURE_IMAGE_FORMAT 0x828F +#define GL_TEXTURE_IMAGE_TYPE 0x8290 +#define GL_GET_TEXTURE_IMAGE_FORMAT 0x8291 +#define GL_GET_TEXTURE_IMAGE_TYPE 0x8292 +#define GL_MIPMAP 0x8293 +#define GL_MANUAL_GENERATE_MIPMAP 0x8294 +#define GL_AUTO_GENERATE_MIPMAP 0x8295 +#define GL_COLOR_ENCODING 0x8296 +#define GL_SRGB_READ 0x8297 +#define GL_SRGB_WRITE 0x8298 +#define GL_FILTER 0x829A +#define GL_VERTEX_TEXTURE 0x829B +#define GL_TESS_CONTROL_TEXTURE 0x829C +#define GL_TESS_EVALUATION_TEXTURE 0x829D +#define GL_GEOMETRY_TEXTURE 0x829E +#define GL_FRAGMENT_TEXTURE 0x829F +#define GL_COMPUTE_TEXTURE 0x82A0 +#define GL_TEXTURE_SHADOW 0x82A1 +#define GL_TEXTURE_GATHER 0x82A2 +#define GL_TEXTURE_GATHER_SHADOW 0x82A3 +#define GL_SHADER_IMAGE_LOAD 0x82A4 +#define GL_SHADER_IMAGE_STORE 0x82A5 +#define GL_SHADER_IMAGE_ATOMIC 0x82A6 +#define GL_IMAGE_TEXEL_SIZE 0x82A7 +#define GL_IMAGE_COMPATIBILITY_CLASS 0x82A8 +#define GL_IMAGE_PIXEL_FORMAT 0x82A9 +#define GL_IMAGE_PIXEL_TYPE 0x82AA +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST 0x82AC +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST 0x82AD +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE 0x82AE +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE 0x82AF +#define GL_TEXTURE_COMPRESSED_BLOCK_WIDTH 0x82B1 +#define GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT 0x82B2 +#define GL_TEXTURE_COMPRESSED_BLOCK_SIZE 0x82B3 +#define GL_CLEAR_BUFFER 0x82B4 +#define GL_TEXTURE_VIEW 0x82B5 +#define GL_VIEW_COMPATIBILITY_CLASS 0x82B6 +#define GL_FULL_SUPPORT 0x82B7 +#define GL_CAVEAT_SUPPORT 0x82B8 +#define GL_IMAGE_CLASS_4_X_32 0x82B9 +#define GL_IMAGE_CLASS_2_X_32 0x82BA +#define GL_IMAGE_CLASS_1_X_32 0x82BB +#define GL_IMAGE_CLASS_4_X_16 0x82BC +#define GL_IMAGE_CLASS_2_X_16 0x82BD +#define GL_IMAGE_CLASS_1_X_16 0x82BE +#define GL_IMAGE_CLASS_4_X_8 0x82BF +#define GL_IMAGE_CLASS_2_X_8 0x82C0 +#define GL_IMAGE_CLASS_1_X_8 0x82C1 +#define GL_IMAGE_CLASS_11_11_10 0x82C2 +#define GL_IMAGE_CLASS_10_10_10_2 0x82C3 +#define GL_VIEW_CLASS_128_BITS 0x82C4 +#define GL_VIEW_CLASS_96_BITS 0x82C5 +#define GL_VIEW_CLASS_64_BITS 0x82C6 +#define GL_VIEW_CLASS_48_BITS 0x82C7 +#define GL_VIEW_CLASS_32_BITS 0x82C8 +#define GL_VIEW_CLASS_24_BITS 0x82C9 +#define GL_VIEW_CLASS_16_BITS 0x82CA +#define GL_VIEW_CLASS_8_BITS 0x82CB +#define GL_VIEW_CLASS_S3TC_DXT1_RGB 0x82CC +#define GL_VIEW_CLASS_S3TC_DXT1_RGBA 0x82CD +#define GL_VIEW_CLASS_S3TC_DXT3_RGBA 0x82CE +#define GL_VIEW_CLASS_S3TC_DXT5_RGBA 0x82CF +#define GL_VIEW_CLASS_RGTC1_RED 0x82D0 +#define GL_VIEW_CLASS_RGTC2_RG 0x82D1 +#define GL_VIEW_CLASS_BPTC_UNORM 0x82D2 +#define GL_VIEW_CLASS_BPTC_FLOAT 0x82D3 +#define GL_UNIFORM 0x92E1 +#define GL_UNIFORM_BLOCK 0x92E2 +#define GL_PROGRAM_INPUT 0x92E3 +#define GL_PROGRAM_OUTPUT 0x92E4 +#define GL_BUFFER_VARIABLE 0x92E5 +#define GL_SHADER_STORAGE_BLOCK 0x92E6 +#define GL_VERTEX_SUBROUTINE 0x92E8 +#define GL_TESS_CONTROL_SUBROUTINE 0x92E9 +#define GL_TESS_EVALUATION_SUBROUTINE 0x92EA +#define GL_GEOMETRY_SUBROUTINE 0x92EB +#define GL_FRAGMENT_SUBROUTINE 0x92EC +#define GL_COMPUTE_SUBROUTINE 0x92ED +#define GL_VERTEX_SUBROUTINE_UNIFORM 0x92EE +#define GL_TESS_CONTROL_SUBROUTINE_UNIFORM 0x92EF +#define GL_TESS_EVALUATION_SUBROUTINE_UNIFORM 0x92F0 +#define GL_GEOMETRY_SUBROUTINE_UNIFORM 0x92F1 +#define GL_FRAGMENT_SUBROUTINE_UNIFORM 0x92F2 +#define GL_COMPUTE_SUBROUTINE_UNIFORM 0x92F3 +#define GL_TRANSFORM_FEEDBACK_VARYING 0x92F4 +#define GL_ACTIVE_RESOURCES 0x92F5 +#define GL_MAX_NAME_LENGTH 0x92F6 +#define GL_MAX_NUM_ACTIVE_VARIABLES 0x92F7 +#define GL_MAX_NUM_COMPATIBLE_SUBROUTINES 0x92F8 +#define GL_NAME_LENGTH 0x92F9 +#define GL_TYPE 0x92FA +#define GL_ARRAY_SIZE 0x92FB +#define GL_OFFSET 0x92FC +#define GL_BLOCK_INDEX 0x92FD +#define GL_ARRAY_STRIDE 0x92FE +#define GL_MATRIX_STRIDE 0x92FF +#define GL_IS_ROW_MAJOR 0x9300 +#define GL_ATOMIC_COUNTER_BUFFER_INDEX 0x9301 +#define GL_BUFFER_BINDING 0x9302 +#define GL_BUFFER_DATA_SIZE 0x9303 +#define GL_NUM_ACTIVE_VARIABLES 0x9304 +#define GL_ACTIVE_VARIABLES 0x9305 +#define GL_REFERENCED_BY_VERTEX_SHADER 0x9306 +#define GL_REFERENCED_BY_TESS_CONTROL_SHADER 0x9307 +#define GL_REFERENCED_BY_TESS_EVALUATION_SHADER 0x9308 +#define GL_REFERENCED_BY_GEOMETRY_SHADER 0x9309 +#define GL_REFERENCED_BY_FRAGMENT_SHADER 0x930A +#define GL_REFERENCED_BY_COMPUTE_SHADER 0x930B +#define GL_TOP_LEVEL_ARRAY_SIZE 0x930C +#define GL_TOP_LEVEL_ARRAY_STRIDE 0x930D +#define GL_LOCATION 0x930E +#define GL_LOCATION_INDEX 0x930F +#define GL_IS_PER_PATCH 0x92E7 +#define GL_SHADER_STORAGE_BUFFER 0x90D2 +#define GL_SHADER_STORAGE_BUFFER_BINDING 0x90D3 +#define GL_SHADER_STORAGE_BUFFER_START 0x90D4 +#define GL_SHADER_STORAGE_BUFFER_SIZE 0x90D5 +#define GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS 0x90D6 +#define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS 0x90D7 +#define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS 0x90D8 +#define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS 0x90D9 +#define GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS 0x90DA +#define GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS 0x90DB +#define GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS 0x90DC +#define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD +#define GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE +#define GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT 0x90DF +#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000 +#define GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES 0x8F39 +#define GL_DEPTH_STENCIL_TEXTURE_MODE 0x90EA +#define GL_TEXTURE_BUFFER_OFFSET 0x919D +#define GL_TEXTURE_BUFFER_SIZE 0x919E +#define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT 0x919F +#define GL_TEXTURE_VIEW_MIN_LEVEL 0x82DB +#define GL_TEXTURE_VIEW_NUM_LEVELS 0x82DC +#define GL_TEXTURE_VIEW_MIN_LAYER 0x82DD +#define GL_TEXTURE_VIEW_NUM_LAYERS 0x82DE +#define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF +#define GL_VERTEX_ATTRIB_BINDING 0x82D4 +#define GL_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D5 +#define GL_VERTEX_BINDING_DIVISOR 0x82D6 +#define GL_VERTEX_BINDING_OFFSET 0x82D7 +#define GL_VERTEX_BINDING_STRIDE 0x82D8 +#define GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D9 +#define GL_MAX_VERTEX_ATTRIB_BINDINGS 0x82DA +#define GL_VERTEX_BINDING_BUFFER 0x8F4F +typedef void (APIENTRYP PFNGLCLEARBUFFERDATAPROC) (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARBUFFERSUBDATAPROC) (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); +typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEINDIRECTPROC) (GLintptr indirect); +typedef void (APIENTRYP PFNGLCOPYIMAGESUBDATAPROC) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +typedef void (APIENTRYP PFNGLFRAMEBUFFERPARAMETERIPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETINTERNALFORMATI64VPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint64 *params); +typedef void (APIENTRYP PFNGLINVALIDATETEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLINVALIDATETEXIMAGEPROC) (GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLINVALIDATEBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLINVALIDATEFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments); +typedef void (APIENTRYP PFNGLINVALIDATESUBFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTPROC) (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLGETPROGRAMINTERFACEIVPROC) (GLuint program, GLenum programInterface, GLenum pname, GLint *params); +typedef GLuint (APIENTRYP PFNGLGETPROGRAMRESOURCEINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCENAMEPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCEIVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLint *params); +typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONPROC) (GLuint program, GLenum programInterface, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name); +typedef void (APIENTRYP PFNGLSHADERSTORAGEBLOCKBINDINGPROC) (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); +typedef void (APIENTRYP PFNGLTEXBUFFERRANGEPROC) (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLTEXSTORAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXSTORAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTUREVIEWPROC) (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); +typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERPROC) (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBBINDINGPROC) (GLuint attribindex, GLuint bindingindex); +typedef void (APIENTRYP PFNGLVERTEXBINDINGDIVISORPROC) (GLuint bindingindex, GLuint divisor); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKPROC) (GLDEBUGPROC callback, const void *userParam); +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGPROC) (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPPROC) (GLenum source, GLuint id, GLsizei length, const GLchar *message); +typedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC) (void); +typedef void (APIENTRYP PFNGLOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei length, const GLchar *label); +typedef void (APIENTRYP PFNGLGETOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +typedef void (APIENTRYP PFNGLOBJECTPTRLABELPROC) (const void *ptr, GLsizei length, const GLchar *label); +typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELPROC) (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glClearBufferData (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearBufferSubData (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glDispatchCompute (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); +GLAPI void APIENTRY glDispatchComputeIndirect (GLintptr indirect); +GLAPI void APIENTRY glCopyImageSubData (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +GLAPI void APIENTRY glFramebufferParameteri (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glGetFramebufferParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetInternalformati64v (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint64 *params); +GLAPI void APIENTRY glInvalidateTexSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glInvalidateTexImage (GLuint texture, GLint level); +GLAPI void APIENTRY glInvalidateBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr length); +GLAPI void APIENTRY glInvalidateBufferData (GLuint buffer); +GLAPI void APIENTRY glInvalidateFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments); +GLAPI void APIENTRY glInvalidateSubFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glMultiDrawArraysIndirect (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride); +GLAPI void APIENTRY glMultiDrawElementsIndirect (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride); +GLAPI void APIENTRY glGetProgramInterfaceiv (GLuint program, GLenum programInterface, GLenum pname, GLint *params); +GLAPI GLuint APIENTRY glGetProgramResourceIndex (GLuint program, GLenum programInterface, const GLchar *name); +GLAPI void APIENTRY glGetProgramResourceName (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +GLAPI void APIENTRY glGetProgramResourceiv (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLint *params); +GLAPI GLint APIENTRY glGetProgramResourceLocation (GLuint program, GLenum programInterface, const GLchar *name); +GLAPI GLint APIENTRY glGetProgramResourceLocationIndex (GLuint program, GLenum programInterface, const GLchar *name); +GLAPI void APIENTRY glShaderStorageBlockBinding (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); +GLAPI void APIENTRY glTexBufferRange (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glTexStorage2DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTexStorage3DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTextureView (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); +GLAPI void APIENTRY glBindVertexBuffer (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +GLAPI void APIENTRY glVertexAttribFormat (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +GLAPI void APIENTRY glVertexAttribIFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexAttribLFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexAttribBinding (GLuint attribindex, GLuint bindingindex); +GLAPI void APIENTRY glVertexBindingDivisor (GLuint bindingindex, GLuint divisor); +GLAPI void APIENTRY glDebugMessageControl (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI void APIENTRY glDebugMessageInsert (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +GLAPI void APIENTRY glDebugMessageCallback (GLDEBUGPROC callback, const void *userParam); +GLAPI GLuint APIENTRY glGetDebugMessageLog (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +GLAPI void APIENTRY glPushDebugGroup (GLenum source, GLuint id, GLsizei length, const GLchar *message); +GLAPI void APIENTRY glPopDebugGroup (void); +GLAPI void APIENTRY glObjectLabel (GLenum identifier, GLuint name, GLsizei length, const GLchar *label); +GLAPI void APIENTRY glGetObjectLabel (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +GLAPI void APIENTRY glObjectPtrLabel (const void *ptr, GLsizei length, const GLchar *label); +GLAPI void APIENTRY glGetObjectPtrLabel (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +#endif +#endif /* GL_VERSION_4_3 */ + +#ifndef GL_VERSION_4_4 +#define GL_VERSION_4_4 1 +#define GL_MAX_VERTEX_ATTRIB_STRIDE 0x82E5 +#define GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED 0x8221 +#define GL_TEXTURE_BUFFER_BINDING 0x8C2A +#define GL_MAP_PERSISTENT_BIT 0x0040 +#define GL_MAP_COHERENT_BIT 0x0080 +#define GL_DYNAMIC_STORAGE_BIT 0x0100 +#define GL_CLIENT_STORAGE_BIT 0x0200 +#define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT 0x00004000 +#define GL_BUFFER_IMMUTABLE_STORAGE 0x821F +#define GL_BUFFER_STORAGE_FLAGS 0x8220 +#define GL_CLEAR_TEXTURE 0x9365 +#define GL_LOCATION_COMPONENT 0x934A +#define GL_TRANSFORM_FEEDBACK_BUFFER_INDEX 0x934B +#define GL_TRANSFORM_FEEDBACK_BUFFER_STRIDE 0x934C +#define GL_QUERY_BUFFER 0x9192 +#define GL_QUERY_BUFFER_BARRIER_BIT 0x00008000 +#define GL_QUERY_BUFFER_BINDING 0x9193 +#define GL_QUERY_RESULT_NO_WAIT 0x9194 +#define GL_MIRROR_CLAMP_TO_EDGE 0x8743 +typedef void (APIENTRYP PFNGLBUFFERSTORAGEPROC) (GLenum target, GLsizeiptr size, const void *data, GLbitfield flags); +typedef void (APIENTRYP PFNGLCLEARTEXIMAGEPROC) (GLuint texture, GLint level, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARTEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLBINDBUFFERSBASEPROC) (GLenum target, GLuint first, GLsizei count, const GLuint *buffers); +typedef void (APIENTRYP PFNGLBINDBUFFERSRANGEPROC) (GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes); +typedef void (APIENTRYP PFNGLBINDTEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures); +typedef void (APIENTRYP PFNGLBINDSAMPLERSPROC) (GLuint first, GLsizei count, const GLuint *samplers); +typedef void (APIENTRYP PFNGLBINDIMAGETEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures); +typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERSPROC) (GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBufferStorage (GLenum target, GLsizeiptr size, const void *data, GLbitfield flags); +GLAPI void APIENTRY glClearTexImage (GLuint texture, GLint level, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearTexSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glBindBuffersBase (GLenum target, GLuint first, GLsizei count, const GLuint *buffers); +GLAPI void APIENTRY glBindBuffersRange (GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes); +GLAPI void APIENTRY glBindTextures (GLuint first, GLsizei count, const GLuint *textures); +GLAPI void APIENTRY glBindSamplers (GLuint first, GLsizei count, const GLuint *samplers); +GLAPI void APIENTRY glBindImageTextures (GLuint first, GLsizei count, const GLuint *textures); +GLAPI void APIENTRY glBindVertexBuffers (GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +#endif +#endif /* GL_VERSION_4_4 */ + +#ifndef GL_VERSION_4_5 +#define GL_VERSION_4_5 1 +#define GL_CONTEXT_LOST 0x0507 +#define GL_NEGATIVE_ONE_TO_ONE 0x935E +#define GL_ZERO_TO_ONE 0x935F +#define GL_CLIP_ORIGIN 0x935C +#define GL_CLIP_DEPTH_MODE 0x935D +#define GL_QUERY_WAIT_INVERTED 0x8E17 +#define GL_QUERY_NO_WAIT_INVERTED 0x8E18 +#define GL_QUERY_BY_REGION_WAIT_INVERTED 0x8E19 +#define GL_QUERY_BY_REGION_NO_WAIT_INVERTED 0x8E1A +#define GL_MAX_CULL_DISTANCES 0x82F9 +#define GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES 0x82FA +#define GL_TEXTURE_TARGET 0x1006 +#define GL_QUERY_TARGET 0x82EA +#define GL_TEXTURE_BINDING 0x82EB +#define GL_GUILTY_CONTEXT_RESET 0x8253 +#define GL_INNOCENT_CONTEXT_RESET 0x8254 +#define GL_UNKNOWN_CONTEXT_RESET 0x8255 +#define GL_RESET_NOTIFICATION_STRATEGY 0x8256 +#define GL_LOSE_CONTEXT_ON_RESET 0x8252 +#define GL_NO_RESET_NOTIFICATION 0x8261 +#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT 0x00000004 +#define GL_CONTEXT_RELEASE_BEHAVIOR 0x82FB +#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82FC +typedef void (APIENTRYP PFNGLCLIPCONTROLPROC) (GLenum origin, GLenum depth); +typedef void (APIENTRYP PFNGLCREATETRANSFORMFEEDBACKSPROC) (GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC) (GLuint xfb, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC) (GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizei size); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKIVPROC) (GLuint xfb, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint *param); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI64_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint64 *param); +typedef void (APIENTRYP PFNGLCREATEBUFFERSPROC) (GLsizei n, GLuint *buffers); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEPROC) (GLuint buffer, GLsizei size, const void *data, GLbitfield flags); +typedef void (APIENTRYP PFNGLNAMEDBUFFERDATAPROC) (GLuint buffer, GLsizei size, const void *data, GLenum usage); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizei size, const void *data); +typedef void (APIENTRYP PFNGLCOPYNAMEDBUFFERSUBDATAPROC) (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizei size); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERDATAPROC) (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERSUBDATAPROC) (GLuint buffer, GLenum internalformat, GLintptr offset, GLsizei size, GLenum format, GLenum type, const void *data); +typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERPROC) (GLuint buffer, GLenum access); +typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERRANGEPROC) (GLuint buffer, GLintptr offset, GLsizei length, GLbitfield access); +typedef GLboolean (APIENTRYP PFNGLUNMAPNAMEDBUFFERPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC) (GLuint buffer, GLintptr offset, GLsizei length); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERIVPROC) (GLuint buffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERI64VPROC) (GLuint buffer, GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVPROC) (GLuint buffer, GLenum pname, void **params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizei size, void *data); +typedef void (APIENTRYP PFNGLCREATEFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC) (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC) (GLuint framebuffer, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC) (GLuint framebuffer, GLenum buf); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC) (GLuint framebuffer, GLsizei n, const GLenum *bufs); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC) (GLuint framebuffer, GLenum src); +typedef void (APIENTRYP PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC) (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments); +typedef void (APIENTRYP PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC) (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERIVPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint *value); +typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint *value); +typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERFVPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat *value); +typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERFIPROC) (GLuint framebuffer, GLenum buffer, const GLfloat depth, GLint stencil); +typedef void (APIENTRYP PFNGLBLITNAMEDFRAMEBUFFERPROC) (GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +typedef GLenum (APIENTRYP PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC) (GLuint framebuffer, GLenum target); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC) (GLuint framebuffer, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC) (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLCREATERENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEPROC) (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC) (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC) (GLuint renderbuffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLCREATETEXTURESPROC) (GLenum target, GLsizei n, GLuint *textures); +typedef void (APIENTRYP PFNGLTEXTUREBUFFERPROC) (GLuint texture, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLTEXTUREBUFFERRANGEPROC) (GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizei size); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE1DPROC) (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DPROC) (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DPROC) (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC) (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC) (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE1DPROC) (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE2DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE3DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC) (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE1DPROC) (GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE2DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE3DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFPROC) (GLuint texture, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFVPROC) (GLuint texture, GLenum pname, const GLfloat *param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIPROC) (GLuint texture, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIIVPROC) (GLuint texture, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIUIVPROC) (GLuint texture, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIVPROC) (GLuint texture, GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLGENERATETEXTUREMIPMAPPROC) (GLuint texture); +typedef void (APIENTRYP PFNGLBINDTEXTUREUNITPROC) (GLuint unit, GLuint texture); +typedef void (APIENTRYP PFNGLGETTEXTUREIMAGEPROC) (GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC) (GLuint texture, GLint level, GLsizei bufSize, void *pixels); +typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERFVPROC) (GLuint texture, GLint level, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERIVPROC) (GLuint texture, GLint level, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERFVPROC) (GLuint texture, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIIVPROC) (GLuint texture, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIUIVPROC) (GLuint texture, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIVPROC) (GLuint texture, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLCREATEVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays); +typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYATTRIBPROC) (GLuint vaobj, GLuint index); +typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYATTRIBPROC) (GLuint vaobj, GLuint index); +typedef void (APIENTRYP PFNGLVERTEXARRAYELEMENTBUFFERPROC) (GLuint vaobj, GLuint buffer); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBUFFERPROC) (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBUFFERSPROC) (GLuint vaobj, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBBINDINGPROC) (GLuint vaobj, GLuint attribindex, GLuint bindingindex); +typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBFORMATPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBIFORMATPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBLFORMATPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYBINDINGDIVISORPROC) (GLuint vaobj, GLuint bindingindex, GLuint divisor); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYIVPROC) (GLuint vaobj, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYINDEXEDIVPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYINDEXED64IVPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint64 *param); +typedef void (APIENTRYP PFNGLCREATESAMPLERSPROC) (GLsizei n, GLuint *samplers); +typedef void (APIENTRYP PFNGLCREATEPROGRAMPIPELINESPROC) (GLsizei n, GLuint *pipelines); +typedef void (APIENTRYP PFNGLCREATEQUERIESPROC) (GLenum target, GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTI64VPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTIVPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTUI64VPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTUIVPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void (APIENTRYP PFNGLMEMORYBARRIERBYREGIONPROC) (GLbitfield barriers); +typedef void (APIENTRYP PFNGLGETTEXTURESUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void *pixels); +typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSPROC) (void); +typedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint lod, GLsizei bufSize, void *pixels); +typedef void (APIENTRYP PFNGLGETNTEXIMAGEPROC) (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +typedef void (APIENTRYP PFNGLGETNUNIFORMDVPROC) (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMFVPROC) (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMIVPROC) (GLuint program, GLint location, GLsizei bufSize, GLint *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMUIVPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint *params); +typedef void (APIENTRYP PFNGLREADNPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); +typedef void (APIENTRYP PFNGLTEXTUREBARRIERPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glClipControl (GLenum origin, GLenum depth); +GLAPI void APIENTRY glCreateTransformFeedbacks (GLsizei n, GLuint *ids); +GLAPI void APIENTRY glTransformFeedbackBufferBase (GLuint xfb, GLuint index, GLuint buffer); +GLAPI void APIENTRY glTransformFeedbackBufferRange (GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizei size); +GLAPI void APIENTRY glGetTransformFeedbackiv (GLuint xfb, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetTransformFeedbacki_v (GLuint xfb, GLenum pname, GLuint index, GLint *param); +GLAPI void APIENTRY glGetTransformFeedbacki64_v (GLuint xfb, GLenum pname, GLuint index, GLint64 *param); +GLAPI void APIENTRY glCreateBuffers (GLsizei n, GLuint *buffers); +GLAPI void APIENTRY glNamedBufferStorage (GLuint buffer, GLsizei size, const void *data, GLbitfield flags); +GLAPI void APIENTRY glNamedBufferData (GLuint buffer, GLsizei size, const void *data, GLenum usage); +GLAPI void APIENTRY glNamedBufferSubData (GLuint buffer, GLintptr offset, GLsizei size, const void *data); +GLAPI void APIENTRY glCopyNamedBufferSubData (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizei size); +GLAPI void APIENTRY glClearNamedBufferData (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearNamedBufferSubData (GLuint buffer, GLenum internalformat, GLintptr offset, GLsizei size, GLenum format, GLenum type, const void *data); +GLAPI void *APIENTRY glMapNamedBuffer (GLuint buffer, GLenum access); +GLAPI void *APIENTRY glMapNamedBufferRange (GLuint buffer, GLintptr offset, GLsizei length, GLbitfield access); +GLAPI GLboolean APIENTRY glUnmapNamedBuffer (GLuint buffer); +GLAPI void APIENTRY glFlushMappedNamedBufferRange (GLuint buffer, GLintptr offset, GLsizei length); +GLAPI void APIENTRY glGetNamedBufferParameteriv (GLuint buffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetNamedBufferParameteri64v (GLuint buffer, GLenum pname, GLint64 *params); +GLAPI void APIENTRY glGetNamedBufferPointerv (GLuint buffer, GLenum pname, void **params); +GLAPI void APIENTRY glGetNamedBufferSubData (GLuint buffer, GLintptr offset, GLsizei size, void *data); +GLAPI void APIENTRY glCreateFramebuffers (GLsizei n, GLuint *framebuffers); +GLAPI void APIENTRY glNamedFramebufferRenderbuffer (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GLAPI void APIENTRY glNamedFramebufferParameteri (GLuint framebuffer, GLenum pname, GLint param); +GLAPI void APIENTRY glNamedFramebufferTexture (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glNamedFramebufferTextureLayer (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI void APIENTRY glNamedFramebufferDrawBuffer (GLuint framebuffer, GLenum buf); +GLAPI void APIENTRY glNamedFramebufferDrawBuffers (GLuint framebuffer, GLsizei n, const GLenum *bufs); +GLAPI void APIENTRY glNamedFramebufferReadBuffer (GLuint framebuffer, GLenum src); +GLAPI void APIENTRY glInvalidateNamedFramebufferData (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments); +GLAPI void APIENTRY glInvalidateNamedFramebufferSubData (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glClearNamedFramebufferiv (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint *value); +GLAPI void APIENTRY glClearNamedFramebufferuiv (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint *value); +GLAPI void APIENTRY glClearNamedFramebufferfv (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat *value); +GLAPI void APIENTRY glClearNamedFramebufferfi (GLuint framebuffer, GLenum buffer, const GLfloat depth, GLint stencil); +GLAPI void APIENTRY glBlitNamedFramebuffer (GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +GLAPI GLenum APIENTRY glCheckNamedFramebufferStatus (GLuint framebuffer, GLenum target); +GLAPI void APIENTRY glGetNamedFramebufferParameteriv (GLuint framebuffer, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetNamedFramebufferAttachmentParameteriv (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); +GLAPI void APIENTRY glCreateRenderbuffers (GLsizei n, GLuint *renderbuffers); +GLAPI void APIENTRY glNamedRenderbufferStorage (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glNamedRenderbufferStorageMultisample (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetNamedRenderbufferParameteriv (GLuint renderbuffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glCreateTextures (GLenum target, GLsizei n, GLuint *textures); +GLAPI void APIENTRY glTextureBuffer (GLuint texture, GLenum internalformat, GLuint buffer); +GLAPI void APIENTRY glTextureBufferRange (GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizei size); +GLAPI void APIENTRY glTextureStorage1D (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width); +GLAPI void APIENTRY glTextureStorage2D (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTextureStorage3D (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glTextureStorage2DMultisample (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTextureStorage3DMultisample (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTextureSubImage1D (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureSubImage2D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureSubImage3D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCompressedTextureSubImage1D (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTextureSubImage2D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTextureSubImage3D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCopyTextureSubImage1D (GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyTextureSubImage2D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glCopyTextureSubImage3D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTextureParameterf (GLuint texture, GLenum pname, GLfloat param); +GLAPI void APIENTRY glTextureParameterfv (GLuint texture, GLenum pname, const GLfloat *param); +GLAPI void APIENTRY glTextureParameteri (GLuint texture, GLenum pname, GLint param); +GLAPI void APIENTRY glTextureParameterIiv (GLuint texture, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTextureParameterIuiv (GLuint texture, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glTextureParameteriv (GLuint texture, GLenum pname, const GLint *param); +GLAPI void APIENTRY glGenerateTextureMipmap (GLuint texture); +GLAPI void APIENTRY glBindTextureUnit (GLuint unit, GLuint texture); +GLAPI void APIENTRY glGetTextureImage (GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +GLAPI void APIENTRY glGetCompressedTextureImage (GLuint texture, GLint level, GLsizei bufSize, void *pixels); +GLAPI void APIENTRY glGetTextureLevelParameterfv (GLuint texture, GLint level, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTextureLevelParameteriv (GLuint texture, GLint level, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTextureParameterfv (GLuint texture, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTextureParameterIiv (GLuint texture, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTextureParameterIuiv (GLuint texture, GLenum pname, GLuint *params); +GLAPI void APIENTRY glGetTextureParameteriv (GLuint texture, GLenum pname, GLint *params); +GLAPI void APIENTRY glCreateVertexArrays (GLsizei n, GLuint *arrays); +GLAPI void APIENTRY glDisableVertexArrayAttrib (GLuint vaobj, GLuint index); +GLAPI void APIENTRY glEnableVertexArrayAttrib (GLuint vaobj, GLuint index); +GLAPI void APIENTRY glVertexArrayElementBuffer (GLuint vaobj, GLuint buffer); +GLAPI void APIENTRY glVertexArrayVertexBuffer (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +GLAPI void APIENTRY glVertexArrayVertexBuffers (GLuint vaobj, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +GLAPI void APIENTRY glVertexArrayAttribBinding (GLuint vaobj, GLuint attribindex, GLuint bindingindex); +GLAPI void APIENTRY glVertexArrayAttribFormat (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayAttribIFormat (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayAttribLFormat (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayBindingDivisor (GLuint vaobj, GLuint bindingindex, GLuint divisor); +GLAPI void APIENTRY glGetVertexArrayiv (GLuint vaobj, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetVertexArrayIndexediv (GLuint vaobj, GLuint index, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetVertexArrayIndexed64iv (GLuint vaobj, GLuint index, GLenum pname, GLint64 *param); +GLAPI void APIENTRY glCreateSamplers (GLsizei n, GLuint *samplers); +GLAPI void APIENTRY glCreateProgramPipelines (GLsizei n, GLuint *pipelines); +GLAPI void APIENTRY glCreateQueries (GLenum target, GLsizei n, GLuint *ids); +GLAPI void APIENTRY glGetQueryBufferObjecti64v (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +GLAPI void APIENTRY glGetQueryBufferObjectiv (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +GLAPI void APIENTRY glGetQueryBufferObjectui64v (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +GLAPI void APIENTRY glGetQueryBufferObjectuiv (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +GLAPI void APIENTRY glMemoryBarrierByRegion (GLbitfield barriers); +GLAPI void APIENTRY glGetTextureSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +GLAPI void APIENTRY glGetCompressedTextureSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void *pixels); +GLAPI GLenum APIENTRY glGetGraphicsResetStatus (void); +GLAPI void APIENTRY glGetnCompressedTexImage (GLenum target, GLint lod, GLsizei bufSize, void *pixels); +GLAPI void APIENTRY glGetnTexImage (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +GLAPI void APIENTRY glGetnUniformdv (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +GLAPI void APIENTRY glGetnUniformfv (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); +GLAPI void APIENTRY glGetnUniformiv (GLuint program, GLint location, GLsizei bufSize, GLint *params); +GLAPI void APIENTRY glGetnUniformuiv (GLuint program, GLint location, GLsizei bufSize, GLuint *params); +GLAPI void APIENTRY glReadnPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); +GLAPI void APIENTRY glTextureBarrier (void); +#endif +#endif /* GL_VERSION_4_5 */ + +#ifndef GL_ARB_ES2_compatibility +#define GL_ARB_ES2_compatibility 1 +#endif /* GL_ARB_ES2_compatibility */ + +#ifndef GL_ARB_ES3_1_compatibility +#define GL_ARB_ES3_1_compatibility 1 +#endif /* GL_ARB_ES3_1_compatibility */ + +#ifndef GL_ARB_ES3_compatibility +#define GL_ARB_ES3_compatibility 1 +#endif /* GL_ARB_ES3_compatibility */ + +#ifndef GL_ARB_arrays_of_arrays +#define GL_ARB_arrays_of_arrays 1 +#endif /* GL_ARB_arrays_of_arrays */ + +#ifndef GL_ARB_base_instance +#define GL_ARB_base_instance 1 +#endif /* GL_ARB_base_instance */ + +#ifndef GL_ARB_bindless_texture +#define GL_ARB_bindless_texture 1 +typedef uint64_t GLuint64EXT; +#define GL_UNSIGNED_INT64_ARB 0x140F +typedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLEARBPROC) (GLuint texture); +typedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLEARBPROC) (GLuint texture, GLuint sampler); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTARBPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC) (GLuint64 handle); +typedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLEARBPROC) (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTARBPROC) (GLuint64 handle, GLenum access); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64ARBPROC) (GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC) (GLuint program, GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *values); +typedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTARBPROC) (GLuint64 handle); +typedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTARBPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64ARBPROC) (GLuint index, GLuint64EXT x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VARBPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VARBPROC) (GLuint index, GLenum pname, GLuint64EXT *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint64 APIENTRY glGetTextureHandleARB (GLuint texture); +GLAPI GLuint64 APIENTRY glGetTextureSamplerHandleARB (GLuint texture, GLuint sampler); +GLAPI void APIENTRY glMakeTextureHandleResidentARB (GLuint64 handle); +GLAPI void APIENTRY glMakeTextureHandleNonResidentARB (GLuint64 handle); +GLAPI GLuint64 APIENTRY glGetImageHandleARB (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +GLAPI void APIENTRY glMakeImageHandleResidentARB (GLuint64 handle, GLenum access); +GLAPI void APIENTRY glMakeImageHandleNonResidentARB (GLuint64 handle); +GLAPI void APIENTRY glUniformHandleui64ARB (GLint location, GLuint64 value); +GLAPI void APIENTRY glUniformHandleui64vARB (GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glProgramUniformHandleui64ARB (GLuint program, GLint location, GLuint64 value); +GLAPI void APIENTRY glProgramUniformHandleui64vARB (GLuint program, GLint location, GLsizei count, const GLuint64 *values); +GLAPI GLboolean APIENTRY glIsTextureHandleResidentARB (GLuint64 handle); +GLAPI GLboolean APIENTRY glIsImageHandleResidentARB (GLuint64 handle); +GLAPI void APIENTRY glVertexAttribL1ui64ARB (GLuint index, GLuint64EXT x); +GLAPI void APIENTRY glVertexAttribL1ui64vARB (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glGetVertexAttribLui64vARB (GLuint index, GLenum pname, GLuint64EXT *params); +#endif +#endif /* GL_ARB_bindless_texture */ + +#ifndef GL_ARB_blend_func_extended +#define GL_ARB_blend_func_extended 1 +#endif /* GL_ARB_blend_func_extended */ + +#ifndef GL_ARB_buffer_storage +#define GL_ARB_buffer_storage 1 +#endif /* GL_ARB_buffer_storage */ + +#ifndef GL_ARB_cl_event +#define GL_ARB_cl_event 1 +struct _cl_context; +struct _cl_event; +#define GL_SYNC_CL_EVENT_ARB 0x8240 +#define GL_SYNC_CL_EVENT_COMPLETE_ARB 0x8241 +typedef GLsync (APIENTRYP PFNGLCREATESYNCFROMCLEVENTARBPROC) (struct _cl_context *context, struct _cl_event *event, GLbitfield flags); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLsync APIENTRY glCreateSyncFromCLeventARB (struct _cl_context *context, struct _cl_event *event, GLbitfield flags); +#endif +#endif /* GL_ARB_cl_event */ + +#ifndef GL_ARB_clear_buffer_object +#define GL_ARB_clear_buffer_object 1 +#endif /* GL_ARB_clear_buffer_object */ + +#ifndef GL_ARB_clear_texture +#define GL_ARB_clear_texture 1 +#endif /* GL_ARB_clear_texture */ + +#ifndef GL_ARB_clip_control +#define GL_ARB_clip_control 1 +#endif /* GL_ARB_clip_control */ + +#ifndef GL_ARB_compressed_texture_pixel_storage +#define GL_ARB_compressed_texture_pixel_storage 1 +#endif /* GL_ARB_compressed_texture_pixel_storage */ + +#ifndef GL_ARB_compute_shader +#define GL_ARB_compute_shader 1 +#endif /* GL_ARB_compute_shader */ + +#ifndef GL_ARB_compute_variable_group_size +#define GL_ARB_compute_variable_group_size 1 +#define GL_MAX_COMPUTE_VARIABLE_GROUP_INVOCATIONS_ARB 0x9344 +#define GL_MAX_COMPUTE_FIXED_GROUP_INVOCATIONS_ARB 0x90EB +#define GL_MAX_COMPUTE_VARIABLE_GROUP_SIZE_ARB 0x9345 +#define GL_MAX_COMPUTE_FIXED_GROUP_SIZE_ARB 0x91BF +typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z, GLuint group_size_x, GLuint group_size_y, GLuint group_size_z); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDispatchComputeGroupSizeARB (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z, GLuint group_size_x, GLuint group_size_y, GLuint group_size_z); +#endif +#endif /* GL_ARB_compute_variable_group_size */ + +#ifndef GL_ARB_conditional_render_inverted +#define GL_ARB_conditional_render_inverted 1 +#endif /* GL_ARB_conditional_render_inverted */ + +#ifndef GL_ARB_conservative_depth +#define GL_ARB_conservative_depth 1 +#endif /* GL_ARB_conservative_depth */ + +#ifndef GL_ARB_copy_buffer +#define GL_ARB_copy_buffer 1 +#endif /* GL_ARB_copy_buffer */ + +#ifndef GL_ARB_copy_image +#define GL_ARB_copy_image 1 +#endif /* GL_ARB_copy_image */ + +#ifndef GL_ARB_cull_distance +#define GL_ARB_cull_distance 1 +#endif /* GL_ARB_cull_distance */ + +#ifndef GL_ARB_debug_output +#define GL_ARB_debug_output 1 +typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +#define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION_ARB 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245 +#define GL_DEBUG_SOURCE_API_ARB 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION_ARB 0x824A +#define GL_DEBUG_SOURCE_OTHER_ARB 0x824B +#define GL_DEBUG_TYPE_ERROR_ARB 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E +#define GL_DEBUG_TYPE_PORTABILITY_ARB 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE_ARB 0x8250 +#define GL_DEBUG_TYPE_OTHER_ARB 0x8251 +#define GL_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES_ARB 0x9145 +#define GL_DEBUG_SEVERITY_HIGH_ARB 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147 +#define GL_DEBUG_SEVERITY_LOW_ARB 0x9148 +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLARBPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTARBPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKARBPROC) (GLDEBUGPROCARB callback, const void *userParam); +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGARBPROC) (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDebugMessageControlARB (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI void APIENTRY glDebugMessageInsertARB (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +GLAPI void APIENTRY glDebugMessageCallbackARB (GLDEBUGPROCARB callback, const void *userParam); +GLAPI GLuint APIENTRY glGetDebugMessageLogARB (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +#endif +#endif /* GL_ARB_debug_output */ + +#ifndef GL_ARB_depth_buffer_float +#define GL_ARB_depth_buffer_float 1 +#endif /* GL_ARB_depth_buffer_float */ + +#ifndef GL_ARB_depth_clamp +#define GL_ARB_depth_clamp 1 +#endif /* GL_ARB_depth_clamp */ + +#ifndef GL_ARB_derivative_control +#define GL_ARB_derivative_control 1 +#endif /* GL_ARB_derivative_control */ + +#ifndef GL_ARB_direct_state_access +#define GL_ARB_direct_state_access 1 +#endif /* GL_ARB_direct_state_access */ + +#ifndef GL_ARB_draw_buffers_blend +#define GL_ARB_draw_buffers_blend 1 +typedef void (APIENTRYP PFNGLBLENDEQUATIONIARBPROC) (GLuint buf, GLenum mode); +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIARBPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLBLENDFUNCIARBPROC) (GLuint buf, GLenum src, GLenum dst); +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIARBPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationiARB (GLuint buf, GLenum mode); +GLAPI void APIENTRY glBlendEquationSeparateiARB (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +GLAPI void APIENTRY glBlendFunciARB (GLuint buf, GLenum src, GLenum dst); +GLAPI void APIENTRY glBlendFuncSeparateiARB (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#endif +#endif /* GL_ARB_draw_buffers_blend */ + +#ifndef GL_ARB_draw_elements_base_vertex +#define GL_ARB_draw_elements_base_vertex 1 +#endif /* GL_ARB_draw_elements_base_vertex */ + +#ifndef GL_ARB_draw_indirect +#define GL_ARB_draw_indirect 1 +#endif /* GL_ARB_draw_indirect */ + +#ifndef GL_ARB_enhanced_layouts +#define GL_ARB_enhanced_layouts 1 +#endif /* GL_ARB_enhanced_layouts */ + +#ifndef GL_ARB_explicit_attrib_location +#define GL_ARB_explicit_attrib_location 1 +#endif /* GL_ARB_explicit_attrib_location */ + +#ifndef GL_ARB_explicit_uniform_location +#define GL_ARB_explicit_uniform_location 1 +#endif /* GL_ARB_explicit_uniform_location */ + +#ifndef GL_ARB_fragment_coord_conventions +#define GL_ARB_fragment_coord_conventions 1 +#endif /* GL_ARB_fragment_coord_conventions */ + +#ifndef GL_ARB_fragment_layer_viewport +#define GL_ARB_fragment_layer_viewport 1 +#endif /* GL_ARB_fragment_layer_viewport */ + +#ifndef GL_ARB_framebuffer_no_attachments +#define GL_ARB_framebuffer_no_attachments 1 +#endif /* GL_ARB_framebuffer_no_attachments */ + +#ifndef GL_ARB_framebuffer_object +#define GL_ARB_framebuffer_object 1 +#endif /* GL_ARB_framebuffer_object */ + +#ifndef GL_ARB_framebuffer_sRGB +#define GL_ARB_framebuffer_sRGB 1 +#endif /* GL_ARB_framebuffer_sRGB */ + +#ifndef GL_ARB_get_program_binary +#define GL_ARB_get_program_binary 1 +#endif /* GL_ARB_get_program_binary */ + +#ifndef GL_ARB_get_texture_sub_image +#define GL_ARB_get_texture_sub_image 1 +#endif /* GL_ARB_get_texture_sub_image */ + +#ifndef GL_ARB_gpu_shader5 +#define GL_ARB_gpu_shader5 1 +#endif /* GL_ARB_gpu_shader5 */ + +#ifndef GL_ARB_gpu_shader_fp64 +#define GL_ARB_gpu_shader_fp64 1 +#endif /* GL_ARB_gpu_shader_fp64 */ + +#ifndef GL_ARB_half_float_vertex +#define GL_ARB_half_float_vertex 1 +#endif /* GL_ARB_half_float_vertex */ + +#ifndef GL_ARB_imaging +#define GL_ARB_imaging 1 +#define GL_BLEND_COLOR 0x8005 +#define GL_BLEND_EQUATION 0x8009 +#endif /* GL_ARB_imaging */ + +#ifndef GL_ARB_indirect_parameters +#define GL_ARB_indirect_parameters 1 +#define GL_PARAMETER_BUFFER_ARB 0x80EE +#define GL_PARAMETER_BUFFER_BINDING_ARB 0x80EF +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTARBPROC) (GLenum mode, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTARBPROC) (GLenum mode, GLenum type, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysIndirectCountARB (GLenum mode, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +GLAPI void APIENTRY glMultiDrawElementsIndirectCountARB (GLenum mode, GLenum type, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#endif +#endif /* GL_ARB_indirect_parameters */ + +#ifndef GL_ARB_internalformat_query +#define GL_ARB_internalformat_query 1 +#endif /* GL_ARB_internalformat_query */ + +#ifndef GL_ARB_internalformat_query2 +#define GL_ARB_internalformat_query2 1 +#define GL_SRGB_DECODE_ARB 0x8299 +#endif /* GL_ARB_internalformat_query2 */ + +#ifndef GL_ARB_invalidate_subdata +#define GL_ARB_invalidate_subdata 1 +#endif /* GL_ARB_invalidate_subdata */ + +#ifndef GL_ARB_map_buffer_alignment +#define GL_ARB_map_buffer_alignment 1 +#endif /* GL_ARB_map_buffer_alignment */ + +#ifndef GL_ARB_map_buffer_range +#define GL_ARB_map_buffer_range 1 +#endif /* GL_ARB_map_buffer_range */ + +#ifndef GL_ARB_multi_bind +#define GL_ARB_multi_bind 1 +#endif /* GL_ARB_multi_bind */ + +#ifndef GL_ARB_multi_draw_indirect +#define GL_ARB_multi_draw_indirect 1 +#endif /* GL_ARB_multi_draw_indirect */ + +#ifndef GL_ARB_occlusion_query2 +#define GL_ARB_occlusion_query2 1 +#endif /* GL_ARB_occlusion_query2 */ + +#ifndef GL_ARB_pipeline_statistics_query +#define GL_ARB_pipeline_statistics_query 1 +#define GL_VERTICES_SUBMITTED_ARB 0x82EE +#define GL_PRIMITIVES_SUBMITTED_ARB 0x82EF +#define GL_VERTEX_SHADER_INVOCATIONS_ARB 0x82F0 +#define GL_TESS_CONTROL_SHADER_PATCHES_ARB 0x82F1 +#define GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB 0x82F2 +#define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB 0x82F3 +#define GL_FRAGMENT_SHADER_INVOCATIONS_ARB 0x82F4 +#define GL_COMPUTE_SHADER_INVOCATIONS_ARB 0x82F5 +#define GL_CLIPPING_INPUT_PRIMITIVES_ARB 0x82F6 +#define GL_CLIPPING_OUTPUT_PRIMITIVES_ARB 0x82F7 +#endif /* GL_ARB_pipeline_statistics_query */ + +#ifndef GL_ARB_program_interface_query +#define GL_ARB_program_interface_query 1 +#endif /* GL_ARB_program_interface_query */ + +#ifndef GL_ARB_provoking_vertex +#define GL_ARB_provoking_vertex 1 +#endif /* GL_ARB_provoking_vertex */ + +#ifndef GL_ARB_query_buffer_object +#define GL_ARB_query_buffer_object 1 +#endif /* GL_ARB_query_buffer_object */ + +#ifndef GL_ARB_robust_buffer_access_behavior +#define GL_ARB_robust_buffer_access_behavior 1 +#endif /* GL_ARB_robust_buffer_access_behavior */ + +#ifndef GL_ARB_robustness +#define GL_ARB_robustness 1 +#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB 0x00000004 +#define GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define GL_GUILTY_CONTEXT_RESET_ARB 0x8253 +#define GL_INNOCENT_CONTEXT_RESET_ARB 0x8254 +#define GL_UNKNOWN_CONTEXT_RESET_ARB 0x8255 +#define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define GL_NO_RESET_NOTIFICATION_ARB 0x8261 +typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSARBPROC) (void); +typedef void (APIENTRYP PFNGLGETNTEXIMAGEARBPROC) (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *img); +typedef void (APIENTRYP PFNGLREADNPIXELSARBPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); +typedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC) (GLenum target, GLint lod, GLsizei bufSize, void *img); +typedef void (APIENTRYP PFNGLGETNUNIFORMFVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLint *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMUIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMDVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLenum APIENTRY glGetGraphicsResetStatusARB (void); +GLAPI void APIENTRY glGetnTexImageARB (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *img); +GLAPI void APIENTRY glReadnPixelsARB (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); +GLAPI void APIENTRY glGetnCompressedTexImageARB (GLenum target, GLint lod, GLsizei bufSize, void *img); +GLAPI void APIENTRY glGetnUniformfvARB (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); +GLAPI void APIENTRY glGetnUniformivARB (GLuint program, GLint location, GLsizei bufSize, GLint *params); +GLAPI void APIENTRY glGetnUniformuivARB (GLuint program, GLint location, GLsizei bufSize, GLuint *params); +GLAPI void APIENTRY glGetnUniformdvARB (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +#endif +#endif /* GL_ARB_robustness */ + +#ifndef GL_ARB_robustness_isolation +#define GL_ARB_robustness_isolation 1 +#endif /* GL_ARB_robustness_isolation */ + +#ifndef GL_ARB_sample_shading +#define GL_ARB_sample_shading 1 +#define GL_SAMPLE_SHADING_ARB 0x8C36 +#define GL_MIN_SAMPLE_SHADING_VALUE_ARB 0x8C37 +typedef void (APIENTRYP PFNGLMINSAMPLESHADINGARBPROC) (GLfloat value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMinSampleShadingARB (GLfloat value); +#endif +#endif /* GL_ARB_sample_shading */ + +#ifndef GL_ARB_sampler_objects +#define GL_ARB_sampler_objects 1 +#endif /* GL_ARB_sampler_objects */ + +#ifndef GL_ARB_seamless_cube_map +#define GL_ARB_seamless_cube_map 1 +#endif /* GL_ARB_seamless_cube_map */ + +#ifndef GL_ARB_seamless_cubemap_per_texture +#define GL_ARB_seamless_cubemap_per_texture 1 +#endif /* GL_ARB_seamless_cubemap_per_texture */ + +#ifndef GL_ARB_separate_shader_objects +#define GL_ARB_separate_shader_objects 1 +#endif /* GL_ARB_separate_shader_objects */ + +#ifndef GL_ARB_shader_atomic_counters +#define GL_ARB_shader_atomic_counters 1 +#endif /* GL_ARB_shader_atomic_counters */ + +#ifndef GL_ARB_shader_bit_encoding +#define GL_ARB_shader_bit_encoding 1 +#endif /* GL_ARB_shader_bit_encoding */ + +#ifndef GL_ARB_shader_draw_parameters +#define GL_ARB_shader_draw_parameters 1 +#endif /* GL_ARB_shader_draw_parameters */ + +#ifndef GL_ARB_shader_group_vote +#define GL_ARB_shader_group_vote 1 +#endif /* GL_ARB_shader_group_vote */ + +#ifndef GL_ARB_shader_image_load_store +#define GL_ARB_shader_image_load_store 1 +#endif /* GL_ARB_shader_image_load_store */ + +#ifndef GL_ARB_shader_image_size +#define GL_ARB_shader_image_size 1 +#endif /* GL_ARB_shader_image_size */ + +#ifndef GL_ARB_shader_precision +#define GL_ARB_shader_precision 1 +#endif /* GL_ARB_shader_precision */ + +#ifndef GL_ARB_shader_stencil_export +#define GL_ARB_shader_stencil_export 1 +#endif /* GL_ARB_shader_stencil_export */ + +#ifndef GL_ARB_shader_storage_buffer_object +#define GL_ARB_shader_storage_buffer_object 1 +#endif /* GL_ARB_shader_storage_buffer_object */ + +#ifndef GL_ARB_shader_subroutine +#define GL_ARB_shader_subroutine 1 +#endif /* GL_ARB_shader_subroutine */ + +#ifndef GL_ARB_shader_texture_image_samples +#define GL_ARB_shader_texture_image_samples 1 +#endif /* GL_ARB_shader_texture_image_samples */ + +#ifndef GL_ARB_shading_language_420pack +#define GL_ARB_shading_language_420pack 1 +#endif /* GL_ARB_shading_language_420pack */ + +#ifndef GL_ARB_shading_language_include +#define GL_ARB_shading_language_include 1 +#define GL_SHADER_INCLUDE_ARB 0x8DAE +#define GL_NAMED_STRING_LENGTH_ARB 0x8DE9 +#define GL_NAMED_STRING_TYPE_ARB 0x8DEA +typedef void (APIENTRYP PFNGLNAMEDSTRINGARBPROC) (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string); +typedef void (APIENTRYP PFNGLDELETENAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name); +typedef void (APIENTRYP PFNGLCOMPILESHADERINCLUDEARBPROC) (GLuint shader, GLsizei count, const GLchar *const*path, const GLint *length); +typedef GLboolean (APIENTRYP PFNGLISNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name); +typedef void (APIENTRYP PFNGLGETNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string); +typedef void (APIENTRYP PFNGLGETNAMEDSTRINGIVARBPROC) (GLint namelen, const GLchar *name, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glNamedStringARB (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string); +GLAPI void APIENTRY glDeleteNamedStringARB (GLint namelen, const GLchar *name); +GLAPI void APIENTRY glCompileShaderIncludeARB (GLuint shader, GLsizei count, const GLchar *const*path, const GLint *length); +GLAPI GLboolean APIENTRY glIsNamedStringARB (GLint namelen, const GLchar *name); +GLAPI void APIENTRY glGetNamedStringARB (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string); +GLAPI void APIENTRY glGetNamedStringivARB (GLint namelen, const GLchar *name, GLenum pname, GLint *params); +#endif +#endif /* GL_ARB_shading_language_include */ + +#ifndef GL_ARB_shading_language_packing +#define GL_ARB_shading_language_packing 1 +#endif /* GL_ARB_shading_language_packing */ + +#ifndef GL_ARB_sparse_buffer +#define GL_ARB_sparse_buffer 1 +#define GL_SPARSE_STORAGE_BIT_ARB 0x0400 +#define GL_SPARSE_BUFFER_PAGE_SIZE_ARB 0x82F8 +typedef void (APIENTRYP PFNGLBUFFERPAGECOMMITMENTARBPROC) (GLenum target, GLintptr offset, GLsizeiptr size, GLboolean commit); +typedef void (APIENTRYP PFNGLNAMEDBUFFERPAGECOMMITMENTEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); +typedef void (APIENTRYP PFNGLNAMEDBUFFERPAGECOMMITMENTARBPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBufferPageCommitmentARB (GLenum target, GLintptr offset, GLsizeiptr size, GLboolean commit); +GLAPI void APIENTRY glNamedBufferPageCommitmentEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); +GLAPI void APIENTRY glNamedBufferPageCommitmentARB (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); +#endif +#endif /* GL_ARB_sparse_buffer */ + +#ifndef GL_ARB_sparse_texture +#define GL_ARB_sparse_texture 1 +#define GL_TEXTURE_SPARSE_ARB 0x91A6 +#define GL_VIRTUAL_PAGE_SIZE_INDEX_ARB 0x91A7 +#define GL_NUM_SPARSE_LEVELS_ARB 0x91AA +#define GL_NUM_VIRTUAL_PAGE_SIZES_ARB 0x91A8 +#define GL_VIRTUAL_PAGE_SIZE_X_ARB 0x9195 +#define GL_VIRTUAL_PAGE_SIZE_Y_ARB 0x9196 +#define GL_VIRTUAL_PAGE_SIZE_Z_ARB 0x9197 +#define GL_MAX_SPARSE_TEXTURE_SIZE_ARB 0x9198 +#define GL_MAX_SPARSE_3D_TEXTURE_SIZE_ARB 0x9199 +#define GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS_ARB 0x919A +#define GL_SPARSE_TEXTURE_FULL_ARRAY_CUBE_MIPMAPS_ARB 0x91A9 +typedef void (APIENTRYP PFNGLTEXPAGECOMMITMENTARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean resident); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexPageCommitmentARB (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean resident); +#endif +#endif /* GL_ARB_sparse_texture */ + +#ifndef GL_ARB_stencil_texturing +#define GL_ARB_stencil_texturing 1 +#endif /* GL_ARB_stencil_texturing */ + +#ifndef GL_ARB_sync +#define GL_ARB_sync 1 +#endif /* GL_ARB_sync */ + +#ifndef GL_ARB_tessellation_shader +#define GL_ARB_tessellation_shader 1 +#endif /* GL_ARB_tessellation_shader */ + +#ifndef GL_ARB_texture_barrier +#define GL_ARB_texture_barrier 1 +#endif /* GL_ARB_texture_barrier */ + +#ifndef GL_ARB_texture_buffer_object_rgb32 +#define GL_ARB_texture_buffer_object_rgb32 1 +#endif /* GL_ARB_texture_buffer_object_rgb32 */ + +#ifndef GL_ARB_texture_buffer_range +#define GL_ARB_texture_buffer_range 1 +#endif /* GL_ARB_texture_buffer_range */ + +#ifndef GL_ARB_texture_compression_bptc +#define GL_ARB_texture_compression_bptc 1 +#define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F +#endif /* GL_ARB_texture_compression_bptc */ + +#ifndef GL_ARB_texture_compression_rgtc +#define GL_ARB_texture_compression_rgtc 1 +#endif /* GL_ARB_texture_compression_rgtc */ + +#ifndef GL_ARB_texture_cube_map_array +#define GL_ARB_texture_cube_map_array 1 +#define GL_TEXTURE_CUBE_MAP_ARRAY_ARB 0x9009 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_ARB 0x900A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB 0x900B +#define GL_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB 0x900D +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900E +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900F +#endif /* GL_ARB_texture_cube_map_array */ + +#ifndef GL_ARB_texture_gather +#define GL_ARB_texture_gather 1 +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5F +#define GL_MAX_PROGRAM_TEXTURE_GATHER_COMPONENTS_ARB 0x8F9F +#endif /* GL_ARB_texture_gather */ + +#ifndef GL_ARB_texture_mirror_clamp_to_edge +#define GL_ARB_texture_mirror_clamp_to_edge 1 +#endif /* GL_ARB_texture_mirror_clamp_to_edge */ + +#ifndef GL_ARB_texture_multisample +#define GL_ARB_texture_multisample 1 +#endif /* GL_ARB_texture_multisample */ + +#ifndef GL_ARB_texture_query_levels +#define GL_ARB_texture_query_levels 1 +#endif /* GL_ARB_texture_query_levels */ + +#ifndef GL_ARB_texture_query_lod +#define GL_ARB_texture_query_lod 1 +#endif /* GL_ARB_texture_query_lod */ + +#ifndef GL_ARB_texture_rg +#define GL_ARB_texture_rg 1 +#endif /* GL_ARB_texture_rg */ + +#ifndef GL_ARB_texture_rgb10_a2ui +#define GL_ARB_texture_rgb10_a2ui 1 +#endif /* GL_ARB_texture_rgb10_a2ui */ + +#ifndef GL_ARB_texture_stencil8 +#define GL_ARB_texture_stencil8 1 +#endif /* GL_ARB_texture_stencil8 */ + +#ifndef GL_ARB_texture_storage +#define GL_ARB_texture_storage 1 +#endif /* GL_ARB_texture_storage */ + +#ifndef GL_ARB_texture_storage_multisample +#define GL_ARB_texture_storage_multisample 1 +#endif /* GL_ARB_texture_storage_multisample */ + +#ifndef GL_ARB_texture_swizzle +#define GL_ARB_texture_swizzle 1 +#endif /* GL_ARB_texture_swizzle */ + +#ifndef GL_ARB_texture_view +#define GL_ARB_texture_view 1 +#endif /* GL_ARB_texture_view */ + +#ifndef GL_ARB_timer_query +#define GL_ARB_timer_query 1 +#endif /* GL_ARB_timer_query */ + +#ifndef GL_ARB_transform_feedback2 +#define GL_ARB_transform_feedback2 1 +#endif /* GL_ARB_transform_feedback2 */ + +#ifndef GL_ARB_transform_feedback3 +#define GL_ARB_transform_feedback3 1 +#endif /* GL_ARB_transform_feedback3 */ + +#ifndef GL_ARB_transform_feedback_instanced +#define GL_ARB_transform_feedback_instanced 1 +#endif /* GL_ARB_transform_feedback_instanced */ + +#ifndef GL_ARB_transform_feedback_overflow_query +#define GL_ARB_transform_feedback_overflow_query 1 +#define GL_TRANSFORM_FEEDBACK_OVERFLOW_ARB 0x82EC +#define GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB 0x82ED +#endif /* GL_ARB_transform_feedback_overflow_query */ + +#ifndef GL_ARB_uniform_buffer_object +#define GL_ARB_uniform_buffer_object 1 +#endif /* GL_ARB_uniform_buffer_object */ + +#ifndef GL_ARB_vertex_array_bgra +#define GL_ARB_vertex_array_bgra 1 +#endif /* GL_ARB_vertex_array_bgra */ + +#ifndef GL_ARB_vertex_array_object +#define GL_ARB_vertex_array_object 1 +#endif /* GL_ARB_vertex_array_object */ + +#ifndef GL_ARB_vertex_attrib_64bit +#define GL_ARB_vertex_attrib_64bit 1 +#endif /* GL_ARB_vertex_attrib_64bit */ + +#ifndef GL_ARB_vertex_attrib_binding +#define GL_ARB_vertex_attrib_binding 1 +#endif /* GL_ARB_vertex_attrib_binding */ + +#ifndef GL_ARB_vertex_type_10f_11f_11f_rev +#define GL_ARB_vertex_type_10f_11f_11f_rev 1 +#endif /* GL_ARB_vertex_type_10f_11f_11f_rev */ + +#ifndef GL_ARB_vertex_type_2_10_10_10_rev +#define GL_ARB_vertex_type_2_10_10_10_rev 1 +#endif /* GL_ARB_vertex_type_2_10_10_10_rev */ + +#ifndef GL_ARB_viewport_array +#define GL_ARB_viewport_array 1 +#endif /* GL_ARB_viewport_array */ + +#ifndef GL_KHR_context_flush_control +#define GL_KHR_context_flush_control 1 +#endif /* GL_KHR_context_flush_control */ + +#ifndef GL_KHR_debug +#define GL_KHR_debug 1 +#endif /* GL_KHR_debug */ + +#ifndef GL_KHR_robust_buffer_access_behavior +#define GL_KHR_robust_buffer_access_behavior 1 +#endif /* GL_KHR_robust_buffer_access_behavior */ + +#ifndef GL_KHR_robustness +#define GL_KHR_robustness 1 +#define GL_CONTEXT_ROBUST_ACCESS 0x90F3 +#endif /* GL_KHR_robustness */ + +#ifndef GL_KHR_texture_compression_astc_hdr +#define GL_KHR_texture_compression_astc_hdr 1 +#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 +#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 +#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 +#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 +#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 +#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 +#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 +#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 +#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 +#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 +#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA +#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB +#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC +#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD +#endif /* GL_KHR_texture_compression_astc_hdr */ + +#ifndef GL_KHR_texture_compression_astc_ldr +#define GL_KHR_texture_compression_astc_ldr 1 +#endif /* GL_KHR_texture_compression_astc_ldr */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Kernels/builtin_kernels.cl b/Kernels/builtin_kernels.cl new file mode 100644 index 00000000..466e2fc1 --- /dev/null +++ b/Kernels/builtin_kernels.cl @@ -0,0 +1,131 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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. +*/ + +#if defined(cl_intel_device_side_vme_enable) + +__kernel __attribute__((reqd_work_group_size(16,1,1))) +void block_motion_estimate_intel( + sampler_t vmeAccelerator, + __read_only image2d_t srcImage, + __read_only image2d_t refImage, + __global short2* predMVs, + __global short2* outMVs, + __global ushort* outDist, + int iterations ) +{ + __local uint dst[64]; + __local ushort* dist = (__local ushort*)&dst[ 8 * 5 ]; + + int gid_0 = get_group_id(0); + int gid_1 = 0; + + for( int i = 0; i < iterations; i++, gid_1++ ) + { + int2 srcCoord = 0; + int2 refCoord = 0; + + srcCoord.x = gid_0 * 16 + get_global_offset(0); + srcCoord.y = gid_1 * 16 + get_global_offset(1); + + short2 predMV = 0; + + #ifndef HW_NULL_CHECK + if( predMVs != NULL ) + #endif + { + predMV = predMVs[ gid_0 + gid_1 * get_num_groups(0) ]; + refCoord.x = predMV.x / 4; + refCoord.y = predMV.y / 4; + refCoord.y = refCoord.y & 0xFFFE; + } + + intel_work_group_vme_mb_query( dst, srcCoord, refCoord, srcImage, refImage, vmeAccelerator ); + barrier(CLK_LOCAL_MEM_FENCE); + + // Write Out Result + + // 4x4 + if( intel_get_accelerator_mb_block_type( vmeAccelerator ) == 0x2 ) + { + int x = get_local_id(0) % 4; + int y = get_local_id(0) / 4; + int index = + ( gid_0 * 4 + x ) + + ( gid_1 * 4 + y ) * get_num_groups(0) * 4; + + short2 val = as_short2( dst[ 8 + ( y * 4 + x ) * 2 ] ); + outMVs[ index ] = val; + + #ifndef HW_NULL_CHECK + if( outDist != NULL ) + #endif + { + outDist[ index ] = dist[ y * 4 + x ]; + } + } + + // 8x8 + if( intel_get_accelerator_mb_block_type( vmeAccelerator ) == 0x1 ) + { + if( get_local_id(0) < 4 ) + { + int x = get_local_id(0) % 2; + int y = get_local_id(0) / 2; + int index = + ( gid_0 * 2 + x ) + + ( gid_1 * 2 + y ) * get_num_groups(0) * 2; + short2 val = as_short2( dst[ 8 + ( y * 2 + x ) * 8 ] ); + outMVs[ index ] = val; + + #ifndef HW_NULL_CHECK + if( outDist != NULL ) + #endif + { + outDist[ index ] = dist[ ( y * 2 + x ) * 4 ]; + } + } + } + + // 16x16 + if( intel_get_accelerator_mb_block_type( vmeAccelerator ) == 0x0 ) + { + if( get_local_id(0) == 0 ) + { + int index = + gid_0 + + gid_1 * get_num_groups(0); + + short2 val = as_short2( dst[8] ); + outMVs[ index ] = val; + + #ifndef HW_NULL_CHECK + if( outDist != NULL ) + #endif + { + outDist[ index ] = dist[ 0 ]; + } + } + } + } +} + +#endif \ No newline at end of file diff --git a/Kernels/precompiled_kernels.cl b/Kernels/precompiled_kernels.cl new file mode 100644 index 00000000..b9f039bc --- /dev/null +++ b/Kernels/precompiled_kernels.cl @@ -0,0 +1,297 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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. +*/ + +#pragma OPENCL EXTENSION cl_khr_byte_addressable_store : enable + +// This is a very slow kernel, but is guaranteed to be correct. +// +// This kernel can work with any local work size. +// The global work size should be at least bytesToRead. + +__kernel void CopyBufferBytes( + const __global uchar* pSrc, + __global uchar* pDst, + uint srcOffsetInBytes, + uint dstOffsetInBytes, + uint bytesToRead ) +{ + uint index = get_global_id(0); + + pSrc += ( srcOffsetInBytes + index ); + pDst += ( dstOffsetInBytes + index ); + + uint lastIndex = bytesToRead / sizeof(uchar); + + if( index < lastIndex ) + { + pDst[ 0 ] = pSrc[ 0 ]; + } +} + +// This is a faster kernel but it only works when the source +// offset and dst offset are multiples of sizeof(uint). +// +// This kernel can work with any local work size. +// The global work size should be at least ceil( bytesToRead / sizeof(uint) ). + +__kernel void CopyBufferUInts( + const __global uint* pSrc, + __global uint* pDst, + uint srcOffsetInUInts, + uint dstOffsetInUInts, + uint bytesToRead ) +{ + uint index = get_global_id(0); + + pSrc += srcOffsetInUInts + index; + pDst += dstOffsetInUInts + index; + + uint lastIndex = bytesToRead / sizeof(uint); + + if( index < lastIndex ) + { + pDst[ 0 ] = pSrc[ 0 ]; + } + else + { + if( index == lastIndex ) + { + const __global uchar* pByteSrc = pSrc; + __global uchar* pByteDst = pDst; + + uint bytesRemaining = bytesToRead % sizeof(uint); + + while( bytesRemaining ) + { + pByteDst[ 0 ] = pByteSrc[ 0 ]; + + bytesRemaining--; + pByteSrc++; + pByteDst++; + } + } + } +} + +// This is a faster kernel but it only works when the source +// offset and dst offset are multiples of sizeof(uint4). +// +// This kernel can work with any local work size. +// The global work size should be at least ceil( bytesToRead / sizeof(uint4) ). + +__kernel void CopyBufferUInt4s( + const __global uint4* pSrc, + __global uint4* pDst, + uint srcOffsetInUInt4s, + uint dstOffsetInUInt4s, + uint bytesToRead ) +{ + uint index = get_global_id(0); + + pSrc += srcOffsetInUInt4s + index; + pDst += dstOffsetInUInt4s + index; + + uint lastIndex = bytesToRead / sizeof(uint4); + + if( index < lastIndex ) + { + pDst[ 0 ] = pSrc[ 0 ]; + } + else + { + if( index == lastIndex ) + { + const __global uchar* pByteSrc = pSrc; + __global uchar* pByteDst = pDst; + + uint bytesRemaining = bytesToRead % sizeof(uint4); + + while( bytesRemaining ) + { + pByteDst[ 0 ] = pByteSrc[ 0 ]; + + bytesRemaining--; + pByteSrc++; + pByteDst++; + } + } + } +} + +// This is an experimental kernel. It only works when the source +// offset and dst offset are multiples of sizeof(uint16). +// +// This kernel can work with any local work size. +// The global work size should be at least ceil( bytesToRead / sizeof(uint16) ). + +__kernel void CopyBufferUInt16s( + const __global uint16* pSrc, + __global uint16* pDst, + uint srcOffsetInUInt16s, + uint dstOffsetInUInt16s, + uint bytesToRead ) +{ + uint index = get_global_id(0); + + pSrc += srcOffsetInUInt16s + index; + pDst += dstOffsetInUInt16s + index; + + uint lastIndex = bytesToRead / sizeof(uint16); + + if( index < lastIndex ) + { + pDst[ 0 ] = pSrc[ 0 ]; + } + else + { + if( index == lastIndex ) + { + const __global uchar* pByteSrc = pSrc; + __global uchar* pByteDst = pDst; + + uint bytesRemaining = bytesToRead % sizeof(uint16); + + while( bytesRemaining ) + { + pByteDst[ 0 ] = pByteSrc[ 0 ]; + + bytesRemaining--; + pByteSrc++; + pByteDst++; + } + } + } +} + +#if __IMAGE_SUPPORT__ + +// Technically, this is probably required, but we can probably avoid it +// on 99% of GPU devices. +#define CHECK_IMAGE_BOUNDS() +//#define CHECK_IMAGE_BOUNDS() if( x < regionX ) + +__kernel void CopyImage2Dto2DFloat( + __read_only image2d_t srcImage, + __write_only image2d_t dstImage, + uint srcOriginX, + uint srcOriginY, + uint srcOriginZ, + uint dstOriginX, + uint dstOriginY, + uint dstOriginZ, + uint regionX, + uint regionY, + uint regionZ ) +{ + const sampler_t samplerInline = + CLK_NORMALIZED_COORDS_FALSE | + CLK_ADDRESS_CLAMP_TO_EDGE | + CLK_FILTER_NEAREST; + + uint x = get_global_id(0); + uint y = get_global_id(1); + + CHECK_IMAGE_BOUNDS() + { + uint srcX = x + srcOriginX; + uint srcY = y + srcOriginY; + + uint dstX = x + dstOriginX; + uint dstY = y + dstOriginY; + + float4 color = read_imagef( srcImage, samplerInline, (int2)( srcX, srcY ) ); + + write_imagef( dstImage, (int2)( dstX, dstY ), color ); + } +} + +__kernel void CopyImage2Dto2DInt( + __read_only image2d_t srcImage, + __write_only image2d_t dstImage, + uint srcOriginX, + uint srcOriginY, + uint srcOriginZ, + uint dstOriginX, + uint dstOriginY, + uint dstOriginZ, + uint regionX, + uint regionY, + uint regionZ ) +{ + const sampler_t samplerInline = + CLK_NORMALIZED_COORDS_FALSE | + CLK_ADDRESS_CLAMP_TO_EDGE | + CLK_FILTER_NEAREST; + + uint x = get_global_id(0); + uint y = get_global_id(1); + + CHECK_IMAGE_BOUNDS() + { + uint srcX = x + srcOriginX; + uint srcY = y + srcOriginY; + + uint dstX = x + dstOriginX; + uint dstY = y + dstOriginY; + + int4 color = read_imagei( srcImage, samplerInline, (int2)( srcX, srcY ) ); + + write_imagei( dstImage, (int2)( dstX, dstY ), color ); + } +} + +__kernel void CopyImage2Dto2DUInt( + __read_only image2d_t srcImage, + __write_only image2d_t dstImage, + uint srcOriginX, + uint srcOriginY, + uint srcOriginZ, + uint dstOriginX, + uint dstOriginY, + uint dstOriginZ, + uint regionX, + uint regionY, + uint regionZ ) +{ + const sampler_t samplerInline = + CLK_NORMALIZED_COORDS_FALSE | + CLK_ADDRESS_CLAMP_TO_EDGE | + CLK_FILTER_NEAREST; + + uint x = get_global_id(0); + uint y = get_global_id(1); + + CHECK_IMAGE_BOUNDS() + { + uint srcX = x + srcOriginX; + uint srcY = y + srcOriginY; + + uint dstX = x + dstOriginX; + uint dstY = y + dstOriginY; + + uint4 color = read_imageui( srcImage, samplerInline, (int2)( srcX, srcY ) ); + + write_imageui( dstImage, (int2)( dstX, dstY ), color ); + } +} + +#endif // __IMAGE_SUPPORT__ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..b685f825 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Intel Corporation + +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. diff --git a/OS/OS.h b/OS/OS.h new file mode 100644 index 00000000..27221600 --- /dev/null +++ b/OS/OS.h @@ -0,0 +1,33 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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. +*/ + +#pragma once + +#if defined(_WIN32) +#include "OS_windows.h" +#elif defined(__linux__) +#include "OS_linux.h" +#elif defined(__APPLE__) +#include "OS_mac.h" +#else +#error Unknown OS! +#endif diff --git a/OS/OS_linux.cpp b/OS/OS_linux.cpp new file mode 100644 index 00000000..0f20d29e --- /dev/null +++ b/OS/OS_linux.cpp @@ -0,0 +1,36 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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 "OS_linux.h" + +namespace OS +{ + +Services::Services( void* poGlobalData ) +{ +} + +Services::~Services() +{ +} + +} diff --git a/OS/OS_linux.h b/OS/OS_linux.h new file mode 100644 index 00000000..0acac699 --- /dev/null +++ b/OS/OS_linux.h @@ -0,0 +1,134 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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. +*/ + +#pragma once + +#include "OS_linux_common.h" + +#include "CL/cl.h" // for clGetPlatformIDs + +namespace OS +{ + +class Services : public Services_Common +{ +public: + Services( void* pGlobalData ); + ~Services(); + + bool Init(); + + bool GetCLInterceptName( + std::string& name ) const; + + bool GetPrecompiledKernelString( + const char*& str, + size_t& length ) const; + + bool GetBuiltinKernelString( + const char*& str, + size_t& length ) const; + + bool ExecuteCommand( + const std::string& filename ) const; + bool StartAubCapture( + const std::string& fileName, + uint64_t delay ) const; + bool StopAubCapture( + uint64_t delay ) const; + +private: + DISALLOW_COPY_AND_ASSIGN( Services ); +}; + +inline bool Services::Init() +{ + return Services_Common::Init(); +} + +inline bool Services::GetCLInterceptName( + std::string& name ) const +{ + Dl_info info; + if( dladdr( (void*)clGetPlatformIDs, &info ) ) + { + name = info.dli_fname; + } + return false; +} + +#ifndef __ANDROID__ +extern "C" char _binary_Kernels_precompiled_kernels_cl_start; +extern "C" char _binary_Kernels_precompiled_kernels_cl_end; +#endif + +inline bool Services::GetPrecompiledKernelString( + const char*& str, + size_t& length ) const +{ +#ifndef __ANDROID__ + str = &_binary_Kernels_precompiled_kernels_cl_start; + length = &_binary_Kernels_precompiled_kernels_cl_end - &_binary_Kernels_precompiled_kernels_cl_start; +#endif + + return true; +} + +#ifndef __ANDROID__ +extern "C" char _binary_Kernels_builtin_kernels_cl_start; +extern "C" char _binary_Kernels_builtin_kernels_cl_end; +#endif + +inline bool Services::GetBuiltinKernelString( + const char*& str, + size_t& length ) const +{ +#ifndef __ANDROID__ + str = &_binary_Kernels_builtin_kernels_cl_start; + length = &_binary_Kernels_builtin_kernels_cl_end - &_binary_Kernels_builtin_kernels_cl_start; +#endif + + return true; +} + +inline bool Services::ExecuteCommand( const std::string& command ) const +{ + int res = system( command.c_str() ); + return res != -1; +} + +// TODO + +inline bool Services::StartAubCapture( + const std::string& fileName, + uint64_t delay ) const +{ + return false; +} + +inline bool Services::StopAubCapture( + uint64_t delay ) const +{ + return false; +} + +} diff --git a/OS/OS_linux_common.cpp b/OS/OS_linux_common.cpp new file mode 100644 index 00000000..5971b4fb --- /dev/null +++ b/OS/OS_linux_common.cpp @@ -0,0 +1,151 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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 "OS_linux_common.h" +#ifdef __ANDROID__ +#include +#include +#include +#endif + +namespace OS +{ + +const char* Services_Common::ENV_PREFIX = ""; +const char* Services_Common::CONFIG_FILE = "config.conf"; +const char* Services_Common::LOG_DIR = NULL; + +Services_Common::Services_Common() +{ +} + +Services_Common::~Services_Common() +{ + pthread_mutex_destroy( &m_CriticalSection ); +} + +bool Services_Common::ReadRegistry( + const std::string& name, + void* pValue, + size_t size ) const +{ + // Look at environment variables first: + { + std::string envName(ENV_PREFIX); + envName += name; + const char *envVal = getenv(envName.c_str()); + if( ( envVal != NULL ) && ( size == sizeof(unsigned int) ) ) + { + unsigned int *puVal = (unsigned int *)pValue; + *puVal = atoi(envVal); + return true; + } + else if( ( envVal != NULL ) && ( strlen(envVal) < size ) ) + { + char* pStr = (char*)pValue; + strcpy( pStr, envVal ); + return true; + } + } + + // Look at the config file second: + bool found = false; + + std::ifstream is; + std::string s; + + std::string configFile; + + const char *envVal = getenv("HOME"); +#ifdef __ANDROID__ + // if ho HOME on Android then use sdcard folder + if( envVal == NULL ) + { + configFile = "/sdcard"; + } + else + { + configFile = envVal; + } +#else + configFile = envVal; +#endif + configFile += "/"; + configFile += CONFIG_FILE; + + is.open( configFile.c_str() ); + if( is.fail() ) + { +#ifdef __ANDROID__ + __android_log_print( ANDROID_LOG_WARN, "clIntercept", "Failed to open config file: %s\n", configFile.c_str() ); +#endif + return false; + } + + while( !is.eof() && !found ) + { + std::getline(is, s); + + // skip blank lines + if( s.length() == 0 ) + { + continue; + } + // skip "comment" lines + if( s.find(";") == 0 || s.find("#") == 0 || s.find("//") == 0 ) + { + continue; + } + + size_t pos = s.find('='); + if( pos != std::string::npos ) + { + std::string var = s.substr( 0, pos ); + var.erase(std::remove_if(var.begin(), var.end(), ::isspace), var.end()); + + std::string value = s.substr( pos + 1 ); + value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end()); + + if( var == name ) + { + if( size == sizeof(unsigned int) ) + { + unsigned int* pUIValue = (unsigned int*)pValue; + std::istringstream iss(value); + iss >> pUIValue[0]; + found = true; + } + else if( value.length() < size ) + { + char* pStr = (char*)pValue; + strcpy( pStr, value.c_str() ); + found = true; + } + } + } + } + + is.close(); + return found; +} + +} diff --git a/OS/OS_linux_common.h b/OS/OS_linux_common.h new file mode 100644 index 00000000..d05b0460 --- /dev/null +++ b/OS/OS_linux_common.h @@ -0,0 +1,306 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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. +*/ + +#pragma once + +#include "OS_timer.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef __ANDROID__ +#include +#endif + +/*****************************************************************************\ + +MACRO: + DISALLOW_COPY_AND_ASSIGN + +Description: + A macro to disallow the copy constructor and operator= functions + This should be used in the private: declarations for a class + +\*****************************************************************************/ +#if !defined(DISALLOW_COPY_AND_ASSIGN) +#define DISALLOW_COPY_AND_ASSIGN( TypeName ) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) +#endif + +namespace OS +{ + +class Services_Common +{ +public: + static const char* ENV_PREFIX; + static const char* CONFIG_FILE; + static const char* LOG_DIR; + + Services_Common(); + ~Services_Common(); + + bool Init(); + + void EnterCriticalSection(); + void LeaveCriticalSection(); + + uint64_t GetProcessID() const; + uint64_t GetThreadID() const; + + std::string GetProcessName() const; + + bool ReadRegistry( + const std::string& name, + void* pValue, + size_t size ) const; + + void OutputDebugString( + const std::string& str ) const; + + uint64_t GetTimer() const; + uint64_t TickToNS( + uint64_t delta ) const; + + void* LoadLibrary( + const std::string& libraryName ) const; + void UnloadLibrary( + void*& pLibrary ) const; + + void* GetFunctionPointer( + void* pLibrary, + const std::string& functionName ) const; + + void GetDumpDirectoryName( + const std::string& subDir, + std::string& directoryName ) const; + void GetDumpDirectoryNameWithoutProcessName( + const std::string& subDir, + std::string& directoryName) const; + void MakeDumpDirectories( + const std::string& fileName ) const; + +private: + Timer m_Timer; + pthread_mutex_t m_CriticalSection; + + DISALLOW_COPY_AND_ASSIGN( Services_Common ); +}; + +inline bool Services_Common::Init() +{ + if( pthread_mutex_init( + &m_CriticalSection, + NULL ) ) + { + return false; + } + + return true; +} + +inline void Services_Common::EnterCriticalSection() +{ + pthread_mutex_lock( &m_CriticalSection ); +} + +inline void Services_Common::LeaveCriticalSection() +{ + pthread_mutex_unlock( &m_CriticalSection ); +} + +inline uint64_t Services_Common::GetProcessID() const +{ + return getpid(); +} + +inline uint64_t Services_Common::GetThreadID() const +{ + // TODO: Is this the thread ID we should be returning? + return pthread_self(); +} + +inline std::string Services_Common::GetProcessName() const +{ + char processName[ 1024 ]; + char* pProcessName = processName; + + size_t bytes = readlink( + "/proc/self/exe", + processName, + sizeof( processName ) - 1 ); + if( bytes ) + { + processName[ bytes ] = '\0'; + + pProcessName = strrchr( processName, '/' ); + pProcessName++; + } + else + { + strncpy( processName, "process.exe", sizeof( processName ) ); + processName[ sizeof( processName ) - 1 ] = 0; + } + + return std::string(pProcessName); +} + +inline void Services_Common::OutputDebugString( + const std::string& str ) const +{ + syslog( LOG_USER | LOG_INFO, "%s", str.c_str() ); +} + +inline uint64_t Services_Common::GetTimer() const +{ + return m_Timer.GetTimer(); +} + +inline uint64_t Services_Common::TickToNS( + uint64_t delta ) const +{ + return m_Timer.TickToNS( delta ); +} + +inline void* Services_Common::LoadLibrary( + const std::string& libraryName ) const +{ + void* pLibrary = dlopen( libraryName.c_str(), RTLD_NOW | RTLD_GLOBAL ); + if( pLibrary == NULL ) + { + fprintf(stderr, "dlopen() error: %s\n", dlerror()); + } + return pLibrary; +} + +inline void Services_Common::UnloadLibrary( + void*& pLibrary ) const +{ + dlclose( pLibrary ); + pLibrary = NULL; +} + +inline void* Services_Common::GetFunctionPointer( + void* pLibrary, + const std::string& functionName ) const +{ + if( pLibrary ) + { + return dlsym( pLibrary, functionName.c_str() ); + } + else + { + return dlsym( RTLD_NEXT, functionName.c_str() ); + } +} + +inline void Services_Common::GetDumpDirectoryName( + const std::string& subDir, + std::string& directoryName ) const +{ + // Get the home directory and add our directory name. + if( LOG_DIR ) + { + // Return log dir override if set in regkeys + directoryName = LOG_DIR; + } + else + { + { +#ifndef __ANDROID__ + directoryName = getenv("HOME"); +#else + const char *envVal = getenv("HOME"); + if( envVal == NULL ) + { + directoryName = "/sdcard/Intel"; + } + else + { + directoryName = envVal; + } +#endif + directoryName += "/"; + directoryName += subDir; + directoryName += "/"; + } + // Add the process name to the directory name. + directoryName += GetProcessName(); + } + +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, "clIntercept", "dumpDir=%s\n", directoryName.c_str()); +#endif +} + +inline void Services_Common::GetDumpDirectoryNameWithoutProcessName( + const std::string& subDir, + std::string& directoryName) const +{ + // Get the home directory and add our directory name. + if( LOG_DIR ) + { + // Return log dir override if set in regkeys + directoryName = LOG_DIR; + } + else + { + directoryName = getenv("HOME"); + directoryName += "/"; + directoryName += subDir; + directoryName += "/"; + } +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, "clIntercept", "dumpDir=%s\n", directoryName.c_str()); +#endif +} + +inline void Services_Common::MakeDumpDirectories( + const std::string& fileName ) const +{ + // The first directory name is the root. We don't + // have to make a directory for it. + std::string::size_type pos = fileName.find( "/" ); + + pos = fileName.find( "/", ++pos ); + while( pos != std::string::npos ) + { + mkdir( + fileName.substr( 0, pos ).c_str(), + 0777 ); + + pos = fileName.find( "/", ++pos ); + } +} + +} diff --git a/OS/OS_mac.cpp b/OS/OS_mac.cpp new file mode 100644 index 00000000..028e27bc --- /dev/null +++ b/OS/OS_mac.cpp @@ -0,0 +1,36 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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 "OS_mac.h" + +namespace OS +{ + +Services::Services( void* poGlobalData ) +{ +} + +Services::~Services() +{ +} + +} diff --git a/OS/OS_mac.h b/OS/OS_mac.h new file mode 100644 index 00000000..8d574187 --- /dev/null +++ b/OS/OS_mac.h @@ -0,0 +1,114 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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. +*/ +#pragma once + +#include "OS_mac_common.h" + +void CLIntercept_Load(void); + +namespace OS +{ + +class Services : public Services_Common +{ +public: + Services( void* pGlobalData ); + ~Services(); + + bool Init(); + + bool GetCLInterceptName( + std::string& name ) const; + + bool GetPrecompiledKernelString( + const char*& str, + size_t& length ) const; + + bool GetBuiltinKernelString( + const char*& str, + size_t& length ) const; + + bool ExecuteCommand( + const std::string& filename ) const; + bool StartAubCapture( + const std::string& fileName, + uint64_t delay ) const; + bool StopAubCapture( + uint64_t delay ) const; + +private: + DISALLOW_COPY_AND_ASSIGN( Services ); +}; + +inline bool Services::Init() +{ + return Services_Common::Init(); +} + +inline bool Services::GetCLInterceptName( + std::string& name ) const +{ + Dl_info info; + if( dladdr( (void*)CLIntercept_Load, &info ) ) + { + name = info.dli_fname; + } + return false; +} + +// TODO: We currently don't support any of the kernels overrides on OSX. + +inline bool Services::GetPrecompiledKernelString( + const char*& str, + size_t& length ) const +{ + return false; +} + +inline bool Services::GetBuiltinKernelString( + const char*& str, + size_t& length ) const +{ + return false; +} + +// TODO + +inline bool Services::ExecuteCommand( const std::string& command ) const +{ + return false; +} + +inline bool Services::StartAubCapture( + const std::string& fileName, + uint64_t delay ) const +{ + return false; +} + +inline bool Services::StopAubCapture( + uint64_t delay ) const +{ + return false; +} + +} diff --git a/OS/OS_mac_common.cpp b/OS/OS_mac_common.cpp new file mode 100644 index 00000000..ab848173 --- /dev/null +++ b/OS/OS_mac_common.cpp @@ -0,0 +1,130 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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 "OS_mac_common.h" + +namespace OS +{ + +const char* Services_Common::ENV_PREFIX = ""; +const char* Services_Common::CONFIG_FILE = "config.conf"; +const char* Services_Common::LOG_DIR = NULL; + +Services_Common::Services_Common() +{ +} + +Services_Common::~Services_Common() +{ + pthread_mutex_destroy( &m_CriticalSection ); +} + +bool Services_Common::ReadRegistry( + const std::string& name, + void* pValue, + size_t size ) const +{ + // Look at environment variables first: + { + std::string envName(ENV_PREFIX); + envName += name; + const char *envVal = getenv(envName.c_str()); + if( ( envVal != NULL ) && ( size == sizeof(unsigned int) ) ) + { + unsigned int *puVal = (unsigned int *)pValue; + *puVal = atoi(envVal); + return true; + } + else if( ( envVal != NULL ) && ( strlen(envVal) < size ) ) + { + char* pStr = (char*)pValue; + strcpy( pStr, envVal ); + return true; + } + } + + // Look at the config file second: + bool found = false; + + std::ifstream is; + std::string s; + + std::string configFile; + + configFile = getenv("HOME"); + configFile += "/"; + configFile += CONFIG_FILE; + + is.open( configFile.c_str() ); + if( is.fail() ) + { + return false; + } + + while( !is.eof() && !found ) + { + std::getline(is, s); + + // skip blank lines + if( s.length() == 0 ) + { + continue; + } + // skip "comment" lines + if( s.find(";") == 0 || s.find("#") == 0 || s.find("//") == 0 ) + { + continue; + } + + size_t pos = s.find('='); + if( pos != std::string::npos ) + { + std::string var = s.substr( 0, pos ); + var.erase(remove_if(var.begin(), var.end(), ::isspace), var.end()); + + std::string value = s.substr( pos + 1 ); + value.erase(remove_if(value.begin(), value.end(), ::isspace), value.end()); + + if( var == name ) + { + if( size == sizeof(unsigned int) ) + { + unsigned int* pUIValue = (unsigned int*)pValue; + std::istringstream iss(value); + iss >> pUIValue[0]; + found = true; + } + else if( value.length() < size ) + { + char* pStr = (char*)pValue; + strcpy( pStr, value.c_str() ); + found = true; + } + } + } + } + + is.close(); + return found; +} + +} diff --git a/OS/OS_mac_common.h b/OS/OS_mac_common.h new file mode 100644 index 00000000..9ae62c7d --- /dev/null +++ b/OS/OS_mac_common.h @@ -0,0 +1,251 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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. +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/*****************************************************************************\ + +MACRO: + DISALLOW_COPY_AND_ASSIGN + +Description: + A macro to disallow the copy constructor and operator= functions + This should be used in the private: declarations for a class + +\*****************************************************************************/ +#if !defined(DISALLOW_COPY_AND_ASSIGN) +#define DISALLOW_COPY_AND_ASSIGN( TypeName ) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) +#endif + +namespace OS +{ + +class Services_Common +{ +public: + static const char* ENV_PREFIX; + static const char* CONFIG_FILE; + static const char* LOG_DIR; + + Services_Common(); + ~Services_Common(); + + bool Init(); + + void EnterCriticalSection(); + void LeaveCriticalSection(); + + uint64_t GetThreadID() const; + + bool ReadRegistry( + const std::string& name, + void* pValue, + size_t size ) const; + + void OutputDebugString( + const std::string& str ) const; + + uint64_t GetTimer() const; + uint64_t TickToNS( + uint64_t delta ) const; + + void* LoadLibrary( + const std::string& libraryName ) const; + void UnloadLibrary( + void*& pLibrary ) const; + + void* GetFunctionPointer( + void* pLibrary, + const std::string& functionName ) const; + + void GetDumpDirectoryName( + const std::string& subDir, + std::string& directoryName ) const; + void GetDumpDirectoryNameWithoutProcessName( + const std::string& subDir, + std::string& directoryName) const; + void MakeDumpDirectories( + const std::string& fileName ) const; + +private: + pthread_mutex_t m_CriticalSection; + + DISALLOW_COPY_AND_ASSIGN( Services_Common ); +}; + +inline bool Services_Common::Init() +{ + if( pthread_mutex_init( + &m_CriticalSection, + NULL ) ) + { + return false; + } + + return true; +} + +inline void Services_Common::EnterCriticalSection() +{ + pthread_mutex_lock( &m_CriticalSection ); +} + +inline void Services_Common::LeaveCriticalSection() +{ + pthread_mutex_unlock( &m_CriticalSection ); +} + +inline uint64_t Services_Common::GetThreadID() const +{ + // TODO + return 0; +} + +inline void Services_Common::OutputDebugString( + const std::string& str ) const +{ + syslog( LOG_USER | LOG_INFO, "%s", str.c_str() ); +} + +inline uint64_t Services_Common::GetTimer() const +{ + timeval i; + gettimeofday( &i, NULL ); + return i.tv_sec * 1000000 + i.tv_usec; +} + +inline uint64_t Services_Common::TickToNS( + uint64_t delta ) const +{ + double ns = delta * 1000.0; + return (uint64_t)ns; +} + +inline void* Services_Common::LoadLibrary( + const std::string& libraryName ) const +{ + void* pLibrary = dlopen( libraryName.c_str(), RTLD_NOW ); + return pLibrary; +} + +inline void Services_Common::UnloadLibrary( + void*& pLibrary ) const +{ + dlclose( pLibrary ); + pLibrary = NULL; +} + +inline void* Services_Common::GetFunctionPointer( + void* pLibrary, + const std::string& functionName ) const +{ + if( pLibrary ) + { + return dlsym( pLibrary, functionName.c_str() ); + } + else + { + return dlsym( RTLD_NEXT, functionName.c_str() ); + } +} + +inline void Services_Common::GetDumpDirectoryName( + const std::string& subDir, + std::string& directoryName ) const +{ + // Get the home directory and add our directory name. + { + directoryName = getenv("HOME"); + directoryName += "/"; + directoryName += subDir; + directoryName += "/"; + } + // Add the process name to the directory name. + { + char processName[ 1024 ]; + char* pProcessName = processName; + + pid_t pid = getpid(); + int ret = proc_pidpath( pid, processName, sizeof(processName) ); + if( ret > 0 ) + { + pProcessName = strrchr( processName, '/' ); + } + else + { + strncpy( processName, "process.exe", sizeof( processName ) ); + processName[ sizeof( processName ) - 1 ] = 0; + } + + directoryName += pProcessName; + } +} + +inline void Services_Common::GetDumpDirectoryNameWithoutProcessName( + const std::string& subDir, + std::string& directoryName) const +{ + // Get the home directory and add our directory name. + { + directoryName = getenv("HOME"); + directoryName += "/"; + directoryName += subDir; + directoryName += "/"; + } +} + +inline void Services_Common::MakeDumpDirectories( + const std::string& fileName ) const +{ + // The first directory name is the root. We don't + // have to make a directory for it. + std::string::size_type pos = fileName.find( "/" ); + + pos = fileName.find( "/", ++pos ); + while( pos != std::string::npos ) + { + mkdir( + fileName.substr( 0, pos ).c_str(), + 0777 ); + + pos = fileName.find( "/", ++pos ); + } +} + +} diff --git a/OS/OS_mac_interpose.h b/OS/OS_mac_interpose.h new file mode 100644 index 00000000..49a08c74 --- /dev/null +++ b/OS/OS_mac_interpose.h @@ -0,0 +1,171 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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. +*/ + +#pragma once + +#define CLINTERCEPT_DYLD_INTERPOSE(_funcname) \ + __attribute__((used)) static const struct \ + { \ + const void* replacement; \ + const void* replacee; \ + } \ + _interpose_ ## _funcname __attribute__ ((section ("__DATA,__interpose"))) = \ + { \ + (const void*)(unsigned long)&CLIRN( _funcname ), \ + (const void*)(unsigned long)&_funcname \ + }; + +CLINTERCEPT_DYLD_INTERPOSE(clGetPlatformIDs); +CLINTERCEPT_DYLD_INTERPOSE(clGetPlatformInfo); +CLINTERCEPT_DYLD_INTERPOSE(clGetDeviceIDs); +CLINTERCEPT_DYLD_INTERPOSE(clGetDeviceInfo); +CLINTERCEPT_DYLD_INTERPOSE(clCreateContext); +CLINTERCEPT_DYLD_INTERPOSE(clCreateContextFromType); +CLINTERCEPT_DYLD_INTERPOSE(clRetainContext); +CLINTERCEPT_DYLD_INTERPOSE(clReleaseContext); +CLINTERCEPT_DYLD_INTERPOSE(clGetContextInfo); +CLINTERCEPT_DYLD_INTERPOSE(clCreateCommandQueue); +CLINTERCEPT_DYLD_INTERPOSE(clRetainCommandQueue); +CLINTERCEPT_DYLD_INTERPOSE(clReleaseCommandQueue); +CLINTERCEPT_DYLD_INTERPOSE(clGetCommandQueueInfo); +CLINTERCEPT_DYLD_INTERPOSE(clSetCommandQueueProperty); +CLINTERCEPT_DYLD_INTERPOSE(clCreateBuffer); +CLINTERCEPT_DYLD_INTERPOSE(clCreateImage2D); +CLINTERCEPT_DYLD_INTERPOSE(clCreateImage3D); +CLINTERCEPT_DYLD_INTERPOSE(clRetainMemObject); +CLINTERCEPT_DYLD_INTERPOSE(clReleaseMemObject); +CLINTERCEPT_DYLD_INTERPOSE(clGetSupportedImageFormats); +CLINTERCEPT_DYLD_INTERPOSE(clGetMemObjectInfo); +CLINTERCEPT_DYLD_INTERPOSE(clGetImageInfo); +CLINTERCEPT_DYLD_INTERPOSE(clCreateSampler); +CLINTERCEPT_DYLD_INTERPOSE(clRetainSampler); +CLINTERCEPT_DYLD_INTERPOSE(clReleaseSampler); +CLINTERCEPT_DYLD_INTERPOSE(clGetSamplerInfo); +CLINTERCEPT_DYLD_INTERPOSE(clCreateProgramWithSource); +CLINTERCEPT_DYLD_INTERPOSE(clCreateProgramWithBinary); +CLINTERCEPT_DYLD_INTERPOSE(clRetainProgram); +CLINTERCEPT_DYLD_INTERPOSE(clReleaseProgram); +CLINTERCEPT_DYLD_INTERPOSE(clBuildProgram); +CLINTERCEPT_DYLD_INTERPOSE(clUnloadCompiler); +CLINTERCEPT_DYLD_INTERPOSE(clGetProgramInfo); +CLINTERCEPT_DYLD_INTERPOSE(clGetProgramBuildInfo); +CLINTERCEPT_DYLD_INTERPOSE(clCreateKernel); +CLINTERCEPT_DYLD_INTERPOSE(clCreateKernelsInProgram); +CLINTERCEPT_DYLD_INTERPOSE(clRetainKernel); +CLINTERCEPT_DYLD_INTERPOSE(clReleaseKernel); +CLINTERCEPT_DYLD_INTERPOSE(clSetKernelArg); +CLINTERCEPT_DYLD_INTERPOSE(clGetKernelInfo); +CLINTERCEPT_DYLD_INTERPOSE(clGetKernelWorkGroupInfo); +CLINTERCEPT_DYLD_INTERPOSE(clWaitForEvents); +CLINTERCEPT_DYLD_INTERPOSE(clGetEventInfo); +CLINTERCEPT_DYLD_INTERPOSE(clRetainEvent); +CLINTERCEPT_DYLD_INTERPOSE(clReleaseEvent); +CLINTERCEPT_DYLD_INTERPOSE(clGetEventProfilingInfo); +CLINTERCEPT_DYLD_INTERPOSE(clFlush); +CLINTERCEPT_DYLD_INTERPOSE(clFinish); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueReadBuffer); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueWriteBuffer); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueCopyBuffer); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueReadImage); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueWriteImage); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueCopyImage); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueCopyImageToBuffer); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueCopyBufferToImage); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueMapBuffer); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueMapImage); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueUnmapMemObject); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueNDRangeKernel); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueTask); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueNativeKernel); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueMarker); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueWaitForEvents); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueBarrier); + +// Optional features? +CLINTERCEPT_DYLD_INTERPOSE(clGetExtensionFunctionAddress); +CLINTERCEPT_DYLD_INTERPOSE(clGetExtensionFunctionAddressForPlatform); + +// OpenCL 1.1 Entry Points (optional) +CLINTERCEPT_DYLD_INTERPOSE(clCreateSubBuffer); +CLINTERCEPT_DYLD_INTERPOSE(clSetMemObjectDestructorCallback); +CLINTERCEPT_DYLD_INTERPOSE(clCreateUserEvent); +CLINTERCEPT_DYLD_INTERPOSE(clSetUserEventStatus); +CLINTERCEPT_DYLD_INTERPOSE(clSetEventCallback); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueReadBufferRect); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueWriteBufferRect); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueCopyBufferRect); + +// OpenCL 1.2 Entry Points (optional) +CLINTERCEPT_DYLD_INTERPOSE(clCompileProgram); +CLINTERCEPT_DYLD_INTERPOSE(clCreateFromGLTexture); +CLINTERCEPT_DYLD_INTERPOSE(clCreateImage); +CLINTERCEPT_DYLD_INTERPOSE(clCreateProgramWithBuiltInKernels); +CLINTERCEPT_DYLD_INTERPOSE(clCreateSubDevices); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueBarrierWithWaitList); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueFillBuffer); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueFillImage); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueMarkerWithWaitList); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueMigrateMemObjects); +CLINTERCEPT_DYLD_INTERPOSE(clGetKernelArgInfo); +CLINTERCEPT_DYLD_INTERPOSE(clLinkProgram); +CLINTERCEPT_DYLD_INTERPOSE(clReleaseDevice); +CLINTERCEPT_DYLD_INTERPOSE(clRetainDevice); +CLINTERCEPT_DYLD_INTERPOSE(clUnloadPlatformCompiler); + +// OpenCL 2.0 Entry Points (optional) +#if 0 +// Disabled for now, until Apple supports OpenCL 2.0. +CLINTERCEPT_DYLD_INTERPOSE(clSVMAlloc); +CLINTERCEPT_DYLD_INTERPOSE(clSVMFree); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueSVMFree); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueSVMMemcpy); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueSVMMemFill); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueSVMMap); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueSVMUnmap); +CLINTERCEPT_DYLD_INTERPOSE(clSetKernelArgSVMPointer); +CLINTERCEPT_DYLD_INTERPOSE(clSetKernelExecInfo); +CLINTERCEPT_DYLD_INTERPOSE(clCreatePipe); +CLINTERCEPT_DYLD_INTERPOSE(clGetPipeInfo); +CLINTERCEPT_DYLD_INTERPOSE(clCreateCommandQueueWithProperties); +CLINTERCEPT_DYLD_INTERPOSE(clCreateSamplerWithProperties); +#endif + +// OpenCL 2.1 Entry Points (optional) +#if 0 +// Disabled for now, until Apple supports OpenCL 2.1. +CLINTERCEPT_DYLD_INTERPOSE(clSetDefaultDeviceCommandQueue); +CLINTERCEPT_DYLD_INTERPOSE(clGetDeviceAndHostTimer); +CLINTERCEPT_DYLD_INTERPOSE(clGetHostTimer); +CLINTERCEPT_DYLD_INTERPOSE(clCreateProgramWithIL); +CLINTERCEPT_DYLD_INTERPOSE(clCloneKernel); +CLINTERCEPT_DYLD_INTERPOSE(clGetKernelSubGroupInfo); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueSVMMigrateMem); +#endif + +// CL-GL Entry Points (optional) +CLINTERCEPT_DYLD_INTERPOSE(clCreateFromGLBuffer); +CLINTERCEPT_DYLD_INTERPOSE(clCreateFromGLTexture2D); +CLINTERCEPT_DYLD_INTERPOSE(clCreateFromGLTexture3D); +CLINTERCEPT_DYLD_INTERPOSE(clCreateFromGLRenderbuffer); +CLINTERCEPT_DYLD_INTERPOSE(clGetGLObjectInfo); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueAcquireGLObjects); +CLINTERCEPT_DYLD_INTERPOSE(clEnqueueReleaseGLObjects); diff --git a/OS/OS_timer.h b/OS/OS_timer.h new file mode 100644 index 00000000..fac6dc9a --- /dev/null +++ b/OS/OS_timer.h @@ -0,0 +1,125 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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. +*/ + +#pragma once + +#if defined(_WIN32) +#include +// Visual Studio 2008 doesn't support stdint.h, but +// Visual Studio 2010 does. When CLIntercept stops +// supporting Visual Studio 2008 we can remove this +// typedef and include stdint.h instead. +typedef unsigned __int64 uint64_t; +#elif defined(__linux__) +#include +#include +#include +#endif + +/*****************************************************************************\ + +MACRO: + DISALLOW_COPY_AND_ASSIGN + +Description: + A macro to disallow the copy constructor and operator= functions + This should be used in the private: declarations for a class + +\*****************************************************************************/ +#if !defined(DISALLOW_COPY_AND_ASSIGN) +#define DISALLOW_COPY_AND_ASSIGN( TypeName ) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) +#endif + +namespace OS +{ + +class Timer +{ +public: + Timer() {}; + ~Timer() {}; + + bool Init( void ); + + uint64_t GetTimer( void ) const; + uint64_t TickToNS( uint64_t delta ) const; + +private: +#if defined(_WIN32) + LARGE_INTEGER m_Freq; +#endif + + DISALLOW_COPY_AND_ASSIGN( Timer ); +}; + +inline bool Timer::Init( void ) +{ +#if defined(_WIN32) + if( ::QueryPerformanceFrequency( &m_Freq ) == FALSE ) + { + return false; + } +#endif + return true; +} + +inline uint64_t Timer::GetTimer( void ) const +{ +#if defined(_WIN32) + LARGE_INTEGER i; + ::QueryPerformanceCounter( &i ); + return (uint64_t)i.QuadPart; +#elif defined(__linux__) +#ifdef USE_OLD_TIMER + timeval i; + gettimeofday( &i, NULL ); + return i.tv_sec * 1000000 + i.tv_usec; +#else + struct timespec t; + clock_gettime( CLOCK_MONOTONIC, &t ); + return t.tv_sec * 1000000000 + t.tv_nsec; +#endif +#else +#error Need to implement Timer::GetTimer! +#endif +} + +inline uint64_t Timer::TickToNS( uint64_t delta ) const +{ +#if defined(_WIN32) + double ns = delta * ( 1000000000.0 / m_Freq.QuadPart ); + return (uint64_t)ns; +#elif defined(__linux__) +#ifdef USE_OLD_TIMER + double ns = delta * 1000.0; + return (uint64_t)ns; +#else + return delta; +#endif +#else +#error Need to implement Timer::TickToNS! +#endif +} + +} diff --git a/OS/OS_windows.cpp b/OS/OS_windows.cpp new file mode 100644 index 00000000..711a9785 --- /dev/null +++ b/OS/OS_windows.cpp @@ -0,0 +1,37 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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 "OS_windows.h" + +namespace OS +{ + +Services::Services( void* pGlobalData ) +{ + m_hInstance = (HINSTANCE)pGlobalData; +} + +Services::~Services() +{ +} + +} \ No newline at end of file diff --git a/OS/OS_windows.h b/OS/OS_windows.h new file mode 100644 index 00000000..34712b22 --- /dev/null +++ b/OS/OS_windows.h @@ -0,0 +1,192 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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. +*/ + +#pragma once + +#include "OS_windows_common.h" + +#include "resource/clIntercept_resource.h" + +namespace OS +{ + +class Services : public Services_Common +{ +public: + Services( void* pGlobalData ); + ~Services(); + + bool Init(); + + bool GetCLInterceptName( + std::string& name ) const; + + bool GetPrecompiledKernelString( + const char*& str, + size_t& length ) const; + + bool GetBuiltinKernelString( + const char*& str, + size_t& length ) const; + + bool ExecuteCommand( + const std::string& filename ) const; + bool StartAubCapture( + const std::string& fileName, + uint64_t delay ) const; + bool StopAubCapture( + uint64_t delay ) const; + +private: + HINSTANCE m_hInstance; + + DISALLOW_COPY_AND_ASSIGN( Services ); +}; + +inline bool Services::Init() +{ + if( m_hInstance == NULL ) + { + return false; + } + + return Services_Common::Init(); +} + +inline bool Services::GetCLInterceptName( + std::string& name ) const +{ + char dllName[ MAX_PATH ]; + + if( GetModuleFileNameA( m_hInstance, dllName, MAX_PATH - 1 ) ) + { + name = dllName; + return true; + } + + return false; +} + +inline bool Services::GetPrecompiledKernelString( + const char*& str, + size_t& length ) const +{ + bool success = false; + + HRSRC hrsrc = ::FindResource( + m_hInstance, + MAKEINTRESOURCE(IDR_TEXT_PRECOMPILED_KERNELS), + "TEXT" ); + + if( hrsrc != NULL ) + { + length = ::SizeofResource( + m_hInstance, + hrsrc ); + + HGLOBAL hres = ::LoadResource( + m_hInstance, + hrsrc ); + if( hres != NULL ) + { + void* pVoid = ::LockResource( hres ); + if( pVoid ) + { + str = (const char*)pVoid; + success = true; + } + } + } + + return success; +} + +inline bool Services::GetBuiltinKernelString( + const char*& str, + size_t& length ) const +{ + bool success = false; + + HRSRC hrsrc = ::FindResource( + m_hInstance, + MAKEINTRESOURCE(IDR_TEXT_BUILTIN_KERNELS), + "TEXT" ); + + if( hrsrc != NULL ) + { + length = ::SizeofResource( + m_hInstance, + hrsrc ); + + HGLOBAL hres = ::LoadResource( + m_hInstance, + hrsrc ); + if( hres != NULL ) + { + void* pVoid = ::LockResource( hres ); + if( pVoid ) + { + str = (const char*)pVoid; + success = true; + } + } + } + + return success; +} + +inline bool Services::ExecuteCommand( const std::string& command ) const +{ + int res = system( command.c_str() ); + return res != -1; +} + +inline bool Services::StartAubCapture( + const std::string& fileName, + uint64_t delay ) const +{ + if( delay ) + { + Sleep( (DWORD)delay ); + } + + std::string command = "kdc.exe " + fileName; + int res = system(command.c_str()); + //fprintf(stderr, "Running the command: %s returned %d\n", command.c_str(), res ); + return res != -1; +} + +inline bool Services::StopAubCapture( + uint64_t delay ) const +{ + if( delay ) + { + Sleep( (DWORD)delay ); + } + + std::string command = "kdc.exe -off"; + int res = system(command.c_str()); + //fprintf(stderr, "Running the command: %s returned %d\n", command.c_str(), res ); + return res != -1; +} + +} \ No newline at end of file diff --git a/OS/OS_windows_common.cpp b/OS/OS_windows_common.cpp new file mode 100644 index 00000000..0d2b723a --- /dev/null +++ b/OS/OS_windows_common.cpp @@ -0,0 +1,41 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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 "OS_windows_common.h" + +namespace OS +{ + +const char* Services_Common::ENV_PREFIX = ""; +const char* Services_Common::REGISTRY_KEY = "SOFTWARE\\INTEL\\IGFX"; +const char* Services_Common::LOG_DIR = NULL; + +Services_Common::Services_Common() +{ +} + +Services_Common::~Services_Common() +{ + DeleteCriticalSection( &m_CriticalSection ); +} + +} \ No newline at end of file diff --git a/OS/OS_windows_common.h b/OS/OS_windows_common.h new file mode 100644 index 00000000..af6b8c0c --- /dev/null +++ b/OS/OS_windows_common.h @@ -0,0 +1,379 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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. +*/ + +#pragma once + +#include "OS_timer.h" + +#include +#include + +/*****************************************************************************\ + +MACRO: + DISALLOW_COPY_AND_ASSIGN + +Description: + A macro to disallow the copy constructor and operator= functions + This should be used in the private: declarations for a class + +\*****************************************************************************/ +#if !defined(DISALLOW_COPY_AND_ASSIGN) +#define DISALLOW_COPY_AND_ASSIGN( TypeName ) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) +#endif + +// Visual Studio 2008 doesn't support stdint.h, but +// Visual Studio 2010 does. When CLIntercept stops +// supporting Visual Studio 2008 we can remove this +// typedef and include stdint.h instead. +typedef unsigned __int64 uint64_t; + +namespace OS +{ + +class Services_Common +{ +public: + static const char* ENV_PREFIX; + static const char* REGISTRY_KEY; + static const char* LOG_DIR; + + Services_Common(); + ~Services_Common(); + + bool Init(); + + void EnterCriticalSection(); + void LeaveCriticalSection(); + + uint64_t GetProcessID() const; + uint64_t GetThreadID() const; + + std::string GetProcessName() const; + + bool ReadRegistry( + const std::string& name, + void* pValue, + size_t size ) const; + + void OutputDebugString( + const std::string& str ) const; + + uint64_t GetTimer() const; + uint64_t TickToNS( + uint64_t delta ) const; + + void* LoadLibrary( + const std::string& libraryName ) const; + void UnloadLibrary( + void*& pLibrary ) const; + + void* GetFunctionPointer( + void* pLibrary, + const std::string& functionName ) const; + + void GetDumpDirectoryName( + const std::string& subDir, + std::string& directoryName ) const; + void GetDumpDirectoryNameWithoutProcessName( + const std::string& subDir, + std::string& directoryName) const; + void MakeDumpDirectories( + const std::string& fileName ) const; + +private: + Timer m_Timer; + CRITICAL_SECTION m_CriticalSection; + + DISALLOW_COPY_AND_ASSIGN( Services_Common ); +}; + +inline bool Services_Common::Init() +{ + if( m_Timer.Init() == false ) + { + return false; + } + + if( ::InitializeCriticalSectionAndSpinCount( + &m_CriticalSection, + 0x400 ) == FALSE ) + { + return false; + } + + return true; +} + +inline void Services_Common::EnterCriticalSection() +{ + ::EnterCriticalSection( &m_CriticalSection ); +} + +inline void Services_Common::LeaveCriticalSection() +{ + ::LeaveCriticalSection( &m_CriticalSection ); +} + +inline uint64_t Services_Common::GetProcessID() const +{ + return GetCurrentProcessId(); +} + +inline uint64_t Services_Common::GetThreadID() const +{ + return GetCurrentThreadId(); +} + +inline std::string Services_Common::GetProcessName() const +{ + char processName[ MAX_PATH ]; + char* pProcessName = processName; + + if( GetModuleFileNameA( NULL, processName, MAX_PATH - 1 ) ) + { + pProcessName = strrchr( processName, '\\' ); + pProcessName++; + } + else + { + strcpy_s( processName, MAX_PATH, "process.exe" ); + } + + return std::string(pProcessName); +} + +inline bool Services_Common::ReadRegistry( + const std::string& name, + void* pValue, + size_t size ) const +{ + // Look at environment variables first: + { + std::string envName(ENV_PREFIX); + envName += name; + + char* envVal = NULL; + size_t len = 0; + errno_t err = _dupenv_s( &envVal, &len, envName.c_str() ); + if( !err ) + { + if( ( envVal != NULL ) && ( size == sizeof(unsigned int) ) ) + { + unsigned int *puVal = (unsigned int *)pValue; + *puVal = atoi(envVal); + return true; + } + else if( ( envVal != NULL ) && ( strlen(envVal) < size ) ) + { + char* pStr = (char*)pValue; + strcpy_s( pStr, size, envVal ); + return true; + } + free( envVal ); + } + } + + LONG success = ERROR_SUCCESS; + HKEY cliKey; + + // Try HKEY_CURRENT_USER first. + + success = ::RegOpenKeyEx( + HKEY_CURRENT_USER, + REGISTRY_KEY, + 0, + KEY_READ, + &cliKey ); + if( ERROR_SUCCESS == success ) + { + DWORD dwSize = (DWORD)size; + + success = ::RegQueryValueEx( + cliKey, + name.c_str(), + NULL, + NULL, + (LPBYTE)pValue, + &dwSize ); + + ::RegCloseKey( cliKey ); + } + + // Only try HKEY_LOCAL_MACHINE if we didn't find the + // control in HKEY_CURRENT_USER. This way we maintain + // backwards compatibility with existing installations + // of CLIntercept, but controls in HKEY_CURRENT_USER + // "win". + + if( ERROR_SUCCESS != success ) + { + success = ::RegOpenKeyEx( + HKEY_LOCAL_MACHINE, + REGISTRY_KEY, + 0, + KEY_READ, + &cliKey ); + if( ERROR_SUCCESS == success ) + { + DWORD dwSize = (DWORD)size; + + success = ::RegQueryValueEx( + cliKey, + name.c_str(), + NULL, + NULL, + (LPBYTE)pValue, + &dwSize ); + + ::RegCloseKey( cliKey ); + } + } + + return ( ERROR_SUCCESS == success ); +} + +inline void Services_Common::OutputDebugString( + const std::string& str ) const +{ + ::OutputDebugString( str.c_str() ); +} + +inline uint64_t Services_Common::GetTimer() const +{ + return m_Timer.GetTimer(); +} + +inline uint64_t Services_Common::TickToNS( + uint64_t delta ) const +{ + return m_Timer.TickToNS( delta ); +} + +inline void* Services_Common::LoadLibrary( + const std::string& libraryName ) const +{ + HMODULE hModule = ::LoadLibraryA( libraryName.c_str() ); + return hModule; +} + +inline void Services_Common::UnloadLibrary( + void*& pLibrary ) const +{ + HMODULE hModule = (HMODULE)pLibrary; + ::FreeLibrary( hModule ); + pLibrary = NULL; +} + +inline void* Services_Common::GetFunctionPointer( + void* pLibrary, + const std::string& functionName ) const +{ + if( pLibrary ) + { + HMODULE hModule = (HMODULE)pLibrary; + return ::GetProcAddress( hModule, functionName.c_str() ); + } + else + { + return NULL; + } +} + +inline void Services_Common::GetDumpDirectoryName( + const std::string& subDir, + std::string& directoryName ) const +{ + // Return log dir override if set in regkeys + if( LOG_DIR ) + { + directoryName = LOG_DIR; + return; + } + + // Get the system root and add our directory name. + { + char* systemDrive = NULL; + size_t length = 0; + + _dupenv_s( &systemDrive, &length, "SystemDrive" ); + + directoryName = systemDrive; + directoryName += "/Intel/"; + directoryName += subDir; + directoryName += "/"; + + free( systemDrive ); + } + + // Add the process name to the directory name. + directoryName += GetProcessName(); +} + +inline void Services_Common::GetDumpDirectoryNameWithoutProcessName( + const std::string& subDir, + std::string& directoryName) const +{ + // Return log dir override if set in regkeys + if( LOG_DIR ) + { + directoryName = LOG_DIR; + return; + } + + // Get the system root and add our directory name. + { + char* systemDrive = NULL; + size_t length = 0; + + _dupenv_s(&systemDrive, &length, "SystemDrive"); + + directoryName = systemDrive; + directoryName += "/Intel/"; + directoryName += subDir; + directoryName += "/"; + + free(systemDrive); + } +} + +inline void Services_Common::MakeDumpDirectories( + const std::string& fileName ) const +{ + // The first directory name is the root. We don't + // have to make a directory for it. + std::string::size_type pos = fileName.find( "/" ); + + pos = fileName.find( "/", ++pos ); + while( pos != std::string::npos ) + { + CreateDirectoryA( + fileName.substr( 0, pos ).c_str(), + NULL ); + + pos = fileName.find( "/", ++pos ); + } +} + +} diff --git a/README.md b/README.md new file mode 100644 index 00000000..2d69769e --- /dev/null +++ b/README.md @@ -0,0 +1,82 @@ +# Intercept Layer for OpenCL™ Applications + +The Intercept Layer for OpenCL Applications is a tool that can intercept +and modify OpenCL calls for debugging and performance analysis. Using the +Intercept Layer for OpenCL Applications requires no application or driver +modifications. + +To operate, the Intercept Layer for OpenCL Applications masquerades as the +OpenCL ICD loader (usually) or as an OpenCL implementation (rarely) and is +loaded when the application intends to load the real OpenCL ICD loader. As +part of the Intercept Layer for OpenCL Application's initialization, it loads +the real OpenCL ICD loader and gets function pointers to the real OpenCL +entry points. Then, whenever the application makes an OpenCL call, the call +is intercepted and can be passed through to the real OpenCL with or without +changes. + +This project adheres to the Intercept Layer for OpenCL Application's +[code of conduct](CODE_OF_CONDUCT.md). By participating, you are expected to +uphold this code. + +## Documentation + +All controls are documented [here](docs/controls.md). + +Instructions to build the Intercept Layer for OpenCL Applications can be found [here](docs/build.md). + +Instructions to install the Intercept Layer for OpenCL Applications can be found [here](docs/install.md). + +Detailed instructions: +* [How to Inject Modified Programs](docs/injecting_programs.md) +* [How to Use the Intercept Layer for OpenCL Applications with VTune](docs/vtune_logging.md) +* [How to Use the Intercept Layer for OpenCL Applications with Chrome](docs/chrome_tracing.md) + +## License + +The Intercept Layer for OpenCL Applications is licensed under the [MIT License](LICENSE). + +Notes: + +* These files are partially generated and hence do not include license file headers, however + they are also licensed under the MIT License: + - [resource/clIntercept.rc](resource/clIntercept.rc) + - [resource/clIntercept_resource.h](resource/clIntercept_resource.h) + - [config/CLIConfig.rc](config/CLIConfig.rc) + - [config/resource.h](config/resource.h) + +### Attached Licenses + +The Intercept Layer for OpenCL Applications uses third-party code licensed under the following licenses: + +* These files are licensed under the [Khronos(tm) License][khronos_cl_license]: + - [CL/cl.h](CL/cl.h) + - [CL/cl_gl.h](CL/cl_gl.h) + - [CL/cl_platform.h](CL/cl_platform.h) + - [GL/glcorearb.h](GL/glcorearb.h) +* These files are licensed under the [Boost Software License - Version 1.0][boost_license]: + - [cmake_modules/GetGitRevisionDescription.cmake](cmake_modules/GetGitRevisionDescription.cmake) + - [cmake_modules/GetGitRevisionDescription.cmake.in](cmake_modules/GetGitRevisionDescription.cmake.in) + +## Support + +Please file a GitHub issue to report an issue or ask questions. Private or +sensitive issues may be submitted via email to this project's maintainer +(Ben Ashbaugh - ben 'dot' ashbaugh 'at' intel 'dot' com), or to any other +Intel GitHub maintainer (see profile for email address). + +## How to Contribute + +Contributions to the Intercept Layer for OpenCL Applications are welcomed and +encouraged. Please see [CONTRIBUTING](CONTRIBUTING.md) for details how to +contribute to the project. + +--- + +OpenCL and the OpenCL logo are trademarks of Apple Inc. used by permission by Khronos. + +\* Other names and brands may be claimed as the property of others. + +Copyright (c) 2018, Intel(R) Corporation + +[khronos_cl_license]: https://github.com/KhronosGroup/OpenCL-Headers/blob/master/LICENSE +[boost_license]: http://www.boost.org/LICENSE_1_0.txt diff --git a/Src/clIntercept.def b/Src/clIntercept.def new file mode 100644 index 00000000..f801b775 --- /dev/null +++ b/Src/clIntercept.def @@ -0,0 +1,140 @@ +; Copyright (c) 2018 Intel Corporation +; +; 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. + +LIBRARY opencl.dll +EXPORTS + clBuildProgram + clCloneKernel + clCompileProgram + clCreateBuffer + clCreateCommandQueue + clCreateCommandQueueWithProperties + clCreateContext + clCreateContextFromType + clCreateFromGLBuffer + clCreateFromGLRenderbuffer + clCreateFromGLTexture + clCreateFromGLTexture2D + clCreateFromGLTexture3D + clCreateImage + clCreateImage2D + clCreateImage3D + clCreateKernel + clCreateKernelsInProgram + clCreatePipe + clCreateProgramWithBinary + clCreateProgramWithBuiltInKernels + clCreateProgramWithIL + clCreateProgramWithSource + clCreateSampler + clCreateSamplerWithProperties + clCreateSubBuffer + clCreateSubDevices + clCreateUserEvent + clEnqueueAcquireGLObjects + clEnqueueBarrier + clEnqueueBarrierWithWaitList + clEnqueueCopyBuffer + clEnqueueCopyBufferRect + clEnqueueCopyBufferToImage + clEnqueueCopyImage + clEnqueueCopyImageToBuffer + clEnqueueFillBuffer + clEnqueueFillImage + clEnqueueMapBuffer + clEnqueueMapImage + clEnqueueMarker + clEnqueueMarkerWithWaitList + clEnqueueMigrateMemObjects + clEnqueueNDRangeKernel + clEnqueueNativeKernel + clEnqueueReadBuffer + clEnqueueReadBufferRect + clEnqueueReadImage + clEnqueueReleaseGLObjects + clEnqueueSVMFree + clEnqueueSVMMap + clEnqueueSVMMemcpy + clEnqueueSVMMemFill + clEnqueueSVMMigrateMem + clEnqueueSVMUnmap + clEnqueueTask + clEnqueueUnmapMemObject + clEnqueueWaitForEvents + clEnqueueWriteBuffer + clEnqueueWriteBufferRect + clEnqueueWriteImage + clFinish + clFlush + clGetCommandQueueInfo + clGetContextInfo + clGetDeviceAndHostTimer + clGetDeviceIDs + clGetDeviceInfo + clGetEventInfo + clGetEventProfilingInfo + clGetExtensionFunctionAddress + clGetExtensionFunctionAddressForPlatform + clGetGLObjectInfo + clGetGLTextureInfo + clGetHostTimer + clGetImageInfo + clGetKernelArgInfo + clGetKernelInfo + clGetKernelSubGroupInfo + clGetKernelWorkGroupInfo + clGetMemObjectInfo + clGetPipeInfo + clGetPlatformIDs + clGetPlatformInfo + clGetProgramBuildInfo + clGetProgramInfo + clGetSamplerInfo + clGetSupportedImageFormats + clLinkProgram + clReleaseCommandQueue + clReleaseContext + clReleaseDevice + clReleaseEvent + clReleaseKernel + clReleaseMemObject + clReleaseProgram + clReleaseSampler + clRetainCommandQueue + clRetainContext + clRetainDevice + clRetainEvent + clRetainKernel + clRetainMemObject + clRetainProgram + clRetainSampler + clSetCommandQueueProperty + clSetDefaultDeviceCommandQueue + clSetEventCallback + clSetKernelArg + clSetKernelArgSVMPointer + clSetKernelExecInfo + clSetMemObjectDestructorCallback + clSetUserEventStatus + clSVMAlloc + clSVMFree + clUnloadCompiler + clUnloadPlatformCompiler + clWaitForEvents \ No newline at end of file diff --git a/Src/clIntercept.map b/Src/clIntercept.map new file mode 100644 index 00000000..515906f2 --- /dev/null +++ b/Src/clIntercept.map @@ -0,0 +1,175 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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. +*/ + +INTERNAL { + global: + _binary_Kernels_builtin_kernels_cl_start; + _binary_Kernels_builtin_kernels_cl_end; + _binary_Kernels_precompiled_kernels_cl_start; + _binary_Kernels_precompiled_kernels_cl_end; + local: + *; +}; + +OPENCL_1.0 { + global: + clBuildProgram; + clCreateBuffer; + clCreateCommandQueue; + clCreateContext; + clCreateContextFromType; + clCreateFromGLBuffer; + clCreateFromGLRenderbuffer; + clCreateFromGLTexture2D; + clCreateFromGLTexture3D; + clCreateImage2D; + clCreateImage3D; + clCreateKernel; + clCreateKernelsInProgram; + clCreateProgramWithBinary; + clCreateProgramWithSource; + clCreateSampler; + clEnqueueAcquireGLObjects; + clEnqueueBarrier; + clEnqueueCopyBuffer; + clEnqueueCopyBufferToImage; + clEnqueueCopyImage; + clEnqueueCopyImageToBuffer; + clEnqueueMapBuffer; + clEnqueueMapImage; + clEnqueueMarker; + clEnqueueNDRangeKernel; + clEnqueueNativeKernel; + clEnqueueReadBuffer; + clEnqueueReadImage; + clEnqueueReleaseGLObjects; + clEnqueueTask; + clEnqueueUnmapMemObject; + clEnqueueWaitForEvents; + clEnqueueWriteBuffer; + clEnqueueWriteImage; + clFinish; + clFlush; + clGetCommandQueueInfo; + clGetContextInfo; + clGetDeviceIDs; + clGetDeviceInfo; + clGetEventInfo; + clGetEventProfilingInfo; + clGetExtensionFunctionAddress; + clGetGLObjectInfo; + clGetGLTextureInfo; + clGetImageInfo; + clGetKernelInfo; + clGetKernelWorkGroupInfo; + clGetMemObjectInfo; + clGetPlatformIDs; + clGetPlatformInfo; + clGetProgramBuildInfo; + clGetProgramInfo; + clGetSamplerInfo; + clGetSupportedImageFormats; + clReleaseCommandQueue; + clReleaseContext; + clReleaseEvent; + clReleaseKernel; + clReleaseMemObject; + clReleaseProgram; + clReleaseSampler; + clRetainCommandQueue; + clRetainContext; + clRetainEvent; + clRetainKernel; + clRetainMemObject; + clRetainProgram; + clRetainSampler; + clSetCommandQueueProperty; + clSetKernelArg; + clUnloadCompiler; + clWaitForEvents; +} INTERNAL; + +OPENCL_1.1 { + global: + clCreateSubBuffer; + clCreateUserEvent; + clEnqueueCopyBufferRect; + clEnqueueReadBufferRect; + clEnqueueWriteBufferRect; + clSetEventCallback; + clSetMemObjectDestructorCallback; + clSetUserEventStatus; +} OPENCL_1.0; + +OPENCL_1.2 { + global: + clCompileProgram; + clCreateFromGLTexture; + clCreateImage; + clCreateProgramWithBuiltInKernels; + clCreateSubDevices; + clEnqueueBarrierWithWaitList; + clEnqueueFillBuffer; + clEnqueueFillImage; + clEnqueueMarkerWithWaitList; + clEnqueueMigrateMemObjects; + clGetExtensionFunctionAddressForPlatform; + clGetKernelArgInfo; + clLinkProgram; + clReleaseDevice; + clRetainDevice; + clUnloadPlatformCompiler; +} OPENCL_1.1; + +OPENCL_2.0 { + global: + clCreateCommandQueueWithProperties; + clCreatePipe; + clCreateSamplerWithProperties; + clEnqueueSVMFree; + clEnqueueSVMMap; + clEnqueueSVMMemcpy; + clEnqueueSVMMemFill; + clEnqueueSVMUnmap; + clGetPipeInfo; + clSetKernelArgSVMPointer; + clSetKernelExecInfo; + clSVMAlloc; + clSVMFree; +} OPENCL_1.2; + +OPENCL_2.1 { + global: + clCloneKernel; + clCreateProgramWithIL; + clEnqueueSVMMigrateMem; + clGetDeviceAndHostTimer; + clGetHostTimer; + clGetKernelSubGroupInfo; + clSetDefaultDeviceCommandQueue; +} OPENCL_2.0; + +OPENCL_2.2 { + global: + clSetProgramReleaseCallback; + clSetProgramSpecializationConstant; +} OPENCL_2.1; diff --git a/Src/cli_ext.h b/Src/cli_ext.h new file mode 100644 index 00000000..ad5f708d --- /dev/null +++ b/Src/cli_ext.h @@ -0,0 +1,701 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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. +*/ + +#pragma once + +// cl_khr_gl_sharing +extern CL_API_ENTRY +cl_int CL_API_CALL clGetGLContextInfoKHR( + const cl_context_properties *properties, + cl_gl_context_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret); + +// cl_khr_gl_event +#define CL_COMMAND_GL_FENCE_SYNC_OBJECT_KHR 0x200D + +// cl_khr_gl_event +extern CL_API_ENTRY +cl_event CL_API_CALL clCreateEventFromGLsyncKHR( + cl_context context, + cl_GLsync sync, + cl_int* errcode_ret); + +#if defined(_WIN32) + +// Minimal set of types for cl_khr_d3d10_sharing. +// Don't include cl_d3d10.h here because we don't want a dependency on d3d10.h. +typedef cl_uint cl_d3d10_device_source_khr; +typedef cl_uint cl_d3d10_device_set_khr; +class ID3D10Buffer; +class ID3D10Texture2D; +class ID3D10Texture3D; + +// cl_khr_d3d10_sharing +extern CL_API_ENTRY +cl_int CL_API_CALL clGetDeviceIDsFromD3D10KHR( + cl_platform_id platform, + cl_d3d10_device_source_khr d3d_device_source, + void* d3d_object, + cl_d3d10_device_set_khr d3d_device_set, + cl_uint num_entries, + cl_device_id* devices, + cl_uint* num_devices); + +// cl_khr_d3d10_sharing +extern CL_API_ENTRY +cl_mem CL_API_CALL clCreateFromD3D10BufferKHR( + cl_context context, + cl_mem_flags flags, + ID3D10Buffer* resource, + cl_int* errcode_ret); + +// cl_khr_d3d10_sharing +extern CL_API_ENTRY +cl_mem CL_API_CALL clCreateFromD3D10Texture2DKHR( + cl_context context, + cl_mem_flags flags, + ID3D10Texture2D* resource, + UINT subresource, + cl_int* errcode_ret); + +// cl_khr_d3d10_sharing +extern CL_API_ENTRY +cl_mem CL_API_CALL clCreateFromD3D10Texture3DKHR( + cl_context context, + cl_mem_flags flags, + ID3D10Texture3D* resource, + UINT subresource, + cl_int* errcode_ret); + +// cl_khr_d3d10_sharing +extern CL_API_ENTRY +cl_int CL_API_CALL clEnqueueAcquireD3D10ObjectsKHR( + 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_khr_d3d10_sharing +extern CL_API_ENTRY +cl_int CL_API_CALL clEnqueueReleaseD3D10ObjectsKHR( + 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); + +// Minimal set of types for cl_khr_d3d11_sharing. +// Don't include cl_d3d11.h here because we don't want a dependency on d3d10.h. +typedef cl_uint cl_d3d11_device_source_khr; +typedef cl_uint cl_d3d11_device_set_khr; +class ID3D11Buffer; +class ID3D11Texture2D; +class ID3D11Texture3D; + +// cl_khr_d3d11_sharing +extern CL_API_ENTRY +cl_int CL_API_CALL clGetDeviceIDsFromD3D11KHR( + cl_platform_id platform, + cl_d3d11_device_source_khr d3d_device_source, + void* d3d_object, + cl_d3d11_device_set_khr d3d_device_set, + cl_uint num_entries, + cl_device_id* devices, + cl_uint* num_devices); + +// cl_khr_d3d11_sharing +extern CL_API_ENTRY +cl_mem CL_API_CALL clCreateFromD3D11BufferKHR( + cl_context context, + cl_mem_flags flags, + ID3D11Buffer* resource, + cl_int* errcode_ret); + +// cl_khr_d3d11_sharing +extern CL_API_ENTRY +cl_mem CL_API_CALL clCreateFromD3D11Texture2DKHR( + cl_context context, + cl_mem_flags flags, + ID3D11Texture2D* resource, + UINT subresource, + cl_int* errcode_ret); + +// cl_khr_d3d11_sharing +extern CL_API_ENTRY +cl_mem CL_API_CALL clCreateFromD3D11Texture3DKHR( + cl_context context, + cl_mem_flags flags, + ID3D11Texture3D* resource, + UINT subresource, + cl_int* errcode_ret); + +// cl_khr_d3d11_sharing +extern CL_API_ENTRY +cl_int CL_API_CALL clEnqueueAcquireD3D11ObjectsKHR( + 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_khr_d3d11_sharing +extern CL_API_ENTRY +cl_int CL_API_CALL clEnqueueReleaseD3D11ObjectsKHR( + 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); + +// Minimal set of types for cl_khr_dx9_media_sharing. +// Don't include cl_d3d9.h here because we don't want a dependency on d3d9.h. +typedef cl_uint cl_dx9_media_adapter_type_khr; +typedef cl_uint cl_dx9_media_adapter_set_khr; +typedef cl_uint cl_dx9_media_adapter_type_khr; +class IDirect3DSurface9; + +// cl_khr_dx9_media_sharing +extern CL_API_ENTRY +cl_int CL_API_CALL clGetDeviceIDsFromDX9MediaAdapterKHR( + cl_platform_id platform, + cl_uint num_media_adapters, + cl_dx9_media_adapter_type_khr* media_adapters_type, + void* media_adapters, + cl_dx9_media_adapter_set_khr media_adapter_set, + cl_uint num_entries, + cl_device_id* devices, + cl_uint* num_devices); + +// cl_khr_dx9_media_sharing +extern CL_API_ENTRY +cl_mem CL_API_CALL clCreateFromDX9MediaSurfaceKHR( + cl_context context, + cl_mem_flags flags, + cl_dx9_media_adapter_type_khr adapter_type, + void* surface_info, + cl_uint plane, + cl_int* errcode_ret); + +// cl_khr_dx9_media_sharing +extern CL_API_ENTRY +cl_int CL_API_CALL clEnqueueAcquireDX9MediaSurfacesKHR( + 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_khr_dx9_media_sharing +extern CL_API_ENTRY +cl_int CL_API_CALL clEnqueueReleaseDX9MediaSurfacesKHR( + 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); + +// Minimal set of types for cl_intel_d3d9_media_sharing. +// Don't include cl_d3d9.h here because we don't want a dependency on d3d9.h. +typedef cl_uint cl_dx9_device_source_intel; +typedef cl_uint cl_dx9_device_set_intel; +class IDirect3DSurface9; + +// cl_intel_dx9_media_sharing +extern CL_API_ENTRY +cl_int CL_API_CALL clGetDeviceIDsFromDX9INTEL( + cl_platform_id platform, + cl_dx9_device_source_intel d3d_device_source, + void *dx9_object, + cl_dx9_device_set_intel d3d_device_set, + cl_uint num_entries, + cl_device_id* devices, + cl_uint* num_devices ); + +// cl_intel_dx9_media_sharing +extern CL_API_ENTRY +cl_mem CL_API_CALL clCreateFromDX9MediaSurfaceINTEL( + cl_context context, + cl_mem_flags flags, + IDirect3DSurface9* resource, + HANDLE sharedHandle, + UINT plane, + cl_int* errcode_ret ); + +// cl_intel_dx9_media_sharing +extern CL_API_ENTRY +cl_int CL_API_CALL clEnqueueAcquireDX9ObjectsINTEL( + 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_intel_dx9_media_sharing +extern CL_API_ENTRY +cl_int CL_API_CALL clEnqueueReleaseDX9ObjectsINTEL( + 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_il_program +#define CL_DEVICE_IL_VERSION_KHR 0x105B +#define CL_PROGRAM_IL_KHR 0x1169 +extern CL_API_ENTRY +cl_program CL_API_CALL clCreateProgramWithILKHR( + cl_context context, + const void* il, + size_t length, + cl_int* errcode_ret ); + +// cl_khr_subgroups +typedef cl_uint cl_kernel_sub_group_info; +extern CL_API_ENTRY +cl_int CL_API_CALL clGetKernelSubGroupInfoKHR( + cl_kernel kernel, + cl_device_id device, + cl_kernel_sub_group_info param_name, + size_t input_value_size, + const void* input_value, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret); + +// cl_khr_create_command_queue +typedef cl_bitfield cl_queue_properties_khr; +extern CL_API_ENTRY +cl_command_queue CL_API_CALL clCreateCommandQueueWithPropertiesKHR( + cl_context context, + cl_device_id device, + const cl_queue_properties_khr* properties, + cl_int* errcode_ret); + +// Unofficial MDAPI extension: +extern CL_API_ENTRY +cl_command_queue CL_API_CALL clCreatePerfCountersCommandQueueINTEL( + cl_context context, + cl_device_id device, + cl_command_queue_properties properties, + cl_uint configuration, + cl_int* errcode_ret); + +extern CL_API_ENTRY +cl_int CL_API_CALL clSetPerformanceConfigurationINTEL( + cl_device_id device, + cl_uint count, + cl_uint* offsets, + cl_uint* values ); + +// Unofficial kernel profiling extension: +#define CL_CONTEXT_KERNEL_PROFILING_MODES_COUNT_INTEL 0x407A +#define CL_CONTEXT_KERNEL_PROFILING_MODE_INFO_INTEL 0x407B +#define CL_KERNEL_IL_SYMBOLS_INTEL 0x407C +#define CL_KERNEL_BINARY_PROGRAM_INTEL 0x407D + +// Unofficial VTune Debug Info extension: +#define CL_PROGRAM_DEBUG_INFO_INTEL 0x4100 +#define CL_PROGRAM_DEBUG_INFO_SIZES_INTEL 0x4101 +#define CL_KERNEL_BINARIES_INTEL 0x4102 +#define CL_KERNEL_BINARY_SIZES_INTEL 0x4103 + +// VME + +typedef struct _cl_accelerator_intel* cl_accelerator_intel; +typedef cl_uint cl_accelerator_type_intel; +typedef cl_uint cl_accelerator_info_intel; + +// Error Codes +#define CL_INVALID_ACCELERATOR_INTEL -1094 +#define CL_INVALID_ACCELERATOR_TYPE_INTEL -1095 +#define CL_INVALID_ACCELERATOR_DESC_INTEL -1096 +#define CL_ACCELERATOR_TYPE_NOT_SUPPORTED_INTEL -1097 + +// cl_device_info +#define CL_DEVICE_ME_VERSION_INTEL 0x407E +#define CL_DEVICE_TRANSFORM_MASK_MAX_WIDTH_INTEL 0x409C +#define CL_DEVICE_TRANSFORM_MASK_MAX_HEIGHT_INTEL 0x409D +#define CL_DEVICE_TRANSFORM_FILTER_MAX_WIDTH_INTEL 0x409E +#define CL_DEVICE_TRANSFORM_FILTER_MAX_HEIGHT_INTEL 0x409F + +// cl_accelerator_type_intel +#define CL_ACCELERATOR_TYPE_MOTION_ESTIMATION_INTEL 0x0 + +// cl_accelerator_info_intel +#define CL_ACCELERATOR_DESCRIPTOR_INTEL 0x4090 +#define CL_ACCELERATOR_REFERENCE_COUNT_INTEL 0x4091 +#define CL_ACCELERATOR_CONTEXT_INTEL 0x4092 +#define CL_ACCELERATOR_TYPE_INTEL 0x4093 + +// cl_motion_detect_desc_intel flags +#define CL_ME_MB_TYPE_16x16_INTEL 0x0 +#define CL_ME_MB_TYPE_8x8_INTEL 0x1 +#define CL_ME_MB_TYPE_4x4_INTEL 0x2 + +#define CL_ME_SUBPIXEL_MODE_INTEGER_INTEL 0x0 +#define CL_ME_SUBPIXEL_MODE_HPEL_INTEL 0x1 +#define CL_ME_SUBPIXEL_MODE_QPEL_INTEL 0x2 + +#define CL_ME_SAD_ADJUST_MODE_NONE_INTEL 0x0 +#define CL_ME_SAD_ADJUST_MODE_HAAR_INTEL 0x1 + +#define CL_ME_SEARCH_PATH_RADIUS_2_2_INTEL 0x0 +#define CL_ME_SEARCH_PATH_RADIUS_4_4_INTEL 0x1 +#define CL_ME_SEARCH_PATH_RADIUS_16_12_INTEL 0x5 + +#define CL_ME_CHROMA_INTRA_PREDICT_ENABLED_INTEL 0x1 +#define CL_ME_LUMA_INTRA_PREDICT_ENABLED_INTEL 0x2 + +#define CL_ME_COST_PENALTY_NONE_INTEL 0x0 +#define CL_ME_COST_PENALTY_LOW_INTEL 0x1 +#define CL_ME_COST_PENALTY_NORMAL_INTEL 0x2 +#define CL_ME_COST_PENALTY_HIGH_INTEL 0x3 + +#define CL_ME_COST_PRECISION_QPEL_INTEL 0x0 +#define CL_ME_COST_PRECISION_HPEL_INTEL 0x1 +#define CL_ME_COST_PRECISION_PEL_INTEL 0x2 +#define CL_ME_COST_PRECISION_DPEL_INTEL 0x3 + +#define CL_ME_VERSION_LEGACY_INTEL 0x0 +#define CL_ME_VERSION_ADVANCED_VER_1_INTEL 0x1 + +typedef struct _cl_motion_estimation_desc_intel { + cl_uint mb_block_type; + cl_uint subpixel_mode; + cl_uint sad_adjust_mode; + cl_uint search_path_type; +} cl_motion_estimation_desc_intel; + +extern CL_API_ENTRY +cl_accelerator_intel CL_API_CALL clCreateAcceleratorINTEL( + cl_context context, + cl_accelerator_type_intel accelerator_type, + size_t descriptor_size, + const void* descriptor, + cl_int* errcode_ret ); + +extern CL_API_ENTRY +cl_int CL_API_CALL clGetAcceleratorInfoINTEL( + cl_accelerator_intel accelerator, + cl_accelerator_info_intel param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret ); + +extern CL_API_ENTRY +cl_int CL_API_CALL clRetainAcceleratorINTEL( + cl_accelerator_intel accelerator ); + +extern CL_API_ENTRY +cl_int CL_API_CALL clReleaseAcceleratorINTEL( + cl_accelerator_intel accelerator ); + +// cl_intel_egl_image_yuv +#define CL_EGL_YUV_PLANE_INTEL 0x4107 + +// cl_intel_simultaneous_sharing +#define CL_DEVICE_SIMULTANEOUS_INTEROPS_INTEL 0x4104 +#define CL_DEVICE_NUM_SIMULTANEOUS_INTEROPS_INTEL 0x4105 + +// cl_intel_thread_local_exec +#define CL_QUEUE_THREAD_LOCAL_EXEC_ENABLE_INTEL (((cl_bitfield)1) << 31) + +// cl_intel_va_api_media_sharing + +#define CL_VA_API_DISPLAY_INTEL 0x4094 +#define CL_PREFERRED_DEVICES_FOR_VA_API_INTEL 0x4095 +#define CL_ALL_DEVICES_FOR_VA_API_INTEL 0x4096 +#define CL_CONTEXT_VA_API_DISPLAY_INTEL 0x4097 +#define CL_MEM_VA_API_SURFACE_INTEL 0x4098 +#define CL_IMAGE_VA_API_PLANE_INTEL 0x4099 +#define CL_COMMAND_ACQUIRE_VA_API_MEDIA_SURFACES_INTEL 0x409A +#define CL_COMMAND_RELEASE_VA_API_MEDIA_SURFACES_INTEL 0x409B + +#define CL_INVALID_VA_API_MEDIA_ADAPTER_INTEL -1098 +#define CL_INVALID_VA_API_MEDIA_SURFACE_INTEL -1099 +#define CL_VA_API_MEDIA_SURFACE_ALREADY_ACQUIRED_INTEL -1100 +#define CL_VA_API_MEDIA_SURFACE_NOT_ACQUIRED_INTEL -1101 + +// Minimal set of types for cl_intel_va_api_media_sharing. +typedef cl_uint cl_va_api_device_source_intel; +typedef cl_uint cl_va_api_device_set_intel; +struct VASurfaceID; + +extern CL_API_ENTRY +cl_int CL_API_CALL clGetDeviceIDsFromVA_APIMediaAdapterINTEL( + cl_platform_id platform, + cl_va_api_device_source_intel media_adapter_type, + void *media_adapter, + cl_va_api_device_set_intel media_adapter_set, + cl_uint num_entries, + cl_device_id *devices, + cl_uint *num_devices); + +extern CL_API_ENTRY +cl_mem CL_API_CALL clCreateFromVA_APIMediaSurfaceINTEL( + cl_context context, + cl_mem_flags flags, + VASurfaceID *surface, + cl_uint plane, + cl_int *errcode_ret); + +extern CL_API_ENTRY +cl_int CL_API_CALL clEnqueueAcquireVA_APIMediaSurfacesINTEL( + 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); + +extern CL_API_ENTRY +cl_int CL_API_CALL clEnqueueReleaseVA_APIMediaSurfacesINTEL( + 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_intel_packed_yuv +#define CL_YUYV_INTEL 0x4076 +#define CL_UYVY_INTEL 0x4077 +#define CL_YVYU_INTEL 0x4078 +#define CL_VYUY_INTEL 0x4079 + +// cl_intel_planar_yuv + +// cl_channel_order +#define CL_NV12_INTEL 0x410E + +// cl_mem_flags +#define CL_MEM_NO_ACCESS_INTEL (1 << 24) +#define CL_MEM_ACCESS_FLAGS_UNRESTRICTED_INTEL (1 << 25) + +// cl_device_info +#define CL_DEVICE_PLANAR_YUV_MAX_WIDTH_INTEL 0x417E +#define CL_DEVICE_PLANAR_YUV_MAX_HEIGHT_INTEL 0x417F + +// cl_intel_required_subgroup_size +#define CL_DEVICE_SUB_GROUP_SIZES_INTEL 0x4108 +#define CL_KERNEL_SPILL_MEM_SIZE_INTEL 0x4109 +#define CL_KERNEL_COMPILE_SUB_GROUP_SIZE_INTEL 0x410A + +// cl_intel_driver_diagnostics +#define CL_CONTEXT_SHOW_DIAGNOSTICS_INTEL 0x4106 + +// cl_intelx_video_enhancement +// This is the base-functionality VEBox extension. +// Note: These are preview enum names and values! + +// cl_device_info +#define CL_DEVICE_VE_VERSION_INTEL 0x4160 +#define CL_DEVICE_VE_ENGINE_COUNT_INTEL 0x4161 + +// cl_queue_properties / cl_command_queue_info +#define CL_QUEUE_VE_ENABLE_INTEL 0x4162 + +// attribute_ids for cl_vebox_attrib_desc_intel +#define CL_VE_ACCELERATOR_ATTRIB_DENOISE_INTEL 0x4163 +#define CL_VE_ACCELERATOR_ATTRIB_DEINTERLACE_INTEL 0x4164 +#define CL_VE_ACCELERATOR_ATTRIB_HOT_PIXEL_CORR_INTEL 0x4165 + +// cl_accelerator_info_intel +#define CL_VE_ACCELERATOR_HISTOGRAMS_INTEL 0x4166 +#define CL_VE_ACCELERATOR_STATISTICS_INTEL 0x4167 +#define CL_VE_ACCELERATOR_STMM_INPUT_INTEL 0x4168 +#define CL_VE_ACCELERATOR_STMM_OUTPUT_INTEL 0x4169 + +// cl_intelx_ve_color_pipeline +// Note: These are preview enum names and values! + +// cl_device_info +#define CL_DEVICE_VE_COLOR_PIPE_VERSION_INTEL 0x416A + +// attribute_ids for cl_vebox_attrib_desc_intel +#define CL_VE_ACCELERATOR_ATTRIB_STD_STE_INTEL 0x416B +#define CL_VE_ACCELERATOR_ATTRIB_GAMUT_COMP_INTEL 0x416C +#define CL_VE_ACCELERATOR_ATTRIB_GECC_INTEL 0x416D +#define CL_VE_ACCELERATOR_ATTRIB_ACE_INTEL 0x416E +#define CL_VE_ACCELERATOR_ATTRIB_ACE_ADV_INTEL 0x416F +#define CL_VE_ACCELERATOR_ATTRIB_TCC_INTEL 0x4170 +#define CL_VE_ACCELERATOR_ATTRIB_PROC_AMP_INTEL 0x4171 +#define CL_VE_ACCELERATOR_ATTRIB_BACK_END_CSC_INTEL 0x4172 +#define CL_VE_ACCELERATOR_ATTRIB_AOI_ALPHA_INTEL 0x4173 +#define CL_VE_ACCELERATOR_ATTRIB_CCM_INTEL 0x4174 +#define CL_VE_ACCELERATOR_ATTRIB_FWD_GAMMA_CORRECT_INTEL 0x4175 +#define CL_VE_ACCELERATOR_ATTRIB_FRONT_END_CSC_INTEL 0x4176 + +// cl_intelx_ve_camera_pipeline +// Note, these are preview enum names and values! + +// cl_device_info +#define CL_DEVICE_VE_CAMERA_PIPE_VERSION_INTEL 0x4177 + +// attribute_ids for cl_vebox_attrib_desc_intel +#define CL_VE_ACCELERATOR_ATTRIB_BLACK_LEVEL_CORR_INTEL 0x4178 +#define CL_VE_ACCELERATOR_ATTRIB_DEMOSAIC_INTEL 0x4179 +#define CL_VE_ACCELERATOR_ATTRIB_WHITE_BALANCE_CORR_INTEL 0x417A +#define CL_VE_ACCELERATOR_ATTRIB_VIGNETTE_INTEL 0x417B + +// HEVC PAK +// Note, this extension is still in development! + +// cl_device_info +#define CL_DEVICE_PAK_VERSION_INTEL 0x4180 +#define CL_DEVICE_PAK_AVAILABLE_CODECS_INTEL 0x4181 + +// cl_queue_properties / cl_command_queue_info +#define CL_QUEUE_PAK_ENABLE_INTEL 0x4189 + +// cl_accelerator_info_intel +#define CL_PAK_CTU_COUNT_INTEL 0x4182 +#define CL_PAK_CTU_WIDTH_INTEL 0x4183 +#define CL_PAK_CTU_HEIGHT_INTEL 0x4184 +#define CL_PAK_MAX_INTRA_DEPTH_INTEL 0x4185 +#define CL_PAK_MAX_INTER_DEPTH_INTEL 0x4186 +#define CL_PAK_NUM_CUS_PER_CTU_INTEL 0x4187 +#define CL_PAK_MV_BUFFER_SIZE_INTEL 0x4188 + +// Error Codes +// These are currently all mapped to CL_INVALID_VALUE. +// Need official error code assignment. +#define CL_INVALID_PAK_CTU_SIZE_INTEL CL_INVALID_VALUE +#define CL_INVALID_PAK_TU_SIZE_INTEL CL_INVALID_VALUE +#define CL_INVALID_PAK_TU_INTRA_DEPTH_INTEL CL_INVALID_VALUE +#define CL_INVALID_PAK_TU_INTER_DEPTH_INTEL CL_INVALID_VALUE +#define CL_INVALID_PAK_BITRATE_RANGE_INTEL CL_INVALID_VALUE +#define CL_INVALID_PAK_INSERTION_INTEL CL_INVALID_VALUE +#define CL_INVALID_PAK_CTU_POSITION_INTEL CL_INVALID_VALUE +#define CL_INVALID_PAK_REFERENCE_IMAGE_INDEX_INTEL CL_INVALID_VALUE + +// Altera Extensions: + +// cl_altera_device_temperature +#define CL_DEVICE_CORE_TEMPERATURE_ALTERA 0x40F3 + +// cl_altera_compiler_mode +#define CL_CONTEXT_COMPILER_MODE_ALTERA 0x40F0 +#define CL_CONTEXT_PROGRAM_EXE_LIBRARY_ROOT_ALTERA 0x40F1 +#define CL_CONTEXT_OFFLINE_DEVICE_ALTERA 0x40F2 + +// These are from the Khronos cl_ext.h: + +// cl_khr_icd +#define CL_PLATFORM_ICD_SUFFIX_KHR 0x0920 +#define CL_PLATFORM_NOT_FOUND_KHR -1001 + +// cl_khr_initalize_memory +#define CL_CONTEXT_MEMORY_INITIALIZE_KHR 0x2030 + +// cl_khr_terminate_context +#define CL_DEVICE_TERMINATE_CAPABILITY_KHR 0x2031 +#define CL_CONTEXT_TERMINATE_KHR 0x2032 + +// cl_khr_spir +#define CL_DEVICE_SPIR_VERSIONS 0x40E0 +#define CL_PROGRAM_BINARY_TYPE_INTERMEDIATE 0x40E1 + +// cl_khr_subgroups +#define CL_KERNEL_MAX_SUB_GROUP_SIZE_FOR_NDRANGE_KHR 0x2033 +#define CL_KERNEL_SUB_GROUP_COUNT_FOR_NDRANGE_KHR 0x2034 + +// cl_nv_device_attribute_query +#define CL_DEVICE_COMPUTE_CAPABILITY_MAJOR_NV 0x4000 +#define CL_DEVICE_COMPUTE_CAPABILITY_MINOR_NV 0x4001 +#define CL_DEVICE_REGISTERS_PER_BLOCK_NV 0x4002 +#define CL_DEVICE_WARP_SIZE_NV 0x4003 +#define CL_DEVICE_GPU_OVERLAP_NV 0x4004 +#define CL_DEVICE_KERNEL_EXEC_TIMEOUT_NV 0x4005 +#define CL_DEVICE_INTEGRATED_MEMORY_NV 0x4006 + +// cl_ext_atomic_counters +#define CL_DEVICE_MAX_ATOMIC_COUNTERS_EXT 0x4032 + +// cl_amd_device_attribute_query +#define CL_DEVICE_PROFILING_TIMER_OFFSET_AMD 0x4036 +#define CL_DEVICE_TOPOLOGY_AMD 0x4037 +#define CL_DEVICE_BOARD_NAME_AMD 0x4038 +#define CL_DEVICE_GLOBAL_FREE_MEMORY_AMD 0x4039 +#define CL_DEVICE_SIMD_PER_COMPUTE_UNIT_AMD 0x4040 +#define CL_DEVICE_SIMD_WIDTH_AMD 0x4041 +#define CL_DEVICE_SIMD_INSTRUCTION_WIDTH_AMD 0x4042 +#define CL_DEVICE_WAVEFRONT_WIDTH_AMD 0x4043 +#define CL_DEVICE_GLOBAL_MEM_CHANNELS_AMD 0x4044 +#define CL_DEVICE_GLOBAL_MEM_CHANNEL_BANKS_AMD 0x4045 +#define CL_DEVICE_GLOBAL_MEM_CHANNEL_BANK_WIDTH_AMD 0x4046 +#define CL_DEVICE_LOCAL_MEM_SIZE_PER_COMPUTE_UNIT_AMD 0x4047 +#define CL_DEVICE_LOCAL_MEM_BANKS_AMD 0x4048 +#define CL_DEVICE_THREAD_TRACE_SUPPORTED_AMD 0x4049 +#define CL_DEVICE_GFXIP_MAJOR_AMD 0x404A +#define CL_DEVICE_GFXIP_MINOR_AMD 0x404B +#define CL_DEVICE_AVAILABLE_ASYNC_QUEUES_AMD 0x404C + +// cl_amd_offline_devices +#define CL_CONTEXT_OFFLINE_DEVICES_AMD 0x403F + +// cl_ext_device_fission +#define CL_DEVICE_PARTITION_EQUALLY_EXT 0x4050 +#define CL_DEVICE_PARTITION_BY_COUNTS_EXT 0x4051 +#define CL_DEVICE_PARTITION_BY_NAMES_EXT 0x4052 +#define CL_DEVICE_PARTITION_BY_AFFINITY_DOMAIN_EXT 0x4053 +#define CL_DEVICE_PARENT_DEVICE_EXT 0x4054 +#define CL_DEVICE_PARTITION_TYPES_EXT 0x4055 +#define CL_DEVICE_AFFINITY_DOMAINS_EXT 0x4056 +#define CL_DEVICE_REFERENCE_COUNT_EXT 0x4057 +#define CL_DEVICE_PARTITION_STYLE_EXT 0x4058 + +#define CL_DEVICE_PARTITION_FAILED_EXT -1057 +#define CL_INVALID_PARTITION_COUNT_EXT -1058 +#define CL_INVALID_PARTITION_NAME_EXT -1059 + +// cl_qcom_ext_host_ptr +#define CL_MEM_EXT_HOST_PTR_QCOM (1 << 29) + +#define CL_DEVICE_EXT_MEM_PADDING_IN_BYTES_QCOM 0x40A0 +#define CL_DEVICE_PAGE_SIZE_QCOM 0x40A1 +#define CL_IMAGE_ROW_ALIGNMENT_QCOM 0x40A2 +#define CL_IMAGE_SLICE_ALIGNMENT_QCOM 0x40A3 +#define CL_MEM_HOST_UNCACHED_QCOM 0x40A4 +#define CL_MEM_HOST_WRITEBACK_QCOM 0x40A5 +#define CL_MEM_HOST_WRITETHROUGH_QCOM 0x40A6 +#define CL_MEM_HOST_WRITE_COMBINING_QCOM 0x40A7 + +// cl_qcom_ion_host_ptr +#define CL_MEM_ION_HOST_PTR_QCOM 0x40A8 + +// cl_arm_printf extension +#define CL_PRINTF_CALLBACK_ARM 0x40B0 +#define CL_PRINTF_BUFFERSIZE_ARM 0x40B1 diff --git a/Src/common.h b/Src/common.h new file mode 100644 index 00000000..86e42c6d --- /dev/null +++ b/Src/common.h @@ -0,0 +1,110 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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. +*/ + +#pragma once + +#define CL_USE_DEPRECATED_OPENCL_1_0_APIS +#define CL_USE_DEPRECATED_OPENCL_1_1_APIS +#define CL_USE_DEPRECATED_OPENCL_2_0_APIS + +#if defined(__ANDROID__) +#include +#elif defined(_WIN32) || defined(__linux__) +#include "GL/glcorearb.h" +#elif defined(__APPLE__) +#include +#else +#error Unknown OS! +#endif + +// Note: This is purposefully including the CLIntercept version of cl.h +// and cl_gl.h, not the system header files. +#include "CL/cl.h" +#include "CL/cl_gl.h" + +#if defined(_WIN32) + #define CLI_DEBUG_BREAK() __debugbreak(); +#elif defined(__linux__) || defined(__APPLE__) + #include + #include + #define CLI_DEBUG_BREAK() raise(SIGTRAP); +#else + #error Unknown OS! +#endif + +#ifdef _DEBUG + #define CLI_ASSERT(x) \ + { \ + if (!(x)) \ + { \ + CLI_DEBUG_BREAK(); \ + } \ + } +#else + #define CLI_ASSERT(x) +#endif + +#if defined(_WIN32) || defined(__linux__) + #define CLIRN( _funcname ) _funcname +#elif defined(__APPLE__) + #define CLIRN( _funcname ) i ## _funcname +#else + #error Unknown OS! +#endif + +#if defined(_WIN32) + #define CLI_SPRINTF(_s, _sz, _f, ...) sprintf_s(_s, _TRUNCATE, _f, ##__VA_ARGS__) + #define CLI_VSPRINTF(_s, _sz, _f, _a) vsnprintf_s(_s, _TRUNCATE, _f, _a) + #define CLI_MEMCPY(_d, _dsz, _s, _sz) memcpy_s(_d, _dsz, _s, _sz) + #define CLI_STRCAT(_d, _dsz, _s) strcat_s(_d, _dsz, _s) + #define CLI_STRTOK(_s, _d, _c) strtok_s(_s, _d, _c) + #define CLI_C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1] +#else + #if !defined(MAX_PATH) + #define MAX_PATH 256 + #endif + #define CLI_SPRINTF(_s, _sz, _f, ...) snprintf(_s, _sz, _f, ##__VA_ARGS__) + #define CLI_VSPRINTF(_s, _sz, _f, _a) vsnprintf(_s, _sz, _f, _a) + // TODO: Investigate how to reliably use memcpy_s on Linux: + #define CLI_MEMCPY(_d, _dsz, _s, _sz) memcpy(_d, _s, _sz) + #define CLI_STRCAT(_d, _dsz, _s) strcat(_d, _s) + #define CLI_STRTOK(_s, _d, _c) strtok_r(_s, _d, _c) + #define CLI_C_ASSERT(e) typedef char __attribute__((unused)) __C_ASSERT__[(e)?1:-1] +#endif + +#define CLI_MAX_STRING_SIZE 1024 + +/*****************************************************************************\ + +MACRO: + DISALLOW_COPY_AND_ASSIGN + +Description: + A macro to disallow the copy constructor and operator= functions + This should be used in the private: declarations for a class + +\*****************************************************************************/ +#if !defined(DISALLOW_COPY_AND_ASSIGN) +#define DISALLOW_COPY_AND_ASSIGN( TypeName ) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) +#endif diff --git a/Src/controls.h b/Src/controls.h new file mode 100644 index 00000000..6dba71de --- /dev/null +++ b/Src/controls.h @@ -0,0 +1,177 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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. +*/ + +#ifndef CLI_CONTROL +#error Must define CLI_CONTROL before including this file! +#endif + +#ifndef CLI_CONTROL_SEPARATOR +#define CLI_CONTROL_SEPARATOR( _name ) +#endif + +CLI_CONTROL_SEPARATOR( Logging Controls: ) +CLI_CONTROL( bool, AppendFiles, false, "By default, the Intercept Layer for OpenCL Applications log files will be created from scratch when the intercept DLL is loaded, and any Intercept Layer for OpenCL Applications report files will be created from scratch when the intercept DLL is unloaded. If AppendFiles is set to a nonzero value, the Intercept Layer for OpenCL Applications will append to an existing file instead of recreating it. This can be useful if an application loads and unloads the intercept DLL multiple times, or to simply preserve log or report data from run-to-run." ) +CLI_CONTROL( bool, LogToFile, false, "If set to a nonzero value, sends log information to the file \"clintercept_log.txt\" instead of to stderr. The log file will be placed in the directory \"%SYSTEMDRIVE%\\Intel\\CLIntercept_Dump\\\"." ) +CLI_CONTROL( bool, LogToDebugger, false, "If set to a nonzero value, sends log information to the debugger instead of to stderr. If both LogToFile and LogToDebugger are nonzero then log information will be sent both to a file and to the debugger." ) +CLI_CONTROL( int, LogIndent, 0, "Indents each log entry by this many spaces." ) +CLI_CONTROL( bool, BuildLogging, false, "If set to a nonzero value, logs the program build log after each call to clBuildProgram(). This will likely only function correctly for synchronous builds. Note that the build log is logged regardless of whether the program built successfully, which allows compiler warnings to be logged for successful compiles." ) +CLI_CONTROL( bool, PreferredWorkGroupSizeMultipleLogging, false, "If set to a nonzero value, logs the preferred work group size multiple for each kernel after each call to clCreateKernel(). On some devices this is the equivalent of the SIMD size for this kernel." ) +CLI_CONTROL( bool, CallLogging, false, "If set to a nonzero value, logs function entry and exit information for every OpenCL call. This can be used to easily determine which OpenCL call is causing an application to crash or fail or if a crash occurs outside of an OpenCL call. This setting is best used with LogToFile or LogToDebugger as it can generate a lot of log data." ) +CLI_CONTROL( bool, CallLoggingEnqueueCounter, false, "If set to a nonzero value, logs the enqueue counter in addition to function entry and exit information for every OpenCL call. This can be used to determine appropriate limits for DumpBuffersMinEnqueue, DumpBuffersMaxEnqueue, DumpImagesMinEnqueue, or DumpBuffersMaxEnqueue. If CallLogging is disabled then this control will have no effect." ) +CLI_CONTROL( bool, CallLoggingThreadId, false, "If set to a nonzero value, logs the ID of the calling thread in addition to function entry and exit information for every OpenCL call. This can be helpful when debugging multi-threading issues." ) +CLI_CONTROL( bool, CallLoggingThreadNumber, false, "If set to a nonzero value, logs the symbolic number of the calling thread in addition to function entry and exit information for every OpenCL call. This can be helpful when debugging multi-threading issues." ) +CLI_CONTROL( bool, CallLoggingElapsedTime, false, "If set to a nonzero value, logs the elapsed time in microseconds in addition to function entry and exit information for every OpenCL call, starting from the time the intercept DLL is loaded." ) +CLI_CONTROL( bool, ITTCallLogging, false, "If set to a nonzero value, logs function entry and exit information for every OpenCL call using the ITT APIs. This feature will only function if the Intercept Layer for OpenCL Applications is built with ITT support." ) +CLI_CONTROL( bool, ChromeCallLogging, false, "If set to a nonzero value, logs function entry and exit information for every OpenCL call to a JSON file that may be used for Chrome Tracing." ) +CLI_CONTROL( bool, ErrorLogging, false, "If set to a nonzero value, logs all OpenCL errors and the function name that caused the error." ) +CLI_CONTROL( bool, ErrorAssert, false, "If set to a nonzero value, breaks into the debugger when an OpenCL error occurs." ) +CLI_CONTROL( bool, ContextCallbackLogging, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will install a callback for every context and log any calls to the context callback. The application's context callback, if any, will be invoked after the Intercept Layer for OpenCL Applications' context callback." ) +CLI_CONTROL( cl_uint, ContextHintLevel, 0, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will attempt to create contexts with the CL_CONTEXT_SHOW_DIAGNOSTICS_INTEL property set to the specified value. If this property is specified by the application, the Intercept Layer for OpenCL Applications will overwrite it with the specified value, otherwise the property and the specified value will be added to the list of context creation properties. This functionality is only available for OpenCL implementations that support the cl_intel_driver_diagnostics extension. If this functionality is not available in the underlying OpenCL implementation, the unmodified list of context properties will be used to create the context instead. More information about this feature, including valid values and their meaning, can be found in the cl_intel_driver_diagnostics extension specification." ) +CLI_CONTROL( bool, EventCallbackLogging, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will install its own callback for every event callback and log the call to the event callback. The application's event callback will be invoked after the Intercept Layer for OpenCL Applications' event callback." ) +CLI_CONTROL( bool, CLInfoLogging, false, "If set to a nonzero value, logs information about the platforms and devices in the system on the first call to clGetPlatformIDs()." ) +CLI_CONTROL( std::string, LogDir, "", "If set, the Intercept Layer for OpenCL Applications will emit logs to this directory instead of the default log directory." ) + +CLI_CONTROL_SEPARATOR( Performance Timing Controls: ) +CLI_CONTROL( bool, HostPerformanceTiming, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will track the minimum, maximum, and average host CPU time for each OpenCL entry point. When the process exits, this information will be printed to the file \"clIntercept_report.txt\" in the directory \"%SYSTEMDRIVE%\\Intel\\CLIntercept_Dump\\\"." ) +CLI_CONTROL( bool, DevicePerformanceTiming, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will add event profiling to track the minimum, maximum, and average device time for each OpenCL command. This operation may be fairly intrusive and may have side effects; in particular it forces all command queues to be created with PROFILING_ENABLED and may increment the reference count for application events. When the process exits, this information will be printed to the file \"clIntercept_report.txt\" in the directory \"%SYSTEMDRIVE%\\Intel\\CLIntercept_Dump\\\"." ) +CLI_CONTROL( bool, DevicePerformanceTimeHashTracking, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will distinguish between OpenCL NDRange kernels from programs with different hashes for the purpose of device performance timing." ) +CLI_CONTROL( bool, DevicePerformanceTimeKernelInfoTracking,false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will distinguish between OpenCL NDRange kernels using information such as the kernel's Preferred Work Group Size Multiple (AKA SIMD size)." ) +CLI_CONTROL( bool, DevicePerformanceTimeGWSTracking, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will distinguish between OpenCL NDRange kernels with different global work sizes for the purpose of device performance timing." ) +CLI_CONTROL( bool, DevicePerformanceTimeLWSTracking, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will distinguish between OpenCL NDRange kernels with different local work sizes for the purpose of device performance timing." ) +CLI_CONTROL( bool, DevicePerformanceTimingSkipUnmap, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will skip device performance timing for unmap operations. This is a workaround for a bug in some OpenCL implementations, where querying events created from unmap operations results in driver crashes." ) +CLI_CONTROL( bool, HostPerformanceTimeLogging, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will log the host elapsed time for each OpenCL entry point. This can be useful to identify OpenCL entry points that execute significantly slower or faster than average on the host." ) +CLI_CONTROL( bool, DevicePerformanceTimeLogging, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will log the device execution time deltas for each OpenCL command. This can be useful to identify specific OpenCL commands that execute significantly slower or faster than average on the device. If DevicePerformanceTiming is disabled then this control will have no effect." ) +CLI_CONTROL( bool, DevicePerformanceTimelineLogging, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will log the device execution times for each OpenCL command. This can be useful to visualize the execution timeline of OpenCL commands that execute on the device. If DevicePerformanceTiming is disabled then this control will have no effect." ) +CLI_CONTROL( std::string, DevicePerfCounterCustom, "", "If set, the Intercept Layer for OpenCL Applications will collect MDAPI metrics for the Metric Set corresponding to this value for each OpenCL command. Frequently used Metric Sets include: ComputeBasic, ComputeExtended, L3_1, Sampler. The output file has the potential to be very big depending on the work load. This operation may be fairly intrusive and may have side effects; in particular it forces all command queues to be created with PROFILING_ENABLED and may increment the reference count for application events. When the process exits, this information will be printed to the file \"clintercept_perfcounter_dump_.txt\" in the directory \"%SYSTEMDRIVE%\\Intel\\CLIntercept_Dump\\\". This feature will only function if the Intercept Layer for OpenCL Applications is built with MDAPI support." ) +CLI_CONTROL( std::string, DevicePerfCounterFile, "", "Full path to a custom MDAPI file. This can be used to add custom Metric Sets." ) +CLI_CONTROL( bool, DevicePerfCounterTiming, false, "If set to a nonzero value and DevicePerfCounterCustom is set, the Intercept Layer for OpenCL Applications will enable Intel GPU Performance Counters to track the minimum, maximum, and average performance counter deltas for each OpenCL command. This operation may be fairly intrusive and may have side effects; in particular it forces all command queues to be created with PROFILING_ENABLED and may increment the reference count for application events. When the process exits, this information will be printed to the file \"clIntercept_report.txt\" in the directory \"%SYSTEMDRIVE%\\Intel\\CLIntercept_Dump\\\". This feature will only function if the Intercept Layer for OpenCL Applications is built with MDAPI support." ) +CLI_CONTROL( bool, ITTPerformanceTiming, false, "[Note: This control makes ITT calls, but they appear to do nothing!] If set to a nonzero value, the Intercept Layer for OpenCL Applications will generate ITT-compatible performance timing data. Similar to DevicePerformanceTiming, this operation may be fairly intrusive and may have side effects; in particular it forces all command queues to be created with PROFILING_ENABLED and may increment the reference count for application events. ITTPerformanceTiming will also silently create OpenCL command queues that support advanced performance counters if this functionality is available. This feature will only function if the Intercept Layer for OpenCL Applications is built with ITT support." ) +CLI_CONTROL( bool, ITTShowOnlyExecutingEvents, false, "[Note: This control makes ITT calls, but they appear to do nothing!] By default, when ITTPerformanceTiming is enabled, the Intercept Layer for OpenCL Applications will generate ITT-compatible information for all states of an OpenCL event: when the command was queued, when it was submitted, when it started executing, and when it finished executing. If ITTShowOnlyExecutingEvents is set to a nonzero value, the Intercept Layer for OpenCL Applications will only generate ITT-compatible instrumentation when an event begins executing and when an event ends executing. Since no information will be displayed about when a command is queued or submitted, this can sometimes make it easier to identify times when the device is idle. This feature will only function if the Intercept Layer for OpenCL Applications is built with ITT support." ) +CLI_CONTROL( bool, ChromePerformanceTiming, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will generate device performance timing information in a JSON file that may be used for Chrome Tracing." ) + +CLI_CONTROL_SEPARATOR( Controls for Dumping and Injecting Programs and Build Options: ) +CLI_CONTROL( bool, OmitProgramNumber, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will omit the program number from dumped file names and hash tracking. This can produce deterministic results even if programs are built in a non-deterministic order (say, by multiple threads)." ) +CLI_CONTROL( bool, SimpleDumpProgramSource, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will dump the last string(s) passed to clCreateProgramWithSource() to the file kernel.cl, and the last program options passed to clBuildProgram() to the file kernel.txt. These files will be dumped to the application's working directory. If an application fails to compile a program and exits the program immediately after detecting a compile failure SimpleDumpProgram may be all that is needed to identify the program and program options that are failing to compile." ) +CLI_CONTROL( bool, DumpProgramSourceScript, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will dump every string passed to clCreateProgramWithSource() to its own file. The directory names and file names for the dumped files match the directory names and file names expected by a modified OpenCL conformance test script to capture kernels. This setting overrides SimpleDumpProgramSource, and if it is set to a nonzero value then the value of SimpleDumpProgramSource is ignored." ) +CLI_CONTROL( bool, DumpProgramSource, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will dump every string passed to clCreateProgramWithSource() to its own file. The files will be dumped to \"%SYSTEMDRIVE%\\Intel\\CLIntercept_Dump\\\". The filename will have the form \"CLI___source.cl\". Program options that are passed to clBuildProgram() or clCompileProgram() will be dumped to the same directory with the filename \"CLI____options.txt\". This setting can be used for information purposes to see all kernels that are used by an application or to dump programs for program injection. This setting overrides DumpProgramSourceScript and SimpleDumpProgramSource, and if it is set to a nozero value then the values of DumpProgramSourceScript and SimpleDumpProgramSource will be ignored." ) +CLI_CONTROL( bool, DumpInputProgramBinaries, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will dump every program binary that is passed to clCreateProgramWithBinary() to its own file. The files will be dumped to \"%SYSTEMDRIVE%\\Intel\\CLIntercept_Dump\\\". The filename will have the form \"CLI___.bin\". This is the input program binary provided by the application, and not a device binary queried from the OpenCL implementation. In particular, note that it may be a SPIR 1.2 binary." ) +CLI_CONTROL( bool, DumpProgramBinaries, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will dump every program binary that was successfully built with clBuildProgram() to its own file. The files will be dumped to \"%SYSTEMDRIVE%\\Intel\\CLIntercept_Dump\\\". The filename will have the form \"CLI____.bin\". Program options that are passed to clBuildProgram() or clCompileProgram() will be dumped to the same directory with the filename \"CLI____options.txt\". This setting can be used to examine compiled program binaries or to dump program binaries for program binary injection. Note that this option dumps the output binary, which is a device binary, after calling clBuildProgram()." ) +CLI_CONTROL( bool, DumpProgramSPIRV, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will dump every program IL binary passed to clCreateProgramWithIL() to its own file. The files will be dumped to \"%SYSTEMDRIVE%\\Intel\\CLIntercept_Dump\\\". The filename will have the form \"CLI___0000.spv\" - for now at least!. Program options that are passed to clBuildProgram() or clCompileProgram() will be dumped to the same directory with the filename \"CLI____options.txt\". This setting can be used for information purposes to see all kernels that are used by an application or to dump SPIRV programs for SPIRV injection." ) +CLI_CONTROL( bool, InjectProgramSource, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will look to inject potentially modified kernel source to clCreateProgramWithSource() and/or potentially modified options to clBuildProgram()." ) +CLI_CONTROL( bool, InjectProgramBinaries, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will look to inject potentially modified kernel binaries via clCreateProgramWithBinary() in place of program text for each call to clCreateProgramWithSource(). This is typically done to reduce program compilation time or to use known good program binaries." ) +CLI_CONTROL( bool, RejectProgramBinaries, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will reject kernel binaries passed via clCreateProgramWithBinary() and return CL_INVALID_BINARY. This can be used to force an application to re-compile program binaries from source." ) +CLI_CONTROL( bool, InjectProgramSPIRV, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will look to inject potentially modified kernel SPIR-V binaries via clCreateProgramWithIL() in place of program text for each call to clCreateProgramWithSource()." ) +CLI_CONTROL( bool, PrependProgramSource, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will look to prepend kernel code from a file to the application provided kernel source passed to clCreateProgramWithSource(). The Intercept Layer for OpenCL Applications will look for kernel source to prepend in the directory \"%SYSTEMDRIVE%\\Intel\\CLIntercept_Dump\\\". The files that are searched for are (in order) \"CLI___prepend.cl\", \"CLI__prepend.cl\", and \"CLI_prepend.cl\"." ) +CLI_CONTROL( std::string, AppendBuildOptions, "", "If set, the Intercept Layer for OpenCL Applications will add these build options to the end of any application provided or injected build options for each call to clBuildProgram()." ) +CLI_CONTROL( bool, DumpProgramBuildLogs, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will dump build logs for every device a program is built for to a separate file. The files will be dumped to \"%SYSTEMDRIVE%\\Intel\\CLIntercept_Dump\\\". The filename will have the form \"CLI_____build_log.txt\"." ) + +CLI_CONTROL_SEPARATOR( Controls for Automatically Creating SPIR-V Modules: ) +CLI_CONTROL( bool, AutoCreateSPIRV, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will automatically create SPIR-V modules by invoking CLANG each time a program is built. The files will be dumped to \"%SYSTEMDRIVE%\\Intel\\CLIntercept_Dump\\\". The filename will have the form \"CLI___.spv\". Because invoking CLANG requires a file containing the OpenCL C source, setting this option implicitly sets DumpProgramSource as well. Additionally, this feature is not available for injected program source." ) +CLI_CONTROL( std::string, SPIRVClang, "clang", "The clang executable used to compile an OpenCL C program to a SPIR-V module. This can be an executable in the system path, a relative path, or a full absolute path." ) +CLI_CONTROL( std::string, SPIRVCLHeader, "opencl.h", "The OpenCL header file used to compile an OpenCL C program to a SPIR-V module. This must be a relative path or a full absolute path." ) +CLI_CONTROL( std::string, SPIRVDis, "spirv-dis", "The spirv-dis executable used to optionally disassemble the compiled SPIR-V module to a SPIR-V text representation. This can be an executable in the system path, a relative path, or a full absolute path." ) +CLI_CONTROL( std::string, DefaultOptions, "-cc1 -x cl -cl-std=CL1.2 -D__OPENCL_C_VERSION__=120 -D__OPENCL_VERSION__=120 -emit-spirv -triple=spir", "This is the list of options that is implicitly passed to CLANG to build a non-OpenCL 2.0 SPIR-V module. Any application-provided build options will be appended to these build options." ) +CLI_CONTROL( std::string, OpenCL2Options, "-cc1 -x cl -cl-std=CL2.0 -D__OPENCL_C_VERSION__=200 -D__OPENCL_VERSION__=200 -emit-spirv -triple=spir", "This is the list of options that is implicitly passed to CLANG to build an OpenCL 2.0 SPIR-V module. Any application-provided build options will be appended to these build options." ) + +CLI_CONTROL_SEPARATOR( Controls for Dumping Buffers and Images: ) +CLI_CONTROL( bool, DumpArgumentsOnSet, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will dump the argument value on calls to clSetKernelArg(). Arguments are dumped as raw binary data to \"%SYSTEMDRIVE%\\Intel\\CLIntercept_Dump\\\\SetKernelArg\". The filenames will have the form \"SetKernelArg__Kernel__Arg_.bin\"." ) +CLI_CONTROL( bool, DumpBuffersAfterCreate, false, "If set, the Intercept Layer for OpenCL Applications will dump buffers to a file after creation. This control still honors the enqueue counter limits, even though no enqueues are involved during buffer creation. Currently only works for cl_mem buffers created from host pointers." ) +CLI_CONTROL( bool, DumpBuffersAfterMap, false, "If set, the Intercept Layer for OpenCL Applications will dump the contents of a buffer to a file after the buffer is mapped. Only valid if the buffer is NOT mapped with CL_MAP_WRITE_INVALIDATE_REGION. If the buffer was mapped non-blocking, this may insert a clFinish() into the command queue, which may have functional or performance implications." ) +CLI_CONTROL( bool, DumpBuffersBeforeUnmap, false, "If set, the Intercept Layer for OpenCL Applications will dump the contents of a buffer to a file immediately before the buffer is unmapped. This is done by inserting a blocking clEnqueueMapBuffer() (and matching clEnqueueUnmapMemObject()) into the command queue, which may have functional or performance implications." ) +CLI_CONTROL( bool, DumpBuffersBeforeEnqueue, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will dump buffers before calls to clEnqueueNDRangeKernel(). Only buffers that are kernel arguments for the kernel being enqueued are dumped. Buffers are dumped as raw binary data to \"%SYSTEMDRIVE%\\Intel\\CLIntercept_Dump\\\\memDumpPreEnqueue\". The filenames will have the form \"Enqueue__Kernel__Arg__Buffer_.bin\"." ) +CLI_CONTROL( bool, DumpBuffersAfterEnqueue, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will dump buffers after calls to clEnqueueNDRangeKernel(). Only buffers that are kernel arguments for the kernel being enqueued are dumped. Buffers are dumped as raw binary data to \"%SYSTEMDRIVE%\\Intel\\CLIntercept_Dump\\\\memDumpPostEnqueue\". The filenames will have the form \"Enqueue__Kernel__Arg__Buffer_.bin\". Note that this is the same naming convention as with DumpBuffersBeforeEnqueue, so the changes resulting from an enqueue can be determined by diff'ing the preEnqueue folder with the postEnqueue folder." ) +CLI_CONTROL( std::string, DumpBuffersForKernel, "", "If set, the Intercept Layer for OpenCL Applications will only dump buffers when the specified kernel is enqueued. This control is ignored unless DumpBuffersBeforeEnqueue or DumpBuffersAfterEnqueue are enabled." ) +CLI_CONTROL( bool, DumpImagesBeforeEnqueue, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will dump images before calls to clEnqueueNDRangeKernel(). Only images that are kernel arguments for the kernel being enqueued are dumped. Images are dumped as raw binary data to \"%SYSTEMDRIVE%\\Intel\\CLIntercept_Dump\\\\memDumpPreEnqueue\". The filenames will have the form \"Enqueue__Kernel__Arg__Image__xx_bpp.raw\"." ) +CLI_CONTROL( bool, DumpImagesAfterEnqueue, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will dump images after calls to clEnqueueNDRangeKernel(). Only images that are kernel arguments for the kernel being enqueued are dumped. Images are dumped as raw binary data to \"%SYSTEMDRIVE%\\Intel\\CLIntercept_Dump\\\\memDumpPostEnqueue\". The filenames will have the form \"Enqueue__Kernel__Arg__Image__xx_bpp.raw\". Note that this is the same naming convention as with DumpImagesBeforeEnqueue, so the changes resulting from an enqueue can be determined by diff'ing the preEnqueue folder with the postEnqueue folder." ) +CLI_CONTROL( std::string, DumpImagesForKernel, "", "If set, the Intercept Layer for OpenCL Applications will only dump image when the specified kernel is enqueued. This control is ignored unless DumpImagesBeforeEnqueue or DumpImagesAfterEnqueue are enabled." ) +CLI_CONTROL( cl_uint, DumpBuffersMinEnqueue, 0, "The Intercept Layer for OpenCL Applications will only dump buffers when the enqueue counter is greater than this value, inclusive." ) +CLI_CONTROL( cl_uint, DumpBuffersMaxEnqueue, UINT_MAX, "The Intercept Layer for OpenCL Applications will only dump buffers when the enqueue counter is less than this value, inclusive." ) +CLI_CONTROL( cl_uint, DumpImagesMinEnqueue, 0, "The Intercept Layer for OpenCL Applications will only dump images when the enqueue counter is greater than this value, inclusive." ) +CLI_CONTROL( cl_uint, DumpImagesMaxEnqueue, UINT_MAX, "The Intercept Layer for OpenCL Applications will only dump images when the enqueue counter is less than this value, inclusive." ) + +CLI_CONTROL_SEPARATOR( AubCapture Controls: ) +CLI_CONTROL( bool, AubCapture, false, "This is the master control for aub capture. The Intercept Layer for OpenCL Applications doesn't implement aub capture itself, but can be used to selectively enable and disable aub capture via kdc.exe." ) +CLI_CONTROL( bool, AubCaptureIndividualEnqueues, false, "If set, the Intercept Layer for OpenCL Applications will invoke kdc.exe to start aub capture before a kernel enqueue, and will also invoke kdc.exe to stop aub capture immediately after the kernel enqueue. Each .daf file will have the form \"AubCapture_Enqueue__kernel_.daf\". Note that non-kernel enqueues such as calls to clEnqueueReadBuffer() and clEnqueueWriteBuffer() will NOT be aub captured when this control is set. The AubCaptureMinEnqueue and AubCaptureMaxEnqueue controls are still honored when AubCaptureIndividualEnqueues is set." ) +CLI_CONTROL( cl_uint, AubCaptureMinEnqueue, 0, "The Intercept Layer for OpenCL Applications will only invoke kdc.exe to enable aub capture when the enqueue counter is greater than this value, inclusive." ) +CLI_CONTROL( cl_uint, AubCaptureMaxEnqueue, UINT_MAX, "The Intercept Layer for OpenCL Applications will invoke kdc.exe to stop aub capture when the encounter is greater than this value, meaning that only enqueues less than this value, inclusive, will be captured. If the enqueue counter never reaches this value, the Intercept Layer for OpenCL Applications will stop aub capture when the DLL is unloaded." ) +CLI_CONTROL( std::string, AubCaptureKernelName, "", "If set, the Intercept Layer for OpenCL Applications will only invoke kdc.exe to enable aub capture when the kernel name equals this name.") +CLI_CONTROL( std::string, AubCaptureKernelGWS, "", "If set, the Intercept Layer for OpenCL Applications will only invoke kdc.exe to enable aub capture when the NDRange global work size matches this string. The string should have the form \"XxYxZ\". The wildcard \"*\" matches all global work sizes.") +CLI_CONTROL( std::string, AubCaptureKernelLWS, "", "If set, the Intercept Layer for OpenCL Applications will only invoke kdc.exe to enable aub capture when the NDRange local work size matches this string. The string should have the form \"XxYxZ\". The wildcard \"*\" matches all local work sizes, and the string \"NULL\" matches a NULL local work size.") +CLI_CONTROL( bool, AubCaptureUniqueKernels, false, "If set, the Intercept Layer for OpenCL Applications will only invoke kdc.exe to enable aub capture if the kernel signature (i.e. hash + kernelname + gws + lws) has not been seen already. The behavior of this control is well-defined when AubCaptureIndividualEnqueues is not set, but it doesn't make much sense without AubCaptureIndividualEnqueues." ) +CLI_CONTROL( cl_uint, AubCaptureNumKernelEnqueuesSkip, 0, "The Intercept Layer for OpenCL Applications will skip this many kernel enqueues before invoking kdc.exe to enable aub capture. The behavior of this control is well-defined when AubCaptureIndividualEnqueues is not set, but it doesn't make much sense without AubCaptureIndividualEnqueues.") +CLI_CONTROL( cl_uint, AubCaptureNumKernelEnqueuesCapture, UINT_MAX, "The Intercept Layer for OpenCL Applications will only capture this many kernel enqueues. The behavior of this control is well-defined when AubCaptureIndividualEnqueues is not set, but it doesn't make much sense without AubCaptureIndividualEnqueues.") +CLI_CONTROL( cl_uint, AubCaptureStartWait, 0, "The Intercept Layer for OpenCL Applications will wait for this many milliseconds before invoking kdc.exe to begin aub capture.") +CLI_CONTROL( cl_uint, AubCaptureEndWait, 0, "The Intercept Layer for OpenCL Applications will wait for this many milliseconds before invoking kdc.exe to end aub capture.") + +CLI_CONTROL_SEPARATOR( Execution Controls: ) +CLI_CONTROL( bool, NoErrors, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will cause all OpenCL APIs to return a successful error status." ) +CLI_CONTROL( bool, FinishAfterEnqueue, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications inserts a call to clFinish() after every enqueue. The command queue that the command was just enqueued to is passed to clFinish(). This can be used to debug possible timing or resource management issues and will likely impact performance." ) +CLI_CONTROL( bool, FlushAfterEnqueue, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications inserts a call to clFlush() after every enqueue. The command queue that the command was just enqueued to is passed to clFlush(). This can also be used to debug possible timing or resource management issues and is slightly less obtrusive than FinishAfterEnqueue but still will likely impact performance. If both FinishAfterEnqueue and FlushAfterEnqueue are nonzero then the Intercept Layer for OpenCL Applications will only insert a call to clFinish() after every enqueue, because clFinish() implies clFlush()." ) +CLI_CONTROL( bool, FlushAfterEnqueueBarrier, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications inserts a call to clFlush() after every barrier enqueue. The command queue that the command was just enqueued to is passed to clFlush(). This has been useful to debug out-of-order queue issues." ) +CLI_CONTROL( bool, InOrderQueue, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will force all queues to be created in-order. This can be used for performance analysis, but may lead to deadlocks in some cases." ) +CLI_CONTROL( bool, NullEnqueue, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will silently ignore any enqueue. This can be used for performance analysis, but will likely cause errors if the application relies on any sort of information from OpenCL events and should be used carefully." ) +CLI_CONTROL( bool, NullLocalWorkSize, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will force the local work size argument to clEnqueueNDRangeKernel() to be NULL, which causes the OpenCL implementation to pick the local work size. Note that this control takes effect before NullLocalWorkSizeX / NullLocalWorkSizeY / NullLocalWorkSizeZ (see below), so enabling both controls will have the effect of forcing a specific local work size." ) +CLI_CONTROL( size_t, NullLocalWorkSizeX, 0, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will set the local work size that will be used if an application passes NULL as the local work size to clEnqueueNDRangeKernel(). 1D dispatches will only look at NullLocalWorkSizeX, 2D dispatches will only look at NullLocalWorkSizeX and NullLocalWorkSizeY, while 3D dispatches will look at NullLocalWorkSizeX, NullLocalWorkSizeY, and NullLocalWorkSizeZ. If the specified values for NullLocalWorkSize do not evenly divide the global work size then the specified values of NullLocalWorkSize will not take effect." ) +CLI_CONTROL( size_t, NullLocalWorkSizeY, 0, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will set the local work size that will be used if an application passes NULL as the local work size to clEnqueueNDRangeKernel(). 1D dispatches will only look at NullLocalWorkSizeX, 2D dispatches will only look at NullLocalWorkSizeX and NullLocalWorkSizeY, while 3D dispatches will look at NullLocalWorkSizeX, NullLocalWorkSizeY, and NullLocalWorkSizeZ. If the specified values for NullLocalWorkSize do not evenly divide the global work size then the specified values of NullLocalWorkSize will not take effect." ) +CLI_CONTROL( size_t, NullLocalWorkSizeZ, 0, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will set the local work size that will be used if an application passes NULL as the local work size to clEnqueueNDRangeKernel(). 1D dispatches will only look at NullLocalWorkSizeX, 2D dispatches will only look at NullLocalWorkSizeX and NullLocalWorkSizeY, while 3D dispatches will look at NullLocalWorkSizeX, NullLocalWorkSizeY, and NullLocalWorkSizeZ. If the specified values for NullLocalWorkSize do not evenly divide the global work size then the specified values of NullLocalWorkSize will not take effect." ) +CLI_CONTROL( bool, InitializeBuffers, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will initialize the contents of allocated buffers with zero. Only valid for non-COPY_HOST_PTR and non-USE_HOST_PTR allocations." ) + +CLI_CONTROL_SEPARATOR( Platform and Device Query Overrides: ) +CLI_CONTROL( std::string, PlatformName, "", "If set to a non-empty value, the clGetPlatformInfo() query for CL_PLATFORM_NAME will return this string instead of the true platform name." ) +CLI_CONTROL( std::string, PlatformVendor, "", "If set to a non-empty value, the clGetPlatformInfo() query for CL_PLATFORM_VENDOR will return this string instead of the true platform vendor." ) +CLI_CONTROL( std::string, PlatformProfile, "", "If set to a non-empty value, the clGetPlatformInfo() query for CL_PLATFORM_PROFILE will return this string instead of the true platform profile." ) +CLI_CONTROL( std::string, PlatformVersion, "", "If set to a non-empty string, the clGetPlatformInfo() query for CL_PLATFORM_VERSION will return this string instead of the true platform version." ) +CLI_CONTROL( cl_uint, DeviceTypeFilter, CL_DEVICE_TYPE_ALL, "Hides all device types that are not in the filter. Note: CL_DEVICE_TYPE_CPU = 2, CL_DEVICE_TYPE_GPU = 4, CL_DEVICE_TYPE_ACCELERATOR = 8, CL_DEVICE_TYPE_CUSTOM = 16." ) +CLI_CONTROL( cl_uint, DeviceType, 0, "If set to a non-zero value, the clGetDeviceInfo() query for CL_DEVICE_TYPE will return this value instead of the true device type. In addition, calls to clGetDeviceIDs() for this device type will return all devices, not just devices of the requested type. This can be used to enumerate all devices (even CPUs) as GPUs, or vice versa." ) +CLI_CONTROL( std::string, DeviceName, "", "If set to a non-empty string, the clGetDeviceInfo() query for CL_DEVICE_NAME will return this value instead of the true device name." ) +CLI_CONTROL( std::string, DeviceVendor, "", "If set to a non-empty string, the clGetDeviceInfo() query for CL_DEVICE_VENDOR will return this value instead of the true device vendor." ) +CLI_CONTROL( std::string, DeviceProfile, "", "If set to a non-empty string, the clGetDeviceInfo() query for CL_DEVICE_PROFILE will return this value instead of the true device profile." ) +CLI_CONTROL( std::string, DeviceVersion, "", "If set to a non-empty string, the clGetDeviceInfo() query for CL_DEVICE_VERSION will return this value instead of the true device version." ) +CLI_CONTROL( std::string, DeviceCVersion, "", "If set to a non-empty string, the clGetDeviceInfo() query for CL_DEVICE_OPENCL_C_VERSION will return this value instead of the true device version." ) +CLI_CONTROL( std::string, DeviceExtensions, "", "If set to a non-empty string, the clGetDeviceInfo() query for CL_DEVICE_EXTENSIONS will return this value instead of the true device extensions string." ) +CLI_CONTROL( cl_uint, DeviceVendorID, 0, "If set to a non-zero value, the clGetDeviceInfo() query for CL_DEVICE_VENDOR will return this value instead of the true device vendor ID." ) +CLI_CONTROL( cl_uint, DeviceMaxComputeUnits, 0, "If set to a non-zero value, the clGetDeviceInfo() query for CL_DEVICE_MAX_COMPUTE_UNITS will return this value instead of the true device max compute units." ) +CLI_CONTROL( cl_uint, DevicePreferredVectorWidthChar, UINT_MAX, "If set to a non-negative value, the clGetDeviceInfo() query for CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR will return this value instead of the true device preferred vector width." ) +CLI_CONTROL( cl_uint, DevicePreferredVectorWidthShort, UINT_MAX, "If set to a non-negative value, the clGetDeviceInfo() query for CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT will return this value instead of the true device preferred vector width." ) +CLI_CONTROL( cl_uint, DevicePreferredVectorWidthInt, UINT_MAX, "If set to a non-negative value, the clGetDeviceInfo() query for CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT will return this value instead of the true device preferred vector width." ) +CLI_CONTROL( cl_uint, DevicePreferredVectorWidthLong, UINT_MAX, "If set to a non-negative value, the clGetDeviceInfo() query for CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG will return this value instead of the true device preferred vector width." ) +CLI_CONTROL( cl_uint, DevicePreferredVectorWidthHalf, UINT_MAX, "If set to a non-negative value, the clGetDeviceInfo() query for CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF will return this value instead of the true device preferred vector width." ) +CLI_CONTROL( cl_uint, DevicePreferredVectorWidthFloat, UINT_MAX, "If set to a non-negative value, the clGetDeviceInfo() query for CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT will return this value instead of the true device preferred vector width." ) +CLI_CONTROL( cl_uint, DevicePreferredVectorWidthDouble, UINT_MAX, "If set to a non-negative value, the clGetDeviceInfo() query for CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE will return this value instead of the true device preferred vector width." ) + +CLI_CONTROL_SEPARATOR( Precompiled Kernel and Builtin Kernel Override Controls: ) +CLI_CONTROL( bool, ForceByteBufferOverrides, false, "If set to a nonzero value, each of the buffer functions that are overridden (via one or more of the keys below) will use a byte-wise operation to read/write/copy the buffer (default behavior is to try to copy multiple bytes at a time, if possible). Note: Requires OpenCL 1.1 or the \"byte addressable store\" extension." ) +CLI_CONTROL( bool, OverrideReadBuffer, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will use a kernel to implement clEnqueueReadBuffer() instead of the implementation's clEnqueueReadBuffer(). Note: Requires OpenCL 1.1 or the \"byte addressable store\" extension." ) +CLI_CONTROL( bool, OverrideWriteBuffer, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will use a kernel to implement clEnqueueWriteBuffer() instead of the implementation's clEnqueueWriteBuffer(). Note: Requires OpenCL 1.1 or the \"byte addressable store\" extension." ) +CLI_CONTROL( bool, OverrideCopyBuffer, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will use a kernel to implement clEnqueueCopyBuffer() instead of the implementation's clEnqueueCopyBuffer(). Note: Requires OpenCL 1.1 or the \"byte addressable store\" extension." ) +CLI_CONTROL( bool, OverrideReadImage, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will use a kernel to implement clEnqueueReadImage() instead of the implementation's clEnqueueReadImage(). Only 2D images are currently supported." ) +CLI_CONTROL( bool, OverrideWriteImage, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will use a kernel to implement clEnqueueWriteImage() instead of the implementation's clEnqueueWriteImage(). Only 2D images are currently supported." ) +CLI_CONTROL( bool, OverrideCopyImage, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will use a kernel to implement clEnqueueCopyImage() instead of the implementation's clEnqueueCopyImage(). Only 2D images are currently supported." ) +CLI_CONTROL( bool, OverrideBuiltinKernels, false, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will use its own version of the built-in OpenCL kernels that may be accessed via clCreateProgramWithBuiltInKernels(). At present, only the VME block_motion_estimate_intel kernel is implemented." ) + +CLI_CONTROL_SEPARATOR( SIMD Survey Controls: ) +CLI_CONTROL( bool, SIMDSurvey, false, "Executes a SIMD survey state machine. The general idea of the SIMD survey state machine is to create and manage three additional kernels for each actual OpenCL kernel, one for each SIMD size. Then, execute and time the three kernels, and choose the fastest for subsequent executions." ) +CLI_CONTROL( cl_uint, SIMDSurveyWarmupIterations, 4, "This is the number of NDRanges that the SIMD survey state machine ignores before starting to time the SIMD survey." ) +CLI_CONTROL( std::string, SIMDSurveySIMD8Option, "-DSIMD_SURVEY=8 ", "This is the build option that is pre-pended to the application-specified build options to create the SIMD8 kernel." ) +CLI_CONTROL( std::string, SIMDSurveySIMD16Option, "-DSIMD_SURVEY=16", "This is the build option that is pre-pended to the application-specified build options to create the SIMD16 kernel." ) +CLI_CONTROL( std::string, SIMDSurveySIMD32Option, "-DSIMD_SURVEY=32", "This is the build option that is pre-pended to the application-specified build options to create the SIMD32 kernel." ) +CLI_CONTROL( bool, SIMDOracle, false, "[Note: Not currently implemented, but the idea behind the SIMD oracle is to save the best SIMD size from run-to-run, so the full SIMD survey does not need to be re-executed.]" ) diff --git a/Src/dispatch.cpp b/Src/dispatch.cpp new file mode 100644 index 00000000..81999575 --- /dev/null +++ b/Src/dispatch.cpp @@ -0,0 +1,8612 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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 + +#include "intercept.h" + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clGetPlatformIDs)( + cl_uint num_entries, + cl_platform_id* platforms, + cl_uint* num_platforms ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + LOG_CLINFO(); + + CALL_LOGGING_ENTER(); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clGetPlatformIDs( + num_entries, + platforms, + num_platforms ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clGetPlatformIDs( + num_entries, + platforms, + num_platforms ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clGetPlatformInfo)( + cl_platform_id platform, + cl_platform_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + std::string platformInfo; + if( pIntercept->callLogging() ) + { + pIntercept->getPlatformInfoString( + platform, + platformInfo ); + } + CALL_LOGGING_ENTER( "platform = [ %s ], param_name = %s (%08X)", + platformInfo.c_str(), + pIntercept->enumName().name( param_name ).c_str(), + param_name ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = CL_SUCCESS; + + if( pIntercept->overrideGetPlatformInfo( + param_name, + param_value_size, + param_value, + param_value_size_ret, + retVal ) == false ) + { + retVal = pIntercept->dispatch().clGetPlatformInfo( + platform, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + } + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clGetPlatformInfo( + platform, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clGetDeviceIDs)( + cl_platform_id platform, + cl_device_type device_type, + cl_uint num_entries, + cl_device_id* devices, + cl_uint* num_devices ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + std::string platformInfo; + if( pIntercept->callLogging() ) + { + pIntercept->getPlatformInfoString( + platform, + platformInfo ); + } + CALL_LOGGING_ENTER( "platform = [ %s ], device_type = %s (%llX)", + platformInfo.c_str(), + pIntercept->enumName().name_device_type( device_type ).c_str(), + device_type ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = CL_SUCCESS; + + device_type = pIntercept->filterDeviceType( device_type ); + + retVal = pIntercept->dispatch().clGetDeviceIDs( + platform, + device_type, + num_entries, + devices, + num_devices ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clGetDeviceIDs( + platform, + device_type, + num_entries, + devices, + num_devices ); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clGetDeviceInfo)( + cl_device_id device, + cl_device_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret ) +{ + CLIntercept* pIntercept = GetIntercept(); + if (pIntercept) + { + std::string deviceInfo; + if( pIntercept->callLogging() ) + { + pIntercept->getDeviceInfoString( + 1, + &device, + deviceInfo ); + } + CALL_LOGGING_ENTER( "device = [ %s ], param_name = %s (%08X)", + deviceInfo.c_str(), + pIntercept->enumName().name( param_name ).c_str(), + param_name ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = CL_SUCCESS; + + if( pIntercept->overrideGetDeviceInfo( + device, + param_name, + param_value_size, + param_value, + param_value_size_ret, + retVal ) == false ) + { + retVal = pIntercept->dispatch().clGetDeviceInfo( + device, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + } + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clGetDeviceInfo( + device, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 1.2 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER(); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clCreateSubDevices( + in_device, + properties, + num_devices, + out_devices, + num_devices_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clCreateSubDevices( + in_device, + properties, + num_devices, + out_devices, + num_devices_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 1.2 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clRetainDevice)( + cl_device_id device ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_uint ref_count = 0; + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetDeviceInfo( + device, + CL_DEVICE_REFERENCE_COUNT, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_ENTER( "[ ref count = %d ] device = %p", + ref_count, + device ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clRetainDevice( + device ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetDeviceInfo( + device, + CL_DEVICE_REFERENCE_COUNT, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_EXIT( "[ ref count = %d ]", + ref_count ); + + return retVal; + } + else + { + return dummyDispatch.clRetainDevice( + device ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 1.2 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clReleaseDevice)( + cl_device_id device ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_uint ref_count = 0; + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetDeviceInfo( + device, + CL_DEVICE_REFERENCE_COUNT, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_ENTER( "[ ref count = %d ] device = %p", + ref_count, + device ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clReleaseDevice( + device ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + if( pIntercept->callLogging() && ref_count != 0 ) + { + // This isn't strictly correct, but it's pretty close, and it + // avoids crashes in some cases for bad implementations. + --ref_count; + } + CALL_LOGGING_EXIT( "[ ref count = %d ]", + ref_count ); + + return retVal; + } + else + { + return dummyDispatch.clReleaseDevice( + device ); + } +} + +#ifdef __ANDROID__ +//Workaround for Android, shared library destructor isn't called +static int contextCount = 0; +static std::mutex mContextCount; +#endif + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_context CL_API_CALL CLIRN(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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_context_properties* newProperties = NULL; + cl_context retVal = NULL; + + std::string contextProperties; + std::string deviceInfo; + if( pIntercept->callLogging() ) + { + pIntercept->getContextPropertiesString( + properties, + contextProperties ); + pIntercept->getDeviceInfoString( + num_devices, + devices, + deviceInfo ); + } + CALL_LOGGING_ENTER( "properties = [ %s ], num_devices = %d, devices = [ %s ]", + contextProperties.c_str(), + num_devices, + deviceInfo.c_str() ); + CREATE_CONTEXT_OVERRIDE_INIT( properties, pfn_notify, user_data, newProperties ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + if( ( retVal == NULL ) && newProperties ) + { + retVal = pIntercept->dispatch().clCreateContext( + newProperties, + num_devices, + devices, + pfn_notify, + user_data, + errcode_ret ); + } + if( retVal == NULL ) + { + retVal = pIntercept->dispatch().clCreateContext( + properties, + num_devices, + devices, + pfn_notify, + user_data, + errcode_ret ); + } + + ITT_ADD_PARAM_AS_METADATA( retVal ); + + INIT_PRECOMPILED_KERNEL_OVERRIDES( retVal ); + INIT_BUILTIN_KERNEL_OVERRIDES( retVal ); + + CPU_PERFORMANCE_TIMING_END(); + CREATE_CONTEXT_OVERRIDE_CLEANUP( retVal, newProperties ); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + +#ifdef __ANDROID__ + mContextCount.lock(); + contextCount ++; + mContextCount.unlock(); +#endif + return retVal; + } + else + { + return dummyDispatch.clCreateContext( + properties, + num_devices, + devices, + pfn_notify, + user_data, + errcode_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_context CL_API_CALL CLIRN(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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_context_properties* newProperties = NULL; + cl_context retVal = NULL; + + std::string contextProperties; + if( pIntercept->callLogging() ) + { + pIntercept->getContextPropertiesString( + properties, + contextProperties ); + } + CALL_LOGGING_ENTER( "properties = [ %s ], device_type = %s (%llX)", + contextProperties.c_str(), + pIntercept->enumName().name_device_type( device_type ).c_str(), + device_type ); + CREATE_CONTEXT_OVERRIDE_INIT( properties, pfn_notify, user_data, newProperties ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + device_type = pIntercept->filterDeviceType( device_type ); + + if( ( retVal == NULL ) && newProperties ) + { + retVal = pIntercept->dispatch().clCreateContextFromType( + newProperties, + device_type, + pfn_notify, + user_data, + errcode_ret ); + } + if( retVal == NULL ) + { + retVal = pIntercept->dispatch().clCreateContextFromType( + properties, + device_type, + pfn_notify, + user_data, + errcode_ret ); + } + + ITT_ADD_PARAM_AS_METADATA( retVal ); + + INIT_PRECOMPILED_KERNEL_OVERRIDES( retVal ); + INIT_BUILTIN_KERNEL_OVERRIDES( retVal ); + + CPU_PERFORMANCE_TIMING_END(); + CREATE_CONTEXT_OVERRIDE_CLEANUP( retVal, newProperties ); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + return dummyDispatch.clCreateContextFromType( + properties, + device_type, + pfn_notify, + user_data, + errcode_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clRetainContext)( + cl_context context ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_uint ref_count = 0; + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetContextInfo( + context, + CL_CONTEXT_REFERENCE_COUNT, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_ENTER( "[ ref count = %d ] context = %p", + ref_count, + context ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clRetainContext( + context ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetContextInfo( + context, + CL_CONTEXT_REFERENCE_COUNT, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_EXIT( "[ ref count = %d ]", + ref_count ); + + return retVal; + } + else + { + return dummyDispatch.clRetainContext( + context ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clReleaseContext)( + cl_context context ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_uint ref_count = 0; + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetContextInfo( + context, + CL_CONTEXT_REFERENCE_COUNT, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_ENTER( "[ ref count = %d ] context = %p", + ref_count, + context ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clReleaseContext( + context ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + if( pIntercept->callLogging() && ref_count != 0 ) + { + // This isn't strictly correct, but it's pretty close, and it + // avoids crashes in some cases for bad implementations. + --ref_count; + } + CALL_LOGGING_EXIT( "[ ref count = %d ]", + ref_count ); + +#if 0 + pIntercept->report(); +#endif + +#ifdef __ANDROID__ + mContextCount.lock(); + contextCount --; + mContextCount.unlock(); + + if( contextCount == 0 ) + { + pIntercept->report(); + } +#endif + return retVal; + } + else + { + return dummyDispatch.clReleaseContext( + context ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clGetContextInfo)( + cl_context context, + cl_context_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER( "param_name = %s (%08X)", + pIntercept->enumName().name( param_name ).c_str(), + param_name ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clGetContextInfo( + context, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clGetContextInfo( + context, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_command_queue CL_API_CALL CLIRN(clCreateCommandQueue)( + cl_context context, + cl_device_id device, + cl_command_queue_properties properties, + cl_int* errcode_ret ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + std::string deviceInfo; + if( pIntercept->callLogging() ) + { + pIntercept->getDeviceInfoString( + 1, + &device, + deviceInfo ); + } + CALL_LOGGING_ENTER( "device = [ %s ], properties = %s (%llX)", + deviceInfo.c_str(), + pIntercept->enumName().name_command_queue_properties( properties ).c_str(), + properties ); + + pIntercept->modifyCommandQueueProperties( properties ); + + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_command_queue retVal = NULL; + +#if defined(USE_MDAPI) + if( !pIntercept->config().DevicePerfCounterCustom.empty() ) + { + retVal = pIntercept->createMDAPICommandQueue( + context, + device, + properties, + errcode_ret ); + } +#endif + + if( retVal == NULL ) + { + retVal = pIntercept->dispatch().clCreateCommandQueue( + context, + device, + properties, + errcode_ret ); + } + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( errcode_ret[0] ); + ITT_REGISTER_COMMAND_QUEUE( retVal, false ); + CHROME_REGISTER_COMMAND_QUEUE( retVal ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + return dummyDispatch.clCreateCommandQueue( + context, + device, + properties, + errcode_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clRetainCommandQueue)( + cl_command_queue command_queue ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_uint ref_count = 0; + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetCommandQueueInfo( + command_queue, + CL_QUEUE_REFERENCE_COUNT, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_ENTER( "[ ref count = %d ] command_queue = %p", + ref_count, + command_queue ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clRetainCommandQueue( + command_queue ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetCommandQueueInfo( + command_queue, + CL_QUEUE_REFERENCE_COUNT, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_EXIT( "[ ref count = %d ]", + ref_count ); + + return retVal; + } + else + { + return dummyDispatch.clRetainCommandQueue( + command_queue ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clReleaseCommandQueue)( + cl_command_queue command_queue ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_uint ref_count = 0; + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetCommandQueueInfo( + command_queue, + CL_QUEUE_REFERENCE_COUNT, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_ENTER( "[ ref count = %d ] command_queue = %p", + ref_count, + command_queue ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clReleaseCommandQueue( + command_queue ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + ITT_RELEASE_COMMAND_QUEUE( command_queue ); + if( pIntercept->callLogging() && ref_count != 0 ) + { + // This isn't strictly correct, but it's pretty close, and it + // avoids crashes in some cases for bad implementations. + --ref_count; + } + CALL_LOGGING_EXIT( "[ ref count = %d ]", + ref_count ); + + return retVal; + } + else + { + return dummyDispatch.clReleaseCommandQueue( + command_queue ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER( "param_name = %s (%08X)", + pIntercept->enumName().name( param_name ).c_str(), + param_name ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clGetCommandQueueInfo( + command_queue, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clGetCommandQueueInfo( + command_queue, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clSetCommandQueueProperty)( + cl_command_queue command_queue, + cl_command_queue_properties properties, + cl_bool enable, + cl_command_queue_properties* old_properties ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER(); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clSetCommandQueueProperty( + command_queue, + properties, + enable, + old_properties ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clSetCommandQueueProperty( + command_queue, + properties, + enable, + old_properties ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_mem CL_API_CALL CLIRN(clCreateBuffer)( + cl_context context, + cl_mem_flags flags, + size_t size, + void* host_ptr, + cl_int* errcode_ret ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER( "flags = %s (%llX), size = %d, host_ptr = %p", + pIntercept->enumName().name_mem_flags( flags ).c_str(), + flags, + size, + host_ptr ); + INITIALIZE_BUFFER_CONTENTS_INIT( flags, size, host_ptr ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_mem retVal = pIntercept->dispatch().clCreateBuffer( + context, + flags, + size, + host_ptr, + errcode_ret ); + + CPU_PERFORMANCE_TIMING_END(); + ADD_BUFFER( retVal ); + INITIALIZE_BUFFER_CONTENTS_CLEANUP( flags, host_ptr ); + DUMP_BUFFER_AFTER_CREATE( retVal, flags, host_ptr, size ); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + return dummyDispatch.clCreateBuffer( + context, + flags, + size, + host_ptr, + errcode_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 1.1 +CL_API_ENTRY cl_mem CL_API_CALL CLIRN(clCreateSubBuffer)( + cl_mem buffer, + cl_mem_flags flags, + cl_buffer_create_type buffer_create_type, + const void *buffer_create_info, + cl_int *errcode_ret ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + std::string argsString; + if( pIntercept->callLogging() ) + { + pIntercept->getCreateSubBufferArgsString( + buffer_create_type, + buffer_create_info, + argsString ); + } + CALL_LOGGING_ENTER( "buffer = %p, flags = %s (%llX), %s", + buffer, + pIntercept->enumName().name_mem_flags( flags ).c_str(), + flags, + argsString.c_str() ); + CPU_PERFORMANCE_TIMING_START(); + + cl_mem retVal = pIntercept->dispatch().clCreateSubBuffer( + buffer, + flags, + buffer_create_type, + buffer_create_info, + errcode_ret ); + + CPU_PERFORMANCE_TIMING_END(); + ADD_BUFFER( retVal ); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + return dummyDispatch.clCreateSubBuffer( + buffer, + flags, + buffer_create_type, + buffer_create_info, + errcode_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 1.2 +CL_API_ENTRY cl_mem CL_API_CALL CLIRN(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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + if( image_desc && image_format ) + { + CALL_LOGGING_ENTER( + "flags = %s (%llX), " + "format->channel_order = %s, " + "format->channel_data_type = %s, " + "desc->type = %s, " + "desc->width = %d, " + "desc->height = %d, " + "desc->depth = %d, " + "desc->array_size = %d, " + "desc->row_pitch = %d, " + "desc->slice_pitch = %d, " + "desc->num_mip_levels = %d, " + "desc->num_samples = %d, " + "desc->mem_object = %p, " + "host_ptr = %p ", + pIntercept->enumName().name_mem_flags( flags ).c_str(), + flags, + pIntercept->enumName().name( image_format->image_channel_order ).c_str(), + pIntercept->enumName().name( image_format->image_channel_data_type ).c_str(), + pIntercept->enumName().name( image_desc->image_type ).c_str(), + image_desc->image_width, + image_desc->image_height, + image_desc->image_depth, + image_desc->image_array_size, + image_desc->image_row_pitch, + image_desc->image_slice_pitch, + image_desc->num_mip_levels, + image_desc->num_samples, + image_desc->mem_object, + host_ptr ); + } + else + { + CALL_LOGGING_ENTER(); + } + + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_mem retVal = pIntercept->dispatch().clCreateImage( + context, + flags, + image_format, + image_desc, + host_ptr, + errcode_ret ); + + CPU_PERFORMANCE_TIMING_END(); + ADD_IMAGE( retVal ); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + return dummyDispatch.clCreateImage( + context, + flags, + image_format, + image_desc, + host_ptr, + errcode_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_mem CL_API_CALL CLIRN(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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + if( image_format ) + { + CALL_LOGGING_ENTER( + "flags = %s (%llX), " + "format->channel_order = %s, " + "format->channel_data_type = %s, " + "image_width = %d, " + "image_height = %d, " + "image_row_pitch = %d, " + "host_ptr = %p ", + pIntercept->enumName().name_mem_flags( flags ).c_str(), + flags, + pIntercept->enumName().name( image_format->image_channel_order ).c_str(), + pIntercept->enumName().name( image_format->image_channel_data_type ).c_str(), + image_width, + image_height, + image_row_pitch, + host_ptr ); + } + else + { + CALL_LOGGING_ENTER(); + } + + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_mem retVal = pIntercept->dispatch().clCreateImage2D( + context, + flags, + image_format, + image_width, + image_height, + image_row_pitch, + host_ptr, + errcode_ret ); + + CPU_PERFORMANCE_TIMING_END(); + ADD_IMAGE( retVal ); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + return dummyDispatch.clCreateImage2D( + context, + flags, + image_format, + image_width, + image_height, + image_row_pitch, + host_ptr, + errcode_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_mem CL_API_CALL CLIRN(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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + if( image_format ) + { + CALL_LOGGING_ENTER( + "flags = %s (%llX), " + "format->channel_order = %s, " + "format->channel_data_type = %s, " + "image_width = %d, " + "image_height = %d, " + "image_row_pitch = %d, " + "image_slice_pitch = %d, " + "host_ptr = %p ", + pIntercept->enumName().name_mem_flags( flags ).c_str(), + flags, + pIntercept->enumName().name( image_format->image_channel_order ).c_str(), + pIntercept->enumName().name( image_format->image_channel_data_type ).c_str(), + image_width, + image_height, + image_depth, + image_row_pitch, + image_slice_pitch, + host_ptr ); + } + else + { + CALL_LOGGING_ENTER(); + } + + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_mem retVal = pIntercept->dispatch().clCreateImage3D( + context, + flags, + image_format, + image_width, + image_height, + image_depth, + image_row_pitch, + image_slice_pitch, + host_ptr, + errcode_ret ); + + CPU_PERFORMANCE_TIMING_END(); + ADD_IMAGE( retVal ); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + return dummyDispatch.clCreateImage3D( + context, + flags, + image_format, + image_width, + image_height, + image_depth, + image_row_pitch, + image_slice_pitch, + host_ptr, + errcode_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clRetainMemObject)( + cl_mem memobj ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_uint ref_count = 0; + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetMemObjectInfo( + memobj, + CL_MEM_REFERENCE_COUNT, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_ENTER( "[ ref count = %d ] mem = %p", + ref_count, + memobj ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clRetainMemObject( + memobj ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetMemObjectInfo( + memobj, + CL_MEM_REFERENCE_COUNT, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_EXIT( "[ ref count = %d ]", + ref_count ); + + return retVal; + } + else + { + return dummyDispatch.clRetainMemObject( + memobj ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clReleaseMemObject)( + cl_mem memobj ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + REMOVE_MEMOBJ( memobj ); + + cl_uint ref_count = 0; + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetMemObjectInfo( + memobj, + CL_MEM_REFERENCE_COUNT, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_ENTER( "[ ref count = %d ] mem = %p", + ref_count, + memobj ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clReleaseMemObject( + memobj ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + if( pIntercept->callLogging() && ref_count != 0 ) + { + // This isn't strictly correct, but it's pretty close, and it + // avoids crashes in some cases for bad implementations. + --ref_count; + } + CALL_LOGGING_EXIT( "[ ref count = %d ]", + ref_count ); + + return retVal; + } + else + { + return dummyDispatch.clReleaseMemObject( + memobj ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER( "flags = %s (%llX), image_type = %s (%X)", + pIntercept->enumName().name_mem_flags( flags ).c_str(), + flags, + pIntercept->enumName().name( image_type ).c_str(), + image_type ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clGetSupportedImageFormats( + context, + flags, + image_type, + num_entries, + image_formats, + num_image_formats ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clGetSupportedImageFormats( + context, + flags, + image_type, + num_entries, + image_formats, + num_image_formats ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clGetMemObjectInfo)( + cl_mem memobj, + cl_mem_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER( "mem = %p, param_name = %s (%08X)", + memobj, + pIntercept->enumName().name( param_name ).c_str(), + param_name ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clGetMemObjectInfo( + memobj, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clGetMemObjectInfo( + memobj, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clGetImageInfo)( + cl_mem image, + cl_image_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER( "mem = %p, param_name = %s (%08X)", + image, + pIntercept->enumName().name( param_name ).c_str(), + param_name ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clGetImageInfo( + image, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clGetImageInfo( + image, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 1.1 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clSetMemObjectDestructorCallback)( + cl_mem memobj, + void (CL_CALLBACK *pfn_notify)( cl_mem, void* ), + void *user_data ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER(); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clSetMemObjectDestructorCallback( + memobj, + pfn_notify, + user_data ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clSetMemObjectDestructorCallback( + memobj, + pfn_notify, + user_data ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_sampler CL_API_CALL CLIRN(clCreateSampler)( + cl_context context, + cl_bool normalized_coords, + cl_addressing_mode addressing_mode, + cl_filter_mode filter_mode, + cl_int* errcode_ret ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + std::string samplerProperties; + if( pIntercept->callLogging() ) + { + cl_sampler_properties sampler_properties[] = { + CL_SAMPLER_NORMALIZED_COORDS, normalized_coords, + CL_SAMPLER_ADDRESSING_MODE, addressing_mode, + CL_SAMPLER_FILTER_MODE, filter_mode, + 0 + }; + pIntercept->getSamplerPropertiesString( + sampler_properties, + samplerProperties ); + } + + CALL_LOGGING_ENTER( "properties = [ %s ]", + samplerProperties.c_str() ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_sampler retVal = pIntercept->dispatch().clCreateSampler( + context, + normalized_coords, + addressing_mode, + filter_mode, + errcode_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + ADD_SAMPLER(retVal, samplerProperties); + + return retVal; + } + else + { + return dummyDispatch.clCreateSampler( + context, + normalized_coords, + addressing_mode, + filter_mode, + errcode_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clRetainSampler)( + cl_sampler sampler ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_uint ref_count = 0; + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetSamplerInfo( + sampler, + CL_SAMPLER_REFERENCE_COUNT, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_ENTER( "[ ref count = %d ] sampler = %p", + ref_count, + sampler ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clRetainSampler( + sampler ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetSamplerInfo( + sampler, + CL_SAMPLER_REFERENCE_COUNT, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_EXIT( "[ ref count = %d ]", + ref_count ); + + return retVal; + } + else + { + return dummyDispatch.clRetainSampler( + sampler ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clReleaseSampler)( + cl_sampler sampler ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_uint ref_count = 0; + if( pIntercept->callLogging() ) + { + pIntercept->dispatch().clGetSamplerInfo( + sampler, + CL_SAMPLER_REFERENCE_COUNT, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_ENTER( "[ ref count = %d ] sampler = %p", + ref_count, + sampler ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clReleaseSampler( + sampler ); + + if ( --ref_count == 0 ) + { + pIntercept->removeSampler( sampler ); + } + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + if( pIntercept->callLogging() && ref_count != 0 ) + { + // This isn't strictly correct, but it's pretty close, and it + // avoids crashes in some cases for bad implementations. + --ref_count; + } + CALL_LOGGING_EXIT( "[ ref count = %d ]", + ref_count ); + + return retVal; + } + else + { + return dummyDispatch.clReleaseSampler( + sampler ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clGetSamplerInfo)( + cl_sampler sampler, + cl_sampler_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER( "param_name = %s (%08X)", + pIntercept->enumName().name( param_name ).c_str(), + param_name ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clGetSamplerInfo( + sampler, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clGetSamplerInfo( + sampler, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_program CL_API_CALL CLIRN(clCreateProgramWithSource)( + cl_context context, + cl_uint count, + const char** strings, + const size_t* lengths, + cl_int* errcode_ret ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + char* singleString = NULL; + uint64_t hash = 0; + + CREATE_COMBINED_PROGRAM_STRING( count, strings, lengths, singleString, hash ); + INJECT_PROGRAM_SOURCE( count, strings, lengths, singleString, hash ); + PREPEND_PROGRAM_SOURCE( count, strings, lengths, singleString, hash ); + + CALL_LOGGING_ENTER( "context = %p, count = %d", + context, + count ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_program retVal = NULL; + + if( ( retVal == NULL ) && + pIntercept->config().InjectProgramBinaries ) + { + retVal = pIntercept->createProgramWithInjectionBinaries( + hash, + context, + errcode_ret ); + } + + if( ( retVal == NULL ) && + pIntercept->config().InjectProgramSPIRV ) + { + retVal = pIntercept->createProgramWithInjectionSPIRV( + hash, + context, + errcode_ret ); + } + + if( retVal == NULL ) + { + retVal = pIntercept->dispatch().clCreateProgramWithSource( + context, + count, + strings, + lengths, + errcode_ret ); + } + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( errcode_ret[0] ); + SIMD_SURVEY_CREATE_PROGRAM_FROM_SOURCE( + retVal, + context, + count, + strings, + lengths ); + CALL_LOGGING_EXIT( "returned %p, program number = %04d", + retVal, + pIntercept->getProgramNumber() ); + + DUMP_PROGRAM_SOURCE( retVal, singleString, hash ); + SAVE_PROGRAM_HASH( retVal, hash ); + DELETE_COMBINED_PROGRAM_STRING( singleString ); + + return retVal; + } + else + { + return dummyDispatch.clCreateProgramWithSource( + context, + count, + strings, + lengths, + errcode_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_program CL_API_CALL CLIRN(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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + uint64_t hash = 0; + + COMPUTE_BINARY_HASH( num_devices, lengths, binaries, hash ); + + CALL_LOGGING_ENTER( "context = %p, num_devices = %d", + context, + num_devices ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_program retVal = NULL; + + if( pIntercept->config().RejectProgramBinaries ) + { + if( errcode_ret != NULL ) + { + errcode_ret[0] = CL_INVALID_BINARY; + } + } + else + { + retVal = pIntercept->dispatch().clCreateProgramWithBinary( + context, + num_devices, + device_list, + lengths, + binaries, + binary_status, + errcode_ret ); + } + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + DUMP_INPUT_PROGRAM_BINARIES( + retVal, + num_devices, + device_list, + lengths, + binaries, + hash ); + SAVE_PROGRAM_HASH( retVal, hash ); + + return retVal; + } + else + { + return dummyDispatch.clCreateProgramWithBinary( + context, + num_devices, + device_list, + lengths, + binaries, + binary_status, + errcode_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 1.2 +CL_API_ENTRY cl_program CL_API_CALL CLIRN(clCreateProgramWithBuiltInKernels)( + cl_context context, + cl_uint num_devices, + const cl_device_id* device_list, + const char* kernel_names, + cl_int* errcode_ret) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER( "context = %p, num_devices = %d, kernel_names = [ %s ]", + context, + num_devices, + kernel_names ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_program retVal = NULL; + + if( ( retVal == NULL ) && + pIntercept->config().OverrideBuiltinKernels ) + { + retVal = pIntercept->createProgramWithBuiltinKernels( + context ); + } + + if( retVal == NULL ) + { + retVal = pIntercept->dispatch().clCreateProgramWithBuiltInKernels( + context, + num_devices, + device_list, + kernel_names, + errcode_ret); + } + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + return dummyDispatch.clCreateProgramWithBuiltInKernels( + context, + num_devices, + device_list, + kernel_names, + errcode_ret); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clRetainProgram)( + cl_program program ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_uint ref_count = 0; + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetProgramInfo( + program, + CL_PROGRAM_REFERENCE_COUNT, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_ENTER( "[ ref count = %d ] program = %p", + ref_count, + program ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clRetainProgram( + program ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetProgramInfo( + program, + CL_PROGRAM_REFERENCE_COUNT, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_EXIT( "[ ref count = %d ]", + ref_count ); + + return retVal; + } + else + { + return dummyDispatch.clRetainProgram( + program ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clReleaseProgram)( + cl_program program ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_uint ref_count = 0; + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetProgramInfo( + program, + CL_PROGRAM_REFERENCE_COUNT, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_ENTER( "[ ref count = %d ] program = %p", + ref_count, + program ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clReleaseProgram( + program ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + if( pIntercept->callLogging() && ref_count != 0 ) + { + // This isn't strictly correct, but it's pretty close, and it + // avoids crashes in some cases for bad implementations. + --ref_count; + } + CALL_LOGGING_EXIT( "[ ref count = %d ]", + ref_count ); + + return retVal; + } + else + { + return dummyDispatch.clReleaseProgram( + program ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + char* newOptions = NULL; + + MODIFY_PROGRAM_OPTIONS( program, options, newOptions ); + DUMP_PROGRAM_OPTIONS( program, options ); + + CALL_LOGGING_ENTER( "program = %p, pfn_notify = %p", program, pfn_notify ); + BUILD_LOGGING_INIT(); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clBuildProgram( + program, + num_devices, + device_list, + options, + pfn_notify, + user_data ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + BUILD_LOGGING( program, num_devices, device_list ); + SIMD_SURVEY_BUILD_PROGRAM( + program, + num_devices, + device_list, + options ); + CALL_LOGGING_EXIT(); + + DUMP_OUTPUT_PROGRAM_BINARIES( program ); + AUTO_CREATE_SPIRV( program, options ); + INCREMENT_PROGRAM_COMPILE_COUNT( program ); + DELETE_MODIFIED_OPTIONS( newOptions ); + + return retVal; + } + else + { + return dummyDispatch.clBuildProgram( + program, + num_devices, + device_list, + options, + pfn_notify, + user_data ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 1.2 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + const bool modified = false; + + DUMP_PROGRAM_OPTIONS( program, options ); + + CALL_LOGGING_ENTER(); + BUILD_LOGGING_INIT(); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clCompileProgram( + program, + num_devices, + device_list, + options, + num_input_headers, + input_headers, + header_include_names, + pfn_notify, + user_data ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + BUILD_LOGGING( program, num_devices, device_list ); + CALL_LOGGING_EXIT(); + + INCREMENT_PROGRAM_COMPILE_COUNT( program ); + + return retVal; + } + else + { + return dummyDispatch.clCompileProgram( + program, + num_devices, + device_list, + options, + num_input_headers, + input_headers, + header_include_names, + pfn_notify, + user_data ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 1.2 +CL_API_ENTRY cl_program CL_API_CALL CLIRN(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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + const bool modified = false; + + CALL_LOGGING_ENTER(); + CHECK_ERROR_INIT( errcode_ret ); + BUILD_LOGGING_INIT(); + CPU_PERFORMANCE_TIMING_START(); + + cl_program retVal = pIntercept->dispatch().clLinkProgram( + context, + num_devices, + device_list, + options, + num_input_programs, + input_programs, + pfn_notify, + user_data, + errcode_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( errcode_ret[0] ); + BUILD_LOGGING( retVal, num_devices, device_list ); + CALL_LOGGING_EXIT(); + + // TODO: Is the resulting program ("retVal") the one that should be + // used here, to determine the hash for dumped options? + DUMP_PROGRAM_OPTIONS( retVal, options ); + INCREMENT_PROGRAM_COMPILE_COUNT( retVal ); + + return retVal; + } + else + { + return dummyDispatch.clLinkProgram( + context, + num_devices, + device_list, + options, + num_input_programs, + input_programs, + pfn_notify, + user_data, + errcode_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 2.2 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clSetProgramReleaseCallback)( + cl_program program, + void (CL_CALLBACK *pfn_notify)(cl_program program, void* user_data), + void* user_data ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER( "program = %p", program ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clSetProgramReleaseCallback( + program, + pfn_notify, + user_data ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clSetProgramReleaseCallback( + program, + pfn_notify, + user_data ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 2.2 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clSetProgramSpecializationConstant)( + cl_program program, + cl_uint spec_id, + size_t spec_size, + const void* spec_value ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER( "program = %p, spec_id = %u, spec_size = %u", program ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clSetProgramSpecializationConstant( + program, + spec_id, + spec_size, + spec_value ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clSetProgramSpecializationConstant( + program, + spec_id, + spec_size, + spec_value ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 1.2 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clUnloadPlatformCompiler)( + cl_platform_id platform ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER(); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clUnloadPlatformCompiler( + platform ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clUnloadPlatformCompiler( + platform); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clUnloadCompiler)( void ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER(); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clUnloadCompiler(); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clUnloadCompiler(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clGetProgramInfo)( + cl_program program, + cl_program_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER( "param_name = %s (%08X)", + pIntercept->enumName().name( param_name ).c_str(), + param_name ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clGetProgramInfo( + program, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clGetProgramInfo( + program, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER( "param_name = %s (%08X)", + pIntercept->enumName().name( param_name ).c_str(), + param_name ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clGetProgramBuildInfo( + program, + device, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clGetProgramBuildInfo( + program, + device, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_kernel CL_API_CALL CLIRN(clCreateKernel)( + cl_program program, + const char* kernel_name, + cl_int* errcode_ret ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER( "program = %p, kernel_name = %s", + program, + kernel_name ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_kernel retVal = NULL; + + if( ( retVal == NULL ) && + pIntercept->config().OverrideBuiltinKernels ) + { + retVal = pIntercept->createBuiltinKernel( + program, + kernel_name, + errcode_ret ); + } + + if( retVal == NULL ) + { + retVal = pIntercept->dispatch().clCreateKernel( + program, + kernel_name, + errcode_ret ); + } + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( errcode_ret[0] ); + SIMD_SURVEY_CREATE_KERNEL( program, retVal, kernel_name ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + if( retVal != NULL ) + { + pIntercept->addKernelName( + retVal, + kernel_name ); + if( pIntercept->config().PreferredWorkGroupSizeMultipleLogging ) + { + pIntercept->logPreferredWorkGroupSizeMultiple( + &retVal, + 1 ); + } + } + + return retVal; + } + else + { + return dummyDispatch.clCreateKernel( + program, + kernel_name, + errcode_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clCreateKernelsInProgram)( + cl_program program, + cl_uint num_kernels, + cl_kernel* kernels, + cl_uint* num_kernels_ret ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_uint local_num_kernels_ret = 0; + + if( num_kernels_ret == NULL ) + { + num_kernels_ret = &local_num_kernels_ret; + } + + CALL_LOGGING_ENTER( "program = %p", program ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clCreateKernelsInProgram( + program, + num_kernels, + kernels, + num_kernels_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + + std::string retString; + if( pIntercept->callLogging() ) + { + pIntercept->getCreateKernelsInProgramRetString( + retVal, + kernels, + num_kernels_ret, + retString ); + } + CALL_LOGGING_EXIT( "%s", retString.c_str() ); + + if( ( retVal == CL_SUCCESS ) && + ( kernels != NULL ) ) + { + pIntercept->addKernelNames( + kernels, + num_kernels_ret[0] ); + if( pIntercept->config().PreferredWorkGroupSizeMultipleLogging ) + { + pIntercept->logPreferredWorkGroupSizeMultiple( + kernels, + num_kernels_ret[0] ); + } + } + + return retVal; + } + else + { + return dummyDispatch.clCreateKernelsInProgram( + program, + num_kernels, + kernels, + num_kernels_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clRetainKernel)( + cl_kernel kernel ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_uint ref_count = 0; + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetKernelInfo( + kernel, + CL_KERNEL_REFERENCE_COUNT, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_ENTER( "[ ref count = %d ] kernel = %p", + ref_count, + kernel ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clRetainKernel( + kernel ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetKernelInfo( + kernel, + CL_KERNEL_REFERENCE_COUNT, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_EXIT( "[ ref count = %d ]", + ref_count ); + + return retVal; + } + else + { + return dummyDispatch.clRetainKernel( + kernel ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clReleaseKernel)( + cl_kernel kernel ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + pIntercept->removeKernel( kernel ); + + cl_uint ref_count = 0; + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetKernelInfo( + kernel, + CL_KERNEL_REFERENCE_COUNT, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_ENTER( "[ ref count = %d ] kernel = %p", + ref_count, + kernel ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clReleaseKernel( + kernel ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + if( pIntercept->callLogging() && ref_count != 0 ) + { + // This isn't strictly correct, but it's pretty close, and it + // avoids crashes in some cases for bad implementations. + --ref_count; + } + CALL_LOGGING_EXIT( "[ ref count = %d ]", + ref_count ); + + return retVal; + } + else + { + return dummyDispatch.clReleaseKernel( + kernel ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clSetKernelArg)( + cl_kernel kernel, + cl_uint arg_index, + size_t arg_size, + const void* arg_value ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + std::string argsString; + if( pIntercept->callLogging() ) + { + pIntercept->getKernelArgString( + arg_index, + arg_size, + arg_value, + argsString ); + } + CALL_LOGGING_ENTER_KERNEL( + kernel, + "kernel = %p, %s", + kernel, + argsString.c_str() ); + + if ( pIntercept->config().DumpArgumentsOnSet ) + { + pIntercept->dumpArgument( kernel, arg_index, arg_size, arg_value ); + } + + SET_KERNEL_ARG( kernel, arg_index, arg_size, arg_value ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clSetKernelArg( + kernel, + arg_index, + arg_size, + arg_value ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + SIMD_SURVEY_SET_KERNEL_ARG( + kernel, + arg_index, + arg_size, + arg_value ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clSetKernelArg( + kernel, + arg_index, + arg_size, + arg_value ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clGetKernelInfo)( + cl_kernel kernel, + cl_kernel_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER_KERNEL( kernel, "param_name = %s (%X)", + pIntercept->enumName().name( param_name ).c_str(), + param_name ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clGetKernelInfo( + kernel, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clGetKernelInfo( + kernel, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 1.2 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER_KERNEL( kernel, "param_name = %s (%X)", + pIntercept->enumName().name( param_name ).c_str(), + param_name ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clGetKernelArgInfo( + kernel, + arg_indx, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clGetKernelArgInfo( + kernel, + arg_indx, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER_KERNEL( kernel, "param_name = %s (%X)", + pIntercept->enumName().name( param_name ).c_str(), + param_name ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clGetKernelWorkGroupInfo( + kernel, + device, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clGetKernelWorkGroupInfo( + kernel, + device, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clWaitForEvents)( + cl_uint num_events, + const cl_event* event_list ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + if( pIntercept->nullEnqueue() == false ) + { + std::string eventList; + if( pIntercept->callLogging() ) + { + pIntercept->getEventListString( + num_events, + event_list, + eventList ); + } + CALL_LOGGING_ENTER( "event_list = %s", + eventList.c_str() ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clWaitForEvents( + num_events, + event_list ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + DEVICE_PERFORMANCE_TIMING_CHECK(); + } + + return retVal; + } + else + { + return dummyDispatch.clWaitForEvents( + num_events, + event_list ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clGetEventInfo)( + cl_event event, + cl_event_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER( "event = %p, param_name = %s (%08X)", + event, + pIntercept->enumName().name( param_name ).c_str(), + param_name ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clGetEventInfo( + event, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + } + + return retVal; + } + else + { + return dummyDispatch.clGetEventInfo( + event, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 1.1 +CL_API_ENTRY cl_event CL_API_CALL CLIRN(clCreateUserEvent)( + cl_context context, + cl_int *errcode_ret ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_event retVal = NULL; + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clCreateUserEvent( + context, + errcode_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + } + + return retVal; + } + else + { + return dummyDispatch.clCreateUserEvent( + context, + errcode_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clRetainEvent)( + cl_event event ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_uint ref_count = 0; + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetEventInfo( + event, + CL_EVENT_REFERENCE_COUNT, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_ENTER( "[ ref count = %d ] event = %p", + ref_count, + event ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clRetainEvent( + event ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetEventInfo( + event, + CL_EVENT_REFERENCE_COUNT, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_EXIT( "[ ref count = %d ]", + ref_count ); + + return retVal; + } + else + { + return dummyDispatch.clRetainEvent( + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clReleaseEvent)( + cl_event event ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_uint ref_count = 0; + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetEventInfo( + event, + CL_EVENT_REFERENCE_COUNT, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_ENTER( "[ ref count = %d ] event = %p", + ref_count, + event ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clReleaseEvent( + event ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + if( pIntercept->callLogging() && ref_count != 0 ) + { + // This isn't strictly correct, but it's pretty close, and it + // avoids crashes in some cases for bad implementations. + --ref_count; + } + CALL_LOGGING_EXIT( "[ ref count = %d ]", + ref_count ); + + return retVal; + } + else + { + return dummyDispatch.clReleaseEvent( + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 1.1 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clSetUserEventStatus)( + cl_event event, + cl_int execution_status ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER(); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clSetUserEventStatus( + event, + execution_status ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clSetUserEventStatus( + event, + execution_status ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 1.1 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clSetEventCallback)( + cl_event event, + cl_int command_exec_callback_type, + void (CL_CALLBACK *pfn_notify)( cl_event, cl_int, void * ), + void *user_data ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER( "event = %p, callback_type = %s (%d)", + event, + pIntercept->enumName().name_command_exec_status( command_exec_callback_type ).c_str(), + command_exec_callback_type ); + EVENT_CALLBACK_OVERRIDE_INIT( pfn_notify, user_data ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clSetEventCallback( + event, + command_exec_callback_type, + pfn_notify, + user_data ); + + CPU_PERFORMANCE_TIMING_END(); + EVENT_CALLBACK_OVERRIDE_CLEANUP( retVal ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clSetEventCallback( + event, + command_exec_callback_type, + pfn_notify, + user_data ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clGetEventProfilingInfo)( + cl_event event, + cl_profiling_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER( "param_name = %s (%08X)", + pIntercept->enumName().name( param_name ).c_str(), + param_name ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clGetEventProfilingInfo( + event, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + } + + return retVal; + } + else + { + return dummyDispatch.clGetEventProfilingInfo( + event, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clFlush)( + cl_command_queue command_queue ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER( "queue = %p", command_queue ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clFlush( + command_queue ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + DEVICE_PERFORMANCE_TIMING_CHECK(); + + return retVal; + } + else + { + return dummyDispatch.clFlush( + command_queue ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clFinish)( + cl_command_queue command_queue ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER( "queue = %p", command_queue ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clFinish( + command_queue ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + DEVICE_PERFORMANCE_TIMING_CHECK(); + + return retVal; + } + else + { + return dummyDispatch.clFinish( + command_queue ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clEnqueueReadBuffer)( + cl_command_queue command_queue, + cl_mem buffer, + cl_bool blocking_read, + size_t offset, + size_t cb, + void* ptr, + cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, + cl_event* event ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER( + "queue = %p, buffer = %p, %s, offset = %d, cb = %d, ptr = %p", + command_queue, + buffer, + blocking_read ? "blocking" : "non-blocking", + offset, + cb, + ptr ); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + ITT_ADD_PARAM_AS_METADATA( blocking_read ); + + if( pIntercept->config().OverrideReadBuffer ) + { + retVal = pIntercept->ReadBuffer( + command_queue, + buffer, + blocking_read, + offset, + cb, + ptr, + num_events_in_wait_list, + event_wait_list, + event ); + } + else + { + retVal = pIntercept->dispatch().clEnqueueReadBuffer( + command_queue, + buffer, + blocking_read, + offset, + cb, + ptr, + num_events_in_wait_list, + event_wait_list, + event ); + } + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + + if( blocking_read ) + { + DEVICE_PERFORMANCE_TIMING_CHECK(); + } + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueReadBuffer( + command_queue, + buffer, + blocking_read, + offset, + cb, + ptr, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 1.1 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clEnqueueReadBufferRect)( + cl_command_queue command_queue, + cl_mem buffer, + cl_bool blocking_read, + const size_t *buffer_origin, + const size_t *host_origin, + 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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + if( ( buffer_origin != NULL ) && + ( host_origin != NULL ) && + ( region != NULL ) ) + { + CALL_LOGGING_ENTER( + "queue = %p, buffer = %p, %s, buffer_origin = < %d, %d, %d >, host_origin = < %d, %d, %d >, region = < %d, %d, %d >, ptr = %p", + command_queue, + buffer, + blocking_read ? "blocking" : "non-blocking", + buffer_origin[0], buffer_origin[1], buffer_origin[2], + host_origin[0], host_origin[1], host_origin[2], + region[0], region[1], region[2], + ptr ); + } + else + { + CALL_LOGGING_ENTER( + "queue = %p, buffer = %p, %s, ptr = %p", + command_queue, + buffer, + blocking_read ? "blocking" : "non-blocking", + ptr ); + } + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + ITT_ADD_PARAM_AS_METADATA( blocking_read ); + + retVal = pIntercept->dispatch().clEnqueueReadBufferRect( + command_queue, + buffer, + blocking_read, + buffer_origin, + host_origin, + region, + buffer_row_pitch, + buffer_slice_pitch, + host_row_pitch, + host_slice_pitch, + ptr, + num_events_in_wait_list, + event_wait_list, + event ); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + + if( blocking_read ) + { + DEVICE_PERFORMANCE_TIMING_CHECK(); + } + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueReadBufferRect( + command_queue, + buffer, + blocking_read, + buffer_origin, + host_origin, + region, + buffer_row_pitch, + buffer_slice_pitch, + host_row_pitch, + host_slice_pitch, + ptr, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clEnqueueWriteBuffer)( + cl_command_queue command_queue, + cl_mem buffer, + cl_bool blocking_write, + size_t offset, + size_t cb, + const void* ptr, + cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, + cl_event* event ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER( + "queue = %p, buffer = %p, %s, offset = %d, cb = %d, ptr = %p", + command_queue, + buffer, + blocking_write ? "blocking" : "non-blocking", + offset, + cb, + ptr ); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + ITT_ADD_PARAM_AS_METADATA( blocking_write ); + + if( pIntercept->config().OverrideWriteBuffer ) + { + retVal = pIntercept->WriteBuffer( + command_queue, + buffer, + blocking_write, + offset, + cb, + ptr, + num_events_in_wait_list, + event_wait_list, + event ); + } + else + { + retVal = pIntercept->dispatch().clEnqueueWriteBuffer( + command_queue, + buffer, + blocking_write, + offset, + cb, + ptr, + num_events_in_wait_list, + event_wait_list, + event ); + } + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + + if( blocking_write ) + { + DEVICE_PERFORMANCE_TIMING_CHECK(); + } + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueWriteBuffer( + command_queue, + buffer, + blocking_write, + offset, + cb, + ptr, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 1.1 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clEnqueueWriteBufferRect)( + cl_command_queue command_queue, + cl_mem buffer, + cl_bool blocking_write, + const size_t *buffer_origin, + const size_t *host_origin, + 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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + if( ( buffer_origin != NULL ) && + ( host_origin != NULL ) && + ( region != NULL ) ) + { + CALL_LOGGING_ENTER( + "queue = %p, buffer = %p, %s, buffer_origin = < %d, %d, %d >, host_origin = < %d, %d, %d >, region = < %d, %d, %d >, ptr = %p", + command_queue, + buffer, + blocking_write ? "blocking" : "non-blocking", + buffer_origin[0], buffer_origin[1], buffer_origin[2], + host_origin[0], host_origin[1], host_origin[2], + region[0], region[1], region[2], + ptr ); + } + else + { + CALL_LOGGING_ENTER( + "queue = %p, buffer = %p, %s, ptr = %p", + command_queue, + buffer, + blocking_write ? "blocking" : "non-blocking", + ptr ); + } + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + ITT_ADD_PARAM_AS_METADATA( blocking_write ); + + retVal = pIntercept->dispatch().clEnqueueWriteBufferRect( + command_queue, + buffer, + blocking_write, + buffer_origin, + host_origin, + region, + buffer_row_pitch, + buffer_slice_pitch, + host_row_pitch, + host_slice_pitch, + ptr, + num_events_in_wait_list, + event_wait_list, + event ); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + + if( blocking_write ) + { + DEVICE_PERFORMANCE_TIMING_CHECK(); + } + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueWriteBufferRect( + command_queue, + buffer, + blocking_write, + buffer_origin, + host_origin, + region, + buffer_row_pitch, + buffer_slice_pitch, + host_row_pitch, + host_slice_pitch, + ptr, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 1.2 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueFillBuffer( + command_queue, + buffer, + pattern, + pattern_size, + offset, + size, + num_events_in_wait_list, + event_wait_list, + event ); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueFillBuffer( + command_queue, + buffer, + pattern, + pattern_size, + offset, + size, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clEnqueueCopyBuffer)( + cl_command_queue command_queue, + cl_mem src_buffer, + cl_mem dst_buffer, + size_t src_offset, + size_t dst_offset, + size_t cb, + cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, + cl_event* event ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER("queue = %p, src_buffer = %p, dst_buffer = %p, src_offset = %u, dst_offset = %u, cb = %d", + command_queue, + src_buffer, + dst_buffer, + src_offset, + dst_offset, + cb ); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + if( pIntercept->config().OverrideCopyBuffer ) + { + retVal = pIntercept->CopyBuffer( + command_queue, + src_buffer, + dst_buffer, + src_offset, + dst_offset, + cb, + num_events_in_wait_list, + event_wait_list, + event ); + } + else + { + retVal = pIntercept->dispatch().clEnqueueCopyBuffer( + command_queue, + src_buffer, + dst_buffer, + src_offset, + dst_offset, + cb, + num_events_in_wait_list, + event_wait_list, + event ); + } + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueCopyBuffer( + command_queue, + src_buffer, + dst_buffer, + src_offset, + dst_offset, + cb, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 1.1 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + if( ( src_origin != NULL ) && + ( dst_origin != NULL ) && + ( region != NULL ) ) + { + CALL_LOGGING_ENTER( + "queue = %p, src_buffer = %p, dst_buffer = %p, src_origin = < %d, %d, %d >, dst_origin = < %d, %d, %d >, region = < %d, %d, %d >", + command_queue, + src_buffer, + dst_buffer, + src_origin[0], src_origin[1], src_origin[2], + dst_origin[0], dst_origin[1], dst_origin[2], + region[0], region[1], region[2] ); + } + else + { + CALL_LOGGING_ENTER( + "queue = %p, src_buffer = %p, dst_buffer = %p", + command_queue, + src_buffer, + dst_buffer ); + } + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueCopyBufferRect( + command_queue, + src_buffer, + dst_buffer, + src_origin, + dst_origin, + region, + src_row_pitch, + src_slice_pitch, + dst_row_pitch, + dst_slice_pitch, + num_events_in_wait_list, + event_wait_list, + event ); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueCopyBufferRect( + command_queue, + src_buffer, + dst_buffer, + src_origin, + dst_origin, + region, + src_row_pitch, + src_slice_pitch, + dst_row_pitch, + dst_slice_pitch, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clEnqueueReadImage)( + cl_command_queue command_queue, + cl_mem image, + cl_bool blocking_read, + const size_t* origin, + const size_t* region, + 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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + if( ( origin != NULL ) && + ( region != NULL ) ) + { + CALL_LOGGING_ENTER( + "queue = %p, image = %p, %s, origin = < %d, %d, %d >, region = < %d, %d, %d >, ptr = %p", + command_queue, + image, + blocking_read ? "blocking" : "non-blocking", + origin[0], origin[1], origin[2], + region[0], region[1], region[2], + ptr ); + } + else + { + CALL_LOGGING_ENTER( + "queue = %p, image = %p, %s, ptr = %p", + command_queue, + image, + blocking_read ? "blocking" : "non-blocking", + ptr ); + } + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + ITT_ADD_PARAM_AS_METADATA( blocking_read ); + + if( pIntercept->config().OverrideReadImage ) + { + retVal = pIntercept->ReadImage( + command_queue, + image, + blocking_read, + origin, + region, + row_pitch, + slice_pitch, + ptr, + num_events_in_wait_list, + event_wait_list, + event ); + } + else + { + retVal = pIntercept->dispatch().clEnqueueReadImage( + command_queue, + image, + blocking_read, + origin, + region, + row_pitch, + slice_pitch, + ptr, + num_events_in_wait_list, + event_wait_list, + event ); + } + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + + if( blocking_read ) + { + DEVICE_PERFORMANCE_TIMING_CHECK(); + } + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueReadImage( + command_queue, + image, + blocking_read, + origin, + region, + row_pitch, + slice_pitch, + ptr, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clEnqueueWriteImage)( + cl_command_queue command_queue, + cl_mem image, + cl_bool blocking_write, + const size_t* origin, + const size_t* region, + 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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER( + "%s, ptr = %p", + blocking_write ? "blocking" : "non-blocking", + ptr ); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + ITT_ADD_PARAM_AS_METADATA( blocking_write ); + + if( pIntercept->config().OverrideWriteImage ) + { + retVal = pIntercept->WriteImage( + command_queue, + image, + blocking_write, + origin, + region, + input_row_pitch, + input_slice_pitch, + ptr, + num_events_in_wait_list, + event_wait_list, + event ); + } + else + { + retVal = pIntercept->dispatch().clEnqueueWriteImage( + command_queue, + image, + blocking_write, + origin, + region, + input_row_pitch, + input_slice_pitch, + ptr, + num_events_in_wait_list, + event_wait_list, + event ); + } + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + + if( blocking_write ) + { + DEVICE_PERFORMANCE_TIMING_CHECK(); + } + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueWriteImage( + command_queue, + image, + blocking_write, + origin, + region, + input_row_pitch, + input_slice_pitch, + ptr, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 1.2 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clEnqueueFillImage)( + cl_command_queue command_queue, + cl_mem image, + const void* fill_color, + const size_t* origin, + const size_t* region, + cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, + cl_event* event ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueFillImage( + command_queue, + image, + fill_color, + origin, + region, + num_events_in_wait_list, + event_wait_list, + event ); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueFillImage( + command_queue, + image, + fill_color, + origin, + region, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clEnqueueCopyImage)( + cl_command_queue command_queue, + cl_mem src_image, + cl_mem dst_image, + const size_t* src_origin, + const size_t* dst_origin, + const size_t* region, + cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, + cl_event* event ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + if( pIntercept->config().OverrideCopyImage ) + { + retVal = pIntercept->CopyImage( + command_queue, + src_image, + dst_image, + src_origin, + dst_origin, + region, + num_events_in_wait_list, + event_wait_list, + event ); + } + else + { + retVal = pIntercept->dispatch().clEnqueueCopyImage( + command_queue, + src_image, + dst_image, + src_origin, + dst_origin, + region, + num_events_in_wait_list, + event_wait_list, + event ); + } + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueCopyImage( + command_queue, + src_image, + dst_image, + src_origin, + dst_origin, + region, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clEnqueueCopyImageToBuffer)( + cl_command_queue command_queue, + cl_mem src_image, + cl_mem dst_buffer, + const size_t* src_origin, + const size_t* region, + size_t dst_offset, + cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, + cl_event* event ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueCopyImageToBuffer( + command_queue, + src_image, + dst_buffer, + src_origin, + region, + dst_offset, + num_events_in_wait_list, + event_wait_list, + event ); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueCopyImageToBuffer( + command_queue, + src_image, + dst_buffer, + src_origin, + region, + dst_offset, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clEnqueueCopyBufferToImage)( + cl_command_queue command_queue, + cl_mem src_buffer, + cl_mem dst_image, + size_t src_offset, + const size_t* dst_origin, + const size_t* region, + cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, + cl_event* event ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueCopyBufferToImage( + command_queue, + src_buffer, + dst_image, + src_offset, + dst_origin, + region, + num_events_in_wait_list, + event_wait_list, + event ); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueCopyBufferToImage( + command_queue, + src_buffer, + dst_image, + src_offset, + dst_origin, + region, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY void* CL_API_CALL CLIRN(clEnqueueMapBuffer)( + cl_command_queue command_queue, + cl_mem buffer, + cl_bool blocking_map, + cl_map_flags map_flags, + size_t offset, + size_t cb, + cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, + cl_event* event, + cl_int* errcode_ret ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + void* retVal = NULL; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + cl_uint map_count = 0; + std::string eventWaitListString; + if( pIntercept->callLogging() ) + { + map_count = 0; + pIntercept->dispatch().clGetMemObjectInfo( + buffer, + CL_MEM_MAP_COUNT, + sizeof( map_count ), + &map_count, + NULL ); + if( num_events_in_wait_list ) + { + std::string eventString; + pIntercept->getEventListString( + num_events_in_wait_list, + event_wait_list, + eventString ); + eventWaitListString += ", event_wait_list = "; + eventWaitListString += eventString; + } + } + CALL_LOGGING_ENTER( + "[ map count = %d ] queue = %p, buffer = %p, %s, map_flags = %s (%llX), offset = %d, cb = %d%s", + map_count, + command_queue, + buffer, + blocking_map ? "blocking" : "non-blocking", + pIntercept->enumName().name_map_flags( map_flags ).c_str(), + map_flags, + offset, + cb, + eventWaitListString.c_str() ); + DEVICE_PERFORMANCE_TIMING_START( event ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + ITT_ADD_PARAM_AS_METADATA( blocking_map ); + + retVal = pIntercept->dispatch().clEnqueueMapBuffer( + command_queue, + buffer, + blocking_map, + map_flags, + offset, + cb, + num_events_in_wait_list, + event_wait_list, + event, + errcode_ret ); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + DUMP_BUFFER_AFTER_MAP( command_queue, buffer, blocking_map, map_flags, retVal, offset, cb ); + CHECK_ERROR( errcode_ret[0] ); + if( pIntercept->callLogging() ) + { + map_count = 0; + pIntercept->dispatch().clGetMemObjectInfo( + buffer, + CL_MEM_MAP_COUNT, + sizeof( map_count ), + &map_count, + NULL ); + } + CALL_LOGGING_EXIT_EVENT(event, "[ map count = %d ] returned %p", + map_count, + retVal ); + + if( blocking_map ) + { + DEVICE_PERFORMANCE_TIMING_CHECK(); + } + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueMapBuffer( + command_queue, + buffer, + blocking_map, + map_flags, + offset, + cb, + num_events_in_wait_list, + event_wait_list, + event, + errcode_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY void* CL_API_CALL CLIRN(clEnqueueMapImage)( + cl_command_queue command_queue, + cl_mem image, + cl_bool blocking_map, + cl_map_flags map_flags, + const size_t* origin, + const size_t* region, + 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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + void* retVal = NULL; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + cl_uint map_count = 0; + if( pIntercept->callLogging() ) + { + map_count = 0; + pIntercept->dispatch().clGetMemObjectInfo( + image, + CL_MEM_MAP_COUNT, + sizeof( map_count ), + &map_count, + NULL ); + } + if( ( origin != NULL ) && + ( region != NULL ) ) + { + CALL_LOGGING_ENTER( + "[ map count = %d ] queue = %p, image = %p, %s, map_flags = %s (%llX), origin = < %d, %d, %d >, region = < %d, %d, %d >", + map_count, + command_queue, + image, + blocking_map ? "blocking" : "non-blocking", + pIntercept->enumName().name_map_flags( map_flags ).c_str(), + map_flags, + origin[0], origin[1], origin[2], + region[0], region[1], region[2] ); + } + else + { + CALL_LOGGING_ENTER( + "[ map count = %d ] queue = %p, image = %p, %s, map_flags = %s (%llX)", + map_count, + command_queue, + image, + blocking_map ? "blocking" : "non-blocking", + pIntercept->enumName().name_map_flags( map_flags ).c_str(), + map_flags ); + } + DEVICE_PERFORMANCE_TIMING_START( event ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + ITT_ADD_PARAM_AS_METADATA( blocking_map ); + + retVal = pIntercept->dispatch().clEnqueueMapImage( + command_queue, + image, + blocking_map, + map_flags, + origin, + region, + image_row_pitch, + image_slice_pitch, + num_events_in_wait_list, + event_wait_list, + event, + errcode_ret ); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( errcode_ret[0] ); + if( pIntercept->callLogging() ) + { + map_count = 0; + pIntercept->dispatch().clGetMemObjectInfo( + image, + CL_MEM_MAP_COUNT, + sizeof( map_count ), + &map_count, + NULL ); + } + CALL_LOGGING_EXIT_EVENT(event, "[ map count = %d ] returned %p", + map_count, + retVal ); + + if( blocking_map ) + { + DEVICE_PERFORMANCE_TIMING_CHECK(); + } + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueMapImage( + command_queue, + image, + blocking_map, + map_flags, + origin, + region, + image_row_pitch, + image_slice_pitch, + num_events_in_wait_list, + event_wait_list, + event, + errcode_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + DUMP_BUFFER_BEFORE_UNMAP( memobj, command_queue ); + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + cl_uint map_count = 0; + std::string eventWaitListString; + if( pIntercept->callLogging() ) + { + map_count = 0; + pIntercept->dispatch().clGetMemObjectInfo( + memobj, + CL_MEM_MAP_COUNT, + sizeof( map_count ), + &map_count, + NULL ); + if( num_events_in_wait_list ) + { + std::string eventString; + pIntercept->getEventListString( + num_events_in_wait_list, + event_wait_list, + eventString ); + eventWaitListString += ", event_wait_list = "; + eventWaitListString += eventString; + } + } + CALL_LOGGING_ENTER( + "[ map count = %d ] queue = %p, memobj = %p, mapped_ptr = %p%s", + map_count, + command_queue, + memobj, + mapped_ptr, + eventWaitListString.c_str() ); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueUnmapMemObject( + command_queue, + memobj, + mapped_ptr, + num_events_in_wait_list, + event_wait_list, + event ); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + if( pIntercept->callLogging() ) + { + map_count = 0; + pIntercept->dispatch().clGetMemObjectInfo( + memobj, + CL_MEM_MAP_COUNT, + sizeof( map_count ), + &map_count, + NULL ); + } + CALL_LOGGING_EXIT_EVENT(event, "[ map count = %d ]", + map_count ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueUnmapMemObject( + command_queue, + memobj, + mapped_ptr, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 1.2 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueMigrateMemObjects( + command_queue, + num_mem_objects, + mem_objects, + flags, + num_events_in_wait_list, + event_wait_list, + event ); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueMigrateMemObjects( + command_queue, + num_mem_objects, + mem_objects, + flags, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + DUMP_BUFFERS_BEFORE_ENQUEUE( kernel, command_queue ); + DUMP_IMAGES_BEFORE_ENQUEUE( kernel, command_queue ); + CHECK_AUBCAPTURE_START_KERNEL( + kernel, + work_dim, + global_work_size, + local_work_size, + command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + if( pIntercept->config().NullLocalWorkSize ) + { + local_work_size = NULL; + } + pIntercept->overrideNullLocalWorkSize( + work_dim, + global_work_size, + local_work_size ); + + std::string argsString; + if( pIntercept->callLogging() ) + { + pIntercept->getEnqueueNDRangeKernelArgsString( + work_dim, + global_work_offset, + global_work_size, + local_work_size, + argsString ); + if( num_events_in_wait_list ) + { + std::string eventString; + pIntercept->getEventListString( + num_events_in_wait_list, + event_wait_list, + eventString ); + argsString += ", event_wait_list = "; + argsString += eventString; + } + } + CALL_LOGGING_ENTER_KERNEL( + kernel, + "queue = %p, kernel = %p, %s", + command_queue, + kernel, + argsString.c_str() ); + + DEVICE_PERFORMANCE_TIMING_START( event ); + SIMD_SURVEY_NDRANGE_KERNEL(kernel); + CPU_PERFORMANCE_TIMING_START(); + +// ITT_ADD_PARAM_AS_METADATA(command_queue); +// ITT_ADD_PARAM_AS_METADATA(kernel); + ITT_ADD_PARAM_AS_METADATA(work_dim); + ITT_ADD_ARRAY_PARAM_AS_METADATA(work_dim, global_work_offset); + ITT_ADD_ARRAY_PARAM_AS_METADATA(work_dim, global_work_size); + ITT_ADD_ARRAY_PARAM_AS_METADATA(work_dim, local_work_size); + ITT_ADD_ARRAY_PARAM_AS_METADATA(num_events_in_wait_list, event_wait_list); + + retVal = CL_INVALID_OPERATION; + + if( ( retVal != CL_SUCCESS ) && + pIntercept->config().OverrideBuiltinKernels ) + { + + retVal = pIntercept->NDRangeBuiltinKernel( + command_queue, + kernel, + work_dim, + global_work_offset, + global_work_size, + local_work_size, + num_events_in_wait_list, + event_wait_list, + event ); + } + + if( retVal != CL_SUCCESS ) + { + retVal = pIntercept->dispatch().clEnqueueNDRangeKernel( + command_queue, + kernel, + work_dim, + global_work_offset, + global_work_size, + local_work_size, + num_events_in_wait_list, + event_wait_list, + event ); + } + + CPU_PERFORMANCE_TIMING_END_KERNEL(kernel); + DEVICE_PERFORMANCE_TIMING_END_KERNEL(event, kernel, work_dim, global_work_size, local_work_size); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + DUMP_BUFFERS_AFTER_ENQUEUE( kernel, command_queue ); + DUMP_IMAGES_AFTER_ENQUEUE( kernel, command_queue ); + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueNDRangeKernel( + command_queue, + kernel, + work_dim, + global_work_offset, + global_work_size, + local_work_size, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START_KERNEL( kernel, 0, NULL, NULL, command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER_KERNEL( kernel ); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueTask( + command_queue, + kernel, + num_events_in_wait_list, + event_wait_list, + event ); + + CPU_PERFORMANCE_TIMING_END_KERNEL(kernel); + DEVICE_PERFORMANCE_TIMING_END_KERNEL(event, kernel, 0, NULL, NULL); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueTask( + command_queue, + kernel, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueNativeKernel( + command_queue, + user_func, + args, + cb_args, + num_mem_objects, + mem_list, + args_mem_loc, + num_events_in_wait_list, + event_wait_list, + event ); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueNativeKernel( + command_queue, + user_func, + args, + cb_args, + num_mem_objects, + mem_list, + args_mem_loc, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clEnqueueMarker)( + cl_command_queue command_queue, + cl_event* event ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER( "queue = %p", + command_queue ); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueMarker( + command_queue, + event ); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueMarker( + command_queue, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clEnqueueWaitForEvents)( + cl_command_queue command_queue, + cl_uint num_events, + const cl_event* event_list ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + std::string eventWaitListString; + if( pIntercept->callLogging() && + num_events ) + { + std::string eventString; + pIntercept->getEventListString( + num_events, + event_list, + eventString ); + eventWaitListString += ", event_list = "; + eventWaitListString += eventString; + } + CALL_LOGGING_ENTER( "queue = %p%s", + command_queue, + eventWaitListString.c_str() ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueWaitForEvents( + command_queue, + num_events, + event_list ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + DEVICE_PERFORMANCE_TIMING_CHECK(); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueWaitForEvents( + command_queue, + num_events, + event_list ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clEnqueueBarrier)( + cl_command_queue command_queue ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER( "queue = %p", + command_queue ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueBarrier( + command_queue ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + FLUSH_AFTER_ENQUEUE_BARRIER( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueBarrier( + command_queue ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 1.2 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clEnqueueMarkerWithWaitList)( + cl_command_queue command_queue, + cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, + cl_event* event ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + std::string eventWaitListString; + if( pIntercept->callLogging() && + num_events_in_wait_list ) + { + std::string eventString; + pIntercept->getEventListString( + num_events_in_wait_list, + event_wait_list, + eventString ); + eventWaitListString += ", event_wait_list = "; + eventWaitListString += eventString; + } + CALL_LOGGING_ENTER( "queue = %p%s", + command_queue, + eventWaitListString.c_str() ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueMarkerWithWaitList( + command_queue, + num_events_in_wait_list, + event_wait_list, + event ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueMarkerWithWaitList( + command_queue, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 1.2 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clEnqueueBarrierWithWaitList)( + cl_command_queue command_queue, + cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, + cl_event* event ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + std::string eventWaitListString; + if( pIntercept->callLogging() && + num_events_in_wait_list ) + { + std::string eventString; + pIntercept->getEventListString( + num_events_in_wait_list, + event_wait_list, + eventString ); + eventWaitListString += ", event_wait_list = "; + eventWaitListString += eventString; + } + CALL_LOGGING_ENTER( "queue = %p%s", + command_queue, + eventWaitListString.c_str() ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueBarrierWithWaitList( + command_queue, + num_events_in_wait_list, + event_wait_list, + event ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + FLUSH_AFTER_ENQUEUE_BARRIER( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueBarrierWithWaitList( + command_queue, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Optional? +CL_API_ENTRY void* CL_API_CALL CLIRN(clGetExtensionFunctionAddress)( + const char* func_name ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clGetExtensionFunctionAddress ) + { + CALL_LOGGING_ENTER( "func_name = %s", func_name ); + CPU_PERFORMANCE_TIMING_START(); + + // First, check to see if this is an extension we know about. + void* retVal = pIntercept->getExtensionFunctionAddress( + NULL, + func_name ); + + // If it's not, call into the dispatch table as usual. + if( retVal == NULL ) + { + retVal = pIntercept->dispatch().clGetExtensionFunctionAddress( + func_name ); + } + + CPU_PERFORMANCE_TIMING_END(); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + return dummyDispatch.clGetExtensionFunctionAddress( + func_name ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Optional? +// OpenCL 1.2 +CL_API_ENTRY void* CL_API_CALL CLIRN(clGetExtensionFunctionAddressForPlatform)( + cl_platform_id platform, + const char* func_name ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clGetExtensionFunctionAddressForPlatform ) + { + std::string platformInfo; + if( pIntercept->callLogging() ) + { + pIntercept->getPlatformInfoString( + platform, + platformInfo ); + } + CALL_LOGGING_ENTER( "platform = [ %s ], func_name = %s", + platformInfo.c_str(), + func_name ); + CPU_PERFORMANCE_TIMING_START(); + + // First, check to see if this is an extension we know about. + void* retVal = pIntercept->getExtensionFunctionAddress( + platform, + func_name ); + + // If it's not, call into the dispatch table as usual. + if( retVal == NULL ) + { + retVal = pIntercept->dispatch().clGetExtensionFunctionAddressForPlatform( + platform, + func_name ); + } + + CPU_PERFORMANCE_TIMING_END(); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + return dummyDispatch.clGetExtensionFunctionAddressForPlatform( + platform, + func_name ); + } +} + +// CL-GL Sharing + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_mem CL_API_CALL CLIRN(clCreateFromGLBuffer)( + cl_context context, + cl_mem_flags flags, + cl_GLuint bufobj, + int* errcode_ret) // Not cl_int*? +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clCreateFromGLBuffer ) + { + CALL_LOGGING_ENTER( + "flags = %s (%llX)", + pIntercept->enumName().name_mem_flags( flags ).c_str(), + flags ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_mem retVal = pIntercept->dispatch().clCreateFromGLBuffer( + context, + flags, + bufobj, + errcode_ret); + + CPU_PERFORMANCE_TIMING_END(); + ADD_BUFFER( retVal ); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + return dummyDispatch.clCreateFromGLBuffer( + context, + flags, + bufobj, + errcode_ret); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Optional? +// OpenCL 1.2 +CL_API_ENTRY cl_mem CL_API_CALL CLIRN(clCreateFromGLTexture)( + cl_context context, + cl_mem_flags flags, + cl_GLenum target, + cl_GLint miplevel, + cl_GLuint texture, + cl_int* errcode_ret ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clCreateFromGLTexture ) + { + CALL_LOGGING_ENTER( + "flags = %s (%llX), " + "texture_target = %s (%d), " + "miplevel = %d, " + "texture = %d", + pIntercept->enumName().name_mem_flags( flags ).c_str(), + flags, + pIntercept->enumName().name_gl( target ).c_str(), + target, + miplevel, + texture ); + + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_mem retVal = pIntercept->dispatch().clCreateFromGLTexture( + context, + flags, + target, + miplevel, + texture, + errcode_ret); + + CPU_PERFORMANCE_TIMING_END(); + ADD_IMAGE( retVal ); + CHECK_ERROR( errcode_ret[0] ); + + pIntercept->logCL_GLTextureDetails( retVal, target, miplevel, texture ); + + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + return dummyDispatch.clCreateFromGLTexture( + context, + flags, + target, + miplevel, + texture, + errcode_ret); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_mem CL_API_CALL CLIRN(clCreateFromGLTexture2D)( + cl_context context, + cl_mem_flags flags, + cl_GLenum target, + cl_GLint miplevel, + cl_GLuint texture, + cl_int* errcode_ret) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clCreateFromGLTexture2D ) + { + CALL_LOGGING_ENTER( + "flags = %s (%llX), " + "texture_target = %s (%d), " + "miplevel = %d, " + "texture = %d", + pIntercept->enumName().name_mem_flags( flags ).c_str(), + flags, + pIntercept->enumName().name_gl( target ).c_str(), + target, + miplevel, + texture ); + + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_mem retVal = pIntercept->dispatch().clCreateFromGLTexture2D( + context, + flags, + target, + miplevel, + texture, + errcode_ret); + + CPU_PERFORMANCE_TIMING_END(); + ADD_IMAGE( retVal ); + CHECK_ERROR( errcode_ret[0] ); + + pIntercept->logCL_GLTextureDetails( retVal, target, miplevel, texture ); + + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + return dummyDispatch.clCreateFromGLTexture2D( + context, + flags, + target, + miplevel, + texture, + errcode_ret); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_mem CL_API_CALL CLIRN(clCreateFromGLTexture3D)( + cl_context context, + cl_mem_flags flags, + cl_GLenum target, + cl_GLint miplevel, + cl_GLuint texture, + cl_int* errcode_ret) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clCreateFromGLTexture3D ) + { + CALL_LOGGING_ENTER( + "flags = %s (%llX), " + "texture_target = %s (%d), " + "miplevel = %d, " + "texture = %d", + pIntercept->enumName().name_mem_flags( flags ).c_str(), + flags, + pIntercept->enumName().name_gl( target ).c_str(), + target, + miplevel, + texture ); + + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_mem retVal = pIntercept->dispatch().clCreateFromGLTexture3D( + context, + flags, + target, + miplevel, + texture, + errcode_ret); + + CPU_PERFORMANCE_TIMING_END(); + ADD_IMAGE( retVal ); + CHECK_ERROR( errcode_ret[0] ); + + pIntercept->logCL_GLTextureDetails( retVal, target, miplevel, texture ); + + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + return dummyDispatch.clCreateFromGLTexture3D( + context, + flags, + target, + miplevel, + texture, + errcode_ret); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_mem CL_API_CALL CLIRN(clCreateFromGLRenderbuffer)( + cl_context context, + cl_mem_flags flags, + cl_GLuint renderbuffer, + cl_int* errcode_ret) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clCreateFromGLRenderbuffer ) + { + CALL_LOGGING_ENTER( + "flags = %s (%llX)", + pIntercept->enumName().name_mem_flags( flags ).c_str(), + flags ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_mem retVal = pIntercept->dispatch().clCreateFromGLRenderbuffer( + context, + flags, + renderbuffer, + errcode_ret); + + CPU_PERFORMANCE_TIMING_END(); + ADD_IMAGE( retVal ); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + return dummyDispatch.clCreateFromGLRenderbuffer( + context, + flags, + renderbuffer, + errcode_ret); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clGetGLObjectInfo)( + cl_mem memobj, + cl_gl_object_type* gl_object_type, + cl_GLuint* gl_object_name) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clGetGLObjectInfo ) + { + CALL_LOGGING_ENTER(); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clGetGLObjectInfo( + memobj, + gl_object_type, + gl_object_name); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clGetGLObjectInfo( + memobj, + gl_object_type, + gl_object_name); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clGetGLTextureInfo)( + cl_mem memobj, + cl_gl_texture_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clGetGLTextureInfo ) + { + CALL_LOGGING_ENTER(); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clGetGLTextureInfo( + memobj, + param_name, + param_value_size, + param_value, + param_value_size_ret); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clGetGLTextureInfo( + memobj, + param_name, + param_value_size, + param_value, + param_value_size_ret); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(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) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clEnqueueAcquireGLObjects ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueAcquireGLObjects( + command_queue, + num_objects, + mem_objects, + num_events_in_wait_list, + event_wait_list, + event); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueAcquireGLObjects( + command_queue, + num_objects, + mem_objects, + num_events_in_wait_list, + event_wait_list, + event); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +CL_API_ENTRY cl_int CL_API_CALL CLIRN(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) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clEnqueueReleaseGLObjects ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueReleaseGLObjects( + command_queue, + num_objects, + mem_objects, + num_events_in_wait_list, + event_wait_list, + event); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + DEVICE_PERFORMANCE_TIMING_CHECK(); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueReleaseGLObjects( + command_queue, + num_objects, + mem_objects, + num_events_in_wait_list, + event_wait_list, + event); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 2.0 +CL_API_ENTRY void* CL_API_CALL CLIRN(clSVMAlloc) ( + cl_context context, + cl_svm_mem_flags flags, + size_t size, + cl_uint alignment) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER( "flags = %s (%llX), size = %d, alignment = %d", + pIntercept->enumName().name_svm_mem_flags( flags ).c_str(), + flags, + size, + alignment ); + CPU_PERFORMANCE_TIMING_START(); + + void* retVal = pIntercept->dispatch().clSVMAlloc( + context, + flags, + size, + alignment ); + + CPU_PERFORMANCE_TIMING_END(); + ADD_SVM_ALLOCATION( retVal, size ); + // There is no error code returned from clSVMAlloc(), so strictly + // speaking we have no error to "check" here. Still, we'll invent + // one if clSVMAlloc() returned NULL, so something will get logged + // if ErrorLogging is enabled. + cl_int errorCode = ( retVal != NULL ) ? CL_SUCCESS : CL_INVALID_OPERATION; + CHECK_ERROR( errorCode ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + return dummyDispatch.clSVMAlloc( + context, + flags, + size, + alignment ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 2.0 +CL_API_ENTRY void CL_API_CALL CLIRN(clSVMFree) ( + cl_context context, + void* svm_pointer) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER( "svm_pointer = %p", + svm_pointer ); + CPU_PERFORMANCE_TIMING_START(); + + pIntercept->dispatch().clSVMFree( + context, + svm_pointer ); + + CPU_PERFORMANCE_TIMING_END(); + REMOVE_SVM_ALLOCATION( svm_pointer ); + CALL_LOGGING_EXIT(); + } + else + { + dummyDispatch.clSVMFree( + context, + svm_pointer ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 2.0 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(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) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueSVMFree( + command_queue, + num_svm_pointers, + svm_pointers, + pfn_free_func, + user_data, + num_events_in_wait_list, + event_wait_list, + event ); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueSVMFree( + command_queue, + num_svm_pointers, + svm_pointers, + pfn_free_func, + user_data, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 2.0 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(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) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueSVMMemcpy( + command_queue, + blocking_copy, + dst_ptr, + src_ptr, + size, + num_events_in_wait_list, + event_wait_list, + event ); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueSVMMemcpy( + command_queue, + blocking_copy, + dst_ptr, + src_ptr, + size, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 2.0 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(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) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueSVMMemFill( + command_queue, + svm_ptr, + pattern, + pattern_size, + size, + num_events_in_wait_list, + event_wait_list, + event ); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueSVMMemFill( + command_queue, + svm_ptr, + pattern, + pattern_size, + size, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 2.0 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clEnqueueSVMMap) ( + cl_command_queue command_queue, + cl_bool blocking_map, + cl_map_flags map_flags, + void* svm_ptr, + size_t size, + cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, + cl_event* event) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueSVMMap( + command_queue, + blocking_map, + map_flags, + svm_ptr, + size, + num_events_in_wait_list, + event_wait_list, + event ); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueSVMMap( + command_queue, + blocking_map, + map_flags, + svm_ptr, + size, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 2.0 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(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) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueSVMUnmap( + command_queue, + svm_ptr, + num_events_in_wait_list, + event_wait_list, + event ); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueSVMUnmap( + command_queue, + svm_ptr, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 2.0 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clSetKernelArgSVMPointer) ( + cl_kernel kernel, + cl_uint arg_index, + const void* arg_value) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER_KERNEL( + kernel, + "kernel = %p, index = %d, value = %p", + kernel, + arg_index, + arg_value ); + SET_KERNEL_ARG_SVM_POINTER( kernel, arg_index, arg_value ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clSetKernelArgSVMPointer( + kernel, + arg_index, + arg_value ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clSetKernelArgSVMPointer( + kernel, + arg_index, + arg_value ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 2.0 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clSetKernelExecInfo) ( + cl_kernel kernel, + cl_kernel_exec_info param_name, + size_t param_value_size, + const void* param_value) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER_KERNEL( kernel ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clSetKernelExecInfo( + kernel, + param_name, + param_value_size, + param_value ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clSetKernelExecInfo( + kernel, + param_name, + param_value_size, + param_value ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 2.0 +CL_API_ENTRY cl_mem CL_API_CALL CLIRN(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) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER(); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_mem retVal = pIntercept->dispatch().clCreatePipe( + context, + flags, + pipe_packet_size, + pipe_max_packets, + properties, + errcode_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clCreatePipe( + context, + flags, + pipe_packet_size, + pipe_max_packets, + properties, + errcode_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 2.0 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clGetPipeInfo) ( + cl_mem pipe, + cl_pipe_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER( "mem = %p, param_name = %s (%08X)", + pipe, + pIntercept->enumName().name( param_name ).c_str(), + param_name ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clGetPipeInfo( + pipe, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clGetPipeInfo( + pipe, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 2.0 +CL_API_ENTRY cl_command_queue CL_API_CALL CLIRN(clCreateCommandQueueWithProperties) ( + cl_context context, + cl_device_id device, + const cl_queue_properties* properties, + cl_int* errcode_ret) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_queue_properties* newProperties = NULL; + cl_command_queue retVal = NULL; + + std::string deviceInfo; + std::string commandQueueProperties; + if( pIntercept->callLogging() ) + { + pIntercept->getDeviceInfoString( + 1, + &device, + deviceInfo ); + pIntercept->getCommandQueuePropertiesString( + properties, + commandQueueProperties ); + } + CALL_LOGGING_ENTER( "device = [ %s ], properties = [ %s ]", + deviceInfo.c_str(), + commandQueueProperties.c_str() ); + CREATE_COMMAND_QUEUE_OVERRIDE_INIT( properties, newProperties ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + +#if defined(USE_MDAPI) + if( !pIntercept->config().DevicePerfCounterCustom.empty() ) + { + retVal = pIntercept->createMDAPICommandQueue( + context, + device, + properties, + errcode_ret ); + } +#endif + + if( ( retVal == NULL ) && newProperties ) + { + retVal = pIntercept->dispatch().clCreateCommandQueueWithProperties( + context, + device, + newProperties, + errcode_ret ); + } + if( retVal == NULL ) + { + retVal = pIntercept->dispatch().clCreateCommandQueueWithProperties( + context, + device, + properties, + errcode_ret ); + } + + CPU_PERFORMANCE_TIMING_END(); + CREATE_COMMAND_QUEUE_OVERRIDE_CLEANUP( retVal, newProperties ); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + return dummyDispatch.clCreateCommandQueueWithProperties( + context, + device, + properties, + errcode_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_khr_create_command_queue +// This function should stay in sync with clCreateCommandQueueWithProperties, above. +CL_API_ENTRY cl_command_queue CL_API_CALL CLIRN(clCreateCommandQueueWithPropertiesKHR) ( + cl_context context, + cl_device_id device, + const cl_queue_properties_khr* properties, + cl_int* errcode_ret) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clCreateCommandQueueWithPropertiesKHR ) + { + cl_queue_properties* newProperties = NULL; + cl_command_queue retVal = NULL; + + std::string deviceInfo; + std::string commandQueueProperties; + if( pIntercept->callLogging() ) + { + pIntercept->getDeviceInfoString( + 1, + &device, + deviceInfo ); + pIntercept->getCommandQueuePropertiesString( + properties, + commandQueueProperties ); + } + CALL_LOGGING_ENTER( "device = [ %s ], properties = [ %s ]", + deviceInfo.c_str(), + commandQueueProperties.c_str() ); + CREATE_COMMAND_QUEUE_OVERRIDE_INIT( properties, newProperties ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + +#if defined(USE_MDAPI) + if( !pIntercept->config().DevicePerfCounterCustom.empty() ) + { + retVal = pIntercept->createMDAPICommandQueue( + context, + device, + properties, + errcode_ret ); + } +#endif + + if( ( retVal == NULL ) && newProperties ) + { + retVal = pIntercept->dispatch().clCreateCommandQueueWithPropertiesKHR( + context, + device, + newProperties, + errcode_ret ); + } + if( retVal == NULL ) + { + retVal = pIntercept->dispatch().clCreateCommandQueueWithPropertiesKHR( + context, + device, + properties, + errcode_ret ); + } + + CPU_PERFORMANCE_TIMING_END(); + CREATE_COMMAND_QUEUE_OVERRIDE_CLEANUP( retVal, newProperties ); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + if( errcode_ret ) + { + errcode_ret[0] = CL_INVALID_OPERATION; + } + return NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 2.0 +CL_API_ENTRY cl_sampler CL_API_CALL CLIRN(clCreateSamplerWithProperties) ( + cl_context context, + const cl_sampler_properties* sampler_properties, + cl_int* errcode_ret) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + std::string samplerProperties; + if( pIntercept->callLogging() ) + { + pIntercept->getSamplerPropertiesString( + sampler_properties, + samplerProperties ); + } + CALL_LOGGING_ENTER( "properties = [ %s ]", + samplerProperties.c_str() ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_sampler retVal = pIntercept->dispatch().clCreateSamplerWithProperties( + context, + sampler_properties, + errcode_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + ADD_SAMPLER( retVal, samplerProperties ); + + return retVal; + } + else + { + return dummyDispatch.clCreateSamplerWithProperties( + context, + sampler_properties, + errcode_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 2.1 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clSetDefaultDeviceCommandQueue) ( + cl_context context, + cl_device_id device, + cl_command_queue command_queue ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER(); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clSetDefaultDeviceCommandQueue( + context, + device, + command_queue ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clSetDefaultDeviceCommandQueue( + context, + device, + command_queue ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 2.1 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clGetDeviceAndHostTimer) ( + cl_device_id device, + cl_ulong* device_timestamp, + cl_ulong* host_timestamp ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER(); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clGetDeviceAndHostTimer( + device, + device_timestamp, + host_timestamp ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clGetDeviceAndHostTimer( + device, + device_timestamp, + host_timestamp ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 2.1 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clGetHostTimer) ( + cl_device_id device, + cl_ulong* host_timestamp ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER(); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clGetHostTimer( + device, + host_timestamp ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clGetHostTimer( + device, + host_timestamp ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 2.1 +CL_API_ENTRY cl_program CL_API_CALL CLIRN(clCreateProgramWithIL) ( + cl_context context, + const void* il, + size_t length, + cl_int *errcode_ret) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + char* injectedSPIRV = NULL; + uint64_t hash = 0; + + COMPUTE_SPIRV_HASH( length, il, hash ); + INJECT_PROGRAM_SPIRV( length, il, injectedSPIRV, hash ); + + CALL_LOGGING_ENTER( "context = %p, length = %u", + context, + length ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_program retVal = pIntercept->dispatch().clCreateProgramWithIL( + context, + il, + length, + errcode_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + DUMP_PROGRAM_SPIRV( retVal, length, il, hash ); + SAVE_PROGRAM_HASH( retVal, hash ); + DELETE_INJECTED_SPIRV( injectedSPIRV ); + + return retVal; + } + else + { + return dummyDispatch.clCreateProgramWithIL( + context, + il, + length, + errcode_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_khr_il_program +// This function should stay in sync with clCreateProgramWithIL, above. +CL_API_ENTRY cl_program CL_API_CALL CLIRN(clCreateProgramWithILKHR) ( + cl_context context, + const void* il, + size_t length, + cl_int *errcode_ret) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clCreateProgramWithILKHR ) + { + char* injectedSPIRV = NULL; + uint64_t hash = 0; + + COMPUTE_SPIRV_HASH( length, il, hash ); + INJECT_PROGRAM_SPIRV( length, il, injectedSPIRV, hash ); + + CALL_LOGGING_ENTER( "context = %p, length = %u", + context, + length ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_program retVal = pIntercept->dispatch().clCreateProgramWithILKHR( + context, + il, + length, + errcode_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + DUMP_PROGRAM_SPIRV( retVal, length, il, hash ); + SAVE_PROGRAM_HASH( retVal, hash ); + DELETE_INJECTED_SPIRV( injectedSPIRV ); + + return retVal; + } + else + { + if( errcode_ret ) + { + errcode_ret[0] = CL_INVALID_OPERATION; + } + return NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 2.1 +CL_API_ENTRY cl_kernel CL_API_CALL CLIRN(clCloneKernel) ( + cl_kernel source_kernel, + cl_int* errcode_ret ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER(); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_kernel retVal = pIntercept->dispatch().clCloneKernel( + source_kernel, + errcode_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + return dummyDispatch.clCloneKernel( + source_kernel, + errcode_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 2.1 +CL_API_ENTRY cl_int CL_API_CALL CLIRN(clGetKernelSubGroupInfo) ( + cl_kernel kernel, + cl_device_id device, + cl_kernel_sub_group_info param_name, + size_t input_value_size, + const void* input_value, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CALL_LOGGING_ENTER(); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clGetKernelSubGroupInfo( + kernel, + device, + param_name, + input_value_size, + input_value, + param_value_size, + param_value, + param_value_size_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clGetKernelSubGroupInfo( + kernel, + device, + param_name, + input_value_size, + input_value, + param_value_size, + param_value, + param_value_size_ret ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_khr_subgroups +// This function should stay in sync with clGetKernelSubGroupInfo, above. +CL_API_ENTRY cl_int CL_API_CALL clGetKernelSubGroupInfoKHR( + cl_kernel kernel, + cl_device_id device, + cl_kernel_sub_group_info param_name, + size_t input_value_size, + const void* input_value, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clGetKernelSubGroupInfoKHR ) + { + cl_int retVal = CL_SUCCESS; + + CALL_LOGGING_ENTER(); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clGetKernelSubGroupInfoKHR( + kernel, + device, + param_name, + input_value_size, + input_value, + param_value_size, + param_value, + param_value_size_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return CL_INVALID_OPERATION; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenCL 2.1 +CL_API_ENTRY cl_int CL_API_CALL clEnqueueSVMMigrateMem( + cl_command_queue command_queue, + cl_uint num_svm_pointers, + const void** svm_pointers, + const size_t* sizes, + cl_mem_migration_flags flags, + cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, + cl_event* event ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueSVMMigrateMem( + command_queue, + num_svm_pointers, + svm_pointers, + sizes, + flags, + num_events_in_wait_list, + event_wait_list, + event ); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + + return retVal; + } + else + { + return dummyDispatch.clEnqueueSVMMigrateMem( + command_queue, + num_svm_pointers, + svm_pointers, + sizes, + flags, + num_events_in_wait_list, + event_wait_list, + event ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_khr_gl_sharing +CL_API_ENTRY cl_int CL_API_CALL clGetGLContextInfoKHR( + const cl_context_properties *properties, + cl_gl_context_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clGetGLContextInfoKHR ) + { + CALL_LOGGING_ENTER(); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clGetGLContextInfoKHR( + properties, + param_name, + param_value_size, + param_value, + param_value_size_ret); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return CL_INVALID_OPERATION; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_khr_gl_event +CL_API_ENTRY cl_event CL_API_CALL clCreateEventFromGLsyncKHR( + cl_context context, + cl_GLsync sync, + cl_int* errcode_ret) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clCreateEventFromGLsyncKHR ) + { + CALL_LOGGING_ENTER(); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_event retVal = pIntercept->dispatch().clCreateEventFromGLsyncKHR( + context, + sync, + errcode_ret); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + if( errcode_ret ) + { + errcode_ret[0] = CL_INVALID_OPERATION; + } + return NULL; + } +} + +#if defined(_WIN32) + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_khr_d3d10_sharing +CL_API_ENTRY cl_int CL_API_CALL clGetDeviceIDsFromD3D10KHR( + cl_platform_id platform, + cl_d3d10_device_source_khr d3d_device_source, + void* d3d_object, + cl_d3d10_device_set_khr d3d_device_set, + cl_uint num_entries, + cl_device_id* devices, + cl_uint* num_devices) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clGetDeviceIDsFromD3D10KHR ) + { + CALL_LOGGING_ENTER(); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clGetDeviceIDsFromD3D10KHR( + platform, + d3d_device_source, + d3d_object, + d3d_device_set, + num_entries, + devices, + num_devices); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return CL_INVALID_OPERATION; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_khr_d3d10_sharing +CL_API_ENTRY cl_mem CL_API_CALL clCreateFromD3D10BufferKHR( + cl_context context, + cl_mem_flags flags, + ID3D10Buffer* resource, + cl_int* errcode_ret) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clCreateFromD3D10BufferKHR ) + { + CALL_LOGGING_ENTER( + "flags = %s (%llX)", + pIntercept->enumName().name_mem_flags( flags ).c_str(), + flags ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_mem retVal = pIntercept->dispatch().clCreateFromD3D10BufferKHR( + context, + flags, + resource, + errcode_ret); + + CPU_PERFORMANCE_TIMING_END(); + ADD_BUFFER( retVal ); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + if( errcode_ret ) + { + errcode_ret[0] = CL_INVALID_OPERATION; + } + return NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_khr_d3d10_sharing +CL_API_ENTRY cl_mem CL_API_CALL clCreateFromD3D10Texture2DKHR( + cl_context context, + cl_mem_flags flags, + ID3D10Texture2D* resource, + UINT subresource, + cl_int* errcode_ret) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clCreateFromD3D10Texture2DKHR ) + { + CALL_LOGGING_ENTER( + "flags = %s (%llX)", + pIntercept->enumName().name_mem_flags( flags ).c_str(), + flags ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_mem retVal = pIntercept->dispatch().clCreateFromD3D10Texture2DKHR( + context, + flags, + resource, + subresource, + errcode_ret); + + CPU_PERFORMANCE_TIMING_END(); + ADD_IMAGE( retVal ); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + if( errcode_ret ) + { + errcode_ret[0] = CL_INVALID_OPERATION; + } + return NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_khr_d3d10_sharing +CL_API_ENTRY cl_mem CL_API_CALL clCreateFromD3D10Texture3DKHR( + cl_context context, + cl_mem_flags flags, + ID3D10Texture3D* resource, + UINT subresource, + cl_int* errcode_ret) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clCreateFromD3D10Texture3DKHR ) + { + CALL_LOGGING_ENTER( + "flags = %s (%llX)", + pIntercept->enumName().name_mem_flags( flags ).c_str(), + flags ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_mem retVal = pIntercept->dispatch().clCreateFromD3D10Texture3DKHR( + context, + flags, + resource, + subresource, + errcode_ret); + + CPU_PERFORMANCE_TIMING_END(); + ADD_IMAGE( retVal ); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + if( errcode_ret ) + { + errcode_ret[0] = CL_INVALID_OPERATION; + } + return NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_khr_d3d10_sharing +CL_API_ENTRY cl_int CL_API_CALL clEnqueueAcquireD3D10ObjectsKHR( + 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) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clEnqueueAcquireD3D10ObjectsKHR ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueAcquireD3D10ObjectsKHR( + command_queue, + num_objects, + mem_objects, + num_events_in_wait_list, + event_wait_list, + event); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return CL_INVALID_OPERATION; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_khr_d3d10_sharing +CL_API_ENTRY cl_int CL_API_CALL clEnqueueReleaseD3D10ObjectsKHR( + 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) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clEnqueueReleaseD3D10ObjectsKHR ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueReleaseD3D10ObjectsKHR( + command_queue, + num_objects, + mem_objects, + num_events_in_wait_list, + event_wait_list, + event); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + DEVICE_PERFORMANCE_TIMING_CHECK(); + + return retVal; + } + else + { + return CL_INVALID_OPERATION; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_khr_d3d11_sharing +CL_API_ENTRY cl_int CL_API_CALL clGetDeviceIDsFromD3D11KHR( + cl_platform_id platform, + cl_d3d11_device_source_khr d3d_device_source, + void* d3d_object, + cl_d3d11_device_set_khr d3d_device_set, + cl_uint num_entries, + cl_device_id* devices, + cl_uint* num_devices) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clGetDeviceIDsFromD3D11KHR ) + { + CALL_LOGGING_ENTER(); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clGetDeviceIDsFromD3D11KHR( + platform, + d3d_device_source, + d3d_object, + d3d_device_set, + num_entries, + devices, + num_devices); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return CL_INVALID_OPERATION; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_khr_d3d11_sharing +CL_API_ENTRY cl_mem CL_API_CALL clCreateFromD3D11BufferKHR( + cl_context context, + cl_mem_flags flags, + ID3D11Buffer* resource, + cl_int* errcode_ret) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clCreateFromD3D11BufferKHR ) + { + CALL_LOGGING_ENTER( + "flags = %s (%llX)", + pIntercept->enumName().name_mem_flags( flags ).c_str(), + flags ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_mem retVal = pIntercept->dispatch().clCreateFromD3D11BufferKHR( + context, + flags, + resource, + errcode_ret); + + CPU_PERFORMANCE_TIMING_END(); + ADD_BUFFER( retVal ); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + if( errcode_ret ) + { + errcode_ret[0] = CL_INVALID_OPERATION; + } + return NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_khr_d3d11_sharing +CL_API_ENTRY cl_mem CL_API_CALL clCreateFromD3D11Texture2DKHR( + cl_context context, + cl_mem_flags flags, + ID3D11Texture2D* resource, + UINT subresource, + cl_int* errcode_ret) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clCreateFromD3D11Texture2DKHR ) + { + CALL_LOGGING_ENTER( + "flags = %s (%llX)", + pIntercept->enumName().name_mem_flags( flags ).c_str(), + flags ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_mem retVal = pIntercept->dispatch().clCreateFromD3D11Texture2DKHR( + context, + flags, + resource, + subresource, + errcode_ret); + + CPU_PERFORMANCE_TIMING_END(); + ADD_IMAGE( retVal ); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + if( errcode_ret ) + { + errcode_ret[0] = CL_INVALID_OPERATION; + } + return NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_khr_d3d11_sharing +CL_API_ENTRY cl_mem CL_API_CALL clCreateFromD3D11Texture3DKHR( + cl_context context, + cl_mem_flags flags, + ID3D11Texture3D* resource, + UINT subresource, + cl_int* errcode_ret) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clCreateFromD3D11Texture3DKHR ) + { + CALL_LOGGING_ENTER( + "flags = %s (%llX)", + pIntercept->enumName().name_mem_flags( flags ).c_str(), + flags ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_mem retVal = pIntercept->dispatch().clCreateFromD3D11Texture3DKHR( + context, + flags, + resource, + subresource, + errcode_ret); + + CPU_PERFORMANCE_TIMING_END(); + ADD_IMAGE( retVal ); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + if( errcode_ret ) + { + errcode_ret[0] = CL_INVALID_OPERATION; + } + return NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_khr_d3d11_sharing +CL_API_ENTRY cl_int CL_API_CALL clEnqueueAcquireD3D11ObjectsKHR( + 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) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clEnqueueAcquireD3D11ObjectsKHR ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueAcquireD3D11ObjectsKHR( + command_queue, + num_objects, + mem_objects, + num_events_in_wait_list, + event_wait_list, + event); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return CL_INVALID_OPERATION; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_khr_d3d11_sharing +CL_API_ENTRY cl_int CL_API_CALL clEnqueueReleaseD3D11ObjectsKHR( + 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) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clEnqueueReleaseD3D11ObjectsKHR ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueReleaseD3D11ObjectsKHR( + command_queue, + num_objects, + mem_objects, + num_events_in_wait_list, + event_wait_list, + event); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + DEVICE_PERFORMANCE_TIMING_CHECK(); + + return retVal; + } + else + { + return CL_INVALID_OPERATION; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_khr_dx9_media_sharing +CL_API_ENTRY cl_int CL_API_CALL clGetDeviceIDsFromDX9MediaAdapterKHR( + cl_platform_id platform, + cl_uint num_media_adapters, + cl_dx9_media_adapter_type_khr* media_adapters_type, + void* media_adapters, + cl_dx9_media_adapter_set_khr media_adapter_set, + cl_uint num_entries, + cl_device_id* devices, + cl_uint* num_devices) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clGetDeviceIDsFromDX9MediaAdapterKHR ) + { + CALL_LOGGING_ENTER(); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clGetDeviceIDsFromDX9MediaAdapterKHR( + platform, + num_media_adapters, + media_adapters_type, + media_adapters, + media_adapter_set, + num_entries, + devices, + num_devices); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return CL_INVALID_OPERATION; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_khr_dx9_media_sharing +CL_API_ENTRY cl_mem CL_API_CALL clCreateFromDX9MediaSurfaceKHR( + cl_context context, + cl_mem_flags flags, + cl_dx9_media_adapter_type_khr adapter_type, + void* surface_info, + cl_uint plane, + cl_int* errcode_ret) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clCreateFromDX9MediaSurfaceKHR ) + { + CALL_LOGGING_ENTER( + "flags = %s (%llX)", + pIntercept->enumName().name_mem_flags( flags ).c_str(), + flags ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_mem retVal = pIntercept->dispatch().clCreateFromDX9MediaSurfaceKHR( + context, + flags, + adapter_type, + surface_info, + plane, + errcode_ret); + + CPU_PERFORMANCE_TIMING_END(); + ADD_IMAGE( retVal ); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + if( errcode_ret ) + { + errcode_ret[0] = CL_INVALID_OPERATION; + } + return NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_khr_dx9_media_sharing +CL_API_ENTRY cl_int CL_API_CALL clEnqueueAcquireDX9MediaSurfacesKHR( + 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) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clEnqueueAcquireDX9MediaSurfacesKHR ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueAcquireDX9MediaSurfacesKHR( + command_queue, + num_objects, + mem_objects, + num_events_in_wait_list, + event_wait_list, + event); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return CL_INVALID_OPERATION; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_khr_dx9_media_sharing +CL_API_ENTRY cl_int CL_API_CALL clEnqueueReleaseDX9MediaSurfacesKHR( + 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) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clEnqueueReleaseDX9MediaSurfacesKHR ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueReleaseDX9MediaSurfacesKHR( + command_queue, + num_objects, + mem_objects, + num_events_in_wait_list, + event_wait_list, + event); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + DEVICE_PERFORMANCE_TIMING_CHECK(); + + return retVal; + } + else + { + return CL_INVALID_OPERATION; + } +} +#endif + +#if defined(_WIN32) +/////////////////////////////////////////////////////////////////////////////// +// +// cl_intel_dx9_media_sharing Extension +CL_API_ENTRY cl_int CL_API_CALL clGetDeviceIDsFromDX9INTEL( + cl_platform_id platform, + cl_dx9_device_source_intel d3d_device_source, + void *dx9_object, + cl_dx9_device_set_intel d3d_device_set, + cl_uint num_entries, + cl_device_id* devices, + cl_uint* num_devices ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clGetDeviceIDsFromDX9INTEL ) + { + CALL_LOGGING_ENTER(); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clGetDeviceIDsFromDX9INTEL( + platform, + d3d_device_source, + dx9_object, + d3d_device_set, + num_entries, + devices, + num_devices); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return CL_INVALID_OPERATION; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_intel_dx9_media_sharing Extension +CL_API_ENTRY cl_mem CL_API_CALL clCreateFromDX9MediaSurfaceINTEL( + cl_context context, + cl_mem_flags flags, + IDirect3DSurface9* resource, + HANDLE sharedHandle, + UINT plane, + cl_int* errcode_ret ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clCreateFromDX9MediaSurfaceINTEL ) + { + CALL_LOGGING_ENTER( + "flags = %s (%llX)", + pIntercept->enumName().name_mem_flags( flags ).c_str(), + flags ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_mem retVal = pIntercept->dispatch().clCreateFromDX9MediaSurfaceINTEL( + context, + flags, + resource, + sharedHandle, + plane, + errcode_ret); + + CPU_PERFORMANCE_TIMING_END(); + ADD_IMAGE( retVal ); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + if( errcode_ret ) + { + errcode_ret[0] = CL_INVALID_OPERATION; + } + return NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_intel_dx9_media_sharing Extension +CL_API_ENTRY cl_int CL_API_CALL clEnqueueAcquireDX9ObjectsINTEL( + 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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clEnqueueAcquireDX9ObjectsINTEL ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueAcquireDX9ObjectsINTEL( + command_queue, + num_objects, + mem_objects, + num_events_in_wait_list, + event_wait_list, + event); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return CL_INVALID_OPERATION; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_intel_dx9_media_sharing Extension +CL_API_ENTRY cl_int CL_API_CALL clEnqueueReleaseDX9ObjectsINTEL( + 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 ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clEnqueueReleaseDX9ObjectsINTEL ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueReleaseDX9ObjectsINTEL( + command_queue, + num_objects, + mem_objects, + num_events_in_wait_list, + event_wait_list, + event); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + DEVICE_PERFORMANCE_TIMING_CHECK(); + + return retVal; + } + else + { + return CL_INVALID_OPERATION; + } +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +// +// Unofficial MDAPI extension: +CL_API_ENTRY cl_command_queue CL_API_CALL clCreatePerfCountersCommandQueueINTEL( + cl_context context, + cl_device_id device, + cl_command_queue_properties properties, + cl_uint configuration, + cl_int* errcode_ret ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clCreatePerfCountersCommandQueueINTEL ) + { + // We don't have to do this, since profiling must be enabled + // for a perf counters command queue, but it doesn't hurt to + // add it, either. + if( pIntercept->config().DevicePerformanceTiming || + pIntercept->config().ITTPerformanceTiming || + pIntercept->config().ChromePerformanceTiming || + pIntercept->config().SIMDSurvey || + !pIntercept->config().DevicePerfCounterCustom.empty() ) + { + properties |= (cl_command_queue_properties)CL_QUEUE_PROFILING_ENABLE; + } + + CALL_LOGGING_ENTER(); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_command_queue retVal = pIntercept->dispatch().clCreatePerfCountersCommandQueueINTEL( + context, + device, + properties, + configuration, + errcode_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( errcode_ret[0] ); + ITT_REGISTER_COMMAND_QUEUE( retVal, true ); + CHROME_REGISTER_COMMAND_QUEUE( retVal ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + if( errcode_ret ) + { + errcode_ret[0] = CL_INVALID_OPERATION; + } + return NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// Unofficial MDAPI extension: +CL_API_ENTRY cl_int CL_API_CALL clSetPerformanceConfigurationINTEL( + cl_device_id device, + cl_uint count, + cl_uint* offsets, + cl_uint* values ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clSetPerformanceConfigurationINTEL ) + { + CALL_LOGGING_ENTER(); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clSetPerformanceConfigurationINTEL( + device, + count, + offsets, + values ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return dummyDispatch.clSetPerformanceConfigurationINTEL( + device, + count, + offsets, + values ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_intel_accelerator +CL_API_ENTRY cl_accelerator_intel CL_API_CALL clCreateAcceleratorINTEL( + cl_context context, + cl_accelerator_type_intel accelerator_type, + size_t descriptor_size, + const void* descriptor, + cl_int* errcode_ret ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clCreateAcceleratorINTEL ) + { + if( ( accelerator_type == CL_ACCELERATOR_TYPE_MOTION_ESTIMATION_INTEL ) && + ( descriptor_size >= sizeof( cl_motion_estimation_desc_intel ) ) ) + { + cl_motion_estimation_desc_intel* desc = + (cl_motion_estimation_desc_intel*)descriptor; + CALL_LOGGING_ENTER( "cl_motion_estimation_desc: mb_block_type = %d, subpixel_mode = %d, sad_adjust_mode = %d, search_path_type = %d", + desc->mb_block_type, + desc->subpixel_mode, + desc->sad_adjust_mode, + desc->search_path_type ); + } + else + { + CALL_LOGGING_ENTER( "accelerator_type = %u", accelerator_type ); + } + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_accelerator_intel retVal = pIntercept->dispatch().clCreateAcceleratorINTEL( + context, + accelerator_type, + descriptor_size, + descriptor, + errcode_ret); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + if( errcode_ret ) + { + errcode_ret[0] = CL_INVALID_OPERATION; + } + return NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_intel_accelerator +CL_API_ENTRY cl_int CL_API_CALL clGetAcceleratorInfoINTEL( + cl_accelerator_intel accelerator, + cl_accelerator_info_intel param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + CALL_LOGGING_ENTER( "param_name = %s (%X)", + pIntercept->enumName().name( param_name ).c_str(), + param_name ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clGetAcceleratorInfoINTEL( + accelerator, + param_name, + param_value_size, + param_value, + param_value_size_ret ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return CL_INVALID_OPERATION; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_intel_accelerator +CL_API_ENTRY cl_int CL_API_CALL clRetainAcceleratorINTEL( + cl_accelerator_intel accelerator ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_uint ref_count = 0; + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetAcceleratorInfoINTEL( + accelerator, + CL_ACCELERATOR_REFERENCE_COUNT_INTEL, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_ENTER( "[ ref count = %d ] accelerator = %p", + ref_count, + accelerator ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clRetainAcceleratorINTEL( + accelerator ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetAcceleratorInfoINTEL( + accelerator, + CL_ACCELERATOR_REFERENCE_COUNT_INTEL, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_EXIT( "[ ref count = %d ]", + ref_count ); + + return retVal; + } + else + { + return CL_INVALID_OPERATION; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_intel_accelerator +CL_API_ENTRY cl_int CL_API_CALL clReleaseAcceleratorINTEL( + cl_accelerator_intel accelerator ) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept ) + { + cl_uint ref_count = 0; + if( pIntercept->callLogging() ) + { + ref_count = 0; + pIntercept->dispatch().clGetAcceleratorInfoINTEL( + accelerator, + CL_ACCELERATOR_REFERENCE_COUNT_INTEL, + sizeof( ref_count ), + &ref_count, + NULL ); + } + CALL_LOGGING_ENTER( "[ ref count = %d ] accelerator = %p", + ref_count, + accelerator ); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clReleaseAcceleratorINTEL( + accelerator ); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + if( pIntercept->callLogging() && ref_count != 0 ) + { + // This isn't strictly correct, but it's pretty close, and it + // avoids crashes in some cases for bad implementations. + --ref_count; + } + CALL_LOGGING_EXIT( "[ ref count = %d ]", + ref_count ); + + return retVal; + } + else + { + return CL_INVALID_OPERATION; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_intel_va_api_media_sharing +CL_API_ENTRY cl_int CL_API_CALL clGetDeviceIDsFromVA_APIMediaAdapterINTEL( + cl_platform_id platform, + cl_va_api_device_source_intel media_adapter_type, + void *media_adapter, + cl_va_api_device_set_intel media_adapter_set, + cl_uint num_entries, + cl_device_id *devices, + cl_uint *num_devices) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clGetDeviceIDsFromVA_APIMediaAdapterINTEL ) + { + CALL_LOGGING_ENTER(); + CPU_PERFORMANCE_TIMING_START(); + + cl_int retVal = pIntercept->dispatch().clGetDeviceIDsFromVA_APIMediaAdapterINTEL( + platform, + media_adapter_type, + media_adapter, + media_adapter_set, + num_entries, + devices, + num_devices); + + CPU_PERFORMANCE_TIMING_END(); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT(); + + return retVal; + } + else + { + return CL_INVALID_OPERATION; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_intel_va_api_media_sharing +CL_API_ENTRY cl_mem CL_API_CALL clCreateFromVA_APIMediaSurfaceINTEL( + cl_context context, + cl_mem_flags flags, + VASurfaceID *surface, + cl_uint plane, + cl_int *errcode_ret) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clCreateFromVA_APIMediaSurfaceINTEL ) + { + CALL_LOGGING_ENTER( + "flags = %s (%llX)", + pIntercept->enumName().name_mem_flags( flags ).c_str(), + flags ); + CHECK_ERROR_INIT( errcode_ret ); + CPU_PERFORMANCE_TIMING_START(); + + cl_mem retVal = pIntercept->dispatch().clCreateFromVA_APIMediaSurfaceINTEL( + context, + flags, + surface, + plane, + errcode_ret); + + CPU_PERFORMANCE_TIMING_END(); + ADD_IMAGE( retVal ); + CHECK_ERROR( errcode_ret[0] ); + CALL_LOGGING_EXIT( "returned %p", retVal ); + + return retVal; + } + else + { + if( errcode_ret ) + { + errcode_ret[0] = CL_INVALID_OPERATION; + } + return NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_intel_va_api_media_sharing +CL_API_ENTRY cl_int CL_API_CALL clEnqueueAcquireVA_APIMediaSurfacesINTEL( + 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) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clEnqueueAcquireVA_APIMediaSurfacesINTEL ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueAcquireVA_APIMediaSurfacesINTEL( + command_queue, + num_objects, + mem_objects, + num_events_in_wait_list, + event_wait_list, + event); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + return retVal; + } + else + { + return CL_INVALID_OPERATION; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +// cl_intel_va_api_media_sharing +CL_API_ENTRY cl_int CL_API_CALL clEnqueueReleaseVA_APIMediaSurfacesINTEL( + 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) +{ + CLIntercept* pIntercept = GetIntercept(); + + if( pIntercept && + pIntercept->dispatch().clEnqueueReleaseVA_APIMediaSurfacesINTEL ) + { + cl_int retVal = CL_SUCCESS; + + CHECK_AUBCAPTURE_START( command_queue ); + + if( pIntercept->nullEnqueue() == false ) + { + CALL_LOGGING_ENTER(); + DEVICE_PERFORMANCE_TIMING_START( event ); + CPU_PERFORMANCE_TIMING_START(); + + retVal = pIntercept->dispatch().clEnqueueReleaseVA_APIMediaSurfacesINTEL( + command_queue, + num_objects, + mem_objects, + num_events_in_wait_list, + event_wait_list, + event); + + CPU_PERFORMANCE_TIMING_END(); + DEVICE_PERFORMANCE_TIMING_END( event ); + CHECK_ERROR( retVal ); + CALL_LOGGING_EXIT_EVENT( event ); + } + + FINISH_OR_FLUSH_AFTER_ENQUEUE( command_queue ); + CHECK_AUBCAPTURE_STOP( command_queue ); + + DEVICE_PERFORMANCE_TIMING_CHECK(); + + return retVal; + } + else + { + return CL_INVALID_OPERATION; + } +} + +#if defined(__APPLE__) +#include "OS/OS_mac_interpose.h" +#endif diff --git a/Src/dispatch.h b/Src/dispatch.h new file mode 100644 index 00000000..fe52c27e --- /dev/null +++ b/Src/dispatch.h @@ -0,0 +1,1220 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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. +*/ + +#pragma once + +#include "common.h" +#include "cli_ext.h" + +#define CLI_API_ENTRY CL_API_ENTRY +#define CLI_API_CALL CL_API_CALL + +struct CLdispatch +{ + cl_int (CLI_API_CALL *clGetPlatformIDs) ( + cl_uint num_entries, + cl_platform_id* platforms, + cl_uint* num_platforms ); + + cl_int (CLI_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_int (CLI_API_CALL *clGetDeviceIDs) ( + cl_platform_id platform, + cl_device_type device_type, + cl_uint num_entries, + cl_device_id* devices, + cl_uint* num_devices ); + + cl_int (CLI_API_CALL *clGetDeviceInfo) ( + cl_device_id device, + cl_device_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret ); + + // OpenCL 1.2 + cl_int (CLI_API_CALL *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 ); + + // OpenCL 1.2 + cl_int (CLI_API_CALL *clRetainDevice) ( + cl_device_id device ); + + // OpenCL 1.2 + cl_int (CLI_API_CALL *clReleaseDevice) ( + cl_device_id device ); + + cl_context (CLI_API_CALL *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_context (CLI_API_CALL *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_int (CLI_API_CALL *clRetainContext) ( + cl_context context ); + + cl_int (CLI_API_CALL *clReleaseContext) ( + cl_context context ); + + cl_int (CLI_API_CALL *clGetContextInfo) ( + cl_context context, + cl_context_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret ); + + cl_command_queue (CLI_API_CALL *clCreateCommandQueue) ( + cl_context context, + cl_device_id device, + cl_command_queue_properties properties, + cl_int* errcode_ret ); + + cl_int (CLI_API_CALL *clRetainCommandQueue) ( + cl_command_queue command_queue ); + + cl_int (CLI_API_CALL *clReleaseCommandQueue) ( + cl_command_queue command_queue ); + + cl_int (CLI_API_CALL *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 ); + + // deprecated OpenCL 1.0 + cl_int (CLI_API_CALL *clSetCommandQueueProperty) ( + cl_command_queue command_queue, + cl_command_queue_properties properties, + cl_bool enable, + cl_command_queue_properties* old_properties ); + + cl_mem (CLI_API_CALL *clCreateBuffer) ( + cl_context context, + cl_mem_flags flags, + size_t size, + void* host_ptr, + cl_int* errcode_ret ); + + // OpenCL 1.1 + cl_mem (CLI_API_CALL *clCreateSubBuffer) ( + cl_mem buffer, + cl_mem_flags flags, + cl_buffer_create_type buffer_create_type, + const void *buffer_create_info, + cl_int *errcode_ret ); + + // OpenCL 1.2 + cl_mem (CLI_API_CALL *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 ); + + // deprecated OpenCL 1.1 + cl_mem (CLI_API_CALL *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 ); + + // deprecated OpenCL 1.1 + cl_mem (CLI_API_CALL *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 ); + + cl_int (CLI_API_CALL *clRetainMemObject) ( + cl_mem memobj ); + + cl_int (CLI_API_CALL *clReleaseMemObject) ( + cl_mem memobj ); + + cl_int (CLI_API_CALL *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_int (CLI_API_CALL *clGetMemObjectInfo) ( + cl_mem memobj, + cl_mem_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret ); + + cl_int (CLI_API_CALL *clGetImageInfo) ( + cl_mem image, + cl_image_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret ); + + // OpenCL 1.1 + cl_int (CLI_API_CALL *clSetMemObjectDestructorCallback) ( + cl_mem memobj, + void (CL_CALLBACK *pfn_notify)( cl_mem, void* ), + void *user_data ); + + cl_sampler (CLI_API_CALL *clCreateSampler) ( + cl_context context, + cl_bool normalized_coords, + cl_addressing_mode addressing_mode, + cl_filter_mode filter_mode, + cl_int* errcode_ret ); + + cl_int (CLI_API_CALL *clRetainSampler) ( + cl_sampler sampler ); + + cl_int (CLI_API_CALL *clReleaseSampler) ( + cl_sampler sampler ); + + cl_int (CLI_API_CALL *clGetSamplerInfo) ( + cl_sampler sampler, + cl_sampler_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret ); + + cl_program (CLI_API_CALL *clCreateProgramWithSource) ( + cl_context context, + cl_uint count, + const char** strings, + const size_t* lengths, + cl_int* errcode_ret ); + + cl_program (CLI_API_CALL *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 ); + + // OpenCL 1.2 + cl_program (CLI_API_CALL *clCreateProgramWithBuiltInKernels) ( + cl_context context, + cl_uint num_devices, + const cl_device_id* device_list, + const char* kernel_names, + cl_int* errcode_ret); + + cl_int (CLI_API_CALL *clRetainProgram) ( + cl_program program ); + + cl_int (CLI_API_CALL *clReleaseProgram) ( + cl_program program ); + + cl_int (CLI_API_CALL *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 ); + + // OpenCL 1.2 + cl_int (CLI_API_CALL *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 ); + + // OpenCL 1.2 + cl_program (CLI_API_CALL *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 ); + + // OpenCL 2.2 + cl_int (CLI_API_CALL *clSetProgramReleaseCallback) ( + cl_program program, + void (CL_CALLBACK *pfn_notify)(cl_program program, void* user_data), + void* user_data ); + + // OpenCL 2.2 + cl_int (CLI_API_CALL *clSetProgramSpecializationConstant) ( + cl_program program, + cl_uint spec_id, + size_t spec_size, + const void* spec_value ); + + // OpenCL 1.2 + cl_int (CLI_API_CALL *clUnloadPlatformCompiler) ( + cl_platform_id platform ); + + // deprecated OpenCL 1.1 + cl_int (CLI_API_CALL *clUnloadCompiler) ( void ); + + cl_int (CLI_API_CALL *clGetProgramInfo) ( + cl_program program, + cl_program_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret ); + + cl_int (CLI_API_CALL *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_kernel (CLI_API_CALL *clCreateKernel) ( + cl_program program, + const char* kernel_name, + cl_int* errcode_ret ); + + cl_int (CLI_API_CALL *clCreateKernelsInProgram) ( + cl_program program, + cl_uint num_kernels, + cl_kernel* kernels, + cl_uint* num_kernels_ret ); + + cl_int (CLI_API_CALL *clRetainKernel) ( + cl_kernel kernel ); + + cl_int (CLI_API_CALL *clReleaseKernel) ( + cl_kernel kernel ); + + cl_int (CLI_API_CALL *clSetKernelArg) ( + cl_kernel kernel, + cl_uint arg_index, + size_t arg_size, + const void* arg_value ); + + cl_int (CLI_API_CALL *clGetKernelInfo) ( + cl_kernel kernel, + cl_kernel_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret ); + + // OpenCL 1.2 + cl_int (CLI_API_CALL *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_int (CLI_API_CALL *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_int (CLI_API_CALL *clWaitForEvents) ( + cl_uint num_events, + const cl_event* event_list ); + + cl_int (CLI_API_CALL *clGetEventInfo) ( + cl_event event, + cl_event_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret ); + + // OpenCL 1.1 + cl_event (CLI_API_CALL *clCreateUserEvent) ( + cl_context context, + cl_int *errcode_ret ); + + cl_int (CLI_API_CALL *clRetainEvent) ( + cl_event event ); + + cl_int (CLI_API_CALL *clReleaseEvent) ( + cl_event event ); + + // OpenCL 1.1 + cl_int (CLI_API_CALL *clSetUserEventStatus) ( + cl_event event, + cl_int execution_status ); + + // OpenCL 1.1 + cl_int (CLI_API_CALL *clSetEventCallback) ( + cl_event event, + cl_int command_exec_callback_type, + void (CL_CALLBACK *pfn_notify)( cl_event, cl_int, void * ), + void *user_data ); + + cl_int (CLI_API_CALL *clGetEventProfilingInfo) ( + cl_event event, + cl_profiling_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret ); + + cl_int (CLI_API_CALL *clFlush) ( + cl_command_queue command_queue ); + + cl_int (CLI_API_CALL *clFinish) ( + cl_command_queue command_queue ); + + cl_int (CLI_API_CALL *clEnqueueReadBuffer) ( + cl_command_queue command_queue, + cl_mem buffer, + cl_bool blocking_read, + size_t offset, + size_t cb, + void* ptr, + cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, + cl_event* event ); + + // OpenCL 1.1 + cl_int (CLI_API_CALL *clEnqueueReadBufferRect) ( + cl_command_queue command_queue, + cl_mem buffer, + cl_bool blocking_read, + const size_t *buffer_origin, + const size_t *host_origin, + 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_int (CLI_API_CALL *clEnqueueWriteBuffer) ( + cl_command_queue command_queue, + cl_mem buffer, + cl_bool blocking_write, + size_t offset, + size_t cb, + const void* ptr, + cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, + cl_event* event ); + + // OpenCL 1.1 + cl_int (CLI_API_CALL *clEnqueueWriteBufferRect) ( + cl_command_queue command_queue, + cl_mem buffer, + cl_bool blocking_write, + const size_t *buffer_origin, + const size_t *host_origin, + 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 ); + + // OpenCL 1.2 + cl_int (CLI_API_CALL *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_int (CLI_API_CALL *clEnqueueCopyBuffer) ( + cl_command_queue command_queue, + cl_mem src_buffer, + cl_mem dst_buffer, + size_t src_offset, + size_t dst_offset, + size_t cb, + cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, + cl_event* event ); + + // OpenCL 1.1 + cl_int (CLI_API_CALL *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_int (CLI_API_CALL *clEnqueueReadImage) ( + cl_command_queue command_queue, + cl_mem image, + cl_bool blocking_read, + const size_t* origin, + const size_t* region, + 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_int (CLI_API_CALL *clEnqueueWriteImage) ( + cl_command_queue command_queue, + cl_mem image, + cl_bool blocking_write, + const size_t* origin, + const size_t* region, + 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 ); + + // OpenCL 1.2 + cl_int (CLI_API_CALL *clEnqueueFillImage) ( + cl_command_queue command_queue, + cl_mem image, + const void* fill_color, + const size_t* origin, + const size_t* region, + cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, + cl_event* event ); + + cl_int (CLI_API_CALL *clEnqueueCopyImage) ( + cl_command_queue command_queue, + cl_mem src_image, + cl_mem dst_image, + const size_t* src_origin, + const size_t* dst_origin, + const size_t* region, + cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, + cl_event* event ); + + cl_int (CLI_API_CALL *clEnqueueCopyImageToBuffer) ( + cl_command_queue command_queue, + cl_mem src_image, + cl_mem dst_buffer, + const size_t* src_origin, + const size_t* region, + size_t dst_offset, + cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, + cl_event* event ); + + cl_int (CLI_API_CALL *clEnqueueCopyBufferToImage) ( + cl_command_queue command_queue, + cl_mem src_buffer, + cl_mem dst_image, + size_t src_offset, + const size_t* dst_origin, + const size_t* region, + cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, + cl_event* event ); + + void* (CLI_API_CALL *clEnqueueMapBuffer) ( + cl_command_queue command_queue, + cl_mem buffer, + cl_bool blocking_map, + cl_map_flags map_flags, + size_t offset, + size_t cb, + cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, + cl_event* event, + cl_int* errcode_ret ); + + void* (CLI_API_CALL *clEnqueueMapImage) ( + cl_command_queue command_queue, + cl_mem image, + cl_bool blocking_map, + cl_map_flags map_flags, + const size_t* origin, + const size_t* region, + 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_int (CLI_API_CALL *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 ); + + // OpenCL 1.2 + cl_int (CLI_API_CALL *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_int (CLI_API_CALL *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_int (CLI_API_CALL *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 ); + + cl_int (CLI_API_CALL *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 ); + + // deprecated OpenCL 1.1 + cl_int (CLI_API_CALL *clEnqueueMarker) ( + cl_command_queue command_queue, + cl_event* event ); + + // deprecated OpenCL 1.1 + cl_int (CLI_API_CALL *clEnqueueWaitForEvents) ( + cl_command_queue command_queue, + cl_uint num_events, + const cl_event* event_list ); + + // deprecated OpenCL 1.1 + cl_int (CLI_API_CALL *clEnqueueBarrier) ( + cl_command_queue command_queue ); + + // OpenCL 1.2 + cl_int (CLI_API_CALL *clEnqueueMarkerWithWaitList) ( + cl_command_queue command_queue, + cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, + cl_event* event ); + + // OpenCL 1.2 + cl_int (CLI_API_CALL *clEnqueueBarrierWithWaitList) ( + cl_command_queue command_queue, + cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, + cl_event* event ); + + // Optional? + // deprecated OpenCL 1.1 + void* (CLI_API_CALL *clGetExtensionFunctionAddress) ( + const char* func_name ); + + // Optional? + // OpenCL 1.2 + void* (CLI_API_CALL *clGetExtensionFunctionAddressForPlatform)( + cl_platform_id platform, + const char* func_name ); + + // CL-GL Sharing + + cl_mem (CLI_API_CALL *clCreateFromGLBuffer) ( + cl_context context, + cl_mem_flags flags, + cl_GLuint bufobj, + int* errcode_ret); // Not cl_int*? + + // OpenCL 1.2 + cl_mem (CLI_API_CALL *clCreateFromGLTexture) ( + cl_context context, + cl_mem_flags flags, + cl_GLenum target, + cl_GLint miplevel, + cl_GLuint texture, + cl_int* errcode_ret ); + + // deprecated OpenCL 1.1 + cl_mem (CLI_API_CALL *clCreateFromGLTexture2D) ( + cl_context context, + cl_mem_flags flags, + cl_GLenum target, + cl_GLint miplevel, + cl_GLuint texture, + cl_int* errcode_ret); + + // deprecated OpenCL 1.1 + cl_mem (CLI_API_CALL *clCreateFromGLTexture3D) ( + cl_context context, + cl_mem_flags flags, + cl_GLenum target, + cl_GLint miplevel, + cl_GLuint texture, + cl_int* errcode_ret); + + cl_mem (CLI_API_CALL *clCreateFromGLRenderbuffer) ( + cl_context context, + cl_mem_flags flags, + cl_GLuint renderbuffer, + cl_int* errcode_ret); + + cl_int (CLI_API_CALL *clGetGLObjectInfo) ( + cl_mem memobj, + cl_gl_object_type* gl_object_type, + cl_GLuint* gl_object_name); + + cl_int (CLI_API_CALL *clGetGLTextureInfo) ( + cl_mem memobj, + cl_gl_texture_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret); + + cl_int (CLI_API_CALL *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); + + cl_int (CLI_API_CALL *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); + + // OpenCL 2.0 + + void* (CLI_API_CALL *clSVMAlloc) ( + cl_context context, + cl_svm_mem_flags flags, + size_t size, + cl_uint alignment); + + void (CLI_API_CALL *clSVMFree) ( + cl_context context, + void* svm_pointer); + + cl_int (CLI_API_CALL *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_int (CLI_API_CALL *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_int (CLI_API_CALL *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_int (CLI_API_CALL *clEnqueueSVMMap) ( + cl_command_queue command_queue, + cl_bool blocking_map, + cl_map_flags map_flags, + void* svm_ptr, + size_t size, + cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, + cl_event* event); + + cl_int (CLI_API_CALL *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_int (CLI_API_CALL *clSetKernelArgSVMPointer) ( + cl_kernel kernel, + cl_uint arg_index, + const void* arg_value); + + cl_int (CLI_API_CALL *clSetKernelExecInfo) ( + cl_kernel kernel, + cl_kernel_exec_info param_name, + size_t param_value_size, + const void* param_value); + + cl_mem (CLI_API_CALL *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_int (CLI_API_CALL *clGetPipeInfo) ( + cl_mem pipe, + cl_pipe_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret); + + cl_command_queue (CLI_API_CALL *clCreateCommandQueueWithProperties) ( + cl_context context, + cl_device_id device, + const cl_queue_properties* properties, + cl_int* errcode_ret); + + cl_sampler (CLI_API_CALL *clCreateSamplerWithProperties) ( + cl_context context, + const cl_sampler_properties* sampler_properties, + cl_int* errcode_ret); + + // OpenCL 2.1 + + cl_int (CLI_API_CALL *clSetDefaultDeviceCommandQueue) ( + cl_context context, + cl_device_id device, + cl_command_queue command_queue ); + + cl_int (CLI_API_CALL *clGetDeviceAndHostTimer) ( + cl_device_id device, + cl_ulong* device_timestamp, + cl_ulong* host_timestamp ); + + cl_int (CLI_API_CALL *clGetHostTimer) ( + cl_device_id device, + cl_ulong* host_timestamp ); + + cl_program (CLI_API_CALL *clCreateProgramWithIL) ( + cl_context context, + const void *il, + size_t length, + cl_int *errcode_ret); + + cl_kernel (CLI_API_CALL *clCloneKernel) ( + cl_kernel source_kernel, + cl_int* errcode_ret ); + + cl_int (CLI_API_CALL *clGetKernelSubGroupInfo) ( + cl_kernel kernel, + cl_device_id device, + cl_kernel_sub_group_info param_name, + size_t input_value_size, + const void* input_value, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret ); + + cl_int (CLI_API_CALL *clEnqueueSVMMigrateMem) ( + cl_command_queue command_queue, + cl_uint num_svm_pointers, + const void** svm_pointers, + const size_t* sizes, + cl_mem_migration_flags flags, + cl_uint num_events_in_wait_list, + const cl_event* event_wait_list, + cl_event* event ); + + // These are Khronos Extensions. + // They aren't exported from the ICD or from this DLL, but we'll still + // put a pointer to them in the CLIntercept dispatch table. + + // cl_khr_gl_sharing + cl_int (CLI_API_CALL *clGetGLContextInfoKHR) ( + const cl_context_properties *properties, + cl_gl_context_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret); + + // cl_khr_gl_event + cl_event (CLI_API_CALL *clCreateEventFromGLsyncKHR) ( + cl_context context, + cl_GLsync sync, + cl_int* errcode_ret); + +#if defined(_WIN32) + // cl_khr_d3d10_sharing + cl_int (CLI_API_CALL *clGetDeviceIDsFromD3D10KHR) ( + cl_platform_id platform, + cl_d3d10_device_source_khr d3d_device_source, + void* d3d_object, + cl_d3d10_device_set_khr d3d_device_set, + cl_uint num_entries, + cl_device_id* devices, + cl_uint* num_devices); + + // cl_khr_d3d10_sharing + cl_mem (CLI_API_CALL *clCreateFromD3D10BufferKHR) ( + cl_context context, + cl_mem_flags flags, + ID3D10Buffer* resource, + cl_int* errcode_ret); + + // cl_khr_d3d10_sharing + cl_mem (CLI_API_CALL *clCreateFromD3D10Texture2DKHR) ( + cl_context context, + cl_mem_flags flags, + ID3D10Texture2D* resource, + UINT subresource, + cl_int* errcode_ret); + + // cl_khr_d3d10_sharing + cl_mem (CLI_API_CALL *clCreateFromD3D10Texture3DKHR) ( + cl_context context, + cl_mem_flags flags, + ID3D10Texture3D* resource, + UINT subresource, + cl_int* errcode_ret); + + // cl_khr_d3d10_sharing + cl_int (CLI_API_CALL *clEnqueueAcquireD3D10ObjectsKHR) ( + 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_khr_d3d10_sharing + cl_int (CLI_API_CALL *clEnqueueReleaseD3D10ObjectsKHR) ( + 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_khr_d3d11_sharing + cl_int (CLI_API_CALL *clGetDeviceIDsFromD3D11KHR) ( + cl_platform_id platform, + cl_d3d11_device_source_khr d3d_device_source, + void* d3d_object, + cl_d3d11_device_set_khr d3d_device_set, + cl_uint num_entries, + cl_device_id* devices, + cl_uint* num_devices); + + // cl_khr_d3d11_sharing + cl_mem (CLI_API_CALL *clCreateFromD3D11BufferKHR) ( + cl_context context, + cl_mem_flags flags, + ID3D11Buffer* resource, + cl_int* errcode_ret); + + // cl_khr_d3d11_sharing + cl_mem (CLI_API_CALL *clCreateFromD3D11Texture2DKHR) ( + cl_context context, + cl_mem_flags flags, + ID3D11Texture2D* resource, + UINT subresource, + cl_int* errcode_ret); + + // cl_khr_d3d11_sharing + cl_mem (CLI_API_CALL *clCreateFromD3D11Texture3DKHR) ( + cl_context context, + cl_mem_flags flags, + ID3D11Texture3D* resource, + UINT subresource, + cl_int* errcode_ret); + + // cl_khr_d3d11_sharing + cl_int (CLI_API_CALL *clEnqueueAcquireD3D11ObjectsKHR) ( + 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_khr_d3d11_sharing + cl_int (CLI_API_CALL *clEnqueueReleaseD3D11ObjectsKHR) ( + 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_khr_dx9_media_sharing + cl_int (CLI_API_CALL *clGetDeviceIDsFromDX9MediaAdapterKHR) ( + cl_platform_id platform, + cl_uint num_media_adapters, + cl_dx9_media_adapter_type_khr* media_adapters_type, + void* media_adapters, + cl_dx9_media_adapter_set_khr media_adapter_set, + cl_uint num_entries, + cl_device_id* devices, + cl_uint* num_devices); + + // cl_khr_dx9_media_sharing + cl_mem (CLI_API_CALL *clCreateFromDX9MediaSurfaceKHR) ( + cl_context context, + cl_mem_flags flags, + cl_dx9_media_adapter_type_khr adapter_type, + void* surface_info, + cl_uint plane, + cl_int* errcode_ret); + + // cl_khr_dx9_media_sharing + cl_int (CLI_API_CALL *clEnqueueAcquireDX9MediaSurfacesKHR) ( + 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_khr_dx9_media_sharing + cl_int (CLI_API_CALL *clEnqueueReleaseDX9MediaSurfacesKHR) ( + 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_il_program + cl_program (CLI_API_CALL *clCreateProgramWithILKHR) ( + cl_context context, + const void *il, + size_t length, + cl_int *errcode_ret); + + // cl_khr_subgroups + cl_int (CLI_API_CALL *clGetKernelSubGroupInfoKHR) ( + cl_kernel kernel, + cl_device_id device, + cl_kernel_sub_group_info param_name, + size_t input_value_size, + const void* input_value, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret); + + // cl_khr_create_command_queue + cl_command_queue (CLI_API_CALL *clCreateCommandQueueWithPropertiesKHR) ( + cl_context context, + cl_device_id device, + const cl_queue_properties_khr* properties, + cl_int* errcode_ret); + + // These are Intel Vendor Extensions. + // They aren't exported from the ICD or from this DLL, but we'll still + // put a pointer to them in the CLIntercept dispatch table. + +#if defined(_WIN32) + // cl_intel_dx9_media_sharing + cl_int (CLI_API_CALL *clGetDeviceIDsFromDX9INTEL) ( + cl_platform_id platform, + cl_dx9_device_source_intel d3d_device_source, + void *dx9_object, + cl_dx9_device_set_intel d3d_device_set, + cl_uint num_entries, + cl_device_id* devices, + cl_uint* num_devices ); + + // cl_intel_dx9_media_sharing + cl_mem (CLI_API_CALL *clCreateFromDX9MediaSurfaceINTEL) ( + cl_context context, + cl_mem_flags flags, + IDirect3DSurface9* resource, + HANDLE sharedHandle, + UINT plane, + cl_int* errcode_ret ); + + // cl_intel_dx9_media_sharing + cl_int (CLI_API_CALL *clEnqueueAcquireDX9ObjectsINTEL) ( + 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_intel_dx9_media_sharing + cl_int (CLI_API_CALL *clEnqueueReleaseDX9ObjectsINTEL) ( + 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 + + // Unofficial MDAPI extension: + cl_command_queue (CLI_API_CALL *clCreatePerfCountersCommandQueueINTEL) ( + cl_context context, + cl_device_id device, + cl_command_queue_properties properties, + cl_uint configuration, + cl_int* errcode_ret); + + // Unofficial MDAPI extension: + cl_int (CL_API_CALL *clSetPerformanceConfigurationINTEL)( + cl_device_id device, + cl_uint count, + cl_uint* offsets, + cl_uint* values ); + + // cl_intel_accelerator + cl_accelerator_intel (CLI_API_CALL *clCreateAcceleratorINTEL) ( + cl_context context, + cl_accelerator_type_intel accelerator_type, + size_t descriptor_size, + const void* descriptor, + cl_int* errcode_ret ); + + // cl_intel_accelerator + cl_int (CLI_API_CALL *clGetAcceleratorInfoINTEL) ( + cl_accelerator_intel accelerator, + cl_accelerator_info_intel param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret ); + + // cl_intel_accelerator + cl_int (CLI_API_CALL *clRetainAcceleratorINTEL) ( + cl_accelerator_intel accelerator ); + + // cl_intel_accelerator + cl_int (CLI_API_CALL *clReleaseAcceleratorINTEL) ( + cl_accelerator_intel accelerator ); + + // cl_intel_va_api_media_sharing + cl_int (CLI_API_CALL *clGetDeviceIDsFromVA_APIMediaAdapterINTEL) ( + cl_platform_id platform, + cl_va_api_device_source_intel media_adapter_type, + void *media_adapter, + cl_va_api_device_set_intel media_adapter_set, + cl_uint num_entries, + cl_device_id *devices, + cl_uint *num_devices); + + // cl_intel_va_api_media_sharing + cl_mem (CLI_API_CALL *clCreateFromVA_APIMediaSurfaceINTEL) ( + cl_context context, + cl_mem_flags flags, + VASurfaceID *surface, + cl_uint plane, + cl_int *errcode_ret); + + // cl_intel_va_api_media_sharing + cl_int (CLI_API_CALL *clEnqueueAcquireVA_APIMediaSurfacesINTEL) ( + 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_intel_va_api_media_sharing + cl_int (CLI_API_CALL *clEnqueueReleaseVA_APIMediaSurfacesINTEL) ( + 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); +}; + +extern CLdispatch dummyDispatch; diff --git a/Src/enummap.cpp b/Src/enummap.cpp new file mode 100644 index 00000000..0d30cc12 --- /dev/null +++ b/Src/enummap.cpp @@ -0,0 +1,973 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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 "enummap.h" + +#include "common.h" + +/* if( _map.find( _enum ) != _map.end() ) fprintf(stderr, "Already found an entry for %08X (%d): new %s, old %s\n", _enum, _enum, #_enum, _map[ _enum ].c_str() ); \ */ + +#define ADD_ENUM_NAME( _map, _enum ) \ +{ \ + CLI_ASSERT( _map.find( _enum ) == _map.end() ); \ + _map[ _enum ] = #_enum; \ +} + +CEnumNameMap::CEnumNameMap() +{ + /* Error Codes */ + ADD_ENUM_NAME( m_cl_int, CL_SUCCESS ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_NOT_FOUND ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_NOT_AVAILABLE ); + ADD_ENUM_NAME( m_cl_int, CL_COMPILER_NOT_AVAILABLE ); + ADD_ENUM_NAME( m_cl_int, CL_MEM_OBJECT_ALLOCATION_FAILURE ); + ADD_ENUM_NAME( m_cl_int, CL_OUT_OF_RESOURCES ); + ADD_ENUM_NAME( m_cl_int, CL_OUT_OF_HOST_MEMORY ); + ADD_ENUM_NAME( m_cl_int, CL_PROFILING_INFO_NOT_AVAILABLE ); + ADD_ENUM_NAME( m_cl_int, CL_MEM_COPY_OVERLAP ); + ADD_ENUM_NAME( m_cl_int, CL_IMAGE_FORMAT_MISMATCH ); + ADD_ENUM_NAME( m_cl_int, CL_IMAGE_FORMAT_NOT_SUPPORTED ); + ADD_ENUM_NAME( m_cl_int, CL_BUILD_PROGRAM_FAILURE ); + ADD_ENUM_NAME( m_cl_int, CL_MAP_FAILURE ); + ADD_ENUM_NAME( m_cl_int, CL_MISALIGNED_SUB_BUFFER_OFFSET ); + ADD_ENUM_NAME( m_cl_int, CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST ); + ADD_ENUM_NAME( m_cl_int, CL_COMPILE_PROGRAM_FAILURE ); + ADD_ENUM_NAME( m_cl_int, CL_LINKER_NOT_AVAILABLE ); + ADD_ENUM_NAME( m_cl_int, CL_LINK_PROGRAM_FAILURE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PARTITION_FAILED ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_ARG_INFO_NOT_AVAILABLE ); + + ADD_ENUM_NAME( m_cl_int, CL_INVALID_VALUE ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_DEVICE_TYPE ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_PLATFORM ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_DEVICE ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_CONTEXT ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_QUEUE_PROPERTIES ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_COMMAND_QUEUE ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_HOST_PTR ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_MEM_OBJECT ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_IMAGE_FORMAT_DESCRIPTOR ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_IMAGE_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_SAMPLER ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_BINARY ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_BUILD_OPTIONS ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_PROGRAM ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_PROGRAM_EXECUTABLE ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_KERNEL_NAME ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_KERNEL_DEFINITION ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_KERNEL ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_ARG_INDEX ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_ARG_VALUE ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_ARG_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_KERNEL_ARGS ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_WORK_DIMENSION ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_WORK_GROUP_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_WORK_ITEM_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_GLOBAL_OFFSET ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_EVENT_WAIT_LIST ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_EVENT ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_OPERATION ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_GL_OBJECT ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_BUFFER_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_MIP_LEVEL ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_GLOBAL_WORK_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_PROPERTY ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_IMAGE_DESCRIPTOR ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_COMPILER_OPTIONS ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_LINKER_OPTIONS ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_DEVICE_PARTITION_COUNT ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_PIPE_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_DEVICE_QUEUE ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_SPEC_ID ); + ADD_ENUM_NAME( m_cl_int, CL_MAX_SIZE_RESTRICTION_EXCEEDED ); + + /* OpenCL Version */ + //CL_VERSION_1_0 1 + //CL_VERSION_1_1 1 + //CL_VERSION_1_2 1 + //CL_VERSION_2_0 1 + //CL_VERSION_2_1 1 + //CL_VERSION_2_2 1 + + /* cl_bool */ + ADD_ENUM_NAME( m_cl_bool, CL_FALSE ); + ADD_ENUM_NAME( m_cl_bool, CL_TRUE ); + //CL_BLOCKING CL_TRUE + //CL_NON_BLOCKING CL_FALSE + + /* cl_platform_info */ + ADD_ENUM_NAME( m_cl_int, CL_PLATFORM_PROFILE ); + ADD_ENUM_NAME( m_cl_int, CL_PLATFORM_VERSION ); + ADD_ENUM_NAME( m_cl_int, CL_PLATFORM_NAME ); + ADD_ENUM_NAME( m_cl_int, CL_PLATFORM_VENDOR ); + ADD_ENUM_NAME( m_cl_int, CL_PLATFORM_EXTENSIONS ); + ADD_ENUM_NAME( m_cl_int, CL_PLATFORM_HOST_TIMER_RESOLUTION ); + + /* cl_device_type - bitfield */ + ADD_ENUM_NAME( m_cl_device_type, CL_DEVICE_TYPE_DEFAULT ); + ADD_ENUM_NAME( m_cl_device_type, CL_DEVICE_TYPE_CPU ); + ADD_ENUM_NAME( m_cl_device_type, CL_DEVICE_TYPE_GPU ); + ADD_ENUM_NAME( m_cl_device_type, CL_DEVICE_TYPE_ACCELERATOR ); + ADD_ENUM_NAME( m_cl_device_type, CL_DEVICE_TYPE_CUSTOM ); + ADD_ENUM_NAME( m_cl_device_type, CL_DEVICE_TYPE_ALL ); + + /* cl_device_info */ + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_TYPE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_VENDOR_ID ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_MAX_COMPUTE_UNITS ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_MAX_WORK_GROUP_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_MAX_WORK_ITEM_SIZES ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_MAX_CLOCK_FREQUENCY ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_ADDRESS_BITS ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_MAX_READ_IMAGE_ARGS ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_MAX_WRITE_IMAGE_ARGS ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_MAX_MEM_ALLOC_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_IMAGE2D_MAX_WIDTH ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_IMAGE2D_MAX_HEIGHT ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_IMAGE3D_MAX_WIDTH ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_IMAGE3D_MAX_HEIGHT ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_IMAGE3D_MAX_DEPTH ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_IMAGE_SUPPORT ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_MAX_PARAMETER_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_MAX_SAMPLERS ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_MEM_BASE_ADDR_ALIGN ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_SINGLE_FP_CONFIG ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_GLOBAL_MEM_CACHE_TYPE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_GLOBAL_MEM_CACHE_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_GLOBAL_MEM_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_MAX_CONSTANT_ARGS ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_LOCAL_MEM_TYPE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_LOCAL_MEM_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_ERROR_CORRECTION_SUPPORT ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PROFILING_TIMER_RESOLUTION ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_ENDIAN_LITTLE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_AVAILABLE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_COMPILER_AVAILABLE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_EXECUTION_CAPABILITIES ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_QUEUE_PROPERTIES ); + // Same value as CL_DEVICE_QUEUE_PROPERTIES: + //CL_DEVICE_QUEUE_ON_HOST_PROPERTIES 0x102A + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_NAME ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_VENDOR ); + ADD_ENUM_NAME( m_cl_int, CL_DRIVER_VERSION ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PROFILE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_VERSION ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_EXTENSIONS ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PLATFORM ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_DOUBLE_FP_CONFIG ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_HALF_FP_CONFIG ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_HOST_UNIFIED_MEMORY ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_NATIVE_VECTOR_WIDTH_INT ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_OPENCL_C_VERSION ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_LINKER_AVAILABLE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_BUILT_IN_KERNELS ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_IMAGE_MAX_BUFFER_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_IMAGE_MAX_ARRAY_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PARENT_DEVICE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PARTITION_MAX_SUB_DEVICES ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PARTITION_PROPERTIES ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PARTITION_AFFINITY_DOMAIN ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PARTITION_TYPE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_REFERENCE_COUNT ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PREFERRED_INTEROP_USER_SYNC ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PRINTF_BUFFER_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_IMAGE_PITCH_ALIGNMENT ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_IMAGE_BASE_ADDRESS_ALIGNMENT ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_MAX_READ_WRITE_IMAGE_ARGS ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_MAX_GLOBAL_VARIABLE_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_QUEUE_ON_DEVICE_PROPERTIES ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_QUEUE_ON_DEVICE_PREFERRED_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_QUEUE_ON_DEVICE_MAX_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_MAX_ON_DEVICE_QUEUES ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_MAX_ON_DEVICE_EVENTS ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_SVM_CAPABILITIES ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_GLOBAL_VARIABLE_PREFERRED_TOTAL_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_MAX_PIPE_ARGS ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PIPE_MAX_ACTIVE_RESERVATIONS ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PIPE_MAX_PACKET_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PREFERRED_PLATFORM_ATOMIC_ALIGNMENT ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PREFERRED_GLOBAL_ATOMIC_ALIGNMENT ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PREFERRED_LOCAL_ATOMIC_ALIGNMENT ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_IL_VERSION ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_MAX_NUM_SUB_GROUPS ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_SUB_GROUP_INDEPENDENT_FORWARD_PROGRESS ); + + /* cl_device_fp_config - bitfield */ + ADD_ENUM_NAME( m_cl_device_fp_config, CL_FP_DENORM ); + ADD_ENUM_NAME( m_cl_device_fp_config, CL_FP_INF_NAN ); + ADD_ENUM_NAME( m_cl_device_fp_config, CL_FP_ROUND_TO_NEAREST ); + ADD_ENUM_NAME( m_cl_device_fp_config, CL_FP_ROUND_TO_ZERO ); + ADD_ENUM_NAME( m_cl_device_fp_config, CL_FP_ROUND_TO_INF ); + ADD_ENUM_NAME( m_cl_device_fp_config, CL_FP_FMA ); + ADD_ENUM_NAME( m_cl_device_fp_config, CL_FP_SOFT_FLOAT ); + ADD_ENUM_NAME( m_cl_device_fp_config, CL_FP_CORRECTLY_ROUNDED_DIVIDE_SQRT ); + + /* cl_device_mem_cache_type */ + ADD_ENUM_NAME( m_cl_device_mem_cache_type, CL_NONE ); + ADD_ENUM_NAME( m_cl_device_mem_cache_type, CL_READ_ONLY_CACHE ); + ADD_ENUM_NAME( m_cl_device_mem_cache_type, CL_READ_WRITE_CACHE ); + + /* cl_device_local_mem_type */ + ADD_ENUM_NAME( m_cl_device_local_mem_type, CL_LOCAL ); + ADD_ENUM_NAME( m_cl_device_local_mem_type, CL_GLOBAL ); + + /* cl_device_exec_capabilities - bitfield */ + ADD_ENUM_NAME( m_cl_device_exec_capabilities, CL_EXEC_KERNEL ); + ADD_ENUM_NAME( m_cl_device_exec_capabilities, CL_EXEC_NATIVE_KERNEL ); + + /* cl_command_queue_properties - bitfield */ + ADD_ENUM_NAME( m_cl_command_queue_properties, CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE ); + ADD_ENUM_NAME( m_cl_command_queue_properties, CL_QUEUE_PROFILING_ENABLE ); + ADD_ENUM_NAME( m_cl_command_queue_properties, CL_QUEUE_ON_DEVICE ); + ADD_ENUM_NAME( m_cl_command_queue_properties, CL_QUEUE_ON_DEVICE_DEFAULT ); + + /* cl_context_info */ + ADD_ENUM_NAME( m_cl_int, CL_CONTEXT_REFERENCE_COUNT ); + ADD_ENUM_NAME( m_cl_int, CL_CONTEXT_DEVICES ); + ADD_ENUM_NAME( m_cl_int, CL_CONTEXT_PROPERTIES ); + ADD_ENUM_NAME( m_cl_int, CL_CONTEXT_NUM_DEVICES ); + + /* cl_context_properties */ + ADD_ENUM_NAME( m_cl_int, CL_CONTEXT_PLATFORM ); + ADD_ENUM_NAME( m_cl_int, CL_CONTEXT_INTEROP_USER_SYNC ); + + /* cl_device_partition_property */ + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PARTITION_EQUALLY ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PARTITION_BY_COUNTS ); + //CL_DEVICE_PARTITION_BY_COUNTS_LIST_END 0x0 + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PARTITION_BY_AFFINITY_DOMAIN ); + + /* cl_device_affinity_domain */ + ADD_ENUM_NAME( m_cl_device_affinity_domain, CL_DEVICE_AFFINITY_DOMAIN_NUMA ); + ADD_ENUM_NAME( m_cl_device_affinity_domain, CL_DEVICE_AFFINITY_DOMAIN_L4_CACHE ); + ADD_ENUM_NAME( m_cl_device_affinity_domain, CL_DEVICE_AFFINITY_DOMAIN_L3_CACHE ); + ADD_ENUM_NAME( m_cl_device_affinity_domain, CL_DEVICE_AFFINITY_DOMAIN_L2_CACHE ); + ADD_ENUM_NAME( m_cl_device_affinity_domain, CL_DEVICE_AFFINITY_DOMAIN_L1_CACHE ); + ADD_ENUM_NAME( m_cl_device_affinity_domain, CL_DEVICE_AFFINITY_DOMAIN_NEXT_PARTITIONABLE ); + + /* cl_device_svm_capabilities */ + ADD_ENUM_NAME( m_cl_device_svm_capabilities, CL_DEVICE_SVM_COARSE_GRAIN_BUFFER ); + ADD_ENUM_NAME( m_cl_device_svm_capabilities, CL_DEVICE_SVM_FINE_GRAIN_BUFFER ); + ADD_ENUM_NAME( m_cl_device_svm_capabilities, CL_DEVICE_SVM_FINE_GRAIN_SYSTEM ); + ADD_ENUM_NAME( m_cl_device_svm_capabilities, CL_DEVICE_SVM_ATOMICS ); + + /* cl_command_queue_info */ + ADD_ENUM_NAME( m_cl_int, CL_QUEUE_CONTEXT ); + ADD_ENUM_NAME( m_cl_int, CL_QUEUE_DEVICE ); + ADD_ENUM_NAME( m_cl_int, CL_QUEUE_REFERENCE_COUNT ); + ADD_ENUM_NAME( m_cl_int, CL_QUEUE_PROPERTIES ); + ADD_ENUM_NAME( m_cl_int, CL_QUEUE_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_QUEUE_DEVICE_DEFAULT ); + + /* cl_mem_flags - bitfield */ + ADD_ENUM_NAME( m_cl_mem_flags, CL_MEM_READ_WRITE ); + ADD_ENUM_NAME( m_cl_mem_flags, CL_MEM_WRITE_ONLY ); + ADD_ENUM_NAME( m_cl_mem_flags, CL_MEM_READ_ONLY ); + ADD_ENUM_NAME( m_cl_mem_flags, CL_MEM_USE_HOST_PTR ); + ADD_ENUM_NAME( m_cl_mem_flags, CL_MEM_ALLOC_HOST_PTR ); + ADD_ENUM_NAME( m_cl_mem_flags, CL_MEM_COPY_HOST_PTR ); + // reserved (1 << 6) + ADD_ENUM_NAME( m_cl_mem_flags, CL_MEM_HOST_WRITE_ONLY ); + ADD_ENUM_NAME( m_cl_mem_flags, CL_MEM_HOST_READ_ONLY ); + ADD_ENUM_NAME( m_cl_mem_flags, CL_MEM_HOST_NO_ACCESS ); + ADD_ENUM_NAME( m_cl_mem_flags, CL_MEM_KERNEL_READ_AND_WRITE ); + + /* cl_mem_migration_flags - bitfield */ + ADD_ENUM_NAME( m_cl_mem_migration_flags, CL_MIGRATE_MEM_OBJECT_HOST ); + ADD_ENUM_NAME( m_cl_mem_migration_flags, CL_MIGRATE_MEM_OBJECT_CONTENT_UNDEFINED ); + + /* cl_channel_order */ + ADD_ENUM_NAME( m_cl_int, CL_R ); + ADD_ENUM_NAME( m_cl_int, CL_A ); + ADD_ENUM_NAME( m_cl_int, CL_RG ); + ADD_ENUM_NAME( m_cl_int, CL_RA ); + ADD_ENUM_NAME( m_cl_int, CL_RGB ); + ADD_ENUM_NAME( m_cl_int, CL_RGBA ); + ADD_ENUM_NAME( m_cl_int, CL_BGRA ); + ADD_ENUM_NAME( m_cl_int, CL_ARGB ); + ADD_ENUM_NAME( m_cl_int, CL_INTENSITY ); + ADD_ENUM_NAME( m_cl_int, CL_LUMINANCE ); + ADD_ENUM_NAME( m_cl_int, CL_Rx ); + ADD_ENUM_NAME( m_cl_int, CL_RGx ); + ADD_ENUM_NAME( m_cl_int, CL_RGBx ); + ADD_ENUM_NAME( m_cl_int, CL_DEPTH ); + ADD_ENUM_NAME( m_cl_int, CL_DEPTH_STENCIL ); + ADD_ENUM_NAME( m_cl_int, CL_sRGB ); + ADD_ENUM_NAME( m_cl_int, CL_sRGBx ); + ADD_ENUM_NAME( m_cl_int, CL_sRGBA ); + ADD_ENUM_NAME( m_cl_int, CL_sBGRA ); + ADD_ENUM_NAME( m_cl_int, CL_ABGR ); + + /* cl_channel_type */ + ADD_ENUM_NAME( m_cl_int, CL_SNORM_INT8 ); + ADD_ENUM_NAME( m_cl_int, CL_SNORM_INT16 ); + ADD_ENUM_NAME( m_cl_int, CL_UNORM_INT8 ); + ADD_ENUM_NAME( m_cl_int, CL_UNORM_INT16 ); + ADD_ENUM_NAME( m_cl_int, CL_UNORM_SHORT_565 ); + ADD_ENUM_NAME( m_cl_int, CL_UNORM_SHORT_555 ); + ADD_ENUM_NAME( m_cl_int, CL_UNORM_INT_101010 ); + ADD_ENUM_NAME( m_cl_int, CL_SIGNED_INT8 ); + ADD_ENUM_NAME( m_cl_int, CL_SIGNED_INT16 ); + ADD_ENUM_NAME( m_cl_int, CL_SIGNED_INT32 ); + ADD_ENUM_NAME( m_cl_int, CL_UNSIGNED_INT8 ); + ADD_ENUM_NAME( m_cl_int, CL_UNSIGNED_INT16 ); + ADD_ENUM_NAME( m_cl_int, CL_UNSIGNED_INT32 ); + ADD_ENUM_NAME( m_cl_int, CL_HALF_FLOAT ); + ADD_ENUM_NAME( m_cl_int, CL_FLOAT ); + ADD_ENUM_NAME( m_cl_int, CL_UNORM_INT24 ); + ADD_ENUM_NAME( m_cl_int, CL_UNORM_INT_101010_2 ); + + /* cl_mem_object_type */ + ADD_ENUM_NAME( m_cl_int, CL_MEM_OBJECT_BUFFER ); + ADD_ENUM_NAME( m_cl_int, CL_MEM_OBJECT_IMAGE2D ); + ADD_ENUM_NAME( m_cl_int, CL_MEM_OBJECT_IMAGE3D ); + ADD_ENUM_NAME( m_cl_int, CL_MEM_OBJECT_IMAGE2D_ARRAY ); + ADD_ENUM_NAME( m_cl_int, CL_MEM_OBJECT_IMAGE1D ); + ADD_ENUM_NAME( m_cl_int, CL_MEM_OBJECT_IMAGE1D_ARRAY ); + ADD_ENUM_NAME( m_cl_int, CL_MEM_OBJECT_IMAGE1D_BUFFER ); + ADD_ENUM_NAME( m_cl_int, CL_MEM_OBJECT_PIPE ); + + /* cl_mem_info */ + ADD_ENUM_NAME( m_cl_int, CL_MEM_TYPE ); + ADD_ENUM_NAME( m_cl_int, CL_MEM_FLAGS ); + ADD_ENUM_NAME( m_cl_int, CL_MEM_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_MEM_HOST_PTR ); + ADD_ENUM_NAME( m_cl_int, CL_MEM_MAP_COUNT ); + ADD_ENUM_NAME( m_cl_int, CL_MEM_REFERENCE_COUNT ); + ADD_ENUM_NAME( m_cl_int, CL_MEM_CONTEXT ); + ADD_ENUM_NAME( m_cl_int, CL_MEM_ASSOCIATED_MEMOBJECT ); + ADD_ENUM_NAME( m_cl_int, CL_MEM_OFFSET ); + ADD_ENUM_NAME( m_cl_int, CL_MEM_USES_SVM_POINTER ); + + /* cl_image_info */ + ADD_ENUM_NAME( m_cl_int, CL_IMAGE_FORMAT ); + ADD_ENUM_NAME( m_cl_int, CL_IMAGE_ELEMENT_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_IMAGE_ROW_PITCH ); + ADD_ENUM_NAME( m_cl_int, CL_IMAGE_SLICE_PITCH ); + ADD_ENUM_NAME( m_cl_int, CL_IMAGE_WIDTH ); + ADD_ENUM_NAME( m_cl_int, CL_IMAGE_HEIGHT ); + ADD_ENUM_NAME( m_cl_int, CL_IMAGE_DEPTH ); + ADD_ENUM_NAME( m_cl_int, CL_IMAGE_ARRAY_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_IMAGE_BUFFER ); + ADD_ENUM_NAME( m_cl_int, CL_IMAGE_NUM_MIP_LEVELS ); + ADD_ENUM_NAME( m_cl_int, CL_IMAGE_NUM_SAMPLES ); + + /* cl_pipe_info */ + ADD_ENUM_NAME( m_cl_int, CL_PIPE_PACKET_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_PIPE_MAX_PACKETS ); + + /* cl_addressing_mode */ + ADD_ENUM_NAME( m_cl_int, CL_ADDRESS_NONE ); + ADD_ENUM_NAME( m_cl_int, CL_ADDRESS_CLAMP_TO_EDGE ); + ADD_ENUM_NAME( m_cl_int, CL_ADDRESS_CLAMP ); + ADD_ENUM_NAME( m_cl_int, CL_ADDRESS_REPEAT ); + ADD_ENUM_NAME( m_cl_int, CL_ADDRESS_MIRRORED_REPEAT ); + + /* cl_filter_mode */ + ADD_ENUM_NAME( m_cl_int, CL_FILTER_NEAREST ); + ADD_ENUM_NAME( m_cl_int, CL_FILTER_LINEAR ); + + /* cl_sampler_info */ + ADD_ENUM_NAME( m_cl_int, CL_SAMPLER_REFERENCE_COUNT ); + ADD_ENUM_NAME( m_cl_int, CL_SAMPLER_CONTEXT ); + ADD_ENUM_NAME( m_cl_int, CL_SAMPLER_NORMALIZED_COORDS ); + ADD_ENUM_NAME( m_cl_int, CL_SAMPLER_ADDRESSING_MODE ); + ADD_ENUM_NAME( m_cl_int, CL_SAMPLER_FILTER_MODE ); + ADD_ENUM_NAME( m_cl_int, CL_SAMPLER_MIP_FILTER_MODE ); + ADD_ENUM_NAME( m_cl_int, CL_SAMPLER_LOD_MIN ); + ADD_ENUM_NAME( m_cl_int, CL_SAMPLER_LOD_MAX ); + + /* cl_map_flags - bitfield */ + ADD_ENUM_NAME( m_cl_map_flags, CL_MAP_READ ); + ADD_ENUM_NAME( m_cl_map_flags, CL_MAP_WRITE ); + ADD_ENUM_NAME( m_cl_map_flags, CL_MAP_WRITE_INVALIDATE_REGION ); + + /* cl_program_info */ + ADD_ENUM_NAME( m_cl_int, CL_PROGRAM_REFERENCE_COUNT ); + ADD_ENUM_NAME( m_cl_int, CL_PROGRAM_CONTEXT ); + ADD_ENUM_NAME( m_cl_int, CL_PROGRAM_NUM_DEVICES ); + ADD_ENUM_NAME( m_cl_int, CL_PROGRAM_DEVICES ); + ADD_ENUM_NAME( m_cl_int, CL_PROGRAM_SOURCE ); + ADD_ENUM_NAME( m_cl_int, CL_PROGRAM_BINARY_SIZES ); + ADD_ENUM_NAME( m_cl_int, CL_PROGRAM_BINARIES ); + ADD_ENUM_NAME( m_cl_int, CL_PROGRAM_NUM_KERNELS ); + ADD_ENUM_NAME( m_cl_int, CL_PROGRAM_KERNEL_NAMES ); + ADD_ENUM_NAME( m_cl_int, CL_PROGRAM_IL ); + ADD_ENUM_NAME( m_cl_int, CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT ); + ADD_ENUM_NAME( m_cl_int, CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT ); + + /* cl_program_build_info */ + ADD_ENUM_NAME( m_cl_int, CL_PROGRAM_BUILD_STATUS ); + ADD_ENUM_NAME( m_cl_int, CL_PROGRAM_BUILD_OPTIONS ); + ADD_ENUM_NAME( m_cl_int, CL_PROGRAM_BUILD_LOG ); + ADD_ENUM_NAME( m_cl_int, CL_PROGRAM_BINARY_TYPE ); + ADD_ENUM_NAME( m_cl_int, CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE ); + + /* cl_program_binary_type */ + ADD_ENUM_NAME( m_cl_program_binary_type, CL_PROGRAM_BINARY_TYPE_NONE ); + ADD_ENUM_NAME( m_cl_program_binary_type, CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT ); + ADD_ENUM_NAME( m_cl_program_binary_type, CL_PROGRAM_BINARY_TYPE_LIBRARY ); + ADD_ENUM_NAME( m_cl_program_binary_type, CL_PROGRAM_BINARY_TYPE_EXECUTABLE ); + + /* cl_build_status */ + ADD_ENUM_NAME( m_cl_build_status, CL_BUILD_SUCCESS ); + ADD_ENUM_NAME( m_cl_build_status, CL_BUILD_NONE ); + ADD_ENUM_NAME( m_cl_build_status, CL_BUILD_ERROR ); + ADD_ENUM_NAME( m_cl_build_status, CL_BUILD_IN_PROGRESS ); + + /* cl_kernel_info */ + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_FUNCTION_NAME ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_NUM_ARGS ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_REFERENCE_COUNT ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_CONTEXT ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_PROGRAM ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_ATTRIBUTES ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_MAX_NUM_SUB_GROUPS ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_COMPILE_NUM_SUB_GROUPS ); + + /* cl_kernel_arg_info */ + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_ARG_ADDRESS_QUALIFIER ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_ARG_ACCESS_QUALIFIER ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_ARG_TYPE_NAME ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_ARG_TYPE_QUALIFIER ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_ARG_NAME ); + + /* cl_kernel_arg_address_qualifier */ + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_ARG_ADDRESS_GLOBAL ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_ARG_ADDRESS_LOCAL ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_ARG_ADDRESS_CONSTANT ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_ARG_ADDRESS_PRIVATE ); + + /* cl_kernel_arg_access_qualifier */ + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_ARG_ACCESS_READ_ONLY ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_ARG_ACCESS_WRITE_ONLY ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_ARG_ACCESS_READ_WRITE ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_ARG_ACCESS_NONE ); + + /* cl_kernel_arg_type_qualifer */ + ADD_ENUM_NAME( m_cl_kernel_arg_type_qualifier, CL_KERNEL_ARG_TYPE_NONE ); + ADD_ENUM_NAME( m_cl_kernel_arg_type_qualifier, CL_KERNEL_ARG_TYPE_CONST ); + ADD_ENUM_NAME( m_cl_kernel_arg_type_qualifier, CL_KERNEL_ARG_TYPE_RESTRICT ); + ADD_ENUM_NAME( m_cl_kernel_arg_type_qualifier, CL_KERNEL_ARG_TYPE_VOLATILE ); + ADD_ENUM_NAME( m_cl_kernel_arg_type_qualifier, CL_KERNEL_ARG_TYPE_PIPE ); + + /* cl_kernel_work_group_info */ + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_WORK_GROUP_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_COMPILE_WORK_GROUP_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_LOCAL_MEM_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_PRIVATE_MEM_SIZE ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_GLOBAL_WORK_SIZE ); + + /* cl_kernel_sub_group_info */ + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_MAX_SUB_GROUP_SIZE_FOR_NDRANGE ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_SUB_GROUP_COUNT_FOR_NDRANGE ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_LOCAL_SIZE_FOR_SUB_GROUP_COUNT ); + + /* cl_kernel_exec_info */ + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_EXEC_INFO_SVM_PTRS ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_EXEC_INFO_SVM_FINE_GRAIN_SYSTEM ); + + /* cl_event_info */ + ADD_ENUM_NAME( m_cl_int, CL_EVENT_COMMAND_QUEUE ); + ADD_ENUM_NAME( m_cl_int, CL_EVENT_COMMAND_TYPE ); + ADD_ENUM_NAME( m_cl_int, CL_EVENT_REFERENCE_COUNT ); + ADD_ENUM_NAME( m_cl_int, CL_EVENT_COMMAND_EXECUTION_STATUS ); + ADD_ENUM_NAME( m_cl_int, CL_EVENT_CONTEXT ); + + /* cl_command_type */ + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_NDRANGE_KERNEL ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_TASK ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_NATIVE_KERNEL ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_READ_BUFFER ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_WRITE_BUFFER ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_COPY_BUFFER ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_READ_IMAGE ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_WRITE_IMAGE ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_COPY_IMAGE ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_COPY_IMAGE_TO_BUFFER ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_COPY_BUFFER_TO_IMAGE ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_MAP_BUFFER ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_MAP_IMAGE ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_UNMAP_MEM_OBJECT ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_MARKER ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_ACQUIRE_GL_OBJECTS ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_RELEASE_GL_OBJECTS ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_READ_BUFFER_RECT ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_WRITE_BUFFER_RECT ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_COPY_BUFFER_RECT ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_USER ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_BARRIER ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_MIGRATE_MEM_OBJECTS ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_FILL_BUFFER ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_FILL_IMAGE ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_SVM_FREE ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_SVM_MEMCPY ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_SVM_MEMFILL ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_SVM_MAP ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_SVM_UNMAP ); + + /* command execution status */ + ADD_ENUM_NAME( m_cl_command_exec_status, CL_COMPLETE ); + ADD_ENUM_NAME( m_cl_command_exec_status, CL_RUNNING ); + ADD_ENUM_NAME( m_cl_command_exec_status, CL_SUBMITTED ); + ADD_ENUM_NAME( m_cl_command_exec_status, CL_QUEUED ); + + /* cl_buffer_create_type */ + ADD_ENUM_NAME( m_cl_int, CL_BUFFER_CREATE_TYPE_REGION ); + + /* cl_profiling_info */ + ADD_ENUM_NAME( m_cl_int, CL_PROFILING_COMMAND_QUEUED ); + ADD_ENUM_NAME( m_cl_int, CL_PROFILING_COMMAND_SUBMIT ); + ADD_ENUM_NAME( m_cl_int, CL_PROFILING_COMMAND_START ); + ADD_ENUM_NAME( m_cl_int, CL_PROFILING_COMMAND_END ); + ADD_ENUM_NAME( m_cl_int, CL_PROFILING_COMMAND_COMPLETE ); + + /* cl_svm_mem_flags */ + ADD_ENUM_NAME( m_cl_svm_mem_flags, CL_MEM_READ_WRITE ); + ADD_ENUM_NAME( m_cl_svm_mem_flags, CL_MEM_WRITE_ONLY ); + ADD_ENUM_NAME( m_cl_svm_mem_flags, CL_MEM_READ_ONLY ); + ADD_ENUM_NAME( m_cl_svm_mem_flags, CL_MEM_SVM_FINE_GRAIN_BUFFER ); + ADD_ENUM_NAME( m_cl_svm_mem_flags, CL_MEM_SVM_ATOMICS ); + + // Intel Extensions + + // Unofficial kernel profiling extension: + ADD_ENUM_NAME( m_cl_int, CL_CONTEXT_KERNEL_PROFILING_MODES_COUNT_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_CONTEXT_KERNEL_PROFILING_MODE_INFO_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_IL_SYMBOLS_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_BINARY_PROGRAM_INTEL ); + + // Unofficial extension (for now) for VTune Debug Info: + ADD_ENUM_NAME( m_cl_int, CL_PROGRAM_DEBUG_INFO_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_PROGRAM_DEBUG_INFO_SIZES_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_BINARIES_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_BINARY_SIZES_INTEL ); + + // VME and VA + + // clGetDeviceInfo + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_ME_VERSION_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_TRANSFORM_MASK_MAX_WIDTH_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_TRANSFORM_MASK_MAX_HEIGHT_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_TRANSFORM_FILTER_MAX_WIDTH_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_TRANSFORM_FILTER_MAX_HEIGHT_INTEL ); + + // Error Codes + ADD_ENUM_NAME( m_cl_int, CL_INVALID_ACCELERATOR_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_ACCELERATOR_TYPE_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_ACCELERATOR_DESC_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_ACCELERATOR_TYPE_NOT_SUPPORTED_INTEL ); + + // cl_accelerator_type_intel + //CL_ACCELERATOR_TYPE_MOTION_ESTIMATION_INTEL 0x0 + + // cl_accelerator_info_intel + ADD_ENUM_NAME( m_cl_int, CL_ACCELERATOR_DESCRIPTOR_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_ACCELERATOR_REFERENCE_COUNT_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_ACCELERATOR_CONTEXT_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_ACCELERATOR_TYPE_INTEL ); + + // cl_motion_detect_desc_intel flags + //CL_ME_MB_TYPE_16x16_INTEL 0x0 + //CL_ME_MB_TYPE_8x8_INTEL 0x1 + //CL_ME_MB_TYPE_4x4_INTEL 0x2 + + //CL_ME_SUBPIXEL_MODE_INTEGER_INTEL 0x0 + //CL_ME_SUBPIXEL_MODE_HPEL_INTEL 0x1 + //CL_ME_SUBPIXEL_MODE_QPEL_INTEL 0x2 + + //CL_ME_SAD_ADJUST_MODE_NONE_INTEL 0x0 + //CL_ME_SAD_ADJUST_MODE_HAAR_INTEL 0x1 + + //CL_ME_SEARCH_PATH_RADIUS_2_2_INTEL 0x0 + //CL_ME_SEARCH_PATH_RADIUS_4_4_INTEL 0x1 + //CL_ME_SEARCH_PATH_RADIUS_16_12_INTEL 0x5 + + //CL_ME_CHROMA_INTRA_PREDICT_ENABLED_INTEL 0x1 + //CL_ME_LUMA_INTRA_PREDICT_ENABLED_INTEL 0x2 + + //CL_ME_COST_PENALTY_NONE_INTEL 0x0 + //CL_ME_COST_PENALTY_LOW_INTEL 0x1 + //CL_ME_COST_PENALTY_NORMAL_INTEL 0x2 + //CL_ME_COST_PENALTY_HIGH_INTEL 0x3 + + //CL_ME_COST_PRECISION_QPEL_INTEL 0x0 + //CL_ME_COST_PRECISION_HPEL_INTEL 0x1 + //CL_ME_COST_PRECISION_PEL_INTEL 0x2 + //CL_ME_COST_PRECISION_DPEL_INTEL 0x3 + + //CL_ME_VERSION_LEGACY_INTEL 0x0 + //CL_ME_VERSION_ADVANCED_VER_1_INTEL 0x1 + + // cl_intel_egl_image_yuv + ADD_ENUM_NAME( m_cl_int, CL_EGL_YUV_PLANE_INTEL ); + + // cl_intel_simultaneous_sharing + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_SIMULTANEOUS_INTEROPS_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_NUM_SIMULTANEOUS_INTEROPS_INTEL ); + + // cl_intel_thread_local_exec + ADD_ENUM_NAME( m_cl_command_queue_properties, CL_QUEUE_THREAD_LOCAL_EXEC_ENABLE_INTEL ); + + // cl_intel_va_api_media_sharing + + ADD_ENUM_NAME( m_cl_int, CL_VA_API_DISPLAY_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_PREFERRED_DEVICES_FOR_VA_API_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_ALL_DEVICES_FOR_VA_API_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_CONTEXT_VA_API_DISPLAY_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_MEM_VA_API_SURFACE_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_IMAGE_VA_API_PLANE_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_ACQUIRE_VA_API_MEDIA_SURFACES_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_RELEASE_VA_API_MEDIA_SURFACES_INTEL ); + + // Error Codes + ADD_ENUM_NAME( m_cl_int, CL_INVALID_VA_API_MEDIA_ADAPTER_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_VA_API_MEDIA_SURFACE_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_VA_API_MEDIA_SURFACE_ALREADY_ACQUIRED_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_VA_API_MEDIA_SURFACE_NOT_ACQUIRED_INTEL ); + + // cl_intel_packed_yuv + ADD_ENUM_NAME( m_cl_int, CL_YUYV_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_UYVY_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_YVYU_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_VYUY_INTEL ); + + // cl_intel_planar_yuv + + ADD_ENUM_NAME( m_cl_int, CL_NV12_INTEL ); + + ADD_ENUM_NAME( m_cl_mem_flags, CL_MEM_NO_ACCESS_INTEL ); + ADD_ENUM_NAME( m_cl_mem_flags, CL_MEM_ACCESS_FLAGS_UNRESTRICTED_INTEL ); + + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PLANAR_YUV_MAX_WIDTH_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PLANAR_YUV_MAX_HEIGHT_INTEL ); + + // cl_intel_required_subgroup_size + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_SUB_GROUP_SIZES_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_SPILL_MEM_SIZE_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_KERNEL_COMPILE_SUB_GROUP_SIZE_INTEL ); + + // cl_intel_driver_diagnostics + ADD_ENUM_NAME( m_cl_int, CL_CONTEXT_SHOW_DIAGNOSTICS_INTEL ); + + // cl_intelx_video_enhancement + // This is the base-functionality VEBox extension. + // Note: These are preview enum names and values! + + // cl_device_info + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_VE_VERSION_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_VE_ENGINE_COUNT_INTEL ); + + // cl_queue_properties - TBD: is this a general purpose enum or a bit? + ADD_ENUM_NAME( m_cl_int, CL_QUEUE_VE_ENABLE_INTEL ); + + // attribute_ids for cl_vebox_attrib_desc_intel + ADD_ENUM_NAME( m_cl_int, CL_VE_ACCELERATOR_ATTRIB_DENOISE_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_VE_ACCELERATOR_ATTRIB_DEINTERLACE_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_VE_ACCELERATOR_ATTRIB_HOT_PIXEL_CORR_INTEL ); + + // cl_accelerator_info_intel + ADD_ENUM_NAME( m_cl_int, CL_VE_ACCELERATOR_HISTOGRAMS_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_VE_ACCELERATOR_STATISTICS_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_VE_ACCELERATOR_STMM_INPUT_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_VE_ACCELERATOR_STMM_OUTPUT_INTEL ); + + // cl_intelx_ve_color_pipeline + // Note: These are preview enum names and values! + + // cl_device_info + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_VE_COLOR_PIPE_VERSION_INTEL ); + + // attribute_ids for cl_vebox_attrib_desc_intel + ADD_ENUM_NAME( m_cl_int, CL_VE_ACCELERATOR_ATTRIB_STD_STE_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_VE_ACCELERATOR_ATTRIB_GAMUT_COMP_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_VE_ACCELERATOR_ATTRIB_GECC_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_VE_ACCELERATOR_ATTRIB_ACE_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_VE_ACCELERATOR_ATTRIB_ACE_ADV_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_VE_ACCELERATOR_ATTRIB_TCC_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_VE_ACCELERATOR_ATTRIB_PROC_AMP_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_VE_ACCELERATOR_ATTRIB_BACK_END_CSC_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_VE_ACCELERATOR_ATTRIB_AOI_ALPHA_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_VE_ACCELERATOR_ATTRIB_CCM_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_VE_ACCELERATOR_ATTRIB_FWD_GAMMA_CORRECT_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_VE_ACCELERATOR_ATTRIB_FRONT_END_CSC_INTEL ); + + // cl_intelx_ve_camera_pipeline + // Note, these are preview enum names and values! + + // cl_device_info + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_VE_CAMERA_PIPE_VERSION_INTEL ); + + // attribute_ids for cl_vebox_attrib_desc_intel + ADD_ENUM_NAME( m_cl_int, CL_VE_ACCELERATOR_ATTRIB_BLACK_LEVEL_CORR_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_VE_ACCELERATOR_ATTRIB_DEMOSAIC_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_VE_ACCELERATOR_ATTRIB_WHITE_BALANCE_CORR_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_VE_ACCELERATOR_ATTRIB_VIGNETTE_INTEL ); + + // HEVC PAK + // Note, this extension is still in development! + + // cl_device_info + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PAK_VERSION_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PAK_AVAILABLE_CODECS_INTEL ); + + // cl_queue_properties / cl_command_queue_info + ADD_ENUM_NAME( m_cl_int, CL_QUEUE_PAK_ENABLE_INTEL ); + + // cl_accelerator_info_intel + ADD_ENUM_NAME( m_cl_int, CL_PAK_CTU_COUNT_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_PAK_CTU_WIDTH_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_PAK_CTU_HEIGHT_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_PAK_MAX_INTRA_DEPTH_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_PAK_MAX_INTER_DEPTH_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_PAK_NUM_CUS_PER_CTU_INTEL ); + ADD_ENUM_NAME( m_cl_int, CL_PAK_MV_BUFFER_SIZE_INTEL ); + + // Error Codes + // These are currently all mapped to CL_INVALID_VALUE. + // Need official error code assignment. + //ADD_ENUM_NAME( m_cl_int, CL_INVALID_PAK_CTU_SIZE_INTEL ); + //ADD_ENUM_NAME( m_cl_int, CL_INVALID_PAK_TU_SIZE_INTEL ); + //ADD_ENUM_NAME( m_cl_int, CL_INVALID_PAK_TU_INTRA_DEPTH_INTEL ); + //ADD_ENUM_NAME( m_cl_int, CL_INVALID_PAK_TU_INTER_DEPTH_INTEL ); + //ADD_ENUM_NAME( m_cl_int, CL_INVALID_PAK_BITRATE_RANGE_INTEL ); + //ADD_ENUM_NAME( m_cl_int, CL_INVALID_PAK_INSERTION_INTEL ); + //ADD_ENUM_NAME( m_cl_int, CL_INVALID_PAK_CTU_POSITION_INTEL ); + //ADD_ENUM_NAME( m_cl_int, CL_INVALID_PAK_REFERENCE_IMAGE_INDEX_INTEL ); + + // Altera Extensions: + + // cl_altera_device_temperature + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_CORE_TEMPERATURE_ALTERA ); + + // cl_altera_compiler_mode + ADD_ENUM_NAME( m_cl_int, CL_CONTEXT_COMPILER_MODE_ALTERA ); + ADD_ENUM_NAME( m_cl_int, CL_CONTEXT_PROGRAM_EXE_LIBRARY_ROOT_ALTERA ); + ADD_ENUM_NAME( m_cl_int, CL_CONTEXT_OFFLINE_DEVICE_ALTERA ); + + // These are enums from the Khronos cl_gl.h header file: + + // cl_gl_object_type + ADD_ENUM_NAME( m_cl_int, CL_GL_OBJECT_BUFFER ); + ADD_ENUM_NAME( m_cl_int, CL_GL_OBJECT_TEXTURE2D ); + ADD_ENUM_NAME( m_cl_int, CL_GL_OBJECT_TEXTURE3D ); + ADD_ENUM_NAME( m_cl_int, CL_GL_OBJECT_RENDERBUFFER ); + ADD_ENUM_NAME( m_cl_int, CL_GL_OBJECT_TEXTURE2D_ARRAY ); + ADD_ENUM_NAME( m_cl_int, CL_GL_OBJECT_TEXTURE1D ); + ADD_ENUM_NAME( m_cl_int, CL_GL_OBJECT_TEXTURE1D_ARRAY ); + ADD_ENUM_NAME( m_cl_int, CL_GL_OBJECT_TEXTURE_BUFFER ); + + // cl_gl_texture_info + ADD_ENUM_NAME( m_cl_int, CL_GL_TEXTURE_TARGET ); + ADD_ENUM_NAME( m_cl_int, CL_GL_MIPMAP_LEVEL ); + ADD_ENUM_NAME( m_cl_int, CL_GL_NUM_SAMPLES ); + + // Error Code + ADD_ENUM_NAME( m_cl_int, CL_INVALID_GL_SHAREGROUP_REFERENCE_KHR ); + + // cl_gl_context_info + ADD_ENUM_NAME( m_cl_int, CL_CURRENT_DEVICE_FOR_GL_CONTEXT_KHR ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICES_FOR_GL_CONTEXT_KHR ); + + // cl_context_properties + ADD_ENUM_NAME( m_cl_int, CL_GL_CONTEXT_KHR ); + ADD_ENUM_NAME( m_cl_int, CL_EGL_DISPLAY_KHR ); + ADD_ENUM_NAME( m_cl_int, CL_GLX_DISPLAY_KHR ); + ADD_ENUM_NAME( m_cl_int, CL_WGL_HDC_KHR ); + ADD_ENUM_NAME( m_cl_int, CL_CGL_SHAREGROUP_KHR ); + + // These enums are from the Khronos cl_gl_ext.h header file: + + // cl_khr_gl_event + ADD_ENUM_NAME( m_cl_int, CL_COMMAND_GL_FENCE_SYNC_OBJECT_KHR ); + + // These are enums from the Khronos cl_ext.h header file: + + // cl_khr_il_program + // These enums are core in OpenCL 2.1. + //CL_DEVICE_IL_VERSION_KHR 0x105B + //CL_PROGRAM_IL_KHR 0x1169 + + // cl_khr_icd + ADD_ENUM_NAME( m_cl_int, CL_PLATFORM_ICD_SUFFIX_KHR ); + ADD_ENUM_NAME( m_cl_int, CL_PLATFORM_NOT_FOUND_KHR ); + + // cl_khr_initalize_memory + ADD_ENUM_NAME( m_cl_int, CL_CONTEXT_MEMORY_INITIALIZE_KHR ); + + // cl_khr_terminate_context + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_TERMINATE_CAPABILITY_KHR ); + ADD_ENUM_NAME( m_cl_int, CL_CONTEXT_TERMINATE_KHR ); + + // cl_khr_spir + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_SPIR_VERSIONS ); + ADD_ENUM_NAME( m_cl_int, CL_PROGRAM_BINARY_TYPE_INTERMEDIATE ); + + // cl_khr_subgroups + // These enums were promoted to core in OpenCL 2.1. + //CL_KERNEL_MAX_SUB_GROUP_SIZE_FOR_NDRANGE_KHR 0x2033 + //CL_KERNEL_SUB_GROUP_COUNT_FOR_NDRANGE_KHR 0x2034 + + // cl_nv_device_attribute_query + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_COMPUTE_CAPABILITY_MAJOR_NV ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_COMPUTE_CAPABILITY_MINOR_NV ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_REGISTERS_PER_BLOCK_NV ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_WARP_SIZE_NV ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_GPU_OVERLAP_NV ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_KERNEL_EXEC_TIMEOUT_NV ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_INTEGRATED_MEMORY_NV ); + + // cl_ext_atomic_counters + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_MAX_ATOMIC_COUNTERS_EXT ); + + // cl_amd_device_attribute_query + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PROFILING_TIMER_OFFSET_AMD ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_TOPOLOGY_AMD ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_BOARD_NAME_AMD ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_GLOBAL_FREE_MEMORY_AMD ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_SIMD_PER_COMPUTE_UNIT_AMD ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_SIMD_WIDTH_AMD ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_SIMD_INSTRUCTION_WIDTH_AMD ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_WAVEFRONT_WIDTH_AMD ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_GLOBAL_MEM_CHANNELS_AMD ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_GLOBAL_MEM_CHANNEL_BANKS_AMD ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_GLOBAL_MEM_CHANNEL_BANK_WIDTH_AMD ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_LOCAL_MEM_SIZE_PER_COMPUTE_UNIT_AMD ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_LOCAL_MEM_BANKS_AMD ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_THREAD_TRACE_SUPPORTED_AMD ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_GFXIP_MAJOR_AMD ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_GFXIP_MINOR_AMD ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_AVAILABLE_ASYNC_QUEUES_AMD ); + + // cl_amd_offline_devices + ADD_ENUM_NAME( m_cl_int, CL_CONTEXT_OFFLINE_DEVICES_AMD ); + + // cl_ext_device_fission extension + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PARTITION_EQUALLY_EXT ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PARTITION_BY_COUNTS_EXT ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PARTITION_BY_NAMES_EXT ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PARTITION_BY_AFFINITY_DOMAIN_EXT ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PARENT_DEVICE_EXT ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PARTITION_TYPES_EXT ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_AFFINITY_DOMAINS_EXT ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_REFERENCE_COUNT_EXT ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PARTITION_STYLE_EXT ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PARTITION_FAILED_EXT ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_PARTITION_COUNT_EXT ); + ADD_ENUM_NAME( m_cl_int, CL_INVALID_PARTITION_NAME_EXT ); + + // cl_qcom_ext_host_ptr extension + ADD_ENUM_NAME( m_cl_mem_flags, CL_MEM_EXT_HOST_PTR_QCOM ); + + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_EXT_MEM_PADDING_IN_BYTES_QCOM ); + ADD_ENUM_NAME( m_cl_int, CL_DEVICE_PAGE_SIZE_QCOM ); + ADD_ENUM_NAME( m_cl_int, CL_IMAGE_ROW_ALIGNMENT_QCOM ); + ADD_ENUM_NAME( m_cl_int, CL_IMAGE_SLICE_ALIGNMENT_QCOM ); + ADD_ENUM_NAME( m_cl_int, CL_MEM_HOST_UNCACHED_QCOM ); + ADD_ENUM_NAME( m_cl_int, CL_MEM_HOST_WRITEBACK_QCOM ); + ADD_ENUM_NAME( m_cl_int, CL_MEM_HOST_WRITETHROUGH_QCOM ); + ADD_ENUM_NAME( m_cl_int, CL_MEM_HOST_WRITE_COMBINING_QCOM ); + + // cl_qcom_ion_host_ptr extension + ADD_ENUM_NAME( m_cl_int, CL_MEM_ION_HOST_PTR_QCOM ); + + // cl_arm_printf extension + ADD_ENUM_NAME( m_cl_int, CL_PRINTF_CALLBACK_ARM ); + ADD_ENUM_NAME( m_cl_int, CL_PRINTF_BUFFERSIZE_ARM ); + +#if !defined(__ANDROID__) && !defined(__APPLE__) + // gl texture targets + ADD_ENUM_NAME( m_GLenum, GL_TEXTURE_BUFFER ); + ADD_ENUM_NAME( m_GLenum, GL_TEXTURE_1D ); + ADD_ENUM_NAME( m_GLenum, GL_TEXTURE_1D_ARRAY ); + ADD_ENUM_NAME( m_GLenum, GL_TEXTURE_2D ); + ADD_ENUM_NAME( m_GLenum, GL_TEXTURE_2D_ARRAY ); + ADD_ENUM_NAME( m_GLenum, GL_TEXTURE_3D ); + ADD_ENUM_NAME( m_GLenum, GL_TEXTURE_CUBE_MAP_POSITIVE_X ); + ADD_ENUM_NAME( m_GLenum, GL_TEXTURE_CUBE_MAP_POSITIVE_Y ); + ADD_ENUM_NAME( m_GLenum, GL_TEXTURE_CUBE_MAP_POSITIVE_Z ); + ADD_ENUM_NAME( m_GLenum, GL_TEXTURE_CUBE_MAP_NEGATIVE_X ); + ADD_ENUM_NAME( m_GLenum, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y ); + ADD_ENUM_NAME( m_GLenum, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z ); + ADD_ENUM_NAME( m_GLenum, GL_TEXTURE_RECTANGLE ); + + // gl texture formats + ADD_ENUM_NAME( m_GLenum, GL_ALPHA ); + ADD_ENUM_NAME( m_GLenum, GL_RGB ); + ADD_ENUM_NAME( m_GLenum, GL_RGBA ); + ADD_ENUM_NAME( m_GLenum, GL_RGBA32F ); + ADD_ENUM_NAME( m_GLenum, GL_RGB32F ); + ADD_ENUM_NAME( m_GLenum, GL_RGBA16F ); + ADD_ENUM_NAME( m_GLenum, GL_RGB16F ); + ADD_ENUM_NAME( m_GLenum, GL_RGBA32UI ); + ADD_ENUM_NAME( m_GLenum, GL_RGB32UI ); + ADD_ENUM_NAME( m_GLenum, GL_RGBA16UI ); + ADD_ENUM_NAME( m_GLenum, GL_RGB16UI ); + ADD_ENUM_NAME( m_GLenum, GL_RGBA8UI ); + ADD_ENUM_NAME( m_GLenum, GL_RGB8UI ); + ADD_ENUM_NAME( m_GLenum, GL_RGBA32I ); + ADD_ENUM_NAME( m_GLenum, GL_RGB32I ); + ADD_ENUM_NAME( m_GLenum, GL_RGBA16I ); + ADD_ENUM_NAME( m_GLenum, GL_RGB16I ); + ADD_ENUM_NAME( m_GLenum, GL_RGBA8I ); + ADD_ENUM_NAME( m_GLenum, GL_RGB8I ); + ADD_ENUM_NAME( m_GLenum, GL_RG ); + ADD_ENUM_NAME( m_GLenum, GL_R8 ); + ADD_ENUM_NAME( m_GLenum, GL_R16 ); + ADD_ENUM_NAME( m_GLenum, GL_RG8 ); + ADD_ENUM_NAME( m_GLenum, GL_RG16 ); + ADD_ENUM_NAME( m_GLenum, GL_R16F ); + ADD_ENUM_NAME( m_GLenum, GL_R32F ); + ADD_ENUM_NAME( m_GLenum, GL_RG16F ); + ADD_ENUM_NAME( m_GLenum, GL_RG32F ); + ADD_ENUM_NAME( m_GLenum, GL_R8I ); + ADD_ENUM_NAME( m_GLenum, GL_R8UI ); + ADD_ENUM_NAME( m_GLenum, GL_R16I ); + ADD_ENUM_NAME( m_GLenum, GL_R16UI ); + ADD_ENUM_NAME( m_GLenum, GL_R32I ); + ADD_ENUM_NAME( m_GLenum, GL_R32UI ); + ADD_ENUM_NAME( m_GLenum, GL_RG8I ); + ADD_ENUM_NAME( m_GLenum, GL_RG8UI ); + ADD_ENUM_NAME( m_GLenum, GL_RG16I ); + ADD_ENUM_NAME( m_GLenum, GL_RG16UI ); + ADD_ENUM_NAME( m_GLenum, GL_RG32I ); + ADD_ENUM_NAME( m_GLenum, GL_RG32UI ); +#endif +} diff --git a/Src/enummap.h b/Src/enummap.h new file mode 100644 index 00000000..bd2af913 --- /dev/null +++ b/Src/enummap.h @@ -0,0 +1,129 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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. +*/ + +#pragma once + +#include "common.h" +#include "cli_ext.h" + +#include +#include + +class CEnumNameMap +{ +public: + CEnumNameMap(); + + #define GENERATE_MAP_AND_FUNC( _name, _type ) \ + private: \ + std::map< _type, std::string > m_##_type; \ + public: \ + std::string _name( _type e ) const \ + { \ + std::map< _type, std::string >::const_iterator i = \ + m_##_type.find( e ); \ + if( i == m_##_type.end() ) \ + { \ + return "**UNKNOWN ENUM**"; \ + } \ + else \ + { \ + return (*i).second; \ + } \ + } + + #define GENERATE_MAP_AND_BITFIELD_FUNC( _name, _type ) \ + private: \ + std::map< _type, std::string > m_##_type; \ + public: \ + std::string _name( _type e ) const \ + { \ + std::string ret = ""; \ + int bit = 0; \ + std::map< _type, std::string >::const_iterator i = \ + m_##_type.find( e ); \ + if( i != m_##_type.end() ) \ + { \ + ret += (*i).second; \ + } \ + else \ + { \ + while( e != 0 ) \ + { \ + _type check = (_type)1 << bit; \ + if( e & check ) \ + { \ + i = m_##_type.find( check ); \ + if( ret.length() ) \ + { \ + ret += " | "; \ + } \ + if( i != m_##_type.end() ) \ + { \ + ret += (*i).second; \ + } \ + else \ + { \ + ret += ""; \ + } \ + e &= ~check; \ + } \ + ++bit; \ + } \ + } \ + return ret; \ + } + + // This type doesn't exist in CL.h, but the enums conflict with + // other regular old cl_int enums. + typedef cl_int cl_command_exec_status; + + // CL bitfield values and plain uints may collide and need their own map. + // GL enums need their own map. + // CL enums that are allocated from the Khronos registry are unique and + // can go into the main/default cl_int map. + GENERATE_MAP_AND_FUNC( name, cl_int ); + GENERATE_MAP_AND_BITFIELD_FUNC( name_bool, cl_bool ); + GENERATE_MAP_AND_FUNC( name_build_status, cl_build_status ); + GENERATE_MAP_AND_FUNC( name_command_exec_status, cl_command_exec_status ); + GENERATE_MAP_AND_BITFIELD_FUNC( name_command_queue_properties, cl_command_queue_properties ); + GENERATE_MAP_AND_BITFIELD_FUNC( name_device_affinity_domain, cl_device_affinity_domain ); + GENERATE_MAP_AND_BITFIELD_FUNC( name_device_exec_capabilities, cl_device_exec_capabilities ); + GENERATE_MAP_AND_BITFIELD_FUNC( name_device_fp_config, cl_device_fp_config ); + GENERATE_MAP_AND_FUNC( name_device_local_mem_type, cl_device_local_mem_type ); + GENERATE_MAP_AND_FUNC( name_device_mem_cache_type, cl_device_mem_cache_type ); + GENERATE_MAP_AND_BITFIELD_FUNC( name_device_svm_capabilities, cl_device_svm_capabilities ); + GENERATE_MAP_AND_BITFIELD_FUNC( name_device_type, cl_device_type ); + GENERATE_MAP_AND_BITFIELD_FUNC( name_kernel_arg_type_qualifier, cl_kernel_arg_type_qualifier ); + GENERATE_MAP_AND_BITFIELD_FUNC( name_map_flags, cl_map_flags ); + GENERATE_MAP_AND_BITFIELD_FUNC( name_mem_flags, cl_mem_flags ); + GENERATE_MAP_AND_BITFIELD_FUNC( name_mem_migration_flags, cl_mem_migration_flags ); + GENERATE_MAP_AND_FUNC( name_program_binary_type, cl_program_binary_type ); + GENERATE_MAP_AND_BITFIELD_FUNC( name_svm_mem_flags, cl_svm_mem_flags ); + GENERATE_MAP_AND_FUNC( name_gl, GLenum ); + + #undef GENERATE_MAP_AND_FUNC + #undef GENERATE_MAP_AND_BITFIELD_FUNC + +private: + DISALLOW_COPY_AND_ASSIGN( CEnumNameMap ); +}; diff --git a/Src/git_version.cpp.in b/Src/git_version.cpp.in new file mode 100644 index 00000000..02b06286 --- /dev/null +++ b/Src/git_version.cpp.in @@ -0,0 +1,27 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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 "intercept.h" + +const char* CLIntercept::sc_GitDescribe = "@GIT_DESCRIBE@"; +const char* CLIntercept::sc_GitRefSpec = "@GIT_REFSPEC@"; +const char* CLIntercept::sc_GitHash = "@GIT_SHA1@"; \ No newline at end of file diff --git a/Src/git_version.rc.in b/Src/git_version.rc.in new file mode 100644 index 00000000..71bece49 --- /dev/null +++ b/Src/git_version.rc.in @@ -0,0 +1,64 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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. +*/ + +// Note: git_version.rc2 is generated from git_version.rc.in. + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 2,2,0,0 + PRODUCTVERSION 2,2,0,0 + FILEFLAGSMASK 0x0L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x0L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Intel(R) Corporation" + VALUE "FileDescription", "Intercept Layer for OpenCL(tm) Applications" + VALUE "FileVersion", "@GIT_DESCRIBE@" + VALUE "InternalName", "CLIntercept" + VALUE "LegalCopyright", "Copyright(C) Intel Corporation 2018" + VALUE "OriginalFilename", "OpenCL.dll" + VALUE "ProductName", "Intercept Layer for OpenCL(tm) Applications" + VALUE "ProductVersion", "@GIT_DESCRIBE@" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// diff --git a/Src/instrumentation.h b/Src/instrumentation.h new file mode 100644 index 00000000..95bf8286 --- /dev/null +++ b/Src/instrumentation.h @@ -0,0 +1,175 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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. +*/ + +#pragma once + +#if defined(USE_ITT) + +#include + +#define INTEL_ITTNOTIFY_API_PRIVATE +#include + +/*****************************************************************************\ +TASK METADATA: +\*****************************************************************************/ +template +struct MapToITTType { enum { value = __itt_metadata_unknown }; }; + +template<> +struct MapToITTType { enum { value = __itt_metadata_u64 }; }; +template<> +struct MapToITTType { enum { value = __itt_metadata_s64 }; }; +template<> +struct MapToITTType { enum { value = __itt_metadata_u32 }; }; +template<> +struct MapToITTType { enum { value = __itt_metadata_s32 }; }; +template<> +struct MapToITTType { enum { value = __itt_metadata_u16 }; }; +template<> +struct MapToITTType { enum { value = __itt_metadata_s16 }; }; +template<> +struct MapToITTType { enum { value = __itt_metadata_float }; }; +template<> +struct MapToITTType { enum { value = __itt_metadata_double }; }; + +// TODO: Is there a standard preprocessor define that can tell us pointer size? +#if defined(_WIN64) || defined(__LP64__) + +CLI_C_ASSERT( sizeof(void*) == 8 ); +template +struct MapToITTType { enum { value = __itt_metadata_u64 }; }; + +#else + +CLI_C_ASSERT( sizeof(void*) == 4 ); +template +struct MapToITTType { enum { value = __itt_metadata_u32 }; }; + +#endif + +template +inline void add_task_metadata( + __itt_domain* domain, + const std::string& name, + const T value ) +{ + __itt_string_handle* itt_string_handle = __itt_string_handle_create(name.c_str()); + __itt_metadata_type metadataType = (__itt_metadata_type)MapToITTType::value; + + __itt_metadata_add_with_scope(domain, __itt_scope_task, itt_string_handle, metadataType, 1, (void*)&value); +} + +template<> +inline void add_task_metadata( + __itt_domain* domain, + const std::string& name, + const cl_image_format* value ) +{ + if( value ) + { + std::string fieldName; + + fieldName = name + ".image_channel_data_type"; + add_task_metadata(domain, fieldName.c_str(), value->image_channel_data_type); + + fieldName = name + ".image_channel_order"; + add_task_metadata(domain, fieldName.c_str(), value->image_channel_order); + } +} + +template +inline void add_task_metadata_array( + __itt_domain* domain, + const std::string& name, + const size_t count, + const T* values ) +{ + if( values ) + { + __itt_string_handle* itt_string_handle = __itt_string_handle_create(name.c_str()); + __itt_metadata_type metadataType = (__itt_metadata_type)MapToITTType::value; + + __itt_metadata_add_with_scope(domain, __itt_scope_task, itt_string_handle, metadataType, count, (void*)values); + } +} + +#define ITT_CALL_LOGGING_ENTER(_kernel) \ + if( pIntercept->config().ITTCallLogging ) \ + { \ + pIntercept->ittInit(); \ + pIntercept->ittCallLoggingEnter( __FUNCTION__, _kernel ); \ + } + +#define ITT_CALL_LOGGING_EXIT() \ + if( pIntercept->config().ITTCallLogging ) \ + { \ + pIntercept->ittInit(); \ + pIntercept->ittCallLoggingExit(); \ + } + +#define ITT_ADD_PARAM_AS_METADATA(_param) \ + if( pIntercept->config().ITTCallLogging ) \ + { \ + pIntercept->ittInit(); \ + __itt_domain* itt_domain = pIntercept->ittDomain(); \ + add_task_metadata( itt_domain, #_param, _param ); \ + } + +#define ITT_ADD_ARRAY_PARAM_AS_METADATA(_count, _param) \ + if( pIntercept->config().ITTCallLogging ) \ + { \ + pIntercept->ittInit(); \ + __itt_domain* itt_domain = pIntercept->ittDomain(); \ + add_task_metadata_array( itt_domain, #_param, _count, _param ); \ + } + +#define ITT_REGISTER_COMMAND_QUEUE(_queue, _perfCounters) \ + if( pIntercept->config().ITTPerformanceTiming ) \ + { \ + pIntercept->ittInit(); \ + pIntercept->ittRegisterCommandQueue( _queue, _perfCounters ); \ + } + +#define ITT_RELEASE_COMMAND_QUEUE(_queue) \ + if( pIntercept->config().ITTPerformanceTiming ) \ + { \ + pIntercept->ittInit(); \ + pIntercept->ittReleaseCommandQueue( _queue ); \ + } + +#else + +#define ITT_CALL_LOGGING_ENTER(_kernel) +#define ITT_CALL_LOGGING_EXIT() +#define ITT_ADD_PARAM_AS_METADATA(_param) +#define ITT_ADD_ARRAY_PARAM_AS_METADATA(_count, _param) +#define ITT_REGISTER_COMMAND_QUEUE(_queue, _perfCounters) +#define ITT_RELEASE_COMMAND_QUEUE(_queue) + +#endif + +#define CHROME_REGISTER_COMMAND_QUEUE(_queue) \ + if( pIntercept->config().ChromePerformanceTiming ) \ + { \ + pIntercept->chromeRegisterCommandQueue( _queue ); \ + } diff --git a/Src/intercept.cpp b/Src/intercept.cpp new file mode 100644 index 00000000..1e39b4a9 --- /dev/null +++ b/Src/intercept.cpp @@ -0,0 +1,10177 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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 +#include +#include +#include +#include +#include +#include +#include // strdate + +#include "common.h" +#include "intercept.h" + +/*****************************************************************************\ + +Inline Function: + Hash + +Description: + Calculates hash from sequence of 32-bit values. + + Jenkins 96-bit mixing function with 32-bit feedback-loop and 64-bit state. + + All magic values are DWORDs of SHA2-256 mixing data: + 0x428a2f98 0x71374491 0xb5c0fbcf 0xe9b5dba5 + 0x3956c25b 0x59f111f1 0x923f82a4 0xab1c5ed5 + + From: http://www.burtleburtle.net/bob/c/lookup2.c + + lookup2.c, by Bob Jenkins, December 1996, Public Domain. + hash(), hash2(), hash3, and mix() are externally useful functions. + Routines to test the hash are included if SELF_TEST is defined. + You can use this free for any purpose. It has no warranty. + +\*****************************************************************************/ +#define HASH_JENKINS_MIX(a,b,c) \ +{ \ + a -= b; a -= c; a ^= (c>>13); \ + b -= c; b -= a; b ^= (a<<8); \ + c -= a; c -= b; c ^= (b>>13); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<16); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>3); \ + b -= c; b -= a; b ^= (a<<10); \ + c -= a; c -= b; c ^= (b>>15); \ +} +static inline uint64_t Hash( + const unsigned int *data, + size_t count ) +{ + unsigned int a = 0x428a2f98, hi = 0x71374491, lo = 0xb5c0fbcf; + while( count-- ) + { + a ^= *(data++); + HASH_JENKINS_MIX( a, hi, lo ); + } + return (((uint64_t)hi)<<32)|lo; +} +#undef HASH_JENKINS_MIX + +const char* CLIntercept::sc_URL = "https://github.com/intel/opencl-intercept-layer"; +const char* CLIntercept::sc_DumpDirectoryName = "CLIntercept_Dump"; +const char* CLIntercept::sc_ReportFileName = "clintercept_report.txt"; +const char* CLIntercept::sc_LogFileName = "clintercept_log.txt"; +const char* CLIntercept::sc_DumpPerfCountersFileNamePrefix = "clintercept_perfcounter"; +const char* CLIntercept::sc_TraceFileName = "clintercept_trace.json"; + +/////////////////////////////////////////////////////////////////////////////// +// +bool CLIntercept::Create( void* pGlobalData, CLIntercept*& pIntercept ) +{ + bool success = false; + + pIntercept = new CLIntercept( pGlobalData ); + if( pIntercept ) + { + success = pIntercept->init(); + if( success == false ) + { + Delete( pIntercept ); + } + } + + return success; +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::Delete( CLIntercept*& pIntercept ) +{ + delete pIntercept; + pIntercept = NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// +CLIntercept::CLIntercept( void* pGlobalData ) + : m_OS( pGlobalData ) +{ + m_Dispatch = dummyDispatch; + + m_OpenCLLibraryHandle = NULL; + + m_LoggedCLInfo = false; + + m_EnqueueCounter = 1; + m_StartTime = 0; + + m_ProgramNumber = 0; + + m_MemAllocNumber = 0; + + m_AubCaptureStarted = false; + m_AubCaptureKernelEnqueueSkipCounter = 0; + m_AubCaptureKernelEnqueueCaptureCounter = 0; + +#define CLI_CONTROL( _type, _name, _init, _desc ) m_Config . _name = _init; +#include "controls.h" +#undef CLI_CONTROL + +#if defined(USE_ITT) + m_ITTInitialized = false; + + m_ITTDomain = NULL; + + //m_ITTQueuedState = NULL; + //m_ITTSubmittedState = NULL; + //m_ITTExecutingState = NULL; + + //m_ITTQueueTrackGroup = NULL; +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +// +CLIntercept::~CLIntercept() +{ + stopAubCapture( NULL ); + report(); + + m_OS.EnterCriticalSection(); + + log( "CLIntercept is shutting down...\n" ); + + // Set the dispatch to the dummy dispatch. The destructor is called + // as the process is terminating. We don't know when each DLL gets + // unloaded, so it's not safe to call into any OpenCL functions in + // our destructor. Setting to the dummy dispatch ensures that no + // OpenCL functions get called. Note that this means we do potentially + // leave some events, kernels, or programs un-released, but since + // the process is terminating, that's probably OK. + m_Dispatch = dummyDispatch; + + if( m_OpenCLLibraryHandle != NULL ) + { + OS().UnloadLibrary( m_OpenCLLibraryHandle ); + } + + { + CCpuTimingStatsMap::iterator i = m_CpuTimingStatsMap.begin(); + while( i != m_CpuTimingStatsMap.end() ) + { + SCpuTimingStats* pCpuTimingStats = (*i).second; + + if( pCpuTimingStats ) + { + delete pCpuTimingStats; + } + + (*i).second = NULL; + ++i; + } + } + + { + CDeviceTimingStatsMap::iterator i = m_DeviceTimingStatsMap.begin(); + while( i != m_DeviceTimingStatsMap.end() ) + { + SDeviceTimingStats* pDeviceTimingStats = (*i).second; + + if( pDeviceTimingStats ) + { + delete pDeviceTimingStats; + } + + (*i).second = NULL; + ++i; + } + } + + { + CEventList::iterator i = m_EventList.begin(); + while( i != m_EventList.end() ) + { + SEventListNode* pEventListNode = (*i); + + if( pEventListNode ) + { + // If we were able to release events, we'd release + // pEventListNode->Event here. + + delete pEventListNode; + } + + (*i) = NULL; + ++i; + } + } + + { + CContextCallbackInfoMap::iterator i = m_ContextCallbackInfoMap.begin(); + while( i != m_ContextCallbackInfoMap.end() ) + { + SContextCallbackInfo* pContextCallbackInfo = (*i).second; + + if( pContextCallbackInfo ) + { + delete pContextCallbackInfo; + } + + (*i).second = NULL; + ++i; + } + } + + { + CPrecompiledKernelOverridesMap::iterator i = m_PrecompiledKernelOverridesMap.begin(); + while( i != m_PrecompiledKernelOverridesMap.end() ) + { + SPrecompiledKernelOverrides* pOverrides = (*i).second; + + if( pOverrides ) + { + // If we were able to release kernels or programs, we'd release + // the override kernels and program here. + + delete pOverrides; + } + + (*i).second = NULL; + ++i; + } + } + + { + CBuiltinKernelOverridesMap::iterator i = m_BuiltinKernelOverridesMap.begin(); + while( i != m_BuiltinKernelOverridesMap.end() ) + { + SBuiltinKernelOverrides* pOverrides = (*i).second; + + if( pOverrides ) + { + // If we were able to release kernels or programs, we'd release + // the override kernels and program here. + + delete pOverrides; + } + + (*i).second = NULL; + ++i; + } + } + + log( "... shutdown complete.\n" ); + + m_InterceptLog.close(); + m_InterceptTrace.close(); + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +template +static bool ReadRegistry( + const OS::Services& OS, + const char* name, + T& value ) +{ + unsigned int readValue = 0; + bool success = OS.ReadRegistry( name, &readValue, sizeof(readValue) ); + if( success ) + { + value = readValue; + } + + return success; +} +template <> +bool ReadRegistry( + const OS::Services& OS, + const char* name, + bool& value ) +{ + unsigned int readValue = 0; + bool success = OS.ReadRegistry( name, &readValue, sizeof(readValue) ); + if( success ) + { + value = ( readValue != 0 ); + } + + return success; +} +template <> +bool ReadRegistry( + const OS::Services& OS, + const char* name, + std::string& value ) +{ + char readValue[256] = ""; + bool success = OS.ReadRegistry( name, readValue, sizeof(readValue) ); + if( success ) + { + value = readValue; + } + + return success; +} + +/////////////////////////////////////////////////////////////////////////////// +// +bool CLIntercept::init() +{ + if( m_OS.Init() == false ) + { +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, "clIntercept", "OS.Init FAILED!\n" ); +#endif + return false; + } + + m_OS.EnterCriticalSection(); + +#if defined(_WIN32) + OS::Services_Common::ENV_PREFIX = "CLI_"; + OS::Services_Common::REGISTRY_KEY = "SOFTWARE\\INTEL\\IGFX\\CLINTERCEPT"; +#elif defined(__linux__) + OS::Services_Common::ENV_PREFIX = "CLI_"; + OS::Services_Common::CONFIG_FILE = "clintercept.conf"; +#endif + + bool breakOnLoad = false; + ReadRegistry( m_OS, "BreakOnLoad", breakOnLoad ); + + if( breakOnLoad ) + { + CLI_DEBUG_BREAK(); + } + + std::string dllName = ""; + ReadRegistry( m_OS, "DllName", dllName ); + + ReadRegistry( m_OS, "SimpleDumpProgram", m_Config.SimpleDumpProgramSource ); // backwards compatible, replaced by SimpleDumpProgramSource + ReadRegistry( m_OS, "DumpProgramsScript", m_Config.DumpProgramSourceScript ); // backwards compatible, replaced by DumpProgramSourceScript + ReadRegistry( m_OS, "DumpProgramsInject", m_Config.DumpProgramSource ); // backwards compatible, replaced by DumpProgramSource + ReadRegistry( m_OS, "InjectPrograms", m_Config.InjectProgramSource ); // backwards compatible, replaced by InjectProgramSource + +#define CLI_CONTROL( _type, _name, _init, _desc ) ReadRegistry( m_OS, #_name, m_Config . _name ); +#include "controls.h" +#undef CLI_CONTROL + + if( m_Config.LogToFile ) + { + std::string fileName = ""; + +#if defined(_WIN32) || defined(__linux__) + if( !m_Config.LogDir.empty() ) + { + std::replace( m_Config.LogDir.begin(), m_Config.LogDir.end(), '\\', '/' ); + OS::Services_Common::LOG_DIR = m_Config.LogDir.c_str(); + } +#endif + OS().GetDumpDirectoryName( sc_DumpDirectoryName, fileName ); + fileName += "/"; + fileName += sc_LogFileName; + + OS().MakeDumpDirectories( fileName ); + + if( m_Config.AppendFiles ) + { + m_InterceptLog.open( fileName.c_str(), std::ios::out | std::ios::app ); + } + else + { + m_InterceptLog.open( fileName.c_str(), std::ios::out ); + } + } + + if( m_Config.ChromeCallLogging || + m_Config.ChromePerformanceTiming ) + { + std::string fileName = ""; + + OS().GetDumpDirectoryName( sc_DumpDirectoryName, fileName ); + fileName += "/"; + fileName += sc_TraceFileName; + + OS().MakeDumpDirectories( fileName ); + m_InterceptTrace.open( fileName.c_str(), std::ios::out ); + m_InterceptTrace << "[\n"; + + uint64_t processId = OS().GetProcessID(); + uint64_t threadId = OS().GetThreadID(); + std::string processName = OS().GetProcessName(); + m_InterceptTrace + << "{\"ph\":\"M\", \"name\":\"process_name\", \"pid\":" << processId + << ", \"tid\":" << threadId + << ", \"args\":{\"name\":\"" << processName + << "\"}},\n"; + //m_InterceptTrace + // << "{\"ph\":\"M\", \"name\":\"thread_name\", \"pid\":" << processId + // << ", \"tid\":" << threadId + // << ", \"args\":{\"name\":\"Host APIs\"}},\n"; + } + + std::string name = ""; + OS().GetCLInterceptName( name ); + + std::string bits = + ( sizeof(void*) == 8 ) ? "64-bit" : + ( sizeof(void*) == 4 ) ? "32-bit" : + "XX-bit"; + + log( "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n" ); + log( "CLIntercept (" + bits + ") is loading...\n" ); + log( "CLintercept file location: " + name + "\n" ); + log( "CLIntercept URL: " + std::string(sc_URL) + "\n" ); +#if defined(CLINTERCEPT_CMAKE) + log( "CLIntercept git description: " + std::string(sc_GitDescribe) + "\n" ); + log( "CLIntercept git refspec: " + std::string(sc_GitRefSpec) + "\n" ); + log( "CLInterecpt git hash: " + std::string(sc_GitHash) + "\n" ); +#endif +#if defined(_WIN32) + log( "CLIntercept environment variable prefix: " + std::string( OS::Services_Common::ENV_PREFIX ) + "\n" ); + log( "CLIntercept registry key: " + std::string( OS::Services_Common::REGISTRY_KEY ) + "\n" ); +#elif defined(__linux__) + log( "CLIntercept environment variable prefix: " + std::string( OS::Services_Common::ENV_PREFIX ) + "\n" ); + log( "CLIntercept config file: " + std::string( OS::Services_Common::CONFIG_FILE ) + "\n" ); +#endif + + // Windows and Linux load the real OpenCL library and retrieve + // the OpenCL entry points from the real library dynamically. +#if defined(_WIN32) || defined(__linux__) + if( dllName != "" ) + { + log( "Read DLL name from user parameters: " + dllName + "\n" ); + log( "Trying to load dispatch from: " + dllName + "\n" ); + + if( initDispatch( dllName ) ) + { + log( "... success!\n" ); + } + } + else + { +#if defined(_WIN32) + + char* windir = NULL; + size_t length = 0; + + _dupenv_s( &windir, &length, "windir" ); + + // Try some common DLL names. + const std::string dllNames[] = + { + "real_opencl.dll", + #if defined(WIN32) + std::string(windir) + "/syswow64/opencl.dll", + #endif + std::string(windir) + "/system32/opencl.dll", + }; + + free( windir ); + +#elif defined(__ANDROID__) + + const std::string dllNames[] = + { + "/system/vendor/lib/real_libOpenCL.so", + "real_libOpenCL.so", + }; + +#elif defined(__linux__) + + const std::string dllNames[] = + { + "./real_libOpenCL.so", + }; + +#else +#error Unknown OS! +#endif + + const int numNames = sizeof(dllNames) / sizeof(dllNames[0]); + int i = 0; + + for( i = 0; i < numNames; i++ ) + { + log( "Trying to load dispatch from: " + dllNames[i] + "\n" ); + + if( initDispatch( dllNames[i] ) ) + { + log( "... success!\n" ); + break; + } + } + } +#elif defined(__APPLE__) + if( initDispatch() ) + { + log( "Dispatch table initialized.\n" ); + } +#else +#error Unknown OS! +#endif + +#define CLI_CONTROL( _type, _name, _init, _desc ) if ( m_Config . _name != _init ) { log( #_name " is set to a non-default value!\n" ); } +#include "controls.h" +#undef CLI_CONTROL + + m_StartTime = m_OS.GetTimer(); + log( "Timer Started!\n" ); + + log( "... loading complete.\n" ); + + m_OS.LeaveCriticalSection(); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::report() +{ + m_OS.EnterCriticalSection(); + + char filepath[MAX_PATH] = ""; + +#if defined(_WIN32) + if( config().DumpProgramSourceScript ) + { + char dirname[MAX_PATH] = ""; + char filename[MAX_PATH] = ""; + + size_t remaining = MAX_PATH; + + char date[9] = ""; + char time[9] = ""; + char* curPos = NULL; + char* nextToken = NULL; + char* pch = NULL; + + // Directory: + + curPos = dirname; + remaining = MAX_PATH; + memset( curPos, 0, MAX_PATH ); + + _strdate_s( date, 9 ); + _strtime_s( time, 9 ); + + memcpy_s( curPos, remaining, "CLShaderDump_", 14 ); + curPos += 13; + remaining -= 13; + + memcpy_s( curPos, remaining, strtok_s( date, "/", &nextToken ), 2 ); + curPos += 2; + remaining -= 2; + + memcpy_s( curPos, remaining, strtok_s( NULL, "/", &nextToken ), 2 ); + curPos += 2; + remaining -= 2; + + memcpy_s( curPos, remaining, strtok_s( NULL, "/", &nextToken ), 2 ); + curPos += 2; + remaining -= 2; + + ::CreateDirectoryA( dirname, NULL ); + + // File: + + curPos = filename; + remaining = MAX_PATH; + memset( curPos, 0, MAX_PATH ); + + if( GetModuleFileNameA( NULL, filename, MAX_PATH-1 ) == 0 ) + { + CLI_ASSERT( 0 ); + strcpy_s( curPos, remaining, "process.exe" ); + } + + pch = strrchr( filename, '\\' ); + pch++; + memcpy_s( curPos, remaining, pch, strlen( pch ) ); + curPos += strlen( pch ) - 4; // -4 to cut off ".exe" + remaining -= strlen( pch ) - 4; + + memcpy_s( curPos, remaining, "_", 2 ); + curPos += 1; + remaining -= 1; + + memcpy_s( curPos, remaining, strtok_s( time, ":", &nextToken ), 2 ); + curPos += 2; + remaining -= 2; + + memcpy_s( curPos, remaining, strtok_s( NULL, ":", &nextToken ), 2 ); + curPos += 2; + remaining -= 2; + + memcpy_s( curPos, remaining, strtok_s( NULL, ":", &nextToken ), 2 ); + curPos += 2; + remaining -= 2; + + CLI_SPRINTF( curPos, remaining, "" ); + curPos += 1; + remaining -= 1; + + CLI_SPRINTF( filepath, MAX_PATH, "%s/%s.%s", dirname, filename, "log" ); + } + else +#endif + { + std::string fileName = ""; + + OS().GetDumpDirectoryName( sc_DumpDirectoryName, fileName ); + fileName += "/"; + fileName += sc_ReportFileName; + + OS().MakeDumpDirectories( fileName ); + + CLI_SPRINTF( filepath, MAX_PATH, "%s", fileName.c_str() ); + } + + // Report + + std::ofstream os; + if( m_Config.AppendFiles ) + { + os.open( filepath, std::ios::out | std::ios::binary | std::ios::app ); + } + else + { + os.open( filepath, std::ios::out | std::ios::binary ); + } + if( os.good() ) + { + if( config().FinishAfterEnqueue ) + { + os << "*** WARNING *** FinishAfterEnqueue Enabled!" << std::endl << std::endl; + } + if( config().FlushAfterEnqueue ) + { + os << "*** WARNING *** FlushAfterEnqueue Enabled!" << std::endl << std::endl; + } + if( config().NullEnqueue ) + { + os << "*** WARNING *** NullEnqueue Enabled!" << std::endl << std::endl; + } + + os << "Total Enqueues: " << m_EnqueueCounter - 1 << std::endl << std::endl; + + if( config().HostPerformanceTiming ) + { + os << std::endl << "Host Performance Timing Results:" << std::endl; + + os << std::endl + << std::right << std::setw(44) << "Function Name" << ", " + << std::right << std::setw( 6) << "Calls" << ", " + << std::right << std::setw(13) << "Average (ns)" << ", " + << std::right << std::setw(13) << "Min (ns)" << ", " + << std::right << std::setw(13) << "Max (ns)" << std::endl; + + uint64_t overallTotalTicks = 0; + CCpuTimingStatsMap::iterator i = m_CpuTimingStatsMap.begin(); + while( i != m_CpuTimingStatsMap.end() ) + { + SCpuTimingStats* pCpuTimingStats = (*i).second; + const std::string& name = (*i).first; + + if( !name.empty() && pCpuTimingStats ) + { + os << std::right << std::setw(44) << name << ", " + << std::right << std::setw( 6) << pCpuTimingStats->NumberOfCalls << ", " + << std::right << std::setw(13) << OS().TickToNS( pCpuTimingStats->TotalTicks ) / pCpuTimingStats->NumberOfCalls << ", " + << std::right << std::setw(13) << OS().TickToNS( pCpuTimingStats->MinTicks ) << ", " + << std::right << std::setw(13) << OS().TickToNS( pCpuTimingStats->MaxTicks ) << std::endl; + + overallTotalTicks += pCpuTimingStats->TotalTicks; + } + + ++i; + } + + os << std::endl + << std::right << std::setw(44) << "Function Name" << ", " + << std::right << std::setw( 6) << "Calls" << ", " + << std::right << std::setw(13) << "Ticks" << ", " + << std::right << std::setw(13) << "Min Ticks" << ", " + << std::right << std::setw(13) << "Max Ticks" << ", " + << std::right << std::setw(13) << "% Ticks" << std::endl; + + i = m_CpuTimingStatsMap.begin(); + while( i != m_CpuTimingStatsMap.end() ) + { + SCpuTimingStats* pCpuTimingStats = (*i).second; + const std::string& name = (*i).first; + + if( !name.empty() && pCpuTimingStats ) + { + os << std::right << std::setw(44) << name << ", " + << std::right << std::setw( 6) << pCpuTimingStats->NumberOfCalls << ", " + << std::right << std::setw(13) << pCpuTimingStats->TotalTicks << ", " + << std::right << std::setw(13) << pCpuTimingStats->MinTicks << ", " + << std::right << std::setw(13) << pCpuTimingStats->MaxTicks << ", " + << std::right << std::setw(13) + << std::fixed << std::setprecision(2) + << ( pCpuTimingStats->TotalTicks * 100.0 ) / ( overallTotalTicks ) << std::endl; + } + + ++i; + } + } + + if( config().DevicePerformanceTiming ) + { + os << std::endl << "Device Performance Timing Results:" << std::endl; + + cl_ulong totalTotalNS = 0; + size_t longestName = 32; + + CDeviceTimingStatsMap::iterator i = m_DeviceTimingStatsMap.begin(); + while( i != m_DeviceTimingStatsMap.end() ) + { + const std::string& name = (*i).first; + SDeviceTimingStats* pDeviceTimingStats = (*i).second; + + if( !name.empty() && pDeviceTimingStats ) + { + totalTotalNS += pDeviceTimingStats->TotalNS; + longestName = std::max< size_t >( name.length(), longestName ); + } + + ++i; + } + + os << std::endl << "Total Time (ns): " << totalTotalNS << std::endl; + + os << std::endl + << std::right << std::setw(longestName) << "Function Name" << ", " + << std::right << std::setw( 6) << "Calls" << ", " + << std::right << std::setw(13) << "Time (ns)" << ", " + << std::right << std::setw( 8) << "Time (%)" << ", " + << std::right << std::setw(13) << "Average (ns)" << ", " + << std::right << std::setw(13) << "Min (ns)" << ", " + << std::right << std::setw(13) << "Max (ns)" << std::endl; + + i = m_DeviceTimingStatsMap.begin(); + while( i != m_DeviceTimingStatsMap.end() ) + { + const std::string& name = (*i).first; + SDeviceTimingStats* pDeviceTimingStats = (*i).second; + + if( !name.empty() && pDeviceTimingStats ) + { + os << std::right << std::setw(longestName) << name << ", " + << std::right << std::setw( 6) << pDeviceTimingStats->NumberOfCalls << ", " + << std::right << std::setw(13) << pDeviceTimingStats->TotalNS << ", " + << std::right << std::setw( 7) << std::fixed << std::setprecision(2) << pDeviceTimingStats->TotalNS * 100.0f / totalTotalNS << "%, " + << std::right << std::setw(13) << pDeviceTimingStats->TotalNS / pDeviceTimingStats->NumberOfCalls << ", " + << std::right << std::setw(13) << pDeviceTimingStats->MinNS << ", " + << std::right << std::setw(13) << pDeviceTimingStats->MaxNS << std::endl; + } + + ++i; + } + } + +#if defined(USE_MDAPI) + if( !config().DevicePerfCounterCustom.empty() ) + { + reportMDAPICounters( os ); + } +#endif + + os.close(); + } + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::getCallLoggingPrefix( + std::string& str ) +{ + if( m_Config.CallLoggingElapsedTime ) + { + uint64_t tickDelta = + OS().GetTimer() - + m_StartTime; + uint64_t usDelta = + OS().TickToNS( tickDelta ) / 1000; + std::ostringstream ss; + + ss << "Time: "; + ss << usDelta; + ss << " "; + + str += ss.str(); + } + + if( m_Config.CallLoggingThreadId || + m_Config.CallLoggingThreadNumber ) + { + uint64_t threadId = OS().GetThreadID(); + std::ostringstream ss; + + if( m_Config.CallLoggingThreadId ) + { + ss << "TID = "; + ss << threadId; + ss << " "; + } + if( m_Config.CallLoggingThreadNumber ) + { + unsigned int threadNum = 0; + if( m_ThreadNumberMap.find( threadId ) != m_ThreadNumberMap.end() ) + { + threadNum = m_ThreadNumberMap[ threadId ]; + } + else + { + threadNum = (unsigned int)m_ThreadNumberMap.size(); + m_ThreadNumberMap[ threadId ] = threadNum; + } + ss << "TNum = "; + ss << threadNum; + ss << " "; + } + + str += ss.str(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::callLoggingEnter( + const std::string& functionName, + const cl_kernel kernel ) +{ + m_OS.EnterCriticalSection(); + + std::string str; + getCallLoggingPrefix( str ); + + str += functionName; + + if( kernel ) + { + const std::string& kernelName = m_KernelNameMap[ kernel ]; + str += "( "; + str += kernelName; + str += " )"; + } + + if( m_Config.CallLoggingEnqueueCounter ) + { + std::ostringstream ss; + ss << ", EnqueueCounter: "; + ss << m_EnqueueCounter; + str += ss.str(); + } + + log( ">>>> " + str + "\n" ); + + m_OS.LeaveCriticalSection(); +} +void CLIntercept::callLoggingEnter( + const std::string& functionName, + const cl_kernel kernel, + const char* formatStr, + ... ) +{ + va_list args; + va_start( args, formatStr ); + + std::string str = functionName; + + if( kernel ) + { + m_OS.EnterCriticalSection(); + + const std::string& kernelName = m_KernelNameMap[ kernel ]; + str += "( "; + str += kernelName; + str += " )"; + + m_OS.LeaveCriticalSection(); + } + + char temp[ CLI_MAX_STRING_SIZE ] = ""; + int size = CLI_VSPRINTF( temp, CLI_MAX_STRING_SIZE, formatStr, args ); + if( size >= 0 && size < CLI_MAX_STRING_SIZE ) + { + str += ": "; + str += temp; + } + else + { + str += ": too long"; + } + callLoggingEnter( str, NULL ); + + va_end( args ); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::callLoggingInfo( + const std::string& str ) +{ + m_OS.EnterCriticalSection(); + + log( "---- " + str + "\n" ); + + m_OS.LeaveCriticalSection(); +} + +void CLIntercept::callLoggingInfo( + const char* formatStr, + ... ) +{ + va_list args; + va_start( args, formatStr ); + + char temp[ CLI_MAX_STRING_SIZE ] = ""; + int size = CLI_VSPRINTF( temp, CLI_MAX_STRING_SIZE, formatStr, args ); + if( size >= 0 && size < CLI_MAX_STRING_SIZE ) + { + callLoggingInfo( std::string( temp ) ); + } + else + { + callLoggingInfo( std::string( "too long" ) ); + } + + va_end( args ); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::callLoggingExit( + const std::string& functionName, + const cl_kernel kernel, + const cl_event* event ) +{ + m_OS.EnterCriticalSection(); + + std::string str; + getCallLoggingPrefix( str ); + + str += functionName; + + if( kernel ) + { + const std::string& kernelName = m_KernelNameMap[ kernel ]; + str += "( "; + str += kernelName; + str += " )"; + } + + if( event ) + { + char temp[ CLI_MAX_STRING_SIZE ] = ""; + CLI_SPRINTF( temp, CLI_MAX_STRING_SIZE, " created event = %p", *event ); + str += temp; + } + + log( "<<<< " + str + "\n" ); + + m_OS.LeaveCriticalSection(); +} +void CLIntercept::callLoggingExit( + const std::string& functionName, + const cl_kernel kernel, + const cl_event* event, + const char* formatStr, + ... ) +{ + va_list args; + va_start( args, formatStr ); + + std::string str = functionName; + + if( kernel ) + { + m_OS.EnterCriticalSection(); + + const std::string& kernelName = m_KernelNameMap[ kernel ]; + str += "( "; + str += kernelName; + str += " )"; + + m_OS.LeaveCriticalSection(); + } + + char temp[ CLI_MAX_STRING_SIZE ] = ""; + + if( event ) + { + CLI_SPRINTF( temp, CLI_MAX_STRING_SIZE, " created event = %p", *event ); + str += temp; + } + + int size = CLI_VSPRINTF( temp, CLI_MAX_STRING_SIZE, formatStr, args ); + if( size >= 0 && size < CLI_MAX_STRING_SIZE ) + { + str += ": "; + str += temp; + } + else + { + str += ": too long"; + } + + callLoggingExit( str, NULL, NULL ); + + va_end( args ); +} + +/////////////////////////////////////////////////////////////////////////////// +// +cl_int CLIntercept::allocateAndGetPlatformInfoString( + cl_platform_id platform, + cl_platform_info param_name, + char*& param_value ) const +{ + cl_int errorCode = CL_SUCCESS; + size_t size = 0; + + if( errorCode == CL_SUCCESS ) + { + if( param_value != NULL ) + { + CLI_ASSERT( 0 ); + delete [] param_value; + param_value = NULL; + } + } + + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetPlatformInfo( + platform, + param_name, + 0, + NULL, + &size ); + } + + if( errorCode == CL_SUCCESS ) + { + param_value = new char[ size ]; + if( param_value == NULL ) + { + errorCode = CL_OUT_OF_HOST_MEMORY; + } + } + + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetPlatformInfo( + platform, + param_name, + size, + param_value, + NULL ); + } + + if( errorCode != CL_SUCCESS ) + { + delete [] param_value; + param_value = NULL; + } + + return errorCode; +} + +/////////////////////////////////////////////////////////////////////////////// +// +cl_int CLIntercept::allocateAndGetDeviceInfoString( + cl_device_id device, + cl_device_info param_name, + char*& param_value ) const +{ + cl_int errorCode = CL_SUCCESS; + size_t size = 0; + + if( errorCode == CL_SUCCESS ) + { + if( param_value != NULL ) + { + CLI_ASSERT( 0 ); + delete [] param_value; + param_value = NULL; + } + } + + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetDeviceInfo( + device, + param_name, + 0, + NULL, + &size ); + } + + if( errorCode == CL_SUCCESS ) + { + param_value = new char[ size ]; + if( param_value == NULL ) + { + errorCode = CL_OUT_OF_HOST_MEMORY; + } + } + + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetDeviceInfo( + device, + param_name, + size, + param_value, + NULL ); + } + + if( errorCode != CL_SUCCESS ) + { + delete [] param_value; + param_value = NULL; + } + + return errorCode; +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::getPlatformInfoString( + const cl_platform_id platform, + std::string& str ) const +{ + str = ""; + + cl_int errorCode = CL_SUCCESS; + + char* platformName = NULL; + + errorCode |= allocateAndGetPlatformInfoString( + platform, + CL_PLATFORM_NAME, + platformName ); + + if( errorCode != CL_SUCCESS ) + { + CLI_ASSERT( 0 ); + str += "ERROR"; + } + else + { + str += platformName; + } + + delete [] platformName; + platformName = NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::getDeviceInfoString( + cl_uint numDevices, + const cl_device_id* devices, + std::string& str ) const +{ + str = ""; + + unsigned int i = 0; + for( i = 0; i < numDevices; i++ ) + { + cl_int errorCode = CL_SUCCESS; + + cl_device_type deviceType = CL_DEVICE_TYPE_DEFAULT; + char* deviceName = NULL; + + errorCode |= dispatch().clGetDeviceInfo( + devices[i], + CL_DEVICE_TYPE, + sizeof( deviceType ), + &deviceType, + NULL ); + errorCode |= allocateAndGetDeviceInfoString( + devices[i], + CL_DEVICE_NAME, + deviceName ); + + if( errorCode != CL_SUCCESS ) + { + CLI_ASSERT( 0 ); + str += "ERROR"; + } + else + { + if( i != 0 ) + { + str += " | "; + } + + if( deviceName ) + { + str += deviceName; + } + str += " ("; + str += enumName().name_device_type( deviceType ); + str += ")"; + } + + delete [] deviceName; + deviceName = NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::getEventListString( + cl_uint numEvents, + const cl_event* eventList, + std::string& str ) const +{ + { + std::ostringstream ss; + ss << "( size = "; + ss << numEvents; + ss << " )[ "; + str += ss.str(); + } + unsigned int i = 0; + for( i = 0; i < numEvents; i++ ) + { + if( i += 0 ) + { + str += ", "; + } + { + char temp[ CLI_MAX_STRING_SIZE ] = ""; + CLI_SPRINTF( temp, CLI_MAX_STRING_SIZE, "%p", eventList[i] ); + str += temp; + } + } + str += " ]"; +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::getContextPropertiesString( + const cl_context_properties* properties, + std::string& str ) const +{ + str = ""; + + if( properties ) + { + while( properties[0] != 0 ) + { + char temp_str[ CLI_MAX_STRING_SIZE ]; + + cl_int property = (cl_int)properties[0]; + str += enumName().name( property ) + " = "; + + switch( property ) + { + case CL_CONTEXT_PLATFORM: + { + const cl_platform_id* pp = (const cl_platform_id*)( properties + 1 ); + const cl_platform_id platform = pp[0]; + std::string platformInfo; + getPlatformInfoString( platform, platformInfo ); + str += platformInfo; + } + break; + case CL_GL_CONTEXT_KHR: + case CL_EGL_DISPLAY_KHR: + case CL_GLX_DISPLAY_KHR: + case CL_WGL_HDC_KHR: + case CL_CGL_SHAREGROUP_KHR: + { + const void** pp = (const void**)( properties + 1 ); + const void* value = pp[0]; + CLI_SPRINTF( temp_str, CLI_MAX_STRING_SIZE, "%p", value ); + str += temp_str; + } + break; + case CL_CONTEXT_INTEROP_USER_SYNC: + { + const cl_bool* pb = (const cl_bool*)( properties + 1); + cl_bool value = pb[0]; + str += enumName().name_bool( value ); + } + break; + default: + str += ""; + break; + } + + properties += 2; + if( properties[0] != 0 ) + { + str += ", "; + } + } + } + else + { + str = "NULL"; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::getSamplerPropertiesString( + const cl_sampler_properties* properties, + std::string& str ) const +{ + str = ""; + + if( properties ) + { + while( properties[0] != 0 ) + { + cl_int property = (cl_int)properties[0]; + str += enumName().name( property ) + " = "; + + switch( property ) + { + case CL_SAMPLER_NORMALIZED_COORDS: + { + const cl_bool* pb = (const cl_bool*)( properties + 1); + cl_bool value = pb[0]; + str += enumName().name_bool( value ); + } + break; + case CL_SAMPLER_ADDRESSING_MODE: + case CL_SAMPLER_FILTER_MODE: + case CL_SAMPLER_MIP_FILTER_MODE: + { + const cl_int* pi = (const cl_int*)( properties + 1); + cl_int value = pi[0]; + str += enumName().name( value ); + } + break; + case CL_SAMPLER_LOD_MIN: + case CL_SAMPLER_LOD_MAX: + { +#if 0 + if( property == CL_SAMPLER_LOD_MAX ) + { + cl_float* pFixup = (cl_float*)( properties + 1); + if( pFixup[0] < 0.5f ) + { + pFixup[0] = 100.0f; + } + } +#endif + + const cl_float* pf = (const cl_float*)( properties + 1 ); + + cl_float value = pf[0]; + + char fstr[ CLI_MAX_STRING_SIZE ]; + CLI_SPRINTF( fstr, CLI_MAX_STRING_SIZE, "%.2f", value ); + str += fstr; + } + break; + default: + str += ""; + break; + } + + properties += 2; + if( properties[0] != 0 ) + { + str += ", "; + } + } + } + else + { + str = "NULL"; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::getCommandQueuePropertiesString( + const cl_queue_properties* properties, + std::string& str ) const +{ + str = ""; + + if( properties ) + { + while( properties[0] != 0 ) + { + cl_int property = (cl_int)properties[0]; + str += enumName().name( property ) + " = "; + + switch( property ) + { + case CL_QUEUE_PROPERTIES: + { + str += ""; + } + break; + case CL_QUEUE_SIZE: + { + const cl_uint* pu = (const cl_uint*)( properties + 1); + cl_uint value = pu[0]; + str += value; + } + break; + default: + str += ""; + break; + } + + properties += 2; + if( properties[0] != 0 ) + { + str += ", "; + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::getCreateKernelsInProgramRetString( + cl_int retVal, + cl_kernel* kernels, + cl_uint* num_kernels_ret, + std::string& str ) const +{ + if( kernels && + num_kernels_ret && + ( num_kernels_ret[0] != 0 ) ) + { + cl_uint numKernels = num_kernels_ret[0]; + + str += "kernels = [ "; + for( cl_uint i = 0; i < numKernels; i++ ) + { + char s[256]; + CLI_SPRINTF( s, 256, "%p", kernels[i] ); + str += s; + + if( i < numKernels - 1 ) + { + str += ", "; + } + } + str += " ]"; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::getKernelArgString( + cl_uint arg_index, + size_t arg_size, + const void* arg_value, + std::string& str ) const +{ + char s[CLI_MAX_STRING_SIZE] = ""; + + if( getSampler( + arg_size, + arg_value, + str ) ) + { + CLI_SPRINTF( s, CLI_MAX_STRING_SIZE, "index = %d, size = %d, value = %s\n", + arg_index, + (unsigned int)arg_size, + str.c_str() ); + } + else if( ( arg_value != NULL ) && + ( arg_size == sizeof(cl_mem) ) ) + { + cl_mem* pMem = (cl_mem*)arg_value; + CLI_SPRINTF( s, CLI_MAX_STRING_SIZE, "index = %d, size = %d, value = %p", + arg_index, + (unsigned int)arg_size, + pMem[0] ); + } + else if( ( arg_value != NULL ) && + ( arg_size == sizeof(cl_uint) ) ) + { + cl_uint* pData = (cl_uint*)arg_value; + CLI_SPRINTF( s, CLI_MAX_STRING_SIZE, "index = %d, size = %d, value = 0x%x", + arg_index, + (unsigned int)arg_size, + pData[0] ); + } + else if( ( arg_value != NULL ) && + ( arg_size == sizeof(cl_ulong) ) ) + { + cl_ulong* pData = (cl_ulong*)arg_value; + CLI_SPRINTF( s, CLI_MAX_STRING_SIZE, "index = %d, size = %d, value = 0x%jx", + arg_index, + (unsigned int)arg_size, + pData[0] ); + } + else if( ( arg_value != NULL ) && + ( arg_size == sizeof(cl_int4) ) ) + { + cl_int4* pData = (cl_int4*)arg_value; + CLI_SPRINTF( s, CLI_MAX_STRING_SIZE, "index = %d, size = %d, valueX = 0x%0x, valueY = 0x%0x, valueZ = 0x%0x, valueW = 0x%0x", + arg_index, + (unsigned int)arg_size, + pData->s[0], + pData->s[1], + pData->s[2], + pData->s[3]); + } + else + { + CLI_SPRINTF( s, CLI_MAX_STRING_SIZE, "index = %d, size = %d", + arg_index, + (unsigned int)arg_size ); + } + + str = s; +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::getEnqueueNDRangeKernelArgsString( + cl_uint work_dim, + const size_t* global_work_offset, + const size_t* global_work_size, + const size_t* local_work_size, + std::string& str ) const +{ + std::ostringstream ss; + + if( global_work_offset ) + { + ss << "global_work_offset = < "; + for( cl_uint i = 0; i < work_dim; i++ ) + { + ss << global_work_offset[i]; + if( i < work_dim - 1 ) + { + ss << ", "; + } + } + ss << " >, "; + } + + ss << "global_work_size = < "; + if( global_work_size ) + { + for( cl_uint i = 0; i < work_dim; i++ ) + { + ss << global_work_size[i]; + if( i < work_dim - 1 ) + { + ss << ", "; + } + } + } + else + { + ss << "NULL?"; + } + ss << " >, "; + + ss << "local_work_size = < "; + if( local_work_size ) + { + for( cl_uint i = 0; i < work_dim; i++ ) + { + ss << local_work_size[i]; + if( i < work_dim - 1 ) + { + ss << ", "; + } + } + } + else + { + ss << "NULL"; + } + ss << " >"; + + str = ss.str(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::getCreateSubBufferArgsString( + cl_buffer_create_type createType, + const void *createInfo, + std::string& str ) const +{ + std::ostringstream ss; + + switch( createType ) + { + case CL_BUFFER_CREATE_TYPE_REGION: + { + cl_buffer_region* pRegion = (cl_buffer_region*)createInfo; + ss << "origin = " + << pRegion->origin + << " size = " + << pRegion->size; + } + break; + default: + ss << ""; + break; + } + + str = ss.str(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::logCLInfo() +{ + if( m_LoggedCLInfo == false ) + { + m_OS.EnterCriticalSection(); + + if( m_LoggedCLInfo == false ) + { + m_LoggedCLInfo = true; + + cl_int errorCode = CL_SUCCESS; + cl_uint numPlatforms = 0; + + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetPlatformIDs( + 0, + NULL, + &numPlatforms ); + } + + if( errorCode == CL_SUCCESS && numPlatforms != 0 ) + { + logf( "\nEnumerated %u platform%s.\n\n", + numPlatforms, + numPlatforms > 1 ? "s" : "" ); + + cl_platform_id* platforms = new cl_platform_id[numPlatforms]; + if( platforms ) + { + errorCode = dispatch().clGetPlatformIDs( + numPlatforms, + platforms, + NULL ); + } + else + { + errorCode = CL_OUT_OF_HOST_MEMORY; + } + + for( cl_uint p = 0; p < numPlatforms; p++ ) + { + if( errorCode == CL_SUCCESS ) + { + logf( "Platform %u:\n", p ); + logPlatformInfo( platforms[p] ); + } + + cl_uint numDevices = 0; + + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetDeviceIDs( + platforms[p], + CL_DEVICE_TYPE_ALL, + 0, + NULL, + &numDevices ); + } + if( errorCode == CL_SUCCESS && numDevices != 0 ) + { + logf( "\tPlatform has %u device%s.\n\n", + numDevices, + numDevices > 1 ? "s" : "" ); + + cl_device_id* devices = new cl_device_id[numDevices]; + if( devices ) + { + errorCode = dispatch().clGetDeviceIDs( + platforms[p], + CL_DEVICE_TYPE_ALL, + numDevices, + devices, + NULL ); + } + else + { + errorCode = CL_OUT_OF_HOST_MEMORY; + } + + for( cl_uint d = 0; d < numDevices; d++ ) + { + if( errorCode == CL_SUCCESS ) + { + logf( "Device %u:\n", d ); + logDeviceInfo( devices[d] ); + log( "\n" ); + } + } + + delete [] devices; + } + } + + delete [] platforms; + } + } + + m_OS.LeaveCriticalSection(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::logBuild( + uint64_t buildTimeStart, + const cl_program program, + cl_uint numDevices, + const cl_device_id* deviceList ) +{ + uint64_t buildTimeEnd = m_OS.GetTimer(); + + m_OS.EnterCriticalSection(); + + cl_device_id* localDeviceList = NULL; + + cl_int errorCode = CL_SUCCESS; + + // There are two possibilities. Either the device_list is NULL, in which + // case we need to get the build log for all devices, or it's non-NULL, + // in which case we only need to get the build log for all devices in + // the device list. + + if( ( errorCode == CL_SUCCESS ) && + ( deviceList == NULL ) ) + { + errorCode = dispatch().clGetProgramInfo( + program, + CL_PROGRAM_NUM_DEVICES, + sizeof( numDevices ), + &numDevices, + NULL ); + + if( errorCode == CL_SUCCESS ) + { + localDeviceList = new cl_device_id[ numDevices ]; + if( localDeviceList == NULL ) + { + errorCode = CL_OUT_OF_HOST_MEMORY; + } + else + { + errorCode = dispatch().clGetProgramInfo( + program, + CL_PROGRAM_DEVICES, + numDevices * sizeof( cl_device_id ), + localDeviceList, + NULL ); + if( errorCode == CL_SUCCESS ) + { + deviceList = localDeviceList; + } + } + } + } + + if( m_Config.BuildLogging && + errorCode == CL_SUCCESS ) + { + unsigned int programNumber = m_ProgramNumberMap[ program ]; + unsigned int compileCount = m_ProgramNumberCompileCountMap[ programNumber ]; + + logf( "Build Info for program %p, number %u, compile %u, for %u device(s):\n", + program, + programNumber, + compileCount, + numDevices ); + + float buildTimeMS = m_OS.TickToNS( buildTimeEnd - buildTimeStart ) / 1e6f; + logf( " Build finished in %.2f ms.\n", buildTimeMS ); + } + + if( errorCode == CL_SUCCESS ) + { + size_t i = 0; + for( i = 0; i < numDevices; i++ ) + { + if( m_Config.BuildLogging ) + { + cl_build_status buildStatus = CL_BUILD_NONE; + errorCode = dispatch().clGetProgramBuildInfo( + program, + deviceList[ i ], + CL_PROGRAM_BUILD_STATUS, + sizeof( buildStatus ), + &buildStatus, + NULL ); + + if( errorCode == CL_SUCCESS ) + { + char* deviceName = NULL; + char* deviceOpenCLCVersion = NULL; + errorCode = allocateAndGetDeviceInfoString( + deviceList[i], + CL_DEVICE_NAME, + deviceName ); + errorCode |= allocateAndGetDeviceInfoString( + deviceList[i], + CL_DEVICE_OPENCL_C_VERSION, + deviceOpenCLCVersion ); + + char str[256] = ""; + + CLI_SPRINTF( str, 256, "Build Status for device %u = ", + (unsigned int)i ); + + std::string message = str; + + if( errorCode == CL_SUCCESS ) + { + message += deviceName; + message += " ("; + message += deviceOpenCLCVersion; + message += "): "; + } + + message += enumName().name_build_status( buildStatus ); + message += "\n"; + + log( message ); + + delete [] deviceName; + deviceName = NULL; + + delete [] deviceOpenCLCVersion; + deviceOpenCLCVersion = NULL; + } + } + + size_t buildLogSize = 0; + errorCode = dispatch().clGetProgramBuildInfo( + program, + deviceList[ i ], + CL_PROGRAM_BUILD_LOG, + 0, + NULL, + &buildLogSize ); + + if( errorCode == CL_SUCCESS ) + { + char* buildLog = new char[ buildLogSize + 1 ]; + if( buildLog ) + { + dispatch().clGetProgramBuildInfo( + program, + deviceList[ i ], + CL_PROGRAM_BUILD_LOG, + buildLogSize, + buildLog, + NULL ); + + // Check if the build log is already null-terminated. + // If it is, we're good, otherwise null terminate it. + if( buildLog[ buildLogSize - 1 ] == '\0' ) + { + buildLogSize--; + } + else + { + buildLog[ buildLogSize ] = '\0'; + } + + if( m_Config.BuildLogging ) + { + log( "-------> Start of Build Log:\n" ); + log( std::string(buildLog) ); + log( "<------- End of Build Log\n\n" ); + } + if( m_Config.DumpProgramBuildLogs ) + { + dumpProgramBuildLog( + program, + deviceList[ i ], + buildLog, + buildLogSize ); + } + + delete [] buildLog; + } + } + } + } + + delete [] localDeviceList; + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::logError( + const std::string& functionName, + cl_int errorCode ) +{ + std::ostringstream ss; + ss << "ERROR! " << functionName << " returned " << enumName().name(errorCode) << " (" << errorCode << ")\n"; + + m_OS.EnterCriticalSection(); + + log( ss.str() ); + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::logFlushOrFinishAfterEnqueueStart( + const std::string& flushOrFinish, + const std::string& functionName ) +{ + m_OS.EnterCriticalSection(); + + log( "Calling " + flushOrFinish + " after " + functionName + "...\n" ); + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::logFlushOrFinishAfterEnqueueEnd( + const std::string& flushOrFinish, + const std::string& functionName, + cl_int errorCode ) +{ + std::ostringstream ss; + ss << "... " << flushOrFinish << " after " << functionName << " returned " << enumName().name( errorCode ) << " (" << errorCode << ")\n"; + + m_OS.EnterCriticalSection(); + + log( ss.str() ); + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::logPreferredWorkGroupSizeMultiple( + const cl_kernel* kernels, + cl_uint numKernels ) +{ + if( numKernels > 0 ) + { + m_OS.EnterCriticalSection(); + + cl_int errorCode = CL_SUCCESS; + + // We can share the program and device list for all kernels. + + cl_kernel queryKernel = kernels[0]; + + // First, get the program for this kernel. + cl_program program = NULL; + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetKernelInfo( + queryKernel, + CL_KERNEL_PROGRAM, + sizeof(program), + &program, + NULL ); + } + + // Next, get the list of devices for the program. + cl_uint numDevices = 0; + cl_device_id* deviceList = NULL; + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetProgramInfo( + program, + CL_PROGRAM_NUM_DEVICES, + sizeof( numDevices ), + &numDevices, + NULL ); + + if( errorCode == CL_SUCCESS ) + { + deviceList = new cl_device_id[ numDevices ]; + if( deviceList == NULL ) + { + errorCode = CL_OUT_OF_HOST_MEMORY; + } + else + { + errorCode = dispatch().clGetProgramInfo( + program, + CL_PROGRAM_DEVICES, + numDevices * sizeof( cl_device_id ), + deviceList, + NULL ); + } + } + } + + // Log the preferred work group size multiple for each kernel, + // for each device. + while( numKernels-- ) + { + cl_kernel kernel = kernels[ numKernels ]; + + if( errorCode == CL_SUCCESS ) + { + const std::string& kernelName = m_KernelNameMap[ kernel ]; + log( "Preferred Work Group Size Multiple for: '" + kernelName + "':\n" ); + } + if( errorCode == CL_SUCCESS ) + { + size_t i = 0; + for( i = 0; i < numDevices; i++ ) + { + size_t kernelPreferredWorkGroupSizeMultiple = 0; + errorCode = dispatch().clGetKernelWorkGroupInfo( + kernel, + deviceList[i], + CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE, + sizeof(kernelPreferredWorkGroupSizeMultiple), + &kernelPreferredWorkGroupSizeMultiple, + NULL ); + if( errorCode == CL_SUCCESS ) + { + char* deviceName = NULL; + + errorCode = allocateAndGetDeviceInfoString( + deviceList[i], + CL_DEVICE_NAME, + deviceName ); + if( errorCode == CL_SUCCESS ) + { + logf( " for device %s: %u\n", + deviceName, + (unsigned int)kernelPreferredWorkGroupSizeMultiple ); + } + + delete [] deviceName; + } + } + } + } + + delete [] deviceList; + + m_OS.LeaveCriticalSection(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::contextCallbackCaller( + const char* errinfo, + const void* private_info, + size_t cb, + void* user_data ) +{ + SContextCallbackInfo* pContextCallbackInfo = + (SContextCallbackInfo*)user_data; + + pContextCallbackInfo->pIntercept->contextCallback( + errinfo, + private_info, + cb ); + if( pContextCallbackInfo->pApplicationCallback ) + { + pContextCallbackInfo->pApplicationCallback( + errinfo, + private_info, + cb, + pContextCallbackInfo->pUserData ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::contextCallback( + const std::string& errinfo, + const void* private_info, + size_t cb ) +{ + m_OS.EnterCriticalSection(); + + char str[256] = ""; + CLI_SPRINTF( str, 256, "=======> Context Callback (private_info = %p, cb = %u):\n", + private_info, + (unsigned int)cb ); + + log( str + errinfo + "\n" + "<======= End of Context Callback\n" ); + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::contextCallbackOverrideInit( + const cl_context_properties* properties, + void (CL_CALLBACK*& pCallback)( const char*, const void*, size_t, void* ), + void*& pUserData, + SContextCallbackInfo*& pContextCallbackInfo, + cl_context_properties*& pLocalContextProperties ) +{ + pContextCallbackInfo = new SContextCallbackInfo; + if( pContextCallbackInfo ) + { + pContextCallbackInfo->pIntercept = this; + pContextCallbackInfo->pApplicationCallback = pCallback; + pContextCallbackInfo->pUserData = pUserData; + + pCallback = CLIntercept::contextCallbackCaller; + pUserData = pContextCallbackInfo; + } + + if( m_Config.ContextHintLevel ) + { + // We want to add a context hints to the context properties, unless + // the context properties already requests performance hints + // (requesting the same property twice is an error). So, look through + // the context properties for the performance hint enum. We need to + // do this anyways to count the number of property pairs. + bool foundPerformanceHintEnum = false; + int numProperties = 0; + if( properties ) + { + while( properties[ numProperties ] != 0 ) + { + if( properties[ numProperties ] == CL_CONTEXT_SHOW_DIAGNOSTICS_INTEL ) + { + foundPerformanceHintEnum = true; + } + numProperties += 2; + } + } + + if( foundPerformanceHintEnum == false ) + { + // The performance hint property isn't already set, so we'll + // need to allocate an extra pair of properties for it. + numProperties += 2; + } + + // Allocate a new array of properties. We need to allocate two + // properties for each pair, plus one property for the terminating + // zero. + pLocalContextProperties = new cl_context_properties[ numProperties + 1 ]; + if( pLocalContextProperties ) + { + // Copy the old properties array to the new properties array, + // if the new properties array exists. + numProperties = 0; + if( properties ) + { + while( properties[ numProperties ] != 0 ) + { + pLocalContextProperties[ numProperties ] = properties[ numProperties ]; + if( properties[ numProperties ] == CL_CONTEXT_SHOW_DIAGNOSTICS_INTEL ) + { + CLI_ASSERT( foundPerformanceHintEnum ); + pLocalContextProperties[ numProperties + 1 ] = m_Config.ContextHintLevel; + } + else + { + pLocalContextProperties[ numProperties + 1 ] = properties[ numProperties + 1 ]; + } + numProperties += 2; + } + } + // Add the performance hint property if it wasn't already set. + if( foundPerformanceHintEnum == false ) + { + pLocalContextProperties[ numProperties ] = CL_CONTEXT_SHOW_DIAGNOSTICS_INTEL; + pLocalContextProperties[ numProperties + 1 ] = m_Config.ContextHintLevel; + numProperties += 2; + } + // Add the terminating zero. + pLocalContextProperties[ numProperties ] = 0; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::contextCallbackOverrideCleanup( + const cl_context context, + SContextCallbackInfo*& pContextCallbackInfo, + cl_context_properties*& pLocalContextProperties ) +{ + if( context && pContextCallbackInfo ) + { + m_OS.EnterCriticalSection(); + + // Check if we already have a context callback info for this context. If + // we do, free it. + SContextCallbackInfo* pOldContextCallbackInfo = + m_ContextCallbackInfoMap[ context ]; + if( pOldContextCallbackInfo ) + { + delete pOldContextCallbackInfo; + pOldContextCallbackInfo = NULL; + } + + m_ContextCallbackInfoMap[ context ] = pContextCallbackInfo; + + m_OS.LeaveCriticalSection(); + } + else + { + delete pContextCallbackInfo; + pContextCallbackInfo = NULL; + } + + if( pLocalContextProperties ) + { + delete pLocalContextProperties; + pLocalContextProperties = NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::eventCallbackCaller( + cl_event event, + cl_int status, + void* user_data ) +{ + SEventCallbackInfo* pEventCallbackInfo = + (SEventCallbackInfo*)user_data; + + CLIntercept* pIntercept = pEventCallbackInfo->pIntercept; + + CALL_LOGGING_ENTER( "event = %p, status = %s (%d)", + event, + pIntercept->enumName().name_command_exec_status( status ).c_str(), + status ); + + pIntercept->eventCallback( + event, + status ); + if( pEventCallbackInfo->pApplicationCallback ) + { + pEventCallbackInfo->pApplicationCallback( + event, + status, + pEventCallbackInfo->pUserData ); + } + + CALL_LOGGING_EXIT(); + + delete pEventCallbackInfo; +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::eventCallback( + cl_event event, + int status ) +{ + // TODO: Since we call log the eventCallbackCaller, do we need to do + // anything here? +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::incrementEnqueueCounter() +{ + m_OS.EnterCriticalSection(); + + m_EnqueueCounter++; + + m_OS.LeaveCriticalSection(); +} + +uint64_t CLIntercept::getEnqueueCounter() +{ + return m_EnqueueCounter; +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::overrideNullLocalWorkSize( + const cl_uint work_dim, + const size_t* global_work_size, + const size_t*& local_work_size ) +{ + if( local_work_size == NULL ) + { + switch( work_dim ) + { + case 1: + if( m_Config.NullLocalWorkSizeX != 0 ) + { + if( global_work_size[0] % m_Config.NullLocalWorkSizeX == 0 ) + { + local_work_size = &m_Config.NullLocalWorkSizeX; + } + else + { + m_OS.EnterCriticalSection(); + logf( "Couldn't override NULL local work size: < %u > %% < %u > != 0!\n", + (unsigned int)global_work_size[0], + (unsigned int)m_Config.NullLocalWorkSizeX ); + m_OS.LeaveCriticalSection(); + } + } + break; + case 2: + if( ( m_Config.NullLocalWorkSizeX != 0 ) && + ( m_Config.NullLocalWorkSizeY != 0 ) ) + { + if( ( global_work_size[0] % m_Config.NullLocalWorkSizeX == 0 ) && + ( global_work_size[1] % m_Config.NullLocalWorkSizeY == 0 ) ) + { + local_work_size = &m_Config.NullLocalWorkSizeX; + } + else + { + m_OS.EnterCriticalSection(); + logf( "Couldn't override NULL local work size: < %u, %u > %% < %u, %u > != 0!\n", + (unsigned int)global_work_size[0], + (unsigned int)global_work_size[1], + (unsigned int)m_Config.NullLocalWorkSizeX, + (unsigned int)m_Config.NullLocalWorkSizeY ); + m_OS.LeaveCriticalSection(); + } + } + break; + case 3: + if( ( m_Config.NullLocalWorkSizeX != 0 ) && + ( m_Config.NullLocalWorkSizeY != 0 ) && + ( m_Config.NullLocalWorkSizeZ != 0 ) ) + { + if( ( global_work_size[0] % m_Config.NullLocalWorkSizeX == 0 ) && + ( global_work_size[1] % m_Config.NullLocalWorkSizeY == 0 ) && + ( global_work_size[2] % m_Config.NullLocalWorkSizeZ == 0 ) ) + { + local_work_size = &m_Config.NullLocalWorkSizeX; + } + else + { + m_OS.EnterCriticalSection(); + logf( "Couldn't override NULL local work size: < %u, %u, %u > %% < %u, %u, %u > != 0!\n", + (unsigned int)global_work_size[0], + (unsigned int)global_work_size[1], + (unsigned int)global_work_size[2], + (unsigned int)m_Config.NullLocalWorkSizeX, + (unsigned int)m_Config.NullLocalWorkSizeY, + (unsigned int)m_Config.NullLocalWorkSizeZ ); + m_OS.LeaveCriticalSection(); + } + } + break; + default: + // Nothing. + break; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::combineProgramStrings( + cl_uint& count, + const char**& strings, + const size_t*& lengths, + char*& singleString ) const +{ + size_t allocSize = 0; + cl_uint i = 0; + + for( i = 0; i < count; i++ ) + { + size_t length = 0; + if( ( lengths == NULL ) || + ( lengths[i] == 0 ) ) + { + length = strlen( strings[i] ); + } + else + { + length = lengths[i]; + } + allocSize += length; + } + + // Allocate a multiple of four bytes. + // Allocate some extra to make sure we're null terminated. + allocSize = ( allocSize + ( 4 + 4 - 1 ) ) & ~( 4 - 1 ); + + singleString = new char[ allocSize ]; + if( singleString ) + { + memset( singleString, 0, allocSize ); + + char* pDst = singleString; + size_t remaining = allocSize; + for( i = 0; i < count; i++ ) + { + size_t length = 0; + if( ( lengths == NULL ) || + ( lengths[i] == 0 ) ) + { + length = strlen( strings[i] ); + } + else + { + length = lengths[i]; + } + CLI_MEMCPY( + pDst, + remaining, + strings[i], + length ); + pDst += length; + remaining -= length; + } + + // Replace any NULL chars between kernels with spaces. + if( count > 1 ) + { + for( char* pStr = singleString; pStr < pDst - 1; pStr++ ) + { + if( *pStr == 0x0 ) + { + *pStr = 0x20; + } + } + } + + count = 1; + strings = ( const char** )&singleString; + lengths = NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::incrementProgramCompileCount( + const cl_program program ) +{ + m_OS.EnterCriticalSection(); + + unsigned int programNumber = m_ProgramNumberMap[ program ]; + unsigned int compileCount = m_ProgramNumberCompileCountMap[ programNumber ]; + + ++compileCount; + + m_ProgramNumberCompileCountMap[ programNumber ] = compileCount; + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +uint64_t CLIntercept::hashString( + const char* singleString, + size_t length ) +{ + uint64_t hash = 0; + + if( singleString != NULL ) + { + const unsigned int* dwProgramSource = (const unsigned int*)singleString; + size_t dwProgramSize = length; + + dwProgramSize = ( dwProgramSize + ( 4 - 1 ) ) & ~( 4 - 1 ); + dwProgramSize /= 4; + + hash = Hash( + dwProgramSource, + dwProgramSize ); + } + + return hash; +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::saveProgramHash( + const cl_program program, + uint64_t hash ) +{ + m_OS.EnterCriticalSection(); + + if( program != NULL ) + { + m_ProgramHashMap[ program ] = hash; + } + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +bool CLIntercept::injectProgramSource( + const uint64_t hash, + cl_uint& count, + const char**& strings, + const size_t*& lengths, + char*& singleString ) +{ + // We don't expect to get here unless we've combined the app's string(s) + // into a single string and computed a hash from it. + CLI_ASSERT( singleString ); + + m_OS.EnterCriticalSection(); + + bool injected = false; + + std::string fileName; + + // Get the dump directory name. + { + OS().GetDumpDirectoryName( sc_DumpDirectoryName, fileName ); + fileName += "/Inject"; + } + + // Make two candidate filenames. They will have the form: + // CLI___source.cl, or + // CLI__source.cl + { + char numberString1[256] = ""; + CLI_SPRINTF( numberString1, 256, "%04u_%08X", + m_ProgramNumber, + (unsigned int)hash ); + + char numberString2[256] = ""; + CLI_SPRINTF( numberString2, 256, "%08X", + (unsigned int)hash ); + + std::string fileName1; + fileName1 = fileName; + fileName1 += "/CLI_"; + fileName1 += numberString1; + fileName1 += "_source.cl"; + + std::string fileName2; + fileName2 = fileName; + fileName2 += "/CLI_"; + fileName2 += numberString2; + fileName2 += "_source.cl"; + + std::ifstream is; + + is.open( + fileName1.c_str(), + std::ios::in | std::ios::binary ); + if( is.good() ) + { + log( "Injecting source file: " + fileName1 + "\n" ); + } + else + { + log( "Injection source file doesn't exist: " + fileName1 + "\n" ); + + is.clear(); + is.open( + fileName2.c_str(), + std::ios::in | std::ios::binary ); + if( is.good() ) + { + log( "Injecting source file: " + fileName2 + "\n" ); + } + else + { + log( "Injection source file doesn't exist: " + fileName2 + "\n" ); + } + } + + if( is.good() ) + { + // The file exists. Figure out how big it is. + size_t filesize = 0; + + is.seekg(0, std::ios::end); + filesize = (size_t)is.tellg(); + is.seekg(0, std::ios::beg); + + char* newSingleString = new char[ filesize + 1 ]; + if( newSingleString ) + { + memset( newSingleString, 0, filesize + 1 ); + + is.read( newSingleString, filesize ); + + delete [] singleString; + + singleString = newSingleString; + count = 1; + strings = ( const char** )&singleString; + lengths = NULL; + + injected = true; + } + + is.close(); + } + } + + m_OS.LeaveCriticalSection(); + return injected; +} + +/////////////////////////////////////////////////////////////////////////////// +// +bool CLIntercept::prependProgramSource( + const uint64_t hash, + cl_uint& count, + const char**& strings, + const size_t*& lengths, + char*& singleString ) +{ + // We don't expect to get here unless we've combined the app's string(s) + // into a single string and computed a hash from it. + CLI_ASSERT( singleString ); + + m_OS.EnterCriticalSection(); + + bool injected = false; + + std::string fileName; + + // Get the dump directory name. + { + OS().GetDumpDirectoryName( sc_DumpDirectoryName, fileName ); + fileName += "/Inject"; + } + + // Make three candidate filenames. They will have the form: + // CLI___prepend.cl, or + // CLI__prepend.cl, or + // CLI_prepend.cl + { + char numberString1[256] = ""; + CLI_SPRINTF( numberString1, 256, "%04u_%08X", + m_ProgramNumber, + (unsigned int)hash ); + + char numberString2[256] = ""; + CLI_SPRINTF( numberString2, 256, "%08X", + (unsigned int)hash ); + + std::string fileName1; + fileName1 = fileName; + fileName1 += "/CLI_"; + fileName1 += numberString1; + fileName1 += "_prepend.cl"; + + std::string fileName2; + fileName2 = fileName; + fileName2 += "/CLI_"; + fileName2 += numberString2; + fileName2 += "_prepend.cl"; + + std::string fileName3; + fileName3 = fileName; + fileName3 += "/CLI_prepend.cl"; + + std::ifstream is; + + is.open( + fileName1.c_str(), + std::ios::in | std::ios::binary ); + if( is.good() ) + { + log( "Prepending source file: " + fileName1 + "\n" ); + } + else + { + log( "Prepend source file doesn't exist: " + fileName1 + "\n" ); + + is.clear(); + is.open( + fileName2.c_str(), + std::ios::in | std::ios::binary ); + if( is.good() ) + { + log( "Prepending source file: " + fileName2 + "\n" ); + } + else + { + log( "Prepend source file doesn't exist: " + fileName2 + "\n" ); + + is.clear(); + is.open( + fileName3.c_str(), + std::ios::in | std::ios::binary ); + if( is.good() ) + { + log( "Prepending source file: " + fileName3 + "\n" ); + } + else + { + log( "Prepend source file doesn't exist: " + fileName3 + "\n" ); + } + } + } + + if( is.good() ) + { + // The file exists. Figure out how big it is. + size_t filesize = 0; + + is.seekg(0, std::ios::end); + filesize = (size_t)is.tellg(); + is.seekg(0, std::ios::beg); + + size_t newSize = + filesize + + strlen(singleString) + + 1; // for the null terminator + + char* newSingleString = new char[ newSize ]; + if( newSingleString ) + { + memset( newSingleString, 0, newSize ); + + is.read( newSingleString, filesize ); + + CLI_STRCAT( newSingleString, newSize, singleString ); + + delete [] singleString; + + singleString = newSingleString; + count = 1; + strings = ( const char** )&singleString; + lengths = NULL; + + injected = true; + } + + is.close(); + } + } + + m_OS.LeaveCriticalSection(); + return injected; +} + +/////////////////////////////////////////////////////////////////////////////// +// +bool CLIntercept::injectProgramSPIRV( + const uint64_t hash, + size_t& length, + const void*& il, + char*& injectedIL ) +{ + m_OS.EnterCriticalSection(); + + bool injected = false; + + std::string fileName; + + // Get the dump directory name. + { + OS().GetDumpDirectoryName( sc_DumpDirectoryName, fileName ); + fileName += "/Inject"; + } + + // Make two candidate filenames. They will have the form: + // CLI___0000.spv, or + // CLI__0000.spv + { + char numberString1[256] = ""; + CLI_SPRINTF( numberString1, 256, "%04u_%08X_0000", + m_ProgramNumber, + (unsigned int)hash ); + + char numberString2[256] = ""; + CLI_SPRINTF( numberString2, 256, "%08X_0000", + (unsigned int)hash ); + + std::string fileName1; + fileName1 = fileName; + fileName1 += "/CLI_"; + fileName1 += numberString1; + fileName1 += ".spv"; + + std::string fileName2; + fileName2 = fileName; + fileName2 += "/CLI_"; + fileName2 += numberString2; + fileName2 += ".spv"; + + std::ifstream is; + + is.open( + fileName1.c_str(), + std::ios::in | std::ios::binary ); + if( is.good() ) + { + log( "Injecting SPIR-V file: " + fileName1 + "\n" ); + } + else + { + log( "Injection SPIR-V file doesn't exist: " + fileName1 + "\n" ); + + is.clear(); + is.open( + fileName2.c_str(), + std::ios::in | std::ios::binary ); + if( is.good() ) + { + log( "Injecting SPIR-V file: " + fileName2 + "\n" ); + } + else + { + log( "Injection SPIR-V file doesn't exist: " + fileName2 + "\n" ); + } + } + + if( is.good() ) + { + // The file exists. Figure out how big it is. + size_t filesize = 0; + + is.seekg(0, std::ios::end); + filesize = (size_t)is.tellg(); + is.seekg(0, std::ios::beg); + + injectedIL = new char[ filesize ]; + if( injectedIL ) + { + is.read( injectedIL, filesize ); + + il = injectedIL; + length = filesize; + + injected = true; + } + + is.close(); + } + } + + m_OS.LeaveCriticalSection(); + return injected; +} + +/////////////////////////////////////////////////////////////////////////////// +// +bool CLIntercept::injectProgramOptions( + const cl_program program, + const char*& options, + char*& newOptions ) +{ + m_OS.EnterCriticalSection(); + + CLI_ASSERT( newOptions == NULL ); + + bool injected = false; + + unsigned int programNumber = m_ProgramNumberMap[ program ]; + uint64_t programHash = m_ProgramHashMap[ program ]; + unsigned int compileCount = m_ProgramNumberCompileCountMap[ programNumber ]; + + std::string fileName; + + // Get the dump directory name. + { + OS().GetDumpDirectoryName( sc_DumpDirectoryName, fileName ); + fileName += "/Inject"; + } + // Make four candidate filenames. They will have the form: + // CLI____options.txt, or + // CLI___options.txt, or + // CLI__options.txt, or + // CLI_options.txt + { + char numberString1[256] = ""; + CLI_SPRINTF( numberString1, 256, "%04u_%08X_%04u", + programNumber, + (unsigned int)programHash, + compileCount ); + + char numberString2[256] = ""; + CLI_SPRINTF( numberString2, 256, "%08X_%04u", + (unsigned int)programHash, + compileCount ); + + char numberString3[256] = ""; + CLI_SPRINTF( numberString3, 256, "%08X", + (unsigned int)programHash ); + + std::string fileName1; + fileName1 = fileName; + fileName1 += "/CLI_"; + fileName1 += numberString1; + fileName1 += "_options.txt"; + + std::string fileName2; + fileName2 = fileName; + fileName2 += "/CLI_"; + fileName2 += numberString2; + fileName2 += "_options.txt"; + + std::string fileName3; + fileName3 = fileName; + fileName3 += "/CLI_"; + fileName3 += numberString3; + fileName3 += "_options.txt"; + + std::string fileName4; + fileName4 = fileName; + fileName4 += "/CLI_options.txt"; + + std::ifstream is; + + is.open( + fileName1.c_str(), + std::ios::in | std::ios::binary ); + if( is.good() ) + { + log( "Injecting options file: " + fileName1 + "\n" ); + } + else + { + log( "Injection options file doesn't exist: " + fileName1 + "\n" ); + + is.clear(); + is.open( + fileName2.c_str(), + std::ios::in | std::ios::binary ); + if( is.good() ) + { + log( "Injecting options file: " + fileName2 + "\n" ); + } + else + { + log( "Injection options file doesn't exist: " + fileName2 + "\n" ); + + is.clear(); + is.open( + fileName3.c_str(), + std::ios::in | std::ios::binary ); + if( is.good() ) + { + log( "Injecting options file: " + fileName3 + "\n" ); + } + else + { + log( "Injection options file doesn't exist: " + fileName3 + "\n" ); + + is.clear(); + is.open( + fileName4.c_str(), + std::ios::in | std::ios::binary ); + if( is.good() ) + { + log( "Injecting options file: " + fileName4 + "\n" ); + } + else + { + log( "Injection options file doesn't exist: " + fileName4 + "\n" ); + } + } + } + } + + if( is.good() ) + { + // The file exists. Figure out how big it is. + size_t filesize = 0; + + is.seekg(0, std::ios::end); + filesize = (size_t)is.tellg(); + is.seekg(0, std::ios::beg); + + newOptions = new char[ filesize + 1 ]; + if( newOptions ) + { + memset( newOptions, 0, filesize + 1 ); + + is.read( newOptions, filesize ); + + options = newOptions; + + injected = true; + } + + is.close(); + } + } + + m_OS.LeaveCriticalSection(); + return injected; +} + +/////////////////////////////////////////////////////////////////////////////// +// +bool CLIntercept::appendBuildOptions( + const char*& options, + char*& newOptions ) +{ + m_OS.EnterCriticalSection(); + + bool modified = false; + + if( options == NULL ) + { + // If the options string does not exist, we can simply point it at the + // options we'd like to "append" to it. We don't need to allocate any + // new memory in this case. We also expect that we haven't allocated + // any new options in this case, because if we did, we would have + // pointed the options string to the new options. + + CLI_ASSERT( newOptions == NULL ); + options = config().AppendBuildOptions.c_str(); + + modified = true; + } + else + { + // If the options string does exist, we have two possibilities: + // Either we've already modified the options so we've already + // allocated new options, or we're still working on the application + // provided options. + + size_t newSize = + strlen(options) + + 1 // for a space + + config().AppendBuildOptions.length() + + 1; // for the null terminator + + char* newNewOptions = new char[ newSize ]; + if( newNewOptions ) + { + memset( newNewOptions, 0, newSize ); + + CLI_STRCAT( newNewOptions, newSize, options ); + CLI_STRCAT( newNewOptions, newSize, " " ); + CLI_STRCAT( newNewOptions, newSize, config().AppendBuildOptions.c_str() ); + + // If we have already allocated new options, we can free them + // now. + if( newOptions ) + { + delete [] newOptions; + newOptions = NULL; + } + + // Either way, the new new options are now the new options. + newOptions = newNewOptions; + options = newOptions; + + modified = true; + } + } + + m_OS.LeaveCriticalSection(); + return modified; +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::dumpProgramSourceScript( + cl_program program, + const char* singleString ) +{ +#if defined(_WIN32) + + m_OS.EnterCriticalSection(); + + CLI_ASSERT( config().DumpProgramSourceScript || config().SimpleDumpProgramSource ); + + char dirname[MAX_PATH] = ""; + char filename[MAX_PATH] = ""; + char filepath[MAX_PATH] = ""; + + if( config().DumpProgramSourceScript ) + { + size_t remaining = MAX_PATH; + + char date[9] = ""; + char time[9] = ""; + char* curPos = NULL; + char* nextToken = NULL; + char* pch = NULL; + + // Directory: + + curPos = dirname; + remaining = MAX_PATH; + memset( curPos, 0, MAX_PATH ); + + _strdate_s( date, 9 ); + _strtime_s( time, 9 ); + + memcpy_s( curPos, remaining, "CLShaderDump_", 14 ); + curPos += 13; + remaining -= 13; + + memcpy_s( curPos, remaining, strtok_s( date, "/", &nextToken ), 2 ); + curPos += 2; + remaining -= 2; + + memcpy_s( curPos, remaining, strtok_s( NULL, "/", &nextToken ), 2 ); + curPos += 2; + remaining -= 2; + + memcpy_s( curPos, remaining, strtok_s( NULL, "/", &nextToken ), 2 ); + curPos += 2; + remaining -= 2; + + ::CreateDirectoryA( dirname, NULL ); + + // File: + + curPos = filename; + remaining = MAX_PATH; + memset( curPos, 0, MAX_PATH ); + + if( GetModuleFileNameA( NULL, filename, MAX_PATH-1 ) == 0 ) + { + CLI_ASSERT( 0 ); + strcpy_s( curPos, remaining, "process.exe" ); + } + + pch = strrchr( filename, '\\' ); + pch++; + memcpy_s( curPos, remaining, pch, strlen( pch ) ); + curPos += strlen( pch ) - 4; // -4 to cut off ".exe" + remaining -= strlen( pch ) - 4; + + memcpy_s( curPos, remaining, "_", 2 ); + curPos += 1; + remaining -= 1; + + memcpy_s( curPos, remaining, strtok_s( time, ":", &nextToken ), 2 ); + curPos += 2; + remaining -= 2; + + memcpy_s( curPos, remaining, strtok_s( NULL, ":", &nextToken ), 2 ); + curPos += 2; + remaining -= 2; + + memcpy_s( curPos, remaining, strtok_s( NULL, ":", &nextToken ), 2 ); + curPos += 2; + remaining -= 2; + + CLI_SPRINTF( curPos, remaining, "_%8.8x", m_ProgramNumber ); + curPos += 9; + remaining -= 9; + } + else + { + CLI_SPRINTF( dirname, MAX_PATH, "." ); + CLI_SPRINTF( filename, MAX_PATH, "kernel" ); + } + + CLI_SPRINTF( filepath, MAX_PATH, "%s/%s.%s", dirname, filename, "cl" ); + + if( singleString ) + { + std::ofstream os; + os.open( + filepath, + std::ios::out | std::ios::binary ); + if( os.good() ) + { + os.write( singleString, strlen( singleString ) ); + os.close(); + } + } + + m_ProgramNumberMap[ program ] = m_ProgramNumber; + m_ProgramNumberCompileCountMap[ m_ProgramNumber ] = 0; + m_ProgramNumber++; + + m_OS.LeaveCriticalSection(); + +#else + CLI_ASSERT( 0 ); +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::dumpProgramSource( + uint64_t hash, + cl_program program, + const char* singleString ) +{ + m_OS.EnterCriticalSection(); + + CLI_ASSERT( config().DumpProgramSource || config().AutoCreateSPIRV ); + + std::string fileName; + + // Get the dump directory name. + { + OS().GetDumpDirectoryName( sc_DumpDirectoryName, fileName ); + } + // Make the filename. It will have the form: + // CLI___source.cl + { + char numberString[256] = ""; + + if( config().OmitProgramNumber ) + { + CLI_SPRINTF( numberString, 256, "%08X", + (unsigned int)hash ); + } + else + { + CLI_SPRINTF( numberString, 256, "%04u_%08X", + m_ProgramNumber, + (unsigned int)hash ); + } + + fileName += "/CLI_"; + fileName += numberString; + fileName += "_source.cl"; + } + // Now make directories as appropriate. + { + OS().MakeDumpDirectories( fileName ); + } + // Dump the program source to a .cl file. + if( singleString ) + { + std::ofstream os; + os.open( + fileName.c_str(), + std::ios::out | std::ios::binary ); + if( os.good() ) + { + log( "Dumping program to file (inject): " + fileName + "\n" ); + + // don't write the null terminator to the file + os.write( singleString, strlen( singleString ) ); + os.close(); + } + } + + m_ProgramNumberMap[ program ] = m_ProgramNumber; + m_ProgramNumberCompileCountMap[ m_ProgramNumber ] = 0; + m_ProgramNumber++; + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::dumpInputProgramBinaries( + uint64_t hash, + const cl_program program, + cl_uint num_devices, + const cl_device_id* device_list, + const size_t* lengths, + const unsigned char** binaries ) +{ + m_OS.EnterCriticalSection(); + + CLI_ASSERT( config().DumpInputProgramBinaries ); + + std::string fileName; + + // Get the dump directory name. + { + OS().GetDumpDirectoryName( sc_DumpDirectoryName, fileName ); + } + + // Make the filename. It will have the form: + // CLI__ + // Leave off the extension for now. + { + char numberString[256] = ""; + + if( config().OmitProgramNumber ) + { + CLI_SPRINTF( numberString, 256, "%08X", + (unsigned int)hash ); + } + else + { + CLI_SPRINTF( numberString, 256, "%04u_%08X", + m_ProgramNumber, + (unsigned int)hash ); + } + + fileName += "/CLI_"; + fileName += numberString; + } + // Now make directories as appropriate. + { + OS().MakeDumpDirectories( fileName ); + } + + for( size_t i = 0; i < num_devices; i++ ) + { + cl_device_type deviceType = CL_DEVICE_TYPE_DEFAULT; + + // It's OK if this fails. If it does, it just + // means that our output file won't have a device + // type. + dispatch().clGetDeviceInfo( + device_list[ i ], + CL_DEVICE_TYPE, + sizeof( deviceType ), + &deviceType, + NULL ); + + std::string outputFileName = fileName; + + if( deviceType & CL_DEVICE_TYPE_CPU ) + { + outputFileName += "_CPU"; + } + if( deviceType & CL_DEVICE_TYPE_GPU ) + { + outputFileName += "_GPU"; + } + if( deviceType & CL_DEVICE_TYPE_ACCELERATOR ) + { + outputFileName += "_ACCELERATOR"; + } + if( deviceType & CL_DEVICE_TYPE_CUSTOM ) + { + outputFileName += "_CUSTOM"; + } + + outputFileName += ".bin"; + + std::ofstream os; + os.open( + outputFileName.c_str(), + std::ios::out | std::ios::binary ); + if( os.good() ) + { + log( "Dumping input program binary to file: " + outputFileName + "\n" ); + + os.write( + (const char*)binaries[ i ], + lengths[ i ] ); + os.close(); + } + } + + m_ProgramNumberMap[ program ] = m_ProgramNumber; + m_ProgramNumberCompileCountMap[ m_ProgramNumber ] = 0; + m_ProgramNumber++; + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::dumpProgramSPIRV( + uint64_t hash, + cl_program program, + const size_t length, + const void* il ) +{ + m_OS.EnterCriticalSection(); + + CLI_ASSERT( config().DumpProgramSPIRV ); + + std::string fileName; + + // Get the dump directory name. + { + OS().GetDumpDirectoryName( sc_DumpDirectoryName, fileName ); + } + + // Make the filename. It will have the form: + // CLI___0000.spv + { + char numberString[256] = ""; + + if( config().OmitProgramNumber ) + { + CLI_SPRINTF( numberString, 256, "%08X_0000", + (unsigned int)hash ); + } + else + { + CLI_SPRINTF( numberString, 256, "%04u_%08X_0000", + m_ProgramNumber, + (unsigned int)hash ); + } + + fileName += "/CLI_"; + fileName += numberString; + fileName += ".spv"; + } + + // Now make directories as appropriate. + { + OS().MakeDumpDirectories( fileName ); + } + + // Dump the program source to a .cl file. + { + std::ofstream os; + os.open( + fileName.c_str(), + std::ios::out | std::ios::binary ); + if( os.good() ) + { + log( "Dumping program to file (inject): " + fileName + "\n" ); + + os.write( (const char*)il, length ); + os.close(); + + // Optionally, run spirv-dis to disassemble the generated module. + if( !config().SPIRVDis.empty() ) + { + std::string command = + config().SPIRVDis + + " -o " + fileName + "t" + + " " + fileName; + + logf( "Running: %s\n", command.c_str() ); + OS().ExecuteCommand( command ); + } + } + } + + m_ProgramNumberMap[ program ] = m_ProgramNumber; + m_ProgramNumberCompileCountMap[ m_ProgramNumber ] = 0; + m_ProgramNumber++; + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::dumpProgramOptionsScript( + const cl_program program, + const char* options ) +{ +#if defined(_WIN32) + + m_OS.EnterCriticalSection(); + + CLI_ASSERT( config().DumpProgramSource || config().SimpleDumpProgramSource ); + + unsigned int programNumber = m_ProgramNumberMap[ program ]; + + if( options ) + { + char dirname[MAX_PATH] = ""; + char filename[MAX_PATH] = ""; + char filepath[MAX_PATH] = ""; + + if( config().DumpProgramSourceScript ) + { + size_t remaining = MAX_PATH; + + char date[9] = ""; + char time[9] = ""; + char* curPos = NULL; + char* nextToken = NULL; + char* pch = NULL; + + // Directory: + + curPos = dirname; + remaining = MAX_PATH; + memset( curPos, 0, MAX_PATH ); + + _strdate_s( date, 9 ); + _strtime_s( time, 9 ); + + memcpy_s( curPos, remaining, "CLShaderDump_", 14 ); + curPos += 13; + remaining -= 13; + + memcpy_s( curPos, remaining, strtok_s( date, "/", &nextToken ), 2 ); + curPos += 2; + remaining -= 2; + + memcpy_s( curPos, remaining, strtok_s( NULL, "/", &nextToken ), 2 ); + curPos += 2; + remaining -= 2; + + memcpy_s( curPos, remaining, strtok_s( NULL, "/", &nextToken ), 2 ); + curPos += 2; + remaining -= 2; + + ::CreateDirectoryA( dirname, NULL ); + + // File: + + curPos = filename; + remaining = MAX_PATH; + memset( curPos, 0, MAX_PATH ); + + if( GetModuleFileNameA( NULL, filename, MAX_PATH-1 ) == 0 ) + { + CLI_ASSERT( 0 ); + strcpy_s( curPos, remaining, "process.exe" ); + } + + pch = strrchr( filename, '\\' ); + pch++; + memcpy_s( curPos, remaining, pch, strlen( pch ) ); + curPos += strlen( pch ) - 4; // -4 to cut off ".exe" + remaining -= strlen( pch ) - 4; + + memcpy_s( curPos, remaining, "_", 2 ); + curPos += 1; + remaining -= 1; + + memcpy_s( curPos, remaining, strtok_s( time, ":", &nextToken ), 2 ); + curPos += 2; + remaining -= 2; + + memcpy_s( curPos, remaining, strtok_s( NULL, ":", &nextToken ), 2 ); + curPos += 2; + remaining -= 2; + + memcpy_s( curPos, remaining, strtok_s( NULL, ":", &nextToken ), 2 ); + curPos += 2; + remaining -= 2; + + CLI_SPRINTF( curPos, remaining, "_%8.8x", programNumber ); + curPos += 9; + remaining -= 9; + } + else + { + CLI_SPRINTF( dirname, MAX_PATH, "." ); + CLI_SPRINTF( filename, MAX_PATH, "kernel" ); + } + + CLI_SPRINTF( filepath, MAX_PATH, "%s/%s.%s", dirname, filename, "txt" ); + + std::ofstream os; + os.open( + filepath, + std::ios::out | std::ios::binary ); + if( os.good() ) + { + os.write( options, strlen( options ) ); + os.close(); + } + } + + m_OS.LeaveCriticalSection(); + +#else + CLI_ASSERT( 0 ); +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::dumpProgramOptions( + const cl_program program, + const char* options ) +{ + m_OS.EnterCriticalSection(); + + CLI_ASSERT( config().DumpProgramSource || config().DumpProgramBinaries || config().DumpProgramSPIRV ); + + unsigned int programNumber = m_ProgramNumberMap[ program ]; + uint64_t programHash = m_ProgramHashMap[ program ]; + unsigned int compileCount = m_ProgramNumberCompileCountMap[ programNumber ]; + + if( options ) + { + std::string fileName; + + // Get the dump directory name. + { + OS().GetDumpDirectoryName( sc_DumpDirectoryName, fileName ); + } + // Make the filename. It will have the form: + // CLI___ + // Leave off the extension for now. + { + char numberString[256] = ""; + + if( config().OmitProgramNumber ) + { + CLI_SPRINTF( numberString, 256, "%08X_%04u", + (unsigned int)programHash, + compileCount ); + } + else + { + CLI_SPRINTF( numberString, 256, "%04u_%08X_%04u", + programNumber, + (unsigned int)programHash, + compileCount ); + } + + fileName += "/CLI_"; + fileName += numberString; + } + // Dump the program source to a .txt file. + { + fileName += "_options.txt"; + std::ofstream os; + os.open( + fileName.c_str(), + std::ios::out | std::ios::binary ); + if( os.good() ) + { + log( "Dumping program options to file (inject): " + fileName + "\n" ); + + // don't write the null terminator to the file + os.write( options, strlen( options) ); + os.close(); + } + } + } + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::dumpProgramBuildLog( + const cl_program program, + const cl_device_id device, + const char* buildLog, + const size_t buildLogSize ) +{ + // We're already in a critical section when we get here, so we don't need to + // grab the critical section again. + + CLI_ASSERT( config().DumpProgramBuildLogs ); + CLI_ASSERT( buildLog ); + + unsigned int programNumber = m_ProgramNumberMap[ program ]; + uint64_t programHash = m_ProgramHashMap[ program ]; + unsigned int compileCount = m_ProgramNumberCompileCountMap[ programNumber ]; + + std::string fileName; + + // Get the dump directory name. + { + OS().GetDumpDirectoryName( sc_DumpDirectoryName, fileName ); + } + // Make the filename. It will have the form: + // CLI___ + // Leave off the extension for now. + { + char numberString[256] = ""; + + if( config().OmitProgramNumber ) + { + CLI_SPRINTF( numberString, 256, "%08X_%04u", + (unsigned int)programHash, + compileCount ); + } + else + { + CLI_SPRINTF( numberString, 256, "%04u_%08X_%04u", + programNumber, + (unsigned int)programHash, + compileCount ); + } + + fileName += "/CLI_"; + fileName += numberString; + } + // Now make directories as appropriate. + { + OS().MakeDumpDirectories( fileName ); + } + + cl_device_type deviceType = CL_DEVICE_TYPE_DEFAULT; + + // It's OK if this fails. If it does, it just + // means that our output file won't have a device + // type. + dispatch().clGetDeviceInfo( + device, + CL_DEVICE_TYPE, + sizeof( deviceType ), + &deviceType, + NULL ); + + if( deviceType & CL_DEVICE_TYPE_CPU ) + { + fileName += "_CPU"; + } + if( deviceType & CL_DEVICE_TYPE_GPU ) + { + fileName += "_GPU"; + } + if( deviceType & CL_DEVICE_TYPE_ACCELERATOR ) + { + fileName += "_ACCELERATOR"; + } + if( deviceType & CL_DEVICE_TYPE_CUSTOM ) + { + fileName += "_CUSTOM"; + } + + fileName += "_build_log.txt"; + + std::ofstream os; + os.open( + fileName.c_str(), + std::ios::out | std::ios::binary ); + if( os.good() ) + { + log( "Dumping build log to file: " + fileName + "\n" ); + + os.write( + buildLog, + buildLogSize ); + os.close(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::updateHostTimingStats( + const std::string& functionName, + cl_kernel kernel, + uint64_t start, + uint64_t end ) +{ + m_OS.EnterCriticalSection(); + + std::string key( functionName ); + if( kernel ) + { + const std::string& kernelName = m_KernelNameMap[ kernel ]; + key += "( "; + key += kernelName; + key += " )"; + } + + SCpuTimingStats* pCpuTimingStats = m_CpuTimingStatsMap[ key ]; + if( pCpuTimingStats == NULL ) + { + pCpuTimingStats = new SCpuTimingStats; + if( pCpuTimingStats == NULL ) + { + // Memory allocation failure. + } + else + { + pCpuTimingStats->NumberOfCalls = 0; + pCpuTimingStats->TotalTicks = 0; + pCpuTimingStats->MinTicks = UINT_MAX; + pCpuTimingStats->MaxTicks = 0; + + m_CpuTimingStatsMap[ key ] = pCpuTimingStats; + } + } + + uint64_t numberOfCalls = 0; + uint64_t tickDelta = end - start; + + if( pCpuTimingStats != NULL ) + { + pCpuTimingStats->NumberOfCalls++; + pCpuTimingStats->TotalTicks += tickDelta; + pCpuTimingStats->MinTicks = std::min< uint64_t >( pCpuTimingStats->MinTicks, tickDelta ); + pCpuTimingStats->MaxTicks = std::max< uint64_t >( pCpuTimingStats->MaxTicks, tickDelta ); + + numberOfCalls = pCpuTimingStats->NumberOfCalls; + } + + if( config().HostPerformanceTimeLogging ) + { + uint64_t nsDelta = OS().TickToNS( tickDelta ); + logf( "Host Time for call %u: %s = %u\n", + (unsigned int)numberOfCalls, + key.c_str(), + (unsigned int)nsDelta ); + } + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::modifyCommandQueueProperties( + cl_command_queue_properties& props ) const +{ + if( config().DevicePerformanceTiming || + config().ITTPerformanceTiming || + config().ChromePerformanceTiming || + config().SIMDSurvey || + !config().DevicePerfCounterCustom.empty() ) + { + props |= (cl_command_queue_properties)CL_QUEUE_PROFILING_ENABLE; + } + if( config().InOrderQueue ) + { + props &= ~(cl_command_queue_properties)CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::createCommandQueueOverrideInit( + const cl_queue_properties* properties, + cl_queue_properties*& pLocalQueueProperties ) const +{ + // We want to add command queue properties, unless command queue + // properties already exist (requesting the same property twice is an + // error). So, look through the queue properties for the command queue + // properties enum. We need to do this anyways to count the number of + // property pairs. + bool foundCommandQueuePropertiesEnum = false; + int numProperties = 0; + if( properties ) + { + while( properties[ numProperties ] != 0 ) + { + if( properties[ numProperties ] == CL_QUEUE_PROPERTIES ) + { + foundCommandQueuePropertiesEnum = true; + } + numProperties += 2; + } + } + + if( foundCommandQueuePropertiesEnum == false ) + { + // The performance hint property isn't already set, so we'll + // need to allocate an extra pair of properties for it. + numProperties += 2; + } + + // Allocate a new array of properties. We need to allocate two + // properties for each pair, plus one property for the terminating + // zero. + pLocalQueueProperties = new cl_queue_properties[ numProperties + 1 ]; + if( pLocalQueueProperties ) + { + // Copy the old properties array to the new properties array, + // if the new properties array exists. + numProperties = 0; + if( properties ) + { + while( properties[ numProperties ] != 0 ) + { + pLocalQueueProperties[ numProperties ] = properties[ numProperties ]; + if( properties[ numProperties ] == CL_QUEUE_PROPERTIES ) + { + CLI_ASSERT( foundCommandQueuePropertiesEnum ); + + cl_command_queue_properties props = properties[ numProperties + 1 ]; + + modifyCommandQueueProperties( props ); + + pLocalQueueProperties[ numProperties + 1 ] = props; + } + else + { + pLocalQueueProperties[ numProperties + 1 ] = + properties[ numProperties + 1 ]; + } + numProperties += 2; + } + } + // Add command queue properties if they aren't already set. + if( foundCommandQueuePropertiesEnum == false ) + { + cl_command_queue_properties props = 0; + + modifyCommandQueueProperties( props ); + + pLocalQueueProperties[ numProperties ] = CL_QUEUE_PROPERTIES; + pLocalQueueProperties[ numProperties + 1 ] = props; + numProperties += 2; + } + // Add the terminating zero. + pLocalQueueProperties[ numProperties ] = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::createCommandQueueOverrideCleanup( + cl_queue_properties*& pLocalQueueProperties ) const +{ + if( pLocalQueueProperties ) + { + delete pLocalQueueProperties; + pLocalQueueProperties = NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::addTimingEvent( + const std::string& functionName, + const uint64_t queuedTime, + const cl_kernel kernel, + const cl_uint workDim, + const size_t* gws, + const size_t* lws, + cl_event event ) +{ + m_OS.EnterCriticalSection(); + + SEventListNode* pNode = new SEventListNode; + if( pNode ) + { + pNode->FunctionName = functionName; + if( kernel ) + { + pNode->KernelName = m_KernelNameMap[ kernel ]; + + if( config().DevicePerformanceTimeHashTracking ) + { + cl_program program = NULL; + dispatch().clGetKernelInfo( + kernel, + CL_KERNEL_PROGRAM, + sizeof(program), + &program, + NULL ); + if( program ) + { + unsigned int programNumber = m_ProgramNumberMap[ program ]; + uint64_t programHash = m_ProgramHashMap[ program ]; + unsigned int compileCount = m_ProgramNumberCompileCountMap[ programNumber ]; + + char hashString[256] = ""; + if( config().OmitProgramNumber ) + { + CLI_SPRINTF( hashString, 256, "(%08X_%04u)", + (unsigned int)programHash, + compileCount ); + } + else + { + CLI_SPRINTF( hashString, 256, "(%04u_%08X_%04u)", + programNumber, + (unsigned int)programHash, + compileCount ); + } + pNode->KernelName += hashString; + } + } + + if( config().DevicePerformanceTimeKernelInfoTracking ) + { + cl_command_queue queue = NULL; + dispatch().clGetEventInfo( + event, + CL_EVENT_COMMAND_QUEUE, + sizeof(queue), + &queue, + NULL ); + if( queue ) + { + cl_device_id device = NULL; + dispatch().clGetCommandQueueInfo( + queue, + CL_QUEUE_DEVICE, + sizeof(device), + &device, + NULL ); + if( device ) + { + std::ostringstream ss; + { + size_t pwgsm = 0; + dispatch().clGetKernelWorkGroupInfo( + kernel, + device, + CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE, + sizeof(pwgsm), + &pwgsm, + NULL ); + if( pwgsm ) + { + ss << " SIMD" << (unsigned int)pwgsm; + } + } + { + cl_ulong slm = 0; + dispatch().clGetKernelWorkGroupInfo( + kernel, + device, + CL_KERNEL_LOCAL_MEM_SIZE, + sizeof(slm), + &slm, + NULL ); + if( slm ) + { + ss << " SLM=" << (unsigned int)slm; + } + } + { + cl_ulong tpm = 0; + dispatch().clGetKernelWorkGroupInfo( + kernel, + device, + CL_KERNEL_PRIVATE_MEM_SIZE, + sizeof(tpm), + &tpm, + NULL ); + if( tpm ) + { + ss << " TPM=" << (unsigned int)tpm; + } + } + { + cl_ulong spill = 0; + dispatch().clGetKernelWorkGroupInfo( + kernel, + device, + CL_KERNEL_SPILL_MEM_SIZE_INTEL, + sizeof(spill), + &spill, + NULL ); + if( spill ) + { + ss << " SPILL=" << (unsigned int)spill; + } + } + pNode->KernelName += ss.str(); + } + } + } + + if( config().DevicePerformanceTimeGWSTracking && gws ) + { + std::ostringstream ss; + ss << " GWS[ "; + if( workDim >= 1 ) + { + ss << gws[0]; + } + if( workDim >= 2 ) + { + ss << " x " << gws[1]; + } + if( workDim >= 3 ) + { + ss << " x " << gws[2]; + } + ss << " ]"; + pNode->KernelName += ss.str(); + } + + if( config().DevicePerformanceTimeLWSTracking ) + { + std::ostringstream ss; + ss << " LWS[ "; + if( lws ) + { + if( workDim >= 1 ) + { + ss << lws[0]; + } + if( workDim >= 2 ) + { + ss << " x " << lws[1]; + } + if( workDim >= 3 ) + { + ss << " x " << lws[2]; + } + } + else + { + ss << "NULL"; + } + ss << " ]"; + pNode->KernelName += ss.str(); + } + } + pNode->QueuedTime = queuedTime; + pNode->Kernel = kernel; // Note: no retain, so cannot count on this value... + pNode->Event = event; + + m_EventList.push_back( pNode ); + } + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::checkTimingEvents() +{ + m_OS.EnterCriticalSection(); + + CEventList::iterator current = m_EventList.begin(); + CEventList::iterator next; + + while( current != m_EventList.end() ) + { + cl_int errorCode = CL_SUCCESS; + cl_int eventStatus = 0; + + next = current; + ++next; + + SEventListNode* pNode = *current; + + errorCode = dispatch().clGetEventInfo( + pNode->Event, + CL_EVENT_COMMAND_EXECUTION_STATUS, + sizeof( eventStatus ), + &eventStatus, + NULL ); + + switch( errorCode ) + { + case CL_SUCCESS: + if( eventStatus == CL_COMPLETE ) + { + if( config().DevicePerformanceTiming || + config().ITTPerformanceTiming || + config().ChromePerformanceTiming || + config().SIMDSurvey ) + { + cl_ulong commandQueued = 0; + cl_ulong commandSubmit = 0; + cl_ulong commandStart = 0; + cl_ulong commandEnd = 0; + + uint64_t numberOfCalls = 0; + + errorCode |= dispatch().clGetEventProfilingInfo( + pNode->Event, + CL_PROFILING_COMMAND_QUEUED, + sizeof( commandQueued ), + &commandQueued, + NULL ); + errorCode |= dispatch().clGetEventProfilingInfo( + pNode->Event, + CL_PROFILING_COMMAND_SUBMIT, + sizeof( commandSubmit ), + &commandSubmit, + NULL ); + errorCode |= dispatch().clGetEventProfilingInfo( + pNode->Event, + CL_PROFILING_COMMAND_START, + sizeof( commandStart ), + &commandStart, + NULL ); + errorCode |= dispatch().clGetEventProfilingInfo( + pNode->Event, + CL_PROFILING_COMMAND_END, + sizeof( commandEnd ), + &commandEnd, + NULL ); + if( errorCode == CL_SUCCESS ) + { + cl_ulong delta = commandEnd - commandStart; + + const std::string& key = + pNode->KernelName.empty() ? + pNode->FunctionName : + pNode->KernelName; + + SDeviceTimingStats* pDeviceTimingStats = m_DeviceTimingStatsMap[ key ]; + if( pDeviceTimingStats == NULL ) + { + pDeviceTimingStats = new SDeviceTimingStats; + if( pDeviceTimingStats == NULL ) + { + // Memory allocation failure. + } + else + { + pDeviceTimingStats->NumberOfCalls = 0; + pDeviceTimingStats->TotalNS = 0; + pDeviceTimingStats->MinNS = CL_ULONG_MAX; + pDeviceTimingStats->MaxNS = 0; + + m_DeviceTimingStatsMap[ key ] = pDeviceTimingStats; + } + } + + if( pDeviceTimingStats != NULL ) + { + pDeviceTimingStats->NumberOfCalls++; + pDeviceTimingStats->TotalNS += delta; + pDeviceTimingStats->MinNS = std::min< cl_ulong >( pDeviceTimingStats->MinNS, delta ); + pDeviceTimingStats->MaxNS = std::max< cl_ulong >( pDeviceTimingStats->MaxNS, delta ); + + numberOfCalls = pDeviceTimingStats->NumberOfCalls; + } + + if( config().DevicePerformanceTimeLogging ) + { + cl_ulong queuedDelta = commandSubmit - commandQueued; + cl_ulong submitDelta = commandStart - commandSubmit; + + logf( "Device Time for call %u to %s = %u ns (queued -> submit), %u ns (submit -> start), %u ns (start -> end)\n", + (cl_uint)numberOfCalls, + key.c_str(), + (cl_uint)queuedDelta, + (cl_uint)submitDelta, + (cl_uint)delta ); + } + + if( config().DevicePerformanceTimelineLogging ) + { + logf( "Device Timeline for call %u to %s = %lu ns (queued), %lu ns (submit), %lu ns (start), %lu ns (end)\n", + (cl_uint)numberOfCalls, + key.c_str(), + commandQueued, + commandSubmit, + commandStart, + commandEnd ); + } + + if( config().SIMDSurvey && + pNode->Kernel ) + { + SSIMDSurveyKernel* pSIMDSurveyKernel = + m_SIMDSurveyKernelMap[ pNode->Kernel ]; + if( pSIMDSurveyKernel ) + { + if( pNode->Kernel == pSIMDSurveyKernel->SIMD8Kernel && + pSIMDSurveyKernel->SIMD8ExecutionTimeNS > delta ) + { + pSIMDSurveyKernel->SIMD8ExecutionTimeNS = delta; + logf( "SIMD Survey: Results: New min SIMD8 Time for kernel %s is: %lu\n", + pNode->KernelName.c_str(), + pSIMDSurveyKernel->SIMD8ExecutionTimeNS ); + } + if( pNode->Kernel == pSIMDSurveyKernel->SIMD16Kernel && + pSIMDSurveyKernel->SIMD16ExecutionTimeNS > delta ) + { + pSIMDSurveyKernel->SIMD16ExecutionTimeNS = delta; + logf( "SIMD Survey: Results: New min SIMD16 Time for kernel %s is: %lu\n", + pNode->KernelName.c_str(), + pSIMDSurveyKernel->SIMD16ExecutionTimeNS ); + } + if( pNode->Kernel == pSIMDSurveyKernel->SIMD32Kernel && + pSIMDSurveyKernel->SIMD32ExecutionTimeNS > delta ) + { + pSIMDSurveyKernel->SIMD32ExecutionTimeNS = delta; + logf( "SIMD Survey: Results: New min SIMD32 Time for kernel %s is: %lu\n", + pNode->KernelName.c_str(), + pSIMDSurveyKernel->SIMD32ExecutionTimeNS ); + } + if( pNode->Kernel != pSIMDSurveyKernel->SIMD8Kernel && + pNode->Kernel != pSIMDSurveyKernel->SIMD16Kernel && + pNode->Kernel != pSIMDSurveyKernel->SIMD32Kernel ) + { + logf( "SIMD Survey: Results: Default Time for kernel %s is: %lu\n", + pNode->KernelName.c_str(), + delta ); + } + } + else + { + logf( "SIMD Survey: Results: Don't have any information kernel %p!?!?\n", + pNode->Kernel ); + } + } + } + } + +#if defined(USE_ITT) + if( config().ITTPerformanceTiming ) + { + const std::string& name = + pNode->KernelName.empty() ? + pNode->FunctionName : + pNode->KernelName; + + ittTraceEvent( + name, + pNode->Event, + pNode->QueuedTime ); + } +#endif + + if( config().ChromePerformanceTiming ) + { + const std::string& name = + pNode->KernelName.empty() ? + pNode->FunctionName : + pNode->KernelName; + + chromeTraceEvent( + name, + pNode->Event, + pNode->QueuedTime ); + } + +#if defined(USE_MDAPI) + if( !config().DevicePerfCounterCustom.empty() ) + { + const std::string& name = + pNode->KernelName.empty() ? + pNode->FunctionName : + pNode->KernelName; + + saveMDAPICounters( + name, + pNode->Event ); + } +#endif + + dispatch().clReleaseEvent( pNode->Event ); + delete pNode; + + m_EventList.erase( current ); + } + break; + case CL_INVALID_EVENT: + { + // This is unexpected. We retained the event when we + // added it to the list. Remove the event from the + // list. + logf( "Unexpectedly got CL_INVALID_EVENT for an event from %s!\n", + pNode->FunctionName.c_str() ); + + delete pNode; + + m_EventList.erase( current ); + } + break; + default: + // nothing + break; + } + + current = next; + } + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::addKernelName( + cl_kernel kernel, + const std::string& kernelName ) +{ + m_OS.EnterCriticalSection(); + + m_KernelNameMap[ kernel ] = kernelName; + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::addKernelNames( + cl_kernel* kernels, + cl_uint numKernels ) +{ + m_OS.EnterCriticalSection(); + + while( numKernels-- ) + { + cl_kernel kernel = kernels[ numKernels ]; + char* kernelName = NULL; + size_t kernelNameSize = 0; + cl_int errorCode = CL_SUCCESS; + + errorCode = dispatch().clGetKernelInfo( + kernel, + CL_KERNEL_FUNCTION_NAME, + 0, + NULL, + &kernelNameSize ); + if( errorCode == CL_SUCCESS ) + { + kernelName = new char[ kernelNameSize + 1 ]; + if( kernelName ) + { + errorCode = dispatch().clGetKernelInfo( + kernel, + CL_KERNEL_FUNCTION_NAME, + kernelNameSize, + kernelName, + NULL ); + if( errorCode == CL_SUCCESS ) + { + kernelName[ kernelNameSize ] = 0; + m_KernelNameMap[ kernel ] = kernelName; + } + + delete [] kernelName; + } + } + } + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::removeKernel( + cl_kernel kernel ) +{ + m_OS.EnterCriticalSection(); + + cl_uint refCount = 0; + cl_int errorCode = CL_SUCCESS; + + errorCode = dispatch().clGetKernelInfo( + kernel, + CL_KERNEL_REFERENCE_COUNT, + sizeof( refCount ), + &refCount, + NULL ); + if( errorCode == CL_SUCCESS ) + { + if( refCount == 1 ) + { + m_KernelNameMap.erase( kernel ); + + SSIMDSurveyKernel* pSIMDSurveyKernel = + m_SIMDSurveyKernelMap[ kernel ]; + if( pSIMDSurveyKernel ) + { + errorCode = dispatch().clReleaseKernel( pSIMDSurveyKernel->SIMD8Kernel ); + errorCode = dispatch().clReleaseKernel( pSIMDSurveyKernel->SIMD16Kernel ); + errorCode = dispatch().clReleaseKernel( pSIMDSurveyKernel->SIMD32Kernel ); + + // Remove the parent kernel and each of the child kernels from the map. + m_SIMDSurveyKernelMap.erase( kernel ); + + m_SIMDSurveyKernelMap.erase( pSIMDSurveyKernel->SIMD8Kernel ); + m_SIMDSurveyKernelMap.erase( pSIMDSurveyKernel->SIMD16Kernel ); + m_SIMDSurveyKernelMap.erase( pSIMDSurveyKernel->SIMD32Kernel ); + + // Also clean up the kernel name map. + m_KernelNameMap.erase( pSIMDSurveyKernel->SIMD8Kernel ); + m_KernelNameMap.erase( pSIMDSurveyKernel->SIMD16Kernel ); + m_KernelNameMap.erase( pSIMDSurveyKernel->SIMD32Kernel ); + + // Done! + delete pSIMDSurveyKernel; + pSIMDSurveyKernel = NULL; + } + } + } + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::addBuffer( + cl_mem buffer ) +{ + if( buffer ) + { + m_OS.EnterCriticalSection(); + + cl_int errorCode = CL_SUCCESS; + size_t size = 0; + + errorCode |= dispatch().clGetMemObjectInfo( + buffer, + CL_MEM_SIZE, + sizeof( size_t ), + &size, + NULL ); + + if( errorCode == CL_SUCCESS ) + { + m_MemAllocNumberMap[ buffer ] = m_MemAllocNumber; + m_BufferInfoMap[ buffer ] = size; + m_MemAllocNumber++; + } + + m_OS.LeaveCriticalSection(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::addSampler( + cl_sampler sampler, + const std::string& str ) +{ + if( sampler ) + { + m_OS.EnterCriticalSection(); + m_SamplerDataMap[sampler] = str; + m_OS.LeaveCriticalSection(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::removeSampler( + cl_sampler sampler ) +{ + if( sampler ) + { + m_OS.EnterCriticalSection(); + + CSamplerDataMap::iterator iter = m_SamplerDataMap.find( sampler ); + if( iter != m_SamplerDataMap.end() ) + { + m_SamplerDataMap.erase( iter ); + } + + m_OS.LeaveCriticalSection(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +bool CLIntercept::getSampler( + size_t size, + const void *arg_value, + std::string& str ) const +{ + bool found = false; + + if( ( arg_value != NULL ) && ( size == sizeof( cl_sampler ) ) ) + { + const cl_sampler sampler = *(const cl_sampler *)arg_value; + + CSamplerDataMap::const_iterator iter = m_SamplerDataMap.find( sampler ); + if( iter != m_SamplerDataMap.end() ) + { + str = iter->second; + found = true; + } + } + + return found; +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::dumpArgument( + cl_kernel kernel, + cl_int arg_index, + size_t size, + const void *pBuffer ) +{ + if ( kernel ) + { + m_OS.EnterCriticalSection(); + + std::string fileName = ""; + + // Get the dump directory name. + { + OS().GetDumpDirectoryName( sc_DumpDirectoryName, fileName ); + fileName += "/SetKernelArg/"; + } + + // Now make directories as appropriate. + { + OS().MakeDumpDirectories( fileName ); + } + + // Add the enqueue count to file name + { + char enqueueCount[ MAX_PATH ]; + + CLI_SPRINTF( enqueueCount, MAX_PATH, "%04u", + (unsigned int)m_EnqueueCounter ); + fileName += "SetKernelArg_"; + fileName += enqueueCount; + } + + // Add the kernel name to the filename + { + fileName += "_Kernel_"; + fileName += m_KernelNameMap[ kernel ]; + } + + // Add the arg number to the file name + { + char argName[ MAX_PATH ]; + + CLI_SPRINTF( argName, MAX_PATH, "%d", arg_index ); + + fileName += "_Arg_"; + fileName += argName; + } + + // Add extension to file name + { + fileName += ".bin"; + } + + // Dump the buffer contents to the file. + { + if( pBuffer != NULL) + { + std::ofstream os; + os.open( + fileName.c_str(), + std::ios_base::out | std::ios_base::binary ); + + if( os.good() ) + { + os.write( (const char *)pBuffer, size ); + os.close(); + } + } + } + + m_OS.LeaveCriticalSection(); + } +} +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::addImage( + cl_mem image ) +{ + if( image ) + { + m_OS.EnterCriticalSection(); + + cl_int errorCode = CL_SUCCESS; + + size_t width = 0; + size_t height = 0; + size_t depth = 0; + size_t arraySize = 0; + size_t elementSize = 0; + + errorCode |= dispatch().clGetImageInfo( + image, + CL_IMAGE_WIDTH, + sizeof(width), + &width, + NULL ); + errorCode |= dispatch().clGetImageInfo( + image, + CL_IMAGE_HEIGHT, + sizeof(height), + &height, + NULL ); + errorCode |= dispatch().clGetImageInfo( + image, + CL_IMAGE_DEPTH, + sizeof(depth), + &depth, + NULL ); + errorCode |= dispatch().clGetImageInfo( + image, + CL_IMAGE_ARRAY_SIZE, + sizeof(arraySize), + &arraySize, + NULL ); + errorCode |= dispatch().clGetImageInfo( + image, + CL_IMAGE_ELEMENT_SIZE, + sizeof(elementSize), + &elementSize, + NULL ); + + if( errorCode == CL_SUCCESS ) + { + SImageInfo imageInfo; + + imageInfo.Region[0] = width; + if( height == 0 ) + { + if( arraySize == 0 ) + { + imageInfo.Region[1] = 1; // 1D iamge + } + else + { + imageInfo.Region[1] = arraySize; // 1D image array + } + } + else + { + imageInfo.Region[1] = height; // 2D image, 3D image, or 3D image array + } + + if( depth == 0 ) + { + if( arraySize == 0 ) + { + imageInfo.Region[2] = 1; // 2D image + } + else + { + imageInfo.Region[2] = arraySize; // 2D image array + } + } + else + { + imageInfo.Region[2] = depth; // 3D image + } + + imageInfo.ElementSize = elementSize; + + m_MemAllocNumberMap[ image ] = m_MemAllocNumber; + m_ImageInfoMap[ image ] = imageInfo; + m_MemAllocNumber++; + } + + m_OS.LeaveCriticalSection(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::removeMemObj( + cl_mem memobj ) +{ + m_OS.EnterCriticalSection(); + + cl_uint refCount = 0; + cl_int errorCode = CL_SUCCESS; + + errorCode = dispatch().clGetMemObjectInfo( + memobj, + CL_MEM_REFERENCE_COUNT, + sizeof( refCount ), + &refCount, + NULL ); + if( errorCode == CL_SUCCESS ) + { + if( refCount == 1 ) + { + m_MemAllocNumberMap.erase( memobj ); + m_BufferInfoMap.erase( memobj ); + m_ImageInfoMap.erase( memobj ); + } + } + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::addSVMAllocation( + void* svmPtr, + size_t size ) +{ + if( svmPtr ) + { + m_OS.EnterCriticalSection(); + + m_MemAllocNumberMap[ svmPtr ] = m_MemAllocNumber; + m_SVMAllocInfoMap[ svmPtr ] = size; + m_MemAllocNumber++; + + m_OS.LeaveCriticalSection(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::removeSVMAllocation( + void* svmPtr ) +{ + m_OS.EnterCriticalSection(); + + m_MemAllocNumberMap.erase( svmPtr ); + m_SVMAllocInfoMap.erase( svmPtr ); + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::setKernelArg( + cl_kernel kernel, + cl_uint arg_index, + cl_mem memobj ) +{ + m_OS.EnterCriticalSection(); + + if( m_MemAllocNumberMap.find( memobj ) != m_MemAllocNumberMap.end() ) + { + CKernelArgMemMap& kernelArgMap = m_KernelArgMap[ kernel ]; + kernelArgMap[ arg_index ] = memobj; + } + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::setKernelArgSVMPointer( + cl_kernel kernel, + cl_uint arg_index, + const void* arg ) +{ + m_OS.EnterCriticalSection(); + + // Unlike clSetKernelArg(), which must pass a cl_mem, clSetKernelArgSVMPointer + // can pass a pointer to the base of a SVM allocation or anywhere inside of + // an SVM allocation. As a result, we may need to search the SVM map to find + // the base address and size of the SVM allocation. Still, try to just lookup + // the SVM allocation in the map, just in case the app sets the base address + // (this may be the common case?). + + CKernelArgMemMap& kernelArgMap = m_KernelArgMap[ kernel ]; + + if( m_SVMAllocInfoMap.find( arg ) != m_SVMAllocInfoMap.end() ) + { + // Got it, the pointer was the base address of an SVM allocation. + kernelArgMap[ arg_index ] = arg; + } + else + { + intptr_t iarg = (intptr_t)arg; + for( CSVMAllocInfoMap::iterator i = m_SVMAllocInfoMap.begin(); + i != m_SVMAllocInfoMap.end(); + ++i ) + { + const void* ptr = (*i).first; + size_t size = (*i).second; + + intptr_t start = (intptr_t)ptr; + intptr_t end = start + size; + if( start <= iarg && + iarg < end ) + { + kernelArgMap[ arg_index ] = ptr; + break; + } + } + } + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::dumpBuffersForKernel( + const std::string& name, + cl_kernel kernel, + cl_command_queue command_queue ) +{ + m_OS.EnterCriticalSection(); + + std::string fileNamePrefix = ""; + + // Get the dump directory name. + { + OS().GetDumpDirectoryName( sc_DumpDirectoryName, fileNamePrefix ); + fileNamePrefix += "/memDump"; + fileNamePrefix += name; + fileNamePrefix += "Enqueue/"; + } + + // Now make directories as appropriate. + { + OS().MakeDumpDirectories( fileNamePrefix ); + } + + CKernelArgMemMap& kernelArgMemMap = m_KernelArgMap[ kernel ]; + CKernelArgMemMap::iterator i = kernelArgMemMap.begin(); + while( i != kernelArgMemMap.end() ) + { + CLI_C_ASSERT( sizeof(void*) == sizeof(cl_mem) ); + cl_uint arg_index = (*i).first; + void* allocation = (void*)(*i).second; + cl_mem memobj = (cl_mem)allocation; + ++i; + if( ( m_SVMAllocInfoMap.find( allocation ) != m_SVMAllocInfoMap.end() ) || + ( m_BufferInfoMap.find( memobj ) != m_BufferInfoMap.end() ) ) + { + unsigned int number = m_MemAllocNumberMap[ memobj ]; + + std::string fileName = fileNamePrefix; + char tmpStr[ MAX_PATH ]; + + // Add the enqueue count to file name + { + CLI_SPRINTF( tmpStr, MAX_PATH, "%04u", + (unsigned int)m_EnqueueCounter ); + + fileName += "Enqueue_"; + fileName += tmpStr; + } + + // Add the kernel name to the filename + { + fileName += "_Kernel_"; + fileName += m_KernelNameMap[ kernel ]; + } + + // Add the arg number to the file name + { + CLI_SPRINTF( tmpStr, MAX_PATH, "%u", arg_index ); + + fileName += "_Arg_"; + fileName += tmpStr; + } + + // Add the buffer number to the file name + { + CLI_SPRINTF( tmpStr, MAX_PATH, "%04u", number ); + + fileName += "_Buffer_"; + fileName += tmpStr; + } + + // Add extension to file name + { + fileName += ".bin"; + } + + // Dump the buffer contents to the file. + if( m_SVMAllocInfoMap.find( allocation ) != m_SVMAllocInfoMap.end() ) + { + size_t size = m_SVMAllocInfoMap[ allocation ]; + + cl_int error = dispatch().clEnqueueSVMMap( + command_queue, + CL_TRUE, + CL_MAP_READ, + allocation, + size, + 0, + NULL, + NULL ); + if( error == CL_SUCCESS ) + { + std::ofstream os; + os.open( + fileName.c_str(), + std::ios::out | std::ios::binary ); + + if( os.good() ) + { + os.write( (const char*)allocation, size ); + os.close(); + } + + dispatch().clEnqueueSVMUnmap( + command_queue, + allocation, + 0, + NULL, + NULL ); + } + } + else if( m_BufferInfoMap.find( memobj ) != m_BufferInfoMap.end() ) + { + size_t size = m_BufferInfoMap[ memobj ]; + + cl_int error = CL_SUCCESS; + void* ptr = dispatch().clEnqueueMapBuffer( + command_queue, + memobj, + CL_TRUE, + CL_MAP_READ, + 0, + size, + 0, + NULL, + NULL, + &error ); + if( error == CL_SUCCESS ) + { + std::ofstream os; + os.open( + fileName.c_str(), + std::ios::out | std::ios::binary ); + + if( os.good() ) + { + os.write( (const char*)ptr, size ); + os.close(); + } + + dispatch().clEnqueueUnmapMemObject( + command_queue, + memobj, + ptr, + 0, + NULL, + NULL ); + } + } + } + } + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::dumpImagesForKernel( + const std::string& name, + cl_kernel kernel, + cl_command_queue command_queue ) +{ + m_OS.EnterCriticalSection(); + + std::string fileNamePrefix = ""; + + // Get the dump directory name. + { + OS().GetDumpDirectoryName( sc_DumpDirectoryName, fileNamePrefix ); + fileNamePrefix += "/memDump"; + fileNamePrefix += name; + fileNamePrefix += "Enqueue/"; + } + + // Now make directories as appropriate. + { + OS().MakeDumpDirectories( fileNamePrefix ); + } + + CKernelArgMemMap& kernelArgMemMap = m_KernelArgMap[ kernel ]; + CKernelArgMemMap::iterator i = kernelArgMemMap.begin(); + + while( i != kernelArgMemMap.end() ) + { + CLI_C_ASSERT( sizeof(void*) == sizeof(cl_mem) ); + + cl_uint arg_index = (*i).first; + cl_mem memobj = (cl_mem)(*i).second; + + ++i; + + if( m_ImageInfoMap.find( memobj ) != m_ImageInfoMap.end() ) + { + const SImageInfo& info = m_ImageInfoMap[ memobj ]; + unsigned int number = m_MemAllocNumberMap[ memobj ]; + + std::string fileName = fileNamePrefix; + char tmpStr[ MAX_PATH ]; + + // Add the enqueue count to file name + { + CLI_SPRINTF( tmpStr, MAX_PATH, "%04u", + (unsigned int)m_EnqueueCounter ); + + fileName += "Enqueue_"; + fileName += tmpStr; + } + + // Add the kernel name to the filename + { + fileName += "_Kernel_"; + fileName += m_KernelNameMap[ kernel ]; + } + + // Add the arg number to the file name + { + CLI_SPRINTF( tmpStr, MAX_PATH, "%u", arg_index ); + + fileName += "_Arg_"; + fileName += tmpStr; + } + + // Add the image number to the file name + { + CLI_SPRINTF( tmpStr, MAX_PATH, "%04u", number ); + + fileName += "_Image_"; + fileName += tmpStr; + } + + // Add the image dimensions to the file name + { + CLI_SPRINTF( tmpStr, MAX_PATH, "_%ux%ux%u_%ubpp", + (unsigned int)info.Region[0], + (unsigned int)info.Region[1], + (unsigned int)info.Region[2], + (unsigned int)info.ElementSize * 8 ); + + fileName += tmpStr; + } + + // Add extension to file name + { + fileName += ".raw"; + } + + // Dump the image contents to the file. + { + size_t size = + info.Region[0] * + info.Region[1] * + info.Region[2] * + info.ElementSize; + char* readImageData = new char[ size ]; + + if( readImageData ) + { + size_t origin[3] = { 0, 0, 0 }; + cl_int error = dispatch().clEnqueueReadImage( + command_queue, + memobj, + CL_TRUE, + origin, + info.Region, + 0, + 0, + readImageData, + 0, + NULL, + NULL ); + + if( error == CL_SUCCESS ) + { + std::ofstream os; + os.open( + fileName.c_str(), + std::ios::out | std::ios::binary ); + + if( os.good() ) + { + os.write( readImageData, size ); + os.close(); + } + } + + delete [] readImageData; + } + } + } + } + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::dumpBuffer( + const std::string& name, + cl_mem memobj, + cl_command_queue command_queue, + void* ptr, + size_t offset, + size_t size ) +{ + m_OS.EnterCriticalSection(); + + if( m_BufferInfoMap.find( memobj ) != m_BufferInfoMap.end() ) + { + std::string fileName = ""; + + // Get the dump directory name. + { + OS().GetDumpDirectoryName( sc_DumpDirectoryName, fileName ); + fileName += "/memDumpCreateMapUnmap/"; + } + + // Now make directories as appropriate. + { + OS().MakeDumpDirectories( fileName ); + } + + fileName += name; + + // Add the buffer number to the file name + { + unsigned int number = m_MemAllocNumberMap[ memobj ]; + + char bufferName[ MAX_PATH ]; + + CLI_SPRINTF( bufferName, MAX_PATH, "%04u", number ); + + fileName += "_Buffer_"; + fileName += bufferName; + } + + // Add the offset to the file name + { + char offsetName[ MAX_PATH ]; + + CLI_SPRINTF( offsetName, MAX_PATH, "%04u", + (unsigned int)offset ); + + fileName += "_Offset_"; + fileName += offsetName; + } + + // Add the enqueue count to file name + { + char enqueueCount[ MAX_PATH ]; + + CLI_SPRINTF( enqueueCount, MAX_PATH, "%04u", + (unsigned int)m_EnqueueCounter ); + + fileName += "_Enqueue_"; + fileName += enqueueCount; + } + + // Add extension to file name + { + fileName += ".bin"; + } + + // Dump the buffer contents to the file. + // There are two possibilities: + // 1) We have a pointer and size already. This might happen + // when the buffer is being created or was just mapped. + // In this case, we can just write this to the file. + // 2) We have no pointer or size. This usually happens when + // the buffer is being unmapped. In this case, we'll + // map and dump the entire buffer. + if( ptr != NULL && size != 0 ) + { + std::ofstream os; + os.open( + fileName.c_str(), + std::ios::out | std::ios::binary ); + + if( os.good() ) + { + os.write( (const char*)ptr, size ); + os.close(); + } + } + else + { + // We should have checked this already... + CLI_ASSERT( m_BufferInfoMap.find( memobj ) != m_BufferInfoMap.end() ); + + size_t size = m_BufferInfoMap[ memobj ]; + + cl_int error = CL_SUCCESS; + ptr = dispatch().clEnqueueMapBuffer( + command_queue, + memobj, + CL_TRUE, + CL_MAP_READ, + 0, + size, + 0, + NULL, + NULL, + &error ); + if( error == CL_SUCCESS ) + { + std::ofstream os; + os.open( + fileName.c_str(), + std::ios::out | std::ios::binary ); + + if( os.good() ) + { + os.write( (const char*)ptr, size ); + os.close(); + } + + dispatch().clEnqueueUnmapMemObject( + command_queue, + memobj, + ptr, + 0, + NULL, + NULL ); + } + } + } + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::startAubCapture( + const std::string& functionName, + const cl_kernel kernel, + const cl_uint workDim, + const size_t* gws, + const size_t* lws, + cl_command_queue command_queue ) +{ + if( m_AubCaptureStarted == false ) + { + m_OS.EnterCriticalSection(); + + // For kernels, perform aub capture skip checks. We'll skip aubcapture if: + // - the current skip counter is less than the specified skip counter, or + // - the current capture counter is greater than or equal to the specified capture counter. + + bool skip = false; + if( kernel != NULL ) + { + if( m_AubCaptureKernelEnqueueSkipCounter < m_Config.AubCaptureNumKernelEnqueuesSkip ) + { + logf( "Skipping kernel aub capture: current skip counter is %u, requested skip counter is %u.\n", + m_AubCaptureKernelEnqueueSkipCounter, + m_Config.AubCaptureNumKernelEnqueuesSkip ); + + skip = true; + ++m_AubCaptureKernelEnqueueSkipCounter; + } + else + { + if( m_AubCaptureKernelEnqueueCaptureCounter >= m_Config.AubCaptureNumKernelEnqueuesCapture ) + { + logf( "Skipping kernel aub capture: current capture counter is %u, requested capture counter is %u.\n", + m_AubCaptureKernelEnqueueCaptureCounter, + m_Config.AubCaptureNumKernelEnqueuesCapture ); + skip = true; + } + + ++m_AubCaptureKernelEnqueueCaptureCounter; + } + } + + if( skip == false && + m_AubCaptureStarted == false ) + { + // Try to call clFinish() on the passed-in command queue. + // This isn't perfect, since we'd really rather call + // clFinish on all command queues to start with a fresh + // capture, but it's better than nothing. + // TODO: Is Flush() sufficient? + dispatch().clFinish( command_queue ); + + char charBuf[ MAX_PATH ]; + + std::string fileName = ""; + + // Get the dump directory name. + { + OS().GetDumpDirectoryName( sc_DumpDirectoryName, fileName ); + fileName += "/"; + fileName += "AubCapture"; + + if( m_Config.AubCaptureIndividualEnqueues ) + { + fileName += "_Enqueue_"; + + CLI_SPRINTF( charBuf, MAX_PATH, "%08u", (cl_uint)m_EnqueueCounter ); + + fileName += charBuf; + fileName += "_"; + + if( kernel ) + { + const std::string& kernelName = m_KernelNameMap[ kernel ]; + fileName += "kernel_"; + fileName += kernelName; + + std::ostringstream ss; + ss << "_G_"; + if( gws ) + { + if( workDim >= 1 ) + { + ss << gws[0]; + } + if( workDim >= 2 ) + { + ss << "x" << gws[1]; + } + if( workDim >= 3 ) + { + ss << "x" << gws[2]; + } + } + else + { + ss << "NULL"; + } + ss << "_L_"; + if( lws ) + { + if( workDim >= 1 ) + { + ss << lws[0]; + } + if( workDim >= 2 ) + { + ss << "x" << lws[1]; + } + if( workDim >= 3 ) + { + ss << "x" << lws[2]; + } + } + else + { + ss << "NULL"; + } + fileName += ss.str(); + } + else + { + fileName += functionName; + } + } + else if( m_Config.AubCaptureMinEnqueue != 0 || + m_Config.AubCaptureMaxEnqueue != UINT_MAX ) + { + fileName += "_Enqueue_"; + + CLI_SPRINTF( charBuf, MAX_PATH, "%08u", m_Config.AubCaptureMinEnqueue ); + + fileName += charBuf; + fileName += "_to_"; + + CLI_SPRINTF( charBuf, MAX_PATH, "%08u", m_Config.AubCaptureMaxEnqueue ); + + fileName += charBuf; + } + + fileName += ".daf"; + } + + // Now make directories as appropriate. + { + OS().MakeDumpDirectories( fileName ); + } + + OS().StartAubCapture( + fileName, + config().AubCaptureStartWait ); + log( "AubCapture started... maybe. Filename is: " + fileName + "\n" ); + + // No matter what, set the flag that aubcapture is started, so we + // don't try again. + m_AubCaptureStarted = true; + } + + m_OS.LeaveCriticalSection(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::stopAubCapture( + cl_command_queue command_queue ) +{ + if( m_AubCaptureStarted == true ) + { + m_OS.EnterCriticalSection(); + + if( m_AubCaptureStarted == true ) + { + if( command_queue ) + { + dispatch().clFinish( command_queue ); + } + + OS().StopAubCapture( + config().AubCaptureEndWait ); + log( "AubCapture stopped.\n" ); + + // No matter what, clar the flag that aubcapture is started, so we + // don't try again. + m_AubCaptureStarted = false; + } + + m_OS.LeaveCriticalSection(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::initPrecompiledKernelOverrides( + const cl_context context ) +{ + m_OS.EnterCriticalSection(); + + cl_int errorCode = CL_SUCCESS; + + // Check to see if overrides already exist. If they do, release them. + SPrecompiledKernelOverrides* pOverrides = + m_PrecompiledKernelOverridesMap[ context ]; + if( pOverrides ) + { + errorCode = dispatch().clReleaseKernel( pOverrides->Kernel_CopyBufferBytes ); + errorCode = dispatch().clReleaseKernel( pOverrides->Kernel_CopyBufferUInts ); + errorCode = dispatch().clReleaseKernel( pOverrides->Kernel_CopyBufferUInt4s ); + errorCode = dispatch().clReleaseKernel( pOverrides->Kernel_CopyBufferUInt16s ); + + errorCode = dispatch().clReleaseKernel( pOverrides->Kernel_CopyImage2Dto2DFloat ); + errorCode = dispatch().clReleaseKernel( pOverrides->Kernel_CopyImage2Dto2DInt ); + errorCode = dispatch().clReleaseKernel( pOverrides->Kernel_CopyImage2Dto2DUInt ); + + errorCode = dispatch().clReleaseProgram( pOverrides->Program ); + + delete pOverrides; + pOverrides = NULL; + + m_PrecompiledKernelOverridesMap[ context ] = NULL; + } + + // Allocate new overrides. + pOverrides = new SPrecompiledKernelOverrides; + if( pOverrides ) + { + pOverrides->Program = NULL; + + pOverrides->Kernel_CopyBufferBytes = NULL; + pOverrides->Kernel_CopyBufferUInts = NULL; + pOverrides->Kernel_CopyBufferUInt4s = NULL; + pOverrides->Kernel_CopyBufferUInt16s = NULL; + + pOverrides->Kernel_CopyImage2Dto2DFloat = NULL; + pOverrides->Kernel_CopyImage2Dto2DInt = NULL; + pOverrides->Kernel_CopyImage2Dto2DUInt = NULL; + + const char* pProgramString = NULL; + size_t programStringLength = 0; + + // Get the program string from the resource embedded into this DLL. + if( errorCode == CL_SUCCESS ) + { + if( m_OS.GetPrecompiledKernelString( + pProgramString, + programStringLength ) == false ) + { + errorCode = CL_INVALID_VALUE; + } + } + + // Create the program: + if( errorCode == CL_SUCCESS ) + { + pOverrides->Program = dispatch().clCreateProgramWithSource( + context, + 1, + &pProgramString, + &programStringLength, + &errorCode ); + } + + // Build the program: + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clBuildProgram( + pOverrides->Program, + 0, + NULL, + NULL, + NULL, + NULL ); + + if( errorCode != CL_SUCCESS ) + { + cl_int tempErrorCode = CL_SUCCESS; + + // Get the number of devices for this context. + cl_uint numDevices = 0; + tempErrorCode = dispatch().clGetContextInfo( + context, + CL_CONTEXT_NUM_DEVICES, + sizeof( numDevices ), + &numDevices, + NULL ); + + if( numDevices != 0 ) + { + cl_device_id* devices = new cl_device_id[ numDevices ]; + if( devices ) + { + tempErrorCode = dispatch().clGetContextInfo( + context, + CL_CONTEXT_DEVICES, + numDevices * sizeof( cl_device_id ), + devices, + NULL ); + + if( tempErrorCode == CL_SUCCESS ) + { + cl_uint i = 0; + for( i = 0; i < numDevices; i++ ) + { + size_t buildLogSize = 0; + dispatch().clGetProgramBuildInfo( + pOverrides->Program, + devices[ i ], + CL_PROGRAM_BUILD_LOG, + 0, + NULL, + &buildLogSize ); + + char* buildLog = new char[ buildLogSize + 1 ]; + if( buildLog ) + { + dispatch().clGetProgramBuildInfo( + pOverrides->Program, + devices[ i ], + CL_PROGRAM_BUILD_LOG, + buildLogSize * sizeof( char ), + buildLog, + NULL ); + + buildLog[ buildLogSize ] = '\0'; + + log( "-------> Start of Build Log:\n" ); + log( buildLog ); + log( "<------- End of Build Log!\n" ); + + delete [] buildLog; + } + } + } + } + } + } + } + + // Create all of the kernels in the program: + + if( config().OverrideReadBuffer || + config().OverrideWriteBuffer || + config().OverrideCopyBuffer ) + { + if( errorCode == CL_SUCCESS ) + { + pOverrides->Kernel_CopyBufferBytes = dispatch().clCreateKernel( + pOverrides->Program, + "CopyBufferBytes", + &errorCode ); + } + if( errorCode == CL_SUCCESS ) + { + pOverrides->Kernel_CopyBufferUInts = dispatch().clCreateKernel( + pOverrides->Program, + "CopyBufferUInts", + &errorCode ); + } + if( errorCode == CL_SUCCESS ) + { + pOverrides->Kernel_CopyBufferUInt4s = dispatch().clCreateKernel( + pOverrides->Program, + "CopyBufferUInt4s", + &errorCode ); + } + if( errorCode == CL_SUCCESS ) + { + pOverrides->Kernel_CopyBufferUInt16s = dispatch().clCreateKernel( + pOverrides->Program, + "CopyBufferUInt16s", + &errorCode ); + } + } + + if( config().OverrideReadImage || + config().OverrideWriteImage || + config().OverrideCopyImage ) + { + // TODO: Check to see if images are supported? + // What should happen if this is a multiple-device context, + // and one device supports images, but another doesn't? + + if( errorCode == CL_SUCCESS ) + { + pOverrides->Kernel_CopyImage2Dto2DFloat = dispatch().clCreateKernel( + pOverrides->Program, + "CopyImage2Dto2DFloat", + &errorCode ); + } + if( errorCode == CL_SUCCESS ) + { + pOverrides->Kernel_CopyImage2Dto2DInt = dispatch().clCreateKernel( + pOverrides->Program, + "CopyImage2Dto2DInt", + &errorCode ); + } + if( errorCode == CL_SUCCESS ) + { + pOverrides->Kernel_CopyImage2Dto2DUInt = dispatch().clCreateKernel( + pOverrides->Program, + "CopyImage2Dto2DUInt", + &errorCode ); + } + } + + if( errorCode == CL_SUCCESS ) + { + m_PrecompiledKernelOverridesMap[ context ] = pOverrides; + } + else + { + delete pOverrides; + pOverrides = NULL; + } + } + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::initBuiltinKernelOverrides( + const cl_context context ) +{ + m_OS.EnterCriticalSection(); + + cl_int errorCode = CL_SUCCESS; + + // Check to see if overrides already exist. If they do, release them. + SBuiltinKernelOverrides* pOverrides = + m_BuiltinKernelOverridesMap[ context ]; + if( pOverrides ) + { + errorCode = dispatch().clReleaseKernel( pOverrides->Kernel_block_motion_estimate_intel ); + + errorCode = dispatch().clReleaseProgram( pOverrides->Program ); + + delete pOverrides; + pOverrides = NULL; + + m_BuiltinKernelOverridesMap[ context ] = NULL; + } + + // Allocate new overrides. + pOverrides = new SBuiltinKernelOverrides; + if( pOverrides ) + { + pOverrides->Program = NULL; + + pOverrides->Kernel_block_motion_estimate_intel = NULL; + + const char* pProgramString = NULL; + size_t programStringLength = 0; + + // Get the program string from the resource embedded into this DLL. + if( errorCode == CL_SUCCESS ) + { + if( m_OS.GetBuiltinKernelString( + pProgramString, + programStringLength ) == false ) + { + errorCode = CL_INVALID_VALUE; + } + } + + // Create the program: + if( errorCode == CL_SUCCESS ) + { + pOverrides->Program = dispatch().clCreateProgramWithSource( + context, + 1, + &pProgramString, + &programStringLength, + &errorCode ); + } + + // Build the program: + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clBuildProgram( + pOverrides->Program, + 0, + NULL, + "-Dcl_intel_device_side_vme_enable -DHW_NULL_CHECK", + NULL, + NULL ); + + if( errorCode != CL_SUCCESS ) + { + cl_int tempErrorCode = CL_SUCCESS; + + // Get the number of devices for this context. + cl_uint numDevices = 0; + tempErrorCode = dispatch().clGetContextInfo( + context, + CL_CONTEXT_NUM_DEVICES, + sizeof( numDevices ), + &numDevices, + NULL ); + + if( numDevices != 0 ) + { + cl_device_id* devices = new cl_device_id[ numDevices ]; + if( devices ) + { + tempErrorCode = dispatch().clGetContextInfo( + context, + CL_CONTEXT_DEVICES, + numDevices * sizeof( cl_device_id ), + devices, + NULL ); + + if( tempErrorCode == CL_SUCCESS ) + { + cl_uint i = 0; + for( i = 0; i < numDevices; i++ ) + { + size_t buildLogSize = 0; + dispatch().clGetProgramBuildInfo( + pOverrides->Program, + devices[ i ], + CL_PROGRAM_BUILD_LOG, + 0, + NULL, + &buildLogSize ); + + char* buildLog = new char[ buildLogSize + 1 ]; + if( buildLog ) + { + dispatch().clGetProgramBuildInfo( + pOverrides->Program, + devices[ i ], + CL_PROGRAM_BUILD_LOG, + buildLogSize * sizeof( char ), + buildLog, + NULL ); + + buildLog[ buildLogSize ] = '\0'; + + log( "-------> Start of Build Log:\n" ); + log( buildLog ); + log( "<------- End of Build Log!\n" ); + + delete [] buildLog; + } + } + } + } + } + } + } + + // Create all of the kernels in the program: + + if( errorCode == CL_SUCCESS ) + { + pOverrides->Kernel_block_motion_estimate_intel = dispatch().clCreateKernel( + pOverrides->Program, + "block_motion_estimate_intel", + &errorCode ); + } + + if( errorCode == CL_SUCCESS ) + { + m_BuiltinKernelOverridesMap[ context ] = pOverrides; + } + else + { + delete pOverrides; + pOverrides = NULL; + } + } + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +cl_program CLIntercept::createProgramWithInjectionBinaries( + uint64_t hash, + cl_context context, + cl_int* errcode_ret ) +{ + m_OS.EnterCriticalSection(); + + cl_int errorCode = CL_SUCCESS; + cl_program program = NULL; + + std::string fileName1; + std::string fileName2; + size_t numDevices = 0; + + if( errorCode == CL_SUCCESS ) + { + std::string fileName; + + // Get the dump directory name. + { + OS().GetDumpDirectoryName( sc_DumpDirectoryName, fileName ); + fileName += "/Inject"; + } + // Make two candidate filenames. They will have the form: + // CLI___0000, or + // CLI__0000 + // Leave off the extension for now. + { + char numberString1[256] = ""; + CLI_SPRINTF( numberString1, 256, "%04u_%08X_0000", + m_ProgramNumber, + (unsigned int)hash ); + + char numberString2[256] = ""; + CLI_SPRINTF( numberString2, 256, "%08X_0000", + (unsigned int)hash ); + + fileName1 = fileName; + fileName1 += "/CLI_"; + fileName1 += numberString1; + + fileName2 = fileName; + fileName2 += "/CLI_"; + fileName2 += numberString2; + } + + errorCode = dispatch().clGetContextInfo( + context, + CL_CONTEXT_DEVICES, + 0, + NULL, + &numDevices ); + } + + cl_device_id* devices = NULL; + + char** programBinaries = NULL; + size_t* programBinarySizes = NULL; + + if( errorCode == CL_SUCCESS ) + { + numDevices = numDevices / sizeof( cl_device_id ); + + devices = new cl_device_id [ numDevices ]; + + programBinaries = new char*[ numDevices ]; + programBinarySizes = new size_t[ numDevices ]; + + if( ( devices == NULL ) || + ( programBinaries == NULL ) || + ( programBinarySizes == NULL ) ) + { + CLI_ASSERT( 0 ); + errorCode = CL_OUT_OF_HOST_MEMORY; + } + } + + if( errorCode == CL_SUCCESS ) + { + for( size_t i = 0; i < numDevices; i++ ) + { + programBinaries[i] = NULL; + } + + errorCode = dispatch().clGetContextInfo( + context, + CL_CONTEXT_DEVICES, + numDevices * sizeof( cl_device_id ), + devices, + NULL ); + + if( errorCode == CL_SUCCESS ) + { + // Assume all binaries exist, until this is proven otherwise. + bool allBinariesExist = true; + + for( size_t i = 0; i < numDevices; i++ ) + { + cl_device_type deviceType = CL_DEVICE_TYPE_DEFAULT; + + // It's OK if this fails. If it does, it just + // means that our output file won't have a device + // type. + dispatch().clGetDeviceInfo( + devices[ i ], + CL_DEVICE_TYPE, + sizeof( deviceType ), + &deviceType, + NULL ); + + std::string suffix; + + if( deviceType & CL_DEVICE_TYPE_CPU ) + { + suffix += "_CPU"; + } + if( deviceType & CL_DEVICE_TYPE_GPU ) + { + suffix += "_GPU"; + } + if( deviceType & CL_DEVICE_TYPE_ACCELERATOR ) + { + suffix += "_ACCELERATOR"; + } + if( deviceType & CL_DEVICE_TYPE_CUSTOM ) + { + suffix += "_CUSTOM"; + } + + suffix += ".bin"; + + std::string inputFileName = fileName1 + suffix; + + std::ifstream is; + is.open( + inputFileName.c_str(), + std::ios::in | std::ios::binary ); + if( is.good() ) + { + log( "Injection binary file exists: " + inputFileName + "\n" ); + } + else + { + log( "Injection binary file doesn't exist: " + inputFileName + "\n" ); + + inputFileName = fileName2 + suffix; + is.clear(); + is.open( + inputFileName.c_str(), + std::ios::in | std::ios::binary ); + if( is.good() ) + { + log( "Injection binary file exists: " + inputFileName + "\n" ); + } + else + { + log( "Injection binary file doesn't exist: " + inputFileName + "\n" ); + } + } + + if( is.good() ) + { + // The file exists. Figure out how big it is. + is.seekg( 0, std::ios::end ); + programBinarySizes[i] = (size_t)is.tellg(); + is.seekg( 0, std::ios::beg ); + + programBinaries[i] = new char[ programBinarySizes[i] ]; + if( programBinaries[i] == NULL ) + { + CLI_ASSERT( 0 ); + errorCode = CL_OUT_OF_HOST_MEMORY; + } + else + { + is.read( programBinaries[i], programBinarySizes[i] ); + } + + is.close(); + } + else + { + log( "Injection binary is missing!\n" ); + allBinariesExist = false; + } + } + + if( allBinariesExist && + ( errorCode == CL_SUCCESS ) ) + { + log( "All injection binaries exist.\n" ); + + program = dispatch().clCreateProgramWithBinary( + context, + (cl_uint)numDevices, + devices, + programBinarySizes, + (const unsigned char**)programBinaries, + NULL, // binary_status + &errorCode ); + if( program ) + { + logf("Injection successful: clCreateProgramWithBinary() returned %p\n", + program ); + } + if( errorCode != CL_SUCCESS ) + { + log( "Injecting binaries failed: clCreateProgramWithBinary() returned %s\n" + + enumName().name( errorCode ) + "\n" ); + } + } + + for( size_t i = 0; i < numDevices; i++ ) + { + programBinarySizes[i] = 0; + + delete [] programBinaries[i]; + programBinaries[i] = NULL; + } + } + } + + delete [] devices; + + delete [] programBinaries; + delete [] programBinarySizes; + + if( errcode_ret ) + { + errcode_ret[0] = errorCode; + } + + m_OS.LeaveCriticalSection(); + return program; +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::dumpProgramBinary( + const cl_program program ) +{ + m_OS.EnterCriticalSection(); + + unsigned int programNumber = m_ProgramNumberMap[ program ]; + uint64_t programHash = m_ProgramHashMap[ program ]; + unsigned int compileCount = m_ProgramNumberCompileCountMap[ programNumber ]; + + std::string fileName; + + // Get the dump directory name. + { + OS().GetDumpDirectoryName( sc_DumpDirectoryName, fileName ); + } + // Make the filename. It will have the form: + // CLI___ + // Leave off the extension for now. + { + char numberString[256] = ""; + + if( config().OmitProgramNumber ) + { + CLI_SPRINTF( numberString, 256, "%08X_%04u", + (unsigned int)programHash, + compileCount ); + } + else + { + CLI_SPRINTF( numberString, 256, "%04u_%08X_%04u", + programNumber, + (unsigned int)programHash, + compileCount ); + } + + fileName += "/CLI_"; + fileName += numberString; + } + // Now make directories as appropriate. + { + OS().MakeDumpDirectories( fileName ); + } + + cl_int errorCode = CL_SUCCESS; + + size_t numDevices = 0; + + if( errorCode == CL_SUCCESS ) + { + // Get all of the devices associated with this program. + errorCode = dispatch().clGetProgramInfo( + program, + CL_PROGRAM_DEVICES, + 0, + NULL, + &numDevices ); + } + + cl_device_id* devices = NULL; + char** programBinaries = NULL; + size_t* programBinarySizes = NULL; + + if( errorCode == CL_SUCCESS ) + { + numDevices /= sizeof( cl_device_id ); + + devices = new cl_device_id[ numDevices ]; + programBinaries = new char*[ numDevices ]; + programBinarySizes = new size_t[ numDevices ]; + + if( ( devices == NULL ) || + ( programBinaries == NULL ) || + ( programBinarySizes == NULL ) ) + { + CLI_ASSERT( 0 ); + errorCode = CL_OUT_OF_HOST_MEMORY; + } + } + + if( errorCode == CL_SUCCESS ) + { + for( size_t i = 0; i < numDevices; i++ ) + { + programBinaries[i] = NULL; + } + + errorCode = dispatch().clGetProgramInfo( + program, + CL_PROGRAM_DEVICES, + numDevices * sizeof( cl_device_id ), + devices, + NULL ); + } + + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetProgramInfo( + program, + CL_PROGRAM_BINARY_SIZES, + numDevices * sizeof( size_t ), + programBinarySizes, + NULL ); + } + + if( errorCode == CL_SUCCESS ) + { + for( size_t i = 0; i < numDevices; i++ ) + { + programBinaries[ i ] = new char[ programBinarySizes[ i ] ]; + if( programBinaries[ i ] == NULL ) + { + errorCode = CL_OUT_OF_HOST_MEMORY; + break; + } + } + + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetProgramInfo( + program, + CL_PROGRAM_BINARIES, + numDevices * sizeof( char* ), + programBinaries, + NULL ); + } + + if( errorCode == CL_SUCCESS ) + { + for( size_t i = 0; i < numDevices; i++ ) + { + cl_device_type deviceType = CL_DEVICE_TYPE_DEFAULT; + + // It's OK if this fails. If it does, it just + // means that our output file won't have a device + // type. + dispatch().clGetDeviceInfo( + devices[ i ], + CL_DEVICE_TYPE, + sizeof( deviceType ), + &deviceType, + NULL ); + + std::string outputFileName = fileName; + + if( deviceType & CL_DEVICE_TYPE_CPU ) + { + outputFileName += "_CPU"; + } + if( deviceType & CL_DEVICE_TYPE_GPU ) + { + outputFileName += "_GPU"; + } + if( deviceType & CL_DEVICE_TYPE_ACCELERATOR ) + { + outputFileName += "_ACCELERATOR"; + } + if( deviceType & CL_DEVICE_TYPE_CUSTOM ) + { + outputFileName += "_CUSTOM"; + } + + outputFileName += ".bin"; + + std::ofstream os; + os.open( + outputFileName.c_str(), + std::ios::out | std::ios::binary ); + if( os.good() ) + { + log( "Dumping program binary to file: " + outputFileName + "\n" ); + + os.write( + programBinaries[ i ], + programBinarySizes[ i ] ); + os.close(); + } + } + } + + for( size_t i = 0; i < numDevices; i++ ) + { + delete [] programBinaries[ i ]; + programBinaries[ i ] = NULL; + } + } + + delete [] devices; + devices = NULL; + + delete [] programBinaries; + programBinaries = NULL; + + delete [] programBinarySizes; + programBinarySizes = NULL; + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +cl_program CLIntercept::createProgramWithInjectionSPIRV( + uint64_t hash, + cl_context context, + cl_int* errcode_ret ) +{ + m_OS.EnterCriticalSection(); + + cl_program program = NULL; + + // Don't bother with any of this if we weren't able to get a pointer to + // the entry point to create a program with IL. + if( dispatch().clCreateProgramWithIL == NULL ) + { + log( "Aborting InjectProgramSPIRV because clCreateProgramWithIL is NULL!\n" ); + } + else + { + std::string fileName; + + // Get the dump directory name. + { + OS().GetDumpDirectoryName(sc_DumpDirectoryName, fileName); + fileName += "/Inject"; + } + + // Make three candidate filenames. They will have the form: + // CLI___0000.spv, or + // CLI__0000.spv + { + char numberString1[256] = ""; + CLI_SPRINTF(numberString1, 256, "%04u_%08X_0000", + m_ProgramNumber, + (unsigned int)hash); + + char numberString2[256] = ""; + CLI_SPRINTF(numberString2, 256, "%08X_0000", + (unsigned int)hash); + + std::string fileName1; + fileName1 = fileName; + fileName1 += "/CLI_"; + fileName1 += numberString1; + fileName1 += ".spv"; + + std::string fileName2; + fileName2 = fileName; + fileName2 += "/CLI_"; + fileName2 += numberString2; + fileName2 += ".spv"; + + std::ifstream is; + + is.open( + fileName1.c_str(), + std::ios::in | std::ios::binary); + if( is.good() ) + { + log("Injecting SPIR-V file: " + fileName1 + "\n"); + } + else + { + log("Injection SPIR-V file doesn't exist: " + fileName1 + "\n"); + + is.clear(); + is.open( + fileName2.c_str(), + std::ios::in | std::ios::binary); + if( is.good() ) + { + log("Injecting SPIR-V file: " + fileName2 + "\n"); + } + else + { + log("Injection SPIR-V file doesn't exist: " + fileName2 + "\n"); + } + } + + if( is.good() ) + { + // The file exists. Figure out how big it is. + size_t filesize = 0; + + is.seekg( 0, std::ios::end ); + filesize = (size_t)is.tellg(); + is.seekg( 0, std::ios::beg ); + + char* newILBinary = new char[ filesize ]; + if( newILBinary == NULL ) + { + CLI_ASSERT( 0 ); + } + else + { + is.read(newILBinary, filesize); + + // Right now, this can still die in the ICD loader if the ICD loader + // exports this entry point but the vendor didn't implement it. It + // would be nice to enhance the ICD loader so it called into a safe + // stub function if the vendor didn't implement an entry point... + program = dispatch().clCreateProgramWithIL( + context, + newILBinary, + filesize, + errcode_ret ); + if( program ) + { + logf("Injection successful: clCreateProgramWithIL() returned %p\n", + program ); + } + + delete[] newILBinary; + } + + is.close(); + } + } + } + + m_OS.LeaveCriticalSection(); + return program; +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::autoCreateSPIRV( + const cl_program program, + const char* raw_options ) +{ + m_OS.EnterCriticalSection(); + + unsigned int programNumber = m_ProgramNumberMap[ program ]; + uint64_t programHash = m_ProgramHashMap[ program ]; + unsigned int compileCount = m_ProgramNumberCompileCountMap[ programNumber ]; + + std::string dumpDirectoryName; + std::string inputFileName; + std::string outputFileName; + + // Get the dump directory name. + { + OS().GetDumpDirectoryName( sc_DumpDirectoryName, dumpDirectoryName ); + } + + // Re-create the input file name. This will be a program source file we dumped + // earlier. It will have the form: + // CLI___source.cl + { + char numberString[256] = ""; + + if( config().OmitProgramNumber ) + { + CLI_SPRINTF( numberString, 256, "%08X", + (unsigned int)programHash ); + } + else + { + CLI_SPRINTF( numberString, 256, "%04u_%08X", + programNumber, + (unsigned int)programHash ); + } + + inputFileName = dumpDirectoryName; + inputFileName += "/CLI_"; + inputFileName += numberString; + inputFileName += "_source.cl"; + } + + // Make the output file name. It will have the form: + // CLI___.spv + { + char numberString[256] = ""; + + if( config().OmitProgramNumber ) + { + CLI_SPRINTF( numberString, 256, "%08X_%04u", + (unsigned int)programHash, + compileCount ); + } + else + { + CLI_SPRINTF( numberString, 256, "%04u_%08X_%04u", + programNumber, + (unsigned int)programHash, + compileCount ); + } + + outputFileName = dumpDirectoryName; + outputFileName += "/CLI_"; + outputFileName += numberString; + outputFileName += ".spv"; + } + + // Now make directories as appropriate. We can use either the input + // or output file name to do this. + { + OS().MakeDumpDirectories( inputFileName ); + } + + std::string options(raw_options ? raw_options : ""); + std::string command; + + // Create the command we will use to invoke CLANG with the right options. + // How we do this will depend on whether this is an OpenCL 1.x or 2.0 + // compilation. We don't distinguish between different versions of + // OpenCL 1.x right now, but we can add this in the future, if desired. + if( options.find( "-cl-std=CL2.0" ) != std::string::npos ) + { + // This is an OpenCL 2.0 compilation. + command = + config().SPIRVClang + + " " + config().OpenCL2Options + + " -include " + config().SPIRVCLHeader + + " " + options + + " -o " + outputFileName + + " " + inputFileName; + } + else + { + // This is an OpenCL 1.x compilation. + command = + config().SPIRVClang + + " " + config().DefaultOptions + + " -include " + config().SPIRVCLHeader + + " " + options + + " -o " + outputFileName + + " " + inputFileName; + } + + logf( "Running: %s\n", command.c_str() ); + OS().ExecuteCommand( command ); + + // Optionally, run spirv-dis to disassemble the generated module. + if( !config().SPIRVDis.empty() ) + { + command = + config().SPIRVDis + + " -o " + outputFileName + "t" + + " " + outputFileName; + + logf( "Running: %s\n", command.c_str() ); + OS().ExecuteCommand( command ); + } + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +cl_int CLIntercept::writeStringToMemory( + size_t param_value_size, + const std::string& param, + size_t* param_value_size_ret, + char* pointer ) const +{ + cl_int errorCode = CL_SUCCESS; + + size_t length = param.length() + 1; + + if( pointer != NULL ) + { + if( param_value_size < length ) + { + errorCode = CL_INVALID_VALUE; + } + else + { + strcpy_s( + pointer, + length, + param.c_str() ); + } + } + + if( param_value_size_ret != NULL ) + { + *param_value_size_ret = length; + } + + return errorCode; +} + +/////////////////////////////////////////////////////////////////////////////// +// +template< class T > +cl_int CLIntercept::writeParamToMemory( + size_t param_value_size, + T param, + size_t *param_value_size_ret, + T* pointer ) const +{ + cl_int errorCode = CL_SUCCESS; + + if( pointer != NULL ) + { + if( param_value_size < sizeof(param) ) + { + errorCode = CL_INVALID_VALUE; + } + else + { + *pointer = param; + } + } + + if( param_value_size_ret != NULL ) + { + *param_value_size_ret = sizeof(param); + } + + return errorCode; +} + +/////////////////////////////////////////////////////////////////////////////// +// +bool CLIntercept::overrideGetPlatformInfo( + cl_platform_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret, + cl_int& errorCode ) +{ + bool override = false; + + m_OS.EnterCriticalSection(); + + switch( param_name ) + { + case CL_PLATFORM_NAME: + if( m_Config.PlatformName != "" ) + { + char* ptr = (char*)param_value; + errorCode = writeStringToMemory( + param_value_size, + m_Config.PlatformName, + param_value_size_ret, + ptr ); + override = true; + } + break; + case CL_PLATFORM_VENDOR: + if( m_Config.PlatformVendor != "" ) + { + char* ptr = (char*)param_value; + errorCode = writeStringToMemory( + param_value_size, + m_Config.PlatformVendor, + param_value_size_ret, + ptr ); + override = true; + } + break; + case CL_PLATFORM_PROFILE: + if( m_Config.PlatformProfile != "" ) + { + char* ptr = (char*)param_value; + errorCode = writeStringToMemory( + param_value_size, + m_Config.PlatformProfile, + param_value_size_ret, + ptr ); + override = true; + } + break; + case CL_PLATFORM_VERSION: + if( m_Config.PlatformVersion != "" ) + { + char* ptr = (char*)param_value; + errorCode = writeStringToMemory( + param_value_size, + m_Config.PlatformVersion, + param_value_size_ret, + ptr ); + override = true; + } + break; + default: + break; + } + + m_OS.LeaveCriticalSection(); + + return override; +} + +/////////////////////////////////////////////////////////////////////////////// +// +bool CLIntercept::overrideGetDeviceInfo( + cl_device_id device, + cl_device_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret, + cl_int& errorCode ) +{ + bool override = false; + + m_OS.EnterCriticalSection(); + + switch( param_name ) + { + case CL_DEVICE_TYPE: + if( m_Config.DeviceType != 0 ) + { + cl_device_type* ptr = (cl_device_type*)param_value; + cl_device_type d = m_Config.DeviceType; + errorCode = writeParamToMemory( + param_value_size, + d, + param_value_size_ret, + ptr ); + override = true; + } + break; + case CL_DEVICE_NAME: + if( m_Config.DeviceName != "" ) + { + char* ptr = (char*)param_value; + errorCode = writeStringToMemory( + param_value_size, + m_Config.DeviceName, + param_value_size_ret, + ptr ); + override = true; + } + break; + case CL_DEVICE_EXTENSIONS: + if( m_Config.DeviceExtensions != "" ) + { + char* ptr = (char*)param_value; + errorCode = writeStringToMemory( + param_value_size, + m_Config.DeviceExtensions, + param_value_size_ret, + ptr ); + override = true; + } + break; + case CL_DEVICE_VENDOR: + if( m_Config.DeviceVendor != "" ) + { + char* ptr = (char*)param_value; + errorCode = writeStringToMemory( + param_value_size, + m_Config.DeviceVendor, + param_value_size_ret, + ptr ); + override = true; + } + break; + case CL_DEVICE_PROFILE: + if( m_Config.DeviceProfile != "" ) + { + char* ptr = (char*)param_value; + errorCode = writeStringToMemory( + param_value_size, + m_Config.DeviceProfile, + param_value_size_ret, + ptr ); + override = true; + } + break; + case CL_DEVICE_VERSION: + if( m_Config.DeviceVersion != "" ) + { + char* ptr = (char*)param_value; + errorCode = writeStringToMemory( + param_value_size, + m_Config.DeviceVersion, + param_value_size_ret, + ptr ); + override = true; + } + break; + case CL_DEVICE_OPENCL_C_VERSION: + if( m_Config.DeviceCVersion != "" ) + { + char* ptr = (char*)param_value; + errorCode = writeStringToMemory( + param_value_size, + m_Config.DeviceCVersion, + param_value_size_ret, + ptr ); + override = true; + } + break; + case CL_DEVICE_VENDOR_ID: + if( m_Config.DeviceVendorID != 0 ) + { + cl_uint* ptr = (cl_uint*)param_value; + errorCode = writeParamToMemory( + param_value_size, + m_Config.DeviceVendorID, + param_value_size_ret, + ptr ); + override = true; + } + break; + case CL_DEVICE_MAX_COMPUTE_UNITS: + if( m_Config.DeviceMaxComputeUnits != 0 ) + { + cl_uint* ptr = (cl_uint*)param_value; + errorCode = writeParamToMemory( + param_value_size, + m_Config.DeviceMaxComputeUnits, + param_value_size_ret, + ptr ); + override = true; + } + break; + case CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR: + if( m_Config.DevicePreferredVectorWidthChar != UINT_MAX ) + { + cl_uint* ptr = (cl_uint*)param_value; + errorCode = writeParamToMemory( + param_value_size, + m_Config.DevicePreferredVectorWidthChar, + param_value_size_ret, + ptr ); + override = true; + } + break; + case CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT: + if( m_Config.DevicePreferredVectorWidthShort != UINT_MAX ) + { + cl_uint* ptr = (cl_uint*)param_value; + errorCode = writeParamToMemory( + param_value_size, + m_Config.DevicePreferredVectorWidthShort, + param_value_size_ret, + ptr ); + override = true; + } + break; + case CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT: + if( m_Config.DevicePreferredVectorWidthInt != UINT_MAX ) + { + cl_uint* ptr = (cl_uint*)param_value; + errorCode = writeParamToMemory( + param_value_size, + m_Config.DevicePreferredVectorWidthInt, + param_value_size_ret, + ptr ); + override = true; + } + break; + case CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG: + if( m_Config.DevicePreferredVectorWidthLong != UINT_MAX ) + { + cl_uint* ptr = (cl_uint*)param_value; + errorCode = writeParamToMemory( + param_value_size, + m_Config.DevicePreferredVectorWidthLong, + param_value_size_ret, + ptr ); + override = true; + } + break; + case CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF: + if( m_Config.DevicePreferredVectorWidthHalf != UINT_MAX ) + { + cl_uint* ptr = (cl_uint*)param_value; + errorCode = writeParamToMemory( + param_value_size, + m_Config.DevicePreferredVectorWidthHalf, + param_value_size_ret, + ptr ); + override = true; + } + break; + case CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT: + if( m_Config.DevicePreferredVectorWidthFloat != UINT_MAX ) + { + cl_uint* ptr = (cl_uint*)param_value; + errorCode = writeParamToMemory( + param_value_size, + m_Config.DevicePreferredVectorWidthFloat, + param_value_size_ret, + ptr ); + override = true; + } + break; + case CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE: + if( m_Config.DevicePreferredVectorWidthDouble != UINT_MAX ) + { + cl_uint* ptr = (cl_uint*)param_value; + errorCode = writeParamToMemory( + param_value_size, + m_Config.DevicePreferredVectorWidthDouble, + param_value_size_ret, + ptr ); + override = true; + } + break; +#if 0 + // This is a hack to get Sandra to try to compile fp64 + // kernels on devices that do not report fp64 capabilities. + case CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE: + { + cl_uint* ptr = (cl_uint*)param_value; + errorCode = writeParamToMemory( + param_value_size, + (cl_uint)1, + param_value_size_ret, + ptr ); + override = true; + } + break; + case CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE: + { + cl_uint* ptr = (cl_uint*)param_value; + errorCode = writeParamToMemory( + param_value_size, + (cl_uint)1, + param_value_size_ret, + ptr ); + override = true; + } + break; +#endif +#if 0 + // This is a hack to get fp16 conformance tests to run on + // Broadwell. + case CL_DEVICE_HALF_FP_CONFIG: + { + cl_device_fp_config value = + CL_FP_ROUND_TO_NEAREST | + CL_FP_ROUND_TO_ZERO | + CL_FP_INF_NAN | + CL_FP_ROUND_TO_INF; + + cl_device_fp_config* ptr = (cl_device_fp_config*)param_value; + errorCode = writeParamToMemory( + param_value_size, + value, + param_value_size_ret, + ptr ); + override = true; + } + break; +#endif +#if 0 + // This is a hack to get fp32 denormal tests to run on Broadwell. + case CL_DEVICE_SINGLE_FP_CONFIG: + { + cl_device_fp_config value = 0; + errorCode = dispatch().clGetDeviceInfo( + device, + param_name, + sizeof(value), + &value, + NULL ); + if( errorCode == CL_SUCCESS ) + { + value |= CL_FP_DENORM; + + cl_device_fp_config* ptr = (cl_device_fp_config*)param_value; + errorCode = writeParamToMemory( + param_value_size, + value, + param_value_size_ret, + ptr ); + override = true; + } + + } + break; +#endif + default: + break; + } + + m_OS.LeaveCriticalSection(); + + return override; +} + +/////////////////////////////////////////////////////////////////////////////// +// +cl_int CLIntercept::ReadBuffer( + cl_command_queue commandQueue, + cl_mem srcBuffer, + cl_bool blockingRead, + size_t srcOffset, + size_t bytesToRead, + void* dstPtr, + cl_uint numEventsInWaitList, + const cl_event* eventWaitList, + cl_event* event ) +{ + m_OS.EnterCriticalSection(); + + cl_int errorCode = CL_SUCCESS; + + cl_context context = NULL; + + // Get the context for this command queue. + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetCommandQueueInfo( + commandQueue, + CL_QUEUE_CONTEXT, + sizeof( context ), + &context, + NULL ); + } + + size_t dstOffset = 0; + + // Align the passed-in pointer to a page boundary. + if( errorCode == CL_SUCCESS ) + { + const size_t alignSize = 4096; + + unsigned char* bptr = (unsigned char*)dstPtr; + uintptr_t uiptr = (uintptr_t)bptr; + + dstOffset = uiptr % alignSize; + bptr -= dstOffset; + + dstPtr = bptr; + } + + cl_mem dstBuffer = NULL; + + // Create a USE_HOST_PTR buffer for the passed-in pointer. + // The size of the buffer will be at least dstOffset + bytesToRead. + if( errorCode == CL_SUCCESS ) + { + size_t dstBufferSize = dstOffset + bytesToRead; + + dstBuffer = dispatch().clCreateBuffer( + context, + CL_MEM_USE_HOST_PTR | CL_MEM_WRITE_ONLY, + dstBufferSize, + dstPtr, + &errorCode ); + } + + if( errorCode == CL_SUCCESS ) + { + errorCode = CopyBufferHelper( + context, + commandQueue, + srcBuffer, + dstBuffer, + srcOffset, + dstOffset, + bytesToRead, + numEventsInWaitList, + eventWaitList, + event ); + } + + // Technically, we need to map and unmap the destination buffer + // to transfer data to our pointer. This will also handle + // blockingRead. + if( errorCode == CL_SUCCESS ) + { + void* mappedPointer = dispatch().clEnqueueMapBuffer( + commandQueue, + dstBuffer, + blockingRead, + CL_MAP_READ, + dstOffset, + bytesToRead, + 0, + NULL, + NULL, + &errorCode ); + + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clEnqueueUnmapMemObject( + commandQueue, + dstBuffer, + mappedPointer, + 0, + NULL, + NULL ); + } + } + + dispatch().clReleaseMemObject( dstBuffer ); + + m_OS.LeaveCriticalSection(); + + return errorCode; +} + +/////////////////////////////////////////////////////////////////////////////// +// +cl_int CLIntercept::WriteBuffer( + cl_command_queue commandQueue, + cl_mem dstBuffer, + cl_bool blockingWrite, + size_t dstOffset, + size_t bytesToWrite, + const void* srcPtr, + cl_uint numEventsInWaitList, + const cl_event* eventWaitList, + cl_event* event ) +{ + m_OS.EnterCriticalSection(); + + cl_int errorCode = CL_SUCCESS; + + cl_context context = NULL; + + // Get the context for this command queue. + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetCommandQueueInfo( + commandQueue, + CL_QUEUE_CONTEXT, + sizeof( context ), + &context, + NULL ); + } + + size_t srcOffset = 0; + + // Align the passed-in pointer to a page boundary. + if( errorCode == CL_SUCCESS ) + { + const size_t alignSize = 4096; + + unsigned char* bptr = (unsigned char*)srcPtr; + uintptr_t uiptr = (uintptr_t)bptr; + + srcOffset = uiptr % alignSize; + bptr -= srcOffset; + + srcPtr = bptr; + } + + cl_mem srcBuffer = NULL; + + // Create a USE_HOST_PTR buffer for the passed-in pointer. + // The size of the buffer will be at least srcOffset + bytesToWrite. + if( errorCode == CL_SUCCESS ) + { + size_t srcBufferSize = srcOffset + bytesToWrite; + + srcBuffer = dispatch().clCreateBuffer( + context, + CL_MEM_USE_HOST_PTR | CL_MEM_READ_ONLY, + srcBufferSize, + (void*)srcPtr, + &errorCode ); + } + + if( errorCode == CL_SUCCESS ) + { + errorCode = CopyBufferHelper( + context, + commandQueue, + srcBuffer, + dstBuffer, + srcOffset, + dstOffset, + bytesToWrite, + numEventsInWaitList, + eventWaitList, + event ); + } + + if( errorCode == CL_SUCCESS ) + { + if( blockingWrite ) + { + errorCode = dispatch().clFinish( + commandQueue ); + } + } + + dispatch().clReleaseMemObject( srcBuffer ); + + m_OS.LeaveCriticalSection(); + + return errorCode; +} + +/////////////////////////////////////////////////////////////////////////////// +// +cl_int CLIntercept::CopyBuffer( + cl_command_queue commandQueue, + cl_mem srcBuffer, + cl_mem dstBuffer, + size_t srcOffset, + size_t dstOffset, + size_t bytesToCopy, + cl_uint numEventsInWaitList, + const cl_event* eventWaitList, + cl_event* event ) +{ + m_OS.EnterCriticalSection(); + + cl_int errorCode = CL_SUCCESS; + + cl_context context = NULL; + + // Get the context for this command queue. + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetCommandQueueInfo( + commandQueue, + CL_QUEUE_CONTEXT, + sizeof( context ), + &context, + NULL ); + } + + if( errorCode == CL_SUCCESS ) + { + errorCode = CopyBufferHelper( + context, + commandQueue, + srcBuffer, + dstBuffer, + srcOffset, + dstOffset, + bytesToCopy, + numEventsInWaitList, + eventWaitList, + event ); + } + + m_OS.LeaveCriticalSection(); + + return errorCode; +} + +/////////////////////////////////////////////////////////////////////////////// +// +cl_int CLIntercept::CopyBufferHelper( + cl_context context, + cl_command_queue commandQueue, + cl_mem srcBuffer, + cl_mem dstBuffer, + size_t srcOffset, + size_t dstOffset, + size_t bytesToCopy, + cl_uint numEventsInWaitList, + const cl_event* eventWaitList, + cl_event* event ) +{ + // This function assumes that it is being called from within a critical + // section, so it does not enter the critical section again. + + cl_int errorCode = CL_SUCCESS; + + SPrecompiledKernelOverrides* pOverrides = NULL; + + // Get the overrides for this context. + if( errorCode == CL_SUCCESS ) + { + pOverrides = m_PrecompiledKernelOverridesMap[ context ]; + if( pOverrides == NULL ) + { + errorCode = CL_INVALID_VALUE; + } + } + + if( false && // disabled - this kernel is slower than the UInt4 kernel + ( m_Config.ForceByteBufferOverrides == false ) && + ( ( srcOffset % 64 ) == 0 ) && + ( ( dstOffset % 64 ) == 0 ) ) + { + if( errorCode == CL_SUCCESS ) + { + errorCode |= dispatch().clSetKernelArg( + pOverrides->Kernel_CopyBufferUInt16s, + 0, + sizeof( srcBuffer ), + &srcBuffer ); + errorCode |= dispatch().clSetKernelArg( + pOverrides->Kernel_CopyBufferUInt16s, + 1, + sizeof( dstBuffer ), + &dstBuffer ); + + cl_uint uiSrcOffsetInUint16s = (cl_uint)( srcOffset / 64 ); + errorCode |= dispatch().clSetKernelArg( + pOverrides->Kernel_CopyBufferUInt16s, + 2, + sizeof( uiSrcOffsetInUint16s ), + &uiSrcOffsetInUint16s ); + + cl_uint uiDstOffsetInUint16s = (cl_uint)( dstOffset / 64 ); + errorCode |= dispatch().clSetKernelArg( + pOverrides->Kernel_CopyBufferUInt16s, + 3, + sizeof( uiDstOffsetInUint16s ), + &uiDstOffsetInUint16s ); + + cl_uint uiBytesToCopy = (cl_uint)( bytesToCopy ); + errorCode |= dispatch().clSetKernelArg( + pOverrides->Kernel_CopyBufferUInt16s, + 4, + sizeof( uiBytesToCopy ), + &uiBytesToCopy ); + + if( errorCode == CL_SUCCESS ) + { + size_t global_work_size = bytesToCopy / 64; + size_t local_work_size = 32; + + // Round up if we don't have an even multiple of UInt16s + if( ( bytesToCopy % 64 ) != 0 ) + { + global_work_size++; + } + + // Make sure global_work_size is an even multiple of local_work_size + if( ( global_work_size % local_work_size ) != 0 ) + { + global_work_size += + local_work_size - + ( global_work_size % local_work_size ); + } + + // Execute kernel + errorCode = dispatch().clEnqueueNDRangeKernel( + commandQueue, + pOverrides->Kernel_CopyBufferUInt16s, + 1, + NULL, + &global_work_size, + &local_work_size, + numEventsInWaitList, + eventWaitList, + event ); + } + } + } + else if( ( m_Config.ForceByteBufferOverrides == false ) && + ( ( srcOffset % 16 ) == 0 ) && + ( ( dstOffset % 16 ) == 0 ) ) + { + if( errorCode == CL_SUCCESS ) + { + errorCode |= dispatch().clSetKernelArg( + pOverrides->Kernel_CopyBufferUInt4s, + 0, + sizeof( srcBuffer ), + &srcBuffer ); + errorCode |= dispatch().clSetKernelArg( + pOverrides->Kernel_CopyBufferUInt4s, + 1, + sizeof( dstBuffer ), + &dstBuffer ); + + cl_uint uiSrcOffsetInUint4s = (cl_uint)( srcOffset / 16 ); + errorCode |= dispatch().clSetKernelArg( + pOverrides->Kernel_CopyBufferUInt4s, + 2, + sizeof( uiSrcOffsetInUint4s ), + &uiSrcOffsetInUint4s ); + + cl_uint uiDstOffsetInUint4s = (cl_uint)( dstOffset / 16 ); + errorCode |= dispatch().clSetKernelArg( + pOverrides->Kernel_CopyBufferUInt4s, + 3, + sizeof( uiDstOffsetInUint4s ), + &uiDstOffsetInUint4s ); + + cl_uint uiBytesToCopy = (cl_uint)( bytesToCopy ); + errorCode |= dispatch().clSetKernelArg( + pOverrides->Kernel_CopyBufferUInt4s, + 4, + sizeof( uiBytesToCopy ), + &uiBytesToCopy ); + + if( errorCode == CL_SUCCESS ) + { + size_t global_work_size = bytesToCopy / 16; + size_t local_work_size = 32; + + // Round up if we don't have an even multiple of UInt4s + if( ( bytesToCopy % 16 ) != 0 ) + { + global_work_size++; + } + + // Make sure global_work_size is an even multiple of local_work_size + if( ( global_work_size % local_work_size ) != 0 ) + { + global_work_size += + local_work_size - + ( global_work_size % local_work_size ); + } + + // Execute kernel + errorCode = dispatch().clEnqueueNDRangeKernel( + commandQueue, + pOverrides->Kernel_CopyBufferUInt4s, + 1, + NULL, + &global_work_size, + &local_work_size, + numEventsInWaitList, + eventWaitList, + event ); + } + } + } + else if( ( m_Config.ForceByteBufferOverrides == false ) && + ( ( srcOffset % 4 ) == 0 ) && + ( ( dstOffset % 4 ) == 0 ) ) + { + if( errorCode == CL_SUCCESS ) + { + errorCode |= dispatch().clSetKernelArg( + pOverrides->Kernel_CopyBufferUInts, + 0, + sizeof( srcBuffer ), + &srcBuffer ); + errorCode |= dispatch().clSetKernelArg( + pOverrides->Kernel_CopyBufferUInts, + 1, + sizeof( dstBuffer ), + &dstBuffer ); + + cl_uint uiSrcOffsetInUints = (cl_uint)( srcOffset / 4 ); + errorCode |= dispatch().clSetKernelArg( + pOverrides->Kernel_CopyBufferUInts, + 2, + sizeof( uiSrcOffsetInUints ), + &uiSrcOffsetInUints ); + + cl_uint uiDstOffsetInUints = (cl_uint)( dstOffset / 4 ); + errorCode |= dispatch().clSetKernelArg( + pOverrides->Kernel_CopyBufferUInts, + 3, + sizeof( uiDstOffsetInUints ), + &uiDstOffsetInUints ); + + cl_uint uiBytesToCopy = (cl_uint)( bytesToCopy ); + errorCode |= dispatch().clSetKernelArg( + pOverrides->Kernel_CopyBufferUInts, + 4, + sizeof( uiBytesToCopy ), + &uiBytesToCopy ); + + if( errorCode == CL_SUCCESS ) + { + size_t global_work_size = bytesToCopy / 4; + size_t local_work_size = 32; + + // Round up if we don't have an even multiple of UInts + if( ( bytesToCopy % 4 ) != 0 ) + { + global_work_size++; + } + + // Make sure global_work_size is an even multiple of local_work_size + if( ( global_work_size % local_work_size ) != 0 ) + { + global_work_size += + local_work_size - + ( global_work_size % local_work_size ); + } + + // Execute kernel + errorCode = dispatch().clEnqueueNDRangeKernel( + commandQueue, + pOverrides->Kernel_CopyBufferUInts, + 1, + NULL, + &global_work_size, + &local_work_size, + numEventsInWaitList, + eventWaitList, + event ); + } + } + } + else + { + if( errorCode == CL_SUCCESS ) + { + errorCode |= dispatch().clSetKernelArg( + pOverrides->Kernel_CopyBufferBytes, + 0, + sizeof( srcBuffer ), + &srcBuffer ); + errorCode |= dispatch().clSetKernelArg( + pOverrides->Kernel_CopyBufferBytes, + 1, + sizeof( dstBuffer ), + &dstBuffer ); + + cl_uint uiSrcOffset = (cl_uint)( srcOffset ); + errorCode |= dispatch().clSetKernelArg( + pOverrides->Kernel_CopyBufferBytes, + 2, + sizeof( uiSrcOffset ), + &uiSrcOffset ); + + cl_uint uiDstOffset = (cl_uint)( dstOffset ); + errorCode |= dispatch().clSetKernelArg( + pOverrides->Kernel_CopyBufferBytes, + 3, + sizeof( uiDstOffset ), + &uiDstOffset ); + + cl_uint uiBytesToCopy = (cl_uint)( bytesToCopy ); + errorCode |= dispatch().clSetKernelArg( + pOverrides->Kernel_CopyBufferBytes, + 4, + sizeof( uiBytesToCopy ), + &uiBytesToCopy ); + + if( errorCode == CL_SUCCESS ) + { + size_t global_work_size = bytesToCopy; + size_t local_work_size = 32; + + // Make sure global_work_size is an even multiple of local_work_size + if( ( global_work_size % local_work_size ) != 0 ) + { + global_work_size += + local_work_size - + ( global_work_size % local_work_size ); + } + + // Execute kernel + errorCode = dispatch().clEnqueueNDRangeKernel( + commandQueue, + pOverrides->Kernel_CopyBufferBytes, + 1, + NULL, + &global_work_size, + &local_work_size, + numEventsInWaitList, + eventWaitList, + event ); + } + } + } + + return errorCode; +} + +/////////////////////////////////////////////////////////////////////////////// +// +cl_int CLIntercept::ReadImage( + cl_command_queue commandQueue, + cl_mem srcImage, + cl_bool blockingRead, + const size_t* srcOrigin, + const size_t* region, + size_t dstRowPitch, + size_t dstSlicePitch, + void* dstPtr, + cl_uint numEventsInWaitList, + const cl_event* eventWaitList, + cl_event* event ) +{ + m_OS.EnterCriticalSection(); + + cl_int errorCode = CL_SUCCESS; + + // Basic error checking, to avoid possible null pointer dereferences. + if( errorCode == CL_SUCCESS ) + { + if( srcOrigin == NULL || region == NULL ) + { + errorCode = CL_INVALID_VALUE; + } + } + + cl_context context = NULL; + + // Get the context for this command queue. + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetCommandQueueInfo( + commandQueue, + CL_QUEUE_CONTEXT, + sizeof( context ), + &context, + NULL ); + } + + // Create a USE_HOST_PTR image for the passed-in pointer. + // The size of the buffer will be at least as big as the region to read. + + // We need to know what type of image to create. + // If region[2] is 1, then a 2D image will suffice, + // otherwise we'll need to create a 3D image. + + // The image will have the same image format as srcImage. + + cl_image_format srcFormat = { 0 }; + + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetImageInfo( + srcImage, + CL_IMAGE_FORMAT, + sizeof( srcFormat ), + &srcFormat, + NULL ); + } + + cl_mem dstImage = NULL; + + if( errorCode == CL_SUCCESS ) + { + if( region[2] == 1 ) + { + // 2D image + dstImage = dispatch().clCreateImage2D( + context, + CL_MEM_USE_HOST_PTR | CL_MEM_WRITE_ONLY, + &srcFormat, + region[0], + region[1], + dstRowPitch, + dstPtr, + &errorCode ); + } + else + { + // 3D image + dstImage = dispatch().clCreateImage3D( + context, + CL_MEM_USE_HOST_PTR | CL_MEM_WRITE_ONLY, + &srcFormat, + region[0], + region[1], + region[2], + dstRowPitch, + dstSlicePitch, + dstPtr, + &errorCode ); + } + } + + size_t dstOrigin[3] = { 0, 0, 0 }; + + if( errorCode == CL_SUCCESS ) + { + errorCode = CopyImageHelper( + context, + commandQueue, + srcImage, + dstImage, + srcOrigin, + dstOrigin, + region, + numEventsInWaitList, + eventWaitList, + event ); + } + + // Technically, we need to map and unmap the destination image + // to transfer data to our pointer. This will also handle + // blockingRead. + if( errorCode == CL_SUCCESS ) + { + void* mappedPointer = dispatch().clEnqueueMapImage( + commandQueue, + dstImage, + blockingRead, + CL_MAP_READ, + dstOrigin, + region, + &dstRowPitch, + &dstSlicePitch, + 0, + NULL, + NULL, + &errorCode ); + + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clEnqueueUnmapMemObject( + commandQueue, + dstImage, + mappedPointer, + 0, + NULL, + NULL ); + } + } + + dispatch().clReleaseMemObject( dstImage ); + + m_OS.LeaveCriticalSection(); + + return errorCode; +} + +/////////////////////////////////////////////////////////////////////////////// +// +cl_int CLIntercept::WriteImage( + cl_command_queue commandQueue, + cl_mem dstImage, + cl_bool blockingWrite, + const size_t* dstOrigin, + const size_t* region, + size_t srcRowPitch, + size_t srcSlicePitch, + const void* srcPtr, + cl_uint numEventsInWaitList, + const cl_event* eventWaitList, + cl_event* event ) +{ + m_OS.EnterCriticalSection(); + + cl_int errorCode = CL_SUCCESS; + + // Basic error checking, to avoid possible null pointer dereferences. + if( errorCode == CL_SUCCESS ) + { + if( dstOrigin == NULL || region == NULL ) + { + errorCode = CL_INVALID_VALUE; + } + } + + cl_context context = NULL; + + // Get the context for this command queue. + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetCommandQueueInfo( + commandQueue, + CL_QUEUE_CONTEXT, + sizeof( context ), + &context, + NULL ); + } + + // Create a USE_HOST_PTR image for the passed-in pointer. + // The size of the buffer will be at least as big as the region to read. + + // We need to know what type of image to create. + // If region[2] is 1, then a 2D image will suffice, + // otherwise we'll need to create a 3D image. + + // The image will have the same image format as srcImage. + + cl_image_format dstFormat = { 0 }; + + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetImageInfo( + dstImage, + CL_IMAGE_FORMAT, + sizeof( dstFormat ), + &dstFormat, + NULL ); + } + + cl_mem srcImage = NULL; + + if( errorCode == CL_SUCCESS ) + { + if( region[2] == 1 ) + { + // 2D image + srcImage = dispatch().clCreateImage2D( + context, + CL_MEM_USE_HOST_PTR | CL_MEM_READ_ONLY, + &dstFormat, + region[0], + region[1], + srcRowPitch, + (void*)srcPtr, + &errorCode ); + } + else + { + // 3D image + srcImage = dispatch().clCreateImage3D( + context, + CL_MEM_USE_HOST_PTR | CL_MEM_READ_ONLY, + &dstFormat, + region[0], + region[1], + region[2], + srcRowPitch, + srcSlicePitch, + (void*)srcPtr, + &errorCode ); + } + } + + size_t srcOrigin[3] = { 0, 0, 0 }; + + if( errorCode == CL_SUCCESS ) + { + errorCode = CopyImageHelper( + context, + commandQueue, + srcImage, + dstImage, + srcOrigin, + dstOrigin, + region, + numEventsInWaitList, + eventWaitList, + event ); + } + + if( errorCode == CL_SUCCESS ) + { + if( blockingWrite ) + { + errorCode = dispatch().clFinish( + commandQueue ); + } + } + + dispatch().clReleaseMemObject( srcImage ); + + m_OS.LeaveCriticalSection(); + + return errorCode; +} + +/////////////////////////////////////////////////////////////////////////////// +// +cl_int CLIntercept::CopyImage( + cl_command_queue commandQueue, + cl_mem srcImage, + cl_mem dstImage, + const size_t* srcOrigin, + const size_t* dstOrigin, + const size_t* region, + cl_uint numEventsInWaitList, + const cl_event* eventWaitList, + cl_event* event ) +{ + m_OS.EnterCriticalSection(); + + cl_int errorCode = CL_SUCCESS; + + cl_context context = NULL; + + // Get the context for this command queue. + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetCommandQueueInfo( + commandQueue, + CL_QUEUE_CONTEXT, + sizeof( context ), + &context, + NULL ); + } + + if( errorCode == CL_SUCCESS ) + { + errorCode = CopyImageHelper( + context, + commandQueue, + srcImage, + dstImage, + srcOrigin, + dstOrigin, + region, + numEventsInWaitList, + eventWaitList, + event ); + } + + m_OS.LeaveCriticalSection(); + + return errorCode; +} + +/////////////////////////////////////////////////////////////////////////////// +// +cl_int CLIntercept::CopyImageHelper( + cl_context context, + cl_command_queue commandQueue, + cl_mem srcImage, + cl_mem dstImage, + const size_t* srcOrigin, + const size_t* dstOrigin, + const size_t* region, + cl_uint numEventsInWaitList, + const cl_event* eventWaitList, + cl_event* event ) +{ + // This function assumes that it is being called from within a critical + // section, so it does not enter the critical section again. + + cl_int errorCode = CL_SUCCESS; + + SPrecompiledKernelOverrides* pOverrides = NULL; + + // Get the overrides for this context. + if( errorCode == CL_SUCCESS ) + { + pOverrides = m_PrecompiledKernelOverridesMap[ context ]; + if( pOverrides == NULL ) + { + errorCode = CL_INVALID_VALUE; + } + } + + // Figure out the type of the source image. + cl_mem_object_type srcType = CL_MEM_OBJECT_BUFFER; + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetMemObjectInfo( + srcImage, + CL_MEM_TYPE, + sizeof( srcType ), + &srcType, + NULL ); + } + + // Figure out the type of the destination image. + cl_mem_object_type dstType = CL_MEM_OBJECT_BUFFER; + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetMemObjectInfo( + srcImage, + CL_MEM_TYPE, + sizeof( dstType ), + &dstType, + NULL ); + } + + // Figure out the format of the source image. + cl_image_format srcFormat = { 0 }; + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetImageInfo( + srcImage, + CL_IMAGE_FORMAT, + sizeof( srcFormat ), + &srcFormat, + NULL ); + } + + // Figure out the format of the destination image. + cl_image_format dstFormat = { 0 }; + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetImageInfo( + dstImage, + CL_IMAGE_FORMAT, + sizeof( dstFormat ), + &dstFormat, + NULL ); + } + + // Image formats must match. + if( errorCode == CL_SUCCESS ) + { + if( ( srcFormat.image_channel_data_type != dstFormat.image_channel_data_type ) || + ( srcFormat.image_channel_order != dstFormat.image_channel_order ) ) + { + errorCode = CL_IMAGE_FORMAT_MISMATCH; + } + switch( srcType ) + { + case CL_MEM_OBJECT_IMAGE2D: + if( ( srcOrigin[2] != 0 ) || + ( region[2] != 1 ) ) + { + errorCode = CL_INVALID_VALUE; + } + break; + case CL_MEM_OBJECT_IMAGE3D: + break; + default: + errorCode = CL_INVALID_OPERATION; + break; + } + switch( dstType ) + { + case CL_MEM_OBJECT_IMAGE2D: + if( ( dstOrigin[2] != 0 ) || + ( region[2] != 1 ) ) + { + errorCode = CL_INVALID_VALUE; + } + break; + case CL_MEM_OBJECT_IMAGE3D: + break; + default: + errorCode = CL_INVALID_OPERATION; + break; + } + } + + cl_kernel kernel = NULL; + if( errorCode == CL_SUCCESS ) + { + switch( srcFormat.image_channel_data_type ) + { + case CL_UNORM_INT8: + case CL_UNORM_INT16: + case CL_SNORM_INT8: + case CL_SNORM_INT16: + case CL_HALF_FLOAT: + case CL_FLOAT: + // "Float" Images + switch( srcType ) + { + case CL_MEM_OBJECT_IMAGE2D: + switch( dstType ) + { + case CL_MEM_OBJECT_IMAGE2D: + // 2D to 2D + kernel = pOverrides->Kernel_CopyImage2Dto2DFloat; + break; + default: + CLI_ASSERT( 0 ); + errorCode = CL_INVALID_OPERATION; + break; + } + break; + default: + CLI_ASSERT( 0 ); + errorCode = CL_INVALID_OPERATION; + break; + } + break; + + case CL_SIGNED_INT8: + case CL_SIGNED_INT16: + case CL_SIGNED_INT32: + // "Int" Images + switch( srcType ) + { + case CL_MEM_OBJECT_IMAGE2D: + switch( dstType ) + { + case CL_MEM_OBJECT_IMAGE2D: + // 2D to 2D + kernel = pOverrides->Kernel_CopyImage2Dto2DInt; + break; + default: + CLI_ASSERT( 0 ); + errorCode = CL_INVALID_OPERATION; + break; + } + break; + default: + CLI_ASSERT( 0 ); + errorCode = CL_INVALID_OPERATION; + break; + } + break; + + case CL_UNSIGNED_INT8: + case CL_UNSIGNED_INT16: + case CL_UNSIGNED_INT32: + // "UInt" Images + switch( srcType ) + { + case CL_MEM_OBJECT_IMAGE2D: + switch( dstType ) + { + case CL_MEM_OBJECT_IMAGE2D: + // 2D to 2D + kernel = pOverrides->Kernel_CopyImage2Dto2DUInt; + break; + default: + CLI_ASSERT( 0 ); + errorCode = CL_INVALID_OPERATION; + break; + } + break; + default: + CLI_ASSERT( 0 ); + errorCode = CL_INVALID_OPERATION; + break; + } + break; + + default: + CLI_ASSERT( 0 ); + errorCode = CL_INVALID_IMAGE_FORMAT_DESCRIPTOR; + break; + } + } + + if( errorCode == CL_SUCCESS ) + { + errorCode |= dispatch().clSetKernelArg( + kernel, + 0, + sizeof( srcImage ), + &srcImage ); + errorCode |= dispatch().clSetKernelArg( + kernel, + 1, + sizeof( dstImage ), + &dstImage ); + + cl_uint uiArg = (cl_uint)( srcOrigin[0] ); + errorCode |= dispatch().clSetKernelArg( + kernel, + 2, + sizeof( uiArg ), + &uiArg ); + uiArg = (cl_uint)( srcOrigin[1] ); + errorCode |= dispatch().clSetKernelArg( + kernel, + 3, + sizeof( uiArg ), + &uiArg ); + uiArg = (cl_uint)( srcOrigin[2] ); + errorCode |= dispatch().clSetKernelArg( + kernel, + 4, + sizeof( uiArg ), + &uiArg ); + + uiArg = (cl_uint)( dstOrigin[0] ); + errorCode |= dispatch().clSetKernelArg( + kernel, + 5, + sizeof( uiArg ), + &uiArg ); + uiArg = (cl_uint)( dstOrigin[1] ); + errorCode |= dispatch().clSetKernelArg( + kernel, + 6, + sizeof( uiArg ), + &uiArg ); + uiArg = (cl_uint)( dstOrigin[2] ); + errorCode |= dispatch().clSetKernelArg( + kernel, + 7, + sizeof( uiArg ), + &uiArg ); + + uiArg = (cl_uint)( region[0] ); + errorCode |= dispatch().clSetKernelArg( + kernel, + 8, + sizeof( uiArg ), + &uiArg ); + uiArg = (cl_uint)( region[1] ); + errorCode |= dispatch().clSetKernelArg( + kernel, + 9, + sizeof( uiArg ), + &uiArg ); + uiArg = (cl_uint)( region[2] ); + errorCode |= dispatch().clSetKernelArg( + kernel, + 10, + sizeof( uiArg ), + &uiArg ); + + if( errorCode == CL_SUCCESS ) + { + size_t global_work_size[3] = + { + region[0], + region[1], + region[2] + }; + size_t local_work_size[3] = + { + 32, + 1, + 1 + }; + + // Make sure global_work_size is an even multiple of local_work_size + if( ( global_work_size[0] % local_work_size[0] ) != 0 ) + { + global_work_size[0] += + local_work_size[0] - + ( global_work_size[0] % local_work_size[0] ); + } + CLI_ASSERT( local_work_size[1] == 1 ); + CLI_ASSERT( local_work_size[2] == 1 ); + + // Execute kernel + errorCode = dispatch().clEnqueueNDRangeKernel( + commandQueue, + kernel, + 3, + NULL, + global_work_size, + local_work_size, + numEventsInWaitList, + eventWaitList, + event ); + } + } + + return errorCode; +} + +/////////////////////////////////////////////////////////////////////////////// +// +cl_program CLIntercept::createProgramWithBuiltinKernels( + cl_context context ) +{ + m_OS.EnterCriticalSection(); + + cl_program program = NULL; + + SBuiltinKernelOverrides* pOverrides = m_BuiltinKernelOverridesMap[ context ]; + if( pOverrides ) + { + program = pOverrides->Program; + dispatch().clRetainProgram( program ); + } + + m_OS.LeaveCriticalSection(); + return program; +} + +/////////////////////////////////////////////////////////////////////////////// +// +cl_kernel CLIntercept::createBuiltinKernel( + cl_program program, + const std::string& kernel_name, + cl_int* errcode_ret ) +{ + m_OS.EnterCriticalSection(); + + cl_int errorCode = CL_SUCCESS; + + cl_context context = NULL; + cl_kernel kernel = NULL; + + // Get the context for this program. + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetProgramInfo( + program, + CL_PROGRAM_CONTEXT, + sizeof( context ), + &context, + NULL ); + } + + SBuiltinKernelOverrides* pOverrides = NULL; + + // Get the overrides for this context. + if( errorCode == CL_SUCCESS ) + { + pOverrides = m_BuiltinKernelOverridesMap[ context ]; + if( pOverrides != NULL ) + { + if( kernel_name == "block_motion_estimate_intel" ) + { + kernel = pOverrides->Kernel_block_motion_estimate_intel; + dispatch().clRetainKernel( kernel ); + if( errcode_ret ) + { + errcode_ret[0] = CL_SUCCESS; + } + } + } + } + + m_OS.LeaveCriticalSection(); + + return kernel; +} + +/////////////////////////////////////////////////////////////////////////////// +// +cl_int CLIntercept::NDRangeBuiltinKernel( + cl_command_queue commandQueue, + 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 ) +{ + m_OS.EnterCriticalSection(); + + cl_int errorCode = CL_SUCCESS; + + cl_context context = NULL; + + // Get the context for this command queue. + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetCommandQueueInfo( + commandQueue, + CL_QUEUE_CONTEXT, + sizeof( context ), + &context, + NULL ); + } + + SBuiltinKernelOverrides* pOverrides = NULL; + + // Get the overrides for this context. + if( errorCode == CL_SUCCESS ) + { + pOverrides = m_BuiltinKernelOverridesMap[ context ]; + if( pOverrides == NULL ) + { + errorCode = CL_INVALID_VALUE; + } + } + + // See if this kernel is one of our overridden builtin kernels. + if( errorCode == CL_SUCCESS ) + { + if( kernel == pOverrides->Kernel_block_motion_estimate_intel ) + { + if( ( work_dim == 2 ) && + ( global_work_size != NULL ) && + ( local_work_size == NULL ) ) + { + const size_t BLOCK_SIZE = 16; + const size_t w = ( global_work_size[0] + BLOCK_SIZE - 1 ) / BLOCK_SIZE; + const size_t h = ( global_work_size[1] + BLOCK_SIZE - 1 ) / BLOCK_SIZE; +#if 0 + const size_t new_global_work_size[] = { w * BLOCK_SIZE, h }; + const size_t new_local_work_size[] = { BLOCK_SIZE, 1 }; + + int iterations = 1; +#else + const size_t new_global_work_size[] = { w * BLOCK_SIZE, 1 }; + const size_t new_local_work_size[] = { BLOCK_SIZE, 1 }; + + int iterations = (int)h; +#endif + errorCode = dispatch().clSetKernelArg( + kernel, + 6, + sizeof( iterations ), + &iterations ); + + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clEnqueueNDRangeKernel( + commandQueue, + kernel, + 2, + global_work_offset, + new_global_work_size, + new_local_work_size, + num_events_in_wait_list, + event_wait_list, + event ); + } + } + } + else + { + errorCode = CL_INVALID_VALUE; + } + } + + m_OS.LeaveCriticalSection(); + + return errorCode; +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::SIMDSurveyCreateProgramFromSource( + const cl_program program, + cl_context context, + cl_uint count, + const char** strings, + const size_t* lengths ) +{ + m_OS.EnterCriticalSection(); + + cl_int errorCode = CL_SUCCESS; + + SSIMDSurveyProgram* pSIMDSurveyProgram = + m_SIMDSurveyProgramMap[ program ]; + if( pSIMDSurveyProgram ) + { + errorCode = dispatch().clReleaseProgram( pSIMDSurveyProgram->SIMD8Program ); + errorCode = dispatch().clReleaseProgram( pSIMDSurveyProgram->SIMD16Program ); + errorCode = dispatch().clReleaseProgram( pSIMDSurveyProgram->SIMD32Program ); + + delete pSIMDSurveyProgram; + pSIMDSurveyProgram = NULL; + + m_SIMDSurveyProgramMap[ program ] = NULL; + } + + pSIMDSurveyProgram = new SSIMDSurveyProgram; + if( pSIMDSurveyProgram ) + { + log( "SIMD Survey: CreateProgramFromSource\n" ); + + pSIMDSurveyProgram->SIMD8Program = dispatch().clCreateProgramWithSource( + context, + count, + strings, + lengths, + &errorCode ); + pSIMDSurveyProgram->SIMD16Program = dispatch().clCreateProgramWithSource( + context, + count, + strings, + lengths, + &errorCode ); + pSIMDSurveyProgram->SIMD32Program = dispatch().clCreateProgramWithSource( + context, + count, + strings, + lengths, + &errorCode ); + + m_SIMDSurveyProgramMap[ program ] = pSIMDSurveyProgram; + } + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::SIMDSurveyBuildProgram( + const cl_program program, + cl_uint numDevices, + const cl_device_id* deviceList, + const char* options ) +{ + m_OS.EnterCriticalSection(); + + cl_int errorCode = CL_SUCCESS; + + SSIMDSurveyProgram* pSIMDSurveyProgram = + m_SIMDSurveyProgramMap[ program ]; + if( pSIMDSurveyProgram ) + { + // Pre-pend the required subgroup size build option. This assumes that + // if the required subgroup size options string is already in the program + // options string then the later option will have precedence. + std::string userOptions( options ? options : "" ); + std::string simd8Options = config().SIMDSurveySIMD8Option + " " + userOptions; + std::string simd16Options = config().SIMDSurveySIMD16Option + " " + userOptions; + std::string simd32Options = config().SIMDSurveySIMD32Option + " " + userOptions; + + log( "SIMD Survey: Building SIMD8 kernel with options: " + simd8Options + "\n" ); + errorCode |= dispatch().clBuildProgram( + pSIMDSurveyProgram->SIMD8Program, + numDevices, + deviceList, + simd8Options.c_str(), + NULL, + NULL ); + log( "SIMD Survey: Building SIMD16 kernel with options: " + simd16Options + "\n" ); + errorCode |= dispatch().clBuildProgram( + pSIMDSurveyProgram->SIMD16Program, + numDevices, + deviceList, + simd16Options.c_str(), + NULL, + NULL ); + log( "SIMD Survey: Building SIMD32 kernel with options: " + simd16Options + "\n" ); + errorCode |= dispatch().clBuildProgram( + pSIMDSurveyProgram->SIMD32Program, + numDevices, + deviceList, + simd32Options.c_str(), + NULL, + NULL ); + if( errorCode != CL_SUCCESS ) + { + log( "SIMD Survey: Building done (with errors).\n" ); + } + else + { + log( "SIMD Survey: Building done.\n" ); + } + } + else + { + logf( "SIMD Survey: BuildProgram: Couldn't find info for program %p!?!?\n", + program ); + } + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::SIMDSurveyCreateKernel( + const cl_program program, + const cl_kernel kernel, + const std::string& kernelName ) +{ + m_OS.EnterCriticalSection(); + + cl_int errorCode = CL_SUCCESS; + + SSIMDSurveyKernel* pSIMDSurveyKernel = + m_SIMDSurveyKernelMap[ kernel ]; + if( pSIMDSurveyKernel ) + { + // I don't think this should happen, assuming we've cleaned up + // correctly when the kernel is released.... + CLI_ASSERT( 0 ); + + // Remove the parent kernel and each of the child kernels from the map. + m_SIMDSurveyKernelMap.erase( kernel ); + + m_SIMDSurveyKernelMap.erase( pSIMDSurveyKernel->SIMD8Kernel ); + m_SIMDSurveyKernelMap.erase( pSIMDSurveyKernel->SIMD16Kernel ); + m_SIMDSurveyKernelMap.erase( pSIMDSurveyKernel->SIMD32Kernel ); + + errorCode = dispatch().clReleaseKernel( pSIMDSurveyKernel->SIMD8Kernel ); + errorCode = dispatch().clReleaseKernel( pSIMDSurveyKernel->SIMD16Kernel ); + errorCode = dispatch().clReleaseKernel( pSIMDSurveyKernel->SIMD32Kernel ); + + delete pSIMDSurveyKernel; + pSIMDSurveyKernel = NULL; + } + + SSIMDSurveyProgram* pSIMDSurveyProgram = + m_SIMDSurveyProgramMap[ program ]; + if( pSIMDSurveyProgram ) + { + pSIMDSurveyKernel = new SSIMDSurveyKernel; + if( pSIMDSurveyKernel ) + { + logf( "SIMD Survey: Creating kernels for %s\n", + kernelName.c_str() ); + + pSIMDSurveyKernel->SIMD8Kernel = dispatch().clCreateKernel( + pSIMDSurveyProgram->SIMD8Program, + kernelName.c_str(), + &errorCode ); + pSIMDSurveyKernel->SIMD16Kernel = dispatch().clCreateKernel( + pSIMDSurveyProgram->SIMD16Program, + kernelName.c_str(), + &errorCode ); + pSIMDSurveyKernel->SIMD32Kernel = dispatch().clCreateKernel( + pSIMDSurveyProgram->SIMD32Program, + kernelName.c_str(), + &errorCode ); + + pSIMDSurveyKernel->SIMD8ExecutionTimeNS = CL_ULONG_MAX; + pSIMDSurveyKernel->SIMD16ExecutionTimeNS = CL_ULONG_MAX; + pSIMDSurveyKernel->SIMD32ExecutionTimeNS = CL_ULONG_MAX; + + pSIMDSurveyKernel->ExecutionNumber = 0; + + // We'll install the same pointer into the map for the real + // parent kernel and for each of the child kernels compiled + // for specific SIMD sizes. The parent kernel is used to + // look up the kernel to execute, and the child kernels are + // used to aggregate the results. + + m_SIMDSurveyKernelMap[ kernel ] = pSIMDSurveyKernel; + + m_SIMDSurveyKernelMap[ pSIMDSurveyKernel->SIMD8Kernel ] = pSIMDSurveyKernel; + m_SIMDSurveyKernelMap[ pSIMDSurveyKernel->SIMD16Kernel ] = pSIMDSurveyKernel; + m_SIMDSurveyKernelMap[ pSIMDSurveyKernel->SIMD32Kernel ] = pSIMDSurveyKernel; + + // Also, keep the kernel name map up-to-date. This is necessary to + // print the right kernel names in e.g. device timing reports. The + // other maps, such as the kernel arg map, don't need to know about + // child kernels, so we don't add anything for them here. + m_KernelNameMap[ pSIMDSurveyKernel->SIMD8Kernel ] = kernelName; + m_KernelNameMap[ pSIMDSurveyKernel->SIMD16Kernel ] = kernelName; + m_KernelNameMap[ pSIMDSurveyKernel->SIMD32Kernel ] = kernelName; + } + } + else + { + logf( "SIMD Survey: CreateKernel: Couldn't find info for program %p!?!?\n", + program ); + } + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::SIMDSurveySetKernelArg( + cl_kernel kernel, + cl_uint argIndex, + size_t argSize, + const void* argValue ) +{ + m_OS.EnterCriticalSection(); + + SSIMDSurveyKernel* pSIMDSurveyKernel = + m_SIMDSurveyKernelMap[ kernel ]; + if( pSIMDSurveyKernel ) + { + dispatch().clSetKernelArg( + pSIMDSurveyKernel->SIMD8Kernel, + argIndex, + argSize, + argValue ); + dispatch().clSetKernelArg( + pSIMDSurveyKernel->SIMD16Kernel, + argIndex, + argSize, + argValue ); + dispatch().clSetKernelArg( + pSIMDSurveyKernel->SIMD32Kernel, + argIndex, + argSize, + argValue ); + } + else + { + logf( "SIMD Survey: SerKernelArg: Couldn't find info for kernel %p!?!?\n", + kernel ); + } + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::SIMDSurveyNDRangeKernel( + cl_kernel& kernel ) +{ + m_OS.EnterCriticalSection(); + + SSIMDSurveyKernel* pSIMDSurveyKernel = + m_SIMDSurveyKernelMap[ kernel ]; + if( pSIMDSurveyKernel ) + { + const std::string& kernelName = m_KernelNameMap[ kernel ]; + + const uint32_t cWarmupIterations = config().SIMDSurveyWarmupIterations; + if( pSIMDSurveyKernel->ExecutionNumber >= cWarmupIterations ) + { + const uint32_t cSample = + pSIMDSurveyKernel->ExecutionNumber - cWarmupIterations; + + // This just tries the three kernels in order from + // 8 -> 16 -> 32, one time each. + // + // Other things we can try: + // - executing each kernel multiple times + // - different orders + switch( cSample ) + { + case 0: + if( pSIMDSurveyKernel->SIMD8Kernel ) + { + log( "SIMD Survey: NDRange: Sampling SIMD8 kernel for " + kernelName + "\n" ); + kernel = pSIMDSurveyKernel->SIMD8Kernel; + } + else + { + log( "SIMD Survey: NDRange: Skipping sample, no SIMD8 kernel exists for " + kernelName + ".\n" ); + } + break; + case 1: + if( pSIMDSurveyKernel->SIMD16Kernel ) + { + log( "SIMD Survey: NDRange: Sampling SIMD16 kernel for " + kernelName + "\n" ); + kernel = pSIMDSurveyKernel->SIMD16Kernel; + } + else + { + log( "SIMD Survey: NDRange: Skipping sample, no SIMD16 kernel exists for " + kernelName + ".\n" ); + } + break; + case 2: + if( pSIMDSurveyKernel->SIMD32Kernel ) + { + log( "SIMD Survey: NDRange: Sampling SIMD32 kernel for " + kernelName + "\n" ); + kernel = pSIMDSurveyKernel->SIMD32Kernel; + } + else + { + log( "SIMD Survey: NDRange: Skipping sample, no SIMD32 kernel exists for " + kernelName + ".\n" ); + } + break; + default: + if( pSIMDSurveyKernel->SIMD8ExecutionTimeNS != CL_ULONG_MAX || + pSIMDSurveyKernel->SIMD16ExecutionTimeNS != CL_ULONG_MAX || + pSIMDSurveyKernel->SIMD32ExecutionTimeNS != CL_ULONG_MAX ) + { + cl_ulong fastestTimeNS = CL_ULONG_MAX; + cl_uint fastestSIMD = 0; + if( pSIMDSurveyKernel->SIMD8ExecutionTimeNS < fastestTimeNS ) + { + fastestTimeNS = pSIMDSurveyKernel->SIMD8ExecutionTimeNS; + fastestSIMD = 8; + kernel = pSIMDSurveyKernel->SIMD8Kernel; + } + if( pSIMDSurveyKernel->SIMD16ExecutionTimeNS < fastestTimeNS ) + { + fastestTimeNS = pSIMDSurveyKernel->SIMD16ExecutionTimeNS; + fastestSIMD = 16; + kernel = pSIMDSurveyKernel->SIMD16Kernel; + } + if( pSIMDSurveyKernel->SIMD32ExecutionTimeNS < fastestTimeNS ) + { + fastestTimeNS = pSIMDSurveyKernel->SIMD32ExecutionTimeNS; + fastestSIMD = 32; + kernel = pSIMDSurveyKernel->SIMD32Kernel; + } + + logf( "SIMD Survey: NDRange: Picking SIMD%u kernel for %s: SIMD8 Time = %u, SIMD16 Time = %u, SIMD32 Time = %u\n", + fastestSIMD, + kernelName.c_str(), + (cl_uint)pSIMDSurveyKernel->SIMD8ExecutionTimeNS, + (cl_uint)pSIMDSurveyKernel->SIMD16ExecutionTimeNS, + (cl_uint)pSIMDSurveyKernel->SIMD32ExecutionTimeNS ); + } + else + { + log( "SIMD Survey: NDRange: No samples for kernel " + kernelName + " (yet?)\n" ); + } + break; + } + } + else + { + logf( "SIMD Survey: NDRange: Executing warmup iteration %d of %d for kernel %s\n", + pSIMDSurveyKernel->ExecutionNumber + 1, + cWarmupIterations, + kernelName.c_str() ); + } + + pSIMDSurveyKernel->ExecutionNumber++; + } + else + { + logf( "SIMD Survey NDRange: Couldn't find info for kernel %p!?!?\n", + kernel ); + } + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +#define CHECK_RETURN_EXTENSION_FUNCTION(funcname) \ +{ \ + if( func_name == #funcname ) \ + { \ + if( dispatch() . funcname == NULL ) \ + { \ + void *func = NULL; \ + if( platform && \ + dispatch().clGetExtensionFunctionAddressForPlatform ) \ + { \ + func = dispatch().clGetExtensionFunctionAddressForPlatform( \ + platform, \ + #funcname ); \ + } else if( dispatch().clGetExtensionFunctionAddress ) \ + { \ + func = dispatch().clGetExtensionFunctionAddress(#funcname); \ + } \ + void** pfunc = (void**)( &m_Dispatch . funcname ); \ + *pfunc = func; \ + } \ + if( dispatch() . funcname ) \ + { \ + return (void*)( funcname ); \ + } \ + } \ +} + +/////////////////////////////////////////////////////////////////////////////// +// +void* CLIntercept::getExtensionFunctionAddress( + cl_platform_id platform, + const std::string& func_name ) const +{ + // KHR Extensions + + // cl_khr_gl_sharing + // Even though all of these functions except for clGetGLContextInfoKHR() + // are exported from the ICD DLL, still call CHECK_RETURN_EXTENSION_FUNCTION + // to handle the case where an intercepted DLL supports the extension but + // does not export the entry point. This will probably never happen in + // practice, but better safe than sorry. +#if defined(_WIN32) || defined(__linux__) + CHECK_RETURN_EXTENSION_FUNCTION( clCreateFromGLBuffer ); + CHECK_RETURN_EXTENSION_FUNCTION( clCreateFromGLTexture ); + CHECK_RETURN_EXTENSION_FUNCTION( clCreateFromGLTexture2D ); + CHECK_RETURN_EXTENSION_FUNCTION( clCreateFromGLTexture3D ); + CHECK_RETURN_EXTENSION_FUNCTION( clCreateFromGLRenderbuffer ); + CHECK_RETURN_EXTENSION_FUNCTION( clGetGLObjectInfo ); + CHECK_RETURN_EXTENSION_FUNCTION( clGetGLTextureInfo ); + CHECK_RETURN_EXTENSION_FUNCTION( clEnqueueAcquireGLObjects ); + CHECK_RETURN_EXTENSION_FUNCTION( clEnqueueReleaseGLObjects ); +#endif + CHECK_RETURN_EXTENSION_FUNCTION( clGetGLContextInfoKHR ); + // cl_khr_gl_event + CHECK_RETURN_EXTENSION_FUNCTION( clCreateEventFromGLsyncKHR ); +#if defined(_WIN32) + // cl_khr_d3d10_sharing + CHECK_RETURN_EXTENSION_FUNCTION( clGetDeviceIDsFromD3D10KHR ); + CHECK_RETURN_EXTENSION_FUNCTION( clCreateFromD3D10BufferKHR ); + CHECK_RETURN_EXTENSION_FUNCTION( clCreateFromD3D10Texture2DKHR ); + CHECK_RETURN_EXTENSION_FUNCTION( clCreateFromD3D10Texture3DKHR ); + CHECK_RETURN_EXTENSION_FUNCTION( clEnqueueAcquireD3D10ObjectsKHR ); + CHECK_RETURN_EXTENSION_FUNCTION( clEnqueueReleaseD3D10ObjectsKHR ); + // cl_khr_d3d11_sharing + CHECK_RETURN_EXTENSION_FUNCTION( clGetDeviceIDsFromD3D11KHR ); + CHECK_RETURN_EXTENSION_FUNCTION( clCreateFromD3D11BufferKHR ); + CHECK_RETURN_EXTENSION_FUNCTION( clCreateFromD3D11Texture2DKHR ); + CHECK_RETURN_EXTENSION_FUNCTION( clCreateFromD3D11Texture3DKHR ); + CHECK_RETURN_EXTENSION_FUNCTION( clEnqueueAcquireD3D11ObjectsKHR ); + CHECK_RETURN_EXTENSION_FUNCTION( clEnqueueReleaseD3D11ObjectsKHR ); + // cl_khr_dx9_media_sharing + CHECK_RETURN_EXTENSION_FUNCTION( clGetDeviceIDsFromDX9MediaAdapterKHR ); + CHECK_RETURN_EXTENSION_FUNCTION( clCreateFromDX9MediaSurfaceKHR ); + CHECK_RETURN_EXTENSION_FUNCTION( clEnqueueAcquireDX9MediaSurfacesKHR ); + CHECK_RETURN_EXTENSION_FUNCTION( clEnqueueReleaseDX9MediaSurfacesKHR ); +#endif + // cl_khr_il_program + CHECK_RETURN_EXTENSION_FUNCTION( clCreateProgramWithILKHR ); + // cl_khr_subgroups + CHECK_RETURN_EXTENSION_FUNCTION( clGetKernelSubGroupInfoKHR ); + // cl_khr_create_command_queue + CHECK_RETURN_EXTENSION_FUNCTION( clCreateCommandQueueWithPropertiesKHR ); + + // Intel Extensions + +#if defined(_WIN32) + // cl_intel_dx9_media_sharing + CHECK_RETURN_EXTENSION_FUNCTION( clGetDeviceIDsFromDX9INTEL ); + CHECK_RETURN_EXTENSION_FUNCTION( clCreateFromDX9MediaSurfaceINTEL ); + CHECK_RETURN_EXTENSION_FUNCTION( clEnqueueAcquireDX9ObjectsINTEL ); + CHECK_RETURN_EXTENSION_FUNCTION( clEnqueueReleaseDX9ObjectsINTEL ); +#endif + + // Unofficial MDAPI extension: + CHECK_RETURN_EXTENSION_FUNCTION( clCreatePerfCountersCommandQueueINTEL ); + CHECK_RETURN_EXTENSION_FUNCTION( clSetPerformanceConfigurationINTEL ); + + // cl_intel_accelerator + CHECK_RETURN_EXTENSION_FUNCTION( clCreateAcceleratorINTEL ); + CHECK_RETURN_EXTENSION_FUNCTION( clGetAcceleratorInfoINTEL ); + CHECK_RETURN_EXTENSION_FUNCTION( clRetainAcceleratorINTEL ); + CHECK_RETURN_EXTENSION_FUNCTION( clReleaseAcceleratorINTEL ); + + // cl_intel_va_api_media_sharing + CHECK_RETURN_EXTENSION_FUNCTION( clGetDeviceIDsFromVA_APIMediaAdapterINTEL ); + CHECK_RETURN_EXTENSION_FUNCTION( clCreateFromVA_APIMediaSurfaceINTEL ); + CHECK_RETURN_EXTENSION_FUNCTION( clEnqueueAcquireVA_APIMediaSurfacesINTEL ); + CHECK_RETURN_EXTENSION_FUNCTION( clEnqueueReleaseVA_APIMediaSurfacesINTEL ); + + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// This function assumes that CLIntercept already has entered its +// critical section. If it hasn't, bad things could happen. +void CLIntercept::log( const std::string& s ) +{ + std::string logString( m_Config.LogIndent, ' ' ); + logString += s; + if( m_Config.LogToFile ) + { + m_InterceptLog << logString; + m_InterceptLog.flush(); + } + if( m_Config.LogToDebugger ) + { + OS().OutputDebugString( logString ); + } + + if( ( m_Config.LogToFile == false ) && + ( m_Config.LogToDebugger == false ) ) + { + std::cerr << logString; + } +} +void CLIntercept::logf( const char* formatStr, ... ) +{ + va_list args; + va_start( args, formatStr ); + + char temp[ CLI_MAX_STRING_SIZE ] = ""; + int size = CLI_VSPRINTF( temp, CLI_MAX_STRING_SIZE, formatStr, args ); + if( size >= 0 && size < CLI_MAX_STRING_SIZE ) + { + log( std::string( temp ) ); + } + else + { + log( std::string( "too long" ) ); + } + + va_end( args ); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::logPlatformInfo( cl_platform_id platform ) +{ + cl_int errorCode = CL_SUCCESS; + + char* platformName = NULL; + char* platformVendor = NULL; + char* platformVersion = NULL; + char* platformProfile = NULL; + char* platformExtensions = NULL; + + errorCode |= allocateAndGetPlatformInfoString( + platform, + CL_PLATFORM_NAME, + platformName ); + errorCode |= allocateAndGetPlatformInfoString( + platform, + CL_PLATFORM_VENDOR, + platformVendor ); + errorCode |= allocateAndGetPlatformInfoString( + platform, + CL_PLATFORM_VERSION, + platformVersion ); + errorCode |= allocateAndGetPlatformInfoString( + platform, + CL_PLATFORM_PROFILE, + platformProfile ); + errorCode |= allocateAndGetPlatformInfoString( + platform, + CL_PLATFORM_EXTENSIONS, + platformExtensions ); + + if( errorCode == CL_SUCCESS ) + { + logf( "\tName: %s\n", platformName ); + logf( "\tVendor: %s\n", platformVendor ); + logf( "\tDriver Version: %s\n", platformVersion ); + logf( "\tProfile: %s\n", platformProfile ); + + int numberOfExtensions = 0; + logf( "\tExtensions:\n" ); + if( platformExtensions ) + { + char* extension = NULL; + char* nextExtension = NULL; + extension = CLI_STRTOK( platformExtensions, " ", &nextExtension ); + while( extension != NULL ) + { + numberOfExtensions++; + logf( "\t\t%s\n", extension ); + extension = CLI_STRTOK( NULL, " ", &nextExtension ); + } + } + logf( "\t\t%d Platform Extensions Found\n", numberOfExtensions ); + } + else + { + log( "\tError getting platform info!\n" ); + } + + delete [] platformName; + delete [] platformVendor; + delete [] platformVersion; + delete [] platformProfile; + delete [] platformExtensions; +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::logDeviceInfo( cl_device_id device ) +{ + cl_int errorCode = CL_SUCCESS; + + cl_device_type deviceType; + char* deviceName = NULL; + char* deviceVendor = NULL; + char* deviceVersion = NULL; + char* driverVersion = NULL; + char* deviceExtensions = NULL; + + errorCode |= dispatch().clGetDeviceInfo( + device, + CL_DEVICE_TYPE, + sizeof( deviceType ), + &deviceType, + NULL ); + errorCode |= allocateAndGetDeviceInfoString( + device, + CL_DEVICE_NAME, + deviceName ); + errorCode |= allocateAndGetDeviceInfoString( + device, + CL_DEVICE_VENDOR, + deviceVendor ); + errorCode |= allocateAndGetDeviceInfoString( + device, + CL_DEVICE_VERSION, + deviceVersion ); + errorCode |= allocateAndGetDeviceInfoString( + device, + CL_DRIVER_VERSION, + driverVersion ); + errorCode |= allocateAndGetDeviceInfoString( + device, + CL_DEVICE_EXTENSIONS, + deviceExtensions ); + + if( errorCode == CL_SUCCESS ) + { + logf( "\tName: %s\n", deviceName ); + logf( "\tVendor: %s\n", deviceVendor ); + logf( "\tVersion: %s\n", deviceVersion ); + logf( "\tDriver Version: %s\n", driverVersion ); + logf( "\tType: %s\n", enumName().name_device_type( deviceType ).c_str() ); + + int numberOfExtensions = 0; + logf( "\tExtensions:\n" ); + if( deviceExtensions ) + { + char* extension = NULL; + char* nextExtension = NULL; + extension = CLI_STRTOK( deviceExtensions, " ", &nextExtension ); + while( extension != NULL ) + { + numberOfExtensions++; + logf( "\t\t%s\n", extension ); + extension = CLI_STRTOK( NULL, " ", &nextExtension ); + } + } + logf( "\t\t%d Device Extensions Found\n", numberOfExtensions ); + } + else + { + log( "Error getting device info!\n" ); + } + + delete [] deviceName; + delete [] deviceVendor; + delete [] deviceVersion; + delete [] driverVersion; + delete [] deviceExtensions; +} + +/////////////////////////////////////////////////////////////////////////////// +// +#if defined(_WIN32) || defined(__linux__) +#define INIT_EXPORTED_FUNC(funcname) \ +{ \ + void* func = OS().GetFunctionPointer(m_OpenCLLibraryHandle, #funcname); \ + if (func == NULL) \ + { \ + log( std::string("Couldn't get exported function pointer to: ") + #funcname + "\n" );\ + success = false; \ + } \ + else \ + { \ + void** pfunc = (void**)( &m_Dispatch . funcname ); \ + *pfunc = func; \ + } \ +} +bool CLIntercept::initDispatch( const std::string& dllName ) +{ + bool success = true; + + if( success ) + { + m_OpenCLLibraryHandle = OS().LoadLibrary( dllName.c_str() ); + if( m_OpenCLLibraryHandle == NULL ) + { + log( std::string("Couldn't load library from: ") + dllName + "\n"); + success = false; + } + } + + if( success ) + { + INIT_EXPORTED_FUNC(clGetPlatformIDs); + INIT_EXPORTED_FUNC(clGetPlatformInfo); + INIT_EXPORTED_FUNC(clGetDeviceIDs); + INIT_EXPORTED_FUNC(clGetDeviceInfo); + INIT_EXPORTED_FUNC(clCreateContext); + INIT_EXPORTED_FUNC(clCreateContextFromType); + INIT_EXPORTED_FUNC(clRetainContext); + INIT_EXPORTED_FUNC(clReleaseContext); + INIT_EXPORTED_FUNC(clGetContextInfo); + INIT_EXPORTED_FUNC(clCreateCommandQueue); + INIT_EXPORTED_FUNC(clRetainCommandQueue); + INIT_EXPORTED_FUNC(clReleaseCommandQueue); + INIT_EXPORTED_FUNC(clGetCommandQueueInfo); + INIT_EXPORTED_FUNC(clSetCommandQueueProperty); + INIT_EXPORTED_FUNC(clCreateBuffer); + INIT_EXPORTED_FUNC(clCreateImage2D); + INIT_EXPORTED_FUNC(clCreateImage3D); + INIT_EXPORTED_FUNC(clRetainMemObject); + INIT_EXPORTED_FUNC(clReleaseMemObject); + INIT_EXPORTED_FUNC(clGetSupportedImageFormats); + INIT_EXPORTED_FUNC(clGetMemObjectInfo); + INIT_EXPORTED_FUNC(clGetImageInfo); + INIT_EXPORTED_FUNC(clCreateSampler); + INIT_EXPORTED_FUNC(clRetainSampler); + INIT_EXPORTED_FUNC(clReleaseSampler); + INIT_EXPORTED_FUNC(clGetSamplerInfo); + INIT_EXPORTED_FUNC(clCreateProgramWithSource); + INIT_EXPORTED_FUNC(clCreateProgramWithBinary); + INIT_EXPORTED_FUNC(clRetainProgram); + INIT_EXPORTED_FUNC(clReleaseProgram); + INIT_EXPORTED_FUNC(clBuildProgram); + INIT_EXPORTED_FUNC(clUnloadCompiler); + INIT_EXPORTED_FUNC(clGetProgramInfo); + INIT_EXPORTED_FUNC(clGetProgramBuildInfo); + INIT_EXPORTED_FUNC(clCreateKernel); + INIT_EXPORTED_FUNC(clCreateKernelsInProgram); + INIT_EXPORTED_FUNC(clRetainKernel); + INIT_EXPORTED_FUNC(clReleaseKernel); + INIT_EXPORTED_FUNC(clSetKernelArg); + INIT_EXPORTED_FUNC(clGetKernelInfo); + INIT_EXPORTED_FUNC(clGetKernelWorkGroupInfo); + INIT_EXPORTED_FUNC(clWaitForEvents); + INIT_EXPORTED_FUNC(clGetEventInfo); + INIT_EXPORTED_FUNC(clRetainEvent); + INIT_EXPORTED_FUNC(clReleaseEvent); + INIT_EXPORTED_FUNC(clGetEventProfilingInfo); + INIT_EXPORTED_FUNC(clFlush); + INIT_EXPORTED_FUNC(clFinish); + INIT_EXPORTED_FUNC(clEnqueueReadBuffer); + INIT_EXPORTED_FUNC(clEnqueueWriteBuffer); + INIT_EXPORTED_FUNC(clEnqueueCopyBuffer); + INIT_EXPORTED_FUNC(clEnqueueReadImage); + INIT_EXPORTED_FUNC(clEnqueueWriteImage); + INIT_EXPORTED_FUNC(clEnqueueCopyImage); + INIT_EXPORTED_FUNC(clEnqueueCopyImageToBuffer); + INIT_EXPORTED_FUNC(clEnqueueCopyBufferToImage); + INIT_EXPORTED_FUNC(clEnqueueMapBuffer); + INIT_EXPORTED_FUNC(clEnqueueMapImage); + INIT_EXPORTED_FUNC(clEnqueueUnmapMemObject); + INIT_EXPORTED_FUNC(clEnqueueNDRangeKernel); + INIT_EXPORTED_FUNC(clEnqueueTask); + INIT_EXPORTED_FUNC(clEnqueueNativeKernel); + INIT_EXPORTED_FUNC(clEnqueueMarker); + INIT_EXPORTED_FUNC(clEnqueueWaitForEvents); + INIT_EXPORTED_FUNC(clEnqueueBarrier); + + bool savedSuccess = success; + + // Optional features? + INIT_EXPORTED_FUNC(clGetExtensionFunctionAddress); + INIT_EXPORTED_FUNC(clGetExtensionFunctionAddressForPlatform); + + // OpenCL 1.1 Entry Points (optional) + INIT_EXPORTED_FUNC(clCreateSubBuffer); + INIT_EXPORTED_FUNC(clSetMemObjectDestructorCallback); + INIT_EXPORTED_FUNC(clCreateUserEvent); + INIT_EXPORTED_FUNC(clSetUserEventStatus); + INIT_EXPORTED_FUNC(clSetEventCallback); + INIT_EXPORTED_FUNC(clEnqueueReadBufferRect); + INIT_EXPORTED_FUNC(clEnqueueWriteBufferRect); + INIT_EXPORTED_FUNC(clEnqueueCopyBufferRect); + + // OpenCL 1.2 Entry Points (optional) + INIT_EXPORTED_FUNC(clCompileProgram); + INIT_EXPORTED_FUNC(clCreateFromGLTexture); + INIT_EXPORTED_FUNC(clCreateImage); + INIT_EXPORTED_FUNC(clCreateProgramWithBuiltInKernels); + INIT_EXPORTED_FUNC(clCreateSubDevices); + INIT_EXPORTED_FUNC(clEnqueueBarrierWithWaitList); + INIT_EXPORTED_FUNC(clEnqueueFillBuffer); + INIT_EXPORTED_FUNC(clEnqueueFillImage); + INIT_EXPORTED_FUNC(clEnqueueMarkerWithWaitList); + INIT_EXPORTED_FUNC(clEnqueueMigrateMemObjects); + INIT_EXPORTED_FUNC(clGetKernelArgInfo); + INIT_EXPORTED_FUNC(clLinkProgram); + INIT_EXPORTED_FUNC(clReleaseDevice); + INIT_EXPORTED_FUNC(clRetainDevice); + INIT_EXPORTED_FUNC(clUnloadPlatformCompiler); + + // OpenCL 2.0 Entry Points (optional) + INIT_EXPORTED_FUNC(clSVMAlloc); + INIT_EXPORTED_FUNC(clSVMFree); + INIT_EXPORTED_FUNC(clEnqueueSVMFree); + INIT_EXPORTED_FUNC(clEnqueueSVMMemcpy); + INIT_EXPORTED_FUNC(clEnqueueSVMMemFill); + INIT_EXPORTED_FUNC(clEnqueueSVMMap); + INIT_EXPORTED_FUNC(clEnqueueSVMUnmap); + INIT_EXPORTED_FUNC(clSetKernelArgSVMPointer); + INIT_EXPORTED_FUNC(clSetKernelExecInfo); + INIT_EXPORTED_FUNC(clCreatePipe); + INIT_EXPORTED_FUNC(clGetPipeInfo); + INIT_EXPORTED_FUNC(clCreateCommandQueueWithProperties); + INIT_EXPORTED_FUNC(clCreateSamplerWithProperties); + + // OpenCL 2.1 Entry Points (optional) + INIT_EXPORTED_FUNC(clSetDefaultDeviceCommandQueue); + INIT_EXPORTED_FUNC(clGetDeviceAndHostTimer); + INIT_EXPORTED_FUNC(clGetHostTimer); + INIT_EXPORTED_FUNC(clCreateProgramWithIL); + INIT_EXPORTED_FUNC(clCloneKernel); + INIT_EXPORTED_FUNC(clGetKernelSubGroupInfo); + INIT_EXPORTED_FUNC(clEnqueueSVMMigrateMem); + + // OpenCL 2.2 Entry Points (optional) + INIT_EXPORTED_FUNC(clSetProgramReleaseCallback); + INIT_EXPORTED_FUNC(clSetProgramSpecializationConstant); + + // CL-GL Entry Points (optional) + INIT_EXPORTED_FUNC(clCreateFromGLBuffer); + INIT_EXPORTED_FUNC(clCreateFromGLTexture); + INIT_EXPORTED_FUNC(clCreateFromGLTexture2D); + INIT_EXPORTED_FUNC(clCreateFromGLTexture3D); + INIT_EXPORTED_FUNC(clCreateFromGLRenderbuffer); + INIT_EXPORTED_FUNC(clGetGLObjectInfo); + INIT_EXPORTED_FUNC(clGetGLTextureInfo ); + INIT_EXPORTED_FUNC(clEnqueueAcquireGLObjects); + INIT_EXPORTED_FUNC(clEnqueueReleaseGLObjects); + + // Extensions (optional) + // Extensions get loaded into the dispatch table on the fly. + + success = savedSuccess; + } + + if( !success ) + { + if( m_OpenCLLibraryHandle != NULL ) + { + OS().UnloadLibrary( m_OpenCLLibraryHandle ); + } + } + + return success; +} +/////////////////////////////////////////////////////////////////////////////// +// +#elif defined(__APPLE__) +#define INIT_CL_FUNC(funcname) \ +{ \ + m_Dispatch . funcname = funcname; \ +} +bool CLIntercept::initDispatch( void ) +{ + INIT_CL_FUNC(clGetPlatformIDs); + INIT_CL_FUNC(clGetPlatformInfo); + INIT_CL_FUNC(clGetDeviceIDs); + INIT_CL_FUNC(clGetDeviceInfo); + INIT_CL_FUNC(clCreateContext); + INIT_CL_FUNC(clCreateContextFromType); + INIT_CL_FUNC(clRetainContext); + INIT_CL_FUNC(clReleaseContext); + INIT_CL_FUNC(clGetContextInfo); + INIT_CL_FUNC(clCreateCommandQueue); + INIT_CL_FUNC(clRetainCommandQueue); + INIT_CL_FUNC(clReleaseCommandQueue); + INIT_CL_FUNC(clGetCommandQueueInfo); + INIT_CL_FUNC(clSetCommandQueueProperty); + INIT_CL_FUNC(clCreateBuffer); + INIT_CL_FUNC(clCreateImage2D); + INIT_CL_FUNC(clCreateImage3D); + INIT_CL_FUNC(clRetainMemObject); + INIT_CL_FUNC(clReleaseMemObject); + INIT_CL_FUNC(clGetSupportedImageFormats); + INIT_CL_FUNC(clGetMemObjectInfo); + INIT_CL_FUNC(clGetImageInfo); + INIT_CL_FUNC(clCreateSampler); + INIT_CL_FUNC(clRetainSampler); + INIT_CL_FUNC(clReleaseSampler); + INIT_CL_FUNC(clGetSamplerInfo); + INIT_CL_FUNC(clCreateProgramWithSource); + INIT_CL_FUNC(clCreateProgramWithBinary); + INIT_CL_FUNC(clRetainProgram); + INIT_CL_FUNC(clReleaseProgram); + INIT_CL_FUNC(clBuildProgram); + INIT_CL_FUNC(clUnloadCompiler); + INIT_CL_FUNC(clGetProgramInfo); + INIT_CL_FUNC(clGetProgramBuildInfo); + INIT_CL_FUNC(clCreateKernel); + INIT_CL_FUNC(clCreateKernelsInProgram); + INIT_CL_FUNC(clRetainKernel); + INIT_CL_FUNC(clReleaseKernel); + INIT_CL_FUNC(clSetKernelArg); + INIT_CL_FUNC(clGetKernelInfo); + INIT_CL_FUNC(clGetKernelWorkGroupInfo); + INIT_CL_FUNC(clWaitForEvents); + INIT_CL_FUNC(clGetEventInfo); + INIT_CL_FUNC(clRetainEvent); + INIT_CL_FUNC(clReleaseEvent); + INIT_CL_FUNC(clGetEventProfilingInfo); + INIT_CL_FUNC(clFlush); + INIT_CL_FUNC(clFinish); + INIT_CL_FUNC(clEnqueueReadBuffer); + INIT_CL_FUNC(clEnqueueWriteBuffer); + INIT_CL_FUNC(clEnqueueCopyBuffer); + INIT_CL_FUNC(clEnqueueReadImage); + INIT_CL_FUNC(clEnqueueWriteImage); + INIT_CL_FUNC(clEnqueueCopyImage); + INIT_CL_FUNC(clEnqueueCopyImageToBuffer); + INIT_CL_FUNC(clEnqueueCopyBufferToImage); + INIT_CL_FUNC(clEnqueueMapBuffer); + INIT_CL_FUNC(clEnqueueMapImage); + INIT_CL_FUNC(clEnqueueUnmapMemObject); + INIT_CL_FUNC(clEnqueueNDRangeKernel); + INIT_CL_FUNC(clEnqueueTask); + INIT_CL_FUNC(clEnqueueNativeKernel); + INIT_CL_FUNC(clEnqueueMarker); + INIT_CL_FUNC(clEnqueueWaitForEvents); + INIT_CL_FUNC(clEnqueueBarrier); + + // Optional features? + INIT_CL_FUNC(clGetExtensionFunctionAddress); + INIT_CL_FUNC(clGetExtensionFunctionAddressForPlatform); + + // OpenCL 1.1 Entry Points (optional) + INIT_CL_FUNC(clCreateSubBuffer); + INIT_CL_FUNC(clSetMemObjectDestructorCallback); + INIT_CL_FUNC(clCreateUserEvent); + INIT_CL_FUNC(clSetUserEventStatus); + INIT_CL_FUNC(clSetEventCallback); + INIT_CL_FUNC(clEnqueueReadBufferRect); + INIT_CL_FUNC(clEnqueueWriteBufferRect); + INIT_CL_FUNC(clEnqueueCopyBufferRect); + + // OpenCL 1.2 Entry Points (optional) + INIT_CL_FUNC(clCompileProgram); + INIT_CL_FUNC(clCreateFromGLTexture); + INIT_CL_FUNC(clCreateImage); + INIT_CL_FUNC(clCreateProgramWithBuiltInKernels); + INIT_CL_FUNC(clCreateSubDevices); + INIT_CL_FUNC(clEnqueueBarrierWithWaitList); + INIT_CL_FUNC(clEnqueueFillBuffer); + INIT_CL_FUNC(clEnqueueFillImage); + INIT_CL_FUNC(clEnqueueMarkerWithWaitList); + INIT_CL_FUNC(clEnqueueMigrateMemObjects); + INIT_CL_FUNC(clGetKernelArgInfo); + INIT_CL_FUNC(clLinkProgram); + INIT_CL_FUNC(clReleaseDevice); + INIT_CL_FUNC(clRetainDevice); + INIT_CL_FUNC(clUnloadPlatformCompiler); + + // CL-GL Entry Points (optional) + INIT_CL_FUNC(clCreateFromGLBuffer); + INIT_CL_FUNC(clCreateFromGLTexture); // OpenCL 1.2 + INIT_CL_FUNC(clCreateFromGLTexture2D); + INIT_CL_FUNC(clCreateFromGLTexture3D); + INIT_CL_FUNC(clCreateFromGLRenderbuffer); + INIT_CL_FUNC(clGetGLObjectInfo); + INIT_CL_FUNC(clGetGLTextureInfo); + INIT_CL_FUNC(clEnqueueAcquireGLObjects); + INIT_CL_FUNC(clEnqueueReleaseGLObjects); + + // Extensions (optional) + // Extensions get loaded into the dispatch table on the fly. + + return true; +} +#else +#error Unknown OS! +#endif + +/////////////////////////////////////////////////////////////////////////////// +// +#if defined(USE_ITT) +void CLIntercept::ittInit() +{ + if( m_ITTInitialized == false ) + { + m_OS.EnterCriticalSection(); + + if( m_ITTInitialized == false ) + { + log( "Initializing ITT...\n" ); + + m_ITTInitialized = true; + + m_ITTDomain = __itt_domain_create( "com.intel.clintercept" ); + if( m_ITTDomain == NULL ) + { + log( "__itt_domain_create() returned NULL!\n" ); + } + + //m_ITTQueuedState = __ittx_task_state_create( m_ITTDomain, "QUEUED" ); + //m_ITTSubmittedState = __ittx_task_state_create( m_ITTDomain, "SUBMITTED" ); + //m_ITTExecutingState = __ittx_task_state_create( m_ITTDomain, "EXECUTING" ); + + //m_ITTQueueTrackGroup = __itt_track_group_create( + // __itt_string_handle_create("Queue tracks"), + // __itt_track_group_type_normal ); + //if( m_ITTQueueTrackGroup == NULL ) + //{ + // log( "__itt_track_group_create() returned NULL!\n" ); + //} + + log( "... done!\n" ); + } + + m_OS.LeaveCriticalSection(); + } +} + +void CLIntercept::ittCallLoggingEnter( + const std::string& functionName, + const cl_kernel kernel ) +{ + std::string str( functionName ); + if( kernel ) + { + m_OS.EnterCriticalSection(); + + const std::string& kernelName = m_KernelNameMap[ kernel ]; + str += "( "; + str += kernelName; + str += " )"; + + m_OS.LeaveCriticalSection(); + } + + __itt_string_handle* itt_string_handle = __itt_string_handle_create( str.c_str() ); + __itt_task_begin(m_ITTDomain, __itt_null, __itt_null, itt_string_handle); +} + +void CLIntercept::ittCallLoggingExit() +{ + __itt_task_end(m_ITTDomain); +} + +void CLIntercept::ittRegisterCommandQueue( + cl_command_queue queue, + bool supportsPerfCounters ) +{ + m_OS.EnterCriticalSection(); + + cl_int errorCode = CL_SUCCESS; + + cl_device_id device = NULL; + cl_device_type deviceType = 0; + cl_command_queue_properties properties = 0; + + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetCommandQueueInfo( + queue, + CL_QUEUE_DEVICE, + sizeof(device), + &device, + NULL); + } + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetDeviceInfo( + device, + CL_DEVICE_TYPE, + sizeof(deviceType), + &deviceType, + NULL ); + } + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetCommandQueueInfo( + queue, + CL_QUEUE_PROPERTIES, + sizeof(properties), + &properties, + NULL ); + } + + SITTQueueInfo* pITTQueueInfo = NULL; + if( errorCode == CL_SUCCESS ) + { + pITTQueueInfo = new SITTQueueInfo; + if( pITTQueueInfo == NULL ) + { + errorCode = CL_OUT_OF_HOST_MEMORY; + } + else + { + pITTQueueInfo->pIntercept = this; + pITTQueueInfo->SupportsPerfCounters = supportsPerfCounters; + + pITTQueueInfo->itt_track = NULL; + pITTQueueInfo->itt_clock_domain = NULL; + pITTQueueInfo->CPUReferenceTime = 0; + pITTQueueInfo->CLReferenceTime = 0; + } + } + + if( errorCode == CL_SUCCESS ) + { + std::string trackName = "OpenCL"; + + if( properties & CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE ) + { + trackName += " Out-Of-Order"; + } + else + { + trackName += " In-Order"; + } + + if( deviceType & CL_DEVICE_TYPE_CPU ) + { + trackName += " CPU"; + } + if( deviceType & CL_DEVICE_TYPE_GPU ) + { + trackName += " GPU"; + } + if( deviceType & CL_DEVICE_TYPE_ACCELERATOR ) + { + trackName += " ACCELERATOR"; + } + if( deviceType & CL_DEVICE_TYPE_CUSTOM ) + { + trackName += " CUSTOM"; + } + + trackName += " Queue, "; + + { + char str[CLI_MAX_STRING_SIZE] = ""; + CLI_SPRINTF( str, CLI_MAX_STRING_SIZE, "Handle = %p", queue ); + trackName = trackName + str; + } + + // Don't fail if the track cannot be created, it just means we + // won't be as detailed in our tracking. + //__itt_track* track = __itt_track_create( + // m_ITTQueueTrackGroup, + // __itt_string_handle_create(trackName.c_str()), + // __itt_track_type_queue ); + //if( track != NULL ) + //{ + // pITTQueueInfo->itt_track = track; + // + // __itt_set_track(track); + // + // __ittx_set_default_state( + // m_ITTDomain, + // m_ITTQueuedState ); + // + // __itt_set_track(NULL); + //} + + dispatch().clRetainCommandQueue( queue ); + + m_ITTQueueInfoMap[ queue ] = pITTQueueInfo; + } + + if( errorCode != CL_SUCCESS ) + { + delete pITTQueueInfo; + pITTQueueInfo = NULL; + } + + m_OS.LeaveCriticalSection(); +} + +void CLIntercept::ittReleaseCommandQueue( + cl_command_queue queue ) +{ + m_OS.EnterCriticalSection(); + + cl_int errorCode = CL_SUCCESS; + cl_uint refCount = 0; + + SITTQueueInfo* pITTQueueInfo = m_ITTQueueInfoMap[ queue ]; + if( pITTQueueInfo ) + { + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetCommandQueueInfo( + queue, + CL_QUEUE_REFERENCE_COUNT, + sizeof( refCount ), + &refCount, + NULL ); + } + + if( ( errorCode == CL_SUCCESS ) && + ( refCount == 1 ) ) + { + dispatch().clReleaseCommandQueue( queue ); + + // I guess we don't delete a track after we've created it? + // Or a clock domain? + + delete pITTQueueInfo; + pITTQueueInfo = NULL; + + m_ITTQueueInfoMap.erase( queue ); + } + } + + m_OS.LeaveCriticalSection(); +} + +void ITTAPI CLIntercept::ittClockInfoCallback( + __itt_clock_info* pClockInfo, + void* pData ) +{ + const SITTQueueInfo* pQueueInfo = (const SITTQueueInfo*)pData; + + uint64_t cpuTickDelta = + pQueueInfo->pIntercept->OS().GetTimer() - + pQueueInfo->CPUReferenceTime; + + uint64_t cpuDeltaNS = pQueueInfo->pIntercept->OS().TickToNS( cpuTickDelta ); + + pClockInfo->clock_base = pQueueInfo->CLReferenceTime + cpuDeltaNS; + pClockInfo->clock_freq = 1000000000; // NS +} + +void CLIntercept::ittTraceEvent( + const std::string& name, + cl_event event, + uint64_t queuedTime ) +{ + cl_int errorCode = CL_SUCCESS; + + cl_command_queue queue = NULL; + cl_command_type type = 0; + + cl_ulong commandQueued = 0; + cl_ulong commandSubmit = 0; + cl_ulong commandStart = 0; + cl_ulong commandEnd = 0; + + errorCode |= dispatch().clGetEventInfo( + event, + CL_EVENT_COMMAND_QUEUE, + sizeof( queue ), + &queue, + NULL ); + + errorCode |= dispatch().clGetEventInfo( + event, + CL_EVENT_COMMAND_TYPE, + sizeof(type), + &type, + NULL ); + + errorCode |= dispatch().clGetEventProfilingInfo( + event, + CL_PROFILING_COMMAND_QUEUED, + sizeof( commandQueued ), + &commandQueued, + NULL ); + errorCode |= dispatch().clGetEventProfilingInfo( + event, + CL_PROFILING_COMMAND_SUBMIT, + sizeof( commandSubmit ), + &commandSubmit, + NULL ); + errorCode |= dispatch().clGetEventProfilingInfo( + event, + CL_PROFILING_COMMAND_START, + sizeof( commandStart ), + &commandStart, + NULL ); + errorCode |= dispatch().clGetEventProfilingInfo( + event, + CL_PROFILING_COMMAND_END, + sizeof( commandEnd ), + &commandEnd, + NULL ); + + if( errorCode == CL_SUCCESS ) + { + // It's possible we don't have any ITT info for this queue. + SITTQueueInfo* pITTQueueInfo = m_ITTQueueInfoMap[ queue ]; + if( pITTQueueInfo != NULL ) + { + __itt_clock_domain* clockDomain = pITTQueueInfo->itt_clock_domain; + if( clockDomain == NULL ) + { + pITTQueueInfo->CPUReferenceTime = queuedTime; + pITTQueueInfo->CLReferenceTime = commandQueued; + + clockDomain = __itt_clock_domain_create( + ittClockInfoCallback, + pITTQueueInfo ); + if( clockDomain == NULL ) + { + log( "__itt_clock_domain_create() returned NULL!\n"); + } + + pITTQueueInfo->itt_clock_domain = clockDomain; + } + + __itt_track* track = pITTQueueInfo->itt_track; + uint64_t clockOffset = 0; + + if( commandQueued == 0 ) + { + clockOffset = queuedTime; + clockOffset -= pITTQueueInfo->CPUReferenceTime; + clockOffset = OS().TickToNS( clockOffset ); + } + + commandQueued += clockOffset; + commandSubmit += clockOffset; + commandStart += clockOffset; + commandEnd += clockOffset; + + __itt_set_track( track ); + + __itt_string_handle* nameHandle = __itt_string_handle_create( name.c_str() ); + __itt_id eventId = __itt_id_make( NULL, (uint64_t)event ); + + __itt_id_create_ex( m_ITTDomain, clockDomain, commandQueued, eventId ); + + if( config().ITTShowOnlyExecutingEvents ) + { + __itt_task_begin_overlapped_ex( m_ITTDomain, clockDomain, commandStart, eventId, __itt_null, nameHandle ); + //__ittx_task_set_state( m_ITTDomain, clockDomain, commandStart, eventId, m_ITTExecutingState ); + __itt_task_end_overlapped_ex( m_ITTDomain, clockDomain, commandEnd, eventId ); + } + else + { + __itt_task_begin_overlapped_ex( m_ITTDomain, clockDomain, commandQueued, eventId, __itt_null, nameHandle ); + //__ittx_task_set_state( m_ITTDomain, clockDomain, commandSubmit, eventId, m_ITTSubmittedState); + //__ittx_task_set_state( m_ITTDomain, clockDomain, commandStart, eventId, m_ITTExecutingState ); + __itt_task_end_overlapped_ex( m_ITTDomain, clockDomain, commandEnd, eventId ); + } + + if( pITTQueueInfo->SupportsPerfCounters ) + { + // TODO: This needs to be updated to use MDAPI. + CLI_ASSERT( 0 ); + } + + __itt_id_destroy_ex( m_ITTDomain, clockDomain, commandEnd, eventId ); + + __itt_set_track(NULL); + } + else + { + log( "ittTraceEvent(): no queue info\n" ); + } + } + else + { + log( "ittTraceEvent(): OpenCL error\n" ); + } +} + +#endif + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::chromeCallLoggingExit( + const std::string& functionName, + const cl_kernel kernel, + uint64_t tickStart, + uint64_t tickEnd ) +{ + std::string str; + str += functionName; + + if( kernel ) + { + const std::string& kernelName = m_KernelNameMap[ kernel ]; + str += "( "; + str += kernelName; + str += " )"; + } + + uint64_t processId = + OS().GetProcessID(); + uint64_t threadId = + OS().GetThreadID(); + + uint64_t usStart = + OS().TickToNS( tickStart - m_StartTime ) / 1000; + uint64_t usDelta = + OS().TickToNS( tickEnd - tickStart ) / 1000; + + m_InterceptTrace + << "{\"ph\":\"X\", \"pid\":" << processId + << ", \"tid\":" << threadId + << ", \"name\":\"" << str + << "\", \"ts\":" << usStart + << ", \"dur\":" << usDelta + << "},\n"; +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::chromeRegisterCommandQueue( + cl_command_queue queue ) +{ + m_OS.EnterCriticalSection(); + + cl_int errorCode = CL_SUCCESS; + + cl_device_id device = NULL; + cl_device_type deviceType = 0; + cl_command_queue_properties properties = 0; + + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetCommandQueueInfo( + queue, + CL_QUEUE_DEVICE, + sizeof(device), + &device, + NULL); + } + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetDeviceInfo( + device, + CL_DEVICE_TYPE, + sizeof(deviceType), + &deviceType, + NULL ); + } + if( errorCode == CL_SUCCESS ) + { + errorCode = dispatch().clGetCommandQueueInfo( + queue, + CL_QUEUE_PROPERTIES, + sizeof(properties), + &properties, + NULL ); + } + + if( errorCode == CL_SUCCESS ) + { + std::string trackName = "OpenCL"; + + if( properties & CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE ) + { + trackName += " Out-Of-Order"; + } + else + { + trackName += " In-Order"; + } + + if( deviceType & CL_DEVICE_TYPE_CPU ) + { + trackName += " CPU"; + } + if( deviceType & CL_DEVICE_TYPE_GPU ) + { + trackName += " GPU"; + } + if( deviceType & CL_DEVICE_TYPE_ACCELERATOR ) + { + trackName += " ACCELERATOR"; + } + if( deviceType & CL_DEVICE_TYPE_CUSTOM ) + { + trackName += " CUSTOM"; + } + + trackName += " Queue"; + + //{ + // char str[CLI_MAX_STRING_SIZE] = ""; + // CLI_SPRINTF( str, CLI_MAX_STRING_SIZE, ", Handle = %p", queue ); + // trackName = trackName + str; + //} + + uint64_t processId = OS().GetProcessID(); + m_InterceptTrace + << "{\"ph\":\"M\", \"name\":\"thread_name\", \"pid\":" << processId + << ", \"tid\":-" << (uintptr_t)queue + << ", \"args\":{\"name\":\"" << trackName + << "\"}},\n"; + } + + m_OS.LeaveCriticalSection(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void CLIntercept::chromeTraceEvent( + const std::string& name, + cl_event event, + uint64_t queuedTime ) +{ + cl_int errorCode = CL_SUCCESS; + + cl_command_queue queue = NULL; + cl_command_type type = 0; + + cl_ulong commandQueued = 0; + cl_ulong commandSubmit = 0; + cl_ulong commandStart = 0; + cl_ulong commandEnd = 0; + + errorCode |= dispatch().clGetEventInfo( + event, + CL_EVENT_COMMAND_QUEUE, + sizeof( queue ), + &queue, + NULL ); + + errorCode |= dispatch().clGetEventInfo( + event, + CL_EVENT_COMMAND_TYPE, + sizeof(type), + &type, + NULL ); + + errorCode |= dispatch().clGetEventProfilingInfo( + event, + CL_PROFILING_COMMAND_QUEUED, + sizeof( commandQueued ), + &commandQueued, + NULL ); + errorCode |= dispatch().clGetEventProfilingInfo( + event, + CL_PROFILING_COMMAND_SUBMIT, + sizeof( commandSubmit ), + &commandSubmit, + NULL ); + errorCode |= dispatch().clGetEventProfilingInfo( + event, + CL_PROFILING_COMMAND_START, + sizeof( commandStart ), + &commandStart, + NULL ); + errorCode |= dispatch().clGetEventProfilingInfo( + event, + CL_PROFILING_COMMAND_END, + sizeof( commandEnd ), + &commandEnd, + NULL ); + + if( errorCode == CL_SUCCESS ) + { + uint64_t normalizedQueuedTimeNS = + OS().TickToNS( queuedTime - m_StartTime ); + uint64_t normalizedStartTimeNS = + ( commandStart - commandQueued ) + normalizedQueuedTimeNS; + + uint64_t usStart = normalizedStartTimeNS / 1000; + uint64_t usDelta = ( commandEnd - commandStart ) / 1000; + + uint64_t processId = OS().GetProcessID(); + m_InterceptTrace + << "{\"ph\":\"X\", \"pid\":" << processId + << ", \"tid\":-" << (uintptr_t)queue + << ", \"name\":\"" << name + << "\", \"ts\":" << usStart + << ", \"dur\":" << usDelta + << "},\n"; + + } + else + { + log( "chromeTraceEvent(): OpenCL error\n" ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// +bool CLIntercept::checkAubCaptureKernelSignature( + const cl_kernel kernel, + cl_uint workDim, + const size_t* gws, + const size_t* lws ) +{ + m_OS.EnterCriticalSection(); + + bool match = true; + + // If the aubcapture kernel name is set, make sure it matches the name + // of the passed-in kernel: + + if( match && + m_Config.AubCaptureKernelName != "" && + m_KernelNameMap[ kernel ] != m_Config.AubCaptureKernelName ) + { + //logf( "Skipping aub capture: kernel name '%s' doesn't match the requested kernel name '%s'.\n", + // m_KernelNameMap[ kernel ].c_str(), + // m_Config.AubCaptureKernelName.c_str() ); + match = false; + } + + // If the aubcapture global work size is set, and it is not set to the + // wildcard ("*"), make sure it matches the passed-in global work size: + + if( match && + m_Config.AubCaptureKernelGWS != "" && + m_Config.AubCaptureKernelGWS != "*" ) + { + std::ostringstream ss; + if( gws ) + { + if( workDim >= 1 ) + { + ss << gws[0]; + } + if( workDim >= 2 ) + { + ss << "x" << gws[1]; + } + if( workDim >= 3 ) + { + ss << "x" << gws[2]; + } + } + else + { + ss << "NULL"; + } + if( m_Config.AubCaptureKernelGWS != ss.str() ) + { + //logf( "Skipping aub capture: global work size %s doesn't match the requested global work size %s.\n", + // ss.str(), + // m_Config.AubCaptureKernelGWS.c_str() ); + match = false; + } + } + + // If the aubcapture local work size is set, and it is not set to the + // wildcard ("*"), make sure it matches the passed-in local work size: + + if( match && + m_Config.AubCaptureKernelLWS != "" && + m_Config.AubCaptureKernelLWS != "*" ) + { + std::ostringstream ss; + if( lws ) + { + if( workDim >= 1 ) + { + ss << lws[0]; + } + if( workDim >= 2 ) + { + ss << "x" << lws[1]; + } + if( workDim >= 3 ) + { + ss << "x" << lws[2]; + } + } + else + { + ss << "NULL"; + } + if( m_Config.AubCaptureKernelLWS != ss.str() ) + { + //logf( "Skipping aub capture: local work size %s doesn't match the requested local work size %s.\n", + // ss.str(), + // m_Config.AubCaptureKernelLWS.c_str() ); + match = false; + } + } + + if( match && + m_Config.AubCaptureUniqueKernels ) + { + std::string key = m_KernelNameMap[ kernel ]; + + { + cl_program program = NULL; + dispatch().clGetKernelInfo( + kernel, + CL_KERNEL_PROGRAM, + sizeof(program), + &program, + NULL ); + if( program ) + { + unsigned int programNumber = m_ProgramNumberMap[ program ]; + uint64_t programHash = m_ProgramHashMap[ program ]; + unsigned int compileCount = m_ProgramNumberCompileCountMap[ programNumber ]; + + char hashString[256] = ""; + if( config().OmitProgramNumber ) + { + CLI_SPRINTF( hashString, 256, "(%08X_%04u)", + (unsigned int)programHash, + compileCount ); + } + else + { + CLI_SPRINTF( hashString, 256, "(%04u_%08X_%04u)", + programNumber, + (unsigned int)programHash, + compileCount ); + } + key += hashString; + } + } + + if( gws ) + { + std::ostringstream ss; + ss << " GWS[ "; + if( gws ) + { + if( workDim >= 1 ) + { + ss << gws[0]; + } + if( workDim >= 2 ) + { + ss << "x" << gws[1]; + } + if( workDim >= 3 ) + { + ss << "x" << gws[2]; + } + } + else + { + ss << "NULL"; + } + ss << " ]"; + key += ss.str(); + } + + { + std::ostringstream ss; + ss << " LWS[ "; + if( lws ) + { + if( workDim >= 1 ) + { + ss << lws[0]; + } + if( workDim >= 2 ) + { + ss << "x" << lws[1]; + } + if( workDim >= 3 ) + { + ss << "x" << lws[2]; + } + } + else + { + ss << "NULL"; + } + ss << " ]"; + key += ss.str(); + } + + if( m_AubCaptureSet.find( key ) == m_AubCaptureSet.end() ) + { + m_AubCaptureSet.insert( key ); + } + else + { + //logf( "Skipping aub capture: key %s was already captured.\n", + // key.c_str() ); + match = false; + } + } + + m_OS.LeaveCriticalSection(); + + return match; +} diff --git a/Src/intercept.h b/Src/intercept.h new file mode 100644 index 00000000..334054a6 --- /dev/null +++ b/Src/intercept.h @@ -0,0 +1,2109 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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. +*/ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "common.h" +#include "enummap.h" +#include "dispatch.h" + +#include "instrumentation.h" + +#if defined(USE_MDAPI) +#include "MetricsDiscoveryHelper.h" +#endif + +#if defined(_WIN32) + +#elif defined(__linux__) || defined(__APPLE__) + +#include +#define strcpy_s( _dst, _size, _src ) strncpy( _dst, _src, _size ) + +#else +#error Unknown OS! +#endif + +#include "OS/OS.h" + +class CLIntercept +{ + struct Config; + +public: + static bool Create( void* pGlobalData, CLIntercept*& pIntercept ); + static void Delete( CLIntercept*& pIntercept ); + + void report(); + + void callLoggingEnter( + const std::string& functionName, + const cl_kernel kernel ); + void callLoggingEnter( + const std::string& functionName, + const cl_kernel kernel, + const char* formatStr, + ... ); + + void callLoggingInfo( + const std::string& str ); + void callLoggingInfo( + const char* formatStr, + ... ); + + void callLoggingExit( + const std::string& functionName, + const cl_kernel kernel, + const cl_event* event ); + void callLoggingExit( + const std::string& functionName, + const cl_kernel kernel, + const cl_event* event, + const char* formatStr, + ... ); + + cl_int allocateAndGetPlatformInfoString( + cl_platform_id platform, + cl_platform_info param_name, + char*& param_value ) const; + cl_int allocateAndGetDeviceInfoString( + cl_device_id device, + cl_device_info param_name, + char*& param_value ) const; + + void getPlatformInfoString( + cl_platform_id platform, + std::string& str ) const; + void getDeviceInfoString( + cl_uint num_devices, + const cl_device_id* devices, + std::string& str ) const; + void getEventListString( + cl_uint num_events, + const cl_event* event_list, + std::string& str ) const; + void getContextPropertiesString( + const cl_context_properties* properties, + std::string& str ) const; + void getSamplerPropertiesString( + const cl_sampler_properties* properties, + std::string& str ) const; + void getCommandQueuePropertiesString( + const cl_queue_properties* properties, + std::string& str ) const; + void getCreateKernelsInProgramRetString( + cl_int retVal, + cl_kernel* kernels, + cl_uint* num_kernels_ret, + std::string& str ) const; + void getKernelArgString( + cl_uint arg_index, + size_t arg_size, + const void* arg_value, + std::string& str ) const; + void getEnqueueNDRangeKernelArgsString( + cl_uint work_dim, + const size_t* global_work_offset, + const size_t* global_work_size, + const size_t* local_work_size, + std::string& str ) const; + void getCreateSubBufferArgsString( + cl_buffer_create_type createType, + const void *createInfo, + std::string& str ) const; + + void logCLInfo(); + void logBuild( + uint64_t buildTimeStart, + const cl_program program, + cl_uint num_devices, + const cl_device_id* device_list ); + void logError( + const std::string& functionName, + cl_int errorCode ); + void logFlushOrFinishAfterEnqueueStart( + const std::string& flushOrFinish, + const std::string& functionName ); + void logFlushOrFinishAfterEnqueueEnd( + const std::string& flushOrFinish, + const std::string& functionName, + cl_int errorCode ); + void logPreferredWorkGroupSizeMultiple( + const cl_kernel* kernels, + cl_uint numKernels ); + + void logCL_GLTextureDetails( cl_mem image, cl_GLenum target, cl_GLint miplevel, cl_GLuint texture ); + + struct SContextCallbackInfo + { + CLIntercept* pIntercept; + void (CL_CALLBACK* pApplicationCallback)( const char*, const void*, size_t, void* ); + void* pUserData; + }; + static void CL_CALLBACK contextCallbackCaller( + const char*, + const void*, + size_t, + void* ); + void contextCallback( + const std::string& errinfo, + const void* private_info, + size_t cb ); + void contextCallbackOverrideInit( + const cl_context_properties* properties, + void (CL_CALLBACK*& pCallback)( const char*, const void*, size_t, void* ), + void*& pUserData, + SContextCallbackInfo*& pContextCallbackInfo, + cl_context_properties*& pLocalContextProperties ); + void contextCallbackOverrideCleanup( + const cl_context context, + SContextCallbackInfo*& pContextCallbackInfo, + cl_context_properties*& pLocalContextProperties ); + + struct SEventCallbackInfo + { + CLIntercept* pIntercept; + void (CL_CALLBACK* pApplicationCallback)( cl_event, cl_int, void* ); + void* pUserData; + }; + static void CL_CALLBACK eventCallbackCaller( + cl_event, + cl_int, + void* ); + void eventCallback( + cl_event event, + cl_int status ); + + void incrementEnqueueCounter(); + uint64_t getEnqueueCounter(); + + void overrideNullLocalWorkSize( + const cl_uint work_dim, + const size_t* global_work_size, + const size_t*& local_work_size ); + + void combineProgramStrings( + cl_uint& count, + const char**& strings, + const size_t*& lengths, + char*& singleString ) const; + + void incrementProgramCompileCount( + const cl_program program ); + uint64_t hashString( + const char* singleString, + size_t length ); + void saveProgramHash( + const cl_program program, + uint64_t hash ); + + bool injectProgramSource( + const uint64_t hash, + cl_uint& count, + const char**& strings, + const size_t*& lengths, + char*& singleString ); + bool prependProgramSource( + const uint64_t hash, + cl_uint& count, + const char**& strings, + const size_t*& lengths, + char*& singleString ); + bool injectProgramSPIRV( + const uint64_t hash, + size_t& length, + const void*& il, + char*& injectedIL ); + bool injectProgramOptions( + const cl_program program, + const char*& options, + char*& newOptions ); + bool appendBuildOptions( + const char*& options, + char*& newOptions ); + void dumpProgramSourceScript( + const cl_program program, + const char* singleString ); + void dumpProgramSource( + uint64_t hash, + const cl_program program, + const char* singleString ); + void dumpInputProgramBinaries( + uint64_t hash, + const cl_program program, + cl_uint num_devices, + const cl_device_id* device_list, + const size_t* lengths, + const unsigned char** binaries ); + void dumpProgramSPIRV( + uint64_t hash, + const cl_program program, + const size_t length, + const void* il ); + void dumpProgramOptionsScript( + const cl_program program, + const char* options ); + void dumpProgramOptions( + const cl_program program, + const char* options ); + void dumpProgramBuildLog( + const cl_program program, + const cl_device_id device, + const char* buildLog, + const size_t buildLogSize ); + + cl_program createProgramWithInjectionBinaries( + uint64_t hash, + cl_context context, + cl_int* errcode_ret ); + void dumpProgramBinary( + const cl_program program ); + + cl_program createProgramWithInjectionSPIRV( + uint64_t hash, + cl_context context, + cl_int* errcode_ret ); + void autoCreateSPIRV( + const cl_program program, + const char* options ); + + void updateHostTimingStats( + const std::string& functionName , + const cl_kernel kernel, + uint64_t start, + uint64_t end ); + + void modifyCommandQueueProperties( + cl_command_queue_properties& props ) const; + void createCommandQueueOverrideInit( + const cl_queue_properties* properties, + cl_queue_properties*& pLocalQueueProperties ) const; + void createCommandQueueOverrideCleanup( + cl_queue_properties*& pLocalQueueProperties ) const; + void addTimingEvent( + const std::string& functionName, + const uint64_t queuedTime, + const cl_kernel kernel, + const cl_uint workDim, + const size_t* gws, + const size_t* lws, + cl_event event ); + void checkTimingEvents(); + + void addKernelName( + const cl_kernel kernel, + const std::string& kernelName ); + + void addKernelNames( + cl_kernel* kernels, + cl_uint numKernels ); + + void removeKernel( + const cl_kernel kernel ); + + void addBuffer( + cl_mem buffer ); + void addSampler( + cl_sampler sampler, + const std::string& str ); + void removeSampler( + cl_sampler sampler ); + bool getSampler( + size_t size, + const void *arg_value, + std::string& str ) const; + void addImage( + cl_mem image ); + void removeMemObj( + cl_mem memobj ); + void addSVMAllocation( + void* svmPtr, + size_t size ); + void removeSVMAllocation( + void* svmPtr ); + void setKernelArg( + cl_kernel kernel, + cl_uint arg_index, + cl_mem memobj ); + void setKernelArgSVMPointer( + cl_kernel kernel, + cl_uint arg_index, + const void* arg ); + void dumpBuffersForKernel( + const std::string& name, + cl_kernel kernel, + cl_command_queue command_queue ); + void dumpImagesForKernel( + const std::string& name, + cl_kernel kernel, + cl_command_queue command_queue ); + void dumpBuffer( + const std::string& name, + cl_mem memobj, + cl_command_queue command_queue, + void* ptr, + size_t offset, + size_t size ); + void dumpArgument( + cl_kernel kernel, + cl_int arg_index, + size_t size, + const void *pBuffer ); + + void startAubCapture( + const std::string& functionName, + const cl_kernel kernel, + const cl_uint workDim, + const size_t* gws, + const size_t* lws, + cl_command_queue commandQueue ); + void stopAubCapture( + cl_command_queue commandQueue ); + + void initPrecompiledKernelOverrides( + const cl_context context ); + void initBuiltinKernelOverrides( + const cl_context context ); + + cl_int writeStringToMemory( + size_t param_value_size, + const std::string& param, + size_t* param_value_size_ret, + char* pointer ) const; + template< class T > + cl_int writeParamToMemory( + size_t param_value_size, + T param, + size_t* param_value_size_ret, + T* pointer ) const; + + bool overrideGetPlatformInfo( + cl_platform_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret, + cl_int& errorCode ); + bool overrideGetDeviceInfo( + cl_device_id device, + cl_platform_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret, + cl_int& errorCode ); + + cl_int ReadBuffer( + cl_command_queue commandQueue, + cl_mem srcBuffer, + cl_bool blockingRead, + size_t srcOffset, + size_t bytesToRead, + void* dstPtr, + cl_uint numEventsInWaitList, + const cl_event* eventWaitList, + cl_event* event ); + cl_int WriteBuffer( + cl_command_queue commandQueue, + cl_mem dstBuffer, + cl_bool blockingWrite, + size_t dstOffset, + size_t bytesToWrite, + const void* srcPtr, + cl_uint numEventsInWaitList, + const cl_event* eventWaitList, + cl_event* event ); + cl_int CopyBuffer( + cl_command_queue commandQueue, + cl_mem srcBuffer, + cl_mem dstBuffer, + size_t srcOffset, + size_t dstOffset, + size_t bytesToCopy, + cl_uint numEventsInWaitList, + const cl_event* eventWaitList, + cl_event* event ); + cl_int CopyBufferHelper( + cl_context context, + cl_command_queue commandQueue, + cl_mem srcBuffer, + cl_mem dstBuffer, + size_t srcOffset, + size_t dstOffset, + size_t bytesToCopy, + cl_uint numEventsInWaitList, + const cl_event* eventWaitList, + cl_event* event ); + + cl_int ReadImage( + cl_command_queue commandQueue, + cl_mem srcImage, + cl_bool blockingRead, + const size_t* srcOrigin, + const size_t* region, + size_t dstRowPitch, + size_t dstSlicePitch, + void* dstPtr, + cl_uint numEventsInWaitList, + const cl_event* eventWaitList, + cl_event* event ); + cl_int WriteImage( + cl_command_queue commandQueue, + cl_mem dstImage, + cl_bool blockingWrite, + const size_t* dstOrigin, + const size_t* region, + size_t srcRowPitch, + size_t srcSlicePitch, + const void* srcPtr, + cl_uint numEventsInWaitList, + const cl_event* eventWaitList, + cl_event* event ); + cl_int CopyImage( + cl_command_queue commandQueue, + cl_mem srcImage, + cl_mem dstImage, + const size_t* srcOrigin, + const size_t* dstOrigin, + const size_t* region, + cl_uint numEventsInWaitList, + const cl_event* eventWaitList, + cl_event* event ); + cl_int CopyImageHelper( + cl_context context, + cl_command_queue commandQueue, + cl_mem srcImage, + cl_mem dstImage, + const size_t* srcOrigin, + const size_t* dstOrigin, + const size_t* region, + cl_uint numEventsInWaitList, + const cl_event* eventWaitList, + cl_event* event ); + + cl_program createProgramWithBuiltinKernels( + cl_context context ); + cl_kernel createBuiltinKernel( + cl_program program, + const std::string& kernel_name, + cl_int* errcode_ret ); + cl_int NDRangeBuiltinKernel( + cl_command_queue commandQueue, + 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 ); + + void SIMDSurveyCreateProgramFromSource( + const cl_program program, + cl_context context, + cl_uint count, + const char** strings, + const size_t* lengths ); + // TODO? + // SIMDSurveyCreateProgramWithBinary + // SIMDSurveyCreateProgramWithIL + void SIMDSurveyBuildProgram( + const cl_program program, + cl_uint numDevices, + const cl_device_id* deviceList, + const char* options ); + void SIMDSurveyCreateKernel( + const cl_program program, + const cl_kernel kernel, + const std::string& kernelName ); + // TODO? + // SIMDSurveyCreateKernelsInProgram(); + // SIMDSurveyCloneKernel(); + void SIMDSurveySetKernelArg( + cl_kernel kernel, + cl_uint argIndex, + size_t argSize, + const void* argValue ); + void SIMDSurveyNDRangeKernel( + cl_kernel& kernel ); + + void* getExtensionFunctionAddress( + cl_platform_id platform, + const std::string& func_name ) const; + +#if defined(USE_MDAPI) + GTDI_CONFIGURATION_SET initCustomPerfCounters( + const std::string& setName ); + cl_command_queue createMDAPICommandQueue( + cl_context context, + cl_device_id device, + cl_command_queue_properties properties, + cl_int* errcode_ret ); + cl_command_queue createMDAPICommandQueue( + cl_context context, + cl_device_id device, + const cl_queue_properties* properties, + cl_int* errcode_ret ); +#endif + + const OS::Services& OS() const; + const CLdispatch& dispatch() const; + const CEnumNameMap& enumName() const; + + const Config& config() const; + + bool callLogging() const; + + bool nullEnqueue() const; + + bool dumpBufferForKernel( const cl_kernel kernel ); + bool dumpImagesForKernel( const cl_kernel kernel ); + bool checkDumpBufferEnqueueLimits() const; + bool checkDumpImageEnqueueLimits() const; + + bool checkAubCaptureEnqueueLimits() const; + bool checkAubCaptureKernelSignature( + const cl_kernel kernel, + cl_uint workDim, + const size_t* gws, + const size_t* lws); + + void saveProgramNumber( const cl_program program ); + unsigned int getProgramNumber() const; + + cl_device_type filterDeviceType( cl_device_type device_type ) const; + +#if defined(USE_ITT) + __itt_domain* ittDomain() const; + + void ittInit(); + + void ittCallLoggingEnter( + const std::string& functionName, + const cl_kernel kernel ); + void ittCallLoggingExit(); + + void ittRegisterCommandQueue( + cl_command_queue queue, + bool supportsPerfCounters ); + void ittReleaseCommandQueue( + cl_command_queue ); + void ittTraceEvent( + const std::string& name, + cl_event event, + uint64_t queuedTime ); +#endif + + void chromeCallLoggingExit( + const std::string& functionName, + const cl_kernel kernel, + uint64_t start, + uint64_t end ); + void chromeRegisterCommandQueue( + cl_command_queue queue ); + void chromeTraceEvent( + const std::string& name, + cl_event event, + uint64_t queuedTime ); + +private: + static const char* sc_URL; + static const char* sc_DumpDirectoryName; + static const char* sc_ReportFileName; + static const char* sc_LogFileName; + static const char* sc_TraceFileName; + static const char* sc_DumpPerfCountersFileNamePrefix; + +#if defined(CLINTERCEPT_CMAKE) + static const char* sc_GitDescribe; + static const char* sc_GitRefSpec; + static const char* sc_GitHash; +#endif + + CLIntercept( void* pGlobalData ); + ~CLIntercept(); + + bool init(); + void log(const std::string& s); + void logf(const char* str, ...); + + void logPlatformInfo( cl_platform_id platform ); + void logDeviceInfo( cl_device_id device ); + +#if defined(_WIN32) || defined(__linux__) + bool initDispatch( const std::string& dllName ); +#elif defined(__APPLE__) + bool initDispatch( void ); +#else +#error Unknown OS! +#endif + + void getCallLoggingPrefix( + std::string& str ); + + OS::Services m_OS; + CLdispatch m_Dispatch; + CEnumNameMap m_EnumNameMap; + + void* m_OpenCLLibraryHandle; + + std::ofstream m_InterceptLog; + std::ofstream m_InterceptTrace; + + bool m_LoggedCLInfo; + + uint64_t m_EnqueueCounter; + uint64_t m_StartTime; + + typedef std::map< uint64_t, unsigned int> CThreadNumberMap; + CThreadNumberMap m_ThreadNumberMap; + + unsigned int m_ProgramNumber; + + typedef std::map< const cl_program, unsigned int > CProgramNumberMap; + CProgramNumberMap m_ProgramNumberMap; + + typedef std::map< const cl_program, uint64_t > CProgramHashMap; + CProgramHashMap m_ProgramHashMap; + + typedef std::map< unsigned int, unsigned int > CProgramNumberCompileCountMap; + CProgramNumberCompileCountMap m_ProgramNumberCompileCountMap; + + struct SCpuTimingStats + { + uint64_t NumberOfCalls; + uint64_t MinTicks; + uint64_t MaxTicks; + uint64_t TotalTicks; + }; + + typedef std::map< std::string, SCpuTimingStats* > CCpuTimingStatsMap; + CCpuTimingStatsMap m_CpuTimingStatsMap; + + struct SDeviceTimingStats + { + uint64_t NumberOfCalls; + cl_ulong MinNS; + cl_ulong MaxNS; + cl_ulong TotalNS; + }; + + typedef std::map< std::string, SDeviceTimingStats* > CDeviceTimingStatsMap; + CDeviceTimingStatsMap m_DeviceTimingStatsMap; + + typedef std::map< const cl_kernel, std::string > CKernelNameMap; + CKernelNameMap m_KernelNameMap; + + struct SEventListNode + { + std::string FunctionName; + std::string KernelName; + uint64_t QueuedTime; + cl_kernel Kernel; + cl_event Event; + }; + + typedef std::list< SEventListNode* > CEventList; + CEventList m_EventList; + +#if defined(USE_MDAPI) + TimingProfile m_DeviceTimingProfile; + + typedef std::pair< std::string, char* > CMDDataEntry; + typedef std::queue< CMDDataEntry* > CMDDataList; + CMDDataList m_MDDataList; + + void saveMDAPICounters( + const std::string& name, + const cl_event event ); + void reportMDAPICounters( + std::ofstream& os ); +#endif + + unsigned int m_MemAllocNumber; + + typedef std::map< const void*, unsigned int > CMemAllocNumberMap; + CMemAllocNumberMap m_MemAllocNumberMap; + + typedef std::map< cl_sampler, std::string > CSamplerDataMap; + CSamplerDataMap m_SamplerDataMap; + + typedef std::map< const cl_mem, size_t > CBufferInfoMap; + CBufferInfoMap m_BufferInfoMap; + + typedef std::map< const void*, size_t > CSVMAllocInfoMap; + CSVMAllocInfoMap m_SVMAllocInfoMap; + + struct SImageInfo + { + size_t Region[3]; + size_t ElementSize; + }; + + typedef std::map< const cl_mem, SImageInfo > CImageInfoMap; + CImageInfoMap m_ImageInfoMap; + + typedef std::map< cl_uint, const void* > CKernelArgMemMap; + typedef std::map< const cl_kernel, CKernelArgMemMap > CKernelArgMap; + CKernelArgMap m_KernelArgMap; + + bool m_AubCaptureStarted; + cl_uint m_AubCaptureKernelEnqueueSkipCounter; + cl_uint m_AubCaptureKernelEnqueueCaptureCounter; + + typedef std::set CAubCaptureSet; + CAubCaptureSet m_AubCaptureSet; + + typedef std::map< const cl_context, SContextCallbackInfo* > CContextCallbackInfoMap; + CContextCallbackInfoMap m_ContextCallbackInfoMap; + + typedef std::map< const cl_event, SEventCallbackInfo* > CEventCallbackInfoMap; + CEventCallbackInfoMap m_EventCallbackInfoMap; + + struct SPrecompiledKernelOverrides + { + cl_program Program; + + cl_kernel Kernel_CopyBufferBytes; + cl_kernel Kernel_CopyBufferUInts; + cl_kernel Kernel_CopyBufferUInt4s; + cl_kernel Kernel_CopyBufferUInt16s; + + cl_kernel Kernel_CopyImage2Dto2DFloat; + cl_kernel Kernel_CopyImage2Dto2DInt; + cl_kernel Kernel_CopyImage2Dto2DUInt; + }; + + typedef std::map< const cl_context, SPrecompiledKernelOverrides* > CPrecompiledKernelOverridesMap; + CPrecompiledKernelOverridesMap m_PrecompiledKernelOverridesMap; + + struct SBuiltinKernelOverrides + { + cl_program Program; + + cl_kernel Kernel_block_motion_estimate_intel; + }; + + typedef std::map< const cl_context, SBuiltinKernelOverrides* > CBuiltinKernelOverridesMap; + CBuiltinKernelOverridesMap m_BuiltinKernelOverridesMap; + + struct SSIMDSurveyProgram + { + cl_program SIMD8Program; + cl_program SIMD16Program; + cl_program SIMD32Program; + }; + struct SSIMDSurveyKernel + { + cl_kernel SIMD8Kernel; + cl_kernel SIMD16Kernel; + cl_kernel SIMD32Kernel; + + cl_ulong SIMD8ExecutionTimeNS; + cl_ulong SIMD16ExecutionTimeNS; + cl_ulong SIMD32ExecutionTimeNS; + + uint32_t ExecutionNumber; + }; + + typedef std::map< const cl_program, SSIMDSurveyProgram* > CSIMDSurveyProgramMap; + CSIMDSurveyProgramMap m_SIMDSurveyProgramMap; + + typedef std::map< const cl_kernel, SSIMDSurveyKernel* > CSIMDSurveyKernelMap; + CSIMDSurveyKernelMap m_SIMDSurveyKernelMap; + + struct Config + { +#define CLI_CONTROL( _type, _name, _init, _desc ) _type _name; +#include "controls.h" +#undef CLI_CONTROL + } m_Config; + +#if defined(USE_ITT) + bool m_ITTInitialized; + + __itt_domain* m_ITTDomain; + + //__ittx_task_state* m_ITTQueuedState; + //__ittx_task_state* m_ITTSubmittedState; + //__ittx_task_state* m_ITTExecutingState; + + //__itt_track_group* m_ITTQueueTrackGroup; + + static void ITTAPI ittClockInfoCallback( + __itt_clock_info* pClockInfo, + void* pData ); + + struct SITTQueueInfo + { + const CLIntercept* pIntercept; + bool SupportsPerfCounters; + + __itt_track* itt_track; + __itt_clock_domain* itt_clock_domain; + uint64_t CPUReferenceTime; + cl_ulong CLReferenceTime; + }; + + typedef std::map< cl_command_queue, SITTQueueInfo* > CITTQueueInfoMap; + CITTQueueInfoMap m_ITTQueueInfoMap; +#endif + + DISALLOW_COPY_AND_ASSIGN( CLIntercept ); +}; + +/////////////////////////////////////////////////////////////////////////////// +// +inline const CLdispatch& CLIntercept::dispatch() const +{ + return m_Dispatch; +} + +/////////////////////////////////////////////////////////////////////////////// +// +inline const OS::Services& CLIntercept::OS() const +{ + return m_OS; +} + +/////////////////////////////////////////////////////////////////////////////// +// +inline const CEnumNameMap& CLIntercept::enumName() const +{ + return m_EnumNameMap; +} + +/////////////////////////////////////////////////////////////////////////////// +// +inline const CLIntercept::Config& CLIntercept::config() const +{ + return m_Config; +} + +/////////////////////////////////////////////////////////////////////////////// +// +#define LOG_CLINFO() \ + if( pIntercept->config().CLInfoLogging ) \ + { \ + pIntercept->logCLInfo(); \ + } + +/////////////////////////////////////////////////////////////////////////////// +// +#define BUILD_LOGGING_INIT() \ + uint64_t buildTimeStart = 0; \ + if( pIntercept->config().BuildLogging ) \ + { \ + buildTimeStart = pIntercept->OS().GetTimer(); \ + } + +#define BUILD_LOGGING( program, num_devices, device_list ) \ + if( pIntercept->config().BuildLogging ) \ + { \ + pIntercept->logBuild( \ + buildTimeStart, \ + program, \ + num_devices, \ + device_list ); \ + } + +/////////////////////////////////////////////////////////////////////////////// +// +inline bool CLIntercept::callLogging() const +{ + return m_Config.CallLogging; +} + +#define CALL_LOGGING_ENTER(...) \ + if( pIntercept->callLogging() ) \ + { \ + pIntercept->callLoggingEnter( __FUNCTION__, NULL, ##__VA_ARGS__ ); \ + } \ + ITT_CALL_LOGGING_ENTER( NULL ); + +#define CALL_LOGGING_ENTER_KERNEL(kernel, ...) \ + if( pIntercept->callLogging() ) \ + { \ + pIntercept->callLoggingEnter( __FUNCTION__, kernel, ##__VA_ARGS__ );\ + } \ + ITT_CALL_LOGGING_ENTER( kernel ); + +#define CALL_LOGGING_INFO(...) \ + if( pIntercept->callLogging() ) \ + { \ + pIntercept->callLoggingInfo( __VA_ARGS__ ); \ + } \ + +#define CALL_LOGGING_EXIT(...) \ + if( pIntercept->callLogging() ) \ + { \ + pIntercept->callLoggingExit( __FUNCTION__, NULL, NULL, ##__VA_ARGS__ ); \ + } \ + ITT_CALL_LOGGING_EXIT(); + +#define CALL_LOGGING_EXIT_EVENT(event, ...) \ + if( pIntercept->callLogging() ) \ + { \ + pIntercept->callLoggingExit( __FUNCTION__, NULL, event, ##__VA_ARGS__ );\ + } \ + ITT_CALL_LOGGING_EXIT(); + +/////////////////////////////////////////////////////////////////////////////// +// +#define CHECK_ERROR_INIT( pErrorCode ) \ + cl_int localErrorCode = CL_SUCCESS; \ + if( ( pIntercept->config().ErrorLogging || \ + pIntercept->config().ErrorAssert || \ + pIntercept->config().NoErrors ) && \ + ( pErrorCode == NULL ) ) \ + { \ + pErrorCode = &localErrorCode; \ + } + +#define CHECK_ERROR( errorCode ) \ + if( ( pIntercept->config().ErrorLogging || \ + pIntercept->config().ErrorAssert || \ + pIntercept->config().NoErrors ) && \ + ( errorCode != CL_SUCCESS ) ) \ + { \ + if( pIntercept->config().ErrorLogging ) \ + { \ + pIntercept->logError( __FUNCTION__, errorCode ); \ + } \ + if( pIntercept->config().ErrorAssert ) \ + { \ + CLI_DEBUG_BREAK(); \ + } \ + if( pIntercept->config().NoErrors ) \ + { \ + errorCode = CL_SUCCESS; \ + } \ + } + +/////////////////////////////////////////////////////////////////////////////// +// +#define CREATE_CONTEXT_OVERRIDE_INIT( _props, _func, _data, _newprops ) \ + CLIntercept::SContextCallbackInfo* pContextCallbackInfo = NULL; \ + if( pIntercept->config().ContextCallbackLogging ) \ + { \ + pIntercept->contextCallbackOverrideInit( \ + _props, \ + _func, \ + _data, \ + pContextCallbackInfo, \ + _newprops ); \ + } + +#define CREATE_CONTEXT_OVERRIDE_CLEANUP( _context, _newprops ) \ + if( pIntercept->config().ContextCallbackLogging ) \ + { \ + pIntercept->contextCallbackOverrideCleanup( \ + _context, \ + pContextCallbackInfo, \ + _newprops ); \ + } + +/////////////////////////////////////////////////////////////////////////////// +// +#define EVENT_CALLBACK_OVERRIDE_INIT( _func, _data ) \ + CLIntercept::SEventCallbackInfo* pEventCallbackInfo = NULL; \ + if( pIntercept->config().EventCallbackLogging ) \ + { \ + pEventCallbackInfo = new CLIntercept::SEventCallbackInfo; \ + if( pEventCallbackInfo ) \ + { \ + pEventCallbackInfo->pIntercept = pIntercept; \ + pEventCallbackInfo->pApplicationCallback = _func; \ + pEventCallbackInfo->pUserData = _data; \ + \ + _func = CLIntercept::eventCallbackCaller; \ + _data = pEventCallbackInfo; \ + } \ + } + +#define EVENT_CALLBACK_OVERRIDE_CLEANUP( _errCode ) \ + if( pIntercept->config().EventCallbackLogging && \ + _errCode != CL_SUCCESS ) \ + { \ + delete pEventCallbackInfo; \ + pEventCallbackInfo = NULL; \ + } + +/////////////////////////////////////////////////////////////////////////////// +// +#define FINISH_OR_FLUSH_AFTER_ENQUEUE( _command_queue ) \ + pIntercept->incrementEnqueueCounter(); \ + if( pIntercept->config().FinishAfterEnqueue ) \ + { \ + pIntercept->logFlushOrFinishAfterEnqueueStart( \ + "clFinish", \ + __FUNCTION__ ); \ + cl_int e = pIntercept->dispatch().clFinish( _command_queue ); \ + pIntercept->logFlushOrFinishAfterEnqueueEnd( \ + "clFinish", \ + __FUNCTION__, \ + e ); \ + pIntercept->checkTimingEvents(); \ + } \ + else if( pIntercept->config().FlushAfterEnqueue ) \ + { \ + /*pIntercept->logFlushOrFinishAfterEnqueueStart(*/ \ + /* "clFlush", */ \ + /* __FUNCTION__ ); */ \ + /* cl_int e = */ pIntercept->dispatch().clFlush( _command_queue ); \ + /*pIntercept->logFlushOrFinishAfterEnqueueEnd(*/ \ + /* "clFlush", */ \ + /* __FUNCTION__, */ \ + /* e ); */ \ + } + +#define FLUSH_AFTER_ENQUEUE_BARRIER( _command_queue ) \ + if( pIntercept->config().FlushAfterEnqueueBarrier ) \ + { \ + /*pIntercept->logFlushOrFinishAfterEnqueueStart(*/ \ + /* "clFlush (for barrier)", */ \ + /* __FUNCTION__ ); */ \ + /* cl_int e = */ pIntercept->dispatch().clFlush( _command_queue ); \ + /*pIntercept->logFlushOrFinishAfterEnqueueEnd(*/ \ + /* "clFlush (for barrier)", */ \ + /* __FUNCTION__, */ \ + /* e ); */ \ + } + +/////////////////////////////////////////////////////////////////////////////// +// +inline bool CLIntercept::nullEnqueue() const +{ + return m_Config.NullEnqueue; +} + +/////////////////////////////////////////////////////////////////////////////// +// +inline bool CLIntercept::dumpBufferForKernel( const cl_kernel kernel ) +{ + return m_Config.DumpBuffersForKernel.empty() || + m_KernelNameMap[ kernel ] == m_Config.DumpBuffersForKernel; +} + +inline bool CLIntercept::dumpImagesForKernel( const cl_kernel kernel ) +{ + return m_Config.DumpImagesForKernel.empty() || + m_KernelNameMap[ kernel ] == m_Config.DumpImagesForKernel; +} + +inline bool CLIntercept::checkDumpBufferEnqueueLimits() const +{ + return ( m_EnqueueCounter >= m_Config.DumpBuffersMinEnqueue ) && + ( m_EnqueueCounter <= m_Config.DumpBuffersMaxEnqueue ); +} + +inline bool CLIntercept::checkDumpImageEnqueueLimits() const +{ + return ( m_EnqueueCounter >= m_Config.DumpImagesMinEnqueue ) && + ( m_EnqueueCounter <= m_Config.DumpImagesMaxEnqueue ); +} + +#define ADD_BUFFER( buffer ) \ + if( buffer && \ + ( pIntercept->config().DumpBuffersAfterCreate || \ + pIntercept->config().DumpBuffersAfterMap || \ + pIntercept->config().DumpBuffersBeforeUnmap || \ + pIntercept->config().DumpBuffersBeforeEnqueue || \ + pIntercept->config().DumpBuffersAfterEnqueue ) ) \ + { \ + pIntercept->addBuffer( buffer ); \ + } + +#define ADD_IMAGE( image ) \ + if( image && \ + ( pIntercept->config().DumpImagesBeforeEnqueue || \ + pIntercept->config().DumpImagesAfterEnqueue ) ) \ + { \ + pIntercept->addImage( image ); \ + } + +#define REMOVE_MEMOBJ( memobj ) \ + if( memobj && \ + ( pIntercept->config().DumpBuffersAfterCreate || \ + pIntercept->config().DumpBuffersAfterMap || \ + pIntercept->config().DumpBuffersBeforeUnmap || \ + pIntercept->config().DumpBuffersBeforeEnqueue || \ + pIntercept->config().DumpBuffersAfterEnqueue || \ + pIntercept->config().DumpImagesBeforeEnqueue || \ + pIntercept->config().DumpImagesAfterEnqueue ) ) \ + { \ + pIntercept->removeMemObj( memobj ); \ + } + +#define ADD_SAMPLER( sampler, str ) \ + if( sampler && \ + pIntercept->callLogging() ) \ + { \ + pIntercept->addSampler( sampler, str ); \ + } + +#define REMOVE_SAMPLER( sampler ) \ + if( sampler && \ + pIntercept->callLogging() ) \ + { \ + pIntercept->removeSampler( sampler ); \ + } + +#define ADD_SVM_ALLOCATION( svmPtr, size ) \ + if( svmPtr && \ + ( pIntercept->config().DumpBuffersBeforeEnqueue || \ + pIntercept->config().DumpBuffersAfterEnqueue ) ) \ + { \ + pIntercept->addSVMAllocation( svmPtr, size ); \ + } + +#define REMOVE_SVM_ALLOCATION( svmPtr ) \ + if( svmPtr && \ + ( pIntercept->config().DumpBuffersBeforeEnqueue || \ + pIntercept->config().DumpBuffersAfterEnqueue ) ) \ + { \ + pIntercept->removeSVMAllocation( svmPtr ); \ + } + +#define SET_KERNEL_ARG( kernel, arg_index, arg_size, arg_value ) \ + if( ( pIntercept->config().DumpBuffersBeforeEnqueue || \ + pIntercept->config().DumpBuffersAfterEnqueue || \ + pIntercept->config().DumpImagesBeforeEnqueue || \ + pIntercept->config().DumpImagesAfterEnqueue ) && \ + ( arg_value != NULL ) && \ + ( arg_size == sizeof(cl_mem) ) ) \ + { \ + cl_mem* pMem = (cl_mem*)arg_value; \ + pIntercept->setKernelArg( kernel, arg_index, pMem[0] ); \ + } + +#define SET_KERNEL_ARG_SVM_POINTER( kernel, arg_index, arg_value ) \ + if( pIntercept->config().DumpBuffersBeforeEnqueue || \ + pIntercept->config().DumpBuffersAfterEnqueue ) \ + { \ + pIntercept->setKernelArgSVMPointer( kernel, arg_index, arg_value ); \ + } + +#define INITIALIZE_BUFFER_CONTENTS_INIT( _flags, _size, _ptr ) \ + char* zeroData = NULL; \ + if( pIntercept->config().InitializeBuffers && \ + _ptr == NULL && \ + !( _flags & ( CL_MEM_USE_HOST_PTR | CL_MEM_COPY_HOST_PTR ) ) ) \ + { \ + zeroData = new char[ _size ]; \ + if( zeroData != NULL ) \ + { \ + memset( zeroData, 0, _size ); \ + _ptr = zeroData; \ + _flags |= (cl_mem_flags)CL_MEM_COPY_HOST_PTR; \ + } \ + } + +// Note: The cleanup setup currently does not reset the flags or host pointer. +// This mostly means that initialized buffers may be dumped after creation, +// whereas if the flags were reset then the dump buffer after create step +// would not be triggered. +#define INITIALIZE_BUFFER_CONTENTS_CLEANUP( _flags, _ptr ) \ + if( zeroData != NULL ) \ + { \ + delete [] zeroData; \ + zeroData = NULL; \ + } + + +#define DUMP_BUFFER_AFTER_CREATE( memobj, flags, ptr, size ) \ + if( memobj && \ + ( flags & ( CL_MEM_COPY_HOST_PTR | CL_MEM_USE_HOST_PTR ) ) && \ + pIntercept->checkDumpBufferEnqueueLimits() && \ + pIntercept->config().DumpBuffersAfterCreate ) \ + { \ + pIntercept->dumpBuffer( "Create", memobj, NULL, ptr, 0, size ); \ + } + +#define DUMP_BUFFER_AFTER_MAP( command_queue, memobj, blocking_map, flags, ptr, offset, size ) \ + if( memobj && \ + !( flags & CL_MAP_WRITE_INVALIDATE_REGION ) && \ + pIntercept->checkDumpBufferEnqueueLimits() && \ + pIntercept->config().DumpBuffersAfterMap ) \ + { \ + if( blocking_map == false ) \ + { \ + pIntercept->dispatch().clFinish( command_queue ); \ + } \ + pIntercept->dumpBuffer( "Map", memobj, NULL, ptr, offset, size ); \ + } + +#define DUMP_BUFFER_BEFORE_UNMAP( memobj, command_queue) \ + if( memobj && \ + command_queue && \ + pIntercept->checkDumpBufferEnqueueLimits() && \ + pIntercept->config().DumpBuffersBeforeUnmap ) \ + { \ + pIntercept->dumpBuffer( "Unmap", memobj, command_queue, NULL, 0, 0 );\ + } + +#define DUMP_BUFFERS_BEFORE_ENQUEUE( kernel, command_queue ) \ + if( pIntercept->checkDumpBufferEnqueueLimits() && \ + pIntercept->config().DumpBuffersBeforeEnqueue && \ + pIntercept->dumpBufferForKernel( kernel ) ) \ + { \ + pIntercept->dumpBuffersForKernel( "Pre", kernel, command_queue ); \ + } + +#define DUMP_BUFFERS_AFTER_ENQUEUE( kernel, command_queue ) \ + if( pIntercept->checkDumpBufferEnqueueLimits() && \ + pIntercept->config().DumpBuffersAfterEnqueue && \ + pIntercept->dumpBufferForKernel( kernel ) ) \ + { \ + pIntercept->dumpBuffersForKernel( "Post", kernel, command_queue ); \ + } + +#define DUMP_IMAGES_BEFORE_ENQUEUE( kernel, command_queue ) \ + if( pIntercept->checkDumpImageEnqueueLimits() && \ + pIntercept->config().DumpImagesBeforeEnqueue && \ + pIntercept->dumpImagesForKernel( kernel ) ) \ + { \ + pIntercept->dumpImagesForKernel( "Pre", kernel, command_queue ); \ + } + +#define DUMP_IMAGES_AFTER_ENQUEUE( kernel, command_queue ) \ + if( pIntercept->checkDumpImageEnqueueLimits() && \ + pIntercept->config().DumpImagesAfterEnqueue && \ + pIntercept->dumpImagesForKernel( kernel ) ) \ + { \ + pIntercept->dumpImagesForKernel( "Post", kernel, command_queue ); \ + } + +/////////////////////////////////////////////////////////////////////////////// +// +inline bool CLIntercept::checkAubCaptureEnqueueLimits() const +{ + return ( m_EnqueueCounter >= m_Config.AubCaptureMinEnqueue ) && + ( m_EnqueueCounter <= m_Config.AubCaptureMaxEnqueue ); +} + +// Note: We do not individually aub capture non-kernel enqueues at the moment. +#define CHECK_AUBCAPTURE_START( command_queue ) \ + if( pIntercept->config().AubCapture && \ + pIntercept->checkAubCaptureEnqueueLimits() && \ + !pIntercept->config().AubCaptureIndividualEnqueues ) \ + { \ + pIntercept->startAubCapture( \ + __FUNCTION__, NULL, 0, NULL, NULL, command_queue ); \ + } + +#define CHECK_AUBCAPTURE_START_KERNEL( kernel, wd, gws, lws, command_queue )\ + if( pIntercept->config().AubCapture && \ + pIntercept->checkAubCaptureEnqueueLimits() && \ + pIntercept->checkAubCaptureKernelSignature( kernel, wd, gws, lws ) )\ + { \ + pIntercept->startAubCapture( \ + __FUNCTION__, kernel, wd, gws, lws, command_queue ); \ + } + +#define CHECK_AUBCAPTURE_STOP( command_queue ) \ + if( pIntercept->config().AubCapture && \ + ( pIntercept->config().AubCaptureIndividualEnqueues || \ + !pIntercept->checkAubCaptureEnqueueLimits() ) ) \ + { \ + pIntercept->stopAubCapture( command_queue ); \ + } + +/////////////////////////////////////////////////////////////////////////////// +// + +// Shared: + +#define SAVE_PROGRAM_HASH( program, hash ) \ + if( pIntercept->config().DevicePerformanceTimeHashTracking || \ + pIntercept->config().DumpProgramSource || \ + pIntercept->config().DumpInputProgramBinaries || \ + pIntercept->config().DumpProgramBinaries || \ + pIntercept->config().DumpProgramSPIRV || \ + pIntercept->config().DumpProgramBuildLogs || \ + pIntercept->config().InjectProgramSource || \ + pIntercept->config().AutoCreateSPIRV || \ + pIntercept->config().AubCaptureUniqueKernels ) \ + { \ + pIntercept->saveProgramHash( program, hash ); \ + } + +// Called from clCreateProgramWithSource: + +#define CREATE_COMBINED_PROGRAM_STRING( count, strings, lengths, singleString, hash ) \ + if( pIntercept->config().DevicePerformanceTimeHashTracking || \ + pIntercept->config().SimpleDumpProgramSource || \ + pIntercept->config().DumpProgramSourceScript || \ + pIntercept->config().DumpProgramSource || \ + pIntercept->config().DumpProgramBinaries || \ + pIntercept->config().DumpProgramSPIRV || \ + pIntercept->config().DumpProgramBuildLogs || \ + pIntercept->config().InjectProgramSource || \ + pIntercept->config().InjectProgramBinaries || \ + pIntercept->config().PrependProgramSource || \ + pIntercept->config().AutoCreateSPIRV || \ + pIntercept->config().AubCaptureUniqueKernels ) \ + { \ + pIntercept->combineProgramStrings( \ + count, \ + strings, \ + lengths, \ + singleString ); \ + hash = pIntercept->hashString( \ + singleString, \ + strlen( singleString ) ); \ + } + +#define INJECT_PROGRAM_SOURCE( count, strings, lengths, singleString, hash ) \ + bool injected = false; \ + if( pIntercept->config().InjectProgramSource ) \ + { \ + injected = pIntercept->injectProgramSource( \ + hash, \ + count, \ + strings, \ + lengths, \ + singleString ); \ + } + +#define PREPEND_PROGRAM_SOURCE( count, strings, lengths, singleString, hash ) \ + if( pIntercept->config().PrependProgramSource ) \ + { \ + injected |= pIntercept->prependProgramSource( \ + hash, \ + count, \ + strings, \ + lengths, \ + singleString ); \ + } + +#define DUMP_PROGRAM_SOURCE( program, singleString, hash ) \ + if( ( injected == false ) && \ + ( pIntercept->config().DumpProgramSource || \ + pIntercept->config().AutoCreateSPIRV ) ) \ + { \ + pIntercept->dumpProgramSource( hash, program, singleString ); \ + } \ + else if( ( injected == false ) && \ + ( pIntercept->config().SimpleDumpProgramSource || \ + pIntercept->config().DumpProgramSourceScript ) ) \ + { \ + pIntercept->dumpProgramSourceScript( program, singleString ); \ + } \ + else \ + { \ + pIntercept->saveProgramNumber( program ); \ + } + +#define DELETE_COMBINED_PROGRAM_STRING( singleString ) \ + delete [] singleString; \ + singleString = NULL; + +// Called from clCreateProgramWithBinary: + +// Note: This does not currently combine program binaries before computing +// the hash. This will work fine for single-device binaries, but may be +// incomplete or incorrect for multi-device binaries. +#define COMPUTE_BINARY_HASH( _num, _lengths, _binaries, _hash ) \ + if( _lengths && _binaries && \ + ( pIntercept->config().DumpInputProgramBinaries || \ + pIntercept->config().DumpProgramBinaries ) ) \ + { \ + _hash = pIntercept->hashString( \ + (const char*)_binaries[0], \ + _lengths[0] ); \ + } + +#define DUMP_INPUT_PROGRAM_BINARIES( _program, _num, _devs, _lengths, _binaries, _hash ) \ + if( pIntercept->config().DumpInputProgramBinaries ) \ + { \ + pIntercept->dumpInputProgramBinaries( \ + _hash, \ + _program, \ + _num, \ + _devs, \ + _lengths, \ + _binaries ); \ + } + +// Called from clCreateProgramWithIL: + +#define COMPUTE_SPIRV_HASH( _length, _il, _hash ) \ + if( _length && _il && pIntercept->config().DumpProgramSPIRV ) \ + { \ + _hash = pIntercept->hashString( \ + (const char*)_il, \ + _length ); \ + } + +#define INJECT_PROGRAM_SPIRV( _length, _il, _injectedSPIRV, _hash ) \ + bool injected = false; \ + if( pIntercept->config().InjectProgramSPIRV ) \ + { \ + injected = pIntercept->injectProgramSPIRV( \ + _hash, \ + _length, \ + _il, \ + _injectedSPIRV ); \ + } + +#define DUMP_PROGRAM_SPIRV( program, length, il, hash ) \ + if( ( injected == false ) && \ + pIntercept->config().DumpProgramSPIRV ) \ + { \ + pIntercept->dumpProgramSPIRV( hash, program, length, il ); \ + } \ + else \ + { \ + pIntercept->saveProgramNumber( program ); \ + } + +#define DELETE_INJECTED_SPIRV( _injectedSPIRV ) \ + delete [] _injectedSPIRV; \ + _injectedSPIRV = NULL; + +// Called from clBuildProgram: + +#define MODIFY_PROGRAM_OPTIONS( program, options, newOptions ) \ + bool modified = false; \ + if( pIntercept->config().InjectProgramSource ) \ + { \ + modified |= pIntercept->injectProgramOptions( \ + program, \ + options, \ + newOptions ); \ + } \ + if( !pIntercept->config().AppendBuildOptions.empty() ) \ + { \ + modified |= pIntercept->appendBuildOptions( \ + options, \ + newOptions ); \ + } + +#define DUMP_PROGRAM_OPTIONS( program, options ) \ + if( ( modified == false ) && \ + ( pIntercept->config().DumpProgramSource || \ + pIntercept->config().DumpProgramBinaries || \ + pIntercept->config().DumpProgramSPIRV ) ) \ + { \ + pIntercept->dumpProgramOptions( program, options ); \ + } \ + else if( ( modified == false ) && \ + ( pIntercept->config().SimpleDumpProgramSource || \ + pIntercept->config().DumpProgramSourceScript ) ) \ + { \ + pIntercept->dumpProgramOptionsScript( program, options ); \ + } + +#define DUMP_OUTPUT_PROGRAM_BINARIES( program ) \ + if( pIntercept->config().DumpProgramBinaries ) \ + { \ + pIntercept->dumpProgramBinary( program ); \ + } + +#define AUTO_CREATE_SPIRV( _program, _options ) \ + if( _program && pIntercept->config().AutoCreateSPIRV ) \ + { \ + pIntercept->autoCreateSPIRV( _program, _options ); \ + } + +#define INCREMENT_PROGRAM_COMPILE_COUNT( _program ) \ + if( _program && \ + ( pIntercept->config().BuildLogging || \ + pIntercept->config().DevicePerformanceTimeHashTracking || \ + pIntercept->config().InjectProgramSource || \ + pIntercept->config().DumpProgramSourceScript || \ + pIntercept->config().DumpProgramSource || \ + pIntercept->config().DumpProgramBinaries || \ + pIntercept->config().DumpProgramSPIRV || \ + pIntercept->config().DumpProgramBuildLogs || \ + pIntercept->config().AutoCreateSPIRV || \ + pIntercept->config().AubCaptureUniqueKernels ) ) \ + { \ + pIntercept->incrementProgramCompileCount( _program ); \ + } + +#define DELETE_MODIFIED_OPTIONS( newOptions ) \ + delete [] newOptions; \ + newOptions = NULL; + +/////////////////////////////////////////////////////////////////////////////// +// +#define INIT_PRECOMPILED_KERNEL_OVERRIDES( context ) \ + if( ( context != NULL ) && \ + ( pIntercept->config().OverrideReadBuffer || \ + pIntercept->config().OverrideWriteBuffer || \ + pIntercept->config().OverrideCopyBuffer || \ + pIntercept->config().OverrideReadImage || \ + pIntercept->config().OverrideWriteImage || \ + pIntercept->config().OverrideCopyImage ) ) \ + { \ + pIntercept->initPrecompiledKernelOverrides( context ); \ + } + +/////////////////////////////////////////////////////////////////////////////// +// +#define INIT_BUILTIN_KERNEL_OVERRIDES( context ) \ + if( ( context != NULL ) && \ + pIntercept->config().OverrideBuiltinKernels ) \ + { \ + pIntercept->initBuiltinKernelOverrides( context ); \ + } + +/////////////////////////////////////////////////////////////////////////////// +// +#define CPU_PERFORMANCE_TIMING_START() \ + uint64_t cpuStart = 0, cpuEnd = 0; \ + if( pIntercept->config().HostPerformanceTiming || \ + pIntercept->config().ChromeCallLogging ) \ + { \ + cpuStart = pIntercept->OS().GetTimer(); \ + } + +#define CPU_PERFORMANCE_TIMING_END() \ + if( pIntercept->config().HostPerformanceTiming || \ + pIntercept->config().ChromeCallLogging ) \ + { \ + cpuEnd = pIntercept->OS().GetTimer(); \ + if( pIntercept->config().HostPerformanceTiming ) \ + { \ + pIntercept->updateHostTimingStats( \ + __FUNCTION__, \ + NULL, \ + cpuStart, \ + cpuEnd ); \ + } \ + if( pIntercept->config().ChromeCallLogging ) \ + { \ + pIntercept->chromeCallLoggingExit( \ + __FUNCTION__, \ + NULL, \ + cpuStart, \ + cpuEnd ); \ + } \ + } + +#define CPU_PERFORMANCE_TIMING_END_KERNEL( _kernel ) \ + if( pIntercept->config().HostPerformanceTiming || \ + pIntercept->config().ChromeCallLogging ) \ + { \ + cpuEnd = pIntercept->OS().GetTimer(); \ + if( pIntercept->config().HostPerformanceTiming ) \ + { \ + pIntercept->updateHostTimingStats( \ + __FUNCTION__, \ + _kernel, \ + cpuStart, \ + cpuEnd ); \ + } \ + if( pIntercept->config().ChromeCallLogging ) \ + { \ + pIntercept->chromeCallLoggingExit( \ + __FUNCTION__, \ + _kernel, \ + cpuStart, \ + cpuEnd ); \ + } \ + } + +/////////////////////////////////////////////////////////////////////////////// +// +#define CREATE_COMMAND_QUEUE_OVERRIDE_INIT( _props, _newprops ) \ + if( pIntercept->config().DevicePerformanceTiming || \ + pIntercept->config().ITTPerformanceTiming || \ + pIntercept->config().ChromePerformanceTiming || \ + pIntercept->config().SIMDSurvey || \ + !pIntercept->config().DevicePerfCounterCustom.empty() || \ + pIntercept->config().InOrderQueue ) \ + { \ + pIntercept->createCommandQueueOverrideInit( \ + _props, \ + _newprops ); \ + } + +#define CREATE_COMMAND_QUEUE_OVERRIDE_CLEANUP( _queue, _newprops ) \ + if( pIntercept->config().DevicePerformanceTiming || \ + pIntercept->config().ITTPerformanceTiming || \ + pIntercept->config().ChromePerformanceTiming || \ + pIntercept->config().SIMDSurvey || \ + !pIntercept->config().DevicePerfCounterCustom.empty() || \ + pIntercept->config().InOrderQueue ) \ + { \ + pIntercept->createCommandQueueOverrideCleanup( \ + _newprops ); \ + } + +#define DEVICE_PERFORMANCE_TIMING_START( pEvent ) \ + uint64_t queuedTime = 0; \ + cl_event local_event = NULL; \ + bool retainAppEvent = true; \ + if( pIntercept->config().DevicePerformanceTiming || \ + pIntercept->config().ITTPerformanceTiming || \ + pIntercept->config().ChromePerformanceTiming || \ + pIntercept->config().SIMDSurvey || \ + !pIntercept->config().DevicePerfCounterCustom.empty() ) \ + { \ + queuedTime = pIntercept->OS().GetTimer(); \ + if( pEvent == NULL ) \ + { \ + pEvent = &local_event; \ + retainAppEvent = false; \ + } \ + } + +#define DEVICE_PERFORMANCE_TIMING_END( pEvent ) \ + if( ( pIntercept->config().DevicePerformanceTiming || \ + pIntercept->config().ITTPerformanceTiming || \ + pIntercept->config().ChromePerformanceTiming || \ + pIntercept->config().SIMDSurvey || \ + !pIntercept->config().DevicePerfCounterCustom.empty() ) && \ + ( pEvent != NULL ) ) \ + { \ + if( pIntercept->config().DevicePerformanceTimingSkipUnmap && \ + std::string(__FUNCTION__) == "clEnqueueUnmapMemObject" ) \ + { \ + if( retainAppEvent == false ) \ + { \ + pIntercept->dispatch().clReleaseEvent( pEvent[0] ); \ + pEvent = NULL; \ + } \ + } \ + else \ + { \ + pIntercept->addTimingEvent( \ + __FUNCTION__, \ + queuedTime, \ + NULL, \ + 0, NULL, NULL, \ + pEvent[0] ); \ + if( retainAppEvent ) \ + { \ + pIntercept->dispatch().clRetainEvent( pEvent[0] ); \ + } \ + else \ + { \ + pEvent = NULL; \ + } \ + } \ + } + +#define DEVICE_PERFORMANCE_TIMING_END_KERNEL( pEvent, kernel, wd, gws, lws )\ + if( ( pIntercept->config().DevicePerformanceTiming || \ + pIntercept->config().ITTPerformanceTiming || \ + pIntercept->config().ChromePerformanceTiming || \ + pIntercept->config().SIMDSurvey || \ + !pIntercept->config().DevicePerfCounterCustom.empty() ) && \ + ( pEvent != NULL ) ) \ + { \ + pIntercept->addTimingEvent( \ + __FUNCTION__, \ + queuedTime, \ + kernel, \ + wd, gws, lws, \ + pEvent[0] ); \ + if( retainAppEvent ) \ + { \ + pIntercept->dispatch().clRetainEvent( pEvent[0] ); \ + } \ + else \ + { \ + pEvent = NULL; \ + } \ + } + +#define DEVICE_PERFORMANCE_TIMING_CHECK() \ + if( pIntercept->config().DevicePerformanceTiming || \ + pIntercept->config().ITTPerformanceTiming || \ + pIntercept->config().ChromePerformanceTiming || \ + pIntercept->config().SIMDSurvey || \ + !pIntercept->config().DevicePerfCounterCustom.empty() ) \ + { \ + pIntercept->checkTimingEvents(); \ + } + +/////////////////////////////////////////////////////////////////////////////// +// +#define SIMD_SURVEY_CREATE_PROGRAM_FROM_SOURCE( _program, _context, _count, _strings, _lengths ) \ + if( pIntercept->config().SIMDSurvey && \ + _program != NULL ) \ + { \ + pIntercept->SIMDSurveyCreateProgramFromSource( \ + _program, \ + _context, \ + _count, \ + _strings, \ + _lengths ); \ + } + +#define SIMD_SURVEY_BUILD_PROGRAM( _program, _numDevices, _deviceList, _options ) \ + if( pIntercept->config().SIMDSurvey && \ + _program != NULL ) \ + { \ + pIntercept->SIMDSurveyBuildProgram( \ + _program, \ + _numDevices, \ + _deviceList, \ + _options ); \ + } + +#define SIMD_SURVEY_CREATE_KERNEL( _program, _kernel, _name ) \ + if( pIntercept->config().SIMDSurvey && \ + _kernel != NULL ) \ + { \ + pIntercept->SIMDSurveyCreateKernel( \ + _program, \ + _kernel, \ + _name ); \ + } + +#define SIMD_SURVEY_SET_KERNEL_ARG( _kernel, _argIndex, _argSize, _argValue ) \ + if( pIntercept->config().SIMDSurvey ) \ + { \ + pIntercept->SIMDSurveySetKernelArg( \ + _kernel, \ + _argIndex, \ + _argSize, \ + _argValue ); \ + } + +#define SIMD_SURVEY_NDRANGE_KERNEL( _kernel ) \ + if( pIntercept->config().SIMDSurvey ) \ + { \ + pIntercept->SIMDSurveyNDRangeKernel( _kernel ); \ + } + +/////////////////////////////////////////////////////////////////////////////// +// +inline void CLIntercept::saveProgramNumber( const cl_program program ) +{ + m_OS.EnterCriticalSection(); + + m_ProgramNumberMap[ program ] = m_ProgramNumber; + m_ProgramNumber++; + + m_OS.LeaveCriticalSection(); +} + +inline unsigned int CLIntercept::getProgramNumber() const +{ + return m_ProgramNumber; +} + +/////////////////////////////////////////////////////////////////////////////// +// +inline cl_device_type CLIntercept::filterDeviceType( cl_device_type device_type ) const +{ + if( m_Config.DeviceType & device_type ) + { + device_type = CL_DEVICE_TYPE_ALL; + } + device_type &= (cl_device_type)m_Config.DeviceTypeFilter; + return device_type; +} + +/////////////////////////////////////////////////////////////////////////////// +// +#if defined(USE_ITT) + +inline __itt_domain* CLIntercept::ittDomain() const +{ + return m_ITTDomain; +} + +#endif + +/////////////////////////////////////////////////////////////////////////////// +// +inline void CLIntercept::logCL_GLTextureDetails( cl_mem image, cl_GLenum target, cl_GLint miplevel, cl_GLuint texture ) +{ + CLIntercept* pIntercept = this; + + cl_image_format cl_format = { 0 }; + size_t cl_elementSize = 0; + size_t cl_rowPitch = 0; + size_t cl_slicePitch = 0; + size_t cl_width = 0; + size_t cl_height = 0; + size_t cl_depth = 0; + + cl_int subErrorCode = CL_SUCCESS; + + if( subErrorCode == CL_SUCCESS ) + { + subErrorCode = dispatch().clGetImageInfo( + image, + CL_IMAGE_FORMAT, + sizeof(cl_format), + &cl_format, + NULL); + } + + if( subErrorCode == CL_SUCCESS ) + { + subErrorCode = dispatch().clGetImageInfo( + image, + CL_IMAGE_ELEMENT_SIZE, + sizeof(cl_elementSize), + &cl_elementSize, + NULL); + } + + if( subErrorCode == CL_SUCCESS ) + { + subErrorCode = dispatch().clGetImageInfo( + image, + CL_IMAGE_ROW_PITCH, + sizeof(cl_rowPitch), + &cl_rowPitch, + NULL); + } + + if( subErrorCode == CL_SUCCESS ) + { + subErrorCode = dispatch().clGetImageInfo( + image, + CL_IMAGE_SLICE_PITCH, + sizeof(cl_slicePitch), + &cl_slicePitch, + NULL); + } + + if( subErrorCode == CL_SUCCESS ) + { + subErrorCode = dispatch().clGetImageInfo( + image, + CL_IMAGE_WIDTH, + sizeof(cl_width), + &cl_width, + NULL); + } + + if( subErrorCode == CL_SUCCESS ) + { + subErrorCode = dispatch().clGetImageInfo( + image, + CL_IMAGE_HEIGHT, + sizeof(cl_height), + &cl_height, + NULL); + } + + if( subErrorCode == CL_SUCCESS ) + { + subErrorCode = dispatch().clGetImageInfo( + image, + CL_IMAGE_DEPTH, + sizeof(cl_depth), + &cl_depth, + NULL); + } + + CALL_LOGGING_INFO( + "CL Channel Order = %s, " + "CL Channel Data Type = %s, " + "CL Row Pitch = %d, " + "CL Slice Pitch = %d, " + "CL Width = %d, " + "CL Height = %d, " + "CL Depth = %d, ", + enumName().name( cl_format.image_channel_order ).c_str(), + enumName().name( cl_format.image_channel_data_type ).c_str(), + cl_rowPitch, + cl_slicePitch, + cl_width, + cl_height, + cl_depth ); + + // OpenGL.lib is not linked into CLIntercept - the OpenGL calls are performed by using GetProcAddress, + // which is only available on windows. +#ifdef _WIN32 + HMODULE glModule = GetModuleHandle( "Opengl32.dll" ); + + if( glModule != NULL ) + { + FARPROC ptrGetTexLevel = GetProcAddress( glModule, "glGetTexLevelParameteriv" ); + FARPROC ptrGetIntegerv = GetProcAddress( glModule, "glGetIntegerv" ); + FARPROC ptrBindTexture = GetProcAddress( glModule, "glBindTexture" ); + FARPROC ptrGetError = GetProcAddress( glModule, "glGetError" ); + + if( ( ptrGetTexLevel != NULL ) && + ( ptrGetIntegerv != NULL ) && + ( ptrBindTexture != NULL ) && + ( ptrGetError != NULL ) ) + { + PFNGLGETTEXLEVELPARAMETERIVPROC _glGetTexLevel = (PFNGLGETTEXLEVELPARAMETERIVPROC)ptrGetTexLevel; + PFNGLGETINTEGERVPROC _glGetInteger = (PFNGLGETINTEGERVPROC)ptrGetIntegerv; + PFNGLBINDTEXTUREPROC _glBindTexture = (PFNGLBINDTEXTUREPROC)ptrBindTexture; + PFNGLGETERRORPROC _glGetError = (PFNGLGETERRORPROC)ptrGetError; + + GLenum gl_error = _glGetError(); + + if( gl_error == GL_FALSE ) + { + GLint restoreTextureId = 0; + + // Save the currently bound texture - we need to to rebind a different + // texture to query it. + switch( target ) + { + case GL_TEXTURE_1D: + _glGetInteger( GL_TEXTURE_BINDING_1D, &restoreTextureId ); + break; + case GL_TEXTURE_1D_ARRAY: + _glGetInteger( GL_TEXTURE_BINDING_1D_ARRAY, &restoreTextureId ); + break; + case GL_TEXTURE_2D: + _glGetInteger( GL_TEXTURE_BINDING_2D, &restoreTextureId ); + break; + case GL_TEXTURE_2D_ARRAY: + _glGetInteger( GL_TEXTURE_BINDING_2D_ARRAY, &restoreTextureId ); + break; + case GL_TEXTURE_3D: + _glGetInteger( GL_TEXTURE_BINDING_3D, &restoreTextureId ); + break; + case GL_TEXTURE_CUBE_MAP: + _glGetInteger( GL_TEXTURE_BINDING_CUBE_MAP, &restoreTextureId ); + break; + case GL_TEXTURE_BUFFER: + _glGetInteger( GL_TEXTURE_BINDING_BUFFER, &restoreTextureId ); + break; + default: + // unexpected texture type + gl_error = GL_TRUE; + break; + } + + if( gl_error == GL_FALSE ) + { + GLint gl_width = 0; + GLint gl_height = 0; + GLint gl_depth = 0; + GLint gl_internal_format = 0; + GLint gl_buffer_size = 0; + GLint gl_buffer_offset = 0; + GLint gl_compressed_texture = GL_FALSE; + + // Bind the texture we want to query + _glBindTexture( target, texture ); + gl_error = _glGetError(); + + + if( gl_error == GL_FALSE ) + { + _glGetTexLevel( + target, + miplevel > 0 ? miplevel : 0, + GL_TEXTURE_INTERNAL_FORMAT, + &gl_internal_format ); + gl_error = _glGetError(); + } + + if( gl_error == GL_FALSE ) + { + _glGetTexLevel( + target, + miplevel > 0 ? miplevel : 0, + GL_TEXTURE_WIDTH, + &gl_width ); + gl_error = _glGetError(); + } + + if( gl_error == GL_FALSE ) + { + _glGetTexLevel( + target, + miplevel > 0 ? miplevel : 0, + GL_TEXTURE_HEIGHT, + &gl_height ); + gl_error = _glGetError(); + } + + if( gl_error == GL_FALSE ) + { + _glGetTexLevel( + target, + miplevel > 0 ? miplevel : 0, + GL_TEXTURE_DEPTH, + &gl_depth ); + gl_error = _glGetError(); + } + + if( gl_error == GL_FALSE ) + { + _glGetTexLevel( + target, + miplevel > 0 ? miplevel : 0, + GL_TEXTURE_BUFFER_SIZE, + &gl_buffer_size ); + gl_error = _glGetError(); + } + + if( gl_error == GL_FALSE ) + { + _glGetTexLevel( + target, + miplevel > 0 ? miplevel : 0, + GL_TEXTURE_BUFFER_OFFSET, + &gl_buffer_offset ); + gl_error = _glGetError(); + } + + // restore original bound texture + _glBindTexture( target, restoreTextureId ); + gl_error = _glGetError(); + + CALL_LOGGING_INFO( + "GL Internal Format = %s (%d), " + "GL Width = %d, " + "GL Height = %d, " + "GL Depth = %d, " + "GL Buffer Size = %d, " + "GL Buffer Offset = %d ", + enumName().name_gl( gl_internal_format ).c_str(), + gl_internal_format, + gl_width, + gl_height, + gl_depth, + gl_buffer_size, + gl_buffer_offset ); + } + } + } + } +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +// +extern CLIntercept* g_pIntercept; + +inline CLIntercept* GetIntercept() +{ + return g_pIntercept; +} diff --git a/Src/main.cpp b/Src/main.cpp new file mode 100644 index 00000000..c36a620b --- /dev/null +++ b/Src/main.cpp @@ -0,0 +1,86 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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 "common.h" +#include "intercept.h" +#ifdef __ANDROID__ +#include +#endif + +CLIntercept* g_pIntercept = NULL; + +#if defined(_WIN32) + +#include + +BOOL APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReasonForCall, LPVOID lpReserved) +{ + switch(dwReasonForCall) { + case DLL_PROCESS_ATTACH: + if( CLIntercept::Create( hInstance, g_pIntercept ) == false ) + { + return FALSE; + } + break; + + case DLL_PROCESS_DETACH: + CLIntercept::Delete( g_pIntercept ); + break; + + case DLL_THREAD_ATTACH: + break; + + case DLL_THREAD_DETACH: + break; + + default: + CLI_ASSERT(0); + break; + } + + return TRUE; +} + +#elif defined(__linux__) || defined(__APPLE__) + +void __attribute__((constructor)) CLIntercept_Load(void); +void __attribute__((destructor)) CLIntercept_Unload(void); + +void CLIntercept_Load(void) +{ +#ifdef __ANDROID__ + __android_log_print( ANDROID_LOG_INFO, "clIntercept", ">>Load.pid=%d\n", getpid() ); +#endif + CLIntercept::Create( NULL, g_pIntercept ); +#ifdef __ANDROID__ + __android_log_print( ANDROID_LOG_INFO, "clIntercept", "< [ ...]) +# +# Returns the refspec and sha hash of the current head revision +# +# git_describe( [ ...]) +# +# Returns the results of git describe on the source tree, and adjusting +# the output so that it tests false if an error occurs. +# +# git_get_exact_tag( [ ...]) +# +# Returns the results of git describe --exact-match on the source tree, +# and adjusting the output so that it tests false if there was no exact +# matching tag. +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +if(__get_git_revision_description) + return() +endif() +set(__get_git_revision_description YES) + +# We must run the following at "include" time, not at function call time, +# to find the path to this module rather than the path to a calling list file +get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) + +function(get_git_head_revision _refspecvar _hashvar) + set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories + set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") + get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) + if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) + # We have reached the root directory, we are not in git + set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE) + set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) + return() + endif() + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + endwhile() + # check if this is a submodule + if(NOT IS_DIRECTORY ${GIT_DIR}) + file(READ ${GIT_DIR} submodule) + string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule}) + get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) + get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE) + endif() + set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") + if(NOT EXISTS "${GIT_DATA}") + file(MAKE_DIRECTORY "${GIT_DATA}") + endif() + + if(NOT EXISTS "${GIT_DIR}/HEAD") + return() + endif() + set(HEAD_FILE "${GIT_DATA}/HEAD") + configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) + + configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" + "${GIT_DATA}/grabRef.cmake" + @ONLY) + include("${GIT_DATA}/grabRef.cmake") + + set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) + set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) +endfunction() + +function(git_describe _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) + return() + endif() + if(NOT hash) + set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) + return() + endif() + + # TODO sanitize + #if((${ARGN}" MATCHES "&&") OR + # (ARGN MATCHES "||") OR + # (ARGN MATCHES "\\;")) + # message("Please report the following error to the project!") + # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") + #endif() + + #message(STATUS "Arguments to execute_process: ${ARGN}") + + execute_process(COMMAND + "${GIT_EXECUTABLE}" + describe + ${hash} + ${ARGN} + WORKING_DIRECTORY + "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE + res + OUTPUT_VARIABLE + out + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() + + set(${_var} "${out}" PARENT_SCOPE) +endfunction() + +function(git_get_exact_tag _var) + git_describe(out --exact-match ${ARGN}) + set(${_var} "${out}" PARENT_SCOPE) +endfunction() diff --git a/cmake_modules/GetGitRevisionDescription.cmake.in b/cmake_modules/GetGitRevisionDescription.cmake.in new file mode 100644 index 00000000..6d8b708e --- /dev/null +++ b/cmake_modules/GetGitRevisionDescription.cmake.in @@ -0,0 +1,41 @@ +# +# Internal file for GetGitRevisionDescription.cmake +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +set(HEAD_HASH) + +file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) + +string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) +if(HEAD_CONTENTS MATCHES "ref") + # named branch + string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") + if(EXISTS "@GIT_DIR@/${HEAD_REF}") + configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) + else() + configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY) + file(READ "@GIT_DATA@/packed-refs" PACKED_REFS) + if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}") + set(HEAD_HASH "${CMAKE_MATCH_1}") + endif() + endif() +else() + # detached HEAD + configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) +endif() + +if(NOT HEAD_HASH) + file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) + string(STRIP "${HEAD_HASH}" HEAD_HASH) +endif() diff --git a/config/CLIConfig.cpp b/config/CLIConfig.cpp new file mode 100644 index 00000000..aad5091d --- /dev/null +++ b/config/CLIConfig.cpp @@ -0,0 +1,1116 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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 "CLIConfig.h" + +#include + +#include + +std::wstring ToWString( const std::string& str ) +{ + int size_needed = MultiByteToWideChar( CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0 ); + std::wstring wstrTo( size_needed, 0 ); + MultiByteToWideChar( CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed ); + return wstrTo; +} +std::wstring ToWString( const std::vector& v ) +{ + int size_needed = MultiByteToWideChar( CP_UTF8, 0, &v[0], (int)v.size(), NULL, 0 ); + std::wstring wstrTo( size_needed, 0 ); + MultiByteToWideChar( CP_UTF8, 0, &v[0], (int)v.size(), &wstrTo[0], size_needed ); + return wstrTo; +} +std::string ToString( const std::wstring &wstr) +{ + int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL); + std::string strTo( size_needed, 0 ); + WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL); + return strTo; +} + +CVariableState::CVariableState() +{ + CurrentIconState.resize(cNumVars, ICON_STATE_DEFAULT); + CurrentIntValue.resize(cNumVars); + CurrentStringValue.resize(cNumVars); + + SetDefaultStates(); +} + +// Initializes Variable State and Icon State to defaults +void CVariableState::SetDefaultStates() +{ + for (int i = 0; i < cNumVars; i++) + { + CurrentIntValue[i] = cVars[i].defIntValue; + CurrentStringValue[i] = ToWString(cVars[i].defStrValue); + CurrentIconState[i] = + cVars[i].Type == CONTROL_TYPE_SEPARATOR ? + ICON_STATE_SEPARATOR : + ICON_STATE_DEFAULT; + } +} + +// Updates all Icon States from Variable States +void CVariableState::UpdateIconStates() +{ + for (int i = 0; i < cNumVars; i++) + { + switch (cVars[i].Type) + { + case CONTROL_TYPE_BOOL: + case CONTROL_TYPE_INT: + if (CurrentIntValue[i] == cVars[i].defIntValue) + { + CurrentIconState[i] = ICON_STATE_DEFAULT; + } + else + { + CurrentIconState[i] = ICON_STATE_NONDEFAULT; + } + break; + case CONTROL_TYPE_STRING: + if (CurrentStringValue[i] == ToWString(cVars[i].defStrValue)) + { + CurrentIconState[i] = ICON_STATE_DEFAULT; + } + else + { + CurrentIconState[i] = ICON_STATE_NONDEFAULT; + } + break; + case CONTROL_TYPE_SEPARATOR: + CurrentIconState[i] = ICON_STATE_SEPARATOR; + break; + } + } +} + + +CControlsPage::CControlsPage( CVariableState* pVariableState ) : + CPropertyPage(IDD_VARS_PAGE, IDS_USER), + m_pVariableState( pVariableState ) +{ +} + +BOOL CControlsPage::OnInitDialog() +{ + CPropertyPage::OnInitDialog(); + + HICON hIcon[NUM_ICON_STATES]; + + m_ImageList.Create(16, 16, 0, NUM_ICON_STATES, 0); + hIcon[ICON_STATE_NONDEFAULT] = AfxGetApp()->LoadIcon(IDI_STATE_NONDEFAULT); + hIcon[ICON_STATE_MODIFIED_NONDEFAULT] = AfxGetApp()->LoadIcon(IDI_STATE_MODIFIED_NONDEFAULT); + hIcon[ICON_STATE_MODIFIED_DEFAULT] = AfxGetApp()->LoadIcon(IDI_STATE_MODIFIED_DEFAULT); + hIcon[ICON_STATE_DEFAULT] = AfxGetApp()->LoadIcon(IDI_STATE_DEFAULT); + hIcon[ICON_STATE_SEPARATOR] = AfxGetApp()->LoadIcon(IDI_STATE_SEPARATOR); + + for( int i = 0; i < NUM_ICON_STATES; i++ ) + { + m_ImageList.Add(hIcon[i]); + } + + m_pListCtrl = (CListCtrl*)GetDlgItem(IDC_VAR_LIST); + m_pListCtrl->SetImageList(&m_ImageList, LVSIL_SMALL); + + m_pListCtrl->InsertColumn(0, L"Header"); + for( int i = 0; i < cNumVars; i++ ) + { + m_pListCtrl->InsertItem( + i, + ToWString( cVars[i].Name ).c_str(), + m_pVariableState->CurrentIconState[i] ); + } + m_pListCtrl->SetColumnWidth(0, LVSCW_AUTOSIZE); + + m_SelectedItem = 0; + m_pListCtrl->SetFocus(); + m_pListCtrl->SetItemState(m_SelectedItem, LVIS_SELECTED, LVIS_SELECTED); + + // Read previous Variable State from the registry + ReadSettingsFromRegistry(); + + // Sync Icon State with Variable State + m_pVariableState->UpdateIconStates(); + UpdateIcons(); + + SetModified(FALSE); + + return TRUE; +} + +// Updates all Icons based on Icon State +void CControlsPage::UpdateIcons() const +{ + for( int i = 0; i < cNumVars; i++ ) + { + UpdateIcon(i); + } +} + +// Updates one Icon based on Icon State +void CControlsPage::UpdateIcon( int vIndex ) const +{ + m_pListCtrl->SetItem( + vIndex, + 0, + LVIF_TEXT | LVIF_IMAGE, + ToWString( cVars[vIndex].Name ).c_str(), + m_pVariableState->CurrentIconState[vIndex], + 0, + 0, + 0 ); +} + +void CControlsPage::ResetDefaults() +{ + m_pVariableState->SetDefaultStates(); + UpdateIcons(); + + WriteSettingsToRegistry(); + + UpdateControl(m_SelectedItem); + + SetModified(FALSE); +} + +// Updates Variable State from the Registry +void CControlsPage::ReadSettingsFromRegistry() +{ + HKEY key; + + LSTATUS success = RegOpenKeyEx( + HKEY_CURRENT_USER, + REGISTRY_KEY, + 0, + KEY_READ, + &key ); + if( success == ERROR_SUCCESS ) + { + for( int i = 0; i < cNumVars; i++ ) + { + std::vector regQuery(128); + + unsigned long regQuerySize = regQuery.size() * sizeof(regQuery[0]); + + success = RegQueryValueEx( + key, + ToWString(cVars[i].Name).c_str(), + NULL, + NULL, + (LPBYTE)regQuery.data(), + ®QuerySize ); + + if( success == ERROR_SUCCESS ) + { + switch (cVars[i].Type) + { + case CONTROL_TYPE_BOOL: + { + int* pInt = (int*)regQuery.data(); + m_pVariableState->CurrentIntValue[i] = pInt[0] ? TRUE : FALSE; + } + break; + case CONTROL_TYPE_INT: + { + int* pInt = (int*)regQuery.data(); + m_pVariableState->CurrentIntValue[i] = pInt[0]; + } + break; + case CONTROL_TYPE_STRING: + { + m_pVariableState->CurrentStringValue[i].assign( regQuery.begin(), regQuery.end() ); + } + break; + case CONTROL_TYPE_SEPARATOR: + break; + } + + m_pVariableState->CurrentIconState[i] = ICON_STATE_NONDEFAULT; + } + + } + + RegCloseKey(key); + } +} + +// Writes Variable State to the Registry +void CControlsPage::WriteSettingsToRegistry() const +{ + HKEY key; + + // 32-bit registry keys. + LSTATUS success = RegCreateKeyEx( + HKEY_CURRENT_USER, + REGISTRY_KEY, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_SET_VALUE, + NULL, + &key, + NULL ); + if( success == ERROR_SUCCESS ) + { + WriteSettingsToRegistryHelper(key); + RegCloseKey(key); + } + + // 64-bit registry keys. This probably isn't needed, but better + // safe than sorry. + success = RegCreateKeyEx( + HKEY_CURRENT_USER, + REGISTRY_KEY, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_WOW64_64KEY | KEY_SET_VALUE, + NULL, + &key, + NULL ); + if( success == ERROR_SUCCESS ) + { + WriteSettingsToRegistryHelper(key); + RegCloseKey(key); + } +} + +void CControlsPage::WriteSettingsToRegistryHelper( HKEY key ) const +{ + for (int i = 0; i < cNumVars; i++) + { + switch (cVars[i].Type) { + case CONTROL_TYPE_BOOL: + case CONTROL_TYPE_INT: + if( m_pVariableState->CurrentIntValue[i] != cVars[i].defIntValue ) + { + DWORD dwValue = (DWORD)m_pVariableState->CurrentIntValue[i]; + + RegSetValueEx( + key, + ToWString(cVars[i].Name).c_str(), + 0, + REG_DWORD, + (CONST BYTE *)&dwValue, + sizeof(DWORD)); + } + else + { + RegDeleteValue( + key, + ToWString(cVars[i].Name).c_str() ); + } + break; + case CONTROL_TYPE_STRING: + if( m_pVariableState->CurrentStringValue[i] != ToWString(cVars[i].defStrValue) ) + { + RegSetValueEx( + key, + ToWString(cVars[i].Name).c_str(), + 0, + REG_SZ, + (const BYTE*)m_pVariableState->CurrentStringValue[i].data(), + m_pVariableState->CurrentStringValue[i].length() * sizeof(wchar_t) ); + } + else + { + RegDeleteValue( + key, + ToWString(cVars[i].Name).c_str() ); + } + break; + case CONTROL_TYPE_SEPARATOR: + break; + } + } +} + +// Updates a Control Based on Current State +void CControlsPage::UpdateControl( int vIndex ) +{ + CButton *cCurEnabled, *cDefEnabled; + CEdit *cCurEdit, *cDefEdit; + + cCurEnabled = (CButton *)GetDlgItem(IDC_CHECK_CUR_ENABLED); + cDefEnabled = (CButton *)GetDlgItem(IDC_CHECK_DEF_ENABLED); + cCurEdit = (CEdit *)GetDlgItem(IDC_CUR_EDIT); + cDefEdit = (CEdit *)GetDlgItem(IDC_DEF_EDIT); + + switch (cVars[vIndex].Type) + { + case CONTROL_TYPE_BOOL: + cCurEnabled->ShowWindow(SW_SHOW); + cDefEnabled->ShowWindow(SW_SHOW); + cCurEdit->ShowWindow(SW_HIDE); + cDefEdit->ShowWindow(SW_HIDE); + + if (m_pVariableState->CurrentIntValue[vIndex]) + { + cCurEnabled->SetCheck(TRUE); + } + else + { + cCurEnabled->SetCheck(FALSE); + } + + if (cVars[vIndex].defIntValue) + { + cDefEnabled->SetCheck(TRUE); + } + else + { + cDefEnabled->SetCheck(FALSE); + } + break; + + // For int and String variables, setting the + // current and default value causes the ON_EN_CHANGE + // method to get called spuriously setting the varsChanged flag + // to workaround this, we first hide the edit control + // set the text to the required value, and then show the control + // The varChange method checks for visibility before doing anything + case CONTROL_TYPE_INT: + cCurEdit->ShowWindow(SW_HIDE); + SetDlgItemInt(IDC_CUR_EDIT, m_pVariableState->CurrentIntValue[vIndex]); + cCurEdit->ShowWindow(SW_SHOW); + cDefEdit->ShowWindow(SW_SHOW); + SetDlgItemInt(IDC_DEF_EDIT, cVars[vIndex].defIntValue); + cCurEnabled->ShowWindow(SW_HIDE); + cDefEnabled->ShowWindow(SW_HIDE); + break; + case CONTROL_TYPE_STRING: + cCurEdit->ShowWindow(SW_HIDE); + SetDlgItemText(IDC_CUR_EDIT, m_pVariableState->CurrentStringValue[vIndex].c_str()); + cCurEdit->ShowWindow(SW_SHOW); + cDefEdit->ShowWindow(SW_SHOW); + SetDlgItemText(IDC_DEF_EDIT, ToWString(cVars[vIndex].defStrValue).c_str()); + cCurEnabled->ShowWindow(SW_HIDE); + cDefEnabled->ShowWindow(SW_HIDE); + break; + case CONTROL_TYPE_SEPARATOR: + cCurEnabled->ShowWindow(SW_HIDE); + cDefEnabled->ShowWindow(SW_HIDE); + cCurEdit->ShowWindow(SW_HIDE); + cDefEdit->ShowWindow(SW_HIDE); + break; + } + + // Set the help text for the variable + SetDlgItemText(IDC_EDIT_HELP, ToWString(cVars[vIndex].HelpText).c_str()); +} + +// Updates a Variable State and Icon State based on a Control +void CControlsPage::UpdateVarState(int vIndex) +{ + CButton *cCurEnabled = (CButton*)GetDlgItem(IDC_CHECK_CUR_ENABLED); + + switch (cVars[vIndex].Type) + { + case CONTROL_TYPE_BOOL: + if (cCurEnabled->GetCheck() == TRUE) + { + m_pVariableState->CurrentIntValue[vIndex] = TRUE; + } + else + { + m_pVariableState->CurrentIntValue[vIndex] = FALSE; + } + break; + + case CONTROL_TYPE_INT: + m_pVariableState->CurrentIntValue[vIndex] = GetDlgItemInt(IDC_CUR_EDIT); + break; + + case CONTROL_TYPE_STRING: + { + int length = GetDlgItem( IDC_CUR_EDIT )->GetWindowTextLength(); + + m_pVariableState->CurrentStringValue[vIndex].resize( length ); + + GetDlgItemText( + IDC_CUR_EDIT, + &m_pVariableState->CurrentStringValue[vIndex][0], + m_pVariableState->CurrentStringValue[vIndex].size() * sizeof(wchar_t) ); + } + break; + + case CONTROL_TYPE_SEPARATOR: + break; + } + + switch (cVars[vIndex].Type) + { + case CONTROL_TYPE_BOOL: + case CONTROL_TYPE_INT: + if (m_pVariableState->CurrentIntValue[vIndex] == cVars[vIndex].defIntValue) + { + if (m_pVariableState->CurrentIconState[vIndex] == ICON_STATE_MODIFIED_NONDEFAULT) + { + m_pVariableState->CurrentIconState[vIndex] = ICON_STATE_DEFAULT; + } + else if (m_pVariableState->CurrentIconState[vIndex] == ICON_STATE_NONDEFAULT) + { + m_pVariableState->CurrentIconState[vIndex] = ICON_STATE_MODIFIED_DEFAULT; + } + } + else + { + if (m_pVariableState->CurrentIconState[vIndex] == ICON_STATE_DEFAULT) + { + m_pVariableState->CurrentIconState[vIndex] = ICON_STATE_MODIFIED_NONDEFAULT; + } + else if (m_pVariableState->CurrentIconState[vIndex] == ICON_STATE_MODIFIED_DEFAULT) + { + m_pVariableState->CurrentIconState[vIndex] = ICON_STATE_NONDEFAULT; + } + } + break; + case CONTROL_TYPE_STRING: + if (m_pVariableState->CurrentStringValue[vIndex] == ToWString(cVars[vIndex].defStrValue)) + { + if (m_pVariableState->CurrentIconState[vIndex] == ICON_STATE_MODIFIED_NONDEFAULT) + { + m_pVariableState->CurrentIconState[vIndex] = ICON_STATE_DEFAULT; + } + else if (m_pVariableState->CurrentIconState[vIndex] == ICON_STATE_NONDEFAULT) + { + m_pVariableState->CurrentIconState[vIndex] = ICON_STATE_MODIFIED_DEFAULT; + } + } + else + { + if (m_pVariableState->CurrentIconState[vIndex] == ICON_STATE_DEFAULT) + { + m_pVariableState->CurrentIconState[vIndex] = ICON_STATE_MODIFIED_NONDEFAULT; + } + else if (m_pVariableState->CurrentIconState[vIndex] == ICON_STATE_MODIFIED_DEFAULT) + { + m_pVariableState->CurrentIconState[vIndex] = ICON_STATE_NONDEFAULT; + } + } + break; + + case CONTROL_TYPE_SEPARATOR: + m_pVariableState->CurrentIconState[vIndex] = ICON_STATE_SEPARATOR; + break; + } +} + +// Handle the Apply or OK Buttons +void CControlsPage::ApplyChanges() +{ + WriteSettingsToRegistry(); + + m_pVariableState->UpdateIconStates(); + UpdateIcons(); + + SetModified(FALSE); +} + +// Called when a boolean Control changes +void CControlsPage::OnVarChangeBool() +{ + CButton *cCurEnabled = (CButton *)GetDlgItem(IDC_CHECK_CUR_ENABLED); + if (cCurEnabled->IsWindowVisible() == FALSE) { + // If the window is not visible, do nothing + return; + } + + UpdateVarState(m_SelectedItem); + UpdateIcon(m_SelectedItem); + + SetModified(TRUE); +} + +// Called when a int/str Control changes +void CControlsPage::OnVarChangeEdit() +{ + CEdit *cCurEdit = (CEdit *)GetDlgItem(IDC_CUR_EDIT); + if (cCurEdit->IsWindowVisible() == FALSE) { + // If the window is not visible, do nothing + return; + } + + UpdateVarState(m_SelectedItem); + UpdateIcon(m_SelectedItem); + SetModified(TRUE); +} + +// Called when the user selects a different item in the list +void CControlsPage::OnSelectionChange() +{ + POSITION p = m_pListCtrl->GetFirstSelectedItemPosition(); + int vIndex; + + if (p == NULL) { + return; + } else { + vIndex = m_pListCtrl->GetNextSelectedItem(p); + } + + UpdateControl(vIndex); + m_SelectedItem = vIndex; +} + +// handles changes in selection of a listCtrl Item +void CControlsPage::OnNotify( NMHDR * pNMHDR, LRESULT * pResult ) +{ + NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR; + + // call the OnSelectionChange function + // only if the state has LVIS_SELECTED + + if (pNMListView->uNewState & LVIS_SELECTED) { + OnSelectionChange(); + } + + *pResult = 0; +} + +BEGIN_MESSAGE_MAP(CControlsPage, CPropertyPage) + ON_BN_CLICKED(IDC_CMD_DEFAULTS, ResetDefaults) + ON_BN_CLICKED(IDC_CHECK_CUR_ENABLED, OnVarChangeBool) + ON_EN_CHANGE(IDC_CUR_EDIT, OnVarChangeEdit) + ON_NOTIFY(LVN_ITEMCHANGED, IDC_VAR_LIST, OnNotify) +END_MESSAGE_MAP() + +/////////////////////////////////////////////////////////////////////////////// + +// ========================================================================== +// About page + +typedef cl_int (CL_API_CALL *pfnGetPlatformIDs) ( + cl_uint num_entries, + cl_platform_id* platforms, + cl_uint* num_platforms ); +typedef cl_int (CL_API_CALL *pfnGetPlatformInfo) ( + cl_platform_id platform, + cl_platform_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret ); +typedef cl_int (CL_API_CALL *pfnGetDeviceIDs) ( + cl_platform_id platform, + cl_device_type device_type, + cl_uint num_entries, + cl_device_id* devices, + cl_uint* num_devices ); +typedef cl_int (CL_API_CALL *pfnGetDeviceInfo) ( + cl_device_id device, + cl_device_info param_name, + size_t param_value_size, + void* param_value, + size_t* param_value_size_ret ); + +CAboutPage::CAboutPage( CVariableState* pVariableState ) : + CPropertyPage(IDD_ABOUT_PAGE, IDS_INFO), + m_pVariableState( pVariableState ) +{ +} + +BOOL CAboutPage::OnInitDialog() +{ + CPropertyPage::OnInitDialog(); + + CComboBox* pPlatformComboBox = (CComboBox*)GetDlgItem(IDC_PLATFORM_LIST); + + HMODULE hModule = ::LoadLibraryA( "opencl.dll" ); + if( hModule ) + { + pfnGetPlatformIDs dclGetPlatformIDs = (pfnGetPlatformIDs)::GetProcAddress( hModule, "clGetPlatformIDs" ); + pfnGetPlatformInfo dclGetPlatformInfo = (pfnGetPlatformInfo)::GetProcAddress( hModule, "clGetPlatformInfo" ); + + cl_int errorCode = CL_SUCCESS; + cl_uint numPlatforms = 0; + + if( errorCode == CL_SUCCESS && + dclGetPlatformIDs ) + { + errorCode = dclGetPlatformIDs( + 0, + NULL, + &numPlatforms ); + } + if( errorCode != CL_SUCCESS || + numPlatforms == 0 ) + { + pPlatformComboBox->AddString(L"No OpenCL platforms detected!"); + } + + m_Platforms.resize( numPlatforms ); + + if( errorCode == CL_SUCCESS && + dclGetPlatformIDs && + dclGetPlatformInfo && + numPlatforms != 0 ) + { + errorCode = dclGetPlatformIDs( + numPlatforms, + m_Platforms.data(), + NULL ); + + for( cl_uint p = 0; p < numPlatforms; p++ ) + { + size_t stringLength = 0; + if( errorCode == CL_SUCCESS ) + { + errorCode = dclGetPlatformInfo( + m_Platforms[p], + CL_PLATFORM_NAME, + 0, + NULL, + &stringLength ); + } + if( errorCode == CL_SUCCESS ) + { + std::vector str( stringLength ); + errorCode = dclGetPlatformInfo( + m_Platforms[p], + CL_PLATFORM_NAME, + str.size(), + str.data(), + NULL ); + if( errorCode == CL_SUCCESS ) + { + pPlatformComboBox->AddString( + ToWString(str).c_str() ); + } + } + } + } + + ::FreeLibrary( hModule ); + } + + pPlatformComboBox->SetCurSel(0); + + OnPlatformListChange(); + OnDeviceListChange(); + + return TRUE; +} + +BOOL CAboutPage::OnSetActive() +{ + CPropertyPage::OnSetActive(); + + CListBox* pConfigSummaryList = (CListBox*)GetDlgItem(IDC_CONFIG_SUMMARY); + + // First, delete any existing strings in the config summary list. + for( int i = pConfigSummaryList->GetCount() - 1; i >= 0; i-- ) + { + pConfigSummaryList->DeleteString( i ); + } + + int n = 0; + for( int i = 0; i < cNumVars; i++ ) + { + if( m_pVariableState->CurrentIconState[i] == ICON_STATE_NONDEFAULT ) + { + std::ostringstream ss; + ss << cVars[i].Name << " = "; + switch( cVars[i].Type ) + { + case CONTROL_TYPE_BOOL: + ss << ( m_pVariableState->CurrentIntValue[i] ? + "true" : + "false" ); + break; + case CONTROL_TYPE_INT: + ss << m_pVariableState->CurrentIntValue[i]; + break; + case CONTROL_TYPE_STRING: + ss << ToString( m_pVariableState->CurrentStringValue[i] ); + break; + default: + ss << ""; + } + pConfigSummaryList->InsertString(n++, ToWString( ss.str() ).c_str() ); + } + } + if( n == 0 ) + { + pConfigSummaryList->InsertString(n++, L"No non-default controls." ); + } + + return TRUE; +} + +static cl_int GetDeviceInfoString( + pfnGetDeviceInfo dclGetDeviceInfo, + cl_device_id device, + cl_device_info param_name, + std::vector& param_value ) +{ + cl_int errorCode = CL_SUCCESS; + size_t size = 0; + + if( errorCode == CL_SUCCESS ) + { + errorCode = dclGetDeviceInfo( + device, + param_name, + 0, + NULL, + &size ); + } + + if( errorCode == CL_SUCCESS ) + { + param_value.resize( size ); + errorCode = dclGetDeviceInfo( + device, + param_name, + param_value.size(), + param_value.data(), + NULL ); + } + + return errorCode; +} + +void CAboutPage::OnPlatformListChange() +{ + CComboBox* pPlatformComboBox = (CComboBox*)GetDlgItem(IDC_PLATFORM_LIST); + CComboBox* pDeviceComboBox = (CComboBox*)GetDlgItem(IDC_DEVICE_LIST); + + // First, delete any existing strings in the device list. + for( int i = pDeviceComboBox->GetCount() - 1; i >= 0; i-- ) + { + pDeviceComboBox->DeleteString( i ); + } + + // Get the currently selected platform index. + cl_uint platformIndex = pPlatformComboBox->GetCurSel(); + + HMODULE hModule = ::LoadLibraryA( "opencl.dll" ); + if( hModule ) + { + pfnGetDeviceIDs dclGetDeviceIDs = (pfnGetDeviceIDs)::GetProcAddress( hModule, "clGetDeviceIDs" ); + pfnGetDeviceInfo dclGetDeviceInfo = (pfnGetDeviceInfo)::GetProcAddress( hModule, "clGetDeviceInfo" ); + + // Get the array of platforms. + cl_int errorCode = CL_SUCCESS; + cl_uint numDevices = 0; + + if( errorCode == CL_SUCCESS && + dclGetDeviceIDs && + platformIndex < m_Platforms.size() ) + { + errorCode = dclGetDeviceIDs( + m_Platforms[platformIndex], + CL_DEVICE_TYPE_ALL, + 0, + NULL, + &numDevices ); + if( errorCode != CL_SUCCESS || + numDevices == 0 ) + { + pDeviceComboBox->AddString(L"No OpenCL devices detected!"); + } + } + else + { + pDeviceComboBox->AddString(L"No OpenCL platforms detected!"); + } + + m_Devices.resize( numDevices ); + + if( errorCode == CL_SUCCESS && + dclGetDeviceIDs && + dclGetDeviceInfo ) + { + errorCode = dclGetDeviceIDs( + m_Platforms[platformIndex], + CL_DEVICE_TYPE_ALL, + m_Devices.size(), + m_Devices.data(), + NULL ); + for( cl_uint d = 0; d < numDevices; d++ ) + { + std::vector str; + if( errorCode == CL_SUCCESS ) + { + errorCode = GetDeviceInfoString( + dclGetDeviceInfo, + m_Devices[d], + CL_DEVICE_NAME, + str ); + } + if( errorCode == CL_SUCCESS ) + { + pDeviceComboBox->AddString( + ToWString(str).c_str() ); + } + } + } + + ::FreeLibrary( hModule ); + } + + pDeviceComboBox->SetCurSel(0); + + OnDeviceListChange(); +} + +void CAboutPage::OnDeviceListChange() +{ + CComboBox* pDeviceComboBox = (CComboBox*)GetDlgItem(IDC_DEVICE_LIST); + CListBox* pDeviceInfoList = (CListBox*)GetDlgItem(IDC_DEVICE_INFO); + + // First, delete any existing strings in the device info list. + for( int i = pDeviceInfoList->GetCount() - 1; i >= 0; i-- ) + { + pDeviceInfoList->DeleteString( i ); + } + + // Get the currently selected device index. + cl_uint deviceIndex = pDeviceComboBox->GetCurSel(); + if( deviceIndex < m_Devices.size() ) + { + HMODULE hModule = ::LoadLibraryA( "opencl.dll" ); + if( hModule ) + { + pfnGetDeviceInfo dclGetDeviceInfo = (pfnGetDeviceInfo)::GetProcAddress( hModule, "clGetDeviceInfo" ); + + cl_int errorCode = CL_SUCCESS; + + if( dclGetDeviceInfo ) + { + std::vector vendor; + errorCode |= GetDeviceInfoString( + dclGetDeviceInfo, + m_Devices[deviceIndex], + CL_DEVICE_VENDOR, + vendor ); + std::vector version; + errorCode |= GetDeviceInfoString( + dclGetDeviceInfo, + m_Devices[deviceIndex], + CL_DEVICE_VERSION, + version ); + std::vector driverVersion; + errorCode |= GetDeviceInfoString( + dclGetDeviceInfo, + m_Devices[deviceIndex], + CL_DRIVER_VERSION, + driverVersion ); + + if( errorCode == CL_SUCCESS ) + { + int n = 0; + pDeviceInfoList->InsertString(n++, ToWString(vendor).c_str() ); + pDeviceInfoList->InsertString(n++, ToWString(version).c_str() ); + pDeviceInfoList->InsertString(n++, ToWString(driverVersion).c_str() ); + } + else + { + pDeviceInfoList->InsertString(0, L"Error getting device info!"); + } + } + else + { + pDeviceInfoList->InsertString(0, L"Error getting device info function pointer!"); + } + + ::FreeLibrary( hModule ); + } + } +} + +BEGIN_MESSAGE_MAP(CAboutPage, CPropertyPage) + ON_CBN_SELCHANGE(IDC_PLATFORM_LIST, OnPlatformListChange) + ON_CBN_SELCHANGE(IDC_DEVICE_LIST, OnDeviceListChange) +END_MESSAGE_MAP() + +// ========================================================================== +// General utility routines + +// This is the message handler for the Apply Button +// Calls the Apply Method for each enabled page + +void CCLInterceptConfigSheet::OnApplyNow() +{ + m_UserPage.ApplyChanges(); + m_UserPage.SetModified(FALSE); + + PostMessage(WM_RESIZEPAGE); +} + +void CCLInterceptConfigSheet::OnOK() +{ + // call the ApplyNow method since the user clicked OK + OnApplyNow(); + + // Signal the end of the dialog + CPropertySheet::EndDialog(IDOK); +} + +BOOL CCLInterceptConfigSheet::OnInitDialog( ) +{ + CPropertySheet::OnInitDialog(); + + ModifyStyle(0, WS_THICKFRAME); + + RECT r; + GetWindowRect(&r); + m_baseSize.x = r.right - r.left; + m_baseSize.y = r.bottom - r.top; + + return TRUE; +} + +void CCLInterceptConfigSheet::OnSize(UINT nType, int cx, int cy) +{ + ModifyStyle(0, WS_THICKFRAME); + + CPropertySheet::OnSize(nType, cx, cy); + + PostMessage(WM_RESIZEPAGE); +} + +afx_msg LRESULT CCLInterceptConfigSheet::OnResizePage(WPARAM wParam, LPARAM lParam) +{ + Invalidate(); + return 0; +} + +BOOL CCLInterceptConfigSheet::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult) +{ + NMHDR* pnmh = (LPNMHDR) lParam; + + // the sheet resizes the page whenever it is activated + // so we need to resize it to what we want + if (TCN_SELCHANGE == pnmh->code) { + // user-defined message needs to be posted because page must + // be resized after TCN_SELCHANGE has been processed + PostMessage (WM_RESIZEPAGE); + } + + return CPropertySheet::OnNotify(wParam, lParam, pResult); +} + +void CCLInterceptConfigSheet::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) +{ + lpMMI->ptMinTrackSize.x = m_baseSize.x; + lpMMI->ptMinTrackSize.y = m_baseSize.y; + CPropertySheet::OnGetMinMaxInfo(lpMMI); +} + +// Message map for the property sheet +BEGIN_MESSAGE_MAP( CCLInterceptConfigSheet, CPropertySheet ) + ON_MESSAGE (WM_RESIZEPAGE, OnResizePage) + ON_WM_GETMINMAXINFO() + ON_BN_CLICKED(ID_APPLY_NOW, OnApplyNow) + ON_BN_CLICKED(IDOK, OnOK) +END_MESSAGE_MAP() + +//======================================================================== + +LONG WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + return DefWindowProc(hWnd, msg, wParam, lParam); +} + +BOOL GetPlatformInfo() +{ + return TRUE; +} + +CLInterceptConfigApp::CLInterceptConfigApp(void) +{ + // create a mutex to ensure only one instance of the OpenGL config app + hMutex = CreateMutex(NULL, FALSE, L"CLInterceptConfig"); + mutexState = GetLastError(); +} + +CLInterceptConfigApp::~CLInterceptConfigApp(void) +{ + // release the mutex + if (hMutex) + { + CloseHandle(hMutex); + hMutex = NULL; + } +} + +BOOL CLInterceptConfigApp::IsAnotherInstanceRunning(void) +{ + return (ERROR_ALREADY_EXISTS == mutexState); +} + +static BOOL CheckHKLMRegistryKey() +{ + HKEY key; + + LSTATUS success = RegOpenKeyEx( + HKEY_LOCAL_MACHINE, + REGISTRY_KEY, + 0, + KEY_READ, + &key ); + if( success == ERROR_SUCCESS ) + { + RegCloseKey(key); + } + + return success == ERROR_SUCCESS; +} + +BOOL CLInterceptConfigApp::InitInstance(void) +{ + if( IsAnotherInstanceRunning() == TRUE ) + { + MessageBox( + NULL, + L"The Intercept Layer for OpenCL Applications Configuration App is already Running!", + L"Error!", + MB_OK); + + AfxGetApp()->ExitInstance(); + return FALSE; + } + + if( CheckHKLMRegistryKey() == TRUE ) + { + MessageBox( + NULL, + L"The Intercept Layer for OpenCL Applications now stores its registry keys " + L"in HKEY_CURRENT_USER, but it appears as though there are registry keys in " + L"HKEY_LOCAL_MACHINE. To avoid confusion it is strongly recommended to " + L"remove the old registry keys in HKEY_LOCAL_MACHINE!", + L"Warning", + MB_OK); + } + + CCLInterceptConfigSheet cliSheet; + m_pMainWnd = &cliSheet; + + cliSheet.DoModal(); + + return FALSE; +} + +CLInterceptConfigApp cliConfigApp; diff --git a/config/CLIConfig.h b/config/CLIConfig.h new file mode 100644 index 00000000..bdd91c80 --- /dev/null +++ b/config/CLIConfig.h @@ -0,0 +1,170 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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. +*/ + +#pragma once + +// MFC include files +#include +#include +#include +#include + +#include + +#include "resource.h" + +#define WM_RESIZEPAGE WM_USER + 111 + +const TCHAR* REGISTRY_KEY = L"SOFTWARE\\INTEL\\IGFX\\CLINTERCEPT"; + +#include +#include "envVars.h" + +enum ICON_STATE +{ + ICON_STATE_NONDEFAULT = 0, + ICON_STATE_MODIFIED_NONDEFAULT = 1, + ICON_STATE_MODIFIED_DEFAULT = 2, + ICON_STATE_DEFAULT = 3, + ICON_STATE_SEPARATOR = 4, + NUM_ICON_STATES +}; + +struct CVariableState +{ + CVariableState(); + + std::vector CurrentIconState; + std::vector CurrentIntValue; + std::vector CurrentStringValue; + + void SetDefaultStates(); + void UpdateIconStates(); +}; + +class CControlsPage : public CPropertyPage +{ +public: + CControlsPage( CVariableState* pVariableState ); + + void ApplyChanges(); + +private: + CVariableState* m_pVariableState; + + CImageList m_ImageList; + CListCtrl* m_pListCtrl; + + int m_SelectedItem; + + void ResetDefaults(); + + void ReadSettingsFromRegistry(); + void WriteSettingsToRegistry() const; + void WriteSettingsToRegistryHelper( HKEY key ) const; + + void UpdateIcons() const; + + void UpdateVarState(int vIndex); + void UpdateControl(int vIndex); + void UpdateIcon(int vIndex) const; + + BOOL OnInitDialog(); + + afx_msg void OnVarChangeBool(); + afx_msg void OnVarChangeEdit(); + afx_msg void OnSelectionChange(); + afx_msg void OnNotify( NMHDR * pNotifyStruct, LRESULT * result ); + + DECLARE_MESSAGE_MAP() +}; + +class CAboutPage : public CPropertyPage +{ +public: + CAboutPage( CVariableState* pVariableState ); + +private: + CVariableState* m_pVariableState; + + std::vector m_Platforms; + std::vector m_Devices; + + BOOL OnInitDialog(); + BOOL OnSetActive(); + + afx_msg void OnPlatformListChange(); + afx_msg void OnDeviceListChange(); + + DECLARE_MESSAGE_MAP() +}; + +class CCLInterceptConfigSheet : public CPropertySheet +{ +public: + CVariableState m_VariableState; + + CAboutPage m_AboutPage; + CControlsPage m_UserPage; + + POINT m_baseSize; + POINT m_lastSize; + + // constructor + CCLInterceptConfigSheet() : + CPropertySheet( L"Intercept Layer for OpenCL Applications Configuration App"), + m_AboutPage( &m_VariableState ), + m_UserPage( &m_VariableState ) + { + m_baseSize.x = m_baseSize.y = 0; + + AddPage( &m_UserPage ); + AddPage( &m_AboutPage ); + } + + BOOL OnInitDialog(); + void OnSize(UINT nType, int cx, int cy); + BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult); + + afx_msg void OnOK(); + afx_msg void OnApplyNow(); + afx_msg LRESULT OnResizePage(WPARAM wParam, LPARAM lParam); + afx_msg void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI); + + DECLARE_MESSAGE_MAP() +}; + +// the main configApp class is derived from a CWinApp +// application class +class CLInterceptConfigApp : public CWinApp +{ +public: + HANDLE hMutex; // to allow only one instance + DWORD mutexState; // + + CLInterceptConfigApp(); + ~CLInterceptConfigApp(); + + BOOL IsAnotherInstanceRunning(); + + virtual BOOL InitInstance(); +}; diff --git a/config/CLIConfig.rc b/config/CLIConfig.rc new file mode 100644 index 00000000..7270fca1 --- /dev/null +++ b/config/CLIConfig.rc @@ -0,0 +1,202 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""CLIConfig_version.rc2""\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUT_PAGE DIALOGEX 0, 0, 393, 301 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | WS_CHILD | WS_CAPTION +CAPTION "Information" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + LTEXT "Platform:",IDC_PLATFORM_LABEL,13,40,28,8 + LTEXT "Device:",IDC_DEVICE_LABEL,13,59,28,8 + LISTBOX IDC_DEVICE_INFO,229,27,150,40,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP,WS_EX_CLIENTEDGE + GROUPBOX "Device Info",IDC_DEVICE_INFO_FRAME,222,15,164,58 + COMBOBOX IDC_PLATFORM_LIST,48,38,167,100,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_DEVICE_LIST,48,58,167,100,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP + CONTROL "https://github.com/intel/opencl-intercept-layer",IDC_URL, + "MfcLink",WS_TABSTOP,48,18,167,15 + LTEXT "Link:",IDC_URL_LABEL,13,19,28,8 + LISTBOX IDC_CONFIG_SUMMARY,13,97,366,188,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP,WS_EX_CLIENTEDGE + GROUPBOX "Configuration Summary",IDC_CONFIGURATION_SUMMARY_FRAME,7,86,379,208 +END + +IDD_VARS_PAGE DIALOGEX 0, 0, 399, 302 +STYLE DS_SETFONT | WS_CHILD | WS_CAPTION +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + CONTROL "List1",IDC_VAR_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOLABELWRAP | LVS_NOCOLUMNHEADER | WS_BORDER | WS_TABSTOP,9,18,274,185 + CONTROL "Enabled",IDC_CHECK_CUR_ENABLED,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_TABSTOP,301,20,67,15 + EDITTEXT IDC_CUR_EDIT,301,20,69,15,ES_MULTILINE | ES_AUTOHSCROLL | NOT WS_VISIBLE + PUSHBUTTON "Reset Defaults",IDC_CMD_DEFAULTS,316,271,68,21 + GROUPBOX "Default Value",IDC_STATIC_DEF,294,46,90,36 + GROUPBOX "Current Value",IDC_STATIC_CUR,294,5,90,36 + GROUPBOX "Control Variable",IDC_VAR_FRAME,4,5,284,204 + GROUPBOX "Description",IDC_STATIC_HELP_GROUP,4,212,284,80 + EDITTEXT IDC_DEF_EDIT,300,60,70,15,ES_AUTOHSCROLL | NOT WS_VISIBLE | WS_DISABLED | NOT WS_TABSTOP + CONTROL "Enabled",IDC_CHECK_DEF_ENABLED,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,300,60,70,15 + GROUPBOX "Legend for Variable States",IDC_STATIC_L0,294,117,90,92 + ICON IDI_STATE_NONDEFAULT,IDC_STATIC_L4,349,157,21,20,SS_SUNKEN,WS_EX_CLIENTEDGE + ICON IDI_STATE_DEFAULT,IDC_STATIC_L8,349,131,21,20,SS_SUNKEN,WS_EX_CLIENTEDGE + ICON IDI_STATE_MODIFIED,IDC_STATIC_L2,349,183,21,20,SS_SUNKEN,WS_EX_CLIENTEDGE + LTEXT "Default State",IDC_STATIC_L7,297,131,35,19 + LTEXT "Non-Default State",IDC_STATIC_L3,297,157,41,22 + LTEXT "Unsaved Variable",IDC_STATIC_L1,297,183,48,21 + EDITTEXT IDC_EDIT_HELP,8,223,276,61,ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY | NOT WS_BORDER | WS_VSCROLL | NOT WS_TABSTOP +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_ABOUT_PAGE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 386 + TOPMARGIN, 7 + BOTTOMMARGIN, 294 + END + + IDD_VARS_PAGE, DIALOG + BEGIN + LEFTMARGIN, 4 + RIGHTMARGIN, 384 + BOTTOMMARGIN, 292 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_LOGO ICON "clintercept_logo.ico" + +IDI_STATE_NONDEFAULT ICON "nondefault.ico" + +IDI_STATE_DEFAULT ICON "default.ico" + +IDI_STATE_DISABLED ICON "disabled.ico" + +IDI_STATE_MODIFIED ICON "modified.ico" + +IDI_STATE_MODIFIED_DEFAULT ICON "modified_default.ico" + +IDI_STATE_MODIFIED_NONDEFAULT ICON "modified_nondefault.ico" + +IDI_STATE_SEPARATOR ICON "separator.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog Info +// + +IDD_ABOUT_PAGE DLGINIT +BEGIN + IDC_URL, 0x37c, 204, 0 +0x4d3c, 0x4346, 0x694c, 0x6b6e, 0x555f, 0x6c72, 0x683e, 0x7474, 0x7370, +0x2f3a, 0x672f, 0x7469, 0x7568, 0x2e62, 0x6f63, 0x2f6d, 0x6e69, 0x6574, +0x2f6c, 0x706f, 0x6e65, 0x6c63, 0x692d, 0x746e, 0x7265, 0x6563, 0x7470, +0x6c2d, 0x7961, 0x7265, 0x2f3c, 0x464d, 0x4c43, 0x6e69, 0x5f6b, 0x7255, +0x3e6c, 0x4d3c, 0x4346, 0x694c, 0x6b6e, 0x555f, 0x6c72, 0x7250, 0x6665, +0x7869, 0x3c3e, 0x4d2f, 0x4346, 0x694c, 0x6b6e, 0x555f, 0x6c72, 0x7250, +0x6665, 0x7869, 0x3c3e, 0x464d, 0x4c43, 0x6e69, 0x5f6b, 0x6f54, 0x6c6f, +0x6974, 0x3e70, 0x2f3c, 0x464d, 0x4c43, 0x6e69, 0x5f6b, 0x6f54, 0x6c6f, +0x6974, 0x3e70, 0x4d3c, 0x4346, 0x694c, 0x6b6e, 0x465f, 0x6c75, 0x546c, +0x7865, 0x5474, 0x6f6f, 0x746c, 0x7069, 0x463e, 0x4c41, 0x4553, 0x2f3c, +0x464d, 0x4c43, 0x6e69, 0x5f6b, 0x7546, 0x6c6c, 0x6554, 0x7478, 0x6f54, +0x6c6f, 0x6974, 0x3e70, + 0 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// AFX_DIALOG_LAYOUT +// + +IDD_ABOUT_PAGE AFX_DIALOG_LAYOUT +BEGIN + 0 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_USER "User Settings" + IDS_INFO "Information" +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "CLIConfig_version.rc2" +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/config/CLIConfig_version.rc2 b/config/CLIConfig_version.rc2 new file mode 100644 index 00000000..85b8464a --- /dev/null +++ b/config/CLIConfig_version.rc2 @@ -0,0 +1,62 @@ +/* +// Copyright (c) 2018 Intel Corporation +// +// 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. +*/ + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 2,2,0,0 + PRODUCTVERSION 2,2,0,0 + FILEFLAGSMASK 0x0L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x0L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Intel(R) Corporation" + VALUE "FileDescription", "Intercept Layer for OpenCL(tm) Applications Configuration App" + VALUE "FileVersion", "v2.2" + VALUE "InternalName", "CLIConfig" + VALUE "LegalCopyright", "Copyright(C) Intel Corporation 2018" + VALUE "OriginalFilename", "CLIConfig.exe" + VALUE "ProductName", "Intercept Layer for OpenCL(tm) Applications Configuration App" + VALUE "ProductVersion", "v2.2" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// diff --git a/config/CMakeLists.txt b/config/CMakeLists.txt new file mode 100644 index 00000000..aa8ed0bc --- /dev/null +++ b/config/CMakeLists.txt @@ -0,0 +1,62 @@ +# Copyright (c) 2018 Intel Corporation +# +# 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. + +set(CMAKE_MFC_FLAG 2) +add_definitions(-DUNICODE -D_UNICODE) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/.. +) + +set( CLICONFIG_ICON_FILES + clintercept_logo.ico + default.ico + disabled.ico + modified.ico + modified_default.ico + modified_nondefault.ico + nondefault.ico + separator.ico +) +source_group( Icons FILES + ${CLICONFIG_ICON_FILES} +) + +set( CLICONFIG_SOURCE_FILES + CLIConfig.cpp + CLIConfig.h + CLIConfig.rc + CLIConfig_version.rc2 + envVars.h + resource.h +) +source_group( Source FILES + ${CLICONFIG_SOURCE_FILES} +) + +add_executable(CLIConfig + ${CLICONFIG_ICON_FILES} + ${CLICONFIG_SOURCE_FILES} +) +set_target_properties(CLIConfig PROPERTIES LINK_FLAGS "/SUBSYSTEM:WINDOWS") + +foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} ) + install(TARGETS CLIConfig DESTINATION ${OUTPUTCONFIG}/Config CONFIGURATIONS ${OUTPUTCONFIG}) +endforeach( OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES ) diff --git a/config/clintercept_logo.ico b/config/clintercept_logo.ico new file mode 100644 index 0000000000000000000000000000000000000000..89ad701aa76e4aa7a5ce9fffe321fd9ae603c296 GIT binary patch literal 215470 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 literal 0 HcmV?d00001 diff --git a/config/default.ico b/config/default.ico new file mode 100644 index 0000000000000000000000000000000000000000..3eaafe50bb9998f177fdc16a887c06e62ed686b8 GIT binary patch literal 766 zcmc&yK@!3s4BU3S>&bH_C!gWdcpA^~*dN4Ahz!;q#EYRJY?g%S06=Gw!5H?G~+ybz0lyZUF;{aS4 zZ}dXH(-#~$143@!gd`b}7C3W>S}UUy79Y%fMd$F8K=mP{m6 zm9}Anv)XH-pwn3}iHCO4a2DI-c^~_S1DgQ7;r{>IRxisAFFpP0-t?x7gZlkezFP1( G{MDYTeu +static CONTROL_TYPE GetControlType() +{ + return CONTROL_TYPE_INT; +} + +template<> +static CONTROL_TYPE GetControlType() +{ + return CONTROL_TYPE_BOOL; +} + +template<> +static CONTROL_TYPE GetControlType() +{ + return CONTROL_TYPE_STRING; +} + +template +static const char* GetStringDefault( T dummy ) +{ + return ""; +} +template<> +static const char* GetStringDefault( const char* init ) +{ + return init; +} + +template +static const int GetIntDefault( T init ) +{ + return init; +} +template<> +static const int GetIntDefault( const char* dummy ) +{ + return 0; +} + +struct VarDescription +{ + CONTROL_TYPE Type; + const std::string Name; + const std::string defStrValue; + int defIntValue; + const std::string HelpText; +}; + +#define CLI_CONTROL( _type, _name, _init, _desc ) \ +{ \ + GetControlType<_type>(), \ + #_name, \ + GetStringDefault(_init), \ + GetIntDefault(_init), \ + _desc, \ +}, + +#define CLI_CONTROL_SEPARATOR( _name ) \ +{ \ + CONTROL_TYPE_SEPARATOR, \ + #_name, \ + "", \ + 0, \ + "", \ +}, + +static const VarDescription cVars[] = +{ + { CONTROL_TYPE_BOOL, "BreakOnLoad", "", 0, "If set to a nonzero value, the Intercept Layer for OpenCL Applications will break into the debugger when the DLL is loaded." }, +#include "..\Src\controls.h" +}; + +const int cNumVars = sizeof(cVars) / sizeof(cVars[0]); + +#undef CLI_CONTROL +#undef CLI_CONTROL_SEPARATOR diff --git a/config/modified.ico b/config/modified.ico new file mode 100644 index 0000000000000000000000000000000000000000..f7cd6b2d3369183ebda713e4144f98a3b8e84509 GIT binary patch literal 766 zcmc&yK@!3s3``mCl8a}`$r(T4)A&-)TzHO0eh@c+*fNe*FD?yXvxJhgL=%Q{mJ~Nb zbb>#_9PA34=zy6hKzoe<5lRTvVaAeDO7JR3c}PT8FboYT{|zO%rxMM)ie&F+Wx;m~GuKaAY^Vo&fp5OTtGUJ9K~cokl(ITQ26AzwiCSeaCtP1-z?Y#(v?- Wn||u2+w>b%uDaj4zSH4%_$%(Ozh*xG literal 0 HcmV?d00001 diff --git a/config/modified_default.ico b/config/modified_default.ico new file mode 100644 index 0000000000000000000000000000000000000000..e131b5d6a0bbe97d8e6469b7513897f65a776875 GIT binary patch literal 766 zcmc&yK@!3s4BU3S>&Y{dlQVw8r|~AQTzHO0eh@c-Vr3kyUR)Z&W=Tszfr%r80}Qtr zIMSbJfp(!y*l`9TwbzIgsl-?vXD*R*rZ++TfvT-&92PSE8$7wE0wSwO5y!siWv)E5 z0O>iUt4OFD25#9^7f+zPa8LNiQ-|#@(rL06e#>=!>wxt?L^de}})~4hjZpg8%>k literal 0 HcmV?d00001 diff --git a/config/modified_nondefault.ico b/config/modified_nondefault.ico new file mode 100644 index 0000000000000000000000000000000000000000..0155bfc7e8194ac32abe3503546b52afac2925a0 GIT binary patch literal 766 zcmc&yF%kkH3|t(0Nn>L}=`(y9U$Um*IhOnYHv#d)oAI=9xezvC??^bnafINZxb1)= z{h1bM7n;MKGZ2|uM`V(j7@OnFC34R6nyB`mR$b9JG^G4Dn0QD5kwv74;n4HNHST+W z^pw(7#J3F-S9Qya2~^#=2|n^Pp!NF1ePm_1IwmGMR| z^gBJ_%o(WUj;)d;Q)>^LxkPJ?(FpYqsy|8=jIMu(T4c6D!hQ^raCaVuXf_#FOf EPrw}_WdHyG literal 0 HcmV?d00001 diff --git a/config/resource.h b/config/resource.h new file mode 100644 index 00000000..0c2e8999 --- /dev/null +++ b/config/resource.h @@ -0,0 +1,56 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by CLIConfig.rc +// +#define IDD_VARS_PAGE 101 +#define IDD_ABOUT_PAGE 105 +#define IDI_LOGO 300 +#define IDI_STATE_NONDEFAULT 301 +#define IDI_STATE_MODIFIED_NONDEFAULT 302 +#define IDI_STATE_MODIFIED_DEFAULT 303 +#define IDI_STATE_DEFAULT 304 +#define IDI_STATE_DISABLED 305 +#define IDI_STATE_MODIFIED 306 +#define IDS_USER 503 +#define IDS_INFO 504 +#define IDI_ICON1 519 +#define IDI_STATE_SEPARATOR 519 +#define IDC_STATIC_L0 1003 +#define IDC_VAR_LIST 2000 +#define IDC_CUR_EDIT 2001 +#define IDC_DEF_EDIT 2002 +#define IDC_CHECK_DEF_ENABLED 2005 +#define IDC_CMD_DEFAULTS 2006 +#define IDC_VAR_FRAME 6000 +#define IDC_DEVICE_INFO 6000 +#define IDC_CHECK_CUR_ENABLED 6001 +#define IDC_CONFIG_SUMMARY 6001 +#define IDC_STATIC_CUR 6002 +#define IDC_STATIC_DEF 6003 +#define IDC_STATIC_L1 6004 +#define IDC_STATIC_L2 6005 +#define IDC_STATIC_L3 6006 +#define IDC_STATIC_L4 6007 +#define IDC_STATIC_L7 6010 +#define IDC_STATIC_L8 6011 +#define IDC_STATIC_HELP_GROUP 6012 +#define IDC_EDIT_HELP 6013 +#define IDC_PLATFORM_LABEL 6016 +#define IDC_DEVICE_LABEL 6017 +#define IDC_URL_LABEL 6018 +#define IDC_PLATFORM_LIST 6019 +#define IDC_DEVICE_LIST 6020 +#define IDC_DEVICE_INFO_FRAME 6021 +#define IDC_URL 6022 +#define IDC_CONFIGURATION_SUMMARY_FRAME 6023 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 520 +#define _APS_NEXT_COMMAND_VALUE 40005 +#define _APS_NEXT_CONTROL_VALUE 6024 +#define _APS_NEXT_SYMED_VALUE 600 +#endif +#endif diff --git a/config/separator.ico b/config/separator.ico new file mode 100644 index 0000000000000000000000000000000000000000..51737910f962ffa8edf9953ae5ee1403cd0c0c9b GIT binary patch literal 766 zcmc&wOBO&d5DU+qm35bn;b=Wtk7MZum7vUEVdF^~Gs)ES15_L#bO0JmHPAB~$Uu%{ zMSlfr1zIg`hyW*I+ycK$$%vE^3wfQ$SsBS8OZp$ot=|{$22bgX`IX^%>K7UcCv + source build/envsetup.sh + lunch + export TOP=`pwd` + export ANDROID_SRC=`pwd` + cd + mm + +The shared library will be named: clIntercept.so and placed in +`/out/target/product//system/lib`. Copy it to target +manually. + +--- + +\* Other names and brands may be claimed as the property of others. + +Copyright (c) 2018, Intel(R) Corporation + +[CMake]: https://cmake.org diff --git a/docs/chrome_tracing.md b/docs/chrome_tracing.md new file mode 100644 index 00000000..ac6e6868 --- /dev/null +++ b/docs/chrome_tracing.md @@ -0,0 +1,67 @@ +# Using the Intercept Layer for OpenCL Applications with Chrome Tracing + +The Intercept Layer for OpenCL Applications includes support for generating +JSON files compatible with the Google Chrome built-in profiler front end. +This document describes how to use the Intercept Layer for OpenCL Applications +to visualize how an OpenCL application executes using Chrome Tracing. + +## Background and Setup + +The Chrome Tracing Format is described [here][chrome_tracing_format]. + +To start up the Chrome Tracing front end, open Chrome and enter +`chrome://tracing` as your "URL". This should give you a Window that +looks like this one: + +![Empty Chrome Tracing Window](images/chrome_tracing_empty.png) + +## Configuring Chrome Tracing + +There are (currently) two Chrome Tracing-related controls for the Intercept +Layer for OpenCL Applications: + +* `ChromeCallLogging`: This is the control for tracing OpenCL host APIs. + It will plot OpenCL calls for each thread of the host application, + similar to those dumped when `CallLogging` is enabled. +* `ChromePerformanceTiming`: This is the control for tracing OpenCL + device commands. It will plot OpenCL commands for each command queue + created by the application, similar to those dumped when + `DevicePerformanceTiming` is enabled. + +## Collecting Chrome Tracing Data + +After setting one of these two controls (or both!), run your application, +and you should see a "CLIntercept_trace.json" file in your CLIntercept_Dump +directory. + +## Visualizing Chrome Tracing Data + +After collecting a "CLIntercept_Trace.json" file, simply click the "load" +button in the "chrome::tracing" UI, or drag your file into Chrome. + +If all goes well you will see a timegraph like this one: + +![Chrome Tracing Example](images/chrome_tracing_example.png) + +You can navigate around the timegraph with 'wasd' controls similar to many +popular games: `w` zooms in, `s` zooms out, `a` goes backwards in time, +and `d` moves forwards in time. + +Let's zoom in a bit and look at the what's in the timegraph: + +![Chrome Tracing Detail](images/chrome_tracing_detail.png) + +## Overhead + +Empirically, the overhead of Chrome Tracing is very low. The difference in +scores between a run of LuxMark without Chrome Tracing vs. enabling +`ChromeCallLogging` and `ChromePerformanceTiming` was less than 1%, and the +trace file size for the 2+ minutes of execution was 26MB. + +--- + +\* Other names and brands may be claimed as the property of others. + +Copyright (c) 2018, Intel(R) Corporation + +[chrome_tracing_format]: https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview diff --git a/docs/controls.md b/docs/controls.md new file mode 100644 index 00000000..b6e831da --- /dev/null +++ b/docs/controls.md @@ -0,0 +1,620 @@ +# How to Use the Intercept Layer for OpenCL Applications + +This file is automatically generated using the script `generate_controls_doc.py`. +Please do not edit it manually! + +By default, the Intercept Layer for OpenCL Applications will not modify any OpenCL +calls. You may notice some status messages being printed to stderr, but otherwise +your application should run exactly as it does without the Intercept Layer for +OpenCL Applications. + +## Controls + +The Intercept Layer for OpenCL Applications is controlled using the Windows +registry, Linux configuration files, or environment variables on all OSes. + +### Windows Registry + +On Windows, the Intercept Layer for OpenCL Applications reads its registry keys +from: + + HKEY_CURRENT_USER\SOFTWARE\INTEL\IGFX\CLIntercept + +This is the recommended registry location as it has several advantages over +HKEY_LOCAL_MACHINE: modifying the registry keys does not require Administrator +access, registry keys do not need to be set in multiple places, and each user +can set their own registry keys without affecting other users. + +For backwards compatibility, the Intercept Layer for OpenCL applications +will still read registry keys from: + + // For 32-bit systems, or 64-bit applications on a 64-bit system: + HKEY_LOCAL_MACHINE\SOFTWARE\INTEL\IGFX\CLIntercept + + // For 32-bit applications on a 64-bit system: + HKEY_LOCAL_MACHINE\SOFTWARE\WoW6432Node\INTEL\IGFX\CLIntercept + +If a registry is set in both HKCU and HKLM, the setting in HKCU will "win". + +### Linux Configuration Files + +On Linux, the Intercept Layer for OpenCL Applications will read control values +from ~/clintercept.conf. Controls may be set by putting the control on its own +line, followed by an equals sign, followed by the value you'd like to set the +option to. Lines that begin with a semi-colon(";"), a hash mark ("#"), or a +C++-style comment ("//") are ignored. For example, to enable CallLogging, put +a line in your ~/clintercept.conf that looks like: + + // Enable CallLogging: + CallLogging=1 + +### Environment Variables + +The Intercept Layer for OpenCL may be controlled using environment variables. +The name of the environment variable control is "CLI_" and the control name, to +distinguish controls from other environment variables, and to make it easy to +list all of the environment variable controls. So, to enable CallLogging, you +could type: + + export CLI_CallLogging=1 + +To disable CallLogging, you could type: + + unset CLI_CallLogging + +To list all environment variable controls, you could type: + + env | grep CLI_ + +### Setup and Loading Controls + +##### `DllName` (string) + +Used to control the DLL or Shared Library that the Intercept Layer for OpenCL Applications loads to make real OpenCL calls. If present, only this DLL name is loaded. If omitted, the Intercept Layer for OpenCL Applications tries to load the real OpenCL DLL from file names in this order: + +- real_OpenCL.dll (anywhere in the system path) +- %WINDIR%\SysWOW64\OpenCL.dll (32-bit DLLs only) +- %WINDIR%\System32\OpenCL.dll + +##### `BreakOnLoad` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will break into the debugger when it is loaded. + +### Logging Controls + +##### `AppendFiles` (bool) + +By default, the Intercept Layer for OpenCL Applications log files will be created from scratch when the intercept DLL is loaded, and any Intercept Layer for OpenCL Applications report files will be created from scratch when the intercept DLL is unloaded. If AppendFiles is set to a nonzero value, the Intercept Layer for OpenCL Applications will append to an existing file instead of recreating it. This can be useful if an application loads and unloads the intercept DLL multiple times, or to simply preserve log or report data from run-to-run. + +##### `LogToFile` (bool) + +If set to a nonzero value, sends log information to the file "clintercept\_log.txt" instead of to stderr. The log file will be placed in the directory "%SYSTEMDRIVE%\\Intel\\CLIntercept\_Dump\\\". + +##### `LogToDebugger` (bool) + +If set to a nonzero value, sends log information to the debugger instead of to stderr. If both LogToFile and LogToDebugger are nonzero then log information will be sent both to a file and to the debugger. + +##### `LogIndent` (int) + +Indents each log entry by this many spaces. + +##### `BuildLogging` (bool) + +If set to a nonzero value, logs the program build log after each call to clBuildProgram(). This will likely only function correctly for synchronous builds. Note that the build log is logged regardless of whether the program built successfully, which allows compiler warnings to be logged for successful compiles. + +##### `PreferredWorkGroupSizeMultipleLogging` (bool) + +If set to a nonzero value, logs the preferred work group size multiple for each kernel after each call to clCreateKernel(). On some devices this is the equivalent of the SIMD size for this kernel. + +##### `CallLogging` (bool) + +If set to a nonzero value, logs function entry and exit information for every OpenCL call. This can be used to easily determine which OpenCL call is causing an application to crash or fail or if a crash occurs outside of an OpenCL call. This setting is best used with LogToFile or LogToDebugger as it can generate a lot of log data. + +##### `CallLoggingEnqueueCounter` (bool) + +If set to a nonzero value, logs the enqueue counter in addition to function entry and exit information for every OpenCL call. This can be used to determine appropriate limits for DumpBuffersMinEnqueue, DumpBuffersMaxEnqueue, DumpImagesMinEnqueue, or DumpBuffersMaxEnqueue. If CallLogging is disabled then this control will have no effect. + +##### `CallLoggingThreadId` (bool) + +If set to a nonzero value, logs the ID of the calling thread in addition to function entry and exit information for every OpenCL call. This can be helpful when debugging multi-threading issues. + +##### `CallLoggingThreadNumber` (bool) + +If set to a nonzero value, logs the symbolic number of the calling thread in addition to function entry and exit information for every OpenCL call. This can be helpful when debugging multi-threading issues. + +##### `CallLoggingElapsedTime` (bool) + +If set to a nonzero value, logs the elapsed time in microseconds in addition to function entry and exit information for every OpenCL call, starting from the time the intercept DLL is loaded. + +##### `ITTCallLogging` (bool) + +If set to a nonzero value, logs function entry and exit information for every OpenCL call using the ITT APIs. This feature will only function if the Intercept Layer for OpenCL Applications is built with ITT support. + +##### `ChromeCallLogging` (bool) + +If set to a nonzero value, logs function entry and exit information for every OpenCL call to a JSON file that may be used for Chrome Tracing. + +##### `ErrorLogging` (bool) + +If set to a nonzero value, logs all OpenCL errors and the function name that caused the error. + +##### `ErrorAssert` (bool) + +If set to a nonzero value, breaks into the debugger when an OpenCL error occurs. + +##### `ContextCallbackLogging` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will install a callback for every context and log any calls to the context callback. The application's context callback, if any, will be invoked after the Intercept Layer for OpenCL Applications' context callback. + +##### `ContextHintLevel` (cl_uint) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will attempt to create contexts with the CL\_CONTEXT\_SHOW\_DIAGNOSTICS\_INTEL property set to the specified value. If this property is specified by the application, the Intercept Layer for OpenCL Applications will overwrite it with the specified value, otherwise the property and the specified value will be added to the list of context creation properties. This functionality is only available for OpenCL implementations that support the cl\_intel\_driver\_diagnostics extension. If this functionality is not available in the underlying OpenCL implementation, the unmodified list of context properties will be used to create the context instead. More information about this feature, including valid values and their meaning, can be found in the cl\_intel\_driver\_diagnostics extension specification. + +##### `EventCallbackLogging` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will install its own callback for every event callback and log the call to the event callback. The application's event callback will be invoked after the Intercept Layer for OpenCL Applications' event callback. + +##### `CLInfoLogging` (bool) + +If set to a nonzero value, logs information about the platforms and devices in the system on the first call to clGetPlatformIDs(). + +##### `LogDir` (string) + +If set, the Intercept Layer for OpenCL Applications will emit logs to this directory instead of the default log directory. + +### Performance Timing Controls + +##### `HostPerformanceTiming` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will track the minimum, maximum, and average host CPU time for each OpenCL entry point. When the process exits, this information will be printed to the file "clIntercept\_report.txt" in the directory "%SYSTEMDRIVE%\\Intel\\CLIntercept\_Dump\\\". + +##### `DevicePerformanceTiming` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will add event profiling to track the minimum, maximum, and average device time for each OpenCL command. This operation may be fairly intrusive and may have side effects; in particular it forces all command queues to be created with PROFILING\_ENABLED and may increment the reference count for application events. When the process exits, this information will be printed to the file "clIntercept\_report.txt" in the directory "%SYSTEMDRIVE%\\Intel\\CLIntercept\_Dump\\\". + +##### `DevicePerformanceTimeHashTracking` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will distinguish between OpenCL NDRange kernels from programs with different hashes for the purpose of device performance timing. + +##### `DevicePerformanceTimeKernelInfoTracking` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will distinguish between OpenCL NDRange kernels using information such as the kernel's Preferred Work Group Size Multiple (AKA SIMD size). + +##### `DevicePerformanceTimeGWSTracking` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will distinguish between OpenCL NDRange kernels with different global work sizes for the purpose of device performance timing. + +##### `DevicePerformanceTimeLWSTracking` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will distinguish between OpenCL NDRange kernels with different local work sizes for the purpose of device performance timing. + +##### `DevicePerformanceTimingSkipUnmap` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will skip device performance timing for unmap operations. This is a workaround for a bug in some OpenCL implementations, where querying events created from unmap operations results in driver crashes. + +##### `HostPerformanceTimeLogging` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will log the host elapsed time for each OpenCL entry point. This can be useful to identify OpenCL entry points that execute significantly slower or faster than average on the host. + +##### `DevicePerformanceTimeLogging` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will log the device execution time deltas for each OpenCL command. This can be useful to identify specific OpenCL commands that execute significantly slower or faster than average on the device. If DevicePerformanceTiming is disabled then this control will have no effect. + +##### `DevicePerformanceTimelineLogging` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will log the device execution times for each OpenCL command. This can be useful to visualize the execution timeline of OpenCL commands that execute on the device. If DevicePerformanceTiming is disabled then this control will have no effect. + +##### `DevicePerfCounterCustom` (string) + +If set, the Intercept Layer for OpenCL Applications will collect MDAPI metrics for the Metric Set corresponding to this value for each OpenCL command. Frequently used Metric Sets include: ComputeBasic, ComputeExtended, L3\_1, Sampler. The output file has the potential to be very big depending on the work load. This operation may be fairly intrusive and may have side effects; in particular it forces all command queues to be created with PROFILING\_ENABLED and may increment the reference count for application events. When the process exits, this information will be printed to the file "clintercept\_perfcounter\_dump\_\.txt" in the directory "%SYSTEMDRIVE%\\Intel\\CLIntercept\_Dump\\\". This feature will only function if the Intercept Layer for OpenCL Applications is built with MDAPI support. + +##### `DevicePerfCounterFile` (string) + +Full path to a custom MDAPI file. This can be used to add custom Metric Sets. + +##### `DevicePerfCounterTiming` (bool) + +If set to a nonzero value and DevicePerfCounterCustom is set, the Intercept Layer for OpenCL Applications will enable Intel GPU Performance Counters to track the minimum, maximum, and average performance counter deltas for each OpenCL command. This operation may be fairly intrusive and may have side effects; in particular it forces all command queues to be created with PROFILING\_ENABLED and may increment the reference count for application events. When the process exits, this information will be printed to the file "clIntercept\_report.txt" in the directory "%SYSTEMDRIVE%\\Intel\\CLIntercept\_Dump\\\". This feature will only function if the Intercept Layer for OpenCL Applications is built with MDAPI support. + +##### `ITTPerformanceTiming` (bool) + +[Note: This control makes ITT calls, but they appear to do nothing!] If set to a nonzero value, the Intercept Layer for OpenCL Applications will generate ITT-compatible performance timing data. Similar to DevicePerformanceTiming, this operation may be fairly intrusive and may have side effects; in particular it forces all command queues to be created with PROFILING\_ENABLED and may increment the reference count for application events. ITTPerformanceTiming will also silently create OpenCL command queues that support advanced performance counters if this functionality is available. This feature will only function if the Intercept Layer for OpenCL Applications is built with ITT support. + +##### `ITTShowOnlyExecutingEvents` (bool) + +[Note: This control makes ITT calls, but they appear to do nothing!] By default, when ITTPerformanceTiming is enabled, the Intercept Layer for OpenCL Applications will generate ITT-compatible information for all states of an OpenCL event: when the command was queued, when it was submitted, when it started executing, and when it finished executing. If ITTShowOnlyExecutingEvents is set to a nonzero value, the Intercept Layer for OpenCL Applications will only generate ITT-compatible instrumentation when an event begins executing and when an event ends executing. Since no information will be displayed about when a command is queued or submitted, this can sometimes make it easier to identify times when the device is idle. This feature will only function if the Intercept Layer for OpenCL Applications is built with ITT support. + +##### `ChromePerformanceTiming` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will generate device performance timing information in a JSON file that may be used for Chrome Tracing. + +### Controls for Dumping and Injecting Programs and Build Options + +##### `OmitProgramNumber` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will omit the program number from dumped file names and hash tracking. This can produce deterministic results even if programs are built in a non-deterministic order (say, by multiple threads). + +##### `SimpleDumpProgramSource` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will dump the last string(s) passed to clCreateProgramWithSource() to the file kernel.cl, and the last program options passed to clBuildProgram() to the file kernel.txt. These files will be dumped to the application's working directory. If an application fails to compile a program and exits the program immediately after detecting a compile failure SimpleDumpProgram may be all that is needed to identify the program and program options that are failing to compile. + +##### `DumpProgramSourceScript` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will dump every string passed to clCreateProgramWithSource() to its own file. The directory names and file names for the dumped files match the directory names and file names expected by a modified OpenCL conformance test script to capture kernels. This setting overrides SimpleDumpProgramSource, and if it is set to a nonzero value then the value of SimpleDumpProgramSource is ignored. + +##### `DumpProgramSource` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will dump every string passed to clCreateProgramWithSource() to its own file. The files will be dumped to "%SYSTEMDRIVE%\\Intel\\CLIntercept\_Dump\\\". The filename will have the form "CLI\_\\_\\_source.cl". Program options that are passed to clBuildProgram() or clCompileProgram() will be dumped to the same directory with the filename "CLI\_\\_\\_\\_options.txt". This setting can be used for information purposes to see all kernels that are used by an application or to dump programs for program injection. This setting overrides DumpProgramSourceScript and SimpleDumpProgramSource, and if it is set to a nozero value then the values of DumpProgramSourceScript and SimpleDumpProgramSource will be ignored. + +##### `DumpInputProgramBinaries` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will dump every program binary that is passed to clCreateProgramWithBinary() to its own file. The files will be dumped to "%SYSTEMDRIVE%\\Intel\\CLIntercept\_Dump\\\". The filename will have the form "CLI\_\\_\\_\.bin". This is the input program binary provided by the application, and not a device binary queried from the OpenCL implementation. In particular, note that it may be a SPIR 1.2 binary. + +##### `DumpProgramBinaries` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will dump every program binary that was successfully built with clBuildProgram() to its own file. The files will be dumped to "%SYSTEMDRIVE%\\Intel\\CLIntercept\_Dump\\\". The filename will have the form "CLI\_\\_\\_\\_\.bin". Program options that are passed to clBuildProgram() or clCompileProgram() will be dumped to the same directory with the filename "CLI\_\\_\\_\\_options.txt". This setting can be used to examine compiled program binaries or to dump program binaries for program binary injection. Note that this option dumps the output binary, which is a device binary, after calling clBuildProgram(). + +##### `DumpProgramSPIRV` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will dump every program IL binary passed to clCreateProgramWithIL() to its own file. The files will be dumped to "%SYSTEMDRIVE%\\Intel\\CLIntercept\_Dump\\\". The filename will have the form "CLI\_\\_\\_0000.spv" - for now at least!. Program options that are passed to clBuildProgram() or clCompileProgram() will be dumped to the same directory with the filename "CLI\_\\_\\_\\_options.txt". This setting can be used for information purposes to see all kernels that are used by an application or to dump SPIRV programs for SPIRV injection. + +##### `InjectProgramSource` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will look to inject potentially modified kernel source to clCreateProgramWithSource() and/or potentially modified options to clBuildProgram(). + +##### `InjectProgramBinaries` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will look to inject potentially modified kernel binaries via clCreateProgramWithBinary() in place of program text for each call to clCreateProgramWithSource(). This is typically done to reduce program compilation time or to use known good program binaries. + +##### `RejectProgramBinaries` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will reject kernel binaries passed via clCreateProgramWithBinary() and return CL\_INVALID\_BINARY. This can be used to force an application to re-compile program binaries from source. + +##### `InjectProgramSPIRV` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will look to inject potentially modified kernel SPIR-V binaries via clCreateProgramWithIL() in place of program text for each call to clCreateProgramWithSource(). + +##### `PrependProgramSource` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will look to prepend kernel code from a file to the application provided kernel source passed to clCreateProgramWithSource(). The Intercept Layer for OpenCL Applications will look for kernel source to prepend in the directory "%SYSTEMDRIVE%\\Intel\\CLIntercept\_Dump\\\". The files that are searched for are (in order) "CLI\_\\_\\_prepend.cl", "CLI\_\\_prepend.cl", and "CLI\_prepend.cl". + +##### `AppendBuildOptions` (string) + +If set, the Intercept Layer for OpenCL Applications will add these build options to the end of any application provided or injected build options for each call to clBuildProgram(). + +##### `DumpProgramBuildLogs` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will dump build logs for every device a program is built for to a separate file. The files will be dumped to "%SYSTEMDRIVE%\\Intel\\CLIntercept\_Dump\\\". The filename will have the form "CLI\_\\_\\_\\_\\_build\_log.txt". + +### Controls for Automatically Creating SPIR-V Modules + +##### `AutoCreateSPIRV` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will automatically create SPIR-V modules by invoking CLANG each time a program is built. The files will be dumped to "%SYSTEMDRIVE%\\Intel\\CLIntercept\_Dump\\\". The filename will have the form "CLI\_\\_\\_\.spv". Because invoking CLANG requires a file containing the OpenCL C source, setting this option implicitly sets DumpProgramSource as well. Additionally, this feature is not available for injected program source. + +##### `SPIRVClang` (string) + +The clang executable used to compile an OpenCL C program to a SPIR-V module. This can be an executable in the system path, a relative path, or a full absolute path. + +##### `SPIRVCLHeader` (string) + +The OpenCL header file used to compile an OpenCL C program to a SPIR-V module. This must be a relative path or a full absolute path. + +##### `SPIRVDis` (string) + +The spirv-dis executable used to optionally disassemble the compiled SPIR-V module to a SPIR-V text representation. This can be an executable in the system path, a relative path, or a full absolute path. + +##### `DefaultOptions` (string) + +This is the list of options that is implicitly passed to CLANG to build a non-OpenCL 2.0 SPIR-V module. Any application-provided build options will be appended to these build options. + +##### `OpenCL2Options` (string) + +This is the list of options that is implicitly passed to CLANG to build an OpenCL 2.0 SPIR-V module. Any application-provided build options will be appended to these build options. + +### Controls for Dumping Buffers and Images + +##### `DumpArgumentsOnSet` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will dump the argument value on calls to clSetKernelArg(). Arguments are dumped as raw binary data to "%SYSTEMDRIVE%\\Intel\\CLIntercept\_Dump\\\\\SetKernelArg". The filenames will have the form "SetKernelArg\_\\_Kernel\_\\_Arg\_\.bin". + +##### `DumpBuffersAfterCreate` (bool) + +If set, the Intercept Layer for OpenCL Applications will dump buffers to a file after creation. This control still honors the enqueue counter limits, even though no enqueues are involved during buffer creation. Currently only works for cl\_mem buffers created from host pointers. + +##### `DumpBuffersAfterMap` (bool) + +If set, the Intercept Layer for OpenCL Applications will dump the contents of a buffer to a file after the buffer is mapped. Only valid if the buffer is NOT mapped with CL\_MAP\_WRITE\_INVALIDATE\_REGION. If the buffer was mapped non-blocking, this may insert a clFinish() into the command queue, which may have functional or performance implications. + +##### `DumpBuffersBeforeUnmap` (bool) + +If set, the Intercept Layer for OpenCL Applications will dump the contents of a buffer to a file immediately before the buffer is unmapped. This is done by inserting a blocking clEnqueueMapBuffer() (and matching clEnqueueUnmapMemObject()) into the command queue, which may have functional or performance implications. + +##### `DumpBuffersBeforeEnqueue` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will dump buffers before calls to clEnqueueNDRangeKernel(). Only buffers that are kernel arguments for the kernel being enqueued are dumped. Buffers are dumped as raw binary data to "%SYSTEMDRIVE%\\Intel\\CLIntercept\_Dump\\\\\memDumpPreEnqueue". The filenames will have the form "Enqueue\_\\_Kernel\_\\_Arg\_\\_Buffer\_\.bin". + +##### `DumpBuffersAfterEnqueue` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will dump buffers after calls to clEnqueueNDRangeKernel(). Only buffers that are kernel arguments for the kernel being enqueued are dumped. Buffers are dumped as raw binary data to "%SYSTEMDRIVE%\\Intel\\CLIntercept\_Dump\\\\\memDumpPostEnqueue". The filenames will have the form "Enqueue\_\\_Kernel\_\\_Arg\_\\_Buffer\_\.bin". Note that this is the same naming convention as with DumpBuffersBeforeEnqueue, so the changes resulting from an enqueue can be determined by diff'ing the preEnqueue folder with the postEnqueue folder. + +##### `DumpBuffersForKernel` (string) + +If set, the Intercept Layer for OpenCL Applications will only dump buffers when the specified kernel is enqueued. This control is ignored unless DumpBuffersBeforeEnqueue or DumpBuffersAfterEnqueue are enabled. + +##### `DumpImagesBeforeEnqueue` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will dump images before calls to clEnqueueNDRangeKernel(). Only images that are kernel arguments for the kernel being enqueued are dumped. Images are dumped as raw binary data to "%SYSTEMDRIVE%\\Intel\\CLIntercept\_Dump\\\\\memDumpPreEnqueue". The filenames will have the form "Enqueue\_\\_Kernel\_\\_Arg\_\\_Image\_\\_\x\x\\_\bpp.raw". + +##### `DumpImagesAfterEnqueue` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will dump images after calls to clEnqueueNDRangeKernel(). Only images that are kernel arguments for the kernel being enqueued are dumped. Images are dumped as raw binary data to "%SYSTEMDRIVE%\\Intel\\CLIntercept\_Dump\\\\\memDumpPostEnqueue". The filenames will have the form "Enqueue\_\\_Kernel\_\\_Arg\_\\_Image\_\\_\x\x\\_\bpp.raw". Note that this is the same naming convention as with DumpImagesBeforeEnqueue, so the changes resulting from an enqueue can be determined by diff'ing the preEnqueue folder with the postEnqueue folder. + +##### `DumpImagesForKernel` (string) + +If set, the Intercept Layer for OpenCL Applications will only dump image when the specified kernel is enqueued. This control is ignored unless DumpImagesBeforeEnqueue or DumpImagesAfterEnqueue are enabled. + +##### `DumpBuffersMinEnqueue` (cl_uint) + +The Intercept Layer for OpenCL Applications will only dump buffers when the enqueue counter is greater than this value, inclusive. + +##### `DumpBuffersMaxEnqueue` (cl_uint) + +The Intercept Layer for OpenCL Applications will only dump buffers when the enqueue counter is less than this value, inclusive. + +##### `DumpImagesMinEnqueue` (cl_uint) + +The Intercept Layer for OpenCL Applications will only dump images when the enqueue counter is greater than this value, inclusive. + +##### `DumpImagesMaxEnqueue` (cl_uint) + +The Intercept Layer for OpenCL Applications will only dump images when the enqueue counter is less than this value, inclusive. + +### AubCapture Controls + +##### `AubCapture` (bool) + +This is the master control for aub capture. The Intercept Layer for OpenCL Applications doesn't implement aub capture itself, but can be used to selectively enable and disable aub capture via kdc.exe. + +##### `AubCaptureIndividualEnqueues` (bool) + +If set, the Intercept Layer for OpenCL Applications will invoke kdc.exe to start aub capture before a kernel enqueue, and will also invoke kdc.exe to stop aub capture immediately after the kernel enqueue. Each .daf file will have the form "AubCapture\_Enqueue\_\\_kernel\_\.daf". Note that non-kernel enqueues such as calls to clEnqueueReadBuffer() and clEnqueueWriteBuffer() will NOT be aub captured when this control is set. The AubCaptureMinEnqueue and AubCaptureMaxEnqueue controls are still honored when AubCaptureIndividualEnqueues is set. + +##### `AubCaptureMinEnqueue` (cl_uint) + +The Intercept Layer for OpenCL Applications will only invoke kdc.exe to enable aub capture when the enqueue counter is greater than this value, inclusive. + +##### `AubCaptureMaxEnqueue` (cl_uint) + +The Intercept Layer for OpenCL Applications will invoke kdc.exe to stop aub capture when the encounter is greater than this value, meaning that only enqueues less than this value, inclusive, will be captured. If the enqueue counter never reaches this value, the Intercept Layer for OpenCL Applications will stop aub capture when the DLL is unloaded. + +##### `AubCaptureKernelName` (string) + +If set, the Intercept Layer for OpenCL Applications will only invoke kdc.exe to enable aub capture when the kernel name equals this name. + +##### `AubCaptureKernelGWS` (string) + +If set, the Intercept Layer for OpenCL Applications will only invoke kdc.exe to enable aub capture when the NDRange global work size matches this string. The string should have the form "XxYxZ". The wildcard "*" matches all global work sizes. + +##### `AubCaptureKernelLWS` (string) + +If set, the Intercept Layer for OpenCL Applications will only invoke kdc.exe to enable aub capture when the NDRange local work size matches this string. The string should have the form "XxYxZ". The wildcard "*" matches all local work sizes, and the string "NULL" matches a NULL local work size. + +##### `AubCaptureUniqueKernels` (bool) + +If set, the Intercept Layer for OpenCL Applications will only invoke kdc.exe to enable aub capture if the kernel signature (i.e. hash + kernelname + gws + lws) has not been seen already. The behavior of this control is well-defined when AubCaptureIndividualEnqueues is not set, but it doesn't make much sense without AubCaptureIndividualEnqueues. + +##### `AubCaptureNumKernelEnqueuesSkip` (cl_uint) + +The Intercept Layer for OpenCL Applications will skip this many kernel enqueues before invoking kdc.exe to enable aub capture. The behavior of this control is well-defined when AubCaptureIndividualEnqueues is not set, but it doesn't make much sense without AubCaptureIndividualEnqueues. + +##### `AubCaptureNumKernelEnqueuesCapture` (cl_uint) + +The Intercept Layer for OpenCL Applications will only capture this many kernel enqueues. The behavior of this control is well-defined when AubCaptureIndividualEnqueues is not set, but it doesn't make much sense without AubCaptureIndividualEnqueues. + +##### `AubCaptureStartWait` (cl_uint) + +The Intercept Layer for OpenCL Applications will wait for this many milliseconds before invoking kdc.exe to begin aub capture. + +##### `AubCaptureEndWait` (cl_uint) + +The Intercept Layer for OpenCL Applications will wait for this many milliseconds before invoking kdc.exe to end aub capture. + +### Execution Controls + +##### `NoErrors` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will cause all OpenCL APIs to return a successful error status. + +##### `FinishAfterEnqueue` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications inserts a call to clFinish() after every enqueue. The command queue that the command was just enqueued to is passed to clFinish(). This can be used to debug possible timing or resource management issues and will likely impact performance. + +##### `FlushAfterEnqueue` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications inserts a call to clFlush() after every enqueue. The command queue that the command was just enqueued to is passed to clFlush(). This can also be used to debug possible timing or resource management issues and is slightly less obtrusive than FinishAfterEnqueue but still will likely impact performance. If both FinishAfterEnqueue and FlushAfterEnqueue are nonzero then the Intercept Layer for OpenCL Applications will only insert a call to clFinish() after every enqueue, because clFinish() implies clFlush(). + +##### `FlushAfterEnqueueBarrier` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications inserts a call to clFlush() after every barrier enqueue. The command queue that the command was just enqueued to is passed to clFlush(). This has been useful to debug out-of-order queue issues. + +##### `InOrderQueue` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will force all queues to be created in-order. This can be used for performance analysis, but may lead to deadlocks in some cases. + +##### `NullEnqueue` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will silently ignore any enqueue. This can be used for performance analysis, but will likely cause errors if the application relies on any sort of information from OpenCL events and should be used carefully. + +##### `NullLocalWorkSize` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will force the local work size argument to clEnqueueNDRangeKernel() to be NULL, which causes the OpenCL implementation to pick the local work size. Note that this control takes effect before NullLocalWorkSizeX / NullLocalWorkSizeY / NullLocalWorkSizeZ (see below), so enabling both controls will have the effect of forcing a specific local work size. + +##### `NullLocalWorkSizeX` (size_t) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will set the local work size that will be used if an application passes NULL as the local work size to clEnqueueNDRangeKernel(). 1D dispatches will only look at NullLocalWorkSizeX, 2D dispatches will only look at NullLocalWorkSizeX and NullLocalWorkSizeY, while 3D dispatches will look at NullLocalWorkSizeX, NullLocalWorkSizeY, and NullLocalWorkSizeZ. If the specified values for NullLocalWorkSize do not evenly divide the global work size then the specified values of NullLocalWorkSize will not take effect. + +##### `NullLocalWorkSizeY` (size_t) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will set the local work size that will be used if an application passes NULL as the local work size to clEnqueueNDRangeKernel(). 1D dispatches will only look at NullLocalWorkSizeX, 2D dispatches will only look at NullLocalWorkSizeX and NullLocalWorkSizeY, while 3D dispatches will look at NullLocalWorkSizeX, NullLocalWorkSizeY, and NullLocalWorkSizeZ. If the specified values for NullLocalWorkSize do not evenly divide the global work size then the specified values of NullLocalWorkSize will not take effect. + +##### `NullLocalWorkSizeZ` (size_t) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will set the local work size that will be used if an application passes NULL as the local work size to clEnqueueNDRangeKernel(). 1D dispatches will only look at NullLocalWorkSizeX, 2D dispatches will only look at NullLocalWorkSizeX and NullLocalWorkSizeY, while 3D dispatches will look at NullLocalWorkSizeX, NullLocalWorkSizeY, and NullLocalWorkSizeZ. If the specified values for NullLocalWorkSize do not evenly divide the global work size then the specified values of NullLocalWorkSize will not take effect. + +##### `InitializeBuffers` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will initialize the contents of allocated buffers with zero. Only valid for non-COPY\_HOST\_PTR and non-USE\_HOST\_PTR allocations. + +### Platform and Device Query Overrides + +##### `PlatformName` (string) + +If set to a non-empty value, the clGetPlatformInfo() query for CL\_PLATFORM\_NAME will return this string instead of the true platform name. + +##### `PlatformVendor` (string) + +If set to a non-empty value, the clGetPlatformInfo() query for CL\_PLATFORM\_VENDOR will return this string instead of the true platform vendor. + +##### `PlatformProfile` (string) + +If set to a non-empty value, the clGetPlatformInfo() query for CL\_PLATFORM\_PROFILE will return this string instead of the true platform profile. + +##### `PlatformVersion` (string) + +If set to a non-empty string, the clGetPlatformInfo() query for CL\_PLATFORM\_VERSION will return this string instead of the true platform version. + +##### `DeviceTypeFilter` (cl_uint) + +Hides all device types that are not in the filter. Note: CL\_DEVICE\_TYPE\_CPU = 2, CL\_DEVICE\_TYPE\_GPU = 4, CL\_DEVICE\_TYPE\_ACCELERATOR = 8, CL\_DEVICE\_TYPE\_CUSTOM = 16. + +##### `DeviceType` (cl_uint) + +If set to a non-zero value, the clGetDeviceInfo() query for CL\_DEVICE\_TYPE will return this value instead of the true device type. In addition, calls to clGetDeviceIDs() for this device type will return all devices, not just devices of the requested type. This can be used to enumerate all devices (even CPUs) as GPUs, or vice versa. + +##### `DeviceName` (string) + +If set to a non-empty string, the clGetDeviceInfo() query for CL\_DEVICE\_NAME will return this value instead of the true device name. + +##### `DeviceVendor` (string) + +If set to a non-empty string, the clGetDeviceInfo() query for CL\_DEVICE\_VENDOR will return this value instead of the true device vendor. + +##### `DeviceProfile` (string) + +If set to a non-empty string, the clGetDeviceInfo() query for CL\_DEVICE\_PROFILE will return this value instead of the true device profile. + +##### `DeviceVersion` (string) + +If set to a non-empty string, the clGetDeviceInfo() query for CL\_DEVICE\_VERSION will return this value instead of the true device version. + +##### `DeviceCVersion` (string) + +If set to a non-empty string, the clGetDeviceInfo() query for CL\_DEVICE\_OPENCL\_C\_VERSION will return this value instead of the true device version. + +##### `DeviceExtensions` (string) + +If set to a non-empty string, the clGetDeviceInfo() query for CL\_DEVICE\_EXTENSIONS will return this value instead of the true device extensions string. + +##### `DeviceVendorID` (cl_uint) + +If set to a non-zero value, the clGetDeviceInfo() query for CL\_DEVICE\_VENDOR will return this value instead of the true device vendor ID. + +##### `DeviceMaxComputeUnits` (cl_uint) + +If set to a non-zero value, the clGetDeviceInfo() query for CL\_DEVICE\_MAX\_COMPUTE\_UNITS will return this value instead of the true device max compute units. + +##### `DevicePreferredVectorWidthChar` (cl_uint) + +If set to a non-negative value, the clGetDeviceInfo() query for CL\_DEVICE\_PREFERRED\_VECTOR\_WIDTH\_CHAR will return this value instead of the true device preferred vector width. + +##### `DevicePreferredVectorWidthShort` (cl_uint) + +If set to a non-negative value, the clGetDeviceInfo() query for CL\_DEVICE\_PREFERRED\_VECTOR\_WIDTH\_SHORT will return this value instead of the true device preferred vector width. + +##### `DevicePreferredVectorWidthInt` (cl_uint) + +If set to a non-negative value, the clGetDeviceInfo() query for CL\_DEVICE\_PREFERRED\_VECTOR\_WIDTH\_INT will return this value instead of the true device preferred vector width. + +##### `DevicePreferredVectorWidthLong` (cl_uint) + +If set to a non-negative value, the clGetDeviceInfo() query for CL\_DEVICE\_PREFERRED\_VECTOR\_WIDTH\_LONG will return this value instead of the true device preferred vector width. + +##### `DevicePreferredVectorWidthHalf` (cl_uint) + +If set to a non-negative value, the clGetDeviceInfo() query for CL\_DEVICE\_PREFERRED\_VECTOR\_WIDTH\_HALF will return this value instead of the true device preferred vector width. + +##### `DevicePreferredVectorWidthFloat` (cl_uint) + +If set to a non-negative value, the clGetDeviceInfo() query for CL\_DEVICE\_PREFERRED\_VECTOR\_WIDTH\_FLOAT will return this value instead of the true device preferred vector width. + +##### `DevicePreferredVectorWidthDouble` (cl_uint) + +If set to a non-negative value, the clGetDeviceInfo() query for CL\_DEVICE\_PREFERRED\_VECTOR\_WIDTH\_DOUBLE will return this value instead of the true device preferred vector width. + +### Precompiled Kernel and Builtin Kernel Override Controls + +##### `ForceByteBufferOverrides` (bool) + +If set to a nonzero value, each of the buffer functions that are overridden (via one or more of the keys below) will use a byte-wise operation to read/write/copy the buffer (default behavior is to try to copy multiple bytes at a time, if possible). Note: Requires OpenCL 1.1 or the "byte addressable store" extension. + +##### `OverrideReadBuffer` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will use a kernel to implement clEnqueueReadBuffer() instead of the implementation's clEnqueueReadBuffer(). Note: Requires OpenCL 1.1 or the "byte addressable store" extension. + +##### `OverrideWriteBuffer` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will use a kernel to implement clEnqueueWriteBuffer() instead of the implementation's clEnqueueWriteBuffer(). Note: Requires OpenCL 1.1 or the "byte addressable store" extension. + +##### `OverrideCopyBuffer` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will use a kernel to implement clEnqueueCopyBuffer() instead of the implementation's clEnqueueCopyBuffer(). Note: Requires OpenCL 1.1 or the "byte addressable store" extension. + +##### `OverrideReadImage` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will use a kernel to implement clEnqueueReadImage() instead of the implementation's clEnqueueReadImage(). Only 2D images are currently supported. + +##### `OverrideWriteImage` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will use a kernel to implement clEnqueueWriteImage() instead of the implementation's clEnqueueWriteImage(). Only 2D images are currently supported. + +##### `OverrideCopyImage` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will use a kernel to implement clEnqueueCopyImage() instead of the implementation's clEnqueueCopyImage(). Only 2D images are currently supported. + +##### `OverrideBuiltinKernels` (bool) + +If set to a nonzero value, the Intercept Layer for OpenCL Applications will use its own version of the built-in OpenCL kernels that may be accessed via clCreateProgramWithBuiltInKernels(). At present, only the VME block\_motion\_estimate\_intel kernel is implemented. + +### SIMD Survey Controls + +##### `SIMDSurvey` (bool) + +Executes a SIMD survey state machine. The general idea of the SIMD survey state machine is to create and manage three additional kernels for each actual OpenCL kernel, one for each SIMD size. Then, execute and time the three kernels, and choose the fastest for subsequent executions. + +##### `SIMDSurveyWarmupIterations` (cl_uint) + +This is the number of NDRanges that the SIMD survey state machine ignores before starting to time the SIMD survey. + +##### `SIMDSurveySIMD8Option` (string) + +This is the build option that is pre-pended to the application-specified build options to create the SIMD8 kernel. + +##### `SIMDSurveySIMD16Option` (string) + +This is the build option that is pre-pended to the application-specified build options to create the SIMD16 kernel. + +##### `SIMDSurveySIMD32Option` (string) + +This is the build option that is pre-pended to the application-specified build options to create the SIMD32 kernel. + +##### `SIMDOracle` (bool) + +[Note: Not currently implemented, but the idea behind the SIMD oracle is to save the best SIMD size from run-to-run, so the full SIMD survey does not need to be re-executed.] + + +--- + +\* Other names and brands may be claimed as the property of others. + +Copyright (c) 2018, Intel(R) Corporation diff --git a/docs/images/chrome_tracing_detail.png b/docs/images/chrome_tracing_detail.png new file mode 100644 index 0000000000000000000000000000000000000000..abe7e144076dd7131d567112d9952b639984f3eb GIT binary patch literal 59970 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 z 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 literal 0 HcmV?d00001 diff --git a/docs/images/chrome_tracing_example.png b/docs/images/chrome_tracing_example.png new file mode 100644 index 0000000000000000000000000000000000000000..f32bb161617baf5db232a8374c9084b48ef51540 GIT binary patch literal 48859 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 WuP5j&-T-?B8c6=emAuO)5B?W6G&KeQ literal 0 HcmV?d00001 diff --git a/docs/images/cmake_itt.png b/docs/images/cmake_itt.png new file mode 100644 index 0000000000000000000000000000000000000000..2149bfd6ac197eada827eca0653f8ed2c1da9b2e GIT binary patch literal 37613 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 VsZ8jWR1|-SnPM$4#kdm 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
  • >f65lo`d(<_jo@Eyy7uOM-gI(i-@Oja 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

    7xZAU0YxuBD(d{=dMhL@vn$_M|Q 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 (Windows) + +or: + + ~/CLIntercept_Dump/ (Linux) + +This directory will also be used for program injection. + +## Step 2: Find the Program or Program Options to Modify + +Look through the dump directory to find the program(s) or program options you'd +like to modify. Note that a program options file may not exist for every program +source file if the application did not provide options to clBuildProgram(). If +this is the case, simply create a new program options file. + +## Step 3: Copy the Program or Program Options to Modify + +Copy the program(s) or program options you'd like to modify to the directory: + + %SYSTEMDRIVE%\Intel\CLIntercept_Dump\\Inject (Windows) + +or: + + ~/CLIntercept_Dump//Inject (Linux) + +This is the directory that is searched when looking for programs to inject. Note +that this is a subdirectory of the dump directory. + +If the application compiles programs deterministically the program(s) or program +options can be copied unchanged. If the application compiles programs +non-deterministically, you may need rename the programs to modify to remove the +program number or compile count from the filename. + +The Intercept Layer for OpenCL Applications searches for program source filenames +to inject in this order: + +* `CLI___source.cl` - This is the default filename dumped + by DumpProgramSource. +* `CLI__source.cl` - This is the default filename with the program number + removed, so the order the application calls clCreateProgramWithSource() does + not matter. + +The Intercept Layer for OpenCL Applications searches for program option filenames +to inject in this order: + +* `CLI____options.txt` - This is the default filename + dumped by DumpProgramSource. +* `CLI___options.txt` - This is the default filename with the program + number removed, so the order the application calls clCreateProgramWithSource() + does not matter. +* `CLI__options.txt` - This has both the program number and compile count + removed, so it will apply the same options every time the program is built. +* `CLI_options.txt` - This injects the same options globally, for all programs, + unless one of the program-specific filenames exists. + +## Step 4: Modify the Program + +Modify the program as desired. Ideas: Change the program source to a more optimal +code sequence. Switch conformant built-ins to native built-ins. Add program +attributes, e.g. for required work group size. + +## Step 5: Set the InjectProgramSource Registry Key and Go! + +If all goes well you will see a line similar to + + Injecting source file: + +or: + + Injecting options file: + +in your log. + +## Notes: + +* The instructions above describe how to dump and inject program source, but you + can also dump and inject program binaries or SPIR-V intermediate representation. + +--- + +\* Other names and brands may be claimed as the property of others. + +Copyright (c) 2018, Intel(R) Corporation diff --git a/docs/install.md b/docs/install.md new file mode 100644 index 00000000..5022aeef --- /dev/null +++ b/docs/install.md @@ -0,0 +1,129 @@ +# How to Install the Intercept Layer for OpenCL Applications + +There are multiple ways to install the Intercept Layer for OpenCL Applications: + +## Windows + +### Local Install + +The easiest (and least obtrusive!) way to install the Intercept Layer for +OpenCL Applications is to: + +1. Put the Intercept Layer for OpenCL Applications OpenCL.dll into your + application's working directory, typically the directory with the + application executable. Since DLLs are often loaded from the current + working directory before other directories in the system path, the + Intercept Layer for OpenCL Applications OpenCL.dll will be loaded + instead of the real OpenCL.dll. +2. To uninstall, simply delete the Intercept Layer for OpenCL Applications + OpenCL.dll from the application's working directory. + +### Global Install + +To install the Intercept Layer for OpenCL Applications globally (for all +OpenCL applications): + +1. Rename your existing OpenCL.dll (typically in c:\windows\system32 for + 32-bit systems or 64-bit DLLs on 64-bit systems, or c:\windows\syswow64 + for 32-bit DLLs on 64-bit systems). + * You may need to rename your existing OpenCL.dll from safe mode, or + from a command prompt with administrative privileges. + * If you rename your existing DLL to real_OpenCL.dll then the renamed + DLL will be automatically loaded by the Intercept Layer for OpenCL + Applications, otherwise you'll need to tell the Intercept Layer for + OpenCL Applications what your real DLL name is. See below. +2. After renaming your real OpenCL.dll, copy the Intercept Layer for + OpenCL Applications version of OpenCL.dll in its place. +3. To uninstall the Intercept Layer for OpenCL Applications using this + method, reverse the steps: First, delete the Intercept Layer for OpenCL + Applications version of OpenCL.dll, then rename the real OpenCL.dll + back to OpenCL.dll. + +This method also works for applications that load OpenCL.dll from an explicit path. + +## Linux + +### Global Install + +1. Find the location of the real icd loader library (libOpenCL.so): + + sudo find . -name libOpenCL* + + To find the libraries and follow symbolic links in one go use: + + sudo find . -name libOpenCL* | while read -r line; do ll "$line"; done + +2. Rename the real icd loader library: + + sudo mv /path/to/lib/libOpenCL.so.1.2 path/to/lib/real_libOpenCL.so.1.2 + +3. Create a symbolic link from real icd loader library to the Intercept Layer for OpenCL Applications library: + + sudo ln -s /path/to/CLIBin/builds/x86_64/libOpenCL.so.1 /path/to/lib/libOpenCL.so.1.2 + +4. Create a config file to control the Intercept Layer for OpenCL Applications. + Behavior is controlled via a config file on the user's root folder (~). To + change the behavior create/edit the configuration file and set the value + for the desired options. Refer to the list below for the available options. + To create the config file or open it for edit: + + gedit ~/clintercept.conf + + Sample content: + + DllName=path/to/lib/real_libOpenCL.so.1 + LogToFile=1 // Enable LogToFile feature + CallLogging=1 // Enable CallLogging feature + +5. Run an OpenCL application, and output will be in ~/CLIntercept_Dump/AppName + +### Targeted Usage + +To intercept many Linux OpenCL applications, instrumentation can be performed +using only environment variables. If the application specifies an rpath or +otherwise circumvents the OS's method of identifying an appropriate +libOpenCL.so, this method won't work. Example: + + LD_LIBRARY_PATH=/path/to/clintercept/build/output CLI_DLLName=/opt/intel/opencl/libOpenCL.so \ + CLI_DumpProgramSource=1 ./oclapplication + +## Mac OSX - Experimental + +The Intercept Layer for OpenCL Applications on OSX uses an OS capability called +"interposition" to intercept OpenCL calls. As such, there is no "global +install" for OSX. To use the Intercept Layer for OpenCL Applications on OSX, +run your application with the environment variable DYLD_INSERT_LIBRARIES set +to the full path to the CLIntercept library. For example: + + OSX Command Prompt$ DYLD_INSERT_LIBRARIES=/full/path/to/clIntercept/OpenCL ./HelloWorld + +## Android - Experimental + +Only global install was tested + +1. on target: + + cd /system/vendor/lib + mv libOpenCL.so real_libOpenCL.so + +2. on host: + + adb push clIntercept.so /system/vendor/lib/libOpenCL.so + +3. configuration file will be in $HOME/clintercept.conf. If $HOME variable is + undefined (GUI application) it is in /sdcard/clintercept.conf. + + Sample config: + + LogToFile=1 + CallLogging=0 + HostPerformanceTiming=1 + DevicePerformanceTiming=1 + HostPerformanceTimeLogging=1 + DevicePerformanceTimeLogging=1 + +--- + +\* Other names and brands may be claimed as the property of others. + +Copyright (c) 2018, Intel(R) Corporation diff --git a/docs/vtune_logging.md b/docs/vtune_logging.md new file mode 100644 index 00000000..17171f1b --- /dev/null +++ b/docs/vtune_logging.md @@ -0,0 +1,77 @@ +# Using the Intercept Layer for OpenCL Applications with Intel(R) VTune(tm) Amplifier XE + +The Intercept Layer for OpenCL Applications can be built with support for the +Intel(R) VTune(tm) [Instrumentation and Tracing Technology (ITT)][itt] APIs, which +can add information about OpenCL-related calls to VTune timegraphs. This document +describes how to build the Intercept Layer for OpenCL Applications with ITT support, +and how to set controls and VTune to display OpenCL events captured by the +Intercept Layer for OpenCL Applications. + +## Why Use the Intercept Layer for OpenCL Applications? + +Recent versions of VTune supports tracing OpenCL programs natively, however you still +may want to use CLIntercept's VTune integration: + +* The VTune OpenCL tracing works for Intel(R) Processor Graphics only, however the + Intercept Layer for OpenCL Applications's OpenCL tracing works for all OpenCL + devices, including non-Intel OpenCL devices. +* The VTune OpenCL tracing works for a subset of OpenCL calls, however the Intercept + Layer for OpenCL Applications's OpenCL tracing works for all OpenCL calls, including + some non-API calls such as event callbacks. +* The Intercept Layer for OpenCL Applications's OpenCL tracing is very lightweight. + +## Building the Intercept Layer for OpenCL Applications with ITT Support + +To build the CLIntercept Intercept Layer for OpenCL Applications with ITT support, +be sure the "ENABLE_ITT" box is checked when configuring CMake: + +![CMake GUI ITT](images/cmake_itt.png) + +You'll also want to be sure that the VTUNE_INCLUDE_DIR and VTUNE_ITTNOTIFY_LIB +variables are detected correctly. They may be setup automatically, but for systems with +non-standard install paths they may need to be setup manually. + +So long as this box is checked and the include and lib variables are setup correctly, +the Intercept Layer for OpenCL Applications should build without errors or warnings and +include ITT support. + +## Configuring the Intercept Layer for OpenCL Applications for VTune + +The main control to configure the Intercept Layer for OpenCL Applications for VTune +is `ITTCallLogging`. This adds API call entry and exit information to VTune timegraphs. + +## Configuring VTune for the Intercept Layer for OpenCL Applications + +To configure VTune for the Intercept Layer for OpenCL Applications, checked the box for +'Analyze user tasks, events, and counters', since the Intercept Layer for OpenCL +Applications's ITT call logging is considered a "user event". + +You do not need to check the box for 'Trace OpenCL and Media SDK programs (Intel Graphics +only)'. This enables the native VTune support for OpenCL tracing. If this box is checked +you'll likely see some OpenCL calls twice on the timegraph, since both the Intercept Layer +for OpenCL Applications and native VTune tracing will be enabled. + +![VTune Config](images/vtune_config.png) + +## Example VTune Output with the Intercept Layer for OpenCL Applications + +Here is example output with ITTCallLogging. Note that all API calls are logged, including +an event callback on a separate thread: + +![VTune Output](images/vtune_output.png) + +## Limitations and Restrictions + +The biggest limitation at the moment is that you don't get any "device timing" information +in VTune, you only get information about host API calls. This means that you won't see +output like the picture below that you'd get with the native VTune tracing: + +![VTune Device Timing](images/vtune_device_timing.png) + +--- + +\* Other names and brands may be claimed as the property of others. + +Copyright (c) 2018, Intel(R) Corporation + +[itt]: https://software.intel.com/en-us/node/544195 diff --git a/resource/clIntercept.rc b/resource/clIntercept.rc new file mode 100644 index 0000000000000000000000000000000000000000..8a35445ae23353d58b31e9bc23d162b46df07c3c GIT binary patch literal 3594 zcmdUyZI9AG5Xa}WiQl2Zn^s?4>NT62vA>&5uo*Pg(vUWwf*05L4K7RCbZS}6*0@78MyCFeD6A@ z(AmMm^Jk1ak(UpwtaA832*i})aUixadq#q;igHXj)t%Ef|dYt%v=Tf4)bNz8{3oDRv9;WOs$ptpqVvt6#fA", "\>") + + docFile.write('##### `' + name + '` (' + type + ')\n\n') + docFile.write(description + '\n\n') + + #print('Got control: ' + name) + #print(' description: ' + description) + + numberOfControls = numberOfControls + 1 + continue + + #print('Not sure what to do with: ' + line) + + docFile.write( GetFooter() ) + + srcFile.close() + docFile.close() + + print('Successfully generated file: ' + nameControlsDoc) + print('Found ' + str(numberOfSeparators) + ' control separators.') + print('Found ' + str(numberOfControls) + ' controls.')

    9-}pBMXMdO51^7kyRXOuIIaK2K_KcAjG*QKNaC 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%0Ab6_oPn?^>$y$J{b#86 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�Scul{*g;o@*XjnEImhLvlcefj0bKYnNF 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 -(L)=zfza0GyeDxyAEKtM1g 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 literal 0 HcmV?d00001 diff --git a/docs/images/vtune_device_timing.png b/docs/images/vtune_device_timing.png new file mode 100644 index 0000000000000000000000000000000000000000..3d32602bf0c36d27fa0a71136d90d5a264f6b3df GIT binary patch literal 82449 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