Fuses 101

Beginners Guide to Fuses in DaVinci Resolve Fusion

What is a Fuse?

Fuses are Lua scripts that allow you to create your own tools in DaVinci Resolve Fusion. You can create everything from regular nodes, to modifier nodes, or even console commands. This guide will just go over the most basic use cases to get you started. You can check out my FusionSDK Reference page after to dive deeper.

Where do they go?

Fuses have their own Fuses folder. It is located in the same directroy as your Macros folder. If you don't know where your Macros folder is, you might need to familiarize yourself a bit more with Fusion before continuing. Macros offer a great way to create your own tools with much less technical knowladge and effort.

Fusion/
  - Fuses/
  - Macros/
  - Templates/
  - Modules/

Anatomy of a Fuse

Every Fuse must have 3 things:

FuRegisterClass

FuRegisterClass() is called when DaVinci Resolve or Fusion first loads our .fuse file on startup. This means that any changes to FuRegisterClass require restarting the app. This also includes creating new .fuse files.

FuRegisterClass(id, class_type, tags);

ArgumentTypeUsage
idStringA unique String for this tool. This is what will show up in .settings files.
class_typeIntA constant indicating what type of tool we are creating. Common class types includeCT_Tool, CT_SourceTool, CT_SinkTool, CT_ConsoleUtility, and CT_Modifier.
tagsTableMost functions in the FusionSDK end with a tags argument, a tags table contains key value pairs. The tags table is where most of the magic happens.
FuRegisterClass("PizzaBlu.SampleOpacity", CT_Tool, {
    REGS_Name = "SampleOpacity",
    REG_NoPreCalcProcess = true,    -- combines the PreCalcProcess() and Process() functions, this is fine for most tools
});

Create

This is where we create any Inputs and Outputs that our tool needs. Create() is called both when a tool is first added, and when the comp is loaded again. If Create() is being called when loading an existing node Fusion will automatically update the inputs to their saved values after we create them. There are a few methods that you will frequently see used in the Create() function:

MethodTypeUsage
self:AddInput(name, id, tags)InputCreate Inputs & setup controls.
self:AddOutput(name, id, tags)OutputCreate Outputs.
self:BeginControlNest(name, id, expand)InputInputs between a call to BeginControlNest and EndControlNest will automatically be grouped into a collapsable nest.
self:EndControlNest()nilSee above.

You can find a more complete reference for the tags for these functions in my FusionSDK Reference.

function Create()

    -- create an "Opacity" slider
    InOpacity = self:AddInput("Opacity", "Opacity", {
        LINKID_DataType = "Number",
        INPID_InputControl = "SliderControl",
        INP_Default = 1.0,
    });

    -- our main Image Input
    -- main Inputs/Outputs show up in the Fusion node graph, instead of the Inspector
    InImage = self:AddInput("Input", "Input", {
        LINKID_DataType = "Image",
        LINK_Main = 1,                                  -- this is the first main input
    });

    -- our main Image Output
    OutImage = self:AddOutput("Output", "Output", {
        LINKID_DataType = "Image",
        LINK_Main = 1,                                  -- this is the first main output
    });

end

Process()

The Process(req) function is the most important part of our fuse. It gets called whenever Fusion needs us to to process new data, usually once per frame. It will also get called whenever our controls change, or if a node we are connected to changes. The Process Function accepts one argument: the Request object which contains important information like the frame being requested and the values of our inputs at that time. It is our job here to update the Request with our own Output data.

Input:GetValue(Request)

The GetValue() shortcut is how we extract our Inputs data from the Request. Not every DataType will require the .Value at the end, but our Number does.

local opacity = InOpacity:GetValue(req).Value;
local img = InImage:GetValue(req);

Output:Set(Request, Parameter)

The Set() shortcut is how we update the Request with our output data. Parameter is the generic name for any data passing through an Input or Output. Make sure to call the correct constructor for your output parameter. The example below passes our input image through unchanged, so it doesn't need to construct a new Image yet.

OutImage:Set(req, img);

Image(tags)

The Image() constructor is how we create a new Image. Like most functions in the FusionSDK, you can configure it with a tags table. Note that if IMG_Like is used, you can still override specific tags.

TagTypeUsage
IMG_LikeImagePass in an existing Image object and copy its attributes (but not image data). This is often all you need for simple one-input, one-output nodes.
IMG_DocumentCompositionIf you do not pass in an existing image with IMG_Like you will need to specify the composition using this tag.
IMG_WidthNumberThe horizontal resolution of the image.
IMG_HeightNumberThe vertical resolution of the image.

Image

\
TagTypeDescription
IMG_GPUImageBoolStore this image in Video Memory instead of System Memory.
IMG_GPUProcessBoolBuilt in image processing functions should use their GPU accelerated counterparts automatically for this image.
IMG_IsMaskBoolIndicates if the image is a mask or not.
IMG_DoAutoTransformBoolAutomatically perform transform concatenation if possible.
IMG_DoAutoDoDBoolAutomatically save memory and processing time by restricting the Image Domain.

Additional advanced tags: IMG_GPUImage IMG_GPUProcess IMG_IsMask IMG_DoAutoTransform and IMG_DoAutoDoD are also available for Image inputs.

-- if we handle copying the image data ourselves
local out = Image({IMG_Like = img});

We will use the Image:CopyOf() method to create a new copy of our input image with the image data already copied over.

-- let fusion copy the image data for us
local out = img:CopyOf();

To apply our Opacity adjustment we will use the built in Image:Gain() method. It is best to use the built in image processing methods when possible to take advantage of the faster non-lua code powering them. The Gain() It is important we call Gain() on our own copy of the image, rather than the input or we would be messing with another tools memory, which is very dangerous. The Gain() method accepts 4 arguments. One for each of the Red, Green, Blue, and Alpha channels.

out:Gain(opacity, opacity, opacity, opacity);

Now, lets put all of that together to create our Process() function.

function Process(req)
    local opacity = InOpacity:GetValue(req).Value;
    local img = InImage:GetValue(req);
    local out = img:CopyOf();
    out:Gain(opacity, opacity, opacity, opacity);
    OutImage:Set(req, out);
end

Sample Code

Here's what that looks like all together. If you haven't figured it by now, we just created our own Opacity node!

FuRegisterClass("PizzaBlu.SampleOpacity", CT_Tool, {
    REGS_Name = "SampleOpacity",
    REG_NoPreCalcProcess = true,
});

function Create()
    InOpacity = self:AddInput("Opacity", "Opacity", {
        LINKID_DataType = "Number",
        INPID_InputControl = "SliderControl",
        INP_Default = 1.0,
    });
    InImage = self:AddInput("Input", "Input", {
        LINKID_DataType = "Image",
        LINK_Main = 1,
    });
    OutImage = self:AddOutput("Output", "Output", {
        LINKID_DataType = "Image",
        LINK_Main = 1,
    });
end

function Process(req)
    local opacity = InOpacity:GetValue(req).Value;
    local img = InImage:GetValue(req);
    local out = img:CopyOf();
    out:Gain(opacity, opacity, opacity, opacity);
    OutImage:Set(req, out);
end