Что касается работы с дополнительными, неиспользуемыми данными, это просто: выделите больше места. Диспетчерские вызовы действуют на целых кратных рабочих группах. Таким образом, вы должны убедиться, что есть достаточно места для хранения того, что вы отправляете.
Просто оставьте его неинициализированным для входного буфера и игнорируйте его при чтении вывода.
Но есть и другие проблемы с вашим шейдером, которые не позволят им работать с диспетчерскими вызовами:
Вы специально разработали свой шейдер для работы только с одной отправкой рабочей группы. То есть независимо от того, сколько рабочих групп вы отправляете, все они будут считывать и записывать одни и те же данные.
Во-первых, как обсуждалось ранее, прекратите указывать абсолютную длину данных буфера. Вы не знаете, сколько рабочих групп будет вызвано во время компиляции; это решение во время выполнения. Поэтому определите время выполнения размера массива.
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
— глобальное трехмерное местоположение каждого рабочего элемента. Каждый рабочий элемент будет иметь уникальный gl_GlobalInvocationId
; точечный продукт просто превращает трехмерное местоположение в одномерный индекс.
В-третьих, в вашей реальной логике используйте 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
индекс всегда будет учитывать все измерения локального размера.
person
Nicol Bolas
schedule
05.05.2016