LWJGL / lwjgl3

LWJGL is a Java library that enables cross-platform access to popular native APIs useful in the development of graphics (OpenGL, Vulkan, bgfx), audio (OpenAL, Opus), parallel computing (OpenCL, CUDA) and XR (OpenVR, LibOVR, OpenXR) applications.
https://www.lwjgl.org
BSD 3-Clause "New" or "Revised" License
4.84k stars 642 forks source link

Better javadoc for constants & enums #707

Open SWinxy opened 2 years ago

SWinxy commented 2 years ago

Description

public static final ints (enums) are documented in an unusual way, with the documentation listing everything before the actual enums in code. Instead, the documentation should be paired with each instance. This may have the side effect of much bigger source files. This would help in APIs like Vulkan, which has a lot of enums.

/**
 * Documentation
 * enum1 - description
 * enum2 - description
 * enum3 - description
**/
public static final int
    ENUM1 = 1
    ENUM2 = 2
    ENUM3 = 3;

/**
 * Documentation
 * enum 1 - description
**/
public static final int ENUM1 = 1;
/**
 * Documentation
 * enum 2 - description
**/
public static final int ENUM2 = 2;
/**
 * Documentation
 * enum 3 - description
**/
public static final int ENUM3 = 3;
Spasi commented 2 years ago

Hey @SWinxy,

The enum javadoc in LWJGL is indeed unusual, but it was created this way intentionally. The reason is simple, it is very useful to be able to see what other options you have available when looking up the documentation of a particular symbol. Especially for bitfield enums that can be ORed together. Since native constants are mapped to plain primitive constants in Java, there is no other way to group related constants together.

That is just my opinion of course and I would like to hear any counterarguments.

The long-term dream is to be able to map native enums to Valhalla primitive types. We would gain type-safety (e.g. you would not be able to pass an arbitrary int where a VkImageType is expected) and there will be more flexibility with javadoc (e.g. generic documentation on the primitive type definition, per-constant documentation on each "instance" of the primitive type).

ws909 commented 2 years ago

The long-term dream is to be able to map native enums to Valhalla primitive types. We would gain type-safety (e.g. you would not be able to pass an arbitrary int where a VkImageType is expected) and there will be more flexibility with javadoc (e.g. generic documentation on the primitive type definition, per-constant documentation on each "instance" of the primitive type).

Unless operator overloading is also added to Java (blog advocating for it at Oracle's own website), including overloading bit-wise operators, what is the plan (not dream) for integer enums? It would be great to have them implemented as enums in Java, but this is not doable, even with enhanced enums, as VK extensions can extend the basic enums. A second-best option is to gain type-safety through primitive classes, but I would absolutely hate to work with these, if I must first unwrap the int value they contain, to perform bit-wise arithmetic.

Spasi commented 2 years ago

Hey @ws909,

You cannot OR together Java enums either, right? (without unwrapping, or custom-code, or some kind of EnumSet usage)

Anyway, it's a valid concern. We'll have to wait and see Valhalla in practice, then try to find a solution that's both convenient and zero-overhead. For example, it would be interesting if code like this:

public static VkAccessFlagBits or(VkAccessFlagBits... bits) {
    int value = 0;
    for (var bit : bits) {
        value |= bit.value;
    }
    return VkAccessFlagBits.of(value);
}

is guaranteed to not allocate anything.

ws909 commented 2 years ago

Hello @Spasi,

No, sadly you can't use bit-wise operators on Java enums, And the EnumSet is very nice to set up a contract of when a single bit, and multiple bits, are allowed, but it's very verbose; extremely much so. And even if they would be grouped into Java enums, you'd have to include all the extension values right away; it's clutter, and it's harder to intuitively tell apart which enum values are available for use.

Your example actually does look better than what I had in mind, but still, it does seem a little bit over the top. If only Java had typedefs, though a primitive class with a single value is likely similar enough to that.

But what has worried me, is that for value classes, C1 will treat these as regular heap objects, while only C2 will use an optimized representation of them. This may change in the future, as the JEP explicitly states that this is an implementation detail, and not an API contract. And this is a problem, because primitive classes, will be treated as value objects in C1. This likely means little performance gain at all, in C1, as of today.

ws909 commented 1 year ago

That is just my opinion of course and I would like to hear any counterarguments.

There's no need to take this into consideration for LWJGL 3, but for native enums, especially those in Vulkan with lots of values, I find it great that they're all documented in the same spot. It provides an easy and very useful overview. It's a delight. What's less nice, is that when I look up an enum value in my source code (I do this very often), and this is not the first value in the enum (0), I can't easily discover the documentation. I have to either jump to the source file to find the first one, or navigate through the IDE's list of suggested auto-completions until I find the right one. If all enum values had a link to the first one (which the documentation is attached to), this would be much easier.

Spasi commented 1 year ago

Hey @ws909,

What IDE do you use? Do you attach sources & javadoc when you setup LWJGL in the IDE, or just the source?

For reference, I use IntelliJ and attach sources only. Invoking javadoc for any enum value displays the javadoc for the entire enum, the same as doing it for the first enum value. This is the expected behavior and no links are necessary.

ws909 commented 1 year ago

IntelliJ IDEA. Pre-built binaries and source (no generated JavaDoc included (I think, at the moment)). It’s really just the simplest solution. Download folder from lwjgl.org, IDEA’s add local library, rest is automatic.

It’s an Ant project managed by IntelliJ.

TheMrMilchmann commented 1 year ago

I can confirm that IDEA 2022.3.3 (Build #IU-223.8836.41) does not reliably display the JavaDoc for the entire enum currently. For example, let's look at the AL_FORMAT_* enum. It is defined as follows:

/** Buffer formats. */
public static final int
    AL_FORMAT_MONO8    = 0x1100,
    AL_FORMAT_MONO16   = 0x1101,
    AL_FORMAT_STEREO8  = 0x1102,
    AL_FORMAT_STEREO16 = 0x1103;

When using AL_FORMAT_MONO8 from a different file, the quick documentation popup looks as expected:

image

However, when using AL_FORMAT_MONO16, it is not:

image

Interestingly, this issue does not occur when activating the quick documentation popup when looking at the source of the definition (EDIT: this discrepancy does seem to stem from IntelliJ sourcing the documentation from the source file instead of the generated JavaDoc here):

image

As can be seen above, I'm using Gradle here for dependency management but I suspect that similar things could happen with other setups. To me, this surely looks like an IntelliJ bug. I did not check YouTrack yet though.

Spasi commented 1 year ago

@TheMrMilchmann does Gradle download the pre-generated javadoc and is it attached to the LWJGL library inside IntelliJ? What happens if you detach it and let IntelliJ generate javadoc from sources on-the-fly, is the issue fixed?

TheMrMilchmann commented 1 year ago

@Spasi After double-checking, I don't think Gradle automatically downloads and attaches the JavaDoc. The project view seems to confirm this assumption.

image

Spasi commented 1 year ago

OK, I can reproduce this too in a separate project with LWJGL artifacts. However, inside the LWJGL project, in demo code for example, it works fine, I get javadoc for all enum values.