diff --git a/demos/gl/src/main/java/androidx/media3/demo/gl/BitmapOverlayVideoProcessor.java b/demos/gl/src/main/java/androidx/media3/demo/gl/BitmapOverlayVideoProcessor.java index cf3945be12..33e44323a3 100644 --- a/demos/gl/src/main/java/androidx/media3/demo/gl/BitmapOverlayVideoProcessor.java +++ b/demos/gl/src/main/java/androidx/media3/demo/gl/BitmapOverlayVideoProcessor.java @@ -27,6 +27,7 @@ import android.graphics.drawable.BitmapDrawable; import android.opengl.GLES20; import android.opengl.GLUtils; import androidx.media3.common.C; +import androidx.media3.common.util.GlProgram; import androidx.media3.common.util.GlUtil; import java.io.IOException; import java.util.Locale; @@ -50,7 +51,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private final Bitmap logoBitmap; private final Canvas overlayCanvas; - private GlUtil.@MonotonicNonNull Program program; + private @MonotonicNonNull GlProgram program; private float bitmapScaleX; private float bitmapScaleY; @@ -78,7 +79,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; public void initialize() { try { program = - new GlUtil.Program( + new GlProgram( context, /* vertexShaderFilePath= */ "bitmap_overlay_video_processor_vertex.glsl", /* fragmentShaderFilePath= */ "bitmap_overlay_video_processor_fragment.glsl"); @@ -117,7 +118,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; GlUtil.checkGlError(); // Run the shader program. - GlUtil.Program program = checkNotNull(this.program); + GlProgram program = checkNotNull(this.program); program.setSamplerTexIdUniform("uTexSampler0", frameTexture, /* unit= */ 0); program.setSamplerTexIdUniform("uTexSampler1", textures[0], /* unit= */ 1); program.setFloatUniform("uScaleX", bitmapScaleX); diff --git a/libraries/common/src/main/java/androidx/media3/common/util/GlProgram.java b/libraries/common/src/main/java/androidx/media3/common/util/GlProgram.java new file mode 100644 index 0000000000..681ec57433 --- /dev/null +++ b/libraries/common/src/main/java/androidx/media3/common/util/GlProgram.java @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.common.util; + +import static androidx.media3.common.util.Assertions.checkNotNull; + +import android.content.Context; +import android.opengl.GLES11Ext; +import android.opengl.GLES20; +import androidx.annotation.Nullable; +import java.io.IOException; +import java.nio.Buffer; +import java.util.HashMap; +import java.util.Map; + +/** + * Represents a GLSL shader program. + * + *
After constructing a program, keep a reference for its lifetime and call {@link #delete()} (or
+ * release the current GL context) when it's no longer needed.
+ */
+@UnstableApi
+public final class GlProgram {
+
+ // https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_YUV_target.txt
+ private static final int GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT = 0x8BE7;
+ /** The identifier of a compiled and linked GLSL shader program. */
+ private final int programId;
+
+ private final Attribute[] attributes;
+ private final Uniform[] uniforms;
+ private final Map This involves slow steps, like compiling, linking, and switching the GL program, so do not
+ * call this in fast rendering loops.
+ *
+ * @param vertexShaderGlsl The vertex shader program.
+ * @param fragmentShaderGlsl The fragment shader program.
+ */
+ public GlProgram(String vertexShaderGlsl, String fragmentShaderGlsl) {
+ programId = GLES20.glCreateProgram();
+ GlUtil.checkGlError();
+
+ // Add the vertex and fragment shaders.
+ addShader(programId, GLES20.GL_VERTEX_SHADER, vertexShaderGlsl);
+ addShader(programId, GLES20.GL_FRAGMENT_SHADER, fragmentShaderGlsl);
+
+ // Link and use the program, and enumerate attributes/uniforms.
+ GLES20.glLinkProgram(programId);
+ int[] linkStatus = new int[] {GLES20.GL_FALSE};
+ GLES20.glGetProgramiv(programId, GLES20.GL_LINK_STATUS, linkStatus, /* offset= */ 0);
+ if (linkStatus[0] != GLES20.GL_TRUE) {
+ GlUtil.throwGlException(
+ "Unable to link shader program: \n" + GLES20.glGetProgramInfoLog(programId));
+ }
+ GLES20.glUseProgram(programId);
+ attributeByName = new HashMap<>();
+ int[] attributeCount = new int[1];
+ GLES20.glGetProgramiv(programId, GLES20.GL_ACTIVE_ATTRIBUTES, attributeCount, /* offset= */ 0);
+ attributes = new Attribute[attributeCount[0]];
+ for (int i = 0; i < attributeCount[0]; i++) {
+ Attribute attribute = Attribute.create(programId, i);
+ attributes[i] = attribute;
+ attributeByName.put(attribute.name, attribute);
+ }
+ uniformByName = new HashMap<>();
+ int[] uniformCount = new int[1];
+ GLES20.glGetProgramiv(programId, GLES20.GL_ACTIVE_UNIFORMS, uniformCount, /* offset= */ 0);
+ uniforms = new Uniform[uniformCount[0]];
+ for (int i = 0; i < uniformCount[0]; i++) {
+ Uniform uniform = Uniform.create(programId, i);
+ uniforms[i] = uniform;
+ uniformByName.put(uniform.name, uniform);
+ }
+ GlUtil.checkGlError();
+ }
+
+ private static void addShader(int programId, int type, String glsl) {
+ int shader = GLES20.glCreateShader(type);
+ GLES20.glShaderSource(shader, glsl);
+ GLES20.glCompileShader(shader);
+
+ int[] result = new int[] {GLES20.GL_FALSE};
+ GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, result, /* offset= */ 0);
+ if (result[0] != GLES20.GL_TRUE) {
+ GlUtil.throwGlException(GLES20.glGetShaderInfoLog(shader) + ", source: " + glsl);
+ }
+
+ GLES20.glAttachShader(programId, shader);
+ GLES20.glDeleteShader(shader);
+ GlUtil.checkGlError();
+ }
+
+ private static int getAttributeLocation(int programId, String attributeName) {
+ return GLES20.glGetAttribLocation(programId, attributeName);
+ }
+
+ /** Returns the location of an {@link Attribute}. */
+ private int getAttributeLocation(String attributeName) {
+ return getAttributeLocation(programId, attributeName);
+ }
+
+ private static int getUniformLocation(int programId, String uniformName) {
+ return GLES20.glGetUniformLocation(programId, uniformName);
+ }
+
+ /** Returns the location of a {@link Uniform}. */
+ public int getUniformLocation(String uniformName) {
+ return getUniformLocation(programId, uniformName);
+ }
+
+ /**
+ * Uses the program.
+ *
+ * Call this in the rendering loop to switch between different programs.
+ */
+ public void use() {
+ // TODO(b/214975934): When multiple GL programs are supported by Transformer, make sure
+ // to call use() to switch between programs.
+ GLES20.glUseProgram(programId);
+ GlUtil.checkGlError();
+ }
+
+ /** Deletes the program. Deleted programs cannot be used again. */
+ public void delete() {
+ GLES20.glDeleteProgram(programId);
+ GlUtil.checkGlError();
+ }
+
+ /**
+ * Returns the location of an {@link Attribute}, which has been enabled as a vertex attribute
+ * array.
+ */
+ public int getAttributeArrayLocationAndEnable(String attributeName) {
+ int location = getAttributeLocation(attributeName);
+ GLES20.glEnableVertexAttribArray(location);
+ GlUtil.checkGlError();
+ return location;
+ }
+
+ /** Sets a float buffer type attribute. */
+ public void setBufferAttribute(String name, float[] values, int size) {
+ checkNotNull(attributeByName.get(name)).setBuffer(values, size);
+ }
+
+ /** Sets a texture sampler type uniform. */
+ public void setSamplerTexIdUniform(String name, int texId, int unit) {
+ checkNotNull(uniformByName.get(name)).setSamplerTexId(texId, unit);
+ }
+
+ /** Sets a float type uniform. */
+ public void setFloatUniform(String name, float value) {
+ checkNotNull(uniformByName.get(name)).setFloat(value);
+ }
+
+ /** Sets a float array type uniform. */
+ public void setFloatsUniform(String name, float[] value) {
+ checkNotNull(uniformByName.get(name)).setFloats(value);
+ }
+
+ /** Binds all attributes and uniforms in the program. */
+ public void bindAttributesAndUniforms() {
+ for (Attribute attribute : attributes) {
+ attribute.bind();
+ }
+ for (Uniform uniform : uniforms) {
+ uniform.bind();
+ }
+ }
+
+ /** Returns the length of the null-terminated C string in {@code cString}. */
+ private static int getCStringLength(byte[] cString) {
+ for (int i = 0; i < cString.length; ++i) {
+ if (cString[i] == '\0') {
+ return i;
+ }
+ }
+ return cString.length;
+ }
+
+ /**
+ * GL attribute, which can be attached to a buffer with {@link Attribute#setBuffer(float[], int)}.
+ */
+ private static final class Attribute {
+
+ /* Returns the attribute at the given index in the program. */
+ public static Attribute create(int programId, int index) {
+ int[] length = new int[1];
+ GLES20.glGetProgramiv(
+ programId, GLES20.GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, length, /* offset= */ 0);
+ byte[] nameBytes = new byte[length[0]];
+
+ GLES20.glGetActiveAttrib(
+ programId,
+ index,
+ length[0],
+ /* unusedLength */ new int[1],
+ /* lengthOffset= */ 0,
+ /* unusedSize */ new int[1],
+ /* sizeOffset= */ 0,
+ /* unusedType */ new int[1],
+ /* typeOffset= */ 0,
+ nameBytes,
+ /* nameOffset= */ 0);
+ String name = new String(nameBytes, /* offset= */ 0, getCStringLength(nameBytes));
+ int location = getAttributeLocation(programId, name);
+
+ return new Attribute(name, index, location);
+ }
+
+ /** The name of the attribute in the GLSL sources. */
+ public final String name;
+
+ private final int index;
+ private final int location;
+
+ @Nullable private Buffer buffer;
+ private int size;
+
+ private Attribute(String name, int index, int location) {
+ this.name = name;
+ this.index = index;
+ this.location = location;
+ }
+
+ /**
+ * Configures {@link #bind()} to attach vertices in {@code buffer} (each of size {@code size}
+ * elements) to this {@link Attribute}.
+ *
+ * @param buffer Buffer to bind to this attribute.
+ * @param size Number of elements per vertex.
+ */
+ public void setBuffer(float[] buffer, int size) {
+ this.buffer = GlUtil.createBuffer(buffer);
+ this.size = size;
+ }
+
+ /**
+ * Sets the vertex attribute to whatever was attached via {@link #setBuffer(float[], int)}.
+ *
+ * Should be called before each drawing call.
+ */
+ public void bind() {
+ Buffer buffer = checkNotNull(this.buffer, "call setBuffer before bind");
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, /* buffer= */ 0);
+ GLES20.glVertexAttribPointer(
+ location, size, GLES20.GL_FLOAT, /* normalized= */ false, /* stride= */ 0, buffer);
+ GLES20.glEnableVertexAttribArray(index);
+ GlUtil.checkGlError();
+ }
+ }
+
+ /**
+ * GL uniform, which can be attached to a sampler using {@link Uniform#setSamplerTexId(int, int)}.
+ */
+ private static final class Uniform {
+
+ /** Returns the uniform at the given index in the program. */
+ public static Uniform create(int programId, int index) {
+ int[] length = new int[1];
+ GLES20.glGetProgramiv(
+ programId, GLES20.GL_ACTIVE_UNIFORM_MAX_LENGTH, length, /* offset= */ 0);
+
+ int[] type = new int[1];
+ byte[] nameBytes = new byte[length[0]];
+
+ GLES20.glGetActiveUniform(
+ programId,
+ index,
+ length[0],
+ /* unusedLength */ new int[1],
+ /* lengthOffset= */ 0,
+ /* unusedSize */ new int[1],
+ /*sizeOffset= */ 0,
+ type,
+ /* typeOffset= */ 0,
+ nameBytes,
+ /* nameOffset= */ 0);
+ String name = new String(nameBytes, /* offset= */ 0, getCStringLength(nameBytes));
+ int location = getUniformLocation(programId, name);
+
+ return new Uniform(name, location, type[0]);
+ }
+
+ /** The name of the uniform in the GLSL sources. */
+ public final String name;
+
+ private final int location;
+ private final int type;
+ private final float[] value;
+
+ private int texId;
+ private int unit;
+
+ private Uniform(String name, int location, int type) {
+ this.name = name;
+ this.location = location;
+ this.type = type;
+ this.value = new float[16];
+ }
+
+ /**
+ * Configures {@link #bind()} to use the specified {@code texId} for this sampler uniform.
+ *
+ * @param texId The GL texture identifier from which to sample.
+ * @param unit The GL texture unit index.
+ */
+ public void setSamplerTexId(int texId, int unit) {
+ this.texId = texId;
+ this.unit = unit;
+ }
+
+ /** Configures {@link #bind()} to use the specified float {@code value} for this uniform. */
+ public void setFloat(float value) {
+ this.value[0] = value;
+ }
+
+ /** Configures {@link #bind()} to use the specified float[] {@code value} for this uniform. */
+ public void setFloats(float[] value) {
+ System.arraycopy(value, /* srcPos= */ 0, this.value, /* destPos= */ 0, value.length);
+ }
+
+ /**
+ * Sets the uniform to whatever value was passed via {@link #setSamplerTexId(int, int)}, {@link
+ * #setFloat(float)} or {@link #setFloats(float[])}.
+ *
+ * Should be called before each drawing call.
+ */
+ public void bind() {
+ if (type == GLES20.GL_FLOAT) {
+ GLES20.glUniform1fv(location, /* count= */ 1, value, /* offset= */ 0);
+ GlUtil.checkGlError();
+ return;
+ }
+
+ if (type == GLES20.GL_FLOAT_MAT3) {
+ GLES20.glUniformMatrix3fv(
+ location, /* count= */ 1, /* transpose= */ false, value, /* offset= */ 0);
+ GlUtil.checkGlError();
+ return;
+ }
+
+ if (type == GLES20.GL_FLOAT_MAT4) {
+ GLES20.glUniformMatrix4fv(
+ location, /* count= */ 1, /* transpose= */ false, value, /* offset= */ 0);
+ GlUtil.checkGlError();
+ return;
+ }
+
+ if (texId == 0) {
+ throw new IllegalStateException("No call to setSamplerTexId() before bind.");
+ }
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + unit);
+ if (type == GLES11Ext.GL_SAMPLER_EXTERNAL_OES || type == GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT) {
+ GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texId);
+ } else if (type == GLES20.GL_SAMPLER_2D) {
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId);
+ } else {
+ throw new IllegalStateException("Unexpected uniform type: " + type);
+ }
+ GLES20.glUniform1i(location, unit);
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
+ GLES20.glTexParameteri(
+ GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameteri(
+ GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
+ GlUtil.checkGlError();
+ }
+ }
+}
diff --git a/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java b/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java
index 879ade7037..b763c6b620 100644
--- a/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java
+++ b/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java
@@ -16,7 +16,6 @@
package androidx.media3.common.util;
import static android.opengl.GLU.gluErrorString;
-import static androidx.media3.common.util.Assertions.checkNotNull;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -33,13 +32,10 @@ import androidx.annotation.RequiresApi;
import androidx.media3.common.C;
import java.io.IOException;
import java.io.InputStream;
-import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
-import java.util.HashMap;
-import java.util.Map;
import javax.microedition.khronos.egl.EGL10;
/** OpenGL ES utilities. */
@@ -55,152 +51,6 @@ public final class GlUtil {
}
}
- /**
- * Represents a GLSL shader program.
- *
- * After constructing a program, keep a reference for its lifetime and call {@link #delete()}
- * (or release the current GL context) when it's no longer needed.
- */
- public static final class Program {
- /** The identifier of a compiled and linked GLSL shader program. */
- private final int programId;
-
- private final Attribute[] attributes;
- private final Uniform[] uniforms;
- private final Map This involves slow steps, like compiling, linking, and switching the GL program, so do not
- * call this in fast rendering loops.
- *
- * @param vertexShaderGlsl The vertex shader program.
- * @param fragmentShaderGlsl The fragment shader program.
- */
- public Program(String vertexShaderGlsl, String fragmentShaderGlsl) {
- programId = GLES20.glCreateProgram();
- checkGlError();
-
- // Add the vertex and fragment shaders.
- addShader(programId, GLES20.GL_VERTEX_SHADER, vertexShaderGlsl);
- addShader(programId, GLES20.GL_FRAGMENT_SHADER, fragmentShaderGlsl);
-
- // Link and use the program, and enumerate attributes/uniforms.
- GLES20.glLinkProgram(programId);
- int[] linkStatus = new int[] {GLES20.GL_FALSE};
- GLES20.glGetProgramiv(programId, GLES20.GL_LINK_STATUS, linkStatus, /* offset= */ 0);
- if (linkStatus[0] != GLES20.GL_TRUE) {
- throwGlException(
- "Unable to link shader program: \n" + GLES20.glGetProgramInfoLog(programId));
- }
- GLES20.glUseProgram(programId);
- attributeByName = new HashMap<>();
- int[] attributeCount = new int[1];
- GLES20.glGetProgramiv(
- programId, GLES20.GL_ACTIVE_ATTRIBUTES, attributeCount, /* offset= */ 0);
- attributes = new Attribute[attributeCount[0]];
- for (int i = 0; i < attributeCount[0]; i++) {
- Attribute attribute = Attribute.create(programId, i);
- attributes[i] = attribute;
- attributeByName.put(attribute.name, attribute);
- }
- uniformByName = new HashMap<>();
- int[] uniformCount = new int[1];
- GLES20.glGetProgramiv(programId, GLES20.GL_ACTIVE_UNIFORMS, uniformCount, /* offset= */ 0);
- uniforms = new Uniform[uniformCount[0]];
- for (int i = 0; i < uniformCount[0]; i++) {
- Uniform uniform = Uniform.create(programId, i);
- uniforms[i] = uniform;
- uniformByName.put(uniform.name, uniform);
- }
- checkGlError();
- }
-
- /**
- * Uses the program.
- *
- * Call this in the rendering loop to switch between different programs.
- */
- public void use() {
- // TODO(http://b/205002913): When multiple GL programs are supported by Transformer, make sure
- // to call use() to switch between programs.
- GLES20.glUseProgram(programId);
- checkGlError();
- }
-
- /** Deletes the program. Deleted programs cannot be used again. */
- public void delete() {
- GLES20.glDeleteProgram(programId);
- checkGlError();
- }
-
- /**
- * Returns the location of an {@link Attribute}, which has been enabled as a vertex attribute
- * array.
- */
- public int getAttributeArrayLocationAndEnable(String attributeName) {
- int location = getAttributeLocation(attributeName);
- GLES20.glEnableVertexAttribArray(location);
- checkGlError();
- return location;
- }
-
- /** Returns the location of an {@link Attribute}. */
- private int getAttributeLocation(String attributeName) {
- return GlUtil.getAttributeLocation(programId, attributeName);
- }
-
- /** Returns the location of a {@link Uniform}. */
- public int getUniformLocation(String uniformName) {
- return GlUtil.getUniformLocation(programId, uniformName);
- }
-
- /** Sets a float buffer type attribute. */
- public void setBufferAttribute(String name, float[] values, int size) {
- checkNotNull(attributeByName.get(name)).setBuffer(values, size);
- }
-
- /** Sets a texture sampler type uniform. */
- public void setSamplerTexIdUniform(String name, int texId, int unit) {
- checkNotNull(uniformByName.get(name)).setSamplerTexId(texId, unit);
- }
-
- /** Sets a float type uniform. */
- public void setFloatUniform(String name, float value) {
- checkNotNull(uniformByName.get(name)).setFloat(value);
- }
-
- /** Sets a float array type uniform. */
- public void setFloatsUniform(String name, float[] value) {
- checkNotNull(uniformByName.get(name)).setFloats(value);
- }
-
- /** Binds all attributes and uniforms in the program. */
- public void bindAttributesAndUniforms() {
- for (Attribute attribute : attributes) {
- attribute.bind();
- }
- for (Uniform uniform : uniforms) {
- uniform.bind();
- }
- }
- }
-
/** Whether to throw a {@link GlException} in case of an OpenGL error. */
public static boolean glAssertionsEnabled = false;
@@ -214,8 +64,6 @@ public final class GlUtil {
// https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_surfaceless_context.txt
private static final String EXTENSION_SURFACELESS_CONTEXT = "EGL_KHR_surfaceless_context";
- // https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_YUV_target.txt
- private static final int GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT = 0x8BE7;
// https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_gl_colorspace.txt
private static final int EGL_GL_COLORSPACE_KHR = 0x309D;
// https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_gl_colorspace_bt2020_linear.txt
@@ -462,31 +310,7 @@ public final class GlUtil {
return texId[0];
}
- private static void addShader(int programId, int type, String glsl) {
- int shader = GLES20.glCreateShader(type);
- GLES20.glShaderSource(shader, glsl);
- GLES20.glCompileShader(shader);
-
- int[] result = new int[] {GLES20.GL_FALSE};
- GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, result, /* offset= */ 0);
- if (result[0] != GLES20.GL_TRUE) {
- throwGlException(GLES20.glGetShaderInfoLog(shader) + ", source: " + glsl);
- }
-
- GLES20.glAttachShader(programId, shader);
- GLES20.glDeleteShader(shader);
- checkGlError();
- }
-
- private static int getAttributeLocation(int programId, String attributeName) {
- return GLES20.glGetAttribLocation(programId, attributeName);
- }
-
- private static int getUniformLocation(int programId, String uniformName) {
- return GLES20.glGetUniformLocation(programId, uniformName);
- }
-
- private static void throwGlException(String errorMsg) {
+ /* package */ static void throwGlException(String errorMsg) {
Log.e(TAG, errorMsg);
if (glAssertionsEnabled) {
throw new GlException(errorMsg);
@@ -499,207 +323,6 @@ public final class GlUtil {
}
}
- /** Returns the length of the null-terminated string in {@code strVal}. */
- private static int strlen(byte[] strVal) {
- for (int i = 0; i < strVal.length; ++i) {
- if (strVal[i] == '\0') {
- return i;
- }
- }
- return strVal.length;
- }
-
- /**
- * GL attribute, which can be attached to a buffer with {@link Attribute#setBuffer(float[], int)}.
- */
- private static final class Attribute {
-
- /* Returns the attribute at the given index in the program. */
- public static Attribute create(int programId, int index) {
- int[] length = new int[1];
- GLES20.glGetProgramiv(
- programId, GLES20.GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, length, /* offset= */ 0);
- byte[] nameBytes = new byte[length[0]];
-
- GLES20.glGetActiveAttrib(
- programId,
- index,
- length[0],
- /* unusedLength */ new int[1],
- /* lengthOffset= */ 0,
- /* unusedSize */ new int[1],
- /* sizeOffset= */ 0,
- /* unusedType */ new int[1],
- /* typeOffset= */ 0,
- nameBytes,
- /* nameOffset= */ 0);
- String name = new String(nameBytes, /* offset= */ 0, strlen(nameBytes));
- int location = getAttributeLocation(programId, name);
-
- return new Attribute(name, index, location);
- }
-
- /** The name of the attribute in the GLSL sources. */
- public final String name;
-
- private final int index;
- private final int location;
-
- @Nullable private Buffer buffer;
- private int size;
-
- private Attribute(String name, int index, int location) {
- this.name = name;
- this.index = index;
- this.location = location;
- }
-
- /**
- * Configures {@link #bind()} to attach vertices in {@code buffer} (each of size {@code size}
- * elements) to this {@link Attribute}.
- *
- * @param buffer Buffer to bind to this attribute.
- * @param size Number of elements per vertex.
- */
- public void setBuffer(float[] buffer, int size) {
- this.buffer = createBuffer(buffer);
- this.size = size;
- }
-
- /**
- * Sets the vertex attribute to whatever was attached via {@link #setBuffer(float[], int)}.
- *
- * Should be called before each drawing call.
- */
- public void bind() {
- Buffer buffer = checkNotNull(this.buffer, "call setBuffer before bind");
- GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, /* buffer= */ 0);
- GLES20.glVertexAttribPointer(
- location, size, GLES20.GL_FLOAT, /* normalized= */ false, /* stride= */ 0, buffer);
- GLES20.glEnableVertexAttribArray(index);
- checkGlError();
- }
- }
-
- /**
- * GL uniform, which can be attached to a sampler using {@link Uniform#setSamplerTexId(int, int)}.
- */
- private static final class Uniform {
-
- /** Returns the uniform at the given index in the program. */
- public static Uniform create(int programId, int index) {
- int[] length = new int[1];
- GLES20.glGetProgramiv(
- programId, GLES20.GL_ACTIVE_UNIFORM_MAX_LENGTH, length, /* offset= */ 0);
-
- int[] type = new int[1];
- byte[] nameBytes = new byte[length[0]];
-
- GLES20.glGetActiveUniform(
- programId,
- index,
- length[0],
- /* unusedLength */ new int[1],
- /* lengthOffset= */ 0,
- /* unusedSize */ new int[1],
- /*sizeOffset= */ 0,
- type,
- /* typeOffset= */ 0,
- nameBytes,
- /* nameOffset= */ 0);
- String name = new String(nameBytes, /* offset= */ 0, strlen(nameBytes));
- int location = getUniformLocation(programId, name);
-
- return new Uniform(name, location, type[0]);
- }
-
- /** The name of the uniform in the GLSL sources. */
- public final String name;
-
- private final int location;
- private final int type;
- private final float[] value;
-
- private int texId;
- private int unit;
-
- private Uniform(String name, int location, int type) {
- this.name = name;
- this.location = location;
- this.type = type;
- this.value = new float[16];
- }
-
- /**
- * Configures {@link #bind()} to use the specified {@code texId} for this sampler uniform.
- *
- * @param texId The GL texture identifier from which to sample.
- * @param unit The GL texture unit index.
- */
- public void setSamplerTexId(int texId, int unit) {
- this.texId = texId;
- this.unit = unit;
- }
-
- /** Configures {@link #bind()} to use the specified float {@code value} for this uniform. */
- public void setFloat(float value) {
- this.value[0] = value;
- }
-
- /** Configures {@link #bind()} to use the specified float[] {@code value} for this uniform. */
- public void setFloats(float[] value) {
- System.arraycopy(value, /* srcPos= */ 0, this.value, /* destPos= */ 0, value.length);
- }
-
- /**
- * Sets the uniform to whatever value was passed via {@link #setSamplerTexId(int, int)}, {@link
- * #setFloat(float)} or {@link #setFloats(float[])}.
- *
- * Should be called before each drawing call.
- */
- public void bind() {
- if (type == GLES20.GL_FLOAT) {
- GLES20.glUniform1fv(location, /* count= */ 1, value, /* offset= */ 0);
- checkGlError();
- return;
- }
-
- if (type == GLES20.GL_FLOAT_MAT3) {
- GLES20.glUniformMatrix3fv(
- location, /* count= */ 1, /* transpose= */ false, value, /* offset= */ 0);
- checkGlError();
- return;
- }
-
- if (type == GLES20.GL_FLOAT_MAT4) {
- GLES20.glUniformMatrix4fv(
- location, /* count= */ 1, /* transpose= */ false, value, /* offset= */ 0);
- checkGlError();
- return;
- }
-
- if (texId == 0) {
- throw new IllegalStateException("No call to setSamplerTexId() before bind.");
- }
- GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + unit);
- if (type == GLES11Ext.GL_SAMPLER_EXTERNAL_OES || type == GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT) {
- GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texId);
- } else if (type == GLES20.GL_SAMPLER_2D) {
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId);
- } else {
- throw new IllegalStateException("Unexpected uniform type: " + type);
- }
- GLES20.glUniform1i(location, unit);
- GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
- GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
- GLES20.glTexParameteri(
- GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
- GLES20.glTexParameteri(
- GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
- checkGlError();
- }
- }
-
@RequiresApi(17)
private static final class Api17 {
private Api17() {}
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoDecoderGLSurfaceView.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoDecoderGLSurfaceView.java
index a4a29ec46c..a24d17096b 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoDecoderGLSurfaceView.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/VideoDecoderGLSurfaceView.java
@@ -23,6 +23,7 @@ import android.opengl.GLSurfaceView;
import android.util.AttributeSet;
import androidx.annotation.Nullable;
import androidx.media3.common.util.Assertions;
+import androidx.media3.common.util.GlProgram;
import androidx.media3.common.util.GlUtil;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.decoder.VideoDecoderOutputBuffer;
@@ -146,7 +147,7 @@ public final class VideoDecoderGLSurfaceView extends GLSurfaceView
// glDrawArrays uses it.
private final FloatBuffer[] textureCoords;
- private GlUtil.@MonotonicNonNull Program program;
+ private @MonotonicNonNull GlProgram program;
private int colorMatrixLocation;
// Accessed only from the GL thread.
@@ -167,7 +168,7 @@ public final class VideoDecoderGLSurfaceView extends GLSurfaceView
@Override
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
- program = new GlUtil.Program(VERTEX_SHADER, FRAGMENT_SHADER);
+ program = new GlProgram(VERTEX_SHADER, FRAGMENT_SHADER);
int posLocation = program.getAttributeArrayLocationAndEnable("in_pos");
GLES20.glVertexAttribPointer(
posLocation,
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/ProjectionRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/ProjectionRenderer.java
index b02cf870d6..2503733f36 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/ProjectionRenderer.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/spherical/ProjectionRenderer.java
@@ -21,6 +21,7 @@ import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
+import androidx.media3.common.util.GlProgram;
import androidx.media3.common.util.GlUtil;
import java.nio.FloatBuffer;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@@ -87,7 +88,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private int stereoMode;
@Nullable private MeshData leftMeshData;
@Nullable private MeshData rightMeshData;
- private GlUtil.@MonotonicNonNull Program program;
+ private @MonotonicNonNull GlProgram program;
// Program related GL items. These are only valid if Program is valid.
private int mvpMatrixHandle;
@@ -114,7 +115,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** Initializes of the GL components. */
public void init() {
- program = new GlUtil.Program(VERTEX_SHADER, FRAGMENT_SHADER);
+ program = new GlProgram(VERTEX_SHADER, FRAGMENT_SHADER);
mvpMatrixHandle = program.getUniformLocation("uMvpMatrix");
uTexMatrixHandle = program.getUniformLocation("uTexMatrix");
positionHandle = program.getAttributeArrayLocationAndEnable("aPosition");
@@ -148,7 +149,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
GLES20.glUniformMatrix3fv(uTexMatrixHandle, 1, false, texMatrix, 0);
- // TODO(b/205002913): Update to use GlUtil.Uniform.bind().
+ // TODO(b/205002913): Update to use GlProgram.Uniform.bind().
GLES20.glUniformMatrix4fv(mvpMatrixHandle, 1, false, mvpMatrix, 0);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);
diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java
index ff201364a0..57f8be3a1f 100644
--- a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java
+++ b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameEditor.java
@@ -31,6 +31,7 @@ import android.view.Surface;
import android.view.SurfaceView;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
+import androidx.media3.common.util.GlProgram;
import androidx.media3.common.util.GlUtil;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
@@ -86,7 +87,7 @@ import java.util.concurrent.atomic.AtomicInteger;
EGLContext eglContext;
EGLSurface eglSurface;
int textureId;
- GlUtil.Program glProgram;
+ GlProgram glProgram;
@Nullable EGLSurface debugPreviewEglSurface = null;
try {
eglDisplay = GlUtil.createEglDisplay();
@@ -141,7 +142,7 @@ import java.util.concurrent.atomic.AtomicInteger;
debugPreviewHeight);
}
- private static GlUtil.Program configureGlProgram(
+ private static GlProgram configureGlProgram(
Context context,
Matrix transformationMatrix,
int textureId,
@@ -157,8 +158,7 @@ import java.util.concurrent.atomic.AtomicInteger;
enableExperimentalHdrEditing
? FRAGMENT_SHADER_COPY_EXTERNAL_YUV_ES3_PATH
: FRAGMENT_SHADER_COPY_EXTERNAL_PATH;
- GlUtil.Program glProgram =
- new GlUtil.Program(context, vertexShaderFilePath, fragmentShaderFilePath);
+ GlProgram glProgram = new GlProgram(context, vertexShaderFilePath, fragmentShaderFilePath);
// Draw the frame on the entire normalized device coordinate space, from -1 to 1, for x and y.
glProgram.setBufferAttribute(
@@ -242,7 +242,7 @@ import java.util.concurrent.atomic.AtomicInteger;
private final AtomicInteger availableInputFrameCount;
private final SurfaceTexture inputSurfaceTexture;
private final Surface inputSurface;
- private final GlUtil.Program glProgram;
+ private final GlProgram glProgram;
private final int outputWidth;
private final int outputHeight;
@Nullable private final EGLSurface debugPreviewEglSurface;
@@ -256,7 +256,7 @@ import java.util.concurrent.atomic.AtomicInteger;
EGLContext eglContext,
EGLSurface eglSurface,
int textureId,
- GlUtil.Program glProgram,
+ GlProgram glProgram,
int outputWidth,
int outputHeight,
@Nullable EGLSurface debugPreviewEglSurface,