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 attributeByName; + private final Map uniformByName; + + /** + * Compiles a GL shader program from vertex and fragment shader GLSL GLES20 code. + * + * @param context The {@link Context}. + * @param vertexShaderFilePath The path to a vertex shader program. + * @param fragmentShaderFilePath The path to a fragment shader program. + * @throws IOException When failing to read shader files. + */ + public GlProgram(Context context, String vertexShaderFilePath, String fragmentShaderFilePath) + throws IOException { + this( + GlUtil.loadAsset(context, vertexShaderFilePath), + GlUtil.loadAsset(context, fragmentShaderFilePath)); + } + + /** + * Creates a GL shader program from vertex and fragment shader GLSL GLES20 code. + * + *

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 attributeByName; - private final Map uniformByName; - - /** - * Compiles a GL shader program from vertex and fragment shader GLSL GLES20 code. - * - * @param context The {@link Context}. - * @param vertexShaderFilePath The path to a vertex shader program. - * @param fragmentShaderFilePath The path to a fragment shader program. - * @throws IOException When failing to read shader files. - */ - public Program(Context context, String vertexShaderFilePath, String fragmentShaderFilePath) - throws IOException { - this(loadAsset(context, vertexShaderFilePath), loadAsset(context, fragmentShaderFilePath)); - } - - /** - * Creates a GL shader program from vertex and fragment shader GLSL GLES20 code. - * - *

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,