committed by
GitHub
parent
ea60a81fdf
commit
103bc19508
16
Pow3r/LinqHelper.cs
Normal file
16
Pow3r/LinqHelper.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Pow3r
|
||||
{
|
||||
public static class LinqHelper
|
||||
{
|
||||
public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
|
||||
{
|
||||
foreach (var item in enumerable)
|
||||
{
|
||||
action(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
21
Pow3r/Pow3r.csproj
Normal file
21
Pow3r/Pow3r.csproj
Normal file
@@ -0,0 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ImGui.NET" Version="1.78.0" />
|
||||
<PackageReference Include="OpenTK" Version="4.5.0" />
|
||||
<PackageReference Include="Veldrid" Version="4.8.0" />
|
||||
<PackageReference Include="Veldrid.SPIRV" Version="1.0.14" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Content.Server\Content.Server.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
127
Pow3r/Program.OpenGL.cs
Normal file
127
Pow3r/Program.OpenGL.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using ImGuiNET;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using OpenTK.Windowing.GraphicsLibraryFramework;
|
||||
|
||||
namespace Pow3r
|
||||
{
|
||||
internal sealed unsafe partial class Program
|
||||
{
|
||||
private int _glFontTexture;
|
||||
|
||||
private void InitOpenGL()
|
||||
{
|
||||
if (GL.GetString(StringName.Extensions).Split(' ').Contains("GL_ARB_debug_output"))
|
||||
GL.Arb.DebugMessageCallback(GLDebugCallbackDelegate, (nint) 0x0105);
|
||||
|
||||
GL.Enable(EnableCap.ScissorTest);
|
||||
GL.Enable(EnableCap.Blend);
|
||||
GL.BlendEquation(BlendEquationMode.FuncAdd);
|
||||
GL.BlendFuncSeparate(
|
||||
BlendingFactorSrc.SrcAlpha,
|
||||
BlendingFactorDest.OneMinusSrcAlpha,
|
||||
BlendingFactorSrc.One,
|
||||
BlendingFactorDest.OneMinusSrcAlpha);
|
||||
|
||||
var io = ImGui.GetIO();
|
||||
|
||||
io.Fonts.GetTexDataAsRGBA32(out byte* pixels, out var width, out var height, out _);
|
||||
|
||||
_glFontTexture = GL.GenTexture();
|
||||
GL.BindTexture(TextureTarget.Texture2D, _glFontTexture);
|
||||
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, width, height, 0, PixelFormat.Bgra, PixelType.UnsignedByte, (nint) pixels);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Nearest);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.Nearest);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int) TextureWrapMode.ClampToEdge);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int) TextureWrapMode.ClampToEdge);
|
||||
|
||||
/*
|
||||
GL.TextureParameter(_fontTexture, TextureParameterName.TextureSwizzleR, 1);
|
||||
GL.TextureParameter(_fontTexture, TextureParameterName.TextureSwizzleG, 1);
|
||||
GL.TextureParameter(_fontTexture, TextureParameterName.TextureSwizzleB, 1);
|
||||
GL.TextureParameter(_fontTexture, TextureParameterName.TextureSwizzleA, (int) All.Red);*/
|
||||
|
||||
io.Fonts.SetTexID((nint) _glFontTexture);
|
||||
io.Fonts.ClearTexData();
|
||||
}
|
||||
|
||||
private void RenderOpenGL()
|
||||
{
|
||||
GLFW.GetFramebufferSize(_window.WindowPtr, out var fbW, out var fbH);
|
||||
GL.Viewport(0, 0, fbW, fbH);
|
||||
GL.Disable(EnableCap.ScissorTest);
|
||||
GL.ClearColor(0, 0, 0, 1);
|
||||
GL.Clear(ClearBufferMask.ColorBufferBit);
|
||||
GL.Enable(EnableCap.ScissorTest);
|
||||
GL.Enable(EnableCap.Texture2D);
|
||||
|
||||
var drawData = ImGui.GetDrawData();
|
||||
|
||||
var l = drawData.DisplayPos.X;
|
||||
var r = drawData.DisplayPos.X + drawData.DisplaySize.X;
|
||||
var t = drawData.DisplayPos.Y;
|
||||
var b = drawData.DisplayPos.Y + drawData.DisplaySize.Y;
|
||||
|
||||
var matrix = Matrix4x4.CreateOrthographicOffCenter(l, r, b, t, -1, 1);
|
||||
|
||||
GL.MatrixMode(MatrixMode.Projection);
|
||||
GL.LoadMatrix((float*) &matrix);
|
||||
|
||||
var clipOff = drawData.DisplayPos;
|
||||
var clipScale = drawData.FramebufferScale;
|
||||
|
||||
GL.EnableClientState(ArrayCap.VertexArray);
|
||||
GL.EnableClientState(ArrayCap.TextureCoordArray);
|
||||
GL.EnableClientState(ArrayCap.ColorArray);
|
||||
|
||||
for (var n = 0; n < drawData.CmdListsCount; n++)
|
||||
{
|
||||
var drawList = drawData.CmdListsRange[n];
|
||||
|
||||
for (var cmdI = 0; cmdI < drawList.CmdBuffer.Size; cmdI++)
|
||||
{
|
||||
var cmd = drawList.CmdBuffer[cmdI];
|
||||
|
||||
GL.BindTexture(TextureTarget.Texture2D, (uint) cmd.TextureId);
|
||||
|
||||
Vector4 clipRect = default;
|
||||
clipRect.X = (cmd.ClipRect.X - clipOff.X) * clipScale.X;
|
||||
clipRect.Y = (cmd.ClipRect.Y - clipOff.Y) * clipScale.Y;
|
||||
clipRect.Z = (cmd.ClipRect.Z - clipOff.X) * clipScale.X;
|
||||
clipRect.W = (cmd.ClipRect.W - clipOff.Y) * clipScale.Y;
|
||||
|
||||
GL.Scissor((int) clipRect.X, (int) (fbH - clipRect.W), (int) (clipRect.Z - clipRect.X),
|
||||
(int) (clipRect.W - clipRect.Y));
|
||||
|
||||
IntPtr adjustedVB = drawList.VtxBuffer.Data + (nint) (sizeof(ImDrawVert) * cmd.VtxOffset);
|
||||
|
||||
GL.VertexPointer(2, VertexPointerType.Float, sizeof(ImDrawVert), adjustedVB);
|
||||
GL.TexCoordPointer(2, TexCoordPointerType.Float, sizeof(ImDrawVert), adjustedVB + 8);
|
||||
GL.ColorPointer(4, ColorPointerType.UnsignedByte, sizeof(ImDrawVert), adjustedVB + 16);
|
||||
|
||||
GL.DrawElements(PrimitiveType.Triangles, (int) cmd.ElemCount,
|
||||
DrawElementsType.UnsignedShort,
|
||||
drawList.IdxBuffer.Data + (nint) (cmd.IdxOffset * 2));
|
||||
}
|
||||
}
|
||||
|
||||
_window.SwapBuffers();
|
||||
}
|
||||
|
||||
private static readonly DebugProcArb GLDebugCallbackDelegate = GLDebugCallback;
|
||||
|
||||
private static void GLDebugCallback(DebugSource source, DebugType type, int id, DebugSeverity severity,
|
||||
int length, IntPtr message, IntPtr userParam)
|
||||
{
|
||||
var msg = Encoding.UTF8.GetString((byte*) message, length);
|
||||
|
||||
if (severity == DebugSeverity.DebugSeverityNotification)
|
||||
return;
|
||||
|
||||
Console.WriteLine($"[{type}][{severity}] {source}: {msg}");
|
||||
}
|
||||
}
|
||||
}
|
||||
71
Pow3r/Program.SaveLoad.cs
Normal file
71
Pow3r/Program.SaveLoad.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Content.Server.Power.Pow3r;
|
||||
using static Content.Server.Power.Pow3r.PowerState;
|
||||
|
||||
namespace Pow3r
|
||||
{
|
||||
internal sealed partial class Program
|
||||
{
|
||||
private void LoadFromDisk()
|
||||
{
|
||||
if (!File.Exists("data.json"))
|
||||
return;
|
||||
|
||||
var dat = JsonSerializer.Deserialize<DiskDat>(File.ReadAllBytes("data.json"), SerializerOptions);
|
||||
|
||||
if (dat == null)
|
||||
return;
|
||||
|
||||
_paused = dat.Paused;
|
||||
_nextId = dat.NextId;
|
||||
_currentSolver = dat.Solver;
|
||||
|
||||
_state = new PowerState
|
||||
{
|
||||
Networks = dat.Networks.ToDictionary(n => n.Id, n => n),
|
||||
Supplies = dat.Supplies.ToDictionary(s => s.Id, s => s),
|
||||
Loads = dat.Loads.ToDictionary(l => l.Id, l => l),
|
||||
Batteries = dat.Batteries.ToDictionary(b => b.Id, b => b)
|
||||
};
|
||||
|
||||
_displayLoads = dat.Loads.ToDictionary(n => n.Id, _ => new DisplayLoad());
|
||||
_displaySupplies = dat.Supplies.ToDictionary(n => n.Id, _ => new DisplaySupply());
|
||||
_displayBatteries = dat.Batteries.ToDictionary(n => n.Id, _ => new DisplayBattery());
|
||||
_displayNetworks = dat.Networks.ToDictionary(n => n.Id, _ => new DisplayNetwork());
|
||||
|
||||
RefreshLinks();
|
||||
}
|
||||
|
||||
private void SaveToDisk()
|
||||
{
|
||||
var data = new DiskDat
|
||||
{
|
||||
Paused = _paused,
|
||||
NextId = _nextId,
|
||||
Solver = _currentSolver,
|
||||
|
||||
Loads = _state.Loads.Values.ToList(),
|
||||
Batteries = _state.Batteries.Values.ToList(),
|
||||
Networks = _state.Networks.Values.ToList(),
|
||||
Supplies = _state.Supplies.Values.ToList()
|
||||
};
|
||||
|
||||
File.WriteAllBytes("data.json", JsonSerializer.SerializeToUtf8Bytes(data, SerializerOptions));
|
||||
}
|
||||
|
||||
private sealed class DiskDat
|
||||
{
|
||||
public bool Paused;
|
||||
public int NextId;
|
||||
public int Solver;
|
||||
|
||||
public List<Load> Loads;
|
||||
public List<Network> Networks;
|
||||
public List<Supply> Supplies;
|
||||
public List<Battery> Batteries;
|
||||
}
|
||||
}
|
||||
}
|
||||
137
Pow3r/Program.Simulation.cs
Normal file
137
Pow3r/Program.Simulation.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Content.Server.Power.Pow3r;
|
||||
using static Content.Server.Power.Pow3r.PowerState;
|
||||
|
||||
|
||||
namespace Pow3r
|
||||
{
|
||||
internal sealed partial class Program
|
||||
{
|
||||
private const int MaxTickData = 180;
|
||||
|
||||
private int _nextId = 1;
|
||||
private PowerState _state = new();
|
||||
private Network _linking;
|
||||
private int _tickDataIdx;
|
||||
private bool _paused;
|
||||
|
||||
private readonly string[] _solverNames =
|
||||
{
|
||||
nameof(GraphWalkSolver),
|
||||
nameof(BatteryRampPegSolver),
|
||||
nameof(NoOpSolver)
|
||||
};
|
||||
|
||||
private readonly IPowerSolver[] _solvers = {
|
||||
new GraphWalkSolver(),
|
||||
new BatteryRampPegSolver(),
|
||||
new NoOpSolver()
|
||||
};
|
||||
|
||||
private int _currentSolver;
|
||||
|
||||
private readonly float[] _simTickTimes = new float[MaxTickData];
|
||||
private readonly Queue<object> _remQueue = new();
|
||||
private readonly Stopwatch _simStopwatch = new Stopwatch();
|
||||
|
||||
private NodeId AllocId()
|
||||
{
|
||||
return new(_nextId++);
|
||||
}
|
||||
|
||||
private void Tick(float frameTime)
|
||||
{
|
||||
if (_paused)
|
||||
return;
|
||||
|
||||
RunSingleStep(frameTime);
|
||||
}
|
||||
|
||||
private void RunSingleStep(float frameTime)
|
||||
{
|
||||
_simStopwatch.Restart();
|
||||
_tickDataIdx = (_tickDataIdx + 1) % MaxTickData;
|
||||
|
||||
_solvers[_currentSolver].Tick(frameTime, _state);
|
||||
|
||||
// Update tick history.
|
||||
foreach (var load in _state.Loads.Values)
|
||||
{
|
||||
var displayLoad = _displayLoads[load.Id];
|
||||
displayLoad.ReceivedPowerData[_tickDataIdx] = load.ReceivingPower;
|
||||
}
|
||||
|
||||
foreach (var supply in _state.Supplies.Values)
|
||||
{
|
||||
var displaySupply = _displaySupplies[supply.Id];
|
||||
displaySupply.SuppliedPowerData[_tickDataIdx] = supply.CurrentSupply;
|
||||
}
|
||||
|
||||
foreach (var battery in _state.Batteries.Values)
|
||||
{
|
||||
var displayBattery = _displayBatteries[battery.Id];
|
||||
displayBattery.StoredPowerData[_tickDataIdx] = battery.CurrentStorage;
|
||||
displayBattery.ReceivingPowerData[_tickDataIdx] = battery.CurrentReceiving;
|
||||
displayBattery.SuppliedPowerData[_tickDataIdx] = battery.CurrentSupply;
|
||||
}
|
||||
|
||||
_simTickTimes[_tickDataIdx] = (float) _simStopwatch.Elapsed.TotalMilliseconds;
|
||||
}
|
||||
|
||||
private void RunSingleStep()
|
||||
{
|
||||
RunSingleStep(1f/_tps);
|
||||
}
|
||||
|
||||
// Link data is stored authoritatively on networks,
|
||||
// but for easy access it is replicated into the linked components.
|
||||
// This is updated here.
|
||||
private void RefreshLinks()
|
||||
{
|
||||
foreach (var battery in _state.Batteries.Values)
|
||||
{
|
||||
battery.LinkedNetworkCharging = default;
|
||||
battery.LinkedNetworkDischarging = default;
|
||||
}
|
||||
|
||||
foreach (var load in _state.Loads.Values)
|
||||
{
|
||||
load.LinkedNetwork = default;
|
||||
}
|
||||
|
||||
foreach (var supply in _state.Supplies.Values)
|
||||
{
|
||||
supply.LinkedNetwork = default;
|
||||
}
|
||||
|
||||
foreach (var network in _state.Networks.Values)
|
||||
{
|
||||
foreach (var loadId in network.Loads)
|
||||
{
|
||||
var load = _state.Loads[loadId];
|
||||
load.LinkedNetwork = network.Id;
|
||||
}
|
||||
|
||||
foreach (var supplyId in network.Supplies)
|
||||
{
|
||||
var supply = _state.Supplies[supplyId];
|
||||
supply.LinkedNetwork = network.Id;
|
||||
}
|
||||
|
||||
foreach (var batteryId in network.BatteriesCharging)
|
||||
{
|
||||
var battery = _state.Batteries[batteryId];
|
||||
battery.LinkedNetworkCharging = network.Id;
|
||||
}
|
||||
|
||||
foreach (var batteryId in network.BatteriesDischarging)
|
||||
{
|
||||
var battery = _state.Batteries[batteryId];
|
||||
battery.LinkedNetworkDischarging = network.Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
459
Pow3r/Program.UI.cs
Normal file
459
Pow3r/Program.UI.cs
Normal file
@@ -0,0 +1,459 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ImGuiNET;
|
||||
using Robust.Shared.Maths;
|
||||
using static ImGuiNET.ImGui;
|
||||
using Color = System.Drawing.Color;
|
||||
using Vector2 = System.Numerics.Vector2;
|
||||
using RobustVec2 = Robust.Shared.Maths.Vector2;
|
||||
using static Content.Server.Power.Pow3r.PowerState;
|
||||
|
||||
namespace Pow3r
|
||||
{
|
||||
internal sealed partial class Program
|
||||
{
|
||||
private bool _showDemo;
|
||||
|
||||
private Dictionary<NodeId, DisplayLoad> _displayLoads = new();
|
||||
private Dictionary<NodeId, DisplayBattery> _displayBatteries = new();
|
||||
private Dictionary<NodeId, DisplayNetwork> _displayNetworks = new();
|
||||
private Dictionary<NodeId, DisplaySupply> _displaySupplies = new();
|
||||
|
||||
private void DoUI(float frameTime)
|
||||
{
|
||||
if (BeginMainMenuBar())
|
||||
{
|
||||
_showDemo ^= MenuItem("Demo");
|
||||
EndMainMenuBar();
|
||||
}
|
||||
|
||||
SetNextWindowSize(new Vector2(150, 200));
|
||||
|
||||
Begin("CreateButtons",
|
||||
ImGuiWindowFlags.NoTitleBar |
|
||||
ImGuiWindowFlags.NoCollapse |
|
||||
ImGuiWindowFlags.NoResize);
|
||||
|
||||
if (Button("Generator"))
|
||||
{
|
||||
var id = AllocId();
|
||||
_state.Supplies.Add(id, new Supply { Id = id });
|
||||
_displaySupplies.Add(id, new DisplaySupply());
|
||||
}
|
||||
|
||||
if (Button("Load"))
|
||||
{
|
||||
var id = AllocId();
|
||||
_state.Loads.Add(id, new Load { Id = id });
|
||||
_displayLoads.Add(id, new DisplayLoad());
|
||||
}
|
||||
|
||||
if (Button("Network"))
|
||||
{
|
||||
var id = AllocId();
|
||||
_state.Networks.Add(id, new Network { Id = id });
|
||||
_displayNetworks.Add(id, new DisplayNetwork());
|
||||
}
|
||||
|
||||
if (Button("Battery"))
|
||||
{
|
||||
var id = AllocId();
|
||||
_state.Batteries.Add(id, new Battery { Id = id });
|
||||
_displayBatteries.Add(id, new DisplayBattery());
|
||||
}
|
||||
|
||||
Checkbox("Paused", ref _paused);
|
||||
SliderInt("TPS", ref _tps, 1, 120);
|
||||
SetNextItemWidth(-1);
|
||||
Combo("", ref _currentSolver, _solverNames, _solverNames.Length);
|
||||
|
||||
if (Button("Single step"))
|
||||
RunSingleStep();
|
||||
|
||||
End();
|
||||
|
||||
Begin("Simulating timing");
|
||||
|
||||
PlotLines("Tick time (ms)", ref _simTickTimes[0], MaxTickData, _tickDataIdx + 1,
|
||||
$"{_simTickTimes[_tickDataIdx]:N2}",
|
||||
0,
|
||||
0.1f, new Vector2(250, 150));
|
||||
|
||||
End();
|
||||
|
||||
Begin("Frame timings");
|
||||
|
||||
PlotLines("Frame (ms)", ref _frameTimings[0], _frameTimings.Length, _frameTimeIdx + 1,
|
||||
$"{_frameTimings[_frameTimeIdx]:N2}",
|
||||
0,
|
||||
33.333f, new Vector2(250, 150));
|
||||
|
||||
End();
|
||||
|
||||
{
|
||||
Begin("Memory");
|
||||
|
||||
var heap = GC.GetTotalMemory(false);
|
||||
Text($"Managed heap: {heap>>20} MiB");
|
||||
|
||||
End();
|
||||
}
|
||||
|
||||
foreach (var network in _state.Networks.Values)
|
||||
{
|
||||
var displayNetwork = _displayNetworks[network.Id];
|
||||
Begin($"Network {network.Id}##Gen{network.Id}");
|
||||
|
||||
Text($"Height: {network.Height}");
|
||||
|
||||
displayNetwork.CurrentWindowPos = CalcWindowCenter();
|
||||
|
||||
if (Button("Delete"))
|
||||
{
|
||||
_remQueue.Enqueue(network);
|
||||
|
||||
if (_linking == network)
|
||||
{
|
||||
_linking = null;
|
||||
}
|
||||
}
|
||||
|
||||
SameLine();
|
||||
|
||||
if (_linking != null)
|
||||
{
|
||||
if (_linking == network && Button("Cancel"))
|
||||
{
|
||||
_linking = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Button("Link..."))
|
||||
{
|
||||
_linking = network;
|
||||
}
|
||||
}
|
||||
|
||||
End();
|
||||
}
|
||||
|
||||
foreach (var load in _state.Loads.Values)
|
||||
{
|
||||
var displayLoad = _displayLoads[load.Id];
|
||||
|
||||
Begin($"Load {load.Id}##Load{load.Id}");
|
||||
|
||||
Checkbox("Enabled", ref load.Enabled);
|
||||
SliderFloat("Desired", ref load.DesiredPower, 0, 1000, "%.0f W");
|
||||
|
||||
displayLoad.CurrentWindowPos = CalcWindowCenter();
|
||||
|
||||
PlotLines("", ref displayLoad.ReceivedPowerData[0], MaxTickData, _tickDataIdx + 1,
|
||||
$"Receiving: {load.ReceivingPower:N1} W",
|
||||
0,
|
||||
load.DesiredPower, new Vector2(250, 150));
|
||||
|
||||
if (Button("Delete"))
|
||||
{
|
||||
_remQueue.Enqueue(load);
|
||||
}
|
||||
|
||||
SameLine();
|
||||
if (_linking != null)
|
||||
{
|
||||
if (Button("Link"))
|
||||
{
|
||||
_linking.Loads.Add(load.Id);
|
||||
_linking = null;
|
||||
RefreshLinks();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (load.LinkedNetwork != default && Button("Unlink"))
|
||||
{
|
||||
var net = _state.Networks[load.LinkedNetwork];
|
||||
net.Loads.Remove(load.Id);
|
||||
load.LinkedNetwork = default;
|
||||
}
|
||||
}
|
||||
|
||||
End();
|
||||
}
|
||||
|
||||
foreach (var supply in _state.Supplies.Values)
|
||||
{
|
||||
var displaySupply = _displaySupplies[supply.Id];
|
||||
Begin($"Generator {supply.Id}##Gen{supply.Id}");
|
||||
|
||||
Checkbox("Enabled", ref supply.Enabled);
|
||||
SliderFloat("Available", ref supply.MaxSupply, 0, 1000, "%.0f W");
|
||||
SliderFloat("Ramp", ref supply.SupplyRampRate, 0, 100, "%.0f W/s");
|
||||
SliderFloat("Tolerance", ref supply.SupplyRampTolerance, 0, 100, "%.0f W");
|
||||
|
||||
displaySupply.CurrentWindowPos = CalcWindowCenter();
|
||||
|
||||
Text($"Ramp Position: {supply.SupplyRampPosition:N1}");
|
||||
|
||||
PlotLines("", ref displaySupply.SuppliedPowerData[0], MaxTickData, _tickDataIdx + 1,
|
||||
$"Supply: {supply.CurrentSupply:N1} W",
|
||||
0, supply.MaxSupply, new Vector2(250, 150));
|
||||
|
||||
if (Button("Delete"))
|
||||
{
|
||||
_remQueue.Enqueue(supply);
|
||||
}
|
||||
|
||||
SameLine();
|
||||
if (_linking != null)
|
||||
{
|
||||
if (Button("Link"))
|
||||
{
|
||||
_linking.Supplies.Add(supply.Id);
|
||||
_linking = null;
|
||||
RefreshLinks();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (supply.LinkedNetwork != default && Button("Unlink"))
|
||||
{
|
||||
var net = _state.Networks[supply.LinkedNetwork];
|
||||
net.Supplies.Remove(supply.Id);
|
||||
supply.LinkedNetwork = default;
|
||||
}
|
||||
}
|
||||
|
||||
End();
|
||||
}
|
||||
|
||||
foreach (var battery in _state.Batteries.Values)
|
||||
{
|
||||
var displayBattery = _displayBatteries[battery.Id];
|
||||
|
||||
Begin($"Battery {battery.Id}##Bat{battery.Id}");
|
||||
|
||||
Checkbox("Enabled", ref battery.Enabled);
|
||||
SliderFloat("Capacity", ref battery.Capacity, 0, 100000, "%.0f J");
|
||||
SliderFloat("Max charge rate", ref battery.MaxChargeRate, 0, 1000, "%.0f W");
|
||||
SliderFloat("Max supply", ref battery.MaxSupply, 0, 1000, "%.0f W");
|
||||
SliderFloat("Ramp", ref battery.SupplyRampRate, 0, 100, "%.0f W/s");
|
||||
SliderFloat("Tolerance", ref battery.SupplyRampTolerance, 0, 100, "%.0f W");
|
||||
var percent = 100 * battery.Efficiency;
|
||||
SliderFloat("Efficiency", ref percent, 0, 100, "%.0f %%");
|
||||
battery.Efficiency = percent / 100;
|
||||
|
||||
displayBattery.CurrentWindowPos = CalcWindowCenter();
|
||||
|
||||
SliderFloat("Ramp position", ref battery.SupplyRampPosition, 0, battery.MaxSupply, "%.0f W");
|
||||
|
||||
PlotLines("", ref displayBattery.SuppliedPowerData[0], MaxTickData, _tickDataIdx + 1,
|
||||
$"OUT: {battery.CurrentSupply:N1} W",
|
||||
0, battery.MaxSupply + 1000, new Vector2(250, 75));
|
||||
|
||||
PlotLines("", ref displayBattery.ReceivingPowerData[0], MaxTickData, _tickDataIdx + 1,
|
||||
$"IN: {battery.CurrentReceiving:N1} W",
|
||||
0, battery.MaxChargeRate + 1000, new Vector2(250, 75));
|
||||
|
||||
PlotLines("", ref displayBattery.StoredPowerData[0], MaxTickData, _tickDataIdx + 1,
|
||||
$"Charge: {battery.CurrentStorage:N1} J",
|
||||
0, battery.Capacity, new Vector2(250, 75));
|
||||
|
||||
if (Button("Delete"))
|
||||
{
|
||||
_remQueue.Enqueue(battery);
|
||||
}
|
||||
|
||||
SameLine();
|
||||
if (_linking != null)
|
||||
{
|
||||
if (battery.LinkedNetworkCharging == default && Button("Link as load"))
|
||||
{
|
||||
_linking.BatteriesCharging.Add(battery.Id);
|
||||
_linking = null;
|
||||
RefreshLinks();
|
||||
}
|
||||
else
|
||||
{
|
||||
SameLine();
|
||||
if (battery.LinkedNetworkDischarging == default && Button("Link as supply"))
|
||||
{
|
||||
_linking.BatteriesDischarging.Add(battery.Id);
|
||||
_linking = null;
|
||||
RefreshLinks();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (battery.LinkedNetworkCharging != default && Button("Unlink loading"))
|
||||
{
|
||||
var net = _state.Networks[battery.LinkedNetworkCharging];
|
||||
net.BatteriesCharging.Remove(battery.Id);
|
||||
battery.LinkedNetworkCharging = default;
|
||||
}
|
||||
else
|
||||
{
|
||||
SameLine();
|
||||
if (battery.LinkedNetworkDischarging != default && Button("Unlink supplying"))
|
||||
{
|
||||
var net = _state.Networks[battery.LinkedNetworkDischarging];
|
||||
net.BatteriesDischarging.Remove(battery.Id);
|
||||
battery.LinkedNetworkDischarging = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Button("Empty"))
|
||||
battery.CurrentStorage = 0;
|
||||
SameLine();
|
||||
if (Button("Fill"))
|
||||
battery.CurrentStorage = battery.Capacity;
|
||||
|
||||
End();
|
||||
}
|
||||
|
||||
var bgDrawList = GetBackgroundDrawList();
|
||||
|
||||
foreach (var network in _state.Networks.Values)
|
||||
{
|
||||
var displayNet = _displayNetworks[network.Id];
|
||||
foreach (var supplyId in network.Supplies)
|
||||
{
|
||||
var supply = _displaySupplies[supplyId];
|
||||
DrawArrowLine(bgDrawList, displayNet.CurrentWindowPos, supply.CurrentWindowPos, Color.LawnGreen);
|
||||
}
|
||||
|
||||
foreach (var loadId in network.Loads)
|
||||
{
|
||||
var load = _displayLoads[loadId];
|
||||
DrawArrowLine(bgDrawList, load.CurrentWindowPos, displayNet.CurrentWindowPos, Color.Red);
|
||||
}
|
||||
|
||||
foreach (var batteryId in network.BatteriesCharging)
|
||||
{
|
||||
var battery = _displayBatteries[batteryId];
|
||||
DrawArrowLine(bgDrawList, battery.CurrentWindowPos, displayNet.CurrentWindowPos, Color.Purple);
|
||||
}
|
||||
|
||||
foreach (var batteryId in network.BatteriesDischarging)
|
||||
{
|
||||
var battery = _displayBatteries[batteryId];
|
||||
DrawArrowLine(bgDrawList, displayNet.CurrentWindowPos, battery.CurrentWindowPos, Color.Cyan);
|
||||
}
|
||||
}
|
||||
|
||||
if (_showDemo)
|
||||
{
|
||||
ShowDemoWindow();
|
||||
}
|
||||
|
||||
var reLink = false;
|
||||
while (_remQueue.TryDequeue(out var item))
|
||||
{
|
||||
switch (item)
|
||||
{
|
||||
case Network n:
|
||||
_state.Networks.Remove(n.Id);
|
||||
_displayNetworks.Remove(n.Id);
|
||||
reLink = true;
|
||||
break;
|
||||
|
||||
case Supply s:
|
||||
_state.Supplies.Remove(s.Id);
|
||||
_state.Networks.Values.ForEach(n => n.Supplies.Remove(s.Id));
|
||||
_displaySupplies.Remove(s.Id);
|
||||
break;
|
||||
|
||||
case Load l:
|
||||
_state.Loads.Remove(l.Id);
|
||||
_state.Networks.Values.ForEach(n => n.Loads.Remove(l.Id));
|
||||
_displayLoads.Remove(l.Id);
|
||||
break;
|
||||
|
||||
case Battery b:
|
||||
_state.Batteries.Remove(b.Id);
|
||||
_state.Networks.Values.ForEach(n => n.BatteriesCharging.Remove(b.Id));
|
||||
_state.Networks.Values.ForEach(n => n.BatteriesDischarging.Remove(b.Id));
|
||||
_displayBatteries.Remove(b.Id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (reLink)
|
||||
RefreshLinks();
|
||||
}
|
||||
|
||||
|
||||
private void DrawArrowLine(ImDrawListPtr ptr, Vector2 a, Vector2 b, Color color)
|
||||
{
|
||||
// A: to
|
||||
// B: from
|
||||
|
||||
const float wingLength = 15;
|
||||
const float thickness = 3;
|
||||
|
||||
var cvtColor = CvtColor(color);
|
||||
|
||||
ptr.AddLine(a, b, cvtColor, thickness);
|
||||
|
||||
var angleA = Angle.FromDegrees(45);
|
||||
var angleB = Angle.FromDegrees(-45);
|
||||
|
||||
var mid = (a + b) / 2;
|
||||
var dir = -Vector2.Normalize(a - b);
|
||||
|
||||
var rVec = new RobustVec2(dir.X, dir.Y);
|
||||
|
||||
var wingADir = CvtVec(angleA.RotateVec(rVec));
|
||||
var wingBDir = CvtVec(angleB.RotateVec(rVec));
|
||||
|
||||
var wingA = wingADir * wingLength + mid;
|
||||
var wingB = wingBDir * wingLength + mid;
|
||||
|
||||
ptr.AddLine(mid, wingA, cvtColor, thickness);
|
||||
ptr.AddLine(mid, wingB, cvtColor, thickness);
|
||||
}
|
||||
|
||||
private static uint CvtColor(Color color)
|
||||
{
|
||||
return color.R | ((uint) color.G << 8) | ((uint) color.B << 16) | ((uint) color.A << 24);
|
||||
}
|
||||
|
||||
private static Vector2 CalcWindowCenter()
|
||||
{
|
||||
return GetWindowPos() + GetWindowSize() / 2;
|
||||
}
|
||||
|
||||
private static Vector2 CvtVec(RobustVec2 vec)
|
||||
{
|
||||
return new Vector2(vec.X, vec.Y);
|
||||
}
|
||||
|
||||
private sealed class DisplayNetwork
|
||||
{
|
||||
public Vector2 CurrentWindowPos;
|
||||
}
|
||||
|
||||
private sealed class DisplayBattery
|
||||
{
|
||||
public Vector2 CurrentWindowPos;
|
||||
public readonly float[] ReceivingPowerData = new float[MaxTickData];
|
||||
public readonly float[] SuppliedPowerData = new float[MaxTickData];
|
||||
public readonly float[] StoredPowerData = new float[MaxTickData];
|
||||
}
|
||||
|
||||
private sealed class DisplayLoad
|
||||
{
|
||||
public Vector2 CurrentWindowPos;
|
||||
public readonly float[] ReceivedPowerData = new float[MaxTickData];
|
||||
}
|
||||
|
||||
private sealed class DisplaySupply
|
||||
{
|
||||
public Vector2 CurrentWindowPos;
|
||||
public readonly float[] SuppliedPowerData = new float[MaxTickData];
|
||||
}
|
||||
}
|
||||
}
|
||||
409
Pow3r/Program.Veldrid.cs
Normal file
409
Pow3r/Program.Veldrid.cs
Normal file
@@ -0,0 +1,409 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using ImGuiNET;
|
||||
using OpenTK.Windowing.GraphicsLibraryFramework;
|
||||
using Veldrid;
|
||||
using Veldrid.OpenGL;
|
||||
using Veldrid.SPIRV;
|
||||
using Veldrid.Vk;
|
||||
|
||||
namespace Pow3r
|
||||
{
|
||||
internal sealed unsafe partial class Program
|
||||
{
|
||||
private const string VDVertexShader = @"
|
||||
#version 460
|
||||
|
||||
layout (location = 0) in vec2 Position;
|
||||
layout (location = 1) in vec2 UV;
|
||||
layout (location = 2) in vec4 Color;
|
||||
|
||||
layout (set = 0, binding = 0) uniform ProjMtx {
|
||||
mat4 _ProjMtx;
|
||||
};
|
||||
|
||||
layout (location = 0) out vec2 Frag_UV;
|
||||
layout (location = 1) out vec4 Frag_Color;
|
||||
|
||||
// Converts a color from sRGB gamma to linear light gamma
|
||||
vec4 toLinear(vec4 sRGB)
|
||||
{
|
||||
bvec3 cutoff = lessThan(sRGB.rgb, vec3(0.04045));
|
||||
vec3 higher = pow((sRGB.rgb + vec3(0.055))/vec3(1.055), vec3(2.4));
|
||||
vec3 lower = sRGB.rgb/vec3(12.92);
|
||||
|
||||
return vec4(mix(higher, lower, cutoff), sRGB.a);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
Frag_UV = UV;
|
||||
Frag_Color = toLinear(Color);
|
||||
gl_Position = _ProjMtx * vec4(Position.xy,0,1);
|
||||
}";
|
||||
|
||||
private const string VDFragmentShader = @"
|
||||
#version 460
|
||||
|
||||
layout (location = 0) in vec2 Frag_UV;
|
||||
layout (location = 1) in vec4 Frag_Color;
|
||||
|
||||
layout (set = 1, binding = 0) uniform texture2D Texture;
|
||||
layout (set = 1, binding = 1) uniform sampler TextureSampler;
|
||||
|
||||
layout (location = 0) out vec4 Out_Color;
|
||||
|
||||
void main()
|
||||
{
|
||||
Out_Color = Frag_Color * texture(sampler2D(Texture, TextureSampler), Frag_UV.st);
|
||||
}";
|
||||
|
||||
private VeldridRenderer _vdRenderer = VeldridRenderer.Vulkan;
|
||||
|
||||
private GraphicsDevice _vdGfxDevice;
|
||||
private CommandList _vdCommandList;
|
||||
private Pipeline _vdPipeline;
|
||||
private Shader[] _vdShaders;
|
||||
private ResourceSet _vdSetTexture;
|
||||
private ResourceSet _vdSetProjMatrix;
|
||||
private Texture _vdTexture;
|
||||
private Sampler _vdSampler;
|
||||
private DeviceBuffer _vdProjMatrixUniformBuffer;
|
||||
private int _vdLastWidth;
|
||||
private int _vdLastHeight;
|
||||
private VdFencedDatum[] _fencedData = Array.Empty<VdFencedDatum>();
|
||||
|
||||
private void InitVeldrid()
|
||||
{
|
||||
var options = new GraphicsDeviceOptions
|
||||
{
|
||||
#if DEBUG
|
||||
Debug = true,
|
||||
#endif
|
||||
HasMainSwapchain = true,
|
||||
SyncToVerticalBlank = _vsync,
|
||||
PreferStandardClipSpaceYDirection = true,
|
||||
SwapchainSrgbFormat = true
|
||||
};
|
||||
|
||||
GLFW.GetFramebufferSize(_window.WindowPtr, out var w, out var h);
|
||||
|
||||
var hwnd = GLFW.GetWin32Window(_window.WindowPtr);
|
||||
var hinstance = GetModuleHandleA(null);
|
||||
|
||||
switch (_vdRenderer)
|
||||
{
|
||||
case VeldridRenderer.Vulkan:
|
||||
_vdGfxDevice = GraphicsDevice.CreateVulkan(
|
||||
options,
|
||||
VkSurfaceSource.CreateWin32((nint) hinstance, hwnd),
|
||||
(uint) w, (uint) h);
|
||||
break;
|
||||
case VeldridRenderer.D3D11:
|
||||
_vdGfxDevice = GraphicsDevice.CreateD3D11(options, hwnd, (uint) w, (uint) h);
|
||||
break;
|
||||
case VeldridRenderer.OpenGL:
|
||||
{
|
||||
var platInfo = new OpenGLPlatformInfo(
|
||||
(nint) _window.WindowPtr,
|
||||
GLFW.GetProcAddress,
|
||||
ptr => GLFW.MakeContextCurrent((Window*) ptr),
|
||||
() => (nint) GLFW.GetCurrentContext(),
|
||||
() => GLFW.MakeContextCurrent(null),
|
||||
ptr => GLFW.DestroyWindow((Window*) ptr),
|
||||
() => GLFW.SwapBuffers(_window.WindowPtr),
|
||||
vsync => GLFW.SwapInterval(vsync ? 1 : 0));
|
||||
|
||||
_vdGfxDevice = GraphicsDevice.CreateOpenGL(options, platInfo, (uint) w, (uint) h);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var factory = _vdGfxDevice.ResourceFactory;
|
||||
|
||||
_vdCommandList = factory.CreateCommandList();
|
||||
_vdCommandList.Name = "Honk";
|
||||
|
||||
var vtxLayout = new VertexLayoutDescription(
|
||||
new VertexElementDescription("Position", VertexElementFormat.Float2,
|
||||
VertexElementSemantic.TextureCoordinate),
|
||||
new VertexElementDescription("UV", VertexElementFormat.Float2, VertexElementSemantic.TextureCoordinate),
|
||||
new VertexElementDescription("Color", VertexElementFormat.Byte4_Norm,
|
||||
VertexElementSemantic.TextureCoordinate));
|
||||
|
||||
var vtxShaderDesc = new ShaderDescription(
|
||||
ShaderStages.Vertex,
|
||||
Encoding.UTF8.GetBytes(VDVertexShader),
|
||||
"main");
|
||||
|
||||
var fragShaderDesc = new ShaderDescription(
|
||||
ShaderStages.Fragment,
|
||||
Encoding.UTF8.GetBytes(VDFragmentShader),
|
||||
"main");
|
||||
|
||||
_vdShaders = factory.CreateFromSpirv(vtxShaderDesc, fragShaderDesc);
|
||||
|
||||
_vdShaders[0].Name = "VertexShader";
|
||||
_vdShaders[1].Name = "FragmentShader";
|
||||
|
||||
var layoutTexture = factory.CreateResourceLayout(new ResourceLayoutDescription(
|
||||
new ResourceLayoutElementDescription(
|
||||
"Texture",
|
||||
ResourceKind.TextureReadOnly,
|
||||
ShaderStages.Fragment),
|
||||
new ResourceLayoutElementDescription(
|
||||
"TextureSampler",
|
||||
ResourceKind.Sampler,
|
||||
ShaderStages.Fragment)));
|
||||
|
||||
layoutTexture.Name = "LayoutTexture";
|
||||
|
||||
var layoutProjMatrix = factory.CreateResourceLayout(new ResourceLayoutDescription(
|
||||
new ResourceLayoutElementDescription(
|
||||
"ProjMtx",
|
||||
ResourceKind.UniformBuffer,
|
||||
ShaderStages.Vertex)));
|
||||
|
||||
layoutProjMatrix.Name = "LayoutProjMatrix";
|
||||
|
||||
var pipelineDesc = new GraphicsPipelineDescription(
|
||||
new BlendStateDescription(
|
||||
RgbaFloat.White,
|
||||
new BlendAttachmentDescription(
|
||||
true,
|
||||
BlendFactor.SourceAlpha,
|
||||
BlendFactor.InverseSourceAlpha,
|
||||
BlendFunction.Add,
|
||||
BlendFactor.One,
|
||||
BlendFactor.InverseSourceAlpha,
|
||||
BlendFunction.Add)
|
||||
),
|
||||
DepthStencilStateDescription.Disabled,
|
||||
new RasterizerStateDescription(
|
||||
FaceCullMode.None,
|
||||
PolygonFillMode.Solid,
|
||||
FrontFace.Clockwise,
|
||||
depthClipEnabled: false,
|
||||
scissorTestEnabled: true),
|
||||
PrimitiveTopology.TriangleList,
|
||||
new ShaderSetDescription(new[] {vtxLayout}, _vdShaders),
|
||||
new[] {layoutProjMatrix, layoutTexture},
|
||||
new OutputDescription(
|
||||
null,
|
||||
new OutputAttachmentDescription(PixelFormat.B8_G8_R8_A8_UNorm_SRgb))
|
||||
);
|
||||
|
||||
_vdPipeline = factory.CreateGraphicsPipeline(pipelineDesc);
|
||||
_vdPipeline.Name = "MainPipeline";
|
||||
|
||||
_vdProjMatrixUniformBuffer = factory.CreateBuffer(new BufferDescription(
|
||||
(uint) sizeof(Matrix4x4),
|
||||
BufferUsage.Dynamic | BufferUsage.UniformBuffer));
|
||||
_vdProjMatrixUniformBuffer.Name = "_vdProjMatrixUniformBuffer";
|
||||
|
||||
_vdSetProjMatrix = factory.CreateResourceSet(new ResourceSetDescription(
|
||||
layoutProjMatrix,
|
||||
_vdProjMatrixUniformBuffer));
|
||||
_vdSetProjMatrix.Name = "_vdSetProjMatrix";
|
||||
var io = ImGui.GetIO();
|
||||
|
||||
io.Fonts.GetTexDataAsRGBA32(out byte* pixels, out var width, out var height, out _);
|
||||
|
||||
_vdTexture = factory.CreateTexture(TextureDescription.Texture2D(
|
||||
(uint) width, (uint) height,
|
||||
mipLevels: 1,
|
||||
arrayLayers: 1,
|
||||
PixelFormat.R8_G8_B8_A8_UNorm_SRgb,
|
||||
TextureUsage.Sampled));
|
||||
|
||||
_vdTexture.Name = "MainTexture";
|
||||
|
||||
_vdSampler = factory.CreateSampler(SamplerDescription.Linear);
|
||||
|
||||
_vdSampler.Name = "MainSampler";
|
||||
|
||||
_vdGfxDevice.UpdateTexture(
|
||||
_vdTexture,
|
||||
(IntPtr) pixels,
|
||||
(uint) (width * height * 4),
|
||||
x: 0, y: 0, z: 0,
|
||||
(uint) width, (uint) height, depth: 1,
|
||||
mipLevel: 0,
|
||||
arrayLayer: 0);
|
||||
|
||||
_vdSetTexture = factory.CreateResourceSet(new ResourceSetDescription(
|
||||
layoutTexture,
|
||||
_vdTexture,
|
||||
_vdSampler));
|
||||
|
||||
_vdSetTexture.Name = "SetTexture";
|
||||
|
||||
io.Fonts.SetTexID((nint) 0);
|
||||
io.Fonts.ClearTexData();
|
||||
|
||||
_vdGfxDevice.ResizeMainWindow((uint) w, (uint) h);
|
||||
_vdGfxDevice.SwapBuffers();
|
||||
}
|
||||
|
||||
private void RenderVeldrid()
|
||||
{
|
||||
GLFW.GetFramebufferSize(_window.WindowPtr, out var fbW, out var fbH);
|
||||
|
||||
if (_vdLastWidth != fbW && _vdLastHeight != fbH)
|
||||
{
|
||||
_vdGfxDevice.ResizeMainWindow((uint) fbW, (uint) fbH);
|
||||
_vdLastWidth = fbW;
|
||||
_vdLastHeight = fbH;
|
||||
}
|
||||
|
||||
_vdCommandList.Begin();
|
||||
_vdCommandList.SetFramebuffer(_vdGfxDevice.SwapchainFramebuffer);
|
||||
|
||||
_vdCommandList.SetViewport(0, new Viewport(0, 0, fbW, fbH, 0, 1));
|
||||
_vdCommandList.ClearColorTarget(0, RgbaFloat.Black);
|
||||
|
||||
var factory = _vdGfxDevice.ResourceFactory;
|
||||
|
||||
var drawData = ImGui.GetDrawData();
|
||||
|
||||
ref var fencedData = ref GetFreeFencedData();
|
||||
ref var vtxBuf = ref fencedData.VertexBuffer;
|
||||
ref var idxBuf = ref fencedData.IndexBuffer;
|
||||
|
||||
var byteLenVtx = (uint) (sizeof(ImDrawVert) * drawData.TotalVtxCount);
|
||||
if (fencedData.VertexBuffer == null || vtxBuf.SizeInBytes < byteLenVtx)
|
||||
{
|
||||
vtxBuf?.Dispose();
|
||||
vtxBuf = factory.CreateBuffer(new BufferDescription(
|
||||
byteLenVtx,
|
||||
BufferUsage.VertexBuffer | BufferUsage.Dynamic));
|
||||
vtxBuf.Name = "_vdVtxBuffer";
|
||||
}
|
||||
|
||||
var byteLenIdx = (uint) (sizeof(ushort) * drawData.TotalIdxCount);
|
||||
if (idxBuf == null || idxBuf.SizeInBytes < byteLenIdx)
|
||||
{
|
||||
idxBuf?.Dispose();
|
||||
idxBuf = factory.CreateBuffer(new BufferDescription(
|
||||
byteLenIdx,
|
||||
BufferUsage.IndexBuffer | BufferUsage.Dynamic));
|
||||
idxBuf.Name = "_vdIdxBuffer";
|
||||
}
|
||||
|
||||
var vtxOffset = 0;
|
||||
var idxOffset = 0;
|
||||
var mappedVtxBuf = MappedToSpan(_vdGfxDevice.Map<ImDrawVert>(vtxBuf, MapMode.Write));
|
||||
var mappedIdxBuf = MappedToSpan(_vdGfxDevice.Map<ushort>(idxBuf, MapMode.Write));
|
||||
|
||||
var l = drawData.DisplayPos.X;
|
||||
var r = drawData.DisplayPos.X + drawData.DisplaySize.X;
|
||||
var t = drawData.DisplayPos.Y;
|
||||
var b = drawData.DisplayPos.Y + drawData.DisplaySize.Y;
|
||||
|
||||
var matrix = Matrix4x4.CreateOrthographicOffCenter(l, r, b, t, -1, 1);
|
||||
|
||||
var clipOff = drawData.DisplayPos;
|
||||
var clipScale = drawData.FramebufferScale;
|
||||
|
||||
_vdCommandList.UpdateBuffer(_vdProjMatrixUniformBuffer, 0, ref matrix);
|
||||
|
||||
_vdCommandList.SetPipeline(_vdPipeline);
|
||||
_vdCommandList.SetGraphicsResourceSet(0, _vdSetProjMatrix);
|
||||
_vdCommandList.SetGraphicsResourceSet(1, _vdSetTexture);
|
||||
_vdCommandList.SetVertexBuffer(0, vtxBuf);
|
||||
_vdCommandList.SetIndexBuffer(idxBuf, IndexFormat.UInt16);
|
||||
|
||||
for (var n = 0; n < drawData.CmdListsCount; n++)
|
||||
{
|
||||
var drawList = drawData.CmdListsRange[n];
|
||||
|
||||
var drawVtx = new Span<ImDrawVert>((void*) drawList.VtxBuffer.Data, drawList.VtxBuffer.Size);
|
||||
var drawIdx = new Span<ushort>((void*) drawList.IdxBuffer.Data, drawList.IdxBuffer.Size);
|
||||
|
||||
drawVtx.CopyTo(mappedVtxBuf[vtxOffset..]);
|
||||
drawIdx.CopyTo(mappedIdxBuf[idxOffset..]);
|
||||
|
||||
for (var cmdI = 0; cmdI < drawList.CmdBuffer.Size; cmdI++)
|
||||
{
|
||||
var cmd = drawList.CmdBuffer[cmdI];
|
||||
|
||||
Vector4 clipRect = default;
|
||||
clipRect.X = (cmd.ClipRect.X - clipOff.X) * clipScale.X;
|
||||
clipRect.Y = (cmd.ClipRect.Y - clipOff.Y) * clipScale.Y;
|
||||
clipRect.Z = (cmd.ClipRect.Z - clipOff.X) * clipScale.X;
|
||||
clipRect.W = (cmd.ClipRect.W - clipOff.Y) * clipScale.Y;
|
||||
|
||||
_vdCommandList.SetScissorRect(
|
||||
0,
|
||||
(uint) clipRect.X,
|
||||
(uint) clipRect.Y,
|
||||
(uint) (clipRect.Z - clipRect.X),
|
||||
(uint) (clipRect.W - clipRect.Y));
|
||||
|
||||
_vdCommandList.DrawIndexed(
|
||||
cmd.ElemCount,
|
||||
1,
|
||||
(uint) (cmd.IdxOffset + idxOffset),
|
||||
(int) (cmd.VtxOffset + vtxOffset),
|
||||
0);
|
||||
}
|
||||
|
||||
vtxOffset += drawVtx.Length;
|
||||
idxOffset += drawIdx.Length;
|
||||
}
|
||||
|
||||
_vdGfxDevice.Unmap(vtxBuf);
|
||||
_vdGfxDevice.Unmap(idxBuf);
|
||||
|
||||
_vdCommandList.End();
|
||||
|
||||
_vdGfxDevice.SubmitCommands(_vdCommandList, fencedData.Fence);
|
||||
_vdGfxDevice.SwapBuffers();
|
||||
}
|
||||
|
||||
private ref VdFencedDatum GetFreeFencedData()
|
||||
{
|
||||
for (var i = 0; i < _fencedData.Length; i++)
|
||||
{
|
||||
ref var fenced = ref _fencedData[i];
|
||||
|
||||
if (fenced.Fence.Signaled)
|
||||
{
|
||||
fenced.Fence.Reset();
|
||||
return ref fenced;
|
||||
}
|
||||
}
|
||||
|
||||
Array.Resize(ref _fencedData, _fencedData.Length + 1);
|
||||
ref var slot = ref _fencedData[^1];
|
||||
slot = new VdFencedDatum {Fence = _vdGfxDevice.ResourceFactory.CreateFence(false)};
|
||||
return ref slot;
|
||||
}
|
||||
|
||||
private static Span<T> MappedToSpan<T>(MappedResourceView<T> mapped) where T : struct
|
||||
{
|
||||
return MemoryMarshal.CreateSpan(ref mapped[0], mapped.Count);
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern void* GetModuleHandleA(byte* lpModuleName);
|
||||
|
||||
private struct VdFencedDatum
|
||||
{
|
||||
public Fence Fence;
|
||||
|
||||
public DeviceBuffer IndexBuffer;
|
||||
public DeviceBuffer VertexBuffer;
|
||||
}
|
||||
|
||||
private enum VeldridRenderer
|
||||
{
|
||||
Vulkan,
|
||||
D3D11,
|
||||
OpenGL
|
||||
}
|
||||
}
|
||||
}
|
||||
389
Pow3r/Program.cs
Normal file
389
Pow3r/Program.cs
Normal file
@@ -0,0 +1,389 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using ImGuiNET;
|
||||
using OpenTK.Windowing.Common;
|
||||
using OpenTK.Windowing.Desktop;
|
||||
using OpenTK.Windowing.GraphicsLibraryFramework;
|
||||
using ErrorCode = OpenTK.Windowing.GraphicsLibraryFramework.ErrorCode;
|
||||
using Vector2 = System.Numerics.Vector2;
|
||||
|
||||
// ReSharper disable PossibleNullReferenceException
|
||||
|
||||
namespace Pow3r
|
||||
{
|
||||
internal sealed unsafe partial class Program
|
||||
{
|
||||
private Renderer _renderer = Renderer.Veldrid;
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
private static byte* GetClipboardTextCallback(void* userData)
|
||||
{
|
||||
return GLFW.GetClipboardStringRaw((Window*) userData);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
private static void SetClipboardTextCallback(void* userData, byte* text)
|
||||
{
|
||||
GLFW.SetClipboardStringRaw((Window*) userData, text);
|
||||
}
|
||||
|
||||
private static readonly GLFWCallbacks.ErrorCallback ErrorCallback = GlfwErrorCallback;
|
||||
|
||||
private static void GlfwErrorCallback(ErrorCode error, string description)
|
||||
{
|
||||
Console.WriteLine($"{error}: {description}");
|
||||
}
|
||||
|
||||
private bool[] _mouseJustPressed = new bool[5];
|
||||
|
||||
private bool _fullscreen;
|
||||
private int _monitorIdx;
|
||||
private bool _vsync = true;
|
||||
private GameWindow _window;
|
||||
private readonly Stopwatch _stopwatch = new();
|
||||
private readonly Cursor*[] _cursors = new Cursor*[9];
|
||||
private readonly float[] _frameTimings = new float[180];
|
||||
private int _frameTimeIdx = 0;
|
||||
private int _tps = 60;
|
||||
|
||||
private void Run(string[] args)
|
||||
{
|
||||
for (var i = 0; i < args.Length; i++)
|
||||
{
|
||||
if (args[i] == "--renderer")
|
||||
{
|
||||
_renderer = Enum.Parse<Renderer>(args[++i]);
|
||||
}
|
||||
else if (args[i] == "--veldrid")
|
||||
{
|
||||
_vdRenderer = Enum.Parse<VeldridRenderer>(args[++i]);
|
||||
}
|
||||
else if (args[i] == "--fullscreen")
|
||||
{
|
||||
_fullscreen = true;
|
||||
}
|
||||
else if (args[i] == "--monitor-idx")
|
||||
{
|
||||
_monitorIdx = int.Parse(args[++i]);
|
||||
}
|
||||
else if (args[i] == "--no-vsync")
|
||||
{
|
||||
_vsync = false;
|
||||
}
|
||||
else if (args[i] == "--help")
|
||||
{
|
||||
Console.WriteLine("--renderer <Veldrid|OpenGL>");
|
||||
Console.WriteLine("--veldrid <Vulkan|OpenGL|D3D11>");
|
||||
Console.WriteLine("--no-vsync");
|
||||
Console.WriteLine("--fullscreen");
|
||||
Console.WriteLine("--monitor-idx");
|
||||
Console.WriteLine("--help");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"unknown arg \"{args[i]}\"");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"Renderer: {_renderer}");
|
||||
if (_renderer == Renderer.Veldrid)
|
||||
Console.WriteLine($"Veldrid API: {_vdRenderer}");
|
||||
|
||||
Console.WriteLine($"Fullscreen: {_fullscreen}");
|
||||
Console.WriteLine($"VSync: {_vsync}");
|
||||
|
||||
//NativeLibrary.Load("nvapi64.dll");
|
||||
GLFW.Init();
|
||||
GLFW.SetErrorCallback(ErrorCallback);
|
||||
|
||||
// var sw = Stopwatch.StartNew();
|
||||
GLFW.WindowHint(WindowHintBool.SrgbCapable, true);
|
||||
var windowSettings = new NativeWindowSettings
|
||||
{
|
||||
Size = (1280, 720),
|
||||
WindowState = WindowState.Maximized,
|
||||
StartVisible = false,
|
||||
|
||||
Title = "Pow3r"
|
||||
};
|
||||
|
||||
|
||||
var openGLBased = _renderer == Renderer.OpenGL ||
|
||||
(_renderer == Renderer.Veldrid && _vdRenderer == VeldridRenderer.OpenGL);
|
||||
|
||||
if (openGLBased)
|
||||
{
|
||||
windowSettings.API = ContextAPI.OpenGL;
|
||||
if (_renderer == Renderer.Veldrid)
|
||||
{
|
||||
windowSettings.Profile = ContextProfile.Core;
|
||||
windowSettings.APIVersion = new Version(4, 6);
|
||||
windowSettings.Flags = ContextFlags.ForwardCompatible;
|
||||
}
|
||||
else
|
||||
{
|
||||
windowSettings.Profile = ContextProfile.Any;
|
||||
windowSettings.APIVersion = new Version(1, 5);
|
||||
}
|
||||
#if DEBUG
|
||||
windowSettings.Flags |= ContextFlags.Debug;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
windowSettings.API = ContextAPI.NoAPI;
|
||||
}
|
||||
|
||||
_window = new GameWindow(GameWindowSettings.Default, windowSettings);
|
||||
|
||||
// Console.WriteLine(sw.ElapsedMilliseconds);
|
||||
|
||||
if (_fullscreen)
|
||||
{
|
||||
var monitors = GLFW.GetMonitors();
|
||||
var monitor = monitors[_monitorIdx];
|
||||
var monitorMode = GLFW.GetVideoMode(monitor);
|
||||
|
||||
GLFW.SetWindowMonitor(
|
||||
_window.WindowPtr,
|
||||
monitor,
|
||||
0, 0,
|
||||
monitorMode->Width,
|
||||
monitorMode->Height,
|
||||
monitorMode->RefreshRate);
|
||||
}
|
||||
|
||||
if (openGLBased)
|
||||
{
|
||||
_window.VSync = _vsync ? VSyncMode.On : VSyncMode.Off;
|
||||
}
|
||||
|
||||
var context = ImGui.CreateContext();
|
||||
ImGui.SetCurrentContext(context);
|
||||
ImGui.StyleColorsDark();
|
||||
|
||||
var io = ImGui.GetIO();
|
||||
io.Fonts.AddFontDefault();
|
||||
|
||||
delegate* unmanaged<void*, byte*> getClipboardCallback = &GetClipboardTextCallback;
|
||||
io.GetClipboardTextFn = (IntPtr) getClipboardCallback;
|
||||
delegate* unmanaged<void*, byte*, void> setClipboardCallback = &SetClipboardTextCallback;
|
||||
io.GetClipboardTextFn = (IntPtr) setClipboardCallback;
|
||||
io.ClipboardUserData = (IntPtr) _window.WindowPtr;
|
||||
io.BackendFlags |= ImGuiBackendFlags.RendererHasVtxOffset;
|
||||
io.BackendFlags |= ImGuiBackendFlags.HasSetMousePos;
|
||||
io.BackendFlags |= ImGuiBackendFlags.HasMouseCursors;
|
||||
|
||||
io.KeyMap[(int) ImGuiKey.Tab] = (int) Keys.Tab;
|
||||
io.KeyMap[(int) ImGuiKey.LeftArrow] = (int) Keys.Left;
|
||||
io.KeyMap[(int) ImGuiKey.RightArrow] = (int) Keys.Right;
|
||||
io.KeyMap[(int) ImGuiKey.UpArrow] = (int) Keys.Up;
|
||||
io.KeyMap[(int) ImGuiKey.DownArrow] = (int) Keys.Down;
|
||||
io.KeyMap[(int) ImGuiKey.PageUp] = (int) Keys.PageUp;
|
||||
io.KeyMap[(int) ImGuiKey.PageDown] = (int) Keys.PageDown;
|
||||
io.KeyMap[(int) ImGuiKey.Home] = (int) Keys.Home;
|
||||
io.KeyMap[(int) ImGuiKey.End] = (int) Keys.End;
|
||||
io.KeyMap[(int) ImGuiKey.Delete] = (int) Keys.Delete;
|
||||
io.KeyMap[(int) ImGuiKey.Backspace] = (int) Keys.Backspace;
|
||||
io.KeyMap[(int) ImGuiKey.Enter] = (int) Keys.Enter;
|
||||
io.KeyMap[(int) ImGuiKey.Escape] = (int) Keys.Escape;
|
||||
io.KeyMap[(int) ImGuiKey.A] = (int) Keys.A;
|
||||
io.KeyMap[(int) ImGuiKey.C] = (int) Keys.C;
|
||||
io.KeyMap[(int) ImGuiKey.V] = (int) Keys.V;
|
||||
io.KeyMap[(int) ImGuiKey.X] = (int) Keys.X;
|
||||
io.KeyMap[(int) ImGuiKey.Y] = (int) Keys.Y;
|
||||
io.KeyMap[(int) ImGuiKey.Z] = (int) Keys.Z;
|
||||
|
||||
_cursors[(int) ImGuiMouseCursor.Arrow] = GLFW.CreateStandardCursor(CursorShape.Arrow);
|
||||
_cursors[(int) ImGuiMouseCursor.TextInput] = GLFW.CreateStandardCursor(CursorShape.IBeam);
|
||||
_cursors[(int) ImGuiMouseCursor.ResizeNS] = GLFW.CreateStandardCursor(CursorShape.VResize);
|
||||
_cursors[(int) ImGuiMouseCursor.ResizeEW] = GLFW.CreateStandardCursor(CursorShape.HResize);
|
||||
_cursors[(int) ImGuiMouseCursor.Hand] = GLFW.CreateStandardCursor(CursorShape.Hand);
|
||||
_cursors[(int) ImGuiMouseCursor.ResizeAll] = GLFW.CreateStandardCursor(CursorShape.Arrow);
|
||||
_cursors[(int) ImGuiMouseCursor.ResizeNESW] = GLFW.CreateStandardCursor(CursorShape.Arrow);
|
||||
_cursors[(int) ImGuiMouseCursor.ResizeNWSE] = GLFW.CreateStandardCursor(CursorShape.Arrow);
|
||||
_cursors[(int) ImGuiMouseCursor.NotAllowed] = GLFW.CreateStandardCursor(CursorShape.Arrow);
|
||||
|
||||
InitRenderer();
|
||||
|
||||
_window.MouseDown += OnMouseDown;
|
||||
_window.TextInput += WindowOnTextInput;
|
||||
_window.MouseWheel += WindowOnMouseWheel;
|
||||
_window.KeyDown += args => KeyCallback(args, true);
|
||||
_window.KeyUp += args => KeyCallback(args, false);
|
||||
|
||||
_stopwatch.Start();
|
||||
|
||||
LoadFromDisk();
|
||||
|
||||
_window.IsVisible = true;
|
||||
|
||||
var lastTick = TimeSpan.Zero;
|
||||
var lastFrame = TimeSpan.Zero;
|
||||
var curTime = TimeSpan.Zero;
|
||||
|
||||
while (!GLFW.WindowShouldClose(_window.WindowPtr))
|
||||
{
|
||||
_window.ProcessEvents();
|
||||
|
||||
var tickSpan = TimeSpan.FromSeconds(1f / _tps);
|
||||
while (curTime - lastTick > tickSpan)
|
||||
{
|
||||
lastTick += tickSpan;
|
||||
|
||||
Tick((float) tickSpan.TotalSeconds);
|
||||
}
|
||||
|
||||
_frameTimeIdx = (_frameTimeIdx + 1) % _frameTimings.Length;
|
||||
|
||||
var dt = curTime - lastFrame;
|
||||
lastFrame = curTime;
|
||||
_frameTimings[_frameTimeIdx] = (float) dt.TotalMilliseconds;
|
||||
|
||||
FrameUpdate((float) dt.TotalSeconds);
|
||||
Render();
|
||||
curTime = _stopwatch.Elapsed;
|
||||
}
|
||||
|
||||
SaveToDisk();
|
||||
}
|
||||
|
||||
private static void KeyCallback(KeyboardKeyEventArgs obj, bool down)
|
||||
{
|
||||
var io = ImGui.GetIO();
|
||||
if (obj.Key ==Keys.Unknown)
|
||||
return;
|
||||
|
||||
var keyInt = (int) obj.Key;
|
||||
io.KeysDown[keyInt] = down;
|
||||
io.KeyCtrl = io.KeysDown[(int) Keys.LeftControl] || io.KeysDown[(int) Keys.RightControl];
|
||||
io.KeyShift = io.KeysDown[(int) Keys.LeftShift] || io.KeysDown[(int) Keys.RightShift];
|
||||
io.KeyAlt = io.KeysDown[(int) Keys.LeftAlt] || io.KeysDown[(int) Keys.RightAlt];
|
||||
}
|
||||
|
||||
private static void WindowOnMouseWheel(MouseWheelEventArgs obj)
|
||||
{
|
||||
var io = ImGui.GetIO();
|
||||
io.MouseWheelH += obj.OffsetX;
|
||||
io.MouseWheel += obj.OffsetY;
|
||||
}
|
||||
|
||||
private static void WindowOnTextInput(TextInputEventArgs obj)
|
||||
{
|
||||
var io = ImGui.GetIO();
|
||||
io.AddInputCharacter((uint) obj.Unicode);
|
||||
}
|
||||
|
||||
private void OnMouseDown(MouseButtonEventArgs obj)
|
||||
{
|
||||
var button = (int) obj.Button;
|
||||
if (obj.IsPressed && button < _mouseJustPressed.Length)
|
||||
_mouseJustPressed[button] = true;
|
||||
}
|
||||
|
||||
private void FrameUpdate(float dt)
|
||||
{
|
||||
//var sw = Stopwatch.StartNew();
|
||||
var io = ImGui.GetIO();
|
||||
GLFW.GetFramebufferSize(_window.WindowPtr, out var fbW, out var fbH);
|
||||
GLFW.GetWindowSize(_window.WindowPtr, out var wW, out var wH);
|
||||
io.DisplaySize = new Vector2(wW, wH);
|
||||
io.DisplayFramebufferScale = new Vector2(fbW / (float) wW, fbH / (float) wH);
|
||||
io.DeltaTime = dt;
|
||||
|
||||
UpdateMouseState(io);
|
||||
UpdateCursorState(io);
|
||||
|
||||
//Console.WriteLine($"INPUT: {sw.Elapsed.TotalMilliseconds}");
|
||||
|
||||
ImGui.NewFrame();
|
||||
|
||||
DoUI(dt);
|
||||
}
|
||||
|
||||
private void UpdateCursorState(ImGuiIOPtr io)
|
||||
{
|
||||
var cursor = ImGui.GetMouseCursor();
|
||||
if (cursor == ImGuiMouseCursor.None)
|
||||
{
|
||||
GLFW.SetInputMode(_window.WindowPtr, CursorStateAttribute.Cursor, CursorModeValue.CursorHidden);
|
||||
}
|
||||
else
|
||||
{
|
||||
GLFW.SetCursor(_window.WindowPtr, _cursors[(int) cursor]);
|
||||
GLFW.SetInputMode(_window.WindowPtr, CursorStateAttribute.Cursor, CursorModeValue.CursorNormal);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateMouseState(ImGuiIOPtr io)
|
||||
{
|
||||
for (var i = 0; i < io.MouseDown.Count; i++)
|
||||
{
|
||||
io.MouseDown[i] = _mouseJustPressed[i] ||
|
||||
GLFW.GetMouseButton(_window.WindowPtr, (MouseButton) i) == InputAction.Press;
|
||||
_mouseJustPressed[i] = false;
|
||||
}
|
||||
|
||||
var oldMousePos = io.MousePos;
|
||||
io.MousePos = new Vector2(float.PositiveInfinity, float.PositiveInfinity);
|
||||
|
||||
var focused = _window.IsFocused;
|
||||
if (focused)
|
||||
{
|
||||
if (io.WantSetMousePos)
|
||||
{
|
||||
GLFW.SetCursorPos(_window.WindowPtr, oldMousePos.X, oldMousePos.Y);
|
||||
}
|
||||
else
|
||||
{
|
||||
GLFW.GetCursorPos(_window.WindowPtr, out var x, out var y);
|
||||
io.MousePos = new Vector2((float) x, (float) y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void InitRenderer()
|
||||
{
|
||||
switch (_renderer)
|
||||
{
|
||||
case Renderer.OpenGL:
|
||||
InitOpenGL();
|
||||
break;
|
||||
|
||||
case Renderer.Veldrid:
|
||||
InitVeldrid();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void Render()
|
||||
{
|
||||
ImGui.Render();
|
||||
|
||||
switch (_renderer)
|
||||
{
|
||||
case Renderer.OpenGL:
|
||||
RenderOpenGL();
|
||||
break;
|
||||
|
||||
case Renderer.Veldrid:
|
||||
RenderVeldrid();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
new Program().Run(args);
|
||||
}
|
||||
|
||||
public enum Renderer
|
||||
{
|
||||
OpenGL,
|
||||
Veldrid
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user