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
Il supporto degli shader di calcolo (compute shader) in Defold è attualmente in anteprima tecnica. Questo significa che alcune funzionalità sono ancora assenti e che l’API potrebbe cambiare in futuro.
Gli shader di calcolo sono uno strumento potente per eseguire calcoli di uso generale sulla GPU. Ti permettono di sfruttare la capacità di elaborazione parallela della GPU per attività come simulazioni fisiche, elaborazione delle immagini e molto altro. Uno shader di calcolo opera sui dati memorizzati in buffer o texture, eseguendo operazioni in parallelo su numerosi thread della GPU. È questo parallelismo a rendere gli shader di calcolo così potenti per i calcoli intensivi.
Poiché gli shader di calcolo sono pensati per eseguire calcoli generici, non ci sono veri limiti a ciò che puoi fare con essi. Ecco alcuni esempi degli usi tipici degli shader di calcolo:
Elaborazione delle immagini
Fisica
Generazione procedurale
Effetti di rendering
A grandi linee, gli shader di calcolo funzionano suddividendo un’attività in molte attività più piccole che possono essere eseguite contemporaneamente. Questo è possibile grazie ai concetti di gruppi di lavoro (work groups) e invocazioni (invocations):
work groups). Ogni gruppo di lavoro contiene un numero fisso di invocazioni (o thread). La dimensione dei gruppi di lavoro e il numero di invocazioni sono definiti nel codice dello shader.La GPU esegue lo shader di calcolo avviando molte invocazioni in parallelo su più gruppi di lavoro, offrendo una notevole potenza di calcolo per le attività adatte.
Per creare un programma di calcolo, fai clic con il pulsante destro su una cartella di destinazione nel browser Assets e seleziona New... ▸ Compute. (Puoi anche selezionare File ▸ New... dal menu e poi selezionare Compute). Assegna un nome al nuovo file di calcolo e premi Ok.

Il nuovo programma di calcolo si aprirà nel Compute Editor.

Il file di calcolo contiene le seguenti informazioni:
.cp) da usare. Lo shader opera su “elementi di lavoro astratti”, quindi non esiste una definizione fissa dei tipi di dati di input e output. Spetta al programmatore definire che cosa deve produrre lo shader di calcolo.A differenza dei materiali, i programmi di calcolo non sono assegnati ad alcun componente e non fanno parte del normale flusso di rendering. Un programma di calcolo deve essere avviato (dispatched) in uno script di rendering per eseguire qualsiasi operazione. Prima di avviarlo, però, devi assicurarti che lo script di rendering contenga un riferimento al programma di calcolo. Attualmente, l’unico modo per rendere il programma di calcolo disponibile a uno script di rendering è aggiungerlo al file .render che contiene il riferimento allo script di rendering:

Per usare il programma di calcolo, devi prima associarlo al contesto di rendering. Questa operazione avviene nello stesso modo dei materiali:
render.set_compute("my_compute")
-- Do compute work here, call render.set_compute() to unbind
render.set_compute()
Le costanti di calcolo vengono applicate automaticamente quando il programma viene avviato, ma non è possibile associare risorse di input o output (texture, buffer e così via) a un programma di calcolo dall’editor. Devi invece farlo tramite gli script di rendering:
render.enable_texture("blur_render_target", "tex_blur")
render.enable_texture(self.storage_texture, "tex_storage")
Per eseguire il programma nello spazio di lavoro che hai definito, devi avviare il programma:
render.dispatch_compute(128, 128, 1)
-- dispatch_compute also accepts an options table as the last argument
-- you can use this argument table to pass in render constants to the dispatch call
local constants = render.constant_buffer()
constants.tint = vmath.vector4(1, 1, 1, 1)
render.dispatch_compute(32, 32, 32, {constants = constants})
Attualmente, per generare qualsiasi tipo di output da un programma di calcolo puoi usare soltanto le texture di archiviazione (storage textures). Una texture di archiviazione è simile a una “texture normale”, ma supporta più funzionalità e opzioni di configurazione. Come suggerisce il nome, le texture di archiviazione possono essere usate come buffer generici da cui leggere e in cui scrivere dati tramite un programma di calcolo. Puoi poi associare lo stesso buffer a un altro programma shader per la lettura.
Per creare una texture di archiviazione in Defold, devi operare da un normale file .script. Gli script di rendering non offrono questa funzionalità, perché le texture dinamiche devono essere create tramite l’API resource, disponibile soltanto nei normali file .script.
-- In a .script file:
function init(self)
-- Create a texture resource like usual, but add the "storage" flag
-- so it can be used as the backing storage for compute programs
local t_backing = resource.create_texture("/my_backing_texture.texturec", {
type = graphics.TEXTURE_TYPE_IMAGE_2D,
width = 128,
height = 128,
format = graphics.TEXTURE_FORMAT_RGBA32F,
flags = graphics.TEXTURE_USAGE_FLAG_STORAGE + graphics.TEXTURE_USAGE_FLAG_SAMPLE,
})
-- get the texture handle from the resource
local t_backing_handle = resource.get_texture_info(t_backing).handle
-- notify the renderer of the backing texture, so it can be bound with 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;
// specify the input resources
uniform vec4 color;
uniform sampler2D texture_in;
// specify the output image
layout(rgba32f) uniform image2D texture_out;
void main()
{
// This isn't a particularly interesting shader, but it demonstrates
// how to read from a texture and constant buffer and write to a 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;
// Write the output value to the storage texture
imageStore(texture_out, tex_coord, output_value);
}
-- In a .script file
-- Here we specify the input texture that we later will bind to the
-- compute program. We can assign this texture to a model component,
-- or enable it to the render context in the render script.
go.property("texture_in", resource.texture())
function init(self)
-- Create a texture resource like usual, but add the "storage" flag
-- so it can be used as the backing storage for compute programs
local t_backing = resource.create_texture("/my_backing_texture.texturec", {
type = graphics.TEXTURE_TYPE_IMAGE_2D,
width = 128,
height = 128,
format = graphics.TEXTURE_FORMAT_RGBA32F,
flags = graphics.TEXTURE_USAGE_FLAG_STORAGE + graphics.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
}
-- notify the renderer of the input and output textures
msg.post("@render:", "set_backing_texture", textures)
end
-- respond to the message "set_backing_texture"
-- to set the backing texture for the compute program
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")
-- We can bind textures to specific named constants
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 the compute program as many times as we have pixels.
-- This constitutes our "working group". The shader will be invoked
-- 128 x 128 x 1 times, or once per pixel.
render.dispatch_compute(128, 128, 1)
-- when we are done with the compute program, we need to unbind it
render.set_compute()
end
Defold attualmente supporta gli shader di calcolo nei seguenti adattatori grafici:
Usa graphics.get_adapter_info() per verificare se l’adattatore grafico attivo supporta gli shader di calcolo. Il campo features contiene un array delle costanti delle funzionalità del contesto supportate dall’adattatore:
local function has_context_feature(feature)
local adapter_info = graphics.get_adapter_info()
for _, supported_feature in ipairs(adapter_info.features) do
if supported_feature == feature then
return true
end
end
return false
end
local compute_shaders_supported = has_context_feature(
graphics.CONTEXT_FEATURE_COMPUTE_SHADER
)
L’array include soltanto le funzionalità supportate; non è una tabella con le costanti delle funzionalità come chiavi. Esegui sempre questa verifica prima di usare gli shader di calcolo quando il gioco può essere eseguito con adattatori grafici diversi o su dispositivi con un supporto dei driver variabile. Il supporto di OpenGL e OpenGL ES dipende dalla versione dell’API e dal driver. Vulkan e Metal tramite MoltenVK supportano gli shader di calcolo dalla versione 1.0. Usa un manifest dell’applicazione per selezionare Vulkan sulle piattaforme in cui non è già il backend grafico predefinito.