Make GlProgram an outer class.
This change makes GlUtil.Program an outer class named GlProgram, and also moves private static helpers as well as the inner classes Attribute and Uniform which were only used by GlUtil.Program to GlProgram. Other static utility methods remain in GlUtil. No functional changes intended. PiperOrigin-RevId: 426119299
This commit is contained in:
parent
ddfd79bb98
commit
7507da51a0
@ -27,6 +27,7 @@ import android.graphics.drawable.BitmapDrawable;
|
|||||||
import android.opengl.GLES20;
|
import android.opengl.GLES20;
|
||||||
import android.opengl.GLUtils;
|
import android.opengl.GLUtils;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
|
import androidx.media3.common.util.GlProgram;
|
||||||
import androidx.media3.common.util.GlUtil;
|
import androidx.media3.common.util.GlUtil;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@ -50,7 +51,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
private final Bitmap logoBitmap;
|
private final Bitmap logoBitmap;
|
||||||
private final Canvas overlayCanvas;
|
private final Canvas overlayCanvas;
|
||||||
|
|
||||||
private GlUtil.@MonotonicNonNull Program program;
|
private @MonotonicNonNull GlProgram program;
|
||||||
|
|
||||||
private float bitmapScaleX;
|
private float bitmapScaleX;
|
||||||
private float bitmapScaleY;
|
private float bitmapScaleY;
|
||||||
@ -78,7 +79,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
public void initialize() {
|
public void initialize() {
|
||||||
try {
|
try {
|
||||||
program =
|
program =
|
||||||
new GlUtil.Program(
|
new GlProgram(
|
||||||
context,
|
context,
|
||||||
/* vertexShaderFilePath= */ "bitmap_overlay_video_processor_vertex.glsl",
|
/* vertexShaderFilePath= */ "bitmap_overlay_video_processor_vertex.glsl",
|
||||||
/* fragmentShaderFilePath= */ "bitmap_overlay_video_processor_fragment.glsl");
|
/* fragmentShaderFilePath= */ "bitmap_overlay_video_processor_fragment.glsl");
|
||||||
@ -117,7 +118,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
GlUtil.checkGlError();
|
GlUtil.checkGlError();
|
||||||
|
|
||||||
// Run the shader program.
|
// Run the shader program.
|
||||||
GlUtil.Program program = checkNotNull(this.program);
|
GlProgram program = checkNotNull(this.program);
|
||||||
program.setSamplerTexIdUniform("uTexSampler0", frameTexture, /* unit= */ 0);
|
program.setSamplerTexIdUniform("uTexSampler0", frameTexture, /* unit= */ 0);
|
||||||
program.setSamplerTexIdUniform("uTexSampler1", textures[0], /* unit= */ 1);
|
program.setSamplerTexIdUniform("uTexSampler1", textures[0], /* unit= */ 1);
|
||||||
program.setFloatUniform("uScaleX", bitmapScaleX);
|
program.setFloatUniform("uScaleX", bitmapScaleX);
|
||||||
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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<String, Attribute> attributeByName;
|
||||||
|
private final Map<String, Uniform> 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.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
*
|
||||||
|
* <p>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)}.
|
||||||
|
*
|
||||||
|
* <p>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[])}.
|
||||||
|
*
|
||||||
|
* <p>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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -16,7 +16,6 @@
|
|||||||
package androidx.media3.common.util;
|
package androidx.media3.common.util;
|
||||||
|
|
||||||
import static android.opengl.GLU.gluErrorString;
|
import static android.opengl.GLU.gluErrorString;
|
||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
@ -33,13 +32,10 @@ import androidx.annotation.RequiresApi;
|
|||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.Buffer;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.nio.FloatBuffer;
|
import java.nio.FloatBuffer;
|
||||||
import java.nio.IntBuffer;
|
import java.nio.IntBuffer;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import javax.microedition.khronos.egl.EGL10;
|
import javax.microedition.khronos.egl.EGL10;
|
||||||
|
|
||||||
/** OpenGL ES utilities. */
|
/** OpenGL ES utilities. */
|
||||||
@ -55,152 +51,6 @@ public final class GlUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a GLSL shader program.
|
|
||||||
*
|
|
||||||
* <p>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<String, Attribute> attributeByName;
|
|
||||||
private final Map<String, Uniform> 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.
|
|
||||||
*
|
|
||||||
* <p>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.
|
|
||||||
*
|
|
||||||
* <p>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. */
|
/** Whether to throw a {@link GlException} in case of an OpenGL error. */
|
||||||
public static boolean glAssertionsEnabled = false;
|
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
|
// https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_surfaceless_context.txt
|
||||||
private static final String EXTENSION_SURFACELESS_CONTEXT = "EGL_KHR_surfaceless_context";
|
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
|
// https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_gl_colorspace.txt
|
||||||
private static final int EGL_GL_COLORSPACE_KHR = 0x309D;
|
private static final int EGL_GL_COLORSPACE_KHR = 0x309D;
|
||||||
// https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_gl_colorspace_bt2020_linear.txt
|
// 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];
|
return texId[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addShader(int programId, int type, String glsl) {
|
/* package */ static void throwGlException(String errorMsg) {
|
||||||
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) {
|
|
||||||
Log.e(TAG, errorMsg);
|
Log.e(TAG, errorMsg);
|
||||||
if (glAssertionsEnabled) {
|
if (glAssertionsEnabled) {
|
||||||
throw new GlException(errorMsg);
|
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)}.
|
|
||||||
*
|
|
||||||
* <p>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[])}.
|
|
||||||
*
|
|
||||||
* <p>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)
|
@RequiresApi(17)
|
||||||
private static final class Api17 {
|
private static final class Api17 {
|
||||||
private Api17() {}
|
private Api17() {}
|
||||||
|
@ -23,6 +23,7 @@ import android.opengl.GLSurfaceView;
|
|||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
|
import androidx.media3.common.util.GlProgram;
|
||||||
import androidx.media3.common.util.GlUtil;
|
import androidx.media3.common.util.GlUtil;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.decoder.VideoDecoderOutputBuffer;
|
import androidx.media3.decoder.VideoDecoderOutputBuffer;
|
||||||
@ -146,7 +147,7 @@ public final class VideoDecoderGLSurfaceView extends GLSurfaceView
|
|||||||
// glDrawArrays uses it.
|
// glDrawArrays uses it.
|
||||||
private final FloatBuffer[] textureCoords;
|
private final FloatBuffer[] textureCoords;
|
||||||
|
|
||||||
private GlUtil.@MonotonicNonNull Program program;
|
private @MonotonicNonNull GlProgram program;
|
||||||
private int colorMatrixLocation;
|
private int colorMatrixLocation;
|
||||||
|
|
||||||
// Accessed only from the GL thread.
|
// Accessed only from the GL thread.
|
||||||
@ -167,7 +168,7 @@ public final class VideoDecoderGLSurfaceView extends GLSurfaceView
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
|
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");
|
int posLocation = program.getAttributeArrayLocationAndEnable("in_pos");
|
||||||
GLES20.glVertexAttribPointer(
|
GLES20.glVertexAttribPointer(
|
||||||
posLocation,
|
posLocation,
|
||||||
|
@ -21,6 +21,7 @@ import android.opengl.GLES11Ext;
|
|||||||
import android.opengl.GLES20;
|
import android.opengl.GLES20;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
|
import androidx.media3.common.util.GlProgram;
|
||||||
import androidx.media3.common.util.GlUtil;
|
import androidx.media3.common.util.GlUtil;
|
||||||
import java.nio.FloatBuffer;
|
import java.nio.FloatBuffer;
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
@ -87,7 +88,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
private int stereoMode;
|
private int stereoMode;
|
||||||
@Nullable private MeshData leftMeshData;
|
@Nullable private MeshData leftMeshData;
|
||||||
@Nullable private MeshData rightMeshData;
|
@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.
|
// Program related GL items. These are only valid if Program is valid.
|
||||||
private int mvpMatrixHandle;
|
private int mvpMatrixHandle;
|
||||||
@ -114,7 +115,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
|
|
||||||
/** Initializes of the GL components. */
|
/** Initializes of the GL components. */
|
||||||
public void init() {
|
public void init() {
|
||||||
program = new GlUtil.Program(VERTEX_SHADER, FRAGMENT_SHADER);
|
program = new GlProgram(VERTEX_SHADER, FRAGMENT_SHADER);
|
||||||
mvpMatrixHandle = program.getUniformLocation("uMvpMatrix");
|
mvpMatrixHandle = program.getUniformLocation("uMvpMatrix");
|
||||||
uTexMatrixHandle = program.getUniformLocation("uTexMatrix");
|
uTexMatrixHandle = program.getUniformLocation("uTexMatrix");
|
||||||
positionHandle = program.getAttributeArrayLocationAndEnable("aPosition");
|
positionHandle = program.getAttributeArrayLocationAndEnable("aPosition");
|
||||||
@ -148,7 +149,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
GLES20.glUniformMatrix3fv(uTexMatrixHandle, 1, false, texMatrix, 0);
|
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.glUniformMatrix4fv(mvpMatrixHandle, 1, false, mvpMatrix, 0);
|
||||||
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
|
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
|
||||||
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);
|
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);
|
||||||
|
@ -31,6 +31,7 @@ import android.view.Surface;
|
|||||||
import android.view.SurfaceView;
|
import android.view.SurfaceView;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
|
import androidx.media3.common.util.GlProgram;
|
||||||
import androidx.media3.common.util.GlUtil;
|
import androidx.media3.common.util.GlUtil;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
@ -86,7 +87,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
EGLContext eglContext;
|
EGLContext eglContext;
|
||||||
EGLSurface eglSurface;
|
EGLSurface eglSurface;
|
||||||
int textureId;
|
int textureId;
|
||||||
GlUtil.Program glProgram;
|
GlProgram glProgram;
|
||||||
@Nullable EGLSurface debugPreviewEglSurface = null;
|
@Nullable EGLSurface debugPreviewEglSurface = null;
|
||||||
try {
|
try {
|
||||||
eglDisplay = GlUtil.createEglDisplay();
|
eglDisplay = GlUtil.createEglDisplay();
|
||||||
@ -141,7 +142,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
debugPreviewHeight);
|
debugPreviewHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GlUtil.Program configureGlProgram(
|
private static GlProgram configureGlProgram(
|
||||||
Context context,
|
Context context,
|
||||||
Matrix transformationMatrix,
|
Matrix transformationMatrix,
|
||||||
int textureId,
|
int textureId,
|
||||||
@ -157,8 +158,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
enableExperimentalHdrEditing
|
enableExperimentalHdrEditing
|
||||||
? FRAGMENT_SHADER_COPY_EXTERNAL_YUV_ES3_PATH
|
? FRAGMENT_SHADER_COPY_EXTERNAL_YUV_ES3_PATH
|
||||||
: FRAGMENT_SHADER_COPY_EXTERNAL_PATH;
|
: FRAGMENT_SHADER_COPY_EXTERNAL_PATH;
|
||||||
GlUtil.Program glProgram =
|
GlProgram glProgram = new GlProgram(context, vertexShaderFilePath, fragmentShaderFilePath);
|
||||||
new GlUtil.Program(context, vertexShaderFilePath, fragmentShaderFilePath);
|
|
||||||
|
|
||||||
// Draw the frame on the entire normalized device coordinate space, from -1 to 1, for x and y.
|
// Draw the frame on the entire normalized device coordinate space, from -1 to 1, for x and y.
|
||||||
glProgram.setBufferAttribute(
|
glProgram.setBufferAttribute(
|
||||||
@ -242,7 +242,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
private final AtomicInteger availableInputFrameCount;
|
private final AtomicInteger availableInputFrameCount;
|
||||||
private final SurfaceTexture inputSurfaceTexture;
|
private final SurfaceTexture inputSurfaceTexture;
|
||||||
private final Surface inputSurface;
|
private final Surface inputSurface;
|
||||||
private final GlUtil.Program glProgram;
|
private final GlProgram glProgram;
|
||||||
private final int outputWidth;
|
private final int outputWidth;
|
||||||
private final int outputHeight;
|
private final int outputHeight;
|
||||||
@Nullable private final EGLSurface debugPreviewEglSurface;
|
@Nullable private final EGLSurface debugPreviewEglSurface;
|
||||||
@ -256,7 +256,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
EGLContext eglContext,
|
EGLContext eglContext,
|
||||||
EGLSurface eglSurface,
|
EGLSurface eglSurface,
|
||||||
int textureId,
|
int textureId,
|
||||||
GlUtil.Program glProgram,
|
GlProgram glProgram,
|
||||||
int outputWidth,
|
int outputWidth,
|
||||||
int outputHeight,
|
int outputHeight,
|
||||||
@Nullable EGLSurface debugPreviewEglSurface,
|
@Nullable EGLSurface debugPreviewEglSurface,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user