Transformer GL: Simplify GL program handling.
Relanding 9788750ddb
, with some changes
applied to improve primarily readability, naming,
and nullness checks.
PiperOrigin-RevId: 406101742
This commit is contained in:
parent
f37e980018
commit
7f64a5a1aa
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.demo.gl;
|
package androidx.media3.demo.gl;
|
||||||
|
|
||||||
|
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;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
@ -26,11 +28,11 @@ import android.opengl.GLES20;
|
|||||||
import android.opengl.GLUtils;
|
import android.opengl.GLUtils;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.util.Assertions;
|
|
||||||
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;
|
||||||
import javax.microedition.khronos.opengles.GL10;
|
import javax.microedition.khronos.opengles.GL10;
|
||||||
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Video processor that demonstrates how to overlay a bitmap on video output using a GL shader. The
|
* Video processor that demonstrates how to overlay a bitmap on video output using a GL shader. The
|
||||||
@ -49,7 +51,7 @@ import javax.microedition.khronos.opengles.GL10;
|
|||||||
private final Bitmap logoBitmap;
|
private final Bitmap logoBitmap;
|
||||||
private final Canvas overlayCanvas;
|
private final Canvas overlayCanvas;
|
||||||
|
|
||||||
private int program;
|
private GlUtil.@MonotonicNonNull Program program;
|
||||||
@Nullable private GlUtil.Attribute[] attributes;
|
@Nullable private GlUtil.Attribute[] attributes;
|
||||||
@Nullable private GlUtil.Uniform[] uniforms;
|
@Nullable private GlUtil.Uniform[] uniforms;
|
||||||
|
|
||||||
@ -77,27 +79,39 @@ import javax.microedition.khronos.opengles.GL10;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
String vertexShaderCode;
|
|
||||||
String fragmentShaderCode;
|
|
||||||
try {
|
try {
|
||||||
vertexShaderCode = GlUtil.loadAsset(context, "bitmap_overlay_video_processor_vertex.glsl");
|
program =
|
||||||
fragmentShaderCode =
|
new GlUtil.Program(
|
||||||
GlUtil.loadAsset(context, "bitmap_overlay_video_processor_fragment.glsl");
|
context,
|
||||||
|
/* vertexShaderFilePath= */ "bitmap_overlay_video_processor_vertex.glsl",
|
||||||
|
/* fragmentShaderFilePath= */ "bitmap_overlay_video_processor_fragment.glsl");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
program = GlUtil.compileProgram(vertexShaderCode, fragmentShaderCode);
|
GlUtil.Attribute[] attributes = program.getAttributes();
|
||||||
GlUtil.Attribute[] attributes = GlUtil.getAttributes(program);
|
|
||||||
GlUtil.Uniform[] uniforms = GlUtil.getUniforms(program);
|
|
||||||
for (GlUtil.Attribute attribute : attributes) {
|
for (GlUtil.Attribute attribute : attributes) {
|
||||||
if (attribute.name.equals("a_position")) {
|
if (attribute.name.equals("a_position")) {
|
||||||
attribute.setBuffer(new float[] {-1, -1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, 1, 1, 0, 1}, 4);
|
attribute.setBuffer(
|
||||||
|
new float[] {
|
||||||
|
-1, -1, 0, 1,
|
||||||
|
1, -1, 0, 1,
|
||||||
|
-1, 1, 0, 1,
|
||||||
|
1, 1, 0, 1
|
||||||
|
},
|
||||||
|
4);
|
||||||
} else if (attribute.name.equals("a_texcoord")) {
|
} else if (attribute.name.equals("a_texcoord")) {
|
||||||
attribute.setBuffer(new float[] {0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1}, 4);
|
attribute.setBuffer(
|
||||||
|
new float[] {
|
||||||
|
0, 0, 0, 1,
|
||||||
|
1, 0, 0, 1,
|
||||||
|
0, 1, 0, 1,
|
||||||
|
1, 1, 0, 1
|
||||||
|
},
|
||||||
|
4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.attributes = attributes;
|
this.attributes = attributes;
|
||||||
this.uniforms = uniforms;
|
this.uniforms = program.getUniforms();
|
||||||
GLES20.glGenTextures(1, textures, 0);
|
GLES20.glGenTextures(1, textures, 0);
|
||||||
GLES20.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
|
GLES20.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
|
||||||
GLES20.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
|
GLES20.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
|
||||||
@ -126,9 +140,9 @@ import javax.microedition.khronos.opengles.GL10;
|
|||||||
GlUtil.checkGlError();
|
GlUtil.checkGlError();
|
||||||
|
|
||||||
// Run the shader program.
|
// Run the shader program.
|
||||||
GlUtil.Uniform[] uniforms = Assertions.checkNotNull(this.uniforms);
|
GlUtil.Uniform[] uniforms = checkNotNull(this.uniforms);
|
||||||
GlUtil.Attribute[] attributes = Assertions.checkNotNull(this.attributes);
|
GlUtil.Attribute[] attributes = checkNotNull(this.attributes);
|
||||||
GLES20.glUseProgram(program);
|
checkNotNull(program).use();
|
||||||
for (GlUtil.Uniform uniform : uniforms) {
|
for (GlUtil.Uniform uniform : uniforms) {
|
||||||
switch (uniform.name) {
|
switch (uniform.name) {
|
||||||
case "tex_sampler_0":
|
case "tex_sampler_0":
|
||||||
|
@ -55,6 +55,160 @@ public final class GlUtil {
|
|||||||
/** Thrown when the required EGL version is not supported by the device. */
|
/** Thrown when the required EGL version is not supported by the device. */
|
||||||
public static final class UnsupportedEglVersionException extends Exception {}
|
public static final class UnsupportedEglVersionException extends Exception {}
|
||||||
|
|
||||||
|
/** GL program. */
|
||||||
|
public static final class Program {
|
||||||
|
/** The identifier of a compiled and linked GLSL shader program. */
|
||||||
|
private final int programId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compiles a GL shader program from vertex and fragment shader GLSL GLES20 code.
|
||||||
|
*
|
||||||
|
* @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(GLES20.GL_VERTEX_SHADER, vertexShaderGlsl);
|
||||||
|
addShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderGlsl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compiles a GL shader program from vertex and fragment shader GLSL GLES20 code.
|
||||||
|
*
|
||||||
|
* @param vertexShaderGlsl The vertex shader program as arrays of strings. Strings are joined by
|
||||||
|
* adding a new line character in between each of them.
|
||||||
|
* @param fragmentShaderGlsl The fragment shader program as arrays of strings. Strings are
|
||||||
|
* joined by adding a new line character in between each of them.
|
||||||
|
*/
|
||||||
|
public Program(String[] vertexShaderGlsl, String[] fragmentShaderGlsl) {
|
||||||
|
this(TextUtils.join("\n", vertexShaderGlsl), TextUtils.join("\n", fragmentShaderGlsl));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Uses the program. */
|
||||||
|
public void use() {
|
||||||
|
// Link and check for errors.
|
||||||
|
GLES20.glLinkProgram(programId);
|
||||||
|
int[] linkStatus = new int[] {GLES20.GL_FALSE};
|
||||||
|
GLES20.glGetProgramiv(programId, GLES20.GL_LINK_STATUS, linkStatus, 0);
|
||||||
|
if (linkStatus[0] != GLES20.GL_TRUE) {
|
||||||
|
throwGlException(
|
||||||
|
"Unable to link shader program: \n" + GLES20.glGetProgramInfoLog(programId));
|
||||||
|
}
|
||||||
|
checkGlError();
|
||||||
|
|
||||||
|
GLES20.glUseProgram(programId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Deletes the program. Deleted programs cannot be used again. */
|
||||||
|
public void delete() {
|
||||||
|
GLES20.glDeleteProgram(programId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the location of an {@link Attribute}. */
|
||||||
|
public int getAttribLocation(String attributeName) {
|
||||||
|
return GLES20.glGetAttribLocation(programId, attributeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the location of a {@link Uniform}. */
|
||||||
|
public int getUniformLocation(String uniformName) {
|
||||||
|
return GLES20.glGetUniformLocation(programId, uniformName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the program's {@link Attribute}s. */
|
||||||
|
public Attribute[] getAttributes() {
|
||||||
|
int[] attributeCount = new int[1];
|
||||||
|
GLES20.glGetProgramiv(programId, GLES20.GL_ACTIVE_ATTRIBUTES, attributeCount, 0);
|
||||||
|
if (attributeCount[0] != 2) {
|
||||||
|
throw new IllegalStateException("Expected two attributes.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Attribute[] attributes = new Attribute[attributeCount[0]];
|
||||||
|
for (int i = 0; i < attributeCount[0]; i++) {
|
||||||
|
attributes[i] = createAttribute(i);
|
||||||
|
}
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the program's {@link Uniform}s. */
|
||||||
|
public Uniform[] getUniforms() {
|
||||||
|
int[] uniformCount = new int[1];
|
||||||
|
GLES20.glGetProgramiv(programId, GLES20.GL_ACTIVE_UNIFORMS, uniformCount, 0);
|
||||||
|
|
||||||
|
Uniform[] uniforms = new Uniform[uniformCount[0]];
|
||||||
|
for (int i = 0; i < uniformCount[0]; i++) {
|
||||||
|
uniforms[i] = createUniform(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return uniforms;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Attribute createAttribute(int index) {
|
||||||
|
int[] length = new int[1];
|
||||||
|
GLES20.glGetProgramiv(programId, GLES20.GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, length, 0);
|
||||||
|
|
||||||
|
int[] type = new int[1];
|
||||||
|
int[] size = new int[1];
|
||||||
|
byte[] nameBytes = new byte[length[0]];
|
||||||
|
int[] ignore = new int[1];
|
||||||
|
|
||||||
|
GLES20.glGetActiveAttrib(
|
||||||
|
programId, index, length[0], ignore, 0, size, 0, type, 0, nameBytes, 0);
|
||||||
|
String name = new String(nameBytes, 0, strlen(nameBytes));
|
||||||
|
int location = getAttribLocation(name);
|
||||||
|
|
||||||
|
return new Attribute(name, index, location);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Uniform createUniform(int index) {
|
||||||
|
int[] length = new int[1];
|
||||||
|
GLES20.glGetProgramiv(programId, GLES20.GL_ACTIVE_UNIFORM_MAX_LENGTH, length, 0);
|
||||||
|
|
||||||
|
int[] type = new int[1];
|
||||||
|
int[] size = new int[1];
|
||||||
|
byte[] nameBytes = new byte[length[0]];
|
||||||
|
int[] ignore = new int[1];
|
||||||
|
|
||||||
|
GLES20.glGetActiveUniform(
|
||||||
|
programId, index, length[0], ignore, 0, size, 0, type, 0, nameBytes, 0);
|
||||||
|
String name = new String(nameBytes, 0, strlen(nameBytes));
|
||||||
|
int location = getUniformLocation(name);
|
||||||
|
|
||||||
|
return new Uniform(name, location, type[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addShader(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, 0);
|
||||||
|
if (result[0] != GLES20.GL_TRUE) {
|
||||||
|
throwGlException(GLES20.glGetShaderInfoLog(shader) + ", source: " + glsl);
|
||||||
|
}
|
||||||
|
|
||||||
|
GLES20.glAttachShader(programId, shader);
|
||||||
|
GLES20.glDeleteShader(shader);
|
||||||
|
checkGlError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GL attribute, which can be attached to a buffer with {@link Attribute#setBuffer(float[], int)}.
|
* GL attribute, which can be attached to a buffer with {@link Attribute#setBuffer(float[], int)}.
|
||||||
*/
|
*/
|
||||||
@ -69,26 +223,11 @@ public final class GlUtil {
|
|||||||
@Nullable private Buffer buffer;
|
@Nullable private Buffer buffer;
|
||||||
private int size;
|
private int size;
|
||||||
|
|
||||||
/**
|
/* Creates a new Attribute. */
|
||||||
* Creates a new GL attribute.
|
public Attribute(String name, int index, int location) {
|
||||||
*
|
this.name = name;
|
||||||
* @param program The identifier of a compiled and linked GLSL shader program.
|
|
||||||
* @param index The index of the attribute. After this instance has been constructed, the name
|
|
||||||
* of the attribute is available via the {@link #name} field.
|
|
||||||
*/
|
|
||||||
public Attribute(int program, int index) {
|
|
||||||
int[] len = new int[1];
|
|
||||||
GLES20.glGetProgramiv(program, GLES20.GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, len, 0);
|
|
||||||
|
|
||||||
int[] type = new int[1];
|
|
||||||
int[] size = new int[1];
|
|
||||||
byte[] nameBytes = new byte[len[0]];
|
|
||||||
int[] ignore = new int[1];
|
|
||||||
|
|
||||||
GLES20.glGetActiveAttrib(program, index, len[0], ignore, 0, size, 0, type, 0, nameBytes, 0);
|
|
||||||
name = new String(nameBytes, 0, strlen(nameBytes));
|
|
||||||
location = GLES20.glGetAttribLocation(program, name);
|
|
||||||
this.index = index;
|
this.index = index;
|
||||||
|
this.location = location;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -138,28 +277,12 @@ public final class GlUtil {
|
|||||||
private int texId;
|
private int texId;
|
||||||
private int unit;
|
private int unit;
|
||||||
|
|
||||||
/**
|
/** Creates a new uniform. */
|
||||||
* Creates a new GL uniform.
|
public Uniform(String name, int location, int type) {
|
||||||
*
|
this.name = name;
|
||||||
* @param program The identifier of a compiled and linked GLSL shader program.
|
this.location = location;
|
||||||
* @param index The index of the uniform. After this instance has been constructed, the name of
|
this.type = type;
|
||||||
* the uniform is available via the {@link #name} field.
|
this.value = new float[16];
|
||||||
*/
|
|
||||||
public Uniform(int program, int index) {
|
|
||||||
int[] len = new int[1];
|
|
||||||
GLES20.glGetProgramiv(program, GLES20.GL_ACTIVE_UNIFORM_MAX_LENGTH, len, 0);
|
|
||||||
|
|
||||||
int[] type = new int[1];
|
|
||||||
int[] size = new int[1];
|
|
||||||
byte[] name = new byte[len[0]];
|
|
||||||
int[] ignore = new int[1];
|
|
||||||
|
|
||||||
GLES20.glGetActiveUniform(program, index, len[0], ignore, 0, size, 0, type, 0, name, 0);
|
|
||||||
this.name = new String(name, 0, strlen(name));
|
|
||||||
location = GLES20.glGetUniformLocation(program, this.name);
|
|
||||||
this.type = type[0];
|
|
||||||
|
|
||||||
value = new float[16];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -333,74 +456,6 @@ public final class GlUtil {
|
|||||||
Api17.focusSurface(eglDisplay, eglContext, surface, width, height);
|
Api17.focusSurface(eglDisplay, eglContext, surface, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a GL shader program from vertex and fragment shader code.
|
|
||||||
*
|
|
||||||
* @param vertexCode GLES20 vertex shader program as arrays of strings. Strings are joined by
|
|
||||||
* adding a new line character in between each of them.
|
|
||||||
* @param fragmentCode GLES20 fragment shader program as arrays of strings. Strings are joined by
|
|
||||||
* adding a new line character in between each of them.
|
|
||||||
* @return GLES20 program id.
|
|
||||||
*/
|
|
||||||
public static int compileProgram(String[] vertexCode, String[] fragmentCode) {
|
|
||||||
return compileProgram(TextUtils.join("\n", vertexCode), TextUtils.join("\n", fragmentCode));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a GL shader program from vertex and fragment shader code.
|
|
||||||
*
|
|
||||||
* @param vertexCode GLES20 vertex shader program.
|
|
||||||
* @param fragmentCode GLES20 fragment shader program.
|
|
||||||
* @return GLES20 program id.
|
|
||||||
*/
|
|
||||||
public static int compileProgram(String vertexCode, String fragmentCode) {
|
|
||||||
int program = GLES20.glCreateProgram();
|
|
||||||
checkGlError();
|
|
||||||
|
|
||||||
// Add the vertex and fragment shaders.
|
|
||||||
addShader(GLES20.GL_VERTEX_SHADER, vertexCode, program);
|
|
||||||
addShader(GLES20.GL_FRAGMENT_SHADER, fragmentCode, program);
|
|
||||||
|
|
||||||
// Link and check for errors.
|
|
||||||
GLES20.glLinkProgram(program);
|
|
||||||
int[] linkStatus = new int[] {GLES20.GL_FALSE};
|
|
||||||
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
|
|
||||||
if (linkStatus[0] != GLES20.GL_TRUE) {
|
|
||||||
throwGlException("Unable to link shader program: \n" + GLES20.glGetProgramInfoLog(program));
|
|
||||||
}
|
|
||||||
checkGlError();
|
|
||||||
|
|
||||||
return program;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the {@link Attribute}s in the specified {@code program}. */
|
|
||||||
public static Attribute[] getAttributes(int program) {
|
|
||||||
int[] attributeCount = new int[1];
|
|
||||||
GLES20.glGetProgramiv(program, GLES20.GL_ACTIVE_ATTRIBUTES, attributeCount, 0);
|
|
||||||
if (attributeCount[0] != 2) {
|
|
||||||
throw new IllegalStateException("Expected two attributes.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Attribute[] attributes = new Attribute[attributeCount[0]];
|
|
||||||
for (int i = 0; i < attributeCount[0]; i++) {
|
|
||||||
attributes[i] = new Attribute(program, i);
|
|
||||||
}
|
|
||||||
return attributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the {@link Uniform}s in the specified {@code program}. */
|
|
||||||
public static Uniform[] getUniforms(int program) {
|
|
||||||
int[] uniformCount = new int[1];
|
|
||||||
GLES20.glGetProgramiv(program, GLES20.GL_ACTIVE_UNIFORMS, uniformCount, 0);
|
|
||||||
|
|
||||||
Uniform[] uniforms = new Uniform[uniformCount[0]];
|
|
||||||
for (int i = 0; i < uniformCount[0]; i++) {
|
|
||||||
uniforms[i] = new Uniform(program, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
return uniforms;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a GL texture.
|
* Deletes a GL texture.
|
||||||
*
|
*
|
||||||
@ -479,22 +534,6 @@ public final class GlUtil {
|
|||||||
return texId[0];
|
return texId[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addShader(int type, String source, int program) {
|
|
||||||
int shader = GLES20.glCreateShader(type);
|
|
||||||
GLES20.glShaderSource(shader, source);
|
|
||||||
GLES20.glCompileShader(shader);
|
|
||||||
|
|
||||||
int[] result = new int[] {GLES20.GL_FALSE};
|
|
||||||
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, result, 0);
|
|
||||||
if (result[0] != GLES20.GL_TRUE) {
|
|
||||||
throwGlException(GLES20.glGetShaderInfoLog(shader) + ", source: " + source);
|
|
||||||
}
|
|
||||||
|
|
||||||
GLES20.glAttachShader(program, shader);
|
|
||||||
GLES20.glDeleteShader(shader);
|
|
||||||
checkGlError();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void throwGlException(String errorMsg) {
|
private static void throwGlException(String errorMsg) {
|
||||||
Log.e(TAG, errorMsg);
|
Log.e(TAG, errorMsg);
|
||||||
if (glAssertionsEnabled) {
|
if (glAssertionsEnabled) {
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.exoplayer.video;
|
package androidx.media3.exoplayer.video;
|
||||||
|
|
||||||
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.opengl.GLES20;
|
import android.opengl.GLES20;
|
||||||
import android.opengl.GLSurfaceView;
|
import android.opengl.GLSurfaceView;
|
||||||
@ -31,6 +33,7 @@ import javax.microedition.khronos.egl.EGLConfig;
|
|||||||
import javax.microedition.khronos.opengles.GL10;
|
import javax.microedition.khronos.opengles.GL10;
|
||||||
import org.checkerframework.checker.nullness.compatqual.NullableType;
|
import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GLSurfaceView implementing {@link VideoDecoderOutputBufferRenderer} for rendering {@link
|
* GLSurfaceView implementing {@link VideoDecoderOutputBufferRenderer} for rendering {@link
|
||||||
@ -143,7 +146,7 @@ public final class VideoDecoderGLSurfaceView extends GLSurfaceView
|
|||||||
// glDrawArrays uses it.
|
// glDrawArrays uses it.
|
||||||
private final FloatBuffer[] textureCoords;
|
private final FloatBuffer[] textureCoords;
|
||||||
|
|
||||||
private int program;
|
private GlUtil.@MonotonicNonNull Program program;
|
||||||
private int colorMatrixLocation;
|
private int colorMatrixLocation;
|
||||||
|
|
||||||
// Accessed only from the GL thread.
|
// Accessed only from the GL thread.
|
||||||
@ -164,9 +167,9 @@ public final class VideoDecoderGLSurfaceView extends GLSurfaceView
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
|
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
|
||||||
program = GlUtil.compileProgram(VERTEX_SHADER, FRAGMENT_SHADER);
|
program = new GlUtil.Program(VERTEX_SHADER, FRAGMENT_SHADER);
|
||||||
GLES20.glUseProgram(program);
|
program.use();
|
||||||
int posLocation = GLES20.glGetAttribLocation(program, "in_pos");
|
int posLocation = program.getAttribLocation("in_pos");
|
||||||
GLES20.glEnableVertexAttribArray(posLocation);
|
GLES20.glEnableVertexAttribArray(posLocation);
|
||||||
GLES20.glVertexAttribPointer(
|
GLES20.glVertexAttribPointer(
|
||||||
posLocation,
|
posLocation,
|
||||||
@ -175,14 +178,14 @@ public final class VideoDecoderGLSurfaceView extends GLSurfaceView
|
|||||||
/* normalized= */ false,
|
/* normalized= */ false,
|
||||||
/* stride= */ 0,
|
/* stride= */ 0,
|
||||||
TEXTURE_VERTICES);
|
TEXTURE_VERTICES);
|
||||||
texLocations[0] = GLES20.glGetAttribLocation(program, "in_tc_y");
|
texLocations[0] = program.getAttribLocation("in_tc_y");
|
||||||
GLES20.glEnableVertexAttribArray(texLocations[0]);
|
GLES20.glEnableVertexAttribArray(texLocations[0]);
|
||||||
texLocations[1] = GLES20.glGetAttribLocation(program, "in_tc_u");
|
texLocations[1] = program.getAttribLocation("in_tc_u");
|
||||||
GLES20.glEnableVertexAttribArray(texLocations[1]);
|
GLES20.glEnableVertexAttribArray(texLocations[1]);
|
||||||
texLocations[2] = GLES20.glGetAttribLocation(program, "in_tc_v");
|
texLocations[2] = program.getAttribLocation("in_tc_v");
|
||||||
GLES20.glEnableVertexAttribArray(texLocations[2]);
|
GLES20.glEnableVertexAttribArray(texLocations[2]);
|
||||||
GlUtil.checkGlError();
|
GlUtil.checkGlError();
|
||||||
colorMatrixLocation = GLES20.glGetUniformLocation(program, "mColorConversion");
|
colorMatrixLocation = program.getUniformLocation("mColorConversion");
|
||||||
GlUtil.checkGlError();
|
GlUtil.checkGlError();
|
||||||
setupTextures();
|
setupTextures();
|
||||||
GlUtil.checkGlError();
|
GlUtil.checkGlError();
|
||||||
@ -209,7 +212,7 @@ public final class VideoDecoderGLSurfaceView extends GLSurfaceView
|
|||||||
renderedOutputBuffer = pendingOutputBuffer;
|
renderedOutputBuffer = pendingOutputBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoDecoderOutputBuffer outputBuffer = Assertions.checkNotNull(renderedOutputBuffer);
|
VideoDecoderOutputBuffer outputBuffer = checkNotNull(renderedOutputBuffer);
|
||||||
|
|
||||||
// Set color matrix. Assume BT709 if the color space is unknown.
|
// Set color matrix. Assume BT709 if the color space is unknown.
|
||||||
float[] colorConversion = kColorConversion709;
|
float[] colorConversion = kColorConversion709;
|
||||||
@ -232,8 +235,8 @@ public final class VideoDecoderGLSurfaceView extends GLSurfaceView
|
|||||||
colorConversion,
|
colorConversion,
|
||||||
/* offset= */ 0);
|
/* offset= */ 0);
|
||||||
|
|
||||||
int[] yuvStrides = Assertions.checkNotNull(outputBuffer.yuvStrides);
|
int[] yuvStrides = checkNotNull(outputBuffer.yuvStrides);
|
||||||
ByteBuffer[] yuvPlanes = Assertions.checkNotNull(outputBuffer.yuvPlanes);
|
ByteBuffer[] yuvPlanes = checkNotNull(outputBuffer.yuvPlanes);
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
int h = (i == 0) ? outputBuffer.height : (outputBuffer.height + 1) / 2;
|
int h = (i == 0) ? outputBuffer.height : (outputBuffer.height + 1) / 2;
|
||||||
@ -296,10 +299,11 @@ public final class VideoDecoderGLSurfaceView extends GLSurfaceView
|
|||||||
surfaceView.requestRender();
|
surfaceView.requestRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresNonNull("program")
|
||||||
private void setupTextures() {
|
private void setupTextures() {
|
||||||
GLES20.glGenTextures(3, yuvTextures, /* offset= */ 0);
|
GLES20.glGenTextures(3, yuvTextures, /* offset= */ 0);
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
GLES20.glUniform1i(GLES20.glGetUniformLocation(program, TEXTURE_UNIFORMS[i]), i);
|
GLES20.glUniform1i(program.getUniformLocation(TEXTURE_UNIFORMS[i]), i);
|
||||||
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i);
|
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i);
|
||||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, yuvTextures[i]);
|
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, yuvTextures[i]);
|
||||||
GLES20.glTexParameterf(
|
GLES20.glTexParameterf(
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.exoplayer.video.spherical;
|
package androidx.media3.exoplayer.video.spherical;
|
||||||
|
|
||||||
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
import static androidx.media3.common.util.GlUtil.checkGlError;
|
import static androidx.media3.common.util.GlUtil.checkGlError;
|
||||||
|
|
||||||
import android.opengl.GLES11Ext;
|
import android.opengl.GLES11Ext;
|
||||||
@ -92,9 +93,9 @@ import java.nio.FloatBuffer;
|
|||||||
private int stereoMode;
|
private int stereoMode;
|
||||||
@Nullable private MeshData leftMeshData;
|
@Nullable private MeshData leftMeshData;
|
||||||
@Nullable private MeshData rightMeshData;
|
@Nullable private MeshData rightMeshData;
|
||||||
|
@Nullable private GlUtil.Program program;
|
||||||
|
|
||||||
// Program related GL items. These are only valid if program != 0.
|
// Program related GL items. These are only valid if program is non-null.
|
||||||
private int program;
|
|
||||||
private int mvpMatrixHandle;
|
private int mvpMatrixHandle;
|
||||||
private int uTexMatrixHandle;
|
private int uTexMatrixHandle;
|
||||||
private int positionHandle;
|
private int positionHandle;
|
||||||
@ -119,12 +120,12 @@ import java.nio.FloatBuffer;
|
|||||||
|
|
||||||
/** Initializes of the GL components. */
|
/** Initializes of the GL components. */
|
||||||
/* package */ void init() {
|
/* package */ void init() {
|
||||||
program = GlUtil.compileProgram(VERTEX_SHADER_CODE, FRAGMENT_SHADER_CODE);
|
program = new GlUtil.Program(VERTEX_SHADER_CODE, FRAGMENT_SHADER_CODE);
|
||||||
mvpMatrixHandle = GLES20.glGetUniformLocation(program, "uMvpMatrix");
|
mvpMatrixHandle = program.getUniformLocation("uMvpMatrix");
|
||||||
uTexMatrixHandle = GLES20.glGetUniformLocation(program, "uTexMatrix");
|
uTexMatrixHandle = program.getUniformLocation("uTexMatrix");
|
||||||
positionHandle = GLES20.glGetAttribLocation(program, "aPosition");
|
positionHandle = program.getAttribLocation("aPosition");
|
||||||
texCoordsHandle = GLES20.glGetAttribLocation(program, "aTexCoords");
|
texCoordsHandle = program.getAttribLocation("aTexCoords");
|
||||||
textureHandle = GLES20.glGetUniformLocation(program, "uTexture");
|
textureHandle = program.getUniformLocation("uTexture");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -143,7 +144,7 @@ import java.nio.FloatBuffer;
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Configure shader.
|
// Configure shader.
|
||||||
GLES20.glUseProgram(program);
|
checkNotNull(program).use();
|
||||||
checkGlError();
|
checkGlError();
|
||||||
|
|
||||||
GLES20.glEnableVertexAttribArray(positionHandle);
|
GLES20.glEnableVertexAttribArray(positionHandle);
|
||||||
@ -196,8 +197,9 @@ import java.nio.FloatBuffer;
|
|||||||
|
|
||||||
/** Cleans up the GL resources. */
|
/** Cleans up the GL resources. */
|
||||||
/* package */ void shutdown() {
|
/* package */ void shutdown() {
|
||||||
if (program != 0) {
|
if (program != null) {
|
||||||
GLES20.glDeleteProgram(program);
|
program.delete();
|
||||||
|
program = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,17 +229,19 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
GlUtil.focusSurface(
|
GlUtil.focusSurface(
|
||||||
eglDisplay, eglContext, eglSurface, decoderInputFormat.width, decoderInputFormat.height);
|
eglDisplay, eglContext, eglSurface, decoderInputFormat.width, decoderInputFormat.height);
|
||||||
decoderTextureId = GlUtil.createExternalTexture();
|
decoderTextureId = GlUtil.createExternalTexture();
|
||||||
String vertexShaderCode;
|
GlUtil.Program copyProgram;
|
||||||
String fragmentShaderCode;
|
|
||||||
try {
|
try {
|
||||||
vertexShaderCode = GlUtil.loadAsset(context, "shaders/blit_vertex_shader.glsl");
|
copyProgram =
|
||||||
fragmentShaderCode = GlUtil.loadAsset(context, "shaders/copy_external_fragment_shader.glsl");
|
new GlUtil.Program(
|
||||||
|
context,
|
||||||
|
/* vertexShaderFilePath= */ "shaders/blit_vertex_shader.glsl",
|
||||||
|
/* fragmentShaderFilePath= */ "shaders/copy_external_fragment_shader.glsl");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
int copyProgram = GlUtil.compileProgram(vertexShaderCode, fragmentShaderCode);
|
|
||||||
GLES20.glUseProgram(copyProgram);
|
copyProgram.use();
|
||||||
GlUtil.Attribute[] copyAttributes = GlUtil.getAttributes(copyProgram);
|
GlUtil.Attribute[] copyAttributes = copyProgram.getAttributes();
|
||||||
checkState(copyAttributes.length == 2, "Expected program to have two vertex attributes.");
|
checkState(copyAttributes.length == 2, "Expected program to have two vertex attributes.");
|
||||||
for (GlUtil.Attribute copyAttribute : copyAttributes) {
|
for (GlUtil.Attribute copyAttribute : copyAttributes) {
|
||||||
if (copyAttribute.name.equals("a_position")) {
|
if (copyAttribute.name.equals("a_position")) {
|
||||||
@ -265,7 +267,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
}
|
}
|
||||||
copyAttribute.bind();
|
copyAttribute.bind();
|
||||||
}
|
}
|
||||||
GlUtil.Uniform[] copyUniforms = GlUtil.getUniforms(copyProgram);
|
GlUtil.Uniform[] copyUniforms = copyProgram.getUniforms();
|
||||||
checkState(copyUniforms.length == 2, "Expected program to have two uniforms.");
|
checkState(copyUniforms.length == 2, "Expected program to have two uniforms.");
|
||||||
for (GlUtil.Uniform copyUniform : copyUniforms) {
|
for (GlUtil.Uniform copyUniform : copyUniforms) {
|
||||||
if (copyUniform.name.equals("tex_sampler")) {
|
if (copyUniform.name.equals("tex_sampler")) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user