Remove EglViewport usages

egloo2
Mattia Iavarone 5 years ago
parent 4f7e1366aa
commit 7f1d5b26b7
  1. 14
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/filter/BaseFilterTest.java
  2. 22
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/filter/MultiFilterTest.java
  3. 16
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/overlay/OverlayDrawerTest.java
  4. 5
      cameraview/src/main/java/com/otaliastudios/cameraview/internal/Issue514Workaround.java
  5. 103
      cameraview/src/main/java/com/otaliastudios/cameraview/internal/egl/EglViewport.java
  6. 27
      cameraview/src/main/java/com/otaliastudios/cameraview/overlay/OverlayDrawer.java
  7. 51
      cameraview/src/main/java/com/otaliastudios/cameraview/preview/GlCameraPreview.java

@ -8,7 +8,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.otaliastudios.cameraview.BaseEglTest;
import com.otaliastudios.cameraview.internal.egl.EglViewport;
import com.otaliastudios.cameraview.internal.GlTextureDrawer;
import com.otaliastudios.opengl.program.GlTextureProgram;
import org.junit.Test;
@ -111,18 +111,18 @@ public class BaseFilterTest extends BaseEglTest {
@Test
public void testDraw() {
// Use an EglViewport which cares about GL setup.
// Use a drawer which cares about GL setup.
filter = spy(new TestFilter());
EglViewport viewport = new EglViewport(filter);
int texture = viewport.createTexture();
GlTextureDrawer drawer = new GlTextureDrawer();
drawer.setFilter(filter);
float[] matrix = new float[16];
viewport.draw(0L, texture, matrix);
float[] matrix = drawer.getTextureTransform();
drawer.draw(0L);
verify(filter, times(1)).onPreDraw(0L, matrix);
verify(filter, times(1)).onDraw(0L);
verify(filter, times(1)).onPostDraw(0L);
viewport.release();
drawer.release();
}
@Test(expected = RuntimeException.class)

@ -11,7 +11,7 @@ import com.otaliastudios.cameraview.filters.AutoFixFilter;
import com.otaliastudios.cameraview.filters.BrightnessFilter;
import com.otaliastudios.cameraview.filters.DuotoneFilter;
import com.otaliastudios.cameraview.filters.VignetteFilter;
import com.otaliastudios.cameraview.internal.egl.EglViewport;
import com.otaliastudios.cameraview.internal.GlTextureDrawer;
import com.otaliastudios.opengl.program.GlProgram;
import org.junit.Test;
@ -142,11 +142,11 @@ public class MultiFilterTest extends BaseEglTest {
DuotoneFilter filter = spy(new DuotoneFilter());
MultiFilter multiFilter = new MultiFilter(filter);
multiFilter.setSize(WIDTH, HEIGHT);
EglViewport viewport = new EglViewport(multiFilter);
int texture = viewport.createTexture();
float[] matrix = new float[16];
viewport.draw(0L, texture, matrix);
viewport.release();
GlTextureDrawer drawer = new GlTextureDrawer();
drawer.setFilter(multiFilter);
float[] matrix = drawer.getTextureTransform();
drawer.draw(0L);
drawer.release();
// The child should have experienced the whole lifecycle.
verify(filter, atLeastOnce()).getVertexShader();
@ -165,7 +165,9 @@ public class MultiFilterTest extends BaseEglTest {
final DuotoneFilter filter2 = spy(new DuotoneFilter());
final MultiFilter multiFilter = new MultiFilter(filter1, filter2);
multiFilter.setSize(WIDTH, HEIGHT);
float[] matrix = new float[16];
GlTextureDrawer drawer = new GlTextureDrawer();
drawer.setFilter(multiFilter);
float[] matrix = drawer.getTextureTransform();
final int[] result = new int[1];
doAnswer(new Answer() {
@ -199,10 +201,8 @@ public class MultiFilterTest extends BaseEglTest {
}
}).when(filter2).draw(eq(0L), any(float[].class));
EglViewport viewport = new EglViewport(multiFilter);
int texture = viewport.createTexture();
viewport.draw(0L, texture, matrix);
viewport.release();
drawer.draw(0L);
drawer.release();
// Verify that both are drawn.
verify(filter1, times(1)).draw(0L, matrix);

@ -7,7 +7,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.otaliastudios.cameraview.BaseEglTest;
import com.otaliastudios.cameraview.internal.egl.EglViewport;
import com.otaliastudios.cameraview.size.Size;
import org.hamcrest.BaseMatcher;
@ -60,22 +59,19 @@ public class OverlayDrawerTest extends BaseEglTest {
@Test
public void testRender() {
OverlayDrawer drawer = new OverlayDrawer(mock(Overlay.class), new Size(WIDTH, HEIGHT));
drawer.mViewport = spy(drawer.mViewport);
drawer.mTextureDrawer = spy(drawer.mTextureDrawer);
drawer.draw(Overlay.Target.PICTURE_SNAPSHOT);
drawer.render(0L);
verify(drawer.mViewport, times(1)).draw(
0L,
drawer.mTextureId,
drawer.getTransform()
);
verify(drawer.mTextureDrawer, times(1)).draw(0L);
}
@Test
public void testRelease() {
OverlayDrawer drawer = new OverlayDrawer(mock(Overlay.class), new Size(WIDTH, HEIGHT));
EglViewport viewport = spy(drawer.mViewport);
drawer.mViewport = viewport;
drawer.mTextureDrawer = spy(drawer.mTextureDrawer);
drawer.release();
verify(viewport, times(1)).release();
verify(drawer.mTextureDrawer, times(1)).release();
}
}

@ -6,7 +6,6 @@ import android.graphics.SurfaceTexture;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.view.Surface;
import com.otaliastudios.cameraview.internal.egl.EglViewport;
import com.otaliastudios.cameraview.preview.RendererThread;
@ -84,7 +83,7 @@ import com.otaliastudios.cameraview.preview.RendererThread;
*
* This makes no sense, since overlaySurfaceTexture.updateTexImage() is setting it to
* overlayTextureId anyway, but it fixes the issue. Specifically, after any draw operation with
* {@link EglViewport}, the bound texture is reset to 0 so this must be undone here. We offer:
* {@link GlTextureDrawer}, the bound texture is reset to 0 so this must be undone here. We offer:
*
* - {@link #beforeOverlayUpdateTexImage()} to be called before the
* {@link SurfaceTexture#updateTexImage()} call
@ -92,7 +91,7 @@ import com.otaliastudios.cameraview.preview.RendererThread;
*
* Since updating and rendering can happen on different threads with a shared EGL context,
* in case they do, the {@link #beforeOverlayUpdateTexImage()}, the actual updateTexImage() and
* finally the {@link EglViewport} drawing operations should be synchronized with a lock.
* finally the {@link GlTextureDrawer} drawing operations should be synchronized with a lock.
*
* REFERENCES
* https://github.com/natario1/CameraView/issues/514

@ -1,103 +0,0 @@
package com.otaliastudios.cameraview.internal.egl;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.CameraLogger;
import com.otaliastudios.cameraview.filter.Filter;
import com.otaliastudios.cameraview.filter.NoFilter;
import com.otaliastudios.opengl.program.GlProgram;
public class EglViewport {
private final static CameraLogger LOG = CameraLogger.create(EglViewport.class.getSimpleName());
private int mProgramHandle = -1;
private int mTextureTarget;
private int mTextureUnit;
private Filter mFilter;
private Filter mPendingFilter;
public EglViewport() {
this(new NoFilter());
}
public EglViewport(@NonNull Filter filter) {
mTextureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES;
mTextureUnit = GLES20.GL_TEXTURE0;
mFilter = filter;
createProgram();
}
private void createProgram() {
mProgramHandle = GlProgram.create(mFilter.getVertexShader(),
mFilter.getFragmentShader());
mFilter.onCreate(mProgramHandle);
}
public void release() {
if (mProgramHandle != -1) {
mFilter.onDestroy();
GLES20.glDeleteProgram(mProgramHandle);
mProgramHandle = -1;
}
}
public int createTexture() {
int[] textures = new int[1];
GLES20.glGenTextures(1, textures, 0);
Egloo.checkGlError("glGenTextures");
int texId = textures[0];
GLES20.glActiveTexture(mTextureUnit);
GLES20.glBindTexture(mTextureTarget, texId);
Egloo.checkGlError("glBindTexture " + texId);
GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.GL_NEAREST);
GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S,
GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T,
GLES20.GL_CLAMP_TO_EDGE);
Egloo.checkGlError("glTexParameter");
return texId;
}
public void setFilter(@NonNull Filter filter) {
// TODO see if this is needed. If setFilter is always called from the correct GL thread,
// we don't need to wait for a new draw call (which might not even happen).
mPendingFilter = filter;
}
public void draw(long timestampUs, int textureId, float[] textureMatrix) {
if (mPendingFilter != null) {
release();
mFilter = mPendingFilter;
mPendingFilter = null;
createProgram();
}
Egloo.checkGlError("draw start");
// Select the program and the active texture.
GLES20.glUseProgram(mProgramHandle);
Egloo.checkGlError("glUseProgram");
GLES20.glActiveTexture(mTextureUnit);
GLES20.glBindTexture(mTextureTarget, textureId);
// Draw.
mFilter.draw(timestampUs, textureMatrix);
// Release.
GLES20.glBindTexture(mTextureTarget, 0);
GLES20.glUseProgram(0);
}
}

@ -12,8 +12,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.otaliastudios.cameraview.CameraLogger;
import com.otaliastudios.cameraview.internal.GlTextureDrawer;
import com.otaliastudios.cameraview.internal.Issue514Workaround;
import com.otaliastudios.cameraview.internal.egl.EglViewport;
import com.otaliastudios.cameraview.size.Size;
import java.nio.Buffer;
@ -27,7 +27,7 @@ import java.nio.Buffer;
* - Renders this into the current EGL window: {@link #render(long)}
* - Applies the {@link Issue514Workaround} the correct way
*
* In the future we might want to use a different approach than {@link EglViewport},
* In the future we might want to use a different approach than {@link GlTextureDrawer},
* {@link SurfaceTexture} and {@link GLES11Ext#GL_TEXTURE_EXTERNAL_OES},
* for example by using a regular {@link GLES20#GL_TEXTURE_2D} that might
* be filled through {@link GLES20#glTexImage2D(int, int, int, int, int, int, int, int, Buffer)}.
@ -40,22 +40,19 @@ public class OverlayDrawer {
private static final CameraLogger LOG = CameraLogger.create(TAG);
private Overlay mOverlay;
@VisibleForTesting int mTextureId;
private SurfaceTexture mSurfaceTexture;
private Surface mSurface;
private float[] mTransform = new float[16];
@VisibleForTesting EglViewport mViewport;
@VisibleForTesting GlTextureDrawer mTextureDrawer;
private Issue514Workaround mIssue514Workaround;
private final Object mIssue514WorkaroundLock = new Object();
public OverlayDrawer(@NonNull Overlay overlay, @NonNull Size size) {
mOverlay = overlay;
mViewport = new EglViewport();
mTextureId = mViewport.createTexture();
mSurfaceTexture = new SurfaceTexture(mTextureId);
mTextureDrawer = new GlTextureDrawer();
mSurfaceTexture = new SurfaceTexture(mTextureDrawer.getTexture().getId());
mSurfaceTexture.setDefaultBufferSize(size.getWidth(), size.getHeight());
mSurface = new Surface(mSurfaceTexture);
mIssue514Workaround = new Issue514Workaround(mTextureId);
mIssue514Workaround = new Issue514Workaround(mTextureDrawer.getTexture().getId());
}
/**
@ -77,7 +74,7 @@ public class OverlayDrawer {
mIssue514Workaround.beforeOverlayUpdateTexImage();
mSurfaceTexture.updateTexImage();
}
mSurfaceTexture.getTransformMatrix(mTransform);
mSurfaceTexture.getTransformMatrix(mTextureDrawer.getTextureTransform());
}
/**
@ -86,7 +83,7 @@ public class OverlayDrawer {
* @return the transform matrix
*/
public float[] getTransform() {
return mTransform;
return mTextureDrawer.getTextureTransform();
}
/**
@ -105,7 +102,7 @@ public class OverlayDrawer {
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
synchronized (mIssue514WorkaroundLock) {
mViewport.draw(timestampUs, mTextureId, mTransform);
mTextureDrawer.draw(timestampUs);
}
}
@ -125,9 +122,9 @@ public class OverlayDrawer {
mSurface.release();
mSurface = null;
}
if (mViewport != null) {
mViewport.release();
mViewport = null;
if (mTextureDrawer != null) {
mTextureDrawer.release();
mTextureDrawer = null;
}
}
}

@ -14,7 +14,7 @@ import android.view.View;
import android.view.ViewGroup;
import com.otaliastudios.cameraview.R;
import com.otaliastudios.cameraview.internal.egl.EglViewport;
import com.otaliastudios.cameraview.internal.GlTextureDrawer;
import com.otaliastudios.cameraview.filter.Filter;
import com.otaliastudios.cameraview.filter.NoFilter;
import com.otaliastudios.cameraview.size.AspectRatio;
@ -63,10 +63,8 @@ import javax.microedition.khronos.opengles.GL10;
public class GlCameraPreview extends FilterCameraPreview<GLSurfaceView, SurfaceTexture> {
private boolean mDispatched;
private final float[] mTransformMatrix = new float[16];
private int mOutputTextureId = 0;
private SurfaceTexture mInputSurfaceTexture;
private EglViewport mOutputViewport;
private GlTextureDrawer mOutputTextureDrawer;
// A synchronized set was not enough to avoid crashes, probably due to external classes
// removing the callback while this set is being iterated. CopyOnWriteArraySet solves this.
private final Set<RendererFrameCallback> mRendererFrameCallbacks = new CopyOnWriteArraySet<>();
@ -146,14 +144,15 @@ public class GlCameraPreview extends FilterCameraPreview<GLSurfaceView, SurfaceT
if (mCurrentFilter == null) {
mCurrentFilter = new NoFilter();
}
mOutputViewport = new EglViewport(mCurrentFilter);
mOutputTextureId = mOutputViewport.createTexture();
mInputSurfaceTexture = new SurfaceTexture(mOutputTextureId);
mOutputTextureDrawer = new GlTextureDrawer();
mOutputTextureDrawer.setFilter(mCurrentFilter);
final int textureId = mOutputTextureDrawer.getTexture().getId();
mInputSurfaceTexture = new SurfaceTexture(textureId);
getView().queueEvent(new Runnable() {
@Override
public void run() {
for (RendererFrameCallback callback : mRendererFrameCallbacks) {
callback.onRendererTextureCreated(mOutputTextureId);
callback.onRendererTextureCreated(textureId);
}
}
});
@ -176,10 +175,9 @@ public class GlCameraPreview extends FilterCameraPreview<GLSurfaceView, SurfaceT
mInputSurfaceTexture.release();
mInputSurfaceTexture = null;
}
mOutputTextureId = 0;
if (mOutputViewport != null) {
mOutputViewport.release();
mOutputViewport = null;
if (mOutputTextureDrawer != null) {
mOutputTextureDrawer.release();
mOutputTextureDrawer = null;
}
}
@ -207,17 +205,17 @@ public class GlCameraPreview extends FilterCameraPreview<GLSurfaceView, SurfaceT
// Latch the latest frame. If there isn't anything new,
// we'll just re-use whatever was there before.
final float[] transform = mOutputTextureDrawer.getTextureTransform();
mInputSurfaceTexture.updateTexImage();
mInputSurfaceTexture.getTransformMatrix(mTransformMatrix);
mInputSurfaceTexture.getTransformMatrix(transform);
// LOG.v("onDrawFrame:", "timestamp:", mInputSurfaceTexture.getTimestamp());
// For Camera2, apply the draw rotation.
// See TextureCameraPreview.setDrawRotation() for info.
if (mDrawRotation != 0) {
Matrix.translateM(mTransformMatrix, 0, 0.5F, 0.5F, 0);
Matrix.rotateM(mTransformMatrix, 0, mDrawRotation, 0, 0, 1);
Matrix.translateM(mTransformMatrix, 0, -0.5F, -0.5F, 0);
Matrix.translateM(transform, 0, 0.5F, 0.5F, 0);
Matrix.rotateM(transform, 0, mDrawRotation, 0, 0, 1);
Matrix.translateM(transform, 0, -0.5F, -0.5F, 0);
}
if (isCropping()) {
@ -228,11 +226,11 @@ public class GlCameraPreview extends FilterCameraPreview<GLSurfaceView, SurfaceT
// of the preview (not the center one).
float translX = (1F - mCropScaleX) / 2F;
float translY = (1F - mCropScaleY) / 2F;
Matrix.translateM(mTransformMatrix, 0, translX, translY, 0);
Matrix.scaleM(mTransformMatrix, 0, mCropScaleX, mCropScaleY, 1);
Matrix.translateM(transform, 0, translX, translY, 0);
Matrix.scaleM(transform, 0, mCropScaleX, mCropScaleY, 1);
}
mOutputViewport.draw(mInputSurfaceTexture.getTimestamp() / 1000L,
mOutputTextureId, mTransformMatrix);
mOutputTextureDrawer.draw(mInputSurfaceTexture.getTimestamp() / 1000L);
for (RendererFrameCallback callback : mRendererFrameCallbacks) {
callback.onRendererFrame(mInputSurfaceTexture, mCropScaleX, mCropScaleY);
}
@ -301,7 +299,10 @@ public class GlCameraPreview extends FilterCameraPreview<GLSurfaceView, SurfaceT
@Override
public void run() {
mRendererFrameCallbacks.add(callback);
if (mOutputTextureId != 0) callback.onRendererTextureCreated(mOutputTextureId);
if (mOutputTextureDrawer != null) {
int textureId = mOutputTextureDrawer.getTexture().getId();
callback.onRendererTextureCreated(textureId);
}
callback.onRendererFilterChanged(mCurrentFilter);
}
});
@ -322,7 +323,7 @@ public class GlCameraPreview extends FilterCameraPreview<GLSurfaceView, SurfaceT
*/
@SuppressWarnings("unused")
protected int getTextureId() {
return mOutputTextureId;
return mOutputTextureDrawer != null ? mOutputTextureDrawer.getTexture().getId() : -1;
}
/**
@ -354,8 +355,8 @@ public class GlCameraPreview extends FilterCameraPreview<GLSurfaceView, SurfaceT
getView().queueEvent(new Runnable() {
@Override
public void run() {
if (mOutputViewport != null) {
mOutputViewport.setFilter(filter);
if (mOutputTextureDrawer != null) {
mOutputTextureDrawer.setFilter(filter);
}
for (RendererFrameCallback callback : mRendererFrameCallbacks) {
callback.onRendererFilterChanged(filter);

Loading…
Cancel
Save