We closed this forum 18 June 2010. It has served us well since 2005 as the ALPHA forum did before it from 2002 to 2005. New discussions are ongoing at the new URL http://forum.processing.org. You'll need to sign up and get a new user account. We're sorry about that inconvenience, but we think it's better in the long run. The content on this forum will remain online.
IndexProcessing DevelopmentCore,  Processing Development Environment (PDE) › Avoiding RendererChangeException (PATCH-0148)
Page Index Toggle Pages: 1
Avoiding RendererChangeException (PATCH-0148) (Read 2744 times)
Avoiding RendererChangeException (PATCH-0148)
Aug 22nd, 2008, 5:15am
 
The Contribute page leads to a FAQ indicating source code contributors should come to Discourse. I apologize if this should go somewhere else.

The change to use RendererChangeException, to change renderers in response to the renderer argument of size(), between Processing 0135 and 0148 caused grief to a project of mine that uses JRuby to teach Processing, Java, and Ruby simultaneously (more to come on that later).

While analyzing the issue, I realized Processing currently uses an exception in a non-exceptional way to change renderers. I created a patch that allows Processing to change renderers without using an exception.

The patch does this by giving PGraphics the ability to request a renderer change and making slight modifications to PApplet to ask PGraphics if it desires a renderer change so that PApplet can run setup() again if needed.

This change would:

1. Get rid of the unexceptional use of exceptions and the need for a RendererChangeException.
2. Improve sketch startup time slightly when it requires a renderer change.
3. Solve my problem so I do not have to maintain a patch to Processing source in my project.

I inlined the Subversion diff below. Please consider it for the next Processing release and let me know if you would like me to put it somewhere else or communicate it in a form easier for you to incorporate into the Processing source.


Index: opengl/src/processing/opengl/PGraphicsOpenGL.java
===================================================================
--- opengl/src/processing/opengl/PGraphicsOpenGL.java
(revision 4275)
+++ opengl/src/processing/opengl/PGraphicsOpenGL.java
(working copy)
@@ -252,7 +252,7 @@
        context.destroy();
        context = null;
        allocate();
-        throw new PApplet.RendererChangeException();
+        requestRendererChange();
      }
    } else if (which == ENABLE_OPENGL_4X_SMOOTH) {
      if (!opengl4X) {
@@ -263,7 +263,7 @@
        context.destroy();
        context = null;
        allocate();
-        throw new PApplet.RendererChangeException();
+        requestRendererChange();
      }
    }
  }
Index: core/src/processing/core/PGraphics.java
===================================================================
--- core/src/processing/core/PGraphics.java
(revision 4275)
+++ core/src/processing/core/PGraphics.java
(working copy)
@@ -612,8 +612,28 @@
  /// Number of V steps (aka "phi") along latitudinally top-to-bottom spanning pi
  public int sphereDetailV = 0;

+  // Tracks if graphics wants renderer change.
+  protected boolean rendererChange = false;

+  /**
+   * Advises graphics to request a renderer change.
+   */
+  protected void requestRendererChange() {
+    rendererChange = true;
+  }

+  /**
+   * Returns true if graphics requests a renderer change. Graphics
+   * will only request a single renderer change per call to
+   * requestRenderChange().
+   */
+  protected boolean requestsRendererChange() {
+    boolean request = rendererChange;
+    rendererChange = false;
+    return request;
+  }
+
+
  //////////////////////////////////////////////////////////////

  // INTERNAL
Index: core/src/processing/core/PApplet.java
===================================================================
--- core/src/processing/core/PApplet.java
(revision 4275)
+++ core/src/processing/core/PApplet.java
(working copy)
@@ -277,15 +277,6 @@
  static public final int MIN_WINDOW_HEIGHT = 128;

  /**
-   * Exception thrown when size() is called the first time.
-   * <P>
-   * This is used internally so that setup() is forced to run twice
-   * when the renderer is changed. This is the only way for us to handle
-   * invoking the new renderer while also in the midst of rendering.
-   */
-  static public class RendererChangeException extends RuntimeException { }
-
-  /**
   * true if no size() command has been executed. This is used to wait until
   * a size has been set before placing in the window and showing it.
   */
@@ -1014,7 +1005,7 @@
      // but with a properly sized render
      // this is for opengl, which needs a valid, properly sized
      // display before calling anything inside setup().
-      throw new RendererChangeException();
+      g.requestRendererChange();
    }
  }

@@ -1372,14 +1363,11 @@
      long now = System.nanoTime();

      if (frameCount == 0) {
-        try {
-          //println("Calling setup()");
-          setup();
-          //println("Done with setup()");
+        setup();
+        if (g.requestsRendererChange()) {

-        } catch (RendererChangeException e) {
-          // Give up, instead set the new renderer and re-attempt setup()
-          return;
+            // Give up, instead set the new renderer and re-attempt setup()
+            return;
        }
        this.defaultSize = false;

Re: Avoiding RendererChangeException (PATCH-0148)
Reply #1 - Aug 22nd, 2008, 10:34pm
 
The issue is that when the renderer changes, setup() needs to exit immediately, otherwise all of the code inside setup() will be run twice. That can be a significant amount, because setup() is used to load images, etc. An exception allows us to exit the setup() method.
Re: Avoiding RendererChangeException (PATCH-0148)
Reply #2 - Aug 23rd, 2008, 7:02am
 
Good point. I went back to the drawing board, but without breaking the existing API expectation of calling size() in setup, you cannot stop forward execution of setup() in a safe manner without excepting.

Why did you have to improve Processing's exception handling by catching a more specific exception instead of catching RuntimeException and checking its message as in 0135? Smiley

To explain, by happy coincidence, when you implement setup() in JRuby and receive an exception out of a Java call (to size()) that it does not rescue, JRuby wraps the exception with an org.jruby.exceptions.RaiseException, which extends RuntimeException, and rethrows it. In 0135, this worked nicely. In 0148, not so much.

JRuby Java integration ticket for reference:

http://jira.codehaus.org/browse/JRUBY-1300

I think I will have to maintain an alternative patch against Processing for my project until the JRuby Java exception pass-through issue gets resolved. To keep the patch at a minimum, I cheated and introduced the pattern of 0135 back into 0148 as a secondary catch while keeping the RendererChangeException pattern in place. Catching RuntimeException rather than JRuby's exception, though less specific, avoids a compile-time dependency on JRuby:

Index: core/src/processing/core/PApplet.java                                    
===================================================================            
--- core/src/processing/core/PApplet.java (revision 4275)
+++ core/src/processing/core/PApplet.java (working copy)
@@ -1378,8 +1378,21 @@                                                          
          //println("Done with setup()");
                                                                               
        } catch (RendererChangeException e) {                                  
+
          // Give up, instead set the new renderer and re-attempt setup()      
          return;                                                              
+
+        // Catches on JRuby RaisedException when setup() implemented in JRuby  
+        // and size() called with a different renderer until resolution of    
+        // http://jira.codehaus.org/browse/JRUBY-1300                          
+        } catch (RuntimeException e) {                                        
+          String message = e.getMessage();
+          String name = RendererChangeException.class.getName();
+          if (message.matches(name)) {
+
+            // Give up, instead set the new renderer and re-attempt setup()
+            return;
+          }
        }
        this.defaultSize = false;

I appreciate you considering the original patch proposal. Thanks for your work on Processing.
Re: Avoiding RendererChangeException (PATCH-0148)
Reply #3 - Aug 27th, 2008, 7:12pm
 
smith wrote on Aug 23rd, 2008, 7:02am:
Why did you have to improve Processing's exception handling by catching a more specific exception instead of catching RuntimeException and checking its message as in 0135 Smiley

Because otherwise I get accused of the code being a hack, like in your original post. Wink

The main thing is being able to cleanly just grab that one exception. It's still a RuntimeException (subclass), however--I don't quite follow why it's handled differently by JRuby.

The catch/check message/rethrow was ugly, and messed with the stack frames a bit because of the re-throw, to make it look like it was coming inside run() instead of inside the code. It was an ugly hack that was nice to get rid of, but I'd rather you not have to keep patching just to keep the JRuby bridge working.
Re: Avoiding RendererChangeException (PATCH-0148)
Reply #4 - Oct 21st, 2008, 8:09am
 
+1 on this; it makes it difficult to use 0152 + OpenGL with JRuby. But, I understand we're in the midst of change. I can be patient.
Page Index Toggle Pages: 1