chris-montero / terra

A sane application development framework for the desktop.
116 stars 5 forks source link

Porting to windows #6

Open justdie386 opened 2 months ago

justdie386 commented 2 months ago

Hi, i found myself looking at the code and thinking about how this could be ported to windows, but it seems like it'll require more work than just doing the equivalent of what xcb does in the C code in a native win32 gui library. I would like to know, have you thought about how this porting will be done? I'd be interested to maybe look into helping, maybe using direct2d as it seems to be the thing that is most fit for this project.

chris-montero commented 2 months ago

I just noticed you also wrote this. I already responded to you on reddit, but it's better to have a github issue for this.

I have no idea how this porting will be done. Porting this project to another platform would be the first multi-platform anything I would ever be doing, but I don't think it would be so much trouble.

I already set up some general architecture for how the files will exist in the project structure so as to make it as easy as possible to add support for whatever platform in the future.

You also mentioned problems with getting lgi to work on windows, but I found this: https://github.com/xournalpp/xournalpp/discussions/4522. Perhaps if we can package everything correctly when deploying an application, we can get lgi to work on windows too?

Although the better option I see would be to drop the lgi dependency altogether. This project makes use of lgi in order to have access to cairo for drawing, and pango for text-drawing. So I see two options:

  1. either we somehow start using direct cairo-lua and pango-lua bindings (which I don't know if they exist), which would allow us to drop the lgi dependency, and also get a massive speed-up, or
  2. we start using something else like skia or blend2d instead of cairo and pango. And then we would probably never have to worry about performance or cross platform issues, at least with skia since skia is also used on android (as far as I know).
justdie386 commented 2 months ago

Using something like skia we could probably setup some luajit ffi bindings. that could make things way faster than to write Lua C api bindings, and it would remove the need to even compile anything to start using the library.

justdie386 commented 2 months ago

I found this, but it doesn't seem very documented, it's the only page that mentions this https://chromium.googlesource.com/skia/+/chrome/m49/site/user/special/lua.md

chris-montero commented 2 months ago

Yes, I found that page too. I have no problem with luajit ffi bindings since I only plan on supporting luajit.

The potential problems I see with skia and blend2d are that skia keeps changing its api, and I think blend2d might be too young yet.

If you, or anyone else, wish to contribute to add support for any other platform, that's ok with me, but I won't be working too closely with you since adding support for other platforms is not my current priority. I first want to solidify the lua api presented to the user, and only then do I plan on doing any work for any other platform.

I'll leave this issue open to track progress in porting to windows.

justdie386 commented 2 months ago

I also read on some papers that mentioned this issue, but I also found something mentions that ski has now a C based api to fix this issue of breaking changes, for it will in fact try to maintain the most backwards compatibility possible: https://chromium.googlesource.com/skia/+/6c17888a586cd27e2063e4287c8d92b1805fea71/experimental/c-api-example/c.md

justdie386 commented 2 months ago

I understand this priority of yours, I won't be starting to port it right away as I have to learn how to use skia, but I think if we wanted to make this happen, I'd need you to help me with the structure we'd have to use for each platform's code,all in a single folder, all the platform specific code in their specific folders, so on and so on. This would also mean getting rid of lgi, Cairo, and pango, which pretty much just sounds like a full on re-write of the library (except the logic, math and so on)

chris-montero commented 2 months ago

ski has now a C based api to fix this issue of breaking changes

I didn't know they plan on having a more stable C api. That's great news.

I'd need you to help me with the structure we'd have to use for each platform's code,all in a single folder, all the platform specific code in their specific folders, so on and so on.

I'd definitely help you or whoever exhibits enough interest in doing work in porting this project to another platform. You can contact me and we can converse about any issues you run into, but I think the work I did for the latest master branch commit covers a lot of the bases for multi-platform support.

In essence: whatever C code you need to write to make it work on windows, just make a dir named "c/src/win", make a main file there called "context.c", which should allow you to create something like a "win_ctx". Then, make two files: "l/platforms/win/app.lua" and "l/platforms/win/window.lua", which should allow you to create an app and window, and I think that should work.

which pretty much just sounds like a full on re-write of the library

That's not true. Even the current code in the library that uses cairo and pango would not be difficult at all to replace, especially since skia seems to have a pretty similar api.

justdie386 commented 2 months ago

I do not know about the skia/cairo api just yet, but if you say it should be easy to just replace them out, this would be great, thanks for the instructions for the files aswell, I'll get to it this weekend!

chris-montero commented 2 months ago

Very important: I found this: https://github.com/luvit/luvi

Seems to be a project that allows you to "compile" lua project directly in to binaries, which is exactly what I wanted to build at some point. If that's what it is, this is very useful because I want to allow users of this project to write their programs, "compile" it somehow and just distribute a binary that works. I'm writing this because if that's the case maybe it allows you to more easily somehow "bundle" lgi to work on windows. I don't have time to try it out now, but I'm just saying that this project will probably move in the direction of using luvi.

Also, I found this: https://github.com/luvit/luv

This project will also do all of its asynchronous and multi-threaded operations using luv. I will replace the current lua-ev bindings that this project with luv in the following days.

justdie386 commented 2 months ago

Funny you mention this, I used it with a discord api wrapper for a year, so I know it's ins and outs, and it's cool for it has its own package manager, and it's based on luajit (I think) so it has luajit's ffi, and a ton of other batteries included, it would indeed be interesting to use this instead, but I think its event loop will require to use a hack around it for GUI because it would make it more power or something along the line of it.

justdie386 commented 2 months ago

Also to follow up on the lgi thing, it's not lgi that is the issue, it's gobject-introspection itself that causes trouble, it seems pretty hard to install on windows outside of msys2 which isn't really ideal. So luvit won't solve the issue. I thought about using glfw3 ffi bindings along blend2d (skia alternative that is just as fast, has a stable api/decent docs, and is 1000x easier to compile (skia uses some weird tool call gn and a ton of other stuff, blend2d just uses cmake with a single dependency))

chris-montero commented 2 months ago

@justdie386 Sounds great! I am very grateful you mentioned glfw3. I didn't know it existed, but looking at it I think it would be very cool to use it. I found these bindings for luajit: https://github.com/Playermet/luajit-glfw

I was actually thinking that it would be best to have this project support drawing on the gpu by default, and only draw on the cpu if there's no alternative. This way this project would become a full-fledged application development toolkit instead of a CPU based, 2d only GUI toolkit.

Also, doesn't this make it so that it automatically renders all opengl calls with the cpu on platforms that don't have a gpu?

but I think its event loop will require to use a hack around it for GUI

I'm not sure what you mean by this, but I just replaced lua-ev with luv. Check out the latest master branch commit.

justdie386 commented 2 months ago

Well blend2d supports gpu + cpu, unlike Cairo, and I'm pretty sure glfw will aswell

Also yeah i don't know if you knew, but luv, luvi, also has luvit, which is a lua interpreter that comes with awesome batteries, such as http, zip stuff, filesystem, event emitters, and its own package manager that works wonders. I thought you meant we switch from luajit to luvit, but otherwise, using luv from normal lua shouldn't mess up the event loop.

justdie386 commented 2 months ago

Also, I have two questions, first: did you know Cairo should support fonts? Why use pango then? Second question: Why did you use x11 for the window handling? If I was able to make lgi work on windows properly, we could instead pretty much just rewrite the x11 code, replacing it with glfw3, and there shouldn't be too much issues making it work absolutely anywhere at all

chris-montero commented 2 months ago

What I mean is that I definitely want the project have 3D capabilities in the future. Probably through having a "gpu" element or something like that. I was thinking we could perhaps use glfw for window creation, and have something like nanovg do 2D drawing directly on the gpu. Maybe something like this: https://github.com/starwing/lua-nanovg

I'm a bit out of my depth with this one since I have never done anything 3D on the gpu, so if you're more experienced, I'm open to advice.

The thing I am most afraid of going with a 2d-only library like blend2d or skia would be the fact that if I want to add a 3d element in the future, I would be held back by the fact that they are both 2d-only libraries (as far as I know). Whereas if I went with something like glfw+nanovg, we would surely be able to do 2D graphics, and we would ensure the posibility of adding a gpu element in the future. Again, if you have more experience on this, I'm listening.

Why use pango then?

Because the cairo docs mention that the built-in cairo api for drawing text is a "toy" api. I wanted to make sure I did it proper back then. I didn't expect I'd be so prone to switching drawing backends like this.

Why did you use x11 for the window handling?

Because that's what I knew best. I come from using AwesomeWM, that's what they used, that's what I'm familiar with, so that's the thing I used just to get this project up-and-running. I don't have any reason to only do X11, and I expected I would add support for other platforms in the future as well.

Also, I do know about luvit, but I'm not entirely convinced about it. It might have batteries included, but as long as the project doesn't need to directly depend on anything from there, I don't see a reason to include it. If any user of this library wants to have zip, http, etc functionality, I think they can just import a library that does that themselves. Also, about the compilation part: as far as I know luvit just uses luajit to compile lua code, and that part is done by the luvi project (I think). So I don't see why not just import what we need, and keep dependencies to a minimum. If there's something we would really benefit from by using luvit, I'm open to persuasion.

justdie386 commented 2 months ago

Mhm, but for me, just the 2d drawing is out of my field of experience, i've been learning about it on my free time, and it looks fairly simple to learn, and i have plenty enough experience in lua to get away with it, but 3d might a little too much, i'm just a guy that wants to help this project going forward, not experienced much in 2d drawing itself... I don't see why one would need 3d capabilities for a gui library too. For the pango and x11 thing, i see what you mean, i'd have done the same if i knew how to use any of those myself...

chris-montero commented 1 month ago

I see. I don't want to put any pressure on you. You contribute as much as you feel comfortable, but I slept on it and decided that the direction I want the project to go into is glfw+nanovg. I want to use these bindings for glfw: https://github.com/Playermet/luajit-glfw And to fork these bindings for nanovg: https://github.com/starwing/lua-nanovg (because they only seem to support lua >= 5.3)

The reason I don't think it's a good idea to only go with a 2d library like blend2d is because people always end up wanting to do 3d stuff and good performance. Just look at the browsers and how they ended up adding webgpu.

And also look at this library: https://github.com/iced-rs/iced It's a gui library for rust that draws directly on the gpu, so I know that what I'm looking for is possible.

Again, if you don't wish to contribute any further, don't feel any pressure to.

justdie386 commented 1 month ago

Well, 3D is for games, not sure what you mean by making a GUI with 3D, and both skia and blend2d support gpu aswell. But if it's nanovg you are choosing, I can look into it

chris-montero commented 1 month ago

By the way, if you wanted to use blend2d or skia, how would you plan to handle drawing on windows? As far as I know you can't normally do 2d graphics with glfw. It only gives you an opengl/vulkan context, so no cpu rendering. It sounds like if we plan on using glfw, we MUST draw 2d graphics on the gpu (with something like nanovg).

In essence what I'm trying to say is: if we use glfw for windows (which requires you to draw using the GPU), but blend2d doesn't have a gpu rendering backend, how do you plan to get the drawing which blend2d produces on the CPU to the GPU?

justdie386 commented 1 month ago

Well true glfw is gpu so this won't cut it. Tho for blend2d and gpu I really don't know, it doesn't seem to mention anywhere wherever it's supporting gpu or cpu or both.

justdie386 commented 1 month ago

But I did get glfw to render a blend2d image without too much code on windows

justdie386 commented 1 month ago

Also maybe sfml could be interesting, https://www.sfml-dev.org/tutorials/2.6/graphics-draw.php bindings could be make easily with sol2, which makes it ridiculously hassle free when compared to the normal lua C api

chris-montero commented 1 month ago

I have no clue what sol2 is lol all these libraries make my head spin man. There's so many options. It's so stupid that the programming world hasn't figured out by now how to draw things on the screen.

It's late for me right now, but I'll investigate tomorrow and let you know what I think. I had a brief look at sfml and it only seems to support very simple 2d shapes, no? https://www.sfml-dev.org/tutorials/2.6/graphics-shape.php

Also, the code where you got glfw to render an image with blend2d. Can you show me that code? I'm curious about how you did that. Blend2d says on their about page "how does blend2d compare to gpu rendering" at the bottom of the page. So it's a cpu only solution. They might do something on the GPU for images (maybe, i'm not sure), but other than that, I'm pretty sure it's cpu only.

justdie386 commented 1 month ago

here you go, i dunno if its efficient took the code from stuff online and put it together, but it seems to display the blend2d drawn images, which means its a success as far as i see it

#include <GLFW/glfw3.h>
#include <blend2d.h>
#include <stdio.h>

// Window dimensions
const int WIDTH = 800;
const int HEIGHT = 600;

int main() {
    // Initialize GLFW
    if (!glfwInit()) return -1;

    // Create a windowed mode window and its OpenGL context
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "GLFW + Blend2D", NULL, NULL);
    if (!window) {
        glfwTerminate();
        return -1;
    }

    // Make the window's context current
    glfwMakeContextCurrent(window);

    // Set up Blend2D image to render to
    BLImage img(WIDTH, HEIGHT, BL_FORMAT_PRGB32);
    BLContext ctx(img);

    // Main loop
    while (!glfwWindowShouldClose(window)) {
        // Clear the image buffer with Blend2D
        ctx.clearAll();
        BLGradient linear(
            BLLinearGradientValues(0, 0, 0, 480));
        // Draw a simple rectangle using Blend2D
        ctx.setCompOp(BL_COMP_OP_SRC_OVER);
        ctx.setFillStyle(BLRgba32(0xFF00FFFF)); // Magenta color
        ctx.fillRect(100, 100, 200, 200); // Draw a rectangle

        // Finalize Blend2D drawing
        ctx.end();

        // Get a pointer to the raw pixel data
        BLImageData imgData;
        img.getData(&imgData);
        // Render the Blend2D image buffer to the GLFW window
        glDrawPixels(WIDTH, HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, imgData.pixelData);

        // Swap front and back buffers
        glfwSwapBuffers(window);

        // Poll for and process events
        glfwPollEvents();
    }

    // Clean up and exit
    glfwDestroyWindow(window);
    glfwTerminate();
    return 0;
}
justdie386 commented 1 month ago

image

chris-montero commented 1 month ago

I see, so you render to an image and you send that to the GPU, but isn't this expensive? I think you need to send the image to the GPU each frame, which would probably create a lot of synchronization between GPU and CPU, which I heard could be a major bottleneck.

By the way, if we were to use blend2d, do you know of any bindings? I only found these ones: https://github.com/Wiladams/lj2blend2d

justdie386 commented 1 month ago

Well I don't know, it worked, all this to show it's possible, dunno if it's a good way of doing it tho

justdie386 commented 1 month ago

Also I had found those bindings before, but I fear it is probably going to be outdated, looking at how old the library is.

chris-montero commented 1 month ago

I'll write this comment partly for my sanity, but also for future consideration.

After my research for the past few days, I found multiple options for 2D vector graphics. Some on the CPU, some on the GPU. I would like to go with something that is able to draw 2D graphics on the GPU primarily since I would like to be able to add support for a 3D element in the future as well. Thus, going with a 2D only solution would normally be off the table. However, many apps do not require any sort of 3D element. So perhaps we could provide multiple backends for drawing, allow the user of this library to check for GPU support, and if none is available but the application requires it (like CAD software), decide to display something like a "GPU not found" message.

Solutions that I found and perhaps we can discus which would be the most appropriate.

Library Language Rendering Has Lua bindings Maintained License
nanovg C GPU (OpenGL) lua-nanovg No here
nanovg-vulkan C GPU (Vulkan) (reuse nanovg lua bindings?) Maybe here
nanovgXC C CPU & GPU (OpenGL) No Yes here
vkvg C GPU (Vulkan) No Yes (I think) MIT
spinel C GPU (Vulkan 1.2) No No NONE (BSD?)
blend2d C++ CPU maybe? (old) Yes ZLib
cairo C CPU luapower/cairo oocairo lgi Yes(ish) GNU LGPL-2.1/Mozilla Public License 1.1
skia C++ CPU & GPU maybe? Yes BSD-3
vg-renderer C++ GPU (bgfx) No Yes BSD-2
pathfinder Rust CPU & GPU (OpenGL 3.0+, OpenGL ES 3.0+, WebGL 2, Metal) No Yes Apache-2.0/MIT
vello Rust GPU No Yes Apache-2.0/MIT
forma Rust CPU & GPU No No Apache-2.0

Also relevant:

glfw - multi-platform window handling luajit glfw bindings Luvit + bgfx + nanovg example Random-access rendering of general vector graphics Massively Parallel Vector Graphics Computer Aided Geometric Design Flattening quadratic Beziers

After considering all of these choices, it seems to me like the best thing to do moving forward would be to use GLFW for adding multi-platform window support. There are already bindings available that seem good here. I think this choice would force us to go through the GPU to draw things on the screen, which means that we probably want to go with a vector graphics library that can draw on the GPU.

We can also go with a CPU based vector graphics library, draw into a frame buffer, and upload that image to the GPU, but we must keep in mind that that would probably create a lot of synchronization between the GPU and the CPU, thus bottlenecking our drawing.

Bindings availability matters, but I think not as much since we can maybe generate bindings with something like this: https://github.com/CapsAdmin/ffibuild.

I'm not sure yet what 2d graphics library to go with exactly, but I'll make a decision tomorrow.

I think the best thing to do is get something that is easy to integrate, so that if it's not a good fit, we can swap it out quickly.

justdie386 commented 1 month ago

I'll be honest, I'm not sure which way to go then, at this point, it's your project, and I guess I'll just follow your call on what to use, because I ran out of ideas. All of our options you just listed, it's your choice now

chris-montero commented 1 month ago

I think glfw would be the best tool to abstract away window handling. This would be wonderful in furthering multi-platform support, and I don't see a downside to it, so I think we should use it.

As for the graphics library, I think we should go with oocairo. It's a direct binding to cairo, which means you shouldn't have any problems with it on windows. I think this is the best choice for the following reasons:

If you do end up working to implement this, glfw+oocairo, that would be wonderful. We can do the same thing that you did in your example above with glfw + blend2d, but we must make sure it's performant enough.

So I recommend: fork this project, make a separate branch, and make it work with glfw. The whole project is very modular so I don't think you'll have many problems. You can just get rid of all the C code in this project, and on the lua side you can just make a new "terra/window.lua" file, and put all the glfw code there. Then, push that branch to this project so I can review it.

Then, you can make another branch based on the glfw one, and try to implement support for oocairo. But I ask this of you first: test oocairo to see if it's performant enough with glfw. Draw a bunch of shapes, rectangles, maybe some arcs and some beziers, fill the paths with gradients, etc. Make it draw the whole window non stop, make the window fullscreen, and count the frames. We must make sure it's performant enough. One of the core features of this project is built-in animation support, so if it can't draw everything at LEAST 60fps, then it's not performant enough and we need to use something else.

What do you think? Do you see any problems with this approach?

justdie386 commented 1 month ago

I agree 100% for the glfw part, this weekend I'll start working on it. For the oocairo, I'm not really sure, I would assume this would remove the need for lgi to be used, but I feel like another problem was that Cairo is a pretty much dead project that is CPU only. Swapping lgi with oocairo, whilst making it possible to use under windows, probably won't be very future proof. Another possibility for 2D drawing we haven't explored, is simply going the platform dependant route, Cairo/lgi works just fine on Linux and macOS. I've been trying to get terra to work on my MacBook but I've been having issues with it not finding xcb-util-errors. If we just made a windows specific backend using direct2d. The last and final option that I find viable is just use skia, and stick to the latest currently stable release, and just update to combat the new api changes every once in a while, because it seems for cross platform that skia truly is the way to go.

justdie386 commented 1 month ago

Also the website for the docs of oocairo seems to now be a "gambling news" website soooo that's strange

chris-montero commented 1 month ago

I agree with you on the drawing library part. Cairo doesn't really seem to have a future.

But I don't want to go the platform dependent part because I don't know direct2d and I don't plan on learning it. I maintain this project so if something breaks, I want to be able to fix it.

I think the best choice is to just use skia. I took a look at some of their docs that I could find and it seems skia has a software rasterizer to draw on the CPU, and a GPU backend called Ganesh: link. It's also multi-platform, supports paths, matrix operations, region operations, etc.

The building might be cumbersome, but we can just build it once, and keep the built project directly into this project, and only re-build it once in a while. I also heard google's skia team are prompt to respond so if we run into issues, we can just ping them.

There's also these lua bindings that I don't know if they work, but I guess we can try.

Edit: I just noticed google's lua bindings are for lua5.2. I know luajit has some compatibility options for lua5.2, but I'm not sure if it's enough to make them work with luajit. Do you know?

justdie386 commented 1 month ago

I feel like the lua bindings would be interesting, I'll check out if it works later today, but I agree that glfw and skia should be the way to go from now on

chris-montero commented 1 month ago

@justdie386 By the way, if the skia lua bindings don't work with luajit, I think we won't be able to go with skia. This is because the project depends on working with luajit since it's using projects like luvi and luv. Also skia is written in C++ which means it's more difficult (I think) to write bindings for it, plus it changes all the time.

If we don't go with skia, I suggest we just go with nanovg and these bindings. I also just saw this project on r/unixporn that uses glfw + the lua nanovg bindings to create a window and render to it so I think it's fine.

justdie386 commented 1 month ago

Ok let's go for nanovg but we have to port it to lua5.1, and I'm not much of an expert in that field, I can give it a shot tho

chris-montero commented 1 month ago

Give it a shot, and if you come across something you can't do, ping me on this issue and I'll help you as soon as I can.

chris-montero commented 1 month ago

@justdie386 Hey man, I don't know if you're working on this anymore, but just wanted to make sure I let you know: I can't maintain this project anymore. I need to find a job, and also I'm very busy with other stuff. If you push any changes, I will probably not be able to assist you. Sorry, a man's gotta eat.

justdie386 commented 1 month ago

All good, I haven’t really been able to get it working on windows in the first place, so sadly enough I had to give up, turns out also that 2D drawing isn’t as fun as I expected.Sent from my iPhoneOn Sep 26, 2024, at 10:27 AM, Chris Montero @.***> wrote: @justdie386 Hey man, I don't know if you're working on this anymore, but just wanted to make sure I let you know: I can't maintain this project anymore. I need to find a job, and also I'm very busy with other stuff. If you push any changes, I will probably not be able to assist you.

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: @.***>