Giter Club home page Giter Club logo

luminance-rs's Introduction

luminance logo

luminance, the safe, type-driven, multi-backend and simple graphics crate

Build Status] docs.rs crates.io License Matrix room

History

luminance is an effort to make graphics rendering simple and elegant. It was originally imagined, designed and implemented by @phaazon in Haskell (here) and eventually ported to Rust in 2016. The core concepts remained the same and the crate has been slowly evolving ever since. At first, used only by @phaazon for his Rust demoscene productions (example here and here, using spectra) and a bunch of curious peeps, it now has more visibility among the graphics ecosystem of Rust.

Currently, that ecosystem is spread into several crates, ideas and people. It is highly recommended to read the great article about the ecosystem by @Icefoxen, here.

However, luminance is a bit different from what it was initially imagined for. People are looking for an easy-to-use crate, with good abstractions and safe-guards against all the bad and dangerous graphics API caveats. luminance has always been about providing a safe, type-safe and elegant API (being Haskell-based makes it heavily use the type system, for instance) but it now has a more unique niche in the ecosystem. Where gfx-hal provides an experience focused on down-to-metal performance and an API very similar to Vulkan’s, luminance provides an API that is, for sure, a bit higher-level, and not Vulkan-based — and hence, yes, it likely won't give you the same performances as with gfx-hal (though no benchmarks have been done so far) — but easier to start with, especially if you don’t already have a background experience with OpenGL or Vulkan.

The strengths of luminance are:

  • Easy to learn: the concepts, based on OpenGL, are applied to graphics, not general-purpose programming on GPU. Using luminance will help you wrap your fingers around what graphics programming is about and it will help you, perhaps, to jump to lower abstractions like gfx-hal, if you ever need to.
  • Performant: by using Rust and being designed around the concept of good performances, luminance should allow you to build nice and fast simulations, animations and video games. Remember that games you played years ago didn’t have Vulkan and were impressive nonetheless. It’s unlikely you will get 100% out of your GPU by using luminance since it’s built over technologies that are not using 100% of your GPU. Unless you need and know exactly why you need 100% of your GPU, you should be just fine™.
  • Elegant: the design is heavily based on functional programming concepts such as typeclasses, associated types, singleton types, existentials, contravariant resources, procedural macros, strong typing, etc. Plus, every bit of possible stateful computations is hidden behind a system of smart state, removing the need to worry about side-effects. luminance still has mutation (unless its Haskell version) but the Rust type system and borrow checker allows for safe mutations.
  • Modern: the whole luminance ecosystem tries its best to stay up-to-date with Rust evolutions and features. On the same level, the underlying technologies are kept up-to-date and might even be replaced if modern, better-suited alternatives emerge (similarly, Vulkan support might eventually get added, though there are no immediate plans to do so).
  • Opinionated enough: a big bet with luminance was to make it opinionated, but not too much. It needs to be opinionated to allow for some design constructs to be possible, optimize performance and allow for extra safety. However, it must not be too opinionated, lest it become a framework. luminance is a library, not a framework, meaning that it will adapt to how you think you should design your software, not the other way around (within the limits of safe design). luminance won't tie your hands.

The luminance ecosystem

It is currently composed of several different crates:

Core crates

  • luminance: the core crate, exposing a graphics API that aims to be easy to learn, safe, type-safe, stateless and fun!
  • luminance-derive: a companion crate to luminance you’re very likely to enjoy; it will help you derive important traits for your application or library to work. You should definitely invest some time in the documentation of this crate; it’s easy and well explained.
  • luminance-front: a front facing set of luminance re-exports to make it easy to use the library as a end-user developer by picking a backend type at compile-time, most of the time based on your compilation target.

Backend crates

  • luminance-gl: a crate gathering OpenGL backends. Several versions might be supported.
  • luminance-webgl: a crate gathering WebGL backends. Several versions might be supported.

Platform crates

Other crates

  • luminance-std140: a crate useful to backend crates based on Khronos / OpenGL’s std140.
  • examples: a combination of examples to show off some features / techniques.

Learning

luminance has two main and official resources to learn:

  • The book. It contains various chapters, including tutorials and onboarding newcomers. It will not provide you with the best description of a given feature as it focuses more on the overall comprehension and explaining than code directly. It also fits people who don’t know anything about rendering.
  • The examples. They are like unit tests: each introduces and focuses on a very specific aspect or feature. You should read them if you are interested in a specific feature. They’re not well suited to learn from scratch and they are weaker than a structured tutorial but more concise. They also provide functional tests as the backend-agnostic architecture allow to very easily add new tests.

You should try both ways and see which one fits the best for you!

Contributing

Please read the CONTRIBUTING document.

Dependent projects

Those projects use luminance:

Licenses

luminance is licensed under BSD-3-Clause and the logo is under CC BY-ND.

luminance-rs's People

Contributors

64kramsystem avatar ambihelical avatar austinjones avatar cedric-h avatar dependabot-preview[bot] avatar dependabot[bot] avatar dkim avatar dsabadie-datadog avatar gitter-badger avatar guillaumegomez avatar hadronized avatar iwikal avatar johndoneth avatar jsonnull avatar kpreid avatar licynthiax avatar linkmauve avatar llogiq avatar magicrb avatar masonium avatar moredread avatar othelarian avatar poignardazur avatar r-englund avatar resinten avatar rgiot avatar sapphire-arches avatar spacialcircumstances avatar zenithsiz avatar zicklag avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

luminance-rs's Issues

The sem map should be updated in the OpenGL backend

I switched to a more direct style (indexes are now stored directly in Sem, not implicit to index position in the slice). This is much better because it’s safer (waaaaaay safer) and allows people to mixin uniforms and uniform blocks (locations semantics vs. binding points / indexes semantics).

Disable culling as default

It can occur that people are confused because of the current default for face culling (#60). The default is:

  • FaceCullingOrder::CCW
  • FaceCullingMode::Back

If one defines geometry in CW order, they’ll get a black screen, which leads to wrong assumptions that there’s a bug. The default should then be set to no culling to prevent that from happening sneakily.

MRT is broken with more than two textures

MRT with:

  1. Texture<Flat, Dim2, RGBA32F>
  2. Texture<Flat, Dim2, R32F>
  3. Texture<Flat, Dim2, RGBA32F>

yields incorrect results for any of the map. The first, diffuse map seems pretty “okish”.

render arbitrary slice of a Tess

one_sub() only seems to render the first n verts.

i want to render half of my buffer, change some uniforms, and then render the other half. is there a idiomatic way to do this?

Cannot get basic triangle drawn

I've attempted to get a simple triangle drawn, using the example provided (this one). The example seems to be slightly outdated, so I modified it slightly - the code for this can be found here. It doesn't work for me (black screen), and neither does the example there.

unsafe traits

We need to review all the traits to tag them as unsafe if needed.

Potential bug: Gpu binding in “most nested” closure might produce indesirable effects

In spectra, I have a wrapper function that will send data to a shader before issuing a call. If we try to bind something there, like a Texture, we might produce an indesirable effect, since the texture will get unbound right before issuing the call. It’s not that simple to reproduce, but:

pipeline(_, |shd_gt| {
  shd_gt.shade(&program, |tess_gt, uniforms| {
    {
      let bound_texture = gpu.bind_texture(&my_texture);
      uniforms.0.update(&bound_texture); // oops
    }

    tess_gt.render(…);
  });
}),

Difference of behavior of the program between Framebuffer::default(..) and Frambuffer::new(..)

Hello,
I may have missed something when digging in the code and documentation, but I'm unable to manually build the framebuffer to display something in the glw window.

If I create the framebuffer using:

let framebuffer = Framebuffer::default(size); 

the drawing corresponds to what I want.

But, if I create the framebuffer using:

let framebuffer : Framebuffer<Flat, Dim2, Texture<_, _, RGBA32F>, ()>= Framebuffer::new(
            &mut dev,
            size,
            0
        ).expect("Unable to create the framebuffer");

or even in order to be more close to the type provided by default

let framebuffer : Framebuffer<Flat, Dim2, (), ()>= Framebuffer::new(
            &mut dev,
            size,
            0
        ).expect("Unable to create the framebuffer");

the program blindly fails: there is no panic but the glfw stays black.

No idea if I'm wrong somewhere or if there is an issue in the Framebuffer builder

edit: if it has an impact, output of the fragment shader is a vec4

instanced attributes ?

Hey,

Can you confirm luminance does not yet support instanced attributes? I see no use of VertexAttribDivisor in the code.

Change the BoundBuffer type variables

For now, we have to use the following boilerplate:

BoundBuffer<'a, Buffer<T>>

We should reduce that to the easier:

BoundBuffer<'a, T>

Same thing for BoundTexture.

ProgramWarning for uniforms

Currently, when mapping uniforms, if we hit an inactive uniform, we end up discarding everything and generate a ProgramError::InactiveUniform. We need to introduce a ProgramWarning type so that we can still go on and return warnings and the uniform interface at the end of the closure. Uniforms that have failed to be mapped should generate the ProgramWarning::InactiveUniform error.

OpenGL object creation must be done via a thread-bound object

Foreword

The current codebase enables people to create buffers and textures like this:

let buf = Buffer::new(1);
let tex = Texture::new([w, h], 0, &Sampler::default()).unwrap();

This is handy but this is also very wrong. The problem with this is that it’s possible to create such objects on any threads. However, an OpenGL context is always bound to a thread.

Rewrite the whole documentation

“The whole” might be a bit overkill, but that’s close to that. Everything has changed so radically that I need to re-check every modules and complete the documentation.

The documentation of pipeline.rs is especially missing. Documentation update will be tracked below.


  • shader/mod.rs
  • shader/program.rs
  • shader/stage.rs
  • blending.rs
  • buffer.rs
  • chain.rs
  • framebuffer.rs
  • lib.rs
  • linear.rs
  • pipeline.rs
  • pixel.rs
  • tess.rs
  • texture.rs
  • vertex.rs

Expanding Uniform’s types is hard

We cannot write a contramap implementation for Uniform, and our current semantics forbid us to implement Uniformable for arbritrary types.

Uniform blocks

Uniform blocks are blocks of uniform memory on the GLSL side. We need to find a way to pass struct stored in a Buffer to uniform blocks.

We need a two-level mapping to occur:

  1. we need to retrieve the index of the uniform block – which is kinda the same thing as uniform location
  2. we need to map that index to a binding point

When binding a uniform buffer, we need to bind it to a buffer index equals to the binding point. This is a bit tricky, yeah.

Get typed Program back

We need to introduce a RawProgram type (which is the current Program) so that we can have Program<T> where T is the type of the uniforms.

Unused uniform semantics

Sometimes – see hadronized/spectra#1 – it’d be great to set a uniform as unused, so that we don’t have to use it or remove it from the interface. The current implementation forbids that. We need to add a unused semantic to uniforms to build uniforms that will just do nothing when we try to update them.

Optimize pipeline allocation

When building a Pipeline and its objects, it’s very likely we’ll be needing heap allocation – because we expect slices.

Something interesting is that we could go even further by using an Iterator instead. This would enable us not to collect() the slices into a temporary Vec.

Renderable pixel formats

Geez, some of them are not! For instance, RGB8UI is not. nvidia is dumb to me because they implement a fallback to actually create RGBA8UI formatted targets.

State caching

We might need to add a new type to the public interface and a new way to manipulate objects, because we’ll need a context object.

What shall we do with shader languages?

Problem

We’re exposing the shader source code as String. I hate that. It’s opaque, it lives at runtime, it doesn’t scale (no cross-language support when writting shaders, no modules/libs). We have to find a way out of that situation

Solution 1 – EDSL via macros

Writing an EDSL would solve that in the most elegant way. The idea is to have a set of types, functions and macros to help creating shader stages directly in rust. Such work is partially implemented in the lsl branch.

Advantages

  • compile-time ;
  • because of being an IRL, we can write several compilers in backends to target shading languages ;
  • possibility to link inputs / outputs directly to the luminance pipeline, adding an extra, cool layer of type safety.

Drawbacks

  • tedious to maintain (rust macros) and hard to use (because of macros errors being a pain in the ass) ;
  • no direct shader hot reloading; even though I don’t really care about that, it might be a problem (can be fixed with hot recompilation with rustc though, so not really a problem, just something to know).

Solution 2 – DSL

We can have a DSL written in a language of our own. We would load it, parse it, compile it and turn it into a graphics executable at runtime. Exactly like we already do with GLSL.

Advantages

  • because of being an IRL, we can write several compilers in backends to target shading languages.

Drawbacks

  • runtime compilation (no static safety) ;
  • basically still String, so as opaque as the current ;
  • no way to link inputs / outputs ; doesn’t enhance the static safety of the pipeline.

Solution 3 – EDSL via compiler plugins

From the reading of this, it should be possible to use the compiler plugins power of rustc to create directly the EDSL in rustc itself.

Advantages

  • compile-time ;
  • because of being an IRL, we can write several compilers in backends to target shading languages ;
  • possibility to link inputs / outputs directly to the luminance pipeline, adding an extra, cool layer of type safety ;
  • seamless integration with Rust!

Drawbacks

  • might be used to maintain / write because it’s a nightly rustc extension ;
  • no feedback on that yet, so we’re going blind.

Which one to choose?

How to cull faces?

Hello, might it be possible to enable face culling? I am considering writing a game with luminance and face culling is a common and easy optimization.

I looked through the docs and source, but I didn't see any mention of this. A workaround I am using for a test/example introduces GL state and unsafe code, which isn't that preferable...

entry(|_| {
    pipeline(&screen, [0., 0., 0., 1.], |shade_gate| {
        shade_gate.shade(&shader, |render_gate, _| {
            render_gate.render(None, true, |tess_gate| {
                unsafe { gl::Enable(gl::CULL_FACE); }
                            
                tess_gate.render((&model).into());

                unsafe { gl::Disable(gl::CULL_FACE); }                            
            });
        });
    });
});

Thanks!

Shall we actually support the concept of “backends”?

Having several backends might have several nasty effects, like not providing the best of each graphics API or being too bloated at a time (no common denominator, for instance). See gfx for an example. What shall we do?

A nice idea I have for now: keep on going with OpenGL 3.3. When the crate is mature enough (soon I reckon), we’ll make luminance a very minimal common graphics API (like just fixing the type of Buffer, Framebuffer, Texture, Sampler, Stage, Program and uniforms), and all the backends will selectively add the specific code to use the raw library the best possible way.

Examples

Hi! I was wondering if there are any examples or tests that are publicly available. I understand most of the functionality of the library but am having some trouble putting it all together, which one or two examples would really help with. Even just a quick rundown of how to render out a simple triangle would be quite helpful.

Merge luminance-gl into luminance

This is a very complex subject since its implementation will have huge impacts on the design of the library and how to use it. I’ll try to explain my thoughts crystal clear.

A bit of history…

luminance was first written in Haskell as a safer OpenGL. The code is still there and as we can see, it’s backed by OpenGL directly. There are no backends. Because the main goal of it is to make OpenGL typesafe, stateless and great.

When I discovered Rust, I decided to migrate luminance from Haskell to Rust. That had two objectives:

  • learn Rust by migrating one of my “most interesting projects” in terms of software architecture
  • have it actually used in my demo

The second point is already fulfilled (see this and this). The first point was important and is also “done”. What comes as an extra feature with the Rust port is the introduction of backends. I thought that would be a great idea because it would introduce loose coupling between the API and the actual GPU driver in use (OpenGL, OpenGL ES, WebGL, vulkan, software, etc.).

As it turns out, with hindsight, I think it’s not a good idea anymore. I talked with several folks on IRC (kvark, nical, people I don’t know, etc.). Most of them don’t really understand where luminance is located on the abstraction layer. And it’s normal, because luminance (the Rust port) is located at several ones! That’s not something “bad”, but it’s confusing and most of all, it precludes me from implementing and designing the API to completely benefit from all of what the hardware provides. For instance, glium is almost the same thing as luminance (Haskell). gfx has several levels of abstractions and provides a backend approach.

So… The current state of luminance (Rust) puts the library exactly between glium and gfx, and it’s confusing, because no one needs such a weird abstracted library. Plus, even though the library is designed for implementing several backends… there’s only one for now: OpenGL 3.3. I wanted the “free” OpenGL version in terms of current hardware. OpenGL 3.0 is too low for certain features I need. OpenGL 4.5 is too high for the hardware support (no mobile version; no WebGL, etc.). OpenGL 3.3 is a pretty good match.

However, I like OpenGL 4.5 a lot. It has DSA and a lot of interesting features. The thing is: if I want to support OpenGL 4.5, I need to support it all. Benefit from its features. If I keep an abstract layer for “any OpenGL versions”, I’ve already lost, because I cannot design the API to use the best of a given version. And it’s even worse if we talk about completely different backends (OpenGL vs. whatever you use).

Practical problems

Among the problems that backend-driven library implies, are a few of them I really struggle with or get annoyed about:

  • because the core of the library lies in the luminance crate, we have to depend on this crate, but we also have to depend on the backend crate to have something actually doing render (luminance-gl for now) ; the number of uses explodes and some items must be imported from one crate but not the other – I really don’t like that. An effort could be made so that we only need to import the backend crate, but that’s boring
  • because of the design of that backend stuff, most objects are tagged with the backend type (for now, gl33::token::GL33. That is, even though I have the backend crate and the core crate in scope, I still need to use that backend type to select the correct trait from the core (because it relies on the backend type) ; I opened a RFC to fix that with trait aliasing, but it won’t get merged soon, so in the meantime, I have to do something about it
  • there’s only one backend (gl33) and because of the reasons discussed above, I really doubt there will ever be something else ; I plan to introduce Vulkan, but I’ll do that when it hits a correct hardware support, and then I’ll just basically replace OpenGL by Vulkan
  • we could get rid of several boring types and traits by directly implementing the safe versions of the objects to abstract over ; for now, because of the traits, a lot of code has to be parameterized with those traits (especially framebuffers and textures), and it goes very, very badly and annoying

All of this has made me come to the realization that it’s way too complicated and that luminance-gl should be merged into luminance, and die and disappear forever.

black textures

i created a very simple debug texture (32x32 Flat Dim2 RGBA8UI, no mipmaps), and i can read and write from it fine, but it always shows up black when i try to use it in my shader. other uniforms work just fine, and my uv coords are being passed to my fragment shader correctly.

uniforms:

pub struct SUniforms<'a: 'b, 'b> {
    pub view: Uniform<[u32; 2]>,
    pub tex: Uniform<&'b BoundTexture<'a, Texture<texture::Flat, texture::Dim2, pixel::RGBA8UI>>>,
}

render loop:

    window.draw(|| {
        entry(| gpu | { 
            pipe(&fb, [1.0, 0.0, 0.0, 1.0], |shader_gate| {
                shader_gate.shade(&program, |render, uniforms| {
                    // update uniforms
                    let tex = gpu.bind_texture(&tex);
                    uniforms.view.update(size);
                    uniforms.tex.update(&tex);    

                    // draw
                    render.render(alpha, |tess_gate| {
                        tess_gate.render((&tess).into());
                    })
                });
            });
        });
    });

renderdoc output

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.