1 /*
2 * Copyright 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18 #define LOG_TAG "FrameRateCtsActivity"
19
20 #include <android/hardware_buffer.h>
21 #include <android/hardware_buffer_jni.h>
22 #include <android/log.h>
23 #include <android/native_window.h>
24 #include <android/native_window_jni.h>
25 #include <android/rect.h>
26 #include <android/surface_control.h>
27 #include <jni.h>
28 #include <utils/Errors.h>
29
30 #include <array>
31 #include <string>
32
33 #define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
34
35 using namespace android;
36
37 namespace {
38
39 class Buffer {
40 public:
Buffer(int width,int height,int rgbaColor)41 Buffer(int width, int height, int rgbaColor) {
42 AHardwareBuffer_Desc desc;
43 memset(&desc, 0, sizeof(desc));
44 desc.width = width;
45 desc.height = height;
46 desc.layers = 1;
47 desc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
48 desc.usage =
49 AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
50 int rc = AHardwareBuffer_allocate(&desc, &mBuffer);
51 if (rc < 0 || mBuffer == nullptr) {
52 ALOGE("AHardwareBuffer_allocate failed: %s (%d)", strerror(-rc), -rc);
53 return;
54 }
55 int8_t* buf = nullptr;
56 int32_t bytesPerPixel = 0;
57 int32_t bytesPerStride = 0;
58 std::string lockFunctionName = "AHardwareBuffer_lockAndGetInfo";
59 rc = AHardwareBuffer_lockAndGetInfo(mBuffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY,
60 /*fence=*/-1,
61 /*rect=*/nullptr, reinterpret_cast<void**>(&buf),
62 &bytesPerPixel, &bytesPerStride);
63 if (rc == INVALID_OPERATION) {
64 // Older versions of gralloc don't implement AHardwareBuffer_lockAndGetInfo(). Fall back
65 // to AHardwareBuffer_lock().
66 lockFunctionName = "AHardwareBuffer_lock";
67 bytesPerPixel = 4;
68 bytesPerStride = width * bytesPerPixel;
69 rc = AHardwareBuffer_lock(mBuffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY,
70 /*fence=*/-1,
71 /*rect=*/nullptr, reinterpret_cast<void**>(&buf));
72 }
73 if (rc < 0 || buf == nullptr) {
74 ALOGE("%s failed: %s (%d)", lockFunctionName.c_str(), strerror(-rc), -rc);
75 AHardwareBuffer_release(mBuffer);
76 mBuffer = nullptr;
77 return;
78 }
79
80 // There's a bug where Qualcomm returns pixels per stride instead of bytes per stride. See
81 // b/149601846.
82 if (bytesPerStride < width * bytesPerPixel) {
83 bytesPerStride *= bytesPerPixel;
84 }
85
86 int8_t* rgbaBytes = reinterpret_cast<int8_t*>(&rgbaColor);
87 for (int row = 0; row < height; row++) {
88 int8_t* ptr = buf + row * bytesPerStride;
89 for (int col = 0; col < width; col++, ptr += bytesPerPixel) {
90 ptr[0] = rgbaBytes[0];
91 ptr[1] = rgbaBytes[1];
92 ptr[2] = rgbaBytes[2];
93 ptr[3] = rgbaBytes[3];
94 }
95 }
96
97 rc = AHardwareBuffer_unlock(mBuffer, /*fence=*/nullptr);
98 if (rc < 0) {
99 ALOGE("AHardwareBuffer_unlock failed: %s (%d)", strerror(-rc), -rc);
100 AHardwareBuffer_release(mBuffer);
101 mBuffer = nullptr;
102 return;
103 }
104 }
105
~Buffer()106 ~Buffer() {
107 if (mBuffer) {
108 AHardwareBuffer_release(mBuffer);
109 }
110 }
111
isValid() const112 bool isValid() const { return mBuffer != nullptr; }
getBuffer() const113 AHardwareBuffer* getBuffer() const { return mBuffer; }
114
115 private:
116 AHardwareBuffer* mBuffer = nullptr;
117 };
118
119 class Surface {
120 public:
Surface(ANativeWindow * parentWindow,const std::string & name,int left,int top,int right,int bottom)121 Surface(ANativeWindow* parentWindow, const std::string& name, int left, int top, int right,
122 int bottom) {
123 mSurface = ASurfaceControl_createFromWindow(parentWindow, name.c_str());
124 if (mSurface == nullptr) {
125 return;
126 }
127
128 mWidth = right - left;
129 mHeight = bottom - top;
130 ARect source{0, 0, mWidth, mHeight};
131 ARect dest{left, top, right, bottom};
132 ASurfaceTransaction* transaction = ASurfaceTransaction_create();
133 ASurfaceTransaction_setGeometry(transaction, mSurface, source, dest,
134 ANATIVEWINDOW_TRANSFORM_IDENTITY);
135 ASurfaceTransaction_apply(transaction);
136 ASurfaceTransaction_delete(transaction);
137 }
138
~Surface()139 ~Surface() {
140 ASurfaceTransaction* transaction = ASurfaceTransaction_create();
141 ASurfaceTransaction_reparent(transaction, mSurface, nullptr);
142 ASurfaceTransaction_apply(transaction);
143 ASurfaceTransaction_delete(transaction);
144 ASurfaceControl_release(mSurface);
145 }
146
isValid() const147 bool isValid() const { return mSurface != nullptr; }
getSurfaceControl() const148 ASurfaceControl* getSurfaceControl() const { return mSurface; }
getWidth() const149 int getWidth() const { return mWidth; }
getHeight() const150 int getHeight() const { return mHeight; }
151
152 private:
153 ASurfaceControl* mSurface = nullptr;
154 int mWidth = 0;
155 int mHeight = 0;
156 };
157
158 struct ANativeWindowRAII {
ANativeWindowRAII__anonb7bd83a40111::ANativeWindowRAII159 ANativeWindowRAII(ANativeWindow *anw = nullptr) :
160 mNw(anw) {
161 }
~ANativeWindowRAII__anonb7bd83a40111::ANativeWindowRAII162 ~ANativeWindowRAII() {
163 if (mNw != nullptr) {
164 ANativeWindow_release(mNw);
165 }
166 }
167 ANativeWindow* mNw;
168 };
169
nativeWindowSetFrameRate(JNIEnv * env,jclass,jobject jSurface,jfloat frameRate,jint compatibility,jint changeFrameRateStrategy)170 jint nativeWindowSetFrameRate(JNIEnv* env, jclass, jobject jSurface, jfloat frameRate,
171 jint compatibility, jint changeFrameRateStrategy) {
172 ANativeWindowRAII window;
173 if (jSurface) {
174 window.mNw = ANativeWindow_fromSurface(env, jSurface);
175 }
176 if (changeFrameRateStrategy == ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS) {
177 return ANativeWindow_setFrameRate(window.mNw, frameRate, compatibility);
178 }
179
180 return ANativeWindow_setFrameRateWithChangeStrategy(window.mNw, frameRate, compatibility,
181 changeFrameRateStrategy);
182 }
183
nativeWindowClearFrameRate(JNIEnv * env,jclass,jobject jSurface)184 jint nativeWindowClearFrameRate(JNIEnv* env, jclass, jobject jSurface) {
185 ANativeWindowRAII window;
186 if (jSurface) {
187 window.mNw = ANativeWindow_fromSurface(env, jSurface);
188 }
189
190 return ANativeWindow_clearFrameRate(window.mNw);
191 }
192
surfaceControlCreate(JNIEnv * env,jclass,jobject jParentSurface,jstring jName,jint left,jint top,jint right,jint bottom)193 jlong surfaceControlCreate(JNIEnv* env, jclass, jobject jParentSurface, jstring jName, jint left,
194 jint top, jint right, jint bottom) {
195 if (!jParentSurface || !jName) {
196 return 0;
197 }
198 ANativeWindowRAII parentWindow = ANativeWindow_fromSurface(env, jParentSurface);
199 if (!parentWindow.mNw) {
200 return 0;
201 }
202
203 const char* name = env->GetStringUTFChars(jName, nullptr);
204 std::string strName = name;
205 env->ReleaseStringUTFChars(jName, name);
206
207 Surface* surface = new Surface(parentWindow.mNw, strName, left, top, right, bottom);
208 if (!surface->isValid()) {
209 delete surface;
210 return 0;
211 }
212
213 return reinterpret_cast<jlong>(surface);
214 }
215
surfaceControlDestroy(JNIEnv *,jclass,jlong surfaceControlLong)216 void surfaceControlDestroy(JNIEnv*, jclass, jlong surfaceControlLong) {
217 if (surfaceControlLong == 0) {
218 return;
219 }
220 delete reinterpret_cast<Surface*>(surfaceControlLong);
221 }
222
surfaceControlSetFrameRate(JNIEnv *,jclass,jlong surfaceControlLong,jfloat frameRate,jint compatibility,jint changeFrameRateStrategy)223 void surfaceControlSetFrameRate(JNIEnv*, jclass, jlong surfaceControlLong, jfloat frameRate,
224 jint compatibility, jint changeFrameRateStrategy) {
225 ASurfaceControl* surfaceControl =
226 reinterpret_cast<Surface*>(surfaceControlLong)->getSurfaceControl();
227 ASurfaceTransaction* transaction = ASurfaceTransaction_create();
228 if (changeFrameRateStrategy == ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS) {
229 ASurfaceTransaction_setFrameRate(transaction, surfaceControl, frameRate,
230 compatibility);
231 } else {
232 ASurfaceTransaction_setFrameRateWithChangeStrategy(transaction, surfaceControl, frameRate,
233 compatibility, changeFrameRateStrategy);
234 }
235 ASurfaceTransaction_apply(transaction);
236 ASurfaceTransaction_delete(transaction);
237 }
238
surfaceControlClearFrameRate(JNIEnv *,jclass,jlong surfaceControlLong)239 void surfaceControlClearFrameRate(JNIEnv*, jclass, jlong surfaceControlLong) {
240 ASurfaceControl* surfaceControl =
241 reinterpret_cast<Surface*>(surfaceControlLong)->getSurfaceControl();
242 ASurfaceTransaction* transaction = ASurfaceTransaction_create();
243 ASurfaceTransaction_clearFrameRate(transaction, surfaceControl);
244 ASurfaceTransaction_apply(transaction);
245 ASurfaceTransaction_delete(transaction);
246 }
247
surfaceControlSetVisibility(JNIEnv *,jclass,jlong surfaceControlLong,jboolean visible)248 void surfaceControlSetVisibility(JNIEnv*, jclass, jlong surfaceControlLong, jboolean visible) {
249 ASurfaceControl* surfaceControl =
250 reinterpret_cast<Surface*>(surfaceControlLong)->getSurfaceControl();
251 ASurfaceTransaction* transaction = ASurfaceTransaction_create();
252 ASurfaceTransaction_setVisibility(transaction, surfaceControl,
253 visible == JNI_TRUE ? ASURFACE_TRANSACTION_VISIBILITY_SHOW
254 : ASURFACE_TRANSACTION_VISIBILITY_HIDE);
255 ASurfaceTransaction_apply(transaction);
256 ASurfaceTransaction_delete(transaction);
257 }
258
surfaceControlPostBuffer(JNIEnv *,jclass,jlong surfaceControlLong,jint argbColor)259 jboolean surfaceControlPostBuffer(JNIEnv*, jclass, jlong surfaceControlLong, jint argbColor) {
260 Surface* surface = reinterpret_cast<Surface*>(surfaceControlLong);
261 ASurfaceControl* surfaceControl = surface->getSurfaceControl();
262 // Android's Color.* values are represented as ARGB. Convert to RGBA.
263 int32_t rgbaColor = 0;
264 int8_t* rgbaColorBytes = reinterpret_cast<int8_t*>(&rgbaColor);
265 rgbaColorBytes[0] = (argbColor >> 16) & 0xff;
266 rgbaColorBytes[1] = (argbColor >> 8) & 0xff;
267 rgbaColorBytes[2] = (argbColor >> 0) & 0xff;
268 rgbaColorBytes[3] = (argbColor >> 24) & 0xff;
269
270 Buffer buffer(surface->getWidth(), surface->getHeight(), rgbaColor);
271 if (!buffer.isValid()) {
272 return JNI_FALSE;
273 }
274
275 ASurfaceTransaction* transaction = ASurfaceTransaction_create();
276 ASurfaceTransaction_setBuffer(transaction, surfaceControl, buffer.getBuffer(), -1);
277 ASurfaceTransaction_apply(transaction);
278 ASurfaceTransaction_delete(transaction);
279 return JNI_TRUE;
280 }
281
282 const std::array<JNINativeMethod, 8> JNI_METHODS = {{
283 {"nativeWindowSetFrameRate", "(Landroid/view/Surface;FII)I",
284 (void*)nativeWindowSetFrameRate},
285 {"nativeSurfaceControlCreate", "(Landroid/view/Surface;Ljava/lang/String;IIII)J",
286 (void*)surfaceControlCreate},
287 {"nativeSurfaceControlDestroy", "(J)V", (void*)surfaceControlDestroy},
288 {"nativeSurfaceControlSetFrameRate", "(JFII)V", (void*)surfaceControlSetFrameRate},
289 {"nativeSurfaceControlSetVisibility", "(JZ)V", (void*)surfaceControlSetVisibility},
290 {"nativeSurfaceControlPostBuffer", "(JI)Z", (void*)surfaceControlPostBuffer},
291 {"nativeWindowClearFrameRate", "(Landroid/view/Surface;)I",
292 (void*)nativeWindowClearFrameRate},
293 {"nativeSurfaceControlClearFrameRate", "(J)V", (void*)surfaceControlClearFrameRate},
294 }};
295
296 } // namespace
297
register_android_graphics_cts_FrameRateCtsActivity(JNIEnv * env)298 int register_android_graphics_cts_FrameRateCtsActivity(JNIEnv* env) {
299 jclass clazz = env->FindClass("android/graphics/cts/FrameRateCtsActivity");
300 return env->RegisterNatives(clazz, JNI_METHODS.data(), JNI_METHODS.size());
301 }
302