mirror of
https://github.com/Ale32bit/Capy64.git
synced 2025-02-18 07:32:30 +00:00
Add Task object for async functions
This commit is contained in:
parent
7fff12c9ac
commit
f9aeb9f793
6 changed files with 436 additions and 63 deletions
|
@ -62,13 +62,14 @@ local function drawVendorImage()
|
|||
|
||||
local w, h = gpu.getSize()
|
||||
local ok, err = pcall(function()
|
||||
local buffer<close>, width, height = gpu.loadImage("/boot/vendor.bmp")
|
||||
local task<close> = gpu.loadImageAsync("/boot/vendor.bmp")
|
||||
local buffer<close> = task:await()
|
||||
|
||||
local x, y =
|
||||
math.ceil((w / 2) - (width / 2)),
|
||||
math.ceil((h / 2) - (height / 2))
|
||||
math.ceil((w / 2) - (buffer.width / 2)),
|
||||
math.ceil((h / 2) - (buffer.height / 2))
|
||||
|
||||
gpu.drawBuffer(buffer, x, y, width, height)
|
||||
gpu.drawBuffer(buffer, x, y)
|
||||
end)
|
||||
|
||||
if not ok then
|
||||
|
|
|
@ -73,7 +73,7 @@ public class Event : IComponent
|
|||
return nargs;
|
||||
}
|
||||
|
||||
private static int L_Pull(IntPtr state)
|
||||
public static int L_Pull(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ using Microsoft.Xna.Framework.Input;
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Capy64.Runtime.Libraries;
|
||||
|
||||
|
@ -123,8 +124,8 @@ public class GPU : IComponent
|
|||
},
|
||||
new()
|
||||
{
|
||||
name = "loadImage",
|
||||
function = L_LoadImage,
|
||||
name = "loadImageAsync",
|
||||
function = L_LoadImageAsync,
|
||||
},
|
||||
new()
|
||||
{
|
||||
|
@ -412,7 +413,7 @@ public class GPU : IComponent
|
|||
_game.Drawing.Canvas.GetData(buffer);
|
||||
|
||||
ObjectManager.PushObject(L, buffer);
|
||||
L.SetMetaTable(GPUBuffer.ObjectType);
|
||||
L.SetMetaTable(GPUBufferMeta.ObjectType);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -421,9 +422,9 @@ public class GPU : IComponent
|
|||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
var buffer = GPUBuffer.CheckBuffer(L, false);
|
||||
var buffer = GPUBufferMeta.CheckBuffer(L, false);
|
||||
|
||||
_game.Drawing.Canvas.SetData(buffer);
|
||||
_game.Drawing.Canvas.SetData(buffer.Buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -438,7 +439,7 @@ public class GPU : IComponent
|
|||
var buffer = new uint[width * height];
|
||||
|
||||
ObjectManager.PushObject(L, buffer);
|
||||
L.SetMetaTable(GPUBuffer.ObjectType);
|
||||
L.SetMetaTable(GPUBufferMeta.ObjectType);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -447,30 +448,23 @@ public class GPU : IComponent
|
|||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
var buffer = GPUBuffer.CheckBuffer(L, false);
|
||||
var buffer = GPUBufferMeta.CheckBuffer(L, false);
|
||||
|
||||
var x = (int)L.CheckInteger(2) - 1;
|
||||
var y = (int)L.CheckInteger(3) - 1;
|
||||
var w = (int)L.CheckInteger(4);
|
||||
var h = (int)L.CheckInteger(5);
|
||||
|
||||
if (w * h != buffer.Length)
|
||||
{
|
||||
L.Error("width and height do not match buffer size");
|
||||
}
|
||||
|
||||
_game.Drawing.DrawBuffer(buffer, new()
|
||||
_game.Drawing.DrawBuffer(buffer.Buffer, new()
|
||||
{
|
||||
X = x,
|
||||
Y = y,
|
||||
Width = w,
|
||||
Height = h,
|
||||
Width = buffer.Width,
|
||||
Height = buffer.Height,
|
||||
});
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int L_LoadImage(IntPtr state)
|
||||
private static int L_LoadImageAsync(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
|
@ -484,6 +478,8 @@ public class GPU : IComponent
|
|||
return 0;
|
||||
}
|
||||
|
||||
var task = TaskMeta.Push(L, GPUBufferMeta.ObjectType);
|
||||
|
||||
Texture2D texture;
|
||||
try
|
||||
{
|
||||
|
@ -491,44 +487,52 @@ public class GPU : IComponent
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
L.Error(e.Message);
|
||||
return 0;
|
||||
task.Reject(e.Message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
var data = new uint[texture.Width * texture.Height];
|
||||
texture.GetData(data);
|
||||
|
||||
if (_game.EngineMode == EngineMode.Classic)
|
||||
Task.Run(() =>
|
||||
{
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
if (_game.EngineMode == EngineMode.Classic)
|
||||
{
|
||||
var value = data[i];
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
var value = data[i];
|
||||
|
||||
// ABGR to RGB
|
||||
value =
|
||||
((value & 0x00_00_00_FFU) << 16) | // move R
|
||||
(value & 0x00_00_FF_00U) | // move G
|
||||
((value & 0x00_FF_00_00U) >> 16); // move B
|
||||
// ABGR to RGB
|
||||
value =
|
||||
((value & 0x00_00_00_FFU) << 16) | // move R
|
||||
(value & 0x00_00_FF_00U) | // move G
|
||||
((value & 0x00_FF_00_00U) >> 16); // move B
|
||||
|
||||
value = ColorPalette.GetColor(value);
|
||||
value = ColorPalette.GetColor(value);
|
||||
|
||||
// RGB to ABGR
|
||||
value =
|
||||
((value & 0x00_FF_00_00U) >> 16) | // move R
|
||||
(value & 0x00_00_FF_00U) | // move G
|
||||
((value & 0x00_00_00_FFU) << 16) | // move B
|
||||
0xFF_00_00_00U;
|
||||
// RGB to ABGR
|
||||
value =
|
||||
((value & 0x00_FF_00_00U) >> 16) | // move R
|
||||
(value & 0x00_00_FF_00U) | // move G
|
||||
((value & 0x00_00_00_FFU) << 16) | // move B
|
||||
0xFF_00_00_00U;
|
||||
|
||||
data[i] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ObjectManager.PushObject(L, data);
|
||||
L.SetMetaTable(GPUBuffer.ObjectType);
|
||||
L.PushInteger(texture.Width);
|
||||
L.PushInteger(texture.Height);
|
||||
var buffer = new GPUBufferMeta.GPUBuffer
|
||||
{
|
||||
Buffer = data,
|
||||
Height = texture.Height,
|
||||
Width = texture.Width,
|
||||
};
|
||||
task.Fulfill(buffer);
|
||||
|
||||
texture.Dispose();
|
||||
texture.Dispose();
|
||||
});
|
||||
|
||||
return 3;
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int L_Clear(IntPtr state)
|
||||
|
|
|
@ -72,6 +72,11 @@ public class Machine : IComponent
|
|||
name = "getClipboard",
|
||||
function = L_GetClipboard,
|
||||
},
|
||||
new()
|
||||
{
|
||||
name = "task",
|
||||
function = L_Task,
|
||||
},
|
||||
new(),
|
||||
};
|
||||
|
||||
|
@ -87,6 +92,18 @@ public class Machine : IComponent
|
|||
return 1;
|
||||
}
|
||||
|
||||
private static int L_Task(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
var task = new Objects.TaskMeta.RuntimeTask("test");
|
||||
|
||||
ObjectManager.PushObject(L, task);
|
||||
L.SetMetaTable(Objects.TaskMeta.ObjectType);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int L_Shutdown(IntPtr _)
|
||||
{
|
||||
RuntimeManager.Shutdown();
|
||||
|
|
|
@ -20,10 +20,17 @@ using System;
|
|||
|
||||
namespace Capy64.Runtime.Objects;
|
||||
|
||||
public class GPUBuffer : IComponent
|
||||
public class GPUBufferMeta : IComponent
|
||||
{
|
||||
public const string ObjectType = "GPUBuffer";
|
||||
|
||||
public struct GPUBuffer
|
||||
{
|
||||
public uint[] Buffer { get; set; }
|
||||
public int Width { get; set; }
|
||||
public int Height { get; set; }
|
||||
}
|
||||
|
||||
private static LuaRegister[] MetaMethods = new LuaRegister[]
|
||||
{
|
||||
new()
|
||||
|
@ -61,7 +68,8 @@ public class GPUBuffer : IComponent
|
|||
};
|
||||
|
||||
private static IGame _game;
|
||||
public GPUBuffer(IGame game) {
|
||||
public GPUBufferMeta(IGame game)
|
||||
{
|
||||
_game = game;
|
||||
}
|
||||
|
||||
|
@ -72,7 +80,7 @@ public class GPUBuffer : IComponent
|
|||
|
||||
public static uint GetColor(uint color)
|
||||
{
|
||||
if(_game.EngineMode == EngineMode.Classic)
|
||||
if (_game.EngineMode == EngineMode.Classic)
|
||||
return ColorPalette.GetColor(color);
|
||||
|
||||
return color;
|
||||
|
@ -84,18 +92,18 @@ public class GPUBuffer : IComponent
|
|||
L.SetFuncs(MetaMethods, 0);
|
||||
}
|
||||
|
||||
public static uint[] ToBuffer(Lua L, bool gc = false)
|
||||
public static GPUBuffer ToBuffer(Lua L, bool gc = false)
|
||||
{
|
||||
return ObjectManager.ToObject<uint[]>(L, 1, gc);
|
||||
return ObjectManager.ToObject<GPUBuffer>(L, 1, gc);
|
||||
}
|
||||
|
||||
public static uint[] CheckBuffer(Lua L, bool gc = false)
|
||||
public static GPUBuffer CheckBuffer(Lua L, bool gc = false)
|
||||
{
|
||||
var obj = ObjectManager.CheckObject<uint[]>(L, 1, ObjectType, gc);
|
||||
if (obj is null)
|
||||
var obj = ObjectManager.CheckObject<GPUBuffer>(L, 1, ObjectType, gc);
|
||||
if (obj.Buffer is null)
|
||||
{
|
||||
L.Error("attempt to use a closed buffer");
|
||||
return null;
|
||||
return default;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
@ -108,19 +116,27 @@ public class GPUBuffer : IComponent
|
|||
|
||||
if (!L.IsInteger(2))
|
||||
{
|
||||
L.PushNil();
|
||||
var vkey = L.ToString(2);
|
||||
|
||||
if (vkey == "width")
|
||||
L.PushInteger(buffer.Width);
|
||||
else if (vkey == "height")
|
||||
L.PushInteger(buffer.Height);
|
||||
else
|
||||
L.PushNil();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
var key = L.ToInteger(2);
|
||||
|
||||
if (key < 0 || key >= buffer.Length)
|
||||
if (key < 0 || key >= buffer.Buffer.Length)
|
||||
{
|
||||
L.PushNil();
|
||||
return 1;
|
||||
}
|
||||
|
||||
var value = buffer[key];
|
||||
var value = buffer.Buffer[key];
|
||||
|
||||
// ABGR to RGB
|
||||
value =
|
||||
|
@ -145,7 +161,7 @@ public class GPUBuffer : IComponent
|
|||
|
||||
var key = L.ToInteger(2);
|
||||
|
||||
if (key < 0 || key >= buffer.Length)
|
||||
if (key < 0 || key >= buffer.Buffer.Length)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -166,7 +182,7 @@ public class GPUBuffer : IComponent
|
|||
0xFF_00_00_00U;
|
||||
|
||||
|
||||
buffer[key] = value;
|
||||
buffer.Buffer[key] = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -186,7 +202,7 @@ public class GPUBuffer : IComponent
|
|||
|
||||
var buffer = CheckBuffer(L, false);
|
||||
|
||||
L.PushInteger(buffer.LongLength);
|
||||
L.PushInteger(buffer.Buffer.LongLength);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -195,7 +211,7 @@ public class GPUBuffer : IComponent
|
|||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
var buffer = ToBuffer(L);
|
||||
if (buffer is not null)
|
||||
if (buffer.Buffer is not null)
|
||||
{
|
||||
L.PushString("GPUBuffer ({0:X})", (ulong)&buffer);
|
||||
}
|
335
Capy64/Runtime/Objects/Task.cs
Normal file
335
Capy64/Runtime/Objects/Task.cs
Normal file
|
@ -0,0 +1,335 @@
|
|||
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using Capy64.API;
|
||||
using Cyotek.Drawing.BitmapFont;
|
||||
using KeraLua;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Capy64.Runtime.Objects;
|
||||
|
||||
public class TaskMeta : IComponent
|
||||
{
|
||||
private static IGame _game;
|
||||
public TaskMeta(IGame game)
|
||||
{
|
||||
_game = game;
|
||||
}
|
||||
|
||||
public const string ObjectType = "Task";
|
||||
|
||||
public enum TaskStatus
|
||||
{
|
||||
Running,
|
||||
Succeeded,
|
||||
Failed,
|
||||
}
|
||||
public class RuntimeTask
|
||||
{
|
||||
public RuntimeTask(string typeName)
|
||||
{
|
||||
TypeName = typeName;
|
||||
}
|
||||
public Guid Guid { get; set; } = Guid.NewGuid();
|
||||
public string TypeName { get; set; }
|
||||
public TaskStatus Status { get; set; } = TaskStatus.Running;
|
||||
public object Result { get; private set; }
|
||||
public string Error { get; private set; }
|
||||
|
||||
public void Fulfill<T>(T obj)
|
||||
{
|
||||
Status = TaskStatus.Succeeded;
|
||||
|
||||
Result = obj;
|
||||
|
||||
_game.LuaRuntime.QueueEvent("task_finish", LK =>
|
||||
{
|
||||
LK.PushString(Guid.ToString());
|
||||
|
||||
ObjectManager.PushObject(LK, obj);
|
||||
LK.SetMetaTable(TypeName);
|
||||
|
||||
LK.PushNil();
|
||||
|
||||
return 3;
|
||||
});
|
||||
}
|
||||
|
||||
public void Fulfill(Action<Lua> lk)
|
||||
{
|
||||
Status = TaskStatus.Succeeded;
|
||||
|
||||
Result = lk;
|
||||
|
||||
_game.LuaRuntime.QueueEvent("task_finish", LK =>
|
||||
{
|
||||
LK.PushString(Guid.ToString());
|
||||
|
||||
lk(LK);
|
||||
|
||||
LK.PushNil();
|
||||
|
||||
return 3;
|
||||
});
|
||||
}
|
||||
|
||||
public void Reject(string error, params object[] args)
|
||||
{
|
||||
Status = TaskStatus.Failed;
|
||||
Error = string.Format(error, args);
|
||||
|
||||
_game.LuaRuntime.QueueEvent("task_finish", LK =>
|
||||
{
|
||||
LK.PushString(Guid.ToString());
|
||||
|
||||
LK.PushNil();
|
||||
|
||||
LK.PushString(Error);
|
||||
|
||||
return 3;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static LuaRegister[] Methods = new LuaRegister[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
name = "await",
|
||||
function = L_Await,
|
||||
},
|
||||
new()
|
||||
{
|
||||
name = "getID",
|
||||
function = L_GetID,
|
||||
},
|
||||
new()
|
||||
{
|
||||
name = "getType",
|
||||
function = L_GetType,
|
||||
},
|
||||
new()
|
||||
{
|
||||
name = "getStatus",
|
||||
function = L_GetStatus,
|
||||
},
|
||||
new()
|
||||
{
|
||||
name = "getResult",
|
||||
function = L_GetResult,
|
||||
},
|
||||
new()
|
||||
{
|
||||
name = "getError",
|
||||
function = L_GetResult,
|
||||
},
|
||||
new(),
|
||||
};
|
||||
|
||||
private static LuaRegister[] MetaMethods = new LuaRegister[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
name = "__index",
|
||||
},
|
||||
new()
|
||||
{
|
||||
name = "__gc",
|
||||
function = LM_GC,
|
||||
},
|
||||
new()
|
||||
{
|
||||
name = "__close",
|
||||
function = LM_GC,
|
||||
},
|
||||
new()
|
||||
{
|
||||
name = "__tostring",
|
||||
function = LM_ToString,
|
||||
},
|
||||
|
||||
new(),
|
||||
};
|
||||
|
||||
public void LuaInit(Lua L)
|
||||
{
|
||||
CreateMeta(L);
|
||||
}
|
||||
|
||||
public static void CreateMeta(Lua L)
|
||||
{
|
||||
L.NewMetaTable(ObjectType);
|
||||
L.SetFuncs(MetaMethods, 0);
|
||||
L.NewLibTable(Methods);
|
||||
L.SetFuncs(Methods, 0);
|
||||
L.SetField(-2, "__index");
|
||||
L.Pop(1);
|
||||
}
|
||||
|
||||
public static RuntimeTask Push(Lua L, string typeName)
|
||||
{
|
||||
var task = new RuntimeTask(typeName);
|
||||
|
||||
ObjectManager.PushObject(L, task);
|
||||
L.SetMetaTable(ObjectType);
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
private static RuntimeTask ToTask(Lua L, bool gc = false)
|
||||
{
|
||||
return ObjectManager.ToObject<RuntimeTask>(L, 1, gc);
|
||||
}
|
||||
|
||||
private static RuntimeTask CheckTask(Lua L, bool gc = false)
|
||||
{
|
||||
var obj = ObjectManager.CheckObject<RuntimeTask>(L, 1, ObjectType, gc);
|
||||
if (obj is null)
|
||||
{
|
||||
L.Error("attempt to use a closed file");
|
||||
return null;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
private static void WaitForTask(Lua L)
|
||||
{
|
||||
L.PushCFunction(Libraries.Event.L_Pull);
|
||||
L.PushString("task_finish");
|
||||
L.CallK(1, 4, 0, LK_Await);
|
||||
}
|
||||
|
||||
private static int L_Await(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
WaitForTask(L);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int LK_Await(IntPtr state, int status, nint ctx)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
var task = CheckTask(L, false);
|
||||
var taskId = L.CheckString(3);
|
||||
|
||||
if (task.Guid.ToString() != taskId)
|
||||
{
|
||||
WaitForTask(L);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
private static int L_GetID(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
var task = CheckTask(L, false);
|
||||
|
||||
L.PushString(task.Guid.ToString());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int L_GetType(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
var task = CheckTask(L, false);
|
||||
|
||||
L.PushString(task.TypeName);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int L_GetStatus(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
var task = CheckTask(L, false);
|
||||
|
||||
L.PushString(task.Status.ToString().ToLower());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int L_GetResult(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
var task = CheckTask(L, false);
|
||||
|
||||
if (task.Status == TaskStatus.Succeeded)
|
||||
{
|
||||
if (task.Result is Action<Lua> lk)
|
||||
{
|
||||
// todo: make use of first-generated data
|
||||
lk(L);
|
||||
}
|
||||
else
|
||||
{
|
||||
ObjectManager.PushObject(L, task.Result);
|
||||
L.SetMetaTable(task.TypeName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
L.PushNil();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int L_GetError(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
var task = CheckTask(L, false);
|
||||
|
||||
if (task.Status == TaskStatus.Failed)
|
||||
{
|
||||
L.PushString(task.Error);
|
||||
}
|
||||
else
|
||||
{
|
||||
L.PushNil();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int LM_GC(IntPtr state)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int LM_ToString(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
var task = ToTask(L);
|
||||
L.PushString("Task<{0}>: {1} ({2})", task?.TypeName, task?.Guid, task?.Status);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue