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

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

6
third_party/webrender/patches/series vendored Normal file
View file

@ -0,0 +1,6 @@
0001-Add-signal-handler-to-catch-segfault-in-build-script.patch
0002-Bug-1646741-Update-gleam-to-0.12.-r-kvark.patch
0003-Bug-1651889.-Update-to-gleam-0.12.1.-r-kvark.patch
0004-Bug-1654699.-Update-core-foundation-core-graphics.-r.patch
0005-Bug-1656236-Update-to-euclid-0.22.-r-kvark.patch
0006-Bump-procedural-masquerade-to-0.1.7.patch

View file

@ -0,0 +1,17 @@
[package]
name = "peek-poke"
version = "0.2.0"
authors = ["Dan Glastonbury <dan.glastonbury@gmail.com>"]
repository = "https://github.com/servo/webrender"
description = "A mechanism for serializing and deserializing data into/from byte buffers, for use in WebRender."
license = "MIT/Apache-2.0"
edition = "2018"
[dependencies]
euclid = { version = "0.22.0", optional = true }
peek-poke-derive = { version = "0.2", path = "./peek-poke-derive", optional = true }
[features]
default = ["derive"]
derive = ["peek-poke-derive"]
extras = ["derive", "euclid"]

View file

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -0,0 +1,44 @@
MIT License
Copyright (c) 2019 Daniel Glastonbury
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
This work incorporates work covered by the following copyright and permission
notice:
Copyright (c) 2019 Devashish Dixit
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,54 @@
# Peeks, Pokes, and Pointers
Peek from and poke structures into byte slices.
## Benchmark
Below are the benchmark results of comparison between `peek-poke` and `bincode` serializing and deserializing same `struct`:
```
struct MyPeekPokeStruct {
a: u8,
b: u16,
c: MyPeekPokeEnum,
d: Option<usize>,
}
enum MyPeekPokeEnum {
Variant1,
Variant2(u16),
}
```
```
Benchmarking struct::serialize/peek_poke::poke_into: Collecting 100 samples in struct::serialize/peek_poke::poke_into
time: [2.7267 ns 2.7321 ns 2.7380 ns]
Benchmarking struct::serialize/bincode::serialize: Collecting 100 samples in est struct::serialize/bincode::serialize
time: [31.264 ns 31.326 ns 31.389 ns]
Benchmarking struct::deserialize/peek_poke::peek_from: Collecting 100 samples struct::deserialize/peek_poke::peek_from
time: [5.3544 ns 5.3672 ns 5.3817 ns]
Benchmarking struct::deserialize/bincode::deserialize: Collecting 100 samples in struct::deserialize/bincode::deserialize
time: [25.155 ns 26.439 ns 27.661 ns]
```
You can run benchmarks by running following command:
```
cargo bench
```
## License
[license]: #license
Licensed under either of
- Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (http://opensource.org/licenses/MIT)
at your option.
see [LICENSE-APACHE](LICENSE-APACHE), [LICENSE-MIT](LICENSE-MIT) for details.
## Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as
defined in the Apache-2.0 license, shall be dual licensed as about, without any additional terms or conditions.

View file

@ -0,0 +1,19 @@
[package]
name = "peek-poke-derive"
version = "0.2.1"
authors = ["Dan Glastonbury <dan.glastonbury@gmail.com>"]
repository = "https://github.com/servo/webrender"
description = "Derive macro for peek-poke."
license = "MIT/Apache-2.0"
edition = "2018"
[lib]
doctest = false
proc-macro = true
[dependencies]
proc-macro2 = "1"
quote = "1"
syn = "1"
synstructure = "0.12"
unicode-xid = "0.2"

View file

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -0,0 +1,44 @@
MIT License
Copyright (c) 2019 Daniel Glastonbury
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
This work incorporates work covered by the following copyright and permission
notice:
Copyright (c) 2019 Devashish Dixit
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,54 @@
# Peeks, Pokes, and Pointers
Peek from and poke structures into byte slices.
## Benchmark
Below are the benchmark results of comparison between `peek-poke` and `bincode` serializing and deserializing same `struct`:
```
struct MyPeekPokeStruct {
a: u8,
b: u16,
c: MyPeekPokeEnum,
d: Option<usize>,
}
enum MyPeekPokeEnum {
Variant1,
Variant2(u16),
}
```
```
Benchmarking struct::serialize/peek_poke::poke_into: Collecting 100 samples in struct::serialize/peek_poke::poke_into
time: [2.7267 ns 2.7321 ns 2.7380 ns]
Benchmarking struct::serialize/bincode::serialize: Collecting 100 samples in est struct::serialize/bincode::serialize
time: [31.264 ns 31.326 ns 31.389 ns]
Benchmarking struct::deserialize/peek_poke::peek_from: Collecting 100 samples struct::deserialize/peek_poke::peek_from
time: [5.3544 ns 5.3672 ns 5.3817 ns]
Benchmarking struct::deserialize/bincode::deserialize: Collecting 100 samples in struct::deserialize/bincode::deserialize
time: [25.155 ns 26.439 ns 27.661 ns]
```
You can run benchmarks by running following command:
```
cargo bench
```
## License
[license]: #license
Licensed under either of
- Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (http://opensource.org/licenses/MIT)
at your option.
see [LICENSE-APACHE](LICENSE-APACHE), [LICENSE-MIT](LICENSE-MIT) for details.
## Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as
defined in the Apache-2.0 license, shall be dual licensed as about, without any additional terms or conditions.

View file

@ -0,0 +1,266 @@
// Copyright 2019 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{Ident, Index, TraitBound};
use synstructure::{decl_derive, Structure, BindStyle, AddBounds};
use unicode_xid::UnicodeXID;
// Internal method for sanitizing an identifier for hygiene purposes.
fn sanitize_ident(s: &str) -> Ident {
let mut res = String::with_capacity(s.len());
for mut c in s.chars() {
if !UnicodeXID::is_xid_continue(c) {
c = '_'
}
// Deduplicate consecutive _ characters.
if res.ends_with('_') && c == '_' {
continue;
}
res.push(c);
}
Ident::new(&res, Span::call_site())
}
/// Calculates size type for number of variants (used for enums)
fn get_discriminant_size_type(len: usize) -> TokenStream {
if len <= <u8>::max_value() as usize {
quote! { u8 }
} else if len <= <u16>::max_value() as usize {
quote! { u16 }
} else {
quote! { u32 }
}
}
fn is_struct(s: &Structure) -> bool {
// a single variant with no prefix is 'struct'
match &s.variants()[..] {
[v] if v.prefix.is_none() => true,
_ => false,
}
}
fn derive_max_size(s: &Structure) -> TokenStream {
let max_size = s.variants().iter().fold(quote!(0), |acc, vi| {
let variant_size = vi.bindings().iter().fold(quote!(0), |acc, bi| {
// compute size of each variant by summing the sizes of its bindings
let ty = &bi.ast().ty;
quote!(#acc + <#ty>::max_size())
});
// find the maximum of each variant
quote! {
max(#acc, #variant_size)
}
});
let body = if is_struct(s) {
max_size
} else {
let discriminant_size_type = get_discriminant_size_type(s.variants().len());
quote! {
#discriminant_size_type ::max_size() + #max_size
}
};
quote! {
#[inline(always)]
fn max_size() -> usize {
use std::cmp::max;
#body
}
}
}
fn derive_peek_from_for_enum(s: &mut Structure) -> TokenStream {
assert!(!is_struct(s));
s.bind_with(|_| BindStyle::Move);
let num_variants = s.variants().len();
let discriminant_size_type = get_discriminant_size_type(num_variants);
let body = s
.variants()
.iter()
.enumerate()
.fold(quote!(), |acc, (i, vi)| {
let bindings = vi
.bindings()
.iter()
.map(|bi| quote!(#bi))
.collect::<Vec<_>>();
let variant_pat = Index::from(i);
let poke_exprs = bindings.iter().fold(quote!(), |acc, bi| {
quote! {
#acc
let (#bi, bytes) = peek_poke::peek_from_default(bytes);
}
});
let construct = vi.construct(|_, i| {
let bi = &bindings[i];
quote!(#bi)
});
quote! {
#acc
#variant_pat => {
#poke_exprs
*output = #construct;
bytes
}
}
});
let type_name = s.ast().ident.to_string();
let max_tag_value = num_variants - 1;
quote! {
#[inline(always)]
unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 {
let (variant, bytes) = peek_poke::peek_from_default::<#discriminant_size_type>(bytes);
match variant {
#body
out_of_range_tag => {
panic!("WRDL: memory corruption detected while parsing {} - enum tag should be <= {}, but was {}",
#type_name, #max_tag_value, out_of_range_tag);
}
}
}
}
}
fn derive_peek_from_for_struct(s: &mut Structure) -> TokenStream {
assert!(is_struct(&s));
s.variants_mut()[0].bind_with(|_| BindStyle::RefMut);
let pat = s.variants()[0].pat();
let peek_exprs = s.variants()[0].bindings().iter().fold(quote!(), |acc, bi| {
let ty = &bi.ast().ty;
quote! {
#acc
let bytes = <#ty>::peek_from(bytes, #bi);
}
});
let body = quote! {
#pat => {
#peek_exprs
bytes
}
};
quote! {
#[inline(always)]
unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 {
match &mut (*output) {
#body
}
}
}
}
fn derive_poke_into(s: &Structure) -> TokenStream {
let is_struct = is_struct(&s);
let discriminant_size_type = get_discriminant_size_type(s.variants().len());
let body = s
.variants()
.iter()
.enumerate()
.fold(quote!(), |acc, (i, vi)| {
let init = if !is_struct {
let index = Index::from(i);
quote! {
let bytes = #discriminant_size_type::poke_into(&#index, bytes);
}
} else {
quote!()
};
let variant_pat = vi.pat();
let poke_exprs = vi.bindings().iter().fold(init, |acc, bi| {
quote! {
#acc
let bytes = #bi.poke_into(bytes);
}
});
quote! {
#acc
#variant_pat => {
#poke_exprs
bytes
}
}
});
quote! {
#[inline(always)]
unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 {
match &*self {
#body
}
}
}
}
fn peek_poke_derive(mut s: Structure) -> TokenStream {
s.binding_name(|_, i| Ident::new(&format!("__self_{}", i), Span::call_site()));
let max_size_fn = derive_max_size(&s);
let poke_into_fn = derive_poke_into(&s);
let peek_from_fn = if is_struct(&s) {
derive_peek_from_for_struct(&mut s)
} else {
derive_peek_from_for_enum(&mut s)
};
let poke_impl = s.gen_impl(quote! {
extern crate peek_poke;
gen unsafe impl peek_poke::Poke for @Self {
#max_size_fn
#poke_into_fn
}
});
// To implement `fn peek_from` we require that types implement `Default`
// trait to create temporary values. This code does the addition all
// manually until https://github.com/mystor/synstructure/issues/24 is fixed.
let default_trait = syn::parse_str::<TraitBound>("::std::default::Default").unwrap();
let peek_trait = syn::parse_str::<TraitBound>("peek_poke::Peek").unwrap();
let ast = s.ast();
let name = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
let mut where_clause = where_clause.cloned();
s.add_trait_bounds(&default_trait, &mut where_clause, AddBounds::Generics);
s.add_trait_bounds(&peek_trait, &mut where_clause, AddBounds::Generics);
let dummy_const: Ident = sanitize_ident(&format!("_DERIVE_peek_poke_Peek_FOR_{}", name));
let peek_impl = quote! {
#[allow(non_upper_case_globals)]
const #dummy_const: () = {
extern crate peek_poke;
impl #impl_generics peek_poke::Peek for #name #ty_generics #where_clause {
#peek_from_fn
}
};
};
quote! {
#poke_impl
#peek_impl
}
}
decl_derive!([PeekPoke] => peek_poke_derive);

View file

@ -0,0 +1,170 @@
// Copyright 2019 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::{Peek, Poke};
use euclid::{Point2D, Rect, SideOffsets2D, Size2D, Transform3D, Vector2D};
unsafe impl<T: Poke, U> Poke for Point2D<T, U> {
#[inline(always)]
fn max_size() -> usize {
2 * T::max_size()
}
#[inline(always)]
unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 {
let bytes = self.x.poke_into(bytes);
let bytes = self.y.poke_into(bytes);
bytes
}
}
impl<T: Peek, U> Peek for Point2D<T, U> {
#[inline(always)]
unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 {
let bytes = T::peek_from(bytes, &mut (*output).x);
let bytes = T::peek_from(bytes, &mut (*output).y);
bytes
}
}
unsafe impl<T: Poke, U> Poke for Rect<T, U> {
#[inline(always)]
fn max_size() -> usize {
Point2D::<T, U>::max_size() + Size2D::<T, U>::max_size()
}
#[inline(always)]
unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 {
let bytes = self.origin.poke_into(bytes);
let bytes = self.size.poke_into(bytes);
bytes
}
}
impl<T: Peek, U> Peek for Rect<T, U> {
#[inline(always)]
unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 {
let bytes = Point2D::<T, U>::peek_from(bytes, &mut (*output).origin);
let bytes = Size2D::<T, U>::peek_from(bytes, &mut (*output).size);
bytes
}
}
unsafe impl<T: Poke, U> Poke for SideOffsets2D<T, U> {
#[inline(always)]
fn max_size() -> usize {
4 * T::max_size()
}
#[inline(always)]
unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 {
let bytes = self.top.poke_into(bytes);
let bytes = self.right.poke_into(bytes);
let bytes = self.bottom.poke_into(bytes);
let bytes = self.left.poke_into(bytes);
bytes
}
}
impl<T: Peek, U> Peek for SideOffsets2D<T, U> {
#[inline(always)]
unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 {
let bytes = T::peek_from(bytes, &mut (*output).top);
let bytes = T::peek_from(bytes, &mut (*output).right);
let bytes = T::peek_from(bytes, &mut (*output).bottom);
let bytes = T::peek_from(bytes, &mut (*output).left);
bytes
}
}
unsafe impl<T: Poke, U> Poke for Size2D<T, U> {
#[inline(always)]
fn max_size() -> usize {
2 * T::max_size()
}
#[inline(always)]
unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 {
let bytes = self.width.poke_into(bytes);
let bytes = self.height.poke_into(bytes);
bytes
}
}
impl<T: Peek, U> Peek for Size2D<T, U> {
#[inline(always)]
unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 {
let bytes = T::peek_from(bytes, &mut (*output).width);
let bytes = T::peek_from(bytes, &mut (*output).height);
bytes
}
}
unsafe impl<T: Poke, S, D> Poke for Transform3D<T, S, D> {
#[inline(always)]
fn max_size() -> usize {
16 * T::max_size()
}
#[inline(always)]
unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 {
let bytes = self.m11.poke_into(bytes);
let bytes = self.m12.poke_into(bytes);
let bytes = self.m13.poke_into(bytes);
let bytes = self.m14.poke_into(bytes);
let bytes = self.m21.poke_into(bytes);
let bytes = self.m22.poke_into(bytes);
let bytes = self.m23.poke_into(bytes);
let bytes = self.m24.poke_into(bytes);
let bytes = self.m31.poke_into(bytes);
let bytes = self.m32.poke_into(bytes);
let bytes = self.m33.poke_into(bytes);
let bytes = self.m34.poke_into(bytes);
let bytes = self.m41.poke_into(bytes);
let bytes = self.m42.poke_into(bytes);
let bytes = self.m43.poke_into(bytes);
let bytes = self.m44.poke_into(bytes);
bytes
}
}
impl<T: Peek, S, D> Peek for Transform3D<T, S, D> {
#[inline(always)]
unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 {
let bytes = T::peek_from(bytes, &mut (*output).m11);
let bytes = T::peek_from(bytes, &mut (*output).m12);
let bytes = T::peek_from(bytes, &mut (*output).m13);
let bytes = T::peek_from(bytes, &mut (*output).m14);
let bytes = T::peek_from(bytes, &mut (*output).m21);
let bytes = T::peek_from(bytes, &mut (*output).m22);
let bytes = T::peek_from(bytes, &mut (*output).m23);
let bytes = T::peek_from(bytes, &mut (*output).m24);
let bytes = T::peek_from(bytes, &mut (*output).m31);
let bytes = T::peek_from(bytes, &mut (*output).m32);
let bytes = T::peek_from(bytes, &mut (*output).m33);
let bytes = T::peek_from(bytes, &mut (*output).m34);
let bytes = T::peek_from(bytes, &mut (*output).m41);
let bytes = T::peek_from(bytes, &mut (*output).m42);
let bytes = T::peek_from(bytes, &mut (*output).m43);
let bytes = T::peek_from(bytes, &mut (*output).m44);
bytes
}
}
unsafe impl<T: Poke, U> Poke for Vector2D<T, U> {
#[inline(always)]
fn max_size() -> usize {
2 * T::max_size()
}
#[inline(always)]
unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 {
let bytes = self.x.poke_into(bytes);
let bytes = self.y.poke_into(bytes);
bytes
}
}
impl<T: Peek, U> Peek for Vector2D<T, U> {
#[inline(always)]
unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 {
let bytes = T::peek_from(bytes, &mut (*output).x);
let bytes = T::peek_from(bytes, &mut (*output).y);
bytes
}
}

View file

@ -0,0 +1,427 @@
// Copyright 2019 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Fast binary serialization and deserialization for types with a known maximum size.
//!
//! ## Binary Encoding Scheme
//!
//! ## Usage
//!
//! ## Comparison to bincode
#[cfg(feature = "derive")]
pub use peek_poke_derive::*;
use core::{marker::PhantomData, mem::size_of, slice};
use crate::{slice_ext::*, vec_ext::*};
mod slice_ext;
mod vec_ext;
union MaybeUninitShim<T: Copy> {
uninit: (),
init: T,
}
/// Peek helper for constructing a `T` by `Copy`ing into an uninitialized stack
/// allocation.
pub unsafe fn peek_from_uninit<T: Copy + Peek>(bytes: *const u8) -> (T, *const u8) {
let mut val = MaybeUninitShim { uninit: () };
let bytes = <T>::peek_from(bytes, &mut val.init);
(val.init, bytes)
}
/// Peek helper for constructing a `T` by `Default` initialized stack
/// allocation.
pub unsafe fn peek_from_default<T: Default + Peek>(bytes: *const u8) -> (T, *const u8) {
let mut val = T::default();
let bytes = <T>::peek_from(bytes, &mut val);
(val, bytes)
}
/// Peek inplace a `T` from a slice of bytes, returning a slice of the remaining
/// bytes. `src` must contain at least `T::max_size()` bytes.
///
/// [`ensure_red_zone`] can be used to add required padding.
pub fn peek_from_slice<'a, T: Peek>(src: &'a [u8], dst: &mut T) -> &'a [u8] {
unsafe {
// If src.len() == T::max_size() then src is at the start of the red-zone.
assert!(T::max_size() < src.len(), "WRDL: unexpected end of display list");
let end_ptr = T::peek_from(src.as_ptr(), dst);
let len = end_ptr as usize - src.as_ptr() as usize;
// Did someone break the T::peek_from() can't read more than T::max_size()
// bytes contract?
assert!(len <= src.len(), "WRDL: Peek::max_size was wrong");
slice::from_raw_parts(end_ptr, src.len() - len)
}
}
/// Poke helper to insert a serialized version of `src` at the beginning for `dst`.
pub fn poke_inplace_slice<T: Poke>(src: &T, dst: &mut [u8]) {
assert!(T::max_size() <= dst.len(), "WRDL: buffer too small to write into");
unsafe {
src.poke_into(dst.as_mut_ptr());
}
}
/// Poke helper to append a serialized version of `src` to the end of `dst`.
pub fn poke_into_vec<T: Poke>(src: &T, dst: &mut Vec<u8>) {
dst.reserve(T::max_size());
unsafe {
let ptr = dst.as_end_mut_ptr();
let end_ptr = src.poke_into(ptr);
dst.set_end_ptr(end_ptr);
}
}
// TODO: Is returning the len of the iterator of any practical use?
pub fn poke_extend_vec<I>(src: I, dst: &mut Vec<u8>) -> usize
where
I: ExactSizeIterator,
I::Item: Poke,
{
let len = src.len();
let max_size = len * I::Item::max_size();
dst.reserve(max_size);
unsafe {
let ptr = dst.as_end_mut_ptr();
// Guard against the possibility of a misbehaved implementation of
// ExactSizeIterator by writing at most `len` items.
let end_ptr = src.take(len).fold(ptr, |ptr, item| item.poke_into(ptr));
dst.set_end_ptr(end_ptr);
}
len
}
/// Add `T::max_size()` "red zone" (padding of zeroes) to the end of the vec of
/// `bytes`. This allows deserialization to assert that at least `T::max_size()`
/// bytes exist at all times.
pub fn ensure_red_zone<T: Poke>(bytes: &mut Vec<u8>) {
bytes.reserve(T::max_size());
unsafe {
let end_ptr = bytes.as_end_mut_ptr();
end_ptr.write_bytes(0, T::max_size());
bytes.set_end_ptr(end_ptr.add(T::max_size()));
}
}
#[inline]
unsafe fn read_verbatim<T>(src: *const u8, dst: *mut T) -> *const u8 {
*dst = (src as *const T).read_unaligned();
src.add(size_of::<T>())
}
#[inline]
unsafe fn write_verbatim<T>(src: T, dst: *mut u8) -> *mut u8 {
(dst as *mut T).write_unaligned(src);
dst.add(size_of::<T>())
}
#[cfg(feature = "extras")]
mod euclid;
/// A trait for values that provide serialization into buffers of bytes.
///
/// # Example
///
/// ```no_run
/// use peek_poke::Poke;
///
/// struct Bar {
/// a: u32,
/// b: u8,
/// c: i16,
/// }
///
/// unsafe impl Poke for Bar {
/// fn max_size() -> usize {
/// <u32>::max_size() + <u8>::max_size() + <i16>::max_size()
/// }
/// unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 {
/// let bytes = self.a.poke_into(bytes);
/// let bytes = self.b.poke_into(bytes);
/// self.c.poke_into(bytes)
/// }
/// }
/// ```
///
/// # Safety
///
/// The `Poke` trait is an `unsafe` trait for the reasons, and implementors must
/// ensure that they adhere to these contracts:
///
/// * `max_size()` query and calculations in general must be correct. Callers
/// of this trait are expected to rely on the contract defined on each
/// method, and implementors must ensure such contracts remain true.
pub unsafe trait Poke {
/// Return the maximum number of bytes that the serialized version of `Self`
/// will occupy.
///
/// # Safety
///
/// Implementors of `Poke` guarantee to not write more than the result of
/// calling `max_size()` into the buffer pointed to by `bytes` when
/// `poke_into()` is called.
fn max_size() -> usize;
/// Serialize into the buffer pointed to by `bytes`.
///
/// Returns a pointer to the next byte after the serialized representation of `Self`.
///
/// # Safety
///
/// This function is unsafe because undefined behavior can result if the
/// caller does not ensure all of the following:
///
/// * `bytes` must denote a valid pointer to a block of memory.
///
/// * `bytes` must pointer to at least the number of bytes returned by
/// `max_size()`.
unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8;
}
/// A trait for values that provide deserialization from buffers of bytes.
///
/// # Example
///
/// ```ignore
/// use peek_poke::Peek;
///
/// struct Bar {
/// a: u32,
/// b: u8,
/// c: i16,
/// }
///
/// ...
///
/// impl Peek for Bar {
/// unsafe fn peek_from(&mut self, bytes: *const u8) -> *const u8 {
/// let bytes = self.a.peek_from(bytes);
/// let bytes = self.b.peek_from(bytes);
/// self.c.peek_from(bytes)
/// }
/// }
/// ```
///
/// # Safety
///
/// The `Peek` trait contains unsafe methods for the following reasons, and
/// implementors must ensure that they adhere to these contracts:
///
/// * Callers of this trait are expected to rely on the contract defined on each
/// method, and implementors must ensure that `peek_from()` doesn't read more
/// bytes from `bytes` than is returned by `Peek::max_size()`.
pub trait Peek: Poke {
/// Deserialize from the buffer pointed to by `bytes`.
///
/// Returns a pointer to the next byte after the unconsumed bytes not used
/// to deserialize the representation of `Self`.
///
/// # Safety
///
/// This function is unsafe because undefined behavior can result if the
/// caller does not ensure all of the following:
///
/// * `bytes` must denote a valid pointer to a block of memory.
///
/// * `bytes` must pointer to at least the number of bytes returned by
/// `Poke::max_size()`.
unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8;
}
macro_rules! impl_poke_for_deref {
(<$($desc:tt)+) => {
unsafe impl <$($desc)+ {
#[inline(always)]
fn max_size() -> usize {
<T>::max_size()
}
unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 {
(**self).poke_into(bytes)
}
}
}
}
impl_poke_for_deref!(<'a, T: Poke> Poke for &'a T);
impl_poke_for_deref!(<'a, T: Poke> Poke for &'a mut T);
macro_rules! impl_for_primitive {
($($ty:ty)+) => {
$(unsafe impl Poke for $ty {
#[inline(always)]
fn max_size() -> usize {
size_of::<Self>()
}
#[inline(always)]
unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 {
write_verbatim(*self, bytes)
}
}
impl Peek for $ty {
#[inline(always)]
unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 {
read_verbatim(bytes, output)
}
})+
};
}
impl_for_primitive! {
i8 i16 i32 i64 isize
u8 u16 u32 u64 usize
f32 f64
}
unsafe impl Poke for bool {
#[inline(always)]
fn max_size() -> usize {
u8::max_size()
}
#[inline]
unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 {
(*self as u8).poke_into(bytes)
}
}
impl Peek for bool {
#[inline]
unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 {
let mut int_bool = 0u8;
let ptr = <u8>::peek_from(bytes, &mut int_bool);
*output = int_bool != 0;
ptr
}
}
unsafe impl<T> Poke for PhantomData<T> {
#[inline(always)]
fn max_size() -> usize {
0
}
#[inline(always)]
unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 {
bytes
}
}
impl<T> Peek for PhantomData<T> {
#[inline(always)]
unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 {
*output = PhantomData;
bytes
}
}
unsafe impl<T: Poke> Poke for Option<T> {
#[inline(always)]
fn max_size() -> usize {
u8::max_size() + T::max_size()
}
#[inline]
unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 {
match self {
None => 0u8.poke_into(bytes),
Some(ref v) => {
let bytes = 1u8.poke_into(bytes);
let bytes = v.poke_into(bytes);
bytes
}
}
}
}
impl<T: Default + Peek> Peek for Option<T> {
#[inline]
unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 {
let (variant, bytes) = peek_from_default::<u8>(bytes);
match variant {
0 => {
*output = None;
bytes
}
1 => {
let (val, bytes) = peek_from_default(bytes);
*output = Some(val);
bytes
}
_ => unreachable!(),
}
}
}
macro_rules! impl_for_arrays {
($($len:tt)+) => {
$(unsafe impl<T: Poke> Poke for [T; $len] {
fn max_size() -> usize {
$len * T::max_size()
}
unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 {
self.iter().fold(bytes, |bytes, e| e.poke_into(bytes))
}
}
impl<T: Peek> Peek for [T; $len] {
unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 {
(&mut *output).iter_mut().fold(bytes, |bytes, e| <T>::peek_from(bytes, e))
}
})+
}
}
impl_for_arrays! {
01 02 03 04 05 06 07 08 09 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32
}
unsafe impl Poke for () {
fn max_size() -> usize {
0
}
unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 {
bytes
}
}
impl Peek for () {
unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 {
*output = ();
bytes
}
}
macro_rules! impl_for_tuple {
($($n:tt: $ty:ident),+) => {
unsafe impl<$($ty: Poke),+> Poke for ($($ty,)+) {
#[inline(always)]
fn max_size() -> usize {
0 $(+ <$ty>::max_size())+
}
unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 {
$(let bytes = self.$n.poke_into(bytes);)+
bytes
}
}
impl<$($ty: Peek),+> Peek for ($($ty,)+) {
unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 {
$(let bytes = $ty::peek_from(bytes, &mut (*output).$n);)+
bytes
}
}
}
}
impl_for_tuple!(0: A);
impl_for_tuple!(0: A, 1: B);
impl_for_tuple!(0: A, 1: B, 2: C);
impl_for_tuple!(0: A, 1: B, 2: C, 3: D);
impl_for_tuple!(0: A, 1: B, 2: C, 3: D, 4: E);

View file

@ -0,0 +1,19 @@
// Copyright 2019 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub trait AsEndMutPtr<T> {
fn as_end_mut_ptr(self) -> *mut T;
}
impl<'a> AsEndMutPtr<u8> for &'a mut [u8] {
fn as_end_mut_ptr(self) -> *mut u8 {
unsafe { self.as_mut_ptr().add(self.len()) }
}
}

View file

@ -0,0 +1,26 @@
// Copyright 2019 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::vec::Vec;
pub trait VecExt {
type Item;
unsafe fn set_end_ptr(&mut self, end: *const Self::Item);
}
impl<T> VecExt for Vec<T> {
type Item = T;
unsafe fn set_end_ptr(&mut self, end: *const T) {
assert!(end as usize >= self.as_ptr() as usize);
let new_len = end as usize - self.as_ptr() as usize;
assert!(new_len <= self.capacity());
self.set_len(new_len);
}
}

View file

@ -0,0 +1,117 @@
// Copyright 2019 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(dead_code)]
use peek_poke::{PeekPoke, Poke};
use std::{marker::PhantomData, mem::size_of};
#[test]
fn test_numbers() {
assert_eq!(u8::max_size(), size_of::<u8>());
assert_eq!(u16::max_size(), size_of::<u16>());
assert_eq!(u32::max_size(), size_of::<u32>());
assert_eq!(u64::max_size(), size_of::<u64>());
assert_eq!(usize::max_size(), size_of::<usize>());
assert_eq!(i8::max_size(), size_of::<i8>());
assert_eq!(i16::max_size(), size_of::<i16>());
assert_eq!(i32::max_size(), size_of::<i32>());
assert_eq!(i64::max_size(), size_of::<i64>());
assert_eq!(isize::max_size(), size_of::<isize>());
// floating
assert_eq!(f32::max_size(), size_of::<f32>());
assert_eq!(f64::max_size(), size_of::<f64>());
}
#[test]
fn test_bool() {
assert_eq!(bool::max_size(), size_of::<u8>());
}
#[test]
fn test_option() {
assert_eq!(
Option::<usize>::max_size(),
<u8>::max_size() + <usize>::max_size()
);
}
#[test]
fn test_fixed_size_array() {
assert_eq!(<[u32; 32]>::max_size(), 32 * size_of::<u32>());
assert_eq!(<[u64; 8]>::max_size(), 8 * size_of::<u64>());
assert_eq!(<[u8; 19]>::max_size(), 19 * size_of::<u8>());
}
#[test]
fn test_tuple() {
assert_eq!(<(isize, )>::max_size(), size_of::<isize>());
assert_eq!(<(isize, isize, isize)>::max_size(), 3 * size_of::<isize>());
assert_eq!(<(isize, ())>::max_size(), size_of::<isize>());
}
#[test]
fn test_basic_struct() {
#[derive(Debug, PeekPoke)]
struct Bar {
a: u32,
b: u32,
c: u32,
}
assert_eq!(<Bar>::max_size(), 3 * <u32>::max_size());
}
#[test]
fn test_enum() {
#[derive(Clone, Copy, PeekPoke)]
enum TestEnum {
NoArg,
OneArg(usize),
Args(usize, usize),
AnotherNoArg,
StructLike { x: usize, y: f32 },
}
assert_eq!(
TestEnum::max_size(),
<u8>::max_size() + 2 * <usize>::max_size()
);
}
#[test]
fn test_enum_cstyle() {
#[repr(u32)]
#[derive(Clone, Copy, PeekPoke)]
enum BorderStyle {
None = 0,
Solid = 1,
Double = 2,
Dotted = 3,
Dashed = 4,
Hidden = 5,
Groove = 6,
Ridge = 7,
Inset = 8,
Outset = 9,
}
assert_eq!(BorderStyle::max_size(), <u8>::max_size());
}
#[test]
fn test_phantom_data() {
struct Bar;
#[derive(PeekPoke)]
struct Foo {
x: u32,
y: u32,
_marker: PhantomData<Bar>,
}
assert_eq!(Foo::max_size(), 2 * size_of::<u32>())
}

View file

@ -0,0 +1,275 @@
// Copyright 2019 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use peek_poke::{Peek, PeekPoke, Poke};
use std::{fmt::Debug, marker::PhantomData};
fn poke_into<V: Peek + Poke>(a: &V) -> Vec<u8> {
let mut v = <Vec<u8>>::with_capacity(<V>::max_size());
let end_ptr = unsafe { a.poke_into(v.as_mut_ptr()) };
let new_size = end_ptr as usize - v.as_ptr() as usize;
assert!(new_size <= v.capacity());
unsafe {
v.set_len(new_size);
}
v
}
#[cfg(not(feature = "option_copy"))]
fn the_same<V>(a: V)
where
V: Debug + Default + PartialEq + Peek + Poke,
{
let v = poke_into(&a);
let (b, end_ptr) = unsafe { peek_poke::peek_from_default(v.as_ptr()) };
let size = end_ptr as usize - v.as_ptr() as usize;
assert_eq!(size, v.len());
assert_eq!(a, b);
}
#[cfg(feature = "option_copy")]
fn the_same<V>(a: V)
where
V: Copy + Debug + PartialEq + Peek + Poke,
{
let v = poke_into(&a);
let mut b = a;
let end_ptr = unsafe { b.peek_from(v.as_ptr()) };
let size = end_ptr as usize - v.as_ptr() as usize;
assert_eq!(size, v.len());
assert_eq!(a, b);
}
#[test]
fn test_numbers() {
// unsigned positive
the_same(5u8);
the_same(5u16);
the_same(5u32);
the_same(5u64);
the_same(5usize);
// signed positive
the_same(5i8);
the_same(5i16);
the_same(5i32);
the_same(5i64);
the_same(5isize);
// signed negative
the_same(-5i8);
the_same(-5i16);
the_same(-5i32);
the_same(-5i64);
the_same(-5isize);
// floating
the_same(-100f32);
the_same(0f32);
the_same(5f32);
the_same(-100f64);
the_same(5f64);
}
#[test]
fn test_bool() {
the_same(true);
the_same(false);
}
#[cfg(any(feature = "option_copy", feature = "option_default"))]
#[test]
fn test_option() {
the_same(Some(5usize));
//the_same(Some("foo bar".to_string()));
the_same(None::<usize>);
}
#[test]
fn test_fixed_size_array() {
the_same([24u32; 32]);
the_same([1u64, 2, 3, 4, 5, 6, 7, 8]);
the_same([0u8; 19]);
}
#[test]
fn test_tuple() {
the_same((1isize, ));
the_same((1isize, 2isize, 3isize));
the_same((1isize, ()));
}
#[test]
fn test_basic_struct() {
#[derive(Copy, Clone, Debug, Default, PartialEq, PeekPoke)]
struct Bar {
a: u32,
b: u32,
c: u32,
#[cfg(any(feature = "option_copy", feature = "option_default"))]
d: Option<u32>,
}
the_same(Bar {
a: 2,
b: 4,
c: 42,
#[cfg(any(feature = "option_copy", feature = "option_default"))]
d: None,
});
}
#[test]
fn test_enum() {
#[derive(Clone, Copy, Debug, PartialEq, PeekPoke)]
enum TestEnum {
NoArg,
OneArg(usize),
Args(usize, usize),
AnotherNoArg,
StructLike { x: usize, y: f32 },
}
impl Default for TestEnum {
fn default() -> Self {
TestEnum::NoArg
}
}
the_same(TestEnum::NoArg);
the_same(TestEnum::OneArg(4));
the_same(TestEnum::Args(4, 5));
the_same(TestEnum::AnotherNoArg);
the_same(TestEnum::StructLike { x: 4, y: 3.14159 });
}
#[test]
fn test_enum_cstyle() {
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PeekPoke)]
enum BorderStyle {
None = 0,
Solid = 1,
Double = 2,
Dotted = 3,
Dashed = 4,
Hidden = 5,
Groove = 6,
Ridge = 7,
Inset = 8,
Outset = 9,
}
impl Default for BorderStyle {
fn default() -> Self {
BorderStyle::None
}
}
the_same(BorderStyle::None);
the_same(BorderStyle::Solid);
the_same(BorderStyle::Double);
the_same(BorderStyle::Dotted);
the_same(BorderStyle::Dashed);
the_same(BorderStyle::Hidden);
the_same(BorderStyle::Groove);
the_same(BorderStyle::Ridge);
the_same(BorderStyle::Inset);
the_same(BorderStyle::Outset);
}
#[test]
fn test_phantom_data() {
struct Bar;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PeekPoke)]
struct Foo {
x: u32,
y: u32,
_marker: PhantomData<Bar>,
}
the_same(Foo {
x: 19,
y: 42,
_marker: PhantomData,
});
}
#[test]
fn test_generic() {
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PeekPoke)]
struct Foo<T> {
x: T,
y: T,
}
the_same(Foo { x: 19.0, y: 42.0 });
}
#[test]
fn test_generic_enum() {
#[derive(Clone, Copy, Debug, Default, PartialEq, PeekPoke)]
pub struct PropertyBindingKey<T> {
pub id: usize,
_phantom: PhantomData<T>,
}
#[derive(Clone, Copy, Debug, PartialEq, PeekPoke)]
pub enum PropertyBinding<T> {
Value(T),
Binding(PropertyBindingKey<T>, T),
}
impl<T: Default> Default for PropertyBinding<T> {
fn default() -> Self {
PropertyBinding::Value(Default::default())
}
}
}
#[cfg(all(feature = "extras", feature = "option_copy"))]
mod extra_tests {
use super::*;
use euclid::{Point2D, Rect, SideOffsets2D, Size2D, Transform3D, Vector2D};
use std::mem::size_of;
#[test]
fn euclid_types() {
the_same(Point2D::<f32>::new(1.0, 2.0));
assert_eq!(Point2D::<f32>::max_size(), 2 * size_of::<f32>());
the_same(Rect::<f32>::new(
Point2D::<f32>::new(0.0, 0.0),
Size2D::<f32>::new(100.0, 80.0),
));
assert_eq!(Rect::<f32>::max_size(), 4 * size_of::<f32>());
the_same(SideOffsets2D::<f32>::new(0.0, 10.0, -1.0, -10.0));
assert_eq!(SideOffsets2D::<f32>::max_size(), 4 * size_of::<f32>());
the_same(Transform3D::<f32>::identity());
assert_eq!(Transform3D::<f32>::max_size(), 16 * size_of::<f32>());
the_same(Vector2D::<f32>::new(1.0, 2.0));
assert_eq!(Vector2D::<f32>::max_size(), 2 * size_of::<f32>());
}
#[test]
fn webrender_api_types() {
type PipelineSourceId = i32;
#[derive(Clone, Copy, Debug, PartialEq, PeekPoke)]
struct PipelineId(pub PipelineSourceId, pub u32);
#[derive(Clone, Copy, Debug, PartialEq, PeekPoke)]
struct ClipChainId(pub u64, pub PipelineId);
#[derive(Clone, Copy, Debug, PartialEq, PeekPoke)]
struct SpatialId(pub usize, pub PipelineId);
the_same(PipelineId(42, 2));
the_same(ClipChainId(19u64, PipelineId(42, 2)));
the_same(SpatialId(19usize, PipelineId(42, 2)));
}
}

6
third_party/webrender/rustfmt.toml vendored Normal file
View file

@ -0,0 +1,6 @@
reorder_imports = false
reorder_imports_in_group = true
reorder_imported_names = true
error_on_line_overflow_comments = false
max_width = 100
spaces_around_ranges = true

39
third_party/webrender/servo-tidy.toml vendored Normal file
View file

@ -0,0 +1,39 @@
[configs]
skip-check-length = false
skip-check-licenses = false
check-alphabetical-order = false
[ignore]
# Ignored packages with duplicated versions
packages = [
"core-foundation",
"core-foundation-sys",
"core-graphics",
"gl_generator",
"gleam",
"rand",
"rand_core",
# https://github.com/trimental/andrew/issues/5
"rusttype",
# https://bugzilla.mozilla.org/show_bug.cgi?id=1615148
"smallvec",
"winapi",
"yaml-rust",
# These are tracked in bug 1587468, see there for pending work.
"proc-macro2",
"quote",
"unicode-xid",
]
# Files that are ignored for all tidy and lint checks.
files = [
"./wrench/src/egl.rs", # Copied from glutin
]
# Many directories are currently ignored while we tidy things up
# gradually.
directories = [
# Generated and upstream code combined with our own. Could use cleanup
"./target",
"./webrender/src",
]

15
third_party/webrender/swgl/Cargo.toml vendored Normal file
View file

@ -0,0 +1,15 @@
[package]
name = "swgl"
version = "0.1.0"
license = "MPL-2.0"
authors = ["The Mozilla Project Developers"]
build = "build.rs"
description = "Software OpenGL implementation for WebRender."
[build-dependencies]
cc = "1.0.46"
glsl-to-cxx = { path = "../glsl-to-cxx" }
webrender_build = { path = "../webrender_build" }
[dependencies]
gleam = "0.12.0"

4
third_party/webrender/swgl/README.md vendored Normal file
View file

@ -0,0 +1,4 @@
swgl
========
Software OpenGL implementation for WebRender

150
third_party/webrender/swgl/build.rs vendored Normal file
View file

@ -0,0 +1,150 @@
/* 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 cc;
extern crate glsl_to_cxx;
extern crate webrender_build;
use std::collections::HashSet;
use std::fmt::Write;
use webrender_build::shader::{ShaderFeatureFlags, get_shader_features};
// Shader key is in "name feature,feature" format.
// File name needs to be formatted as "name_feature_feature".
fn shader_file(shader_key: &str) -> String {
shader_key.replace(' ', "_").replace(',', "_")
}
fn write_load_shader(shader_keys: &[String]) {
let mut load_shader = String::new();
for s in shader_keys {
let _ = write!(load_shader, "#include \"{}.h\"\n", shader_file(s));
}
load_shader.push_str("ProgramLoader load_shader(const char* name) {\n");
for s in shader_keys {
let _ = write!(load_shader, " if (!strcmp(name, \"{}\")) {{ return {}_program::loader; }}\n",
s, shader_file(s));
}
load_shader.push_str(" return nullptr;\n}\n");
std::fs::write(std::env::var("OUT_DIR").unwrap() + "/load_shader.h", load_shader).unwrap();
}
fn process_imports(shader_dir: &str, shader: &str, included: &mut HashSet<String>, output: &mut String) {
if !included.insert(shader.into()) {
return;
}
println!("cargo:rerun-if-changed={}/{}.glsl", shader_dir, shader);
let source = std::fs::read_to_string(format!("{}/{}.glsl", shader_dir, shader)).unwrap();
for line in source.lines() {
if line.starts_with("#include ") {
let imports = line["#include ".len() ..].split(',');
for import in imports {
process_imports(shader_dir, import, included, output);
}
} else if line.starts_with("#version ") || line.starts_with("#extension ") {
// ignore
} else {
output.push_str(line);
output.push('\n');
}
}
}
fn translate_shader(shader_key: &str, shader_dir: &str) {
let mut imported = String::from("#define SWGL 1\n");
let _ = write!(imported, "#define WR_MAX_VERTEX_TEXTURE_WIDTH {}U\n",
webrender_build::MAX_VERTEX_TEXTURE_WIDTH);
let (basename, features) =
shader_key.split_at(shader_key.find(' ').unwrap_or(shader_key.len()));
if !features.is_empty() {
for feature in features.trim().split(',') {
let _ = write!(imported, "#define WR_FEATURE_{}\n", feature);
}
}
process_imports(shader_dir, basename, &mut HashSet::new(), &mut imported);
let shader = shader_file(shader_key);
let out_dir = std::env::var("OUT_DIR").unwrap();
let imp_name = format!("{}/{}.c", out_dir, shader);
std::fs::write(&imp_name, imported).unwrap();
let mut build = cc::Build::new();
if build.get_compiler().is_like_msvc() {
build.flag("/EP");
} else {
build.flag("-xc").flag("-P");
}
build.file(&imp_name);
let vs = build.clone()
.define("WR_VERTEX_SHADER", Some("1"))
.expand();
let fs = build.clone()
.define("WR_FRAGMENT_SHADER", Some("1"))
.expand();
let vs_name = format!("{}/{}.vert", out_dir, shader);
let fs_name = format!("{}/{}.frag", out_dir, shader);
std::fs::write(&vs_name, vs).unwrap();
std::fs::write(&fs_name, fs).unwrap();
let mut args = vec![
"glsl_to_cxx".to_string(),
vs_name,
fs_name,
];
let frag_include = format!("{}/{}.frag.h", shader_dir, shader);
if std::path::Path::new(&frag_include).exists() {
println!("cargo:rerun-if-changed={}/{}.frag.h", shader_dir, shader);
args.push(frag_include);
}
let result = glsl_to_cxx::translate(&mut args.into_iter());
std::fs::write(format!("{}/{}.h", out_dir, shader), result).unwrap();
}
fn main() {
let shader_dir = match std::env::var("MOZ_SRC") {
Ok(dir) => dir + "/gfx/wr/webrender/res",
Err(_) => std::env::var("CARGO_MANIFEST_DIR").unwrap() + "/../webrender/res",
};
let shader_flags =
ShaderFeatureFlags::GL |
ShaderFeatureFlags::DUAL_SOURCE_BLENDING;
let mut shaders: Vec<String> = Vec::new();
for (name, features) in get_shader_features(shader_flags) {
shaders.extend(features.iter().map(|f| {
if f.is_empty() { name.to_owned() } else { format!("{} {}", name, f) }
}));
}
shaders.sort();
for shader in &shaders {
translate_shader(shader, &shader_dir);
}
write_load_shader(&shaders);
println!("cargo:rerun-if-changed=src/gl_defs.h");
println!("cargo:rerun-if-changed=src/glsl.h");
println!("cargo:rerun-if-changed=src/program.h");
println!("cargo:rerun-if-changed=src/texture.h");
println!("cargo:rerun-if-changed=src/vector_type.h");
println!("cargo:rerun-if-changed=src/gl.cc");
cc::Build::new()
.cpp(true)
.file("src/gl.cc")
.flag("-std=c++14")
.flag("-UMOZILLA_CONFIG_H")
.flag("-fno-exceptions")
.flag("-fno-rtti")
.flag("-fno-math-errno")
.define("_GLIBCXX_USE_CXX11_ABI", Some("0"))
.include(shader_dir)
.include("src")
.include(std::env::var("OUT_DIR").unwrap())
.compile("gl_cc");
}

4015
third_party/webrender/swgl/src/gl.cc vendored Normal file

File diff suppressed because it is too large Load diff

176
third_party/webrender/swgl/src/gl_defs.h vendored Normal file
View file

@ -0,0 +1,176 @@
/* 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/. */
typedef int8_t GLbyte;
typedef uint8_t GLubyte;
typedef int16_t GLshort;
typedef uint16_t GLushort;
typedef int32_t GLint;
typedef uint32_t GLuint;
typedef int64_t GLint64;
typedef uint64_t GLuint64;
typedef float GLfloat;
typedef double GLdouble;
typedef uint32_t GLenum;
typedef int32_t GLboolean;
typedef uint32_t GLbitfield;
typedef int32_t GLsizei;
typedef size_t GLsizeiptr;
typedef intptr_t GLintptr;
#define GL_NO_ERROR 0
#define GL_RGBA32F 0x8814
#define GL_RGBA8 0x8058
#define GL_R8 0x8229
#define GL_RGBA32I 0x8D82
#define GL_BGRA8 0x93A1
#define GL_BYTE 0x1400
#define GL_UNSIGNED_BYTE 0x1401
#define GL_SHORT 0x1402
#define GL_UNSIGNED_SHORT 0x1403
#define GL_INT 0x1404
#define GL_UNSIGNED_INT 0x1405
#define GL_FLOAT 0x1406
#define GL_RED 0x1903
#define GL_GREEN 0x1904
#define GL_BLUE 0x1905
#define GL_ALPHA 0x1906
#define GL_RGB 0x1907
#define GL_RGBA 0x1908
#define GL_RGBA_INTEGER 0x8D99
#define GL_BGRA 0x80E1
#define GL_DEPTH_COMPONENT 0x1902
#define GL_DEPTH_COMPONENT16 0x81A5
#define GL_DEPTH_COMPONENT24 0x81A6
#define GL_DEPTH_COMPONENT32 0x81A7
#define GL_ARRAY_BUFFER 0x8892
#define GL_ELEMENT_ARRAY_BUFFER 0x8893
#define GL_READ_FRAMEBUFFER 0x8CA8
#define GL_DRAW_FRAMEBUFFER 0x8CA9
#define GL_FRAMEBUFFER 0x8D40
#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6
#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA
#define GL_RENDERBUFFER 0x8D41
#define GL_COLOR_ATTACHMENT0 0x8CE0
#define GL_DEPTH_ATTACHMENT 0x8D00
#define GL_STENCIL_ATTACHMENT 0x8D20
#define GL_FRAMEBUFFER_COMPLETE 0x8CD5
#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6
#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7
#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB
#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC
#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD
#define GL_COLOR_BUFFER_BIT 0x00004000
#define GL_DEPTH_BUFFER_BIT 0x00000100
#define GL_STENCIL_BUFFER_BIT 0x00000400
#define GL_PIXEL_PACK_BUFFER 0x88EB
#define GL_PIXEL_UNPACK_BUFFER 0x88EC
#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED
#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF
#define GL_UNPACK_ROW_LENGTH 0x0CF2
#define GL_UNPACK_ALIGNMENT 0x0CF5
#define GL_QUERY_RESULT 0x8866
#define GL_QUERY_RESULT_AVAILABLE 0x8867
#define GL_TIME_ELAPSED 0x88BF
#define GL_SAMPLES_PASSED 0x8914
#define GL_NEAREST 0x2600
#define GL_LINEAR 0x2601
#define GL_NEAREST_MIPMAP_NEAREST 0x2700
#define GL_NEAREST_MIPMAP_LINEAR 0x2702
#define GL_LINEAR_MIPMAP_NEAREST 0x2701
#define GL_LINEAR_MIPMAP_LINEAR 0x2703
#define GL_TEXTURE_WRAP_S 0x2802
#define GL_TEXTURE_WRAP_T 0x2803
#define GL_TEXTURE_MAG_FILTER 0x2800
#define GL_TEXTURE_MIN_FILTER 0x2801
#define GL_CLAMP_TO_EDGE 0x812F
#define GL_TEXTURE_2D 0x0DE1
#define GL_TEXTURE_3D 0x806F
#define GL_TEXTURE_2D_ARRAY 0x8C1A
#define GL_TEXTURE_RECTANGLE 0x84F5
#define GL_TEXTURE0 0x84C0
#define GL_TEXTURE1 0x84C1
#define GL_TEXTURE2 0x84C2
#define GL_TEXTURE3 0x84C3
#define GL_TEXTURE4 0x84C4
#define GL_TEXTURE5 0x84C5
#define GL_TEXTURE6 0x84C6
#define GL_TEXTURE7 0x84C7
#define GL_TEXTURE8 0x84C8
#define GL_TEXTURE9 0x84C9
#define GL_TEXTURE10 0x84CA
#define GL_TEXTURE11 0x84CB
#define GL_TEXTURE12 0x84CC
#define GL_TEXTURE13 0x84CD
#define GL_TEXTURE14 0x84CE
#define GL_TEXTURE15 0x84CF
#define GL_MAX_TEXTURE_UNITS 0x84E2
#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872
#define GL_MAX_TEXTURE_SIZE 0x0D33
#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF
#define GL_VERTEX_SHADER 0x8B31
#define GL_FRAGMENT_SHADER 0x8B30
#define GL_BLEND 0x0BE2
#define GL_ZERO 0
#define GL_ONE 1
#define GL_SRC_COLOR 0x0300
#define GL_ONE_MINUS_SRC_COLOR 0x0301
#define GL_SRC_ALPHA 0x0302
#define GL_ONE_MINUS_SRC_ALPHA 0x0303
#define GL_DST_ALPHA 0x0304
#define GL_ONE_MINUS_DST_ALPHA 0x0305
#define GL_DST_COLOR 0x0306
#define GL_ONE_MINUS_DST_COLOR 0x0307
#define GL_CONSTANT_COLOR 0x8001
#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002
#define GL_CONSTANT_ALPHA 0x8003
#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004
#define GL_SRC1_ALPHA 0x8589
#define GL_SRC1_COLOR 0x88F9
#define GL_ONE_MINUS_SRC1_COLOR 0x88FA
#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB
#define GL_FUNC_ADD 0x8006
#define GL_NEVER 0x0200
#define GL_LESS 0x0201
#define GL_EQUAL 0x0202
#define GL_LEQUAL 0x0203
#define GL_GREATER 0x0204
#define GL_NOTEQUAL 0x0205
#define GL_GEQUAL 0x0206
#define GL_ALWAYS 0x0207
#define GL_DEPTH_TEST 0x0B71
#define GL_DEPTH_WRITEMASK 0x0B72
#define GL_SCISSOR_TEST 0x0C11
#define GL_VENDOR 0x1F00
#define GL_RENDERER 0x1F01
#define GL_VERSION 0x1F02
#define GL_EXTENSIONS 0x1F03
#define GL_NUM_EXTENSIONS 0x821D
#define GL_POINTS 0x0000
#define GL_LINES 0x0001
#define GL_LINE_LOOP 0x0002
#define GL_LINE_STRIP 0x0003
#define GL_TRIANGLES 0x0004
#define GL_TRIANGLE_STRIP 0x0005
#define GL_TRIANGLE_FAN 0x0006
#define GL_QUADS 0x0007

3240
third_party/webrender/swgl/src/glsl.h vendored Normal file

File diff suppressed because it is too large Load diff

12
third_party/webrender/swgl/src/lib.rs vendored Normal file
View file

@ -0,0 +1,12 @@
/* 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/. */
#![crate_name = "swgl"]
#![crate_type = "lib"]
extern crate gleam;
mod swgl_fns;
pub use crate::swgl_fns::*;

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