Исходник [MoonLoader] MoonCL - параллельное программирование в Lua на OpenCL (Pre-Release)

Loverxzc

Участник
Автор темы
44
20

MoonCL - либа для параллельных вычислений на видеоядре прямо в Lua скрипте.

Часть рутинного кода написана вместе с нейронкой, её код был проверен вручную, я знаком с такими понятиями, как: буффер, аллокация(alloc) и деаллокация(free), сборщик мусора и т. п. Полных тестов на стабильность не было. Вы имеете полное право закидать меня кокэшками за это.
Скрипт в пре-релизе, если есть какие-то замечания или предложения - Issues и Pull Requests ждут, буду рад получить помощь.
Много кто может задать вопрос: "А нахуя мне OpenCL, если есть effil?". И это справедливо подмечено, вся эта затея пришла в голову чисто случайно, сделано по приколу.
Возможно, эта либа даст кому-нибудь возможность сделать что-то абсурдное или глобальное, хз.


Дает возможность считать тяжелую математику параллельно на GPU через OpenCL 1.2 без фризов игры. Вся тяжелая работа выносится в асинхронную очередь, а скрипт просто опрашивает статус в тике. Для пары десятков или сотен проверок она не нужна - быстрее сделать обычным циклом или через FFI. Смысл появляется там, где надо пачками считать тысячи элементов: поиск дистанций, рейкасты по куче хитбоксов, пространственные фильтры или матричные трансформации точек.

Установка​

Кинуть папку moonloader в корень игры.
Получится примерно так:

moonloader/lib/mooncl/
├── init.lua
└── mooncl.dll

Вшитые ядра​

Базовые ядра уже внутри DLL:
  • batch_distance - дистанция от массива точек до одной целевой
  • ray_aabb_intersect - рейкаст луча по массиву хитбоксов (AABB), возвращает дистанцию или -1.0
  • mat4_transform_points - умножение массива float4 на матрицу 4x4
  • batch_spatial_filter - проверка радиуса, пишет маску 1/0
  • batch_clamp / batch_lerp - кламп и интерполяция массивов

Пример использования​

Считаем расстояния для 10 000 точек:
require("moonloader")
local mooncl = require("mooncl")
local ffi = require("ffi")

function main()
    while not isSampAvailable() do wait(0) end

    local ok, dev = mooncl.init()
    if not ok then return print("MoonCL init error: " .. dev) end
    print("MoonCL run on: " .. dev)

    local COUNT = 10000

    -- Данные в RAM
    local points = mooncl.new_float4_array(COUNT)
    for i = 0, COUNT - 1 do
        points[i].x, points[i].y, points[i].z, points[i].w = i, i * 0.5, 0.0, 1.0
    end

    -- Буферы в VRAM
    local in_buf = mooncl.create_buffer(COUNT * 16)
    local out_buf = mooncl.create_buffer(COUNT * 4)
    in_buf:write(points)

    -- Настройка ядра
    local kernel = mooncl.get_kernel("batch_distance")
    kernel:set_buffer(0, in_buf)
    kernel:set_float4(1, 0, 0, 0, 0)
    kernel:set_buffer(2, out_buf)
    kernel:set_int(3, COUNT)

    -- Считаем асинхронно
    local run_evt = kernel:run_async(COUNT)
    while not run_evt:is_done() do wait(0) end

    -- Забираем результат
    local results = ffi.new("float[?]", COUNT)
    local read_evt = out_buf:read_async(results)
    while not read_evt:is_done() do wait(0) end

    print("Result for 500: " .. results[500])
    -- Необязательно, но есть возможность вручную освободить память
    in_buf:free()
    out_buf:free()
    kernel:free()
end

Результат:
Снимок экрана_20260904_212408.png

Свои ядра

Если ты продвинутый гей, то можно скомпилировать любой свой код ядра на лету прямо в скрипте:

Lua:
local src = [[
__kernel void add(__global const float* a, __global const float* b, __global float* c, int count) {
    int id = get_global_id(0);
    if (id >= count) return;
    c[id] = a[id] + b[id];
}
]]

local kernel, err = mooncl.compile(src, "add")
if not kernel then return print("Compile error:\n" .. err) end


Исходник и инструкция:
 

MrCreepTon

Неизвестный
Всефорумный модератор
2,411
5,578
Ну как будто если для самп скрипта нужно использовать GPU, то либо алгоритм неэффективно написан, либо стоит кэшировать результаты.
Но как наличие такой возможности - круто
 
  • Нравится
Реакции: Loverxzc