Skip to content

Commit b67d023

Browse files
committed
initial OpenCL 3.1 emulation layer
1 parent 0243f19 commit b67d023

11 files changed

Lines changed: 1324 additions & 0 deletions

File tree

‎include/layer_util.hpp‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,21 @@ static inline bool checkStringForExtension(
134134

135135
return supported;
136136
}
137+
138+
#ifdef CL_VERSION_3_0
139+
140+
static inline bool checkForSupport(
141+
const std::vector<cl_name_version>& supported,
142+
const char* name,
143+
const cl_version version = CL_MAKE_VERSION(1, 0, 0))
144+
{
145+
for (auto& check : supported) {
146+
if (strcmp(name, check.name) == 0 &&
147+
version <= check.version) {
148+
return true;
149+
}
150+
}
151+
return false;
152+
}
153+
154+
#endif // CL_VERSION_3_0
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright (c) 2025 Ben Ashbaugh
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
add_opencl_layer(
6+
NUMBER 13
7+
TARGET SGRotateEmu
8+
VERSION 300
9+
SOURCES main.cpp emulate.cpp emulate.h)

‎layers/13_sgrotateemu/emulate.cpp‎

Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
/*
2+
// Copyright (c) 2025-2026 Ben Ashbaugh
3+
//
4+
// SPDX-License-Identifier: MIT
5+
*/
6+
7+
#include <CL/cl.h>
8+
#include <CL/cl_layer.h>
9+
10+
#include <algorithm>
11+
#include <array>
12+
#include <atomic>
13+
#include <map>
14+
#include <string>
15+
#include <vector>
16+
17+
#include "layer_util.hpp"
18+
19+
#include "emulate.h"
20+
#include "sgrotate.cl.h"
21+
22+
static constexpr cl_version version_cl_khr_subgroup_rotate =
23+
CL_MAKE_VERSION(1, 0, 0);
24+
25+
struct SDeviceInfo
26+
{
27+
bool emulate_cl_khr_subgroup_rotate = false;
28+
};
29+
30+
struct SLayerContext
31+
{
32+
SLayerContext()
33+
{
34+
cl_uint numPlatforms = 0;
35+
g_pNextDispatch->clGetPlatformIDs(
36+
0,
37+
nullptr,
38+
&numPlatforms);
39+
40+
std::vector<cl_platform_id> platforms;
41+
platforms.resize(numPlatforms);
42+
g_pNextDispatch->clGetPlatformIDs(
43+
numPlatforms,
44+
platforms.data(),
45+
nullptr);
46+
47+
for (auto platform : platforms) {
48+
getDeviceInfoForPlatform(platform);
49+
}
50+
}
51+
52+
const SDeviceInfo& getDeviceInfo(cl_device_id device) const
53+
{
54+
// TODO: query the parent device if this is a sub-device?
55+
return m_DeviceInfo.at(device);
56+
}
57+
58+
const SDeviceInfo& getDeviceInfo(cl_device_id device)
59+
{
60+
// TODO: query the parent device if this is a sub-device?
61+
return m_DeviceInfo[device];
62+
}
63+
64+
private:
65+
std::map<cl_device_id, SDeviceInfo> m_DeviceInfo;
66+
67+
void getDeviceInfoForPlatform(cl_platform_id platform)
68+
{
69+
cl_uint numDevices = 0;
70+
g_pNextDispatch->clGetDeviceIDs(
71+
platform,
72+
CL_DEVICE_TYPE_ALL,
73+
0,
74+
nullptr,
75+
&numDevices);
76+
77+
std::vector<cl_device_id> devices;
78+
devices.resize(numDevices);
79+
g_pNextDispatch->clGetDeviceIDs(
80+
platform,
81+
CL_DEVICE_TYPE_ALL,
82+
numDevices,
83+
devices.data(),
84+
nullptr);
85+
86+
for (auto device : devices) {
87+
SDeviceInfo& deviceInfo = m_DeviceInfo[device];
88+
89+
size_t size = 0;
90+
91+
std::string deviceExtensions;
92+
g_pNextDispatch->clGetDeviceInfo(
93+
device,
94+
CL_DEVICE_EXTENSIONS,
95+
0,
96+
nullptr,
97+
&size);
98+
99+
// Do not emulate if there are no supported extensions.
100+
if (size == 0) continue;
101+
102+
deviceExtensions.resize(size);
103+
g_pNextDispatch->clGetDeviceInfo(
104+
device,
105+
CL_DEVICE_EXTENSIONS,
106+
size,
107+
&deviceExtensions[0],
108+
nullptr);
109+
deviceExtensions.pop_back();
110+
111+
// If we already support the extension natively, then there is no
112+
// need to emulate it.
113+
if (checkStringForExtension(
114+
deviceExtensions.c_str(),
115+
CL_KHR_SUBGROUP_ROTATE_EXTENSION_NAME)) {
116+
continue;
117+
}
118+
119+
// Emulation requires support for subgroup shuffles
120+
if (checkStringForExtension(
121+
deviceExtensions.c_str(),
122+
CL_KHR_SUBGROUP_SHUFFLE_EXTENSION_NAME)) {
123+
deviceInfo.emulate_cl_khr_subgroup_rotate = true;
124+
}
125+
}
126+
}
127+
};
128+
129+
SLayerContext& getLayerContext(void)
130+
{
131+
static SLayerContext c;
132+
return c;
133+
}
134+
135+
static inline bool doEmulation(cl_device_id device)
136+
{
137+
const auto& deviceInfo = getLayerContext().getDeviceInfo(device);
138+
return deviceInfo.emulate_cl_khr_subgroup_rotate;
139+
}
140+
141+
static inline bool doEmulation(cl_context context)
142+
{
143+
cl_uint numDevices = 0;
144+
g_pNextDispatch->clGetContextInfo(
145+
context,
146+
CL_CONTEXT_NUM_DEVICES,
147+
sizeof(numDevices),
148+
&numDevices,
149+
nullptr);
150+
151+
std::vector<cl_device_id> devices(numDevices);
152+
g_pNextDispatch->clGetContextInfo(
153+
context,
154+
CL_CONTEXT_DEVICES,
155+
numDevices * sizeof(cl_device_id),
156+
devices.data(),
157+
nullptr);
158+
159+
return std::all_of(
160+
devices.begin(),
161+
devices.end(),
162+
[](cl_device_id device) { return doEmulation(device); });
163+
}
164+
165+
cl_program clCreateProgramWithSource_override(
166+
cl_context context,
167+
cl_uint count,
168+
const char** strings,
169+
const size_t* lengths,
170+
cl_int* errcode_ret)
171+
{
172+
if (!doEmulation(context)) {
173+
return g_pNextDispatch->clCreateProgramWithSource(
174+
context,
175+
count,
176+
strings,
177+
lengths,
178+
errcode_ret);
179+
}
180+
181+
if (count == 0 || strings == nullptr) {
182+
if (errcode_ret != nullptr) {
183+
*errcode_ret = CL_INVALID_VALUE;
184+
}
185+
return nullptr;
186+
}
187+
188+
std::vector<const char*> newStrings;
189+
newStrings.reserve(count + 1);
190+
newStrings.insert(newStrings.end(), g_SubgroupRotateString);
191+
newStrings.insert(newStrings.end(), strings, strings + count);
192+
193+
std::vector<size_t> newLengths;
194+
if (lengths != nullptr) {
195+
newLengths.reserve(count + 1);
196+
newLengths.insert(newLengths.end(), 0); // g_SubgroupRotateString is nul-terminated
197+
newLengths.insert(newLengths.end(), lengths, lengths + count);
198+
}
199+
200+
return g_pNextDispatch->clCreateProgramWithSource(
201+
context,
202+
count + 1,
203+
newStrings.data(),
204+
newLengths.size() ? newLengths.data() : nullptr,
205+
errcode_ret);
206+
}
207+
208+
209+
bool clGetDeviceInfo_override(
210+
cl_device_id device,
211+
cl_device_info param_name,
212+
size_t param_value_size,
213+
void* param_value,
214+
size_t* param_value_size_ret,
215+
cl_int* errcode_ret)
216+
{
217+
if (!doEmulation(device)) {
218+
return false;
219+
}
220+
221+
switch(param_name) {
222+
case CL_DEVICE_EXTENSIONS:
223+
{
224+
size_t size = 0;
225+
g_pNextDispatch->clGetDeviceInfo(
226+
device,
227+
CL_DEVICE_EXTENSIONS,
228+
0,
229+
nullptr,
230+
&size );
231+
232+
std::vector<char> deviceExtensions(size);
233+
g_pNextDispatch->clGetDeviceInfo(
234+
device,
235+
CL_DEVICE_EXTENSIONS,
236+
size,
237+
deviceExtensions.data(),
238+
nullptr );
239+
240+
std::string newExtensions;
241+
newExtensions += CL_KHR_SUBGROUP_ROTATE_EXTENSION_NAME;
242+
243+
std::string oldExtensions(deviceExtensions.data());
244+
245+
// If the old extension string ends with a space ensure the
246+
// new extension string does too.
247+
if( oldExtensions.back() == ' ' )
248+
{
249+
newExtensions += ' ';
250+
}
251+
else
252+
{
253+
oldExtensions += ' ';
254+
}
255+
256+
oldExtensions += newExtensions;
257+
258+
auto ptr = (char*)param_value;
259+
cl_int errorCode = writeStringToMemory(
260+
param_value_size,
261+
oldExtensions.c_str(),
262+
param_value_size_ret,
263+
ptr );
264+
if( errcode_ret )
265+
{
266+
errcode_ret[0] = errorCode;
267+
}
268+
return true;
269+
}
270+
break;
271+
case CL_DEVICE_EXTENSIONS_WITH_VERSION:
272+
{
273+
size_t size = 0;
274+
g_pNextDispatch->clGetDeviceInfo(
275+
device,
276+
CL_DEVICE_EXTENSIONS_WITH_VERSION,
277+
0,
278+
nullptr,
279+
&size );
280+
281+
size_t numExtensions = size / sizeof(cl_name_version);
282+
std::vector<cl_name_version> extensions(numExtensions);
283+
g_pNextDispatch->clGetDeviceInfo(
284+
device,
285+
CL_DEVICE_EXTENSIONS_WITH_VERSION,
286+
size,
287+
extensions.data(),
288+
nullptr );
289+
290+
extensions.emplace_back();
291+
cl_name_version& extension = extensions.back();
292+
293+
memset(extension.name, 0, CL_NAME_VERSION_MAX_NAME_SIZE);
294+
strcpy(extension.name, CL_KHR_SUBGROUP_ROTATE_EXTENSION_NAME);
295+
296+
extension.version = version_cl_khr_subgroup_rotate;
297+
298+
auto ptr = (cl_name_version*)param_value;
299+
cl_int errorCode = writeVectorToMemory(
300+
param_value_size,
301+
extensions,
302+
param_value_size_ret,
303+
ptr );
304+
if( errcode_ret )
305+
{
306+
errcode_ret[0] = errorCode;
307+
}
308+
return true;
309+
}
310+
break;
311+
default: break;
312+
}
313+
314+
return false;
315+
}

‎layers/13_sgrotateemu/emulate.h‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
// Copyright (c) 2025-2026 Ben Ashbaugh
3+
//
4+
// SPDX-License-Identifier: MIT
5+
*/
6+
7+
#include <CL/cl.h>
8+
#include <CL/cl_ext.h>
9+
10+
extern const struct _cl_icd_dispatch* g_pNextDispatch;
11+
12+
///////////////////////////////////////////////////////////////////////////////
13+
// Override Functions
14+
15+
cl_program clCreateProgramWithSource_override(
16+
cl_context context,
17+
cl_uint count,
18+
const char** strings,
19+
const size_t* lengths,
20+
cl_int* errcode_ret);
21+
22+
bool clGetDeviceInfo_override(
23+
cl_device_id device,
24+
cl_device_info param_name,
25+
size_t param_value_size,
26+
void* param_value,
27+
size_t* param_value_size_ret,
28+
cl_int* errcode_ret);

0 commit comments

Comments
 (0)