Gfx - A minimalist and easy to use graphics API.

Related tags

Miscellaneous gfx
Overview

gfx

gfx is a minimalist and easy to use graphics API built on top of Direct3D12/HLSL intended for rapid prototyping.

It supports:

  • Full shader reflection; no need for root signatures, pipeline states, descriptor tables, register indexing, etc.
  • Internal management of descriptor heaps, resource views, memory allocation, etc.
  • "Garbage collection" defers the release of freed resources to ensure the GPU is done with it; you can create and destroy anything at any time without worrying about synchronization.
  • Automatic placement of pipeline barriers and resource transitions.
  • Runtime shader reloading; simply call the gfxKernelReloadAll() function.
  • Basic GPU-based parallel primitives (min/max/sum scans and reductions as well as key-only/key-value pair sorting for various data types).
  • DXR-1.1 raytracing (i.e. using RayQuery<> object); also known as inline raytracing.

On top of gfx.h, three optional layers are available:

  • gfx_imgui.h: adds Dear ImGui support.
  • gfx_scene.h: supports loading .obj files (.gltf coming soon).
  • gfx_window.h: window creation and basic event management.

Usage

Include the header corresponding to the desired level of functionality.

In one .cpp file, define #define GFX_IMPLEMENTATION_DEFINE and include said header to include the implementation details.

Motivation

This project was born mostly out of frustration while researching rendering techniques using explicit APIs such as Direct3D12 or Vulkan.

These APIs intend to deliver higher performance for complex workloads on both the CPU and the GPU by giving more responsibility to the developer; unfortunately this is to the detriment of productivity especially during the initial prototyping/research phase due to their high level of complexity.

gfx aims to provide an efficient API that's easy to learn, pleasant to use and quick to get things set up, while offering rapid iterations; it allows to get that "start from scratch" efficiency while still offering access to the basic necessary functionality (low-level access to GPU work scheduling, drawing UI overlays, etc.).

Drawing a triangle

Some example code for drawing a simple colored triangle:

// main.cpp

#define GFX_IMPLEMENTATION_DEFINE
#include "gfx_window.h"

int main()
{
    auto window = gfxCreateWindow(1280, 720);
    auto gfx = gfxCreateContext(window);

    float vertices[] = {  0.5f, -0.5f, 0.0f,
                          0.0f,  0.7f, 0.0f,
                         -0.5f, -0.5f, 0.0f };
    auto vertex_buffer = gfxCreateBuffer(gfx, sizeof(vertices), vertices);

    auto program = gfxCreateProgram(gfx, "triangle");
    auto kernel = gfxCreateGraphicsKernel(gfx, program);

    for (auto time = 0.0f; !gfxWindowIsCloseRequested(window); time += 0.1f)
    {
        gfxWindowPumpEvents(window);

        float color[] = { 0.5f * cosf(time) + 0.5f,
                          0.5f * sinf(time) + 0.5f,
                          1.0f };
        gfxProgramSetParameter(gfx, program, "Color", color);

        gfxCommandBindKernel(gfx, kernel);
        gfxCommandBindVertexBuffer(gfx, vertex_buffer);

        gfxCommandDraw(gfx, 3);

        gfxFrame(gfx);
    }

    gfxDestroyContext(gfx);
    gfxDestroyWindow(window);

    return 0;
}

Here's the corresponding vertex shader:

// triangle.vert

float4 main(float3 pos : Position) : SV_Position
{
    return float4(pos, 1.0f);
}

And pixel shader:

// triangle.frag

float3 Color;

float4 main() : SV_Target
{
    return float4(Color, 1.0f);
}
Issues
  • Add support for non-opaque geometry.

    Add support for non-opaque geometry.

    This sets the opaque flag onto geometry in the spatial structure to improve traversal performance in the case that an objects material does not contain an alpha mask. This enables quickly detecting alpha masked geometry during ray traversal.

    The main changes are that the gltf loader will now not automatically convert images to 4 channel. This allows us to detect which images have an alpha channel in the source and use that to detect non-opaque geometry. The conversion to 4 channel is than moved to gltf.

    The RaytracingPrimitiveBuild functions now take an additional parameter used to signal to the spatial structure whether that object contains opaque or non-opaque geometry by setting the corresponding flag (before this was unset which flags everything as non-opaque)

    opened by maoliver-amd 5
  • CMakeLists Error - Beginner

    CMakeLists Error - Beginner

    Hi,

    Can I ask if there is any basic tutorial how to build the project using CMakeLists?

    By default I get the following errors on Windows Visual Studio 2019 x64:

    image

    opened by petrasvestartas 5
  • Using RWByteAddressBuffer and ByteAddressBuffer

    Using RWByteAddressBuffer and ByteAddressBuffer

    Host side:

    byteBuffer = gfxCreateBuffer<uint32_t>(gfx, (nBytes / sizeof(uint32_t) + 1));
    gfxProgramSetParameter(..., .., "byteBufferRW", byteBuffer);
    gfxProgramSetParameter(..., .., "byteBuffer", byteBuffer);
    

    Comp Shader:

    RWByteAddressBuffer byteBufferRW;
    byteBufferRW.Store(idx, value);
    
    ByteAddressBuffer byteBuffer;
    loadVal = byteBuffer.Load(idx);
    

    value and loadVal aren't same. Seems like I am creating the byte addressable buffer wrong?

    opened by sayan1an 4
  • Array of textures

    Array of textures

    Hi,

    I'm trying to do texture mapping using ray-query. Shader side code would look something like this -

    myArrayOfTextures[giveMeMaterialIdx(committedInstanceIndex)].Load(uv)

    As such, I need to create an array-of-textures. Texture2DArrays aren't helpful as all slices must have the same resolution. Any suggestions or alternatives?

    opened by sayan1an 4
  • Simple example how to render a cube and add imgui buttons?

    Simple example how to render a cube and add imgui buttons?

    Hi, your tool/engine looks great, from https://twitter.com/ocornut/status/1371548539046727680 What are the plans for that tool? I realize this is still early on, but I tried to create a simple 'hello world' but couldn't render a simple obj cube (and a ImGUI:Button)

    Could you help? Here is the code:

    #include "gfx_window.h"
    #include "gfx_scene.h"
    #include "gfx_imgui.h"
    
    int main(int argc, char* argv[])
    {
    	GfxWindow window = gfxCreateWindow(1024, 768, "hello gfx");
    	GfxContext context = gfxCreateContext(window);
    	GfxScene scene  = gfxCreateScene();
    	gfxImGuiInitialize(context);
    
    	GfxResult res = gfxSceneImport(scene, "cube.obj");
    	auto cam = gfxSceneCreateCamera(scene);
    	gfxSceneSetActiveCamera(scene, cam);
    	
    	while (!gfxWindowIsCloseRequested(window))
    	{
    		GfxResult res = gfxWindowPumpEvents(window);
    		
    		// https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
    		bool key = gfxWindowIsKeyDown(window, 0x42); 
    		if (key)		{			printf("hi!\n");		}
    		
    		gfxFrame(context);
    		
    		gfxImGuiRender();
    		
    		gfxFinish(context);
    
    	}
    
    	gfxSceneClear(scene);
    	gfxDestroyScene(scene);
    	gfxDestroyContext(context);
    	gfxDestroyWindow(window);
    }
    

    Add to CMakeLists.txt

    add_executable(test test.cpp)
    target_link_libraries(test gfx)
    

    cube_obj.zip

    opened by erwincoumans 4
  • Dumping textures to file

    Dumping textures to file

    Well, what are my options here?

    I don't see a gfxCommandCopyTextureToBuffer(), but I can copy Gpu-Buffers to Cpu memory and access it for processing.

    Maybe this is an option - copy texture (gpu) to gpu-buffer with a shader, then copy gpu-buffer to mapped cpu-memory?

    Is there a better option?

    opened by sayan1an 3
  • Requesting Non-PBR material support

    Requesting Non-PBR material support

    Is it possible to extend materials to have kd, ks and shininess loaded from .mtl files? Tinyobjloader already loads these properties, just need support from gfx.

    Alternatively, an approximate conversion from non-pbr to pbr would also help. e.g. metallicity = ks / (ks + kd) and roughness = sqrt(sqrt(2 / (2 + shininess))).

    opened by sayan1an 3
  • Fix light animation

    Fix light animation

    Firstly I changed the delta light transform to point toward the light instead of away (and fixed a bug)

    Then ive updated the animation code as when there are multiple animations the current behavior assigns every detected animated node to every animation. This breaks animations as multiple incorrect animations are applied to each node wrongly attached to it. After spending alot of time digging around trying to understand what was going on I realised my fix is very similar to the one Sylvain did earlier that was reverted. So let me know if this version has any similar issues

    opened by maoliver-amd 2
  • Dump shader blobs (bytecodes and root signatures)

    Dump shader blobs (bytecodes and root signatures)

    Radeon GPU Analyzer can provide additional informations from DXIL using the command line.

    These changes dump the shader bytecodes and the root signatures used by the application.

    Last version help for DirectX 12 mode (rga.exe -s dx12):

    *** DirectX 12 mode options (Windows only) ***
    ==============================================
    
    Usage: rga.exe [options]
    
    Generic options:
      -l [ --list-asics ]      List the known GPU codenames, architecture names and
                               variant names.
                               To target a specific GPU, use its codename as the 
                               argument to the "-c" command line switch.
      -c [ --asic ] arg        Which ASIC to target.  Repeatable.
      --version                Print version string.
      -h [ --help ]            Produce this help message.
      -a [ --analysis ] arg    Path to output analysis file.
      -b [ --binary ] arg      Path to ELF binary output file.
      --isa arg                Path to output ISA disassembly file(s).
      --livereg arg            Path to live register analysis output file(s).
      --cfg arg                Path to per-block control flow graph output file(s).
      --cfg-i arg              Path to per-instruction control flow graph output 
                               file(s).
      -s [ --source-kind ] arg Source platform: dx12 for DirectX 12, dxr for DXR, 
                               dx11 for DirectX 11, vulkan for Vulkan, opengl for 
                               OpenGL, opencl for OpenCL offline mode and amdil for
                               AMDIL.
      -u [ --updates ]         Check for available updates.
      -v [ --verbose ]         Print command line strings that RGA uses to launch 
                               external processes.
    
      --il arg              Path to output IL (intermediate language) disassembly 
                            file(s).
    
    Macro and Include paths Options:
      -D [ --define ] arg      Define symbol or symbol=value. Repeatable.
      -I [ --IncludePath ] arg Additional include path required for compilation.  
                               Repeatable.
    
    DirectX 12 mode options:
      --vs arg               Full path to hlsl file where vertex shader is defined.
      --hs arg               Full path to hlsl file where hull shader is defined.
      --ds arg               Full path to hlsl file where domain shader is defined.
      --gs arg               Full path to hlsl file where geometry shader is 
                             defined.
      --ps arg               Full path to hlsl file where pixel shader is defined.
      --cs arg               Full path to hlsl file where compute shader is 
                             defined.
      --all-hlsl arg         Full path to the hlsl file to be used for all stages. 
                             You can use this option if all of your shaders are 
                             defined in the same hlsl file, to avoid repeating the 
                             --<stage> argument. If you use this option in addition
                             to --<stage> or --<stage>-blob, then the --<stage> or 
                             --<stage>-blob option would override this option for 
                             stage <stage>.
      --vs-blob arg          Full path to compiled DXBC or DXIL binary where vertex
                             shader is found.
      --hs-blob arg          Full path to compiled DXBC or DXIL binary where hull 
                             shader is found.
      --ds-blob arg          Full path to compiled DXBC or DXIL binary domain 
                             shader is found.
      --gs-blob arg          Full path to compiled DXBC or DXIL binary geometry 
                             shader is found.
      --ps-blob arg          Full path to compiled DXBC or DXIL binary pixel shader
                             is found.
      --cs-blob arg          Full path to compiled DXBC or DXIL binary where 
                             compute shader is found.
      --vs-dxil-dis arg      Full path to the DXIL or DXBC disassembly output file 
                             for vertex shader. Note that this option is only valid
                             for textual input (HLSL).
      --hs-dxil-dis arg      Full path to the DXIL or DXBC disassembly output file 
                             for hull shader. Note that this option is only valid 
                             for textual input (HLSL).
      --ds-dxil-dis arg      Full path to the DXIL or DXBC disassembly output file 
                             for domain shader. Note that this option is only valid
                             for textual input (HLSL).
      --gs-dxil-dis arg      Full path to the DXIL or DXBC disassembly output file 
                             for geometry shader. Note that this option is only 
                             valid for textual input (HLSL).
      --ps-dxil-dis arg      Full path to the DXIL or DXBC disassembly output file 
                             for pixel shader. Note that this option is only valid 
                             for textual input (HLSL).
      --cs-dxil-dis arg      Full path to the DXIL or DXBC disassembly output file 
                             for compute shader. Note that this option is only 
                             valid for textual input (HLSL).
      --vs-entry arg         Entry-point name of vertex shader.
      --hs-entry arg         Entry-point name of hull shader.
      --ds-entry arg         Entry-point name of domain shader.
      --gs-entry arg         Entry-point name of geometry shader.
      --ps-entry arg         Entry-point name of pixel shader.
      --cs-entry arg         Entry-point name of compute shader.
      --vs-model arg         Shader model of vertex shader (e.g. vs_5_1 or vs_6_0).
      --hs-model arg         Shader model of hull shader (e.g. hs_5_1 or hs_6_0).
      --ds-model arg         Shader model of domain shader (e.g. ds_5_1 or ds_6_0).
      --gs-model arg         Shader model of geometry shader (e.g. gs_5_1 or 
                             gs_6_0).
      --ps-model arg         Shader model of pixel shader (e.g. ps_5_1 or ps_6_0).
      --cs-model arg         Shader model of compute shader (e.g. cs_5_1 or 
                             cs_6_0).
      --all-model arg        Shader model to be used for all stages (e.g. 5_1 or 
                             6_0). Instead of specifying the model for each stage 
                             specifically, you can use this option to pass the 
                             shader model version and RGA would auto-generate the 
                             relevant model for every stage in the pipeline. Note 
                             that if you use this option together with any of the 
                             <stage>-model options, for any stage <stage> the 
                             <stage>-model option would override --all-model.
      --rs-bin arg           Full path to the serialized root signature to be used 
                             in the compilation process.
      --rs-hlsl arg          Full path to the HLSL file where the RootSignature 
                             macro is defined. If there is only a single hlsl input
                             file, this option is not required.
      --rs-macro arg         The name of the RootSignature macro in the HLSL code. 
                             If specified, the root signature would be compiled 
                             from the HLSL source. Use this if your shader does not
                             include the [RootSignature()] attribute but has a root
                             signature macro definedin the source code. Please also
                             check the description for --rs-hlsl, which is related 
                             to this option.
      --rs-macro-version arg The version of the RootSignature macro specified 
                             through the rs - macro option.By default, 
                             'rootsig_1_1' would be assumed.
      --gpso arg             Full path to .gpso file that describes the graphics 
                             pipeline state (required for graphics pipelines only).
                             You can generate a template by using the 
                             --gpso-template option.
      --gpso-template arg    Full path to where to save the template pipeline state
                             description file. You can then edit the file to match 
                             your pipeline.
      --elf-dis arg          Full path to output text file where disassembly of the
                             pipeline ELF binary would be saved.
    
    DXC options:
      --dxc arg             Path to an alternative DXC package (a folder containing
                            DXC.exe, dxcompiler.dll and dxil.dll). If specified, 
                            RGA would use DXC from the given path rather than the 
                            package that it ships with.
      --dxc-opt arg         Additional options to be passed to DXC when performing 
                            front-end compilation.
      --dxc-opt-file arg    Full path to text file from which to read the 
                            additional options to be passed to DXC when performing 
                            front-end compilation. Since specifying verbose options
                            which include macro definitions can be tedious in the 
                            command line, and at times even impossible, you can use
                            this option to specify the DXC command line arguments 
                            in a text file which would be read by RGA and passed 
                            as-is to DXC in the front-end compilation step.
    
    Other DX12 options:
      --debug-layer         Enable the D3D12 debug layer.
      --offline             Assume no AMD display adapter is installed.
      --amdxc arg           Path to a an alternative amdxc64.dll to be loaded in 
                            offline mode (must be used together with --offline). If
                            provided, the alternative amdxc64.dll would be used as 
                            the DX12 offline driver, instead of the driver that is 
                            bundled with the tool.
    
    Examples:
      View supported ASICS for DX12:
        rga.exe -s dx12 -l
      Compile a cs_5_1 compute shader named "CSMain" for gfx1010, where the root signature is referenced through a [RootSignature()] attribute. Generate ISA disassembly and resource usage statistics:
        rga.exe -s dx12 -c gfx1010 --cs C:\shaders\FillLightGridCS_8.hlsl --cs-entry CSMain --cs-model cs_5_1 --isa C:\output\isa.txt -a C:\output\stats.txt
      Compile a graphics pipeline with model 6.0 vertex ("VSMain") and pixel ("PSMain") shaders defined in vert.hlsl and pixel.hlsl respectively, while the root signature is in a pre-compiled binary file. Generate ISA disassembly and resource usage statistics:
        rga.exe -s dx12 --vs C:\shaders\vert.hlsl --vs-model vs_6_0 --vs-entry VSMain --ps C:\shaders\pixel.hlsl --ps-model ps_6_0 --ps-entry PSMain --gpso C:\shaders\state.gpso --rs-bin C:\rootSignatures\rs.bin --isa C:\output\disassembly.txt -a C:\output\stats.txt 
      Compile a graphics pipeline with vertex ("VSMain") and pixel ("PSMain") shaders defined both in shader.hlsl, while the root signature is in a pre-compiled binary file. Generate ISA disassembly and resource usage statistics:
        rga.exe -s dx12 --all-hlsl C:\shaders\shaders.hlsl --all-model 6_0 --vs-entry VSMain --ps-entry PSMain --gpso C:\shaders\state.gpso --rs-bin C:\rootSignatures\rs.bin --isa C:\output\disassembly.txt -a C:\output\stats.txt
      Compile a cs_5_1 compute shader named "CSMain" for Radeon VII (gfx906), where the root signature is in a binary file (C:\RS\FillLightRS.rs.fxo). Generate ISA disassembly and resource usage statistics:
        rga.exe -s dx12 -c gfx906 --cs C:\shaders\FillLightGridCS_8.hlsl --cs-model cs_5_1 --cs-entry main --rs-bin C:\RS\FillLightRS.rs.fxo --isa C:\output\lightcs_dis.txt -a C:\output\stats.txt
      Compile a DXIL or DXBC blob for Navi10 (gfx1010) and generate ISA disassembly:
        rga.exe -s dx12 -c gfx1010 --cs-blob C:\shaders\FillLightGridCS_8.obj --isa C:\output\lightcs_dis.txt
    
    opened by smeunier-amd 2
  • Compute Shader

    Compute Shader

    Is this the right way to create a compute kernel?

     program = gfxCreateProgram(gfx, "myCompute");
     kernel = gfxCreateComputeKernel(gfx, program, "main");
    
    opened by sayan1an 2
  • Prevent multiple DX warnings due to resource barrier.

    Prevent multiple DX warnings due to resource barrier.

    This silences a never ending stream of "DX WARNING: ID3D12CommandList::ResourceBarrier: Called on the same subresource(s) of Resource(XXX) in separate Barrier Descs which is inefficient and likely unintentional". There is already a todo about needing multiple resource flags which may be the proper long term fix. This however fixes it in the mean time.

    opened by maoliver-amd 0
  • Logging

    Logging

    gfx relies a lot on logging to report errors. When developing in Visual Studio or another IDE, we always can read the debug output but when the application is run by Pix or another tool, we can't anymore.

    Ensuring a console is always available would be great.

    opened by smeunier-amd 2
  • Frame graph

    Frame graph

    gfx_scene.h is of great help for loading assets.

    Some kind of gfx_frame_graph.h would help a lot of managing resources and developing/reusing/sharing render techniques (a set of render passes).

    opened by smeunier-amd 2
Owner
Guillaume Boissé
Graphics coder at AMD, previously SIE R&D (PlayStation).
Guillaume Boissé
GFX Demo for the ESP-IDF

Display Drivers and Demo for GFX This is a Demo of GFX With Several Display Drivers This is not GFX itself, but it includes it. GFX Documentation is b

honey the codewitch 61 May 17, 2022
Adafruit GFX compatible arduino library for using cat thermal printers with the ESP32

CatGFX This library offers a Adafruit GFX "driver" for ESP32 and the cheap cat (or rabbit?) BLE thermal printers like this one: For usage information

Claus Näveke 2 May 19, 2022
Handcrafted Flutter application well organized and easy to understand and easy to use.

Handcrafted Flutter application well organized and easy to understand and easy to use.

Justin Dah-kenangnon 2 Feb 1, 2022
GraphicsFuzz provides tools for automatically finding and simplifying bugs in graphics drivers, specifically graphics shader compilers.

GraphicsFuzz GraphicsFuzz is a set of tools for testing shader compilers GraphicsFuzz provides tools for automatically finding and simplifying bugs in

Google 499 Jun 22, 2022
Second life for famous JPEGView - fast and tiny viewer/editor for JPEG, BMP, PNG, WEBP, TGA, GIF and TIFF images with a minimalist GUI and base image processing.

JPEGView-Image-Viewer-and-Editor Updated Dec 07 2021. Version 1.1.1.0 has been released. Download link1, link2 added. Second life for famous JPEGView

Ann Hatt 22 Jul 3, 2022
F Graphics Library (FGL) is a small graphics C++ portable library for LCD displays on embedded systems

F Graphics Library (FGL) Full documentation: fgl.docsforge.com (By Filipe Chagas) F Graphics Library is a C++ library that I created for use in embedd

Filipe Chagas 8 Dec 14, 2021
CSC404: Computer Graphics [CG] & CSL402: Computer Graphics Lab [CG Lab]

COMPUTER-GRAPHICS-AND-COMPUTER-GRAPHICS-LAB CSC404: CG & CSL402: CG LAB [SEMESTER IV] Syllabus CG - Reference Books THE WALL MEGA SATISH - AUTHOR CG C

AMEY THAKUR 7 Apr 28, 2022
Minimalist protocol buffer decoder and encoder in C++

protozero Minimalistic protocol buffer decoder and encoder in C++. Designed for high performance. Suitable for writing zero copy parsers and encoders

Mapbox 217 Jun 28, 2022
F3D - Fast and minimalist 3D viewer

F3D - Fast and minimalist 3D viewer By Michael Migliore and Mathieu Westphal. F3D (pronounced /fɛd/) is a VTK-based 3D viewer following the KISS princ

null 515 Jun 25, 2022
isabel - Simple, minimalist note manager.

isabel isabel - Simple, minimalist note manager. Usage Type name and body of note and press Ctrl+s for save note. Press Tab to open notes list. Press

null 1 Oct 2, 2021
Legacy stepper motor analyzer - A DYI minimalist hardware stepper motor analyzer with graphical touch screen.

Simple Stepper Motor Analyzer NOTE: This is the legacy STM32 based design which was replaced by the single board, Raspberry Pi Pico design at https://

Zapta 158 Mar 21, 2022
A Sol-inspired minimalist Lua binding for Zig.

zoltan A Sol-inspired minimalist Lua binding for Zig. Features Supports Zig 0.9.0 Lua tables table creation from Zig get/set/create methods possible k

null 66 Jun 20, 2022
Reference Implementations of P0267, the proposed 2D graphics API for ISO C++

P0267 Reference Implementation Please read the LICENSE before cloning or forking the code as there is important information there! Please see the wiki

cpp-io2d 301 Jun 11, 2022
Rewritten version of the MiniLibX graphics API used by 42, using glfw & glad. Running on OpenGL.

Written by W2.Wizard for the 42 Network A recreation of the MiniLibX library used by 42, using GLFW & glad, running on OpenGL. The goal of MLX42 is to

Codam 70 Jun 30, 2022
A small and easy to use neural net implementation for C++. Just download and #include!

NN++ A short, self-contained, and easy-to-use neural net implementation for C++. It includes the neural net implementation and a Matrix class for basi

Gil Dekel 224 Jun 7, 2022
Fast and easy to use, high frequency trading framework for betfair

Hedg Fast and easy to use, high frequency trading framework for betfair About Hedg In the sports trading industry, low latency is really important. Th

Oluwatosin Alagbe 8 Jun 11, 2022
Samir Teymurov 1 Oct 6, 2021
Open Source Cheat for Apex Legends, designed for ease of use. Made to understand reversing of Apex Legends and respawn's modified source engine as well as their Easy Anti Cheat Implementation.

Apex-Legends-SDK Open Source Cheat for Apex Legends, designed for ease of use. Made to understand reversing of Apex Legends and respawn's modified sou

null 86 Jun 24, 2022
MasterPlan is a project management software / visual idea board software. It attempts to be easy to use, lightweight, and fun.

MasterPlan is a customizeable graphical project management software for independent users or small teams. If you need to share plans across a whole co

SolarLune 407 Jul 3, 2022