Vendor the current version of WebRender

This is a step toward upgrading WebRender, which will be upgraded and
patched in the `third_party` directory. This change vendors the current
private branch of WebRender that we use and adds a `patches` directory
which tracks the changes on top of the upstream WebRender commit
described by third_party/webrender/patches/head.
This commit is contained in:
Martin Robinson 2023-07-03 17:43:57 +02:00
parent c19eb800de
commit 49277f5c3f
No known key found for this signature in database
GPG key ID: D56AA4FA55EFE6F8
1215 changed files with 185677 additions and 34 deletions

6
Cargo.lock generated
View file

@ -4396,7 +4396,6 @@ dependencies = [
[[package]]
name = "peek-poke"
version = "0.2.0"
source = "git+https://github.com/jdm/webrender?branch=crash-backtrace#415b9ba32648667313f6f4ce7965752285bf0b26"
dependencies = [
"euclid",
"peek-poke-derive",
@ -4405,7 +4404,6 @@ dependencies = [
[[package]]
name = "peek-poke-derive"
version = "0.2.1"
source = "git+https://github.com/jdm/webrender?branch=crash-backtrace#415b9ba32648667313f6f4ce7965752285bf0b26"
dependencies = [
"proc-macro2",
"quote",
@ -6990,7 +6988,6 @@ dependencies = [
[[package]]
name = "webrender"
version = "0.61.0"
source = "git+https://github.com/jdm/webrender?branch=crash-backtrace#415b9ba32648667313f6f4ce7965752285bf0b26"
dependencies = [
"backtrace",
"base64 0.10.1",
@ -7034,7 +7031,6 @@ dependencies = [
[[package]]
name = "webrender_api"
version = "0.61.0"
source = "git+https://github.com/jdm/webrender?branch=crash-backtrace#415b9ba32648667313f6f4ce7965752285bf0b26"
dependencies = [
"app_units",
"bitflags",
@ -7055,7 +7051,6 @@ dependencies = [
[[package]]
name = "webrender_build"
version = "0.0.1"
source = "git+https://github.com/jdm/webrender?branch=crash-backtrace#415b9ba32648667313f6f4ce7965752285bf0b26"
dependencies = [
"bitflags",
"lazy_static",
@ -7320,7 +7315,6 @@ dependencies = [
[[package]]
name = "wr_malloc_size_of"
version = "0.0.1"
source = "git+https://github.com/jdm/webrender?branch=crash-backtrace#415b9ba32648667313f6f4ce7965752285bf0b26"
dependencies = [
"app_units",
"euclid",

View file

@ -78,6 +78,8 @@ unicode-script = "0.5"
url = "2.0"
uuid = { version = "1.3.4", features = ["v4"] }
webdriver = "0.48.0"
webrender = { git = "https://github.com/servo/webrender", features = ["capture"] }
webrender_api = { git = "https://github.com/servo/webrender" }
winapi = "0.3"
xi-unicode = "0.1.0"
xml5ever = "0.17"
@ -104,7 +106,9 @@ mio = { git = "https://github.com/servo/mio.git", branch = "servo-mio-0.6.22" }
# fork that bumps crates since the original repo is archived.
immeta = { git = "https://github.com/fabricedesre/immeta.git" }
# https://github.com/servo/servo/issues/27515#issuecomment-671474054
# This is required because we want all dependencies that use WebRender to
# use our vendored version.
[patch."https://github.com/servo/webrender"]
webrender = { git = "https://github.com/jdm/webrender", branch = "crash-backtrace" }
webrender_api = { git = "https://github.com/jdm/webrender", branch = "crash-backtrace" }
webrender = { path = "third_party/webrender/webrender" }
webrender_api = { path = "third_party/webrender/webrender_api" }

View file

@ -44,8 +44,8 @@ surfman = { workspace = true, features = ["sm-angle","sm-angle-default"] }
surfman-chains = { workspace = true }
surfman-chains-api = { workspace = true }
time = { workspace = true, optional = true }
webrender = { git = "https://github.com/servo/webrender" }
webrender_api = { git = "https://github.com/servo/webrender" }
webrender = { workspace = true }
webrender_api = { workspace = true }
webrender_surfman = { path = "../webrender_surfman" }
webrender_traits = { path = "../webrender_traits" }
webxr-api = { git = "https://github.com/servo/webxr", features = ["ipc"] }

View file

@ -29,5 +29,5 @@ servo_config = { path = "../config" }
sparkle = { workspace = true }
style = { path = "../style" }
time = { workspace = true, optional = true }
webrender_api = { git = "https://github.com/servo/webrender" }
webrender_api = { workspace = true }
webxr-api = { git = "https://github.com/servo/webxr", features = ["ipc"] }

View file

@ -39,8 +39,8 @@ servo_geometry = { path = "../geometry" }
servo_url = { path = "../url" }
style_traits = { path = "../style_traits" }
time = { workspace = true }
webrender = { git = "https://github.com/servo/webrender", features = ["capture"] }
webrender_api = { git = "https://github.com/servo/webrender" }
webrender = { workspace = true }
webrender_api = { workspace = true }
webrender_surfman = { path = "../webrender_surfman" }
webxr = { git = "https://github.com/servo/webxr" }

View file

@ -42,7 +42,7 @@ servo_remutex = { path = "../remutex" }
servo_url = { path = "../url" }
style_traits = { path = "../style_traits" }
webgpu = { path = "../webgpu" }
webrender_api = { git = "https://github.com/servo/webrender" }
webrender_api = { workspace = true }
webrender_traits = { path = "../webrender_traits" }
webxr-api = { git = "https://github.com/servo/webxr", features = ["ipc"] }

View file

@ -21,5 +21,5 @@ num-derive = "0.3"
num-traits = { workspace = true }
serde = { workspace = true }
servo_url = { path = "../url" }
webrender_api = { git = "https://github.com/servo/webrender" }
webrender_api = { workspace = true }
webxr-api = { git = "https://github.com/servo/webxr", features = ["ipc"] }

View file

@ -38,7 +38,7 @@ time = { workspace = true }
ucd = "0.1.1"
unicode-bidi = { workspace = true, features = ["with_serde"] }
unicode-script = { workspace = true }
webrender_api = { git = "https://github.com/servo/webrender" }
webrender_api = { workspace = true }
xi-unicode = { workspace = true }
[target.'cfg(target_os = "macos")'.dependencies]

View file

@ -15,4 +15,4 @@ malloc_size_of = { path = "../malloc_size_of" }
malloc_size_of_derive = { workspace = true }
range = { path = "../range" }
serde = { workspace = true }
webrender_api = { git = "https://github.com/servo/webrender" }
webrender_api = { workspace = true }

View file

@ -49,7 +49,7 @@ style = { path = "../style", features = ["servo", "servo-layout-2013"] }
style_traits = { path = "../style_traits" }
unicode-bidi = { workspace = true, features = ["with_serde"] }
unicode-script = { workspace = true }
webrender_api = { git = "https://github.com/servo/webrender" }
webrender_api = { workspace = true }
xi-unicode = { workspace = true }
[dev-dependencies]

View file

@ -44,7 +44,7 @@ servo_url = { path = "../url" }
style = { path = "../style", features = ["servo", "servo-layout-2020"] }
style_traits = { path = "../style_traits" }
unicode-script = { workspace = true }
webrender_api = { git = "https://github.com/servo/webrender" }
webrender_api = { workspace = true }
[dev-dependencies]
lazy_static = { workspace = true }

View file

@ -49,4 +49,4 @@ servo_url = { path = "../url" }
style = { path = "../style" }
style_traits = { path = "../style_traits" }
time = { workspace = true }
webrender_api = { git = "https://github.com/servo/webrender" }
webrender_api = { workspace = true }

View file

@ -45,4 +45,4 @@ servo_config = { path = "../config" }
servo_url = { path = "../url" }
style = { path = "../style" }
style_traits = { path = "../style_traits" }
webrender_api = { git = "https://github.com/servo/webrender" }
webrender_api = { workspace = true }

View file

@ -20,4 +20,4 @@ net_traits = { path = "../net_traits" }
profile_traits = { path = "../profile_traits" }
script_traits = { path = "../script_traits" }
servo_url = { path = "../url" }
webrender_api = { git = "https://github.com/servo/webrender" }
webrender_api = { workspace = true }

View file

@ -49,5 +49,5 @@ tokio = { workspace = true }
url = { workspace = true, optional = true }
uuid = { workspace = true, optional = true }
void = "1.0.2"
webrender_api = { git = "https://github.com/servo/webrender", optional = true }
webrender_api = { workspace = true, optional = true }
xml5ever = { workspace = true, optional = true }

View file

@ -19,5 +19,5 @@ log = { workspace = true }
serde = { workspace = true }
servo-media = { git = "https://github.com/servo/media" }
servo_config = { path = "../config" }
webrender_api = { git = "https://github.com/servo/webrender" }
webrender_api = { workspace = true }
webrender_traits = { path = "../webrender_traits" }

View file

@ -19,7 +19,7 @@ malloc_size_of = { path = "../malloc_size_of" }
malloc_size_of_derive = { workspace = true }
parking_lot = { workspace = true }
serde = { workspace = true }
webrender_api = { git = "https://github.com/servo/webrender" }
webrender_api = { workspace = true }
[dev-dependencies]
size_of_test = { path = "../size_of_test" }

View file

@ -65,7 +65,7 @@ tokio-stream = "0.1"
tungstenite = "0.19"
url = { workspace = true }
uuid = { workspace = true }
webrender_api = { git = "https://github.com/servo/webrender" }
webrender_api = { workspace = true }
[dev-dependencies]
futures = {version = "0.3", features = ["compat"]}

View file

@ -117,7 +117,7 @@ utf-8 = "0.7"
uuid = { workspace = true, features = ["serde"] }
webdriver = { workspace = true }
webgpu = { path = "../webgpu" }
webrender_api = { git = "https://github.com/servo/webrender" }
webrender_api = { workspace = true }
webxr-api = { git = "https://github.com/servo/webxr", features = ["ipc"] }
xml5ever = { workspace = true }

View file

@ -43,7 +43,7 @@ time = { workspace = true }
uuid = { workspace = true }
webdriver = { workspace = true }
webgpu = { path = "../webgpu" }
webrender_api = { git = "https://github.com/servo/webrender" }
webrender_api = { workspace = true }
webxr-api = { git = "https://github.com/servo/webxr", features = ["ipc"] }
[dev-dependencies]

View file

@ -81,8 +81,8 @@ style_traits = { path = "../style_traits", features = ["servo"] }
surfman = { workspace = true }
webdriver_server = { path = "../webdriver_server", optional = true }
webgpu = { path = "../webgpu" }
webrender = { git = "https://github.com/servo/webrender" }
webrender_api = { git = "https://github.com/servo/webrender" }
webrender = { workspace = true }
webrender_api = { workspace = true }
webrender_surfman = { path = "../webrender_surfman" }
webrender_traits = { path = "../webrender_traits" }
webxr = { git = "https://github.com/servo/webxr" }

View file

@ -29,4 +29,4 @@ servo_atoms = { path = "../atoms", optional = true }
servo_url = { path = "../url", optional = true }
to_shmem = { path = "../to_shmem" }
to_shmem_derive = { path = "../to_shmem_derive" }
webrender_api = { git = "https://github.com/servo/webrender", optional = true }
webrender_api = { workspace = true, optional = true }

View file

@ -20,7 +20,7 @@ msg = { path = "../msg" }
serde = { workspace = true, features = ["serde_derive"] }
servo_config = { path = "../config" }
smallvec = { workspace = true, features = ["serde"] }
webrender_api = { git = "https://github.com/servo/webrender" }
webrender_api = { workspace = true }
webrender_traits = { path = "../webrender_traits" }
wgpu-core = { version = "0.6.0", git = "https://github.com/gfx-rs/wgpu", features = ["replay", "trace", "serial-pass"], rev = "e72724a6e393503c73f37e86aa9317a5c62e32b8" }
wgpu-types = { version = "0.6.0", git = "https://github.com/gfx-rs/wgpu", features = ["replay", "trace"], rev = "e72724a6e393503c73f37e86aa9317a5c62e32b8" }

View file

@ -12,5 +12,5 @@ path = "lib.rs"
[dependencies]
euclid = { workspace = true }
webrender_api = { git = "https://github.com/servo/webrender" }
webrender_api = { workspace = true }

View file

@ -1,3 +1,4 @@
match_block_trailing_comma = true
binop_separator = "Back"
reorder_imports = true
ignore = [ "third_party" ]

27
third_party/webrender/.gitignore vendored Normal file
View file

@ -0,0 +1,27 @@
target/
*~
*#
# WR internals
captures
wrench/json_frames
wrench/ron_frames
# Editors
*.swp
*.swo
# IntelliJ
.idea
*.iws
*.iml
# Gradle
.gradle
# VSCode
.vscode
.vs
# System
.fuse_hidden*

176
third_party/webrender/.taskcluster.yml vendored Normal file
View file

@ -0,0 +1,176 @@
# This file specifies the configuration needed to test WebRender using the
# Taskcluster infrastructure. Most of this should be relatively
# self-explanatory; this file was originally generated by using the
# Taskcluster-GitHub integration quickstart tool which no longer exists.
#
# See https://community-tc.services.mozilla.com/docs
version: 1
policy:
pullRequests: public
# This file triggers a set of tasks; the ones targeting Linux are run in a docker
# container using docker-worker (which is a worker type provided by TaskCluster).
# The OS X ones are run in a custom worker type, for which we have worker
# instances configured and running.
tasks:
$if: 'tasks_for in ["github-push", "github-pull-request"]'
then:
$let:
should_run:
$if: 'tasks_for == "github-push"'
# for pushes, run on any branch but master
then: {$eval: 'event.ref != "refs/heads/master"'}
# for PRs, run for opened and synchronized events
else: {$eval: 'event.action in ["opened", "synchronize"]'}
repo_url:
$if: 'tasks_for == "github-push"'
then: ${event.repository.clone_url}
else: ${event.pull_request.head.repo.clone_url}
sha:
$if: 'tasks_for == "github-push"'
then: ${event.after}
else: ${event.pull_request.head.sha}
login: ${event.sender.login}
branch:
$if: 'tasks_for == "github-push"'
then:
$if: 'event.ref[:11] == "refs/heads/"'
then: ${event.ref[11:]}
else: ${event.ref}
else: ${event.pull_request.head.ref}
in:
$if: should_run
then:
# For the docker-worker tasks, the Docker image used
# (staktrace/webrender-test:debian-v6) was created using the Dockerfile in
# ci-scripts/docker-image.
#
# The docker image may need to be updated over time if the set of required
# packages increases. Note in particular that rust/cargo are not part of the
# docker image, and are re-installed using rustup on each CI run. This ensures
# the latest stable rust compiler is always used.
# CI runs will be triggered on opening PRs, updating PRs, and pushes to the
# repository.
- metadata:
name: Linux release tests
description: Runs release-mode WebRender CI stuff on a Linux TC worker
owner: noreply@mozilla.com
source: ${repo_url}
provisionerId: proj-webrender
workerType: ci-linux
deadline: {$fromNow: '1 day'}
payload:
maxRunTime: 7200
image: 'staktrace/webrender-test:debian-v6'
env:
RUST_BACKTRACE: 'full'
RUSTFLAGS: '--deny warnings'
command:
- /bin/bash
- '--login'
- '-c'
- >-
curl https://sh.rustup.rs -sSf | sh -s -- -y &&
source $HOME/.cargo/env &&
git clone ${repo_url} webrender && cd webrender &&
git checkout ${sha} &&
servo-tidy &&
ci-scripts/linux-release-tests.sh
routes:
- "index.project.webrender.ci.${login}.${branch}.linux-release"
- metadata:
name: Linux debug tests
description: Runs debug-mode WebRender CI stuff on a Linux TC worker
owner: noreply@mozilla.com
source: ${repo_url}
provisionerId: proj-webrender
workerType: ci-linux
deadline: {$fromNow: '1 day'}
payload:
maxRunTime: 7200
image: 'staktrace/webrender-test:debian-v6'
env:
RUST_BACKTRACE: 'full'
RUSTFLAGS: '--deny warnings'
command:
- /bin/bash
- '--login'
- '-c'
- >-
curl https://sh.rustup.rs -sSf | sh -s -- -y &&
source $HOME/.cargo/env &&
git clone ${repo_url} webrender && cd webrender &&
git checkout ${sha} &&
servo-tidy &&
ci-scripts/linux-debug-tests.sh
routes:
- "index.project.webrender.ci.${login}.${branch}.linux-debug"
# For the OS X jobs we use a pool of machines that we are managing, because
# Mozilla releng doesn't have any spare OS X machines for us at this time.
# Talk to :kats or :jrmuizel if you need more details about this. The machines
# are hooked up to taskcluster using taskcluster-worker; they use a workerpool
# of proj-webrender/ci-macos. They are set up with all the dependencies needed
# to build and test webrender, including Rust (currently 1.41.1), servo-tidy,
# mako, zlib, etc. Note that unlike the docker-worker used for Linux, these
# machines WILL persist state from one run to the next, so any cleanup needs
# to be handled explicitly.
# macOS tasks currently disabled, see bug 1639217.
# - metadata:
# name: OS X release tests
# description: Runs release-mode WebRender CI stuff on a OS X TC worker
# owner: noreply@mozilla.com
# source: ${repo_url}
# provisionerId: 'proj-webrender'
# workerType: 'ci-macos'
# deadline: {$fromNow: '1 day'}
# payload:
# maxRunTime: 3600
# command:
# - - /bin/bash
# - '--login'
# - '-vec'
# - |
# git clone ${repo_url} webrender
# cd webrender
# git checkout ${sha}
# source $HOME/servotidy-venv/bin/activate
# servo-tidy
# export RUST_BACKTRACE=full
# export RUSTFLAGS='--deny warnings'
# export PKG_CONFIG_PATH="/usr/local/opt/zlib/lib/pkgconfig:$PKG_CONFIG_PATH"
# echo 'exec make -j1 "$@"' > $HOME/make # See #2638
# chmod +x $HOME/make
# export MAKE="$HOME/make"
# ci-scripts/macos-release-tests.sh
# routes:
# - "index.project.webrender.ci.${login}.${branch}.osx-release"
# - metadata:
# name: OS X debug tests
# description: Runs debug-mode WebRender CI stuff on a OS X TC worker
# owner: noreply@mozilla.com
# source: ${repo_url}
# provisionerId: 'proj-webrender'
# workerType: 'ci-macos'
# deadline: {$fromNow: '1 day'}
# payload:
# maxRunTime: 3600
# command:
# - - /bin/bash
# - '--login'
# - '-vec'
# - |
# git clone ${repo_url} webrender
# cd webrender
# git checkout ${sha}
# source $HOME/servotidy-venv/bin/activate
# servo-tidy
# export RUST_BACKTRACE=full
# export RUSTFLAGS='--deny warnings'
# export PKG_CONFIG_PATH="/usr/local/opt/zlib/lib/pkgconfig:$PKG_CONFIG_PATH"
# echo 'exec make -j1 "$@"' > $HOME/make # See #2638
# chmod +x $HOME/make
# export MAKE="$HOME/make"
# ci-scripts/macos-debug-tests.sh
# routes:
# - "index.project.webrender.ci.${login}.${branch}.osx-debug"

2267
third_party/webrender/Cargo.lock generated vendored Normal file

File diff suppressed because it is too large Load diff

25
third_party/webrender/Cargo.toml vendored Normal file
View file

@ -0,0 +1,25 @@
[workspace]
members = [
"direct-composition",
"examples",
"webrender",
"webrender_api",
"wrench",
"example-compositor/compositor",
"tileview",
]
[profile.release]
debug = true
panic = "abort"
[profile.dev]
panic = "abort"
# Running wrench on android built with master cargo-apk results in a crash
# due to a mismatched version of android_glue (a dependency of winit).
# Override it to use a suitable version of android_glue.
# See https://github.com/rust-windowing/android-rs-glue/issues/239.
# This can be removed once a new version of android_glue is published to crates.io.
[patch.crates-io]
android_glue = { git = "https://github.com/rust-windowing/android-rs-glue.git", rev = "e3ac6edea5814e1faca0c31ea8fac6877cb929ea" }

374
third_party/webrender/LICENSE vendored Normal file
View file

@ -0,0 +1,374 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

53
third_party/webrender/README.md vendored Normal file
View file

@ -0,0 +1,53 @@
# WebRender
[![Version](https://img.shields.io/crates/v/webrender.svg)](https://crates.io/crates/webrender)
WebRender is a GPU-based 2D rendering engine written in [Rust](https://www.rust-lang.org/). [Firefox](https://www.mozilla.org/firefox), the research web browser [Servo](https://github.com/servo/servo), and other GUI frameworks draw with it. It currently uses the OpenGL API internally.
Note that the canonical home for this code is in gfx/wr folder of the
mozilla-central repository at https://hg.mozilla.org/mozilla-central. The
Github repository at https://github.com/servo/webrender should be considered
a downstream mirror, although it contains additional metadata (such as Github
wiki pages) that do not exist in mozilla-central. Pull requests against the
Github repository are still being accepted, although once reviewed, they will
be landed on mozilla-central first and then mirrored back. If you are familiar
with the mozilla-central contribution workflow, filing bugs in
[Bugzilla](https://bugzilla.mozilla.org/enter_bug.cgi?product=Core&component=Graphics%3A%20WebRender)
and submitting patches there would be preferred.
## Update as a Dependency
After updating shaders in WebRender, go to servo and:
* Go to the servo directory and do ./mach update-cargo -p webrender
* Create a pull request to servo
## Use WebRender with Servo
To use a local copy of WebRender with servo, go to your servo build directory and:
* Edit Cargo.toml
* Add at the end of the file:
```
[patch."https://github.com/servo/webrender"]
"webrender" = { path = "<path>/webrender" }
"webrender_api" = { path = "<path>/webrender_api" }
```
where `<path>` is the path to your local copy of WebRender.
* Build as normal
## Documentation
The Wiki has a [few pages](https://github.com/servo/webrender/wiki/) describing the internals and conventions of WebRender.
## Testing
Tests run using OSMesa to get consistent rendering across platforms.
Still there may be differences depending on font libraries on your system, for
example.
See [this gist](https://gist.github.com/finalfantasia/129cae811e02bf4551ac) for
how to make the text tests useful in Fedora, for example.

View file

@ -0,0 +1,12 @@
FROM debian:buster-20200422
# Debian 10 doesn't have openjdk-8, so add the Debian 9 repository, which contains it.
RUN sed s/buster/stretch/ /etc/apt/sources.list | tee /etc/apt/sources.list.d/stretch.list
COPY setup.sh /root
RUN cd /root && ./setup.sh
RUN useradd -d /home/worker -s /bin/bash -m worker
USER worker
WORKDIR /home/worker
CMD /bin/bash

View file

@ -0,0 +1,37 @@
#!/usr/bin/env bash
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/. */
set -o errexit
set -o nounset
set -o pipefail
set -o xtrace
test "$(whoami)" == 'root'
# Install stuff we need
apt-get -y update
apt-get install -y \
bzip2 \
cmake \
curl \
gcc \
git \
g++ \
libfontconfig1-dev \
libgl1-mesa-dev \
libx11-dev \
openjdk-8-jdk \
pkg-config \
python \
python-mako \
python-pip \
python-setuptools \
python-voluptuous \
python-yaml \
software-properties-common
# Other stuff we need
pip install servo-tidy==0.3.0

View file

@ -0,0 +1,36 @@
#!/usr/bin/env bash
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/. */
# This must be run from the root webrender directory!
# Users may set the CARGOFLAGS environment variable to pass
# additional flags to cargo if desired.
set -o errexit
set -o nounset
set -o pipefail
set -o xtrace
CARGOFLAGS=${CARGOFLAGS:-"--verbose"} # default to --verbose if not set
pushd webrender
cargo build ${CARGOFLAGS} --no-default-features
cargo build ${CARGOFLAGS} --no-default-features --features capture
cargo build ${CARGOFLAGS} --features capture,profiler
cargo build ${CARGOFLAGS} --features replay
popd
pushd wrench
cargo build ${CARGOFLAGS} --features env_logger
OPTIMIZED=0 python script/headless.py reftest
popd
pushd examples
cargo build ${CARGOFLAGS}
popd
cargo test ${CARGOFLAGS} \
--all --exclude compositor-windows --exclude compositor \
--exclude glsl-to-cxx --exclude swgl

View file

@ -0,0 +1,22 @@
#!/usr/bin/env bash
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/. */
# This must be run from the root webrender directory!
# Users may set the CARGOFLAGS environment variable to pass
# additional flags to cargo if desired.
set -o errexit
set -o nounset
set -o pipefail
set -o xtrace
CARGOFLAGS=${CARGOFLAGS:-""} # default to empty if not set
pushd wrench
python script/headless.py reftest
python script/headless.py rawtest
cargo build ${CARGOFLAGS} --release
popd

View file

@ -0,0 +1,42 @@
#!/usr/bin/env bash
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/. */
# This must be run from the root webrender directory!
# Users may set the CARGOFLAGS environment variable to pass
# additional flags to cargo if desired.
# Note that this script is run in a special cross-compiling configuration,
# where CARGOTESTFLAGS includes `--no-run`, and the binaries produced by
# `cargo test` are run on a different machine. When making changes to this
# file, please ensure any such binaries produced by `cargo test` are not
# deleted, or they may not get run as expected.
set -o errexit
set -o nounset
set -o pipefail
set -o xtrace
CARGOFLAGS=${CARGOFLAGS:-"--verbose"} # default to --verbose if not set
CARGOTESTFLAGS=${CARGOTESTFLAGS:-""}
pushd webrender
cargo check ${CARGOFLAGS} --no-default-features
cargo check ${CARGOFLAGS} --no-default-features --features capture
cargo check ${CARGOFLAGS} --features capture,profiler
cargo check ${CARGOFLAGS} --features replay
popd
pushd wrench
cargo check ${CARGOFLAGS} --features env_logger
popd
pushd examples
cargo check ${CARGOFLAGS}
popd
cargo test ${CARGOFLAGS} ${CARGOTESTFLAGS} \
--all --exclude compositor-windows --exclude compositor \
--exclude glsl-to-cxx --exclude swgl

View file

@ -0,0 +1,29 @@
#!/usr/bin/env bash
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/. */
# This must be run from the root webrender directory!
# Users may set the CARGOFLAGS environment variable to pass
# additional flags to cargo if desired.
# The WRENCH_BINARY environment variable, if set, is used to run
# the precached reftest.
set -o errexit
set -o nounset
set -o pipefail
set -o xtrace
CARGOFLAGS=${CARGOFLAGS:-""} # default to empty if not set
WRENCH_BINARY=${WRENCH_BINARY:-""}
pushd wrench
python script/headless.py reftest
if [[ -z "${WRENCH_BINARY}" ]]; then
cargo build ${CARGOFLAGS} --release
WRENCH_BINARY="../target/release/wrench"
fi
"${WRENCH_BINARY}" --precache \
reftest reftests/clip/fixed-position-clipping.yaml
popd

View file

@ -0,0 +1,124 @@
# http://blogs.technet.com/b/heyscriptingguy/archive/2010/07/07/hey-scripting-guy-how-can-i-change-my-desktop-monitor-resolution-via-windows-powershell.aspx
Function Set-ScreenResolution {
param (
[Parameter(Mandatory=$true,
Position = 0)]
[int]
$Width,
[Parameter(Mandatory=$true,
Position = 1)]
[int]
$Height
)
$pinvokeCode = @"
using System;
using System.Runtime.InteropServices;
namespace Resolution
{
[StructLayout(LayoutKind.Sequential)]
public struct DEVMODE1
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string dmDeviceName;
public short dmSpecVersion;
public short dmDriverVersion;
public short dmSize;
public short dmDriverExtra;
public int dmFields;
public short dmOrientation;
public short dmPaperSize;
public short dmPaperLength;
public short dmPaperWidth;
public short dmScale;
public short dmCopies;
public short dmDefaultSource;
public short dmPrintQuality;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string dmFormName;
public short dmLogPixels;
public short dmBitsPerPel;
public int dmPelsWidth;
public int dmPelsHeight;
public int dmDisplayFlags;
public int dmDisplayFrequency;
public int dmICMMethod;
public int dmICMIntent;
public int dmMediaType;
public int dmDitherType;
public int dmReserved1;
public int dmReserved2;
public int dmPanningWidth;
public int dmPanningHeight;
};
class User_32
{
[DllImport("user32.dll")]
public static extern int EnumDisplaySettings(string deviceName, int modeNum, ref DEVMODE1 devMode);
[DllImport("user32.dll")]
public static extern int ChangeDisplaySettings(ref DEVMODE1 devMode, int flags);
public const int ENUM_CURRENT_SETTINGS = -1;
public const int CDS_UPDATEREGISTRY = 0x01;
public const int CDS_TEST = 0x02;
public const int DISP_CHANGE_SUCCESSFUL = 0;
public const int DISP_CHANGE_RESTART = 1;
public const int DISP_CHANGE_FAILED = -1;
}
public class PrmaryScreenResolution
{
static public string ChangeResolution(int width, int height)
{
DEVMODE1 dm = GetDevMode1();
if (0 != User_32.EnumDisplaySettings(null, User_32.ENUM_CURRENT_SETTINGS, ref dm))
{
dm.dmPelsWidth = width;
dm.dmPelsHeight = height;
int iRet = User_32.ChangeDisplaySettings(ref dm, User_32.CDS_TEST);
if (iRet == User_32.DISP_CHANGE_FAILED)
{
return "Unable To Process Your Request. Sorry For This Inconvenience.";
}
else
{
iRet = User_32.ChangeDisplaySettings(ref dm, User_32.CDS_UPDATEREGISTRY);
switch (iRet)
{
case User_32.DISP_CHANGE_SUCCESSFUL:
{
return "Success";
}
case User_32.DISP_CHANGE_RESTART:
{
return "You Need To Reboot For The Change To Happen.\n If You Feel Any Problem After Rebooting Your Machine\nThen Try To Change Resolution In Safe Mode.";
}
default:
{
return "Failed To Change The Resolution";
}
}
}
}
else
{
return "Failed To Change The Resolution.";
}
}
private static DEVMODE1 GetDevMode1()
{
DEVMODE1 dm = new DEVMODE1();
dm.dmDeviceName = new String(new char[32]);
dm.dmFormName = new String(new char[32]);
dm.dmSize = (short)Marshal.SizeOf(dm);
return dm;
}
}
}
"@
Add-Type $pinvokeCode -ErrorAction SilentlyContinue
[Resolution.PrmaryScreenResolution]::ChangeResolution($width,$height)
}

View file

@ -0,0 +1,36 @@
:: This Source Code Form is subject to the terms of the Mozilla Public
:: License, v. 2.0. If a copy of the MPL was not distributed with this
:: file, You can obtain one at http://mozilla.org/MPL/2.0/. */
:: This must be run from the root webrender directory!
:: Users may set the CARGOFLAGS environment variable to pass
:: additional flags to cargo if desired.
if NOT DEFINED CARGOFLAGS SET CARGOFLAGS=--verbose
pushd webrender_api
cargo test %CARGOFLAGS%
if %ERRORLEVEL% NEQ 0 EXIT /b %ERRORLEVEL%
popd
pushd webrender
cargo test %CARGOFLAGS%
if %ERRORLEVEL% NEQ 0 EXIT /b %ERRORLEVEL%
popd
pushd wrench
cargo test --verbose
if %ERRORLEVEL% NEQ 0 EXIT /b %ERRORLEVEL%
cargo run --release -- --angle reftest
if %ERRORLEVEL% NEQ 0 EXIT /b %ERRORLEVEL%
popd
pushd examples
cargo check --verbose
if %ERRORLEVEL% NEQ 0 EXIT /b %ERRORLEVEL%
popd
pushd direct-composition
cargo check --verbose
if %ERRORLEVEL% NEQ 0 EXIT /b %ERRORLEVEL%
popd

View file

@ -0,0 +1,6 @@
{
"presets": [
["env", { "modules": false }],
"stage-3"
]
}

View file

@ -0,0 +1,11 @@
.DS_Store
node_modules/
npm-debug.log
yarn-error.log
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln

View file

@ -0,0 +1,23 @@
# WebRender Debugger
A web based debugger for WebRender.
## Using the debugger
Build your application with the debugger feature enabled, for example in wrench:
```
cargo build --features=debugger
```
Now, open your browser and open the debugger/index.html file. Click Connect and
the debugger will attempt to connect to WR via websocket.
## Using the debugger with Gecko
In the Gecko source tree, open ```gfx/webrender_bindings/Cargo.toml``` in a text editor.
Add ```features = ['debugger']``` to the end of the file (in the ```dependencies.webrender``` section).
Vendor the rust dependencies locally for the debugger (we don't want these committed to the repo):
```./mach vendor rust```
Now, build and run as usual, and the debugger will be available.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>WebRender Debugger</title>
</head>
<body>
<div id="app"></div>
<script src="dist/build.js"></script>
</body>
</html>

7606
third_party/webrender/debugger/package-lock.json generated vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,36 @@
{
"name": "debugger",
"description": "WebRender Debugger",
"version": "1.0.0",
"author": "Glenn Watson <github@intuitionlibrary.com>",
"license": "MIT",
"private": true,
"scripts": {
"dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
},
"dependencies": {
"buefy": "^0.6.7",
"vue": "^2.5.11",
"vue-material-design-icons": "^0.8.2",
"vuex": "^3.0.1"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
],
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-env": "^1.6.0",
"babel-preset-stage-3": "^6.24.1",
"cross-env": "^5.0.5",
"css-loader": "^0.28.7",
"file-loader": "^1.1.4",
"vue-loader": "^13.0.5",
"vue-template-compiler": "^2.4.4",
"webpack": "^3.6.0",
"webpack-dev-server": "^2.9.1"
}
}

View file

@ -0,0 +1,55 @@
<template>
<div>
<app-navbar></app-navbar>
<div class="section">
<div class="container">
<div class="columns">
<div class="column is-3">
<app-navmenu></app-navmenu>
</div>
<div class="column">
<app-options v-if="page == 'options'"></app-options>
<app-passview v-if="page == 'passes'"></app-passview>
<app-rendertaskview v-if="page == 'render_tasks'"></app-rendertaskview>
<app-documentview v-if="page == 'documents'"></app-documentview>
<app-clipscrolltreeview v-if="page == 'clip_scroll_tree'"></app-clipscrolltreeview>
<app-screenshotview v-if="page == 'screenshot'"></app-screenshotview>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import NavBar from './components/NavBar.vue'
import NavMenu from './components/NavMenu.vue'
import OptionsPage from './components/OptionsPage.vue'
import PassViewPage from './components/PassViewPage.vue'
import RenderTaskViewPage from './components/RenderTaskViewPage.vue'
import DocumentViewPage from './components/DocumentViewPage.vue'
import ClipScrollTreeViewPage from './components/ClipScrollTreeViewPage.vue'
import ScreenshotPage from './components/ScreenshotPage.vue'
export default {
name: 'app',
components: {
'app-navbar': NavBar,
'app-navmenu': NavMenu,
'app-options': OptionsPage,
'app-passview': PassViewPage,
'app-rendertaskview': RenderTaskViewPage,
'app-documentview': DocumentViewPage,
'app-clipscrolltreeview': ClipScrollTreeViewPage,
'app-screenshotview': ScreenshotPage,
},
computed: {
page() {
return this.$store.state.page;
}
},
}
</script>
<style>
</style>

View file

@ -0,0 +1,37 @@
<template>
<div class="box">
<h1 class="title">Clip-Scroll Tree <a :disabled="disabled" v-on:click="fetch" class="button is-info">Refresh</a></h1>
<hr/>
<div>
<ul>
<app-treeview :model=clip_scroll_tree></app-treeview>
</ul>
</div>
</div>
</template>
<script>
import TreeView from './TreeView.vue'
export default {
components: {
'app-treeview': TreeView,
},
methods: {
fetch: function() {
this.$store.dispatch('sendMessage', "fetch_clip_scroll_tree");
}
},
computed: {
disabled() {
return !this.$store.state.connected
},
clip_scroll_tree() {
return this.$store.state.clip_scroll_tree
}
},
}
</script>
<style>
</style>

View file

@ -0,0 +1,37 @@
<template>
<div class="box">
<h1 class="title">Documents <a :disabled="disabled" v-on:click="fetch" class="button is-info">Refresh</a></h1>
<hr/>
<div>
<ul>
<app-treeview :model=documents></app-treeview>
</ul>
</div>
</div>
</template>
<script>
import TreeView from './TreeView.vue'
export default {
components: {
'app-treeview': TreeView,
},
methods: {
fetch: function() {
this.$store.dispatch('sendMessage', "fetch_documents");
}
},
computed: {
disabled() {
return !this.$store.state.connected
},
documents() {
return this.$store.state.documents
}
},
}
</script>
<style>
</style>

View file

@ -0,0 +1,41 @@
<template>
<nav class="navbar has-shadow">
<div class="navbar-brand">
<a class="navbar-item" href="#">WebRender Debugger</a>
</div>
<div class="navbar-menu">
<div class="navbar-start"></div>
<div class="navbar-end">
<div class="navbar-item">
<p class="control">
<button v-if="isConnected" @click="disconnect" class="button is-danger">Disconnect</button>
<button v-else @click="connect" class="button is-success">Connect</button>
</p>
</div>
</div>
</div>
</nav>
</template>
<script>
export default {
computed: {
isConnected() {
return this.$store.state.connected;
},
},
methods: {
connect() {
this.$store.dispatch('connect');
},
disconnect() {
this.$store.dispatch('disconnect');
},
}
}
</script>
<style>
</style>

View file

@ -0,0 +1,33 @@
<template>
<aside class="menu">
<p class="menu-label">
Pages
</p>
<ul class="menu-list">
<li><a @click="setPage('options')" :class="{ 'is-active': page == 'options' }">Debug Options</a></li>
<li><a @click="setPage('passes')" :class="{ 'is-active': page == 'passes' }">Passes</a></li>
<li><a @click="setPage('render_tasks')" :class="{ 'is-active': page == 'render_tasks' }">Render Tasks</a></li>
<li><a @click="setPage('documents')" :class="{ 'is-active': page == 'documents' }">Documents</a></li>
<li><a @click="setPage('clip_scroll_tree')" v-bind:class="{ 'is-active': page == 'clip_scroll_tree' }">Clip-Scroll Tree</a></li>
<li><a @click="setPage('screenshot')" v-bind:class="{ 'is-active': page == 'screenshot' }">Screenshot</a></li>
</ul>
</aside>
</template>
<script>
export default {
methods: {
setPage(name) {
this.$store.commit('setPage', name);
},
},
computed: {
page() {
return this.$store.state.page;
}
},
}
</script>
<style>
</style>

View file

@ -0,0 +1,162 @@
<template>
<div class="box">
<div class="field">
<label class="checkbox">
<input type="checkbox" :disabled="disabled" v-on:click="setProfiler($event.target.checked)">
Profiler
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox" :disabled="disabled" v-on:click="setTextureCacheDebugger($event.target.checked)">
Texture cache debugger
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox" :disabled="disabled" v-on:click="setRenderTargetDebugger($event.target.checked)">
Render target debugger
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox" :disabled="disabled" v-on:click="setAlphaRectsDebugger($event.target.checked)">
Alpha primitive rects debugger
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox" :disabled="disabled" v-on:click="setGpuTimeQueries($event.target.checked)">
Enable GPU time queries
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox" :disabled="disabled" v-on:click="setGpuSampleQueries($event.target.checked)">
Enable GPU sample queries
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox" :disabled="disabled" v-on:click="setOpaquePass(!$event.target.checked)">
Disable opaque pass
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox" :disabled="disabled" v-on:click="setAlphaPass(!$event.target.checked)">
Disable alpha pass
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox" :disabled="disabled" v-on:click="setClipMasks(!$event.target.checked)">
Disable clip masks
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox" :disabled="disabled" v-on:click="setTextPrims(!$event.target.checked)">
Disable text primitives
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox" :disabled="disabled" v-on:click="setGradientPrims(!$event.target.checked)">
Disable gradient primitives
</label>
</div>
</div>
</template>
<script>
export default {
computed: {
disabled() {
return !this.$store.state.connected
}
},
methods: {
setProfiler(enabled) {
if (enabled) {
this.$store.dispatch('sendMessage', "enable_profiler");
} else {
this.$store.dispatch('sendMessage', "disable_profiler");
}
},
setTextureCacheDebugger(enabled) {
if (enabled) {
this.$store.dispatch('sendMessage', "enable_texture_cache_debug");
} else {
this.$store.dispatch('sendMessage', "disable_texture_cache_debug");
}
},
setRenderTargetDebugger(enabled) {
if (enabled) {
this.$store.dispatch('sendMessage', "enable_render_target_debug");
} else {
this.$store.dispatch('sendMessage', "disable_render_target_debug");
}
},
setAlphaRectsDebugger(enabled) {
if (enabled) {
this.$store.dispatch('sendMessage', "enable_alpha_rects_debug");
} else {
this.$store.dispatch('sendMessage', "disable_alpha_rects_debug");
}
},
setGpuTimeQueries(enabled) {
if (enabled) {
this.$store.dispatch('sendMessage', "enable_gpu_time_queries");
} else {
this.$store.dispatch('sendMessage', "disable_gpu_time_queries");
}
},
setGpuSampleQueries(enabled) {
if (enabled) {
this.$store.dispatch('sendMessage', "enable_gpu_sample_queries");
} else {
this.$store.dispatch('sendMessage', "disable_gpu_sample_queries");
}
},
setOpaquePass(enabled) {
if (enabled) {
this.$store.dispatch('sendMessage', "enable_opaque_pass");
} else {
this.$store.dispatch('sendMessage', "disable_opaque_pass");
}
},
setAlphaPass(enabled) {
if (enabled) {
this.$store.dispatch('sendMessage', "enable_alpha_pass");
} else {
this.$store.dispatch('sendMessage', "disable_alpha_pass");
}
},
setClipMasks(enabled) {
if (enabled) {
this.$store.dispatch('sendMessage', "enable_clip_masks");
} else {
this.$store.dispatch('sendMessage', "disable_clip_masks");
}
},
setTextPrims(enabled) {
if (enabled) {
this.$store.dispatch('sendMessage', "enable_text_prims");
} else {
this.$store.dispatch('sendMessage', "disable_text_prims");
}
},
setGradientPrims(enabled) {
if (enabled) {
this.$store.dispatch('sendMessage', "enable_gradient_prims");
} else {
this.$store.dispatch('sendMessage', "disable_gradient_prims");
}
}
},
}
</script>
<style>
</style>

View file

@ -0,0 +1,37 @@
<template>
<div class="box">
<h1 class="title">Passes <a :disabled="disabled" v-on:click="fetch" class="button is-info">Refresh</a></h1>
<hr/>
<div v-for="(pass, pass_index) in passes">
<p class="has-text-black-bis">Pass {{pass_index}}</p>
<div v-for="(target, target_index) in pass.targets">
<p style="text-indent: 2em;" class="has-text-grey-dark">Target {{target_index}} ({{target.kind}})</p>
<div v-for="(batch, batch_index) in target.batches">
<p style="text-indent: 4em;" class="has-text-grey">Batch {{batch_index}} ({{batch.description}}, {{batch.kind}}, {{batch.count}} instances)</p>
</div>
</div>
<hr/>
</div>
</div>
</template>
<script>
export default {
methods: {
fetch: function() {
this.$store.dispatch('sendMessage', "fetch_passes");
}
},
computed: {
disabled() {
return !this.$store.state.connected
},
passes() {
return this.$store.state.passes
}
},
}
</script>
<style>
</style>

View file

@ -0,0 +1,37 @@
<template>
<div class="box">
<h1 class="title">Render Tasks <a :disabled="disabled" v-on:click="fetch" class="button is-info">Refresh</a></h1>
<hr/>
<div>
<ul>
<app-treeview :model=render_tasks></app-treeview>
</ul>
</div>
</div>
</template>
<script>
import TreeView from './TreeView.vue'
export default {
components: {
'app-treeview': TreeView,
},
methods: {
fetch: function() {
this.$store.dispatch('sendMessage', "fetch_render_tasks");
}
},
computed: {
disabled() {
return !this.$store.state.connected
},
render_tasks() {
return this.$store.state.render_tasks
}
},
}
</script>
<style>
</style>

View file

@ -0,0 +1,32 @@
<template>
<div class="box">
<h1 class="title">Screenshot <a :disabled="disabled" v-on:click="fetch" class="button is-info">Refresh</a></h1>
<hr/>
<div>
<ul>
<img v-if="screenshot.length > 0" style="transform: scaleY(-1); width: 1024px; height:768px" :src="'data:image/png;base64,' + screenshot" />
</ul>
</div>
</div>
</template>
<script>
export default {
computed: {
disabled() {
return !this.$store.state.connected
},
screenshot() {
return this.$store.state.screenshot
},
},
methods: {
fetch: function() {
this.$store.dispatch('sendMessage', "fetch_screenshot");
}
},
}
</script>
<style>
</style>

View file

@ -0,0 +1,40 @@
<template>
<li>
<div v-on:click="toggle">
<span v-if="isFolder">[{{open ? '-' : '+'}}]</span>
{{model.description}}
</div>
<ul style="padding-left: 1em; line-height: 1.5em;" v-show="open" v-if="isFolder">
<treeview v-for="model in model.children" :model="model"></treeview>
</ul>
</li>
</template>
<script>
export default {
name: 'treeview',
props: [
'model',
],
data: function () {
return {
open: false
}
},
computed: {
isFolder: function () {
return this.model.children && this.model.children.length
}
},
methods: {
toggle: function () {
if (this.isFolder) {
this.open = !this.open
}
},
},
}
</script>
<style>
</style>

View file

@ -0,0 +1,14 @@
import Vue from 'vue';
import Buefy from 'buefy';
import 'buefy/dist/buefy.css';
import "vue-material-design-icons/styles.css";
import App from './App.vue';
import store from './store';
Vue.use(Buefy);
new Vue({
el: '#app',
store,
render: h => h(App)
})

View file

@ -0,0 +1,105 @@
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
class Connection {
constructor() {
this.ws = null;
}
connect(context) {
var ws = new WebSocket("ws://127.0.0.1:3583");
ws.onopen = function() {
context.commit('setConnected', true);
}
ws.onmessage = function(evt) {
var json = JSON.parse(evt.data);
if (json['kind'] == "passes") {
context.commit('setPasses', json['passes']);
} else if (json['kind'] == "render_tasks") {
context.commit('setRenderTasks', json['root']);
} else if (json['kind'] == "documents") {
context.commit('setDocuments', json['root']);
} else if (json['kind'] == "clip_scroll_tree") {
context.commit('setClipScrollTree', json['root']);
} else if (json['kind'] == "screenshot") {
context.commit('setScreenshot', json['data']);
} else {
console.warn("unknown message kind: " + json['kind']);
}
}
ws.onclose = function() {
context.commit('setConnected', false);
}
this.ws = ws;
}
send(msg) {
if (this.ws !== null) {
this.ws.send(msg);
}
}
disconnect() {
if (this.ws !== null) {
this.ws.close();
this.ws = null;
}
}
}
var connection = new Connection();
const store = new Vuex.Store({
strict: true,
state: {
connected: false,
page: 'options',
passes: [],
render_tasks: [],
documents: [],
clip_scroll_tree: [],
screenshot: [],
},
mutations: {
setConnected(state, connected) {
state.connected = connected;
},
setPage(state, name) {
state.page = name;
},
setPasses(state, passes) {
state.passes = passes;
},
setRenderTasks(state, render_tasks) {
state.render_tasks = render_tasks;
},
setDocuments(state, documents) {
state.documents = documents;
},
setClipScrollTree(state, clip_scroll_tree) {
state.clip_scroll_tree = clip_scroll_tree;
},
setScreenshot(state, screenshot) {
state.screenshot = screenshot;
},
},
actions: {
connect(context) {
connection.connect(context);
},
disconnect(context) {
connection.disconnect();
},
sendMessage(context, msg) {
connection.send(msg);
}
}
});
export default store;

View file

@ -0,0 +1,81 @@
var path = require('path')
var webpack = require('webpack')
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'build.js'
},
module: {
rules: [
{
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader'
],
}, {
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
}
// other vue-loader options go here
}
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
}
]
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
},
alias : {
"icons": path.resolve(__dirname, "node_modules/vue-material-design-icons")
},
extensions: ['*', '.js', '.vue', '.json']
},
devServer: {
historyApiFallback: true,
noInfo: true,
overlay: true
},
performance: {
hints: false
},
devtool: '#eval-source-map'
}
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = '#source-map'
// http://vue-loader.vuejs.org/en/workflow/production.html
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
warnings: false
}
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
}

View file

@ -0,0 +1,14 @@
[package]
name = "direct-composition"
version = "0.1.0"
authors = ["Simon Sapin <simon.sapin@exyr.org>"]
license = "MPL-2.0"
edition = "2018"
[target.'cfg(windows)'.dependencies]
euclid = "0.22"
gleam = "0.12"
mozangle = {version = "0.3.1", features = ["egl"]}
webrender = {path = "../webrender"}
winapi = {version = "0.3", features = ["winerror", "d3d11", "dcomp"]}
winit = "0.19"

View file

@ -0,0 +1,112 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::ops;
use std::ptr;
use winapi::Interface;
use winapi::ctypes::c_void;
use winapi::shared::guiddef::GUID;
use winapi::shared::winerror::HRESULT;
use winapi::shared::winerror::SUCCEEDED;
use winapi::um::unknwnbase::IUnknown;
pub fn as_ptr<T>(x: &T) -> *mut T {
x as *const T as _
}
pub trait CheckHResult {
fn check_hresult(self);
}
impl CheckHResult for HRESULT {
fn check_hresult(self) {
if !SUCCEEDED(self) {
panic_com(self)
}
}
}
fn panic_com(hresult: HRESULT) -> ! {
panic!("COM error 0x{:08X}", hresult as u32)
}
/// Forked from <https://github.com/retep998/wio-rs/blob/44093f7db8/src/com.rs>
#[derive(PartialEq, Debug)]
pub struct ComPtr<T>(*mut T) where T: Interface;
impl<T> ComPtr<T> where T: Interface {
/// Creates a `ComPtr` to wrap a raw pointer.
/// It takes ownership over the pointer which means it does __not__ call `AddRef`.
/// `T` __must__ be a COM interface that inherits from `IUnknown`.
pub unsafe fn from_raw(ptr: *mut T) -> ComPtr<T> {
assert!(!ptr.is_null());
ComPtr(ptr)
}
/// For use with APIs that take an interface UUID and
/// "return" a new COM object through a `*mut *mut c_void` out-parameter.
pub unsafe fn new_with_uuid<F>(f: F) -> Self
where F: FnOnce(&GUID, *mut *mut c_void) -> HRESULT
{
Self::new_with(|ptr| f(&T::uuidof(), ptr as _))
}
/// For use with APIs that "return" a new COM object through a `*mut *mut T` out-parameter.
pub unsafe fn new_with<F>(f: F) -> Self
where F: FnOnce(*mut *mut T) -> HRESULT
{
let mut ptr = ptr::null_mut();
let hresult = f(&mut ptr);
if SUCCEEDED(hresult) {
ComPtr::from_raw(ptr)
} else {
if !ptr.is_null() {
let ptr = ptr as *mut IUnknown;
(*ptr).Release();
}
panic_com(hresult)
}
}
pub fn as_raw(&self) -> *mut T {
self.0
}
fn as_unknown(&self) -> &IUnknown {
unsafe {
&*(self.0 as *mut IUnknown)
}
}
/// Performs QueryInterface fun.
pub fn cast<U>(&self) -> ComPtr<U> where U: Interface {
unsafe {
ComPtr::<U>::new_with_uuid(|uuid, ptr| self.as_unknown().QueryInterface(uuid, ptr))
}
}
}
impl<T> ops::Deref for ComPtr<T> where T: Interface {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.0 }
}
}
impl<T> Clone for ComPtr<T> where T: Interface {
fn clone(&self) -> Self {
unsafe {
self.as_unknown().AddRef();
ComPtr(self.0)
}
}
}
impl<T> Drop for ComPtr<T> where T: Interface {
fn drop(&mut self) {
unsafe {
self.as_unknown().Release();
}
}
}

View file

@ -0,0 +1,174 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use mozangle::egl::ffi::*;
use std::os::raw::c_void;
use std::ptr;
use std::rc::Rc;
use winapi::um::d3d11::ID3D11Device;
use winapi::um::d3d11::ID3D11Texture2D;
pub use mozangle::egl::get_proc_address;
pub struct SharedEglThings {
device: EGLDeviceEXT,
display: types::EGLDisplay,
config: types::EGLConfig,
context: types::EGLContext,
}
fn cast_attributes(slice: &[types::EGLenum]) -> &EGLint {
unsafe {
&*(slice.as_ptr() as *const EGLint)
}
}
macro_rules! attributes {
($( $key: expr => $value: expr, )*) => {
cast_attributes(&[
$( $key, $value, )*
NONE,
])
}
}
impl SharedEglThings {
pub unsafe fn new(d3d_device: *mut ID3D11Device) -> Rc<Self> {
let device = eglCreateDeviceANGLE(
D3D11_DEVICE_ANGLE,
d3d_device as *mut c_void,
ptr::null(),
).check();
let display = GetPlatformDisplayEXT(
PLATFORM_DEVICE_EXT,
device,
attributes! [
EXPERIMENTAL_PRESENT_PATH_ANGLE => EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE,
],
).check();
Initialize(display, ptr::null_mut(), ptr::null_mut()).check();
// Adapted from
// https://searchfox.org/mozilla-central/rev/056a4057/gfx/gl/GLContextProviderEGL.cpp#635
let mut configs = [ptr::null(); 64];
let mut num_configs = 0;
ChooseConfig(
display,
attributes! [
SURFACE_TYPE => WINDOW_BIT,
RENDERABLE_TYPE => OPENGL_ES2_BIT,
RED_SIZE => 8,
GREEN_SIZE => 8,
BLUE_SIZE => 8,
ALPHA_SIZE => 8,
],
configs.as_mut_ptr(),
configs.len() as i32,
&mut num_configs,
).check();
let config = pick_config(&configs[..num_configs as usize]);
let context = CreateContext(
display, config, NO_CONTEXT,
attributes![
CONTEXT_CLIENT_VERSION => 3,
]
).check();
MakeCurrent(display, NO_SURFACE, NO_SURFACE, context).check();
Rc::new(SharedEglThings { device, display, config, context })
}
}
fn pick_config(configs: &[types::EGLConfig]) -> types::EGLConfig {
// FIXME: better criteria to make this choice?
// Firefox uses GetConfigAttrib to find a config that has the requested r/g/b/a sizes
// https://searchfox.org/mozilla-central/rev/056a4057/gfx/gl/GLContextProviderEGL.cpp#662-685
configs[0]
}
impl Drop for SharedEglThings {
fn drop(&mut self) {
unsafe {
// FIXME does EGLDisplay or EGLConfig need clean up? How?
DestroyContext(self.display, self.context).check();
eglReleaseDeviceANGLE(self.device).check();
}
}
}
pub struct PerVisualEglThings {
shared: Rc<SharedEglThings>,
surface: types::EGLSurface,
}
impl PerVisualEglThings {
pub unsafe fn new(shared: Rc<SharedEglThings>, buffer: *const ID3D11Texture2D,
width: u32, height: u32)
-> Self {
let surface = CreatePbufferFromClientBuffer(
shared.display,
D3D_TEXTURE_ANGLE,
buffer as types::EGLClientBuffer,
shared.config,
attributes! [
WIDTH => width,
HEIGHT => height,
FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE => TRUE,
],
).check();
PerVisualEglThings { shared, surface }
}
pub fn make_current(&self) {
unsafe {
MakeCurrent(self.shared.display, self.surface, self.surface, self.shared.context).check();
}
}
}
impl Drop for PerVisualEglThings {
fn drop(&mut self) {
unsafe {
DestroySurface(self.shared.display, self.surface).check();
}
}
}
fn check_error() {
unsafe {
let error = GetError() as types::EGLenum;
assert_eq!(error, SUCCESS, "0x{:x} != 0x{:x}", error, SUCCESS);
}
}
trait Check {
fn check(self) -> Self;
}
impl Check for *const c_void {
fn check(self) -> Self {
check_error();
assert!(!self.is_null());
self
}
}
impl Check for *mut c_void {
fn check(self) -> Self {
check_error();
assert!(!self.is_null());
self
}
}
impl Check for types::EGLBoolean {
fn check(self) -> Self {
check_error();
assert_eq!(self, TRUE);
self
}
}

View file

@ -0,0 +1,179 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#![cfg(windows)]
extern crate gleam;
extern crate mozangle;
extern crate winapi;
use com::{ComPtr, CheckHResult, as_ptr};
use std::ptr;
use std::rc::Rc;
use winapi::shared::dxgi1_2::DXGI_SWAP_CHAIN_DESC1;
use winapi::shared::dxgi1_2::IDXGIFactory2;
use winapi::shared::minwindef::{TRUE, FALSE};
use winapi::shared::windef::HWND;
use winapi::um::d3d11::ID3D11Device;
use winapi::um::dcomp::IDCompositionDevice;
use winapi::um::dcomp::IDCompositionTarget;
use winapi::um::dcomp::IDCompositionVisual;
mod com;
mod egl;
pub struct DirectComposition {
d3d_device: ComPtr<ID3D11Device>,
dxgi_factory: ComPtr<IDXGIFactory2>,
egl: Rc<egl::SharedEglThings>,
pub gleam: Rc<dyn gleam::gl::Gl>,
composition_device: ComPtr<IDCompositionDevice>,
root_visual: ComPtr<IDCompositionVisual>,
#[allow(unused)] // Needs to be kept alive
composition_target: ComPtr<IDCompositionTarget>,
}
impl DirectComposition {
/// Initialize DirectComposition in the given window
///
/// # Safety
///
/// `hwnd` must be a valid handle to a window.
pub unsafe fn new(hwnd: HWND) -> Self {
let d3d_device = ComPtr::new_with(|ptr_ptr| winapi::um::d3d11::D3D11CreateDevice(
ptr::null_mut(),
winapi::um::d3dcommon::D3D_DRIVER_TYPE_HARDWARE,
ptr::null_mut(),
winapi::um::d3d11::D3D11_CREATE_DEVICE_BGRA_SUPPORT |
if cfg!(debug_assertions) {
winapi::um::d3d11::D3D11_CREATE_DEVICE_DEBUG
} else {
0
},
ptr::null_mut(),
0,
winapi::um::d3d11::D3D11_SDK_VERSION,
ptr_ptr,
&mut 0,
ptr::null_mut(),
));
let egl = egl::SharedEglThings::new(d3d_device.as_raw());
let gleam = gleam::gl::GlesFns::load_with(egl::get_proc_address);
let dxgi_device = d3d_device.cast::<winapi::shared::dxgi::IDXGIDevice>();
// https://msdn.microsoft.com/en-us/library/windows/desktop/hh404556(v=vs.85).aspx#code-snippet-1
// “Because you can create a Direct3D device without creating a swap chain,
// you might need to retrieve the factory that is used to create the device
// in order to create a swap chain.”
let adapter = ComPtr::new_with(|ptr_ptr| dxgi_device.GetAdapter(ptr_ptr));
let dxgi_factory = ComPtr::<IDXGIFactory2>::new_with_uuid(|uuid, ptr_ptr| {
adapter.GetParent(uuid, ptr_ptr)
});
// Create the DirectComposition device object.
let composition_device = ComPtr::<IDCompositionDevice>::new_with_uuid(|uuid, ptr_ptr| {
winapi::um::dcomp::DCompositionCreateDevice(&*dxgi_device, uuid, ptr_ptr)
});
// Create the composition target object based on the
// specified application window.
let composition_target = ComPtr::new_with(|ptr_ptr| {
composition_device.CreateTargetForHwnd(hwnd, TRUE, ptr_ptr)
});
let root_visual = ComPtr::new_with(|ptr_ptr| composition_device.CreateVisual(ptr_ptr));
composition_target.SetRoot(&*root_visual).check_hresult();
DirectComposition {
d3d_device, dxgi_factory,
egl, gleam,
composition_device, composition_target, root_visual,
}
}
/// Execute changes to the DirectComposition scene.
pub fn commit(&self) {
unsafe {
self.composition_device.Commit().check_hresult()
}
}
pub fn create_angle_visual(&self, width: u32, height: u32) -> AngleVisual {
unsafe {
let desc = DXGI_SWAP_CHAIN_DESC1 {
Width: width,
Height: height,
Format: winapi::shared::dxgiformat::DXGI_FORMAT_B8G8R8A8_UNORM,
Stereo: FALSE,
SampleDesc: winapi::shared::dxgitype::DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
BufferUsage: winapi::shared::dxgitype::DXGI_USAGE_RENDER_TARGET_OUTPUT,
BufferCount: 2,
Scaling: winapi::shared::dxgi1_2::DXGI_SCALING_STRETCH,
SwapEffect: winapi::shared::dxgi::DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
AlphaMode: winapi::shared::dxgi1_2::DXGI_ALPHA_MODE_PREMULTIPLIED,
Flags: 0,
};
let swap_chain = ComPtr::<winapi::shared::dxgi1_2::IDXGISwapChain1>::new_with(|ptr_ptr| {
self.dxgi_factory.CreateSwapChainForComposition(
as_ptr(&self.d3d_device),
&desc,
ptr::null_mut(),
ptr_ptr,
)
});
let back_buffer = ComPtr::<winapi::um::d3d11::ID3D11Texture2D>::new_with_uuid(|uuid, ptr_ptr| {
swap_chain.GetBuffer(0, uuid, ptr_ptr)
});
let egl = egl::PerVisualEglThings::new(self.egl.clone(), &*back_buffer, width, height);
let gleam = self.gleam.clone();
let visual = ComPtr::new_with(|ptr_ptr| self.composition_device.CreateVisual(ptr_ptr));
visual.SetContent(&*****swap_chain).check_hresult();
self.root_visual.AddVisual(&*visual, FALSE, ptr::null_mut()).check_hresult();
AngleVisual { visual, swap_chain, egl, gleam }
}
}
}
/// A DirectComposition "visual" configured for rendering with Direct3D.
pub struct AngleVisual {
visual: ComPtr<IDCompositionVisual>,
swap_chain: ComPtr<winapi::shared::dxgi1_2::IDXGISwapChain1>,
egl: egl::PerVisualEglThings,
pub gleam: Rc<dyn gleam::gl::Gl>,
}
impl AngleVisual {
pub fn set_offset_x(&self, offset_x: f32) {
unsafe {
self.visual.SetOffsetX_1(offset_x).check_hresult()
}
}
pub fn set_offset_y(&self, offset_y: f32) {
unsafe {
self.visual.SetOffsetY_1(offset_y).check_hresult()
}
}
pub fn make_current(&self) {
self.egl.make_current()
}
pub fn present(&self) {
self.gleam.finish();
unsafe {
self.swap_chain.Present(0, 0).check_hresult()
}
}
}

View file

@ -0,0 +1,11 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#[cfg(not(windows))]
fn main() {
println!("This demo only runs on Windows.");
}
#[cfg(windows)]
include!("main_windows.rs");

View file

@ -0,0 +1,212 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
extern crate direct_composition;
extern crate euclid;
extern crate gleam;
extern crate webrender;
extern crate winit;
use euclid::size2;
use direct_composition::DirectComposition;
use std::sync::mpsc;
use webrender::api;
use winit::os::windows::{WindowExt, WindowBuilderExt};
use winit::dpi::LogicalSize;
fn main() {
let mut events_loop = winit::EventsLoop::new();
let (tx, rx) = mpsc::channel();
let notifier = Box::new(Notifier { events_proxy: events_loop.create_proxy(), tx });
let window = winit::WindowBuilder::new()
.with_title("WebRender + ANGLE + DirectComposition")
.with_dimensions(LogicalSize::new(1024., 768.))
.with_decorations(true)
.with_transparency(true)
.with_no_redirection_bitmap(true)
.build(&events_loop)
.unwrap();
let composition = direct_composition_from_window(&window);
let factor = window.get_hidpi_factor() as f32;
let mut clicks: usize = 0;
let mut offset_y = 100.;
let mut rects = [
Rectangle::new(&composition, &notifier, factor, size2(300, 200), 0., 0.2, 0.4, 1.),
Rectangle::new(&composition, &notifier, factor, size2(400, 300), 0., 0.5, 0., 0.5),
];
rects[0].render(factor, &rx);
rects[1].render(factor, &rx);
rects[0].visual.set_offset_x(100.);
rects[0].visual.set_offset_y(50.);
rects[1].visual.set_offset_x(200.);
rects[1].visual.set_offset_y(offset_y);
composition.commit();
events_loop.run_forever(|event| {
if let winit::Event::WindowEvent { event, .. } = event {
match event {
winit::WindowEvent::CloseRequested => {
return winit::ControlFlow::Break
}
winit::WindowEvent::MouseWheel { delta, .. } => {
let dy = match delta {
winit::MouseScrollDelta::LineDelta(_, dy) => dy,
winit::MouseScrollDelta::PixelDelta(pos) => pos.y as f32,
};
offset_y = (offset_y - 10. * dy).max(0.).min(468.);
rects[1].visual.set_offset_y(offset_y);
composition.commit();
}
winit::WindowEvent::MouseInput {
button: winit::MouseButton::Left,
state: winit::ElementState::Pressed,
..
} => {
clicks += 1;
let rect = &mut rects[clicks % 2];
rect.color.g += 0.1;
rect.color.g %= 1.;
rect.render(factor, &rx)
}
_ => {}
}
}
winit::ControlFlow::Continue
});
}
fn direct_composition_from_window(window: &winit::Window) -> DirectComposition {
unsafe {
DirectComposition::new(window.get_hwnd() as _)
}
}
struct Rectangle {
visual: direct_composition::AngleVisual,
renderer: Option<webrender::Renderer>,
api: api::RenderApi,
document_id: api::DocumentId,
size: api::units::DeviceIntSize,
color: api::ColorF,
}
impl Rectangle {
fn new(composition: &DirectComposition, notifier: &Box<Notifier>,
device_pixel_ratio: f32, size: api::units::DeviceIntSize, r: f32, g: f32, b: f32, a: f32)
-> Self {
let visual = composition.create_angle_visual(size.width as u32, size.height as u32);
visual.make_current();
let (renderer, sender) = webrender::Renderer::new(
composition.gleam.clone(),
notifier.clone(),
webrender::RendererOptions {
clear_color: Some(api::ColorF::new(0., 0., 0., 0.)),
device_pixel_ratio,
..webrender::RendererOptions::default()
},
None,
size,
).unwrap();
let api = sender.create_api();
Rectangle {
visual,
renderer: Some(renderer),
document_id: api.add_document(size, 0),
api,
size,
color: api::ColorF { r, g, b, a },
}
}
fn render(&mut self, device_pixel_ratio: f32, rx: &mpsc::Receiver<()>) {
self.visual.make_current();
let pipeline_id = api::PipelineId(0, 0);
let layout_size = self.size.to_f32() / euclid::Scale::new(device_pixel_ratio);
let mut builder = api::DisplayListBuilder::new(pipeline_id, layout_size);
let rect = euclid::Rect::new(euclid::Point2D::zero(), layout_size);
let region = api::ComplexClipRegion::new(
rect,
api::BorderRadius::uniform(20.),
api::ClipMode::Clip
);
let clip_id = builder.define_clip_rounded_rect(
&api::SpaceAndClipInfo::root_scroll(pipeline_id),
region,
);
builder.push_rect(
&api::CommonItemProperties::new(
rect,
api::SpaceAndClipInfo {
spatial_id: api::SpatialId::root_scroll_node(pipeline_id),
clip_id,
},
),
rect,
self.color,
);
let mut transaction = api::Transaction::new();
transaction.set_display_list(
api::Epoch(0),
None,
layout_size,
builder.finalize(),
true,
);
transaction.set_root_pipeline(pipeline_id);
transaction.generate_frame();
self.api.send_transaction(self.document_id, transaction);
rx.recv().unwrap();
let renderer = self.renderer.as_mut().unwrap();
renderer.update();
renderer.render(self.size).unwrap();
let _ = renderer.flush_pipeline_info();
self.visual.present();
}
}
impl Drop for Rectangle {
fn drop(&mut self) {
self.renderer.take().unwrap().deinit()
}
}
#[derive(Clone)]
struct Notifier {
events_proxy: winit::EventsLoopProxy,
tx: mpsc::Sender<()>,
}
impl api::RenderNotifier for Notifier {
fn clone(&self) -> Box<dyn api::RenderNotifier> {
Box::new(Clone::clone(self))
}
fn wake_up(&self) {
self.tx.send(()).unwrap();
let _ = self.events_proxy.wakeup();
}
fn new_frame_ready(&self,
_: api::DocumentId,
_: bool,
_: bool,
_: Option<u64>) {
self.wake_up();
}
}

View file

@ -0,0 +1,9 @@
[package]
name = "compositor-windows"
version = "0.1.0"
authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
edition = "2018"
license = "MPL-2.0"
[build-dependencies]
cc = "1.0"

View file

@ -0,0 +1,25 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
fn main() {
// HACK - This build script relies on Gecko having been built, so that the ANGLE libraries
// have already been compiled. It also assumes they are being built with an in-tree
// x86_64 object directory.
cc::Build::new()
.file("src/lib.cpp")
.include("../../../angle/checkout/include")
.compile("windows");
// Set up linker paths for ANGLE that is built by Gecko
println!("cargo:rustc-link-search=../../obj-x86_64-pc-mingw32/gfx/angle/targets/libEGL");
println!("cargo:rustc-link-search=../../obj-x86_64-pc-mingw32/gfx/angle/targets/libGLESv2");
// Link to libEGL and libGLESv2 (ANGLE) and D3D11 + DirectComposition
println!("cargo:rustc-link-lib=libEGL");
println!("cargo:rustc-link-lib=libGLESv2");
println!("cargo:rustc-link-lib=dcomp");
println!("cargo:rustc-link-lib=d3d11");
println!("cargo:rustc-link-lib=dwmapi");
}

View file

@ -0,0 +1,802 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#define UNICODE
#include <windows.h>
#include <math.h>
#include <dcomp.h>
#include <d3d11.h>
#include <assert.h>
#include <map>
#include <vector>
#include <dwmapi.h>
#include <unordered_map>
#define EGL_EGL_PROTOTYPES 1
#define EGL_EGLEXT_PROTOTYPES 1
#define GL_GLEXT_PROTOTYPES 1
#include "EGL/egl.h"
#include "EGL/eglext.h"
#include "EGL/eglext_angle.h"
#include "GL/gl.h"
#include "GLES/gl.h"
#include "GLES/glext.h"
#include "GLES3/gl3.h"
#define NUM_QUERIES 2
#define USE_VIRTUAL_SURFACES
#define VIRTUAL_OFFSET 512 * 1024
enum SyncMode {
None = 0,
Swap = 1,
Commit = 2,
Flush = 3,
Query = 4,
};
// The OS compositor representation of a picture cache tile.
struct Tile {
#ifndef USE_VIRTUAL_SURFACES
// Represents the underlying DirectComposition surface texture that gets drawn into.
IDCompositionSurface *pSurface;
// Represents the node in the visual tree that defines the properties of this tile (clip, position etc).
IDCompositionVisual2 *pVisual;
#endif
};
struct TileKey {
int x;
int y;
TileKey(int ax, int ay) : x(ax), y(ay) {}
};
bool operator==(const TileKey &k0, const TileKey &k1) {
return k0.x == k1.x && k0.y == k1.y;
}
struct TileKeyHasher {
size_t operator()(const TileKey &key) const {
return key.x ^ key.y;
}
};
struct Surface {
int tile_width;
int tile_height;
bool is_opaque;
std::unordered_map<TileKey, Tile, TileKeyHasher> tiles;
IDCompositionVisual2 *pVisual;
#ifdef USE_VIRTUAL_SURFACES
IDCompositionVirtualSurface *pVirtualSurface;
#endif
};
struct CachedFrameBuffer {
int width;
int height;
GLuint fboId;
GLuint depthRboId;
};
struct Window {
// Win32 window details
HWND hWnd;
HINSTANCE hInstance;
bool enable_compositor;
RECT client_rect;
SyncMode sync_mode;
// Main interfaces to D3D11 and DirectComposition
ID3D11Device *pD3D11Device;
IDCompositionDesktopDevice *pDCompDevice;
IDCompositionTarget *pDCompTarget;
IDXGIDevice *pDXGIDevice;
ID3D11Query *pQueries[NUM_QUERIES];
int current_query;
// ANGLE interfaces that wrap the D3D device
EGLDeviceEXT EGLDevice;
EGLDisplay EGLDisplay;
EGLContext EGLContext;
EGLConfig config;
// Framebuffer surface for debug mode when we are not using DC
EGLSurface fb_surface;
// The currently bound surface, valid during bind() and unbind()
IDCompositionSurface *pCurrentSurface;
EGLImage mEGLImage;
GLuint mColorRBO;
// The root of the DC visual tree. Nothing is drawn on this, but
// all child tiles are parented to here.
IDCompositionVisual2 *pRoot;
IDCompositionVisualDebug *pVisualDebug;
std::vector<CachedFrameBuffer> mFrameBuffers;
// Maintain list of layer state between frames to avoid visual tree rebuild.
std::vector<uint64_t> mCurrentLayers;
std::vector<uint64_t> mPrevLayers;
// Maps WR surface IDs to each OS surface
std::unordered_map<uint64_t, Surface> surfaces;
};
static const wchar_t *CLASS_NAME = L"WR DirectComposite";
static GLuint GetOrCreateFbo(Window *window, int aWidth, int aHeight) {
GLuint fboId = 0;
// Check if we have a cached FBO with matching dimensions
for (auto it = window->mFrameBuffers.begin(); it != window->mFrameBuffers.end(); ++it) {
if (it->width == aWidth && it->height == aHeight) {
fboId = it->fboId;
break;
}
}
// If not, create a new FBO with attached depth buffer
if (fboId == 0) {
// Create the depth buffer
GLuint depthRboId;
glGenRenderbuffers(1, &depthRboId);
glBindRenderbuffer(GL_RENDERBUFFER, depthRboId);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24,
aWidth, aHeight);
// Create the framebuffer and attach the depth buffer to it
glGenFramebuffers(1, &fboId);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER,
GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, depthRboId);
// Store this in the cache for future calls.
CachedFrameBuffer frame_buffer_info;
frame_buffer_info.width = aWidth;
frame_buffer_info.height = aHeight;
frame_buffer_info.fboId = fboId;
frame_buffer_info.depthRboId = depthRboId;
window->mFrameBuffers.push_back(frame_buffer_info);
}
return fboId;
}
static LRESULT CALLBACK WndProc(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam
) {
switch (message) {
case WM_DESTROY:
PostQuitMessage(0);
return 1;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
extern "C" {
Window *com_dc_create_window(int width, int height, bool enable_compositor, SyncMode sync_mode) {
// Create a simple Win32 window
Window *window = new Window;
window->hInstance = GetModuleHandle(NULL);
window->enable_compositor = enable_compositor;
window->mEGLImage = EGL_NO_IMAGE;
window->sync_mode = sync_mode;
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = window->hInstance;
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);;
wcex.lpszMenuName = nullptr;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.lpszClassName = CLASS_NAME;
RegisterClassEx(&wcex);
int dpiX = 0;
int dpiY = 0;
HDC hdc = GetDC(NULL);
if (hdc) {
dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
dpiY = GetDeviceCaps(hdc, LOGPIXELSY);
ReleaseDC(NULL, hdc);
}
RECT window_rect = { 0, 0, width, height };
AdjustWindowRect(&window_rect, WS_OVERLAPPEDWINDOW, FALSE);
UINT window_width = static_cast<UINT>(ceil(float(window_rect.right - window_rect.left) * dpiX / 96.f));
UINT window_height = static_cast<UINT>(ceil(float(window_rect.bottom - window_rect.top) * dpiY / 96.f));
LPCWSTR name;
DWORD style;
if (enable_compositor) {
name = L"example-compositor (DirectComposition)";
style = WS_EX_NOREDIRECTIONBITMAP;
} else {
name = L"example-compositor (Simple)";
style = 0;
}
window->hWnd = CreateWindowEx(
style,
CLASS_NAME,
name,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
window_width,
window_height,
NULL,
NULL,
window->hInstance,
NULL
);
ShowWindow(window->hWnd, SW_SHOWNORMAL);
UpdateWindow(window->hWnd);
GetClientRect(window->hWnd, &window->client_rect);
// Create a D3D11 device
D3D_FEATURE_LEVEL featureLevelSupported;
HRESULT hr = D3D11CreateDevice(
nullptr,
D3D_DRIVER_TYPE_HARDWARE,
NULL,
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
NULL,
0,
D3D11_SDK_VERSION,
&window->pD3D11Device,
&featureLevelSupported,
nullptr
);
assert(SUCCEEDED(hr));
D3D11_QUERY_DESC query_desc;
memset(&query_desc, 0, sizeof(query_desc));
query_desc.Query = D3D11_QUERY_EVENT;
for (int i=0 ; i < NUM_QUERIES ; ++i) {
hr = window->pD3D11Device->CreateQuery(&query_desc, &window->pQueries[i]);
assert(SUCCEEDED(hr));
}
window->current_query = 0;
hr = window->pD3D11Device->QueryInterface(&window->pDXGIDevice);
assert(SUCCEEDED(hr));
// Create a DirectComposition device
hr = DCompositionCreateDevice2(
window->pDXGIDevice,
__uuidof(IDCompositionDesktopDevice),
(void **) &window->pDCompDevice
);
assert(SUCCEEDED(hr));
// Create a DirectComposition target for a Win32 window handle
hr = window->pDCompDevice->CreateTargetForHwnd(
window->hWnd,
TRUE,
&window->pDCompTarget
);
assert(SUCCEEDED(hr));
// Create an ANGLE EGL device that wraps D3D11
window->EGLDevice = eglCreateDeviceANGLE(
EGL_D3D11_DEVICE_ANGLE,
window->pD3D11Device,
nullptr
);
EGLint display_attribs[] = {
EGL_NONE
};
window->EGLDisplay = eglGetPlatformDisplayEXT(
EGL_PLATFORM_DEVICE_EXT,
window->EGLDevice,
display_attribs
);
eglInitialize(
window->EGLDisplay,
nullptr,
nullptr
);
EGLint num_configs = 0;
EGLint cfg_attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 24,
EGL_NONE
};
EGLConfig configs[32];
eglChooseConfig(
window->EGLDisplay,
cfg_attribs,
configs,
sizeof(configs) / sizeof(EGLConfig),
&num_configs
);
assert(num_configs > 0);
window->config = configs[0];
if (window->enable_compositor) {
window->fb_surface = EGL_NO_SURFACE;
} else {
window->fb_surface = eglCreateWindowSurface(
window->EGLDisplay,
window->config,
window->hWnd,
NULL
);
assert(window->fb_surface != EGL_NO_SURFACE);
}
EGLint ctx_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 3,
EGL_NONE
};
// Create an EGL context that can be used for drawing
window->EGLContext = eglCreateContext(
window->EGLDisplay,
window->config,
EGL_NO_CONTEXT,
ctx_attribs
);
// Create the root of the DirectComposition visual tree
hr = window->pDCompDevice->CreateVisual(&window->pRoot);
assert(SUCCEEDED(hr));
hr = window->pDCompTarget->SetRoot(window->pRoot);
assert(SUCCEEDED(hr));
hr = window->pRoot->QueryInterface(
__uuidof(IDCompositionVisualDebug),
(void **) &window->pVisualDebug
);
assert(SUCCEEDED(hr));
// Uncomment this to see redraw regions during composite
// window->pVisualDebug->EnableRedrawRegions();
EGLBoolean ok = eglMakeCurrent(
window->EGLDisplay,
window->fb_surface,
window->fb_surface,
window->EGLContext
);
assert(ok);
return window;
}
void com_dc_destroy_window(Window *window) {
for (auto surface_it=window->surfaces.begin() ; surface_it != window->surfaces.end() ; ++surface_it) {
Surface &surface = surface_it->second;
#ifndef USE_VIRTUAL_SURFACES
for (auto tile_it=surface.tiles.begin() ; tile_it != surface.tiles.end() ; ++tile_it) {
tile_it->second.pSurface->Release();
tile_it->second.pVisual->Release();
}
#endif
surface.pVisual->Release();
}
if (window->fb_surface != EGL_NO_SURFACE) {
eglDestroySurface(window->EGLDisplay, window->fb_surface);
}
eglDestroyContext(window->EGLDisplay, window->EGLContext);
eglTerminate(window->EGLDisplay);
eglReleaseDeviceANGLE(window->EGLDevice);
for (int i=0 ; i < NUM_QUERIES ; ++i) {
window->pQueries[i]->Release();
}
window->pRoot->Release();
window->pVisualDebug->Release();
window->pD3D11Device->Release();
window->pDXGIDevice->Release();
window->pDCompDevice->Release();
window->pDCompTarget->Release();
CloseWindow(window->hWnd);
UnregisterClass(CLASS_NAME, window->hInstance);
delete window;
}
bool com_dc_tick(Window *) {
// Check and dispatch the windows event loop
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
return false;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return true;
}
void com_dc_swap_buffers(Window *window) {
// If not using DC mode, then do a normal EGL swap buffers.
if (window->fb_surface != EGL_NO_SURFACE) {
switch (window->sync_mode) {
case SyncMode::None:
eglSwapInterval(window->EGLDisplay, 0);
break;
case SyncMode::Swap:
eglSwapInterval(window->EGLDisplay, 1);
break;
default:
assert(false); // unexpected vsync mode for simple compositor.
break;
}
eglSwapBuffers(window->EGLDisplay, window->fb_surface);
} else {
switch (window->sync_mode) {
case SyncMode::None:
break;
case SyncMode::Commit:
window->pDCompDevice->WaitForCommitCompletion();
break;
case SyncMode::Flush:
DwmFlush();
break;
case SyncMode::Query:
// todo!!!!
break;
default:
assert(false); // unexpected vsync mode for native compositor
break;
}
}
}
// Create a new DC surface
void com_dc_create_surface(
Window *window,
uint64_t id,
int tile_width,
int tile_height,
bool is_opaque
) {
assert(window->surfaces.count(id) == 0);
Surface surface;
surface.tile_width = tile_width;
surface.tile_height = tile_height;
surface.is_opaque = is_opaque;
// Create the visual node in the DC tree that stores properties
HRESULT hr = window->pDCompDevice->CreateVisual(&surface.pVisual);
assert(SUCCEEDED(hr));
#ifdef USE_VIRTUAL_SURFACES
DXGI_ALPHA_MODE alpha_mode = surface.is_opaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED;
hr = window->pDCompDevice->CreateVirtualSurface(
VIRTUAL_OFFSET * 2,
VIRTUAL_OFFSET * 2,
DXGI_FORMAT_B8G8R8A8_UNORM,
alpha_mode,
&surface.pVirtualSurface
);
assert(SUCCEEDED(hr));
// Bind the surface memory to this visual
hr = surface.pVisual->SetContent(surface.pVirtualSurface);
assert(SUCCEEDED(hr));
#endif
window->surfaces[id] = surface;
}
void com_dc_create_tile(
Window *window,
uint64_t id,
int x,
int y
) {
assert(window->surfaces.count(id) == 1);
Surface &surface = window->surfaces[id];
TileKey key(x, y);
assert(surface.tiles.count(key) == 0);
Tile tile;
#ifndef USE_VIRTUAL_SURFACES
// Create the video memory surface.
DXGI_ALPHA_MODE alpha_mode = surface.is_opaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED;
HRESULT hr = window->pDCompDevice->CreateSurface(
surface.tile_width,
surface.tile_height,
DXGI_FORMAT_B8G8R8A8_UNORM,
alpha_mode,
&tile.pSurface
);
assert(SUCCEEDED(hr));
// Create the visual node in the DC tree that stores properties
hr = window->pDCompDevice->CreateVisual(&tile.pVisual);
assert(SUCCEEDED(hr));
// Bind the surface memory to this visual
hr = tile.pVisual->SetContent(tile.pSurface);
assert(SUCCEEDED(hr));
// Place the visual in local-space of this surface
float offset_x = (float) (x * surface.tile_width);
float offset_y = (float) (y * surface.tile_height);
tile.pVisual->SetOffsetX(offset_x);
tile.pVisual->SetOffsetY(offset_y);
surface.pVisual->AddVisual(
tile.pVisual,
FALSE,
NULL
);
#endif
surface.tiles[key] = tile;
}
void com_dc_destroy_tile(
Window *window,
uint64_t id,
int x,
int y
) {
assert(window->surfaces.count(id) == 1);
Surface &surface = window->surfaces[id];
TileKey key(x, y);
assert(surface.tiles.count(key) == 1);
Tile &tile = surface.tiles[key];
#ifndef USE_VIRTUAL_SURFACES
surface.pVisual->RemoveVisual(tile.pVisual);
tile.pVisual->Release();
tile.pSurface->Release();
#endif
surface.tiles.erase(key);
}
void com_dc_destroy_surface(
Window *window,
uint64_t id
) {
assert(window->surfaces.count(id) == 1);
Surface &surface = window->surfaces[id];
window->pRoot->RemoveVisual(surface.pVisual);
#ifdef USE_VIRTUAL_SURFACES
surface.pVirtualSurface->Release();
#else
// Release the video memory and visual in the tree
for (auto tile_it=surface.tiles.begin() ; tile_it != surface.tiles.end() ; ++tile_it) {
tile_it->second.pSurface->Release();
tile_it->second.pVisual->Release();
}
#endif
surface.pVisual->Release();
window->surfaces.erase(id);
}
// Bind a DC surface to allow issuing GL commands to it
GLuint com_dc_bind_surface(
Window *window,
uint64_t surface_id,
int tile_x,
int tile_y,
int *x_offset,
int *y_offset,
int dirty_x0,
int dirty_y0,
int dirty_width,
int dirty_height
) {
assert(window->surfaces.count(surface_id) == 1);
Surface &surface = window->surfaces[surface_id];
TileKey key(tile_x, tile_y);
assert(surface.tiles.count(key) == 1);
Tile &tile = surface.tiles[key];
// Inform DC that we want to draw on this surface. DC uses texture
// atlases when the tiles are small. It returns an offset where the
// client code must draw into this surface when this happens.
RECT update_rect;
update_rect.left = dirty_x0;
update_rect.top = dirty_y0;
update_rect.right = dirty_x0 + dirty_width;
update_rect.bottom = dirty_y0 + dirty_height;
POINT offset;
D3D11_TEXTURE2D_DESC desc;
ID3D11Texture2D *pTexture;
HRESULT hr;
// Store the current surface for unbinding later
#ifdef USE_VIRTUAL_SURFACES
LONG tile_offset_x = VIRTUAL_OFFSET + tile_x * surface.tile_width;
LONG tile_offset_y = VIRTUAL_OFFSET + tile_y * surface.tile_height;
update_rect.left += tile_offset_x;
update_rect.top += tile_offset_y;
update_rect.right += tile_offset_x;
update_rect.bottom += tile_offset_y;
hr = surface.pVirtualSurface->BeginDraw(
&update_rect,
__uuidof(ID3D11Texture2D),
(void **) &pTexture,
&offset
);
window->pCurrentSurface = surface.pVirtualSurface;
#else
hr = tile.pSurface->BeginDraw(
&update_rect,
__uuidof(ID3D11Texture2D),
(void **) &pTexture,
&offset
);
window->pCurrentSurface = tile.pSurface;
#endif
// DC includes the origin of the dirty / update rect in the draw offset,
// undo that here since WR expects it to be an absolute offset.
assert(SUCCEEDED(hr));
offset.x -= dirty_x0;
offset.y -= dirty_y0;
pTexture->GetDesc(&desc);
*x_offset = offset.x;
*y_offset = offset.y;
// Construct an EGLImage wrapper around the D3D texture for ANGLE.
const EGLAttrib attribs[] = { EGL_NONE };
window->mEGLImage = eglCreateImage(
window->EGLDisplay,
EGL_NO_CONTEXT,
EGL_D3D11_TEXTURE_ANGLE,
static_cast<EGLClientBuffer>(pTexture),
attribs
);
// Get the current FBO and RBO id, so we can restore them later
GLint currentFboId, currentRboId;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &currentFboId);
glGetIntegerv(GL_RENDERBUFFER_BINDING, &currentRboId);
// Create a render buffer object that is backed by the EGL image.
glGenRenderbuffers(1, &window->mColorRBO);
glBindRenderbuffer(GL_RENDERBUFFER, window->mColorRBO);
glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, window->mEGLImage);
// Get or create an FBO for the specified dimensions
GLuint fboId = GetOrCreateFbo(window, desc.Width, desc.Height);
// Attach the new renderbuffer to the FBO
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER,
window->mColorRBO);
// Restore previous FBO and RBO bindings
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentFboId);
glBindRenderbuffer(GL_RENDERBUFFER, currentRboId);
return fboId;
}
// Unbind a currently bound DC surface
void com_dc_unbind_surface(Window *window) {
HRESULT hr = window->pCurrentSurface->EndDraw();
assert(SUCCEEDED(hr));
glDeleteRenderbuffers(1, &window->mColorRBO);
window->mColorRBO = 0;
eglDestroyImage(window->EGLDisplay, window->mEGLImage);
window->mEGLImage = EGL_NO_IMAGE;
}
void com_dc_begin_transaction(Window *) {
}
// Add a DC surface to the visual tree. Called per-frame to build the composition.
void com_dc_add_surface(
Window *window,
uint64_t id,
int x,
int y,
int clip_x,
int clip_y,
int clip_w,
int clip_h
) {
Surface surface = window->surfaces[id];
window->mCurrentLayers.push_back(id);
// Place the visual - this changes frame to frame based on scroll position
// of the slice.
float offset_x = (float) (x + window->client_rect.left);
float offset_y = (float) (y + window->client_rect.top);
#ifdef USE_VIRTUAL_SURFACES
offset_x -= VIRTUAL_OFFSET;
offset_y -= VIRTUAL_OFFSET;
#endif
surface.pVisual->SetOffsetX(offset_x);
surface.pVisual->SetOffsetY(offset_y);
// Set the clip rect - converting from world space to the pre-offset space
// that DC requires for rectangle clips.
D2D_RECT_F clip_rect;
clip_rect.left = clip_x - offset_x;
clip_rect.top = clip_y - offset_y;
clip_rect.right = clip_rect.left + clip_w;
clip_rect.bottom = clip_rect.top + clip_h;
surface.pVisual->SetClip(clip_rect);
}
// Finish the composition transaction, telling DC to composite
void com_dc_end_transaction(Window *window) {
bool same = window->mPrevLayers == window->mCurrentLayers;
if (!same) {
HRESULT hr = window->pRoot->RemoveAllVisuals();
assert(SUCCEEDED(hr));
for (auto it = window->mCurrentLayers.begin(); it != window->mCurrentLayers.end(); ++it) {
Surface &surface = window->surfaces[*it];
// Add this visual as the last element in the visual tree (z-order is implicit,
// based on the order tiles are added).
hr = window->pRoot->AddVisual(
surface.pVisual,
FALSE,
NULL
);
assert(SUCCEEDED(hr));
}
}
window->mPrevLayers.swap(window->mCurrentLayers);
window->mCurrentLayers.clear();
HRESULT hr = window->pDCompDevice->Commit();
assert(SUCCEEDED(hr));
}
// Get a pointer to an EGL symbol
void *com_dc_get_proc_address(const char *name) {
return eglGetProcAddress(name);
}
}

View file

@ -0,0 +1,261 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::os::raw::{c_void, c_char};
/*
This is a very simple (and unsafe!) rust wrapper for the DirectComposite / D3D11 / ANGLE
implementation in lib.cpp.
It just proxies the calls from the Compositor impl to the C99 code. This is very
hacky and not suitable for production!
*/
// Opaque wrapper for the Window type in lib.cpp
#[repr(C)]
pub struct Window {
_unused: [u8; 0]
}
// C99 functions that do the compositor work
extern {
fn com_dc_create_window(
width: i32,
height: i32,
enable_compositor: bool,
sync_mode: i32,
) -> *mut Window;
fn com_dc_destroy_window(window: *mut Window);
fn com_dc_tick(window: *mut Window) -> bool;
fn com_dc_get_proc_address(name: *const c_char) -> *const c_void;
fn com_dc_swap_buffers(window: *mut Window);
fn com_dc_create_surface(
window: *mut Window,
id: u64,
tile_width: i32,
tile_height: i32,
is_opaque: bool,
);
fn com_dc_create_tile(
window: *mut Window,
id: u64,
x: i32,
y: i32,
);
fn com_dc_destroy_tile(
window: *mut Window,
id: u64,
x: i32,
y: i32,
);
fn com_dc_destroy_surface(
window: *mut Window,
id: u64,
);
fn com_dc_bind_surface(
window: *mut Window,
surface_id: u64,
tile_x: i32,
tile_y: i32,
x_offset: &mut i32,
y_offset: &mut i32,
dirty_x0: i32,
dirty_y0: i32,
dirty_width: i32,
dirty_height: i32,
) -> u32;
fn com_dc_unbind_surface(window: *mut Window);
fn com_dc_begin_transaction(window: *mut Window);
fn com_dc_add_surface(
window: *mut Window,
id: u64,
x: i32,
y: i32,
clip_x: i32,
clip_y: i32,
clip_w: i32,
clip_h: i32,
);
fn com_dc_end_transaction(window: *mut Window);
}
pub fn create_window(
width: i32,
height: i32,
enable_compositor: bool,
sync_mode: i32,
) -> *mut Window {
unsafe {
com_dc_create_window(width, height, enable_compositor, sync_mode)
}
}
pub fn destroy_window(window: *mut Window) {
unsafe {
com_dc_destroy_window(window);
}
}
pub fn tick(window: *mut Window) -> bool {
unsafe {
com_dc_tick(window)
}
}
pub fn get_proc_address(name: *const c_char) -> *const c_void {
unsafe {
com_dc_get_proc_address(name)
}
}
pub fn create_surface(
window: *mut Window,
id: u64,
tile_width: i32,
tile_height: i32,
is_opaque: bool,
) {
unsafe {
com_dc_create_surface(
window,
id,
tile_width,
tile_height,
is_opaque,
)
}
}
pub fn create_tile(
window: *mut Window,
id: u64,
x: i32,
y: i32,
) {
unsafe {
com_dc_create_tile(
window,
id,
x,
y,
)
}
}
pub fn destroy_tile(
window: *mut Window,
id: u64,
x: i32,
y: i32,
) {
unsafe {
com_dc_destroy_tile(
window,
id,
x,
y,
)
}
}
pub fn destroy_surface(
window: *mut Window,
id: u64,
) {
unsafe {
com_dc_destroy_surface(
window,
id,
)
}
}
pub fn bind_surface(
window: *mut Window,
surface_id: u64,
tile_x: i32,
tile_y: i32,
dirty_x0: i32,
dirty_y0: i32,
dirty_width: i32,
dirty_height: i32,
) -> (u32, i32, i32) {
unsafe {
let mut x_offset = 0;
let mut y_offset = 0;
let fbo_id = com_dc_bind_surface(
window,
surface_id,
tile_x,
tile_y,
&mut x_offset,
&mut y_offset,
dirty_x0,
dirty_y0,
dirty_width,
dirty_height,
);
(fbo_id, x_offset, y_offset)
}
}
pub fn add_surface(
window: *mut Window,
id: u64,
x: i32,
y: i32,
clip_x: i32,
clip_y: i32,
clip_w: i32,
clip_h: i32,
) {
unsafe {
com_dc_add_surface(
window,
id,
x,
y,
clip_x,
clip_y,
clip_w,
clip_h,
)
}
}
pub fn begin_transaction(window: *mut Window) {
unsafe {
com_dc_begin_transaction(window)
}
}
pub fn unbind_surface(window: *mut Window) {
unsafe {
com_dc_unbind_surface(window)
}
}
pub fn end_transaction(window: *mut Window) {
unsafe {
com_dc_end_transaction(window)
}
}
pub fn swap_buffers(window: *mut Window) {
unsafe {
com_dc_swap_buffers(window);
}
}

View file

@ -0,0 +1,13 @@
[package]
name = "compositor"
version = "0.1.0"
authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
edition = "2018"
license = "MPL-2.0"
[dependencies]
webrender = { path = "../../webrender" }
gleam = "0.12.0"
[target.'cfg(windows)'.dependencies]
compositor-windows = { path = "../compositor-windows" }

View file

@ -0,0 +1,484 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
An example of how to implement the Compositor trait that
allows picture caching surfaces to be composited by the operating
system.
The current example supports DirectComposite on Windows only.
*/
use euclid::Angle;
use gleam::gl;
use std::ffi::CString;
use std::sync::mpsc;
use webrender::api::*;
use webrender::api::units::*;
#[cfg(target_os = "windows")]
use compositor_windows as compositor;
use std::{env, f32, process};
// A very hacky integration with DirectComposite. It proxies calls from the compositor
// interface to a simple C99 library which does the DirectComposition / D3D11 / ANGLE
// interfacing. This is a very unsafe impl due to the way the window pointer is passed
// around!
struct DirectCompositeInterface {
window: *mut compositor::Window,
}
impl DirectCompositeInterface {
fn new(window: *mut compositor::Window) -> Self {
DirectCompositeInterface {
window,
}
}
}
impl webrender::Compositor for DirectCompositeInterface {
fn create_surface(
&mut self,
id: webrender::NativeSurfaceId,
tile_size: DeviceIntSize,
is_opaque: bool,
) {
compositor::create_surface(
self.window,
id.0,
tile_size.width,
tile_size.height,
is_opaque,
);
}
fn destroy_surface(
&mut self,
id: webrender::NativeSurfaceId,
) {
compositor::destroy_surface(self.window, id.0);
}
fn create_tile(
&mut self,
id: webrender::NativeTileId,
) {
compositor::create_tile(
self.window,
id.surface_id.0,
id.x,
id.y,
);
}
fn destroy_tile(
&mut self,
id: webrender::NativeTileId,
) {
compositor::destroy_tile(
self.window,
id.surface_id.0,
id.x,
id.y,
);
}
fn bind(
&mut self,
id: webrender::NativeTileId,
dirty_rect: DeviceIntRect,
) -> webrender::NativeSurfaceInfo {
let (fbo_id, x, y) = compositor::bind_surface(
self.window,
id.surface_id.0,
id.x,
id.y,
dirty_rect.origin.x,
dirty_rect.origin.y,
dirty_rect.size.width,
dirty_rect.size.height,
);
webrender::NativeSurfaceInfo {
origin: DeviceIntPoint::new(x, y),
fbo_id,
}
}
fn unbind(&mut self) {
compositor::unbind_surface(self.window);
}
fn begin_frame(&mut self) {
compositor::begin_transaction(self.window);
}
fn add_surface(
&mut self,
id: webrender::NativeSurfaceId,
position: DeviceIntPoint,
clip_rect: DeviceIntRect,
) {
compositor::add_surface(
self.window,
id.0,
position.x,
position.y,
clip_rect.origin.x,
clip_rect.origin.y,
clip_rect.size.width,
clip_rect.size.height,
);
}
fn end_frame(&mut self) {
compositor::end_transaction(self.window);
}
}
// Simplisitic implementation of the WR notifier interface to know when a frame
// has been prepared and can be rendered.
struct Notifier {
tx: mpsc::Sender<()>,
}
impl Notifier {
fn new(tx: mpsc::Sender<()>) -> Self {
Notifier {
tx,
}
}
}
impl RenderNotifier for Notifier {
fn clone(&self) -> Box<dyn RenderNotifier> {
Box::new(Notifier {
tx: self.tx.clone()
})
}
fn wake_up(&self) {
}
fn new_frame_ready(&self,
_: DocumentId,
_scrolled: bool,
_composite_needed: bool,
_render_time: Option<u64>) {
self.tx.send(()).ok();
}
}
fn push_rotated_rect(
builder: &mut DisplayListBuilder,
rect: LayoutRect,
color: ColorF,
spatial_id: SpatialId,
root_pipeline_id: PipelineId,
angle: f32,
time: f32,
) {
let color = color.scale_rgb(time);
let rotation = LayoutTransform::create_rotation(
0.0,
0.0,
1.0,
Angle::radians(2.0 * std::f32::consts::PI * angle),
);
let transform_origin = LayoutVector3D::new(
rect.origin.x + rect.size.width * 0.5,
rect.origin.y + rect.size.height * 0.5,
0.0,
);
let transform = rotation
.pre_translate(-transform_origin)
.post_translate(transform_origin);
let spatial_id = builder.push_reference_frame(
LayoutPoint::zero(),
spatial_id,
TransformStyle::Flat,
PropertyBinding::Value(transform),
ReferenceFrameKind::Transform,
);
builder.push_rect(
&CommonItemProperties::new(
rect,
SpaceAndClipInfo {
spatial_id,
clip_id: ClipId::root(root_pipeline_id),
},
),
rect,
color,
);
}
fn build_display_list(
builder: &mut DisplayListBuilder,
scroll_id: ExternalScrollId,
root_pipeline_id: PipelineId,
layout_size: LayoutSize,
time: f32,
invalidations: Invalidations,
) {
let size_factor = match invalidations {
Invalidations::Small => 0.1,
Invalidations::Large | Invalidations::Scrolling => 1.0,
};
let fixed_space_info = SpaceAndClipInfo {
spatial_id: SpatialId::root_scroll_node(root_pipeline_id),
clip_id: ClipId::root(root_pipeline_id),
};
let scroll_space_info = builder.define_scroll_frame(
&fixed_space_info,
Some(scroll_id),
LayoutRect::new(LayoutPoint::zero(), layout_size),
LayoutRect::new(LayoutPoint::zero(), layout_size),
ScrollSensitivity::Script,
LayoutVector2D::zero(),
);
builder.push_rect(
&CommonItemProperties::new(
LayoutRect::new(LayoutPoint::zero(), layout_size).inflate(-10.0, -10.0),
fixed_space_info,
),
LayoutRect::new(LayoutPoint::zero(), layout_size).inflate(-10.0, -10.0),
ColorF::new(0.8, 0.8, 0.8, 1.0),
);
push_rotated_rect(
builder,
LayoutRect::new(
LayoutPoint::new(100.0, 100.0),
LayoutSize::new(size_factor * 400.0, size_factor * 400.0),
),
ColorF::new(1.0, 0.0, 0.0, 1.0),
scroll_space_info.spatial_id,
root_pipeline_id,
time,
time,
);
push_rotated_rect(
builder,
LayoutRect::new(
LayoutPoint::new(800.0, 100.0),
LayoutSize::new(size_factor * 100.0, size_factor * 600.0),
),
ColorF::new(0.0, 1.0, 0.0, 1.0),
fixed_space_info.spatial_id,
root_pipeline_id,
0.2,
time,
);
push_rotated_rect(
builder,
LayoutRect::new(
LayoutPoint::new(700.0, 200.0),
LayoutSize::new(size_factor * 300.0, size_factor * 300.0),
),
ColorF::new(0.0, 0.0, 1.0, 1.0),
scroll_space_info.spatial_id,
root_pipeline_id,
0.1,
time,
);
}
#[derive(Debug, Copy, Clone)]
enum Invalidations {
Large,
Small,
Scrolling,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
enum Sync {
None = 0,
Swap = 1,
Commit = 2,
Flush = 3,
Query = 4,
}
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() != 6 {
println!("USAGE: compositor [native|none] [small|large|scroll] [none|swap|commit|flush|query] width height");
process::exit(0);
}
let enable_compositor = match args[1].parse::<String>().unwrap().as_str() {
"native" => true,
"none" => false,
_ => panic!("invalid compositor [native, none]"),
};
let inv_mode = match args[2].parse::<String>().unwrap().as_str() {
"small" => Invalidations::Small,
"large" => Invalidations::Large,
"scroll" => Invalidations::Scrolling,
_ => panic!("invalid invalidations [small, large, scroll]"),
};
let sync_mode = match args[3].parse::<String>().unwrap().as_str() {
"none" => Sync::None,
"swap" => Sync::Swap,
"commit" => Sync::Commit,
"flush" => Sync::Flush,
"query" => Sync::Query,
_ => panic!("invalid sync mode [none, swap, commit, flush, query]"),
};
let width = args[4].parse().unwrap();
let height = args[5].parse().unwrap();
let device_size = DeviceIntSize::new(width, height);
// Load GL, construct WR and the native compositor interface.
let window = compositor::create_window(
device_size.width,
device_size.height,
enable_compositor,
sync_mode as i32,
);
let debug_flags = DebugFlags::empty();
let compositor_config = if enable_compositor {
webrender::CompositorConfig::Native {
max_update_rects: 1,
compositor: Box::new(DirectCompositeInterface::new(window)),
}
} else {
webrender::CompositorConfig::Draw {
max_partial_present_rects: 0,
}
};
let opts = webrender::RendererOptions {
clear_color: Some(ColorF::new(1.0, 1.0, 1.0, 1.0)),
debug_flags,
enable_picture_caching: true,
compositor_config,
..webrender::RendererOptions::default()
};
let (tx, rx) = mpsc::channel();
let notifier = Box::new(Notifier::new(tx));
let gl = unsafe {
gl::GlesFns::load_with(
|symbol| {
let symbol = CString::new(symbol).unwrap();
let ptr = compositor::get_proc_address(symbol.as_ptr());
ptr
}
)
};
let (mut renderer, sender) = webrender::Renderer::new(
gl.clone(),
notifier,
opts,
None,
device_size,
).unwrap();
let api = sender.create_api();
let document_id = api.add_document(device_size, 0);
let device_pixel_ratio = 1.0;
let mut current_epoch = Epoch(0);
let root_pipeline_id = PipelineId(0, 0);
let layout_size = device_size.to_f32() / euclid::Scale::new(device_pixel_ratio);
let mut time = 0.0;
let scroll_id = ExternalScrollId(3, root_pipeline_id);
// Kick off first transaction which will mean we get a notify below to build the DL and render.
let mut txn = Transaction::new();
txn.set_root_pipeline(root_pipeline_id);
if let Invalidations::Scrolling = inv_mode {
let mut root_builder = DisplayListBuilder::new(root_pipeline_id, layout_size);
build_display_list(
&mut root_builder,
scroll_id,
root_pipeline_id,
layout_size,
1.0,
inv_mode,
);
txn.set_display_list(
current_epoch,
None,
layout_size,
root_builder.finalize(),
true,
);
}
txn.generate_frame();
api.send_transaction(document_id, txn);
// Tick the compositor (in this sample, we don't block on UI events)
while compositor::tick(window) {
// If there is a new frame ready to draw
if let Ok(..) = rx.try_recv() {
// Update and render. This will invoke the native compositor interface implemented above
// as required.
renderer.update();
renderer.render(device_size).unwrap();
let _ = renderer.flush_pipeline_info();
// Construct a simple display list that can be drawn and composited by DC.
let mut txn = Transaction::new();
match inv_mode {
Invalidations::Small | Invalidations::Large => {
let mut root_builder = DisplayListBuilder::new(root_pipeline_id, layout_size);
build_display_list(
&mut root_builder,
scroll_id,
root_pipeline_id,
layout_size,
time,
inv_mode,
);
txn.set_display_list(
current_epoch,
None,
layout_size,
root_builder.finalize(),
true,
);
}
Invalidations::Scrolling => {
let d = 0.5 - 0.5 * (2.0 * f32::consts::PI * 5.0 * time).cos();
txn.scroll_node_with_id(
LayoutPoint::new(0.0, (d * 100.0).round()),
scroll_id,
ScrollClamping::NoClamping,
);
}
}
txn.generate_frame();
api.send_transaction(document_id, txn);
current_epoch.0 += 1;
time += 0.001;
if time > 1.0 {
time = 0.0;
}
// This does nothing when native compositor is enabled
compositor::swap_buffers(window);
}
}
renderer.deinit();
compositor::destroy_window(window);
}

View file

@ -0,0 +1,71 @@
[package]
name = "webrender-examples"
version = "0.1.0"
authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
license = "MPL-2.0"
repository = "https://github.com/servo/webrender"
edition = "2018"
[[bin]]
name = "alpha_perf"
path = "alpha_perf.rs"
[[bin]]
name = "animation"
path = "animation.rs"
[[bin]]
name = "basic"
path = "basic.rs"
[[bin]]
name = "blob"
path = "blob.rs"
[[bin]]
name = "document"
path = "document.rs"
[[bin]]
name = "frame_output"
path = "frame_output.rs"
[[bin]]
name = "iframe"
path = "iframe.rs"
[[bin]]
name = "image_resize"
path = "image_resize.rs"
[[bin]]
name = "multiwindow"
path = "multiwindow.rs"
[[bin]]
name = "scrolling"
path = "scrolling.rs"
[[bin]]
name = "texture_cache_stress"
path = "texture_cache_stress.rs"
[[bin]]
name = "yuv"
path = "yuv.rs"
[features]
debug = ["webrender/capture", "webrender/debugger", "webrender/profiler"]
[dependencies]
app_units = "0.7"
env_logger = "0.5"
euclid = "0.22"
gleam = "0.12"
glutin = "0.21"
rayon = "1"
webrender = { path = "../webrender" }
winit = "0.19"
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.7"

View file

@ -0,0 +1,8 @@
# Examples
This directory contains a collection of examples which uses the WebRender API.
To run an example e.g. `basic`, try:
```
cargo run --bin basic
```

View file

@ -0,0 +1,93 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
extern crate euclid;
extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate winit;
#[path = "common/boilerplate.rs"]
mod boilerplate;
use crate::boilerplate::{Example, HandyDandyRectBuilder};
use std::cmp;
use webrender::api::*;
use webrender::api::units::DeviceIntSize;
struct App {
rect_count: usize,
}
impl Example for App {
fn render(
&mut self,
_api: &mut RenderApi,
builder: &mut DisplayListBuilder,
_txn: &mut Transaction,
_device_size: DeviceIntSize,
pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let bounds = (0, 0).to(1920, 1080);
let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
builder.push_simple_stacking_context(
bounds.origin,
space_and_clip.spatial_id,
PrimitiveFlags::IS_BACKFACE_VISIBLE,
);
for _ in 0 .. self.rect_count {
builder.push_rect(
&CommonItemProperties::new(bounds, space_and_clip),
bounds,
ColorF::new(1.0, 1.0, 1.0, 0.05)
);
}
builder.pop_stacking_context();
}
fn on_event(
&mut self,
event: winit::WindowEvent,
_api: &mut RenderApi,
_document_id: DocumentId
) -> bool {
match event {
winit::WindowEvent::KeyboardInput {
input: winit::KeyboardInput {
state: winit::ElementState::Pressed,
virtual_keycode: Some(key),
..
},
..
} => {
match key {
winit::VirtualKeyCode::Right => {
self.rect_count += 1;
println!("rects = {}", self.rect_count);
}
winit::VirtualKeyCode::Left => {
self.rect_count = cmp::max(self.rect_count, 1) - 1;
println!("rects = {}", self.rect_count);
}
_ => {}
};
}
_ => (),
}
true
}
}
fn main() {
let mut app = App {
rect_count: 1,
};
boilerplate::main_wrapper(&mut app, None);
}

View file

@ -0,0 +1,219 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! This example creates a 200x200 white rect and allows the user to move it
//! around by using the arrow keys and rotate with '<'/'>'.
//! It does this by using the animation API.
//! The example also features seamless opaque/transparent split of a
//! rounded cornered rectangle, which is done automatically during the
//! scene building for render optimization.
extern crate euclid;
extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate winit;
#[path = "common/boilerplate.rs"]
mod boilerplate;
use crate::boilerplate::{Example, HandyDandyRectBuilder};
use euclid::Angle;
use webrender::api::*;
use webrender::api::units::*;
struct App {
property_key0: PropertyBindingKey<LayoutTransform>,
property_key1: PropertyBindingKey<LayoutTransform>,
property_key2: PropertyBindingKey<LayoutTransform>,
opacity_key: PropertyBindingKey<f32>,
opacity: f32,
angle0: f32,
angle1: f32,
angle2: f32,
}
impl App {
fn add_rounded_rect(
&mut self,
bounds: LayoutRect,
color: ColorF,
builder: &mut DisplayListBuilder,
pipeline_id: PipelineId,
property_key: PropertyBindingKey<LayoutTransform>,
opacity_key: Option<PropertyBindingKey<f32>>,
) {
let filters = match opacity_key {
Some(opacity_key) => {
vec![
FilterOp::Opacity(PropertyBinding::Binding(opacity_key, self.opacity), self.opacity),
]
}
None => {
vec![]
}
};
let spatial_id = builder.push_reference_frame(
bounds.origin,
SpatialId::root_scroll_node(pipeline_id),
TransformStyle::Flat,
PropertyBinding::Binding(property_key, LayoutTransform::identity()),
ReferenceFrameKind::Transform,
);
builder.push_simple_stacking_context_with_filters(
LayoutPoint::zero(),
spatial_id,
PrimitiveFlags::IS_BACKFACE_VISIBLE,
&filters,
&[],
&[]
);
let space_and_clip = SpaceAndClipInfo {
spatial_id,
clip_id: ClipId::root(pipeline_id),
};
let clip_bounds = LayoutRect::new(LayoutPoint::zero(), bounds.size);
let complex_clip = ComplexClipRegion {
rect: clip_bounds,
radii: BorderRadius::uniform(30.0),
mode: ClipMode::Clip,
};
let clip_id = builder.define_clip_rounded_rect(
&space_and_clip,
complex_clip,
);
// Fill it with a white rect
builder.push_rect(
&CommonItemProperties::new(
LayoutRect::new(LayoutPoint::zero(), bounds.size),
SpaceAndClipInfo {
spatial_id,
clip_id,
}
),
LayoutRect::new(LayoutPoint::zero(), bounds.size),
color,
);
builder.pop_stacking_context();
builder.pop_reference_frame();
}
}
impl Example for App {
const WIDTH: u32 = 2048;
const HEIGHT: u32 = 1536;
fn render(
&mut self,
_api: &mut RenderApi,
builder: &mut DisplayListBuilder,
_txn: &mut Transaction,
_device_size: DeviceIntSize,
pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let opacity_key = self.opacity_key;
let bounds = (150, 150).to(250, 250);
let key0 = self.property_key0;
self.add_rounded_rect(bounds, ColorF::new(1.0, 0.0, 0.0, 0.5), builder, pipeline_id, key0, Some(opacity_key));
let bounds = (400, 400).to(600, 600);
let key1 = self.property_key1;
self.add_rounded_rect(bounds, ColorF::new(0.0, 1.0, 0.0, 0.5), builder, pipeline_id, key1, None);
let bounds = (200, 500).to(350, 580);
let key2 = self.property_key2;
self.add_rounded_rect(bounds, ColorF::new(0.0, 0.0, 1.0, 0.5), builder, pipeline_id, key2, None);
}
fn on_event(&mut self, win_event: winit::WindowEvent, api: &mut RenderApi, document_id: DocumentId) -> bool {
let mut rebuild_display_list = false;
match win_event {
winit::WindowEvent::KeyboardInput {
input: winit::KeyboardInput {
state: winit::ElementState::Pressed,
virtual_keycode: Some(key),
..
},
..
} => {
let (delta_angle, delta_opacity) = match key {
winit::VirtualKeyCode::Down => (0.0, -0.1),
winit::VirtualKeyCode::Up => (0.0, 0.1),
winit::VirtualKeyCode::Right => (1.0, 0.0),
winit::VirtualKeyCode::Left => (-1.0, 0.0),
winit::VirtualKeyCode::R => {
rebuild_display_list = true;
(0.0, 0.0)
}
_ => return false,
};
// Update the transform based on the keyboard input and push it to
// webrender using the generate_frame API. This will recomposite with
// the updated transform.
self.opacity += delta_opacity;
self.angle0 += delta_angle * 0.1;
self.angle1 += delta_angle * 0.2;
self.angle2 -= delta_angle * 0.15;
let xf0 = LayoutTransform::rotation(0.0, 0.0, 1.0, Angle::radians(self.angle0));
let xf1 = LayoutTransform::rotation(0.0, 0.0, 1.0, Angle::radians(self.angle1));
let xf2 = LayoutTransform::rotation(0.0, 0.0, 1.0, Angle::radians(self.angle2));
let mut txn = Transaction::new();
txn.update_dynamic_properties(
DynamicProperties {
transforms: vec![
PropertyValue {
key: self.property_key0,
value: xf0,
},
PropertyValue {
key: self.property_key1,
value: xf1,
},
PropertyValue {
key: self.property_key2,
value: xf2,
},
],
floats: vec![
PropertyValue {
key: self.opacity_key,
value: self.opacity,
}
],
colors: vec![],
},
);
txn.generate_frame();
api.send_transaction(document_id, txn);
}
_ => (),
}
rebuild_display_list
}
}
fn main() {
let mut app = App {
property_key0: PropertyBindingKey::new(42), // arbitrary magic number
property_key1: PropertyBindingKey::new(44), // arbitrary magic number
property_key2: PropertyBindingKey::new(45), // arbitrary magic number
opacity_key: PropertyBindingKey::new(43),
opacity: 0.5,
angle0: 0.0,
angle1: 0.0,
angle2: 0.0,
};
boilerplate::main_wrapper(&mut app, None);
}

321
third_party/webrender/examples/basic.rs vendored Normal file
View file

@ -0,0 +1,321 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
extern crate euclid;
extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate winit;
#[path = "common/boilerplate.rs"]
mod boilerplate;
use crate::boilerplate::{Example, HandyDandyRectBuilder};
use euclid::vec2;
use winit::TouchPhase;
use std::collections::HashMap;
use webrender::ShaderPrecacheFlags;
use webrender::api::*;
use webrender::api::units::*;
#[derive(Debug)]
enum Gesture {
None,
Pan,
Zoom,
}
#[derive(Debug)]
struct Touch {
id: u64,
start_x: f32,
start_y: f32,
current_x: f32,
current_y: f32,
}
fn dist(x0: f32, y0: f32, x1: f32, y1: f32) -> f32 {
let dx = x0 - x1;
let dy = y0 - y1;
((dx * dx) + (dy * dy)).sqrt()
}
impl Touch {
fn distance_from_start(&self) -> f32 {
dist(self.start_x, self.start_y, self.current_x, self.current_y)
}
fn initial_distance_from_other(&self, other: &Touch) -> f32 {
dist(self.start_x, self.start_y, other.start_x, other.start_y)
}
fn current_distance_from_other(&self, other: &Touch) -> f32 {
dist(
self.current_x,
self.current_y,
other.current_x,
other.current_y,
)
}
}
struct TouchState {
active_touches: HashMap<u64, Touch>,
current_gesture: Gesture,
start_zoom: f32,
current_zoom: f32,
start_pan: DeviceIntPoint,
current_pan: DeviceIntPoint,
}
enum TouchResult {
None,
Pan(DeviceIntPoint),
Zoom(f32),
}
impl TouchState {
fn new() -> TouchState {
TouchState {
active_touches: HashMap::new(),
current_gesture: Gesture::None,
start_zoom: 1.0,
current_zoom: 1.0,
start_pan: DeviceIntPoint::zero(),
current_pan: DeviceIntPoint::zero(),
}
}
fn handle_event(&mut self, touch: winit::Touch) -> TouchResult {
match touch.phase {
TouchPhase::Started => {
debug_assert!(!self.active_touches.contains_key(&touch.id));
self.active_touches.insert(
touch.id,
Touch {
id: touch.id,
start_x: touch.location.x as f32,
start_y: touch.location.y as f32,
current_x: touch.location.x as f32,
current_y: touch.location.y as f32,
},
);
self.current_gesture = Gesture::None;
}
TouchPhase::Moved => {
match self.active_touches.get_mut(&touch.id) {
Some(active_touch) => {
active_touch.current_x = touch.location.x as f32;
active_touch.current_y = touch.location.y as f32;
}
None => panic!("move touch event with unknown touch id!"),
}
match self.current_gesture {
Gesture::None => {
let mut over_threshold_count = 0;
let active_touch_count = self.active_touches.len();
for (_, touch) in &self.active_touches {
if touch.distance_from_start() > 8.0 {
over_threshold_count += 1;
}
}
if active_touch_count == over_threshold_count {
if active_touch_count == 1 {
self.start_pan = self.current_pan;
self.current_gesture = Gesture::Pan;
} else if active_touch_count == 2 {
self.start_zoom = self.current_zoom;
self.current_gesture = Gesture::Zoom;
}
}
}
Gesture::Pan => {
let keys: Vec<u64> = self.active_touches.keys().cloned().collect();
debug_assert!(keys.len() == 1);
let active_touch = &self.active_touches[&keys[0]];
let x = active_touch.current_x - active_touch.start_x;
let y = active_touch.current_y - active_touch.start_y;
self.current_pan.x = self.start_pan.x + x.round() as i32;
self.current_pan.y = self.start_pan.y + y.round() as i32;
return TouchResult::Pan(self.current_pan);
}
Gesture::Zoom => {
let keys: Vec<u64> = self.active_touches.keys().cloned().collect();
debug_assert!(keys.len() == 2);
let touch0 = &self.active_touches[&keys[0]];
let touch1 = &self.active_touches[&keys[1]];
let initial_distance = touch0.initial_distance_from_other(touch1);
let current_distance = touch0.current_distance_from_other(touch1);
self.current_zoom = self.start_zoom * current_distance / initial_distance;
return TouchResult::Zoom(self.current_zoom);
}
}
}
TouchPhase::Ended | TouchPhase::Cancelled => {
self.active_touches.remove(&touch.id).unwrap();
self.current_gesture = Gesture::None;
}
}
TouchResult::None
}
}
fn main() {
let mut app = App {
touch_state: TouchState::new(),
};
boilerplate::main_wrapper(&mut app, None);
}
struct App {
touch_state: TouchState,
}
impl Example for App {
// Make this the only example to test all shaders for compile errors.
const PRECACHE_SHADER_FLAGS: ShaderPrecacheFlags = ShaderPrecacheFlags::FULL_COMPILE;
fn render(
&mut self,
api: &mut RenderApi,
builder: &mut DisplayListBuilder,
txn: &mut Transaction,
_: DeviceIntSize,
pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let content_bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
let root_space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
let spatial_id = root_space_and_clip.spatial_id;
builder.push_simple_stacking_context(
content_bounds.origin,
spatial_id,
PrimitiveFlags::IS_BACKFACE_VISIBLE,
);
let image_mask_key = api.generate_image_key();
txn.add_image(
image_mask_key,
ImageDescriptor::new(2, 2, ImageFormat::R8, ImageDescriptorFlags::IS_OPAQUE),
ImageData::new(vec![0, 80, 180, 255]),
None,
);
let mask = ImageMask {
image: image_mask_key,
rect: (75, 75).by(100, 100),
repeat: false,
};
let complex = ComplexClipRegion::new(
(50, 50).to(150, 150),
BorderRadius::uniform(20.0),
ClipMode::Clip
);
let mask_clip_id = builder.define_clip_image_mask(
&root_space_and_clip,
mask,
);
let clip_id = builder.define_clip_rounded_rect(
&SpaceAndClipInfo {
spatial_id: root_space_and_clip.spatial_id,
clip_id: mask_clip_id,
},
complex,
);
builder.push_rect(
&CommonItemProperties::new(
(100, 100).to(200, 200),
SpaceAndClipInfo { spatial_id, clip_id },
),
(100, 100).to(200, 200),
ColorF::new(0.0, 1.0, 0.0, 1.0),
);
builder.push_rect(
&CommonItemProperties::new(
(250, 100).to(350, 200),
SpaceAndClipInfo { spatial_id, clip_id },
),
(250, 100).to(350, 200),
ColorF::new(0.0, 1.0, 0.0, 1.0),
);
let border_side = BorderSide {
color: ColorF::new(0.0, 0.0, 1.0, 1.0),
style: BorderStyle::Groove,
};
let border_widths = LayoutSideOffsets::new_all_same(10.0);
let border_details = BorderDetails::Normal(NormalBorder {
top: border_side,
right: border_side,
bottom: border_side,
left: border_side,
radius: BorderRadius::uniform(20.0),
do_aa: true,
});
let bounds = (100, 100).to(200, 200);
builder.push_border(
&CommonItemProperties::new(
bounds,
SpaceAndClipInfo { spatial_id, clip_id },
),
bounds,
border_widths,
border_details,
);
if false {
// draw box shadow?
let simple_box_bounds = (20, 200).by(50, 50);
let offset = vec2(10.0, 10.0);
let color = ColorF::new(1.0, 1.0, 1.0, 1.0);
let blur_radius = 0.0;
let spread_radius = 0.0;
let simple_border_radius = 8.0;
let box_shadow_type = BoxShadowClipMode::Inset;
builder.push_box_shadow(
&CommonItemProperties::new(content_bounds, root_space_and_clip),
simple_box_bounds,
offset,
color,
blur_radius,
spread_radius,
BorderRadius::uniform(simple_border_radius),
box_shadow_type,
);
}
builder.pop_stacking_context();
}
fn on_event(&mut self, event: winit::WindowEvent, api: &mut RenderApi, document_id: DocumentId) -> bool {
let mut txn = Transaction::new();
match event {
winit::WindowEvent::Touch(touch) => match self.touch_state.handle_event(touch) {
TouchResult::Pan(pan) => {
txn.set_pan(pan);
}
TouchResult::Zoom(zoom) => {
txn.set_pinch_zoom(ZoomFactor::new(zoom));
}
TouchResult::None => {}
},
_ => (),
}
if !txn.is_empty() {
txn.generate_frame();
api.send_transaction(document_id, txn);
}
false
}
}

289
third_party/webrender/examples/blob.rs vendored Normal file
View file

@ -0,0 +1,289 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
extern crate gleam;
extern crate glutin;
extern crate rayon;
extern crate webrender;
extern crate winit;
#[path = "common/boilerplate.rs"]
mod boilerplate;
use crate::boilerplate::{Example, HandyDandyRectBuilder};
use rayon::{ThreadPool, ThreadPoolBuilder};
use rayon::prelude::*;
use std::collections::HashMap;
use std::sync::Arc;
use webrender::api::{self, DisplayListBuilder, DocumentId, PipelineId, PrimitiveFlags, RenderApi, Transaction};
use webrender::api::{ColorF, CommonItemProperties, SpaceAndClipInfo, ImageDescriptorFlags};
use webrender::api::units::*;
use webrender::euclid::size2;
// This example shows how to implement a very basic BlobImageHandler that can only render
// a checkerboard pattern.
// The deserialized command list internally used by this example is just a color.
type ImageRenderingCommands = api::ColorU;
// Serialize/deserialize the blob.
// For real usecases you should probably use serde rather than doing it by hand.
fn serialize_blob(color: api::ColorU) -> Arc<Vec<u8>> {
Arc::new(vec![color.r, color.g, color.b, color.a])
}
fn deserialize_blob(blob: &[u8]) -> Result<ImageRenderingCommands, ()> {
let mut iter = blob.iter();
return match (iter.next(), iter.next(), iter.next(), iter.next()) {
(Some(&r), Some(&g), Some(&b), Some(&a)) => Ok(api::ColorU::new(r, g, b, a)),
(Some(&a), None, None, None) => Ok(api::ColorU::new(a, a, a, a)),
_ => Err(()),
};
}
// This is the function that applies the deserialized drawing commands and generates
// actual image data.
fn render_blob(
commands: Arc<ImageRenderingCommands>,
descriptor: &api::BlobImageDescriptor,
tile: TileOffset,
) -> api::BlobImageResult {
let color = *commands;
// Note: This implementation ignores the dirty rect which isn't incorrect
// but is a missed optimization.
// Allocate storage for the result. Right now the resource cache expects the
// tiles to have have no stride or offset.
let bpp = 4;
let mut texels = Vec::with_capacity((descriptor.rect.size.area() * bpp) as usize);
// Generate a per-tile pattern to see it in the demo. For a real use case it would not
// make sense for the rendered content to depend on its tile.
let tile_checker = (tile.x % 2 == 0) != (tile.y % 2 == 0);
let [w, h] = descriptor.rect.size.to_array();
let offset = descriptor.rect.origin;
for y in 0..h {
for x in 0..w {
// Apply the tile's offset. This is important: all drawing commands should be
// translated by this offset to give correct results with tiled blob images.
let x2 = x + offset.x;
let y2 = y + offset.y;
// Render a simple checkerboard pattern
let checker = if (x2 % 20 >= 10) != (y2 % 20 >= 10) {
1
} else {
0
};
// ..nested in the per-tile checkerboard pattern
let tc = if tile_checker { 0 } else { (1 - checker) * 40 };
match descriptor.format {
api::ImageFormat::BGRA8 => {
texels.push(color.b * checker + tc);
texels.push(color.g * checker + tc);
texels.push(color.r * checker + tc);
texels.push(color.a * checker + tc);
}
api::ImageFormat::R8 => {
texels.push(color.a * checker + tc);
}
_ => {
return Err(api::BlobImageError::Other(
format!("Unsupported image format"),
));
}
}
}
}
Ok(api::RasterizedBlobImage {
data: Arc::new(texels),
rasterized_rect: size2(w, h).into(),
})
}
struct CheckerboardRenderer {
// We are going to defer the rendering work to worker threads.
// Using a pre-built Arc<ThreadPool> rather than creating our own threads
// makes it possible to share the same thread pool as the glyph renderer (if we
// want to).
workers: Arc<ThreadPool>,
// The deserialized drawing commands.
// In this example we store them in Arcs. This isn't necessary since in this simplified
// case the command list is a simple 32 bits value and would be cheap to clone before sending
// to the workers. But in a more realistic scenario the commands would typically be bigger
// and more expensive to clone, so let's pretend it is also the case here.
image_cmds: HashMap<api::BlobImageKey, Arc<ImageRenderingCommands>>,
}
impl CheckerboardRenderer {
fn new(workers: Arc<ThreadPool>) -> Self {
CheckerboardRenderer {
image_cmds: HashMap::new(),
workers,
}
}
}
impl api::BlobImageHandler for CheckerboardRenderer {
fn create_similar(&self) -> Box<dyn api::BlobImageHandler> {
Box::new(CheckerboardRenderer::new(Arc::clone(&self.workers)))
}
fn add(&mut self, key: api::BlobImageKey, cmds: Arc<api::BlobImageData>,
_visible_rect: &DeviceIntRect, _: api::TileSize) {
self.image_cmds
.insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap()));
}
fn update(&mut self, key: api::BlobImageKey, cmds: Arc<api::BlobImageData>,
_visible_rect: &DeviceIntRect, _dirty_rect: &BlobDirtyRect) {
// Here, updating is just replacing the current version of the commands with
// the new one (no incremental updates).
self.image_cmds
.insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap()));
}
fn delete(&mut self, key: api::BlobImageKey) {
self.image_cmds.remove(&key);
}
fn prepare_resources(
&mut self,
_services: &dyn api::BlobImageResources,
_requests: &[api::BlobImageParams],
) {}
fn enable_multithreading(&mut self, _: bool) {}
fn delete_font(&mut self, _font: api::FontKey) {}
fn delete_font_instance(&mut self, _instance: api::FontInstanceKey) {}
fn clear_namespace(&mut self, _namespace: api::IdNamespace) {}
fn create_blob_rasterizer(&mut self) -> Box<dyn api::AsyncBlobImageRasterizer> {
Box::new(Rasterizer {
workers: Arc::clone(&self.workers),
image_cmds: self.image_cmds.clone(),
})
}
}
struct Rasterizer {
workers: Arc<ThreadPool>,
image_cmds: HashMap<api::BlobImageKey, Arc<ImageRenderingCommands>>,
}
impl api::AsyncBlobImageRasterizer for Rasterizer {
fn rasterize(
&mut self,
requests: &[api::BlobImageParams],
_low_priority: bool
) -> Vec<(api::BlobImageRequest, api::BlobImageResult)> {
let requests: Vec<(&api::BlobImageParams, Arc<ImageRenderingCommands>)> = requests.into_iter().map(|params| {
(params, Arc::clone(&self.image_cmds[&params.request.key]))
}).collect();
self.workers.install(|| {
requests.into_par_iter().map(|(params, commands)| {
(params.request, render_blob(commands, &params.descriptor, params.request.tile))
}).collect()
})
}
}
struct App {}
impl Example for App {
fn render(
&mut self,
api: &mut RenderApi,
builder: &mut DisplayListBuilder,
txn: &mut Transaction,
_device_size: DeviceIntSize,
pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
builder.push_simple_stacking_context(
LayoutPoint::zero(),
space_and_clip.spatial_id,
PrimitiveFlags::IS_BACKFACE_VISIBLE,
);
let size1 = DeviceIntSize::new(500, 500);
let blob_img1 = api.generate_blob_image_key();
txn.add_blob_image(
blob_img1,
api::ImageDescriptor::new(
size1.width,
size1.height,
api::ImageFormat::BGRA8,
ImageDescriptorFlags::IS_OPAQUE,
),
serialize_blob(api::ColorU::new(50, 50, 150, 255)),
size1.into(),
Some(128),
);
let bounds = (30, 30).by(size1.width, size1.height);
builder.push_image(
&CommonItemProperties::new(bounds, space_and_clip),
bounds,
api::ImageRendering::Auto,
api::AlphaType::PremultipliedAlpha,
blob_img1.as_image(),
ColorF::WHITE,
);
let size2 = DeviceIntSize::new(256, 256);
let blob_img2 = api.generate_blob_image_key();
txn.add_blob_image(
blob_img2,
api::ImageDescriptor::new(
size2.width,
size2.height,
api::ImageFormat::BGRA8,
ImageDescriptorFlags::IS_OPAQUE,
),
serialize_blob(api::ColorU::new(50, 150, 50, 255)),
size2.into(),
None,
);
let bounds = (600, 600).by(size2.width, size2.height);
builder.push_image(
&CommonItemProperties::new(bounds, space_and_clip),
bounds,
api::ImageRendering::Auto,
api::AlphaType::PremultipliedAlpha,
blob_img2.as_image(),
ColorF::WHITE,
);
builder.pop_stacking_context();
}
}
fn main() {
let workers =
ThreadPoolBuilder::new().thread_name(|idx| format!("WebRender:Worker#{}", idx))
.build();
let workers = Arc::new(workers.unwrap());
let opts = webrender::RendererOptions {
workers: Some(Arc::clone(&workers)),
// Register our blob renderer, so that WebRender integrates it in the resource cache..
// Share the same pool of worker threads between WebRender and our blob renderer.
blob_image_handler: Some(Box::new(CheckerboardRenderer::new(Arc::clone(&workers)))),
..Default::default()
};
let mut app = App {};
boilerplate::main_wrapper(&mut app, Some(opts));
}

View file

@ -0,0 +1,338 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use gleam::gl;
use glutin;
use std::env;
use std::path::PathBuf;
use webrender;
use winit;
use webrender::{DebugFlags, ShaderPrecacheFlags};
use webrender::api::*;
use webrender::api::units::*;
struct Notifier {
events_proxy: winit::EventsLoopProxy,
}
impl Notifier {
fn new(events_proxy: winit::EventsLoopProxy) -> Notifier {
Notifier { events_proxy }
}
}
impl RenderNotifier for Notifier {
fn clone(&self) -> Box<dyn RenderNotifier> {
Box::new(Notifier {
events_proxy: self.events_proxy.clone(),
})
}
fn wake_up(&self) {
#[cfg(not(target_os = "android"))]
let _ = self.events_proxy.wakeup();
}
fn new_frame_ready(&self,
_: DocumentId,
_scrolled: bool,
_composite_needed: bool,
_render_time: Option<u64>) {
self.wake_up();
}
}
pub trait HandyDandyRectBuilder {
fn to(&self, x2: i32, y2: i32) -> LayoutRect;
fn by(&self, w: i32, h: i32) -> LayoutRect;
}
// Allows doing `(x, y).to(x2, y2)` or `(x, y).by(width, height)` with i32
// values to build a f32 LayoutRect
impl HandyDandyRectBuilder for (i32, i32) {
fn to(&self, x2: i32, y2: i32) -> LayoutRect {
LayoutRect::new(
LayoutPoint::new(self.0 as f32, self.1 as f32),
LayoutSize::new((x2 - self.0) as f32, (y2 - self.1) as f32),
)
}
fn by(&self, w: i32, h: i32) -> LayoutRect {
LayoutRect::new(
LayoutPoint::new(self.0 as f32, self.1 as f32),
LayoutSize::new(w as f32, h as f32),
)
}
}
pub trait Example {
const TITLE: &'static str = "WebRender Sample App";
const PRECACHE_SHADER_FLAGS: ShaderPrecacheFlags = ShaderPrecacheFlags::EMPTY;
const WIDTH: u32 = 1920;
const HEIGHT: u32 = 1080;
fn render(
&mut self,
api: &mut RenderApi,
builder: &mut DisplayListBuilder,
txn: &mut Transaction,
device_size: DeviceIntSize,
pipeline_id: PipelineId,
document_id: DocumentId,
);
fn on_event(
&mut self,
_: winit::WindowEvent,
_: &mut RenderApi,
_: DocumentId,
) -> bool {
false
}
fn get_image_handlers(
&mut self,
_gl: &dyn gl::Gl,
) -> (Option<Box<dyn ExternalImageHandler>>,
Option<Box<dyn OutputImageHandler>>) {
(None, None)
}
fn draw_custom(&mut self, _gl: &dyn gl::Gl) {
}
}
pub fn main_wrapper<E: Example>(
example: &mut E,
options: Option<webrender::RendererOptions>,
) {
env_logger::init();
#[cfg(target_os = "macos")]
{
use core_foundation::{self as cf, base::TCFType};
let i = cf::bundle::CFBundle::main_bundle().info_dictionary();
let mut i = unsafe { i.to_mutable() };
i.set(
cf::string::CFString::new("NSSupportsAutomaticGraphicsSwitching"),
cf::boolean::CFBoolean::true_value().into_CFType(),
);
}
let args: Vec<String> = env::args().collect();
let res_path = if args.len() > 1 {
Some(PathBuf::from(&args[1]))
} else {
None
};
let mut events_loop = winit::EventsLoop::new();
let window_builder = winit::WindowBuilder::new()
.with_title(E::TITLE)
.with_multitouch()
.with_dimensions(winit::dpi::LogicalSize::new(E::WIDTH as f64, E::HEIGHT as f64));
let windowed_context = glutin::ContextBuilder::new()
.with_gl(glutin::GlRequest::GlThenGles {
opengl_version: (3, 2),
opengles_version: (3, 0),
})
.build_windowed(window_builder, &events_loop)
.unwrap();
let windowed_context = unsafe { windowed_context.make_current().unwrap() };
let gl = match windowed_context.get_api() {
glutin::Api::OpenGl => unsafe {
gl::GlFns::load_with(
|symbol| windowed_context.get_proc_address(symbol) as *const _
)
},
glutin::Api::OpenGlEs => unsafe {
gl::GlesFns::load_with(
|symbol| windowed_context.get_proc_address(symbol) as *const _
)
},
glutin::Api::WebGl => unimplemented!(),
};
println!("OpenGL version {}", gl.get_string(gl::VERSION));
println!("Shader resource path: {:?}", res_path);
let device_pixel_ratio = windowed_context.window().get_hidpi_factor() as f32;
println!("Device pixel ratio: {}", device_pixel_ratio);
println!("Loading shaders...");
let mut debug_flags = DebugFlags::ECHO_DRIVER_MESSAGES | DebugFlags::TEXTURE_CACHE_DBG;
let opts = webrender::RendererOptions {
resource_override_path: res_path,
precache_flags: E::PRECACHE_SHADER_FLAGS,
device_pixel_ratio,
clear_color: Some(ColorF::new(0.3, 0.0, 0.0, 1.0)),
debug_flags,
//allow_texture_swizzling: false,
..options.unwrap_or(webrender::RendererOptions::default())
};
let device_size = {
let size = windowed_context
.window()
.get_inner_size()
.unwrap()
.to_physical(device_pixel_ratio as f64);
DeviceIntSize::new(size.width as i32, size.height as i32)
};
let notifier = Box::new(Notifier::new(events_loop.create_proxy()));
let (mut renderer, sender) = webrender::Renderer::new(
gl.clone(),
notifier,
opts,
None,
device_size,
).unwrap();
let mut api = sender.create_api();
let document_id = api.add_document(device_size, 0);
let (external, output) = example.get_image_handlers(&*gl);
if let Some(output_image_handler) = output {
renderer.set_output_image_handler(output_image_handler);
}
if let Some(external_image_handler) = external {
renderer.set_external_image_handler(external_image_handler);
}
let epoch = Epoch(0);
let pipeline_id = PipelineId(0, 0);
let layout_size = device_size.to_f32() / euclid::Scale::new(device_pixel_ratio);
let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
let mut txn = Transaction::new();
example.render(
&mut api,
&mut builder,
&mut txn,
device_size,
pipeline_id,
document_id,
);
txn.set_display_list(
epoch,
Some(ColorF::new(0.3, 0.0, 0.0, 1.0)),
layout_size,
builder.finalize(),
true,
);
txn.set_root_pipeline(pipeline_id);
txn.generate_frame();
api.send_transaction(document_id, txn);
println!("Entering event loop");
events_loop.run_forever(|global_event| {
let mut txn = Transaction::new();
let mut custom_event = true;
let old_flags = debug_flags;
let win_event = match global_event {
winit::Event::WindowEvent { event, .. } => event,
_ => return winit::ControlFlow::Continue,
};
match win_event {
winit::WindowEvent::CloseRequested => return winit::ControlFlow::Break,
winit::WindowEvent::AxisMotion { .. } |
winit::WindowEvent::CursorMoved { .. } => {
custom_event = example.on_event(
win_event,
&mut api,
document_id,
);
// skip high-frequency events from triggering a frame draw.
if !custom_event {
return winit::ControlFlow::Continue;
}
},
winit::WindowEvent::KeyboardInput {
input: winit::KeyboardInput {
state: winit::ElementState::Pressed,
virtual_keycode: Some(key),
..
},
..
} => match key {
winit::VirtualKeyCode::Escape => return winit::ControlFlow::Break,
winit::VirtualKeyCode::P => debug_flags.toggle(DebugFlags::PROFILER_DBG),
winit::VirtualKeyCode::O => debug_flags.toggle(DebugFlags::RENDER_TARGET_DBG),
winit::VirtualKeyCode::I => debug_flags.toggle(DebugFlags::TEXTURE_CACHE_DBG),
winit::VirtualKeyCode::S => debug_flags.toggle(DebugFlags::COMPACT_PROFILER),
winit::VirtualKeyCode::T => debug_flags.toggle(DebugFlags::PICTURE_CACHING_DBG),
winit::VirtualKeyCode::Q => debug_flags.toggle(
DebugFlags::GPU_TIME_QUERIES | DebugFlags::GPU_SAMPLE_QUERIES
),
winit::VirtualKeyCode::F => debug_flags.toggle(
DebugFlags::NEW_FRAME_INDICATOR | DebugFlags::NEW_SCENE_INDICATOR
),
winit::VirtualKeyCode::G => debug_flags.toggle(DebugFlags::GPU_CACHE_DBG),
winit::VirtualKeyCode::Key1 => txn.set_document_view(
device_size.into(),
1.0
),
winit::VirtualKeyCode::Key2 => txn.set_document_view(
device_size.into(),
2.0
),
winit::VirtualKeyCode::M => api.notify_memory_pressure(),
winit::VirtualKeyCode::C => {
let path: PathBuf = "../captures/example".into();
//TODO: switch between SCENE/FRAME capture types
// based on "shift" modifier, when `glutin` is updated.
let bits = CaptureBits::all();
api.save_capture(path, bits);
},
_ => {
custom_event = example.on_event(
win_event,
&mut api,
document_id,
)
},
},
other => custom_event = example.on_event(
other,
&mut api,
document_id,
),
};
if debug_flags != old_flags {
api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
}
if custom_event {
let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
example.render(
&mut api,
&mut builder,
&mut txn,
device_size,
pipeline_id,
document_id,
);
txn.set_display_list(
epoch,
Some(ColorF::new(0.3, 0.0, 0.0, 1.0)),
layout_size,
builder.finalize(),
true,
);
txn.generate_frame();
}
api.send_transaction(document_id, txn);
renderer.update();
renderer.render(device_size).unwrap();
let _ = renderer.flush_pipeline_info();
example.draw_custom(&*gl);
windowed_context.swap_buffers().ok();
winit::ControlFlow::Continue
});
renderer.deinit();
}

View file

@ -0,0 +1,19 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use webrender::api::{ImageData, ImageDescriptor, ImageFormat, ImageDescriptorFlags};
pub fn make_checkerboard(width: u32, height: u32) -> (ImageDescriptor, ImageData) {
let mut image_data = Vec::new();
for y in 0 .. height {
for x in 0 .. width {
let lum = 255 * (((x & 8) == 0) ^ ((y & 8) == 0)) as u8;
image_data.extend_from_slice(&[lum, lum, lum, 0xff]);
}
}
(
ImageDescriptor::new(width as i32, height as i32, ImageFormat::BGRA8, ImageDescriptorFlags::IS_OPAQUE),
ImageData::new(image_data)
)
}

View file

@ -0,0 +1,149 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
extern crate euclid;
extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate winit;
#[path = "common/boilerplate.rs"]
mod boilerplate;
use crate::boilerplate::Example;
use euclid::Scale;
use webrender::api::*;
use webrender::api::units::*;
// This example creates multiple documents overlapping each other with
// specified layer indices.
struct Document {
id: DocumentId,
pipeline_id: PipelineId,
content_rect: LayoutRect,
color: ColorF,
}
struct App {
documents: Vec<Document>,
}
impl App {
fn init(
&mut self,
api: &mut RenderApi,
device_pixel_ratio: f32,
) {
let init_data = vec![
(
PipelineId(1, 0),
-2,
ColorF::new(0.0, 1.0, 0.0, 1.0),
DeviceIntPoint::new(0, 0),
),
(
PipelineId(2, 0),
-1,
ColorF::new(1.0, 1.0, 0.0, 1.0),
DeviceIntPoint::new(200, 0),
),
(
PipelineId(3, 0),
0,
ColorF::new(1.0, 0.0, 0.0, 1.0),
DeviceIntPoint::new(200, 200),
),
(
PipelineId(4, 0),
1,
ColorF::new(1.0, 0.0, 1.0, 1.0),
DeviceIntPoint::new(0, 200),
),
];
for (pipeline_id, layer, color, offset) in init_data {
let size = DeviceIntSize::new(250, 250);
let bounds = DeviceIntRect::new(offset, size);
let document_id = api.add_document(size, layer);
let mut txn = Transaction::new();
txn.set_document_view(bounds, device_pixel_ratio);
txn.set_root_pipeline(pipeline_id);
api.send_transaction(document_id, txn);
self.documents.push(Document {
id: document_id,
pipeline_id,
content_rect: LayoutRect::new(
LayoutPoint::origin(),
bounds.size.to_f32() / Scale::new(device_pixel_ratio),
),
color,
});
}
}
}
impl Example for App {
fn render(
&mut self,
api: &mut RenderApi,
base_builder: &mut DisplayListBuilder,
_txn: &mut Transaction,
device_size: DeviceIntSize,
_pipeline_id: PipelineId,
_: DocumentId,
) {
if self.documents.is_empty() {
let device_pixel_ratio = device_size.width as f32 /
base_builder.content_size().width;
// this is the first run, hack around the boilerplate,
// which assumes an example only needs one document
self.init(api, device_pixel_ratio);
}
for doc in &self.documents {
let space_and_clip = SpaceAndClipInfo::root_scroll(doc.pipeline_id);
let mut builder = DisplayListBuilder::new(
doc.pipeline_id,
doc.content_rect.size,
);
let local_rect = LayoutRect::new(
LayoutPoint::zero(),
doc.content_rect.size,
);
builder.push_simple_stacking_context(
doc.content_rect.origin,
space_and_clip.spatial_id,
PrimitiveFlags::IS_BACKFACE_VISIBLE,
);
builder.push_rect(
&CommonItemProperties::new(local_rect, space_and_clip),
local_rect,
doc.color,
);
builder.pop_stacking_context();
let mut txn = Transaction::new();
txn.set_display_list(
Epoch(0),
None,
doc.content_rect.size,
builder.finalize(),
true,
);
txn.generate_frame();
api.send_transaction(doc.id, txn);
}
}
}
fn main() {
let mut app = App {
documents: Vec::new(),
};
boilerplate::main_wrapper(&mut app, None);
}

View file

@ -0,0 +1,238 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
extern crate euclid;
extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate winit;
#[path = "common/boilerplate.rs"]
mod boilerplate;
use crate::boilerplate::{Example, HandyDandyRectBuilder};
use euclid::Scale;
use gleam::gl;
use webrender::api::*;
use webrender::api::units::*;
// This example demonstrates using the frame output feature to copy
// the output of a WR framebuffer to a custom texture.
#[derive(Debug)]
struct Document {
id: DocumentId,
pipeline_id: PipelineId,
content_rect: LayoutRect,
color: ColorF,
}
struct App {
external_image_key: Option<ImageKey>,
output_document: Option<Document>
}
struct OutputHandler {
texture_id: gl::GLuint
}
struct ExternalHandler {
texture_id: gl::GLuint
}
impl OutputImageHandler for OutputHandler {
fn lock(&mut self, _id: PipelineId) -> Option<(u32, FramebufferIntSize)> {
Some((self.texture_id, FramebufferIntSize::new(500, 500)))
}
fn unlock(&mut self, _id: PipelineId) {}
}
impl ExternalImageHandler for ExternalHandler {
fn lock(
&mut self,
_key: ExternalImageId,
_channel_index: u8,
_rendering: ImageRendering
) -> ExternalImage {
ExternalImage {
uv: TexelRect::new(0.0, 0.0, 1.0, 1.0),
source: ExternalImageSource::NativeTexture(self.texture_id),
}
}
fn unlock(&mut self, _key: ExternalImageId, _channel_index: u8) {}
}
impl App {
fn init_output_document(
&mut self,
api: &mut RenderApi,
device_size: DeviceIntSize,
device_pixel_ratio: f32,
) {
// Generate the external image key that will be used to render the output document to the root document.
self.external_image_key = Some(api.generate_image_key());
let pipeline_id = PipelineId(1, 0);
let layer = 1;
let color = ColorF::new(1., 1., 0., 1.);
let document_id = api.add_document(device_size, layer);
api.enable_frame_output(document_id, pipeline_id, true);
api.set_document_view(
document_id,
device_size.into(),
device_pixel_ratio,
);
let document = Document {
id: document_id,
pipeline_id,
content_rect: LayoutRect::new(
LayoutPoint::zero(),
device_size.to_f32() / Scale::new(device_pixel_ratio),
),
color,
};
let mut txn = Transaction::new();
txn.add_image(
self.external_image_key.unwrap(),
ImageDescriptor::new(100, 100, ImageFormat::BGRA8, ImageDescriptorFlags::IS_OPAQUE),
ImageData::External(ExternalImageData {
id: ExternalImageId(0),
channel_index: 0,
image_type: ExternalImageType::TextureHandle(TextureTarget::Default),
}),
None,
);
let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
let mut builder = DisplayListBuilder::new(
document.pipeline_id,
document.content_rect.size,
);
builder.push_simple_stacking_context(
document.content_rect.origin,
space_and_clip.spatial_id,
PrimitiveFlags::IS_BACKFACE_VISIBLE,
);
builder.push_rect(
&CommonItemProperties::new(document.content_rect, space_and_clip),
document.content_rect,
ColorF::new(1.0, 1.0, 0.0, 1.0)
);
builder.pop_stacking_context();
txn.set_root_pipeline(pipeline_id);
txn.set_display_list(
Epoch(0),
Some(document.color),
document.content_rect.size,
builder.finalize(),
true,
);
txn.generate_frame();
api.send_transaction(document.id, txn);
self.output_document = Some(document);
}
}
impl Example for App {
fn render(
&mut self,
api: &mut RenderApi,
builder: &mut DisplayListBuilder,
_txn: &mut Transaction,
device_size: DeviceIntSize,
pipeline_id: PipelineId,
_document_id: DocumentId,
) {
if self.output_document.is_none() {
let device_pixel_ratio = device_size.width as f32 /
builder.content_size().width;
self.init_output_document(api, DeviceIntSize::new(200, 200), device_pixel_ratio);
}
let bounds = (100, 100).to(200, 200);
let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
builder.push_simple_stacking_context(
bounds.origin,
space_and_clip.spatial_id,
PrimitiveFlags::IS_BACKFACE_VISIBLE,
);
builder.push_image(
&CommonItemProperties::new(bounds, space_and_clip),
bounds,
ImageRendering::Auto,
AlphaType::PremultipliedAlpha,
self.external_image_key.unwrap(),
ColorF::WHITE,
);
builder.pop_stacking_context();
}
fn get_image_handlers(
&mut self,
gl: &dyn gl::Gl,
) -> (Option<Box<dyn ExternalImageHandler>>,
Option<Box<dyn OutputImageHandler>>) {
let texture_id = gl.gen_textures(1)[0];
gl.bind_texture(gl::TEXTURE_2D, texture_id);
gl.tex_parameter_i(
gl::TEXTURE_2D,
gl::TEXTURE_MAG_FILTER,
gl::LINEAR as gl::GLint,
);
gl.tex_parameter_i(
gl::TEXTURE_2D,
gl::TEXTURE_MIN_FILTER,
gl::LINEAR as gl::GLint,
);
gl.tex_parameter_i(
gl::TEXTURE_2D,
gl::TEXTURE_WRAP_S,
gl::CLAMP_TO_EDGE as gl::GLint,
);
gl.tex_parameter_i(
gl::TEXTURE_2D,
gl::TEXTURE_WRAP_T,
gl::CLAMP_TO_EDGE as gl::GLint,
);
gl.tex_image_2d(
gl::TEXTURE_2D,
0,
gl::RGBA as gl::GLint,
100,
100,
0,
gl::BGRA,
gl::UNSIGNED_BYTE,
None,
);
gl.bind_texture(gl::TEXTURE_2D, 0);
(
Some(Box::new(ExternalHandler { texture_id })),
Some(Box::new(OutputHandler { texture_id }))
)
}
}
fn main() {
let mut app = App {
external_image_key: None,
output_document: None
};
boilerplate::main_wrapper(&mut app, None);
}

View file

@ -0,0 +1,95 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate winit;
#[path = "common/boilerplate.rs"]
mod boilerplate;
use crate::boilerplate::{Example, HandyDandyRectBuilder};
use webrender::api::*;
use webrender::api::units::*;
// This example uses the push_iframe API to nest a second pipeline's displaylist
// inside the root pipeline's display list. When it works, a green square is
// shown. If it fails, a red square is shown.
struct App {}
impl Example for App {
fn render(
&mut self,
api: &mut RenderApi,
builder: &mut DisplayListBuilder,
_txn: &mut Transaction,
_device_size: DeviceIntSize,
pipeline_id: PipelineId,
document_id: DocumentId,
) {
// All the sub_* things are for the nested pipeline
let sub_size = DeviceIntSize::new(100, 100);
let sub_bounds = (0, 0).to(sub_size.width as i32, sub_size.height as i32);
let sub_pipeline_id = PipelineId(pipeline_id.0, 42);
let mut sub_builder = DisplayListBuilder::new(sub_pipeline_id, sub_bounds.size);
let mut space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
sub_builder.push_simple_stacking_context(
sub_bounds.origin,
space_and_clip.spatial_id,
PrimitiveFlags::IS_BACKFACE_VISIBLE,
);
// green rect visible == success
sub_builder.push_rect(
&CommonItemProperties::new(sub_bounds, space_and_clip),
sub_bounds,
ColorF::new(0.0, 1.0, 0.0, 1.0)
);
sub_builder.pop_stacking_context();
let mut txn = Transaction::new();
txn.set_display_list(
Epoch(0),
None,
sub_bounds.size,
sub_builder.finalize(),
true,
);
api.send_transaction(document_id, txn);
space_and_clip.spatial_id = builder.push_reference_frame(
sub_bounds.origin,
space_and_clip.spatial_id,
TransformStyle::Flat,
PropertyBinding::Binding(PropertyBindingKey::new(42), LayoutTransform::identity()),
ReferenceFrameKind::Transform,
);
// And this is for the root pipeline
builder.push_simple_stacking_context(
sub_bounds.origin,
space_and_clip.spatial_id,
PrimitiveFlags::IS_BACKFACE_VISIBLE,
);
// red rect under the iframe: if this is visible, things have gone wrong
builder.push_rect(
&CommonItemProperties::new(sub_bounds, space_and_clip),
sub_bounds,
ColorF::new(1.0, 0.0, 0.0, 1.0)
);
builder.push_iframe(sub_bounds, sub_bounds, &space_and_clip, sub_pipeline_id, false);
builder.pop_stacking_context();
builder.pop_reference_frame();
}
}
fn main() {
let mut app = App {};
boilerplate::main_wrapper(&mut app, None);
}

View file

@ -0,0 +1,121 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate winit;
#[path = "common/boilerplate.rs"]
mod boilerplate;
#[path = "common/image_helper.rs"]
mod image_helper;
use crate::boilerplate::{Example, HandyDandyRectBuilder};
use webrender::api::*;
use webrender::api::units::*;
struct App {
image_key: ImageKey,
}
impl Example for App {
fn render(
&mut self,
_api: &mut RenderApi,
builder: &mut DisplayListBuilder,
txn: &mut Transaction,
_device_size: DeviceIntSize,
pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let (image_descriptor, image_data) = image_helper::make_checkerboard(32, 32);
txn.add_image(
self.image_key,
image_descriptor,
image_data,
None,
);
let bounds = (0, 0).to(512, 512);
let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
builder.push_simple_stacking_context(
bounds.origin,
space_and_clip.spatial_id,
PrimitiveFlags::IS_BACKFACE_VISIBLE,
);
let image_size = LayoutSize::new(100.0, 100.0);
builder.push_image(
&CommonItemProperties::new(
LayoutRect::new(LayoutPoint::new(100.0, 100.0), image_size),
space_and_clip,
),
bounds,
ImageRendering::Auto,
AlphaType::PremultipliedAlpha,
self.image_key,
ColorF::WHITE,
);
builder.push_image(
&CommonItemProperties::new(
LayoutRect::new(LayoutPoint::new(250.0, 100.0), image_size),
space_and_clip,
),
bounds,
ImageRendering::Pixelated,
AlphaType::PremultipliedAlpha,
self.image_key,
ColorF::WHITE,
);
builder.pop_stacking_context();
}
fn on_event(&mut self, event: winit::WindowEvent, api: &mut RenderApi, document_id: DocumentId) -> bool {
match event {
winit::WindowEvent::KeyboardInput {
input: winit::KeyboardInput {
state: winit::ElementState::Pressed,
virtual_keycode: Some(winit::VirtualKeyCode::Space),
..
},
..
} => {
let mut image_data = Vec::new();
for y in 0 .. 64 {
for x in 0 .. 64 {
let r = 255 * ((y & 32) == 0) as u8;
let g = 255 * ((x & 32) == 0) as u8;
image_data.extend_from_slice(&[0, g, r, 0xff]);
}
}
let mut txn = Transaction::new();
txn.update_image(
self.image_key,
ImageDescriptor::new(64, 64, ImageFormat::BGRA8, ImageDescriptorFlags::IS_OPAQUE),
ImageData::new(image_data),
&DirtyRect::All,
);
let mut txn = Transaction::new();
txn.generate_frame();
api.send_transaction(document_id, txn);
}
_ => {}
}
false
}
}
fn main() {
let mut app = App {
image_key: ImageKey(IdNamespace(0), 0),
};
boilerplate::main_wrapper(&mut app, None);
}

View file

@ -0,0 +1,326 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
extern crate euclid;
extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate winit;
use gleam::gl;
use glutin::NotCurrent;
use std::fs::File;
use std::io::Read;
use webrender::api::*;
use webrender::api::units::*;
use webrender::DebugFlags;
use winit::dpi::LogicalSize;
struct Notifier {
events_proxy: winit::EventsLoopProxy,
}
impl Notifier {
fn new(events_proxy: winit::EventsLoopProxy) -> Notifier {
Notifier { events_proxy }
}
}
impl RenderNotifier for Notifier {
fn clone(&self) -> Box<dyn RenderNotifier> {
Box::new(Notifier {
events_proxy: self.events_proxy.clone(),
})
}
fn wake_up(&self) {
#[cfg(not(target_os = "android"))]
let _ = self.events_proxy.wakeup();
}
fn new_frame_ready(&self,
_: DocumentId,
_scrolled: bool,
_composite_needed: bool,
_render_time: Option<u64>) {
self.wake_up();
}
}
struct Window {
events_loop: winit::EventsLoop, //TODO: share events loop?
context: Option<glutin::WindowedContext<NotCurrent>>,
renderer: webrender::Renderer,
name: &'static str,
pipeline_id: PipelineId,
document_id: DocumentId,
epoch: Epoch,
api: RenderApi,
font_instance_key: FontInstanceKey,
}
impl Window {
fn new(name: &'static str, clear_color: ColorF) -> Self {
let events_loop = winit::EventsLoop::new();
let window_builder = winit::WindowBuilder::new()
.with_title(name)
.with_multitouch()
.with_dimensions(LogicalSize::new(800., 600.));
let context = glutin::ContextBuilder::new()
.with_gl(glutin::GlRequest::GlThenGles {
opengl_version: (3, 2),
opengles_version: (3, 0),
})
.build_windowed(window_builder, &events_loop)
.unwrap();
let context = unsafe { context.make_current().unwrap() };
let gl = match context.get_api() {
glutin::Api::OpenGl => unsafe {
gl::GlFns::load_with(|symbol| context.get_proc_address(symbol) as *const _)
},
glutin::Api::OpenGlEs => unsafe {
gl::GlesFns::load_with(|symbol| context.get_proc_address(symbol) as *const _)
},
glutin::Api::WebGl => unimplemented!(),
};
let device_pixel_ratio = context.window().get_hidpi_factor() as f32;
let opts = webrender::RendererOptions {
device_pixel_ratio,
clear_color: Some(clear_color),
..webrender::RendererOptions::default()
};
let device_size = {
let size = context
.window()
.get_inner_size()
.unwrap()
.to_physical(device_pixel_ratio as f64);
DeviceIntSize::new(size.width as i32, size.height as i32)
};
let notifier = Box::new(Notifier::new(events_loop.create_proxy()));
let (renderer, sender) = webrender::Renderer::new(gl.clone(), notifier, opts, None, device_size).unwrap();
let mut api = sender.create_api();
let document_id = api.add_document(device_size, 0);
let epoch = Epoch(0);
let pipeline_id = PipelineId(0, 0);
let mut txn = Transaction::new();
let font_key = api.generate_font_key();
let font_bytes = load_file("../wrench/reftests/text/FreeSans.ttf");
txn.add_raw_font(font_key, font_bytes, 0);
let font_instance_key = api.generate_font_instance_key();
txn.add_font_instance(font_instance_key, font_key, 32.0, None, None, Vec::new());
api.send_transaction(document_id, txn);
Window {
events_loop,
context: Some(unsafe { context.make_not_current().unwrap() }),
renderer,
name,
epoch,
pipeline_id,
document_id,
api,
font_instance_key,
}
}
fn tick(&mut self) -> bool {
let mut do_exit = false;
let my_name = &self.name;
let renderer = &mut self.renderer;
let api = &mut self.api;
self.events_loop.poll_events(|global_event| match global_event {
winit::Event::WindowEvent { event, .. } => match event {
winit::WindowEvent::CloseRequested |
winit::WindowEvent::KeyboardInput {
input: winit::KeyboardInput {
virtual_keycode: Some(winit::VirtualKeyCode::Escape),
..
},
..
} => {
do_exit = true
}
winit::WindowEvent::KeyboardInput {
input: winit::KeyboardInput {
state: winit::ElementState::Pressed,
virtual_keycode: Some(winit::VirtualKeyCode::P),
..
},
..
} => {
println!("set flags {}", my_name);
api.send_debug_cmd(DebugCommand::SetFlags(DebugFlags::PROFILER_DBG))
}
_ => {}
}
_ => {}
});
if do_exit {
return true
}
let context = unsafe { self.context.take().unwrap().make_current().unwrap() };
let device_pixel_ratio = context.window().get_hidpi_factor() as f32;
let device_size = {
let size = context
.window()
.get_inner_size()
.unwrap()
.to_physical(device_pixel_ratio as f64);
DeviceIntSize::new(size.width as i32, size.height as i32)
};
let layout_size = device_size.to_f32() / euclid::Scale::new(device_pixel_ratio);
let mut txn = Transaction::new();
let mut builder = DisplayListBuilder::new(self.pipeline_id, layout_size);
let space_and_clip = SpaceAndClipInfo::root_scroll(self.pipeline_id);
let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
builder.push_simple_stacking_context(
bounds.origin,
space_and_clip.spatial_id,
PrimitiveFlags::IS_BACKFACE_VISIBLE,
);
builder.push_rect(
&CommonItemProperties::new(
LayoutRect::new(
LayoutPoint::new(100.0, 200.0),
LayoutSize::new(100.0, 200.0),
),
space_and_clip,
),
LayoutRect::new(
LayoutPoint::new(100.0, 200.0),
LayoutSize::new(100.0, 200.0),
),
ColorF::new(0.0, 1.0, 0.0, 1.0));
let text_bounds = LayoutRect::new(
LayoutPoint::new(100.0, 50.0),
LayoutSize::new(700.0, 200.0)
);
let glyphs = vec![
GlyphInstance {
index: 48,
point: LayoutPoint::new(100.0, 100.0),
},
GlyphInstance {
index: 68,
point: LayoutPoint::new(150.0, 100.0),
},
GlyphInstance {
index: 80,
point: LayoutPoint::new(200.0, 100.0),
},
GlyphInstance {
index: 82,
point: LayoutPoint::new(250.0, 100.0),
},
GlyphInstance {
index: 81,
point: LayoutPoint::new(300.0, 100.0),
},
GlyphInstance {
index: 3,
point: LayoutPoint::new(350.0, 100.0),
},
GlyphInstance {
index: 86,
point: LayoutPoint::new(400.0, 100.0),
},
GlyphInstance {
index: 79,
point: LayoutPoint::new(450.0, 100.0),
},
GlyphInstance {
index: 72,
point: LayoutPoint::new(500.0, 100.0),
},
GlyphInstance {
index: 83,
point: LayoutPoint::new(550.0, 100.0),
},
GlyphInstance {
index: 87,
point: LayoutPoint::new(600.0, 100.0),
},
GlyphInstance {
index: 17,
point: LayoutPoint::new(650.0, 100.0),
},
];
builder.push_text(
&CommonItemProperties::new(
text_bounds,
space_and_clip,
),
text_bounds,
&glyphs,
self.font_instance_key,
ColorF::new(1.0, 1.0, 0.0, 1.0),
None,
);
builder.pop_stacking_context();
txn.set_display_list(
self.epoch,
None,
layout_size,
builder.finalize(),
true,
);
txn.set_root_pipeline(self.pipeline_id);
txn.generate_frame();
api.send_transaction(self.document_id, txn);
renderer.update();
renderer.render(device_size).unwrap();
context.swap_buffers().ok();
self.context = Some(unsafe { context.make_not_current().unwrap() });
false
}
fn deinit(self) {
self.renderer.deinit();
}
}
fn main() {
let mut win1 = Window::new("window1", ColorF::new(0.3, 0.0, 0.0, 1.0));
let mut win2 = Window::new("window2", ColorF::new(0.0, 0.3, 0.0, 1.0));
loop {
if win1.tick() {
break;
}
if win2.tick() {
break;
}
}
win1.deinit();
win2.deinit();
}
fn load_file(name: &str) -> Vec<u8> {
let mut file = File::open(name).unwrap();
let mut buffer = vec![];
file.read_to_end(&mut buffer).unwrap();
buffer
}

View file

@ -0,0 +1,231 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
extern crate euclid;
extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate winit;
#[path = "common/boilerplate.rs"]
mod boilerplate;
use crate::boilerplate::{Example, HandyDandyRectBuilder};
use euclid::SideOffsets2D;
use webrender::api::*;
use webrender::api::units::*;
use winit::dpi::LogicalPosition;
struct App {
cursor_position: WorldPoint,
}
impl Example for App {
fn render(
&mut self,
_api: &mut RenderApi,
builder: &mut DisplayListBuilder,
_txn: &mut Transaction,
_device_size: DeviceIntSize,
pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let root_space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
builder.push_simple_stacking_context(
LayoutPoint::zero(),
root_space_and_clip.spatial_id,
PrimitiveFlags::IS_BACKFACE_VISIBLE,
);
if true {
// scrolling and clips stuff
// let's make a scrollbox
let scrollbox = (0, 0).to(300, 400);
builder.push_simple_stacking_context(
LayoutPoint::new(10., 10.),
root_space_and_clip.spatial_id,
PrimitiveFlags::IS_BACKFACE_VISIBLE,
);
// set the scrolling clip
let space_and_clip1 = builder.define_scroll_frame(
&root_space_and_clip,
None,
(0, 0).by(1000, 1000),
scrollbox,
ScrollSensitivity::ScriptAndInputEvents,
LayoutVector2D::zero(),
);
// now put some content into it.
// start with a white background
let mut info = CommonItemProperties::new((0, 0).to(1000, 1000), space_and_clip1);
info.hit_info = Some((0, 1));
builder.push_rect(&info, info.clip_rect, ColorF::new(1.0, 1.0, 1.0, 1.0));
// let's make a 50x50 blue square as a visual reference
let mut info = CommonItemProperties::new((0, 0).to(50, 50), space_and_clip1);
info.hit_info = Some((0, 2));
builder.push_rect(&info, info.clip_rect, ColorF::new(0.0, 0.0, 1.0, 1.0));
// and a 50x50 green square next to it with an offset clip
// to see what that looks like
let mut info = CommonItemProperties::new(
(50, 0).to(100, 50).intersection(&(60, 10).to(110, 60)).unwrap(),
space_and_clip1,
);
info.hit_info = Some((0, 3));
builder.push_rect(&info, info.clip_rect, ColorF::new(0.0, 1.0, 0.0, 1.0));
// Below the above rectangles, set up a nested scrollbox. It's still in
// the same stacking context, so note that the rects passed in need to
// be relative to the stacking context.
let space_and_clip2 = builder.define_scroll_frame(
&space_and_clip1,
None,
(0, 100).to(300, 1000),
(0, 100).to(200, 300),
ScrollSensitivity::ScriptAndInputEvents,
LayoutVector2D::zero(),
);
// give it a giant gray background just to distinguish it and to easily
// visually identify the nested scrollbox
let mut info = CommonItemProperties::new(
(-1000, -1000).to(5000, 5000),
space_and_clip2,
);
info.hit_info = Some((0, 4));
builder.push_rect(&info, info.clip_rect, ColorF::new(0.5, 0.5, 0.5, 1.0));
// add a teal square to visualize the scrolling/clipping behaviour
// as you scroll the nested scrollbox
let mut info = CommonItemProperties::new((0, 200).to(50, 250), space_and_clip2);
info.hit_info = Some((0, 5));
builder.push_rect(&info, info.clip_rect, ColorF::new(0.0, 1.0, 1.0, 1.0));
// Add a sticky frame. It will "stick" twice while scrolling, once
// at a margin of 10px from the bottom, for 40 pixels of scrolling,
// and once at a margin of 10px from the top, for 60 pixels of
// scrolling.
let sticky_id = builder.define_sticky_frame(
space_and_clip2.spatial_id,
(50, 350).by(50, 50),
SideOffsets2D::new(Some(10.0), None, Some(10.0), None),
StickyOffsetBounds::new(-40.0, 60.0),
StickyOffsetBounds::new(0.0, 0.0),
LayoutVector2D::new(0.0, 0.0)
);
let mut info = CommonItemProperties::new(
(50, 350).by(50, 50),
SpaceAndClipInfo {
spatial_id: sticky_id,
clip_id: space_and_clip2.clip_id,
},
);
info.hit_info = Some((0, 6));
builder.push_rect(
&info,
info.clip_rect,
ColorF::new(0.5, 0.5, 1.0, 1.0),
);
// just for good measure add another teal square further down and to
// the right, which can be scrolled into view by the user
let mut info = CommonItemProperties::new(
(250, 350).to(300, 400),
space_and_clip2,
);
info.hit_info = Some((0, 7));
builder.push_rect(&info, info.clip_rect, ColorF::new(0.0, 1.0, 1.0, 1.0));
builder.pop_stacking_context();
}
builder.pop_stacking_context();
}
fn on_event(&mut self, event: winit::WindowEvent, api: &mut RenderApi, document_id: DocumentId) -> bool {
let mut txn = Transaction::new();
match event {
winit::WindowEvent::KeyboardInput {
input: winit::KeyboardInput {
state: winit::ElementState::Pressed,
virtual_keycode: Some(key),
..
},
..
} => {
let offset = match key {
winit::VirtualKeyCode::Down => Some((0.0, -10.0)),
winit::VirtualKeyCode::Up => Some((0.0, 10.0)),
winit::VirtualKeyCode::Right => Some((-10.0, 0.0)),
winit::VirtualKeyCode::Left => Some((10.0, 0.0)),
_ => None,
};
let zoom = match key {
winit::VirtualKeyCode::Key0 => Some(1.0),
winit::VirtualKeyCode::Minus => Some(0.8),
winit::VirtualKeyCode::Equals => Some(1.25),
_ => None,
};
if let Some(offset) = offset {
txn.scroll(
ScrollLocation::Delta(LayoutVector2D::new(offset.0, offset.1)),
self.cursor_position,
);
txn.generate_frame();
}
if let Some(zoom) = zoom {
txn.set_pinch_zoom(ZoomFactor::new(zoom));
txn.generate_frame();
}
}
winit::WindowEvent::CursorMoved { position: LogicalPosition { x, y }, .. } => {
self.cursor_position = WorldPoint::new(x as f32, y as f32);
}
winit::WindowEvent::MouseWheel { delta, .. } => {
const LINE_HEIGHT: f32 = 38.0;
let (dx, dy) = match delta {
winit::MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
winit::MouseScrollDelta::PixelDelta(pos) => (pos.x as f32, pos.y as f32),
};
txn.scroll(
ScrollLocation::Delta(LayoutVector2D::new(dx, dy)),
self.cursor_position,
);
txn.generate_frame();
}
winit::WindowEvent::MouseInput { .. } => {
let results = api.hit_test(
document_id,
None,
self.cursor_position,
HitTestFlags::FIND_ALL
);
println!("Hit test results:");
for item in &results.items {
println!("{:?}", item);
}
println!("");
}
_ => (),
}
api.send_transaction(document_id, txn);
false
}
}
fn main() {
let mut app = App {
cursor_position: WorldPoint::zero(),
};
boilerplate::main_wrapper(&mut app, None);
}

View file

@ -0,0 +1,322 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate winit;
#[path = "common/boilerplate.rs"]
mod boilerplate;
use crate::boilerplate::{Example, HandyDandyRectBuilder};
use gleam::gl;
use std::mem;
use webrender::api::*;
use webrender::api::units::*;
struct ImageGenerator {
patterns: [[u8; 3]; 6],
next_pattern: usize,
current_image: Vec<u8>,
}
impl ImageGenerator {
fn new() -> Self {
ImageGenerator {
next_pattern: 0,
patterns: [
[1, 0, 0],
[0, 1, 0],
[0, 0, 1],
[1, 1, 0],
[0, 1, 1],
[1, 0, 1],
],
current_image: Vec::new(),
}
}
fn generate_image(&mut self, size: i32) {
let pattern = &self.patterns[self.next_pattern];
self.current_image.clear();
for y in 0 .. size {
for x in 0 .. size {
let lum = 255 * (1 - (((x & 8) == 0) ^ ((y & 8) == 0)) as u8);
self.current_image.extend_from_slice(&[
lum * pattern[0],
lum * pattern[1],
lum * pattern[2],
0xff,
]);
}
}
self.next_pattern = (self.next_pattern + 1) % self.patterns.len();
}
fn take(&mut self) -> Vec<u8> {
mem::replace(&mut self.current_image, Vec::new())
}
}
impl ExternalImageHandler for ImageGenerator {
fn lock(
&mut self,
_key: ExternalImageId,
channel_index: u8,
_rendering: ImageRendering
) -> ExternalImage {
self.generate_image(channel_index as i32);
ExternalImage {
uv: TexelRect::new(0.0, 0.0, 1.0, 1.0),
source: ExternalImageSource::RawData(&self.current_image),
}
}
fn unlock(&mut self, _key: ExternalImageId, _channel_index: u8) {}
}
struct App {
stress_keys: Vec<ImageKey>,
image_key: Option<ImageKey>,
image_generator: ImageGenerator,
swap_keys: Vec<ImageKey>,
swap_index: usize,
}
impl Example for App {
fn render(
&mut self,
api: &mut RenderApi,
builder: &mut DisplayListBuilder,
txn: &mut Transaction,
_device_size: DeviceIntSize,
pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let bounds = (0, 0).to(512, 512);
let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
builder.push_simple_stacking_context(
bounds.origin,
space_and_clip.spatial_id,
PrimitiveFlags::IS_BACKFACE_VISIBLE,
);
let x0 = 50.0;
let y0 = 50.0;
let image_size = LayoutSize::new(4.0, 4.0);
if self.swap_keys.is_empty() {
let key0 = api.generate_image_key();
let key1 = api.generate_image_key();
self.image_generator.generate_image(128);
txn.add_image(
key0,
ImageDescriptor::new(128, 128, ImageFormat::BGRA8, ImageDescriptorFlags::IS_OPAQUE),
ImageData::new(self.image_generator.take()),
None,
);
self.image_generator.generate_image(128);
txn.add_image(
key1,
ImageDescriptor::new(128, 128, ImageFormat::BGRA8, ImageDescriptorFlags::IS_OPAQUE),
ImageData::new(self.image_generator.take()),
None,
);
self.swap_keys.push(key0);
self.swap_keys.push(key1);
}
for (i, key) in self.stress_keys.iter().enumerate() {
let x = (i % 128) as f32;
let y = (i / 128) as f32;
let info = CommonItemProperties::new(
LayoutRect::new(
LayoutPoint::new(x0 + image_size.width * x, y0 + image_size.height * y),
image_size,
),
space_and_clip,
);
builder.push_image(
&info,
bounds,
ImageRendering::Auto,
AlphaType::PremultipliedAlpha,
*key,
ColorF::WHITE,
);
}
if let Some(image_key) = self.image_key {
let image_size = LayoutSize::new(100.0, 100.0);
let info = CommonItemProperties::new(
LayoutRect::new(LayoutPoint::new(100.0, 100.0), image_size),
space_and_clip,
);
builder.push_image(
&info,
bounds,
ImageRendering::Auto,
AlphaType::PremultipliedAlpha,
image_key,
ColorF::WHITE,
);
}
let swap_key = self.swap_keys[self.swap_index];
let image_size = LayoutSize::new(64.0, 64.0);
let info = CommonItemProperties::new(
LayoutRect::new(LayoutPoint::new(100.0, 400.0), image_size),
space_and_clip,
);
builder.push_image(
&info,
bounds,
ImageRendering::Auto,
AlphaType::PremultipliedAlpha,
swap_key,
ColorF::WHITE,
);
self.swap_index = 1 - self.swap_index;
builder.pop_stacking_context();
}
fn on_event(
&mut self,
event: winit::WindowEvent,
api: &mut RenderApi,
document_id: DocumentId,
) -> bool {
match event {
winit::WindowEvent::KeyboardInput {
input: winit::KeyboardInput {
state: winit::ElementState::Pressed,
virtual_keycode: Some(key),
..
},
..
} => {
let mut txn = Transaction::new();
match key {
winit::VirtualKeyCode::S => {
self.stress_keys.clear();
for _ in 0 .. 16 {
for _ in 0 .. 16 {
let size = 4;
let image_key = api.generate_image_key();
self.image_generator.generate_image(size);
txn.add_image(
image_key,
ImageDescriptor::new(
size,
size,
ImageFormat::BGRA8,
ImageDescriptorFlags::IS_OPAQUE,
),
ImageData::new(self.image_generator.take()),
None,
);
self.stress_keys.push(image_key);
}
}
}
winit::VirtualKeyCode::D => if let Some(image_key) = self.image_key.take() {
txn.delete_image(image_key);
},
winit::VirtualKeyCode::U => if let Some(image_key) = self.image_key {
let size = 128;
self.image_generator.generate_image(size);
txn.update_image(
image_key,
ImageDescriptor::new(size, size, ImageFormat::BGRA8, ImageDescriptorFlags::IS_OPAQUE),
ImageData::new(self.image_generator.take()),
&DirtyRect::All,
);
},
winit::VirtualKeyCode::E => {
if let Some(image_key) = self.image_key.take() {
txn.delete_image(image_key);
}
let size = 32;
let image_key = api.generate_image_key();
let image_data = ExternalImageData {
id: ExternalImageId(0),
channel_index: size as u8,
image_type: ExternalImageType::Buffer,
};
txn.add_image(
image_key,
ImageDescriptor::new(size, size, ImageFormat::BGRA8, ImageDescriptorFlags::IS_OPAQUE),
ImageData::External(image_data),
None,
);
self.image_key = Some(image_key);
}
winit::VirtualKeyCode::R => {
if let Some(image_key) = self.image_key.take() {
txn.delete_image(image_key);
}
let image_key = api.generate_image_key();
let size = 32;
self.image_generator.generate_image(size);
txn.add_image(
image_key,
ImageDescriptor::new(size, size, ImageFormat::BGRA8, ImageDescriptorFlags::IS_OPAQUE),
ImageData::new(self.image_generator.take()),
None,
);
self.image_key = Some(image_key);
}
_ => {}
}
api.send_transaction(document_id, txn);
return true;
}
_ => {}
}
false
}
fn get_image_handlers(
&mut self,
_gl: &dyn gl::Gl,
) -> (Option<Box<dyn ExternalImageHandler>>,
Option<Box<dyn OutputImageHandler>>) {
(Some(Box::new(ImageGenerator::new())), None)
}
}
fn main() {
let mut app = App {
image_key: None,
stress_keys: Vec::new(),
image_generator: ImageGenerator::new(),
swap_keys: Vec::new(),
swap_index: 0,
};
boilerplate::main_wrapper(&mut app, None);
}

226
third_party/webrender/examples/yuv.rs vendored Normal file
View file

@ -0,0 +1,226 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate winit;
#[path = "common/boilerplate.rs"]
mod boilerplate;
use crate::boilerplate::Example;
use gleam::gl;
use webrender::api::*;
use webrender::api::units::*;
fn init_gl_texture(
id: gl::GLuint,
internal: gl::GLenum,
external: gl::GLenum,
bytes: &[u8],
gl: &dyn gl::Gl,
) {
gl.bind_texture(gl::TEXTURE_2D, id);
gl.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as gl::GLint);
gl.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as gl::GLint);
gl.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as gl::GLint);
gl.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as gl::GLint);
gl.tex_image_2d(
gl::TEXTURE_2D,
0,
internal as gl::GLint,
100,
100,
0,
external,
gl::UNSIGNED_BYTE,
Some(bytes),
);
gl.bind_texture(gl::TEXTURE_2D, 0);
}
struct YuvImageProvider {
texture_ids: Vec<gl::GLuint>,
}
impl YuvImageProvider {
fn new(gl: &dyn gl::Gl) -> Self {
let texture_ids = gl.gen_textures(4);
init_gl_texture(texture_ids[0], gl::RED, gl::RED, &[127; 100 * 100], gl);
init_gl_texture(texture_ids[1], gl::RG8, gl::RG, &[0; 100 * 100 * 2], gl);
init_gl_texture(texture_ids[2], gl::RED, gl::RED, &[127; 100 * 100], gl);
init_gl_texture(texture_ids[3], gl::RED, gl::RED, &[127; 100 * 100], gl);
YuvImageProvider {
texture_ids
}
}
}
impl ExternalImageHandler for YuvImageProvider {
fn lock(
&mut self,
key: ExternalImageId,
_channel_index: u8,
_rendering: ImageRendering
) -> ExternalImage {
let id = self.texture_ids[key.0 as usize];
ExternalImage {
uv: TexelRect::new(0.0, 0.0, 1.0, 1.0),
source: ExternalImageSource::NativeTexture(id),
}
}
fn unlock(&mut self, _key: ExternalImageId, _channel_index: u8) {
}
}
struct App {
texture_id: gl::GLuint,
current_value: u8,
}
impl Example for App {
fn render(
&mut self,
api: &mut RenderApi,
builder: &mut DisplayListBuilder,
txn: &mut Transaction,
_device_size: DeviceIntSize,
pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
builder.push_simple_stacking_context(
bounds.origin,
space_and_clip.spatial_id,
PrimitiveFlags::IS_BACKFACE_VISIBLE,
);
let yuv_chanel1 = api.generate_image_key();
let yuv_chanel2 = api.generate_image_key();
let yuv_chanel2_1 = api.generate_image_key();
let yuv_chanel3 = api.generate_image_key();
txn.add_image(
yuv_chanel1,
ImageDescriptor::new(100, 100, ImageFormat::R8, ImageDescriptorFlags::IS_OPAQUE),
ImageData::External(ExternalImageData {
id: ExternalImageId(0),
channel_index: 0,
image_type: ExternalImageType::TextureHandle(
TextureTarget::Default,
),
}),
None,
);
txn.add_image(
yuv_chanel2,
ImageDescriptor::new(100, 100, ImageFormat::RG8, ImageDescriptorFlags::IS_OPAQUE),
ImageData::External(ExternalImageData {
id: ExternalImageId(1),
channel_index: 0,
image_type: ExternalImageType::TextureHandle(
TextureTarget::Default,
),
}),
None,
);
txn.add_image(
yuv_chanel2_1,
ImageDescriptor::new(100, 100, ImageFormat::R8, ImageDescriptorFlags::IS_OPAQUE),
ImageData::External(ExternalImageData {
id: ExternalImageId(2),
channel_index: 0,
image_type: ExternalImageType::TextureHandle(
TextureTarget::Default,
),
}),
None,
);
txn.add_image(
yuv_chanel3,
ImageDescriptor::new(100, 100, ImageFormat::R8, ImageDescriptorFlags::IS_OPAQUE),
ImageData::External(ExternalImageData {
id: ExternalImageId(3),
channel_index: 0,
image_type: ExternalImageType::TextureHandle(
TextureTarget::Default,
),
}),
None,
);
let info = CommonItemProperties::new(
LayoutRect::new(LayoutPoint::new(100.0, 0.0), LayoutSize::new(100.0, 100.0)),
space_and_clip,
);
builder.push_yuv_image(
&info,
bounds,
YuvData::NV12(yuv_chanel1, yuv_chanel2),
ColorDepth::Color8,
YuvColorSpace::Rec601,
ColorRange::Limited,
ImageRendering::Auto,
);
let info = CommonItemProperties::new(
LayoutRect::new(LayoutPoint::new(300.0, 0.0), LayoutSize::new(100.0, 100.0)),
space_and_clip,
);
builder.push_yuv_image(
&info,
bounds,
YuvData::PlanarYCbCr(yuv_chanel1, yuv_chanel2_1, yuv_chanel3),
ColorDepth::Color8,
YuvColorSpace::Rec601,
ColorRange::Limited,
ImageRendering::Auto,
);
builder.pop_stacking_context();
}
fn on_event(
&mut self,
_event: winit::WindowEvent,
_api: &mut RenderApi,
_document_id: DocumentId,
) -> bool {
false
}
fn get_image_handlers(
&mut self,
gl: &dyn gl::Gl,
) -> (Option<Box<dyn ExternalImageHandler>>,
Option<Box<dyn OutputImageHandler>>) {
let provider = YuvImageProvider::new(gl);
self.texture_id = provider.texture_ids[0];
(Some(Box::new(provider)), None)
}
fn draw_custom(&mut self, gl: &dyn gl::Gl) {
init_gl_texture(self.texture_id, gl::RED, gl::RED, &[self.current_value; 100 * 100], gl);
self.current_value = self.current_value.wrapping_add(1);
}
}
fn main() {
let mut app = App {
texture_id: 0,
current_value: 0,
};
let opts = webrender::RendererOptions {
debug_flags: webrender::DebugFlags::NEW_FRAME_INDICATOR | webrender::DebugFlags::NEW_SCENE_INDICATOR,
..Default::default()
};
boilerplate::main_wrapper(&mut app, Some(opts));
}

View file

@ -0,0 +1,11 @@
[package]
name = "glsl-to-cxx"
version = "0.1.0"
license = "MPL-2.0"
authors = ["The Mozilla Project Developers"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
glsl = "4.0"

View file

@ -0,0 +1,3 @@
A GLSL to C++ translator.
Translates GLSL to vectorized C++. Intended for use with WebRender software backend.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,8 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use glsl_to_cxx::translate;
fn main() {
println!("{}", translate(&mut std::env::args()));
}

View file

@ -0,0 +1,226 @@
From 34d968adeda2e06b057a13d14a88df5766b38eda Mon Sep 17 00:00:00 2001
From: Josh Matthews <josh@joshmatthews.net>
Date: Mon, 6 Jul 2020 14:37:42 -0400
Subject: [PATCH] Add signal handler to catch segfault in build script.
---
Cargo.lock | 11 +++++
webrender/Cargo.toml | 5 ++
webrender/backtrace.rs | 103 +++++++++++++++++++++++++++++++++++++++++
webrender/build.rs | 30 ++++++++++++
4 files changed, 149 insertions(+)
create mode 100644 webrender/backtrace.rs
diff --git a/Cargo.lock b/Cargo.lock
index afdd336ae..cf91162d5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1477,6 +1477,14 @@ dependencies = [
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "sig"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "slab"
version = "0.4.2"
@@ -1750,6 +1758,7 @@ dependencies = [
name = "webrender"
version = "0.61.0"
dependencies = [
+ "backtrace 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)",
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
"bincode 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1780,6 +1789,7 @@ dependencies = [
"ron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)",
+ "sig 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"svg_fmt 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2178,6 +2188,7 @@ dependencies = [
"checksum servo-freetype-sys 4.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2c4ccb6d0d32d277d3ef7dea86203d8210945eb7a45fba89dd445b3595dd0dfc"
"checksum sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
"checksum shared_library 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11"
+"checksum sig 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6567e29578f9bfade6a5d94a32b9a4256348358d2a3f448cab0021f9a02614a2"
"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
"checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6"
"checksum smallvec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05720e22615919e4734f6a99ceae50d00226c3c5aca406e102ebc33298214e0a"
diff --git a/webrender/Cargo.toml b/webrender/Cargo.toml
index dcf26d913..f7679da57 100644
--- a/webrender/Cargo.toml
+++ b/webrender/Cargo.toml
@@ -59,6 +59,11 @@ tracy-rs = { version = "0.1" }
mozangle = "0.3.1"
rand = "0.4"
+[target.'cfg(any(target_os = "macos", target_os = "linux"))'.build-dependencies]
+backtrace = "0.3"
+sig = "1.0"
+libc = "0.2"
+
[target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies]
freetype = { version = "0.4", default-features = false }
libc = "0.2"
diff --git a/webrender/backtrace.rs b/webrender/backtrace.rs
new file mode 100644
index 000000000..aa6bb6b32
--- /dev/null
+++ b/webrender/backtrace.rs
@@ -0,0 +1,103 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Similar to `println!("{:?}", Backtrace::new())`, but doesnt allocate.
+//!
+//! Seems to fix some deadlocks: https://github.com/servo/servo/issues/24881
+//!
+//! FIXME: if/when a future version of the `backtrace` crate has
+//! https://github.com/rust-lang/backtrace-rs/pull/265, use that instead.
+
+use std::fmt::{self, Write};
+use backtrace::{BytesOrWideString, PrintFmt};
+
+#[inline(never)]
+pub(crate) fn print(w: &mut dyn std::io::Write) -> Result<(), std::io::Error> {
+ write!(w, "{:?}", Print {
+ print_fn_address: print as usize,
+ })
+}
+
+struct Print {
+ print_fn_address: usize,
+}
+
+impl fmt::Debug for Print {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ // Safety: were in a signal handler that is about to call `libc::_exit`.
+ // Potential data races from using `*_unsynchronized` functions are perhaps
+ // less bad than potential deadlocks?
+ unsafe {
+ let mut print_fn_frame = 0;
+ let mut frame_count = 0;
+ backtrace::trace_unsynchronized(|frame| {
+ let found = frame.symbol_address() as usize == self.print_fn_address;
+ if found {
+ print_fn_frame = frame_count;
+ }
+ frame_count += 1;
+ !found
+ });
+
+ let mode = PrintFmt::Short;
+ let mut p = print_path;
+ let mut f = backtrace::BacktraceFmt::new(fmt, mode, &mut p);
+ f.add_context()?;
+ let mut result = Ok(());
+ let mut frame_count = 0;
+ backtrace::trace_unsynchronized(|frame| {
+ let skip = frame_count < print_fn_frame;
+ frame_count += 1;
+ if skip {
+ return true
+ }
+
+ let mut frame_fmt = f.frame();
+ let mut any_symbol = false;
+ backtrace::resolve_frame_unsynchronized(frame, |symbol| {
+ any_symbol = true;
+ if let Err(e) = frame_fmt.symbol(frame, symbol) {
+ result = Err(e)
+ }
+ });
+ if !any_symbol {
+ if let Err(e) = frame_fmt.print_raw(frame.ip(), None, None, None) {
+ result = Err(e)
+ }
+ }
+ result.is_ok()
+ });
+ result?;
+ f.finish()
+ }
+ }
+}
+
+fn print_path(fmt: &mut fmt::Formatter, path: BytesOrWideString) -> fmt::Result {
+ match path {
+ BytesOrWideString::Bytes(mut bytes) => {
+ loop {
+ match std::str::from_utf8(bytes) {
+ Ok(s) => {
+ fmt.write_str(s)?;
+ break;
+ }
+ Err(err) => {
+ fmt.write_char(std::char::REPLACEMENT_CHARACTER)?;
+ match err.error_len() {
+ Some(len) => bytes = &bytes[err.valid_up_to() + len..],
+ None => break,
+ }
+ }
+ }
+ }
+ }
+ BytesOrWideString::Wide(wide) => {
+ for c in std::char::decode_utf16(wide.iter().cloned()) {
+ fmt.write_char(c.unwrap_or(std::char::REPLACEMENT_CHARACTER))?
+ }
+ }
+ }
+ Ok(())
+}
diff --git a/webrender/build.rs b/webrender/build.rs
index 3521d1342..36a7f17a8 100644
--- a/webrender/build.rs
+++ b/webrender/build.rs
@@ -244,7 +244,37 @@ fn write_optimized_shaders(shader_dir: &Path, shader_file: &mut File, out_dir: &
Ok(())
}
+#[cfg(any(target_os = "macos", target_os = "linux"))]
+mod backtrace;
+
+#[cfg(any(target_os = "macos", target_os = "linux"))]
+extern "C" fn handler(sig: i32) {
+ use std::sync::atomic;
+ static BEEN_HERE_BEFORE: atomic::AtomicBool = atomic::AtomicBool::new(false);
+ if !BEEN_HERE_BEFORE.swap(true, atomic::Ordering::SeqCst) {
+ let stdout = std::io::stdout();
+ let mut stdout = stdout.lock();
+ let _ = write!(&mut stdout, "Stack trace");
+ if let Some(name) = std::thread::current().name() {
+ let _ = write!(&mut stdout, " for thread \"{}\"", name);
+ }
+ let _ = write!(&mut stdout, "\n");
+ let _ = backtrace::print(&mut stdout);
+ }
+ unsafe {
+ libc::_exit(sig);
+ }
+}
+
fn main() -> Result<(), std::io::Error> {
+ #[cfg(any(target_os = "macos", target_os = "linux"))]
+ {
+ sig::signal!(sig::ffi::Sig::SEGV, handler); // handle segfaults
+ sig::signal!(sig::ffi::Sig::ILL, handler); // handle stack overflow and unsupported CPUs
+ sig::signal!(sig::ffi::Sig::IOT, handler); // handle double panics
+ sig::signal!(sig::ffi::Sig::BUS, handler); // handle invalid memory access
+ }
+
let out_dir = env::var("OUT_DIR").unwrap_or("out".to_owned());
let shaders_file_path = Path::new(&out_dir).join("shaders.rs");
--
2.39.2

View file

@ -0,0 +1,174 @@
From 299c4db222eb9f0acd9623b66b8f3d0a8a8f77f2 Mon Sep 17 00:00:00 2001
From: Jeff Muizelaar <jmuizelaar@mozilla.com>
Date: Fri, 19 Jun 2020 04:10:02 +0000
Subject: [PATCH 2/6] Bug 1646741 - Update gleam to 0.12. r=kvark
For stride calculation and SSBOs
Differential Revision: https://phabricator.services.mozilla.com/D80191
[ghsync] From https://hg.mozilla.org/mozilla-central/rev/ef8485a16d099e24f4832178664c5a93a28396ec
---
Cargo.lock | 16 ++++++++--------
direct-composition/Cargo.toml | 2 +-
example-compositor/compositor/Cargo.toml | 2 +-
examples/Cargo.toml | 2 +-
swgl/Cargo.toml | 2 +-
webrender/Cargo.toml | 2 +-
wrench/Cargo.toml | 2 +-
7 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index cf91162d5..3eb484f26 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -257,7 +257,7 @@ name = "compositor"
version = "0.1.0"
dependencies = [
"compositor-windows 0.1.0",
- "gleam 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"webrender 0.61.0",
]
@@ -435,7 +435,7 @@ name = "direct-composition"
version = "0.1.0"
dependencies = [
"euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "gleam 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"mozangle 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"webrender 0.61.0",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -617,7 +617,7 @@ dependencies = [
[[package]]
name = "gleam"
-version = "0.11.0"
+version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"gl_generator 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1545,7 +1545,7 @@ name = "swgl"
version = "0.1.0"
dependencies = [
"cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)",
- "gleam 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glsl-to-cxx 0.1.0",
"webrender_build 0.0.1",
]
@@ -1773,7 +1773,7 @@ dependencies = [
"euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)",
"freetype 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "gleam 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glslopt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"image 0.23.3 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1808,7 +1808,7 @@ dependencies = [
"core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "gleam 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glutin 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"webrender 0.61.0",
@@ -1938,7 +1938,7 @@ dependencies = [
"env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)",
"font-loader 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "gleam 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glutin 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)",
"image 0.23.3 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2087,7 +2087,7 @@ dependencies = [
"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
"checksum gl_generator 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ca98bbde17256e02d17336a6bdb5a50f7d0ccacee502e191d3e3d0ec2f96f84a"
"checksum gl_generator 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d"
-"checksum gleam 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9a13b5bb12ab457c15400b43cbba5971df5c1898b6a9c30cc8c52cb01baa112"
+"checksum gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8d023b0b00c16960f0f82816f2f546dabe937e75b25c7d6ce09a63e6a52d71e"
"checksum gleam 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "cae10d7c99d0e77b4766e850a60898a17c1abaf01075531f1066f03dc7dc5fc5"
"checksum glsl 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "766443890761b3c4edcce86cafaac97971b200662fbdd0446eb7c6b99b4401ea"
"checksum glslopt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f22b383fcf6f85c4a268af39a0758ec40970e5f9f8fe9809e4415d48409b8379"
diff --git a/direct-composition/Cargo.toml b/direct-composition/Cargo.toml
index d099402d8..3506aec02 100644
--- a/direct-composition/Cargo.toml
+++ b/direct-composition/Cargo.toml
@@ -7,7 +7,7 @@ edition = "2018"
[target.'cfg(windows)'.dependencies]
euclid = "0.20"
-gleam = "0.11"
+gleam = "0.12"
mozangle = {version = "0.3.1", features = ["egl"]}
webrender = {path = "../webrender"}
winapi = {version = "0.3", features = ["winerror", "d3d11", "dcomp"]}
diff --git a/example-compositor/compositor/Cargo.toml b/example-compositor/compositor/Cargo.toml
index ce4d61928..d505e9ad2 100644
--- a/example-compositor/compositor/Cargo.toml
+++ b/example-compositor/compositor/Cargo.toml
@@ -7,7 +7,7 @@ license = "MPL-2.0"
[dependencies]
webrender = { path = "../../webrender" }
-gleam = "0.11.0"
+gleam = "0.12.0"
[target.'cfg(windows)'.dependencies]
compositor-windows = { path = "../compositor-windows" }
diff --git a/examples/Cargo.toml b/examples/Cargo.toml
index 31c695f98..09e658ca2 100644
--- a/examples/Cargo.toml
+++ b/examples/Cargo.toml
@@ -61,7 +61,7 @@ debug = ["webrender/capture", "webrender/debugger", "webrender/profiler"]
app_units = "0.7"
env_logger = "0.5"
euclid = "0.20"
-gleam = "0.11"
+gleam = "0.12"
glutin = "0.21"
rayon = "1"
webrender = { path = "../webrender" }
diff --git a/swgl/Cargo.toml b/swgl/Cargo.toml
index 3d57edbab..bc5a04b0a 100644
--- a/swgl/Cargo.toml
+++ b/swgl/Cargo.toml
@@ -12,4 +12,4 @@ glsl-to-cxx = { path = "../glsl-to-cxx" }
webrender_build = { path = "../webrender_build" }
[dependencies]
-gleam = "0.11.0"
+gleam = "0.12.0"
diff --git a/webrender/Cargo.toml b/webrender/Cargo.toml
index f7679da57..2b0ab14fb 100644
--- a/webrender/Cargo.toml
+++ b/webrender/Cargo.toml
@@ -34,7 +34,7 @@ cfg-if = "0.1.2"
cstr = "0.1.2"
euclid = { version = "0.20.0", features = ["serde"] }
fxhash = "0.2.1"
-gleam = "0.11.0"
+gleam = "0.12.0"
image_loader = { optional = true, version = "0.23", package = "image", default-features = false, features = ["png"] }
lazy_static = "1"
log = "0.4"
diff --git a/wrench/Cargo.toml b/wrench/Cargo.toml
index 988e2537a..4ba95e4c4 100644
--- a/wrench/Cargo.toml
+++ b/wrench/Cargo.toml
@@ -12,7 +12,7 @@ bincode = "1.0"
byteorder = "1.0"
env_logger = { version = "0.5", optional = true }
euclid = "0.20"
-gleam = "0.11"
+gleam = "0.12"
glutin = "0.21"
app_units = "0.7"
clap = { version = "2", features = ["yaml"] }
--
2.39.2

View file

@ -0,0 +1,107 @@
From 3767bd8938b5b849bc23bb7ac490cb0c27655560 Mon Sep 17 00:00:00 2001
From: Jeff Muizelaar <jmuizelaar@mozilla.com>
Date: Sat, 11 Jul 2020 09:42:33 +0000
Subject: [PATCH 3/6] Bug 1651889. Update to gleam 0.12.1. r=kvark
This should fix a crash caused by an unexpected pixel type.
Differential Revision: https://phabricator.services.mozilla.com/D83167
[ghsync] From https://hg.mozilla.org/mozilla-central/rev/b850773b54e129888b8fb2f1e3bc68f528aeccbf
---
Cargo.lock | 16 ++++++++--------
webrender/Cargo.toml | 2 +-
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 3eb484f26..24f92084c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -257,7 +257,7 @@ name = "compositor"
version = "0.1.0"
dependencies = [
"compositor-windows 0.1.0",
- "gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gleam 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
"webrender 0.61.0",
]
@@ -435,7 +435,7 @@ name = "direct-composition"
version = "0.1.0"
dependencies = [
"euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gleam 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
"mozangle 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"webrender 0.61.0",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -617,7 +617,7 @@ dependencies = [
[[package]]
name = "gleam"
-version = "0.12.0"
+version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"gl_generator 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1545,7 +1545,7 @@ name = "swgl"
version = "0.1.0"
dependencies = [
"cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)",
- "gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gleam 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glsl-to-cxx 0.1.0",
"webrender_build 0.0.1",
]
@@ -1773,7 +1773,7 @@ dependencies = [
"euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)",
"freetype 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gleam 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glslopt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"image 0.23.3 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1808,7 +1808,7 @@ dependencies = [
"core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gleam 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glutin 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"webrender 0.61.0",
@@ -1938,7 +1938,7 @@ dependencies = [
"env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)",
"font-loader 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gleam 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glutin 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)",
"image 0.23.3 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2087,7 +2087,7 @@ dependencies = [
"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
"checksum gl_generator 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ca98bbde17256e02d17336a6bdb5a50f7d0ccacee502e191d3e3d0ec2f96f84a"
"checksum gl_generator 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d"
-"checksum gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8d023b0b00c16960f0f82816f2f546dabe937e75b25c7d6ce09a63e6a52d71e"
+"checksum gleam 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3fdef5b9df6d3a261b80a5ac55e13bf93945725df2463c1b0a2e5a527dce0d37"
"checksum gleam 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "cae10d7c99d0e77b4766e850a60898a17c1abaf01075531f1066f03dc7dc5fc5"
"checksum glsl 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "766443890761b3c4edcce86cafaac97971b200662fbdd0446eb7c6b99b4401ea"
"checksum glslopt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f22b383fcf6f85c4a268af39a0758ec40970e5f9f8fe9809e4415d48409b8379"
diff --git a/webrender/Cargo.toml b/webrender/Cargo.toml
index 2b0ab14fb..3fa6630bd 100644
--- a/webrender/Cargo.toml
+++ b/webrender/Cargo.toml
@@ -34,7 +34,7 @@ cfg-if = "0.1.2"
cstr = "0.1.2"
euclid = { version = "0.20.0", features = ["serde"] }
fxhash = "0.2.1"
-gleam = "0.12.0"
+gleam = "0.12.1"
image_loader = { optional = true, version = "0.23", package = "image", default-features = false, features = ["png"] }
lazy_static = "1"
log = "0.4"
--
2.39.2

View file

@ -0,0 +1,328 @@
From 920168aff79a7cf52980b0c90965a591f2f4204a Mon Sep 17 00:00:00 2001
From: Jeff Muizelaar <jmuizelaar@mozilla.com>
Date: Fri, 24 Jul 2020 09:54:10 +0000
Subject: [PATCH 4/6] Bug 1654699. Update core-foundation/core-graphics.
r=kvark,keeler,chunmin
This includes updates to authenticator, cubeb-coreaudio,
metal, gfx-backend-vulkan, gfx-backend-metal, freetype
libloading is duplicated because of ash
Differential Revision: https://phabricator.services.mozilla.com/D84688
[ghsync] From https://hg.mozilla.org/mozilla-central/rev/45fc4a780b2b4a9e047eceba73b39b988f719c58
---
Cargo.lock | 110 +++++++++++++++++++++++++--------------
webrender/Cargo.toml | 10 ++--
webrender_api/Cargo.toml | 4 +-
wrench/Cargo.toml | 6 +--
4 files changed, 80 insertions(+), 50 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 24f92084c..617092292 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -286,6 +286,15 @@ dependencies = [
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
]
+[[package]]
+name = "core-foundation"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "core-foundation-sys 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
[[package]]
name = "core-foundation-sys"
version = "0.6.2"
@@ -296,6 +305,11 @@ name = "core-foundation-sys"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
[[package]]
name = "core-graphics"
version = "0.17.3"
@@ -309,22 +323,34 @@ dependencies = [
[[package]]
name = "core-graphics"
-version = "0.19.0"
+version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "core-foundation 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "core-graphics-types 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "core-graphics-types"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "core-foundation 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "core-text"
-version = "15.0.0"
+version = "19.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "core-graphics 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "core-foundation 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "core-graphics 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
"foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -510,13 +536,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "font-loader"
-version = "0.9.0"
+version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "core-text 15.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "core-foundation 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "core-text 19.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
- "servo-fontconfig 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "servo-fontconfig 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -535,11 +561,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "freetype"
-version = "0.4.1"
+version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
+ "freetype-sys 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
- "servo-freetype-sys 4.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "freetype-sys"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cmake 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1431,29 +1467,20 @@ dependencies = [
[[package]]
name = "servo-fontconfig"
-version = "0.4.0"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
- "servo-fontconfig-sys 4.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "servo-fontconfig-sys 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "servo-fontconfig-sys"
-version = "4.0.9"
+version = "5.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"expat-sys 2.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
- "servo-freetype-sys 4.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "servo-freetype-sys"
-version = "4.0.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "cmake 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
+ "freetype-sys 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1765,13 +1792,13 @@ dependencies = [
"build-parallel 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "core-graphics 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "core-text 15.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "core-foundation 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "core-graphics 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "core-text 19.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cstr 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"dwrote 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "freetype 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "freetype 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gleam 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glslopt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1822,8 +1849,8 @@ dependencies = [
"app_units 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "core-graphics 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "core-foundation 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "core-graphics 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
"derive_more 0.99.5 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)",
"malloc_size_of_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1931,13 +1958,13 @@ dependencies = [
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "core-graphics 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "core-foundation 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "core-graphics 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
"dwrote 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)",
"euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "font-loader 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "font-loader 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gleam 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glutin 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)",
"image 0.23.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2051,11 +2078,14 @@ dependencies = [
"checksum cocoa 0.18.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1706996401131526e36b3b49f0c4d912639ce110996f3ca144d78946727bce54"
"checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d"
"checksum core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171"
+"checksum core-foundation 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3b5ed8e7e76c45974e15e41bfa8d5b0483cd90191639e01d8f5f1e606299d3fb"
"checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b"
"checksum core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
+"checksum core-foundation-sys 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9a21fa21941700a3cd8fcb4091f361a6a712fac632f85d9f487cc892045d55c6"
"checksum core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)" = "56790968ab1c8a1202a102e6de05fc6e1ec87da99e4e93e9a7d13efbfc1e95a9"
-"checksum core-graphics 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "59e78b2e0aaf43f08e7ae0d6bc96895ef72ff0921c7d4ff4762201b2dba376dd"
-"checksum core-text 15.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "131b3fd1f8bd5db9f2b398fa4fdb6008c64afc04d447c306ac2c7e98fba2a61d"
+"checksum core-graphics 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f6082396a349fa49674ba1bda4077332a18bf150e8fa75745ece07085e29a113"
+"checksum core-graphics-types 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e92f5d519093a4178296707dbaa3880eae85a5ef5386675f361a1cf25376e93c"
+"checksum core-text 19.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "04dfae50af11e72657fe7174cddb1ecddc5398037f7f6f39533ad69207c9a4e2"
"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
"checksum crossbeam 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "bd66663db5a988098a89599d4857919b3acf7f61402e61365acfd3919857b9be"
"checksum crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
@@ -2075,10 +2105,11 @@ dependencies = [
"checksum euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c6a5b0c779cd0b744c73a1d2083faf181080d696903cdad99a3b03d015d7030"
"checksum expat-sys 2.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "658f19728920138342f68408b7cf7644d90d4784353d8ebc32e7e8663dbe45fa"
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
-"checksum font-loader 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "933a61458662fbc8e3cd22cdb8331edbd78545fc044e1e2cd3d742f6ce06aa41"
+"checksum font-loader 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c49d6b4c11dca1a1dd931a34a9f397e2da91abe3de4110505f3530a80e560b52"
"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
-"checksum freetype 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "11926b2b410b469d0e9399eca4cbbe237a9ef02176c485803b29216307e8e028"
+"checksum freetype 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bee38378a9e3db1cc693b4f88d166ae375338a0ff75cb8263e1c601d51f35dc6"
+"checksum freetype-sys 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a"
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
@@ -2183,9 +2214,8 @@ dependencies = [
"checksum serde_bytes 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "325a073952621257820e7a3469f55ba4726d8b28657e7e36653d1c36dc2c84ae"
"checksum serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c"
"checksum serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)" = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9"
-"checksum servo-fontconfig 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a088f8d775a5c5314aae09bd77340bc9c67d72b9a45258be34c83548b4814cd9"
-"checksum servo-fontconfig-sys 4.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "62b3e166450f523f4db06c14f02a2d39e76d49b5d8cbd224338d93e3595c156c"
-"checksum servo-freetype-sys 4.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2c4ccb6d0d32d277d3ef7dea86203d8210945eb7a45fba89dd445b3595dd0dfc"
+"checksum servo-fontconfig 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7e3e22fe5fd73d04ebf0daa049d3efe3eae55369ce38ab16d07ddd9ac5c217c"
+"checksum servo-fontconfig-sys 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e36b879db9892dfa40f95da1c38a835d41634b825fbd8c4c418093d53c24b388"
"checksum sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
"checksum shared_library 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11"
"checksum sig 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6567e29578f9bfade6a5d94a32b9a4256348358d2a3f448cab0021f9a02614a2"
diff --git a/webrender/Cargo.toml b/webrender/Cargo.toml
index 3fa6630bd..d05cf5979 100644
--- a/webrender/Cargo.toml
+++ b/webrender/Cargo.toml
@@ -10,7 +10,7 @@ edition = "2018"
[features]
default = ["freetype-lib"]
-freetype-lib = ["freetype/servo-freetype-sys"]
+freetype-lib = ["freetype/freetype-sys"]
profiler = ["tracy-rs/enable_profiler"]
debugger = ["ws", "serde_json", "serde", "image_loader", "base64"]
capture = ["api/serialize", "ron", "serde", "smallvec/serde"]
@@ -65,13 +65,13 @@ sig = "1.0"
libc = "0.2"
[target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies]
-freetype = { version = "0.4", default-features = false }
+freetype = { version = "0.7", default-features = false }
libc = "0.2"
[target.'cfg(target_os = "windows")'.dependencies]
dwrote = "0.11"
[target.'cfg(target_os = "macos")'.dependencies]
-core-foundation = "0.7"
-core-graphics = "0.19"
-core-text = { version = "15", default-features = false }
+core-foundation = "0.9"
+core-graphics = "0.22"
+core-text = { version = "19", default-features = false }
diff --git a/webrender_api/Cargo.toml b/webrender_api/Cargo.toml
index 6b8b1e5f2..4ed7ce8e2 100644
--- a/webrender_api/Cargo.toml
+++ b/webrender_api/Cargo.toml
@@ -28,5 +28,5 @@ malloc_size_of = { version = "0.0.1", path = "../wr_malloc_size_of", package = "
peek-poke = { version = "0.2", path = "../peek-poke", features = ["extras"] }
[target.'cfg(target_os = "macos")'.dependencies]
-core-foundation = "0.7"
-core-graphics = "0.19"
+core-foundation = "0.9"
+core-graphics = "0.22"
diff --git a/wrench/Cargo.toml b/wrench/Cargo.toml
index 4ba95e4c4..33679485a 100644
--- a/wrench/Cargo.toml
+++ b/wrench/Cargo.toml
@@ -38,8 +38,8 @@ default-features = false
features = ["png"]
[target.'cfg(target_os = "macos")'.dependencies]
-core-graphics = "0.19"
-core-foundation = "0.7"
+core-graphics = "0.22"
+core-foundation = "0.9"
[features]
default = [ "env_logger" ]
@@ -51,7 +51,7 @@ dwrote = "0.11"
mozangle = {version = "0.3.1", features = ["egl"]}
[target.'cfg(all(unix, not(target_os = "android")))'.dependencies]
-font-loader = "0.9"
+font-loader = "0.11"
# Configuration information used when building wrench as an APK.
[package.metadata.android]
--
2.39.2

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,53 @@
From 415b9ba32648667313f6f4ce7965752285bf0b26 Mon Sep 17 00:00:00 2001
From: Martin Robinson <mrobinson@igalia.com>
Date: Thu, 12 Jan 2023 12:35:31 +0100
Subject: [PATCH 2/2] Bump procedural-masquerade to 0.1.7
This fixes a build issue in this branch.
---
Cargo.lock | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index b6085604cae8e18de3273bcddac43fa0a7e1abd1..b7055733e57fcd0acff07881ef72369b560c2abe 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -417,7 +417,7 @@ version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cstr-macros 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "procedural-masquerade 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "procedural-masquerade 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -425,7 +425,7 @@ name = "cstr-macros"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "procedural-masquerade 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "procedural-masquerade 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1212,7 +1212,7 @@ dependencies = [
[[package]]
name = "procedural-masquerade"
-version = "0.1.6"
+version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -2182,7 +2182,7 @@ dependencies = [
"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
"checksum proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3"
-"checksum procedural-masquerade 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9a1574a51c3fd37b26d2c0032b649d08a7d51d4cca9c41bbc5bf7118fa4509d0"
+"checksum procedural-masquerade 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1383dff4092fe903ac180e391a8d4121cc48f08ccf850614b0290c6673b69d"
"checksum quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
"checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f"
--
2.39.2

1
third_party/webrender/patches/head vendored Normal file
View file

@ -0,0 +1 @@
1175acad2d4f49fa712e105c84149ac7f394261d

Some files were not shown because too many files have changed in this diff Show more