mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-07 18:04:46 +00:00
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:
parent
cef4893007
commit
2719e9d65e
@ -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",
|
||||
|
@ -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
27
gfx/wr/.gitignore
vendored
Normal 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
224
gfx/wr/.taskcluster.yml
Normal 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
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
16
gfx/wr/Cargo.toml
Normal 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
374
gfx/wr/LICENSE
Normal 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
39
gfx/wr/README.md
Normal 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
30
gfx/wr/appveyor.yml
Normal 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
6
gfx/wr/debugger/.babelrc
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"presets": [
|
||||
["env", { "modules": false }],
|
||||
"stage-3"
|
||||
]
|
||||
}
|
9
gfx/wr/debugger/.editorconfig
Normal file
9
gfx/wr/debugger/.editorconfig
Normal 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
11
gfx/wr/debugger/.gitignore
vendored
Normal 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
23
gfx/wr/debugger/README.md
Normal 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
12
gfx/wr/debugger/dist/build.js
vendored
Normal file
File diff suppressed because one or more lines are too long
11
gfx/wr/debugger/index.html
Normal file
11
gfx/wr/debugger/index.html
Normal 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>
|
36
gfx/wr/debugger/package.json
Normal file
36
gfx/wr/debugger/package.json
Normal 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"
|
||||
}
|
||||
}
|
55
gfx/wr/debugger/src/App.vue
Normal file
55
gfx/wr/debugger/src/App.vue
Normal 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>
|
37
gfx/wr/debugger/src/components/ClipScrollTreeViewPage.vue
Normal file
37
gfx/wr/debugger/src/components/ClipScrollTreeViewPage.vue
Normal 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>
|
37
gfx/wr/debugger/src/components/DocumentViewPage.vue
Normal file
37
gfx/wr/debugger/src/components/DocumentViewPage.vue
Normal 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>
|
41
gfx/wr/debugger/src/components/NavBar.vue
Normal file
41
gfx/wr/debugger/src/components/NavBar.vue
Normal 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>
|
33
gfx/wr/debugger/src/components/NavMenu.vue
Normal file
33
gfx/wr/debugger/src/components/NavMenu.vue
Normal 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>
|
97
gfx/wr/debugger/src/components/OptionsPage.vue
Normal file
97
gfx/wr/debugger/src/components/OptionsPage.vue
Normal 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>
|
37
gfx/wr/debugger/src/components/PassViewPage.vue
Normal file
37
gfx/wr/debugger/src/components/PassViewPage.vue
Normal 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>
|
37
gfx/wr/debugger/src/components/RenderTaskViewPage.vue
Normal file
37
gfx/wr/debugger/src/components/RenderTaskViewPage.vue
Normal 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>
|
32
gfx/wr/debugger/src/components/ScreenshotPage.vue
Normal file
32
gfx/wr/debugger/src/components/ScreenshotPage.vue
Normal 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>
|
40
gfx/wr/debugger/src/components/TreeView.vue
Normal file
40
gfx/wr/debugger/src/components/TreeView.vue
Normal 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>
|
14
gfx/wr/debugger/src/main.js
Normal file
14
gfx/wr/debugger/src/main.js
Normal 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)
|
||||
})
|
105
gfx/wr/debugger/src/store/index.js
Normal file
105
gfx/wr/debugger/src/store/index.js
Normal 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;
|
81
gfx/wr/debugger/webpack.config.js
Normal file
81
gfx/wr/debugger/webpack.config.js
Normal 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
|
||||
})
|
||||
])
|
||||
}
|
13
gfx/wr/direct-composition/Cargo.toml
Normal file
13
gfx/wr/direct-composition/Cargo.toml
Normal 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"
|
112
gfx/wr/direct-composition/src/com.rs
Normal file
112
gfx/wr/direct-composition/src/com.rs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
174
gfx/wr/direct-composition/src/egl.rs
Normal file
174
gfx/wr/direct-composition/src/egl.rs
Normal 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
|
||||
}
|
||||
}
|
179
gfx/wr/direct-composition/src/lib.rs
Normal file
179
gfx/wr/direct-composition/src/lib.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
11
gfx/wr/direct-composition/src/main.rs
Normal file
11
gfx/wr/direct-composition/src/main.rs
Normal 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");
|
201
gfx/wr/direct-composition/src/main_windows.rs
Normal file
201
gfx/wr/direct-composition/src/main_windows.rs
Normal 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, ¬ifier, factor, size(300, 200), 0., 0.2, 0.4, 1.),
|
||||
Rectangle::new(&composition, ¬ifier, 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();
|
||||
}
|
||||
}
|
67
gfx/wr/examples/Cargo.toml
Normal file
67
gfx/wr/examples/Cargo.toml
Normal 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"
|
8
gfx/wr/examples/README.md
Normal file
8
gfx/wr/examples/README.md
Normal 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
|
||||
```
|
90
gfx/wr/examples/alpha_perf.rs
Normal file
90
gfx/wr/examples/alpha_perf.rs
Normal 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);
|
||||
}
|
152
gfx/wr/examples/animation.rs
Normal file
152
gfx/wr/examples/animation.rs
Normal 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
295
gfx/wr/examples/basic.rs
Normal 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
272
gfx/wr/examples/blob.rs
Normal 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[¶ms.request.key]))
|
||||
}).collect();
|
||||
|
||||
self.workers.install(|| {
|
||||
requests.into_par_iter().map(|(params, commands)| {
|
||||
(params.request, render_blob(commands, ¶ms.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));
|
||||
}
|
315
gfx/wr/examples/common/boilerplate.rs
Normal file
315
gfx/wr/examples/common/boilerplate.rs
Normal 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();
|
||||
}
|
19
gfx/wr/examples/common/image_helper.rs
Normal file
19
gfx/wr/examples/common/image_helper.rs
Normal 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
147
gfx/wr/examples/document.rs
Normal 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);
|
||||
}
|
235
gfx/wr/examples/frame_output.rs
Normal file
235
gfx/wr/examples/frame_output.rs
Normal 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
94
gfx/wr/examples/iframe.rs
Normal 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);
|
||||
}
|
126
gfx/wr/examples/image_resize.rs
Normal file
126
gfx/wr/examples/image_resize.rs
Normal 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);
|
||||
}
|
317
gfx/wr/examples/multiwindow.rs
Normal file
317
gfx/wr/examples/multiwindow.rs
Normal 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
|
||||
}
|
225
gfx/wr/examples/scrolling.rs
Normal file
225
gfx/wr/examples/scrolling.rs
Normal 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);
|
||||
}
|
320
gfx/wr/examples/texture_cache_stress.rs
Normal file
320
gfx/wr/examples/texture_cache_stress.rs
Normal 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
222
gfx/wr/examples/yuv.rs
Normal 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
6
gfx/wr/rustfmt.toml
Normal 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
31
gfx/wr/servo-tidy.toml
Normal 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
Loading…
Reference in New Issue
Block a user