Commit b86088bb authored by Bo Liu's avatar Bo Liu Committed by Commit Bot

aw: Add a smoke test for real hardware rendering

Test renders 4 colors in 4 quadrants and use glReadPixels to read the
center pixel of each quadrant and and check if the rendering result
matches expectation.

Change-Id: If7b89506c1b84448e98f18b58c85aecff99f04bc
Reviewed-on: https://chromium-review.googlesource.com/c/1476120
Commit-Queue: Bo <boliu@chromium.org>
Reviewed-by: default avatarTobias Sargeant <tobiasjs@chromium.org>
Cr-Commit-Position: refs/heads/master@{#634715}
parent d9747f9b
...@@ -11,6 +11,7 @@ import static org.chromium.android_webview.test.OnlyRunIn.ProcessMode.SINGLE_PRO ...@@ -11,6 +11,7 @@ import static org.chromium.android_webview.test.OnlyRunIn.ProcessMode.SINGLE_PRO
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
...@@ -755,6 +756,68 @@ public class AwContentsTest { ...@@ -755,6 +756,68 @@ public class AwContentsTest {
Assert.assertEquals(0, consoleHelper.getMessages().size()); Assert.assertEquals(0, consoleHelper.getMessages().size());
} }
@Test
@Feature({"AndroidWebView"})
@SmallTest
public void testHardwareRenderingSmokeTest() throws Throwable {
AwTestContainerView testView =
mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
final AwContents awContents = testView.getAwContents();
String html = "<html>"
+ " <body style=\""
+ " padding: 0;"
+ " margin: 0;"
+ " display: grid;"
+ " display: grid;"
+ " grid-template-columns: 50% 50%;"
+ " grid-template-rows: 50% 50%;\">"
+ " <div style=\"background-color: rgb(255, 0, 0);\"></div>"
+ " <div style=\"background-color: rgb(0, 255, 0);\"></div>"
+ " <div style=\"background-color: rgb(0, 0, 255);\"></div>"
+ " <div style=\"background-color: rgb(128, 128, 128);\"></div>"
+ " </body>"
+ "</html>";
mActivityTestRule.loadDataSync(testView.getAwContents(),
mContentsClient.getOnPageFinishedHelper(), html, "text/html", false);
mActivityTestRule.waitForVisualStateCallback(testView.getAwContents());
// Poll for 10s in case raster is slow.
final Object lock = new Object();
final Object[] resultHolder = new Object[1];
for (int i = 0; i < 100; ++i) {
mActivityTestRule.runOnUiThread(() -> {
testView.readbackQuadrantColors((int[] result) -> {
synchronized (lock) {
resultHolder[0] = result;
lock.notifyAll();
}
});
});
int[] quadrantColors;
synchronized (lock) {
while (resultHolder[0] == null) {
lock.wait();
}
quadrantColors = (int[]) resultHolder[0];
}
if (Color.rgb(255, 0, 0) == quadrantColors[0]
&& Color.rgb(0, 255, 0) == quadrantColors[1]
&& Color.rgb(0, 0, 255) == quadrantColors[2]
&& Color.rgb(128, 128, 128) == quadrantColors[3]) {
return;
}
Thread.sleep(100);
}
// If this test is failing for your CL, then chances are your change is breaking Android
// WebView hardware rendering. Please build the "real" webview and check if this is the
// case and if so, fix your CL.
int[] quadrantColors = (int[]) resultHolder[0];
Assert.assertEquals(Color.rgb(255, 0, 0), quadrantColors[0]);
Assert.assertEquals(Color.rgb(0, 255, 0), quadrantColors[1]);
Assert.assertEquals(Color.rgb(0, 0, 255), quadrantColors[2]);
Assert.assertEquals(Color.rgb(128, 128, 128), quadrantColors[3]);
}
@Test @Test
@Feature({"AndroidWebView"}) @Feature({"AndroidWebView"})
@SmallTest @SmallTest
......
...@@ -9,6 +9,7 @@ import android.content.Context; ...@@ -9,6 +9,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PixelFormat; import android.graphics.PixelFormat;
import android.graphics.Rect; import android.graphics.Rect;
import android.opengl.GLSurfaceView; import android.opengl.GLSurfaceView;
...@@ -27,8 +28,11 @@ import android.widget.FrameLayout; ...@@ -27,8 +28,11 @@ import android.widget.FrameLayout;
import org.chromium.android_webview.AwContents; import org.chromium.android_webview.AwContents;
import org.chromium.android_webview.AwDrawFnImpl; import org.chromium.android_webview.AwDrawFnImpl;
import org.chromium.android_webview.shell.DrawFn; import org.chromium.android_webview.shell.DrawFn;
import org.chromium.base.Callback;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
import java.nio.ByteBuffer;
import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10; import javax.microedition.khronos.opengles.GL10;
...@@ -55,10 +59,12 @@ public class AwTestContainerView extends FrameLayout { ...@@ -55,10 +59,12 @@ public class AwTestContainerView extends FrameLayout {
// are protected by it. // are protected by it.
private final Object mSyncLock = new Object(); private final Object mSyncLock = new Object();
private int mFunctor; private int mFunctor;
private int mLastDrawnFunctor;
private boolean mSyncDone; private boolean mSyncDone;
private boolean mPendingDestroy; private boolean mPendingDestroy;
private int mLastScrollX; private int mLastScrollX;
private int mLastScrollY; private int mLastScrollY;
private Callback<int[]> mQuadrantReadbackCallback;
// Only used by drawGL on render thread to store the value of scroll offsets at most recent // Only used by drawGL on render thread to store the value of scroll offsets at most recent
// sync for subsequent draws. // sync for subsequent draws.
...@@ -80,7 +86,7 @@ public class AwTestContainerView extends FrameLayout { ...@@ -80,7 +86,7 @@ public class AwTestContainerView extends FrameLayout {
@Override @Override
public void onDrawFrame(GL10 gl) { public void onDrawFrame(GL10 gl) {
HardwareView.this.onDrawFrame(mWidth, mHeight); HardwareView.this.onDrawFrame(gl, mWidth, mHeight);
} }
@Override @Override
...@@ -98,6 +104,14 @@ public class AwTestContainerView extends FrameLayout { ...@@ -98,6 +104,14 @@ public class AwTestContainerView extends FrameLayout {
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
} }
public void readbackQuadrantColors(Callback<int[]> callback) {
synchronized (mSyncLock) {
assert mQuadrantReadbackCallback == null;
mQuadrantReadbackCallback = callback;
}
super.requestRender();
}
public boolean isReadyToRender() { public boolean isReadyToRender() {
return mHaveSurface; return mHaveSurface;
} }
...@@ -169,32 +183,69 @@ public class AwTestContainerView extends FrameLayout { ...@@ -169,32 +183,69 @@ public class AwTestContainerView extends FrameLayout {
} }
} }
public void onDrawFrame(int width, int height) { public void onDrawFrame(GL10 gl, int width, int height) {
int functor; int functor;
int scrollX; int scrollX;
int scrollY; int scrollY;
synchronized (mSyncLock) { synchronized (mSyncLock) {
functor = mFunctor;
mFunctor = 0;
scrollX = mLastScrollX; scrollX = mLastScrollX;
scrollY = mLastScrollY; scrollY = mLastScrollY;
if (functor != 0) { if (mFunctor != 0) {
assert !mSyncDone; assert !mSyncDone;
functor = mFunctor;
mLastDrawnFunctor = mFunctor;
mFunctor = 0;
DrawFn.sync(functor, false); DrawFn.sync(functor, false);
mSyncDone = true; mSyncDone = true;
mSyncLock.notifyAll(); mSyncLock.notifyAll();
} else if (mPendingDestroy) { } else {
DrawFn.destroyReleased(); functor = mLastDrawnFunctor;
mPendingDestroy = false; if (mPendingDestroy) {
mSyncLock.notifyAll(); DrawFn.destroyReleased();
return; mPendingDestroy = false;
mLastDrawnFunctor = 0;
mSyncLock.notifyAll();
return;
}
} }
} }
if (functor != 0) { if (functor != 0) {
DrawFn.drawGL(functor, width, height, scrollX, scrollY); DrawFn.drawGL(functor, width, height, scrollX, scrollY);
Callback<int[]> quadrantReadbackCallback = null;
synchronized (mSyncLock) {
if (mQuadrantReadbackCallback != null) {
quadrantReadbackCallback = mQuadrantReadbackCallback;
mQuadrantReadbackCallback = null;
}
}
if (quadrantReadbackCallback != null) {
int quadrantColors[] = new int[4];
int quarterWidth = width / 4;
int quarterHeight = height / 4;
ByteBuffer buffer = ByteBuffer.allocate(4);
gl.glReadPixels(quarterWidth, quarterHeight * 3, 1, 1, GL10.GL_RGBA,
GL10.GL_UNSIGNED_BYTE, buffer);
quadrantColors[0] = readbackToColor(buffer);
gl.glReadPixels(quarterWidth * 3, quarterHeight * 3, 1, 1, GL10.GL_RGBA,
GL10.GL_UNSIGNED_BYTE, buffer);
quadrantColors[1] = readbackToColor(buffer);
gl.glReadPixels(quarterWidth, quarterHeight, 1, 1, GL10.GL_RGBA,
GL10.GL_UNSIGNED_BYTE, buffer);
quadrantColors[2] = readbackToColor(buffer);
gl.glReadPixels(quarterWidth * 3, quarterHeight, 1, 1, GL10.GL_RGBA,
GL10.GL_UNSIGNED_BYTE, buffer);
quadrantColors[3] = readbackToColor(buffer);
quadrantReadbackCallback.onResult(quadrantColors);
}
} }
} }
private int readbackToColor(ByteBuffer buffer) {
return Color.argb(buffer.get(3) & 0xff, buffer.get(0) & 0xff, buffer.get(1) & 0xff,
buffer.get(2) & 0xff);
}
} }
private static boolean sCreatedOnce; private static boolean sCreatedOnce;
...@@ -234,6 +285,14 @@ public class AwTestContainerView extends FrameLayout { ...@@ -234,6 +285,14 @@ public class AwTestContainerView extends FrameLayout {
return mHardwareView != null; return mHardwareView != null;
} }
/**
* Use glReadPixels to get 4 pixels from center of 4 quadrants. Result is in row-major order.
*/
public void readbackQuadrantColors(Callback<int[]> callback) {
assert isBackedByHardwareView();
mHardwareView.readbackQuadrantColors(callback);
}
public WebContents getWebContents() { public WebContents getWebContents() {
return mAwContents.getWebContents(); return mAwContents.getWebContents();
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment