Store LUT bitmap as texture with processor creation.

* Before this CL, the texture was stored during the construction of the LUT processor. This failed since if one creates a list of GlEffects on the application thread, the texture will get stored in the application thread during the effect creation and not on the GL thread, which executes the FrameProcessors.
* This is an issue since the executing thread then can't index from the texture stored on a different thread.

PiperOrigin-RevId: 476388021
(cherry picked from commit 44b9aec0b973b1cd5df31df32fff276f340e6ce4)
This commit is contained in:
leonwind 2022-09-23 16:19:43 +00:00 committed by microkatz
parent a7102992ba
commit 09e0dd8505
3 changed files with 37 additions and 18 deletions

View File

@ -356,12 +356,15 @@ public final class GlUtil {
// TODO(b/201293185): Consider handling adjustments for sizes > GL_MAX_TEXTURE_SIZE // TODO(b/201293185): Consider handling adjustments for sizes > GL_MAX_TEXTURE_SIZE
// (ex. downscaling appropriately) in a texture processor instead of asserting incorrect // (ex. downscaling appropriately) in a texture processor instead of asserting incorrect
// values. // values.
// For valid GL sizes, see: // For valid GL sizes, see:
// https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glTexImage2D.xml // https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glTexImage2D.xml
int[] maxTextureSizeBuffer = new int[1]; int[] maxTextureSizeBuffer = new int[1];
GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxTextureSizeBuffer, 0); GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxTextureSizeBuffer, 0);
int maxTextureSize = maxTextureSizeBuffer[0]; int maxTextureSize = maxTextureSizeBuffer[0];
checkState(
maxTextureSize > 0,
"Create a OpenGL context first or run the GL methods on an OpenGL thread.");
if (width < 0 || height < 0) { if (width < 0 || height < 0) {
throw new GlException("width or height is less than 0"); throw new GlException("width or height is less than 0");
} }

View File

@ -17,6 +17,7 @@
package androidx.media3.effect; package androidx.media3.effect;
import android.content.Context; import android.content.Context;
import androidx.annotation.WorkerThread;
import androidx.media3.common.FrameProcessingException; import androidx.media3.common.FrameProcessingException;
import androidx.media3.common.util.GlUtil; import androidx.media3.common.util.GlUtil;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
@ -40,7 +41,9 @@ public interface ColorLut extends GlEffect {
/** Releases the OpenGL texture of the LUT. */ /** Releases the OpenGL texture of the LUT. */
void release() throws GlUtil.GlException; void release() throws GlUtil.GlException;
/** This method must be executed on the same thread as other GL commands. */
@Override @Override
@WorkerThread
default ColorLutProcessor toGlTextureProcessor(Context context, boolean useHdr) default ColorLutProcessor toGlTextureProcessor(Context context, boolean useHdr)
throws FrameProcessingException { throws FrameProcessingException {
return new ColorLutProcessor(context, /* colorLut= */ this, useHdr); return new ColorLutProcessor(context, /* colorLut= */ this, useHdr);

View File

@ -23,6 +23,7 @@ import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.opengl.GLES20; import android.opengl.GLES20;
import android.opengl.GLUtils; import android.opengl.GLUtils;
import androidx.media3.common.Format;
import androidx.media3.common.FrameProcessingException; import androidx.media3.common.FrameProcessingException;
import androidx.media3.common.util.GlUtil; import androidx.media3.common.util.GlUtil;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
@ -31,8 +32,8 @@ import androidx.media3.common.util.Util;
/** Transforms the colors of a frame by applying the same color lookup table to each frame. */ /** Transforms the colors of a frame by applying the same color lookup table to each frame. */
@UnstableApi @UnstableApi
public class SingleColorLut implements ColorLut { public class SingleColorLut implements ColorLut {
private final int lutTextureId; private final Bitmap lut;
private final int length; private int lutTextureId;
/** /**
* Creates a new instance. * Creates a new instance.
@ -40,7 +41,7 @@ public class SingleColorLut implements ColorLut {
* <p>{@code lutCube} needs to be a {@code N x N x N} cube and each element is an integer * <p>{@code lutCube} needs to be a {@code N x N x N} cube and each element is an integer
* representing a color using the {@link Bitmap.Config#ARGB_8888} format. * representing a color using the {@link Bitmap.Config#ARGB_8888} format.
*/ */
public static SingleColorLut createFromCube(int[][][] lutCube) throws GlUtil.GlException { public static SingleColorLut createFromCube(int[][][] lutCube) {
checkArgument( checkArgument(
lutCube.length > 0 && lutCube[0].length > 0 && lutCube[0][0].length > 0, lutCube.length > 0 && lutCube[0].length > 0 && lutCube[0][0].length > 0,
"LUT must have three dimensions."); "LUT must have three dimensions.");
@ -60,7 +61,7 @@ public class SingleColorLut implements ColorLut {
* N^2}. Each element must be an integer representing a color using the {@link * N^2}. Each element must be an integer representing a color using the {@link
* Bitmap.Config#ARGB_8888} format. * Bitmap.Config#ARGB_8888} format.
*/ */
public static SingleColorLut createFromBitmap(Bitmap lut) throws GlUtil.GlException { public static SingleColorLut createFromBitmap(Bitmap lut) {
checkArgument( checkArgument(
lut.getWidth() * lut.getWidth() == lut.getHeight(), lut.getWidth() * lut.getWidth() == lut.getHeight(),
Util.formatInvariant( Util.formatInvariant(
@ -72,18 +73,9 @@ public class SingleColorLut implements ColorLut {
return new SingleColorLut(lut); return new SingleColorLut(lut);
} }
private SingleColorLut(Bitmap lut) throws GlUtil.GlException { private SingleColorLut(Bitmap lut) {
length = lut.getWidth(); this.lut = lut;
lutTextureId = storeLutAsTexture(lut); lutTextureId = Format.NO_VALUE;
}
private static int storeLutAsTexture(Bitmap bitmap) throws GlUtil.GlException {
int lutTextureId =
GlUtil.createTexture(
bitmap.getWidth(), bitmap.getHeight(), /* useHighPrecisionColorComponents= */ false);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, /* level= */ 0, bitmap, /* border= */ 0);
GlUtil.checkGlError();
return lutTextureId;
} }
/** /**
@ -136,14 +128,19 @@ public class SingleColorLut implements ColorLut {
Bitmap.Config.ARGB_8888); Bitmap.Config.ARGB_8888);
} }
/** Must be called after {@link #toGlTextureProcessor(Context, boolean)}. */
@Override @Override
public int getLutTextureId(long presentationTimeUs) { public int getLutTextureId(long presentationTimeUs) {
checkState(
lutTextureId != Format.NO_VALUE,
"The LUT has not been stored as a texture in OpenGL yet. You must to call"
+ " #toGlTextureProcessor() first.");
return lutTextureId; return lutTextureId;
} }
@Override @Override
public int getLength(long presentationTimeUs) { public int getLength(long presentationTimeUs) {
return length; return lut.getWidth();
} }
@Override @Override
@ -155,6 +152,22 @@ public class SingleColorLut implements ColorLut {
public ColorLutProcessor toGlTextureProcessor(Context context, boolean useHdr) public ColorLutProcessor toGlTextureProcessor(Context context, boolean useHdr)
throws FrameProcessingException { throws FrameProcessingException {
checkState(!useHdr, "HDR is currently not supported."); checkState(!useHdr, "HDR is currently not supported.");
try {
lutTextureId = storeLutAsTexture(lut);
} catch (GlUtil.GlException e) {
throw new FrameProcessingException("Could not store the LUT as a texture.", e);
}
return new ColorLutProcessor(context, /* colorLut= */ this, useHdr); return new ColorLutProcessor(context, /* colorLut= */ this, useHdr);
} }
private static int storeLutAsTexture(Bitmap bitmap) throws GlUtil.GlException {
int lutTextureId =
GlUtil.createTexture(
bitmap.getWidth(), bitmap.getHeight(), /* useHighPrecisionColorComponents= */ false);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, /* level= */ 0, bitmap, /* border= */ 0);
GlUtil.checkGlError();
return lutTextureId;
}
} }