diff options
Diffstat (limited to 'libs')
167 files changed, 4919 insertions, 1344 deletions
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java index 92e575804bbe..ca3a5112bc55 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java @@ -192,7 +192,7 @@ class SettingsSidecarImpl extends StubSidecar { Rect featureRect = new Rect(left, top, right, bottom); rotateRectToDisplayRotation(featureRect, displayId); transformToWindowSpaceRect(featureRect, windowToken); - if (!featureRect.isEmpty()) { + if (isNotZero(featureRect)) { SidecarDisplayFeature feature = new SidecarDisplayFeature(); feature.setRect(featureRect); feature.setType(type); @@ -207,6 +207,10 @@ class SettingsSidecarImpl extends StubSidecar { return features; } + private static boolean isNotZero(Rect rect) { + return rect.height() > 0 || rect.width() > 0; + } + @Override protected void onListenersChanged() { if (mSettingsObserver == null) { diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index b8934dc8c583..843b17703676 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -22,8 +22,4 @@ android_library { "res", ], manifest: "AndroidManifest.xml", - - platform_apis: true, - sdk_version: "current", - min_sdk_version: "system_current", } diff --git a/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_gain_animation.xml b/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_gain_animation.xml new file mode 100644 index 000000000000..29d9b257cc59 --- /dev/null +++ b/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_gain_animation.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" + android:propertyName="alpha" + android:valueTo="1" + android:interpolator="@android:interpolator/fast_out_slow_in" + android:duration="100" /> diff --git a/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_loss_animation.xml b/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_loss_animation.xml new file mode 100644 index 000000000000..70f553b89657 --- /dev/null +++ b/libs/WindowManager/Shell/res/anim/tv_pip_controls_focus_loss_animation.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" + android:propertyName="alpha" + android:valueTo="0" + android:interpolator="@android:interpolator/fast_out_slow_in" + android:duration="100" /> diff --git a/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_in_animation.xml b/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_in_animation.xml new file mode 100644 index 000000000000..29d9b257cc59 --- /dev/null +++ b/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_in_animation.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" + android:propertyName="alpha" + android:valueTo="1" + android:interpolator="@android:interpolator/fast_out_slow_in" + android:duration="100" /> diff --git a/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_out_animation.xml b/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_out_animation.xml new file mode 100644 index 000000000000..70f553b89657 --- /dev/null +++ b/libs/WindowManager/Shell/res/anim/tv_pip_menu_fade_out_animation.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" + android:propertyName="alpha" + android:valueTo="0" + android:interpolator="@android:interpolator/fast_out_slow_in" + android:duration="100" /> diff --git a/libs/WindowManager/Shell/res/drawable/floating_dismiss_gradient.xml b/libs/WindowManager/Shell/res/drawable/floating_dismiss_gradient.xml new file mode 100644 index 000000000000..8b3057d5841e --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/floating_dismiss_gradient.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<shape + xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <gradient + android:angle="270" + android:startColor="#00000000" + android:endColor="#77000000" + android:type="linear" /> +</shape>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/floating_dismiss_gradient_transition.xml b/libs/WindowManager/Shell/res/drawable/floating_dismiss_gradient_transition.xml new file mode 100644 index 000000000000..772d0a5ea89b --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/floating_dismiss_gradient_transition.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<transition xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:drawable="@android:color/transparent" /> + <item android:drawable="@drawable/floating_dismiss_gradient" /> +</transition>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/pip_expand.xml b/libs/WindowManager/Shell/res/drawable/pip_expand.xml new file mode 100644 index 000000000000..c99d81934aab --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/pip_expand.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="36dp" + android:height="36dp" + android:viewportWidth="36" + android:viewportHeight="36"> + + <path + android:pathData="M0 0h36v36H0z" /> + <path + android:fillColor="#FFFFFF" + android:pathData="M10 21H7v8h8v-3h-5v-5zm-3-6h3v-5h5V7H7v8zm19 11h-5v3h8v-8h-3v5zM21 +7v3h5v5h3V7h-8z" /> +</vector>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml new file mode 100644 index 000000000000..bcc850a854de --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24.0dp" + android:height="24.0dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z" + android:fillColor="#FFFFFFFF"/> +</vector> diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_fullscreen_white.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_fullscreen_white.xml new file mode 100644 index 000000000000..56699dc04e10 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/pip_ic_fullscreen_white.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="#FFFFFF" + android:pathData="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" /> +</vector> diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_pause_white.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_pause_white.xml new file mode 100644 index 000000000000..ef9b2d9c1c63 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/pip_ic_pause_white.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + + <path + android:fillColor="#FFFFFF" + android:pathData="M6 19h4V5H6v14zm8-14v14h4V5h-4z" /> +</vector> diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_play_arrow_white.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_play_arrow_white.xml new file mode 100644 index 000000000000..f12d2cbebc87 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/pip_ic_play_arrow_white.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + + <path + android:fillColor="#FFFFFF" + android:pathData="M8 5v14l11-7z" /> +</vector> diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_settings.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_settings.xml new file mode 100644 index 000000000000..b61e98ce2f9f --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/pip_ic_settings.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M13.85,22.25h-3.7c-0.74,0 -1.36,-0.54 -1.45,-1.27l-0.27,-1.89c-0.27,-0.14 -0.53,-0.29 -0.79,-0.46l-1.8,0.72c-0.7,0.26 -1.47,-0.03 -1.81,-0.65L2.2,15.53c-0.35,-0.66 -0.2,-1.44 0.36,-1.88l1.53,-1.19c-0.01,-0.15 -0.02,-0.3 -0.02,-0.46c0,-0.15 0.01,-0.31 0.02,-0.46l-1.52,-1.19C1.98,9.9 1.83,9.09 2.2,8.47l1.85,-3.19c0.34,-0.62 1.11,-0.9 1.79,-0.63l1.81,0.73c0.26,-0.17 0.52,-0.32 0.78,-0.46l0.27,-1.91c0.09,-0.7 0.71,-1.25 1.44,-1.25h3.7c0.74,0 1.36,0.54 1.45,1.27l0.27,1.89c0.27,0.14 0.53,0.29 0.79,0.46l1.8,-0.72c0.71,-0.26 1.48,0.03 1.82,0.65l1.84,3.18c0.36,0.66 0.2,1.44 -0.36,1.88l-1.52,1.19c0.01,0.15 0.02,0.3 0.02,0.46s-0.01,0.31 -0.02,0.46l1.52,1.19c0.56,0.45 0.72,1.23 0.37,1.86l-1.86,3.22c-0.34,0.62 -1.11,0.9 -1.8,0.63l-1.8,-0.72c-0.26,0.17 -0.52,0.32 -0.78,0.46l-0.27,1.91C15.21,21.71 14.59,22.25 13.85,22.25zM13.32,20.72c0,0.01 0,0.01 0,0.02L13.32,20.72zM10.68,20.7l0,0.02C10.69,20.72 10.69,20.71 10.68,20.7zM10.62,20.25h2.76l0.37,-2.55l0.53,-0.22c0.44,-0.18 0.88,-0.44 1.34,-0.78l0.45,-0.34l2.38,0.96l1.38,-2.4l-2.03,-1.58l0.07,-0.56c0.03,-0.26 0.06,-0.51 0.06,-0.78c0,-0.27 -0.03,-0.53 -0.06,-0.78l-0.07,-0.56l2.03,-1.58l-1.39,-2.4l-2.39,0.96l-0.45,-0.35c-0.42,-0.32 -0.87,-0.58 -1.33,-0.77L13.75,6.3l-0.37,-2.55h-2.76L10.25,6.3L9.72,6.51C9.28,6.7 8.84,6.95 8.38,7.3L7.93,7.63L5.55,6.68L4.16,9.07l2.03,1.58l-0.07,0.56C6.09,11.47 6.06,11.74 6.06,12c0,0.26 0.02,0.53 0.06,0.78l0.07,0.56l-2.03,1.58l1.38,2.4l2.39,-0.96l0.45,0.35c0.43,0.33 0.86,0.58 1.33,0.77l0.53,0.22L10.62,20.25zM18.22,17.72c0,0.01 -0.01,0.02 -0.01,0.03L18.22,17.72zM5.77,17.71l0.01,0.02C5.78,17.72 5.77,17.71 5.77,17.71zM3.93,9.47L3.93,9.47C3.93,9.47 3.93,9.47 3.93,9.47zM18.22,6.27c0,0.01 0.01,0.02 0.01,0.02L18.22,6.27zM5.79,6.25L5.78,6.27C5.78,6.27 5.79,6.26 5.79,6.25zM13.31,3.28c0,0.01 0,0.01 0,0.02L13.31,3.28zM10.69,3.26l0,0.02C10.69,3.27 10.69,3.27 10.69,3.26z"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M12,12m-3.5,0a3.5,3.5 0,1 1,7 0a3.5,3.5 0,1 1,-7 0"/> +</vector> diff --git a/libs/WindowManager/Shell/res/drawable/pip_icon.xml b/libs/WindowManager/Shell/res/drawable/pip_icon.xml new file mode 100644 index 000000000000..b19d907d1ff3 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/pip_icon.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="36dp" + android:height="36dp" + android:viewportWidth="25" + android:viewportHeight="25"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M19,7h-8v6h8L19,7zM21,3L3,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,1.98 2,1.98h18c1.1,0 2,-0.88 2,-1.98L23,5c0,-1.1 -0.9,-2 -2,-2zM21,19.01L3,19.01L3,4.98h18v14.03z"/> +</vector>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/pip_resize_handle.xml b/libs/WindowManager/Shell/res/drawable/pip_resize_handle.xml new file mode 100644 index 000000000000..4d1e080cf466 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/pip_resize_handle.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="12.0dp" + android:height="12.0dp" + android:viewportWidth="12" + android:viewportHeight="12"> + <group + android:translateX="12" + android:rotation="90"> + <path + android:fillColor="#FFFFFF" + android:pathData="M3.41421 0L2 1.41422L10.4853 9.8995L11.8995 8.48528L3.41421 0ZM2.41421 4.24268L1 5.65689L6.65685 11.3137L8.07107 9.89953L2.41421 4.24268Z" /> + </group> +</vector> diff --git a/libs/WindowManager/Shell/res/drawable/tv_pip_button_focused.xml b/libs/WindowManager/Shell/res/drawable/tv_pip_button_focused.xml new file mode 100644 index 000000000000..cce13035dba7 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/tv_pip_button_focused.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="#9AFFFFFF" android:radius="17dp" /> diff --git a/libs/WindowManager/Shell/res/layout/pip_menu_action.xml b/libs/WindowManager/Shell/res/layout/pip_menu_action.xml new file mode 100644 index 000000000000..7a026ca63f50 --- /dev/null +++ b/libs/WindowManager/Shell/res/layout/pip_menu_action.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<ImageButton + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="@dimen/pip_action_size" + android:layout_height="@dimen/pip_action_size" + android:padding="@dimen/pip_action_padding" + android:background="?android:selectableItemBackgroundBorderless" + android:forceHasOverlappingRendering="false" /> diff --git a/libs/WindowManager/Shell/res/layout/pip_menu_activity.xml b/libs/WindowManager/Shell/res/layout/pip_menu_activity.xml new file mode 100644 index 000000000000..2e0a5e09e34f --- /dev/null +++ b/libs/WindowManager/Shell/res/layout/pip_menu_activity.xml @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/background" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <!-- Menu layout --> + <FrameLayout + android:id="@+id/menu_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:forceHasOverlappingRendering="false" + android:accessibilityTraversalAfter="@id/dismiss"> + + <!-- The margins for this container is calculated in the code depending on whether the + actions_container is visible. --> + <FrameLayout + android:id="@+id/expand_container" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <ImageButton + android:id="@+id/expand_button" + android:layout_width="60dp" + android:layout_height="60dp" + android:layout_gravity="center" + android:contentDescription="@string/pip_phone_expand" + android:padding="10dp" + android:src="@drawable/pip_expand" + android:background="?android:selectableItemBackgroundBorderless" /> + </FrameLayout> + + <FrameLayout + android:id="@+id/actions_container" + android:layout_width="match_parent" + android:layout_height="@dimen/pip_action_size" + android:layout_gravity="bottom" + android:visibility="invisible"> + <LinearLayout + android:id="@+id/actions_group" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_gravity="center_horizontal" + android:orientation="horizontal" + android:divider="@android:color/transparent" + android:showDividers="middle" /> + </FrameLayout> + </FrameLayout> + + <LinearLayout + android:id="@+id/top_end_container" + android:layout_gravity="top|end" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <ImageButton + android:id="@+id/settings" + android:layout_width="@dimen/pip_action_size" + android:layout_height="@dimen/pip_action_size" + android:padding="@dimen/pip_action_padding" + android:contentDescription="@string/pip_phone_settings" + android:src="@drawable/pip_ic_settings" + android:background="?android:selectableItemBackgroundBorderless" /> + + <ImageButton + android:id="@+id/dismiss" + android:layout_width="@dimen/pip_action_size" + android:layout_height="@dimen/pip_action_size" + android:padding="@dimen/pip_action_padding" + android:contentDescription="@string/pip_phone_close" + android:src="@drawable/pip_ic_close_white" + android:background="?android:selectableItemBackgroundBorderless" /> + </LinearLayout> + + <!--TODO (b/156917828): Add content description for a11y purposes?--> + <ImageButton + android:id="@+id/resize_handle" + android:layout_width="@dimen/pip_resize_handle_size" + android:layout_height="@dimen/pip_resize_handle_size" + android:layout_gravity="top|start" + android:layout_margin="@dimen/pip_resize_handle_margin" + android:padding="@dimen/pip_resize_handle_padding" + android:src="@drawable/pip_resize_handle" + android:background="?android:selectableItemBackgroundBorderless" /> +</FrameLayout> diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_control_button.xml b/libs/WindowManager/Shell/res/layout/tv_pip_control_button.xml new file mode 100644 index 000000000000..72287c144bed --- /dev/null +++ b/libs/WindowManager/Shell/res/layout/tv_pip_control_button.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<!-- Layout for {@link com.android.systemui.pip.tv.PipControlButtonView}. --> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + + <ImageView android:id="@+id/button" + android:layout_width="34dp" + android:layout_height="34dp" + android:layout_alignParentTop="true" + android:layout_centerHorizontal="true" + android:focusable="true" + android:src="@drawable/tv_pip_button_focused" + android:importantForAccessibility="yes" /> + + <ImageView android:id="@+id/icon" + android:layout_width="34dp" + android:layout_height="34dp" + android:layout_alignParentTop="true" + android:layout_centerHorizontal="true" + android:padding="5dp" + android:importantForAccessibility="no" /> + + <TextView android:id="@+id/desc" + android:layout_width="100dp" + android:layout_height="wrap_content" + android:layout_below="@id/icon" + android:layout_centerHorizontal="true" + android:layout_marginTop="3dp" + android:gravity="center" + android:text="@string/pip_fullscreen" + android:alpha="0" + android:fontFamily="sans-serif" + android:textSize="12sp" + android:textColor="#EEEEEE" + android:importantForAccessibility="no" /> +</merge> diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_controls.xml b/libs/WindowManager/Shell/res/layout/tv_pip_controls.xml new file mode 100644 index 000000000000..22e0452d620d --- /dev/null +++ b/libs/WindowManager/Shell/res/layout/tv_pip_controls.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<!-- Layout for {@link com.android.systemui.pip.tv.PipControlsView}. --> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + + <com.android.systemui.pip.tv.PipControlButtonView + android:id="@+id/full_button" + android:layout_width="@dimen/picture_in_picture_button_width" + android:layout_height="wrap_content" + android:src="@drawable/pip_ic_fullscreen_white" + android:text="@string/pip_fullscreen" /> + + <com.android.systemui.pip.tv.PipControlButtonView + android:id="@+id/close_button" + android:layout_width="@dimen/picture_in_picture_button_width" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/picture_in_picture_button_start_margin" + android:src="@drawable/pip_ic_close_white" + android:text="@string/pip_close" /> + + <com.android.systemui.pip.tv.PipControlButtonView + android:id="@+id/play_pause_button" + android:layout_width="@dimen/picture_in_picture_button_width" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/picture_in_picture_button_start_margin" + android:src="@drawable/pip_ic_pause_white" + android:text="@string/pip_pause" + android:visibility="gone" /> +</merge> diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_custom_control.xml b/libs/WindowManager/Shell/res/layout/tv_pip_custom_control.xml new file mode 100644 index 000000000000..e6cd1122ca77 --- /dev/null +++ b/libs/WindowManager/Shell/res/layout/tv_pip_custom_control.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<com.android.systemui.pip.tv.PipControlButtonView + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="@dimen/picture_in_picture_button_width" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/picture_in_picture_button_start_margin" /> diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml new file mode 100644 index 000000000000..a049787b40b9 --- /dev/null +++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="horizontal" + android:paddingTop="350dp" + android:background="#CC000000" + android:gravity="top|center_horizontal" + android:clipChildren="false"> + + <com.android.systemui.pip.tv.PipControlsView + android:id="@+id/pip_controls" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:alpha="0" /> +</LinearLayout> diff --git a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml new file mode 100644 index 000000000000..7920fd237a08 --- /dev/null +++ b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <!-- The dimensions to user for picture-in-picture action buttons. --> + <dimen name="picture_in_picture_button_width">100dp</dimen> + <dimen name="picture_in_picture_button_start_margin">-50dp</dimen> +</resources> + diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml index c894eb0133b5..245c0725c2a8 100644 --- a/libs/WindowManager/Shell/res/values/config.xml +++ b/libs/WindowManager/Shell/res/values/config.xml @@ -1,21 +1,29 @@ <?xml version="1.0" encoding="utf-8"?> <!-- -/* -** Copyright 2019, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> <resources> -</resources>
\ No newline at end of file + <!-- Animation duration for resizing of PIP when entering/exiting. --> + <integer name="config_pipResizeAnimationDuration">425</integer> + + <!-- Allow dragging the PIP to a location to close it --> + <bool name="config_pipEnableDismissDragToEdge">true</bool> + + <!-- Allow PIP to resize to a slightly bigger state upon touch/showing the menu --> + <bool name="config_pipEnableResizeForMenu">true</bool> + + <!-- Allow PIP to enable round corner, see also R.dimen.pip_corner_radius --> + <bool name="config_pipEnableRoundCorner">false</bool> +</resources> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml new file mode 100644 index 000000000000..1c1217681b9f --- /dev/null +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <dimen name="dismiss_circle_size">52dp</dimen> + + <!-- The height of the gradient indicating the dismiss edge when moving a PIP. --> + <dimen name="floating_dismiss_gradient_height">250dp</dimen> + + <!-- The padding around a PiP actions. --> + <dimen name="pip_action_padding">12dp</dimen> + + <!-- The height of the PiP actions container in which the actions are vertically centered. --> + <dimen name="pip_action_size">48dp</dimen> + + <!-- The padding between actions in the PiP in landscape Note that the PiP does not reflect + the configuration of the device, so we can't use -land resources. --> + <dimen name="pip_between_action_padding_land">8dp</dimen> + + <!-- The buffer to use when calculating whether the pip is in an adjust zone. --> + <dimen name="pip_bottom_offset_buffer">1dp</dimen> + + <!-- The corner radius for PiP window. --> + <dimen name="pip_corner_radius">8dp</dimen> + + <!-- The bottom margin of the PIP drag to dismiss info text shown when moving a PIP. --> + <dimen name="pip_dismiss_text_bottom_margin">24dp</dimen> + + <!-- The bottom margin of the expand container when there are actions. + Equal to pip_action_size - pip_action_padding. --> + <dimen name="pip_expand_container_edge_margin">30dp</dimen> + + <!-- The shortest-edge size of the expanded PiP. --> + <dimen name="pip_expanded_shortest_edge_size">160dp</dimen> + + <!-- The additional offset to apply to the IME animation to account for the input field. --> + <dimen name="pip_ime_offset">48dp</dimen> + + <!-- The touchable/draggable edge size for PIP resize. --> + <dimen name="pip_resize_edge_size">48dp</dimen> + + <!-- PIP Resize handle size, margin and padding. --> + <dimen name="pip_resize_handle_size">12dp</dimen> + <dimen name="pip_resize_handle_margin">4dp</dimen> + <dimen name="pip_resize_handle_padding">0dp</dimen> +</resources> diff --git a/libs/WindowManager/Shell/res/values/ids.xml b/libs/WindowManager/Shell/res/values/ids.xml new file mode 100644 index 000000000000..ed20398f309d --- /dev/null +++ b/libs/WindowManager/Shell/res/values/ids.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <item type="id" name="action_pip_resize" /> +</resources> diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml new file mode 100644 index 000000000000..6752b56fcdf3 --- /dev/null +++ b/libs/WindowManager/Shell/res/values/strings.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Label for PIP close button [CHAR LIMIT=NONE]--> + <string name="pip_phone_close">Close</string> + + <!-- Making the PIP fullscreen [CHAR LIMIT=25] --> + <string name="pip_phone_expand">Expand</string> + + <!-- Label for PIP settings button [CHAR LIMIT=NONE]--> + <string name="pip_phone_settings">Settings</string> + + <!-- Title of menu shown over picture-in-picture. Used for accessibility. --> + <string name="pip_menu_title">Menu</string> + + <!-- PiP BTW notification title. [CHAR LIMIT=50] --> + <string name="pip_notification_title"><xliff:g id="name" example="Google Maps">%s</xliff:g> is in picture-in-picture</string> + + <!-- PiP BTW notification description. [CHAR LIMIT=NONE] --> + <string name="pip_notification_message">If you don\'t want <xliff:g id="name" example="Google Maps">%s</xliff:g> to use this feature, tap to open settings and turn it off.</string> + + <!-- Button to play the current media on picture-in-picture (PIP) [CHAR LIMIT=30] --> + <string name="pip_play">Play</string> + + <!-- Button to pause the current media on picture-in-picture (PIP) [CHAR LIMIT=30] --> + <string name="pip_pause">Pause</string> + + <!-- Button to skip to the next media on picture-in-picture (PIP) [CHAR LIMIT=30] --> + <string name="pip_skip_to_next">Skip to next</string> + + <!-- Button to skip to the prev media on picture-in-picture (PIP) [CHAR LIMIT=30] --> + <string name="pip_skip_to_prev">Skip to previous</string> + + <!-- Accessibility action for resizing PIP [CHAR LIMIT=NONE] --> + <string name="accessibility_action_pip_resize">Resize</string> + + <!-- TODO Deprecated. Label for PIP action to Minimize the PIP. DO NOT TRANSLATE [CHAR LIMIT=25] --> + <string name="pip_phone_minimize">Minimize</string> + + <!-- TODO Deprecated. Label for PIP the drag to dismiss hint. DO NOT TRANSLATE [CHAR LIMIT=NONE]--> + <string name="pip_phone_dismiss_hint">Drag down to dismiss</string> +</resources> diff --git a/libs/WindowManager/Shell/res/values/strings_tv.xml b/libs/WindowManager/Shell/res/values/strings_tv.xml new file mode 100644 index 000000000000..2dfdcabaa931 --- /dev/null +++ b/libs/WindowManager/Shell/res/values/strings_tv.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Picture-in-Picture (PIP) notification --> + <!-- Title for the notification channel for TV PIP controls. [CHAR LIMIT=NONE] --> + <string name="notification_channel_tv_pip">Picture-in-Picture</string> + + <!-- Title of the picture-in-picture (PIP) notification title + when the media doesn't have title [CHAR LIMIT=NONE] --> + <string name="pip_notification_unknown_title">(No title program)</string> + + <!-- Picture-in-Picture (PIP) menu --> + <eat-comment /> + <!-- Button to close picture-in-picture (PIP) in PIP menu [CHAR LIMIT=30] --> + <string name="pip_close">Close PIP</string> + + <!-- Button to move picture-in-picture (PIP) screen to the fullscreen in PIP menu [CHAR LIMIT=30] --> + <string name="pip_fullscreen">Full screen</string> +</resources> + diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java new file mode 100644 index 000000000000..3263f79888d6 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.common; + +import android.os.Handler; +import android.os.RemoteException; +import android.view.IDisplayWindowRotationCallback; +import android.view.IDisplayWindowRotationController; +import android.view.IWindowManager; +import android.window.WindowContainerTransaction; + +import java.util.ArrayList; + +/** + * This module deals with display rotations coming from WM. When WM starts a rotation: after it has + * frozen the screen, it will call into this class. This will then call all registered local + * controllers and give them a chance to queue up task changes to be applied synchronously with that + * rotation. + */ +public class DisplayChangeController { + + private final Handler mHandler; + private final IWindowManager mWmService; + + private final ArrayList<OnDisplayChangingListener> mRotationListener = + new ArrayList<>(); + private final ArrayList<OnDisplayChangingListener> mTmpListeners = new ArrayList<>(); + + private final IDisplayWindowRotationController mDisplayRotationController = + new IDisplayWindowRotationController.Stub() { + @Override + public void onRotateDisplay(int displayId, final int fromRotation, + final int toRotation, IDisplayWindowRotationCallback callback) { + mHandler.post(() -> { + WindowContainerTransaction t = new WindowContainerTransaction(); + synchronized (mRotationListener) { + mTmpListeners.clear(); + // Make a local copy in case the handlers add/remove themselves. + mTmpListeners.addAll(mRotationListener); + } + for (OnDisplayChangingListener c : mTmpListeners) { + c.onRotateDisplay(displayId, fromRotation, toRotation, t); + } + try { + callback.continueRotateDisplay(toRotation, t); + } catch (RemoteException e) { + } + }); + } + }; + + public DisplayChangeController(Handler mainHandler, IWindowManager wmService) { + mHandler = mainHandler; + mWmService = wmService; + try { + mWmService.setDisplayWindowRotationController(mDisplayRotationController); + } catch (RemoteException e) { + throw new RuntimeException("Unable to register rotation controller"); + } + } + + /** + * Adds a display rotation controller. + */ + public void addRotationListener(OnDisplayChangingListener listener) { + synchronized (mRotationListener) { + mRotationListener.add(listener); + } + } + + /** + * Removes a display rotation controller. + */ + public void removeRotationListener(OnDisplayChangingListener listener) { + synchronized (mRotationListener) { + mRotationListener.remove(listener); + } + } + + /** + * Give a listener a chance to queue up configuration changes to execute as part of a + * display rotation. The contents of {@link #onRotateDisplay} must run synchronously. + */ + public interface OnDisplayChangingListener { + /** + * Called before the display is rotated. Contents of this method must run synchronously. + * @param displayId Id of display that is rotating. + * @param fromRotation starting rotation of the display. + * @param toRotation target rotation of the display (after rotating). + * @param t A task transaction to populate. + */ + void onRotateDisplay(int displayId, int fromRotation, int toRotation, + WindowContainerTransaction t); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java new file mode 100644 index 000000000000..418973204add --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.common; + +import android.annotation.Nullable; +import android.content.Context; +import android.content.res.Configuration; +import android.hardware.display.DisplayManager; +import android.os.Handler; +import android.os.RemoteException; +import android.util.Slog; +import android.util.SparseArray; +import android.view.Display; +import android.view.IDisplayWindowListener; +import android.view.IWindowManager; + +import com.android.wm.shell.common.DisplayChangeController.OnDisplayChangingListener; + +import java.util.ArrayList; + +/** + * This module deals with display rotations coming from WM. When WM starts a rotation: after it has + * frozen the screen, it will call into this class. This will then call all registered local + * controllers and give them a chance to queue up task changes to be applied synchronously with that + * rotation. + */ +public class DisplayController { + private static final String TAG = "DisplayController"; + + private final Handler mHandler; + private final Context mContext; + private final IWindowManager mWmService; + private final DisplayChangeController mChangeController; + + private final SparseArray<DisplayRecord> mDisplays = new SparseArray<>(); + private final ArrayList<OnDisplaysChangedListener> mDisplayChangedListeners = new ArrayList<>(); + + /** + * Gets a display by id from DisplayManager. + */ + public Display getDisplay(int displayId) { + final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); + return displayManager.getDisplay(displayId); + } + + private final IDisplayWindowListener mDisplayContainerListener = + new IDisplayWindowListener.Stub() { + @Override + public void onDisplayAdded(int displayId) { + mHandler.post(() -> { + synchronized (mDisplays) { + if (mDisplays.get(displayId) != null) { + return; + } + Display display = getDisplay(displayId); + if (display == null) { + // It's likely that the display is private to some app and thus not + // accessible by system-ui. + return; + } + DisplayRecord record = new DisplayRecord(); + record.mDisplayId = displayId; + record.mContext = (displayId == Display.DEFAULT_DISPLAY) ? mContext + : mContext.createDisplayContext(display); + record.mDisplayLayout = new DisplayLayout(record.mContext, display); + mDisplays.put(displayId, record); + for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { + mDisplayChangedListeners.get(i).onDisplayAdded(displayId); + } + } + }); + } + + @Override + public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { + mHandler.post(() -> { + synchronized (mDisplays) { + DisplayRecord dr = mDisplays.get(displayId); + if (dr == null) { + Slog.w(TAG, "Skipping Display Configuration change on non-added" + + " display."); + return; + } + Display display = getDisplay(displayId); + if (display == null) { + Slog.w(TAG, "Skipping Display Configuration change on invalid" + + " display. It may have been removed."); + return; + } + Context perDisplayContext = mContext; + if (displayId != Display.DEFAULT_DISPLAY) { + perDisplayContext = mContext.createDisplayContext(display); + } + dr.mContext = perDisplayContext.createConfigurationContext(newConfig); + dr.mDisplayLayout = new DisplayLayout(dr.mContext, display); + for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { + mDisplayChangedListeners.get(i).onDisplayConfigurationChanged( + displayId, newConfig); + } + } + }); + } + + @Override + public void onDisplayRemoved(int displayId) { + mHandler.post(() -> { + synchronized (mDisplays) { + if (mDisplays.get(displayId) == null) { + return; + } + for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { + mDisplayChangedListeners.get(i).onDisplayRemoved(displayId); + } + mDisplays.remove(displayId); + } + }); + } + + @Override + public void onFixedRotationStarted(int displayId, int newRotation) { + mHandler.post(() -> { + synchronized (mDisplays) { + if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) { + Slog.w(TAG, "Skipping onFixedRotationStarted on unknown" + + " display, displayId=" + displayId); + return; + } + for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { + mDisplayChangedListeners.get(i).onFixedRotationStarted( + displayId, newRotation); + } + } + }); + } + + @Override + public void onFixedRotationFinished(int displayId) { + mHandler.post(() -> { + synchronized (mDisplays) { + if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) { + Slog.w(TAG, "Skipping onFixedRotationFinished on unknown" + + " display, displayId=" + displayId); + return; + } + for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { + mDisplayChangedListeners.get(i).onFixedRotationFinished(displayId); + } + } + }); + } + }; + + public DisplayController(Context context, Handler handler, + IWindowManager wmService) { + mHandler = handler; + mContext = context; + mWmService = wmService; + mChangeController = new DisplayChangeController(mHandler, mWmService); + try { + mWmService.registerDisplayWindowListener(mDisplayContainerListener); + } catch (RemoteException e) { + throw new RuntimeException("Unable to register hierarchy listener"); + } + } + + /** + * Gets the DisplayLayout associated with a display. + */ + public @Nullable DisplayLayout getDisplayLayout(int displayId) { + final DisplayRecord r = mDisplays.get(displayId); + return r != null ? r.mDisplayLayout : null; + } + + /** + * Gets a display-specific context for a display. + */ + public @Nullable Context getDisplayContext(int displayId) { + final DisplayRecord r = mDisplays.get(displayId); + return r != null ? r.mContext : null; + } + + /** + * Add a display window-container listener. It will get notified whenever a display's + * configuration changes or when displays are added/removed from the WM hierarchy. + */ + public void addDisplayWindowListener(OnDisplaysChangedListener listener) { + synchronized (mDisplays) { + if (mDisplayChangedListeners.contains(listener)) { + return; + } + mDisplayChangedListeners.add(listener); + for (int i = 0; i < mDisplays.size(); ++i) { + listener.onDisplayAdded(mDisplays.keyAt(i)); + } + } + } + + /** + * Remove a display window-container listener. + */ + public void removeDisplayWindowListener(OnDisplaysChangedListener listener) { + synchronized (mDisplays) { + mDisplayChangedListeners.remove(listener); + } + } + + /** + * Adds a display rotation controller. + */ + public void addDisplayChangingController(OnDisplayChangingListener controller) { + mChangeController.addRotationListener(controller); + } + + /** + * Removes a display rotation controller. + */ + public void removeDisplayChangingController(OnDisplayChangingListener controller) { + mChangeController.removeRotationListener(controller); + } + + private static class DisplayRecord { + int mDisplayId; + Context mContext; + DisplayLayout mDisplayLayout; + } + + /** + * Gets notified when a display is added/removed to the WM hierarchy and when a display's + * window-configuration changes. + * + * @see IDisplayWindowListener + */ + public interface OnDisplaysChangedListener { + /** + * Called when a display has been added to the WM hierarchy. + */ + default void onDisplayAdded(int displayId) {} + + /** + * Called when a display's window-container configuration changes. + */ + default void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {} + + /** + * Called when a display is removed. + */ + default void onDisplayRemoved(int displayId) {} + + /** + * Called when fixed rotation on a display is started. + */ + default void onFixedRotationStarted(int displayId, int newRotation) {} + + /** + * Called when fixed rotation on a display is finished. + */ + default void onFixedRotationFinished(int displayId) {} + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java new file mode 100644 index 000000000000..aeda2d923490 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java @@ -0,0 +1,521 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.common; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.annotation.IntDef; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Point; +import android.graphics.Rect; +import android.os.Handler; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Slog; +import android.util.SparseArray; +import android.view.IDisplayWindowInsetsController; +import android.view.IWindowManager; +import android.view.InsetsSource; +import android.view.InsetsSourceControl; +import android.view.InsetsState; +import android.view.Surface; +import android.view.SurfaceControl; +import android.view.WindowInsets; +import android.view.animation.Interpolator; +import android.view.animation.PathInterpolator; + +import com.android.internal.view.IInputMethodManager; + +import java.util.ArrayList; + +/** + * Manages IME control at the display-level. This occurs when IME comes up in multi-window mode. + */ +public class DisplayImeController implements DisplayController.OnDisplaysChangedListener { + private static final String TAG = "DisplayImeController"; + + private static final boolean DEBUG = false; + + // NOTE: All these constants came from InsetsController. + public static final int ANIMATION_DURATION_SHOW_MS = 275; + public static final int ANIMATION_DURATION_HIDE_MS = 340; + public static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f); + private static final int DIRECTION_NONE = 0; + private static final int DIRECTION_SHOW = 1; + private static final int DIRECTION_HIDE = 2; + private static final int FLOATING_IME_BOTTOM_INSET = -80; + + protected final IWindowManager mWmService; + protected final Handler mHandler; + private final TransactionPool mTransactionPool; + private final DisplayController mDisplayController; + private final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>(); + private final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>(); + + protected DisplayImeController(IWindowManager wmService, DisplayController displayController, + Handler mainHandler, TransactionPool transactionPool) { + mHandler = mainHandler; + mWmService = wmService; + mTransactionPool = transactionPool; + mDisplayController = displayController; + } + + protected void startMonitorDisplays() { + mDisplayController.addDisplayWindowListener(this); + } + + @Override + public void onDisplayAdded(int displayId) { + // Add's a system-ui window-manager specifically for ime. This type is special because + // WM will defer IME inset handling to it in multi-window scenarious. + PerDisplay pd = new PerDisplay(displayId, + mDisplayController.getDisplayLayout(displayId).rotation()); + try { + mWmService.setDisplayWindowInsetsController(displayId, pd); + } catch (RemoteException e) { + Slog.w(TAG, "Unable to set insets controller on display " + displayId); + } + mImePerDisplay.put(displayId, pd); + } + + @Override + public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { + PerDisplay pd = mImePerDisplay.get(displayId); + if (pd == null) { + return; + } + if (mDisplayController.getDisplayLayout(displayId).rotation() + != pd.mRotation && isImeShowing(displayId)) { + pd.startAnimation(true, false /* forceRestart */); + } + } + + @Override + public void onDisplayRemoved(int displayId) { + try { + mWmService.setDisplayWindowInsetsController(displayId, null); + } catch (RemoteException e) { + Slog.w(TAG, "Unable to remove insets controller on display " + displayId); + } + mImePerDisplay.remove(displayId); + } + + private boolean isImeShowing(int displayId) { + PerDisplay pd = mImePerDisplay.get(displayId); + if (pd == null) { + return false; + } + final InsetsSource imeSource = pd.mInsetsState.getSource(InsetsState.ITYPE_IME); + return imeSource != null && pd.mImeSourceControl != null && imeSource.isVisible(); + } + + private void dispatchPositionChanged(int displayId, int imeTop, + SurfaceControl.Transaction t) { + synchronized (mPositionProcessors) { + for (ImePositionProcessor pp : mPositionProcessors) { + pp.onImePositionChanged(displayId, imeTop, t); + } + } + } + + @ImePositionProcessor.ImeAnimationFlags + private int dispatchStartPositioning(int displayId, int hiddenTop, int shownTop, + boolean show, boolean isFloating, SurfaceControl.Transaction t) { + synchronized (mPositionProcessors) { + int flags = 0; + for (ImePositionProcessor pp : mPositionProcessors) { + flags |= pp.onImeStartPositioning( + displayId, hiddenTop, shownTop, show, isFloating, t); + } + return flags; + } + } + + private void dispatchEndPositioning(int displayId, boolean cancel, + SurfaceControl.Transaction t) { + synchronized (mPositionProcessors) { + for (ImePositionProcessor pp : mPositionProcessors) { + pp.onImeEndPositioning(displayId, cancel, t); + } + } + } + + /** + * Adds an {@link ImePositionProcessor} to be called during ime position updates. + */ + public void addPositionProcessor(ImePositionProcessor processor) { + synchronized (mPositionProcessors) { + if (mPositionProcessors.contains(processor)) { + return; + } + mPositionProcessors.add(processor); + } + } + + /** + * Removes an {@link ImePositionProcessor} to be called during ime position updates. + */ + public void removePositionProcessor(ImePositionProcessor processor) { + synchronized (mPositionProcessors) { + mPositionProcessors.remove(processor); + } + } + + class PerDisplay extends IDisplayWindowInsetsController.Stub { + final int mDisplayId; + final InsetsState mInsetsState = new InsetsState(); + InsetsSourceControl mImeSourceControl = null; + int mAnimationDirection = DIRECTION_NONE; + ValueAnimator mAnimation = null; + int mRotation = Surface.ROTATION_0; + boolean mImeShowing = false; + final Rect mImeFrame = new Rect(); + boolean mAnimateAlpha = true; + + PerDisplay(int displayId, int initialRotation) { + mDisplayId = displayId; + mRotation = initialRotation; + } + + @Override + public void insetsChanged(InsetsState insetsState) { + mHandler.post(() -> { + if (mInsetsState.equals(insetsState)) { + return; + } + + final InsetsSource newSource = insetsState.getSource(InsetsState.ITYPE_IME); + final Rect newFrame = newSource.getFrame(); + final Rect oldFrame = mInsetsState.getSource(InsetsState.ITYPE_IME).getFrame(); + + mInsetsState.set(insetsState, true /* copySources */); + if (mImeShowing && !newFrame.equals(oldFrame) && newSource.isVisible()) { + if (DEBUG) Slog.d(TAG, "insetsChanged when IME showing, restart animation"); + startAnimation(mImeShowing, true /* forceRestart */); + } + }); + } + + @Override + public void insetsControlChanged(InsetsState insetsState, + InsetsSourceControl[] activeControls) { + insetsChanged(insetsState); + if (activeControls != null) { + for (InsetsSourceControl activeControl : activeControls) { + if (activeControl == null) { + continue; + } + if (activeControl.getType() == InsetsState.ITYPE_IME) { + mHandler.post(() -> { + final Point lastSurfacePosition = mImeSourceControl != null + ? mImeSourceControl.getSurfacePosition() : null; + mImeSourceControl = activeControl; + if (!activeControl.getSurfacePosition().equals(lastSurfacePosition) + && mAnimation != null) { + startAnimation(mImeShowing, true /* forceRestart */); + } else if (!mImeShowing) { + removeImeSurface(); + } + }); + } + } + } + } + + @Override + public void showInsets(int types, boolean fromIme) { + if ((types & WindowInsets.Type.ime()) == 0) { + return; + } + if (DEBUG) Slog.d(TAG, "Got showInsets for ime"); + mHandler.post(() -> startAnimation(true /* show */, false /* forceRestart */)); + } + + @Override + public void hideInsets(int types, boolean fromIme) { + if ((types & WindowInsets.Type.ime()) == 0) { + return; + } + if (DEBUG) Slog.d(TAG, "Got hideInsets for ime"); + mHandler.post(() -> startAnimation(false /* show */, false /* forceRestart */)); + } + + @Override + public void topFocusedWindowChanged(String packageName) { + // no-op + } + + /** + * Sends the local visibility state back to window manager. Needed for legacy adjustForIme. + */ + private void setVisibleDirectly(boolean visible) { + mInsetsState.getSource(InsetsState.ITYPE_IME).setVisible(visible); + try { + mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState); + } catch (RemoteException e) { + } + } + + private int imeTop(float surfaceOffset) { + return mImeFrame.top + (int) surfaceOffset; + } + + private boolean calcIsFloating(InsetsSource imeSource) { + final Rect frame = imeSource.getFrame(); + if (frame.height() == 0) { + return true; + } + // Some Floating Input Methods will still report a frame, but the frame is actually + // a nav-bar inset created by WM and not part of the IME (despite being reported as + // an IME inset). For now, we assume that no non-floating IME will be <= this nav bar + // frame height so any reported frame that is <= nav-bar frame height is assumed to + // be floating. + return frame.height() <= mDisplayController.getDisplayLayout(mDisplayId) + .navBarFrameHeight(); + } + + private void startAnimation(final boolean show, final boolean forceRestart) { + final InsetsSource imeSource = mInsetsState.getSource(InsetsState.ITYPE_IME); + if (imeSource == null || mImeSourceControl == null) { + return; + } + final Rect newFrame = imeSource.getFrame(); + final boolean isFloating = calcIsFloating(imeSource) && show; + if (isFloating) { + // This is a "floating" or "expanded" IME, so to get animations, just + // pretend the ime has some size just below the screen. + mImeFrame.set(newFrame); + final int floatingInset = (int) (mDisplayController.getDisplayLayout(mDisplayId) + .density() * FLOATING_IME_BOTTOM_INSET); + mImeFrame.bottom -= floatingInset; + } else if (newFrame.height() != 0) { + // Don't set a new frame if it's empty and hiding -- this maintains continuity + mImeFrame.set(newFrame); + } + if (DEBUG) { + Slog.d(TAG, "Run startAnim show:" + show + " was:" + + (mAnimationDirection == DIRECTION_SHOW ? "SHOW" + : (mAnimationDirection == DIRECTION_HIDE ? "HIDE" : "NONE"))); + } + if (!forceRestart && (mAnimationDirection == DIRECTION_SHOW && show) + || (mAnimationDirection == DIRECTION_HIDE && !show)) { + return; + } + boolean seek = false; + float seekValue = 0; + if (mAnimation != null) { + if (mAnimation.isRunning()) { + seekValue = (float) mAnimation.getAnimatedValue(); + seek = true; + } + mAnimation.cancel(); + } + final float defaultY = mImeSourceControl.getSurfacePosition().y; + final float x = mImeSourceControl.getSurfacePosition().x; + final float hiddenY = defaultY + mImeFrame.height(); + final float shownY = defaultY; + final float startY = show ? hiddenY : shownY; + final float endY = show ? shownY : hiddenY; + if (mAnimationDirection == DIRECTION_NONE && mImeShowing && show) { + // IME is already showing, so set seek to end + seekValue = shownY; + seek = true; + } + mAnimationDirection = show ? DIRECTION_SHOW : DIRECTION_HIDE; + mImeShowing = show; + mAnimation = ValueAnimator.ofFloat(startY, endY); + mAnimation.setDuration( + show ? ANIMATION_DURATION_SHOW_MS : ANIMATION_DURATION_HIDE_MS); + if (seek) { + mAnimation.setCurrentFraction((seekValue - startY) / (endY - startY)); + } + + mAnimation.addUpdateListener(animation -> { + SurfaceControl.Transaction t = mTransactionPool.acquire(); + float value = (float) animation.getAnimatedValue(); + t.setPosition(mImeSourceControl.getLeash(), x, value); + final float alpha = (mAnimateAlpha || isFloating) + ? (value - hiddenY) / (shownY - hiddenY) : 1.f; + t.setAlpha(mImeSourceControl.getLeash(), alpha); + dispatchPositionChanged(mDisplayId, imeTop(value), t); + t.apply(); + mTransactionPool.release(t); + }); + mAnimation.setInterpolator(INTERPOLATOR); + mAnimation.addListener(new AnimatorListenerAdapter() { + private boolean mCancelled = false; + + @Override + public void onAnimationStart(Animator animation) { + SurfaceControl.Transaction t = mTransactionPool.acquire(); + t.setPosition(mImeSourceControl.getLeash(), x, startY); + if (DEBUG) { + Slog.d(TAG, "onAnimationStart d:" + mDisplayId + " top:" + + imeTop(hiddenY) + "->" + imeTop(shownY) + + " showing:" + (mAnimationDirection == DIRECTION_SHOW)); + } + int flags = dispatchStartPositioning(mDisplayId, imeTop(hiddenY), + imeTop(shownY), mAnimationDirection == DIRECTION_SHOW, isFloating, t); + mAnimateAlpha = (flags & ImePositionProcessor.IME_ANIMATION_NO_ALPHA) == 0; + final float alpha = (mAnimateAlpha || isFloating) + ? (startY - hiddenY) / (shownY - hiddenY) + : 1.f; + t.setAlpha(mImeSourceControl.getLeash(), alpha); + if (mAnimationDirection == DIRECTION_SHOW) { + t.show(mImeSourceControl.getLeash()); + } + t.apply(); + mTransactionPool.release(t); + } + + @Override + public void onAnimationCancel(Animator animation) { + mCancelled = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + if (DEBUG) Slog.d(TAG, "onAnimationEnd " + mCancelled); + SurfaceControl.Transaction t = mTransactionPool.acquire(); + if (!mCancelled) { + t.setPosition(mImeSourceControl.getLeash(), x, endY); + t.setAlpha(mImeSourceControl.getLeash(), 1.f); + } + dispatchEndPositioning(mDisplayId, mCancelled, t); + if (mAnimationDirection == DIRECTION_HIDE && !mCancelled) { + t.hide(mImeSourceControl.getLeash()); + removeImeSurface(); + } + t.apply(); + mTransactionPool.release(t); + + mAnimationDirection = DIRECTION_NONE; + mAnimation = null; + } + }); + if (!show) { + // When going away, queue up insets change first, otherwise any bounds changes + // can have a "flicker" of ime-provided insets. + setVisibleDirectly(false /* visible */); + } + mAnimation.start(); + if (show) { + // When showing away, queue up insets change last, otherwise any bounds changes + // can have a "flicker" of ime-provided insets. + setVisibleDirectly(true /* visible */); + } + } + } + + void removeImeSurface() { + final IInputMethodManager imms = getImms(); + if (imms != null) { + try { + // Remove the IME surface to make the insets invisible for + // non-client controlled insets. + imms.removeImeSurface(); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to remove IME surface.", e); + } + } + } + + /** + * Allows other things to synchronize with the ime position + */ + public interface ImePositionProcessor { + /** + * Indicates that ime shouldn't animate alpha. It will always be opaque. Used when stuff + * behind the IME shouldn't be visible (for example during split-screen adjustment where + * there is nothing behind the ime). + */ + int IME_ANIMATION_NO_ALPHA = 1; + + /** @hide */ + @IntDef(prefix = {"IME_ANIMATION_"}, value = { + IME_ANIMATION_NO_ALPHA, + }) + @interface ImeAnimationFlags { + } + + /** + * Called when the IME position is starting to animate. + * + * @param hiddenTop The y position of the top of the IME surface when it is hidden. + * @param shownTop The y position of the top of the IME surface when it is shown. + * @param showing {@code true} when we are animating from hidden to shown, {@code false} + * when animating from shown to hidden. + * @param isFloating {@code true} when the ime is a floating ime (doesn't inset). + * @return flags that may alter how ime itself is animated (eg. no-alpha). + */ + @ImeAnimationFlags + default int onImeStartPositioning(int displayId, int hiddenTop, int shownTop, + boolean showing, boolean isFloating, SurfaceControl.Transaction t) { + return 0; + } + + /** + * Called when the ime position changed. This is expected to be a synchronous call on the + * animation thread. Operations can be added to the transaction to be applied in sync. + * + * @param imeTop The current y position of the top of the IME surface. + */ + default void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t) { + } + + /** + * Called when the IME position is done animating. + * + * @param cancel {@code true} if this was cancelled. This implies another start is coming. + */ + default void onImeEndPositioning(int displayId, boolean cancel, + SurfaceControl.Transaction t) { + } + } + + public IInputMethodManager getImms() { + return IInputMethodManager.Stub.asInterface( + ServiceManager.getService(Context.INPUT_METHOD_SERVICE)); + } + + /** Builds {@link DisplayImeController} instance. */ + public static class Builder { + private IWindowManager mWmService; + private DisplayController mDisplayController; + private Handler mHandler; + private TransactionPool mTransactionPool; + + public Builder(IWindowManager wmService, DisplayController displayController, + Handler handler, TransactionPool transactionPool) { + mWmService = wmService; + mDisplayController = displayController; + mHandler = handler; + mTransactionPool = transactionPool; + } + + /** Builds and initializes {@link DisplayImeController} instance. */ + public DisplayImeController build() { + DisplayImeController displayImeController = new DisplayImeController(mWmService, + mDisplayController, mHandler, mTransactionPool); + // Separates startMonitorDisplays from constructor to prevent circular init issue. + displayImeController.startMonitorDisplays(); + return displayImeController; + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java new file mode 100644 index 000000000000..3181dbf74ace --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java @@ -0,0 +1,511 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.common; + +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static android.content.res.Configuration.UI_MODE_TYPE_CAR; +import static android.content.res.Configuration.UI_MODE_TYPE_MASK; +import static android.os.Process.SYSTEM_UID; +import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS; +import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; +import static android.view.Surface.ROTATION_0; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.content.ContentResolver; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Insets; +import android.graphics.Rect; +import android.os.SystemProperties; +import android.provider.Settings; +import android.util.DisplayMetrics; +import android.util.RotationUtils; +import android.util.Size; +import android.view.Display; +import android.view.DisplayCutout; +import android.view.DisplayInfo; +import android.view.Gravity; +import android.view.Surface; + +import com.android.internal.R; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Contains information about the layout-properties of a display. This refers to internal layout + * like insets/cutout/rotation. In general, this can be thought of as the shell analog to + * DisplayPolicy. + */ +public class DisplayLayout { + @IntDef(prefix = { "NAV_BAR_" }, value = { + NAV_BAR_LEFT, + NAV_BAR_RIGHT, + NAV_BAR_BOTTOM, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface NavBarPosition {} + + // Navigation bar position values + public static final int NAV_BAR_LEFT = 1 << 0; + public static final int NAV_BAR_RIGHT = 1 << 1; + public static final int NAV_BAR_BOTTOM = 1 << 2; + + private int mUiMode; + private int mWidth; + private int mHeight; + private DisplayCutout mCutout; + private int mRotation; + private int mDensityDpi; + private final Rect mNonDecorInsets = new Rect(); + private final Rect mStableInsets = new Rect(); + private boolean mHasNavigationBar = false; + private boolean mHasStatusBar = false; + private int mNavBarFrameHeight = 0; + + /** + * Create empty layout. + */ + public DisplayLayout() { + } + + /** + * Construct a custom display layout using a DisplayInfo. + * @param info + * @param res + */ + public DisplayLayout(DisplayInfo info, Resources res, boolean hasNavigationBar, + boolean hasStatusBar) { + init(info, res, hasNavigationBar, hasStatusBar); + } + + /** + * Construct a display layout based on a live display. + * @param context Used for resources. + */ + public DisplayLayout(@NonNull Context context, @NonNull Display rawDisplay) { + final int displayId = rawDisplay.getDisplayId(); + DisplayInfo info = new DisplayInfo(); + rawDisplay.getDisplayInfo(info); + init(info, context.getResources(), hasNavigationBar(info, context, displayId), + hasStatusBar(displayId)); + } + + public DisplayLayout(DisplayLayout dl) { + set(dl); + } + + /** sets this DisplayLayout to a copy of another on. */ + public void set(DisplayLayout dl) { + mUiMode = dl.mUiMode; + mWidth = dl.mWidth; + mHeight = dl.mHeight; + mCutout = dl.mCutout; + mRotation = dl.mRotation; + mDensityDpi = dl.mDensityDpi; + mHasNavigationBar = dl.mHasNavigationBar; + mHasStatusBar = dl.mHasStatusBar; + mNonDecorInsets.set(dl.mNonDecorInsets); + mStableInsets.set(dl.mStableInsets); + } + + private void init(DisplayInfo info, Resources res, boolean hasNavigationBar, + boolean hasStatusBar) { + mUiMode = res.getConfiguration().uiMode; + mWidth = info.logicalWidth; + mHeight = info.logicalHeight; + mRotation = info.rotation; + mCutout = info.displayCutout; + mDensityDpi = info.logicalDensityDpi; + mHasNavigationBar = hasNavigationBar; + mHasStatusBar = hasStatusBar; + recalcInsets(res); + } + + private void recalcInsets(Resources res) { + computeNonDecorInsets(res, mRotation, mWidth, mHeight, mCutout, mUiMode, mNonDecorInsets, + mHasNavigationBar); + mStableInsets.set(mNonDecorInsets); + if (mHasStatusBar) { + convertNonDecorInsetsToStableInsets(res, mStableInsets, mWidth, mHeight, mHasStatusBar); + } + mNavBarFrameHeight = getNavigationBarFrameHeight(res, mWidth > mHeight); + } + + /** + * Apply a rotation to this layout and its parameters. + * @param res + * @param targetRotation + */ + public void rotateTo(Resources res, @Surface.Rotation int targetRotation) { + final int rotationDelta = (targetRotation - mRotation + 4) % 4; + final boolean changeOrient = (rotationDelta % 2) != 0; + + final int origWidth = mWidth; + final int origHeight = mHeight; + + mRotation = targetRotation; + if (changeOrient) { + mWidth = origHeight; + mHeight = origWidth; + } + + if (mCutout != null && !mCutout.isEmpty()) { + mCutout = calculateDisplayCutoutForRotation(mCutout, rotationDelta, origWidth, + origHeight); + } + + recalcInsets(res); + } + + /** Get this layout's non-decor insets. */ + public Rect nonDecorInsets() { + return mNonDecorInsets; + } + + /** Get this layout's stable insets. */ + public Rect stableInsets() { + return mStableInsets; + } + + /** Get this layout's width. */ + public int width() { + return mWidth; + } + + /** Get this layout's height. */ + public int height() { + return mHeight; + } + + /** Get this layout's display rotation. */ + public int rotation() { + return mRotation; + } + + /** Get this layout's display density. */ + public int densityDpi() { + return mDensityDpi; + } + + /** Get the density scale for the display. */ + public float density() { + return mDensityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE; + } + + /** Get whether this layout is landscape. */ + public boolean isLandscape() { + return mWidth > mHeight; + } + + /** Get the navbar frame height (used by ime). */ + public int navBarFrameHeight() { + return mNavBarFrameHeight; + } + + /** Gets the orientation of this layout */ + public int getOrientation() { + return (mWidth > mHeight) ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; + } + + /** Gets the calculated stable-bounds for this layout */ + public void getStableBounds(Rect outBounds) { + outBounds.set(0, 0, mWidth, mHeight); + outBounds.inset(mStableInsets); + } + + /** + * Gets navigation bar position for this layout + * @return Navigation bar position for this layout. + */ + public @NavBarPosition int getNavigationBarPosition(Resources res) { + return navigationBarPosition(res, mWidth, mHeight, mRotation); + } + + /** + * Rotates bounds as if parentBounds and bounds are a group. The group is rotated by `delta` + * 90-degree counter-clockwise increments. This assumes that parentBounds is at 0,0 and + * remains at 0,0 after rotation. + * + * Only 'bounds' is mutated. + */ + public static void rotateBounds(Rect inOutBounds, Rect parentBounds, int delta) { + int rdelta = ((delta % 4) + 4) % 4; + int origLeft = inOutBounds.left; + switch (rdelta) { + case 0: + return; + case 1: + inOutBounds.left = inOutBounds.top; + inOutBounds.top = parentBounds.right - inOutBounds.right; + inOutBounds.right = inOutBounds.bottom; + inOutBounds.bottom = parentBounds.right - origLeft; + return; + case 2: + inOutBounds.left = parentBounds.right - inOutBounds.right; + inOutBounds.right = parentBounds.right - origLeft; + return; + case 3: + inOutBounds.left = parentBounds.bottom - inOutBounds.bottom; + inOutBounds.bottom = inOutBounds.right; + inOutBounds.right = parentBounds.bottom - inOutBounds.top; + inOutBounds.top = origLeft; + return; + } + } + + /** + * Calculates the stable insets if we already have the non-decor insets. + */ + private static void convertNonDecorInsetsToStableInsets(Resources res, Rect inOutInsets, + int displayWidth, int displayHeight, boolean hasStatusBar) { + if (!hasStatusBar) { + return; + } + int statusBarHeight = getStatusBarHeight(displayWidth > displayHeight, res); + inOutInsets.top = Math.max(inOutInsets.top, statusBarHeight); + } + + /** + * Calculates the insets for the areas that could never be removed in Honeycomb, i.e. system + * bar or button bar. + * + * @param displayRotation the current display rotation + * @param displayWidth the current display width + * @param displayHeight the current display height + * @param displayCutout the current display cutout + * @param outInsets the insets to return + */ + static void computeNonDecorInsets(Resources res, int displayRotation, int displayWidth, + int displayHeight, DisplayCutout displayCutout, int uiMode, Rect outInsets, + boolean hasNavigationBar) { + outInsets.setEmpty(); + + // Only navigation bar + if (hasNavigationBar) { + int position = navigationBarPosition(res, displayWidth, displayHeight, displayRotation); + int navBarSize = + getNavigationBarSize(res, position, displayWidth > displayHeight, uiMode); + if (position == NAV_BAR_BOTTOM) { + outInsets.bottom = navBarSize; + } else if (position == NAV_BAR_RIGHT) { + outInsets.right = navBarSize; + } else if (position == NAV_BAR_LEFT) { + outInsets.left = navBarSize; + } + } + + if (displayCutout != null) { + outInsets.left += displayCutout.getSafeInsetLeft(); + outInsets.top += displayCutout.getSafeInsetTop(); + outInsets.right += displayCutout.getSafeInsetRight(); + outInsets.bottom += displayCutout.getSafeInsetBottom(); + } + } + + /** + * Calculates the stable insets without running a layout. + * + * @param displayRotation the current display rotation + * @param displayWidth the current display width + * @param displayHeight the current display height + * @param displayCutout the current display cutout + * @param outInsets the insets to return + */ + static void computeStableInsets(Resources res, int displayRotation, int displayWidth, + int displayHeight, DisplayCutout displayCutout, int uiMode, Rect outInsets, + boolean hasNavigationBar, boolean hasStatusBar) { + outInsets.setEmpty(); + + // Navigation bar and status bar. + computeNonDecorInsets(res, displayRotation, displayWidth, displayHeight, displayCutout, + uiMode, outInsets, hasNavigationBar); + convertNonDecorInsetsToStableInsets(res, outInsets, displayWidth, displayHeight, + hasStatusBar); + } + + /** Retrieve the statusbar height from resources. */ + static int getStatusBarHeight(boolean landscape, Resources res) { + return landscape ? res.getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_height_landscape) + : res.getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_height_portrait); + } + + /** Calculate the DisplayCutout for a particular display size/rotation. */ + public static DisplayCutout calculateDisplayCutoutForRotation( + DisplayCutout cutout, int rotation, int displayWidth, int displayHeight) { + if (cutout == null || cutout == DisplayCutout.NO_CUTOUT) { + return null; + } + final Insets waterfallInsets = + RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation); + if (rotation == ROTATION_0) { + return computeSafeInsets(cutout, displayWidth, displayHeight); + } + final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270); + Rect[] cutoutRects = cutout.getBoundingRectsAll(); + final Rect[] newBounds = new Rect[cutoutRects.length]; + final Rect displayBounds = new Rect(0, 0, displayWidth, displayHeight); + for (int i = 0; i < cutoutRects.length; ++i) { + final Rect rect = new Rect(cutoutRects[i]); + if (!rect.isEmpty()) { + rotateBounds(rect, displayBounds, rotation); + } + newBounds[getBoundIndexFromRotation(i, rotation)] = rect; + } + return computeSafeInsets( + DisplayCutout.fromBoundsAndWaterfall(newBounds, waterfallInsets), + rotated ? displayHeight : displayWidth, + rotated ? displayWidth : displayHeight); + } + + private static int getBoundIndexFromRotation(int index, int rotation) { + return (index - rotation) < 0 + ? index - rotation + DisplayCutout.BOUNDS_POSITION_LENGTH + : index - rotation; + } + + /** Calculate safe insets. */ + public static DisplayCutout computeSafeInsets(DisplayCutout inner, + int displayWidth, int displayHeight) { + if (inner == DisplayCutout.NO_CUTOUT) { + return null; + } + + final Size displaySize = new Size(displayWidth, displayHeight); + final Rect safeInsets = computeSafeInsets(displaySize, inner); + return inner.replaceSafeInsets(safeInsets); + } + + private static Rect computeSafeInsets( + Size displaySize, DisplayCutout cutout) { + if (displaySize.getWidth() == displaySize.getHeight()) { + throw new UnsupportedOperationException("not implemented: display=" + displaySize + + " cutout=" + cutout); + } + + int leftInset = Math.max(cutout.getWaterfallInsets().left, + findCutoutInsetForSide(displaySize, cutout.getBoundingRectLeft(), Gravity.LEFT)); + int topInset = Math.max(cutout.getWaterfallInsets().top, + findCutoutInsetForSide(displaySize, cutout.getBoundingRectTop(), Gravity.TOP)); + int rightInset = Math.max(cutout.getWaterfallInsets().right, + findCutoutInsetForSide(displaySize, cutout.getBoundingRectRight(), Gravity.RIGHT)); + int bottomInset = Math.max(cutout.getWaterfallInsets().bottom, + findCutoutInsetForSide(displaySize, cutout.getBoundingRectBottom(), + Gravity.BOTTOM)); + + return new Rect(leftInset, topInset, rightInset, bottomInset); + } + + private static int findCutoutInsetForSide(Size display, Rect boundingRect, int gravity) { + if (boundingRect.isEmpty()) { + return 0; + } + + int inset = 0; + switch (gravity) { + case Gravity.TOP: + return Math.max(inset, boundingRect.bottom); + case Gravity.BOTTOM: + return Math.max(inset, display.getHeight() - boundingRect.top); + case Gravity.LEFT: + return Math.max(inset, boundingRect.right); + case Gravity.RIGHT: + return Math.max(inset, display.getWidth() - boundingRect.left); + default: + throw new IllegalArgumentException("unknown gravity: " + gravity); + } + } + + static boolean hasNavigationBar(DisplayInfo info, Context context, int displayId) { + if (displayId == Display.DEFAULT_DISPLAY) { + // Allow a system property to override this. Used by the emulator. + final String navBarOverride = SystemProperties.get("qemu.hw.mainkeys"); + if ("1".equals(navBarOverride)) { + return false; + } else if ("0".equals(navBarOverride)) { + return true; + } + return context.getResources().getBoolean(R.bool.config_showNavigationBar); + } else { + boolean isUntrustedVirtualDisplay = info.type == Display.TYPE_VIRTUAL + && info.ownerUid != SYSTEM_UID; + final ContentResolver resolver = context.getContentResolver(); + boolean forceDesktopOnExternal = Settings.Global.getInt(resolver, + DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0) != 0; + + return ((info.flags & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0 + || (forceDesktopOnExternal && !isUntrustedVirtualDisplay)); + // TODO(b/142569966): make sure VR2D and DisplayWindowSettings are moved here somehow. + } + } + + static boolean hasStatusBar(int displayId) { + return displayId == Display.DEFAULT_DISPLAY; + } + + /** Retrieve navigation bar position from resources based on rotation and size. */ + public static @NavBarPosition int navigationBarPosition(Resources res, int displayWidth, + int displayHeight, int rotation) { + boolean navBarCanMove = displayWidth != displayHeight && res.getBoolean( + com.android.internal.R.bool.config_navBarCanMove); + if (navBarCanMove && displayWidth > displayHeight) { + if (rotation == Surface.ROTATION_90) { + return NAV_BAR_RIGHT; + } else { + return NAV_BAR_LEFT; + } + } + return NAV_BAR_BOTTOM; + } + + /** Retrieve navigation bar size from resources based on side/orientation/ui-mode */ + public static int getNavigationBarSize(Resources res, int navBarSide, boolean landscape, + int uiMode) { + final boolean carMode = (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR; + if (carMode) { + if (navBarSide == NAV_BAR_BOTTOM) { + return res.getDimensionPixelSize(landscape + ? R.dimen.navigation_bar_height_landscape_car_mode + : R.dimen.navigation_bar_height_car_mode); + } else { + return res.getDimensionPixelSize(R.dimen.navigation_bar_width_car_mode); + } + + } else { + if (navBarSide == NAV_BAR_BOTTOM) { + return res.getDimensionPixelSize(landscape + ? R.dimen.navigation_bar_height_landscape + : R.dimen.navigation_bar_height); + } else { + return res.getDimensionPixelSize(R.dimen.navigation_bar_width); + } + } + } + + /** @see com.android.server.wm.DisplayPolicy#getNavigationBarFrameHeight */ + public static int getNavigationBarFrameHeight(Resources res, boolean landscape) { + return res.getDimensionPixelSize(landscape + ? R.dimen.navigation_bar_frame_height_landscape + : R.dimen.navigation_bar_frame_height); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java new file mode 100644 index 000000000000..8abe9eeb6a9a --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java @@ -0,0 +1,393 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.common; + +import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; + +import android.annotation.NonNull; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.Region; +import android.os.Bundle; +import android.os.IBinder; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.util.MergedConfiguration; +import android.util.Slog; +import android.util.SparseArray; +import android.view.Display; +import android.view.DisplayCutout; +import android.view.DragEvent; +import android.view.IScrollCaptureController; +import android.view.IWindow; +import android.view.IWindowManager; +import android.view.IWindowSession; +import android.view.IWindowSessionCallback; +import android.view.InsetsSourceControl; +import android.view.InsetsState; +import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.WindowlessWindowManager; + +import com.android.internal.os.IResultReceiver; + +import java.util.HashMap; + +/** + * Represents the "windowing" layer of the WM Shell. This layer allows shell components to place and + * manipulate windows without talking to WindowManager. + */ +public class SystemWindows { + private static final String TAG = "SystemWindows"; + + private final SparseArray<PerDisplay> mPerDisplay = new SparseArray<>(); + private final HashMap<View, SurfaceControlViewHost> mViewRoots = new HashMap<>(); + private final DisplayController mDisplayController; + private final IWindowManager mWmService; + private IWindowSession mSession; + + private final DisplayController.OnDisplaysChangedListener mDisplayListener = + new DisplayController.OnDisplaysChangedListener() { + @Override + public void onDisplayAdded(int displayId) { } + + @Override + public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { + PerDisplay pd = mPerDisplay.get(displayId); + if (pd == null) { + return; + } + pd.updateConfiguration(newConfig); + } + + @Override + public void onDisplayRemoved(int displayId) { } + }; + + public SystemWindows(DisplayController displayController, IWindowManager wmService) { + mWmService = wmService; + mDisplayController = displayController; + mDisplayController.addDisplayWindowListener(mDisplayListener); + try { + mSession = wmService.openSession( + new IWindowSessionCallback.Stub() { + @Override + public void onAnimatorScaleChanged(float scale) {} + }); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to create layer", e); + } + } + + /** + * Adds a view to system-ui window management. + */ + public void addView(View view, WindowManager.LayoutParams attrs, int displayId, + int windowType) { + PerDisplay pd = mPerDisplay.get(displayId); + if (pd == null) { + pd = new PerDisplay(displayId); + mPerDisplay.put(displayId, pd); + } + pd.addView(view, attrs, windowType); + } + + /** + * Removes a view from system-ui window management. + * @param view + */ + public void removeView(View view) { + SurfaceControlViewHost root = mViewRoots.remove(view); + root.release(); + } + + /** + * Updates the layout params of a view. + */ + public void updateViewLayout(@NonNull View view, ViewGroup.LayoutParams params) { + SurfaceControlViewHost root = mViewRoots.get(view); + if (root == null || !(params instanceof WindowManager.LayoutParams)) { + return; + } + view.setLayoutParams(params); + root.relayout((WindowManager.LayoutParams) params); + } + + /** + * Sets the touchable region of a view's window. This will be cropped to the window size. + * @param view + * @param region + */ + public void setTouchableRegion(@NonNull View view, Region region) { + SurfaceControlViewHost root = mViewRoots.get(view); + if (root == null) { + return; + } + WindowlessWindowManager wwm = root.getWindowlessWM(); + if (!(wwm instanceof SysUiWindowManager)) { + return; + } + ((SysUiWindowManager) wwm).setTouchableRegionForWindow(view, region); + } + + /** + * Adds a root for system-ui window management with no views. Only useful for IME. + */ + public void addRoot(int displayId, int windowType) { + PerDisplay pd = mPerDisplay.get(displayId); + if (pd == null) { + pd = new PerDisplay(displayId); + mPerDisplay.put(displayId, pd); + } + pd.addRoot(windowType); + } + + /** + * Get the IWindow token for a specific root. + * + * @param windowType A window type from {@link WindowManager}. + */ + IWindow getWindow(int displayId, int windowType) { + PerDisplay pd = mPerDisplay.get(displayId); + if (pd == null) { + return null; + } + return pd.getWindow(windowType); + } + + /** + * Gets the SurfaceControl associated with a root view. This is the same surface that backs the + * ViewRootImpl. + */ + public SurfaceControl getViewSurface(View rootView) { + for (int i = 0; i < mPerDisplay.size(); ++i) { + for (int iWm = 0; iWm < mPerDisplay.valueAt(i).mWwms.size(); ++iWm) { + SurfaceControl out = mPerDisplay.valueAt(i).mWwms.valueAt(iWm) + .getSurfaceControlForWindow(rootView); + if (out != null) { + return out; + } + } + } + return null; + } + + private class PerDisplay { + final int mDisplayId; + private final SparseArray<SysUiWindowManager> mWwms = new SparseArray<>(); + + PerDisplay(int displayId) { + mDisplayId = displayId; + } + + public void addView(View view, WindowManager.LayoutParams attrs, int windowType) { + SysUiWindowManager wwm = addRoot(windowType); + if (wwm == null) { + Slog.e(TAG, "Unable to create systemui root"); + return; + } + final Display display = mDisplayController.getDisplay(mDisplayId); + SurfaceControlViewHost viewRoot = + new SurfaceControlViewHost( + view.getContext(), display, wwm, true /* useSfChoreographer */); + attrs.flags |= FLAG_HARDWARE_ACCELERATED; + viewRoot.setView(view, attrs); + mViewRoots.put(view, viewRoot); + + try { + mWmService.setShellRootAccessibilityWindow(mDisplayId, windowType, + viewRoot.getWindowToken()); + } catch (RemoteException e) { + Slog.e(TAG, "Error setting accessibility window for " + mDisplayId + ":" + + windowType, e); + } + } + + SysUiWindowManager addRoot(int windowType) { + SysUiWindowManager wwm = mWwms.get(windowType); + if (wwm != null) { + return wwm; + } + SurfaceControl rootSurface = null; + ContainerWindow win = new ContainerWindow(); + try { + rootSurface = mWmService.addShellRoot(mDisplayId, win, windowType); + } catch (RemoteException e) { + } + if (rootSurface == null) { + Slog.e(TAG, "Unable to get root surfacecontrol for systemui"); + return null; + } + Context displayContext = mDisplayController.getDisplayContext(mDisplayId); + wwm = new SysUiWindowManager(mDisplayId, displayContext, rootSurface, win); + mWwms.put(windowType, wwm); + return wwm; + } + + IWindow getWindow(int windowType) { + SysUiWindowManager wwm = mWwms.get(windowType); + if (wwm == null) { + return null; + } + return wwm.mContainerWindow; + } + + void updateConfiguration(Configuration configuration) { + for (int i = 0; i < mWwms.size(); ++i) { + mWwms.valueAt(i).updateConfiguration(configuration); + } + } + } + + /** + * A subclass of WindowlessWindowManager that provides insets to its viewroots. + */ + public class SysUiWindowManager extends WindowlessWindowManager { + final int mDisplayId; + ContainerWindow mContainerWindow; + public SysUiWindowManager(int displayId, Context ctx, SurfaceControl rootSurface, + ContainerWindow container) { + super(ctx.getResources().getConfiguration(), rootSurface, null /* hostInputToken */); + mContainerWindow = container; + mDisplayId = displayId; + } + + @Override + public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs, + int requestedWidth, int requestedHeight, int viewVisibility, int flags, + long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, + Rect outVisibleInsets, Rect outStableInsets, + DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration, + SurfaceControl outSurfaceControl, InsetsState outInsetsState, + InsetsSourceControl[] outActiveControls, Point outSurfaceSize, + SurfaceControl outBLASTSurfaceControl) { + int res = super.relayout(window, seq, attrs, requestedWidth, requestedHeight, + viewVisibility, flags, frameNumber, outFrame, outOverscanInsets, + outContentInsets, outVisibleInsets, outStableInsets, + cutout, mergedConfiguration, outSurfaceControl, outInsetsState, + outActiveControls, outSurfaceSize, outBLASTSurfaceControl); + if (res != 0) { + return res; + } + DisplayLayout dl = mDisplayController.getDisplayLayout(mDisplayId); + outStableInsets.set(dl.stableInsets()); + return 0; + } + + void updateConfiguration(Configuration configuration) { + setConfiguration(configuration); + } + + SurfaceControl getSurfaceControlForWindow(View rootView) { + return getSurfaceControl(rootView); + } + + void setTouchableRegionForWindow(View rootView, Region region) { + IBinder token = rootView.getWindowToken(); + if (token == null) { + return; + } + setTouchRegion(token, region); + } + } + + static class ContainerWindow extends IWindow.Stub { + ContainerWindow() {} + + @Override + public void resized(Rect frame, Rect contentInsets, Rect visibleInsets, Rect stableInsets, + boolean reportDraw, MergedConfiguration newMergedConfiguration, Rect backDropFrame, + boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, + DisplayCutout.ParcelableWrapper displayCutout) {} + + @Override + public void locationInParentDisplayChanged(Point offset) {} + + @Override + public void insetsChanged(InsetsState insetsState) {} + + @Override + public void insetsControlChanged(InsetsState insetsState, + InsetsSourceControl[] activeControls) {} + + @Override + public void showInsets(int types, boolean fromIme) {} + + @Override + public void hideInsets(int types, boolean fromIme) {} + + @Override + public void moved(int newX, int newY) {} + + @Override + public void dispatchAppVisibility(boolean visible) {} + + @Override + public void dispatchGetNewSurface() {} + + @Override + public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {} + + @Override + public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {} + + @Override + public void closeSystemDialogs(String reason) {} + + @Override + public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, + float zoom, boolean sync) {} + + @Override + public void dispatchWallpaperCommand(String action, int x, int y, + int z, Bundle extras, boolean sync) {} + + /* Drag/drop */ + @Override + public void dispatchDragEvent(DragEvent event) {} + + @Override + public void updatePointerIcon(float x, float y) {} + + @Override + public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility, + int localValue, int localChanges) {} + + @Override + public void dispatchWindowShown() {} + + @Override + public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) {} + + @Override + public void dispatchPointerCaptureChanged(boolean hasCapture) {} + + @Override + public void requestScrollCapture(IScrollCaptureController controller) { + try { + controller.onClientUnavailable(); + } catch (RemoteException ex) { + // ignore + } + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TransactionPool.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TransactionPool.java new file mode 100644 index 000000000000..4c34566b0d98 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TransactionPool.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.common; + +import android.util.Pools; +import android.view.SurfaceControl; + +/** + * Provides a synchronized pool of {@link SurfaceControl.Transaction}s to minimize allocations. + */ +public class TransactionPool { + private final Pools.SynchronizedPool<SurfaceControl.Transaction> mTransactionPool = + new Pools.SynchronizedPool<>(4); + + public TransactionPool() { + } + + /** Gets a transaction from the pool. */ + public SurfaceControl.Transaction acquire() { + SurfaceControl.Transaction t = mTransactionPool.acquire(); + if (t == null) { + return new SurfaceControl.Transaction(); + } + return t; + } + + /** + * Return a transaction to the pool. DO NOT call {@link SurfaceControl.Transaction#close()} if + * returning to pool. + */ + public void release(SurfaceControl.Transaction t) { + if (!mTransactionPool.release(t)) { + t.close(); + } + } +} diff --git a/libs/WindowManager/Shell/tests/Android.bp b/libs/WindowManager/Shell/tests/Android.bp index 78fa45ebdf94..9868879cebb9 100644 --- a/libs/WindowManager/Shell/tests/Android.bp +++ b/libs/WindowManager/Shell/tests/Android.bp @@ -36,9 +36,6 @@ android_test { "libstaticjvmtiagent", ], - sdk_version: "current", - platform_apis: true, - optimize: { enabled: false, }, diff --git a/libs/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/WindowManagerShellTest.java index 376875b143a1..f1ead3c8a441 100644 --- a/libs/WindowManager/Shell/tests/src/com/android/wm/shell/tests/WindowManagerShellTest.java +++ b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/WindowManagerShellTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,13 +14,11 @@ * limitations under the License. */ -package com.android.wm.shell.tests; +package com.android.wm.shell; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.wm.shell.WindowManagerShell; - import org.junit.Test; import org.junit.runner.RunWith; diff --git a/libs/WindowManager/Shell/tests/src/com/android/wm/shell/common/DisplayLayoutTest.java b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/common/DisplayLayoutTest.java new file mode 100644 index 000000000000..2b5b77e49e3a --- /dev/null +++ b/libs/WindowManager/Shell/tests/src/com/android/wm/shell/common/DisplayLayoutTest.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.common; + +import static android.content.res.Configuration.UI_MODE_TYPE_NORMAL; +import static android.view.Surface.ROTATION_0; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Insets; +import android.graphics.Rect; +import android.view.DisplayCutout; +import android.view.DisplayInfo; + +import androidx.test.filters.SmallTest; + +import com.android.internal.R; + +import org.junit.Test; + +@SmallTest +public class DisplayLayoutTest { + + @Test + public void testInsets() { + Resources res = createResources(40, 50, false, 30, 40); + // Test empty display, no bars or anything + DisplayInfo info = createDisplayInfo(1000, 1500, 0, ROTATION_0); + DisplayLayout dl = new DisplayLayout(info, res, false, false); + assertEquals(new Rect(0, 0, 0, 0), dl.stableInsets()); + assertEquals(new Rect(0, 0, 0, 0), dl.nonDecorInsets()); + + // Test with bars + dl = new DisplayLayout(info, res, true, true); + assertEquals(new Rect(0, 40, 0, 50), dl.stableInsets()); + assertEquals(new Rect(0, 0, 0, 50), dl.nonDecorInsets()); + + // Test just cutout + info = createDisplayInfo(1000, 1500, 60, ROTATION_0); + dl = new DisplayLayout(info, res, false, false); + assertEquals(new Rect(0, 60, 0, 0), dl.stableInsets()); + assertEquals(new Rect(0, 60, 0, 0), dl.nonDecorInsets()); + + // Test with bars and cutout + dl = new DisplayLayout(info, res, true, true); + assertEquals(new Rect(0, 60, 0, 50), dl.stableInsets()); + assertEquals(new Rect(0, 60, 0, 50), dl.nonDecorInsets()); + } + + @Test + public void testRotate() { + // Basic rotate utility + Rect testParent = new Rect(0, 0, 1000, 600); + Rect testInner = new Rect(40, 20, 120, 80); + Rect testResult = new Rect(testInner); + DisplayLayout.rotateBounds(testResult, testParent, 1); + assertEquals(new Rect(20, 880, 80, 960), testResult); + testResult.set(testInner); + DisplayLayout.rotateBounds(testResult, testParent, 2); + assertEquals(new Rect(880, 20, 960, 80), testResult); + testResult.set(testInner); + DisplayLayout.rotateBounds(testResult, testParent, 3); + assertEquals(new Rect(520, 40, 580, 120), testResult); + + Resources res = createResources(40, 50, false, 30, 40); + DisplayInfo info = createDisplayInfo(1000, 1500, 60, ROTATION_0); + DisplayLayout dl = new DisplayLayout(info, res, true, true); + assertEquals(new Rect(0, 60, 0, 50), dl.stableInsets()); + assertEquals(new Rect(0, 60, 0, 50), dl.nonDecorInsets()); + + // Rotate to 90 + dl.rotateTo(res, ROTATION_90); + assertEquals(new Rect(60, 30, 0, 40), dl.stableInsets()); + assertEquals(new Rect(60, 0, 0, 40), dl.nonDecorInsets()); + + // Rotate with moving navbar + res = createResources(40, 50, true, 30, 40); + dl = new DisplayLayout(info, res, true, true); + dl.rotateTo(res, ROTATION_270); + assertEquals(new Rect(40, 30, 60, 0), dl.stableInsets()); + assertEquals(new Rect(40, 0, 60, 0), dl.nonDecorInsets()); + } + + private Resources createResources( + int navLand, int navPort, boolean navMoves, int statusLand, int statusPort) { + Configuration cfg = new Configuration(); + cfg.uiMode = UI_MODE_TYPE_NORMAL; + Resources res = mock(Resources.class); + doReturn(navLand).when(res).getDimensionPixelSize( + R.dimen.navigation_bar_height_landscape_car_mode); + doReturn(navPort).when(res).getDimensionPixelSize(R.dimen.navigation_bar_height_car_mode); + doReturn(navLand).when(res).getDimensionPixelSize(R.dimen.navigation_bar_width_car_mode); + doReturn(navLand).when(res).getDimensionPixelSize(R.dimen.navigation_bar_height_landscape); + doReturn(navPort).when(res).getDimensionPixelSize(R.dimen.navigation_bar_height); + doReturn(navLand).when(res).getDimensionPixelSize(R.dimen.navigation_bar_width); + doReturn(navMoves).when(res).getBoolean(R.bool.config_navBarCanMove); + doReturn(statusLand).when(res).getDimensionPixelSize(R.dimen.status_bar_height_landscape); + doReturn(statusPort).when(res).getDimensionPixelSize(R.dimen.status_bar_height_portrait); + doReturn(cfg).when(res).getConfiguration(); + return res; + } + + private DisplayInfo createDisplayInfo(int width, int height, int cutoutHeight, int rotation) { + DisplayInfo info = new DisplayInfo(); + info.logicalWidth = width; + info.logicalHeight = height; + info.rotation = rotation; + if (cutoutHeight > 0) { + info.displayCutout = new DisplayCutout( + Insets.of(0, cutoutHeight, 0, 0) /* safeInsets */, null /* boundLeft */, + new Rect(width / 2 - cutoutHeight, 0, width / 2 + cutoutHeight, + cutoutHeight) /* boundTop */, null /* boundRight */, + null /* boundBottom */); + } else { + info.displayCutout = DisplayCutout.NO_CUTOUT; + } + info.logicalDensityDpi = 300; + return info; + } +} diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index aa34edf487fe..8ab7da5257ab 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -155,6 +155,7 @@ cc_test { android: { srcs: [ "tests/BackupData_test.cpp", + "tests/BackupHelpers_test.cpp", "tests/ObbFile_test.cpp", "tests/PosixUtils_test.cpp", ], diff --git a/libs/androidfw/BackupHelpers.cpp b/libs/androidfw/BackupHelpers.cpp index 8bfe2b6a259a..e80e9486c8b2 100644 --- a/libs/androidfw/BackupHelpers.cpp +++ b/libs/androidfw/BackupHelpers.cpp @@ -479,7 +479,7 @@ void send_tarfile_chunk(BackupDataWriter* writer, const char* buffer, size_t siz } int write_tarfile(const String8& packageName, const String8& domain, - const String8& rootpath, const String8& filepath, off_t* outSize, + const String8& rootpath, const String8& filepath, off64_t* outSize, BackupDataWriter* writer) { // In the output stream everything is stored relative to the root diff --git a/libs/androidfw/include/androidfw/BackupHelpers.h b/libs/androidfw/include/androidfw/BackupHelpers.h index 2da247b77c0a..a0fa13662cb9 100644 --- a/libs/androidfw/include/androidfw/BackupHelpers.h +++ b/libs/androidfw/include/androidfw/BackupHelpers.h @@ -137,7 +137,7 @@ int back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapsh char const* const* files, char const* const *keys, int fileCount); int write_tarfile(const String8& packageName, const String8& domain, - const String8& rootPath, const String8& filePath, off_t* outSize, + const String8& rootPath, const String8& filePath, off64_t* outSize, BackupDataWriter* outputStream); class RestoreHelperBase diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index e351a46d633a..e10a7f3f5c61 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -1717,6 +1717,10 @@ struct ResTable_overlayable_policy_header // The overlay must be signed with the same signature as the actor declared for the target // resource ACTOR_SIGNATURE = 0x00000080, + + // The overlay must be signed with the same signature as the reference package declared + // in the SystemConfig + CONFIG_SIGNATURE = 0x00000100, }; using PolicyBitmask = uint32_t; diff --git a/libs/androidfw/tests/BackupHelpers_test.cpp b/libs/androidfw/tests/BackupHelpers_test.cpp new file mode 100644 index 000000000000..86b7fb361228 --- /dev/null +++ b/libs/androidfw/tests/BackupHelpers_test.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BackupHelpers_test" +#include <androidfw/BackupHelpers.h> + +#include <gtest/gtest.h> + +#include <fcntl.h> +#include <utils/String8.h> +#include <android-base/file.h> + +namespace android { + +class BackupHelpersTest : public testing::Test { +protected: + + virtual void SetUp() { + } + virtual void TearDown() { + } +}; + +TEST_F(BackupHelpersTest, WriteTarFileWithSizeLessThan2GB) { + TemporaryFile tf; + // Allocate a 1 KB file. + off64_t fileSize = 1024; + ASSERT_EQ(0, posix_fallocate64(tf.fd, 0, fileSize)); + off64_t tarSize = 0; + int err = write_tarfile(/* packageName */ String8("test-pkg"), /* domain */ String8(""), /* rootpath */ String8(""), /* filePath */ String8(tf.path), /* outSize */ &tarSize, /* writer */ NULL); + ASSERT_EQ(err, 0); + // Returned tarSize includes 512 B for the header. + off64_t expectedTarSize = fileSize + 512; + ASSERT_EQ(tarSize, expectedTarSize); +} + +TEST_F(BackupHelpersTest, WriteTarFileWithSizeGreaterThan2GB) { + TemporaryFile tf; + // Allocate a 2 GB file. + off64_t fileSize = 2ll * 1024ll * 1024ll * 1024ll + 512ll; + ASSERT_EQ(0, posix_fallocate64(tf.fd, 0, fileSize)); + off64_t tarSize = 0; + int err = write_tarfile(/* packageName */ String8("test-pkg"), /* domain */ String8(""), /* rootpath */ String8(""), /* filePath */ String8(tf.path), /* outSize */ &tarSize, /* writer */ NULL); + ASSERT_EQ(err, 0); + // Returned tarSize includes 512 B for the header. + off64_t expectedTarSize = fileSize + 512; + ASSERT_EQ(tarSize, expectedTarSize); +} +} + diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index aa842ff6a7b7..9ae5ad97ed36 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -53,8 +53,6 @@ cc_defaults { host: { include_dirs: [ "external/vulkan-headers/include", - "frameworks/native/libs/math/include", - "frameworks/native/libs/ui/include", ], cflags: [ "-Wno-unused-variable", @@ -71,6 +69,10 @@ cc_defaults { "libminikin", ], + static_libs: [ + "libui-types", + ], + target: { android: { shared_libs: [ @@ -83,7 +85,6 @@ cc_defaults { "libGLESv2", "libGLESv3", "libvulkan", - "libui", "libnativedisplay", "libnativewindow", "libprotobuf-cpp-lite", @@ -152,6 +153,45 @@ cc_defaults { } // ------------------------ +// framework-graphics jar +// ------------------------ + +java_sdk_library { + name: "framework-graphics", + defaults: ["framework-module-defaults"], + visibility: [ + "//frameworks/base", // Framework + ], + + srcs: [ + ":framework-graphics-srcs", + ], + + permitted_packages: [ + "android.graphics", + ], + + // TODO: once framework-graphics is officially part of the + // UI-rendering module this line would no longer be + // needed. + installable: true, + + // Disable api_lint that the defaults enable + // TODO: enable this + api_lint: { + enabled: false, + }, +} + +filegroup { + name: "framework-graphics-srcs", + srcs: [ + "apex/java/**/*.java", + ], + path: "apex/java" +} + +// ------------------------ // APEX // ------------------------ @@ -346,6 +386,7 @@ cc_defaults { "libstatspull", "libstatssocket", "libpdfium", + "libbinder_ndk", ], static_libs: [ "libgif", @@ -457,6 +498,7 @@ cc_defaults { "service/GraphicsStatsService.cpp", "thread/CommonPool.cpp", "utils/GLUtils.cpp", + "utils/NdkUtils.cpp", "utils/StringUtils.cpp", "AutoBackendTextureRelease.cpp", "DeferredLayerUpdater.cpp", @@ -499,6 +541,11 @@ cc_library { "android_graphics_jni", ], export_header_lib_headers: ["android_graphics_apex_headers"], + target: { + android: { + version_script: "libhwui.map.txt", + } + }, } cc_library_static { @@ -516,6 +563,7 @@ cc_defaults { android: { shared_libs: [ "libgui", + "libui", ], } }, diff --git a/libs/hwui/AnimationContext.h b/libs/hwui/AnimationContext.h index 74d5e79c0b77..f8a2072ffbdb 100644 --- a/libs/hwui/AnimationContext.h +++ b/libs/hwui/AnimationContext.h @@ -77,8 +77,8 @@ class AnimationContext { PREVENT_COPY_AND_ASSIGN(AnimationContext); public: - ANDROID_API explicit AnimationContext(renderthread::TimeLord& clock); - ANDROID_API virtual ~AnimationContext(); + explicit AnimationContext(renderthread::TimeLord& clock); + virtual ~AnimationContext(); nsecs_t frameTimeMs() { return mFrameTimeMs; } bool hasAnimations() { @@ -87,22 +87,22 @@ public: // Will always add to the next frame list, which is swapped when // startFrame() is called - ANDROID_API void addAnimatingRenderNode(RenderNode& node); + void addAnimatingRenderNode(RenderNode& node); // Marks the start of a frame, which will update the frame time and move all // next frame animations into the current frame - ANDROID_API virtual void startFrame(TreeInfo::TraversalMode mode); + virtual void startFrame(TreeInfo::TraversalMode mode); // Runs any animations still left in mCurrentFrameAnimations that were not run // as part of the standard RenderNode:prepareTree pass. - ANDROID_API virtual void runRemainingAnimations(TreeInfo& info); + virtual void runRemainingAnimations(TreeInfo& info); - ANDROID_API virtual void callOnFinished(BaseRenderNodeAnimator* animator, + virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener); - ANDROID_API virtual void destroy(); + virtual void destroy(); - ANDROID_API virtual void pauseAnimators() {} + virtual void pauseAnimators() {} private: friend class AnimationHandle; diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h index ed7b6eb1cf4a..3c9f1ea1b6e3 100644 --- a/libs/hwui/Animator.h +++ b/libs/hwui/Animator.h @@ -39,10 +39,10 @@ class RenderProperties; class AnimationListener : public VirtualLightRefBase { public: - ANDROID_API virtual void onAnimationFinished(BaseRenderNodeAnimator*) = 0; + virtual void onAnimationFinished(BaseRenderNodeAnimator*) = 0; protected: - ANDROID_API virtual ~AnimationListener() {} + virtual ~AnimationListener() {} }; enum class RepeatMode { @@ -55,34 +55,34 @@ class BaseRenderNodeAnimator : public VirtualLightRefBase { PREVENT_COPY_AND_ASSIGN(BaseRenderNodeAnimator); public: - ANDROID_API void setStartValue(float value); - ANDROID_API void setInterpolator(Interpolator* interpolator); - ANDROID_API void setDuration(nsecs_t durationInMs); - ANDROID_API nsecs_t duration() { return mDuration; } - ANDROID_API void setStartDelay(nsecs_t startDelayInMs); - ANDROID_API nsecs_t startDelay() { return mStartDelay; } - ANDROID_API void setListener(AnimationListener* listener) { mListener = listener; } + void setStartValue(float value); + void setInterpolator(Interpolator* interpolator); + void setDuration(nsecs_t durationInMs); + nsecs_t duration() { return mDuration; } + void setStartDelay(nsecs_t startDelayInMs); + nsecs_t startDelay() { return mStartDelay; } + void setListener(AnimationListener* listener) { mListener = listener; } AnimationListener* listener() { return mListener.get(); } - ANDROID_API void setAllowRunningAsync(bool mayRunAsync) { mMayRunAsync = mayRunAsync; } + void setAllowRunningAsync(bool mayRunAsync) { mMayRunAsync = mayRunAsync; } bool mayRunAsync() { return mMayRunAsync; } - ANDROID_API void start(); - ANDROID_API virtual void reset(); - ANDROID_API void reverse(); + void start(); + virtual void reset(); + void reverse(); // Terminates the animation at its current progress. - ANDROID_API void cancel(); + void cancel(); // Terminates the animation and skip to the end of the animation. - ANDROID_API virtual void end(); + virtual void end(); void attach(RenderNode* target); virtual void onAttached() {} void detach() { mTarget = nullptr; } - ANDROID_API void pushStaging(AnimationContext& context); - ANDROID_API bool animate(AnimationContext& context); + void pushStaging(AnimationContext& context); + bool animate(AnimationContext& context); // Returns the remaining time in ms for the animation. Note this should only be called during // an animation on RenderThread. - ANDROID_API nsecs_t getRemainingPlayTime(); + nsecs_t getRemainingPlayTime(); bool isRunning() { return mPlayState == PlayState::Running || mPlayState == PlayState::Reversing; @@ -90,7 +90,7 @@ public: bool isFinished() { return mPlayState == PlayState::Finished; } float finalValue() { return mFinalValue; } - ANDROID_API virtual uint32_t dirtyMask() = 0; + virtual uint32_t dirtyMask() = 0; void forceEndNow(AnimationContext& context); RenderNode* target() { return mTarget; } @@ -196,9 +196,9 @@ public: ALPHA, }; - ANDROID_API RenderPropertyAnimator(RenderProperty property, float finalValue); + RenderPropertyAnimator(RenderProperty property, float finalValue); - ANDROID_API virtual uint32_t dirtyMask(); + virtual uint32_t dirtyMask(); protected: virtual float getValue(RenderNode* target) const override; @@ -221,10 +221,10 @@ private: class CanvasPropertyPrimitiveAnimator : public BaseRenderNodeAnimator { public: - ANDROID_API CanvasPropertyPrimitiveAnimator(CanvasPropertyPrimitive* property, + CanvasPropertyPrimitiveAnimator(CanvasPropertyPrimitive* property, float finalValue); - ANDROID_API virtual uint32_t dirtyMask(); + virtual uint32_t dirtyMask(); protected: virtual float getValue(RenderNode* target) const override; @@ -241,10 +241,10 @@ public: ALPHA, }; - ANDROID_API CanvasPropertyPaintAnimator(CanvasPropertyPaint* property, PaintField field, + CanvasPropertyPaintAnimator(CanvasPropertyPaint* property, PaintField field, float finalValue); - ANDROID_API virtual uint32_t dirtyMask(); + virtual uint32_t dirtyMask(); protected: virtual float getValue(RenderNode* target) const override; @@ -257,9 +257,9 @@ private: class RevealAnimator : public BaseRenderNodeAnimator { public: - ANDROID_API RevealAnimator(int centerX, int centerY, float startValue, float finalValue); + RevealAnimator(int centerX, int centerY, float startValue, float finalValue); - ANDROID_API virtual uint32_t dirtyMask(); + virtual uint32_t dirtyMask(); protected: virtual float getValue(RenderNode* target) const override; diff --git a/libs/hwui/AnimatorManager.h b/libs/hwui/AnimatorManager.h index 9575391a8b3f..a0df01d5962c 100644 --- a/libs/hwui/AnimatorManager.h +++ b/libs/hwui/AnimatorManager.h @@ -54,7 +54,7 @@ public: void animateNoDamage(TreeInfo& info); // Hard-ends all animators. May only be called on the UI thread. - ANDROID_API void endAllStagingAnimators(); + void endAllStagingAnimators(); // Hard-ends all animators that have been pushed. Used for cleanup if // the ActivityContext is being destroyed diff --git a/libs/hwui/AutoBackendTextureRelease.cpp b/libs/hwui/AutoBackendTextureRelease.cpp index 72747e8fa543..33264d5d5c86 100644 --- a/libs/hwui/AutoBackendTextureRelease.cpp +++ b/libs/hwui/AutoBackendTextureRelease.cpp @@ -25,7 +25,8 @@ using namespace android::uirenderer::renderthread; namespace android { namespace uirenderer { -AutoBackendTextureRelease::AutoBackendTextureRelease(GrContext* context, AHardwareBuffer* buffer) { +AutoBackendTextureRelease::AutoBackendTextureRelease(GrDirectContext* context, + AHardwareBuffer* buffer) { AHardwareBuffer_Desc desc; AHardwareBuffer_describe(buffer, &desc); bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT); @@ -67,8 +68,9 @@ static void releaseProc(SkImage::ReleaseContext releaseContext) { textureRelease->unref(false); } -void AutoBackendTextureRelease::makeImage(AHardwareBuffer* buffer, android_dataspace dataspace, - GrContext* context) { +void AutoBackendTextureRelease::makeImage(AHardwareBuffer* buffer, + android_dataspace dataspace, + GrDirectContext* context) { AHardwareBuffer_Desc desc; AHardwareBuffer_describe(buffer, &desc); SkColorType colorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format); @@ -81,7 +83,7 @@ void AutoBackendTextureRelease::makeImage(AHardwareBuffer* buffer, android_datas } } -void AutoBackendTextureRelease::newBufferContent(GrContext* context) { +void AutoBackendTextureRelease::newBufferContent(GrDirectContext* context) { if (mBackendTexture.isValid()) { mUpdateProc(mImageCtx, context); } diff --git a/libs/hwui/AutoBackendTextureRelease.h b/libs/hwui/AutoBackendTextureRelease.h index acdd63cb7921..06f51fcd1105 100644 --- a/libs/hwui/AutoBackendTextureRelease.h +++ b/libs/hwui/AutoBackendTextureRelease.h @@ -31,7 +31,8 @@ namespace uirenderer { */ class AutoBackendTextureRelease final { public: - AutoBackendTextureRelease(GrContext* context, AHardwareBuffer* buffer); + AutoBackendTextureRelease(GrDirectContext* context, + AHardwareBuffer* buffer); const GrBackendTexture& getTexture() const { return mBackendTexture; } @@ -42,9 +43,11 @@ public: inline sk_sp<SkImage> getImage() const { return mImage; } - void makeImage(AHardwareBuffer* buffer, android_dataspace dataspace, GrContext* context); + void makeImage(AHardwareBuffer* buffer, + android_dataspace dataspace, + GrDirectContext* context); - void newBufferContent(GrContext* context); + void newBufferContent(GrDirectContext* context); private: // The only way to invoke dtor is with unref, when mUsageCount is 0. diff --git a/libs/hwui/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp index 8c37d73366c2..9d03ce5252a3 100644 --- a/libs/hwui/CanvasTransform.cpp +++ b/libs/hwui/CanvasTransform.cpp @@ -22,7 +22,6 @@ #include <SkGradientShader.h> #include <SkPaint.h> #include <SkShader.h> -#include <ui/ColorSpace.h> #include <algorithm> #include <cmath> diff --git a/libs/hwui/ColorMode.h b/libs/hwui/ColorMode.h new file mode 100644 index 000000000000..6d387f9ef43d --- /dev/null +++ b/libs/hwui/ColorMode.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +namespace android::uirenderer { + +// Must match the constants in ActivityInfo.java +enum class ColorMode { + // SRGB means HWUI will produce buffer in SRGB color space. + Default = 0, + // WideColorGamut selects the most optimal colorspace & format for the device's display + // Most commonly DisplayP3 + RGBA_8888 currently. + WideColorGamut = 1, + // HDR Rec2020 + F16 + Hdr = 2, + // HDR Rec2020 + 1010102 + Hdr10 = 3, +}; + +} // namespace android::uirenderer diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h index 030a20f31c42..2faa9d012d66 100644 --- a/libs/hwui/DamageAccumulator.h +++ b/libs/hwui/DamageAccumulator.h @@ -58,7 +58,7 @@ public: // Returns the current dirty area, *NOT* transformed by pushed transforms void peekAtDirty(SkRect* dest) const; - ANDROID_API void computeCurrentTransform(Matrix4* outMatrix) const; + void computeCurrentTransform(Matrix4* outMatrix) const; void finish(SkRect* totalDirty); diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index 67d8c07e61de..6589dbd50cf7 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -189,7 +189,7 @@ void DeferredLayerUpdater::detachSurfaceTexture() { sk_sp<SkImage> DeferredLayerUpdater::ImageSlot::createIfNeeded(AHardwareBuffer* buffer, android_dataspace dataspace, bool forceCreate, - GrContext* context) { + GrDirectContext* context) { if (!mTextureRelease || !mTextureRelease->getImage().get() || dataspace != mDataspace || forceCreate || mBuffer != buffer) { if (buffer != mBuffer) { diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h index c44c0d537fa7..6731e9c428d6 100644 --- a/libs/hwui/DeferredLayerUpdater.h +++ b/libs/hwui/DeferredLayerUpdater.h @@ -44,11 +44,11 @@ class DeferredLayerUpdater : public VirtualLightRefBase, public IGpuContextCallb public: // Note that DeferredLayerUpdater assumes it is taking ownership of the layer // and will not call incrementRef on it as a result. - ANDROID_API explicit DeferredLayerUpdater(RenderState& renderState); + explicit DeferredLayerUpdater(RenderState& renderState); - ANDROID_API ~DeferredLayerUpdater(); + ~DeferredLayerUpdater(); - ANDROID_API bool setSize(int width, int height) { + bool setSize(int width, int height) { if (mWidth != width || mHeight != height) { mWidth = width; mHeight = height; @@ -60,7 +60,7 @@ public: int getWidth() { return mWidth; } int getHeight() { return mHeight; } - ANDROID_API bool setBlend(bool blend) { + bool setBlend(bool blend) { if (blend != mBlend) { mBlend = blend; return true; @@ -68,18 +68,18 @@ public: return false; } - ANDROID_API void setSurfaceTexture(AutoTextureRelease&& consumer); + void setSurfaceTexture(AutoTextureRelease&& consumer); - ANDROID_API void updateTexImage() { mUpdateTexImage = true; } + void updateTexImage() { mUpdateTexImage = true; } - ANDROID_API void setTransform(const SkMatrix* matrix) { + void setTransform(const SkMatrix* matrix) { delete mTransform; mTransform = matrix ? new SkMatrix(*matrix) : nullptr; } SkMatrix* getTransform() { return mTransform; } - ANDROID_API void setPaint(const SkPaint* paint); + void setPaint(const SkPaint* paint); void apply(); @@ -106,7 +106,7 @@ private: ~ImageSlot() { clear(); } sk_sp<SkImage> createIfNeeded(AHardwareBuffer* buffer, android_dataspace dataspace, - bool forceCreate, GrContext* context); + bool forceCreate, GrDirectContext* context); private: void clear(); diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp index c24224cbbd67..07594715a84c 100644 --- a/libs/hwui/DeviceInfo.cpp +++ b/libs/hwui/DeviceInfo.cpp @@ -15,6 +15,8 @@ */ #include <DeviceInfo.h> +#include <android/hardware_buffer.h> +#include <apex/display.h> #include <log/log.h> #include <utils/Errors.h> @@ -30,14 +32,47 @@ DeviceInfo* DeviceInfo::get() { DeviceInfo::DeviceInfo() { #if HWUI_NULL_GPU - mMaxTextureSize = NULL_GPU_MAX_TEXTURE_SIZE; + mMaxTextureSize = NULL_GPU_MAX_TEXTURE_SIZE; #else - mMaxTextureSize = -1; + mMaxTextureSize = -1; #endif - updateDisplayInfo(); } -DeviceInfo::~DeviceInfo() { - ADisplay_release(mDisplays); + +void DeviceInfo::updateDisplayInfo() { + if (Properties::isolatedProcess) { + return; + } + + ADisplay** displays; + int size = ADisplay_acquirePhysicalDisplays(&displays); + + if (size <= 0) { + LOG_ALWAYS_FATAL("Failed to acquire physical displays for WCG support!"); + } + + for (int i = 0; i < size; ++i) { + // Pick the first internal display for querying the display type + // In practice this is controlled by a sysprop so it doesn't really + // matter which display we use. + if (ADisplay_getDisplayType(displays[i]) == DISPLAY_TYPE_INTERNAL) { + // We get the dataspace from DisplayManager already. Allocate space + // for the result here but we don't actually care about using it. + ADataSpace dataspace; + AHardwareBuffer_Format pixelFormat; + ADisplay_getPreferredWideColorFormat(displays[i], &dataspace, &pixelFormat); + + if (pixelFormat == AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM) { + mWideColorType = SkColorType::kN32_SkColorType; + } else if (pixelFormat == AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT) { + mWideColorType = SkColorType::kRGBA_F16_SkColorType; + } else { + LOG_ALWAYS_FATAL("Unreachable: unsupported pixel format: %d", pixelFormat); + } + ADisplay_release(displays); + return; + } + } + LOG_ALWAYS_FATAL("Failed to find a valid physical display for WCG support!"); } int DeviceInfo::maxTextureSize() const { @@ -49,75 +84,29 @@ void DeviceInfo::setMaxTextureSize(int maxTextureSize) { DeviceInfo::get()->mMaxTextureSize = maxTextureSize; } +void DeviceInfo::setWideColorDataspace(ADataSpace dataspace) { + switch (dataspace) { + case ADATASPACE_DISPLAY_P3: + get()->mWideColorSpace = + SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDisplayP3); + break; + case ADATASPACE_SCRGB: + get()->mWideColorSpace = SkColorSpace::MakeSRGB(); + break; + case ADATASPACE_SRGB: + // when sRGB is returned, it means wide color gamut is not supported. + get()->mWideColorSpace = SkColorSpace::MakeSRGB(); + break; + default: + LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space."); + } +} + void DeviceInfo::onRefreshRateChanged(int64_t vsyncPeriod) { mVsyncPeriod = vsyncPeriod; } -void DeviceInfo::updateDisplayInfo() { - if (Properties::isolatedProcess) { - return; - } - - if (mCurrentConfig == nullptr) { - mDisplaysSize = ADisplay_acquirePhysicalDisplays(&mDisplays); - LOG_ALWAYS_FATAL_IF(mDisplays == nullptr || mDisplaysSize <= 0, - "Failed to get physical displays: no connected display: %d!", mDisplaysSize); - for (size_t i = 0; i < mDisplaysSize; i++) { - ADisplayType type = ADisplay_getDisplayType(mDisplays[i]); - if (type == ADisplayType::DISPLAY_TYPE_INTERNAL) { - mPhysicalDisplayIndex = i; - break; - } - } - LOG_ALWAYS_FATAL_IF(mPhysicalDisplayIndex < 0, "Failed to find a connected physical display!"); - - - // Since we now just got the primary display for the first time, then - // store the primary display metadata here. - ADisplay* primaryDisplay = mDisplays[mPhysicalDisplayIndex]; - mMaxRefreshRate = ADisplay_getMaxSupportedFps(primaryDisplay); - ADataSpace dataspace; - AHardwareBuffer_Format format; - ADisplay_getPreferredWideColorFormat(primaryDisplay, &dataspace, &format); - switch (dataspace) { - case ADATASPACE_DISPLAY_P3: - mWideColorSpace = - SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3); - break; - case ADATASPACE_SCRGB: - mWideColorSpace = SkColorSpace::MakeSRGB(); - break; - case ADATASPACE_SRGB: - // when sRGB is returned, it means wide color gamut is not supported. - mWideColorSpace = SkColorSpace::MakeSRGB(); - break; - default: - LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space."); - } - switch (format) { - case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM: - mWideColorType = SkColorType::kN32_SkColorType; - break; - case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT: - mWideColorType = SkColorType::kRGBA_F16_SkColorType; - break; - default: - LOG_ALWAYS_FATAL("Unreachable: unsupported pixel format."); - } - } - // This method may have been called when the display config changed, so - // sync with the current configuration. - ADisplay* primaryDisplay = mDisplays[mPhysicalDisplayIndex]; - status_t status = ADisplay_getCurrentConfig(primaryDisplay, &mCurrentConfig); - LOG_ALWAYS_FATAL_IF(status, "Failed to get display config, error %d", status); - - mWidth = ADisplayConfig_getWidth(mCurrentConfig); - mHeight = ADisplayConfig_getHeight(mCurrentConfig); - mDensity = ADisplayConfig_getDensity(mCurrentConfig); - mVsyncPeriod = static_cast<int64_t>(1000000000 / ADisplayConfig_getFps(mCurrentConfig)); - mCompositorOffset = ADisplayConfig_getCompositorOffsetNanos(mCurrentConfig); - mAppOffset = ADisplayConfig_getAppVsyncOffsetNanos(mCurrentConfig); -} +std::atomic<float> DeviceInfo::sDensity = 2.0; } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h index 16a22f4706f5..27be62269959 100644 --- a/libs/hwui/DeviceInfo.h +++ b/libs/hwui/DeviceInfo.h @@ -16,8 +16,10 @@ #ifndef DEVICEINFO_H #define DEVICEINFO_H -#include <apex/display.h> #include <SkImageInfo.h> +#include <android/data_space.h> + +#include <mutex> #include "utils/Macros.h" @@ -36,16 +38,37 @@ public: static float getMaxRefreshRate() { return get()->mMaxRefreshRate; } static int32_t getWidth() { return get()->mWidth; } static int32_t getHeight() { return get()->mHeight; } - static float getDensity() { return get()->mDensity; } + // Gets the density in density-independent pixels + static float getDensity() { return sDensity.load(); } static int64_t getVsyncPeriod() { return get()->mVsyncPeriod; } - static int64_t getCompositorOffset() { return get()->mCompositorOffset; } - static int64_t getAppOffset() { return get()->mAppOffset; } + static int64_t getCompositorOffset() { return get()->getCompositorOffsetInternal(); } + static int64_t getAppOffset() { return get()->mAppVsyncOffsetNanos; } + // Sets the density in density-independent pixels + static void setDensity(float density) { sDensity.store(density); } + static void setMaxRefreshRate(float refreshRate) { get()->mMaxRefreshRate = refreshRate; } + static void setWidth(int32_t width) { get()->mWidth = width; } + static void setHeight(int32_t height) { get()->mHeight = height; } + static void setRefreshRate(float refreshRate) { + get()->mVsyncPeriod = static_cast<int64_t>(1000000000 / refreshRate); + } + static void setPresentationDeadlineNanos(int64_t deadlineNanos) { + get()->mPresentationDeadlineNanos = deadlineNanos; + } + static void setAppVsyncOffsetNanos(int64_t offsetNanos) { + get()->mAppVsyncOffsetNanos = offsetNanos; + } + static void setWideColorDataspace(ADataSpace dataspace); // this value is only valid after the GPU has been initialized and there is a valid graphics // context or if you are using the HWUI_NULL_GPU int maxTextureSize() const; sk_sp<SkColorSpace> getWideColorSpace() const { return mWideColorSpace; } - SkColorType getWideColorType() const { return mWideColorType; } + SkColorType getWideColorType() { + static std::once_flag kFlag; + // lazily update display info from SF here, so that the call is performed by RenderThread. + std::call_once(kFlag, [&, this]() { updateDisplayInfo(); }); + return mWideColorType; + } // This method should be called whenever the display refresh rate changes. void onRefreshRateChanged(int64_t vsyncPeriod); @@ -54,24 +77,32 @@ private: friend class renderthread::RenderThread; static void setMaxTextureSize(int maxTextureSize); void updateDisplayInfo(); + int64_t getCompositorOffsetInternal() const { + // Assume that SF takes around a millisecond to latch buffers after + // waking up + return mVsyncPeriod - (mPresentationDeadlineNanos - 1000000); + } DeviceInfo(); - ~DeviceInfo(); + ~DeviceInfo() = default; int mMaxTextureSize; sk_sp<SkColorSpace> mWideColorSpace = SkColorSpace::MakeSRGB(); SkColorType mWideColorType = SkColorType::kN32_SkColorType; - ADisplayConfig* mCurrentConfig = nullptr; - ADisplay** mDisplays = nullptr; int mDisplaysSize = 0; int mPhysicalDisplayIndex = -1; float mMaxRefreshRate = 60.0; int32_t mWidth = 1080; int32_t mHeight = 1920; - float mDensity = 2.0; int64_t mVsyncPeriod = 16666666; - int64_t mCompositorOffset = 0; - int64_t mAppOffset = 0; + // Magically corresponds with an sf offset of 0 for a sane default. + int64_t mPresentationDeadlineNanos = 17666666; + int64_t mAppVsyncOffsetNanos = 0; + + // Density is not retrieved from the ADisplay apis, so this may potentially + // be called on multiple threads. + // Unit is density-independent pixels + static std::atomic<float> sDensity; }; } /* namespace uirenderer */ diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h index 51674fbd557e..dc30617009e7 100644 --- a/libs/hwui/FrameInfo.h +++ b/libs/hwui/FrameInfo.h @@ -69,7 +69,7 @@ enum { }; }; -class ANDROID_API UiFrameInfoBuilder { +class UiFrameInfoBuilder { public: explicit UiFrameInfoBuilder(int64_t* buffer) : mBuffer(buffer) { memset(mBuffer, 0, UI_THREAD_FRAME_INFO_SIZE * sizeof(int64_t)); diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp index a3d552faeb0a..87244427a719 100644 --- a/libs/hwui/HardwareBitmapUploader.cpp +++ b/libs/hwui/HardwareBitmapUploader.cpp @@ -16,24 +16,27 @@ #include "HardwareBitmapUploader.h" -#include "hwui/Bitmap.h" -#include "renderthread/EglManager.h" -#include "renderthread/VulkanManager.h" -#include "thread/ThreadBase.h" -#include "utils/TimeUtils.h" - +#include <EGL/egl.h> #include <EGL/eglext.h> #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> #include <GLES3/gl3.h> -#include <GrContext.h> +#include <GrDirectContext.h> #include <SkCanvas.h> #include <SkImage.h> #include <utils/GLUtils.h> +#include <utils/NdkUtils.h> #include <utils/Trace.h> #include <utils/TraceUtils.h> + #include <thread> +#include "hwui/Bitmap.h" +#include "renderthread/EglManager.h" +#include "renderthread/VulkanManager.h" +#include "thread/ThreadBase.h" +#include "utils/TimeUtils.h" + namespace android::uirenderer { class AHBUploader; @@ -42,7 +45,7 @@ class AHBUploader; static sp<AHBUploader> sUploader = nullptr; struct FormatInfo { - PixelFormat pixelFormat; + AHardwareBuffer_Format bufferFormat; GLint format, type; VkFormat vkFormat; bool isSupported = false; @@ -71,10 +74,10 @@ public: } bool uploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format, - sp<GraphicBuffer> graphicBuffer) { + AHardwareBuffer* ahb) { ATRACE_CALL(); beginUpload(); - bool result = onUploadHardwareBitmap(bitmap, format, graphicBuffer); + bool result = onUploadHardwareBitmap(bitmap, format, ahb); endUpload(); return result; } @@ -93,7 +96,7 @@ private: virtual void onDestroy() = 0; virtual bool onUploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format, - sp<GraphicBuffer> graphicBuffer) = 0; + AHardwareBuffer* ahb) = 0; virtual void onBeginUpload() = 0; bool shouldTimeOutLocked() { @@ -165,16 +168,16 @@ private: } bool onUploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format, - sp<GraphicBuffer> graphicBuffer) override { + AHardwareBuffer* ahb) override { ATRACE_CALL(); EGLDisplay display = getUploadEglDisplay(); LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s", uirenderer::renderthread::EglManager::eglErrorString()); - // We use an EGLImage to access the content of the GraphicBuffer + // We use an EGLImage to access the content of the buffer // The EGL image is later bound to a 2D texture - EGLClientBuffer clientBuffer = (EGLClientBuffer)graphicBuffer->getNativeBuffer(); + const EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(ahb); AutoEglImage autoImage(display, clientBuffer); if (autoImage.image == EGL_NO_IMAGE_KHR) { ALOGW("Could not create EGL image, err =%s", @@ -272,17 +275,17 @@ private: } bool onUploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format, - sp<GraphicBuffer> graphicBuffer) override { + AHardwareBuffer* ahb) override { ATRACE_CALL(); std::lock_guard _lock{mLock}; - sk_sp<SkImage> image = SkImage::MakeFromAHardwareBufferWithData(mGrContext.get(), - bitmap.pixmap(), reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get())); + sk_sp<SkImage> image = + SkImage::MakeFromAHardwareBufferWithData(mGrContext.get(), bitmap.pixmap(), ahb); return (image.get() != nullptr); } - sk_sp<GrContext> mGrContext; + sk_sp<GrDirectContext> mGrContext; renderthread::VulkanManager mVulkanManager; std::mutex mVkLock; }; @@ -294,13 +297,17 @@ bool HardwareBitmapUploader::hasFP16Support() { // Gralloc shouldn't let us create a USAGE_HW_TEXTURE if GLES is unable to consume it, so // we don't need to double-check the GLES version/extension. std::call_once(sOnce, []() { - sp<GraphicBuffer> buffer = new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBA_FP16, - GraphicBuffer::USAGE_HW_TEXTURE | - GraphicBuffer::USAGE_SW_WRITE_NEVER | - GraphicBuffer::USAGE_SW_READ_NEVER, - "tempFp16Buffer"); - status_t error = buffer->initCheck(); - hasFP16Support = !error; + AHardwareBuffer_Desc desc = { + .width = 1, + .height = 1, + .layers = 1, + .format = AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT, + .usage = AHARDWAREBUFFER_USAGE_CPU_READ_NEVER | + AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER | + AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE, + }; + UniqueAHardwareBuffer buffer = allocateAHardwareBuffer(desc); + hasFP16Support = buffer != nullptr; }); return hasFP16Support; @@ -314,7 +321,7 @@ static FormatInfo determineFormat(const SkBitmap& skBitmap, bool usingGL) { [[fallthrough]]; // ARGB_4444 is upconverted to RGBA_8888 case kARGB_4444_SkColorType: - formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888; + formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; formatInfo.format = GL_RGBA; formatInfo.type = GL_UNSIGNED_BYTE; formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM; @@ -323,25 +330,25 @@ static FormatInfo determineFormat(const SkBitmap& skBitmap, bool usingGL) { formatInfo.isSupported = HardwareBitmapUploader::hasFP16Support(); if (formatInfo.isSupported) { formatInfo.type = GL_HALF_FLOAT; - formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_FP16; + formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT; formatInfo.vkFormat = VK_FORMAT_R16G16B16A16_SFLOAT; } else { formatInfo.type = GL_UNSIGNED_BYTE; - formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888; + formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM; } formatInfo.format = GL_RGBA; break; case kRGB_565_SkColorType: formatInfo.isSupported = true; - formatInfo.pixelFormat = PIXEL_FORMAT_RGB_565; + formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM; formatInfo.format = GL_RGB; formatInfo.type = GL_UNSIGNED_SHORT_5_6_5; formatInfo.vkFormat = VK_FORMAT_R5G6B5_UNORM_PACK16; break; case kGray_8_SkColorType: formatInfo.isSupported = usingGL; - formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_8888; + formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; formatInfo.format = GL_LUMINANCE; formatInfo.type = GL_UNSIGNED_BYTE; formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM; @@ -394,28 +401,27 @@ sk_sp<Bitmap> HardwareBitmapUploader::allocateHardwareBitmap(const SkBitmap& sou } SkBitmap bitmap = makeHwCompatible(format, sourceBitmap); - sp<GraphicBuffer> buffer = new GraphicBuffer( - static_cast<uint32_t>(bitmap.width()), static_cast<uint32_t>(bitmap.height()), - format.pixelFormat, - GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER | - GraphicBuffer::USAGE_SW_READ_NEVER, - std::string("Bitmap::allocateHardwareBitmap pid [") + std::to_string(getpid()) + - "]"); - - status_t error = buffer->initCheck(); - if (error < 0) { - ALOGW("createGraphicBuffer() failed in GraphicBuffer.create()"); + AHardwareBuffer_Desc desc = { + .width = static_cast<uint32_t>(bitmap.width()), + .height = static_cast<uint32_t>(bitmap.height()), + .layers = 1, + .format = format.bufferFormat, + .usage = AHARDWAREBUFFER_USAGE_CPU_READ_NEVER | AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER | + AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE, + }; + UniqueAHardwareBuffer ahb = allocateAHardwareBuffer(desc); + if (!ahb) { + ALOGW("allocateHardwareBitmap() failed in AHardwareBuffer_allocate()"); return nullptr; - } + }; createUploader(usingGL); - if (!sUploader->uploadHardwareBitmap(bitmap, format, buffer)) { + if (!sUploader->uploadHardwareBitmap(bitmap, format, ahb.get())) { return nullptr; } - return Bitmap::createFrom(buffer->toAHardwareBuffer(), bitmap.colorType(), - bitmap.refColorSpace(), bitmap.alphaType(), - Bitmap::computePalette(bitmap)); + return Bitmap::createFrom(ahb.get(), bitmap.colorType(), bitmap.refColorSpace(), + bitmap.alphaType(), Bitmap::computePalette(bitmap)); } void HardwareBitmapUploader::initialize() { diff --git a/libs/hwui/HardwareBitmapUploader.h b/libs/hwui/HardwareBitmapUploader.h index 72243d23dd35..ad7a95a4fa03 100644 --- a/libs/hwui/HardwareBitmapUploader.h +++ b/libs/hwui/HardwareBitmapUploader.h @@ -20,7 +20,7 @@ namespace android::uirenderer { -class ANDROID_API HardwareBitmapUploader { +class HardwareBitmapUploader { public: static void initialize(); static void terminate(); diff --git a/libs/hwui/Interpolator.h b/libs/hwui/Interpolator.h index 452988fc8711..131cc3935f10 100644 --- a/libs/hwui/Interpolator.h +++ b/libs/hwui/Interpolator.h @@ -37,12 +37,12 @@ protected: Interpolator() {} }; -class ANDROID_API AccelerateDecelerateInterpolator : public Interpolator { +class AccelerateDecelerateInterpolator : public Interpolator { public: virtual float interpolate(float input) override; }; -class ANDROID_API AccelerateInterpolator : public Interpolator { +class AccelerateInterpolator : public Interpolator { public: explicit AccelerateInterpolator(float factor) : mFactor(factor), mDoubleFactor(factor * 2) {} virtual float interpolate(float input) override; @@ -52,7 +52,7 @@ private: const float mDoubleFactor; }; -class ANDROID_API AnticipateInterpolator : public Interpolator { +class AnticipateInterpolator : public Interpolator { public: explicit AnticipateInterpolator(float tension) : mTension(tension) {} virtual float interpolate(float input) override; @@ -61,7 +61,7 @@ private: const float mTension; }; -class ANDROID_API AnticipateOvershootInterpolator : public Interpolator { +class AnticipateOvershootInterpolator : public Interpolator { public: explicit AnticipateOvershootInterpolator(float tension) : mTension(tension) {} virtual float interpolate(float input) override; @@ -70,12 +70,12 @@ private: const float mTension; }; -class ANDROID_API BounceInterpolator : public Interpolator { +class BounceInterpolator : public Interpolator { public: virtual float interpolate(float input) override; }; -class ANDROID_API CycleInterpolator : public Interpolator { +class CycleInterpolator : public Interpolator { public: explicit CycleInterpolator(float cycles) : mCycles(cycles) {} virtual float interpolate(float input) override; @@ -84,7 +84,7 @@ private: const float mCycles; }; -class ANDROID_API DecelerateInterpolator : public Interpolator { +class DecelerateInterpolator : public Interpolator { public: explicit DecelerateInterpolator(float factor) : mFactor(factor) {} virtual float interpolate(float input) override; @@ -93,12 +93,12 @@ private: const float mFactor; }; -class ANDROID_API LinearInterpolator : public Interpolator { +class LinearInterpolator : public Interpolator { public: virtual float interpolate(float input) override { return input; } }; -class ANDROID_API OvershootInterpolator : public Interpolator { +class OvershootInterpolator : public Interpolator { public: explicit OvershootInterpolator(float tension) : mTension(tension) {} virtual float interpolate(float input) override; @@ -107,7 +107,7 @@ private: const float mTension; }; -class ANDROID_API PathInterpolator : public Interpolator { +class PathInterpolator : public Interpolator { public: explicit PathInterpolator(std::vector<float>&& x, std::vector<float>&& y) : mX(x), mY(y) {} virtual float interpolate(float input) override; @@ -117,7 +117,7 @@ private: std::vector<float> mY; }; -class ANDROID_API LUTInterpolator : public Interpolator { +class LUTInterpolator : public Interpolator { public: LUTInterpolator(float* values, size_t size); ~LUTInterpolator(); diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h index 0c515a41689d..4c6e1a0a6eee 100644 --- a/libs/hwui/Matrix.h +++ b/libs/hwui/Matrix.h @@ -44,7 +44,7 @@ namespace uirenderer { // Classes /////////////////////////////////////////////////////////////////////////////// -class ANDROID_API Matrix4 { +class Matrix4 { public: float data[16]; diff --git a/libs/hwui/PathParser.h b/libs/hwui/PathParser.h index 878bb7c0f137..859697eb3e9b 100644 --- a/libs/hwui/PathParser.h +++ b/libs/hwui/PathParser.h @@ -30,17 +30,17 @@ namespace uirenderer { class PathParser { public: - struct ANDROID_API ParseResult { + struct ParseResult { bool failureOccurred = false; std::string failureMessage; }; /** * Parse the string literal and create a Skia Path. Return true on success. */ - ANDROID_API static void parseAsciiStringForSkPath(SkPath* outPath, ParseResult* result, - const char* pathStr, size_t strLength); - ANDROID_API static void getPathDataFromAsciiString(PathData* outData, ParseResult* result, - const char* pathStr, size_t strLength); + static void parseAsciiStringForSkPath(SkPath* outPath, ParseResult* result, + const char* pathStr, size_t strLength); + static void getPathDataFromAsciiString(PathData* outData, ParseResult* result, + const char* pathStr, size_t strLength); static void dump(const PathData& data); static void validateVerbAndPoints(char verb, size_t points, ParseResult* result); }; diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 446e81e65bb8..ba44d056dda3 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -78,6 +78,7 @@ bool Properties::isolatedProcess = false; int Properties::contextPriority = 0; int Properties::defaultRenderAhead = -1; +float Properties::defaultSdrWhitePoint = 200.f; bool Properties::load() { bool prevDebugLayersUpdates = debugLayersUpdates; diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index d3ecb54d94f6..85a0f4aa7809 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -213,10 +213,10 @@ public: static int overrideSpotShadowStrength; static ProfileType getProfileType(); - ANDROID_API static RenderPipelineType peekRenderPipelineType(); - ANDROID_API static RenderPipelineType getRenderPipelineType(); + static RenderPipelineType peekRenderPipelineType(); + static RenderPipelineType getRenderPipelineType(); - ANDROID_API static bool enableHighContrastText; + static bool enableHighContrastText; // Should be used only by test apps static bool waitForGpuCompletion; @@ -235,20 +235,22 @@ public: static bool skpCaptureEnabled; // For experimentation b/68769804 - ANDROID_API static bool enableRTAnimations; + static bool enableRTAnimations; // Used for testing only to change the render pipeline. static void overrideRenderPipelineType(RenderPipelineType); static bool runningInEmulator; - ANDROID_API static bool debuggingEnabled; - ANDROID_API static bool isolatedProcess; + static bool debuggingEnabled; + static bool isolatedProcess; - ANDROID_API static int contextPriority; + static int contextPriority; static int defaultRenderAhead; + static float defaultSdrWhitePoint; + private: static ProfileType sProfileType; static bool sDisableProfileBars; diff --git a/libs/hwui/PropertyValuesAnimatorSet.h b/libs/hwui/PropertyValuesAnimatorSet.h index e4214b22d1cc..c04a0b9b0fe7 100644 --- a/libs/hwui/PropertyValuesAnimatorSet.h +++ b/libs/hwui/PropertyValuesAnimatorSet.h @@ -44,7 +44,7 @@ private: }; // TODO: This class should really be named VectorDrawableAnimator -class ANDROID_API PropertyValuesAnimatorSet : public BaseRenderNodeAnimator { +class PropertyValuesAnimatorSet : public BaseRenderNodeAnimator { public: friend class PropertyAnimatorSetListener; PropertyValuesAnimatorSet(); diff --git a/libs/hwui/PropertyValuesHolder.h b/libs/hwui/PropertyValuesHolder.h index 0a799d3c0b5c..bb26cbe7bc9b 100644 --- a/libs/hwui/PropertyValuesHolder.h +++ b/libs/hwui/PropertyValuesHolder.h @@ -28,7 +28,7 @@ namespace uirenderer { * When a fraction in [0f, 1f] is provided, the holder will calculate an interpolated value based * on its start and end value, and set the new value on the VectorDrawble's corresponding property. */ -class ANDROID_API PropertyValuesHolder { +class PropertyValuesHolder { public: virtual void setFraction(float fraction) = 0; virtual ~PropertyValuesHolder() {} @@ -49,19 +49,19 @@ public: } }; -class ANDROID_API ColorEvaluator : public Evaluator<SkColor> { +class ColorEvaluator : public Evaluator<SkColor> { public: virtual void evaluate(SkColor* outColor, const SkColor& from, const SkColor& to, float fraction) const override; }; -class ANDROID_API PathEvaluator : public Evaluator<PathData> { +class PathEvaluator : public Evaluator<PathData> { virtual void evaluate(PathData* out, const PathData& from, const PathData& to, float fraction) const override; }; template <typename T> -class ANDROID_API PropertyValuesHolderImpl : public PropertyValuesHolder { +class PropertyValuesHolderImpl : public PropertyValuesHolder { public: PropertyValuesHolderImpl(const T& startValue, const T& endValue) : mStartValue(startValue), mEndValue(endValue) {} @@ -85,7 +85,7 @@ protected: T mEndValue; }; -class ANDROID_API GroupPropertyValuesHolder : public PropertyValuesHolderImpl<float> { +class GroupPropertyValuesHolder : public PropertyValuesHolderImpl<float> { public: GroupPropertyValuesHolder(VectorDrawable::Group* ptr, int propertyId, float startValue, float endValue) @@ -99,7 +99,7 @@ private: int mPropertyId; }; -class ANDROID_API FullPathColorPropertyValuesHolder : public PropertyValuesHolderImpl<SkColor> { +class FullPathColorPropertyValuesHolder : public PropertyValuesHolderImpl<SkColor> { public: FullPathColorPropertyValuesHolder(VectorDrawable::FullPath* ptr, int propertyId, SkColor startValue, SkColor endValue) @@ -116,7 +116,7 @@ private: int mPropertyId; }; -class ANDROID_API FullPathPropertyValuesHolder : public PropertyValuesHolderImpl<float> { +class FullPathPropertyValuesHolder : public PropertyValuesHolderImpl<float> { public: FullPathPropertyValuesHolder(VectorDrawable::FullPath* ptr, int propertyId, float startValue, float endValue) @@ -132,7 +132,7 @@ private: int mPropertyId; }; -class ANDROID_API PathDataPropertyValuesHolder : public PropertyValuesHolderImpl<PathData> { +class PathDataPropertyValuesHolder : public PropertyValuesHolderImpl<PathData> { public: PathDataPropertyValuesHolder(VectorDrawable::Path* ptr, PathData* startValue, PathData* endValue) @@ -146,7 +146,7 @@ private: PathData mPathData; }; -class ANDROID_API RootAlphaPropertyValuesHolder : public PropertyValuesHolderImpl<float> { +class RootAlphaPropertyValuesHolder : public PropertyValuesHolderImpl<float> { public: RootAlphaPropertyValuesHolder(VectorDrawable::Tree* tree, float startValue, float endValue) : PropertyValuesHolderImpl(startValue, endValue), mTree(tree) { diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp index 39900e65cb8a..b71bb07dbc86 100644 --- a/libs/hwui/Readback.cpp +++ b/libs/hwui/Readback.cpp @@ -18,7 +18,6 @@ #include <sync/sync.h> #include <system/window.h> -#include <ui/GraphicBuffer.h> #include "DeferredLayerUpdater.h" #include "Properties.h" @@ -28,6 +27,7 @@ #include "renderthread/VulkanManager.h" #include "utils/Color.h" #include "utils/MathUtils.h" +#include "utils/NdkUtils.h" #include "utils/TraceUtils.h" using namespace android::uirenderer::renderthread; @@ -54,8 +54,7 @@ CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& srcRect, return CopyResult::SourceEmpty; } - std::unique_ptr<AHardwareBuffer, decltype(&AHardwareBuffer_release)> sourceBuffer( - rawSourceBuffer, AHardwareBuffer_release); + UniqueAHardwareBuffer sourceBuffer{rawSourceBuffer}; AHardwareBuffer_Desc description; AHardwareBuffer_describe(sourceBuffer.get(), &description); if (description.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) { @@ -119,7 +118,7 @@ CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTran } int imgWidth = image->width(); int imgHeight = image->height(); - sk_sp<GrContext> grContext = sk_ref_sp(mRenderThread.getGrContext()); + sk_sp<GrDirectContext> grContext = sk_ref_sp(mRenderThread.getGrContext()); if (bitmap->colorType() == kRGBA_F16_SkColorType && !grContext->colorTypeSupportedAsSurface(bitmap->colorType())) { diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index dc467c41baed..892d43a763a9 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -96,7 +96,7 @@ struct Restore final : Op { struct SaveLayer final : Op { static const auto kType = Type::SaveLayer; SaveLayer(const SkRect* bounds, const SkPaint* paint, const SkImageFilter* backdrop, - const SkImage* clipMask, const SkMatrix* clipMatrix, SkCanvas::SaveLayerFlags flags) { + SkCanvas::SaveLayerFlags flags) { if (bounds) { this->bounds = *bounds; } @@ -104,19 +104,14 @@ struct SaveLayer final : Op { this->paint = *paint; } this->backdrop = sk_ref_sp(backdrop); - this->clipMask = sk_ref_sp(clipMask); - this->clipMatrix = clipMatrix ? *clipMatrix : SkMatrix::I(); this->flags = flags; } SkRect bounds = kUnset; SkPaint paint; sk_sp<const SkImageFilter> backdrop; - sk_sp<const SkImage> clipMask; - SkMatrix clipMatrix; SkCanvas::SaveLayerFlags flags; void draw(SkCanvas* c, const SkMatrix&) const { - c->saveLayer({maybe_unset(bounds), &paint, backdrop.get(), clipMask.get(), - clipMatrix.isIdentity() ? nullptr : &clipMatrix, flags}); + c->saveLayer({maybe_unset(bounds), &paint, backdrop.get(), flags}); } }; struct SaveBehind final : Op { @@ -132,9 +127,9 @@ struct SaveBehind final : Op { struct Concat44 final : Op { static const auto kType = Type::Concat44; - Concat44(const SkScalar m[16]) { memcpy(colMajor, m, sizeof(colMajor)); } - SkScalar colMajor[16]; - void draw(SkCanvas* c, const SkMatrix&) const { c->experimental_concat44(colMajor); } + Concat44(const SkM44& m) : matrix(m) {} + SkM44 matrix; + void draw(SkCanvas* c, const SkMatrix&) const { c->concat(matrix); } }; struct Concat final : Op { static const auto kType = Type::Concat; @@ -448,14 +443,13 @@ struct DrawPoints final : Op { }; struct DrawVertices final : Op { static const auto kType = Type::DrawVertices; - DrawVertices(const SkVertices* v, int bc, SkBlendMode m, const SkPaint& p) - : vertices(sk_ref_sp(const_cast<SkVertices*>(v))), boneCount(bc), mode(m), paint(p) {} + DrawVertices(const SkVertices* v, SkBlendMode m, const SkPaint& p) + : vertices(sk_ref_sp(const_cast<SkVertices*>(v))), mode(m), paint(p) {} sk_sp<SkVertices> vertices; - int boneCount; SkBlendMode mode; SkPaint paint; void draw(SkCanvas* c, const SkMatrix&) const { - c->drawVertices(vertices, pod<SkVertices::Bone>(this), boneCount, mode, paint); + c->drawVertices(vertices, mode, paint); } }; struct DrawAtlas final : Op { @@ -565,17 +559,16 @@ void DisplayListData::restore() { this->push<Restore>(0); } void DisplayListData::saveLayer(const SkRect* bounds, const SkPaint* paint, - const SkImageFilter* backdrop, const SkImage* clipMask, - const SkMatrix* clipMatrix, SkCanvas::SaveLayerFlags flags) { - this->push<SaveLayer>(0, bounds, paint, backdrop, clipMask, clipMatrix, flags); + const SkImageFilter* backdrop, SkCanvas::SaveLayerFlags flags) { + this->push<SaveLayer>(0, bounds, paint, backdrop, flags); } void DisplayListData::saveBehind(const SkRect* subset) { this->push<SaveBehind>(0, subset); } -void DisplayListData::concat44(const SkScalar colMajor[16]) { - this->push<Concat44>(0, colMajor); +void DisplayListData::concat(const SkM44& m) { + this->push<Concat44>(0, m); } void DisplayListData::concat(const SkMatrix& matrix) { this->push<Concat>(0, matrix); @@ -686,11 +679,8 @@ void DisplayListData::drawPoints(SkCanvas::PointMode mode, size_t count, const S void* pod = this->push<DrawPoints>(count * sizeof(SkPoint), mode, count, paint); copy_v(pod, points, count); } -void DisplayListData::drawVertices(const SkVertices* vertices, const SkVertices::Bone bones[], - int boneCount, SkBlendMode mode, const SkPaint& paint) { - void* pod = this->push<DrawVertices>(boneCount * sizeof(SkVertices::Bone), vertices, boneCount, - mode, paint); - copy_v(pod, bones, boneCount); +void DisplayListData::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) { + this->push<DrawVertices>(0, vertices, mode, paint); } void DisplayListData::drawAtlas(const SkImage* atlas, const SkRSXform xforms[], const SkRect texs[], const SkColor colors[], int count, SkBlendMode xfermode, @@ -823,8 +813,7 @@ void RecordingCanvas::willSave() { fDL->save(); } SkCanvas::SaveLayerStrategy RecordingCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) { - fDL->saveLayer(rec.fBounds, rec.fPaint, rec.fBackdrop, rec.fClipMask, rec.fClipMatrix, - rec.fSaveLayerFlags); + fDL->saveLayer(rec.fBounds, rec.fPaint, rec.fBackdrop, rec.fSaveLayerFlags); return SkCanvas::kNoLayer_SaveLayerStrategy; } void RecordingCanvas::willRestore() { @@ -841,8 +830,8 @@ bool RecordingCanvas::onDoSaveBehind(const SkRect* subset) { return false; } -void RecordingCanvas::didConcat44(const SkScalar colMajor[16]) { - fDL->concat44(colMajor); +void RecordingCanvas::didConcat44(const SkM44& m) { + fDL->concat(m); } void RecordingCanvas::didConcat(const SkMatrix& matrix) { fDL->concat(matrix); @@ -929,24 +918,6 @@ void RecordingCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScala fDL->drawTextBlob(blob, x, y, paint); } -void RecordingCanvas::onDrawBitmap(const SkBitmap& bm, SkScalar x, SkScalar y, - const SkPaint* paint) { - fDL->drawImage(SkImage::MakeFromBitmap(bm), x, y, paint, BitmapPalette::Unknown); -} -void RecordingCanvas::onDrawBitmapNine(const SkBitmap& bm, const SkIRect& center, const SkRect& dst, - const SkPaint* paint) { - fDL->drawImageNine(SkImage::MakeFromBitmap(bm), center, dst, paint); -} -void RecordingCanvas::onDrawBitmapRect(const SkBitmap& bm, const SkRect* src, const SkRect& dst, - const SkPaint* paint, SrcRectConstraint constraint) { - fDL->drawImageRect(SkImage::MakeFromBitmap(bm), src, dst, paint, constraint, - BitmapPalette::Unknown); -} -void RecordingCanvas::onDrawBitmapLattice(const SkBitmap& bm, const SkCanvas::Lattice& lattice, - const SkRect& dst, const SkPaint* paint) { - fDL->drawImageLattice(SkImage::MakeFromBitmap(bm), lattice, dst, paint, BitmapPalette::Unknown); -} - void RecordingCanvas::drawImage(const sk_sp<SkImage>& image, SkScalar x, SkScalar y, const SkPaint* paint, BitmapPalette palette) { fDL->drawImage(image, x, y, paint, palette); @@ -1007,9 +978,8 @@ void RecordingCanvas::onDrawPoints(SkCanvas::PointMode mode, size_t count, const fDL->drawPoints(mode, count, pts, paint); } void RecordingCanvas::onDrawVerticesObject(const SkVertices* vertices, - const SkVertices::Bone bones[], int boneCount, SkBlendMode mode, const SkPaint& paint) { - fDL->drawVertices(vertices, bones, boneCount, mode, paint); + fDL->drawVertices(vertices, mode, paint); } void RecordingCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xforms[], const SkRect texs[], const SkColor colors[], int count, diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 7eb1ce3eb18a..63d120c4ca19 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -77,12 +77,11 @@ private: void flush(); void save(); - void saveLayer(const SkRect*, const SkPaint*, const SkImageFilter*, const SkImage*, - const SkMatrix*, SkCanvas::SaveLayerFlags); + void saveLayer(const SkRect*, const SkPaint*, const SkImageFilter*, SkCanvas::SaveLayerFlags); void saveBehind(const SkRect*); void restore(); - void concat44(const SkScalar colMajor[16]); + void concat(const SkM44&); void concat(const SkMatrix&); void setMatrix(const SkMatrix&); void scale(SkScalar, SkScalar); @@ -120,8 +119,7 @@ private: void drawPatch(const SkPoint[12], const SkColor[4], const SkPoint[4], SkBlendMode, const SkPaint&); void drawPoints(SkCanvas::PointMode, size_t, const SkPoint[], const SkPaint&); - void drawVertices(const SkVertices*, const SkVertices::Bone bones[], int boneCount, SkBlendMode, - const SkPaint&); + void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&); void drawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int, SkBlendMode, const SkRect*, const SkPaint*); void drawShadowRec(const SkPath&, const SkDrawShadowRec&); @@ -155,7 +153,7 @@ public: void onFlush() override; - void didConcat44(const SkScalar[16]) override; + void didConcat44(const SkM44&) override; void didConcat(const SkMatrix&) override; void didSetMatrix(const SkMatrix&) override; void didScale(SkScalar, SkScalar) override; @@ -182,13 +180,6 @@ public: void onDrawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&) override; - void onDrawBitmap(const SkBitmap&, SkScalar, SkScalar, const SkPaint*) override; - void onDrawBitmapLattice(const SkBitmap&, const Lattice&, const SkRect&, - const SkPaint*) override; - void onDrawBitmapNine(const SkBitmap&, const SkIRect&, const SkRect&, const SkPaint*) override; - void onDrawBitmapRect(const SkBitmap&, const SkRect*, const SkRect&, const SkPaint*, - SrcRectConstraint) override; - void drawImage(const sk_sp<SkImage>& image, SkScalar left, SkScalar top, const SkPaint* paint, BitmapPalette pallete); @@ -206,8 +197,7 @@ public: void onDrawPatch(const SkPoint[12], const SkColor[4], const SkPoint[4], SkBlendMode, const SkPaint&) override; void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) override; - void onDrawVerticesObject(const SkVertices*, const SkVertices::Bone bones[], int boneCount, - SkBlendMode, const SkPaint&) override; + void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override; void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int, SkBlendMode, const SkRect*, const SkPaint*) override; void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override; diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index c0ec2174bb35..6d5e62e955bb 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -94,17 +94,17 @@ public: DISPLAY_LIST = 1 << 14, }; - ANDROID_API RenderNode(); - ANDROID_API virtual ~RenderNode(); + RenderNode(); + virtual ~RenderNode(); // See flags defined in DisplayList.java enum ReplayFlag { kReplayFlag_ClipChildren = 0x1 }; - ANDROID_API void setStagingDisplayList(DisplayList* newData); + void setStagingDisplayList(DisplayList* newData); - ANDROID_API void output(); - ANDROID_API int getUsageSize(); - ANDROID_API int getAllocatedSize(); + void output(); + int getUsageSize(); + int getAllocatedSize(); bool isRenderable() const { return mDisplayList && !mDisplayList->isEmpty(); } @@ -149,12 +149,12 @@ public: int getHeight() const { return properties().getHeight(); } - ANDROID_API virtual void prepareTree(TreeInfo& info); + virtual void prepareTree(TreeInfo& info); void destroyHardwareResources(TreeInfo* info = nullptr); void destroyLayers(); // UI thread only! - ANDROID_API void addAnimator(const sp<BaseRenderNodeAnimator>& animator); + void addAnimator(const sp<BaseRenderNodeAnimator>& animator); void removeAnimator(const sp<BaseRenderNodeAnimator>& animator); // This can only happen during pushStaging() @@ -179,7 +179,7 @@ public: // the frameNumber to appropriately batch/synchronize these transactions. // There is no other filtering/batching to ensure that only the "final" // state called once per frame. - class ANDROID_API PositionListener : public VirtualLightRefBase { + class PositionListener : public VirtualLightRefBase { public: virtual ~PositionListener() {} // Called when the RenderNode's position changes @@ -190,14 +190,14 @@ public: virtual void onPositionLost(RenderNode& node, const TreeInfo* info) = 0; }; - ANDROID_API void setPositionListener(PositionListener* listener) { + void setPositionListener(PositionListener* listener) { mStagingPositionListener = listener; mPositionListenerDirty = true; } // This is only modified in MODE_FULL, so it can be safely accessed // on the UI thread. - ANDROID_API bool hasParents() { return mParentCount; } + bool hasParents() { return mParentCount; } void onRemovedFromTree(TreeInfo* info); diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index 24f6035b6708..ef4cd1f1eb62 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -69,7 +69,7 @@ enum ClippingFlags { CLIP_TO_CLIP_BOUNDS = 0x1 << 1, }; -class ANDROID_API LayerProperties { +class LayerProperties { public: bool setType(LayerType type) { if (RP_SET(mType, type)) { @@ -123,7 +123,7 @@ private: /* * Data structure that holds the properties for a RenderNode */ -class ANDROID_API RenderProperties { +class RenderProperties { public: RenderProperties(); virtual ~RenderProperties(); diff --git a/libs/hwui/RootRenderNode.h b/libs/hwui/RootRenderNode.h index 12de4ecac94b..1d3f5a8a51e0 100644 --- a/libs/hwui/RootRenderNode.h +++ b/libs/hwui/RootRenderNode.h @@ -27,16 +27,16 @@ namespace android::uirenderer { -class ANDROID_API RootRenderNode : public RenderNode { +class RootRenderNode : public RenderNode { public: - ANDROID_API explicit RootRenderNode(std::unique_ptr<ErrorHandler> errorHandler) + explicit RootRenderNode(std::unique_ptr<ErrorHandler> errorHandler) : RenderNode(), mErrorHandler(std::move(errorHandler)) {} - ANDROID_API virtual ~RootRenderNode() {} + virtual ~RootRenderNode() {} virtual void prepareTree(TreeInfo& info) override; - ANDROID_API void attachAnimatingNode(RenderNode* animatingNode); + void attachAnimatingNode(RenderNode* animatingNode); void attachPendingVectorDrawableAnimators(); @@ -53,9 +53,9 @@ public: void pushStagingVectorDrawableAnimators(AnimationContext* context); - ANDROID_API void destroy(); + void destroy(); - ANDROID_API void addVectorDrawableAnimator(PropertyValuesAnimatorSet* anim); + void addVectorDrawableAnimator(PropertyValuesAnimatorSet* anim); private: const std::unique_ptr<ErrorHandler> mErrorHandler; @@ -75,12 +75,11 @@ private: }; #ifdef __ANDROID__ // Layoutlib does not support Animations -class ANDROID_API ContextFactoryImpl : public IContextFactory { +class ContextFactoryImpl : public IContextFactory { public: - ANDROID_API explicit ContextFactoryImpl(RootRenderNode* rootNode) : mRootNode(rootNode) {} + explicit ContextFactoryImpl(RootRenderNode* rootNode) : mRootNode(rootNode) {} - ANDROID_API virtual AnimationContext* createAnimationContext( - renderthread::TimeLord& clock) override; + virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override; private: RootRenderNode* mRootNode; diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 941437998838..242b8b0d139e 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -40,6 +40,7 @@ #include <SkShader.h> #include <SkTemplates.h> #include <SkTextBlob.h> +#include <SkVertices.h> #include <memory> #include <optional> @@ -842,9 +843,4 @@ void SkiaCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) { LOG_ALWAYS_FATAL("SkiaCanvas can't directly draw RenderNodes"); } -void SkiaCanvas::callDrawGLFunction(Functor* functor, - uirenderer::GlFunctorLifecycleListener* listener) { - LOG_ALWAYS_FATAL("SkiaCanvas can't directly draw GL Content"); -} - } // namespace android diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index 1eb089d8764c..1df2b2671659 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -57,8 +57,8 @@ public: LOG_ALWAYS_FATAL("SkiaCanvas does not produce a DisplayList"); return nullptr; } - virtual void insertReorderBarrier(bool enableReorder) override { - LOG_ALWAYS_FATAL("SkiaCanvas does not support reordering barriers"); + virtual void enableZ(bool enableZ) override { + LOG_ALWAYS_FATAL("SkiaCanvas does not support enableZ"); } virtual void setBitmap(const SkBitmap& bitmap) override; @@ -152,8 +152,6 @@ public: virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) override; virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override; - virtual void callDrawGLFunction(Functor* functor, - uirenderer::GlFunctorLifecycleListener* listener) override; virtual void drawPicture(const SkPicture& picture) override; protected: diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h index e1b6f2adde74..ac7d41e0d600 100644 --- a/libs/hwui/VectorDrawable.h +++ b/libs/hwui/VectorDrawable.h @@ -97,7 +97,7 @@ private: bool* mStagingDirty; }; -class ANDROID_API Node { +class Node { public: class Properties { public: @@ -127,9 +127,9 @@ protected: PropertyChangedListener* mPropertyChangedListener = nullptr; }; -class ANDROID_API Path : public Node { +class Path : public Node { public: - struct ANDROID_API Data { + struct Data { std::vector<char> verbs; std::vector<size_t> verbSizes; std::vector<float> points; @@ -200,7 +200,7 @@ private: bool mStagingPropertiesDirty = true; }; -class ANDROID_API FullPath : public Path { +class FullPath : public Path { public: class FullPathProperties : public Properties { public: @@ -369,7 +369,7 @@ private: bool mAntiAlias = true; }; -class ANDROID_API ClipPath : public Path { +class ClipPath : public Path { public: ClipPath(const ClipPath& path) : Path(path) {} ClipPath(const char* path, size_t strLength) : Path(path, strLength) {} @@ -378,7 +378,7 @@ public: virtual void setAntiAlias(bool aa) {} }; -class ANDROID_API Group : public Node { +class Group : public Node { public: class GroupProperties : public Properties { public: @@ -498,7 +498,7 @@ private: std::vector<std::unique_ptr<Node> > mChildren; }; -class ANDROID_API Tree : public VirtualLightRefBase { +class Tree : public VirtualLightRefBase { public: explicit Tree(Group* rootNode) : mRootNode(rootNode) { mRootNode->setPropertyChangedListener(&mPropertyChangedListener); diff --git a/libs/hwui/apex/java/android/graphics/ColorMatrix.java b/libs/hwui/apex/java/android/graphics/ColorMatrix.java new file mode 100644 index 000000000000..6299b2c47ea1 --- /dev/null +++ b/libs/hwui/apex/java/android/graphics/ColorMatrix.java @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics; + +import java.util.Arrays; + +/** + * 4x5 matrix for transforming the color and alpha components of a Bitmap. + * The matrix can be passed as single array, and is treated as follows: + * + * <pre> + * [ a, b, c, d, e, + * f, g, h, i, j, + * k, l, m, n, o, + * p, q, r, s, t ]</pre> + * + * <p> + * When applied to a color <code>[R, G, B, A]</code>, the resulting color + * is computed as: + * </p> + * + * <pre> + * R’ = a*R + b*G + c*B + d*A + e; + * G’ = f*R + g*G + h*B + i*A + j; + * B’ = k*R + l*G + m*B + n*A + o; + * A’ = p*R + q*G + r*B + s*A + t;</pre> + * + * <p> + * That resulting color <code>[R’, G’, B’, A’]</code> + * then has each channel clamped to the <code>0</code> to <code>255</code> + * range. + * </p> + * + * <p> + * The sample ColorMatrix below inverts incoming colors by scaling each + * channel by <code>-1</code>, and then shifting the result up by + * <code>255</code> to remain in the standard color space. + * </p> + * + * <pre> + * [ -1, 0, 0, 0, 255, + * 0, -1, 0, 0, 255, + * 0, 0, -1, 0, 255, + * 0, 0, 0, 1, 0 ]</pre> + */ +@SuppressWarnings({ "MismatchedReadAndWriteOfArray", "PointlessArithmeticExpression" }) +public class ColorMatrix { + private final float[] mArray = new float[20]; + + /** + * Create a new colormatrix initialized to identity (as if reset() had + * been called). + */ + public ColorMatrix() { + reset(); + } + + /** + * Create a new colormatrix initialized with the specified array of values. + */ + public ColorMatrix(float[] src) { + System.arraycopy(src, 0, mArray, 0, 20); + } + + /** + * Create a new colormatrix initialized with the specified colormatrix. + */ + public ColorMatrix(ColorMatrix src) { + System.arraycopy(src.mArray, 0, mArray, 0, 20); + } + + /** + * Return the array of floats representing this colormatrix. + */ + public final float[] getArray() { return mArray; } + + /** + * Set this colormatrix to identity: + * <pre> + * [ 1 0 0 0 0 - red vector + * 0 1 0 0 0 - green vector + * 0 0 1 0 0 - blue vector + * 0 0 0 1 0 ] - alpha vector + * </pre> + */ + public void reset() { + final float[] a = mArray; + Arrays.fill(a, 0); + a[0] = a[6] = a[12] = a[18] = 1; + } + + /** + * Assign the src colormatrix into this matrix, copying all of its values. + */ + public void set(ColorMatrix src) { + System.arraycopy(src.mArray, 0, mArray, 0, 20); + } + + /** + * Assign the array of floats into this matrix, copying all of its values. + */ + public void set(float[] src) { + System.arraycopy(src, 0, mArray, 0, 20); + } + + /** + * Set this colormatrix to scale by the specified values. + */ + public void setScale(float rScale, float gScale, float bScale, + float aScale) { + final float[] a = mArray; + + for (int i = 19; i > 0; --i) { + a[i] = 0; + } + a[0] = rScale; + a[6] = gScale; + a[12] = bScale; + a[18] = aScale; + } + + /** + * Set the rotation on a color axis by the specified values. + * <p> + * <code>axis=0</code> correspond to a rotation around the RED color + * <code>axis=1</code> correspond to a rotation around the GREEN color + * <code>axis=2</code> correspond to a rotation around the BLUE color + * </p> + */ + public void setRotate(int axis, float degrees) { + reset(); + double radians = degrees * Math.PI / 180d; + float cosine = (float) Math.cos(radians); + float sine = (float) Math.sin(radians); + switch (axis) { + // Rotation around the red color + case 0: + mArray[6] = mArray[12] = cosine; + mArray[7] = sine; + mArray[11] = -sine; + break; + // Rotation around the green color + case 1: + mArray[0] = mArray[12] = cosine; + mArray[2] = -sine; + mArray[10] = sine; + break; + // Rotation around the blue color + case 2: + mArray[0] = mArray[6] = cosine; + mArray[1] = sine; + mArray[5] = -sine; + break; + default: + throw new RuntimeException(); + } + } + + /** + * Set this colormatrix to the concatenation of the two specified + * colormatrices, such that the resulting colormatrix has the same effect + * as applying matB and then applying matA. + * <p> + * It is legal for either matA or matB to be the same colormatrix as this. + * </p> + */ + public void setConcat(ColorMatrix matA, ColorMatrix matB) { + float[] tmp; + if (matA == this || matB == this) { + tmp = new float[20]; + } else { + tmp = mArray; + } + + final float[] a = matA.mArray; + final float[] b = matB.mArray; + int index = 0; + for (int j = 0; j < 20; j += 5) { + for (int i = 0; i < 4; i++) { + tmp[index++] = a[j + 0] * b[i + 0] + a[j + 1] * b[i + 5] + + a[j + 2] * b[i + 10] + a[j + 3] * b[i + 15]; + } + tmp[index++] = a[j + 0] * b[4] + a[j + 1] * b[9] + + a[j + 2] * b[14] + a[j + 3] * b[19] + + a[j + 4]; + } + + if (tmp != mArray) { + System.arraycopy(tmp, 0, mArray, 0, 20); + } + } + + /** + * Concat this colormatrix with the specified prematrix. + * <p> + * This is logically the same as calling setConcat(this, prematrix); + * </p> + */ + public void preConcat(ColorMatrix prematrix) { + setConcat(this, prematrix); + } + + /** + * Concat this colormatrix with the specified postmatrix. + * <p> + * This is logically the same as calling setConcat(postmatrix, this); + * </p> + */ + public void postConcat(ColorMatrix postmatrix) { + setConcat(postmatrix, this); + } + + /////////////////////////////////////////////////////////////////////////// + + /** + * Set the matrix to affect the saturation of colors. + * + * @param sat A value of 0 maps the color to gray-scale. 1 is identity. + */ + public void setSaturation(float sat) { + reset(); + float[] m = mArray; + + final float invSat = 1 - sat; + final float R = 0.213f * invSat; + final float G = 0.715f * invSat; + final float B = 0.072f * invSat; + + m[0] = R + sat; m[1] = G; m[2] = B; + m[5] = R; m[6] = G + sat; m[7] = B; + m[10] = R; m[11] = G; m[12] = B + sat; + } + + /** + * Set the matrix to convert RGB to YUV + */ + public void setRGB2YUV() { + reset(); + float[] m = mArray; + // these coefficients match those in libjpeg + m[0] = 0.299f; m[1] = 0.587f; m[2] = 0.114f; + m[5] = -0.16874f; m[6] = -0.33126f; m[7] = 0.5f; + m[10] = 0.5f; m[11] = -0.41869f; m[12] = -0.08131f; + } + + /** + * Set the matrix to convert from YUV to RGB + */ + public void setYUV2RGB() { + reset(); + float[] m = mArray; + // these coefficients match those in libjpeg + m[2] = 1.402f; + m[5] = 1; m[6] = -0.34414f; m[7] = -0.71414f; + m[10] = 1; m[11] = 1.772f; m[12] = 0; + } + + @Override + public boolean equals(Object obj) { + // if (obj == this) return true; -- NaN value would mean matrix != itself + if (!(obj instanceof ColorMatrix)) { + return false; + } + + // we don't use Arrays.equals(), since that considers NaN == NaN + final float[] other = ((ColorMatrix) obj).mArray; + for (int i = 0; i < 20; i++) { + if (other[i] != mArray[i]) { + return false; + } + } + return true; + } +} diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp index a114e2f42157..12e2e8135278 100644 --- a/libs/hwui/apex/jni_runtime.cpp +++ b/libs/hwui/apex/jni_runtime.cpp @@ -16,14 +16,14 @@ #include "android/graphics/jni_runtime.h" -#include <android/log.h> -#include <nativehelper/JNIHelp.h> -#include <sys/cdefs.h> - #include <EGL/egl.h> #include <GraphicsJNI.h> #include <Properties.h> #include <SkGraphics.h> +#include <android/log.h> +#include <nativehelper/JNIHelp.h> +#include <sys/cdefs.h> +#include <vulkan/vulkan.h> #undef LOG_TAG #define LOG_TAG "AndroidGraphicsJNI" @@ -61,6 +61,7 @@ extern int register_android_graphics_Path(JNIEnv* env); extern int register_android_graphics_PathMeasure(JNIEnv* env); extern int register_android_graphics_Picture(JNIEnv*); extern int register_android_graphics_Region(JNIEnv* env); +extern int register_android_graphics_TextureLayer(JNIEnv* env); extern int register_android_graphics_animation_NativeInterpolatorFactory(JNIEnv* env); extern int register_android_graphics_animation_RenderNodeAnimator(JNIEnv* env); extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env); @@ -76,7 +77,6 @@ extern int register_android_graphics_text_LineBreaker(JNIEnv *env); extern int register_android_util_PathParser(JNIEnv* env); extern int register_android_view_DisplayListCanvas(JNIEnv* env); extern int register_android_view_RenderNode(JNIEnv* env); -extern int register_android_view_TextureLayer(JNIEnv* env); extern int register_android_view_ThreadedRenderer(JNIEnv* env); #ifdef NDEBUG @@ -123,6 +123,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_graphics_Picture), REG_JNI(register_android_graphics_Region), REG_JNI(register_android_graphics_Shader), + REG_JNI(register_android_graphics_TextureLayer), REG_JNI(register_android_graphics_Typeface), REG_JNI(register_android_graphics_YuvImage), REG_JNI(register_android_graphics_animation_NativeInterpolatorFactory), @@ -140,7 +141,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_util_PathParser), REG_JNI(register_android_view_RenderNode), REG_JNI(register_android_view_DisplayListCanvas), - REG_JNI(register_android_view_TextureLayer), REG_JNI(register_android_view_ThreadedRenderer), }; @@ -172,6 +172,11 @@ using android::uirenderer::RenderPipelineType; void zygote_preload_graphics() { if (Properties::peekRenderPipelineType() == RenderPipelineType::SkiaGL) { + // Preload GL driver if HWUI renders with GL backend. eglGetDisplay(EGL_DEFAULT_DISPLAY); + } else { + // Preload Vulkan driver if HWUI renders with Vulkan backend. + uint32_t apiVersion; + vkEnumerateInstanceVersion(&apiVersion); } -}
\ No newline at end of file +} diff --git a/libs/hwui/api/current.txt b/libs/hwui/api/current.txt new file mode 100644 index 000000000000..c396a2032eed --- /dev/null +++ b/libs/hwui/api/current.txt @@ -0,0 +1,23 @@ +// Signature format: 2.0 +package android.graphics { + + public class ColorMatrix { + ctor public ColorMatrix(); + ctor public ColorMatrix(float[]); + ctor public ColorMatrix(android.graphics.ColorMatrix); + method public final float[] getArray(); + method public void postConcat(android.graphics.ColorMatrix); + method public void preConcat(android.graphics.ColorMatrix); + method public void reset(); + method public void set(android.graphics.ColorMatrix); + method public void set(float[]); + method public void setConcat(android.graphics.ColorMatrix, android.graphics.ColorMatrix); + method public void setRGB2YUV(); + method public void setRotate(int, float); + method public void setSaturation(float); + method public void setScale(float, float, float, float); + method public void setYUV2RGB(); + } + +} + diff --git a/libs/hwui/api/module-lib-current.txt b/libs/hwui/api/module-lib-current.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/libs/hwui/api/module-lib-current.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/libs/hwui/api/module-lib-removed.txt b/libs/hwui/api/module-lib-removed.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/libs/hwui/api/module-lib-removed.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/libs/hwui/api/removed.txt b/libs/hwui/api/removed.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/libs/hwui/api/removed.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/libs/hwui/api/system-current.txt b/libs/hwui/api/system-current.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/libs/hwui/api/system-current.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/libs/hwui/api/system-removed.txt b/libs/hwui/api/system-removed.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/libs/hwui/api/system-removed.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h index f0aa35acf71b..f81a5a40b44e 100644 --- a/libs/hwui/hwui/AnimatedImageDrawable.h +++ b/libs/hwui/hwui/AnimatedImageDrawable.h @@ -44,7 +44,7 @@ public: * This class can be drawn into Canvas.h and maintains the state needed to drive * the animation from the RenderThread. */ -class ANDROID_API AnimatedImageDrawable : public SkDrawable { +class AnimatedImageDrawable : public SkDrawable { public: // bytesUsed includes the approximate sizes of the SkAnimatedImage and the SkPictures in the // Snapshots. diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 60ef4371d38d..1a89cfd5d0ad 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -33,7 +33,6 @@ #ifndef _WIN32 #include <binder/IServiceManager.h> #endif -#include <ui/PixelFormat.h> #include <SkCanvas.h> #include <SkImagePriv.h> @@ -132,15 +131,8 @@ sk_sp<Bitmap> Bitmap::allocateHeapBitmap(size_t size, const SkImageInfo& info, s return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes)); } -void FreePixelRef(void* addr, void* context) { - auto pixelRef = (SkPixelRef*)context; - pixelRef->unref(); -} - sk_sp<Bitmap> Bitmap::createFrom(const SkImageInfo& info, SkPixelRef& pixelRef) { - pixelRef.ref(); - return sk_sp<Bitmap>(new Bitmap((void*)pixelRef.pixels(), (void*)&pixelRef, FreePixelRef, info, - pixelRef.rowBytes())); + return sk_sp<Bitmap>(new Bitmap(pixelRef, info)); } @@ -230,14 +222,12 @@ Bitmap::Bitmap(void* address, size_t size, const SkImageInfo& info, size_t rowBy mPixelStorage.heap.size = size; } -Bitmap::Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info, - size_t rowBytes) - : SkPixelRef(info.width(), info.height(), address, rowBytes) +Bitmap::Bitmap(SkPixelRef& pixelRef, const SkImageInfo& info) + : SkPixelRef(info.width(), info.height(), pixelRef.pixels(), pixelRef.rowBytes()) , mInfo(validateAlpha(info)) - , mPixelStorageType(PixelStorageType::External) { - mPixelStorage.external.address = address; - mPixelStorage.external.context = context; - mPixelStorage.external.freeFunc = freeFunc; + , mPixelStorageType(PixelStorageType::WrappedPixelRef) { + pixelRef.ref(); + mPixelStorage.wrapped.pixelRef = &pixelRef; } Bitmap::Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes) @@ -266,9 +256,8 @@ Bitmap::Bitmap(AHardwareBuffer* buffer, const SkImageInfo& info, size_t rowBytes Bitmap::~Bitmap() { switch (mPixelStorageType) { - case PixelStorageType::External: - mPixelStorage.external.freeFunc(mPixelStorage.external.address, - mPixelStorage.external.context); + case PixelStorageType::WrappedPixelRef: + mPixelStorage.wrapped.pixelRef->unref(); break; case PixelStorageType::Ashmem: #ifndef _WIN32 // ashmem not implemented on Windows @@ -300,19 +289,6 @@ void Bitmap::setHasHardwareMipMap(bool hasMipMap) { mHasHardwareMipMap = hasMipMap; } -void* Bitmap::getStorage() const { - switch (mPixelStorageType) { - case PixelStorageType::External: - return mPixelStorage.external.address; - case PixelStorageType::Ashmem: - return mPixelStorage.ashmem.address; - case PixelStorageType::Heap: - return mPixelStorage.heap.address; - case PixelStorageType::Hardware: - return nullptr; - } -} - int Bitmap::getAshmemFd() const { switch (mPixelStorageType) { case PixelStorageType::Ashmem: diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h index b8b59947a57b..6ece7ef9f329 100644 --- a/libs/hwui/hwui/Bitmap.h +++ b/libs/hwui/hwui/Bitmap.h @@ -32,7 +32,7 @@ class SkWStream; namespace android { enum class PixelStorageType { - External, + WrappedPixelRef, Heap, Ashmem, Hardware, @@ -56,7 +56,7 @@ class PixelStorage; typedef void (*FreeFunc)(void* addr, void* context); -class ANDROID_API Bitmap : public SkPixelRef { +class Bitmap : public SkPixelRef { public: /* The allocate factories not only construct the Bitmap object but also allocate the * backing store whose type is determined by the specific method that is called. @@ -71,6 +71,7 @@ public: static sk_sp<Bitmap> allocateHardwareBitmap(const SkBitmap& bitmap); static sk_sp<Bitmap> allocateHeapBitmap(SkBitmap* bitmap); static sk_sp<Bitmap> allocateHeapBitmap(const SkImageInfo& info); + static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& i, size_t rowBytes); /* The createFrom factories construct a new Bitmap object by wrapping the already allocated * memory that is provided as an input param. @@ -160,11 +161,9 @@ public: int32_t quality, SkWStream* stream); private: static sk_sp<Bitmap> allocateAshmemBitmap(size_t size, const SkImageInfo& i, size_t rowBytes); - static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& i, size_t rowBytes); Bitmap(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes); - Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info, - size_t rowBytes); + Bitmap(SkPixelRef& pixelRef, const SkImageInfo& info); Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes); #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration Bitmap(AHardwareBuffer* buffer, const SkImageInfo& info, size_t rowBytes, @@ -178,7 +177,6 @@ private: #endif virtual ~Bitmap(); - void* getStorage() const; SkImageInfo mInfo; @@ -191,10 +189,8 @@ private: union { struct { - void* address; - void* context; - FreeFunc freeFunc; - } external; + SkPixelRef* pixelRef; + } wrapped; struct { void* address; int fd; diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index 27dfed305a94..333567b0cf91 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -20,7 +20,6 @@ #include <utils/Functor.h> #include <androidfw/ResourceTypes.h> -#include "GlFunctorLifecycleListener.h" #include "Properties.h" #include "utils/Macros.h" @@ -144,7 +143,7 @@ public: virtual void resetRecording(int width, int height, uirenderer::RenderNode* renderNode = nullptr) = 0; virtual uirenderer::DisplayList* finishRecording() = 0; - virtual void insertReorderBarrier(bool enableReorder) = 0; + virtual void enableZ(bool enableZ) = 0; bool isHighContrastText() const { return uirenderer::Properties::enableHighContrastText; } @@ -162,8 +161,7 @@ public: virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) = 0; virtual void drawRenderNode(uirenderer::RenderNode* renderNode) = 0; - virtual void callDrawGLFunction(Functor* functor, - uirenderer::GlFunctorLifecycleListener* listener) = 0; + virtual void drawWebViewFunctor(int /*functor*/) { LOG_ALWAYS_FATAL("Not supported"); } diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp index 6a12a203b9f8..a6137b073d5a 100644 --- a/libs/hwui/hwui/MinikinSkia.cpp +++ b/libs/hwui/hwui/MinikinSkia.cpp @@ -125,22 +125,22 @@ const std::vector<minikin::FontVariation>& MinikinFontSkia::GetAxes() const { std::shared_ptr<minikin::MinikinFont> MinikinFontSkia::createFontWithVariation( const std::vector<minikin::FontVariation>& variations) const { - SkFontArguments params; + SkFontArguments args; int ttcIndex; std::unique_ptr<SkStreamAsset> stream(mTypeface->openStream(&ttcIndex)); LOG_ALWAYS_FATAL_IF(stream == nullptr, "openStream failed"); - params.setCollectionIndex(ttcIndex); - std::vector<SkFontArguments::Axis> skAxes; - skAxes.resize(variations.size()); + args.setCollectionIndex(ttcIndex); + std::vector<SkFontArguments::VariationPosition::Coordinate> skVariation; + skVariation.resize(variations.size()); for (size_t i = 0; i < variations.size(); i++) { - skAxes[i].fTag = variations[i].axisTag; - skAxes[i].fStyleValue = SkFloatToScalar(variations[i].value); + skVariation[i].axis = variations[i].axisTag; + skVariation[i].value = SkFloatToScalar(variations[i].value); } - params.setAxes(skAxes.data(), skAxes.size()); + args.setVariationDesignPosition({skVariation.data(), static_cast<int>(skVariation.size())}); sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); - sk_sp<SkTypeface> face(fm->makeFromStream(std::move(stream), params)); + sk_sp<SkTypeface> face(fm->makeFromStream(std::move(stream), args)); return std::make_shared<MinikinFontSkia>(std::move(face), mFontData, mFontSize, mFilePath, ttcIndex, variations); diff --git a/libs/hwui/hwui/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h index cbf409504675..0eacde9a467e 100644 --- a/libs/hwui/hwui/MinikinUtils.h +++ b/libs/hwui/hwui/MinikinUtils.h @@ -39,30 +39,30 @@ namespace android { class MinikinUtils { public: - ANDROID_API static minikin::MinikinPaint prepareMinikinPaint(const Paint* paint, + static minikin::MinikinPaint prepareMinikinPaint(const Paint* paint, const Typeface* typeface); - ANDROID_API static minikin::Layout doLayout(const Paint* paint, minikin::Bidi bidiFlags, + static minikin::Layout doLayout(const Paint* paint, minikin::Bidi bidiFlags, const Typeface* typeface, const uint16_t* buf, size_t bufSize, size_t start, size_t count, size_t contextStart, size_t contextCount, minikin::MeasuredText* mt); - ANDROID_API static float measureText(const Paint* paint, minikin::Bidi bidiFlags, + static float measureText(const Paint* paint, minikin::Bidi bidiFlags, const Typeface* typeface, const uint16_t* buf, size_t start, size_t count, size_t bufSize, float* advances); - ANDROID_API static bool hasVariationSelector(const Typeface* typeface, uint32_t codepoint, + static bool hasVariationSelector(const Typeface* typeface, uint32_t codepoint, uint32_t vs); - ANDROID_API static float xOffsetForTextAlign(Paint* paint, const minikin::Layout& layout); + static float xOffsetForTextAlign(Paint* paint, const minikin::Layout& layout); - ANDROID_API static float hOffsetForTextAlign(Paint* paint, const minikin::Layout& layout, + static float hOffsetForTextAlign(Paint* paint, const minikin::Layout& layout, const SkPath& path); // f is a functor of type void f(size_t start, size_t end); template <typename F> - ANDROID_API static void forFontRun(const minikin::Layout& layout, Paint* paint, F& f) { + static void forFontRun(const minikin::Layout& layout, Paint* paint, F& f) { float saveSkewX = paint->getSkFont().getSkewX(); bool savefakeBold = paint->getSkFont().isEmbolden(); const minikin::MinikinFont* curFont = nullptr; diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h index 281ecd27d780..e75e9e7c6933 100644 --- a/libs/hwui/hwui/Paint.h +++ b/libs/hwui/hwui/Paint.h @@ -32,7 +32,7 @@ namespace android { -class ANDROID_API Paint : public SkPaint { +class Paint : public SkPaint { public: // Default values for underlined and strikethrough text, // as defined by Skia in SkTextFormatParams.h. diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp index c0663a9bc699..eb9885a4436a 100755 --- a/libs/hwui/jni/Bitmap.cpp +++ b/libs/hwui/jni/Bitmap.cpp @@ -19,12 +19,19 @@ #include <utils/Color.h> #ifdef __ANDROID__ // Layoutlib does not support graphic buffer, parcel or render thread +#include <android-base/unique_fd.h> +#include <android/binder_parcel.h> +#include <android/binder_parcel_jni.h> +#include <android/binder_parcel_platform.h> +#include <android/binder_parcel_utils.h> #include <private/android/AHardwareBufferHelpers.h> -#include <binder/Parcel.h> +#include <cutils/ashmem.h> #include <dlfcn.h> #include <renderthread/RenderProxy.h> +#include <sys/mman.h> #endif +#include <inttypes.h> #include <string.h> #include <memory> #include <string> @@ -567,152 +574,296 @@ static void Bitmap_setHasMipMap(JNIEnv* env, jobject, jlong bitmapHandle, /////////////////////////////////////////////////////////////////////////////// +// TODO: Move somewhere else #ifdef __ANDROID__ // Layoutlib does not support parcel -static struct parcel_offsets_t -{ - jclass clazz; - jfieldID mNativePtr; -} gParcelOffsets; - -static Parcel* parcelForJavaObject(JNIEnv* env, jobject obj) { - if (obj) { - Parcel* p = (Parcel*)env->GetLongField(obj, gParcelOffsets.mNativePtr); - if (p != NULL) { - return p; + +class ScopedParcel { +public: + explicit ScopedParcel(JNIEnv* env, jobject parcel) { + mParcel = AParcel_fromJavaParcel(env, parcel); + } + + ~ScopedParcel() { AParcel_delete(mParcel); } + + int32_t readInt32() { + int32_t temp = 0; + // TODO: This behavior-matches what android::Parcel does + // but this should probably be better + if (AParcel_readInt32(mParcel, &temp) != STATUS_OK) { + temp = 0; } - jniThrowException(env, "java/lang/IllegalStateException", "Parcel has been finalized!"); + return temp; + } + + uint32_t readUint32() { + uint32_t temp = 0; + // TODO: This behavior-matches what android::Parcel does + // but this should probably be better + if (AParcel_readUint32(mParcel, &temp) != STATUS_OK) { + temp = 0; + } + return temp; + } + + void writeInt32(int32_t value) { AParcel_writeInt32(mParcel, value); } + + void writeUint32(uint32_t value) { AParcel_writeUint32(mParcel, value); } + + bool allowFds() const { return AParcel_getAllowFds(mParcel); } + + std::optional<sk_sp<SkData>> readData() { + struct Data { + void* ptr = nullptr; + size_t size = 0; + } data; + auto error = AParcel_readByteArray(mParcel, &data, + [](void* arrayData, int32_t length, + int8_t** outBuffer) -> bool { + Data* data = reinterpret_cast<Data*>(arrayData); + if (length > 0) { + data->ptr = sk_malloc_canfail(length); + if (!data->ptr) { + return false; + } + *outBuffer = + reinterpret_cast<int8_t*>(data->ptr); + data->size = length; + } + return true; + }); + if (error != STATUS_OK || data.size <= 0) { + sk_free(data.ptr); + return std::nullopt; + } else { + return SkData::MakeFromMalloc(data.ptr, data.size); + } + } + + void writeData(const std::optional<sk_sp<SkData>>& optData) { + if (optData) { + const auto& data = *optData; + AParcel_writeByteArray(mParcel, reinterpret_cast<const int8_t*>(data->data()), + data->size()); + } else { + AParcel_writeByteArray(mParcel, nullptr, -1); + } + } + + AParcel* get() { return mParcel; } + +private: + AParcel* mParcel; +}; + +enum class BlobType : int32_t { + IN_PLACE, + ASHMEM, +}; + +#define ON_ERROR_RETURN(X) \ + if ((error = (X)) != STATUS_OK) return error + +template <typename T, typename U> +static binder_status_t readBlob(AParcel* parcel, T inPlaceCallback, U ashmemCallback) { + binder_status_t error = STATUS_OK; + BlobType type; + static_assert(sizeof(BlobType) == sizeof(int32_t)); + ON_ERROR_RETURN(AParcel_readInt32(parcel, (int32_t*)&type)); + if (type == BlobType::IN_PLACE) { + struct Data { + std::unique_ptr<int8_t[]> ptr = nullptr; + int32_t size = 0; + } data; + ON_ERROR_RETURN( + AParcel_readByteArray(parcel, &data, + [](void* arrayData, int32_t length, int8_t** outBuffer) { + Data* data = reinterpret_cast<Data*>(arrayData); + if (length > 0) { + data->ptr = std::make_unique<int8_t[]>(length); + data->size = length; + *outBuffer = data->ptr.get(); + } + return data->ptr != nullptr; + })); + inPlaceCallback(std::move(data.ptr), data.size); + return STATUS_OK; + } else if (type == BlobType::ASHMEM) { + int rawFd = -1; + int32_t size = 0; + ON_ERROR_RETURN(AParcel_readInt32(parcel, &size)); + ON_ERROR_RETURN(AParcel_readParcelFileDescriptor(parcel, &rawFd)); + android::base::unique_fd fd(rawFd); + ashmemCallback(std::move(fd), size); + return STATUS_OK; + } else { + // Although the above if/else was "exhaustive" guard against unknown types + return STATUS_UNKNOWN_ERROR; } - return NULL; } -#endif + +static constexpr size_t BLOB_INPLACE_LIMIT = 12 * 1024; +// Fail fast if we can't use ashmem and the size exceeds this limit - the binder transaction +// wouldn't go through, anyway +// TODO: Can we get this from somewhere? +static constexpr size_t BLOB_MAX_INPLACE_LIMIT = 1 * 1024 * 1024; +static constexpr bool shouldUseAshmem(AParcel* parcel, int32_t size) { + return size > BLOB_INPLACE_LIMIT && AParcel_getAllowFds(parcel); +} + +static binder_status_t writeBlobFromFd(AParcel* parcel, int32_t size, int fd) { + binder_status_t error = STATUS_OK; + ON_ERROR_RETURN(AParcel_writeInt32(parcel, static_cast<int32_t>(BlobType::ASHMEM))); + ON_ERROR_RETURN(AParcel_writeInt32(parcel, size)); + ON_ERROR_RETURN(AParcel_writeParcelFileDescriptor(parcel, fd)); + return STATUS_OK; +} + +static binder_status_t writeBlob(AParcel* parcel, const int32_t size, const void* data, bool immutable) { + if (size <= 0 || data == nullptr) { + return STATUS_NOT_ENOUGH_DATA; + } + binder_status_t error = STATUS_OK; + if (shouldUseAshmem(parcel, size)) { + // Create new ashmem region with read/write priv + base::unique_fd fd(ashmem_create_region("bitmap", size)); + if (fd.get() < 0) { + return STATUS_NO_MEMORY; + } + + { + void* dest = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0); + if (dest == MAP_FAILED) { + return STATUS_NO_MEMORY; + } + memcpy(dest, data, size); + munmap(dest, size); + } + + if (immutable && ashmem_set_prot_region(fd.get(), PROT_READ) < 0) { + return STATUS_UNKNOWN_ERROR; + } + // Workaround b/149851140 in AParcel_writeParcelFileDescriptor + int rawFd = fd.release(); + error = writeBlobFromFd(parcel, size, rawFd); + close(rawFd); + return error; + } else { + if (size > BLOB_MAX_INPLACE_LIMIT) { + return STATUS_FAILED_TRANSACTION; + } + ON_ERROR_RETURN(AParcel_writeInt32(parcel, static_cast<int32_t>(BlobType::IN_PLACE))); + ON_ERROR_RETURN(AParcel_writeByteArray(parcel, static_cast<const int8_t*>(data), size)); + return STATUS_OK; + } +} + +#undef ON_ERROR_RETURN + +#endif // __ANDROID__ // Layoutlib does not support parcel // This is the maximum possible size because the SkColorSpace must be // representable (and therefore serializable) using a matrix and numerical // transfer function. If we allow more color space representations in the // framework, we may need to update this maximum size. -static constexpr uint32_t kMaxColorSpaceSerializedBytes = 80; +static constexpr size_t kMaxColorSpaceSerializedBytes = 80; + +static constexpr auto RuntimeException = "java/lang/RuntimeException"; + +static bool validateImageInfo(const SkImageInfo& info, int32_t rowBytes) { + // TODO: Can we avoid making a SkBitmap for this? + return SkBitmap().setInfo(info, rowBytes); +} static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { #ifdef __ANDROID__ // Layoutlib does not support parcel if (parcel == NULL) { - SkDebugf("-------- unparcel parcel is NULL\n"); + jniThrowNullPointerException(env, "parcel cannot be null"); return NULL; } - android::Parcel* p = parcelForJavaObject(env, parcel); + ScopedParcel p(env, parcel); - const bool isMutable = p->readInt32() != 0; - const SkColorType colorType = (SkColorType)p->readInt32(); - const SkAlphaType alphaType = (SkAlphaType)p->readInt32(); - const uint32_t colorSpaceSize = p->readUint32(); + const bool isMutable = p.readInt32(); + const SkColorType colorType = static_cast<SkColorType>(p.readInt32()); + const SkAlphaType alphaType = static_cast<SkAlphaType>(p.readInt32()); sk_sp<SkColorSpace> colorSpace; - if (colorSpaceSize > 0) { - if (colorSpaceSize > kMaxColorSpaceSerializedBytes) { + const auto optColorSpaceData = p.readData(); + if (optColorSpaceData) { + const auto& colorSpaceData = *optColorSpaceData; + if (colorSpaceData->size() > kMaxColorSpaceSerializedBytes) { ALOGD("Bitmap_createFromParcel: Serialized SkColorSpace is larger than expected: " - "%d bytes\n", colorSpaceSize); + "%zu bytes (max: %zu)\n", + colorSpaceData->size(), kMaxColorSpaceSerializedBytes); } - const void* data = p->readInplace(colorSpaceSize); - if (data) { - colorSpace = SkColorSpace::Deserialize(data, colorSpaceSize); - } else { - ALOGD("Bitmap_createFromParcel: Unable to read serialized SkColorSpace data\n"); - } + colorSpace = SkColorSpace::Deserialize(colorSpaceData->data(), colorSpaceData->size()); } - const int width = p->readInt32(); - const int height = p->readInt32(); - const int rowBytes = p->readInt32(); - const int density = p->readInt32(); + const int32_t width = p.readInt32(); + const int32_t height = p.readInt32(); + const int32_t rowBytes = p.readInt32(); + const int32_t density = p.readInt32(); if (kN32_SkColorType != colorType && kRGBA_F16_SkColorType != colorType && kRGB_565_SkColorType != colorType && kARGB_4444_SkColorType != colorType && kAlpha_8_SkColorType != colorType) { - SkDebugf("Bitmap_createFromParcel unknown colortype: %d\n", colorType); + jniThrowExceptionFmt(env, RuntimeException, + "Bitmap_createFromParcel unknown colortype: %d\n", colorType); return NULL; } - std::unique_ptr<SkBitmap> bitmap(new SkBitmap); - if (!bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType, colorSpace), - rowBytes)) { + auto imageInfo = SkImageInfo::Make(width, height, colorType, alphaType, colorSpace); + size_t allocationSize = 0; + if (!validateImageInfo(imageInfo, rowBytes)) { + jniThrowRuntimeException(env, "Received bad SkImageInfo"); return NULL; } - - // Read the bitmap blob. - size_t size = bitmap->computeByteSize(); - android::Parcel::ReadableBlob blob; - android::status_t status = p->readBlob(size, &blob); - if (status) { - doThrowRE(env, "Could not read bitmap blob."); + if (!Bitmap::computeAllocationSize(rowBytes, height, &allocationSize)) { + jniThrowExceptionFmt(env, RuntimeException, + "Received bad bitmap size: width=%d, height=%d, rowBytes=%d", width, + height, rowBytes); return NULL; } - - // Map the bitmap in place from the ashmem region if possible otherwise copy. sk_sp<Bitmap> nativeBitmap; - // If the blob is mutable we have ownership of the region and can always use it - // If the blob is immutable _and_ we're immutable, we can then still use it - if (blob.fd() >= 0 && (blob.isMutable() || !isMutable)) { -#if DEBUG_PARCEL - ALOGD("Bitmap.createFromParcel: mapped contents of bitmap from %s blob " - "(fds %s)", - blob.isMutable() ? "mutable" : "immutable", - p->allowFds() ? "allowed" : "forbidden"); -#endif - // Dup the file descriptor so we can keep a reference to it after the Parcel - // is disposed. - int dupFd = fcntl(blob.fd(), F_DUPFD_CLOEXEC, 0); - if (dupFd < 0) { - ALOGE("Error allocating dup fd. Error:%d", errno); - blob.release(); - doThrowRE(env, "Could not allocate dup blob fd."); - return NULL; - } - - // Map the pixels in place and take ownership of the ashmem region. We must also respect the - // rowBytes value already set on the bitmap instead of attempting to compute our own. - nativeBitmap = Bitmap::createFrom(bitmap->info(), bitmap->rowBytes(), dupFd, - const_cast<void*>(blob.data()), size, !isMutable); - if (!nativeBitmap) { - close(dupFd); - blob.release(); - doThrowRE(env, "Could not allocate ashmem pixel ref."); - return NULL; - } - - // Clear the blob handle, don't release it. - blob.clear(); - } else { -#if DEBUG_PARCEL - if (blob.fd() >= 0) { - ALOGD("Bitmap.createFromParcel: copied contents of mutable bitmap " - "from immutable blob (fds %s)", - p->allowFds() ? "allowed" : "forbidden"); - } else { - ALOGD("Bitmap.createFromParcel: copied contents from %s blob " - "(fds %s)", - blob.isMutable() ? "mutable" : "immutable", - p->allowFds() ? "allowed" : "forbidden"); - } -#endif - - // Copy the pixels into a new buffer. - nativeBitmap = Bitmap::allocateHeapBitmap(bitmap.get()); - if (!nativeBitmap) { - blob.release(); - doThrowRE(env, "Could not allocate java pixel ref."); - return NULL; - } - memcpy(bitmap->getPixels(), blob.data(), size); - - // Release the blob handle. - blob.release(); + binder_status_t error = readBlob( + p.get(), + // In place callback + [&](std::unique_ptr<int8_t[]> buffer, int32_t size) { + nativeBitmap = Bitmap::allocateHeapBitmap(allocationSize, imageInfo, rowBytes); + if (nativeBitmap) { + memcpy(nativeBitmap->pixels(), buffer.get(), size); + } + }, + // Ashmem callback + [&](android::base::unique_fd fd, int32_t size) { + int flags = PROT_READ; + if (isMutable) { + flags |= PROT_WRITE; + } + void* addr = mmap(nullptr, size, flags, MAP_SHARED, fd.get(), 0); + if (addr == MAP_FAILED) { + const int err = errno; + ALOGW("mmap failed, error %d (%s)", err, strerror(err)); + return; + } + nativeBitmap = + Bitmap::createFrom(imageInfo, rowBytes, fd.release(), addr, size, !isMutable); + }); + if (error != STATUS_OK) { + // TODO: Stringify the error, see signalExceptionForError in android_util_Binder.cpp + jniThrowExceptionFmt(env, RuntimeException, "Failed to read from Parcel, error=%d", error); + return nullptr; + } + if (!nativeBitmap) { + jniThrowRuntimeException(env, "Could not allocate java pixel ref."); + return nullptr; } - return createBitmap(env, nativeBitmap.release(), - getPremulBitmapCreateFlags(isMutable), NULL, NULL, density); + return createBitmap(env, nativeBitmap.release(), getPremulBitmapCreateFlags(isMutable), nullptr, + nullptr, density); #else - doThrowRE(env, "Cannot use parcels outside of Android"); + jniThrowRuntimeException(env, "Cannot use parcels outside of Android"); return NULL; #endif } @@ -725,48 +876,38 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, return JNI_FALSE; } - android::Parcel* p = parcelForJavaObject(env, parcel); + ScopedParcel p(env, parcel); SkBitmap bitmap; auto bitmapWrapper = reinterpret_cast<BitmapWrapper*>(bitmapHandle); bitmapWrapper->getSkBitmap(&bitmap); - p->writeInt32(!bitmap.isImmutable()); - p->writeInt32(bitmap.colorType()); - p->writeInt32(bitmap.alphaType()); + p.writeInt32(!bitmap.isImmutable()); + p.writeInt32(bitmap.colorType()); + p.writeInt32(bitmap.alphaType()); SkColorSpace* colorSpace = bitmap.colorSpace(); if (colorSpace != nullptr) { - sk_sp<SkData> data = colorSpace->serialize(); - size_t size = data->size(); - p->writeUint32(size); - if (size > 0) { - if (size > kMaxColorSpaceSerializedBytes) { - ALOGD("Bitmap_writeToParcel: Serialized SkColorSpace is larger than expected: " - "%zu bytes\n", size); - } - - p->write(data->data(), size); - } + p.writeData(colorSpace->serialize()); } else { - p->writeUint32(0); + p.writeData(std::nullopt); } - p->writeInt32(bitmap.width()); - p->writeInt32(bitmap.height()); - p->writeInt32(bitmap.rowBytes()); - p->writeInt32(density); + p.writeInt32(bitmap.width()); + p.writeInt32(bitmap.height()); + p.writeInt32(bitmap.rowBytes()); + p.writeInt32(density); // Transfer the underlying ashmem region if we have one and it's immutable. - android::status_t status; + binder_status_t status; int fd = bitmapWrapper->bitmap().getAshmemFd(); - if (fd >= 0 && bitmap.isImmutable() && p->allowFds()) { + if (fd >= 0 && p.allowFds() && bitmap.isImmutable()) { #if DEBUG_PARCEL ALOGD("Bitmap.writeToParcel: transferring immutable bitmap's ashmem fd as " - "immutable blob (fds %s)", - p->allowFds() ? "allowed" : "forbidden"); + "immutable blob (fds %s)", + p.allowFds() ? "allowed" : "forbidden"); #endif - status = p->writeDupImmutableBlobFileDescriptor(fd); - if (status) { + status = writeBlobFromFd(p.get(), bitmapWrapper->bitmap().getAllocationByteCount(), fd); + if (status != STATUS_OK) { doThrowRE(env, "Could not write bitmap blob file descriptor."); return JNI_FALSE; } @@ -776,26 +917,15 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, // Copy the bitmap to a new blob. #if DEBUG_PARCEL ALOGD("Bitmap.writeToParcel: copying bitmap into new blob (fds %s)", - p->allowFds() ? "allowed" : "forbidden"); + p.allowFds() ? "allowed" : "forbidden"); #endif - const bool mutableCopy = !bitmap.isImmutable(); size_t size = bitmap.computeByteSize(); - android::Parcel::WritableBlob blob; - status = p->writeBlob(size, mutableCopy, &blob); + status = writeBlob(p.get(), size, bitmap.getPixels(), bitmap.isImmutable()); if (status) { doThrowRE(env, "Could not copy bitmap to parcel blob."); return JNI_FALSE; } - - const void* pSrc = bitmap.getPixels(); - if (pSrc == NULL) { - memset(blob.data(), 0, size); - } else { - memcpy(blob.data(), pSrc, size); - } - - blob.release(); return JNI_TRUE; #else doThrowRE(env, "Cannot use parcels outside of Android"); @@ -1074,13 +1204,16 @@ static jobject Bitmap_wrapHardwareBufferBitmap(JNIEnv* env, jobject, jobject har static jobject Bitmap_getHardwareBuffer(JNIEnv* env, jobject, jlong bitmapPtr) { #ifdef __ANDROID__ // Layoutlib does not support graphic buffer LocalScopedBitmap bitmapHandle(bitmapPtr); - LOG_ALWAYS_FATAL_IF(!bitmapHandle->isHardware(), + if (!bitmapHandle->isHardware()) { + jniThrowException(env, "java/lang/IllegalStateException", "Hardware config is only supported config in Bitmap_getHardwareBuffer"); + return nullptr; + } Bitmap& bitmap = bitmapHandle->bitmap(); return AHardwareBuffer_toHardwareBuffer(env, bitmap.hardwareBuffer()); #else - return NULL; + return nullptr; #endif } @@ -1091,6 +1224,14 @@ static jboolean Bitmap_isImmutable(CRITICAL_JNI_PARAMS_COMMA jlong bitmapHandle) return bitmapHolder->bitmap().isImmutable() ? JNI_TRUE : JNI_FALSE; } +static jboolean Bitmap_isBackedByAshmem(CRITICAL_JNI_PARAMS_COMMA jlong bitmapHandle) { + LocalScopedBitmap bitmapHolder(bitmapHandle); + if (!bitmapHolder.valid()) return JNI_FALSE; + + return bitmapHolder->bitmap().pixelStorageType() == PixelStorageType::Ashmem ? JNI_TRUE + : JNI_FALSE; +} + static void Bitmap_setImmutable(JNIEnv* env, jobject, jlong bitmapHandle) { LocalScopedBitmap bitmapHolder(bitmapHandle); if (!bitmapHolder.valid()) return; @@ -1157,12 +1298,11 @@ static const JNINativeMethod gBitmapMethods[] = { { "nativeSetImmutable", "(J)V", (void*)Bitmap_setImmutable}, // ------------ @CriticalNative ---------------- - { "nativeIsImmutable", "(J)Z", (void*)Bitmap_isImmutable} + { "nativeIsImmutable", "(J)Z", (void*)Bitmap_isImmutable}, + { "nativeIsBackedByAshmem", "(J)Z", (void*)Bitmap_isBackedByAshmem} }; -const char* const kParcelPathName = "android/os/Parcel"; - int register_android_graphics_Bitmap(JNIEnv* env) { gBitmap_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Bitmap")); @@ -1180,9 +1320,6 @@ int register_android_graphics_Bitmap(JNIEnv* env) AHardwareBuffer_toHardwareBuffer = (AHB_to_HB)dlsym(handle_, "AHardwareBuffer_toHardwareBuffer"); LOG_ALWAYS_FATAL_IF(AHardwareBuffer_toHardwareBuffer == nullptr, " Failed to find required symbol AHardwareBuffer_toHardwareBuffer!"); - - gParcelOffsets.clazz = MakeGlobalRefOrDie(env, FindClassOrDie(env, kParcelPathName)); - gParcelOffsets.mNativePtr = GetFieldIDOrDie(env, gParcelOffsets.clazz, "mNativePtr", "J"); #endif return android::RegisterMethodsOrDie(env, "android/graphics/Bitmap", gBitmapMethods, NELEM(gBitmapMethods)); diff --git a/libs/hwui/jni/BitmapFactory.cpp b/libs/hwui/jni/BitmapFactory.cpp index e8e89d81bdb7..7d2583a2ac01 100644 --- a/libs/hwui/jni/BitmapFactory.cpp +++ b/libs/hwui/jni/BitmapFactory.cpp @@ -3,12 +3,11 @@ #include "BitmapFactory.h" #include "CreateJavaOutputStreamAdaptor.h" +#include "FrontBufferedStream.h" #include "GraphicsJNI.h" #include "MimeType.h" #include "NinePatchPeeker.h" #include "SkAndroidCodec.h" -#include "SkBRDAllocator.h" -#include "SkFrontBufferedStream.h" #include "SkMath.h" #include "SkPixelRef.h" #include "SkStream.h" @@ -510,8 +509,8 @@ static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteA std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage)); if (stream.get()) { - std::unique_ptr<SkStreamRewindable> bufferedStream( - SkFrontBufferedStream::Make(std::move(stream), SkCodec::MinBufferedBytesNeeded())); + std::unique_ptr<SkStreamRewindable> bufferedStream(skia::FrontBufferedStream::Make( + std::move(stream), SkCodec::MinBufferedBytesNeeded())); SkASSERT(bufferedStream.get() != NULL); bitmap = doDecode(env, std::move(bufferedStream), padding, options, inBitmapHandle, colorSpaceHandle); @@ -565,8 +564,8 @@ static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fi // Use a buffered stream. Although an SkFILEStream can be rewound, this // ensures that SkImageDecoder::Factory never rewinds beyond the // current position of the file descriptor. - std::unique_ptr<SkStreamRewindable> stream(SkFrontBufferedStream::Make(std::move(fileStream), - SkCodec::MinBufferedBytesNeeded())); + std::unique_ptr<SkStreamRewindable> stream(skia::FrontBufferedStream::Make( + std::move(fileStream), SkCodec::MinBufferedBytesNeeded())); return doDecode(env, std::move(stream), padding, bitmapFactoryOptions, inBitmapHandle, colorSpaceHandle); diff --git a/libs/hwui/jni/BitmapRegionDecoder.cpp b/libs/hwui/jni/BitmapRegionDecoder.cpp index 712351382d97..4cc05ef6f13b 100644 --- a/libs/hwui/jni/BitmapRegionDecoder.cpp +++ b/libs/hwui/jni/BitmapRegionDecoder.cpp @@ -22,8 +22,8 @@ #include "GraphicsJNI.h" #include "Utils.h" +#include "BitmapRegionDecoder.h" #include "SkBitmap.h" -#include "SkBitmapRegionDecoder.h" #include "SkCodec.h" #include "SkData.h" #include "SkStream.h" @@ -36,10 +36,8 @@ using namespace android; -static jobject createBitmapRegionDecoder(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream) { - std::unique_ptr<SkBitmapRegionDecoder> brd( - SkBitmapRegionDecoder::Create(stream.release(), - SkBitmapRegionDecoder::kAndroidCodec_Strategy)); +static jobject createBitmapRegionDecoder(JNIEnv* env, sk_sp<SkData> data) { + auto brd = skia::BitmapRegionDecoder::Make(std::move(data)); if (!brd) { doThrowIOE(env, "Image format not supported"); return nullObjectReturn("CreateBitmapRegionDecoder returned null"); @@ -49,21 +47,13 @@ static jobject createBitmapRegionDecoder(JNIEnv* env, std::unique_ptr<SkStreamRe } static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray, - jint offset, jint length, jboolean isShareable) { - /* If isShareable we could decide to just wrap the java array and - share it, but that means adding a globalref to the java array object - For now we just always copy the array's data if isShareable. - */ + jint offset, jint length) { AutoJavaByteArray ar(env, byteArray); - std::unique_ptr<SkMemoryStream> stream(new SkMemoryStream(ar.ptr() + offset, length, true)); - - // the decoder owns the stream. - jobject brd = createBitmapRegionDecoder(env, std::move(stream)); - return brd; + return createBitmapRegionDecoder(env, SkData::MakeWithCopy(ar.ptr() + offset, length)); } static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz, - jobject fileDescriptor, jboolean isShareable) { + jobject fileDescriptor) { NPE_CHECK_RETURN_ZERO(env, fileDescriptor); jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); @@ -74,41 +64,28 @@ static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz, return nullObjectReturn("fstat return -1"); } - sk_sp<SkData> data(SkData::MakeFromFD(descriptor)); - std::unique_ptr<SkMemoryStream> stream(new SkMemoryStream(std::move(data))); - - // the decoder owns the stream. - jobject brd = createBitmapRegionDecoder(env, std::move(stream)); - return brd; + return createBitmapRegionDecoder(env, SkData::MakeFromFD(descriptor)); } -static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz, - jobject is, // InputStream - jbyteArray storage, // byte[] - jboolean isShareable) { - jobject brd = NULL; - // for now we don't allow shareable with java inputstreams - std::unique_ptr<SkStreamRewindable> stream(CopyJavaInputStream(env, is, storage)); - - if (stream) { - // the decoder owns the stream. - brd = createBitmapRegionDecoder(env, std::move(stream)); +static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz, jobject is, // InputStream + jbyteArray storage) { // byte[] + jobject brd = nullptr; + sk_sp<SkData> data = CopyJavaInputStream(env, is, storage); + + if (data) { + brd = createBitmapRegionDecoder(env, std::move(data)); } return brd; } -static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz, - jlong native_asset, // Asset - jboolean isShareable) { +static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz, jlong native_asset) { Asset* asset = reinterpret_cast<Asset*>(native_asset); - std::unique_ptr<SkMemoryStream> stream(CopyAssetToStream(asset)); - if (NULL == stream) { - return NULL; + sk_sp<SkData> data = CopyAssetToData(asset); + if (!data) { + return nullptr; } - // the decoder owns the stream. - jobject brd = createBitmapRegionDecoder(env, std::move(stream)); - return brd; + return createBitmapRegionDecoder(env, data); } /* @@ -158,7 +135,7 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in recycledBytes = recycledBitmap->getAllocationByteCount(); } - SkBitmapRegionDecoder* brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); + auto* brd = reinterpret_cast<skia::BitmapRegionDecoder*>(brdHandle); SkColorType decodeColorType = brd->computeOutputColorType(colorType); if (decodeColorType == kRGBA_F16_SkColorType && isHardware && !uirenderer::HardwareBitmapUploader::hasFP16Support()) { @@ -166,7 +143,7 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in } // Set up the pixel allocator - SkBRDAllocator* allocator = nullptr; + skia::BRDAllocator* allocator = nullptr; RecyclingClippingPixelAllocator recycleAlloc(recycledBitmap, recycledBytes); HeapAllocator heapAlloc; if (javaBitmap) { @@ -230,20 +207,17 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in } static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) { - SkBitmapRegionDecoder* brd = - reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); + auto* brd = reinterpret_cast<skia::BitmapRegionDecoder*>(brdHandle); return static_cast<jint>(brd->height()); } static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) { - SkBitmapRegionDecoder* brd = - reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); + auto* brd = reinterpret_cast<skia::BitmapRegionDecoder*>(brdHandle); return static_cast<jint>(brd->width()); } static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) { - SkBitmapRegionDecoder* brd = - reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); + auto* brd = reinterpret_cast<skia::BitmapRegionDecoder*>(brdHandle); delete brd; } @@ -261,22 +235,22 @@ static const JNINativeMethod gBitmapRegionDecoderMethods[] = { { "nativeClean", "(J)V", (void*)nativeClean}, { "nativeNewInstance", - "([BIIZ)Landroid/graphics/BitmapRegionDecoder;", + "([BII)Landroid/graphics/BitmapRegionDecoder;", (void*)nativeNewInstanceFromByteArray }, { "nativeNewInstance", - "(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;", + "(Ljava/io/InputStream;[B)Landroid/graphics/BitmapRegionDecoder;", (void*)nativeNewInstanceFromStream }, { "nativeNewInstance", - "(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;", + "(Ljava/io/FileDescriptor;)Landroid/graphics/BitmapRegionDecoder;", (void*)nativeNewInstanceFromFileDescriptor }, { "nativeNewInstance", - "(JZ)Landroid/graphics/BitmapRegionDecoder;", + "(J)Landroid/graphics/BitmapRegionDecoder;", (void*)nativeNewInstanceFromAsset }, }; diff --git a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp index f1c6b29204b2..785a5dc995ab 100644 --- a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp +++ b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp @@ -177,8 +177,12 @@ SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray s return JavaInputStreamAdaptor::Create(env, stream, storage, swallowExceptions); } -static SkMemoryStream* adaptor_to_mem_stream(SkStream* stream) { - SkASSERT(stream != NULL); +sk_sp<SkData> CopyJavaInputStream(JNIEnv* env, jobject inputStream, jbyteArray storage) { + std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, inputStream, storage)); + if (!stream) { + return nullptr; + } + size_t bufferSize = 4096; size_t streamLen = 0; size_t len; @@ -194,18 +198,7 @@ static SkMemoryStream* adaptor_to_mem_stream(SkStream* stream) { } data = (char*)sk_realloc_throw(data, streamLen); - SkMemoryStream* streamMem = new SkMemoryStream(); - streamMem->setMemoryOwned(data, streamLen); - return streamMem; -} - -SkStreamRewindable* CopyJavaInputStream(JNIEnv* env, jobject stream, - jbyteArray storage) { - std::unique_ptr<SkStream> adaptor(CreateJavaInputStreamAdaptor(env, stream, storage)); - if (NULL == adaptor.get()) { - return NULL; - } - return adaptor_to_mem_stream(adaptor.get()); + return SkData::MakeFromMalloc(data, streamLen); } /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.h b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.h index 849418da01a1..bae40f1e8d2f 100644 --- a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.h +++ b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.h @@ -2,6 +2,7 @@ #define _ANDROID_GRAPHICS_CREATE_JAVA_OUTPUT_STREAM_ADAPTOR_H_ #include "jni.h" +#include "SkData.h" class SkMemoryStream; class SkStream; @@ -27,15 +28,14 @@ SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray s bool swallowExceptions = true); /** - * Copy a Java InputStream. The result will be rewindable. + * Copy a Java InputStream to an SkData. * @param env JNIEnv object. * @param stream Pointer to Java InputStream. * @param storage Java byte array for retrieving data from the * Java InputStream. - * @return SkStreamRewindable The data in stream will be copied - * to a new SkStreamRewindable. + * @return SkData containing the stream's data. */ -SkStreamRewindable* CopyJavaInputStream(JNIEnv* env, jobject stream, jbyteArray storage); +sk_sp<SkData> CopyJavaInputStream(JNIEnv* env, jobject stream, jbyteArray storage); SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray storage); diff --git a/libs/hwui/jni/FontFamily.cpp b/libs/hwui/jni/FontFamily.cpp index a2fef1e19328..68eaa0a3ca54 100644 --- a/libs/hwui/jni/FontFamily.cpp +++ b/libs/hwui/jni/FontFamily.cpp @@ -104,21 +104,21 @@ static jlong FontFamily_getFamilyReleaseFunc(CRITICAL_JNI_PARAMS) { static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp<SkData>&& data, int ttcIndex, jint weight, jint italic) { - FatVector<SkFontArguments::Axis, 2> skiaAxes; + FatVector<SkFontArguments::VariationPosition::Coordinate, 2> skVariation; for (const auto& axis : builder->axes) { - skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value}); + skVariation.push_back({axis.axisTag, axis.value}); } const size_t fontSize = data->size(); const void* fontPtr = data->data(); std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data))); - SkFontArguments params; - params.setCollectionIndex(ttcIndex); - params.setAxes(skiaAxes.data(), skiaAxes.size()); + SkFontArguments args; + args.setCollectionIndex(ttcIndex); + args.setVariationDesignPosition({skVariation.data(), static_cast<int>(skVariation.size())}); sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); - sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), params)); + sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), args)); if (face == NULL) { ALOGE("addFont failed to create font, invalid request"); builder->axes.clear(); diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp index f76ecb4c9c8a..ecbb55ec878d 100644 --- a/libs/hwui/jni/Graphics.cpp +++ b/libs/hwui/jni/Graphics.cpp @@ -470,7 +470,7 @@ SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region) /////////////////////////////////////////////////////////////////////////////////////////// -jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap) +jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, skia::BitmapRegionDecoder* bitmap) { ALOG_ASSERT(bitmap != NULL); diff --git a/libs/hwui/jni/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h index b58a740a4c27..79ab617411e3 100644 --- a/libs/hwui/jni/GraphicsJNI.h +++ b/libs/hwui/jni/GraphicsJNI.h @@ -4,8 +4,8 @@ #include <cutils/compiler.h> #include "Bitmap.h" +#include "BRDAllocator.h" #include "SkBitmap.h" -#include "SkBRDAllocator.h" #include "SkCodec.h" #include "SkPixelRef.h" #include "SkMallocPixelRef.h" @@ -17,10 +17,12 @@ #include "graphics_jni_helpers.h" -class SkBitmapRegionDecoder; class SkCanvas; namespace android { +namespace skia { + class BitmapRegionDecoder; +} class Paint; struct Typeface; } @@ -103,7 +105,8 @@ public: static jobject createRegion(JNIEnv* env, SkRegion* region); - static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap); + static jobject createBitmapRegionDecoder(JNIEnv* env, + android::skia::BitmapRegionDecoder* bitmap); /** * Given a bitmap we natively allocate a memory block to store the contents @@ -154,7 +157,7 @@ private: static JavaVM* mJavaVM; }; -class HeapAllocator : public SkBRDAllocator { +class HeapAllocator : public android::skia::BRDAllocator { public: HeapAllocator() { }; ~HeapAllocator() { }; @@ -181,7 +184,7 @@ private: * the decoded output to fit in the recycled bitmap if necessary. * This allocator implements that behavior. * - * Skia's SkBitmapRegionDecoder expects the memory that + * Skia's BitmapRegionDecoder expects the memory that * is allocated to be large enough to decode the entire region * that is requested. It will decode directly into the memory * that is provided. @@ -200,7 +203,7 @@ private: * reuse it again, given that it still may be in use from our * first allocation. */ -class RecyclingClippingPixelAllocator : public SkBRDAllocator { +class RecyclingClippingPixelAllocator : public android::skia::BRDAllocator { public: RecyclingClippingPixelAllocator(android::Bitmap* recycledBitmap, diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp index 41d939bd6373..1f4fd230e55e 100644 --- a/libs/hwui/jni/ImageDecoder.cpp +++ b/libs/hwui/jni/ImageDecoder.cpp @@ -27,9 +27,9 @@ #include <hwui/ImageDecoder.h> #include <HardwareBitmapUploader.h> +#include <FrontBufferedStream.h> #include <SkAndroidCodec.h> #include <SkEncodedImageFormat.h> -#include <SkFrontBufferedStream.h> #include <SkStream.h> #include <androidfw/Asset.h> @@ -187,8 +187,7 @@ static jobject ImageDecoder_nCreateInputStream(JNIEnv* env, jobject /*clazz*/, } std::unique_ptr<SkStream> bufferedStream( - SkFrontBufferedStream::Make(std::move(stream), - SkCodec::MinBufferedBytesNeeded())); + skia::FrontBufferedStream::Make(std::move(stream), SkCodec::MinBufferedBytesNeeded())); return native_create(env, std::move(bufferedStream), source, preferAnimation); } diff --git a/libs/hwui/jni/Movie.cpp b/libs/hwui/jni/Movie.cpp index ede0ca8cda5b..bb8c99a73edf 100644 --- a/libs/hwui/jni/Movie.cpp +++ b/libs/hwui/jni/Movie.cpp @@ -1,7 +1,7 @@ #include "CreateJavaOutputStreamAdaptor.h" +#include "FrontBufferedStream.h" #include "GraphicsJNI.h" #include <nativehelper/ScopedLocalRef.h> -#include "SkFrontBufferedStream.h" #include "Movie.h" #include "SkStream.h" #include "SkUtils.h" @@ -100,10 +100,8 @@ static jobject movie_decodeStream(JNIEnv* env, jobject clazz, jobject istream) { // Need to buffer enough input to be able to rewind as much as might be read by a decoder // trying to determine the stream's format. The only decoder for movies is GIF, which // will only read 6. - // FIXME: Get this number from SkImageDecoder - // bufferedStream takes ownership of strm - std::unique_ptr<SkStreamRewindable> bufferedStream(SkFrontBufferedStream::Make( - std::unique_ptr<SkStream>(strm), 6)); + std::unique_ptr<SkStreamRewindable> bufferedStream( + android::skia::FrontBufferedStream::Make(std::unique_ptr<SkStream>(strm), 6)); SkASSERT(bufferedStream.get() != NULL); Movie* moov = Movie::DecodeStream(bufferedStream.get()); diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp index 0f6837640524..e76aace601be 100644 --- a/libs/hwui/jni/Shader.cpp +++ b/libs/hwui/jni/Shader.cpp @@ -228,9 +228,12 @@ static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderFactory, jlo static jlong RuntimeShader_createShaderFactory(JNIEnv* env, jobject, jstring sksl) { ScopedUtfChars strSksl(env, sksl); - sk_sp<SkRuntimeEffect> effect = std::get<0>(SkRuntimeEffect::Make(SkString(strSksl.c_str()))); - ThrowIAE_IfNull(env, effect); - + auto result = SkRuntimeEffect::Make(SkString(strSksl.c_str())); + sk_sp<SkRuntimeEffect> effect = std::get<0>(result); + if (!effect) { + const auto& err = std::get<1>(result); + doThrowIAE(env, err.c_str()); + } return reinterpret_cast<jlong>(effect.release()); } diff --git a/libs/hwui/jni/Utils.cpp b/libs/hwui/jni/Utils.cpp index 34fd6687d52c..ac2f5b77d23a 100644 --- a/libs/hwui/jni/Utils.cpp +++ b/libs/hwui/jni/Utils.cpp @@ -114,7 +114,7 @@ size_t AssetStreamAdaptor::read(void* buffer, size_t size) { return amount; } -SkMemoryStream* android::CopyAssetToStream(Asset* asset) { +sk_sp<SkData> android::CopyAssetToData(Asset* asset) { if (NULL == asset) { return NULL; } @@ -138,7 +138,7 @@ SkMemoryStream* android::CopyAssetToStream(Asset* asset) { return NULL; } - return new SkMemoryStream(std::move(data)); + return data; } jobject android::nullObjectReturn(const char msg[]) { diff --git a/libs/hwui/jni/Utils.h b/libs/hwui/jni/Utils.h index f628cc3c85ed..6cdf44d85a5a 100644 --- a/libs/hwui/jni/Utils.h +++ b/libs/hwui/jni/Utils.h @@ -46,12 +46,11 @@ private: }; /** - * Make a deep copy of the asset, and return it as a stream, or NULL if there + * Make a deep copy of the asset, and return it as an SkData, or NULL if there * was an error. - * FIXME: If we could "ref/reopen" the asset, we may not need to copy it here. */ -SkMemoryStream* CopyAssetToStream(Asset*); +sk_sp<SkData> CopyAssetToData(Asset*); /** Restore the file descriptor's offset in our destructor */ diff --git a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp index 54822f1f07e2..7c1422de0984 100644 --- a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp +++ b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp @@ -67,39 +67,7 @@ private: JavaVM* mVm; jobject mRunnable; }; - -class GlFunctorReleasedCallbackBridge : public GlFunctorLifecycleListener { -public: - GlFunctorReleasedCallbackBridge(JNIEnv* env, jobject javaCallback) { - mLooper = Looper::getForThread(); - mMessage = new InvokeRunnableMessage(env, javaCallback); - } - - virtual void onGlFunctorReleased(Functor* functor) override { - mLooper->sendMessage(mMessage, 0); - } - -private: - sp<Looper> mLooper; - sp<InvokeRunnableMessage> mMessage; -}; -#endif - -// ---------------- @FastNative ----------------------------- - -static void android_view_DisplayListCanvas_callDrawGLFunction(JNIEnv* env, jobject clazz, - jlong canvasPtr, jlong functorPtr, jobject releasedCallback) { -#ifdef __ANDROID__ // Layoutlib does not support GL - Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr); - Functor* functor = reinterpret_cast<Functor*>(functorPtr); - sp<GlFunctorReleasedCallbackBridge> bridge; - if (releasedCallback) { - bridge = new GlFunctorReleasedCallbackBridge(env, releasedCallback); - } - canvas->callDrawGLFunction(functor, bridge.get()); #endif -} - // ---------------- @CriticalNative ------------------------- @@ -124,10 +92,10 @@ static jint android_view_DisplayListCanvas_getMaxTextureSize(CRITICAL_JNI_PARAMS #endif } -static void android_view_DisplayListCanvas_insertReorderBarrier(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, +static void android_view_DisplayListCanvas_enableZ(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jboolean reorderEnable) { Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr); - canvas->insertReorderBarrier(reorderEnable); + canvas->enableZ(reorderEnable); } static jlong android_view_DisplayListCanvas_finishRecording(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr) { @@ -183,18 +151,12 @@ static void android_view_DisplayListCanvas_drawWebViewFunctor(CRITICAL_JNI_PARAM const char* const kClassPathName = "android/graphics/RecordingCanvas"; static JNINativeMethod gMethods[] = { - - // ------------ @FastNative ------------------ - - { "nCallDrawGLFunction", "(JJLjava/lang/Runnable;)V", - (void*) android_view_DisplayListCanvas_callDrawGLFunction }, - // ------------ @CriticalNative -------------- { "nCreateDisplayListCanvas", "(JII)J", (void*) android_view_DisplayListCanvas_createDisplayListCanvas }, { "nResetDisplayListCanvas", "(JJII)V", (void*) android_view_DisplayListCanvas_resetDisplayListCanvas }, { "nGetMaximumTextureWidth", "()I", (void*) android_view_DisplayListCanvas_getMaxTextureSize }, { "nGetMaximumTextureHeight", "()I", (void*) android_view_DisplayListCanvas_getMaxTextureSize }, - { "nInsertReorderBarrier", "(JZ)V", (void*) android_view_DisplayListCanvas_insertReorderBarrier }, + { "nEnableZ", "(JZ)V", (void*) android_view_DisplayListCanvas_enableZ }, { "nFinishRecording", "(J)J", (void*) android_view_DisplayListCanvas_finishRecording }, { "nDrawRenderNode", "(JJ)V", (void*) android_view_DisplayListCanvas_drawRenderNode }, { "nDrawTextureLayer", "(JJ)V", (void*) android_view_DisplayListCanvas_drawTextureLayer }, diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index 9815e85db880..fc594da19708 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -143,11 +143,10 @@ static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, job } static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz, - jboolean translucent, jboolean isWideGamut, jlong rootRenderNodePtr) { + jboolean translucent, jlong rootRenderNodePtr) { RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootRenderNodePtr); ContextFactoryImpl factory(rootRenderNode); RenderProxy* proxy = new RenderProxy(translucent, rootRenderNode, &factory); - proxy->setWideGamut(isWideGamut); return (jlong) proxy; } @@ -218,10 +217,15 @@ static void android_view_ThreadedRenderer_setOpaque(JNIEnv* env, jobject clazz, proxy->setOpaque(opaque); } -static void android_view_ThreadedRenderer_setWideGamut(JNIEnv* env, jobject clazz, - jlong proxyPtr, jboolean wideGamut) { +static void android_view_ThreadedRenderer_setColorMode(JNIEnv* env, jobject clazz, + jlong proxyPtr, jint colorMode) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); - proxy->setWideGamut(wideGamut); + proxy->setColorMode(static_cast<ColorMode>(colorMode)); +} + +static void android_view_ThreadedRenderer_setSdrWhitePoint(JNIEnv* env, jobject clazz, + jlong proxyPtr, jfloat sdrWhitePoint) { + Properties::defaultSdrWhitePoint = sdrWhitePoint; } static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz, @@ -256,12 +260,6 @@ static void android_view_ThreadedRenderer_registerVectorDrawableAnimator(JNIEnv* rootRenderNode->addVectorDrawableAnimator(animator); } -static void android_view_ThreadedRenderer_invokeFunctor(JNIEnv* env, jobject clazz, - jlong functorPtr, jboolean waitForCompletion) { - Functor* functor = reinterpret_cast<Functor*>(functorPtr); - RenderProxy::invokeFunctor(functor, waitForCompletion); -} - static jlong android_view_ThreadedRenderer_createTextureLayer(JNIEnv* env, jobject clazz, jlong proxyPtr) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); @@ -593,6 +591,28 @@ static void android_view_ThreadedRenderer_preload(JNIEnv*, jclass) { RenderProxy::preload(); } +// Plumbs the display density down to DeviceInfo. +static void android_view_ThreadedRenderer_setDisplayDensityDpi(JNIEnv*, jclass, jint densityDpi) { + // Convert from dpi to density-independent pixels. + const float density = densityDpi / 160.0; + DeviceInfo::setDensity(density); +} + +static void android_view_ThreadedRenderer_initDisplayInfo(JNIEnv*, jclass, jint physicalWidth, + jint physicalHeight, jfloat refreshRate, + jfloat maxRefreshRate, + jint wideColorDataspace, + jlong appVsyncOffsetNanos, + jlong presentationDeadlineNanos) { + DeviceInfo::setWidth(physicalWidth); + DeviceInfo::setHeight(physicalHeight); + DeviceInfo::setRefreshRate(refreshRate); + DeviceInfo::setMaxRefreshRate(maxRefreshRate); + DeviceInfo::setWideColorDataspace(static_cast<ADataSpace>(wideColorDataspace)); + DeviceInfo::setAppVsyncOffsetNanos(appVsyncOffsetNanos); + DeviceInfo::setPresentationDeadlineNanos(presentationDeadlineNanos); +} + // ---------------------------------------------------------------------------- // HardwareRendererObserver // ---------------------------------------------------------------------------- @@ -637,67 +657,83 @@ static void android_view_ThreadedRenderer_setupShadersDiskCache(JNIEnv* env, job const char* const kClassPathName = "android/graphics/HardwareRenderer"; static const JNINativeMethod gMethods[] = { - { "nRotateProcessStatsBuffer", "()V", (void*) android_view_ThreadedRenderer_rotateProcessStatsBuffer }, - { "nSetProcessStatsBuffer", "(I)V", (void*) android_view_ThreadedRenderer_setProcessStatsBuffer }, - { "nGetRenderThreadTid", "(J)I", (void*) android_view_ThreadedRenderer_getRenderThreadTid }, - { "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode }, - { "nCreateProxy", "(ZZJ)J", (void*) android_view_ThreadedRenderer_createProxy }, - { "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy }, - { "nLoadSystemProperties", "(J)Z", (void*) android_view_ThreadedRenderer_loadSystemProperties }, - { "nSetName", "(JLjava/lang/String;)V", (void*) android_view_ThreadedRenderer_setName }, - { "nSetSurface", "(JLandroid/view/Surface;Z)V", (void*) android_view_ThreadedRenderer_setSurface }, - { "nPause", "(J)Z", (void*) android_view_ThreadedRenderer_pause }, - { "nSetStopped", "(JZ)V", (void*) android_view_ThreadedRenderer_setStopped }, - { "nSetLightAlpha", "(JFF)V", (void*) android_view_ThreadedRenderer_setLightAlpha }, - { "nSetLightGeometry", "(JFFFF)V", (void*) android_view_ThreadedRenderer_setLightGeometry }, - { "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque }, - { "nSetWideGamut", "(JZ)V", (void*) android_view_ThreadedRenderer_setWideGamut }, - { "nSyncAndDrawFrame", "(J[JI)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame }, - { "nDestroy", "(JJ)V", (void*) android_view_ThreadedRenderer_destroy }, - { "nRegisterAnimatingRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_registerAnimatingRenderNode }, - { "nRegisterVectorDrawableAnimator", "(JJ)V", (void*) android_view_ThreadedRenderer_registerVectorDrawableAnimator }, - { "nInvokeFunctor", "(JZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor }, - { "nCreateTextureLayer", "(J)J", (void*) android_view_ThreadedRenderer_createTextureLayer }, - { "nBuildLayer", "(JJ)V", (void*) android_view_ThreadedRenderer_buildLayer }, - { "nCopyLayerInto", "(JJJ)Z", (void*) android_view_ThreadedRenderer_copyLayerInto }, - { "nPushLayerUpdate", "(JJ)V", (void*) android_view_ThreadedRenderer_pushLayerUpdate }, - { "nCancelLayerUpdate", "(JJ)V", (void*) android_view_ThreadedRenderer_cancelLayerUpdate }, - { "nDetachSurfaceTexture", "(JJ)V", (void*) android_view_ThreadedRenderer_detachSurfaceTexture }, - { "nDestroyHardwareResources", "(J)V", (void*) android_view_ThreadedRenderer_destroyHardwareResources }, - { "nTrimMemory", "(I)V", (void*) android_view_ThreadedRenderer_trimMemory }, - { "nOverrideProperty", "(Ljava/lang/String;Ljava/lang/String;)V", (void*) android_view_ThreadedRenderer_overrideProperty }, - { "nFence", "(J)V", (void*) android_view_ThreadedRenderer_fence }, - { "nStopDrawing", "(J)V", (void*) android_view_ThreadedRenderer_stopDrawing }, - { "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending }, - { "nDumpProfileInfo", "(JLjava/io/FileDescriptor;I)V", (void*) android_view_ThreadedRenderer_dumpProfileInfo }, - { "setupShadersDiskCache", "(Ljava/lang/String;Ljava/lang/String;)V", - (void*) android_view_ThreadedRenderer_setupShadersDiskCache }, - { "nAddRenderNode", "(JJZ)V", (void*) android_view_ThreadedRenderer_addRenderNode}, - { "nRemoveRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_removeRenderNode}, - { "nDrawRenderNode", "(JJ)V", (void*) android_view_ThreadedRendererd_drawRenderNode}, - { "nSetContentDrawBounds", "(JIIII)V", (void*)android_view_ThreadedRenderer_setContentDrawBounds}, - { "nSetPictureCaptureCallback", "(JLandroid/graphics/HardwareRenderer$PictureCapturedCallback;)V", - (void*) android_view_ThreadedRenderer_setPictureCapturedCallbackJNI }, - { "nSetFrameCallback", "(JLandroid/graphics/HardwareRenderer$FrameDrawingCallback;)V", - (void*)android_view_ThreadedRenderer_setFrameCallback}, - { "nSetFrameCompleteCallback", "(JLandroid/graphics/HardwareRenderer$FrameCompleteCallback;)V", - (void*)android_view_ThreadedRenderer_setFrameCompleteCallback }, - { "nAddObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_addObserver }, - { "nRemoveObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_removeObserver }, - { "nCopySurfaceInto", "(Landroid/view/Surface;IIIIJ)I", - (void*)android_view_ThreadedRenderer_copySurfaceInto }, - { "nCreateHardwareBitmap", "(JII)Landroid/graphics/Bitmap;", - (void*)android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode }, - { "disableVsync", "()V", (void*)android_view_ThreadedRenderer_disableVsync }, - { "nSetHighContrastText", "(Z)V", (void*)android_view_ThreadedRenderer_setHighContrastText }, - { "nHackySetRTAnimationsEnabled", "(Z)V", - (void*)android_view_ThreadedRenderer_hackySetRTAnimationsEnabled }, - { "nSetDebuggingEnabled", "(Z)V", (void*)android_view_ThreadedRenderer_setDebuggingEnabled }, - { "nSetIsolatedProcess", "(Z)V", (void*)android_view_ThreadedRenderer_setIsolatedProcess }, - { "nSetContextPriority", "(I)V", (void*)android_view_ThreadedRenderer_setContextPriority }, - { "nAllocateBuffers", "(J)V", (void*)android_view_ThreadedRenderer_allocateBuffers }, - { "nSetForceDark", "(JZ)V", (void*)android_view_ThreadedRenderer_setForceDark }, - { "preload", "()V", (void*)android_view_ThreadedRenderer_preload }, + {"nRotateProcessStatsBuffer", "()V", + (void*)android_view_ThreadedRenderer_rotateProcessStatsBuffer}, + {"nSetProcessStatsBuffer", "(I)V", + (void*)android_view_ThreadedRenderer_setProcessStatsBuffer}, + {"nGetRenderThreadTid", "(J)I", (void*)android_view_ThreadedRenderer_getRenderThreadTid}, + {"nCreateRootRenderNode", "()J", (void*)android_view_ThreadedRenderer_createRootRenderNode}, + {"nCreateProxy", "(ZJ)J", (void*)android_view_ThreadedRenderer_createProxy}, + {"nDeleteProxy", "(J)V", (void*)android_view_ThreadedRenderer_deleteProxy}, + {"nLoadSystemProperties", "(J)Z", + (void*)android_view_ThreadedRenderer_loadSystemProperties}, + {"nSetName", "(JLjava/lang/String;)V", (void*)android_view_ThreadedRenderer_setName}, + {"nSetSurface", "(JLandroid/view/Surface;Z)V", + (void*)android_view_ThreadedRenderer_setSurface}, + {"nPause", "(J)Z", (void*)android_view_ThreadedRenderer_pause}, + {"nSetStopped", "(JZ)V", (void*)android_view_ThreadedRenderer_setStopped}, + {"nSetLightAlpha", "(JFF)V", (void*)android_view_ThreadedRenderer_setLightAlpha}, + {"nSetLightGeometry", "(JFFFF)V", (void*)android_view_ThreadedRenderer_setLightGeometry}, + {"nSetOpaque", "(JZ)V", (void*)android_view_ThreadedRenderer_setOpaque}, + {"nSetColorMode", "(JI)V", (void*)android_view_ThreadedRenderer_setColorMode}, + {"nSetSdrWhitePoint", "(JF)V", (void*)android_view_ThreadedRenderer_setSdrWhitePoint}, + {"nSyncAndDrawFrame", "(J[JI)I", (void*)android_view_ThreadedRenderer_syncAndDrawFrame}, + {"nDestroy", "(JJ)V", (void*)android_view_ThreadedRenderer_destroy}, + {"nRegisterAnimatingRenderNode", "(JJ)V", + (void*)android_view_ThreadedRenderer_registerAnimatingRenderNode}, + {"nRegisterVectorDrawableAnimator", "(JJ)V", + (void*)android_view_ThreadedRenderer_registerVectorDrawableAnimator}, + {"nCreateTextureLayer", "(J)J", (void*)android_view_ThreadedRenderer_createTextureLayer}, + {"nBuildLayer", "(JJ)V", (void*)android_view_ThreadedRenderer_buildLayer}, + {"nCopyLayerInto", "(JJJ)Z", (void*)android_view_ThreadedRenderer_copyLayerInto}, + {"nPushLayerUpdate", "(JJ)V", (void*)android_view_ThreadedRenderer_pushLayerUpdate}, + {"nCancelLayerUpdate", "(JJ)V", (void*)android_view_ThreadedRenderer_cancelLayerUpdate}, + {"nDetachSurfaceTexture", "(JJ)V", + (void*)android_view_ThreadedRenderer_detachSurfaceTexture}, + {"nDestroyHardwareResources", "(J)V", + (void*)android_view_ThreadedRenderer_destroyHardwareResources}, + {"nTrimMemory", "(I)V", (void*)android_view_ThreadedRenderer_trimMemory}, + {"nOverrideProperty", "(Ljava/lang/String;Ljava/lang/String;)V", + (void*)android_view_ThreadedRenderer_overrideProperty}, + {"nFence", "(J)V", (void*)android_view_ThreadedRenderer_fence}, + {"nStopDrawing", "(J)V", (void*)android_view_ThreadedRenderer_stopDrawing}, + {"nNotifyFramePending", "(J)V", (void*)android_view_ThreadedRenderer_notifyFramePending}, + {"nDumpProfileInfo", "(JLjava/io/FileDescriptor;I)V", + (void*)android_view_ThreadedRenderer_dumpProfileInfo}, + {"setupShadersDiskCache", "(Ljava/lang/String;Ljava/lang/String;)V", + (void*)android_view_ThreadedRenderer_setupShadersDiskCache}, + {"nAddRenderNode", "(JJZ)V", (void*)android_view_ThreadedRenderer_addRenderNode}, + {"nRemoveRenderNode", "(JJ)V", (void*)android_view_ThreadedRenderer_removeRenderNode}, + {"nDrawRenderNode", "(JJ)V", (void*)android_view_ThreadedRendererd_drawRenderNode}, + {"nSetContentDrawBounds", "(JIIII)V", + (void*)android_view_ThreadedRenderer_setContentDrawBounds}, + {"nSetPictureCaptureCallback", + "(JLandroid/graphics/HardwareRenderer$PictureCapturedCallback;)V", + (void*)android_view_ThreadedRenderer_setPictureCapturedCallbackJNI}, + {"nSetFrameCallback", "(JLandroid/graphics/HardwareRenderer$FrameDrawingCallback;)V", + (void*)android_view_ThreadedRenderer_setFrameCallback}, + {"nSetFrameCompleteCallback", + "(JLandroid/graphics/HardwareRenderer$FrameCompleteCallback;)V", + (void*)android_view_ThreadedRenderer_setFrameCompleteCallback}, + {"nAddObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_addObserver}, + {"nRemoveObserver", "(JJ)V", (void*)android_view_ThreadedRenderer_removeObserver}, + {"nCopySurfaceInto", "(Landroid/view/Surface;IIIIJ)I", + (void*)android_view_ThreadedRenderer_copySurfaceInto}, + {"nCreateHardwareBitmap", "(JII)Landroid/graphics/Bitmap;", + (void*)android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode}, + {"disableVsync", "()V", (void*)android_view_ThreadedRenderer_disableVsync}, + {"nSetHighContrastText", "(Z)V", (void*)android_view_ThreadedRenderer_setHighContrastText}, + {"nHackySetRTAnimationsEnabled", "(Z)V", + (void*)android_view_ThreadedRenderer_hackySetRTAnimationsEnabled}, + {"nSetDebuggingEnabled", "(Z)V", (void*)android_view_ThreadedRenderer_setDebuggingEnabled}, + {"nSetIsolatedProcess", "(Z)V", (void*)android_view_ThreadedRenderer_setIsolatedProcess}, + {"nSetContextPriority", "(I)V", (void*)android_view_ThreadedRenderer_setContextPriority}, + {"nAllocateBuffers", "(J)V", (void*)android_view_ThreadedRenderer_allocateBuffers}, + {"nSetForceDark", "(JZ)V", (void*)android_view_ThreadedRenderer_setForceDark}, + {"nSetDisplayDensityDpi", "(I)V", + (void*)android_view_ThreadedRenderer_setDisplayDensityDpi}, + {"nInitDisplayInfo", "(IIFFIJJ)V", (void*)android_view_ThreadedRenderer_initDisplayInfo}, + {"preload", "()V", (void*)android_view_ThreadedRenderer_preload}, }; static JavaVM* mJvm = nullptr; diff --git a/libs/hwui/jni/android_graphics_TextureLayer.cpp b/libs/hwui/jni/android_graphics_TextureLayer.cpp index bd20269d3751..4dbb24ce4347 100644 --- a/libs/hwui/jni/android_graphics_TextureLayer.cpp +++ b/libs/hwui/jni/android_graphics_TextureLayer.cpp @@ -67,7 +67,7 @@ static void TextureLayer_updateSurfaceTexture(JNIEnv* env, jobject clazz, // JNI Glue // ---------------------------------------------------------------------------- -const char* const kClassPathName = "android/view/TextureLayer"; +const char* const kClassPathName = "android/graphics/TextureLayer"; static const JNINativeMethod gMethods[] = { { "nPrepare", "(JIIZ)Z", (void*) TextureLayer_prepare }, @@ -78,7 +78,7 @@ static const JNINativeMethod gMethods[] = { { "nUpdateSurfaceTexture", "(J)V", (void*) TextureLayer_updateSurfaceTexture }, }; -int register_android_view_TextureLayer(JNIEnv* env) { +int register_android_graphics_TextureLayer(JNIEnv* env) { return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); } diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp index 5714cd1d0390..996cdceed8a7 100644 --- a/libs/hwui/jni/fonts/Font.cpp +++ b/libs/hwui/jni/fonts/Font.cpp @@ -93,19 +93,19 @@ static jlong Font_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr, jo sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize, release_global_ref, reinterpret_cast<void*>(fontRef))); - FatVector<SkFontArguments::Axis, 2> skiaAxes; + FatVector<SkFontArguments::VariationPosition::Coordinate, 2> skVariation; for (const auto& axis : builder->axes) { - skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value}); + skVariation.push_back({axis.axisTag, axis.value}); } std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data))); - SkFontArguments params; - params.setCollectionIndex(ttcIndex); - params.setAxes(skiaAxes.data(), skiaAxes.size()); + SkFontArguments args; + args.setCollectionIndex(ttcIndex); + args.setVariationDesignPosition({skVariation.data(), static_cast<int>(skVariation.size())}); sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); - sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), params)); + sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), args)); if (face == nullptr) { jniThrowException(env, "java/lang/IllegalArgumentException", "Failed to create internal object. maybe invalid font data."); diff --git a/libs/hwui/libhwui.map.txt b/libs/hwui/libhwui.map.txt new file mode 100644 index 000000000000..73de0d12a60b --- /dev/null +++ b/libs/hwui/libhwui.map.txt @@ -0,0 +1,70 @@ +LIBHWUI { + global: + /* listing of all C APIs to be exposed by libhwui to consumers outside of the module */ + ABitmap_getInfoFromJava; + ABitmap_acquireBitmapFromJava; + ABitmap_copy; + ABitmap_acquireRef; + ABitmap_releaseRef; + ABitmap_getInfo; + ABitmap_getDataSpace; + ABitmap_getPixels; + ABitmap_notifyPixelsChanged; + ABitmapConfig_getFormatFromConfig; + ABitmapConfig_getConfigFromFormat; + ABitmap_compress; + ABitmap_getHardwareBuffer; + ACanvas_isSupportedPixelFormat; + ACanvas_getNativeHandleFromJava; + ACanvas_createCanvas; + ACanvas_destroyCanvas; + ACanvas_setBuffer; + ACanvas_clipRect; + ACanvas_clipOutRect; + ACanvas_drawRect; + ACanvas_drawBitmap; + init_android_graphics; + register_android_graphics_classes; + register_android_graphics_GraphicsStatsService; + zygote_preload_graphics; + AMatrix_getContents; + APaint_createPaint; + APaint_destroyPaint; + APaint_setBlendMode; + ARegionIterator_acquireIterator; + ARegionIterator_releaseIterator; + ARegionIterator_isComplex; + ARegionIterator_isDone; + ARegionIterator_next; + ARegionIterator_getRect; + ARegionIterator_getTotalBounds; + ARenderThread_dumpGraphicsMemory; + local: + *; +}; + +LIBHWUI_PLATFORM { + global: + extern "C++" { + /* required by libwebviewchromium_plat_support */ + android::uirenderer::ColorSpaceToADataSpace*; + android::uirenderer::WebViewFunctor_*; + GraphicsJNI::getNativeCanvas*; + SkCanvasStateUtils::ReleaseCanvasState*; + SkColorSpace::toXYZD50*; + SkColorSpace::transferFn*; + /* required by libjnigraphics */ + android::ImageDecoder::*; + android::uirenderer::DataSpaceToColorSpace*; + android::uirenderer::ColorSpaceToADataSpace*; + getMimeType*; + SkAndroidCodec::*; + SkCodec::MakeFromStream*; + SkColorInfo::*; + SkFILEStream::SkFILEStream*; + SkImageInfo::*; + SkMemoryStream::SkMemoryStream*; + }; + local: + *; +}; diff --git a/libs/hwui/pipeline/skia/FunctorDrawable.h b/libs/hwui/pipeline/skia/FunctorDrawable.h index cf2f93b95e71..988a896b6267 100644 --- a/libs/hwui/pipeline/skia/FunctorDrawable.h +++ b/libs/hwui/pipeline/skia/FunctorDrawable.h @@ -16,8 +16,6 @@ #pragma once -#include "GlFunctorLifecycleListener.h" - #include <SkCanvas.h> #include <SkDrawable.h> @@ -36,44 +34,21 @@ namespace skiapipeline { */ class FunctorDrawable : public SkDrawable { public: - FunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas) - : mBounds(canvas->getLocalClipBounds()) - , mAnyFunctor(std::in_place_type<LegacyFunctor>, functor, listener) {} - FunctorDrawable(int functor, SkCanvas* canvas) : mBounds(canvas->getLocalClipBounds()) - , mAnyFunctor(std::in_place_type<NewFunctor>, functor) {} + , mWebViewHandle(WebViewFunctorManager::instance().handleFor(functor)) {} virtual ~FunctorDrawable() {} virtual void syncFunctor(const WebViewSyncData& data) const { - if (mAnyFunctor.index() == 0) { - std::get<0>(mAnyFunctor).handle->sync(data); - } else { - (*(std::get<1>(mAnyFunctor).functor))(DrawGlInfo::kModeSync, nullptr); - } + mWebViewHandle->sync(data); } protected: virtual SkRect onGetBounds() override { return mBounds; } const SkRect mBounds; - - struct LegacyFunctor { - explicit LegacyFunctor(Functor* functor, GlFunctorLifecycleListener* listener) - : functor(functor), listener(listener) {} - Functor* functor; - sp<GlFunctorLifecycleListener> listener; - }; - - struct NewFunctor { - explicit NewFunctor(int functor) { - handle = WebViewFunctorManager::instance().handleFor(functor); - } - sp<WebViewFunctor::Handle> handle; - }; - - std::variant<NewFunctor, LegacyFunctor> mAnyFunctor; + sp<WebViewFunctor::Handle> mWebViewHandle; }; } // namespace skiapipeline diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp index 8f67f97fb4bc..dd0fc695c246 100644 --- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp @@ -15,10 +15,9 @@ */ #include "GLFunctorDrawable.h" -#include <GrContext.h> +#include <GrDirectContext.h> #include <private/hwui/DrawGlInfo.h> #include "FunctorDrawable.h" -#include "GlFunctorLifecycleListener.h" #include "GrBackendSurface.h" #include "GrRenderTarget.h" #include "GrRenderTargetContext.h" @@ -26,20 +25,12 @@ #include "SkAndroidFrameworkUtils.h" #include "SkClipStack.h" #include "SkRect.h" -#include "include/private/SkM44.h" +#include "SkM44.h" namespace android { namespace uirenderer { namespace skiapipeline { -GLFunctorDrawable::~GLFunctorDrawable() { - if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) { - if (lp->listener) { - lp->listener->onGlFunctorReleased(lp->functor); - } - } -} - static void setScissor(int viewportHeight, const SkIRect& clip) { SkASSERT(!clip.isEmpty()); // transform to Y-flipped GL space, and prevent negatives @@ -85,7 +76,7 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { SkIRect surfaceBounds = canvas->internal_private_getTopLayerBounds(); SkIRect clipBounds = canvas->getDeviceClipBounds(); - SkM44 mat4(canvas->experimental_getLocalToDevice()); + SkM44 mat4(canvas->getLocalToDevice()); SkRegion clipRegion; canvas->temporary_internal_getRgnClip(&clipRegion); @@ -186,11 +177,7 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { setScissor(info.height, clipRegion.getBounds()); } - if (mAnyFunctor.index() == 0) { - std::get<0>(mAnyFunctor).handle->drawGl(info); - } else { - (*(std::get<1>(mAnyFunctor).functor))(DrawGlInfo::kModeDraw, &info); - } + mWebViewHandle->drawGl(info); if (clearStencilAfterFunctor) { // clear stencil buffer as it may be used by Skia diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.h b/libs/hwui/pipeline/skia/GLFunctorDrawable.h index 2ea4f67428bc..4092e8dfa3a5 100644 --- a/libs/hwui/pipeline/skia/GLFunctorDrawable.h +++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.h @@ -33,7 +33,7 @@ class GLFunctorDrawable : public FunctorDrawable { public: using FunctorDrawable::FunctorDrawable; - virtual ~GLFunctorDrawable(); + virtual ~GLFunctorDrawable() {} protected: void onDraw(SkCanvas* canvas) override; diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp index f839213e9007..f95f347cffaf 100644 --- a/libs/hwui/pipeline/skia/LayerDrawable.cpp +++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp @@ -29,7 +29,7 @@ namespace skiapipeline { void LayerDrawable::onDraw(SkCanvas* canvas) { Layer* layer = mLayerUpdater->backingLayer(); if (layer) { - DrawLayer(canvas->getGrContext(), canvas, layer, nullptr, nullptr, true); + DrawLayer(canvas->recordingContext(), canvas, layer, nullptr, nullptr, true); } } @@ -67,8 +67,12 @@ static bool shouldFilterRect(const SkMatrix& matrix, const SkRect& srcRect, cons isIntegerAligned(dstDevRect.y())); } -bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer, - const SkRect* srcRect, const SkRect* dstRect, +// TODO: Context arg probably doesn't belong here – do debug check at callsite instead. +bool LayerDrawable::DrawLayer(GrRecordingContext* context, + SkCanvas* canvas, + Layer* layer, + const SkRect* srcRect, + const SkRect* dstRect, bool useLayerTransform) { if (context == nullptr) { SkDEBUGF(("Attempting to draw LayerDrawable into an unsupported surface")); diff --git a/libs/hwui/pipeline/skia/LayerDrawable.h b/libs/hwui/pipeline/skia/LayerDrawable.h index 7cd515ae9fcb..ffbb480023ac 100644 --- a/libs/hwui/pipeline/skia/LayerDrawable.h +++ b/libs/hwui/pipeline/skia/LayerDrawable.h @@ -32,8 +32,12 @@ class LayerDrawable : public SkDrawable { public: explicit LayerDrawable(DeferredLayerUpdater* layerUpdater) : mLayerUpdater(layerUpdater) {} - static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer, const SkRect* srcRect, - const SkRect* dstRect, bool useLayerTransform); + static bool DrawLayer(GrRecordingContext* context, + SkCanvas* canvas, + Layer* layer, + const SkRect* srcRect, + const SkRect* dstRect, + bool useLayerTransform); protected: virtual SkRect onGetBounds() override { diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h index 0898017d52a1..5b8e668a56f4 100644 --- a/libs/hwui/pipeline/skia/ShaderCache.h +++ b/libs/hwui/pipeline/skia/ShaderCache.h @@ -37,7 +37,7 @@ public: * "get" returns a pointer to the singleton ShaderCache object. This * singleton object will never be destroyed. */ - ANDROID_API static ShaderCache& get(); + static ShaderCache& get(); /** * initShaderDiskCache" loads the serialized cache contents from disk, diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index 24a6228242a5..389fe7eed7c7 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -87,6 +87,8 @@ bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, con // Note: The default preference of pixel format is RGBA_8888, when other // pixel format is available, we should branch out and do more check. fboInfo.fFormat = GL_RGBA8; + } else if (colorType == kRGBA_1010102_SkColorType) { + fboInfo.fFormat = GL_RGB10_A2; } else { LOG_ALWAYS_FATAL("Unsupported color type."); } diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 5088494d6a07..6dd36981e8aa 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -35,6 +35,7 @@ #include "VectorDrawable.h" #include "thread/CommonPool.h" #include "tools/SkSharingProc.h" +#include "utils/Color.h" #include "utils/String8.h" #include "utils/TraceUtils.h" @@ -145,7 +146,7 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) if (cachedContext.get() != currentContext) { if (cachedContext.get()) { ATRACE_NAME("flush layers (context changed)"); - cachedContext->flush(); + cachedContext->flushAndSubmit(); } cachedContext.reset(SkSafeRef(currentContext)); } @@ -153,7 +154,7 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) if (cachedContext.get()) { ATRACE_NAME("flush layers"); - cachedContext->flush(); + cachedContext->flushAndSubmit(); } } @@ -450,7 +451,7 @@ void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& cli } ATRACE_NAME("flush commands"); - surface->getCanvas()->flush(); + surface->flushAndSubmit(); Properties::skpCaptureEnabled = previousSkpEnabled; } @@ -587,14 +588,23 @@ void SkiaPipeline::dumpResourceCacheUsage() const { void SkiaPipeline::setSurfaceColorProperties(ColorMode colorMode) { mColorMode = colorMode; - if (colorMode == ColorMode::SRGB) { - mSurfaceColorType = SkColorType::kN32_SkColorType; - mSurfaceColorSpace = SkColorSpace::MakeSRGB(); - } else if (colorMode == ColorMode::WideColorGamut) { - mSurfaceColorType = DeviceInfo::get()->getWideColorType(); - mSurfaceColorSpace = DeviceInfo::get()->getWideColorSpace(); - } else { - LOG_ALWAYS_FATAL("Unreachable: unsupported color mode."); + switch (colorMode) { + case ColorMode::Default: + mSurfaceColorType = SkColorType::kN32_SkColorType; + mSurfaceColorSpace = SkColorSpace::MakeSRGB(); + break; + case ColorMode::WideColorGamut: + mSurfaceColorType = DeviceInfo::get()->getWideColorType(); + mSurfaceColorSpace = DeviceInfo::get()->getWideColorSpace(); + break; + case ColorMode::Hdr: + mSurfaceColorType = SkColorType::kRGBA_F16_SkColorType; + mSurfaceColorSpace = SkColorSpace::MakeRGB(GetPQSkTransferFunction(), SkNamedGamut::kRec2020); + break; + case ColorMode::Hdr10: + mSurfaceColorType = SkColorType::kRGBA_1010102_SkColorType; + mSurfaceColorSpace = SkColorSpace::MakeRGB(GetPQSkTransferFunction(), SkNamedGamut::kRec2020); + break; } } diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index 8341164edc19..100bfb6b159a 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -50,7 +50,7 @@ public: bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator, ErrorHandler* errorHandler) override; - void setSurfaceColorProperties(renderthread::ColorMode colorMode) override; + void setSurfaceColorProperties(ColorMode colorMode) override; SkColorType getSurfaceColorType() const override { return mSurfaceColorType; } sk_sp<SkColorSpace> getSurfaceColorSpace() override { return mSurfaceColorSpace; } @@ -76,7 +76,7 @@ protected: renderthread::RenderThread& mRenderThread; - renderthread::ColorMode mColorMode = renderthread::ColorMode::SRGB; + ColorMode mColorMode = ColorMode::Default; SkColorType mSurfaceColorType; sk_sp<SkColorSpace> mSurfaceColorSpace; diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index d67cf8c9c73f..e292cbdd101f 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -57,7 +57,7 @@ void SkiaRecordingCanvas::initDisplayList(uirenderer::RenderNode* renderNode, in uirenderer::DisplayList* SkiaRecordingCanvas::finishRecording() { // close any existing chunks if necessary - insertReorderBarrier(false); + enableZ(false); mRecorder.restoreToCount(1); return mDisplayList.release(); } @@ -85,8 +85,8 @@ void SkiaRecordingCanvas::drawCircle(uirenderer::CanvasPropertyPrimitive* x, drawDrawable(mDisplayList->allocateDrawable<AnimatedCircle>(x, y, radius, paint)); } -void SkiaRecordingCanvas::insertReorderBarrier(bool enableReorder) { - if (mCurrentBarrier && enableReorder) { +void SkiaRecordingCanvas::enableZ(bool enableZ) { + if (mCurrentBarrier && enableZ) { // Already in a re-order section, nothing to do return; } @@ -98,7 +98,7 @@ void SkiaRecordingCanvas::insertReorderBarrier(bool enableReorder) { mCurrentBarrier = nullptr; drawDrawable(drawable); } - if (enableReorder) { + if (enableZ) { mCurrentBarrier = mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(mDisplayList.get()); drawDrawable(mCurrentBarrier); @@ -132,23 +132,6 @@ void SkiaRecordingCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) { } } - -void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor, - uirenderer::GlFunctorLifecycleListener* listener) { -#ifdef __ANDROID__ // Layoutlib does not support GL, Vulcan etc. - FunctorDrawable* functorDrawable; - if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { - functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>( - functor, listener, asSkCanvas()); - } else { - functorDrawable = - mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, listener, asSkCanvas()); - } - mDisplayList->mChildFunctors.push_back(functorDrawable); - drawDrawable(functorDrawable); -#endif -} - void SkiaRecordingCanvas::drawWebViewFunctor(int functor) { #ifdef __ANDROID__ // Layoutlib does not support GL, Vulcan etc. FunctorDrawable* functorDrawable; diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h index bd5274c94e75..83e934974afd 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h @@ -69,11 +69,10 @@ public: virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override; - virtual void insertReorderBarrier(bool enableReorder) override; + virtual void enableZ(bool enableZ) override; virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) override; virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override; - virtual void callDrawGLFunction(Functor* functor, - uirenderer::GlFunctorLifecycleListener* listener) override; + void drawWebViewFunctor(int functor) override; private: diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp index 68f111752a4c..50b45e6eb7ec 100644 --- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp @@ -20,7 +20,7 @@ #include <GrBackendDrawableInfo.h> #include <SkAndroidFrameworkUtils.h> #include <SkImage.h> -#include "include/private/SkM44.h" +#include <SkM44.h> #include <utils/Color.h> #include <utils/Trace.h> #include <utils/TraceUtils.h> @@ -121,12 +121,7 @@ std::unique_ptr<FunctorDrawable::GpuDrawHandler> VkFunctorDrawable::onSnapGpuDra return nullptr; } std::unique_ptr<VkFunctorDrawHandler> draw; - if (mAnyFunctor.index() == 0) { - return std::make_unique<VkFunctorDrawHandler>(std::get<0>(mAnyFunctor).handle, matrix, clip, - image_info); - } else { - LOG_ALWAYS_FATAL("Not implemented"); - } + return std::make_unique<VkFunctorDrawHandler>(mWebViewHandle, matrix, clip, image_info); } } // namespace skiapipeline diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.h b/libs/hwui/pipeline/skia/VkFunctorDrawable.h index d3f97773b91d..fbfc6e76595e 100644 --- a/libs/hwui/pipeline/skia/VkFunctorDrawable.h +++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.h @@ -19,7 +19,6 @@ #include "FunctorDrawable.h" #include <SkImageInfo.h> -#include <ui/GraphicBuffer.h> #include <utils/RefBase.h> namespace android { diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp index 241d3708def5..403d9075dbd1 100644 --- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp @@ -15,23 +15,24 @@ */ #include "VkInteropFunctorDrawable.h" -#include <private/hwui/DrawGlInfo.h> +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <GLES3/gl3.h> +#include <private/hwui/DrawGlInfo.h> #include <utils/Color.h> +#include <utils/GLUtils.h> #include <utils/Trace.h> #include <utils/TraceUtils.h> + #include <thread> + #include "renderthread/EglManager.h" #include "thread/ThreadBase.h" #include "utils/TimeUtils.h" -#include <EGL/eglext.h> -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> -#include <GLES3/gl3.h> - -#include <utils/GLUtils.h> - namespace android { namespace uirenderer { namespace skiapipeline { @@ -75,20 +76,23 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) { SkImageInfo surfaceInfo = canvas->imageInfo(); - if (!mFrameBuffer.get() || mFBInfo != surfaceInfo) { + if (mFrameBuffer == nullptr || mFBInfo != surfaceInfo) { // Buffer will be used as an OpenGL ES render target. - mFrameBuffer = new GraphicBuffer( - // TODO: try to reduce the size of the buffer: possibly by using clip bounds. - static_cast<uint32_t>(surfaceInfo.width()), - static_cast<uint32_t>(surfaceInfo.height()), - ColorTypeToPixelFormat(surfaceInfo.colorType()), - GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER | - GraphicBuffer::USAGE_SW_READ_NEVER | GraphicBuffer::USAGE_HW_RENDER, - std::string("VkInteropFunctorDrawable::onDraw pid [") + std::to_string(getpid()) + - "]"); - status_t error = mFrameBuffer->initCheck(); - if (error < 0) { - ALOGW("VkInteropFunctorDrawable::onDraw() failed in GraphicBuffer.create()"); + AHardwareBuffer_Desc desc = { + .width = static_cast<uint32_t>(surfaceInfo.width()), + .height = static_cast<uint32_t>(surfaceInfo.height()), + .layers = 1, + .format = ColorTypeToBufferFormat(surfaceInfo.colorType()), + .usage = AHARDWAREBUFFER_USAGE_CPU_READ_NEVER | + AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER | + AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | + AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER, + }; + + mFrameBuffer = allocateAHardwareBuffer(desc); + + if (!mFrameBuffer) { + ALOGW("VkInteropFunctorDrawable::onDraw() failed in AHardwareBuffer_allocate()"); return; } @@ -106,7 +110,7 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) { uirenderer::renderthread::EglManager::eglErrorString()); // We use an EGLImage to access the content of the GraphicBuffer // The EGL image is later bound to a 2D texture - EGLClientBuffer clientBuffer = (EGLClientBuffer)mFrameBuffer->getNativeBuffer(); + const EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(mFrameBuffer.get()); AutoEglImage autoImage(display, clientBuffer); if (autoImage.image == EGL_NO_IMAGE_KHR) { ALOGW("Could not create EGL image, err =%s", @@ -121,7 +125,7 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) { glBindTexture(GL_TEXTURE_2D, 0); DrawGlInfo info; - SkM44 mat4(canvas->experimental_getLocalToDevice()); + SkM44 mat4(canvas->getLocalToDevice()); SkIRect clipBounds = canvas->getDeviceClipBounds(); info.clipLeft = clipBounds.fLeft; @@ -151,11 +155,7 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) { glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); - if (mAnyFunctor.index() == 0) { - std::get<0>(mAnyFunctor).handle->drawGl(info); - } else { - (*(std::get<1>(mAnyFunctor).functor))(DrawGlInfo::kModeDraw, &info); - } + mWebViewHandle->drawGl(info); EGLSyncKHR glDrawFinishedFence = eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL); @@ -179,22 +179,13 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) { // drawing into the offscreen surface, so we need to reset it here. canvas->resetMatrix(); - auto functorImage = SkImage::MakeFromAHardwareBuffer( - reinterpret_cast<AHardwareBuffer*>(mFrameBuffer.get()), kPremul_SkAlphaType, - canvas->imageInfo().refColorSpace(), kBottomLeft_GrSurfaceOrigin); + auto functorImage = SkImage::MakeFromAHardwareBuffer(mFrameBuffer.get(), kPremul_SkAlphaType, + canvas->imageInfo().refColorSpace(), + kBottomLeft_GrSurfaceOrigin); canvas->drawImage(functorImage, 0, 0, &paint); canvas->restore(); } -VkInteropFunctorDrawable::~VkInteropFunctorDrawable() { - if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) { - if (lp->listener) { - ScopedDrawRequest _drawRequest{}; - lp->listener->onGlFunctorReleased(lp->functor); - } - } -} - void VkInteropFunctorDrawable::syncFunctor(const WebViewSyncData& data) const { ScopedDrawRequest _drawRequest{}; FunctorDrawable::syncFunctor(data); diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h index c47ee114263f..e6ea175929c0 100644 --- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h +++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h @@ -16,11 +16,12 @@ #pragma once -#include "FunctorDrawable.h" - -#include <ui/GraphicBuffer.h> +#include <android/hardware_buffer.h> +#include <utils/NdkUtils.h> #include <utils/RefBase.h> +#include "FunctorDrawable.h" + namespace android { namespace uirenderer { @@ -34,7 +35,7 @@ class VkInteropFunctorDrawable : public FunctorDrawable { public: using FunctorDrawable::FunctorDrawable; - virtual ~VkInteropFunctorDrawable(); + virtual ~VkInteropFunctorDrawable() {} static void vkInvokeFunctor(Functor* functor); @@ -45,7 +46,7 @@ protected: private: // Variables below describe/store temporary offscreen buffer used for Vulkan pipeline. - sp<GraphicBuffer> mFrameBuffer; + UniqueAHardwareBuffer mFrameBuffer; SkImageInfo mFBInfo; }; diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index 1e5877356e8d..b57dee4897ac 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -101,7 +101,7 @@ void CacheManager::trimMemory(TrimMemoryMode mode) { return; } - mGrContext->flush(); + mGrContext->flushAndSubmit(); switch (mode) { case TrimMemoryMode::Complete: @@ -122,14 +122,15 @@ void CacheManager::trimMemory(TrimMemoryMode mode) { // We must sync the cpu to make sure deletions of resources still queued up on the GPU actually // happen. - mGrContext->flush(kSyncCpu_GrFlushFlag, 0, nullptr); + mGrContext->flush({}); + mGrContext->submit(true); } void CacheManager::trimStaleResources() { if (!mGrContext) { return; } - mGrContext->flush(); + mGrContext->flushAndSubmit(); mGrContext->purgeResourcesNotUsedInMs(std::chrono::seconds(30)); } diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index a362bd220936..13d544c68e95 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -174,7 +174,10 @@ void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) { } else { mNativeSurface = nullptr; } + setupPipelineSurface(); +} +void CanvasContext::setupPipelineSurface() { bool hasSurface = mRenderPipeline->setSurface( mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior); @@ -184,7 +187,7 @@ void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) { mFrameNumber = -1; - if (window != nullptr && hasSurface) { + if (mNativeSurface != nullptr && hasSurface) { mHaveNewSurface = true; mSwapHistory.clear(); // Enable frame stats after the surface has been bound to the appropriate graphics API. @@ -239,9 +242,9 @@ void CanvasContext::setOpaque(bool opaque) { mOpaque = opaque; } -void CanvasContext::setWideGamut(bool wideGamut) { - ColorMode colorMode = wideGamut ? ColorMode::WideColorGamut : ColorMode::SRGB; - mRenderPipeline->setSurfaceColorProperties(colorMode); +void CanvasContext::setColorMode(ColorMode mode) { + mRenderPipeline->setSurfaceColorProperties(mode); + setupPipelineSurface(); } bool CanvasContext::makeCurrent() { diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 0f1b8aebf56c..cba710f01063 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -30,6 +30,7 @@ #include "renderthread/RenderTask.h" #include "renderthread/RenderThread.h" #include "utils/RingBuffer.h" +#include "ColorMode.h" #include <SkBitmap.h> #include <SkRect.h> @@ -119,7 +120,7 @@ public: void setLightAlpha(uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha); void setLightGeometry(const Vector3& lightCenter, float lightRadius); void setOpaque(bool opaque); - void setWideGamut(bool wideGamut); + void setColorMode(ColorMode mode); bool makeCurrent(); void prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued, RenderNode* target); void draw(); @@ -170,9 +171,9 @@ public: } // Used to queue up work that needs to be completed before this frame completes - ANDROID_API void enqueueFrameWork(std::function<void()>&& func); + void enqueueFrameWork(std::function<void()>&& func); - ANDROID_API int64_t getFrameNumber(); + int64_t getFrameNumber(); void waitOnFences(); @@ -211,6 +212,7 @@ private: bool isSwapChainStuffed(); bool surfaceRequiresRedraw(); void setPresentTime(); + void setupPipelineSurface(); SkRect computeDirtyRect(const Frame& frame, SkRect* dirty); diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 5e0471c08d67..2a8aa8c3e5b7 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -76,6 +76,7 @@ static struct { bool glColorSpace = false; bool scRGB = false; bool displayP3 = false; + bool hdr = false; bool contextPriority = false; bool surfacelessContext = false; bool nativeFenceSync = false; @@ -86,7 +87,8 @@ static struct { EglManager::EglManager() : mEglDisplay(EGL_NO_DISPLAY) , mEglConfig(nullptr) - , mEglConfigWideGamut(nullptr) + , mEglConfigF16(nullptr) + , mEglConfig1010102(nullptr) , mEglContext(EGL_NO_CONTEXT) , mPBufferSurface(EGL_NO_SURFACE) , mCurrentSurface(EGL_NO_SURFACE) @@ -136,15 +138,14 @@ void EglManager::initialize() { LOG_ALWAYS_FATAL_IF(!DeviceInfo::get()->getWideColorSpace()->toXYZD50(&wideColorGamut), "Could not get gamut matrix from wideColorSpace"); bool hasWideColorSpaceExtension = false; - if (memcmp(&wideColorGamut, &SkNamedGamut::kDCIP3, sizeof(wideColorGamut)) == 0) { + if (memcmp(&wideColorGamut, &SkNamedGamut::kDisplayP3, sizeof(wideColorGamut)) == 0) { hasWideColorSpaceExtension = EglExtensions.displayP3; } else if (memcmp(&wideColorGamut, &SkNamedGamut::kSRGB, sizeof(wideColorGamut)) == 0) { hasWideColorSpaceExtension = EglExtensions.scRGB; } else { LOG_ALWAYS_FATAL("Unsupported wide color space."); } - mHasWideColorGamutSupport = EglExtensions.glColorSpace && hasWideColorSpaceExtension && - mEglConfigWideGamut != EGL_NO_CONFIG_KHR; + mHasWideColorGamutSupport = EglExtensions.glColorSpace && hasWideColorSpaceExtension; } EGLConfig EglManager::load8BitsConfig(EGLDisplay display, EglManager::SwapBehavior swapBehavior) { @@ -177,6 +178,35 @@ EGLConfig EglManager::load8BitsConfig(EGLDisplay display, EglManager::SwapBehavi return config; } +EGLConfig EglManager::load1010102Config(EGLDisplay display, SwapBehavior swapBehavior) { + EGLint eglSwapBehavior = + (swapBehavior == SwapBehavior::Preserved) ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; + // If we reached this point, we have a valid swap behavior + EGLint attribs[] = {EGL_RENDERABLE_TYPE, + EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, + 10, + EGL_GREEN_SIZE, + 10, + EGL_BLUE_SIZE, + 10, + EGL_ALPHA_SIZE, + 2, + EGL_DEPTH_SIZE, + 0, + EGL_STENCIL_SIZE, + STENCIL_BUFFER_SIZE, + EGL_SURFACE_TYPE, + EGL_WINDOW_BIT | eglSwapBehavior, + EGL_NONE}; + EGLConfig config = EGL_NO_CONFIG_KHR; + EGLint numConfigs = 1; + if (!eglChooseConfig(display, attribs, &config, numConfigs, &numConfigs) || numConfigs != 1) { + return EGL_NO_CONFIG_KHR; + } + return config; +} + EGLConfig EglManager::loadFP16Config(EGLDisplay display, SwapBehavior swapBehavior) { EGLint eglSwapBehavior = (swapBehavior == SwapBehavior::Preserved) ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; @@ -208,8 +238,12 @@ EGLConfig EglManager::loadFP16Config(EGLDisplay display, SwapBehavior swapBehavi return config; } +extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); + void EglManager::initExtensions() { auto extensions = StringUtils::split(eglQueryString(mEglDisplay, EGL_EXTENSIONS)); + auto extensionsAndroid = + StringUtils::split(eglQueryStringImplementationANDROID(mEglDisplay, EGL_EXTENSIONS)); // For our purposes we don't care if EGL_BUFFER_AGE is a result of // EGL_EXT_buffer_age or EGL_KHR_partial_update as our usage is covered @@ -226,11 +260,15 @@ void EglManager::initExtensions() { EglExtensions.pixelFormatFloat = extensions.has("EGL_EXT_pixel_format_float"); EglExtensions.scRGB = extensions.has("EGL_EXT_gl_colorspace_scrgb"); EglExtensions.displayP3 = extensions.has("EGL_EXT_gl_colorspace_display_p3_passthrough"); + EglExtensions.hdr = extensions.has("EGL_EXT_gl_colorspace_bt2020_pq"); EglExtensions.contextPriority = extensions.has("EGL_IMG_context_priority"); EglExtensions.surfacelessContext = extensions.has("EGL_KHR_surfaceless_context"); - EglExtensions.nativeFenceSync = extensions.has("EGL_ANDROID_native_fence_sync"); EglExtensions.fenceSync = extensions.has("EGL_KHR_fence_sync"); EglExtensions.waitSync = extensions.has("EGL_KHR_wait_sync"); + + // EGL_ANDROID_native_fence_sync is not exposed to applications, so access + // this through the private Android-specific query instead. + EglExtensions.nativeFenceSync = extensionsAndroid.has("EGL_ANDROID_native_fence_sync"); } bool EglManager::hasEglContext() { @@ -253,18 +291,20 @@ void EglManager::loadConfigs() { LOG_ALWAYS_FATAL("Failed to choose config, error = %s", eglErrorString()); } } - SkColorType wideColorType = DeviceInfo::get()->getWideColorType(); // When we reach this point, we have a valid swap behavior - if (wideColorType == SkColorType::kRGBA_F16_SkColorType && EglExtensions.pixelFormatFloat) { - mEglConfigWideGamut = loadFP16Config(mEglDisplay, mSwapBehavior); - if (mEglConfigWideGamut == EGL_NO_CONFIG_KHR) { + if (EglExtensions.pixelFormatFloat) { + mEglConfigF16 = loadFP16Config(mEglDisplay, mSwapBehavior); + if (mEglConfigF16 == EGL_NO_CONFIG_KHR) { ALOGE("Device claims wide gamut support, cannot find matching config, error = %s", eglErrorString()); EglExtensions.pixelFormatFloat = false; } - } else if (wideColorType == SkColorType::kN32_SkColorType) { - mEglConfigWideGamut = load8BitsConfig(mEglDisplay, mSwapBehavior); + } + mEglConfig1010102 = load1010102Config(mEglDisplay, mSwapBehavior); + if (mEglConfig1010102 == EGL_NO_CONFIG_KHR) { + ALOGW("Failed to initialize 101010-2 format, error = %s", + eglErrorString()); } } @@ -304,8 +344,9 @@ Result<EGLSurface, EGLint> EglManager::createSurface(EGLNativeWindowType window, sk_sp<SkColorSpace> colorSpace) { LOG_ALWAYS_FATAL_IF(!hasEglContext(), "Not initialized"); - bool wideColorGamut = colorMode == ColorMode::WideColorGamut && mHasWideColorGamutSupport && - EglExtensions.noConfigContext; + if (!mHasWideColorGamutSupport || !EglExtensions.noConfigContext) { + colorMode = ColorMode::Default; + } // The color space we want to use depends on whether linear blending is turned // on and whether the app has requested wide color gamut rendering. When wide @@ -331,26 +372,47 @@ Result<EGLSurface, EGLint> EglManager::createSurface(EGLNativeWindowType window, // list is considered empty if the first entry is EGL_NONE EGLint attribs[] = {EGL_NONE, EGL_NONE, EGL_NONE}; + EGLConfig config = mEglConfig; + if (DeviceInfo::get()->getWideColorType() == kRGBA_F16_SkColorType) { + if (mEglConfigF16 == EGL_NO_CONFIG_KHR) { + colorMode = ColorMode::Default; + } else { + config = mEglConfigF16; + } + } if (EglExtensions.glColorSpace) { attribs[0] = EGL_GL_COLORSPACE_KHR; - if (wideColorGamut) { - skcms_Matrix3x3 colorGamut; - LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&colorGamut), - "Could not get gamut matrix from color space"); - if (memcmp(&colorGamut, &SkNamedGamut::kDCIP3, sizeof(colorGamut)) == 0) { - attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT; - } else if (memcmp(&colorGamut, &SkNamedGamut::kSRGB, sizeof(colorGamut)) == 0) { - attribs[1] = EGL_GL_COLORSPACE_SCRGB_EXT; - } else { - LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space."); + switch (colorMode) { + case ColorMode::Default: + attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR; + break; + case ColorMode::WideColorGamut: { + skcms_Matrix3x3 colorGamut; + LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&colorGamut), + "Could not get gamut matrix from color space"); + if (memcmp(&colorGamut, &SkNamedGamut::kDisplayP3, sizeof(colorGamut)) == 0) { + attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT; + } else if (memcmp(&colorGamut, &SkNamedGamut::kSRGB, sizeof(colorGamut)) == 0) { + attribs[1] = EGL_GL_COLORSPACE_SCRGB_EXT; + } else if (memcmp(&colorGamut, &SkNamedGamut::kRec2020, sizeof(colorGamut)) == 0) { + attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT; + } else { + LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space."); + } + break; } - } else { - attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR; + case ColorMode::Hdr: + config = mEglConfigF16; + attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT; + break; + case ColorMode::Hdr10: + config = mEglConfig1010102; + attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT; + break; } } - EGLSurface surface = eglCreateWindowSurface( - mEglDisplay, wideColorGamut ? mEglConfigWideGamut : mEglConfig, window, attribs); + EGLSurface surface = eglCreateWindowSurface(mEglDisplay, config, window, attribs); if (surface == EGL_NO_SURFACE) { return Error<EGLint>{eglGetError()}; } diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h index a893e245b214..69f3ed014c53 100644 --- a/libs/hwui/renderthread/EglManager.h +++ b/libs/hwui/renderthread/EglManager.h @@ -21,7 +21,6 @@ #include <SkImageInfo.h> #include <SkRect.h> #include <cutils/compiler.h> -#include <ui/GraphicBuffer.h> #include <utils/StrongPointer.h> #include "IRenderPipeline.h" @@ -89,6 +88,7 @@ private: static EGLConfig load8BitsConfig(EGLDisplay display, SwapBehavior swapBehavior); static EGLConfig loadFP16Config(EGLDisplay display, SwapBehavior swapBehavior); + static EGLConfig load1010102Config(EGLDisplay display, SwapBehavior swapBehavior); void initExtensions(); void createPBufferSurface(); @@ -98,7 +98,8 @@ private: EGLDisplay mEglDisplay; EGLConfig mEglConfig; - EGLConfig mEglConfigWideGamut; + EGLConfig mEglConfigF16; + EGLConfig mEglConfig1010102; EGLContext mEglContext; EGLSurface mPBufferSurface; EGLSurface mCurrentSurface; diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h index c3c22869a42f..a04738d6a6f0 100644 --- a/libs/hwui/renderthread/IRenderPipeline.h +++ b/libs/hwui/renderthread/IRenderPipeline.h @@ -22,6 +22,7 @@ #include "Lighting.h" #include "SwapBehavior.h" #include "hwui/Bitmap.h" +#include "ColorMode.h" #include <SkRect.h> #include <utils/RefBase.h> @@ -42,16 +43,6 @@ namespace renderthread { enum class MakeCurrentResult { AlreadyCurrent, Failed, Succeeded }; -enum class ColorMode { - // SRGB means HWUI will produce buffer in SRGB color space. - SRGB, - // WideColorGamut means HWUI would support rendering scRGB non-linear into - // a signed buffer with enough range to support the wide color gamut of the - // display. - WideColorGamut, - // Hdr -}; - class Frame; class IRenderPipeline { diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp index dcf1fc189588..c29cc11fa7ea 100644 --- a/libs/hwui/renderthread/ReliableSurface.cpp +++ b/libs/hwui/renderthread/ReliableSurface.cpp @@ -149,21 +149,25 @@ ANativeWindowBuffer* ReliableSurface::acquireFallbackBuffer(int error) { return AHardwareBuffer_to_ANativeWindowBuffer(mScratchBuffer.get()); } - AHardwareBuffer_Desc desc; - desc.usage = mUsage; - desc.format = mFormat; - desc.width = 1; - desc.height = 1; - desc.layers = 1; - desc.rfu0 = 0; - desc.rfu1 = 0; - AHardwareBuffer* newBuffer = nullptr; - int err = AHardwareBuffer_allocate(&desc, &newBuffer); - if (err) { + AHardwareBuffer_Desc desc = AHardwareBuffer_Desc{ + .usage = mUsage, + .format = mFormat, + .width = 1, + .height = 1, + .layers = 1, + .rfu0 = 0, + .rfu1 = 0, + }; + + AHardwareBuffer* newBuffer; + int result = AHardwareBuffer_allocate(&desc, &newBuffer); + + if (result != NO_ERROR) { // Allocate failed, that sucks - ALOGW("Failed to allocate scratch buffer, error=%d", err); + ALOGW("Failed to allocate scratch buffer, error=%d", result); return nullptr; } + mScratchBuffer.reset(newBuffer); return AHardwareBuffer_to_ANativeWindowBuffer(newBuffer); } diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h index f699eb1fe6b3..41969e776fc8 100644 --- a/libs/hwui/renderthread/ReliableSurface.h +++ b/libs/hwui/renderthread/ReliableSurface.h @@ -21,6 +21,7 @@ #include <apex/window.h> #include <utils/Errors.h> #include <utils/Macros.h> +#include <utils/NdkUtils.h> #include <utils/StrongPointer.h> #include <memory> @@ -67,8 +68,7 @@ private: uint64_t mUsage = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER; AHardwareBuffer_Format mFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; - std::unique_ptr<AHardwareBuffer, void (*)(AHardwareBuffer*)> mScratchBuffer{ - nullptr, AHardwareBuffer_release}; + UniqueAHardwareBuffer mScratchBuffer; ANativeWindowBuffer* mReservedBuffer = nullptr; base::unique_fd mReservedFenceFd; bool mHasDequeuedBuffer = false; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index b66a13d1efda..aad0cca80cdc 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -109,8 +109,8 @@ void RenderProxy::setOpaque(bool opaque) { mRenderThread.queue().post([=]() { mContext->setOpaque(opaque); }); } -void RenderProxy::setWideGamut(bool wideGamut) { - mRenderThread.queue().post([=]() { mContext->setWideGamut(wideGamut); }); +void RenderProxy::setColorMode(ColorMode mode) { + mRenderThread.queue().post([=]() { mContext->setColorMode(mode); }); } int64_t* RenderProxy::frameInfo() { @@ -128,20 +128,6 @@ void RenderProxy::destroy() { mRenderThread.queue().runSync([=]() { mContext->destroy(); }); } -void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) { - ATRACE_CALL(); - RenderThread& thread = RenderThread::getInstance(); - auto invoke = [&thread, functor]() { CanvasContext::invokeFunctor(thread, functor); }; - if (waitForCompletion) { - // waitForCompletion = true is expected to be fairly rare and only - // happen in destruction. Thus it should be fine to temporarily - // create a Mutex - thread.queue().runSync(std::move(invoke)); - } else { - thread.queue().post(std::move(invoke)); - } -} - void RenderProxy::destroyFunctor(int functor) { ATRACE_CALL(); RenderThread& thread = RenderThread::getInstance(); diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 3baeb2f7a476..33dabc9895b1 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -24,6 +24,7 @@ #include "../FrameMetricsObserver.h" #include "../IContextFactory.h" +#include "ColorMode.h" #include "DrawFrameTask.h" #include "SwapBehavior.h" #include "hwui/Bitmap.h" @@ -60,69 +61,67 @@ enum { * references RenderProxy fields. This is safe as RenderProxy cannot * be deleted if it is blocked inside a call. */ -class ANDROID_API RenderProxy { +class RenderProxy { public: - ANDROID_API RenderProxy(bool opaque, RenderNode* rootNode, IContextFactory* contextFactory); - ANDROID_API virtual ~RenderProxy(); + RenderProxy(bool opaque, RenderNode* rootNode, IContextFactory* contextFactory); + virtual ~RenderProxy(); // Won't take effect until next EGLSurface creation - ANDROID_API void setSwapBehavior(SwapBehavior swapBehavior); - ANDROID_API bool loadSystemProperties(); - ANDROID_API void setName(const char* name); - - ANDROID_API void setSurface(ANativeWindow* window, bool enableTimeout = true); - ANDROID_API void allocateBuffers(); - ANDROID_API bool pause(); - ANDROID_API void setStopped(bool stopped); - ANDROID_API void setLightAlpha(uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha); - ANDROID_API void setLightGeometry(const Vector3& lightCenter, float lightRadius); - ANDROID_API void setOpaque(bool opaque); - ANDROID_API void setWideGamut(bool wideGamut); - ANDROID_API int64_t* frameInfo(); - ANDROID_API int syncAndDrawFrame(); - ANDROID_API void destroy(); - - ANDROID_API static void invokeFunctor(Functor* functor, bool waitForCompletion); + void setSwapBehavior(SwapBehavior swapBehavior); + bool loadSystemProperties(); + void setName(const char* name); + + void setSurface(ANativeWindow* window, bool enableTimeout = true); + void allocateBuffers(); + bool pause(); + void setStopped(bool stopped); + void setLightAlpha(uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha); + void setLightGeometry(const Vector3& lightCenter, float lightRadius); + void setOpaque(bool opaque); + void setColorMode(ColorMode mode); + int64_t* frameInfo(); + int syncAndDrawFrame(); + void destroy(); + static void destroyFunctor(int functor); - ANDROID_API DeferredLayerUpdater* createTextureLayer(); - ANDROID_API void buildLayer(RenderNode* node); - ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap& bitmap); - ANDROID_API void pushLayerUpdate(DeferredLayerUpdater* layer); - ANDROID_API void cancelLayerUpdate(DeferredLayerUpdater* layer); - ANDROID_API void detachSurfaceTexture(DeferredLayerUpdater* layer); + DeferredLayerUpdater* createTextureLayer(); + void buildLayer(RenderNode* node); + bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap& bitmap); + void pushLayerUpdate(DeferredLayerUpdater* layer); + void cancelLayerUpdate(DeferredLayerUpdater* layer); + void detachSurfaceTexture(DeferredLayerUpdater* layer); - ANDROID_API void destroyHardwareResources(); - ANDROID_API static void trimMemory(int level); - ANDROID_API static void overrideProperty(const char* name, const char* value); + void destroyHardwareResources(); + static void trimMemory(int level); + static void overrideProperty(const char* name, const char* value); - ANDROID_API void fence(); - ANDROID_API static int maxTextureSize(); - ANDROID_API void stopDrawing(); - ANDROID_API void notifyFramePending(); + void fence(); + static int maxTextureSize(); + void stopDrawing(); + void notifyFramePending(); - ANDROID_API void dumpProfileInfo(int fd, int dumpFlags); + void dumpProfileInfo(int fd, int dumpFlags); // Not exported, only used for testing void resetProfileInfo(); uint32_t frameTimePercentile(int p); - ANDROID_API static void dumpGraphicsMemory(int fd); + static void dumpGraphicsMemory(int fd); - ANDROID_API static void rotateProcessStatsBuffer(); - ANDROID_API static void setProcessStatsBuffer(int fd); - ANDROID_API int getRenderThreadTid(); + static void rotateProcessStatsBuffer(); + static void setProcessStatsBuffer(int fd); + int getRenderThreadTid(); - ANDROID_API void addRenderNode(RenderNode* node, bool placeFront); - ANDROID_API void removeRenderNode(RenderNode* node); - ANDROID_API void drawRenderNode(RenderNode* node); - ANDROID_API void setContentDrawBounds(int left, int top, int right, int bottom); - ANDROID_API void setPictureCapturedCallback( - const std::function<void(sk_sp<SkPicture>&&)>& callback); - ANDROID_API void setFrameCallback(std::function<void(int64_t)>&& callback); - ANDROID_API void setFrameCompleteCallback(std::function<void(int64_t)>&& callback); + void addRenderNode(RenderNode* node, bool placeFront); + void removeRenderNode(RenderNode* node); + void drawRenderNode(RenderNode* node); + void setContentDrawBounds(int left, int top, int right, int bottom); + void setPictureCapturedCallback(const std::function<void(sk_sp<SkPicture>&&)>& callback); + void setFrameCallback(std::function<void(int64_t)>&& callback); + void setFrameCompleteCallback(std::function<void(int64_t)>&& callback); - ANDROID_API void addFrameMetricsObserver(FrameMetricsObserver* observer); - ANDROID_API void removeFrameMetricsObserver(FrameMetricsObserver* observer); - ANDROID_API void setForceDark(bool enable); + void addFrameMetricsObserver(FrameMetricsObserver* observer); + void removeFrameMetricsObserver(FrameMetricsObserver* observer); + void setForceDark(bool enable); /** * Sets a render-ahead depth on the backing renderer. This will increase latency by @@ -139,17 +138,17 @@ public: * * @param renderAhead How far to render ahead, must be in the range [0..2] */ - ANDROID_API void setRenderAheadDepth(int renderAhead); + void setRenderAheadDepth(int renderAhead); - ANDROID_API static int copySurfaceInto(ANativeWindow* window, int left, int top, int right, + static int copySurfaceInto(ANativeWindow* window, int left, int top, int right, int bottom, SkBitmap* bitmap); - ANDROID_API static void prepareToDraw(Bitmap& bitmap); + static void prepareToDraw(Bitmap& bitmap); static int copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap); - ANDROID_API static void disableVsync(); + static void disableVsync(); - ANDROID_API static void preload(); + static void preload(); private: RenderThread& mRenderThread; diff --git a/libs/hwui/renderthread/RenderTask.h b/libs/hwui/renderthread/RenderTask.h index c56a3578ad58..3e3a381d65fe 100644 --- a/libs/hwui/renderthread/RenderTask.h +++ b/libs/hwui/renderthread/RenderTask.h @@ -45,12 +45,12 @@ namespace renderthread { * malloc/free churn of small objects? */ -class ANDROID_API RenderTask { +class RenderTask { public: - ANDROID_API RenderTask() : mNext(nullptr), mRunAt(0) {} - ANDROID_API virtual ~RenderTask() {} + RenderTask() : mNext(nullptr), mRunAt(0) {} + virtual ~RenderTask() {} - ANDROID_API virtual void run() = 0; + virtual void run() = 0; RenderTask* mNext; nsecs_t mRunAt; // nano-seconds on the SYSTEM_TIME_MONOTONIC clock diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 206b58f62ea7..565fb61c8994 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -190,7 +190,7 @@ void RenderThread::requireGlContext() { auto glesVersion = reinterpret_cast<const char*>(glGetString(GL_VERSION)); auto size = glesVersion ? strlen(glesVersion) : -1; cacheManager().configureContext(&options, glesVersion, size); - sk_sp<GrContext> grContext(GrContext::MakeGL(std::move(glInterface), options)); + sk_sp<GrDirectContext> grContext(GrDirectContext::MakeGL(std::move(glInterface), options)); LOG_ALWAYS_FATAL_IF(!grContext.get()); setGrContext(grContext); } @@ -204,7 +204,7 @@ void RenderThread::requireVkContext() { initGrContextOptions(options); auto vkDriverVersion = mVkManager->getDriverVersion(); cacheManager().configureContext(&options, &vkDriverVersion, sizeof(vkDriverVersion)); - sk_sp<GrContext> grContext = mVkManager->createContext(options); + sk_sp<GrDirectContext> grContext = mVkManager->createContext(options); LOG_ALWAYS_FATAL_IF(!grContext.get()); setGrContext(grContext); } @@ -263,7 +263,7 @@ Readback& RenderThread::readback() { return *mReadback; } -void RenderThread::setGrContext(sk_sp<GrContext> context) { +void RenderThread::setGrContext(sk_sp<GrDirectContext> context) { mCacheManager->reset(context); if (mGrContext) { mRenderState->onContextDestroyed(); diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index 8be46a6d16e1..b8ce55650516 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -17,7 +17,7 @@ #ifndef RENDERTHREAD_H_ #define RENDERTHREAD_H_ -#include <GrContext.h> +#include <GrDirectContext.h> #include <SkBitmap.h> #include <apex/choreographer.h> #include <cutils/compiler.h> @@ -88,7 +88,7 @@ class RenderThread : private ThreadBase { public: // Sets a callback that fires before any RenderThread setup has occurred. - ANDROID_API static void setOnStartHook(JVMAttachHook onStartHook); + static void setOnStartHook(JVMAttachHook onStartHook); static JVMAttachHook getOnStartHook(); WorkQueue& queue() { return ThreadBase::queue(); } @@ -106,8 +106,8 @@ public: ProfileDataContainer& globalProfileData() { return mGlobalProfileData; } Readback& readback(); - GrContext* getGrContext() const { return mGrContext.get(); } - void setGrContext(sk_sp<GrContext> cxt); + GrDirectContext* getGrContext() const { return mGrContext.get(); } + void setGrContext(sk_sp<GrDirectContext> cxt); CacheManager& cacheManager() { return *mCacheManager; } VulkanManager& vulkanManager() { return *mVkManager; } @@ -186,7 +186,7 @@ private: ProfileDataContainer mGlobalProfileData; Readback* mReadback = nullptr; - sk_sp<GrContext> mGrContext; + sk_sp<GrDirectContext> mGrContext; CacheManager* mCacheManager; VulkanManager* mVkManager; }; diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index ba70afc8b8d2..249936eb485e 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -20,7 +20,7 @@ #include <EGL/eglext.h> #include <GrBackendSemaphore.h> #include <GrBackendSurface.h> -#include <GrContext.h> +#include <GrDirectContext.h> #include <GrTypes.h> #include <android/sync.h> #include <ui/FatVector.h> @@ -369,7 +369,7 @@ void VulkanManager::initialize() { } } -sk_sp<GrContext> VulkanManager::createContext(const GrContextOptions& options) { +sk_sp<GrDirectContext> VulkanManager::createContext(const GrContextOptions& options) { auto getProc = [](const char* proc_name, VkInstance instance, VkDevice device) { if (device != VK_NULL_HANDLE) { return vkGetDeviceProcAddr(device, proc_name); @@ -388,7 +388,7 @@ sk_sp<GrContext> VulkanManager::createContext(const GrContextOptions& options) { backendContext.fDeviceFeatures2 = &mPhysicalDeviceFeatures2; backendContext.fGetProc = std::move(getProc); - return GrContext::MakeVulkan(backendContext, options); + return GrDirectContext::MakeVulkan(backendContext, options); } VkFunctorInitParams VulkanManager::getVkFunctorInitParams() const { @@ -459,7 +459,7 @@ Frame VulkanManager::dequeueNextBuffer(VulkanSurface* surface) { // The following flush blocks the GPU immediately instead of waiting for other // drawing ops. It seems dequeue_fence is not respected otherwise. // TODO: remove the flush after finding why backendSemaphore is not working. - bufferInfo->skSurface->flush(); + bufferInfo->skSurface->flushAndSubmit(); } } } @@ -525,9 +525,15 @@ void VulkanManager::swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect) int fenceFd = -1; DestroySemaphoreInfo* destroyInfo = new DestroySemaphoreInfo(mDestroySemaphore, mDevice, semaphore); + GrFlushInfo flushInfo; + flushInfo.fNumSemaphores = 1; + flushInfo.fSignalSemaphores = &backendSemaphore; + flushInfo.fFinishedProc = destroy_semaphore; + flushInfo.fFinishedContext = destroyInfo; GrSemaphoresSubmitted submitted = bufferInfo->skSurface->flush( - SkSurface::BackendSurfaceAccess::kPresent, kNone_GrFlushFlags, 1, &backendSemaphore, - destroy_semaphore, destroyInfo); + SkSurface::BackendSurfaceAccess::kPresent, flushInfo); + ALOGE_IF(!bufferInfo->skSurface->getContext(), "Surface is not backed by gpu"); + bufferInfo->skSurface->getContext()->submit(); if (submitted == GrSemaphoresSubmitted::kYes) { VkSemaphoreGetFdInfoKHR getFdInfo; getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR; @@ -556,9 +562,11 @@ void VulkanManager::destroySurface(VulkanSurface* surface) { delete surface; } -VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode colorMode, +VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, + ColorMode colorMode, sk_sp<SkColorSpace> surfaceColorSpace, - SkColorType surfaceColorType, GrContext* grContext, + SkColorType surfaceColorType, + GrDirectContext* grContext, uint32_t extraBuffers) { LOG_ALWAYS_FATAL_IF(!hasVkContext(), "Not initialized"); if (!window) { @@ -569,7 +577,7 @@ VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode col *this, extraBuffers); } -status_t VulkanManager::fenceWait(int fence, GrContext* grContext) { +status_t VulkanManager::fenceWait(int fence, GrDirectContext* grContext) { if (!hasVkContext()) { ALOGE("VulkanManager::fenceWait: VkDevice not initialized"); return INVALID_OPERATION; @@ -612,12 +620,12 @@ status_t VulkanManager::fenceWait(int fence, GrContext* grContext) { // Skia takes ownership of the semaphore and will delete it once the wait has finished. grContext->wait(1, &beSemaphore); - grContext->flush(); + grContext->flushAndSubmit(); return OK; } -status_t VulkanManager::createReleaseFence(int* nativeFence, GrContext* grContext) { +status_t VulkanManager::createReleaseFence(int* nativeFence, GrDirectContext* grContext) { *nativeFence = -1; if (!hasVkContext()) { ALOGE("VulkanManager::createReleaseFence: VkDevice not initialized"); @@ -648,8 +656,13 @@ status_t VulkanManager::createReleaseFence(int* nativeFence, GrContext* grContex // Even if Skia fails to submit the semaphore, it will still call the destroy_semaphore callback // which will remove its ref to the semaphore. The VulkanManager must still release its ref, // when it is done with the semaphore. - GrSemaphoresSubmitted submitted = grContext->flush(kNone_GrFlushFlags, 1, &backendSemaphore, - destroy_semaphore, destroyInfo); + GrFlushInfo flushInfo; + flushInfo.fNumSemaphores = 1; + flushInfo.fSignalSemaphores = &backendSemaphore; + flushInfo.fFinishedProc = destroy_semaphore; + flushInfo.fFinishedContext = destroyInfo; + GrSemaphoresSubmitted submitted = grContext->flush(flushInfo); + grContext->submit(); if (submitted == GrSemaphoresSubmitted::kNo) { ALOGE("VulkanManager::createReleaseFence: Failed to submit semaphore"); diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index 8b19f13fdfb9..3f2df8d75d89 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -57,9 +57,11 @@ public: bool hasVkContext() { return mDevice != VK_NULL_HANDLE; } // Create and destroy functions for wrapping an ANativeWindow in a VulkanSurface - VulkanSurface* createSurface(ANativeWindow* window, ColorMode colorMode, + VulkanSurface* createSurface(ANativeWindow* window, + ColorMode colorMode, sk_sp<SkColorSpace> surfaceColorSpace, - SkColorType surfaceColorType, GrContext* grContext, + SkColorType surfaceColorType, + GrDirectContext* grContext, uint32_t extraBuffers); void destroySurface(VulkanSurface* surface); @@ -70,18 +72,18 @@ public: void destroy(); // Inserts a wait on fence command into the Vulkan command buffer. - status_t fenceWait(int fence, GrContext* grContext); + status_t fenceWait(int fence, GrDirectContext* grContext); // Creates a fence that is signaled when all the pending Vulkan commands are finished on the // GPU. - status_t createReleaseFence(int* nativeFence, GrContext* grContext); + status_t createReleaseFence(int* nativeFence, GrDirectContext* grContext); // Returned pointers are owned by VulkanManager. // An instance of VkFunctorInitParams returned from getVkFunctorInitParams refers to // the internal state of VulkanManager: VulkanManager must be alive to use the returned value. VkFunctorInitParams getVkFunctorInitParams() const; - sk_sp<GrContext> createContext(const GrContextOptions& options); + sk_sp<GrDirectContext> createContext(const GrContextOptions& options); uint32_t getDriverVersion() const { return mDriverVersion; } diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp index a7ea21d8c4de..1da09b454da7 100644 --- a/libs/hwui/renderthread/VulkanSurface.cpp +++ b/libs/hwui/renderthread/VulkanSurface.cpp @@ -200,16 +200,16 @@ bool VulkanSurface::InitializeWindowInfoStruct(ANativeWindow* window, ColorMode "Could not get gamut matrix from color space"); if (memcmp(&surfaceGamut, &SkNamedGamut::kSRGB, sizeof(surfaceGamut)) == 0) { outWindowInfo->dataspace = HAL_DATASPACE_V0_SCRGB; - } else if (memcmp(&surfaceGamut, &SkNamedGamut::kDCIP3, sizeof(surfaceGamut)) == 0) { + } else if (memcmp(&surfaceGamut, &SkNamedGamut::kDisplayP3, sizeof(surfaceGamut)) == 0) { outWindowInfo->dataspace = HAL_DATASPACE_DISPLAY_P3; } else { LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space."); } } - outWindowInfo->pixelFormat = ColorTypeToPixelFormat(colorType); + outWindowInfo->bufferFormat = ColorTypeToBufferFormat(colorType); VkFormat vkPixelFormat = VK_FORMAT_R8G8B8A8_UNORM; - if (outWindowInfo->pixelFormat == PIXEL_FORMAT_RGBA_FP16) { + if (outWindowInfo->bufferFormat == AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT) { vkPixelFormat = VK_FORMAT_R16G16B16A16_SFLOAT; } @@ -263,10 +263,10 @@ bool VulkanSurface::InitializeWindowInfoStruct(ANativeWindow* window, ColorMode bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& windowInfo) { ATRACE_CALL(); - int err = native_window_set_buffers_format(window, windowInfo.pixelFormat); + int err = native_window_set_buffers_format(window, windowInfo.bufferFormat); if (err != 0) { ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_format(%d) failed: %s (%d)", - windowInfo.pixelFormat, strerror(-err), err); + windowInfo.bufferFormat, strerror(-err), err); return false; } diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h index bd2362612a13..40a44b11c0bc 100644 --- a/libs/hwui/renderthread/VulkanSurface.h +++ b/libs/hwui/renderthread/VulkanSurface.h @@ -17,8 +17,6 @@ #include <system/graphics.h> #include <system/window.h> -#include <ui/BufferQueueDefs.h> -#include <ui/PixelFormat.h> #include <vulkan/vulkan.h> #include <SkRefCnt.h> @@ -91,7 +89,7 @@ private: struct WindowInfo { SkISize size; - PixelFormat pixelFormat; + uint32_t bufferFormat; android_dataspace dataspace; int transform; size_t bufferCount; @@ -111,8 +109,13 @@ private: static bool UpdateWindow(ANativeWindow* window, const WindowInfo& windowInfo); void releaseBuffers(); + // TODO: This number comes from ui/BufferQueueDefs. We're not pulling the + // header in so that we don't need to depend on libui, but we should share + // this constant somewhere. But right now it's okay to keep here because we + // can't safely change the slot count anyways. + static constexpr size_t kNumBufferSlots = 64; // TODO: Just use a vector? - NativeBufferInfo mNativeBuffers[android::BufferQueueDefs::NUM_BUFFER_SLOTS]; + NativeBufferInfo mNativeBuffers[kNumBufferSlots]; sp<ANativeWindow> mNativeWindow; WindowInfo mWindowInfo; diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp index 644d5fbd5bf9..e4198017aee0 100644 --- a/libs/hwui/service/GraphicsStatsService.cpp +++ b/libs/hwui/service/GraphicsStatsService.cpp @@ -559,6 +559,7 @@ void GraphicsStatsService::finishDumpInMemory(Dump* dump, AStatsEventList* data, AStatsEvent_writeBool(event, !lastFullDay); AStatsEvent_build(event); } + delete dump; } diff --git a/libs/hwui/service/GraphicsStatsService.h b/libs/hwui/service/GraphicsStatsService.h index 59e21d039c9d..4063f749f808 100644 --- a/libs/hwui/service/GraphicsStatsService.h +++ b/libs/hwui/service/GraphicsStatsService.h @@ -44,18 +44,16 @@ public: ProtobufStatsd, }; - ANDROID_API static void saveBuffer(const std::string& path, const std::string& package, - int64_t versionCode, int64_t startTime, int64_t endTime, - const ProfileData* data); - - ANDROID_API static Dump* createDump(int outFd, DumpType type); - ANDROID_API static void addToDump(Dump* dump, const std::string& path, - const std::string& package, int64_t versionCode, - int64_t startTime, int64_t endTime, const ProfileData* data); - ANDROID_API static void addToDump(Dump* dump, const std::string& path); - ANDROID_API static void finishDump(Dump* dump); - ANDROID_API static void finishDumpInMemory(Dump* dump, AStatsEventList* data, - bool lastFullDay); + static void saveBuffer(const std::string& path, const std::string& package, int64_t versionCode, + int64_t startTime, int64_t endTime, const ProfileData* data); + + static Dump* createDump(int outFd, DumpType type); + static void addToDump(Dump* dump, const std::string& path, const std::string& package, + int64_t versionCode, int64_t startTime, int64_t endTime, + const ProfileData* data); + static void addToDump(Dump* dump, const std::string& path); + static void finishDump(Dump* dump); + static void finishDumpInMemory(Dump* dump, AStatsEventList* data, bool lastFullDay); // Visible for testing static bool parseFromFile(const std::string& path, protos::GraphicsStatsProto* output); diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index 91a808df3657..36c5a8c1b3de 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -287,18 +287,6 @@ public: static std::unique_ptr<uint16_t[]> asciiToUtf16(const char* str); - class MockFunctor : public Functor { - public: - virtual status_t operator()(int what, void* data) { - mLastMode = what; - return DrawGlInfo::kStatusDone; - } - int getLastMode() const { return mLastMode; } - - private: - int mLastMode = -1; - }; - static SkColor getColor(const sk_sp<SkSurface>& surface, int x, int y); static SkRect getClipBounds(const SkCanvas* canvas); @@ -311,30 +299,32 @@ public: int glesDraw = 0; }; - static void expectOnRenderThread() { EXPECT_EQ(gettid(), TestUtils::getRenderThreadTid()); } + static void expectOnRenderThread(const std::string_view& function = "unknown") { + EXPECT_EQ(gettid(), TestUtils::getRenderThreadTid()) << "Called on wrong thread: " << function; + } static WebViewFunctorCallbacks createMockFunctor(RenderMode mode) { auto callbacks = WebViewFunctorCallbacks{ .onSync = [](int functor, void* client_data, const WebViewSyncData& data) { - expectOnRenderThread(); + expectOnRenderThread("onSync"); sMockFunctorCounts[functor].sync++; }, .onContextDestroyed = [](int functor, void* client_data) { - expectOnRenderThread(); + expectOnRenderThread("onContextDestroyed"); sMockFunctorCounts[functor].contextDestroyed++; }, .onDestroyed = [](int functor, void* client_data) { - expectOnRenderThread(); + expectOnRenderThread("onDestroyed"); sMockFunctorCounts[functor].destroyed++; }, }; switch (mode) { case RenderMode::OpenGL_ES: callbacks.gles.draw = [](int functor, void* client_data, const DrawGlInfo& params) { - expectOnRenderThread(); + expectOnRenderThread("draw"); sMockFunctorCounts[functor].glesDraw++; }; break; diff --git a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp index f4fce277454d..edadf78db051 100644 --- a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp +++ b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp @@ -56,9 +56,9 @@ public: (float)magnifier->height(), 0, 0, (float)props.getWidth(), (float)props.getHeight(), nullptr); }); - canvas.insertReorderBarrier(true); + canvas.enableZ(true); canvas.drawRenderNode(zoomImageView.get()); - canvas.insertReorderBarrier(false); + canvas.enableZ(false); } void doFrame(int frameNr) override { diff --git a/libs/hwui/tests/common/scenes/RecentsAnimation.cpp b/libs/hwui/tests/common/scenes/RecentsAnimation.cpp index 3480a0f18407..1c2507867f6e 100644 --- a/libs/hwui/tests/common/scenes/RecentsAnimation.cpp +++ b/libs/hwui/tests/common/scenes/RecentsAnimation.cpp @@ -36,7 +36,7 @@ public: int cardsize = std::min(width, height) - dp(64); renderer.drawColor(Color::White, SkBlendMode::kSrcOver); - renderer.insertReorderBarrier(true); + renderer.enableZ(true); int x = dp(32); for (int i = 0; i < 4; i++) { @@ -52,7 +52,7 @@ public: mCards.push_back(card); } - renderer.insertReorderBarrier(false); + renderer.enableZ(false); } void doFrame(int frameNr) override { diff --git a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp index 80b5cc191089..f37bcbc3ee1b 100644 --- a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp +++ b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp @@ -29,7 +29,7 @@ public: sp<RenderNode> card; void createContent(int width, int height, Canvas& canvas) override { canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); - canvas.insertReorderBarrier(true); + canvas.enableZ(true); card = TestUtils::createNode(50, 50, 250, 250, [](RenderProperties& props, Canvas& canvas) { canvas.drawColor(0xFFFF00FF, SkBlendMode::kSrcOver); @@ -47,7 +47,7 @@ public: }); canvas.drawRenderNode(card.get()); - canvas.insertReorderBarrier(false); + canvas.enableZ(false); } void doFrame(int frameNr) override { int curFrame = frameNr % 150; diff --git a/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp b/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp index 314e922e9f38..163745b04ed2 100644 --- a/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp +++ b/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp @@ -27,7 +27,7 @@ public: std::vector<sp<RenderNode> > cards; void createContent(int width, int height, Canvas& canvas) override { canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); - canvas.insertReorderBarrier(true); + canvas.enableZ(true); int ci = 0; for (int x = 0; x < width; x += mSpacing) { @@ -45,7 +45,7 @@ public: } } - canvas.insertReorderBarrier(false); + canvas.enableZ(false); } void doFrame(int frameNr) override { int curFrame = frameNr % 50; diff --git a/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp b/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp index bdc991ba1890..c13e80e8c204 100644 --- a/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp +++ b/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp @@ -29,7 +29,7 @@ public: std::vector<sp<RenderNode> > cards; void createContent(int width, int height, Canvas& canvas) override { canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); - canvas.insertReorderBarrier(true); + canvas.enableZ(true); for (int x = dp(8); x < (width - dp(58)); x += dp(58)) { for (int y = dp(8); y < (height - dp(58)); y += dp(58)) { @@ -39,7 +39,7 @@ public: } } - canvas.insertReorderBarrier(false); + canvas.enableZ(false); } void doFrame(int frameNr) override { int curFrame = frameNr % 150; diff --git a/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp b/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp index a12fd4d69280..772b98e32220 100644 --- a/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp @@ -29,7 +29,7 @@ public: std::vector<sp<RenderNode> > cards; void createContent(int width, int height, Canvas& canvas) override { canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); - canvas.insertReorderBarrier(true); + canvas.enableZ(true); for (int x = dp(16); x < (width - dp(116)); x += dp(116)) { for (int y = dp(16); y < (height - dp(116)); y += dp(116)) { @@ -39,7 +39,7 @@ public: } } - canvas.insertReorderBarrier(false); + canvas.enableZ(false); } void doFrame(int frameNr) override { int curFrame = frameNr % 150; diff --git a/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp b/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp index 9f599100200e..0019da5fd80b 100644 --- a/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp @@ -29,7 +29,7 @@ public: std::vector<sp<RenderNode> > cards; void createContent(int width, int height, Canvas& canvas) override { canvas.drawColor(0xFFFFFFFF, SkBlendMode::kSrcOver); - canvas.insertReorderBarrier(true); + canvas.enableZ(true); int outset = 50; for (int i = 0; i < 10; i++) { @@ -39,7 +39,7 @@ public: cards.push_back(card); } - canvas.insertReorderBarrier(false); + canvas.enableZ(false); } void doFrame(int frameNr) override { int curFrame = frameNr % 10; diff --git a/libs/hwui/tests/common/scenes/TvApp.cpp b/libs/hwui/tests/common/scenes/TvApp.cpp index bac887053d2f..1b0a07a98b3f 100644 --- a/libs/hwui/tests/common/scenes/TvApp.cpp +++ b/libs/hwui/tests/common/scenes/TvApp.cpp @@ -67,7 +67,7 @@ public: mBg = createBitmapNode(canvas, 0xFF9C27B0, 0, 0, width, height); canvas.drawRenderNode(mBg.get()); - canvas.insertReorderBarrier(true); + canvas.enableZ(true); mSingleBitmap = mAllocator(dp(160), dp(120), kRGBA_8888_SkColorType, [](SkBitmap& skBitmap) { skBitmap.eraseColor(0xFF0000FF); }); @@ -80,7 +80,7 @@ public: mCards.push_back(card); } } - canvas.insertReorderBarrier(false); + canvas.enableZ(false); } void doFrame(int frameNr) override { diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp index 4ce6c32470ea..d393c693c774 100644 --- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp +++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp @@ -133,14 +133,14 @@ void BM_DisplayListCanvas_basicViewGroupDraw(benchmark::State& benchState) { int clipRestoreCount = canvas->save(SaveFlags::MatrixClip); canvas->clipRect(1, 1, 199, 199, SkClipOp::kIntersect); - canvas->insertReorderBarrier(true); + canvas->enableZ(true); // Draw child loop for (int i = 0; i < benchState.range(0); i++) { canvas->drawRenderNode(child.get()); } - canvas->insertReorderBarrier(false); + canvas->enableZ(false); canvas->restoreToCount(clipRestoreCount); delete canvas->finishRecording(); diff --git a/libs/hwui/tests/scripts/prep_generic.sh b/libs/hwui/tests/scripts/prep_generic.sh index 223bf373c65a..89826ff69463 100755 --- a/libs/hwui/tests/scripts/prep_generic.sh +++ b/libs/hwui/tests/scripts/prep_generic.sh @@ -28,11 +28,17 @@ # performance between different device models. # Fun notes for maintaining this file: -# `expr` can deal with ints > INT32_MAX, but if compares cannot. This is why we use MHz. -# `expr` can sometimes evaluate right-to-left. This is why we use parens. +# $((arithmetic expressions)) can deal with ints > INT32_MAX, but if compares cannot. This is +# why we use MHz. +# $((arithmetic expressions)) can sometimes evaluate right-to-left. This is why we use parens. # Everything below the initial host-check isn't bash - Android uses mksh # mksh allows `\n` in an echo, bash doesn't # can't use `awk` +# can't use `sed` +# can't use `cut` on < L +# can't use `expr` on < L + +ARG_CORES=${1:-big} CPU_TARGET_FREQ_PERCENT=50 GPU_TARGET_FREQ_PERCENT=50 @@ -43,7 +49,7 @@ if [ "`command -v getprop`" == "" ]; then echo "Pushing $0 and running it on device..." dest=/data/local/tmp/`basename $0` adb push $0 ${dest} - adb shell ${dest} + adb shell ${dest} $@ adb shell rm ${dest} exit else @@ -56,7 +62,7 @@ if [ "`command -v getprop`" == "" ]; then fi # require root -if [ "`id -u`" -ne "0" ]; then +if [[ `id` != "uid=0"* ]]; then echo "Not running as root, cannot lock clocks, aborting" exit -1 fi @@ -64,74 +70,175 @@ fi DEVICE=`getprop ro.product.device` MODEL=`getprop ro.product.model` -# Find CPU max frequency, and lock big cores to an available frequency -# that's >= $CPU_TARGET_FREQ_PERCENT% of max. Disable other cores. +if [ "$ARG_CORES" == "big" ]; then + CPU_IDEAL_START_FREQ_KHZ=0 +elif [ "$ARG_CORES" == "little" ]; then + CPU_IDEAL_START_FREQ_KHZ=100000000 ## finding min of max freqs, so start at 100M KHz (100 GHz) +else + echo "Invalid argument \$1 for ARG_CORES, should be 'big' or 'little', but was $ARG_CORES" + exit -1 +fi + +function_core_check() { + if [ "$ARG_CORES" == "big" ]; then + [ $1 -gt $2 ] + elif [ "$ARG_CORES" == "little" ]; then + [ $1 -lt $2 ] + else + echo "Invalid argument \$1 for ARG_CORES, should be 'big' or 'little', but was $ARG_CORES" + exit -1 + fi +} + +function_setup_go() { + if [ -f /d/fpsgo/common/force_onoff ]; then + # Disable fpsgo + echo 0 > /d/fpsgo/common/force_onoff + fpsgoState=`cat /d/fpsgo/common/force_onoff` + if [ "$fpsgoState" != "0" ] && [ "$fpsgoState" != "force off" ]; then + echo "Failed to disable fpsgo" + exit -1 + fi + fi +} + +# Find the min or max (little vs big) of CPU max frequency, and lock cores of the selected type to +# an available frequency that's >= $CPU_TARGET_FREQ_PERCENT% of max. Disable other cores. function_lock_cpu() { CPU_BASE=/sys/devices/system/cpu GOV=cpufreq/scaling_governor + # Options to make clock locking on go devices more sticky. + function_setup_go + # Find max CPU freq, and associated list of available freqs - cpuMaxFreq=0 + cpuIdealFreq=$CPU_IDEAL_START_FREQ_KHZ cpuAvailFreqCmpr=0 cpuAvailFreq=0 enableIndices='' disableIndices='' cpu=0 - while [ -f ${CPU_BASE}/cpu${cpu}/online ]; do - # enable core, so we can find its frequencies - echo 1 > ${CPU_BASE}/cpu${cpu}/online + while [ -d ${CPU_BASE}/cpu${cpu}/cpufreq ]; do + # Try to enable core, so we can find its frequencies. + # Note: In cases where the online file is inaccessible, it represents a + # core which cannot be turned off, so we simply assume it is enabled if + # this command fails. + if [ -f "$CPU_BASE/cpu$cpu/online" ]; then + echo 1 > ${CPU_BASE}/cpu${cpu}/online || true + fi + + # set userspace governor on all CPUs to ensure freq scaling is disabled + echo userspace > ${CPU_BASE}/cpu${cpu}/${GOV} maxFreq=`cat ${CPU_BASE}/cpu$cpu/cpufreq/cpuinfo_max_freq` availFreq=`cat ${CPU_BASE}/cpu$cpu/cpufreq/scaling_available_frequencies` availFreqCmpr=${availFreq// /-} - if [ ${maxFreq} -gt ${cpuMaxFreq} ]; then - # new highest max freq, look for cpus with same max freq and same avail freq list - cpuMaxFreq=${maxFreq} + if (function_core_check $maxFreq $cpuIdealFreq); then + # new min/max of max freq, look for cpus with same max freq and same avail freq list + cpuIdealFreq=${maxFreq} cpuAvailFreq=${availFreq} cpuAvailFreqCmpr=${availFreqCmpr} - if [ -z ${disableIndices} ]; then + if [ -z "$disableIndices" ]; then disableIndices="$enableIndices" else disableIndices="$disableIndices $enableIndices" fi enableIndices=${cpu} - elif [ ${maxFreq} == ${cpuMaxFreq} ] && [ ${availFreqCmpr} == ${cpuAvailFreqCmpr} ]; then + elif [ ${maxFreq} == ${cpuIdealFreq} ] && [ ${availFreqCmpr} == ${cpuAvailFreqCmpr} ]; then enableIndices="$enableIndices $cpu" else - disableIndices="$disableIndices $cpu" + if [ -z "$disableIndices" ]; then + disableIndices="$cpu" + else + disableIndices="$disableIndices $cpu" + fi fi + cpu=$(($cpu + 1)) done + # check that some CPUs will be enabled + if [ -z "$enableIndices" ]; then + echo "Failed to find any $ARG_CORES cores to enable, aborting." + exit -1 + fi + # Chose a frequency to lock to that's >= $CPU_TARGET_FREQ_PERCENT% of max # (below, 100M = 1K for KHz->MHz * 100 for %) - TARGET_FREQ_MHZ=`expr \( ${cpuMaxFreq} \* ${CPU_TARGET_FREQ_PERCENT} \) \/ 100000` + TARGET_FREQ_MHZ=$(( ($cpuIdealFreq * $CPU_TARGET_FREQ_PERCENT) / 100000 )) chosenFreq=0 + chosenFreqDiff=100000000 for freq in ${cpuAvailFreq}; do - freqMhz=`expr ${freq} \/ 1000` + freqMhz=$(( ${freq} / 1000 )) if [ ${freqMhz} -ge ${TARGET_FREQ_MHZ} ]; then - chosenFreq=${freq} - break + newChosenFreqDiff=$(( $freq - $TARGET_FREQ_MHZ )) + if [ $newChosenFreqDiff -lt $chosenFreqDiff ]; then + chosenFreq=${freq} + chosenFreqDiff=$(( $chosenFreq - $TARGET_FREQ_MHZ )) + fi fi done + # Lock wembley clocks using high-priority op code method. + # This block depends on the shell utility awk, which is only available on API 27+ + if [ "$DEVICE" == "wembley" ]; then + # Get list of available frequencies to lock to by parsing the op-code list. + AVAIL_OP_FREQS=`cat /proc/cpufreq/MT_CPU_DVFS_LL/cpufreq_oppidx \ + | awk '{print $2}' \ + | tail -n +3 \ + | while read line; do + echo "${line:1:${#line}-2}" + done` + + # Compute the closest available frequency to the desired frequency, $chosenFreq. + # This assumes the op codes listen in /proc/cpufreq/MT_CPU_DVFS_LL/cpufreq_oppidx are listed + # in order and 0-indexed. + opCode=-1 + opFreq=0 + currOpCode=-1 + for currOpFreq in $AVAIL_OP_FREQS; do + currOpCode=$((currOpCode + 1)) + + prevDiff=$((chosenFreq-opFreq)) + prevDiff=`function_abs $prevDiff` + currDiff=$((chosenFreq-currOpFreq)) + currDiff=`function_abs $currDiff` + if [ $currDiff -lt $prevDiff ]; then + opCode="$currOpCode" + opFreq="$currOpFreq" + fi + done + + echo "$opCode" > /proc/ppm/policy/ut_fix_freq_idx + fi + # enable 'big' CPUs for cpu in ${enableIndices}; do freq=${CPU_BASE}/cpu$cpu/cpufreq - echo 1 > ${CPU_BASE}/cpu${cpu}/online - echo userspace > ${CPU_BASE}/cpu${cpu}/${GOV} + # Try to enable core, so we can find its frequencies. + # Note: In cases where the online file is inaccessible, it represents a + # core which cannot be turned off, so we simply assume it is enabled if + # this command fails. + if [ -f "$CPU_BASE/cpu$cpu/online" ]; then + echo 1 > ${CPU_BASE}/cpu${cpu}/online || true + fi + + # scaling_max_freq must be set before scaling_min_freq echo ${chosenFreq} > ${freq}/scaling_max_freq echo ${chosenFreq} > ${freq}/scaling_min_freq echo ${chosenFreq} > ${freq}/scaling_setspeed + # Give system a bit of time to propagate the change to scaling_setspeed. + sleep 0.1 + # validate setting the freq worked obsCur=`cat ${freq}/scaling_cur_freq` obsMin=`cat ${freq}/scaling_min_freq` obsMax=`cat ${freq}/scaling_max_freq` - if [ obsCur -ne ${chosenFreq} ] || [ obsMin -ne ${chosenFreq} ] || [ obsMax -ne ${chosenFreq} ]; then + if [ "$obsCur" -ne "$chosenFreq" ] || [ "$obsMin" -ne "$chosenFreq" ] || [ "$obsMax" -ne "$chosenFreq" ]; then echo "Failed to set CPU$cpu to $chosenFreq Hz! Aborting..." echo "scaling_cur_freq = $obsCur" echo "scaling_min_freq = $obsMin" @@ -145,8 +252,20 @@ function_lock_cpu() { echo 0 > ${CPU_BASE}/cpu${cpu}/online done - echo "\nLocked CPUs ${enableIndices// /,} to $chosenFreq / $maxFreq KHz" + echo "==================================" + echo "Locked CPUs ${enableIndices// /,} to $chosenFreq / $cpuIdealFreq KHz" echo "Disabled CPUs ${disableIndices// /,}" + echo "==================================" +} + +# Returns the absolute value of the first arg passed to this helper. +function_abs() { + n=$1 + if [ $n -lt 0 ]; then + echo "$((n * -1 ))" + else + echo "$n" + fi } # If we have a Qualcomm GPU, find its max frequency, and lock to @@ -154,12 +273,12 @@ function_lock_cpu() { function_lock_gpu_kgsl() { if [ ! -d /sys/class/kgsl/kgsl-3d0/ ]; then # not kgsl, abort - echo "\nCurrently don't support locking GPU clocks of $MODEL ($DEVICE)" + echo "Currently don't support locking GPU clocks of $MODEL ($DEVICE)" return -1 fi if [ ${DEVICE} == "walleye" ] || [ ${DEVICE} == "taimen" ]; then # Workaround crash - echo "\nUnable to lock GPU clocks of $MODEL ($DEVICE)" + echo "Unable to lock GPU clocks of $MODEL ($DEVICE)" return -1 fi @@ -174,13 +293,13 @@ function_lock_gpu_kgsl() { done # (below, 100M = 1M for MHz * 100 for %) - TARGET_FREQ_MHZ=`expr \( ${gpuMaxFreq} \* ${GPU_TARGET_FREQ_PERCENT} \) \/ 100000000` + TARGET_FREQ_MHZ=$(( (${gpuMaxFreq} * ${GPU_TARGET_FREQ_PERCENT}) / 100000000 )) chosenFreq=${gpuMaxFreq} index=0 chosenIndex=0 for freq in ${gpuAvailFreq}; do - freqMhz=`expr ${freq} \/ 1000000` + freqMhz=$(( ${freq} / 1000000 )) if [ ${freqMhz} -ge ${TARGET_FREQ_MHZ} ] && [ ${chosenFreq} -ge ${freq} ]; then # note avail freq are generally in reverse order, so we don't break out of this loop chosenFreq=${freq} @@ -190,7 +309,7 @@ function_lock_gpu_kgsl() { done lastIndex=$(($index - 1)) - firstFreq=`echo $gpuAvailFreq | cut -d" " -f1` + firstFreq=`function_cut_first_from_space_seperated_list $gpuAvailFreq` if [ ${gpuMaxFreq} != ${firstFreq} ]; then # pwrlevel is index of desired freq among available frequencies, from highest to lowest. @@ -226,24 +345,40 @@ function_lock_gpu_kgsl() { echo "index = $chosenIndex" exit -1 fi - echo "\nLocked GPU to $chosenFreq / $gpuMaxFreq Hz" + echo "Locked GPU to $chosenFreq / $gpuMaxFreq Hz" +} + +# cut is not available on some devices (Nexus 5 running LRX22C). +function_cut_first_from_space_seperated_list() { + list=$1 + + for freq in $list; do + echo $freq + break + done } # kill processes that manage thermals / scaling -stop thermal-engine -stop perfd -stop vendor.thermal-engine -stop vendor.perfd +stop thermal-engine || true +stop perfd || true +stop vendor.thermal-engine || true +stop vendor.perfd || true +setprop vendor.powerhal.init 0 || true +setprop ctl.interface_restart android.hardware.power@1.0::IPower/default || true function_lock_cpu -function_lock_gpu_kgsl +if [ "$DEVICE" -ne "wembley" ]; then + function_lock_gpu_kgsl +else + echo "Unable to lock gpu clocks of $MODEL ($DEVICE)." +fi # Memory bus - hardcoded per-device for now if [ ${DEVICE} == "marlin" ] || [ ${DEVICE} == "sailfish" ]; then echo 13763 > /sys/class/devfreq/soc:qcom,gpubw/max_freq else - echo "\nUnable to lock memory bus of $MODEL ($DEVICE)." + echo "Unable to lock memory bus of $MODEL ($DEVICE)." fi -echo "\n$DEVICE clocks have been locked - to reset, reboot the device\n"
\ No newline at end of file +echo "$DEVICE clocks have been locked - to reset, reboot the device" diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp index c83a3c88cbdd..edd3e4e4f4d4 100644 --- a/libs/hwui/tests/unit/CacheManagerTests.cpp +++ b/libs/hwui/tests/unit/CacheManagerTests.cpp @@ -26,7 +26,7 @@ using namespace android; using namespace android::uirenderer; using namespace android::uirenderer::renderthread; -static size_t getCacheUsage(GrContext* grContext) { +static size_t getCacheUsage(GrDirectContext* grContext) { size_t cacheUsage; grContext->getResourceCacheUsage(nullptr, &cacheUsage); return cacheUsage; @@ -35,7 +35,7 @@ static size_t getCacheUsage(GrContext* grContext) { RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, trimMemory) { int32_t width = DeviceInfo::get()->getWidth(); int32_t height = DeviceInfo::get()->getHeight(); - GrContext* grContext = renderThread.getGrContext(); + GrDirectContext* grContext = renderThread.getGrContext(); ASSERT_TRUE(grContext != nullptr); // create pairs of offscreen render targets and images until we exceed the @@ -47,7 +47,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, trimMemory) { sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(grContext, SkBudgeted::kYes, info); surface->getCanvas()->drawColor(SK_AlphaTRANSPARENT); - grContext->flush(); + grContext->flushAndSubmit(); surfaces.push_back(surface); } diff --git a/libs/hwui/tests/unit/CanvasContextTests.cpp b/libs/hwui/tests/unit/CanvasContextTests.cpp index 28cff5b9b154..1771c3590e10 100644 --- a/libs/hwui/tests/unit/CanvasContextTests.cpp +++ b/libs/hwui/tests/unit/CanvasContextTests.cpp @@ -42,14 +42,3 @@ RENDERTHREAD_TEST(CanvasContext, create) { canvasContext->destroy(); } - -RENDERTHREAD_TEST(CanvasContext, invokeFunctor) { - TestUtils::MockFunctor functor; - CanvasContext::invokeFunctor(renderThread, &functor); - if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { - // we currently don't support OpenGL WebViews on the Vulkan backend - ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeProcessNoContext); - } else { - ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeProcess); - } -} diff --git a/libs/hwui/tests/unit/FatalTestCanvas.h b/libs/hwui/tests/unit/FatalTestCanvas.h index 1723c2eb4948..76ae0853b477 100644 --- a/libs/hwui/tests/unit/FatalTestCanvas.h +++ b/libs/hwui/tests/unit/FatalTestCanvas.h @@ -81,21 +81,6 @@ public: const SkPaint*) { ADD_FAILURE() << "onDrawImageLattice not expected in this test"; } - void onDrawBitmap(const SkBitmap&, SkScalar dx, SkScalar dy, const SkPaint*) { - ADD_FAILURE() << "onDrawBitmap not expected in this test"; - } - void onDrawBitmapRect(const SkBitmap&, const SkRect*, const SkRect&, const SkPaint*, - SrcRectConstraint) { - ADD_FAILURE() << "onDrawBitmapRect not expected in this test"; - } - void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst, - const SkPaint*) { - ADD_FAILURE() << "onDrawBitmapNine not expected in this test"; - } - void onDrawBitmapLattice(const SkBitmap&, const Lattice& lattice, const SkRect& dst, - const SkPaint*) { - ADD_FAILURE() << "onDrawBitmapLattice not expected in this test"; - } void onClipRRect(const SkRRect& rrect, SkClipOp, ClipEdgeStyle) { ADD_FAILURE() << "onClipRRect not expected in this test"; } diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp index 3632be06c45f..7aa6be8722cf 100644 --- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp @@ -108,27 +108,27 @@ protected: TEST(RenderNodeDrawable, zReorder) { auto parent = TestUtils::createSkiaNode(0, 0, 100, 100, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - canvas.insertReorderBarrier(true); - canvas.insertReorderBarrier(false); + canvas.enableZ(true); + canvas.enableZ(false); drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder drawOrderedRect(&canvas, 1); - canvas.insertReorderBarrier(true); + canvas.enableZ(true); drawOrderedNode(&canvas, 6, 2.0f); drawOrderedRect(&canvas, 3); drawOrderedNode(&canvas, 4, 0.0f); drawOrderedRect(&canvas, 5); drawOrderedNode(&canvas, 2, -2.0f); drawOrderedNode(&canvas, 7, 2.0f); - canvas.insertReorderBarrier(false); + canvas.enableZ(false); drawOrderedRect(&canvas, 8); drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder - canvas.insertReorderBarrier(true); // reorder a node ahead of drawrect op + canvas.enableZ(true); // reorder a node ahead of drawrect op drawOrderedRect(&canvas, 11); drawOrderedNode(&canvas, 10, -1.0f); - canvas.insertReorderBarrier(false); - canvas.insertReorderBarrier(true); // test with two empty reorder sections - canvas.insertReorderBarrier(true); - canvas.insertReorderBarrier(false); + canvas.enableZ(false); + canvas.enableZ(true); // test with two empty reorder sections + canvas.enableZ(true); + canvas.enableZ(false); drawOrderedRect(&canvas, 12); }); @@ -1142,7 +1142,7 @@ TEST(ReorderBarrierDrawable, testShadowMatrix) { 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { canvas.translate(TRANSLATE_X, TRANSLATE_Y); - canvas.insertReorderBarrier(true); + canvas.enableZ(true); auto node = TestUtils::createSkiaNode( CASTER_X, CASTER_Y, CASTER_X + CASTER_WIDTH, CASTER_Y + CASTER_HEIGHT, @@ -1152,7 +1152,7 @@ TEST(ReorderBarrierDrawable, testShadowMatrix) { props.mutableOutline().setShouldClip(true); }); canvas.drawRenderNode(node.get()); - canvas.insertReorderBarrier(false); + canvas.enableZ(false); }); // create a canvas not backed by any device/pixels, but with dimensions to avoid quick rejection @@ -1169,7 +1169,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaRecordingCanvas, drawVectorDrawable) { class VectorDrawableTestCanvas : public TestCanvasBase { public: VectorDrawableTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {} - void onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst, + void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst, const SkPaint* paint, SrcRectConstraint constraint) override { const int index = mDrawCounter++; switch (index) { diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp index 1cd9bd8ee9d9..c19e1ed6ce75 100644 --- a/libs/hwui/tests/unit/RenderNodeTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeTests.cpp @@ -231,39 +231,41 @@ TEST(RenderNode, multiTreeValidity) { } TEST(RenderNode, releasedCallback) { - class DecRefOnReleased : public GlFunctorLifecycleListener { - public: - explicit DecRefOnReleased(int* refcnt) : mRefCnt(refcnt) {} - void onGlFunctorReleased(Functor* functor) override { *mRefCnt -= 1; } - - private: - int* mRefCnt; - }; - - int refcnt = 0; - sp<DecRefOnReleased> listener(new DecRefOnReleased(&refcnt)); - Functor noopFunctor; + int functor = WebViewFunctor_create( + nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES); auto node = TestUtils::createNode(0, 0, 200, 400, [&](RenderProperties& props, Canvas& canvas) { - refcnt++; - canvas.callDrawGLFunction(&noopFunctor, listener.get()); + canvas.drawWebViewFunctor(functor); + }); + TestUtils::runOnRenderThreadUnmanaged([&] (RenderThread&) { + TestUtils::syncHierarchyPropertiesAndDisplayList(node); }); - TestUtils::syncHierarchyPropertiesAndDisplayList(node); - EXPECT_EQ(1, refcnt); + auto& counts = TestUtils::countsForFunctor(functor); + EXPECT_EQ(1, counts.sync); + EXPECT_EQ(0, counts.destroyed); TestUtils::recordNode(*node, [&](Canvas& canvas) { - refcnt++; - canvas.callDrawGLFunction(&noopFunctor, listener.get()); + canvas.drawWebViewFunctor(functor); }); - EXPECT_EQ(2, refcnt); + EXPECT_EQ(1, counts.sync); + EXPECT_EQ(0, counts.destroyed); - TestUtils::syncHierarchyPropertiesAndDisplayList(node); - EXPECT_EQ(1, refcnt); + TestUtils::runOnRenderThreadUnmanaged([&] (RenderThread&) { + TestUtils::syncHierarchyPropertiesAndDisplayList(node); + }); + EXPECT_EQ(2, counts.sync); + EXPECT_EQ(0, counts.destroyed); + + WebViewFunctor_release(functor); + EXPECT_EQ(2, counts.sync); + EXPECT_EQ(0, counts.destroyed); TestUtils::recordNode(*node, [](Canvas& canvas) {}); - EXPECT_EQ(1, refcnt); - TestUtils::syncHierarchyPropertiesAndDisplayList(node); - EXPECT_EQ(0, refcnt); + TestUtils::runOnRenderThreadUnmanaged([&] (RenderThread&) { + TestUtils::syncHierarchyPropertiesAndDisplayList(node); + }); + EXPECT_EQ(2, counts.sync); + EXPECT_EQ(1, counts.destroyed); } RENDERTHREAD_TEST(RenderNode, prepareTree_nullableDisplayList) { diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp index d08aea668b2a..74a565439f85 100644 --- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp +++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp @@ -48,7 +48,10 @@ TEST(SkiaDisplayList, reset) { SkCanvas dummyCanvas; RenderNodeDrawable drawable(nullptr, &dummyCanvas); skiaDL->mChildNodes.emplace_back(nullptr, &dummyCanvas); - GLFunctorDrawable functorDrawable(nullptr, nullptr, &dummyCanvas); + int functor1 = WebViewFunctor_create( + nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES); + GLFunctorDrawable functorDrawable{functor1, &dummyCanvas}; + WebViewFunctor_release(functor1); skiaDL->mChildFunctors.push_back(&functorDrawable); skiaDL->mMutableImages.push_back(nullptr); skiaDL->appendVD(nullptr); @@ -97,16 +100,13 @@ TEST(SkiaDisplayList, syncContexts) { SkiaDisplayList skiaDL; SkCanvas dummyCanvas; - TestUtils::MockFunctor functor; - GLFunctorDrawable functorDrawable(&functor, nullptr, &dummyCanvas); - skiaDL.mChildFunctors.push_back(&functorDrawable); - int functor2 = WebViewFunctor_create( + int functor1 = WebViewFunctor_create( nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES); - auto& counts = TestUtils::countsForFunctor(functor2); + auto& counts = TestUtils::countsForFunctor(functor1); skiaDL.mChildFunctors.push_back( - skiaDL.allocateDrawable<GLFunctorDrawable>(functor2, &dummyCanvas)); - WebViewFunctor_release(functor2); + skiaDL.allocateDrawable<GLFunctorDrawable>(functor1, &dummyCanvas)); + WebViewFunctor_release(functor1); SkRect bounds = SkRect::MakeWH(200, 200); VectorDrawableRoot vectorDrawable(new VectorDrawable::Group()); @@ -120,7 +120,6 @@ TEST(SkiaDisplayList, syncContexts) { }); }); - EXPECT_EQ(functor.getLastMode(), DrawGlInfo::kModeSync); EXPECT_EQ(counts.sync, 1); EXPECT_EQ(counts.destroyed, 0); EXPECT_EQ(vectorDrawable.mutateProperties()->getBounds(), bounds); diff --git a/libs/hwui/utils/Blur.h b/libs/hwui/utils/Blur.h index d6b41b83def8..6b822f01e25c 100644 --- a/libs/hwui/utils/Blur.h +++ b/libs/hwui/utils/Blur.h @@ -26,9 +26,9 @@ namespace uirenderer { class Blur { public: // If radius > 0, return the corresponding sigma, else return 0 - ANDROID_API static float convertRadiusToSigma(float radius); + static float convertRadiusToSigma(float radius); // If sigma > 0.5, return the corresponding radius, else return 0 - ANDROID_API static float convertSigmaToRadius(float sigma); + static float convertSigmaToRadius(float sigma); // If the original radius was on an integer boundary then after the sigma to // radius conversion a small rounding error may be introduced. This function // accounts for that error and snaps to the appropriate integer boundary. diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp index 71a27ced2e09..87512f0354c8 100644 --- a/libs/hwui/utils/Color.cpp +++ b/libs/hwui/utils/Color.cpp @@ -16,8 +16,8 @@ #include "Color.h" -#include <utils/Log.h> #include <ui/ColorSpace.h> +#include <utils/Log.h> #ifdef __ANDROID__ // Layoutlib does not support hardware buffers or native windows #include <android/hardware_buffer.h> @@ -26,6 +26,7 @@ #include <algorithm> #include <cmath> +#include <Properties.h> namespace android { namespace uirenderer { @@ -72,46 +73,34 @@ SkImageInfo BufferDescriptionToImageInfo(const AHardwareBuffer_Desc& bufferDesc, sk_sp<SkColorSpace> colorSpace) { return createImageInfo(bufferDesc.width, bufferDesc.height, bufferDesc.format, colorSpace); } -#endif -android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType) { +uint32_t ColorTypeToBufferFormat(SkColorType colorType) { switch (colorType) { case kRGBA_8888_SkColorType: - return PIXEL_FORMAT_RGBA_8888; + return AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; case kRGBA_F16_SkColorType: - return PIXEL_FORMAT_RGBA_FP16; + return AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT; case kRGB_565_SkColorType: - return PIXEL_FORMAT_RGB_565; + return AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM; case kRGB_888x_SkColorType: - return PIXEL_FORMAT_RGBX_8888; + return AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM; case kRGBA_1010102_SkColorType: - return PIXEL_FORMAT_RGBA_1010102; + return AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM; case kARGB_4444_SkColorType: - return PIXEL_FORMAT_RGBA_4444; + // Hardcoding the value from android::PixelFormat + static constexpr uint64_t kRGBA4444 = 7; + return kRGBA4444; default: ALOGV("Unsupported colorType: %d, return RGBA_8888 by default", (int)colorType); - return PIXEL_FORMAT_RGBA_8888; - } -} - -SkColorType PixelFormatToColorType(android::PixelFormat format) { - switch (format) { - case PIXEL_FORMAT_RGBX_8888: return kRGB_888x_SkColorType; - case PIXEL_FORMAT_RGBA_8888: return kRGBA_8888_SkColorType; - case PIXEL_FORMAT_RGBA_FP16: return kRGBA_F16_SkColorType; - case PIXEL_FORMAT_RGB_565: return kRGB_565_SkColorType; - case PIXEL_FORMAT_RGBA_1010102: return kRGBA_1010102_SkColorType; - case PIXEL_FORMAT_RGBA_4444: return kARGB_4444_SkColorType; - default: - ALOGV("Unsupported PixelFormat: %d, return kUnknown_SkColorType by default", format); - return kUnknown_SkColorType; + return AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; } } +#endif namespace { static constexpr skcms_TransferFunction k2Dot6 = {2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}; -// Skia's SkNamedGamut::kDCIP3 is based on a white point of D65. This gamut +// Skia's SkNamedGamut::kDisplayP3 is based on a white point of D65. This gamut // matches the white point used by ColorSpace.Named.DCIP3. static constexpr skcms_Matrix3x3 kDCIP3 = {{ {0.486143, 0.323835, 0.154234}, @@ -180,7 +169,7 @@ android_dataspace ColorSpaceToADataSpace(SkColorSpace* colorSpace, SkColorType c } } - if (nearlyEqual(fn, SkNamedTransferFn::kSRGB) && nearlyEqual(gamut, SkNamedGamut::kDCIP3)) { + if (nearlyEqual(fn, SkNamedTransferFn::kSRGB) && nearlyEqual(gamut, SkNamedGamut::kDisplayP3)) { return HAL_DATASPACE_DISPLAY_P3; } @@ -221,7 +210,7 @@ sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) { gamut = SkNamedGamut::kRec2020; break; case HAL_DATASPACE_STANDARD_DCI_P3: - gamut = SkNamedGamut::kDCIP3; + gamut = SkNamedGamut::kDisplayP3; break; case HAL_DATASPACE_STANDARD_ADOBE_RGB: gamut = SkNamedGamut::kAdobeRGB; @@ -356,5 +345,23 @@ SkColor LabToSRGB(const Lab& lab, SkAlpha alpha) { static_cast<uint8_t>(rgb.b * 255)); } +skcms_TransferFunction GetPQSkTransferFunction(float sdr_white_level) { + if (sdr_white_level <= 0.f) { + sdr_white_level = Properties::defaultSdrWhitePoint; + } + // The generic PQ transfer function produces normalized luminance values i.e. + // the range 0-1 represents 0-10000 nits for the reference display, but we + // want to map 1.0 to |sdr_white_level| nits so we need to scale accordingly. + const double w = 10000. / sdr_white_level; + // Distribute scaling factor W by scaling A and B with X ^ (1/F): + // ((A + Bx^C) / (D + Ex^C))^F * W = ((A + Bx^C) / (D + Ex^C) * W^(1/F))^F + // See https://crbug.com/1058580#c32 for discussion. + skcms_TransferFunction fn = SkNamedTransferFn::kPQ; + const double ws = pow(w, 1. / fn.f); + fn.a = ws * fn.a; + fn.b = ws * fn.b; + return fn; +} + } // namespace uirenderer } // namespace android diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h index a76f7e499c37..1654072fd264 100644 --- a/libs/hwui/utils/Color.h +++ b/libs/hwui/utils/Color.h @@ -16,14 +16,12 @@ #ifndef COLOR_H #define COLOR_H -#include <math.h> -#include <cutils/compiler.h> -#include <system/graphics.h> -#include <ui/PixelFormat.h> - #include <SkColor.h> #include <SkColorSpace.h> #include <SkImageInfo.h> +#include <cutils/compiler.h> +#include <math.h> +#include <system/graphics.h> struct ANativeWindow_Buffer; struct AHardwareBuffer_Desc; @@ -93,15 +91,14 @@ static constexpr float EOCF_sRGB(float srgb) { } #ifdef __ANDROID__ // Layoutlib does not support hardware buffers or native windows -ANDROID_API SkImageInfo ANativeWindowToImageInfo(const ANativeWindow_Buffer& buffer, +SkImageInfo ANativeWindowToImageInfo(const ANativeWindow_Buffer& buffer, sk_sp<SkColorSpace> colorSpace); SkImageInfo BufferDescriptionToImageInfo(const AHardwareBuffer_Desc& bufferDesc, sk_sp<SkColorSpace> colorSpace); -#endif -android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType); -ANDROID_API SkColorType PixelFormatToColorType(android::PixelFormat format); +uint32_t ColorTypeToBufferFormat(SkColorType colorType); +#endif ANDROID_API sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace); @@ -129,6 +126,7 @@ struct Lab { Lab sRGBToLab(SkColor color); SkColor LabToSRGB(const Lab& lab, SkAlpha alpha); +skcms_TransferFunction GetPQSkTransferFunction(float sdr_white_level = 0.f); } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h index cc8d83f10d43..62bf39ca8a7a 100644 --- a/libs/hwui/utils/MathUtils.h +++ b/libs/hwui/utils/MathUtils.h @@ -31,7 +31,9 @@ public: * Check for floats that are close enough to zero. */ inline static bool isZero(float value) { - return (value >= -NON_ZERO_EPSILON) && (value <= NON_ZERO_EPSILON); + // Using fabsf is more performant as ARM computes + // fabsf in a single instruction. + return fabsf(value) <= NON_ZERO_EPSILON; } inline static bool isOne(float value) { diff --git a/libs/hwui/GlFunctorLifecycleListener.h b/libs/hwui/utils/NdkUtils.cpp index 5adc46961c8b..de6274ee5bcc 100644 --- a/libs/hwui/GlFunctorLifecycleListener.h +++ b/libs/hwui/utils/NdkUtils.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,19 +14,19 @@ * limitations under the License. */ -#pragma once - -#include <utils/Functor.h> -#include <utils/RefBase.h> +#include <utils/NdkUtils.h> namespace android { namespace uirenderer { -class GlFunctorLifecycleListener : public VirtualLightRefBase { -public: - virtual ~GlFunctorLifecycleListener() {} - virtual void onGlFunctorReleased(Functor* functor) = 0; -}; +UniqueAHardwareBuffer allocateAHardwareBuffer(const AHardwareBuffer_Desc& desc) { + AHardwareBuffer* buffer; + if (AHardwareBuffer_allocate(&desc, &buffer) != 0) { + return nullptr; + } else { + return UniqueAHardwareBuffer{buffer}; + } +} } // namespace uirenderer } // namespace android diff --git a/libs/hwui/utils/NdkUtils.h b/libs/hwui/utils/NdkUtils.h new file mode 100644 index 000000000000..f218eb2ff404 --- /dev/null +++ b/libs/hwui/utils/NdkUtils.h @@ -0,0 +1,38 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android/hardware_buffer.h> + +#include <memory> + +namespace android { +namespace uirenderer { + +// Deleter for an AHardwareBuffer, to be passed to an std::unique_ptr. +struct AHardwareBuffer_deleter { + void operator()(AHardwareBuffer* ahb) const { AHardwareBuffer_release(ahb); } +}; + +using UniqueAHardwareBuffer = std::unique_ptr<AHardwareBuffer, AHardwareBuffer_deleter>; + +// Allocates a UniqueAHardwareBuffer with the provided buffer description. +// Returns nullptr if allocation did not succeed. +UniqueAHardwareBuffer allocateAHardwareBuffer(const AHardwareBuffer_Desc& desc); + +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/utils/VectorDrawableUtils.h b/libs/hwui/utils/VectorDrawableUtils.h index 4be48fb942fc..4f63959165db 100644 --- a/libs/hwui/utils/VectorDrawableUtils.h +++ b/libs/hwui/utils/VectorDrawableUtils.h @@ -28,10 +28,10 @@ namespace uirenderer { class VectorDrawableUtils { public: - ANDROID_API static bool canMorph(const PathData& morphFrom, const PathData& morphTo); - ANDROID_API static bool interpolatePathData(PathData* outData, const PathData& morphFrom, + static bool canMorph(const PathData& morphFrom, const PathData& morphTo); + static bool interpolatePathData(PathData* outData, const PathData& morphFrom, const PathData& morphTo, float fraction); - ANDROID_API static void verbsToPath(SkPath* outPath, const PathData& data); + static void verbsToPath(SkPath* outPath, const PathData& data); static void interpolatePaths(PathData* outPathData, const PathData& from, const PathData& to, float fraction); }; diff --git a/libs/usb/tests/AccessoryChat/AndroidManifest.xml b/libs/usb/tests/AccessoryChat/AndroidManifest.xml index 6667ebaa4d49..b93eeab11324 100644 --- a/libs/usb/tests/AccessoryChat/AndroidManifest.xml +++ b/libs/usb/tests/AccessoryChat/AndroidManifest.xml @@ -15,26 +15,28 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.accessorychat"> + package="com.android.accessorychat"> - <uses-feature android:name="android.hardware.usb.accessory" /> + <uses-feature android:name="android.hardware.usb.accessory"/> <application android:label="Accessory Chat"> - <activity android:name="AccessoryChat" android:label="Accessory Chat"> + <activity android:name="AccessoryChat" + android:label="Accessory Chat" + android:exported="true"> <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.DEFAULT" /> - <category android:name="android.intent.category.LAUNCHER" /> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.DEFAULT"/> + <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> <intent-filter> - <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" /> + <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"/> </intent-filter> <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" - android:resource="@xml/accessory_filter" /> + android:resource="@xml/accessory_filter"/> </activity> </application> - <uses-sdk android:minSdkVersion="12" /> + <uses-sdk android:minSdkVersion="12"/> </manifest> |