Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

android: fix broken rendering in emulators #31727

Merged
merged 1 commit into from Mar 18, 2024

Conversation

mukilan
Copy link
Member

@mukilan mukilan commented Mar 18, 2024

Android's OpenGL emulation layer (goldfish-opengl) has pre-processing logic that looks for samplers of the type samplerExternalOES and does a simple textual replacement to change the type to sampler2D before compilation. It also marks the sampler as 'replaced' so it can emulate the correct type at runtime.

However, this logic can lead to false positives when the sampler is declared inside conditional macros. Hence, the sampler's type can be incorrectly marked as samplerExternalOES even though the #if, #ifdef conditional logic would have declared the type as sampler2D. This seems to be a known limitation.

WebRender (in particular the shared.glsl include) has such conditional declaration of the texture units used from the shaders. In particular, the sampler sColor0 here is declared within ifdefs to have different types depending on the flags enabled, to allow the shader to work with different image target kinds.

WebRender also maintain two versions of the compiled shaders in its cache:

  1. An unoptimized version with all the conditional logic preserved in the source until the shader is compiled at runtime on Android, when the shader is actually used.
  2. Multiple optimized versions for combinations of features required These versions are produced during Servo build. Thus the optimized versions eliminate most of the conditional declarations at build time.

The bug in Servo with current code is because, by default, WebRender uses the unoptimized versions of the shaders. This means the conditional GLSL source is evaluated at runtime by the Android emulator and thus ends up with the incorrect type for the sColor0 sampler unit, which breaks the texture sampling in the fragment shader, causing it to always return Vec4(0, 0, 0, 1) and rendering all elements on the page black.

This change forces WebRender to use the optimized version as a workaround - the optimized versions have unconditional code for the sampler declarations so are not susceptible to the emulator issue.

Fixes #31726.

android fixed

Android's OpenGL emulation layer (goldfish-opengl) has pre-processing
logic that looks for samplers of the type `samplerExternalOES` and
does a simple textual [replacement to change the type][1] to `sampler2D`
before compilation. It also [marks the sampler][2] as 'replaced' so
it can emulate the correct type at runtime.

However, this logic can lead to false positives when the sampler is
declared inside conditional macros. Hence, the sampler's type can be
incorrectly marked as `samplerExternalOES` even though the #if, #ifdef
conditional logic would have declared the type as `sampler2D`.
This seems to be a [known limitation][3].

WebRender (in particular the shared.glsl include) has such conditional
declaration of the texture units used from the shaders.
In particular, the sampler [sColor0 here][4] is declared within ifdefs
to have different types depending on the flags enabled, to allow the
shader to work with different image target kinds.

WebRender also maintain two versions of the compiled shaders in its cache:
  1. An unoptimized version with all the conditional logic preserved
     in the source until the shader is compiled at runtime on Android,
     when the shader is actually used.
  2. Multiple optimized versions for combinations of features required
     These versions are produced during Servo [build][5]. Thus the
     optimized versions eliminate most of the conditional declarations
     at build time.

The bug in Servo with current code is because, [by default][6], WebRender
uses the *unoptimized* versions of the shaders. This means the conditional
GLSL source is evaluated at runtime by the Android emulator and thus ends
up with the incorrect type for the sColor0 sampler unit, which breaks the
[texture sampling in the fragment shader][7], causing it to always return
Vec4(0, 0, 0, 1) and rendering all elements on the page black.

This change forces WebRender to use the *optimized* version as a
workaround - the optimized versions have unconditional code for the
sampler declarations so are not susceptible to the emulator issue.

[1]: https://android.googlesource.com/device/generic/goldfish-opengl/+/refs/tags/android-platform-11.0.0_r40/system/GLESv2_enc/GL2Encoder.cpp#1644
[2]: https://android.googlesource.com/device/generic/goldfish-opengl/+/refs/tags/android-platform-11.0.0_r40/system/GLESv2_enc/GL2Encoder.cpp#1673
[3]: https://android.googlesource.com/device/generic/goldfish-opengl/+/refs/tags/android-platform-11.0.0_r40/system/GLESv2_enc/GL2Encoder.cpp#1571
[4]: https://github.com/servo/webrender/blob/b36399019cadcadeeed53759c96870577d4da136/webrender/res/shared.glsl#L206
[5]: https://github.com/servo/webrender/blob/b36399019cadcadeeed53759c96870577d4da136/webrender/build.rs#L289
[6]: https://github.com/servo/webrender/blob/b36399019cadcadeeed53759c96870577d4da136/webrender/src/renderer/init.rs#L214
[7]: https://github.com/servo/webrender/blob/b36399019cadcadeeed53759c96870577d4da136/webrender/res/composite.glsl#L189

Fixes servo#31726.

Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
// on Android emulators with unoptimized shaders. This is due to a known
// issue in the emulator's OpenGL emulation layer.
// See: https://github.com/servo/servo/issues/31726
use_optimized_shaders: true,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've manually tested the NixOS and Android builds but I'm no sure if this change could potentially break other platforms. WebRender does have fallback logic to use unoptimized versions if the optimized version is not present in the build. However, let me know if we should instead enable this only for android i.e use_optimized_shaders: cfg!(target_os = "android") instead.

@mrobinson mrobinson added the T-linux-wpt-2020 Do a try run of the WPT label Mar 18, 2024
@github-actions github-actions bot removed the T-linux-wpt-2020 Do a try run of the WPT label Mar 18, 2024
Copy link

🔨 Triggering try run (#8323421004) for Linux WPT

@mrobinson
Copy link
Member

Wow! Excellent detective work. It seems likely that we'd prefer to use something called "optimized shaders" for production and release builds. Have you been able to report this problem upstream in WebRender?

Copy link

Test results for linux-wpt-layout-2020 from try job (#8323421004):

Flaky unexpected result (12)
  • OK /_mozilla/mozilla/task_queue_throttling.any.html (#22519)
    • FAIL [expected PASS] subtest: Throttling the performance timeline task queue.

      assert_true: expected true got false
      

  • TIMEOUT [expected OK] /_webgl/conformance/glsl/misc/shader-with-non-reserved-words.html (#16216)
    • NOTRUN [expected PASS] subtest: Overall test
  • TIMEOUT [expected OK] /_webgl/conformance/uniforms/out-of-bounds-uniform-array-access.html (#26225)
    • NOTRUN [expected PASS] subtest: Overall test
  • TIMEOUT [expected PASS] /css/css-color/animation/opacity-animation-ending-correctly-001.html (#29215)
  • OK /css/cssom-view/MediaQueryList-addListener-removeListener.html (#24569)
    • PASS [expected FAIL] subtest: listeners are called correct number of times
  • TIMEOUT /fetch/metadata/generated/element-img-environment-change.sub.html (#30111)
    • PASS [expected FAIL] subtest: sec-fetch-site - Not sent to non-trustworthy same-origin destination, no attributes
  • OK /html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-cross-origin.sub.window.html (#29056)
    • FAIL [expected PASS] subtest: Cross-origin navigation started from unload handler must be ignored

      promise_test: Unhandled rejection with value: object "SecurityError: The operation is insecure."
      

  • OK /html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin.window.html (#29049)
    • PASS [expected FAIL] subtest: Same-origin navigation started from unload handler must be ignored
  • PASS [expected CRASH] /html/canvas/element/manual/drawing-text-to-the-canvas/canvas.2d.disconnected-font-size-math.html (#30063)
  • FAIL [expected CRASH] /html/canvas/element/manual/text/canvas.2d.disconnected.html (#30063)
  • OK [expected TIMEOUT] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-3.html (#24057)
    • FAIL [expected TIMEOUT] subtest: Check that popups from a sandboxed iframe escape the sandbox if allow-popups-to-escape-sandbox is used

      assert_equals: It came from a sandboxed iframe expected "null" but got "http://web-platform.test:8000"
      

  • TIMEOUT [expected OK] /resource-timing/nested-context-navigations-iframe.html (#24311)
    • TIMEOUT [expected PASS] subtest: Test that iframe navigations are not observable by the parent, even after history navigations by the parent

      Test timed out
      

    • NOTRUN [expected PASS] subtest: Test that crossorigin iframe navigations are not observable by the parent, even after history navigations by the parent
    • NOTRUN [expected PASS] subtest: Test that cross-site iframe navigations are not observable by the parent, even after history navigations by the parent
    • NOTRUN [expected PASS] subtest: Test that iframe navigations are not observable by the parent
    • NOTRUN [expected PASS] subtest: Test that crossorigin iframe navigations are not observable by the parent
    • NOTRUN [expected PASS] subtest: Test that cross-site iframe navigations are not observable by the parent
    • NOTRUN [expected PASS] subtest: Test that iframe refreshes are not observable by the parent
    • NOTRUN [expected PASS] subtest: Test that crossorigin iframe refreshes are not observable by the parent
    • NOTRUN [expected PASS] subtest: Test that cross-site iframe refreshes are not observable by the parent
Stable unexpected results that are known to be intermittent (15)
  • FAIL [expected PASS] /_mozilla/css/dirty_viewport.html (#13731)
  • FAIL [expected PASS] /_mozilla/css/iframe/hide_and_show.html (#15265)
  • FAIL [expected PASS] /_mozilla/mozilla/iframe/resize_after_load.html (#13573)
  • TIMEOUT [expected OK] /_webgl/conformance/glsl/misc/shader-uniform-packing-restrictions.html (#28103)
    • NOTRUN [expected PASS] subtest: Overall test
  • OK /css/css-fonts/variations/at-font-face-font-matching.html (#20684)
    • PASS [expected FAIL] subtest: Matching font-weight: '400' should prefer '400' over '450 460'
    • PASS [expected FAIL] subtest: Matching font-weight: '500' should prefer '400' over '350 399'
    • PASS [expected FAIL] subtest: Matching font-stretch: '90%' should prefer '50% 80%' over '60% 70%'
    • PASS [expected FAIL] subtest: Matching font-style: 'italic' should prefer 'oblique 5deg' over 'normal'
    • PASS [expected FAIL] subtest: Matching font-style: 'oblique 20deg' should prefer 'italic' over 'oblique 0deg'
    • PASS [expected FAIL] subtest: Matching font-style: 'oblique 20deg' should prefer 'oblique 0deg' over 'oblique -50deg -20deg'
    • PASS [expected FAIL] subtest: Matching font-style: 'oblique 10deg' should prefer 'oblique 15deg 20deg' over 'oblique 30deg 60deg'
    • PASS [expected FAIL] subtest: Matching font-style: 'oblique 10deg' should prefer 'oblique -50deg -20deg' over 'oblique -40deg -30deg'
    • PASS [expected FAIL] subtest: Matching font-style: 'oblique 0deg' should prefer 'oblique 0deg' over 'oblique 5deg'
  • TIMEOUT /fetch/metadata/generated/css-images.sub.tentative.html (#29047)
    • PASS [expected TIMEOUT] subtest: background-image sec-fetch-site - HTTPS downgrade (header not sent)
  • TIMEOUT [expected CRASH] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-1.html (#22647)
  • OK [expected CRASH] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-2.html (#22667)
    • FAIL [expected TIMEOUT] subtest: Check that popups from a sandboxed iframe escape the sandbox if allow-popups-to-escape-sandbox is used

      assert_equals: It came from a sandboxed iframe expected "null" but got "http://web-platform.test:8000"
      

  • TIMEOUT [expected CRASH] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-1.html (#24066)
  • CRASH [expected OK] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-2.html (#22154)
  • OK /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-3.html (#24066)
    • FAIL [expected NOTRUN] subtest: Check that popups from a sandboxed iframe do not escape the sandbox

      assert_equals: It came from a sandboxed iframe expected "null" but got "http://web-platform.test:8000"
      

  • OK [expected CRASH] /html/semantics/forms/the-fieldset-element/disabled-003.html (#31730)
  • TIMEOUT /resource-timing/test_resource_timing.html (#25720)
    • FAIL [expected PASS] subtest: PerformanceEntry has correct name, initiatorType, startTime, and duration (img)

      assert_equals: expected 11666688 but got 11666944
      

  • OK [expected TIMEOUT] /webmessaging/with-ports/017.html (#24486)
    • PASS [expected TIMEOUT] subtest: origin of the script that invoked the method, about:blank
  • TIMEOUT [expected OK] /webmessaging/without-ports/018.html (#24485)
    • TIMEOUT [expected PASS] subtest: origin of the script that invoked the method, javascript:

      Test timed out
      

Copy link

✨ Try run (#8323421004) succeeded.

@mrobinson mrobinson added this pull request to the merge queue Mar 18, 2024
@mukilan
Copy link
Member Author

mukilan commented Mar 18, 2024

Thanks for the review, @mrobinson!

Have you been able to report this problem upstream in WebRender?

Not yet. I will create an issue in https://bugzilla.mozilla.org

@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Mar 18, 2024
@mukilan
Copy link
Member Author

mukilan commented Mar 18, 2024

Looks like a network failure in 2013 test and a single test in 2020 failed:

  ▶ OK /css/css-values/calc-in-media-queries-with-mixed-units.html
  │   ▶ FAIL [expected PASS] box should be orange if the calc between vw+px in @media was correct
  │   │   → assert_equals: expected "rgb(255, 165, 0)" but got "rgb(0, 0, 255)

Since the try job passed, I'll add the PR to the queue again.

@mukilan mukilan added this pull request to the merge queue Mar 18, 2024
@mrobinson
Copy link
Member

Looks like a network failure in 2013 test and a single test in 2020 failed:

  ▶ OK /css/css-values/calc-in-media-queries-with-mixed-units.html
  │   ▶ FAIL [expected PASS] box should be orange if the calc between vw+px in @media was correct
  │   │   → assert_equals: expected "rgb(255, 165, 0)" but got "rgb(0, 0, 255)

Since the try job passed, I'll add the PR to the queue again.

Do you mind opening an I-Intermittent issue for this failure?

Merged via the queue into servo:main with commit 55bb289 Mar 18, 2024
38 checks passed
@mukilan mukilan deleted the fix-rendering-in-android-emulator branch March 18, 2024 12:24
@jdm
Copy link
Member

jdm commented Mar 18, 2024

What a wild bug. Thanks for the explanation!

@mukilan
Copy link
Member Author

mukilan commented Mar 26, 2024

JFYI, I've reported the issue upstream with steps to reproduce using wrench

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Webpages not rendering on Android x86 and x86_64 emulators.
3 participants