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
Die Unterstützung für Compute-Shader in Defold befindet sich derzeit in der technischen Vorschau. Das bedeutet, dass einige Funktionen fehlen und sich die API in Zukunft möglicherweise ändern kann.
Compute-Shader (compute shaders) sind ein leistungsfähiges Werkzeug für allgemeine Berechnungen auf der GPU. Sie ermöglichen es dir, die parallele Rechenleistung der GPU für Aufgaben wie Physiksimulationen, Bildverarbeitung und mehr zu nutzen. Ein Compute-Shader arbeitet mit Daten, die in Puffern oder Texturen gespeichert sind, und führt Operationen parallel über viele GPU-Threads aus. Diese Parallelität macht Compute-Shader so leistungsfähig bei rechenintensiven Aufgaben.
Da Compute-Shader für allgemeine Berechnungen vorgesehen sind, gibt es praktisch keine Grenzen für ihre Einsatzmöglichkeiten. Hier sind einige Beispiele für typische Anwendungen von Compute-Shadern:
Bildverarbeitung
Physik
Prozedurale Erzeugung
Rendering-Effekte
Grundsätzlich teilen Compute-Shader eine Aufgabe in viele kleinere Aufgaben auf, die gleichzeitig ausgeführt werden können. Dafür werden die Konzepte der Arbeitsgruppen (work groups) und Aufrufe (invocations) verwendet:
Arbeitsgruppen. Jede Arbeitsgruppe enthält eine feste Anzahl von Aufrufen (oder Threads). Die Größe der Arbeitsgruppen und die Anzahl der Aufrufe werden im Shader-Code definiert.Die GPU führt den Compute-Shader aus, indem sie viele Aufrufe über mehrere Arbeitsgruppen hinweg parallel startet. Dadurch stellt sie erhebliche Rechenleistung für geeignete Aufgaben bereit.
Um ein Compute-Programm zu erstellen, führe einen Rechtsklick auf einen Zielordner im Browser Assets aus und wähle New... ▸ Compute. (Du kannst auch File ▸ New... im Menü und anschließend Compute wählen.) Gib der neuen Compute-Datei einen Namen und klicke auf Ok.

Die neue Compute-Datei wird im Compute Editor geöffnet.

Die Compute-Datei enthält die folgenden Informationen:
.cp). Der Shader arbeitet mit „abstrakten Arbeitselementen“. Das bedeutet, dass es keine feste Definition der Datentypen für Ein- und Ausgaben gibt. Du legst beim Programmieren fest, was der Compute-Shader erzeugen soll.Im Gegensatz zu Materialien werden Compute-Programme keinen Komponenten (components) zugewiesen und sind nicht Teil des normalen Rendering-Ablaufs. Ein Compute-Programm muss in einem Render-Skript gestartet werden, um Arbeit auszuführen. Vor dem Start musst du jedoch sicherstellen, dass das Render-Skript eine Referenz auf das Compute-Programm besitzt. Derzeit kann ein Render-Skript nur auf das Compute-Programm zugreifen, wenn du es der Datei mit der Erweiterung .render hinzufügst, die die Referenz auf dein Render-Skript enthält:

Um das Compute-Programm zu verwenden, muss es zunächst an den Render-Kontext gebunden werden. Das geschieht auf die gleiche Weise wie bei Materialien:
render.set_compute("my_compute")
-- Do compute work here, call render.set_compute() to unbind
render.set_compute()
Die Compute-Konstanten werden beim Start des Programms automatisch angewendet. Es gibt jedoch keine Möglichkeit, Eingabe- oder Ausgaberessourcen (Texturen, Puffer und so weiter) über den Editor an ein Compute-Programm zu binden. Stattdessen musst du dies über Render-Skripte tun:
render.enable_texture("blur_render_target", "tex_blur")
render.enable_texture(self.storage_texture, "tex_storage")
Um das Programm in dem von dir festgelegten Arbeitsraum auszuführen, musst du es starten:
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})
Derzeit lassen sich Ausgaben jeglicher Art aus einem Compute-Programm nur über Speichertexturen (storage textures) erzeugen. Eine Speichertextur ähnelt einer „normalen Textur“, bietet aber mehr Funktionen und Konfigurationsmöglichkeiten. Speichertexturen können, wie der Name nahelegt, als allgemeiner Puffer verwendet werden, aus dem du in einem Compute-Programm Daten lesen und in den du Daten schreiben kannst. Anschließend kannst du denselben Puffer zum Lesen an ein anderes Shader-Programm binden.
Um eine Speichertextur in Defold zu erstellen, musst du eine reguläre .script-Datei verwenden. Render-Skripte bieten diese Funktion nicht, da dynamische Texturen über die resource-API erstellt werden müssen, die nur in regulären .script-Dateien verfügbar ist.
-- 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 unterstützt Compute-Shader derzeit mit den folgenden Grafikadaptern:
Verwende graphics.get_adapter_info(), um zu prüfen, ob der aktive Grafikadapter Compute-Shader unterstützt. Das Feld features enthält ein Array der Kontextfunktionskonstanten, die der Adapter unterstützt:
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
)
Das Array enthält nur unterstützte Funktionen; es ist keine Tabelle, deren Schlüssel die Funktionskonstanten sind. Führe diese Prüfung immer vor der Verwendung von Compute-Shadern durch, wenn das Spiel mit verschiedenen Grafikadaptern oder auf Geräten mit unterschiedlicher Treiberunterstützung laufen kann. Die Unterstützung durch OpenGL und OpenGL ES hängt von der API-Version und dem Treiber ab. Vulkan und Metal über MoltenVK unterstützen Compute-Shader ab Version 1.0. Verwende ein Anwendungsmanifest, um Vulkan auf Plattformen auszuwählen, auf denen es noch nicht das standardmäßige Grafik-Backend ist.