the Fiddler's picture

[OpenGL] Deprecated methods - how to treat them?

This has been discussed before, but no conclusion was reached: how should OpenTK treat deprecated methods?

Possible approaches, sorted from least to most intrusive:

  1. Do nothing. (This is the current approach)
  2. Add a message to the method summary: "This method is deprecated." (Minimally intrusive, no compile-time warning)
  3. Mark methods with the ObsoleteAttribute. (Compilation warning, potential breakage with "Treat warnings as errors" option)
  4. Move methods to a different assembly, called OpenTK.Graphics.Deprecated. (Reduces OpenTK.dll size by 287KB (15%), see below for more details)
  5. [New] Create two OpenTK.dlls, one with deprecated functions, one without. The consumer picks one. (one-time decision, no breakage, however increases download size and makes OpenTK build-system / distribution slightly more complex. Idea provided by Mincus)

I have toyed with approach #4 with mixed results so far. Ideally, we would like to introduce minimal breaking changes to the consumer:

  1. Add a reference to OpenTK.Graphics.Deprecated.dll
  2. Add "using OpenTK.Graphics.Deprecated".
  3. Continue using OpenTK as before.

The question is how to implement this? One possible approach is to create a GL class in the deprecated namespace that inherits from OpenTK.Graphics.GL:

// OpenTK.dll
namespace OpenTK.Graphics
{
    public class GL { /* Non-deprecated methods */ }
}
 
// OpenTK.Graphics.Deprecated.dll
namespace OpenTK.Graphics.Deprecated
{
    public class GL : OpenTK.Graphics.GL { /* Deprecated methods */ }
 
}

The user can consume this as follows:

using OpenTK.Graphics;
using GL = OpenTK.Graphics.Deprecated.GL;
...
GL.ClearColor(Color.MidnightBlue);

Questions:

  1. Which approach (1-4) do you prefer? Why? (If you have another idea, please post it!)
  2. Do you consider the approach I outlined as too intrusive or complex? It requires a new reference and a single line of code to each source file that uses deprecated methods.
  3. Can you think of a better / simpler alternative to using GL = OpenTK.Graphics.Deprecated.GL?
  4. Do you know wheter C++/CLI, Boo, IronPython, IronRuby have anything equivalent to the "using" directive? F# and VB.Net have, so it's not a problem.

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Inertia's picture

1.1 & 1.2) Not a solution.
1.3) sounds interesting, individual warnings can be disabled so treating warnings as errors is not a problem. I've never tried this, but does it work to flag a single value in an enum as deprecated too? So for example calling GL.Enable( EnableCap.Light0 ); would generate a warning, but GL.Enable( EnableCap.DepthTest ); does not.
1.4) The flaws with this might be that it's non-obvious and it cannot eliminate deprecated tokens from enums. Do you have any numbers how much faster the GL ProcAddresses are loaded without the legacy stuff? But this certainly is a good idea too.

the Fiddler's picture

1.3) Indeed, you can mark individual enum values as Obsolete. Never thought of this before, but it opens up some interesting possibilities!

You can disable [Obsolete] warnings with #pragma warning disable 0681 or the "/nowarn:0681" compiler option. Unfortunately, there is no way to disable this warning for e.g. OpenTK.Graphics but leave it intact for your code, unless you manually pepper the code with "#pragma warning disable/restore".

In that light, 1.4 might be less annoying, as it's a "do and forget" solution.

1.4) Indeed, this requires more setup on the user side (1.3 is a mere warning by default, while 1.4 will cause cause compilation to fail unless you add the necessary declarations). It also adds the potential for future breakage, once more methods are marked as deprecated.

The cost of the extra assembly probably outweights the benefit of fewer GetProcAddress calls. I don't have exact numbers, but the number of deprecated functions is close to ~250 (judging from the size of the dll).

Inertia's picture

Something that has not yet been brought up: How do we deal with another situation where more deprecation occurs? For example Transform Feedback might become deprecated at some point in favor of OpenCL.

Our previous suggestions all consider only a separation between legacy GL and forward GL. Maybe in 5 years it will look like this:

legacy GL - OpenGL 1.0-3.0
forward GL - OpenGL 3.1-4.5
future GL - OpenGL 5.0-x.y

Mincus's picture

There's the clunky #define method similar to Microsoft's CRT deprecation stuff from C/C++ that is a possibility.
I've not played with #defines much in C#, but I suspect something similar could be knocked up so that there was a warning at compile time from both individual enum values and functions.
To disable the warnings, a user would just put #define use_opentk_deprecated or something similar in the files that use those calls.

Another option with splitting it out is that instead of creating a seperate OpenTK.Graphics.Deprecated, perhaps create two binaries, one with ALL the calls (current and deprecated) and one with just the current set of calls in.
The user would just need to switch to the deprecated DLL and nothing else.

Both of these would, imo, cause minimum interference, but they feel rather crude in some ways and doubtless have other issues.
Just throwing ideas out. :o)

the Fiddler's picture

As far as I can see, the lifetime of a method in the deprecation model looks like this:

  1. Vendor-specific extension (ATI, NV, APPLE, ...)
  2. Cross-vendor extension (EXT)
  3. ARB-blessed extension (ARB - likely to become core in next revision)
  4. Core
  5. Deprecated
  6. Extension (implemented at the disrcetion of each vendor)
  7. Dead (no vendor implements)

Now, we have two options: we can say that OpenTK will always track the latest version of the specs, treating the first four categories as core and the rest as deprecated (whatever we decide that "deprecated" means for us); or we can move OpenTK.Graphics outside of OpenTK and have several versions side by side:

  • OpenTK.dll (contains, math, input and platform-specific functions)
  • OpenTK.Graphics31.dll (tracks OpenGL 3.1)
  • OpenTK.Graphics32.dll (tracks OpenGL 3.2)

When you create a new application, you pick a target OpenGL version and stick with that, without fear that a future spec revision (and subsequent OpenTK release) might offer a different OpenGL API and break your code.

Edit:

Mincus wrote:

Both of these would, imo, cause minimum interference, but they feel rather crude in some ways and doubtless have other issues.
Just throwing ideas out. :o)

Keep 'em coming.

Unfortunately (or fortunately, depending on your point of view) .Net interfaces cannot be influenced by the consuming application. This means that the OpenTK.Graphics API is fixed as soon as OpenTK is compiled. If you need two different APIs (e.g. one with deprecated functions, one without), you have to use conditional compilation on the OpenTK side and allow the consuming application to pick which of the dlls it prefers.

This only leaves your "two binaries" model as an option, which indeed works. This is somewhat similar to model 1.4, with the difference that you have two complete dlls (one with deprecated functions, one without) and the consumer picks one. Model 1.4, on the other hand, offers one complete dll (without deprecated functions) and one "addon" (for the deprecated ones) and the consumer uses either the first or both.

Your idea is superior, in that the user can pick the correct dll and never need to change a single line of code. The cost is a more complicated compilation model for OpenTK (needs two passes, one for each binary, with different options) and slightly increased download size.

Inertia's picture

It's hard to make a call here, on one side I'd like to see the legacy stuff move to it's own .dll and R.I.P., but as long as enums still contain legacy tokens the split is not complete (see GL.Enable example above). #define or [Obsolete] both can deal with enums much better. #define probably results in the lightest .dll after building OpenTK and can handle future deprecations better than [Obsolete] or creating a 3rd .dll, however it will turn GL.cs from inconvenient-legible to painfully-legible.

the Fiddler's picture

I'd say GL.cs left the inconvenient-legible state a few revisions ago, when it gained documentation and debugging functionality. As of right now, it weights at 5727KB of source and has a 50% chance to make Visual Studio hang. Better check GLDelegates.cs instead, it contains the same information but it's much more manageable.

Inertia's picture

Well if that implies you have no objections about #defines, why not? IIRC it was one of the very first suggestions how to handle the problem, but I don't remember why it was abandoned.
It would also have the advantage that all CLS compliant overloads could be tied to a conditional, so you can compile OpenTK to use uint everywhere and only contain GL 3.1 functions&token. (No intent to start another CLS-Compliance usefulness discussion, but I would not object adding this manually to OpenTK.Audio to reduce overload choices)

the Fiddler's picture

No objections on that front. I just wish to gather more information before committing to a solution.

I will push this functionality to trunk shortly, so we can play with actual code instead of theory.

enablerbr's picture

this is probably selfish of me. as i'm new to OpenGL and would like to start just coding for OpenGL3.0/3.1. it would make my life easier if all i had to call was say a OpenGL v3.+ dll. so that i would only be dealing with v3.+. therefor less likely to get confused. hopefully lesson the learning process at the same time as i wouldn't be using previous OpenGL stuff by mistake.