-
Notifications
You must be signed in to change notification settings - Fork 37
Notes for Contributors
I've been programming on and off for over 30 years, but I am relatively new to Java and to rendering. I tend to start with experiments and then refactor heavily. I also tend to adapt my style and approach to the situation or even to my mood that day. I am doing this for fun and motivation matters.
It's also important to remember that Canvas is a work in progress and changing rapidly. I tend to leave more "noise" in code that is still experimental or likely to be replaced.
The flip side of #1 is that I'm relatively flexible. If something bothers you and there is some practical benefit to changing it, there's a good chance I'll accept your PR and appreciate you for making Canvas better. For code you are contributing, I care mostly that it is clear, performant and functional. However, see Contribution Guidelines, below.
I will frequently stop and re-evaluate the problem space and alternative approaches, including some that may seem ridiculous or impractical at the outset. I appreciate contrary and radical points of view when they lead to better idea and outcomes. For me, this is never personal. I keep it respectful and avoid making personal attacks. I expect that will also. If you find it difficult to maintain that sort of detachment, then this may not be the project for you.
Debate is useful; shipping code is satisfying. I learn by doing, and a working, viable implementation beats a hypothetical solution. (Until that solution can be implemented!)
Point is: I tend to switch between Hmmm, what is best? and DO IT NOW! frequently and without notice. This isn't something I can change.
I don't have many rules, but here they are.
-
By contributing, you grant perpetual and non-exclusive rights under the Apache 2.0 license agreement.
-
All contributions must be your own or covered under the next rule.
-
Many rendering problems have readily adaptable code available via a compatible license. If such code is not available in a library appropriate for inclusion, then excerpts or adaptations may be contributed with appropriate attribution and disclosures, per the source license.
-
Smaller and more frequent PRs are generally preferable to big PRs that take a long time because the code base can change rapidly. "Dark launch" WIP commits will be considered for this reason - just don't break anything.
-
If you know you'll be working on a big feature for an extended time, try to create isolation from the rest of the codebase via interfaces, possibly with some reorganization. Preliminary refactoring to prepare for such efforts is absolutely OK.
-
Use
assertwhere units tests aren't viable (they usually aren't) and run your JVM in dev with the -ea parameter. This is mainly useful for catching race conditions and as a safety net when reasoning about complex state that is concurrently maintained.
Performance is always a constraint in rendering code. The following practices are recommended:
Modern processors are so fast that memory bandwidth is almost the only thing that matters. Prevent cache misses and maximize register usage at almost any cost.
LOR is a big topic but in simplest terms this means using primitive types and arrays of primitive types with minimal indirection. FastUtil collections are usually a decent choice.
Because the main client thread owns the GL context, it is (mostly) the only thread that can do anything render related. For our purposes, it is the scarce resource.
Ideally we want the client thread to be preparing for draw calls and waiting for draw calls to complete, and nothing else.
Many optimizations vary in effectiveness with different hardware/software configurations. Measure performance via instrumentation and/or VisualVM and also provide a way to turn it off.
Also highly variable and situational. For an introduction to the concerns, see this stackexchange thread
Use minimal scoping (private when possible), final members, and concrete types. For API implementations, keep interface type hierarchies clean and avoid multiple implementations. Obviously static methods can help here but work best when the parameters are primitive types.
Another big topic and highly situational. Atomics can help but come with their own costs and limitations. Maintaining per-thread mutable data with immutable shared data is very good when workable.