Skip to content

Commit

Permalink
Fix HW rendering in Android V
Browse files Browse the repository at this point in the history
ShadowNativeBaseRecordingCanvas was missing the `callNativeMethodsByDefault`
annotation param, which meant that all the native methods were no-ops in
Android V.

Add some additional tests to capture this issue.

PiperOrigin-RevId: 628318882
  • Loading branch information
hoisie authored and tkiela1 committed Apr 26, 2024
1 parent b551e50 commit 41a9904
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -1,29 +1,73 @@
package org.robolectric.integrationtests.nativegraphics;

import static android.os.Build.VERSION_CODES.S;
import static com.google.common.truth.Truth.assertThat;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.PixelCopy;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.annotation.Config;

@RunWith(RobolectricTestRunner.class)
@Config(minSdk = S)
public class HardwareAcceleratedActivityRenderTest {
@Test
public void setupHardwareAcceleratedActivity() {
// This will exercise much of the HardwareRenderer / RenderNode / RecordingCanvas native code.
ActivityController<Activity> controller = Robolectric.buildActivity(Activity.class);
controller
.get()
.getWindow()
.setFlags(
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
controller.setup();
public void hardwareAcceleratedActivity_setup() throws Exception {
// Setting up an Activity is a smoke test that exercises much of the HardwareRenderer /
// RenderNode / RecordingCanvas native code.
Robolectric.setupActivity(HardwareAcceleratedActivity.class);
}

@Test
public void hardwareAcceleratedActivity_pixelCopy() throws Exception {
System.setProperty("robolectric.pixelCopyRenderMode", "hardware");
try {
HardwareAcceleratedActivity activity =
Robolectric.setupActivity(HardwareAcceleratedActivity.class);
Window window = activity.getWindow();
View decorView = window.getDecorView();
Bitmap bitmap =
Bitmap.createBitmap(decorView.getWidth(), decorView.getHeight(), Bitmap.Config.ARGB_8888);
CountDownLatch latch = new CountDownLatch(1);
PixelCopy.request(
window, bitmap, copyResult -> latch.countDown(), new Handler(Looper.getMainLooper()));
latch.await(1, TimeUnit.SECONDS);
assertThat(bitmap.getPixel(100, 100)).isEqualTo(Color.RED);
} finally {
System.clearProperty("robolectric.pixelCopyRenderMode");
}
}

static class HardwareAcceleratedActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// TODO(hoisie): manually setting these flags should not be required. Robolectric should
// set them automatically by default (they have been default since ICS).
getWindow()
.setFlags(
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
FrameLayout frameLayout = new FrameLayout(this);
frameLayout.setLayoutParams(
new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
frameLayout.setBackgroundColor(Color.RED);
setContentView(frameLayout);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
package org.robolectric.integrationtests.nativegraphics;

import static android.os.Build.VERSION_CODES.Q;
import static android.os.Build.VERSION_CODES.S;
import static com.google.common.truth.Truth.assertThat;

import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.HardwareRenderer;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.RecordingCanvas;
import android.graphics.RenderNode;
import android.media.Image;
import android.media.Image.Plane;
import android.media.ImageReader;
import android.view.Choreographer;
import android.view.Surface;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
Expand All @@ -24,4 +36,38 @@ public void choreographer_firstCalled() {
// HardwareRenderer.nHackySetRTAnimationsEnabled. Ensure that RNG is loaded if this happens.
var unused = Choreographer.getInstance();
}

@Test
@Config(minSdk = S)
public void imageReader_readsRenderedDisplayList() {
int width = 100;
int height = 100;

try (ImageReader imageReader =
ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 1)) {
HardwareRenderer renderer = new HardwareRenderer();
RenderNode displayList = createDisplayList(width, height);
Surface surface = imageReader.getSurface();
renderer.setSurface(surface);
Image nativeImage = imageReader.acquireNextImage();
renderer.setContentRoot(displayList);
renderer.createRenderRequest().syncAndDraw();
Plane[] planes = nativeImage.getPlanes();
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(planes[0].getBuffer());
surface.release();
assertThat(bitmap.getPixel(50, 50)).isEqualTo(Color.RED);
}
}

private static RenderNode createDisplayList(int width, int height) {
RenderNode renderNode = new RenderNode("RedNode");
renderNode.setPosition(0, 0, width, height);
RecordingCanvas canvas = renderNode.beginRecording();
Paint paint = new Paint();
paint.setColor(Color.RED);
canvas.drawRect(0, 0, width, height, paint);
renderNode.endRecording();
return renderNode;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
value = BaseRecordingCanvas.class,
minSdk = Q,
shadowPicker = Picker.class,
callNativeMethodsByDefault = true,
isInAndroidSdk = false)
public class ShadowNativeBaseRecordingCanvas extends ShadowNativeCanvas {

Expand Down

0 comments on commit 41a9904

Please sign in to comment.