Я довольно новичок во всем мире OpenCL, и я создал два довольно простых Ядра, и я пытаюсь объединить их в цепочку, но я получаю довольно ложные результаты. Когда они запускаются индивидуально, они работают, как и ожидалось, но когда их объединяют, я вижу странные результаты.
Итак, каждое ядро в отдельности выглядит так
Vector 3 Noise
__kernel void addVector3Noise( __global struct State* states, __global float3* randomVector3Values){
int stateNum = get_global_id(0);
struct State state = states[stateNum];
float3 randomVal = randomVector3Values[stateNum];
struct State newState;
newState.Vec3 = (float3)(state.Vec3.x + randomVal.x,state.Vec3.y + randomVal.y,state.Vec3.z + randomVal.z);
newState.Vec4 = state.Vec4;
states[stateNum] = newState;
}
Для проверки этого, все государства имели Vec3
из [ 1.0f, 1.0f, 1.0f]
все случайные значения одинаковы, поэтому вывод, который я получаю, представляет собой массив состояний со значениями [2.0f, 2.0f, 2.0f]
как я и ожидал.
Vector 4 Noise
__kernel void addVector4Noise(__global struct State* states,
__global float3* randomVector4Values){
int stateNum = get_global_id(0);
struct State state = states[stateNum];
float3 randomVal = randomVector4Values[stateNum];
float4 newVector4 = randomQuaternionRotation(state.Vector4, randomVal);
struct State newState;
newState.Vector3 = state.Vector3;
newState.Vector4= newVector4;
states[stateNum] = newState;
}
Запуск этого с очень простыми тестовыми данными также дает мне то, что я хочу.
Теперь проблема возникает при объединении их в цепочку. Я вызывал их в порядке Vector 4 noise -> Vector 3 noise. Теперь, когда я запускаю ядро шума Vector 4, я вижу, как меняются значения вектора 3, и эти изменения, похоже, следуют шаблону.
Итак, после того, как ядро vector 4 будет запущено, я ожидаю, что вектор 3 в каждом состоянии будет таким же, как когда он был подключен. Это означает, что каждое состояние будет иметь значение Vector 3 [1.0f,1.0f,1.0f]
Вот то, что я на самом деле вижу, как вектор 3 выходит:
[1.0,1.0,1.0]
[0.576367259,1.0,1.0]
[0.999199867,0.6448302,1.0]
[1.313311, 1.067663, 0.3307195]
[-0.08005857, 1.067663, 1.450237]
[1, 0.2340522, 1.136126]
[1, 1, 0.3025152]
[1, 1, 1]
И этот шаблон повторяется во всех значениях вектора 3. Обратите внимание, что в ядре это просто копирование Vector3 из предыдущего состояния в новое состояние.
Вот как я их связал вместе, используя OpenCL.Net
using (var env = "*".CreateCLEnvironment(DeviceType.Gpu))
{
var source = LoadProgram("kernels.cl");
var context = env.Context;
ErrorCode errorCode;
var program = Cl.CreateProgramWithSource(context, 1u, source, null, out errorCode);
CheckSuccess(errorCode);
errorCode = Cl.BuildProgram(program, (uint)env.Devices.Length, env.Devices, "-cl-opt-disable", null,
IntPtr.Zero);
if (errorCode != ErrorCode.Success)
{
var info = Cl.GetProgramBuildInfo(program, env.Devices[0], ProgramBuildInfo.Log, out errorCode).ToString();
throw new Exception(info);
}
var kernels = Cl.CreateKernelsInProgram(program, out errorCode);
CheckSuccess(errorCode);
var Vector4NoiseKernel = kernels[0];
var Vector3NoiseKernel = kernels[1];
var rnd = new Random();
var states = Enumerable.Range(1, ArrayLength)
.Select(_ => new State
{
Vector3 = new Vector3(1, 1, 1),
Vector4 = new Vector4(0.5f,0.5f,0.5f,0.5f)
})
.ToArray();
var randomVector4Values = Enumerable.Range(1, ArrayLength)
.Select(_ => new Vector3(2f, 2f, 2f))
.ToArray();
var randomVector3Values = Enumerable.Range(1, ArrayLength)
.Select(_ => new Vector3(1f, 1f, 1f))
.ToArray();
var vector4StatesBuffer = context.CreateBuffer(states, MemFlags.ReadWrite);
var randomVector4ValuesBuffer = context.CreateBuffer(randomVector4Values, MemFlags.ReadOnly);
Event ev;
Cl.SetKernelArg(vector4NoiseKernel, 0, vector4StatesBuffer);
Cl.SetKernelArg(vector4NoiseKernel, 1, randomVector4ValuesBuffer);
errorCode = Cl.EnqueueNDRangeKernel(env.CommandQueues[0], vector4NoiseKernel, 1, null
, new[] { new IntPtr(ArrayLength) }, new[] { new IntPtr(1) }, 0u, null, out ev);
errorCode.Check();
env.CommandQueues[0].ReadFromBuffer(vector4StatesBuffer, states, waitFor: ev);
var randomVector3ValuesBuffer = context.CreateBuffer(randomVector3Values, MemFlags.ReadOnly);
var vector3StatesBuffer = context.CreateBuffer(states, MemFlags.ReadWrite);Cl.SetKernelArg(vector3NoiseKernel, 0, vector3StatesBuffer);
Cl.SetKernelArg(vector3NoiseKernel, 1, randomVector3ValuesBuffer);
errorCode = Cl.EnqueueNDRangeKernel(env.CommandQueues[0], vector3NoiseKernel, 1, null
, new[] { new IntPtr(ArrayLength) }, new[] { new IntPtr(1) }, 0u, null, out ev);
errorCode.Check();
Cl.Finish(env.CommandQueues[0]).Check();
env.CommandQueues[0].ReadFromBuffer(vector3StatesBuffer, states, waitFor: ev);
}
Извините за огромную кучу кода, но это проект игровой площадки, и я в значительной степени просто извергаю идеи, так что аккуратность и элегантность здесь не проблема 🙂
Заранее благодарим за любую помощь, которую вы можете оказать.
РЕДАКТИРОВАТЬ
Итак, первое, что я сделал сегодня утром, это вытащил каждое ядро в свой собственный файл cl и убедился, что у каждого есть своя собственная версия состояния с только тем, что для него требуется (Vector4 и Vector3 соответственно), а также новый оператор using все gubbins, которые идут вместе с этим для недавно отделенного ядра шума Vector3. К моей радости, ядро шума Vector4 сделало именно то, что я и ожидал, однако, когда дело дошло до шума Vector3, возникла проблема, аналогичная ранее. Все еще проходя в [1.0f,1.0f,1.0f]
как и случайные значения, и начальные значения Vector3, и он все еще не производит вывод, который я ожидаю. Шаблон, который повторяется на этот раз:
[2.0f,2.0f,2.0f]
[1.0f,2.0f,2.0f]
[2.0f,1.0f,2.0f]
[2.0f,2.0f,1.0f]
[2.0f,2.0f,2.0f]
В OpenCL 3-компонентные векторные типы занимают тот же размер, что и 4-компонентные векторные типы. Например, float3
определяется как 16 байтов, а не 12 байтов. Если структуры данных, которые вы используете на хосте ( Vector3
класс в данном случае) не одного размера, вы, скорее всего, столкнетесь с проблемами.
Шаблон в выводе в вашем отредактированном посте — три 2.0
а потом 1.0
Это указывает на то, что это, вероятно, причина поведения, которое вы видите.
Одним из решений будет использование Vector4
на принимающей стороне вместо Vector3
,
Итак, после того, как @jprice указал на разницу в размерах между типами C # и OpenCL, мне удалось решить эту проблему, явно объявив общий размер моей структуры на стороне C # в соответствии с тем, что ожидается на стороне OpenCL, так что теперь моя структура выглядит следующим образом.
[StructLayout(LayoutKind.Sequential, Size = 32)]
public struct State
{
public Vector3 Vector3;
public Vector4 Vector4;
}
Как float3
а также float4
на стороне OpenCL — 16 байтов, выделение 32 байтов для моей структуры, содержащей оба, привело к правильному поведению.