<bigger>Gpu</bigger>
Probably the most complicated node i will need to introduce here
TIP: It is based off of the digistuff gpu

<big>What is it used for</big>
So, say you are in a situation where you are rendering something, like lines for example, or you need to fill an area,
in a luacontroller, that would be very slow because of the strict limit and the fact that you don't have luajit optimizations.

Introducing: the gpu
It lets you do these things, and fast

<b>Definitions: Buffer</b> 
- an area ranging from 1x1 to 128x128, doesn't have to be a square
- a gpu can hold 8 buffers
- unlike the digistuff gpu, once minetest restarts, the buffers will be gone
- unlike the digistuff gpu, buffer indexing is from 1 to 8 instead of 0 to 7

<big>The way you enter commands</big>

1)
<mono>send_to(links.gpu, {
$C1    type = "blabla",
$C1    ...
})</mono>

You can also send multiple commands at once.

2)
<mono>send_to(links.gpu,{
$C1    {
$C1        type = "blabla",
$C1    },
$C1    {
$C1        type = "blabla" -- multiple commands!
$C1    },
$C1    ...
})</mono>

<big>Limits</big>
- 32 commands in 1 message
- max use is 100ms per second, if you go above this, it will just cut off your commands, you can see the lag in the infotext

<big>Commands</big>
<b>create_buffer</b>
$C1<mono>send_to(links.gpu,{
$C1    type = "create_buffer",
$C1    index = 1, -- a number between 1 and 8
$C1    xsize = 128, -- a number between 1 and 128
$C1    ysize = 128, -- a number between 1 and 128
$C1    fill = color, -- any colorspec, if omitted, it's black
$C1})</mono>
Creates a buffer, a buffer is required for every command.

<b>send</b>
$C1<mono>send_to(links.gpu,{
$C1    type = "send",
$C1    index = 1,
$C1    to_pos = vector.add(pos, links.matrix_screen[1][1]) -- an absolute position, if it's nil it will send to the luacontroller
$C1})</mono>
Sends a buffer to to_pos, the buffer is a table indexed by x[y][x], the color is in format "#RRGGBB"

<b>send_region</b>
$C1<mono>send_to(links.gpu,{
$C1    type = "send_region",
$C1    index = 1,
$C1    x1 = 1,
$C1    y1 = 1,
$C1    x2 = 128,
$C1    y2 = 128,
$C1    to_pos = vector.add(pos, links.matrix_screen[1][1]) -- an absolute position, if it's nil it will send to the luacontroller
$C1})</mono>
Sends a region of the buffer.

<b>draw_rect</b>
$C1<mono>send_to(links.gpu,{
$C1    type = "draw_rect",
$C1    index = 1,
$C1    x1 = 1,
$C1    y1 = 1,
$C1    x2 = 128,
$C1    y2 = 128,
$C1
$C1    fill = <color or nil>,
$C1    edge = <color or nil>,
$C1})</mono>

Draws a rectangle
If <mono>fill</mono> is <mono>nil</mono> then it will just draw the edges of the rectangle,
if <mono>edge</mono> is <mono>nil</mono> then <mono>edge</mono> will be set to the value of <mono>fill</mono>

<b>draw_line</b>
$C1<mono>send_to(links.gpu,{
$C1    type = "draw_line",
$C1    index = 1,
$C1    x1 = 1,
$C1    y1 = 1,
$C1    x2 = 128,
$C1    y2 = 128,
$C1
$C1    color = color,
$C1    antialias = true,
$C1})</mono>
Draws a line from x1 y1 to x2 y2.

<b>draw_point</b>
$C1<mono>send_to(links.gpu,{
$C1    type = "draw_point",
$C1    index = 1,
$C1    x = 1,
$C1    y = 1,
$C1
$C1    color = color,
$C1})</mono>
Sets a single pixel at the given coordinates to the given color.

<b>copy</b>
$C1<mono>send_to(links.gpu,{
$C1    type = "copy",
$C1    src = 1, -- a buffer index
$C1    dst = 2, -- a buffer index, can be equal to src
$C1    xsize = 1, -- the size of the area
$C1    ysize = 1, -- the size of the area
$C1
$C1    dstx = 1,
$C1    dsty = 1,
$C1
$C1    srcx = 1,
$C1    srcy = 1,
$C1
$C1    transparent_color = <color> -- the color used if blend_mode == "overlay",
$C1    blend_mode = "normal", 
$C1})</mono>

Copies a section from src to dst
Blend modes:
- normal - puts everything from src to dst
- nop - does nothing
- overlay - puts everything that's not the transparent color
- add - adds the colors of both buffers
- sub - subtracts the destination color from the source color
- isub - subtracts the source color from the destination color
- average - takes the average of the color
- and/or/xor/xnor/not/nand/nor - bitwise blending
- tohsv/rgbtohsv (they do the same thing) - they put the hue in red, saturation in green, and value in blue
- torgb/hsvtorgb (they do the same thing) - they undo the tohsv operation
- anything else is same as normal

They all put their values into dst

<b>load</b>
$C1<mono>send_to(links.gpu,{
$C1    type = "load",
$C1    index = 1,
$C1    buffer = <buffer>,
$C1})</mono>
Similar to createbuffer, puts the buffer in the command (indexed x[y][x]) into the gpu, at buffer index described in the command.

<b>send_packed</b>
$C1<mono>send_to(links.gpu,{
$C1    type = "send_packed",
$C1    index = 1,
$C1    to_pos = <ABSOLUTE pos or nil>,
$C1    base64 = true,
$C1})</mono>
This is very different to the digistuff gpu's sendpacked command.
The format is something like "rgbrgbrgbrgbrgbrgbrgb" where rgb is string.byte(r)..string.byte(g)..string.byte(b)

It's colorspec_to_bytes(color) .. colorspec_to_bytes(color) and so on
You can index it by doing:
<mono>
local base_idx = (((y-1)*xsize + x)*3)-2
local packed_color = string.sub(packed_data,base_idx, base_idx + 2)
local r, g, b = packed_color:byte(1), packed_color:byte(2), packed_color:byte(3)
</mono>

If base64 is enabled, it just uses core.encode_base64 to encode it

<b>send_png</b>
$C1<mono>send_to(links.gpu,{
$C1    type = "send_png",
$C1    index = 1,
$C1    to_pos = <ABSOLUTE pos or nil>,
$C1})</mono>
Encodes the buffer as a png, then base64s that, and applies a very small amount of compression

Can be directly used with the [png: texture modifier

<b>load_packed</b>
$C1<mono>send_to(links.gpu,{
$C1    type = "load_packed",
$C1    index = 1,
$C1    data = "heh",
$C1    x1 = 1,
$C1    y1 = 1,
$C1    x2 = 128,
$C1    y2 = 128,
$C1    base64 = true,
$C1})</mono>

Loads packed data into a Buffer

<b>circle</b>
$C1<mono>send_to(links.gpu,{
$C1    type = "circle",
$C1    index = 1,
$C1    r = 5,
$C1    x = 6,
$C1    y = 10,
$C1    hollow = false,
$C1    color = <color>
$C1})</mono>

Draws a circle, with r being the radius

<b>fill</b>
$C1<mono>send_to(links.gpu,{
$C1    type = "fill",
$C1    index = 1,
$C1    tolerance = 5,
$C1    x = 6,
$C1    y = 10,
$C1    color = <color>
$C1})</mono>

Performs a flood fill starting from x,y
Warning: has the potential to be laggy, when used on a 128x128 buffer, it took like on average 30ms
If you have any ideas on how to improve this, please let me (frog :>) know (or contribute to Skyblock Zero yourself)

<b>text</b>
$C1<mono>send_to(links.gpu,{
$C1    type = "text",
$C1    index = 1,
$C1    x = 6,
$C1    y = 10,
$C1    text = "hi :3",
$C1    font = nil, -- currently unused, but by default, it's "default"
$C1})</mono>

Draws text starting from y, x.
If you want to suggest a new font, you can make a pull request

<b>transform</b>
Warning: can be kinda laggy
$C1<mono>send_to(links.gpu,{
$C1    type = "transform",
$C1    index = 1,
$C1    x1 = 1,
$C1    y1 = 1,
$C1    x2 = 128,
$C1    y2 = 128,
$C1    min_x = 1,
$C1    max_x = 128,
$C1    min_y = 1,
$C1    max_y = 128,
$C1    transparent_color = "black",
$C1    matrix = { -- 2x2 linear transformation, this matrix specifically doesn't do anything
$C1        {1, 0},
$C1        {0, 1}
$C1    },
$C1    matrix = { -- 3x3 matrix for affine transformation, this matrix specifically doesn't do anything
$C1        {1, 0, 0},
$C1        {0, 1, 0},
$C1        {0, 0, 1}
$C1    },
$C1    origin_x = 128/2,
$C1    origin_y = 128/2,
$C1})</mono>

Can perform an affine or linear transformation
If you don't know what those are, they allow you to rotate and scale stuff, here are some resources for learning:
<action name=url url=https://en.wikipedia.org/wiki/Transformation_matrix>https://en.wikipedia.org/wiki/Transformation_matrix</action> - most useful to me was "examples in 2 dimensions"
<action name=url url=https://en.wikipedia.org/wiki/Affine_transformation>https://en.wikipedia.org/wiki/Affine_transformation</action> - most useful to me was "image transformation" section, also you can do some funny perspective stuff if you use the bottom row

min_x max_x min_y max_y will limit it to that region *forcefully*


<b>Convolution matrix</b>
$C1<mono>send_to(links.gpu,{
$C1    type = "convolution_matrix",
$C1    index = 1,
$C1    x1 = 1,
$C1    y1 = 1,
$C1    x2 = 128,
$C1    y2 = 128,
$C1    matrix = { -- can be a 5x5 matrix or a 3x3 matrix, this one does a box blur
$C1        {-1/9,-1/9,-1/9},
$C1        {-1/9,-1/9,-1/9},
$C1        {-1/9,-1/9,-1/9},
$C1    }
$C1})</mono>

See <action name=url url=https://en.wikipedia.org/wiki/Kernel_(image_processing)>https://en.wikipedia.org/wiki/Kernel_(image_processing)</action>
Can be laggy

<b>Shaders</b>
$C1<mono>send_to(links.gpu,{
$C1    type = "shader",
$C1    index = 1,
$C1    shader = "return {r=0,g=0,b=0}"
$C1})</mono>

They apply a shader... now this one is a little complicated....
It iterates the lua code over all the pixels in that buffer.
The lua code must return a table of {r=int,g=int,b=int} and must not have any strings (or blocky comments) because i didn't want to deal with them.
The code is limited to 500 instructions

The environment is immutable, and very simple:
- math.* - includes everything from math.* but without math.randomseed
- buffer[index(x,y)] - gives you the pixel at coordinates x, y
- index(x,y) - helps you index the buffer

Use local variables when trying to do anything
Shaders are async, so if you do something like:
1) create buffer at index 1 with color "black"
2) apply shader
3) send_png

then it will not do what you would expect... it will just give you a black png

To solve this, you need to do something like this:

1) Create buffer at index 1 with color "black"
2) apply shader
3) wait 0.5 seconds or listen for a { type = "shader_complete", err = string[]|nil } message
4) send_png

<bigger>Example</bigger>
1) Big mandelbrot set with GPU shaders
- requires you have 16 matrix screens, linked with names 1, 2, 3, ..., 16, in a square 

<mono>
$C1send_to(links.gpu,{
$C1    {
$C1        type = "create_buffer",
$C1        index = 1,
$C1        xsize = 128,
$C1        ysize = 128,
$C1        fill = "#555555"
$C1    },
$C1    {
$C1        type = "shader",
$C1        index = 1,
$C1        shader = [[ 
$C1             local maxi = 20
$C1             local scale = 1/50
$C1             local offsetx, offsety = 80*scale, 60*scale
$C1             local intensity = 1
$C1             local function normal_map(x)
$C1                 if x < maxi then
$C1                    return math.floor(intensity*math.sqrt(x/maxi)*255)
$C1                 else
$C1                   return 0
$C1                 end
$C1             end
$C1
$C1             local zi, zr, i = 0,0,0
$C1             local x = (x*scale)-offsetx
$C1             local y = (y*scale)-offsety
$C1             while i < maxi do
$C1                if (zi*zi+zr*zr >= 4) then break end
$C1                zr, zi, i = zr*zr-zi*zi+x, 2*zr*zi+y, i+1
$C1             end
$C1
$C1             local v = normal_map(i)
$C1             return {r=v/2, g=v, b=v/2}
$C1        ]],
$C1    },
$C1})
$C1wait(0.5) -- shaders are async, so wait a bit
$C1send_to(links.gpu, {
$C1    {
$C1        type = "send_region",
$C1        index = 1,
$C1        x1 = 1, y1 = 1, x2 = 32, y2 = 32,
$C1        to_pos = pos+links["1"][1],
$C1    },
$C1    {
$C1        type = "send_region",
$C1        index = 1,
$C1        x1 = 32, y1 = 1, x2 = 64, y2 = 32,
$C1        to_pos = pos+links["2"][1],
$C1    },
$C1    {
$C1        type = "send_region",
$C1        index = 1,
$C1        x1 = 64, y1 = 1, x2 = 96, y2 = 32,
$C1        to_pos = pos+links["3"][1],
$C1    },
$C1    {
$C1        type = "send_region",
$C1        index = 1,
$C1        x1 = 96, y1 = 1, x2 = 128, y2 = 32,
$C1        to_pos = pos+links["4"][1],
$C1    },
$C1    {
$C1        type = "send_region",
$C1        index = 1,
$C1        x1 = 1, y1 = 32, x2 = 32, y2 = 64,
$C1        to_pos = pos+links["5"][1],
$C1    },
$C1    {
$C1        type = "send_region",
$C1        index = 1,
$C1        x1 = 32, y1 = 32, x2 = 64, y2 = 64,
$C1        to_pos = pos+links["6"][1],
$C1    },{
$C1        type = "send_region",
$C1        index = 1,
$C1        x1 = 64, y1 = 32, x2 = 96, y2 = 64,
$C1        to_pos = pos+links["7"][1],
$C1    },
$C1    {
$C1        type = "send_region",
$C1        index = 1,
$C1        x1 = 96, y1 = 32, x2 = 128, y2 = 64,
$C1        to_pos = pos+links["8"][1],
$C1    },
$C1
$C1    {
$C1        type = "send_region",
$C1        index = 1,
$C1        x1 = 1, y1 = 64, x2 = 32, y2 = 96,
$C1        to_pos = pos+links["9"][1],
$C1    },
$C1    {
$C1        type = "send_region",
$C1        index = 1,
$C1        x1 = 32, y1 = 64, x2 = 64, y2 = 96,
$C1        to_pos = pos+links["10"][1],
$C1    },
$C1    {
$C1        type = "send_region",
$C1        index = 1,
$C1        x1 = 64, y1 = 64, x2 = 96, y2 = 96,
$C1        to_pos = pos+links["11"][1],
$C1    },
$C1    {
$C1        type = "send_region",
$C1        index = 1,
$C1        x1 = 96, y1 = 64, x2 = 128, y2 = 96,
$C1        to_pos = pos+links["12"][1],
$C1    },
$C1
$C1
$C1    {
$C1        type = "send_region",
$C1        index = 1,
$C1        x1 = 1, y1 = 96, x2 = 32, y2 = 128,
$C1        to_pos = pos+links["13"][1],
$C1    },
$C1    {
$C1        type = "send_region",
$C1        index = 1,
$C1        x1 = 32, y1 = 96, x2 = 64, y2 = 128,
$C1        to_pos = pos+links["14"][1],
$C1    },
$C1    {
$C1        type = "send_region",
$C1        index = 1,
$C1        x1 = 64, y1 = 96, x2 = 96, y2 = 128,
$C1        to_pos = pos+links["15"][1],
$C1    },
$C1    {
$C1        type = "send_region",
$C1        index = 1,
$C1        x1 = 96, y1 = 96, x2 = 128, y2 = 128,
$C1        to_pos = pos+links["16"][1],
$C1}})
$C1</mono>