Bug 1507524 - Move webrender to gfx/wr. r=jrmuizel

This patch copies the webrender repository contents into gfx/wr. The
existing files from gfx/webrender, gfx/webrender_api, and gfx/wrench are
moved, and the remaining files are added. The revision being used is the
same as before.

In addition, the mozilla-central top-level Cargo.toml and the
gfx/webrender_bindings/Cargo.toml files are updated to reflect the new
structure.

Differential Revision: https://phabricator.services.mozilla.com/D12059

--HG--
rename : gfx/webrender/src/device/mod.rs => gfx/wr/direct-composition/src/main.rs
rename : gfx/webrender/Cargo.toml => gfx/wr/webrender/Cargo.toml
rename : gfx/webrender/build.rs => gfx/wr/webrender/build.rs
rename : gfx/webrender/doc/CLIPPING_AND_POSITIONING.md => gfx/wr/webrender/doc/CLIPPING_AND_POSITIONING.md
rename : gfx/webrender/doc/blob.md => gfx/wr/webrender/doc/blob.md
rename : gfx/webrender/doc/text-rendering.md => gfx/wr/webrender/doc/text-rendering.md
rename : gfx/webrender/res/Proggy.ttf => gfx/wr/webrender/res/Proggy.ttf
rename : gfx/webrender/res/area-lut.tga => gfx/wr/webrender/res/area-lut.tga
rename : gfx/webrender/res/base.glsl => gfx/wr/webrender/res/base.glsl
rename : gfx/webrender/res/brush.glsl => gfx/wr/webrender/res/brush.glsl
rename : gfx/webrender/res/brush_blend.glsl => gfx/wr/webrender/res/brush_blend.glsl
rename : gfx/webrender/res/brush_image.glsl => gfx/wr/webrender/res/brush_image.glsl
rename : gfx/webrender/res/brush_linear_gradient.glsl => gfx/wr/webrender/res/brush_linear_gradient.glsl
rename : gfx/webrender/res/brush_mix_blend.glsl => gfx/wr/webrender/res/brush_mix_blend.glsl
rename : gfx/webrender/res/brush_radial_gradient.glsl => gfx/wr/webrender/res/brush_radial_gradient.glsl
rename : gfx/webrender/res/brush_solid.glsl => gfx/wr/webrender/res/brush_solid.glsl
rename : gfx/webrender/res/brush_yuv_image.glsl => gfx/wr/webrender/res/brush_yuv_image.glsl
rename : gfx/webrender/res/clip_shared.glsl => gfx/wr/webrender/res/clip_shared.glsl
rename : gfx/webrender/res/cs_blur.glsl => gfx/wr/webrender/res/cs_blur.glsl
rename : gfx/webrender/res/cs_border_segment.glsl => gfx/wr/webrender/res/cs_border_segment.glsl
rename : gfx/webrender/res/cs_border_solid.glsl => gfx/wr/webrender/res/cs_border_solid.glsl
rename : gfx/webrender/res/cs_clip_box_shadow.glsl => gfx/wr/webrender/res/cs_clip_box_shadow.glsl
rename : gfx/webrender/res/cs_clip_image.glsl => gfx/wr/webrender/res/cs_clip_image.glsl
rename : gfx/webrender/res/cs_clip_rectangle.glsl => gfx/wr/webrender/res/cs_clip_rectangle.glsl
rename : gfx/webrender/res/cs_line_decoration.glsl => gfx/wr/webrender/res/cs_line_decoration.glsl
rename : gfx/webrender/res/cs_scale.glsl => gfx/wr/webrender/res/cs_scale.glsl
rename : gfx/webrender/res/debug_color.glsl => gfx/wr/webrender/res/debug_color.glsl
rename : gfx/webrender/res/debug_font.glsl => gfx/wr/webrender/res/debug_font.glsl
rename : gfx/webrender/res/ellipse.glsl => gfx/wr/webrender/res/ellipse.glsl
rename : gfx/webrender/res/gpu_cache.glsl => gfx/wr/webrender/res/gpu_cache.glsl
rename : gfx/webrender/res/gpu_cache_update.glsl => gfx/wr/webrender/res/gpu_cache_update.glsl
rename : gfx/webrender/res/pf_vector_cover.glsl => gfx/wr/webrender/res/pf_vector_cover.glsl
rename : gfx/webrender/res/pf_vector_stencil.glsl => gfx/wr/webrender/res/pf_vector_stencil.glsl
rename : gfx/webrender/res/prim_shared.glsl => gfx/wr/webrender/res/prim_shared.glsl
rename : gfx/webrender/res/ps_split_composite.glsl => gfx/wr/webrender/res/ps_split_composite.glsl
rename : gfx/webrender/res/ps_text_run.glsl => gfx/wr/webrender/res/ps_text_run.glsl
rename : gfx/webrender/res/rect.glsl => gfx/wr/webrender/res/rect.glsl
rename : gfx/webrender/res/render_task.glsl => gfx/wr/webrender/res/render_task.glsl
rename : gfx/webrender/res/shared.glsl => gfx/wr/webrender/res/shared.glsl
rename : gfx/webrender/res/shared_other.glsl => gfx/wr/webrender/res/shared_other.glsl
rename : gfx/webrender/res/snap.glsl => gfx/wr/webrender/res/snap.glsl
rename : gfx/webrender/res/transform.glsl => gfx/wr/webrender/res/transform.glsl
rename : gfx/webrender/src/batch.rs => gfx/wr/webrender/src/batch.rs
rename : gfx/webrender/src/border.rs => gfx/wr/webrender/src/border.rs
rename : gfx/webrender/src/box_shadow.rs => gfx/wr/webrender/src/box_shadow.rs
rename : gfx/webrender/src/capture.rs => gfx/wr/webrender/src/capture.rs
rename : gfx/webrender/src/clip.rs => gfx/wr/webrender/src/clip.rs
rename : gfx/webrender/src/clip_scroll_tree.rs => gfx/wr/webrender/src/clip_scroll_tree.rs
rename : gfx/webrender/src/debug_colors.rs => gfx/wr/webrender/src/debug_colors.rs
rename : gfx/webrender/src/debug_font_data.rs => gfx/wr/webrender/src/debug_font_data.rs
rename : gfx/webrender/src/debug_render.rs => gfx/wr/webrender/src/debug_render.rs
rename : gfx/webrender/src/debug_server.rs => gfx/wr/webrender/src/debug_server.rs
rename : gfx/webrender/src/device/gl.rs => gfx/wr/webrender/src/device/gl.rs
rename : gfx/webrender/src/device/mod.rs => gfx/wr/webrender/src/device/mod.rs
rename : gfx/webrender/src/device/query_gl.rs => gfx/wr/webrender/src/device/query_gl.rs
rename : gfx/webrender/src/display_list_flattener.rs => gfx/wr/webrender/src/display_list_flattener.rs
rename : gfx/webrender/src/ellipse.rs => gfx/wr/webrender/src/ellipse.rs
rename : gfx/webrender/src/frame_builder.rs => gfx/wr/webrender/src/frame_builder.rs
rename : gfx/webrender/src/freelist.rs => gfx/wr/webrender/src/freelist.rs
rename : gfx/webrender/src/gamma_lut.rs => gfx/wr/webrender/src/gamma_lut.rs
rename : gfx/webrender/src/glyph_cache.rs => gfx/wr/webrender/src/glyph_cache.rs
rename : gfx/webrender/src/glyph_rasterizer/mod.rs => gfx/wr/webrender/src/glyph_rasterizer/mod.rs
rename : gfx/webrender/src/glyph_rasterizer/no_pathfinder.rs => gfx/wr/webrender/src/glyph_rasterizer/no_pathfinder.rs
rename : gfx/webrender/src/glyph_rasterizer/pathfinder.rs => gfx/wr/webrender/src/glyph_rasterizer/pathfinder.rs
rename : gfx/webrender/src/gpu_cache.rs => gfx/wr/webrender/src/gpu_cache.rs
rename : gfx/webrender/src/gpu_glyph_renderer.rs => gfx/wr/webrender/src/gpu_glyph_renderer.rs
rename : gfx/webrender/src/gpu_types.rs => gfx/wr/webrender/src/gpu_types.rs
rename : gfx/webrender/src/hit_test.rs => gfx/wr/webrender/src/hit_test.rs
rename : gfx/webrender/src/image.rs => gfx/wr/webrender/src/image.rs
rename : gfx/webrender/src/intern.rs => gfx/wr/webrender/src/intern.rs
rename : gfx/webrender/src/internal_types.rs => gfx/wr/webrender/src/internal_types.rs
rename : gfx/webrender/src/lib.rs => gfx/wr/webrender/src/lib.rs
rename : gfx/webrender/src/picture.rs => gfx/wr/webrender/src/picture.rs
rename : gfx/webrender/src/platform/macos/font.rs => gfx/wr/webrender/src/platform/macos/font.rs
rename : gfx/webrender/src/platform/unix/font.rs => gfx/wr/webrender/src/platform/unix/font.rs
rename : gfx/webrender/src/platform/windows/font.rs => gfx/wr/webrender/src/platform/windows/font.rs
rename : gfx/webrender/src/prim_store.rs => gfx/wr/webrender/src/prim_store.rs
rename : gfx/webrender/src/print_tree.rs => gfx/wr/webrender/src/print_tree.rs
rename : gfx/webrender/src/profiler.rs => gfx/wr/webrender/src/profiler.rs
rename : gfx/webrender/src/record.rs => gfx/wr/webrender/src/record.rs
rename : gfx/webrender/src/render_backend.rs => gfx/wr/webrender/src/render_backend.rs
rename : gfx/webrender/src/render_task.rs => gfx/wr/webrender/src/render_task.rs
rename : gfx/webrender/src/renderer.rs => gfx/wr/webrender/src/renderer.rs
rename : gfx/webrender/src/resource_cache.rs => gfx/wr/webrender/src/resource_cache.rs
rename : gfx/webrender/src/scene.rs => gfx/wr/webrender/src/scene.rs
rename : gfx/webrender/src/scene_builder.rs => gfx/wr/webrender/src/scene_builder.rs
rename : gfx/webrender/src/segment.rs => gfx/wr/webrender/src/segment.rs
rename : gfx/webrender/src/shade.rs => gfx/wr/webrender/src/shade.rs
rename : gfx/webrender/src/spatial_node.rs => gfx/wr/webrender/src/spatial_node.rs
rename : gfx/webrender/src/surface.rs => gfx/wr/webrender/src/surface.rs
rename : gfx/webrender/src/texture_allocator.rs => gfx/wr/webrender/src/texture_allocator.rs
rename : gfx/webrender/src/texture_cache.rs => gfx/wr/webrender/src/texture_cache.rs
rename : gfx/webrender/src/tiling.rs => gfx/wr/webrender/src/tiling.rs
rename : gfx/webrender/src/util.rs => gfx/wr/webrender/src/util.rs
rename : gfx/webrender/tests/angle_shader_validation.rs => gfx/wr/webrender/tests/angle_shader_validation.rs
rename : gfx/webrender/tests/bug_124.html => gfx/wr/webrender/tests/bug_124.html
rename : gfx/webrender/tests/bug_134.html => gfx/wr/webrender/tests/bug_134.html
rename : gfx/webrender/tests/bug_137.html => gfx/wr/webrender/tests/bug_137.html
rename : gfx/webrender/tests/bug_143.html => gfx/wr/webrender/tests/bug_143.html
rename : gfx/webrender/tests/bug_159.html => gfx/wr/webrender/tests/bug_159.html
rename : gfx/webrender/tests/bug_166.html => gfx/wr/webrender/tests/bug_166.html
rename : gfx/webrender/tests/bug_176.html => gfx/wr/webrender/tests/bug_176.html
rename : gfx/webrender/tests/bug_177.html => gfx/wr/webrender/tests/bug_177.html
rename : gfx/webrender/tests/bug_178.html => gfx/wr/webrender/tests/bug_178.html
rename : gfx/webrender/tests/bug_203a.html => gfx/wr/webrender/tests/bug_203a.html
rename : gfx/webrender/tests/bug_203b.html => gfx/wr/webrender/tests/bug_203b.html
rename : gfx/webrender/tests/bug_servo_10136.html => gfx/wr/webrender/tests/bug_servo_10136.html
rename : gfx/webrender/tests/bug_servo_10164.html => gfx/wr/webrender/tests/bug_servo_10164.html
rename : gfx/webrender/tests/bug_servo_10307.html => gfx/wr/webrender/tests/bug_servo_10307.html
rename : gfx/webrender/tests/bug_servo_11358.html => gfx/wr/webrender/tests/bug_servo_11358.html
rename : gfx/webrender/tests/bug_servo_9983a.html => gfx/wr/webrender/tests/bug_servo_9983a.html
rename : gfx/webrender/tests/color_pattern_1.png => gfx/wr/webrender/tests/color_pattern_1.png
rename : gfx/webrender/tests/color_pattern_2.png => gfx/wr/webrender/tests/color_pattern_2.png
rename : gfx/webrender/tests/fixed-position.html => gfx/wr/webrender/tests/fixed-position.html
rename : gfx/webrender/tests/mix-blend-mode-2.html => gfx/wr/webrender/tests/mix-blend-mode-2.html
rename : gfx/webrender/tests/mix-blend-mode.html => gfx/wr/webrender/tests/mix-blend-mode.html
rename : gfx/webrender/tests/nav-1.html => gfx/wr/webrender/tests/nav-1.html
rename : gfx/webrender/tests/nav-2.html => gfx/wr/webrender/tests/nav-2.html
rename : gfx/webrender_api/Cargo.toml => gfx/wr/webrender_api/Cargo.toml
rename : gfx/webrender_api/src/api.rs => gfx/wr/webrender_api/src/api.rs
rename : gfx/webrender_api/src/channel.rs => gfx/wr/webrender_api/src/channel.rs
rename : gfx/webrender_api/src/channel_ipc.rs => gfx/wr/webrender_api/src/channel_ipc.rs
rename : gfx/webrender_api/src/channel_mpsc.rs => gfx/wr/webrender_api/src/channel_mpsc.rs
rename : gfx/webrender_api/src/color.rs => gfx/wr/webrender_api/src/color.rs
rename : gfx/webrender_api/src/display_item.rs => gfx/wr/webrender_api/src/display_item.rs
rename : gfx/webrender_api/src/display_list.rs => gfx/wr/webrender_api/src/display_list.rs
rename : gfx/webrender_api/src/font.rs => gfx/wr/webrender_api/src/font.rs
rename : gfx/webrender_api/src/gradient_builder.rs => gfx/wr/webrender_api/src/gradient_builder.rs
rename : gfx/webrender_api/src/image.rs => gfx/wr/webrender_api/src/image.rs
rename : gfx/webrender_api/src/lib.rs => gfx/wr/webrender_api/src/lib.rs
rename : gfx/webrender_api/src/units.rs => gfx/wr/webrender_api/src/units.rs
rename : gfx/wrench/.gitignore => gfx/wr/wrench/.gitignore
rename : gfx/wrench/Cargo.toml => gfx/wr/wrench/Cargo.toml
rename : gfx/wrench/README.md => gfx/wr/wrench/README.md
rename : gfx/wrench/build.rs => gfx/wr/wrench/build.rs
rename : gfx/webrender/res/Proggy.ttf => gfx/wr/wrench/reftests/text/Proggy.ttf
rename : gfx/wrench/res/wrench.exe.manifest => gfx/wr/wrench/res/wrench.exe.manifest
rename : gfx/wrench/src/angle.rs => gfx/wr/wrench/src/angle.rs
rename : gfx/wrench/src/args.yaml => gfx/wr/wrench/src/args.yaml
rename : gfx/wrench/src/binary_frame_reader.rs => gfx/wr/wrench/src/binary_frame_reader.rs
rename : gfx/wrench/src/blob.rs => gfx/wr/wrench/src/blob.rs
rename : gfx/wrench/src/cgfont_to_data.rs => gfx/wr/wrench/src/cgfont_to_data.rs
rename : gfx/wrench/src/egl.rs => gfx/wr/wrench/src/egl.rs
rename : gfx/wrench/src/json_frame_writer.rs => gfx/wr/wrench/src/json_frame_writer.rs
rename : gfx/wrench/src/main.rs => gfx/wr/wrench/src/main.rs
rename : gfx/wrench/src/parse_function.rs => gfx/wr/wrench/src/parse_function.rs
rename : gfx/wrench/src/perf.rs => gfx/wr/wrench/src/perf.rs
rename : gfx/wrench/src/png.rs => gfx/wr/wrench/src/png.rs
rename : gfx/wrench/src/premultiply.rs => gfx/wr/wrench/src/premultiply.rs
rename : gfx/wrench/src/rawtest.rs => gfx/wr/wrench/src/rawtest.rs
rename : gfx/wrench/src/reftest.rs => gfx/wr/wrench/src/reftest.rs
rename : gfx/wrench/src/ron_frame_writer.rs => gfx/wr/wrench/src/ron_frame_writer.rs
rename : gfx/wrench/src/scene.rs => gfx/wr/wrench/src/scene.rs
rename : gfx/wrench/src/wrench.rs => gfx/wr/wrench/src/wrench.rs
rename : gfx/wrench/src/yaml_frame_reader.rs => gfx/wr/wrench/src/yaml_frame_reader.rs
rename : gfx/wrench/src/yaml_frame_writer.rs => gfx/wr/wrench/src/yaml_frame_writer.rs
rename : gfx/wrench/src/yaml_helper.rs => gfx/wr/wrench/src/yaml_helper.rs
extra : moz-landing-system : lando
This commit is contained in:
Kartikaya Gupta 2018-11-22 21:47:48 +00:00
parent cef4893007
commit 2719e9d65e
893 changed files with 38717 additions and 8 deletions

View File

@ -20,24 +20,19 @@ members = [
# of the workspace and their dev-dependencies won't be included.
exclude = [
# Exclude third-party code vendored into mozilla-central.
"gfx/webrender",
"gfx/webrender_api",
"gfx/webrender_bindings",
"servo",
"third_party/rust",
# Excluded because this is a standalone tool for developers and not intended
# to be built as part of mozilla-central and is not shipped to users.
"gfx/wrench",
# Excluded because these crates have their own Cargo workspaces so they can't
# be included in the top-level one.
"gfx/wr",
"media/audioipc",
"media/cubeb-rs",
# Excluded because they are used only as dependencies, not top-level targets,
# so we don't need to vendor their dev-dependencies.
"dom/webauthn/u2f-hid-rs",
"gfx/webrender_bindings",
"media/mp4parse-rust/mp4parse",
"media/mp4parse-rust/mp4parse_capi",
"media/mp4parse-rust/mp4parse_fallible",

View File

@ -17,7 +17,7 @@ uuid = { version = "0.6", features = ["v4"] }
fxhash = "0.2.1"
[dependencies.webrender]
path = "../webrender"
path = "../wr/webrender"
version = "0.57.2"
default-features = false
features = ["capture", "serialize_program"]

27
gfx/wr/.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*

224
gfx/wr/.taskcluster.yml Normal file
View File

@ -0,0 +1,224 @@
# 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 at https://tools.taskcluster.net/quickstart/ and then expanded
# as needed.
#
version: 0
allowPullRequests: public
metadata:
name: WebRender
description: Runs WebRender tests in TaskCluster
owner: '{{ event.head.user.email }}'
source: '{{ event.head.repo.url }}'
# 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:
# For the docker-worker tasks, the Docker image used
# (staktrace/webrender-test:freetype28) was created using this Dockerfile:
# ---
# FROM ubuntu:16.04
# RUN apt-get -y update && apt-get install -y curl git python python-pip cmake pkg-config libx11-dev libgl1-mesa-dev libfontconfig1-dev software-properties-common
# RUN add-apt-repository -y -u ppa:glasen/freetype2
# RUN apt-get -y install freetype2-demos
# RUN pip install mako voluptuous PyYAML servo-tidy
# RUN useradd -d /home/worker -s /bin/bash -m worker
# USER worker
# WORKDIR /home/worker
# ENV PATH $PATH:/home/worker/.cargo/bin
# CMD /bin/bash
# ---
#
# 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: '{{ event.head.user.email }}'
source: '{{ event.head.repo.url }}'
provisionerId: '{{ taskcluster.docker.provisionerId }}'
workerType: '{{ taskcluster.docker.workerType }}'
extra:
github:
events:
- pull_request.opened
- pull_request.synchronize
- push
excludeBranches:
- master
payload:
maxRunTime: 7200
image: 'staktrace/webrender-test:freetype28'
env:
RUST_BACKTRACE: 'full'
RUSTFLAGS: '--deny warnings'
command:
- /bin/bash
- '--login'
- '-c'
- >-
curl https://sh.rustup.rs -sSf | sh -s -- -y &&
git clone {{event.head.repo.url}} webrender && cd webrender &&
git checkout {{event.head.sha}} &&
servo-tidy &&
(cd wrench && python script/headless.py reftest) &&
(cd wrench && python script/headless.py rawtest) &&
(cd wrench && cargo build --release)
routes:
- "index.garbage.webrender.ci.{{event.head.user.login}}.{{event.head.repo.branch}}.linux-release"
- metadata:
name: Linux debug tests
description: Runs debug-mode WebRender CI stuff on a Linux TC worker
owner: '{{ event.head.user.email }}'
source: '{{ event.head.repo.url }}'
provisionerId: '{{ taskcluster.docker.provisionerId }}'
workerType: '{{ taskcluster.docker.workerType }}'
extra:
github:
events:
- pull_request.opened
- pull_request.synchronize
- push
excludeBranches:
- master
payload:
maxRunTime: 7200
image: 'staktrace/webrender-test:freetype28'
env:
RUST_BACKTRACE: 'full'
RUSTFLAGS: '--deny warnings'
command:
- /bin/bash
- '--login'
- '-c'
- >-
curl https://sh.rustup.rs -sSf | sh -s -- -y &&
git clone {{event.head.repo.url}} webrender && cd webrender &&
git checkout {{event.head.sha}} &&
servo-tidy &&
(cd webrender_api && cargo test --verbose --features "ipc") &&
(cd webrender && cargo build --verbose --no-default-features) &&
(cd webrender && cargo build --verbose --no-default-features --features capture) &&
(cd webrender && cargo build --verbose --features capture,profiler) &&
(cd webrender && cargo build --verbose --features replay) &&
(cd wrench && cargo build --verbose --features env_logger) &&
(cd examples && cargo build --verbose) &&
(cargo test --all --verbose)
routes:
- "index.garbage.webrender.ci.{{event.head.user.login}}.{{event.head.repo.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 worker-type
# of kats-webrender-ci-osx. They are set up with all the dependencies needed
# to build and test webrender, including Rust (currently 1.24), 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.
- metadata:
name: OS X release tests
description: Runs release-mode WebRender CI stuff on a OS X TC worker
owner: '{{ event.head.user.email }}'
source: '{{ event.head.repo.url }}'
provisionerId: 'localprovisioner'
workerType: 'webrender-ci-osx'
extra:
github:
events:
- pull_request.opened
- pull_request.synchronize
- push
excludeBranches:
- master
payload:
maxRunTime: 3600
command:
- - /bin/bash
- '--login'
- '-vec'
- |
git clone {{event.head.repo.url}} webrender
cd webrender
git checkout {{event.head.sha}}
source $HOME/servotidy-venv/bin/activate
servo-tidy
sccache --stop-server || true
mkdir -p ../artifacts
RUST_LOG=sccache=trace SCCACHE_ERROR_LOG=$PWD/../artifacts/sccache.log sccache --start-server
export RUST_BACKTRACE=full
export RUSTFLAGS='--deny warnings'
export PKG_CONFIG_PATH="/usr/local/opt/zlib/lib/pkgconfig:$PKG_CONFIG_PATH"
export RUSTC_WRAPPER=sccache
echo 'exec make -j1 "$@"' > $HOME/make # See #2638
chmod +x $HOME/make
export MAKE="$HOME/make"
(cd wrench && python script/headless.py reftest)
(cd wrench && cargo build --release)
(cd wrench && cargo run --release -- --precache reftest reftests/clip/fixed-position-clipping.yaml)
sccache --stop-server || true
artifacts:
- name: public/sccache.log
path: artifacts/sccache.log
type: file
routes:
- "index.garbage.webrender.ci.{{event.head.user.login}}.{{event.head.repo.branch}}.osx-release"
- metadata:
name: OS X debug tests
description: Runs debug-mode WebRender CI stuff on a OS X TC worker
owner: '{{ event.head.user.email }}'
source: '{{ event.head.repo.url }}'
provisionerId: 'localprovisioner'
workerType: 'webrender-ci-osx'
extra:
github:
events:
- pull_request.opened
- pull_request.synchronize
- push
excludeBranches:
- master
payload:
maxRunTime: 3600
command:
- - /bin/bash
- '--login'
- '-vec'
- |
git clone {{event.head.repo.url}} webrender
cd webrender
git checkout {{event.head.sha}}
source $HOME/servotidy-venv/bin/activate
servo-tidy
sccache --stop-server || true
mkdir -p ../artifacts
RUST_LOG=sccache=trace SCCACHE_ERROR_LOG=$PWD/../artifacts/sccache.log sccache --start-server
export RUST_BACKTRACE=full
export RUSTFLAGS='--deny warnings'
export PKG_CONFIG_PATH="/usr/local/opt/zlib/lib/pkgconfig:$PKG_CONFIG_PATH"
export RUSTC_WRAPPER=sccache
echo 'exec make -j1 "$@"' > $HOME/make # See #2638
chmod +x $HOME/make
export MAKE="$HOME/make"
(cd webrender_api && cargo test --verbose --features "ipc")
(cd webrender && cargo check --verbose --no-default-features)
(cd webrender && cargo check --verbose --no-default-features --features capture)
(cd webrender && cargo check --verbose --features capture,profiler)
(cd webrender && cargo check --verbose --features replay)
(cd webrender && cargo check --verbose --no-default-features --features pathfinder)
(cd wrench && cargo check --verbose --features env_logger)
(cd examples && cargo check --verbose)
(cargo test --all --verbose)
sccache --stop-server || true
artifacts:
- name: public/sccache.log
path: artifacts/sccache.log
type: file
routes:
- "index.garbage.webrender.ci.{{event.head.user.login}}.{{event.head.repo.branch}}.osx-debug"

1864
gfx/wr/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

16
gfx/wr/Cargo.toml Normal file
View File

@ -0,0 +1,16 @@
[workspace]
license = "MPL-2.0"
members = [
"direct-composition",
"examples",
"webrender",
"webrender_api",
"wrench",
]
[profile.release]
debug = true
panic = "abort"
[patch.crates-io]
serde_derive = { git = "https://github.com/servo/serde", branch = "deserialize_from_enums9", feature="deserialize_in_place" }

374
gfx/wr/LICENSE 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.

39
gfx/wr/README.md Normal file
View File

@ -0,0 +1,39 @@
# WebRender
GPU renderer for the Web content, used by Servo.
## 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.

30
gfx/wr/appveyor.yml Normal file
View File

@ -0,0 +1,30 @@
before_test:
- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/set-screenresolution.ps1'))
- ps: Set-ScreenResolution 1920 1080
environment:
PATH: 'C:\msys64\mingw64\bin;C:\msys64\usr\bin;%PATH%;C:\Rust\bin'
RUST_BACKTRACE: 1
TARGET: x86_64-pc-windows-msvc
install:
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-1.27.0-${env:TARGET}.msi"
- msiexec /passive /i "rust-1.27.0-%TARGET%.msi" ADDLOCAL=Rustc,Cargo,Std INSTALLDIR=C:\Rust
- rustc -V
- cargo -V
build: false
test_script:
- cd webrender_api
- cargo test --verbose
- cd ../webrender
- cargo test --verbose
- cargo check --verbose --no-default-features --features pathfinder
- cd ../wrench
- cargo test --verbose
- cargo run --release -- --angle reftest
- cd ../examples
- cargo check --verbose
- cd ../direct-composition
- cargo check --verbose

6
gfx/wr/debugger/.babelrc Normal file
View File

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

View File

@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

11
gfx/wr/debugger/.gitignore vendored Normal file
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

23
gfx/wr/debugger/README.md Normal file
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.

12
gfx/wr/debugger/dist/build.js vendored Normal file

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>

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.2",
"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,97 @@
<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>
</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");
}
}
},
}
</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/lib/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,13 @@
[package]
name = "direct-composition"
version = "0.1.0"
authors = ["Simon Sapin <simon.sapin@exyr.org>"]
license = "MPL-2.0"
[target.'cfg(windows)'.dependencies]
euclid = "0.19"
gleam = "0.6.2"
mozangle = {version = "0.1", features = ["egl"]}
webrender = {path = "../webrender"}
winapi = {version = "0.3", features = ["winerror", "d3d11", "dcomp"]}
winit = "0.16"

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<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<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,201 @@
/* 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 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 size = api::DeviceIntSize::new;
let mut rects = [
Rectangle::new(&composition, &notifier, factor, size(300, 200), 0., 0.2, 0.4, 1.),
Rectangle::new(&composition, &notifier, factor, size(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::DeviceIntSize,
color: api::ColorF,
}
impl Rectangle {
fn new(composition: &DirectComposition, notifier: &Box<Notifier>,
device_pixel_ratio: f32, size: api::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,
).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::TypedScale::new(device_pixel_ratio);
let mut builder = api::DisplayListBuilder::new(pipeline_id, layout_size);
let rect = euclid::TypedRect::new(euclid::TypedPoint2D::zero(), layout_size);
let region = api::ComplexClipRegion::new(
rect,
api::BorderRadius::uniform(20.),
api::ClipMode::Clip
);
let clip_id = builder.define_clip(rect, vec![region], None);
builder.push_clip_id(clip_id);
builder.push_rect(&api::PrimitiveInfo::new(rect), self.color);
builder.pop_clip_id();
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<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,67 @@
[package]
name = "webrender-examples"
version = "0.1.0"
authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
license = "MPL-2.0"
repository = "https://github.com/servo/webrender"
[[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", "webrender/debug_renderer"]
[dependencies]
app_units = "0.7"
env_logger = "0.5"
euclid = "0.19"
gleam = "0.6.2"
glutin = "0.17"
rayon = "1"
webrender = { path = "../webrender" }
winit = "0.16"

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,90 @@
/* 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 boilerplate::{Example, HandyDandyRectBuilder};
use std::cmp;
use webrender::api::*;
struct App {
rect_count: usize,
}
impl Example for App {
fn render(
&mut self,
_api: &RenderApi,
builder: &mut DisplayListBuilder,
_txn: &mut Transaction,
_framebuffer_size: DeviceIntSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let bounds = (0, 0).to(1920, 1080);
let info = LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
None,
TransformStyle::Flat,
MixBlendMode::Normal,
&[],
RasterSpace::Screen,
);
for _ in 0 .. self.rect_count {
builder.push_rect(&info, ColorF::new(1.0, 1.0, 1.0, 0.05));
}
builder.pop_stacking_context();
}
fn on_event(
&mut self,
event: winit::WindowEvent,
_api: &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,152 @@
/* 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 boilerplate::{Example, HandyDandyRectBuilder};
use euclid::Angle;
use webrender::api::*;
struct App {
property_key: PropertyBindingKey<LayoutTransform>,
opacity_key: PropertyBindingKey<f32>,
transform: LayoutTransform,
opacity: f32,
}
impl Example for App {
fn render(
&mut self,
_api: &RenderApi,
builder: &mut DisplayListBuilder,
_txn: &mut Transaction,
_framebuffer_size: DeviceIntSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
// Create a 200x200 stacking context with an animated transform property.
let bounds = (0, 0).to(200, 200);
let filters = [
FilterOp::Opacity(PropertyBinding::Binding(self.opacity_key, self.opacity), self.opacity),
];
let info = LayoutPrimitiveInfo::new(bounds);
let reference_frame_id = builder.push_reference_frame(
&info,
Some(PropertyBinding::Binding(self.property_key, LayoutTransform::identity())),
None,
);
builder.push_clip_id(reference_frame_id);
let info = LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
None,
TransformStyle::Flat,
MixBlendMode::Normal,
&filters,
RasterSpace::Screen,
);
let complex_clip = ComplexClipRegion {
rect: bounds,
radii: BorderRadius::uniform(50.0),
mode: ClipMode::Clip,
};
let clip_id = builder.define_clip(bounds, vec![complex_clip], None);
builder.push_clip_id(clip_id);
// Fill it with a white rect
builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0));
builder.pop_clip_id();
builder.pop_stacking_context();
builder.pop_clip_id();
builder.pop_reference_frame();
}
fn on_event(&mut self, win_event: winit::WindowEvent, api: &RenderApi, document_id: DocumentId) -> bool {
match win_event {
winit::WindowEvent::KeyboardInput {
input: winit::KeyboardInput {
state: winit::ElementState::Pressed,
virtual_keycode: Some(key),
..
},
..
} => {
let (offset_x, offset_y, angle, delta_opacity) = match key {
winit::VirtualKeyCode::Down => (0.0, 10.0, 0.0, 0.0),
winit::VirtualKeyCode::Up => (0.0, -10.0, 0.0, 0.0),
winit::VirtualKeyCode::Right => (10.0, 0.0, 0.0, 0.0),
winit::VirtualKeyCode::Left => (-10.0, 0.0, 0.0, 0.0),
winit::VirtualKeyCode::Comma => (0.0, 0.0, 0.1, 0.0),
winit::VirtualKeyCode::Period => (0.0, 0.0, -0.1, 0.0),
winit::VirtualKeyCode::Z => (0.0, 0.0, 0.0, -0.1),
winit::VirtualKeyCode::X => (0.0, 0.0, 0.0, 0.1),
_ => 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;
let new_transform = self.transform
.pre_rotate(0.0, 0.0, 1.0, Angle::radians(angle))
.post_translate(LayoutVector3D::new(offset_x, offset_y, 0.0));
let mut txn = Transaction::new();
txn.update_dynamic_properties(
DynamicProperties {
transforms: vec![
PropertyValue {
key: self.property_key,
value: new_transform,
},
],
floats: vec![
PropertyValue {
key: self.opacity_key,
value: self.opacity,
}
],
},
);
txn.generate_frame();
api.send_transaction(document_id, txn);
self.transform = new_transform;
}
_ => (),
}
false
}
}
fn main() {
let mut app = App {
property_key: PropertyBindingKey::new(42), // arbitrary magic number
opacity_key: PropertyBindingKey::new(43),
transform: LayoutTransform::create_translation(0.0, 0.0, 0.0),
opacity: 0.5,
};
boilerplate::main_wrapper(&mut app, None);
}

295
gfx/wr/examples/basic.rs Normal file
View File

@ -0,0 +1,295 @@
/* 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 app_units;
extern crate euclid;
extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate winit;
#[path = "common/boilerplate.rs"]
mod boilerplate;
use boilerplate::{Example, HandyDandyRectBuilder};
use euclid::vec2;
use winit::TouchPhase;
use std::collections::HashMap;
use webrender::ShaderPrecacheFlags;
use webrender::api::*;
#[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: &RenderApi,
builder: &mut DisplayListBuilder,
txn: &mut Transaction,
_: DeviceIntSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
let info = LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
None,
TransformStyle::Flat,
MixBlendMode::Normal,
&[],
RasterSpace::Screen,
);
let image_mask_key = api.generate_image_key();
txn.add_image(
image_mask_key,
ImageDescriptor::new(2, 2, ImageFormat::R8, true, false),
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 id = builder.define_clip(bounds, vec![complex], Some(mask));
builder.push_clip_id(id);
let info = LayoutPrimitiveInfo::new((100, 100).to(200, 200));
builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0));
let info = LayoutPrimitiveInfo::new((250, 100).to(350, 200));
builder.push_rect(&info, 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 info = LayoutPrimitiveInfo::new((100, 100).to(200, 200));
builder.push_border(&info, border_widths, border_details);
builder.pop_clip_id();
if false {
// draw box shadow?
let rect = LayoutRect::zero();
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;
let info = LayoutPrimitiveInfo::with_clip_rect(rect, bounds);
builder.push_box_shadow(
&info,
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: &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
}
}

272
gfx/wr/examples/blob.rs Normal file
View File

@ -0,0 +1,272 @@
/* 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 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, RenderApi, Transaction};
use webrender::api::{ColorF, DeviceIntRect, DeviceIntPoint};
// 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) -> Vec<u8> {
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: Option<api::TileOffset>,
) -> api::BlobImageResult {
let color = *commands;
// Allocate storage for the result. Right now the resource cache expects the
// tiles to have have no stride or offset.
let mut texels = Vec::with_capacity((descriptor.size.width * descriptor.size.height * 4) 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 = match tile {
Some(tile) => (tile.x % 2 == 0) != (tile.y % 2 == 0),
None => true,
};
for y in 0 .. descriptor.size.height {
for x in 0 .. descriptor.size.width {
// 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 + descriptor.offset.x as i32;
let y2 = y + descriptor.offset.y as i32;
// 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: DeviceIntRect {
origin: DeviceIntPoint::origin(),
size: descriptor.size,
},
})
}
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::ImageKey, Arc<ImageRenderingCommands>>,
}
impl CheckerboardRenderer {
fn new(workers: Arc<ThreadPool>) -> Self {
CheckerboardRenderer {
image_cmds: HashMap::new(),
workers,
}
}
}
impl api::BlobImageHandler for CheckerboardRenderer {
fn add(&mut self, key: api::ImageKey, cmds: Arc<api::BlobImageData>, _: Option<api::TileSize>) {
self.image_cmds
.insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap()));
}
fn update(&mut self, key: api::ImageKey, cmds: Arc<api::BlobImageData>, _dirty_rect: Option<api::DeviceIntRect>) {
// 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::ImageKey) {
self.image_cmds.remove(&key);
}
fn prepare_resources(
&mut self,
_services: &api::BlobImageResources,
_requests: &[api::BlobImageParams],
) {}
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<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::ImageKey, 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: &RenderApi,
builder: &mut DisplayListBuilder,
txn: &mut Transaction,
_framebuffer_size: api::DeviceIntSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let blob_img1 = api.generate_image_key();
txn.add_image(
blob_img1,
api::ImageDescriptor::new(500, 500, api::ImageFormat::BGRA8, true, false),
api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 50, 150, 255))),
Some(128),
);
let blob_img2 = api.generate_image_key();
txn.add_image(
blob_img2,
api::ImageDescriptor::new(200, 200, api::ImageFormat::BGRA8, true, false),
api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 150, 50, 255))),
None,
);
let bounds = api::LayoutRect::new(api::LayoutPoint::zero(), builder.content_size());
let info = api::LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
None,
api::TransformStyle::Flat,
api::MixBlendMode::Normal,
&[],
api::RasterSpace::Screen,
);
let info = api::LayoutPrimitiveInfo::new((30, 30).by(500, 500));
builder.push_image(
&info,
api::LayoutSize::new(500.0, 500.0),
api::LayoutSize::new(0.0, 0.0),
api::ImageRendering::Auto,
api::AlphaType::PremultipliedAlpha,
blob_img1,
ColorF::WHITE,
);
let info = api::LayoutPrimitiveInfo::new((600, 600).by(200, 200));
builder.push_image(
&info,
api::LayoutSize::new(200.0, 200.0),
api::LayoutSize::new(0.0, 0.0),
api::ImageRendering::Auto,
api::AlphaType::PremultipliedAlpha,
blob_img2,
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,315 @@
/* 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 env_logger;
extern crate euclid;
use gleam::gl;
use glutin::{self, GlContext};
use std::env;
use std::path::PathBuf;
use webrender;
use winit;
use webrender::ShaderPrecacheFlags;
use webrender::api::*;
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<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: &RenderApi,
builder: &mut DisplayListBuilder,
txn: &mut Transaction,
framebuffer_size: DeviceIntSize,
pipeline_id: PipelineId,
document_id: DocumentId,
);
fn on_event(
&mut self,
winit::WindowEvent,
&RenderApi,
DocumentId,
) -> bool {
false
}
fn get_image_handlers(
&mut self,
_gl: &gl::Gl,
) -> (Option<Box<webrender::ExternalImageHandler>>,
Option<Box<webrender::OutputImageHandler>>) {
(None, None)
}
fn draw_custom(&mut self, _gl: &gl::Gl) {
}
}
pub fn main_wrapper<E: Example>(
example: &mut E,
options: Option<webrender::RendererOptions>,
) {
env_logger::init();
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 context_builder = glutin::ContextBuilder::new()
.with_gl(glutin::GlRequest::GlThenGles {
opengl_version: (3, 2),
opengles_version: (3, 0),
});
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 window = glutin::GlWindow::new(window_builder, context_builder, &events_loop)
.unwrap();
unsafe {
window.make_current().ok();
}
let gl = match window.get_api() {
glutin::Api::OpenGl => unsafe {
gl::GlFns::load_with(|symbol| window.get_proc_address(symbol) as *const _)
},
glutin::Api::OpenGlEs => unsafe {
gl::GlesFns::load_with(|symbol| window.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 = window.get_hidpi_factor() as f32;
println!("Device pixel ratio: {}", device_pixel_ratio);
println!("Loading shaders...");
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)),
//scatter_gpu_cache_updates: false,
debug_flags: webrender::DebugFlags::ECHO_DRIVER_MESSAGES,
..options.unwrap_or(webrender::RendererOptions::default())
};
let framebuffer_size = {
let size = 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).unwrap();
let api = sender.create_api();
let document_id = api.add_document(framebuffer_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 = framebuffer_size.to_f32() / euclid::TypedScale::new(device_pixel_ratio);
let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
let mut txn = Transaction::new();
example.render(
&api,
&mut builder,
&mut txn,
framebuffer_size,
pipeline_id,
document_id,
);
txn.set_display_list(
epoch,
None,
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;
match global_event {
winit::Event::WindowEvent {
event: winit::WindowEvent::CloseRequested,
..
} => return winit::ControlFlow::Break,
winit::Event::WindowEvent {
event: 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 => renderer.toggle_debug_flags(webrender::DebugFlags::PROFILER_DBG),
winit::VirtualKeyCode::O => renderer.toggle_debug_flags(webrender::DebugFlags::RENDER_TARGET_DBG),
winit::VirtualKeyCode::I => renderer.toggle_debug_flags(webrender::DebugFlags::TEXTURE_CACHE_DBG),
winit::VirtualKeyCode::S => renderer.toggle_debug_flags(webrender::DebugFlags::COMPACT_PROFILER),
winit::VirtualKeyCode::Q => renderer.toggle_debug_flags(
webrender::DebugFlags::GPU_TIME_QUERIES | webrender::DebugFlags::GPU_SAMPLE_QUERIES
),
winit::VirtualKeyCode::F => renderer.toggle_debug_flags(
webrender::DebugFlags::NEW_FRAME_INDICATOR | webrender::DebugFlags::NEW_SCENE_INDICATOR
),
winit::VirtualKeyCode::G => api.send_debug_cmd(
// go through the API so that we reach the render backend
DebugCommand::EnableGpuCacheDebug(
!renderer.get_debug_flags().contains(webrender::DebugFlags::GPU_CACHE_DBG)
),
),
winit::VirtualKeyCode::Key1 => txn.set_window_parameters(
framebuffer_size,
DeviceIntRect::new(DeviceIntPoint::zero(), framebuffer_size),
1.0
),
winit::VirtualKeyCode::Key2 => txn.set_window_parameters(
framebuffer_size,
DeviceIntRect::new(DeviceIntPoint::zero(), framebuffer_size),
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);
},
_ => {
let win_event = match global_event {
winit::Event::WindowEvent { event, .. } => event,
_ => unreachable!()
};
custom_event = example.on_event(
win_event,
&api,
document_id,
)
},
},
winit::Event::WindowEvent { event, .. } => custom_event = example.on_event(
event,
&api,
document_id,
),
_ => return winit::ControlFlow::Continue,
};
if custom_event {
let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
example.render(
&api,
&mut builder,
&mut txn,
framebuffer_size,
pipeline_id,
document_id,
);
txn.set_display_list(
epoch,
None,
layout_size,
builder.finalize(),
true,
);
txn.generate_frame();
}
api.send_transaction(document_id, txn);
renderer.update();
renderer.render(framebuffer_size).unwrap();
let _ = renderer.flush_pipeline_info();
example.draw_custom(&*gl);
window.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};
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, true, false),
ImageData::new(image_data)
)
}

147
gfx/wr/examples/document.rs Normal file
View File

@ -0,0 +1,147 @@
/* 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 boilerplate::Example;
use euclid::TypedScale;
use webrender::api::*;
// 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: &RenderApi,
framebuffer_size: DeviceIntSize,
device_pixel_ratio: f32,
) {
let init_data = vec![
(
PipelineId(1, 0),
-1,
ColorF::new(0.0, 1.0, 0.0, 1.0),
DeviceIntPoint::new(0, 0),
),
(
PipelineId(2, 0),
-2,
ColorF::new(1.0, 1.0, 0.0, 1.0),
DeviceIntPoint::new(200, 0),
),
(
PipelineId(3, 0),
-3,
ColorF::new(1.0, 0.0, 0.0, 1.0),
DeviceIntPoint::new(200, 200),
),
(
PipelineId(4, 0),
-4,
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_window_parameters(framebuffer_size, 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: bounds.to_f32() / TypedScale::new(device_pixel_ratio),
color,
});
}
}
}
impl Example for App {
fn render(
&mut self,
api: &RenderApi,
base_builder: &mut DisplayListBuilder,
_txn: &mut Transaction,
framebuffer_size: DeviceIntSize,
_: PipelineId,
_: DocumentId,
) {
if self.documents.is_empty() {
let device_pixel_ratio = framebuffer_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, framebuffer_size, device_pixel_ratio);
}
for doc in &self.documents {
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_stacking_context(
&LayoutPrimitiveInfo::new(doc.content_rect),
None,
TransformStyle::Flat,
MixBlendMode::Normal,
&[],
RasterSpace::Screen,
);
builder.push_rect(
&LayoutPrimitiveInfo::new(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,235 @@
/* 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 boilerplate::{Example, HandyDandyRectBuilder};
use euclid::TypedScale;
use gleam::gl;
use webrender::api::*;
// 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 webrender::OutputImageHandler for OutputHandler {
fn lock(&mut self, _id: PipelineId) -> Option<(u32, DeviceIntSize)> {
Some((self.texture_id, DeviceIntSize::new(500, 500)))
}
fn unlock(&mut self, _id: PipelineId) {}
}
impl webrender::ExternalImageHandler for ExternalHandler {
fn lock(
&mut self,
_key: ExternalImageId,
_channel_index: u8,
_rendering: ImageRendering
) -> webrender::ExternalImage {
webrender::ExternalImage {
uv: TexelRect::new(0.0, 0.0, 1.0, 1.0),
source: webrender::ExternalImageSource::NativeTexture(self.texture_id),
}
}
fn unlock(&mut self, _key: ExternalImageId, _channel_index: u8) {}
}
impl App {
fn init_output_document(
&mut self,
api: &RenderApi,
framebuffer_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 bounds = DeviceIntRect::new(DeviceIntPoint::zero(), framebuffer_size);
let document_id = api.add_document(framebuffer_size, layer);
let document = Document {
id: document_id,
pipeline_id,
content_rect: bounds.to_f32() / TypedScale::new(device_pixel_ratio),
color,
};
let mut txn = Transaction::new();
txn.enable_frame_output(document.pipeline_id, true);
api.send_transaction(document.id, txn);
let mut txn = Transaction::new();
txn.add_image(
self.external_image_key.unwrap(),
ImageDescriptor::new(100, 100, ImageFormat::BGRA8, true, false),
ImageData::External(ExternalImageData {
id: ExternalImageId(0),
channel_index: 0,
image_type: ExternalImageType::TextureHandle(TextureTarget::Default),
}),
None,
);
let info = LayoutPrimitiveInfo::new(document.content_rect);
let mut builder = DisplayListBuilder::new(
document.pipeline_id,
document.content_rect.size,
);
builder.push_stacking_context(
&info,
None,
TransformStyle::Flat,
MixBlendMode::Normal,
&[],
RasterSpace::Screen,
);
builder.push_rect(&info, 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: &RenderApi,
builder: &mut DisplayListBuilder,
_txn: &mut Transaction,
framebuffer_size: DeviceIntSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
if self.output_document.is_none() {
let device_pixel_ratio = framebuffer_size.width as f32 /
builder.content_size().width;
self.init_output_document(api, DeviceIntSize::new(200, 200), device_pixel_ratio);
}
let info = LayoutPrimitiveInfo::new((100, 100).to(200, 200));
builder.push_stacking_context(
&info,
None,
TransformStyle::Flat,
MixBlendMode::Normal,
&[],
RasterSpace::Screen,
);
builder.push_image(
&info,
info.rect.size,
LayoutSize::zero(),
ImageRendering::Auto,
AlphaType::PremultipliedAlpha,
self.external_image_key.unwrap(),
ColorF::WHITE,
);
builder.pop_stacking_context();
}
fn get_image_handlers(
&mut self,
gl: &gl::Gl,
) -> (Option<Box<webrender::ExternalImageHandler>>,
Option<Box<webrender::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);
}

94
gfx/wr/examples/iframe.rs Normal file
View File

@ -0,0 +1,94 @@
/* 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 boilerplate::{Example, HandyDandyRectBuilder};
use webrender::api::*;
// 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: &RenderApi,
builder: &mut DisplayListBuilder,
_txn: &mut Transaction,
_framebuffer_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 info = LayoutPrimitiveInfo::new(sub_bounds);
sub_builder.push_stacking_context(
&info,
None,
TransformStyle::Flat,
MixBlendMode::Normal,
&[],
RasterSpace::Screen,
);
// green rect visible == success
sub_builder.push_rect(&info, 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);
let info = LayoutPrimitiveInfo::new(sub_bounds);
let reference_frame_id = builder.push_reference_frame(
&info,
Some(PropertyBinding::Binding(PropertyBindingKey::new(42), LayoutTransform::identity())),
None,
);
builder.push_clip_id(reference_frame_id);
// And this is for the root pipeline
builder.push_stacking_context(
&info,
None,
TransformStyle::Flat,
MixBlendMode::Normal,
&[],
RasterSpace::Screen,
);
// red rect under the iframe: if this is visible, things have gone wrong
builder.push_rect(&info, ColorF::new(1.0, 0.0, 0.0, 1.0));
builder.push_iframe(&info, sub_pipeline_id, false);
builder.pop_stacking_context();
builder.pop_clip_id();
builder.pop_reference_frame();
}
}
fn main() {
let mut app = App {};
boilerplate::main_wrapper(&mut app, None);
}

View File

@ -0,0 +1,126 @@
/* 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 boilerplate::{Example, HandyDandyRectBuilder};
use webrender::api::*;
struct App {
image_key: ImageKey,
}
impl Example for App {
fn render(
&mut self,
_api: &RenderApi,
builder: &mut DisplayListBuilder,
txn: &mut Transaction,
_framebuffer_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 info = LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
None,
TransformStyle::Flat,
MixBlendMode::Normal,
&[],
RasterSpace::Screen,
);
let image_size = LayoutSize::new(100.0, 100.0);
let info = LayoutPrimitiveInfo::with_clip_rect(
LayoutRect::new(LayoutPoint::new(100.0, 100.0), image_size),
bounds,
);
builder.push_image(
&info,
image_size,
LayoutSize::zero(),
ImageRendering::Auto,
AlphaType::PremultipliedAlpha,
self.image_key,
ColorF::WHITE,
);
let info = LayoutPrimitiveInfo::with_clip_rect(
LayoutRect::new(LayoutPoint::new(250.0, 100.0), image_size),
bounds,
);
builder.push_image(
&info,
image_size,
LayoutSize::zero(),
ImageRendering::Pixelated,
AlphaType::PremultipliedAlpha,
self.image_key,
ColorF::WHITE,
);
builder.pop_stacking_context();
}
fn on_event(&mut self, event: winit::WindowEvent, api: &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, true, false),
ImageData::new(image_data),
None,
);
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,317 @@
/* 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 app_units;
extern crate euclid;
extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate winit;
use app_units::Au;
use gleam::gl;
use glutin::GlContext;
use std::fs::File;
use std::io::Read;
use webrender::api::*;
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<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?
window: glutin::GlWindow,
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 context_builder = glutin::ContextBuilder::new()
.with_gl(glutin::GlRequest::GlThenGles {
opengl_version: (3, 2),
opengles_version: (3, 0),
});
let window_builder = winit::WindowBuilder::new()
.with_title(name)
.with_multitouch()
.with_dimensions(LogicalSize::new(800., 600.));
let window = glutin::GlWindow::new(window_builder, context_builder, &events_loop)
.unwrap();
unsafe {
window.make_current().ok();
}
let gl = match window.get_api() {
glutin::Api::OpenGl => unsafe {
gl::GlFns::load_with(|symbol| window.get_proc_address(symbol) as *const _)
},
glutin::Api::OpenGlEs => unsafe {
gl::GlesFns::load_with(|symbol| window.get_proc_address(symbol) as *const _)
},
glutin::Api::WebGl => unimplemented!(),
};
let device_pixel_ratio = window.get_hidpi_factor() as f32;
let opts = webrender::RendererOptions {
device_pixel_ratio,
clear_color: Some(clear_color),
..webrender::RendererOptions::default()
};
let framebuffer_size = {
let size = 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).unwrap();
let api = sender.create_api();
let document_id = api.add_document(framebuffer_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, Au::from_px(32), None, None, Vec::new());
api.send_transaction(document_id, txn);
Window {
events_loop,
window,
renderer,
name,
epoch,
pipeline_id,
document_id,
api,
font_instance_key,
}
}
fn tick(&mut self) -> bool {
unsafe {
self.window.make_current().ok();
}
let mut do_exit = false;
let my_name = &self.name;
let renderer = &mut self.renderer;
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!("toggle flags {}", my_name);
renderer.toggle_debug_flags(webrender::DebugFlags::PROFILER_DBG);
}
_ => {}
}
_ => {}
});
if do_exit {
return true
}
let device_pixel_ratio = self.window.get_hidpi_factor() as f32;
let framebuffer_size = {
let size = self.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 = framebuffer_size.to_f32() / euclid::TypedScale::new(device_pixel_ratio);
let mut txn = Transaction::new();
let mut builder = DisplayListBuilder::new(self.pipeline_id, layout_size);
let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
let info = LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
None,
TransformStyle::Flat,
MixBlendMode::Normal,
&[],
RasterSpace::Screen,
);
let info = LayoutPrimitiveInfo::new(LayoutRect::new(
LayoutPoint::new(100.0, 100.0),
LayoutSize::new(100.0, 200.0)
));
builder.push_rect(&info, 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),
},
];
let info = LayoutPrimitiveInfo::new(text_bounds);
builder.push_text(
&info,
&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();
self.api.send_transaction(self.document_id, txn);
renderer.update();
renderer.render(framebuffer_size).unwrap();
self.window.swap_buffers().ok();
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,225 @@
/* 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 boilerplate::{Example, HandyDandyRectBuilder};
use euclid::SideOffsets2D;
use webrender::api::*;
use winit::dpi::LogicalPosition;
struct App {
cursor_position: WorldPoint,
}
impl Example for App {
fn render(
&mut self,
_api: &RenderApi,
builder: &mut DisplayListBuilder,
_txn: &mut Transaction,
_framebuffer_size: DeviceIntSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let info = LayoutPrimitiveInfo::new(
LayoutRect::new(LayoutPoint::zero(), builder.content_size())
);
builder.push_stacking_context(
&info,
None,
TransformStyle::Flat,
MixBlendMode::Normal,
&[],
RasterSpace::Screen,
);
if true {
// scrolling and clips stuff
// let's make a scrollbox
let scrollbox = (0, 0).to(300, 400);
builder.push_stacking_context(
&LayoutPrimitiveInfo::new((10, 10).by(0, 0)),
None,
TransformStyle::Flat,
MixBlendMode::Normal,
&[],
RasterSpace::Screen,
);
// set the scrolling clip
let clip_id = builder.define_scroll_frame(
None,
(0, 0).by(1000, 1000),
scrollbox,
vec![],
None,
ScrollSensitivity::ScriptAndInputEvents,
);
builder.push_clip_id(clip_id);
// now put some content into it.
// start with a white background
let mut info = LayoutPrimitiveInfo::new((0, 0).to(1000, 1000));
info.tag = Some((0, 1));
builder.push_rect(&info, ColorF::new(1.0, 1.0, 1.0, 1.0));
// let's make a 50x50 blue square as a visual reference
let mut info = LayoutPrimitiveInfo::new((0, 0).to(50, 50));
info.tag = Some((0, 2));
builder.push_rect(&info, 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 =
LayoutPrimitiveInfo::with_clip_rect((50, 0).to(100, 50), (60, 10).to(110, 60));
info.tag = Some((0, 3));
builder.push_rect(&info, 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 nested_clip_id = builder.define_scroll_frame(
None,
(0, 100).to(300, 1000),
(0, 100).to(200, 300),
vec![],
None,
ScrollSensitivity::ScriptAndInputEvents,
);
builder.push_clip_id(nested_clip_id);
// give it a giant gray background just to distinguish it and to easily
// visually identify the nested scrollbox
let mut info = LayoutPrimitiveInfo::new((-1000, -1000).to(5000, 5000));
info.tag = Some((0, 4));
builder.push_rect(&info, 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 = LayoutPrimitiveInfo::new((0, 200).to(50, 250));
info.tag = Some((0, 5));
builder.push_rect(&info, 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(
(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)
);
builder.push_clip_id(sticky_id);
let mut info = LayoutPrimitiveInfo::new((50, 350).by(50, 50));
info.tag = Some((0, 6));
builder.push_rect(&info, ColorF::new(0.5, 0.5, 1.0, 1.0));
builder.pop_clip_id(); // sticky_id
// 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 = LayoutPrimitiveInfo::new((250, 350).to(300, 400));
info.tag = Some((0, 7));
builder.push_rect(&info, ColorF::new(0.0, 1.0, 1.0, 1.0));
builder.pop_clip_id(); // nested_clip_id
builder.pop_clip_id(); // clip_id
builder.pop_stacking_context();
}
builder.pop_stacking_context();
}
fn on_event(&mut self, event: winit::WindowEvent, api: &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,320 @@
/* 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 boilerplate::{Example, HandyDandyRectBuilder};
use gleam::gl;
use std::mem;
use webrender::api::*;
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 webrender::ExternalImageHandler for ImageGenerator {
fn lock(
&mut self,
_key: ExternalImageId,
channel_index: u8,
_rendering: ImageRendering
) -> webrender::ExternalImage {
self.generate_image(channel_index as i32);
webrender::ExternalImage {
uv: TexelRect::new(0.0, 0.0, 1.0, 1.0),
source: webrender::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: &RenderApi,
builder: &mut DisplayListBuilder,
txn: &mut Transaction,
_framebuffer_size: DeviceIntSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let bounds = (0, 0).to(512, 512);
let info = LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
None,
TransformStyle::Flat,
MixBlendMode::Normal,
&[],
RasterSpace::Screen,
);
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, true, false),
ImageData::new(self.image_generator.take()),
None,
);
self.image_generator.generate_image(128);
txn.add_image(
key1,
ImageDescriptor::new(128, 128, ImageFormat::BGRA8, true, false),
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 = LayoutPrimitiveInfo::with_clip_rect(
LayoutRect::new(
LayoutPoint::new(x0 + image_size.width * x, y0 + image_size.height * y),
image_size,
),
bounds,
);
builder.push_image(
&info,
image_size,
LayoutSize::zero(),
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 = LayoutPrimitiveInfo::with_clip_rect(
LayoutRect::new(LayoutPoint::new(100.0, 100.0), image_size),
bounds,
);
builder.push_image(
&info,
image_size,
LayoutSize::zero(),
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 = LayoutPrimitiveInfo::with_clip_rect(
LayoutRect::new(LayoutPoint::new(100.0, 400.0), image_size),
bounds,
);
builder.push_image(
&info,
image_size,
LayoutSize::zero(),
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: &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, true, false),
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, true, false),
ImageData::new(self.image_generator.take()),
None,
);
},
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, true, false),
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, true, false),
ImageData::new(self.image_generator.take()),
None,
);
self.image_key = Some(image_key);
}
_ => {}
}
api.update_resources(txn.resource_updates);
return true;
}
_ => {}
}
false
}
fn get_image_handlers(
&mut self,
_gl: &gl::Gl,
) -> (Option<Box<webrender::ExternalImageHandler>>,
Option<Box<webrender::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);
}

222
gfx/wr/examples/yuv.rs Normal file
View File

@ -0,0 +1,222 @@
/* 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 boilerplate::Example;
use gleam::gl;
use webrender::api::*;
fn init_gl_texture(
id: gl::GLuint,
internal: gl::GLenum,
external: gl::GLenum,
bytes: &[u8],
gl: &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: &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 webrender::ExternalImageHandler for YuvImageProvider {
fn lock(
&mut self,
key: ExternalImageId,
_channel_index: u8,
_rendering: ImageRendering
) -> webrender::ExternalImage {
let id = self.texture_ids[key.0 as usize];
webrender::ExternalImage {
uv: TexelRect::new(0.0, 0.0, 1.0, 1.0),
source: webrender::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: &RenderApi,
builder: &mut DisplayListBuilder,
txn: &mut Transaction,
_framebuffer_size: DeviceIntSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
let info = LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
None,
TransformStyle::Flat,
MixBlendMode::Normal,
&[],
RasterSpace::Screen,
);
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, true, false),
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, true, false),
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, true, false),
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, true, false),
ImageData::External(ExternalImageData {
id: ExternalImageId(3),
channel_index: 0,
image_type: ExternalImageType::TextureHandle(
TextureTarget::Default,
),
}),
None,
);
let info = LayoutPrimitiveInfo::with_clip_rect(
LayoutRect::new(LayoutPoint::new(100.0, 0.0), LayoutSize::new(100.0, 100.0)),
bounds,
);
builder.push_yuv_image(
&info,
YuvData::NV12(yuv_chanel1, yuv_chanel2),
ColorDepth::Color8,
YuvColorSpace::Rec601,
ImageRendering::Auto,
);
let info = LayoutPrimitiveInfo::with_clip_rect(
LayoutRect::new(LayoutPoint::new(300.0, 0.0), LayoutSize::new(100.0, 100.0)),
bounds,
);
builder.push_yuv_image(
&info,
YuvData::PlanarYCbCr(yuv_chanel1, yuv_chanel2_1, yuv_chanel3),
ColorDepth::Color8,
YuvColorSpace::Rec601,
ImageRendering::Auto,
);
builder.pop_stacking_context();
}
fn on_event(
&mut self,
_event: winit::WindowEvent,
_api: &RenderApi,
_document_id: DocumentId,
) -> bool {
false
}
fn get_image_handlers(
&mut self,
gl: &gl::Gl,
) -> (Option<Box<webrender::ExternalImageHandler>>,
Option<Box<webrender::OutputImageHandler>>) {
let provider = YuvImageProvider::new(gl);
self.texture_id = provider.texture_ids[0];
(Some(Box::new(provider)), None)
}
fn draw_custom(&mut self, gl: &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));
}

6
gfx/wr/rustfmt.toml 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

31
gfx/wr/servo-tidy.toml Normal file
View File

@ -0,0 +1,31 @@
[configs]
skip-check-length = false
skip-check-licenses = false
check-alphabetical-order = false
[ignore]
# Ignored packages with duplicated versions
packages = [
"crossbeam-epoch",
"crossbeam-utils",
"lazy_static",
"log",
"rand",
"winapi",
"core-graphics",
"core-text",
"yaml-rust",
]
# 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",
]

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