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
Obsługa shaderów obliczeniowych (ang. compute shaders) w Defold jest obecnie w fazie technical preview. Oznacza to, że pewnych funkcji jeszcze brakuje i że API może w przyszłości ulec zmianie.
Shadery obliczeniowe są potężnym narzędziem do wykonywania obliczeń ogólnego przeznaczenia na GPU. Pozwalają wykorzystać równoległą moc obliczeniową GPU do zadań takich jak symulacje fizyki, przetwarzanie obrazów i wiele innych. Shader obliczeniowy działa na danych przechowywanych w buforach lub teksturach, wykonując operacje równolegle w wielu wątkach GPU. Właśnie ta równoległość sprawia, że shadery obliczeniowe są tak skuteczne przy wymagających obliczeniach.
Ponieważ shadery obliczeniowe służą do ogólnych obliczeń, praktycznie nie ma ograniczeń co do tego, do czego możesz ich użyć. Oto kilka przykładów typowych zastosowań shaderów obliczeniowych:
Przetwarzanie obrazów
Fizyka
occlusion culling, frustum cullingGenerowanie proceduralne
Efekty renderowania
global illumination): symulowanie realistycznego oświetlenia przez przybliżenie sposobu, w jaki światło odbija się w scenie.voxelization): tworzenie trójwymiarowej siatki wokseli z danych siatki.Na wysokim poziomie shadery obliczeniowe działają przez podział zadania na wiele mniejszych zadań, które mogą zostać wykonane jednocześnie. Odbywa się to dzięki pojęciom work groups i invocations:
work groups. Każda grupa robocza zawiera stałą liczbę wywołań (wątków). Rozmiar grup roboczych i liczba wywołań są definiowane w kodzie shadera.GPU wykonuje shader obliczeniowy, uruchamiając równolegle wiele wywołań w wielu grupach roboczych, co zapewnia znaczną moc obliczeniową dla odpowiednich zadań.
Aby utworzyć program compute, right click docelowy folder w widoku Assets i wybierz New... ▸ Compute. Możesz też wybrać z menu File ▸ New..., a następnie Compute. Nadaj nazwę nowemu plikowi compute i naciśnij Ok.

Nowy plik compute otworzy się w Compute Editor.

Plik compute zawiera następujące informacje:
.cp), którego należy użyć. Shader działa na „abstrakcyjnych elementach roboczych”, co oznacza, że nie ma stałej definicji typów danych wejściowych i wyjściowych. To programista definiuje, co shader obliczeniowy ma wygenerować.W odróżnieniu od materiałów programy compute nie są przypisywane do żadnych komponentów i nie są częścią normalnego przebiegu renderowania. Aby wykonały jakąkolwiek pracę, trzeba je wywołać (dispatch) w skrypcie do renderowania. Zanim jednak to zrobisz, musisz upewnić się, że skrypt do renderowania ma odwołanie do programu compute. Obecnie jedynym sposobem, aby skrypt do renderowania wiedział o programie compute, jest dodanie go do pliku .render, który przechowuje odwołanie do twojego skryptu do renderowania:

Aby użyć programu compute, trzeba go najpierw powiązać z kontekstem renderowania. Robi się to tak samo jak w przypadku materiałów:
render.set_compute("my_compute")
-- Tutaj wykonuj pracę compute; wywołaj render.set_compute(), aby odwiązać program
render.set_compute()
Choć stałe compute zostaną automatycznie zastosowane podczas dispatchowania programu, z poziomu edytora nie da się powiązać z programem compute żadnych zasobów wejściowych ani wyjściowych (tekstur, buforów itd.). Zamiast tego trzeba to zrobić w skryptach do renderowania:
render.enable_texture("blur_render_target", "tex_blur")
render.enable_texture(self.storage_texture, "tex_storage")
Aby uruchomić program w wybranej przez siebie przestrzeni roboczej, musisz go wywołać:
render.dispatch_compute(128, 128, 1)
-- dispatch_compute przyjmuje też tabelę opcji jako ostatni argument
-- tej tabeli możesz użyć do przekazania stałych renderowania do wywołania dispatch
local constants = render.constant_buffer()
constants.tint = vmath.vector4(1, 1, 1, 1)
render.dispatch_compute(32, 32, 32, {constants = constants})
Obecnie generowanie jakiegokolwiek rodzaju danych wyjściowych z programu compute jest możliwe tylko za pomocą storage textures. Tekstura storage jest podobna do „zwykłej tekstury”, ale oferuje więcej funkcji i możliwości konfiguracji. Jak sugeruje nazwa, tekstury storage mogą służyć jako ogólny bufor, z którego program compute może odczytywać i do którego może zapisywać dane. Ten sam bufor możesz potem powiązać z innym programem shadera do odczytu.
Aby utworzyć teksturę storage w Defold, musisz zrobić to ze zwykłego pliku .script. Skrypty do renderowania nie mają tej funkcjonalności, ponieważ tekstury dynamiczne trzeba tworzyć przez API zasobów, które jest dostępne tylko w zwykłych plikach .script.
-- W pliku .script:
function init(self)
-- Utwórz zasób tekstury jak zwykle, ale dodaj flagę "storage",
-- aby można go było użyć jako zaplecza dla programów 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,
})
-- pobierz uchwyt tekstury z zasobu
local t_backing_handle = resource.get_texture_info(t_backing).handle
-- powiadom renderer o teksturze zaplecza, aby można było ją powiązać przez 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;
// określ zasoby wejściowe
uniform vec4 color;
uniform sampler2D texture_in;
// określ obraz wyjściowy
layout(rgba32f) uniform image2D texture_out;
void main()
{
// To nie jest szczególnie ciekawy shader, ale pokazuje,
// jak czytać z tekstury i bufora stałych oraz zapisywać do tekstury storage
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;
// Zapisz wartość wyjściową do tekstury storage
imageStore(texture_out, tex_coord, output_value);
}
-- W pliku .script:
-- Tutaj określamy teksturę wejściową, którą później powiążemy
-- z programem compute. Możemy przypisać tę teksturę do komponentu modelu
-- albo włączyć ją w kontekście renderowania w skrypcie do renderowania.
go.property("texture_in", resource.texture())
function init(self)
-- Utwórz zasób tekstury jak zwykle, ale dodaj flagę "storage",
-- aby można go było użyć jako zaplecza dla programów 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
}
-- powiadom renderer o teksturach wejściowej i wyjściowej
msg.post("@render:", "set_backing_texture", textures)
end
-- reaguj na wiadomość "set_backing_texture",
-- aby ustawić teksturę zaplecza dla programu 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")
-- Możemy powiązać tekstury z konkretnymi nazwanymi stałymi
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))
-- Uruchom program compute tyle razy, ile mamy pikseli.
-- To stanowi naszą "grupę roboczą". Shader zostanie wywołany
-- 128 x 128 x 1 razy, czyli raz na piksel.
render.dispatch_compute(128, 128, 1)
-- Gdy skończymy pracę z programem compute, musimy go odwiązać
render.set_compute()
end
Defold obsługuje obecnie shadery obliczeniowe na następujących adapterach graficznych:
Obecnie nie ma sposobu, aby sprawdzić, czy uruchomiony klient obsługuje shadery obliczeniowe. Oznacza to, że jeśli adapter graficzny jest oparty na OpenGL lub OpenGL ES, nie ma gwarancji, że klient obsłuży uruchamianie shaderów obliczeniowych. Vulkan i Metal obsługują shadery obliczeniowe od wersji 1.0. Aby użyć Vulkan, musisz utworzyć własny manifest i wybrać ten backend.