Skip to content

Conversation

@mrobinson
Copy link
Member

This change ensures that all Surfman surfaces used for WebRendering
rendering are released in the WebGL thread properly by:

  • Moving WebGLThread shutdown to the renderer. The renderer starts it,
    so it makes sense that the renderer also shuts it down when it can
    guarantee that no WebRender painting is currently happening and no
    images will be in use again.
  • Delaying WebGL context release until all WebRender operations have
    finished. This ensures that the SwapChain is still available for use
    in the external image handler.

The second point is accomplished by sharing a usage map between the
renderer and the WebGLThread which tracks whether WebRender is
currently rendering a particular context. In addition, once the WebGL
external image handler is notified that WebRender has finished
rendering, it informs the WebGLThread so that it can released any
contexts already marked for deletion.

Testing: This fixes very common intermittent crashes when running
/_webgl/conformance locally. It should reduce the incidence of these flaky
crashes on the CI.
Fixes: #40399.
Fixes: #40417.

This change ensures that all Surfman surfaces used for WebRendering
rendering are released in the WebGL thread properly by:

- Moving `WebGLThread` shutdown to the renderer. The renderer starts it,
  so it makes sense that the renderer also shuts it down when it can
  guarantee that no WebRender painting is currently happening and no
  images will be in use again.
- Delaying WebGL context release until all WebRender operations have
  finished. This ensures that the `SwapChain` is still available for use
  in the external image handler.

The second point is accomplished by sharing a usage map between the
renderer and the `WebGLThread` which tracks whether WebRender is
currently rendering a particular context. In addition, once the WebGL
external image handler is notified that WebRender has finished
rendering, it informs the WebGLThread so that it can released any
contexts already marked for deletion.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
@mrobinson mrobinson requested a review from sagudev as a code owner November 19, 2025 09:16
@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Nov 19, 2025
@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Nov 19, 2025
@mrobinson mrobinson added the T-linux-wpt Do a try run of the WPT label Nov 19, 2025
@github-actions github-actions bot removed the T-linux-wpt Do a try run of the WPT label Nov 19, 2025
@github-actions
Copy link

🔨 Triggering try run (#19500825967) for Linux (WPT)

@codecov-commenter
Copy link

⚠️ JUnit XML file not found

The CLI was unable to find any JUnit XML files to upload.
For more help, visit our troubleshooting guide.

@github-actions
Copy link

Test results for linux-wpt from try job (#19500825967):

Flaky unexpected result (37)
  • CRASH [expected ERROR] /IndexedDB/error-attributes.any.serviceworker.html
  • OK /IndexedDB/idbfactory_open.any.html
    • FAIL [expected PASS] subtest: Calling open() with version argument 1.5 should not throw.

      assert_equals: version expected 1 but got 9007199254740991
      

  • TIMEOUT [expected OK] /_mozilla/mozilla/img_load_more_than_cache.html
  • OK /_mozilla/webxr/create_session.https.html
    • FAIL [expected PASS] subtest: create_session

      can't access property "simulateDeviceConnection", navigator.xr.test is undefined
      

  • ERROR [expected TIMEOUT] /_mozilla/webxr/sessionavailable.https.html
  • CRASH [expected OK] /_webgl/conformance/textures/misc/texture-active-bind.html
  • OK /_webgl/conformance/textures/misc/texture-upload-size.html (#21770)
    • PASS [expected FAIL] subtest: WebGL test #45
    • PASS [expected FAIL] subtest: WebGL test #47
    • PASS [expected FAIL] subtest: WebGL test #49
    • PASS [expected FAIL] subtest: WebGL test #51
    • FAIL [expected PASS] subtest: WebGL test #53

      assert_true: Texture was smaller than the expected size 2x2 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #55

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : when calling texSubImage2D with the same texture upload with offset 1, 1 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #57

      assert_true: Texture was smaller than the expected size 2x2 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #59

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : when calling texSubImage2D with the same texture upload with offset 1, 1 expected true got false
      

    • PASS [expected FAIL] subtest: WebGL test #61
    • PASS [expected FAIL] subtest: WebGL test #63
    • And 14 more unexpected results...
  • OK [expected TIMEOUT] /cookiestore/httponly_cookies.https.window.html (#40697)
    • FAIL [expected TIMEOUT] subtest: HttpOnly cookies are not observed

      assert_equals: HttpOnly cookie we wrote using HTTP in cookie jar is invisible to script expected (undefined) undefined but got (string) "cspViolationReportCookie1=1"
      

    • FAIL [expected NOTRUN] subtest: HttpOnly cookies can not be set by document.cookie

      assert_equals: Trying to store an HttpOnly cookie with document.cookie fails expected "cookie1=value1; cookie3=value3" but got "HTTPONLY-cookie=value; cookie1=value1; cookie3=value3"
      

    • FAIL [expected NOTRUN] subtest: HttpOnly cookies can not be set by CookieStore

      assert_equals: httpOnly is not an option for CookieStore.set() expected "cookie1=value1; cookie2=value2; cookie3=value3" but got "HTTPONLY-cookie=value; cookie1=value1; cookie2=value2; cookie3=value3"
      

    • FAIL [expected NOTRUN] subtest: HttpOnly cookies are not deleted/overwritten

      assert_equals: HttpOnly cookie is not deleted expected (string) "HTTPONLY-cookie=value" but got (undefined) undefined
      

  • OK /css/css-fonts/generic-family-keywords-003.html (#38994)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted system-ui (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted generic(nastaliq) (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted ui-monospace (drawing text in a canvas)
    • PASS [expected FAIL] subtest: @font-face matching for quoted and unquoted ui-rounded (drawing text in a canvas)
  • OK /custom-elements/form-associated/ElementInternals-setFormValue.html (#29174)
    • PASS [expected FAIL] subtest: Single value - empty name exists
    • PASS [expected FAIL] subtest: Newline normalization - \r\n in value (urlencoded)
  • CRASH [expected ERROR] /encoding/streams/decode-ignore-bom.any.serviceworker.html
  • OK /fetch/metadata/generated/css-font-face.sub.tentative.html (#34624)
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Not sent to non-trustworthy same-site destination
    • FAIL [expected PASS] subtest: sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination

      promise_test: Unhandled rejection with value: object "Error: Failed to query for recorded headers."
      

  • ERROR /fetch/metadata/generated/serviceworker.https.sub.html (#36247)
    • FAIL [expected PASS] subtest: sec-fetch-site - Same origin, no options - registration

      promise_test: Unhandled rejection with value: object "Error: Failed to query for recorded headers."
      

  • OK /html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-nosrc.html (#34819)
    • PASS [expected FAIL] subtest: link click
  • OK /html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment.html (#20768)
    • FAIL [expected PASS] subtest: Tests that a fragment navigation in the unload handler will not block the initial navigation

      assert_equals: expected "" but got "#fragment"
      

  • 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
  • OK /html/browsers/history/the-history-interface/traverse_the_history_2.html (#21383)
    • PASS [expected FAIL] subtest: Multiple history traversals, last would be aborted
  • OK /html/browsers/history/the-history-interface/traverse_the_history_4.html (#21383)
    • PASS [expected FAIL] subtest: Multiple history traversals, last would be aborted
  • CRASH [expected OK] /html/browsers/windows/document-domain-nested-navigate.window.html
  • OK [expected TIMEOUT] /html/interaction/focus/the-autofocus-attribute/autofocus-dialog.html (#29087)
    • FAIL [expected TIMEOUT] subtest: &lt;dialog&gt;-contained autofocus element gets focused when the dialog is shown

      assert_equals: expected "DIV" but got "BODY"
      

  • TIMEOUT [expected OK] /html/interaction/focus/the-autofocus-attribute/update-the-rendering.html (#24145)
    • TIMEOUT [expected FAIL] subtest: "Flush autofocus candidates" should be happen before a scroll event and animation frame callbacks

      Test timed out
      

  • OK /html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-2.html (#39703)
    • FAIL [expected PASS] subtest: Meta refresh of the original iframe is not blocked if moved into a sandboxed iframe

      uncaught exception: Error: assert_unreached: The iframe into which the meta was moved must not refresh Reached unreachable code
      

  • OK /html/semantics/forms/form-submission-0/multipart-formdata.window.html (#28725)
    • PASS [expected FAIL] subtest: multipart/form-data: 0x00 in name (normal form)
    • PASS [expected FAIL] subtest: multipart/form-data: 0x00 in value (formdata event)
  • OK /html/semantics/forms/form-submission-0/text-plain.window.html (#28687)
    • FAIL [expected PASS] subtest: text/plain: Basic test (formdata event)

      assert_equals: expected "basic=test\r\n" but got ""
      

    • FAIL [expected PASS] subtest: text/plain: Basic File test (normal form)

      assert_equals: expected "basic=file-test.txt\r\n" but got ""
      

  • OK /html/semantics/forms/form-submission-0/urlencoded2.window.html (#28687)
    • FAIL [expected PASS] subtest: application/x-www-form-urlencoded: Basic test (formdata event)

      assert_equals: expected "basic=test" but got ""
      

    • FAIL [expected PASS] subtest: application/x-www-form-urlencoded: Basic File test (normal form)

      assert_equals: expected "basic=file-test.txt" but got ""
      

    • PASS [expected FAIL] subtest: application/x-www-form-urlencoded: \r\n in name (normal form)
    • PASS [expected FAIL] subtest: application/x-www-form-urlencoded: backslash in filename (normal form)
  • OK [expected CRASH] /html/semantics/forms/the-fieldset-element/disabled-003.html (#31730, #39631)
  • OK /html/webappapis/user-prompts/print-during-unload.html (#35944)
    • FAIL [expected PASS] subtest: print() during unload

      assert_array_equals: expected property 1 to be "destination" but got "error: window.print is not a function" (expected array ["start", "destination"] got ["start", "error: window.print is not a function"])
      

  • OK /navigation-timing/test-navigation-type-reload.html (#33334)
    • PASS [expected FAIL] subtest: Reload domContentLoadedEventEnd &gt; Original domContentLoadedEventEnd
    • PASS [expected FAIL] subtest: Reload domInteractive &gt; Original domInteractive
  • OK /preload/preload-invalid-resources.html (#39091)
    • PASS [expected FAIL] subtest: Preloading an invalid image (missing) should preload and not re-fetch
  • OK /preload/preload-xhr.html (#39092)
    • FAIL [expected PASS] subtest: Make an XHR request immediately after creating link rel=preload.

      assert_equals: resources/dummy.xml?token=0a19c0aa-998e-4cd5-b927-761f0927a06f expected 1 but got 0
      

  • ERROR [expected OK] /resource-timing/cors-preflight.any.html (#28694)
  • CRASH [expected OK] /trusted-types/TrustedTypePolicy-CSP-no-name.html
  • CRASH [expected OK] /trusted-types/block-string-assignment-to-DedicatedWorker-setTimeout-setInterval.html
  • TIMEOUT [expected OK] /trusted-types/trusted-types-navigation.html?01-05 (#38975)
    • TIMEOUT [expected PASS] subtest: Navigate a window via anchor with javascript:-urls in report-only mode.

      Test timed out
      

    • NOTRUN [expected PASS] subtest: Navigate a window via anchor with javascript:-urls w/ default policy in report-only mode.
    • NOTRUN [expected PASS] subtest: Navigate a frame via anchor with javascript:-urls in enforcing mode.
  • OK [expected TIMEOUT] /trusted-types/trusted-types-navigation.html?06-10 (#37920)
    • PASS [expected TIMEOUT] subtest: Navigate a frame via anchor with javascript:-urls w/ default policy in report-only mode.
    • FAIL [expected NOTRUN] subtest: Navigate a window via anchor with javascript:-urls w/ a default policy throwing an exception in enforcing mode.

      promise_test: Unhandled rejection with value: "Unexpected message received: \"No securitypolicyviolation reported!\""
      

    • FAIL [expected NOTRUN] subtest: Navigate a window via anchor with javascript:-urls w/ a default policy throwing an exception in report-only mode.

      promise_test: Unhandled rejection with value: "Unexpected message received: \"No securitypolicyviolation reported!\""
      

  • TIMEOUT /trusted-types/trusted-types-navigation.html?31-35 (#38034)
    • TIMEOUT [expected PASS] subtest: Navigate a frame via form-submission with javascript:-urls in report-only mode.

      Test timed out
      

    • NOTRUN [expected TIMEOUT] subtest: Navigate a frame via form-submission with javascript:-urls w/ default policy in report-only mode.
  • CRASH [expected OK] /webaudio/the-audio-api/the-audiobuffersourcenode-interface/ctor-audiobuffersource.html
Stable unexpected results that are known to be intermittent (19)
  • OK /IndexedDB/idbobjectstore_getAll.any.html (#39276)
    • PASS [expected FAIL] subtest: Get all values with transaction.commit()
  • OK /IndexedDB/idbobjectstore_getAll.any.worker.html (#39400)
    • PASS [expected FAIL] subtest: Get all values with transaction.commit()
  • FAIL [expected PASS] /_mozilla/mozilla/sslfail.html (#10760)
  • TIMEOUT [expected OK] /_mozilla/mozilla/window_resize_event.html (#36741)
    • TIMEOUT [expected PASS] subtest: Popup onresize event fires after resizeTo

      Test timed out
      

  • TIMEOUT /content-security-policy/inheritance/location-reload.html (#38983)
    • PASS [expected FAIL] subtest: location.reload() of empty iframe.
  • OK /css/css-cascade/layer-cssom-order-reverse.html (#36094)
    • PASS [expected FAIL] subtest: Delete layer invalidates @font-face
  • OK /css/css-fonts/generic-family-keywords-001.html (#37467)
    • FAIL [expected PASS] subtest: @font-face matching for quoted and unquoted generic(khmer-mul)

      assert_equals: quoted generic(khmer-mul) matches  @font-face rule expected 50 but got 30
      

  • OK /fetch/fetch-later/new-window.https.window.html (#32036)
    • FAIL [expected PASS] subtest: A same-origin window[target=''][features=''] can trigger fetchLater.

      assert_equals: Number of sent beacons does not match expected count: expected 1 but got 0
      

    • FAIL [expected PASS] subtest: A same-origin window[target=''][features='popup'] can trigger fetchLater.

      assert_equals: Number of sent beacons does not match expected count: expected 1 but got 0
      

    • FAIL [expected PASS] subtest: A same-origin window[target='_blank'][features=''] can trigger fetchLater.

      assert_equals: Number of sent beacons does not match expected count: expected 1 but got 0
      

    • FAIL [expected PASS] subtest: A same-origin window[target='_blank'][features='popup'] can trigger fetchLater.

      assert_equals: Number of sent beacons does not match expected count: expected 1 but got 0
      

  • OK /fetch/metadata/generated/css-font-face.https.sub.tentative.html (#32732)
    • PASS [expected FAIL] subtest: sec-fetch-dest
    • PASS [expected FAIL] subtest: sec-fetch-user
    • FAIL [expected PASS] subtest: sec-fetch-storage-access - Cross-site

      promise_test: Unhandled rejection with value: object "Error: Failed to query for recorded headers."
      

  • OK /fetch/metadata/generated/element-img-environment-change.https.sub.html (#30111)
    • FAIL [expected PASS] subtest: sec-fetch-site - Same-Origin -&gt; Same-Site -&gt; Same-Origin redirect, no attributes

      promise_test: Unhandled rejection with value: object "Error: Failed to query for recorded headers."
      

    • PASS [expected FAIL] subtest: sec-fetch-site - Cross-Site -&gt; Same Origin, no attributes
    • FAIL [expected PASS] subtest: sec-fetch-site - Cross-Site -&gt; Same-Site, no attributes

      promise_test: Unhandled rejection with value: object "Error: Failed to query for recorded headers."
      

    • PASS [expected FAIL] subtest: sec-fetch-site - Cross-Site -&gt; Cross-Site, no attributes
    • FAIL [expected PASS] subtest: sec-fetch-site - Same-Origin -&gt; Same Origin, no attributes

      promise_test: Unhandled rejection with value: object "Error: Failed to query for recorded headers."
      

    • PASS [expected FAIL] subtest: sec-fetch-site - Same-Origin -&gt; Same-Site, no attributes
    • FAIL [expected PASS] subtest: sec-fetch-site - Same-Origin -&gt; Cross-Site, no attributes

      promise_test: Unhandled rejection with value: object "Error: Failed to query for recorded headers."
      

    • PASS [expected FAIL] subtest: sec-fetch-site - Same-Site -&gt; Cross-Site, no attributes
    • PASS [expected FAIL] subtest: sec-fetch-user - no attributes
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Same site, no attributes
  • OK /fetch/metadata/generated/element-img-environment-change.sub.html (#30111)
    • PASS [expected FAIL] subtest: sec-fetch-site - Not sent to non-trustworthy cross-site destination, no attributes
    • PASS [expected FAIL] subtest: sec-fetch-mode - Not sent to non-trustworthy same-origin destination, no attributes
    • PASS [expected FAIL] subtest: sec-fetch-mode - Not sent to non-trustworthy cross-site destination, no attributes
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination, no attributes
    • FAIL [expected PASS] subtest: sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination, no attributes

      promise_test: Unhandled rejection with value: object "Error: Failed to query for recorded headers."
      

  • 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."
      

  • ERROR [expected OK] /html/infrastructure/common-dom-interfaces/collections/domstringlist.html (#40665)
  • TIMEOUT /html/interaction/focus/the-autofocus-attribute/supported-elements.html (#24145)
    • FAIL [expected TIMEOUT] subtest: Element with tabindex should support autofocus

      assert_equals: expected "SPAN" but got "BODY"
      

    • PASS [expected NOTRUN] subtest: Non-HTMLElement should not support autofocus
    • TIMEOUT [expected NOTRUN] subtest: Host element with delegatesFocus should support autofocus

      Test timed out
      

  • CRASH [expected OK] /pointerevents/compat/pointerevent_touch-action_two-finger_interaction.html (#40418)
  • OK /preload/preload-error.sub.html (#37177)
    • FAIL [expected PASS] subtest: success (fetch): main

      assert_greater_than: http://web-platform.test:8000/preload/resources/dummy.xml?label=fetch should be loaded expected a number greater than 0 but got 0
      

    • PASS [expected FAIL] subtest: 404 (fetch): main
    • PASS [expected FAIL] subtest: CORS (fetch): main
  • TIMEOUT [expected OK] /trusted-types/trusted-types-navigation.html?26-30 (#38807)
    • TIMEOUT [expected FAIL] subtest: Navigate a window via form-submission with javascript:-urls in report-only mode.

      Test timed out
      

    • NOTRUN [expected PASS] subtest: Navigate a window via form-submission with javascript:-urls w/ default policy in report-only mode.
    • NOTRUN [expected FAIL] subtest: Navigate a frame via form-submission with javascript:-urls in enforcing mode.
    • NOTRUN [expected PASS] subtest: Navigate a frame via form-submission with javascript:-urls w/ default policy in enforcing mode.
  • OK [expected TIMEOUT] /webstorage/localstorage-about-blank-3P-iframe-opens-3P-window.partitioned.html (#29053)
    • PASS [expected TIMEOUT] subtest: StorageKey: test 3P about:blank window opened from a 3P iframe
  • OK [expected ERROR] /webxr/render_state_update.https.html (#27535)

@github-actions
Copy link

✨ Try run (#19500825967) succeeded.

@mrobinson mrobinson added this pull request to the merge queue Nov 19, 2025
@servo-highfive servo-highfive added the S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. label Nov 19, 2025
Merged via the queue into servo:main with commit 465b13c Nov 19, 2025
73 checks passed
@mrobinson mrobinson deleted the ensure-all-webgl-surfman-surfaces-are-released branch November 19, 2025 14:06
@servo-highfive servo-highfive removed the S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. label Nov 19, 2025
github-merge-queue bot pushed a commit that referenced this pull request Nov 19, 2025
…#40739)

This should prevent comments like this:
#40736 (comment)

---------

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
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.

Intermittent panic during webgl tests Panic during shutdown with WebGL page (MacOS)

4 participants