Что касается работы с лишними, необычными данными, это легко: выделить больше места. Вызов диспетчера работает со всеми краткими рабочими группами. Таким образом, вы должны убедиться, что есть достаточное хранилище для того, что вы отправляете.
Просто оставьте его неинициализированным для входного буфера и игнорировать его, когда вы читаете вывод в
Но есть и другие проблемы, связанные с вашим шейдером, которые мешают им работать с диспетчерскими вызовами:.
Вы специально разработали свой шейдер, чтобы работать только для одной групповой отправки. То есть, независимо от того, сколько рабочих групп вы отправляете, все они будут читать и писать одни и те же данные.
Во-первых, as previously discussed, прекратите предоставление абсолютной длины буферным данным. Вы не знаете, сколько рабочих групп будет вызываться во время компиляции; это решение времени исполнения. Поэтому определите размер выполнения массива.
layout (binding = 0) readonly buffer block1
{
float input_data[];
};
layout (binding = 1) writeonly buffer block2
{
float output_data[];
};
Также обратите внимание на отсутствие coherent
. Вы используете , но не, используя эти буферы любым способом, который требовал бы этого определителя.
Ваши данные shared
все еще должны иметь размер.
Во-вторых, каждый рабочий элемент отвечает за чтение определенного значения от input_data
и запись определенного значения в output_data
. В вашем текущем коде этот индекс равен id
, но ваш текущий код вычисляет его только по индексу рабочего элемента в рабочей группе. Для того, чтобы вычислить его для всех элементов работы во всех рабочих группах, сделайте следующее:
const uint id = dot(gl_GlobalInvocationID,
vec3(1, gl_NumWorkGroups.x, gl_NumWorkGroups.y * gl_NumWorkGroups.x)
Точка-продукт просто причудливый способ делать умножений, а затем суммированием компонентов. gl_GlobalInvocationID
- это 3D-местоположение по всему миру для каждого рабочего элемента. Каждый рабочий элемент будет иметь уникальный gl_GlobalInvocationId
; dot-продукт просто превращает 3D-местоположение в 1D-индекс.
В-третьих, в вашей фактической логике используйте gid
только для доступа к данным в буферах. При доступе к данным в вашем общем хранилище, вам нужно использовать gl_LocalInvocationIndex
(который, по существу, что id
раньше):
const uint lid = gl_LocalInvocationIndex;
shared_data[lid * 2] = input_data[id * 2];
shared_data[lid * 2 + 1] = input_data[id * 2 + 1];
for (step = 0; step < steps; step++)
{
mask = (1 << step) - 1;
rd_id = ((lid >> step) << (step + 1)) + mask;
wr_id = rd_id + 1 + (lid & mask);
shared_data[wr_id] += shared_data[rd_id];
barrier();
}
output_data[id * 2] = shared_data[lid * 2];
output_data[id * 2 + 1] = shared_data[lid * 2 + 1];
Это лучше использовать gl_LocalInvocationIndex
вместо gl_LocalInvocationID.x
, потому что вы можете когда-нибудь нужно больше деталей работать в чем вы можете получить только с одним измерением локального размера. С gl_LocalInvocationIndex
индекс всегда учитывает все размеры локального размера.
Спасибо за ваши наблюдения. Но я не уверен, что понимаю ответ на мой вопрос. Имея массив из 3000 элементов, для которого я хочу вычислить сумму префикса, лучше всего сделать это, чтобы наложить его на 4096 элементов и просто игнорировать результаты, которые меня не волнуют (при условии, что я сохраняю local_size_x = 1024)? – markwalberg
@BogdanPetcu: Это общая идея. Я не уверен, сколько перекрестных помех есть в ваших рабочих элементах, поэтому меньшие локальные размеры могут быть лучше. –