<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 (use only local variables), and very simple:
- math.* - includes everything from math.* but without math.randomseed
- buffer - the buffer... is a copy of the buffer, is immutable
- x, y - the current coordinates
- buffer[index(x,y)] - gives you the pixel at coordinates x, y
- index(x,y) - helps you index the buffer, you can do conways game of life with this but good luck!

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, from top left to bottom right

<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>
