Android: Allow construction of GlTextureFrameBuffer from non-OpenGL thread

This CL makes it possible to create a GlTextureFrameBuffer from any
thread. The actual GL resources will be allocated the first time
setSize() is called. The purpose is to be able to use 'final' variables
more often for this class and avoid @Nullable annotations.

Bug: None
Change-Id: I350304bcd33fd674990254df37a615995972f322
Reviewed-on: https://webrtc-review.googlesource.com/69241
Commit-Queue: Magnus Jedvert <magjed@webrtc.org>
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22835}
diff --git a/sdk/android/api/org/webrtc/EglRenderer.java b/sdk/android/api/org/webrtc/EglRenderer.java
index c6e9812..dccd111 100644
--- a/sdk/android/api/org/webrtc/EglRenderer.java
+++ b/sdk/android/api/org/webrtc/EglRenderer.java
@@ -131,7 +131,8 @@
   private long renderSwapBufferTimeNs;
 
   // Used for bitmap capturing.
-  @Nullable private GlTextureFrameBuffer bitmapTextureFramebuffer;
+  private final GlTextureFrameBuffer bitmapTextureFramebuffer =
+      new GlTextureFrameBuffer(GLES20.GL_RGBA);
 
   private final Runnable logStatisticsRunnable = new Runnable() {
     @Override
@@ -233,10 +234,7 @@
           drawer = null;
         }
         frameDrawer.release();
-        if (bitmapTextureFramebuffer != null) {
-          bitmapTextureFramebuffer.release();
-          bitmapTextureFramebuffer = null;
-        }
+        bitmapTextureFramebuffer.release();
         if (eglBase != null) {
           logD("eglBase detach and release.");
           eglBase.detachCurrent();
@@ -637,9 +635,6 @@
         continue;
       }
 
-      if (bitmapTextureFramebuffer == null) {
-        bitmapTextureFramebuffer = new GlTextureFrameBuffer(GLES20.GL_RGBA);
-      }
       bitmapTextureFramebuffer.setSize(scaledWidth, scaledHeight);
 
       GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, bitmapTextureFramebuffer.getFrameBufferId());
diff --git a/sdk/android/api/org/webrtc/GlTextureFrameBuffer.java b/sdk/android/api/org/webrtc/GlTextureFrameBuffer.java
index 164dff2..b906fe5 100644
--- a/sdk/android/api/org/webrtc/GlTextureFrameBuffer.java
+++ b/sdk/android/api/org/webrtc/GlTextureFrameBuffer.java
@@ -15,13 +15,13 @@
 /**
  * Helper class for handling OpenGL framebuffer with only color attachment and no depth or stencil
  * buffer. Intended for simple tasks such as texture copy, texture downscaling, and texture color
- * conversion.
+ * conversion. This class is not thread safe and must be used by a thread with an active GL context.
  */
 // TODO(magjed): Add unittests for this class.
 public class GlTextureFrameBuffer {
-  private final int frameBufferId;
-  private final int textureId;
   private final int pixelFormat;
+  private int frameBufferId;
+  private int textureId;
   private int width;
   private int height;
 
@@ -39,16 +39,8 @@
       default:
         throw new IllegalArgumentException("Invalid pixel format: " + pixelFormat);
     }
-
-    // Create texture.
-    textureId = GlUtil.generateTexture(GLES20.GL_TEXTURE_2D);
     this.width = 0;
     this.height = 0;
-
-    // Create framebuffer object.
-    final int frameBuffers[] = new int[1];
-    GLES20.glGenFramebuffers(1, frameBuffers, 0);
-    frameBufferId = frameBuffers[0];
   }
 
   /**
@@ -57,7 +49,7 @@
    * least once before using the framebuffer. May be called multiple times to change size.
    */
   public void setSize(int width, int height) {
-    if (width == 0 || height == 0) {
+    if (width <= 0 || height <= 0) {
       throw new IllegalArgumentException("Invalid size: " + width + "x" + height);
     }
     if (width == this.width && height == this.height) {
@@ -65,6 +57,15 @@
     }
     this.width = width;
     this.height = height;
+    // Lazy allocation the first time setSize() is called.
+    if (textureId == 0) {
+      textureId = GlUtil.generateTexture(GLES20.GL_TEXTURE_2D);
+    }
+    if (frameBufferId == 0) {
+      final int frameBuffers[] = new int[1];
+      GLES20.glGenFramebuffers(1, frameBuffers, 0);
+      frameBufferId = frameBuffers[0];
+    }
 
     // Allocate texture.
     GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
@@ -96,10 +97,12 @@
     return height;
   }
 
+  /** Gets the OpenGL frame buffer id. This value is only valid after setSize() has been called. */
   public int getFrameBufferId() {
     return frameBufferId;
   }
 
+  /** Gets the OpenGL texture id. This value is only valid after setSize() has been called. */
   public int getTextureId() {
     return textureId;
   }
@@ -110,7 +113,9 @@
    */
   public void release() {
     GLES20.glDeleteTextures(1, new int[] {textureId}, 0);
+    textureId = 0;
     GLES20.glDeleteFramebuffers(1, new int[] {frameBufferId}, 0);
+    frameBufferId = 0;
     width = 0;
     height = 0;
   }