This translation is community contributed and may not be up to date. We only maintain the English version of the documentation. Read this manual in English
Поддержка compute-шейдеров в Defold в настоящее время находится в стадии technical preview. Это означает, что некоторых возможностей пока не хватает, а API потенциально может измениться в будущем.
Compute-шейдеры являются мощным инструментом для выполнения вычислений общего назначения на GPU. Они позволяют задействовать возможности параллельной обработки графического процессора для таких задач, как физические симуляции, обработка изображений и многое другое. Compute-шейдер работает с данными, хранящимися в буферах или текстурах, выполняя операции параллельно во множестве GPU-потоков. Именно этот параллелизм и делает compute-шейдеры столь полезными для ресурсоемких вычислений.
Поскольку compute-шейдеры предназначены для вычислений общего назначения, фактически нет предела тому, что с их помощью можно реализовать. Вот несколько типичных примеров использования compute-шейдеров:
Обработка изображений
Физика
Процедурная генерация
Эффекты рендеринга
На высоком уровне compute-шейдеры работают, разбивая задачу на множество более мелких подзадач, которые могут выполняться одновременно. Это достигается за счет концепций work groups и invocations:
work groups. Каждая рабочая группа содержит фиксированное число invocations (или потоков). Размер рабочих групп и количество invocations определяются в коде шейдера.GPU выполняет compute-шейдер, параллельно запуская множество invocations в нескольких рабочих группах, что обеспечивает значительную вычислительную мощность для подходящих задач.
Чтобы создать compute-программу, кликните ПКМ по целевой папке в браузере Assets и выберите New... ▸ Compute. (Также можно выбрать File ▸ New... в меню, а затем Compute). Укажите имя нового compute-файла и нажмите Ok.

Новый compute-файл откроется в Compute Editor.

Compute-файл содержит следующую информацию:
.cp), который нужно использовать. Шейдер работает с “абстрактными рабочими элементами”, то есть для входных и выходных данных нет фиксированного определения типов. Программист сам определяет, какой результат должен выдавать compute-шейдер.В отличие от материалов, compute-программы не назначаются компонентам и не участвуют в обычном процессе рендеринга. Чтобы compute-программа выполнила какую-либо работу, ее необходимо dispatched из render-скрипта. Однако перед dispatch нужно убедиться, что render-скрипт имеет ссылку на compute-программу. В настоящее время единственный способ сообщить render-скрипту о compute-программе заключается в том, чтобы добавить ее в .render-файл, который хранит ссылку на ваш render-скрипт:

Чтобы использовать compute-программу, ее сначала необходимо привязать к render-контексту. Это делается так же, как и для материалов:
render.set_compute("my_compute")
-- Выполнить compute-работу здесь, вызовите render.set_compute() для отвязки
render.set_compute()
Хотя константы compute будут автоматически применены при dispatch программы, из редактора нельзя привязать к compute-программе входные или выходные ресурсы (текстуры, буферы и т.д.). Вместо этого это нужно делать из render-скриптов:
render.enable_texture("blur_render_target", "tex_blur")
render.enable_texture(self.storage_texture, "tex_storage")
Чтобы запустить программу в выбранном вами рабочем пространстве, необходимо выполнить dispatch программы:
render.dispatch_compute(128, 128, 1)
-- dispatch_compute также принимает таблицу options последним аргументом
-- эту таблицу можно использовать для передачи render-констант в вызов dispatch
local constants = render.constant_buffer()
constants.tint = vmath.vector4(1, 1, 1, 1)
render.dispatch_compute(32, 32, 32, {constants = constants})
В настоящее время вывод любого типа данных из compute-программы можно реализовать только через storage textures. Storage texture похожа на “обычную текстуру”, но поддерживает больше возможностей и настроек. Storage textures, как следует из названия, можно использовать как универсальный буфер, в который можно читать и записывать данные из compute-программы. Затем тот же буфер можно привязать к другой шейдерной программе для чтения.
Чтобы создать storage texture в Defold, это нужно сделать из обычного .script-файла. Render-скрипты такой возможности не имеют, поскольку динамические текстуры создаются через resource API, доступный только в обычных .script-файлах.
-- В .script-файле:
function init(self)
-- Создаем texture resource как обычно, но добавляем флаг "storage",
-- чтобы ее можно было использовать как backing storage для compute-программ
local t_backing = resource.create_texture("/my_backing_texture.texturec", {
type = resource.TEXTURE_TYPE_IMAGE_2D,
width = 128,
height = 128,
format = resource.TEXTURE_FORMAT_RGBA32F,
flags = resource.TEXTURE_USAGE_FLAG_STORAGE + resource.TEXTURE_USAGE_FLAG_SAMPLE,
})
-- получаем handle текстуры из ресурса
local t_backing_handle = resource.get_texture_info(t_backing).handle
-- уведомляем renderer о backing texture, чтобы ее можно было привязать через render.enable_texture
msg.post("@render:", "set_backing_texture", { handle = t_backing_handle })
end
// compute.cp
#version 450
layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
// указываем входные ресурсы
uniform vec4 color;
uniform sampler2D texture_in;
// указываем выходное изображение
layout(rgba32f) uniform image2D texture_out;
void main()
{
// Это не самый интересный шейдер, но он демонстрирует,
// как читать из текстуры и constant buffer и писать в storage texture
ivec2 tex_coord = ivec2(gl_GlobalInvocationID.xy);
vec4 output_value = vec4(0.0, 0.0, 0.0, 1.0);
vec2 tex_coord_uv = vec2(float(tex_coord.x)/(gl_NumWorkGroups.x), float(tex_coord.y)/(gl_NumWorkGroups.y));
vec4 input_value = texture(texture_in, tex_coord_uv);
output_value.rgb = input_value.rgb * color.rgb;
// Записываем выходное значение в storage texture
imageStore(texture_out, tex_coord, output_value);
}
-- В .script-файле
-- Здесь мы указываем входную текстуру, которую позже привяжем
-- к compute-программе. Эту текстуру можно назначить model-компоненту
-- или включить ее в render-контекст из render-скрипта.
go.property("texture_in", resource.texture())
function init(self)
-- Создаем texture resource как обычно, но добавляем флаг "storage",
-- чтобы ее можно было использовать как backing storage для compute-программ
local t_backing = resource.create_texture("/my_backing_texture.texturec", {
type = resource.TEXTURE_TYPE_IMAGE_2D,
width = 128,
height = 128,
format = resource.TEXTURE_FORMAT_RGBA32F,
flags = resource.TEXTURE_USAGE_FLAG_STORAGE + resource.TEXTURE_USAGE_FLAG_SAMPLE,
})
local textures = {
texture_in = resource.get_texture_info(self.texture_in).handle,
texture_out = resource.get_texture_info(t_backing).handle
}
-- уведомляем renderer о входной и выходной текстурах
msg.post("@render:", "set_backing_texture", textures)
end
-- отвечаем на сообщение "set_backing_texture",
-- чтобы задать backing texture для compute-программы
function on_message(self, message_id, message)
if message_id == hash("set_backing_texture") then
self.texture_in = message.texture_in
self.texture_out = message.texture_out
end
end
function update(self)
render.set_compute("compute")
-- Мы можем привязывать текстуры к конкретным именованным константам
render.enable_texture(self.texture_in, "texture_in")
render.enable_texture(self.texture_out, "texture_out")
render.set_constant("color", vmath.vector4(0.5, 0.5, 0.5, 1.0))
-- Выполняем dispatch compute-программы столько раз, сколько у нас пикселей.
-- Это и образует нашу "рабочую группу". Шейдер будет вызван
-- 128 x 128 x 1 раз, то есть один раз на каждый пиксель.
render.dispatch_compute(128, 128, 1)
-- когда работа с compute-программой завершена, нужно отвязать ее
render.set_compute()
end
В настоящее время Defold поддерживает compute-шейдеры в следующих графических адаптерах:
В настоящее время нет способа проверить, поддерживает ли запущенный клиент compute-шейдеры. Это означает, что нет гарантии, что клиент сможет запускать compute-шейдеры, если используется графический адаптер на основе OpenGL или OpenGL ES. Vulkan и Metal поддерживают compute-шейдеры начиная с версии 1.0. Чтобы использовать Vulkan, необходимо создать custom manifest и выбрать Vulkan в качестве backend.
Did you spot an error or do you have a suggestion? Please let us know on GitHub!
GITHUB