Merge mozilla-central to inbound. a=merge CLOSED TREE

This commit is contained in:
shindli 2018-02-20 21:15:10 +02:00
Родитель ede90c5816 f1c5255ad4
Коммит 54668feb23
388 изменённых файлов: 30877 добавлений и 7263 удалений

Просмотреть файл

@ -3,4 +3,4 @@ prefs =
layout.css.devPixelsPerPx='2'
[../browser_startup_images.js]
skip-if = !debug || (os == 'win' && (os_version == '6.1')) # hidpi results in the toolbar overflowing on Win 7
skip-if = !debug || ((os == 'win' && (os_version == '6.1')) || os == 'linux') # hidpi results in the toolbar overflowing on Win 7 and Linux

Просмотреть файл

@ -7,11 +7,15 @@ requestLongerTimeout(2);
add_task(async function() {
let aboutURLs = [];
// List of about: URLs that will initiate network requests.
let networkURLs = [
// List of about: URLs that may cause problem, so we skip them in this test.
let skipURLs = [
// about:credits will initiate network request.
"credits",
"telemetry" // about:telemetry will fetch Telemetry asynchrounously and takes
// longer, we skip this for now.
// about:telemetry will fetch Telemetry asynchronously and takes longer,
// so we skip this for now.
"telemetry",
// about:downloads causes a shutdown leak with stylo-chrome. bug 1419943.
"downloads"
];
for (let cid in Cc) {
@ -27,7 +31,7 @@ add_task(async function() {
let uri = Services.io.newURI("about:" + aboutType);
let flags = am.getURIFlags(uri);
if (!(flags & Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT) &&
!networkURLs.includes(aboutType)) {
!skipURLs.includes(aboutType)) {
aboutURLs.push(aboutType);
}
} catch (e) {

Просмотреть файл

@ -620,15 +620,16 @@ PlacesController.prototype = {
}
}
// Make sure correct PluralForms are diplayed when multiple pages are selected.
var deleteHistoryItem = document.getElementById("placesContext_delete_history");
deleteHistoryItem.label = PlacesUIUtils.getPluralString("cmd.deletePages.label",
metadata.length);
deleteHistoryItem.accessKey = PlacesUIUtils.getString("cmd.deletePages.accesskey");
var createBookmarkItem = document.getElementById("placesContext_createBookmark");
createBookmarkItem.label = PlacesUIUtils.getPluralString("cmd.bookmarkPages.label",
metadata.length);
createBookmarkItem.accessKey = PlacesUIUtils.getString("cmd.bookmarkPages.accesskey");
// Make sure to display the correct string when multiple pages are selected.
let stringId = metadata.length === 1 ? "SinglePage" : "MultiplePages";
let deleteHistoryItem = document.getElementById("placesContext_delete_history");
deleteHistoryItem.label = PlacesUIUtils.getString(`cmd.delete${stringId}.label`);
deleteHistoryItem.accessKey = PlacesUIUtils.getString(`cmd.delete${stringId}.accesskey`);
let createBookmarkItem = document.getElementById("placesContext_createBookmark");
createBookmarkItem.label = PlacesUIUtils.getString(`cmd.bookmark${stringId}.label`);
createBookmarkItem.accessKey = PlacesUIUtils.getString(`cmd.bookmark${stringId}.accesskey`);
return usableItemCount > 0;
},

Просмотреть файл

@ -25,11 +25,11 @@
"PlacesUIUtils", "resource:///modules/PlacesUIUtils.jsm");
ChromeUtils.defineModuleGetter(window,
"PlacesTransactions", "resource://gre/modules/PlacesTransactions.jsm");
XPCOMUtils.defineLazyScriptGetter(window, "PlacesTreeView",
"chrome://browser/content/places/treeView.js");
]]></script>
<script type="application/javascript"
src="chrome://browser/content/places/controller.js"/>
<script type="application/javascript"
src="chrome://browser/content/places/treeView.js"/>
<!-- Bookmarks and history tooltip -->
<tooltip id="bhTooltip" noautohide="true"

Просмотреть файл

@ -284,7 +284,7 @@
<button id="clearSiteDataButton"
class="accessory-button"
icon="clear"
label="&clearSiteData.label;" accesskey="&clearSiteData.accesskey;"/>
label="&clearSiteData1.label;" accesskey="&clearSiteData1.accesskey;"/>
</vbox>
</hbox>
</groupbox>

Просмотреть файл

@ -24,6 +24,7 @@ function getPersistentStoragePermStatus(origin) {
}
// Test listing site using quota usage or site using appcache
// This is currently disabled because of bug 1414751.
add_task(async function() {
await SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
@ -64,7 +65,7 @@ add_task(async function() {
request.callback = resolve;
});
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
}).skip(); // Bug 1414751
// Test buttons are disabled and loading message shown while updating sites
add_task(async function() {

Просмотреть файл

@ -29,9 +29,6 @@ unset MAKECAB
ac_add_options --target=i686-w64-mingw32
ac_add_options --with-toolchain-prefix=i686-w64-mingw32-
ac_add_options --enable-debug
ac_add_options --disable-optimize
# GCC compiling for Windows exposes a lot of warnings. We are tracking them in Bug 1394433
ac_add_options --disable-warnings-as-errors

Просмотреть файл

@ -0,0 +1,4 @@
. "$topsrcdir/browser/config/mozconfigs/win32/mingw32"
ac_add_options --enable-debug
ac_add_options --disable-optimize

Просмотреть файл

@ -83,7 +83,6 @@ switchtabResultLabel=Tab
keywordResultLabel=Keyword
searchengineResultLabel=Search
# LOCALIZATION NOTE (lockPrompt.text)
# %S will be replaced with the application name.
lockPrompt.title=Browser Startup Error
@ -91,12 +90,18 @@ lockPrompt.text=The bookmarks and history system will not be functional because
lockPromptInfoButton.label=Learn More
lockPromptInfoButton.accessKey=L
# LOCALIZATION NOTE (deletePagesLabel): Semi-colon list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
cmd.deletePages.label=Delete Page;Delete Pages
cmd.deletePages.accesskey=D
# LOCALIZATION NOTE (cmd.deleteSinglePage.accesskey,
# cmd.deleteMultiplePages.accesskey): these accesskeys can use the same
# character, since they're never displayed at the same time
cmd.deleteSinglePage.label=Delete Page
cmd.deleteSinglePage.accesskey=D
cmd.deleteMultiplePages.label=Delete Pages
cmd.deleteMultiplePages.accesskey=D
# LOCALIZATION NOTE (bookmarkPagesLabel): Semi-colon list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
cmd.bookmarkPages.label=Bookmark Page;Bookmark Pages
cmd.bookmarkPages.accesskey=B
# LOCALIZATION NOTE (cmd.bookmarkSinglePage.accesskey,
# cmd.bookmarkMultiplePages.accesskey): these accesskeys can use the same
# character, since they're never displayed at the same time
cmd.bookmarkSinglePage.label=Bookmark Page
cmd.bookmarkSinglePage.accesskey=B
cmd.bookmarkMultiplePages.label=Bookmark Pages
cmd.bookmarkMultiplePages.accesskey=B

Просмотреть файл

@ -55,8 +55,8 @@ available. -->
<!-- Site Data section manages sites using Storage API and is under Network -->
<!ENTITY siteData.label "Site Data">
<!ENTITY clearSiteData.label "Clear All Data">
<!ENTITY clearSiteData.accesskey "l">
<!ENTITY clearSiteData1.label "Clear Data">
<!ENTITY clearSiteData1.accesskey "l">
<!ENTITY siteDataSettings.label "Settings…">
<!ENTITY siteDataSettings.accesskey "i">
<!ENTITY siteDataLearnMoreLink.label "Learn more">

Просмотреть файл

@ -250,7 +250,7 @@ NewWebConsoleFrame.prototype = {
if (this.isBrowserConsole) {
shortcuts.on(l10n.getStr("webconsole.close.key"),
this.window.close.bind(this.window));
this.window.top.close.bind(this.window.top));
ZoomKeys.register(this.window);
} else if (Services.prefs.getBoolPref(PREF_SIDEBAR_ENABLED)) {

Просмотреть файл

@ -15,6 +15,15 @@
* [Code reviews](./contributing/code-reviews.md)
* [Filing good bugs](./contributing/filing-good-bugs.md)
* [Investigating performance issues](./contributing/performance.md)
* [Automated tests](tests/README.md)
* Running tests
* [`xpcshell`](tests/xpcshell.md)
* [Chrome mochitests](tests/mochitest-chrome.md)
* [DevTools mochitests](tests/mochitest-devtools.md)
* [Writing tests](tests/writing-tests.md)
* [Debugging intermittent failures](tests/debugging-intermittents.md)
* [Performance tests (DAMP)](tests/performance-tests.md)
* [Writing a new test](tests/writing-perf-tests.md)
* [Bugs and issue trackers](bugs-issues.md)
* [Files and directories](files/README.md)
* [Adding New Files](files/adding-files.md)
@ -46,12 +55,3 @@
* [Registering A New Actor](backend/actor-registration.md)
* [Actor Best Practices](backend/actor-best-practices.md)
* [Preferences](preferences.md)
* [Automated tests](tests/README.md)
* Running tests
* [`xpcshell`](tests/xpcshell.md)
* [Chrome mochitests](tests/mochitest-chrome.md)
* [DevTools mochitests](tests/mochitest-devtools.md)
* [Writing tests](tests/writing-tests.md)
* [Debugging intermittent failures](tests/debugging-intermittents.md)
* [Performance tests (DAMP)](tests/performance-tests.md)
* [Writing a new test](tests/writing-perf-tests.md)

Просмотреть файл

@ -1229,7 +1229,15 @@ nsIContent::GetContainingShadowHost() const
void
nsIContent::SetAssignedSlot(HTMLSlotElement* aSlot)
{
ExtendedContentSlots()->mAssignedSlot = aSlot;
MOZ_ASSERT(aSlot || GetExistingExtendedContentSlots());
nsExtendedContentSlots* slots = ExtendedContentSlots();
RefPtr<HTMLSlotElement> oldSlot = slots->mAssignedSlot.forget();
slots->mAssignedSlot = aSlot;
if (oldSlot != aSlot && IsElement() && AsElement()->HasServoData()) {
ServoRestyleManager::ClearServoDataFromSubtree(AsElement());
}
}
void
@ -1250,8 +1258,8 @@ nsIContent::SetXBLInsertionPoint(nsIContent* aContent)
// We just changed the flattened tree, so any Servo style data is now invalid.
// We rely on nsXBLService::LoadBindings to re-traverse the subtree afterwards.
if (oldInsertionPoint != aContent &&
IsStyledByServo() && IsElement() && AsElement()->HasServoData()) {
if (oldInsertionPoint != aContent && IsElement() &&
AsElement()->HasServoData()) {
ServoRestyleManager::ClearServoDataFromSubtree(AsElement());
}
}

Просмотреть файл

@ -57,7 +57,6 @@ ShadowRoot::ShadowRoot(Element* aElement, bool aClosed,
: DocumentFragment(aNodeInfo)
, DocumentOrShadowRoot(*this)
, mProtoBinding(aProtoBinding)
, mInsertionPointChanged(false)
, mIsComposedDocParticipant(false)
{
SetHost(aElement);
@ -433,32 +432,6 @@ ShadowRoot::MaybeReassignElement(Element* aElement,
return false;
}
void
ShadowRoot::DistributionChanged()
{
// FIXME(emilio): We could be more granular in a bunch of cases.
auto* host = GetHost();
if (!host || !host->IsInComposedDoc()) {
return;
}
auto* shell = OwnerDoc()->GetShell();
if (!shell) {
return;
}
shell->DestroyFramesForAndRestyle(host);
}
void
ShadowRoot::DistributeAllNodes()
{
//XXX Handle <slot>.
DistributionChanged();
}
Element*
ShadowRoot::GetActiveElement()
{
@ -513,7 +486,7 @@ ShadowRoot::AttributeChanged(nsIDocument* aDocument,
return;
}
//XXX optimize this!
// FIXME(emilio): We could be more granular in a bunch of cases.
shell->DestroyFramesForAndRestyle(aElement);
}

Просмотреть файл

@ -83,12 +83,6 @@ public:
return &DocumentOrShadowRoot::EnsureDOMStyleSheets();
}
/**
* Distributes all the explicit children of the pool host to the content
* insertion points in this ShadowRoot.
*/
void DistributeAllNodes();
private:
/**
@ -113,19 +107,10 @@ private:
const HTMLSlotElement* UnassignSlotFor(nsIContent* aContent,
const nsAString& aSlotName);
/**
* Called when we redistribute content after insertion points have changed.
*/
void DistributionChanged();
bool IsPooledNode(nsIContent* aChild) const;
public:
void AddSlot(HTMLSlotElement* aSlot);
void RemoveSlot(HTMLSlotElement* aSlot);
void SetInsertionPointChanged() { mInsertionPointChanged = true; }
void SetAssociatedBinding(nsXBLBinding* aBinding) { mAssociatedBinding = aBinding; }
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
@ -141,7 +126,11 @@ public:
void SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError);
void StyleSheetChanged();
bool IsComposedDocParticipant() { return mIsComposedDocParticipant; }
bool IsComposedDocParticipant() const
{
return mIsComposedDocParticipant;
}
void SetIsComposedDocParticipant(bool aIsComposedDocParticipant)
{
mIsComposedDocParticipant = aIsComposedDocParticipant;
@ -165,12 +154,6 @@ protected:
// owns |mProtoBinding|.
RefPtr<nsXBLBinding> mAssociatedBinding;
// A boolean that indicates that an insertion point was added or removed
// from this ShadowRoot and that the nodes need to be redistributed into
// the insertion points. After this flag is set, nodes will be distributed
// on the next mutation event.
bool mInsertionPointChanged;
// Flag to indicate whether the descendants of this shadow root are part of the
// composed document. Ideally, we would use a node flag on nodes to
// mark whether it is in the composed document, but we have run out of flags

Просмотреть файл

@ -3860,6 +3860,23 @@ nsDocument::TryChannelCharset(nsIChannel *aChannel,
}
}
static inline void
AssertNoStaleServoDataIn(const nsINode& aSubtreeRoot)
{
#ifdef DEBUG
for (const nsINode* node = aSubtreeRoot.GetFirstChild();
node;
node = node->GetNextNode()) {
if (node->IsElement()) {
MOZ_ASSERT(!node->AsElement()->HasServoData());
if (auto* shadow = node->AsElement()->GetShadowRoot()) {
AssertNoStaleServoDataIn(*shadow);
}
}
}
#endif
}
already_AddRefed<nsIPresShell>
nsDocument::CreateShell(nsPresContext* aContext, nsViewManager* aViewManager,
StyleSetHandle aStyleSet)
@ -3869,18 +3886,7 @@ nsDocument::CreateShell(nsPresContext* aContext, nsViewManager* aViewManager,
NS_ENSURE_FALSE(GetBFCacheEntry(), nullptr);
FillStyleSet(aStyleSet);
{
#ifdef DEBUG
for (nsINode* node = static_cast<nsINode*>(this)->GetFirstChild();
node;
node = node->GetNextNode(this)) {
if (node->IsElement()) {
MOZ_ASSERT(!node->AsElement()->HasServoData());
}
}
#endif
}
AssertNoStaleServoDataIn(static_cast<nsINode&>(*this));
RefPtr<PresShell> shell = new PresShell;
// Note: we don't hold a ref to the shell (it holds a ref to us)
@ -4011,15 +4017,7 @@ nsDocument::DeleteShell()
if (IsStyledByServo()) {
ClearStaleServoData();
#ifdef DEBUG
for (nsINode* node = static_cast<nsINode*>(this)->GetFirstChild();
node;
node = node->GetNextNode(this)) {
if (node->IsElement()) {
MOZ_ASSERT(!node->AsElement()->HasServoData());
}
}
#endif
AssertNoStaleServoDataIn(static_cast<nsINode&>(*this));
}
}

Просмотреть файл

@ -29,6 +29,9 @@ const char* mozilla::dom::ContentPrefs::gEarlyPrefs[] = {
"browser.sessionhistory.max_total_viewers",
#if defined(NIGHTLY_BUILD) || defined(DEBUG)
"browser.startup.record",
#endif
#if defined(ANDROID)
"consoleservice.logcat",
#endif
"content.cors.disable",
"content.cors.no_private_data",

Просмотреть файл

@ -3846,7 +3846,7 @@ SourceListener::Activate(SourceMediaStream* aStream,
MakeUnique<DeviceState>(
aAudioDevice,
aAudioDevice->GetMediaSource() == dom::MediaSourceEnum::Microphone &&
Preferences::GetBool("media.getusermedia.microphone.off_while_disabled.enabled", false));
Preferences::GetBool("media.getusermedia.microphone.off_while_disabled.enabled", true));
}
if (aVideoDevice) {
@ -4225,7 +4225,7 @@ SourceListener::CapturingAudio() const
{
MOZ_ASSERT(NS_IsMainThread());
return Activated() && mAudioDeviceState && !mAudioDeviceState->mStopped &&
(mAudioDeviceState->mDevice->mSource->IsFake() ||
(!mAudioDeviceState->mDevice->mSource->IsFake() ||
Preferences::GetBool("media.navigator.permission.fake"));
}

Просмотреть файл

@ -855,6 +855,12 @@
await pc2.setLocalDescription(answer);
pc1.removeTrack(pc1.getSenders()[0]);
hasProps(pc1.getSenders(),
[
{track: null},
{track: video}
]);
hasProps(pc1.getTransceivers(),
[
{
@ -871,6 +877,12 @@
pc1.removeTrack(pc1.getSenders()[1]);
hasProps(pc1.getSenders(),
[
{track: null},
{track: null}
]);
hasProps(pc1.getTransceivers(),
[
{

Просмотреть файл

@ -552,7 +552,8 @@ mozInlineSpellChecker::mozInlineSpellChecker() :
mNumPendingUpdateCurrentDictionary(0),
mDisabledAsyncToken(0),
mNeedsCheckAfterNavigation(false),
mFullSpellCheckScheduled(false)
mFullSpellCheckScheduled(false),
mIsListeningToEditActions(false)
{
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (prefs)

Просмотреть файл

@ -25,7 +25,7 @@ lazy_static = "1"
log = "0.4"
num-traits = "0.1.32"
time = "0.1"
rayon = "0.8"
rayon = "1"
webrender_api = {path = "../webrender_api"}
bitflags = "1.0"
thread_profiler = "0.1.1"

Просмотреть файл

@ -11,7 +11,7 @@ extern crate webrender;
mod boilerplate;
use boilerplate::{Example, HandyDandyRectBuilder};
use rayon::{Configuration as ThreadPoolConfig, ThreadPool};
use rayon::{ThreadPool, ThreadPoolBuilder};
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::sync::Arc;
@ -282,10 +282,11 @@ impl Example for App {
}
fn main() {
let worker_config =
ThreadPoolConfig::new().thread_name(|idx| format!("WebRender:Worker#{}", idx));
let workers =
ThreadPoolBuilder::new().thread_name(|idx| format!("WebRender:Worker#{}", idx))
.build();
let workers = Arc::new(ThreadPool::new(worker_config).unwrap());
let workers = Arc::new(workers.unwrap());
let opts = webrender::RendererOptions {
workers: Some(Arc::clone(&workers)),

Просмотреть файл

@ -15,6 +15,8 @@ void brush_vs(
#define VECS_PER_BRUSH_PRIM 2
#define VECS_PER_SEGMENT 2
#define BRUSH_FLAG_PERSPECTIVE_INTERPOLATION 1
struct BrushInstance {
int picture_address;
int prim_address;
@ -24,6 +26,7 @@ struct BrushInstance {
int z;
int segment_index;
int edge_mask;
int flags;
ivec3 user_data;
};
@ -37,7 +40,8 @@ BrushInstance load_brush() {
bi.scroll_node_id = aData0.z & 0xffff;
bi.z = aData0.w;
bi.segment_index = aData1.x & 0xffff;
bi.edge_mask = aData1.x >> 16;
bi.edge_mask = (aData1.x >> 16) & 0xff;
bi.flags = (aData1.x >> 24);
bi.user_data = aData1.yzw;
return bi;
@ -135,6 +139,8 @@ void main(void) {
#endif
} else {
bvec4 edge_mask = notEqual(brush.edge_mask & ivec4(1, 2, 4, 8), ivec4(0));
bool do_perspective_interpolation = (brush.flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) != 0;
vi = write_transform_vertex(
local_segment_rect,
brush_prim.local_rect,
@ -142,7 +148,8 @@ void main(void) {
mix(vec4(0.0), vec4(1.0), edge_mask),
float(brush.z),
scroll_node,
pic_task
pic_task,
do_perspective_interpolation
);
}

Просмотреть файл

@ -3,11 +3,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#define VECS_PER_SPECIFIC_BRUSH 5
#define FORCE_NO_PERSPECTIVE
#include shared,prim_shared,brush
varying vec3 vUv;
varying float vW;
flat varying float vAmount;
flat varying int vOp;
@ -25,10 +25,9 @@ void brush_vs(
) {
PictureTask src_task = fetch_picture_task(user_data.x);
vec2 texture_size = vec2(textureSize(sColor0, 0).xy);
vec2 uv = (vi.snapped_device_pos +
vec2 uv = vi.snapped_device_pos +
src_task.common_data.task_rect.p0 -
src_task.content_origin) * vi.w;
vW = vi.w;
src_task.content_origin;
vUv = vec3(uv / texture_size, src_task.common_data.texture_layer_index);
vOp = user_data.y;
@ -115,8 +114,7 @@ vec4 Opacity(vec4 Cs, float amount) {
}
vec4 brush_fs() {
vec2 uv = vUv.xy / vW;
vec4 Cs = texture(sColor0, vec3(uv, vUv.z));
vec4 Cs = texture(sColor0, vUv);
// Un-premultiply the input.
Cs.rgb /= Cs.a;

Просмотреть файл

@ -6,9 +6,6 @@
#include shared,prim_shared,brush
varying vec3 vUv;
varying float vW;
varying vec3 vSrcUv;
varying vec3 vBackdropUv;
flat varying int vOp;
@ -24,18 +21,17 @@ void brush_vs(
) {
vec2 texture_size = vec2(textureSize(sCacheRGBA8, 0));
vOp = user_data.x;
vW = vi.w;
PictureTask src_task = fetch_picture_task(user_data.z);
vec2 src_uv = (vi.snapped_device_pos +
vec2 src_uv = vi.snapped_device_pos +
src_task.common_data.task_rect.p0 -
src_task.content_origin) * vi.w;
src_task.content_origin;
vSrcUv = vec3(src_uv / texture_size, src_task.common_data.texture_layer_index);
RenderTaskCommonData backdrop_task = fetch_render_task_common_data(user_data.y);
vec2 backdrop_uv = (vi.snapped_device_pos +
vec2 backdrop_uv = vi.snapped_device_pos +
backdrop_task.task_rect.p0 -
src_task.content_origin) * vi.w;
src_task.content_origin;
vBackdropUv = vec3(backdrop_uv / texture_size, backdrop_task.texture_layer_index);
}
#endif
@ -205,10 +201,8 @@ const int MixBlendMode_Color = 14;
const int MixBlendMode_Luminosity = 15;
vec4 brush_fs() {
vec2 uv = vUv.xy / vW;
vec4 Cb = texture(sCacheRGBA8, vBackdropUv);
vec4 Cs = texture(sCacheRGBA8, vSrcUv);
vec4 Cb = textureLod(sCacheRGBA8, vBackdropUv, 0.0);
vec4 Cs = textureLod(sCacheRGBA8, vSrcUv, 0.0);
if (Cb.a == 0.0) {
return Cs;

Просмотреть файл

@ -2,7 +2,9 @@
* 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/. */
#include shared,prim_shared
#define VECS_PER_SPECIFIC_BRUSH 2
#include shared,prim_shared,brush
flat varying int vGradientAddress;
flat varying float vGradientRepeat;
@ -12,24 +14,34 @@ flat varying vec2 vEndCenter;
flat varying float vStartRadius;
flat varying float vEndRadius;
flat varying vec2 vTileSize;
flat varying vec2 vTileRepeat;
varying vec2 vPos;
#ifdef WR_FEATURE_ALPHA_PASS
varying vec2 vLocalPos;
#endif
#ifdef WR_VERTEX_SHADER
void main(void) {
Primitive prim = load_primitive();
RadialGradient gradient = fetch_radial_gradient(prim.specific_prim_address);
VertexInfo vi = write_vertex(prim.local_rect,
prim.local_clip_rect,
prim.z,
prim.scroll_node,
prim.task,
prim.local_rect);
struct RadialGradient {
vec4 start_end_center;
vec4 start_end_radius_ratio_xy_extend_mode;
};
vPos = vi.local_pos - prim.local_rect.p0;
RadialGradient fetch_radial_gradient(int address) {
vec4 data[2] = fetch_from_resource_cache_2(address);
return RadialGradient(data[0], data[1]);
}
void brush_vs(
VertexInfo vi,
int prim_address,
RectWithSize local_rect,
ivec3 user_data,
PictureTask pic_task
) {
RadialGradient gradient = fetch_radial_gradient(prim_address);
vPos = vi.local_pos - local_rect.p0;
vStartCenter = gradient.start_end_center.xy;
vEndCenter = gradient.start_end_center.zw;
@ -37,38 +49,28 @@ void main(void) {
vStartRadius = gradient.start_end_radius_ratio_xy_extend_mode.x;
vEndRadius = gradient.start_end_radius_ratio_xy_extend_mode.y;
vTileSize = gradient.tile_size_repeat.xy;
vTileRepeat = gradient.tile_size_repeat.zw;
// Transform all coordinates by the y scale so the
// fragment shader can work with circles
float ratio_xy = gradient.start_end_radius_ratio_xy_extend_mode.z;
vPos.y *= ratio_xy;
vStartCenter.y *= ratio_xy;
vEndCenter.y *= ratio_xy;
vTileSize.y *= ratio_xy;
vTileRepeat.y *= ratio_xy;
vGradientAddress = prim.specific_prim_address + VECS_PER_GRADIENT;
vGradientAddress = user_data.x;
// Whether to repeat the gradient instead of clamping.
vGradientRepeat = float(int(gradient.start_end_radius_ratio_xy_extend_mode.w) != EXTEND_MODE_CLAMP);
write_clip(vi.screen_pos, prim.clip_area);
#ifdef WR_FEATURE_ALPHA_PASS
vLocalPos = vi.local_pos;
#endif
}
#endif
#ifdef WR_FRAGMENT_SHADER
void main(void) {
vec2 pos = mod(vPos, vTileRepeat);
if (pos.x >= vTileSize.x ||
pos.y >= vTileSize.y) {
discard;
}
vec4 brush_fs() {
vec2 cd = vEndCenter - vStartCenter;
vec2 pd = pos - vStartCenter;
vec2 pd = vPos - vStartCenter;
float rd = vEndRadius - vStartRadius;
// Solve for t in length(t * cd - pd) = vStartRadius + t * rd
@ -110,6 +112,10 @@ void main(void) {
offset,
vGradientRepeat);
oFragColor = color * do_clip();
#ifdef WR_FEATURE_ALPHA_PASS
color *= init_transform_fs(vLocalPos);
#endif
return color;
}
#endif

Просмотреть файл

@ -0,0 +1,166 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#define VECS_PER_SPECIFIC_BRUSH 0
#include shared,prim_shared,brush
// TODO(gw): Consider whether we should even have separate shader compilations
// for the various YUV modes. To save on the number of shaders we
// need to compile, it might be worth just doing this as an
// uber-shader instead.
// TODO(gw): Regardless of the above, we should remove the separate shader
// compilations for the different color space matrix below. That
// can be provided by a branch in the VS and pushed through the
// interpolators, or even as a uniform that breaks batches, rather
// that needing to compile to / select a different shader when
// there is a different color space.
#ifdef WR_FEATURE_ALPHA_PASS
varying vec2 vLocalPos;
#endif
#if defined (WR_FEATURE_YUV_PLANAR)
varying vec3 vUv_Y;
flat varying vec4 vUvBounds_Y;
varying vec3 vUv_U;
flat varying vec4 vUvBounds_U;
varying vec3 vUv_V;
flat varying vec4 vUvBounds_V;
#elif defined (WR_FEATURE_YUV_NV12)
varying vec3 vUv_Y;
flat varying vec4 vUvBounds_Y;
varying vec3 vUv_UV;
flat varying vec4 vUvBounds_UV;
#elif defined (WR_FEATURE_YUV_INTERLEAVED)
varying vec3 vUv_YUV;
flat varying vec4 vUvBounds_YUV;
#endif
#ifdef WR_VERTEX_SHADER
void write_uv_rect(
int resource_id,
vec2 f,
vec2 texture_size,
out vec3 uv,
out vec4 uv_bounds
) {
ImageResource res = fetch_image_resource(resource_id);
vec2 uv0 = res.uv_rect.p0;
vec2 uv1 = res.uv_rect.p1;
uv.xy = mix(uv0, uv1, f);
uv.z = res.layer;
uv_bounds = vec4(uv0 + vec2(0.5), uv1 - vec2(0.5));
#ifndef WR_FEATURE_TEXTURE_RECT
uv.xy /= texture_size;
uv_bounds /= texture_size.xyxy;
#endif
}
void brush_vs(
VertexInfo vi,
int prim_address,
RectWithSize local_rect,
ivec3 user_data,
PictureTask pic_task
) {
vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size;
#ifdef WR_FEATURE_ALPHA_PASS
vLocalPos = vi.local_pos;
#endif
#if defined (WR_FEATURE_YUV_PLANAR)
write_uv_rect(user_data.x, f, vec2(textureSize(sColor0, 0).xy), vUv_Y, vUvBounds_Y);
write_uv_rect(user_data.y, f, vec2(textureSize(sColor1, 0).xy), vUv_U, vUvBounds_U);
write_uv_rect(user_data.z, f, vec2(textureSize(sColor2, 0).xy), vUv_V, vUvBounds_V);
#elif defined (WR_FEATURE_YUV_NV12)
write_uv_rect(user_data.x, f, vec2(textureSize(sColor0, 0).xy), vUv_Y, vUvBounds_Y);
write_uv_rect(user_data.y, f, vec2(textureSize(sColor1, 0).xy), vUv_UV, vUvBounds_UV);
#elif defined (WR_FEATURE_YUV_INTERLEAVED)
write_uv_rect(user_data.x, f, vec2(textureSize(sColor0, 0).xy), vUv_YUV, vUvBounds_YUV);
#endif //WR_FEATURE_YUV_*
}
#endif
#ifdef WR_FRAGMENT_SHADER
#if !defined(WR_FEATURE_YUV_REC601) && !defined(WR_FEATURE_YUV_REC709)
#define WR_FEATURE_YUV_REC601
#endif
// The constants added to the Y, U and V components are applied in the fragment shader.
#if defined(WR_FEATURE_YUV_REC601)
// From Rec601:
// [R] [1.1643835616438356, 0.0, 1.5960267857142858 ] [Y - 16]
// [G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708 ] x [U - 128]
// [B] [1.1643835616438356, 2.017232142857143, 8.862867620416422e-17] [V - 128]
//
// For the range [0,1] instead of [0,255].
//
// The matrix is stored in column-major.
const mat3 YuvColorMatrix = mat3(
1.16438, 1.16438, 1.16438,
0.0, -0.39176, 2.01723,
1.59603, -0.81297, 0.0
);
#elif defined(WR_FEATURE_YUV_REC709)
// From Rec709:
// [R] [1.1643835616438356, 4.2781193979771426e-17, 1.7927410714285714] [Y - 16]
// [G] = [1.1643835616438358, -0.21324861427372963, -0.532909328559444 ] x [U - 128]
// [B] [1.1643835616438356, 2.1124017857142854, 0.0 ] [V - 128]
//
// For the range [0,1] instead of [0,255]:
//
// The matrix is stored in column-major.
const mat3 YuvColorMatrix = mat3(
1.16438, 1.16438, 1.16438,
0.0 , -0.21325, 2.11240,
1.79274, -0.53291, 0.0
);
#endif
vec4 brush_fs() {
vec3 yuv_value;
#if defined (WR_FEATURE_YUV_PLANAR)
// The yuv_planar format should have this third texture coordinate.
vec2 uv_y = clamp(vUv_Y.xy, vUvBounds_Y.xy, vUvBounds_Y.zw);
vec2 uv_u = clamp(vUv_U.xy, vUvBounds_U.xy, vUvBounds_U.zw);
vec2 uv_v = clamp(vUv_V.xy, vUvBounds_V.xy, vUvBounds_V.zw);
yuv_value.x = TEX_SAMPLE(sColor0, vec3(uv_y, vUv_Y.z)).r;
yuv_value.y = TEX_SAMPLE(sColor1, vec3(uv_u, vUv_U.z)).r;
yuv_value.z = TEX_SAMPLE(sColor2, vec3(uv_v, vUv_V.z)).r;
#elif defined (WR_FEATURE_YUV_NV12)
vec2 uv_y = clamp(vUv_Y.xy, vUvBounds_Y.xy, vUvBounds_Y.zw);
vec2 uv_uv = clamp(vUv_UV.xy, vUvBounds_UV.xy, vUvBounds_UV.zw);
yuv_value.x = TEX_SAMPLE(sColor0, vec3(uv_y, vUv_Y.z)).r;
yuv_value.yz = TEX_SAMPLE(sColor1, vec3(uv_uv, vUv_UV.z)).rg;
#elif defined (WR_FEATURE_YUV_INTERLEAVED)
// "The Y, Cb and Cr color channels within the 422 data are mapped into
// the existing green, blue and red color channels."
// https://www.khronos.org/registry/OpenGL/extensions/APPLE/APPLE_rgb_422.txt
vec2 uv_y = clamp(vUv_YUV.xy, vUvBounds_YUV.xy, vUvBounds_YUV.zw);
yuv_value = TEX_SAMPLE(sColor0, vec3(uv_y, vUv_YUV.z)).gbr;
#else
yuv_value = vec3(0.0);
#endif
// See the YuvColorMatrix definition for an explanation of where the constants come from.
vec3 rgb = YuvColorMatrix * (yuv_value - vec3(0.06275, 0.50196, 0.50196));
vec4 color = vec4(rgb, 1.0);
#ifdef WR_FEATURE_ALPHA_PASS
color *= init_transform_fs(vLocalPos);
#endif
return color;
}
#endif

Просмотреть файл

@ -4,73 +4,31 @@
#ifdef WR_FRAGMENT_SHADER
// One iteration of Newton's method on the 2D equation of an ellipse:
//
// Signed distance to an ellipse.
// Taken from http://www.iquilezles.org/www/articles/ellipsedist/ellipsedist.htm
// Note that this fails for exact circles.
// E(x, y) = x^2/a^2 + y^2/b^2 - 1
//
float sdEllipse( vec2 p, in vec2 ab ) {
p = abs( p ); if( p.x > p.y ){ p=p.yx; ab=ab.yx; }
float l = ab.y*ab.y - ab.x*ab.x;
float m = ab.x*p.x/l;
float n = ab.y*p.y/l;
float m2 = m*m;
float n2 = n*n;
float c = (m2 + n2 - 1.0)/3.0;
float c3 = c*c*c;
float q = c3 + m2*n2*2.0;
float d = c3 + m2*n2;
float g = m + m*n2;
float co;
if( d<0.0 )
{
float p = acos(q/c3)/3.0;
float s = cos(p);
float t = sin(p)*sqrt(3.0);
float rx = sqrt( -c*(s + t + 2.0) + m2 );
float ry = sqrt( -c*(s - t + 2.0) + m2 );
co = ( ry + sign(l)*rx + abs(g)/(rx*ry) - m)/2.0;
}
else
{
float h = 2.0*m*n*sqrt( d );
float s = sign(q+h)*pow( abs(q+h), 1.0/3.0 );
float u = sign(q-h)*pow( abs(q-h), 1.0/3.0 );
float rx = -s - u - c*4.0 + 2.0*m2;
float ry = (s - u)*sqrt(3.0);
float rm = sqrt( rx*rx + ry*ry );
float p = ry/sqrt(rm-rx);
co = (p + 2.0*g/rm - m)/2.0;
}
float si = sqrt( 1.0 - co*co );
vec2 r = vec2( ab.x*co, ab.y*si );
return length(r - p ) * sign(p.y-r.y);
}
// The Jacobian of this equation is:
//
// J(E(x, y)) = [ 2*x/a^2 2*y/b^2 ]
//
// We approximate the distance with:
//
// E(x, y) / ||J(E(x, y))||
//
// See G. Taubin, "Distance Approximations for Rasterizing Implicit
// Curves", section 3.
float distance_to_ellipse(vec2 p, vec2 radii, float aa_range) {
// sdEllipse fails on exact circles, so handle equal
// radii here. The branch coherency should make this
// a performance win for the circle case too.
float len = length(p);
if (radii.x == radii.y) {
return len - radii.x;
float dist;
if (any(lessThanEqual(radii, vec2(0.0)))) {
dist = length(p);
} else {
if (len < min(radii.x, radii.y) - aa_range) {
return -aa_range;
} else if (len > max(radii.x, radii.y) + aa_range) {
return aa_range;
}
return sdEllipse(p, radii);
vec2 invRadiiSq = 1.0 / (radii * radii);
float g = dot(p * p * invRadiiSq, vec2(1.0)) - 1.0;
vec2 dG = 2.0 * p * invRadiiSq;
dist = g * inversesqrt(dot(dG, dG));
}
return clamp(dist, -aa_range, aa_range);
}
float clip_against_ellipse_if_needed(

Просмотреть файл

@ -315,27 +315,6 @@ Gradient fetch_gradient(int address) {
return Gradient(data[0], data[1], data[2]);
}
struct GradientStop {
vec4 color;
vec4 offset;
};
GradientStop fetch_gradient_stop(int address) {
vec4 data[2] = fetch_from_resource_cache_2(address);
return GradientStop(data[0], data[1]);
}
struct RadialGradient {
vec4 start_end_center;
vec4 start_end_radius_ratio_xy_extend_mode;
vec4 tile_size_repeat;
};
RadialGradient fetch_radial_gradient(int address) {
vec4 data[3] = fetch_from_resource_cache_3(address);
return RadialGradient(data[0], data[1], data[2]);
}
struct Glyph {
vec2 offset;
};
@ -624,7 +603,8 @@ VertexInfo write_transform_vertex(RectWithSize local_segment_rect,
vec4 clip_edge_mask,
float z,
ClipScrollNode scroll_node,
PictureTask task) {
PictureTask task,
bool do_perspective_interpolation) {
// Calculate a clip rect from local_rect + local clip
RectWithEndpoint clip_rect = to_rect_with_endpoint(local_clip_rect);
RectWithEndpoint segment_rect = to_rect_with_endpoint(local_segment_rect);
@ -661,13 +641,16 @@ VertexInfo write_transform_vertex(RectWithSize local_segment_rect,
vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio;
vec2 task_offset = task.common_data.task_rect.p0 - task.content_origin;
// Force w = 1, if we don't want perspective interpolation (for
// example, drawing a screen-space quad on an element with a
// perspective transform).
world_pos.w = mix(1.0, world_pos.w, do_perspective_interpolation);
// We want the world space coords to be perspective divided by W.
// We also want that to apply to any interpolators. However, we
// want a constant Z across the primitive, since we're using it
// for draw ordering - so scale by the W coord to ensure this.
vec4 final_pos = vec4((device_pos + task_offset) * world_pos.w,
z * world_pos.w,
world_pos.w);
vec4 final_pos = vec4(device_pos + task_offset, z, 1.0) * world_pos.w;
gl_Position = uTransform * final_pos;
vLocalBounds = mix(
@ -694,7 +677,8 @@ VertexInfo write_transform_vertex_primitive(Primitive prim) {
vec4(1.0),
prim.z,
prim.scroll_node,
prim.task
prim.task,
true
);
}

Просмотреть файл

@ -320,7 +320,8 @@ void main(void) {
edge_mask,
prim.z,
prim.scroll_node,
prim.task);
prim.task,
true);
#else
VertexInfo vi = write_vertex(segment_rect,
prim.local_clip_rect,

Просмотреть файл

@ -233,7 +233,8 @@ void main(void) {
edge_mask,
prim.z,
prim.scroll_node,
prim.task);
prim.task,
true);
#else
VertexInfo vi = write_vertex(segment_rect,
prim.local_clip_rect,

Просмотреть файл

@ -9,6 +9,16 @@ varying vec4 vColor;
varying vec2 vLocalPos;
#ifdef WR_VERTEX_SHADER
struct GradientStop {
vec4 color;
vec4 offset;
};
GradientStop fetch_gradient_stop(int address) {
vec4 data[2] = fetch_from_resource_cache_2(address);
return GradientStop(data[0], data[1]);
}
void main(void) {
Primitive prim = load_primitive();
Gradient gradient = fetch_gradient(prim.specific_prim_address);
@ -73,7 +83,8 @@ void main(void) {
vec4(1.0),
prim.z,
prim.scroll_node,
prim.task);
prim.task,
true);
vLocalPos = vi.local_pos;
vec2 f = (vi.local_pos.xy - prim.local_rect.p0) / prim.local_rect.size;
#else

Просмотреть файл

@ -1,197 +0,0 @@
/* 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/. */
#include shared,prim_shared
// If this is in WR_FEATURE_TEXTURE_RECT mode, the rect and size use non-normalized
// texture coordinates. Otherwise, it uses normalized texture coordinates. Please
// check GL_TEXTURE_RECTANGLE.
flat varying vec2 vTextureOffsetY; // Offset of the y plane into the texture atlas.
flat varying vec2 vTextureOffsetU; // Offset of the u plane into the texture atlas.
flat varying vec2 vTextureOffsetV; // Offset of the v plane into the texture atlas.
flat varying vec2 vTextureSizeY; // Size of the y plane in the texture atlas.
flat varying vec2 vTextureSizeUv; // Size of the u and v planes in the texture atlas.
flat varying vec2 vStretchSize;
flat varying vec2 vHalfTexelY; // Normalized length of the half of a Y texel.
flat varying vec2 vHalfTexelUv; // Normalized length of the half of u and v texels.
flat varying vec3 vLayers;
#ifdef WR_FEATURE_TRANSFORM
flat varying vec4 vLocalRect;
#endif
varying vec2 vLocalPos;
#ifdef WR_VERTEX_SHADER
struct YuvImage {
vec2 size;
};
YuvImage fetch_yuv_image(int address) {
vec4 data = fetch_from_resource_cache_1(address);
return YuvImage(data.xy);
}
void main(void) {
Primitive prim = load_primitive();
#ifdef WR_FEATURE_TRANSFORM
VertexInfo vi = write_transform_vertex_primitive(prim);
vLocalPos = vi.local_pos;
vLocalRect = vec4(prim.local_rect.p0, prim.local_rect.p0 + prim.local_rect.size);
#else
VertexInfo vi = write_vertex(prim.local_rect,
prim.local_clip_rect,
prim.z,
prim.scroll_node,
prim.task,
prim.local_rect);
vLocalPos = vi.local_pos - prim.local_rect.p0;
#endif
write_clip(vi.screen_pos, prim.clip_area);
ImageResource y_rect = fetch_image_resource(prim.user_data0);
vLayers = vec3(y_rect.layer, 0.0, 0.0);
#ifndef WR_FEATURE_INTERLEAVED_Y_CB_CR // only 1 channel
ImageResource u_rect = fetch_image_resource(prim.user_data1);
vLayers.y = u_rect.layer;
#ifndef WR_FEATURE_NV12 // 2 channel
ImageResource v_rect = fetch_image_resource(prim.user_data2);
vLayers.z = v_rect.layer;
#endif
#endif
// If this is in WR_FEATURE_TEXTURE_RECT mode, the rect and size use
// non-normalized texture coordinates.
#ifdef WR_FEATURE_TEXTURE_RECT
vec2 y_texture_size_normalization_factor = vec2(1, 1);
#else
vec2 y_texture_size_normalization_factor = vec2(textureSize(sColor0, 0));
#endif
vec2 y_st0 = y_rect.uv_rect.p0 / y_texture_size_normalization_factor;
vec2 y_st1 = y_rect.uv_rect.p1 / y_texture_size_normalization_factor;
vTextureSizeY = y_st1 - y_st0;
vTextureOffsetY = y_st0;
#ifndef WR_FEATURE_INTERLEAVED_Y_CB_CR
// This assumes the U and V surfaces have the same size.
#ifdef WR_FEATURE_TEXTURE_RECT
vec2 uv_texture_size_normalization_factor = vec2(1, 1);
#else
vec2 uv_texture_size_normalization_factor = vec2(textureSize(sColor1, 0));
#endif
vec2 u_st0 = u_rect.uv_rect.p0 / uv_texture_size_normalization_factor;
vec2 u_st1 = u_rect.uv_rect.p1 / uv_texture_size_normalization_factor;
#ifndef WR_FEATURE_NV12
vec2 v_st0 = v_rect.uv_rect.p0 / uv_texture_size_normalization_factor;
#endif
vTextureSizeUv = u_st1 - u_st0;
vTextureOffsetU = u_st0;
#ifndef WR_FEATURE_NV12
vTextureOffsetV = v_st0;
#endif
#endif
YuvImage image = fetch_yuv_image(prim.specific_prim_address);
vStretchSize = image.size;
vHalfTexelY = vec2(0.5) / y_texture_size_normalization_factor;
#ifndef WR_FEATURE_INTERLEAVED_Y_CB_CR
vHalfTexelUv = vec2(0.5) / uv_texture_size_normalization_factor;
#endif
}
#endif
#ifdef WR_FRAGMENT_SHADER
#if !defined(WR_FEATURE_YUV_REC601) && !defined(WR_FEATURE_YUV_REC709)
#define WR_FEATURE_YUV_REC601
#endif
// The constants added to the Y, U and V components are applied in the fragment shader.
#if defined(WR_FEATURE_YUV_REC601)
// From Rec601:
// [R] [1.1643835616438356, 0.0, 1.5960267857142858 ] [Y - 16]
// [G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708 ] x [U - 128]
// [B] [1.1643835616438356, 2.017232142857143, 8.862867620416422e-17] [V - 128]
//
// For the range [0,1] instead of [0,255].
//
// The matrix is stored in column-major.
const mat3 YuvColorMatrix = mat3(
1.16438, 1.16438, 1.16438,
0.0, -0.39176, 2.01723,
1.59603, -0.81297, 0.0
);
#elif defined(WR_FEATURE_YUV_REC709)
// From Rec709:
// [R] [1.1643835616438356, 4.2781193979771426e-17, 1.7927410714285714] [Y - 16]
// [G] = [1.1643835616438358, -0.21324861427372963, -0.532909328559444 ] x [U - 128]
// [B] [1.1643835616438356, 2.1124017857142854, 0.0 ] [V - 128]
//
// For the range [0,1] instead of [0,255]:
//
// The matrix is stored in column-major.
const mat3 YuvColorMatrix = mat3(
1.16438, 1.16438, 1.16438,
0.0 , -0.21325, 2.11240,
1.79274, -0.53291, 0.0
);
#endif
void main(void) {
#ifdef WR_FEATURE_TRANSFORM
float alpha = init_transform_fs(vLocalPos);
// We clamp the texture coordinate calculation here to the local rectangle boundaries,
// which makes the edge of the texture stretch instead of repeat.
vec2 relative_pos_in_rect = clamp(vLocalPos, vLocalRect.xy, vLocalRect.zw) - vLocalRect.xy;
#else
float alpha = 1.0;;
vec2 relative_pos_in_rect = vLocalPos;
#endif
alpha *= do_clip();
// We clamp the texture coordinates to the half-pixel offset from the borders
// in order to avoid sampling outside of the texture area.
vec2 st_y = vTextureOffsetY + clamp(
relative_pos_in_rect / vStretchSize * vTextureSizeY,
vHalfTexelY, vTextureSizeY - vHalfTexelY);
#ifndef WR_FEATURE_INTERLEAVED_Y_CB_CR
vec2 uv_offset = clamp(
relative_pos_in_rect / vStretchSize * vTextureSizeUv,
vHalfTexelUv, vTextureSizeUv - vHalfTexelUv);
// NV12 only uses 2 textures. The sColor0 is for y and sColor1 is for uv.
// The texture coordinates of u and v are the same. So, we could skip the
// st_v if the format is NV12.
vec2 st_u = vTextureOffsetU + uv_offset;
#endif
vec3 yuv_value;
#ifdef WR_FEATURE_INTERLEAVED_Y_CB_CR
// "The Y, Cb and Cr color channels within the 422 data are mapped into
// the existing green, blue and red color channels."
// https://www.khronos.org/registry/OpenGL/extensions/APPLE/APPLE_rgb_422.txt
yuv_value = TEX_SAMPLE(sColor0, vec3(st_y, vLayers.x)).gbr;
#elif defined(WR_FEATURE_NV12)
yuv_value.x = TEX_SAMPLE(sColor0, vec3(st_y, vLayers.x)).r;
yuv_value.yz = TEX_SAMPLE(sColor1, vec3(st_u, vLayers.y)).rg;
#else
// The yuv_planar format should have this third texture coordinate.
vec2 st_v = vTextureOffsetV + uv_offset;
yuv_value.x = TEX_SAMPLE(sColor0, vec3(st_y, vLayers.x)).r;
yuv_value.y = TEX_SAMPLE(sColor1, vec3(st_u, vLayers.y)).r;
yuv_value.z = TEX_SAMPLE(sColor2, vec3(st_v, vLayers.z)).r;
#endif
// See the YuvColorMatrix definition for an explanation of where the constants come from.
vec3 rgb = YuvColorMatrix * (yuv_value - vec3(0.06275, 0.50196, 0.50196));
oFragColor = vec4(rgb * alpha, alpha);
}
#endif

Просмотреть файл

@ -7,22 +7,20 @@ use api::{DeviceUintRect, DeviceUintPoint, DeviceUintSize, ExternalImageType, Fi
use api::{DeviceIntPoint, LayerPoint, SubpixelDirection, YuvColorSpace, YuvFormat};
use api::{LayerToWorldTransform, WorldPixel};
use border::{BorderCornerInstance, BorderCornerSide, BorderEdgeKind};
use clip::{ClipSource, ClipStore};
use clip::{ClipSource, ClipStore, ClipWorkItem};
use clip_scroll_tree::{CoordinateSystemId};
use euclid::{TypedTransform3D, vec3};
use glyph_rasterizer::GlyphFormat;
use gpu_cache::{GpuCache, GpuCacheAddress};
use gpu_types::{BrushImageKind, BrushInstance, ClipChainRectIndex};
use gpu_types::{BrushFlags, BrushImageKind, BrushInstance, ClipChainRectIndex};
use gpu_types::{ClipMaskInstance, ClipScrollNodeIndex};
use gpu_types::{CompositePrimitiveInstance, PrimitiveInstance, SimplePrimitiveInstance};
use internal_types::{FastHashMap, SourceTexture};
use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
use picture::{ContentOrigin, PictureCompositeMode, PictureKind, PicturePrimitive, PictureSurface};
use plane_split::{BspSplitter, Polygon, Splitter};
use prim_store::{ImageSource, PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore};
use prim_store::{BrushPrimitive, BrushKind, DeferredResolve, EdgeAaSegmentMask, PrimitiveRun};
use render_task::{ClipWorkItem};
use render_task::{RenderTaskAddress, RenderTaskId};
use render_task::{RenderTaskKind, RenderTaskTree};
use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKind, RenderTaskTree};
use renderer::{BlendMode, ImageBufferKind};
use renderer::BLOCKS_PER_UV_RECT;
use resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache};
@ -40,10 +38,8 @@ const OPAQUE_TASK_ADDRESS: RenderTaskAddress = RenderTaskAddress(0x7fff);
pub enum TransformBatchKind {
TextRun(GlyphFormat),
Image(ImageBufferKind),
YuvImage(ImageBufferKind, YuvFormat, YuvColorSpace),
AlignedGradient,
AngleGradient,
RadialGradient,
BorderCorner,
BorderEdge,
}
@ -80,6 +76,8 @@ pub enum BrushBatchKind {
source_id: RenderTaskId,
backdrop_id: RenderTaskId,
},
YuvImage(ImageBufferKind, YuvFormat, YuvColorSpace),
RadialGradient,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
@ -953,6 +951,7 @@ impl AlphaBatchBuilder {
z,
segment_index: 0,
edge_flags: EdgeAaSegmentMask::empty(),
brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
user_data: [
cache_task_address.0 as i32,
BrushImageKind::Simple as i32,
@ -1039,6 +1038,7 @@ impl AlphaBatchBuilder {
z,
segment_index: 0,
edge_flags: EdgeAaSegmentMask::empty(),
brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
user_data: [
cache_task_address.0 as i32,
BrushImageKind::Simple as i32,
@ -1052,12 +1052,12 @@ impl AlphaBatchBuilder {
}
let secondary_id = secondary_render_task_id.expect("no secondary!?");
let render_task = &render_tasks[secondary_id];
let saved_index = render_tasks[secondary_id].saved_index.expect("no saved index!?");
debug_assert_ne!(saved_index, SavedTargetIndex::PENDING);
let secondary_task_address = render_tasks.get_task_address(secondary_id);
let render_pass_index = render_task.pass_index.expect("no render_pass_index!?");
let secondary_textures = BatchTextures {
colors: [
SourceTexture::RenderTaskCacheRGBA8(render_pass_index),
SourceTexture::RenderTaskCache(saved_index),
SourceTexture::Invalid,
SourceTexture::Invalid,
],
@ -1116,6 +1116,7 @@ impl AlphaBatchBuilder {
z,
segment_index: 0,
edge_flags: EdgeAaSegmentMask::empty(),
brush_flags: BrushFlags::empty(),
user_data: [
cache_task_address.0 as i32,
filter_mode,
@ -1155,6 +1156,7 @@ impl AlphaBatchBuilder {
z,
segment_index: 0,
edge_flags: EdgeAaSegmentMask::empty(),
brush_flags: BrushFlags::empty(),
user_data: [
mode as u32 as i32,
backdrop_task_address.0 as i32,
@ -1226,73 +1228,6 @@ impl AlphaBatchBuilder {
let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
batch.push(base_instance.build(0, 0, 0));
}
PrimitiveKind::RadialGradient => {
let kind = BatchKind::Transformable(
transform_kind,
TransformBatchKind::RadialGradient,
);
let key = BatchKey::new(kind, non_segmented_blend_mode, no_textures);
let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
batch.push(base_instance.build(0, 0, 0));
}
PrimitiveKind::YuvImage => {
let mut textures = BatchTextures::no_texture();
let mut uv_rect_addresses = [0; 3];
let image_yuv_cpu =
&ctx.prim_store.cpu_yuv_images[prim_metadata.cpu_prim_index.0];
//yuv channel
let channel_count = image_yuv_cpu.format.get_plane_num();
debug_assert!(channel_count <= 3);
for channel in 0 .. channel_count {
let image_key = image_yuv_cpu.yuv_key[channel];
let cache_item = resolve_image(
ImageRequest {
key: image_key,
rendering: image_yuv_cpu.image_rendering,
tile: None,
},
ctx.resource_cache,
gpu_cache,
deferred_resolves,
);
if cache_item.texture_id == SourceTexture::Invalid {
warn!("Warnings: skip a PrimitiveKind::YuvImage");
debug!("at {:?}.", task_relative_bounding_rect);
return;
}
textures.colors[channel] = cache_item.texture_id;
uv_rect_addresses[channel] = cache_item.uv_rect_handle.as_int(gpu_cache);
}
// All yuv textures should be the same type.
let buffer_kind = get_buffer_kind(textures.colors[0]);
assert!(
textures.colors[1 .. image_yuv_cpu.format.get_plane_num()]
.iter()
.all(|&tid| buffer_kind == get_buffer_kind(tid))
);
let kind = BatchKind::Transformable(
transform_kind,
TransformBatchKind::YuvImage(
buffer_kind,
image_yuv_cpu.format,
image_yuv_cpu.color_space,
),
);
let key = BatchKey::new(kind, non_segmented_blend_mode, textures);
let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
batch.push(base_instance.build(
uv_rect_addresses[0],
uv_rect_addresses[1],
uv_rect_addresses[2],
));
}
}
}
@ -1324,6 +1259,7 @@ impl AlphaBatchBuilder {
z,
segment_index: 0,
edge_flags: EdgeAaSegmentMask::all(),
brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
user_data,
};
@ -1440,6 +1376,71 @@ impl BrushPrimitive {
[0; 3],
))
}
BrushKind::RadialGradient { ref stops_handle, .. } => {
Some((
BrushBatchKind::RadialGradient,
BatchTextures::no_texture(),
[
stops_handle.as_int(gpu_cache),
0,
0,
],
))
}
BrushKind::YuvImage { format, yuv_key, image_rendering, color_space } => {
let mut textures = BatchTextures::no_texture();
let mut uv_rect_addresses = [0; 3];
//yuv channel
let channel_count = format.get_plane_num();
debug_assert!(channel_count <= 3);
for channel in 0 .. channel_count {
let image_key = yuv_key[channel];
let cache_item = resolve_image(
ImageRequest {
key: image_key,
rendering: image_rendering,
tile: None,
},
resource_cache,
gpu_cache,
deferred_resolves,
);
if cache_item.texture_id == SourceTexture::Invalid {
warn!("Warnings: skip a PrimitiveKind::YuvImage");
return None;
}
textures.colors[channel] = cache_item.texture_id;
uv_rect_addresses[channel] = cache_item.uv_rect_handle.as_int(gpu_cache);
}
// All yuv textures should be the same type.
let buffer_kind = get_buffer_kind(textures.colors[0]);
assert!(
textures.colors[1 .. format.get_plane_num()]
.iter()
.all(|&tid| buffer_kind == get_buffer_kind(tid))
);
let kind = BrushBatchKind::YuvImage(
buffer_kind,
format,
color_space,
);
Some((
kind,
textures,
[
uv_rect_addresses[0],
uv_rect_addresses[1],
uv_rect_addresses[2],
],
))
}
BrushKind::Mask { .. } => {
unreachable!("bug: mask brushes not expected in normal alpha pass");
}
@ -1463,10 +1464,8 @@ impl AlphaBatchHelpers for PrimitiveStore {
}
PrimitiveKind::Border |
PrimitiveKind::YuvImage |
PrimitiveKind::AlignedGradient |
PrimitiveKind::AngleGradient |
PrimitiveKind::RadialGradient |
PrimitiveKind::Picture => {
BlendMode::PremultipliedAlpha
}
@ -1486,6 +1485,8 @@ impl AlphaBatchHelpers for PrimitiveStore {
BrushKind::Solid { .. } |
BrushKind::Mask { .. } |
BrushKind::Line { .. } |
BrushKind::YuvImage { .. } |
BrushKind::RadialGradient { .. } |
BrushKind::Picture => {
BlendMode::PremultipliedAlpha
}

Просмотреть файл

@ -2,15 +2,14 @@
* 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 api::{BorderRadius, BorderSide, BorderStyle, BorderWidths, ClipAndScrollInfo, ColorF};
use api::{LayerPoint, LayerRect, LayerPrimitiveInfo, LayerSize};
use api::{NormalBorder, RepeatMode, TexelRect};
use api::{BorderRadius, BorderSide, BorderStyle, BorderWidths, ColorF, LayerPoint};
use api::{LayerPrimitiveInfo, LayerRect, LayerSize, NormalBorder, RepeatMode, TexelRect};
use clip::ClipSource;
use ellipse::Ellipse;
use frame_builder::FrameBuilder;
use gpu_cache::GpuDataRequest;
use prim_store::{BorderPrimitiveCpu, BrushSegment, BrushSegmentDescriptor};
use prim_store::{BrushClipMaskKind, EdgeAaSegmentMask, PrimitiveContainer};
use prim_store::{BorderPrimitiveCpu, BrushClipMaskKind, BrushSegment, BrushSegmentDescriptor};
use prim_store::{EdgeAaSegmentMask, PrimitiveContainer, ScrollNodeAndClipChain};
use util::{lerp, pack_as_float};
#[repr(u8)]
@ -286,7 +285,7 @@ impl FrameBuilder {
info: &LayerPrimitiveInfo,
border: &NormalBorder,
widths: &BorderWidths,
clip_and_scroll: ClipAndScrollInfo,
clip_and_scroll: ScrollNodeAndClipChain,
corner_instances: [BorderCornerInstance; 4],
edges: [BorderEdgeKind; 4],
clip_sources: Vec<ClipSource>,
@ -354,7 +353,7 @@ impl FrameBuilder {
info: &LayerPrimitiveInfo,
border: &NormalBorder,
widths: &BorderWidths,
clip_and_scroll: ClipAndScrollInfo,
clip_and_scroll: ScrollNodeAndClipChain,
) {
// The border shader is quite expensive. For simple borders, we can just draw
// the border with a few rectangles. This generally gives better batching, and

Просмотреть файл

@ -2,16 +2,15 @@
* 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 api::{ColorF, LayerPoint, LayerRect, LayerSize, LayerVector2D};
use api::{BorderRadius, BoxShadowClipMode, LayoutSize, LayerPrimitiveInfo};
use api::{ClipMode, ClipAndScrollInfo, ComplexClipRegion, LocalClip};
use api::{PipelineId};
use api::{BorderRadius, BoxShadowClipMode, ClipMode, ColorF, ComplexClipRegion, LayerPoint};
use api::{LayerPrimitiveInfo, LayerRect, LayerSize, LayerVector2D, LayoutSize, LocalClip};
use api::PipelineId;
use app_units::Au;
use clip::ClipSource;
use frame_builder::FrameBuilder;
use gpu_types::BrushImageKind;
use prim_store::{PrimitiveContainer};
use prim_store::{BrushMaskKind, BrushKind, BrushPrimitive};
use prim_store::{BrushKind, BrushMaskKind, BrushPrimitive, PrimitiveContainer};
use prim_store::ScrollNodeAndClipChain;
use picture::PicturePrimitive;
use util::RectHelpers;
use render_task::MAX_BLUR_STD_DEVIATION;
@ -53,7 +52,7 @@ impl FrameBuilder {
pub fn add_box_shadow(
&mut self,
pipeline_id: PipelineId,
clip_and_scroll: ClipAndScrollInfo,
clip_and_scroll: ScrollNodeAndClipChain,
prim_info: &LayerPrimitiveInfo,
box_offset: &LayerVector2D,
color: &ColorF,
@ -88,7 +87,8 @@ impl FrameBuilder {
if box_offset.x == 0.0 && box_offset.y == 0.0 && spread_amount == 0.0 {
return;
}
let mut clips = Vec::new();
let mut clips = Vec::with_capacity(2);
clips.push(ClipSource::Rectangle(*prim_info.local_clip.clip_rect()));
let fast_info = match clip_mode {
BoxShadowClipMode::Outset => {
@ -264,8 +264,10 @@ impl FrameBuilder {
border_radius,
ClipMode::ClipOut,
));
let pic_info = LayerPrimitiveInfo::new(pic_rect);
let pic_info = LayerPrimitiveInfo::with_clip_rect(
pic_rect,
*prim_info.local_clip.clip_rect()
);
self.add_primitive(
clip_and_scroll,
&pic_info,
@ -332,6 +334,12 @@ impl FrameBuilder {
clip_and_scroll
);
let clip_rect = prim_info.local_clip.clip_rect();
let clip_rect = match prim_info.rect.intersection(clip_rect) {
Some(clip_rect) => clip_rect,
None => return,
};
// Draw the picture one pixel outside the original
// rect to account for the inflate above. This
// extra edge will be clipped by the local clip
@ -339,7 +347,7 @@ impl FrameBuilder {
let pic_rect = prim_info.rect.inflate(inflate_size + box_offset.x.abs(), inflate_size + box_offset.y.abs());
let pic_info = LayerPrimitiveInfo::with_clip_rect(
pic_rect,
prim_info.rect
clip_rect
);
// Add a normal clip to ensure nothing gets drawn

Просмотреть файл

@ -6,12 +6,15 @@ use api::{BorderRadius, ClipMode, ComplexClipRegion, DeviceIntRect, DevicePixelS
use api::{ImageRendering, LayerRect, LayerToWorldTransform, LayoutPoint, LayoutVector2D};
use api::LocalClip;
use border::{BorderCornerClipSource, ensure_no_corner_overlap};
use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId};
use ellipse::Ellipse;
use freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
use gpu_types::ClipScrollNodeIndex;
use prim_store::{ClipData, ImageMaskData};
use resource_cache::{ImageRequest, ResourceCache};
use util::{MaxRect, MatrixHelpers, calculate_screen_bounding_rect, extract_inner_rect_safe};
use std::rc::Rc;
pub type ClipStore = FreeList<ClipSources>;
pub type ClipSourcesHandle = FreeListHandle<ClipSources>;
@ -349,3 +352,106 @@ pub fn rounded_rectangle_contains_point(point: &LayoutPoint,
true
}
pub type ClipChainNodeRef = Option<Rc<ClipChainNode>>;
#[derive(Debug, Clone)]
pub struct ClipChainNode {
pub work_item: ClipWorkItem,
pub local_clip_rect: LayerRect,
pub screen_outer_rect: DeviceIntRect,
pub screen_inner_rect: DeviceIntRect,
pub prev: ClipChainNodeRef,
}
#[derive(Debug, Clone)]
pub struct ClipChain {
pub parent_index: Option<ClipChainIndex>,
pub combined_outer_screen_rect: DeviceIntRect,
pub combined_inner_screen_rect: DeviceIntRect,
pub nodes: ClipChainNodeRef,
}
impl ClipChain {
pub fn empty(screen_rect: &DeviceIntRect) -> ClipChain {
ClipChain {
parent_index: None,
combined_inner_screen_rect: *screen_rect,
combined_outer_screen_rect: *screen_rect,
nodes: None,
}
}
pub fn new_with_added_node(
&self,
work_item: ClipWorkItem,
local_clip_rect: LayerRect,
screen_outer_rect: DeviceIntRect,
screen_inner_rect: DeviceIntRect,
) -> ClipChain {
// If the new node's inner rectangle completely surrounds our outer rectangle,
// we can discard the new node entirely since it isn't going to affect anything.
if screen_inner_rect.contains_rect(&self.combined_outer_screen_rect) {
return self.clone();
}
let new_node = ClipChainNode {
work_item,
local_clip_rect,
screen_outer_rect,
screen_inner_rect,
prev: None,
};
let mut new_chain = self.clone();
new_chain.add_node(new_node);
new_chain
}
pub fn add_node(&mut self, mut new_node: ClipChainNode) {
new_node.prev = self.nodes.clone();
// If this clip's outer rectangle is completely enclosed by the clip
// chain's inner rectangle, then the only clip that matters from this point
// on is this clip. We can disconnect this clip from the parent clip chain.
if self.combined_inner_screen_rect.contains_rect(&new_node.screen_outer_rect) {
new_node.prev = None;
}
self.combined_outer_screen_rect =
self.combined_outer_screen_rect.intersection(&new_node.screen_outer_rect)
.unwrap_or_else(DeviceIntRect::zero);
self.combined_inner_screen_rect =
self.combined_inner_screen_rect.intersection(&new_node.screen_inner_rect)
.unwrap_or_else(DeviceIntRect::zero);
self.nodes = Some(Rc::new(new_node));
}
}
pub struct ClipChainNodeIter {
pub current: ClipChainNodeRef,
}
impl Iterator for ClipChainNodeIter {
type Item = Rc<ClipChainNode>;
fn next(&mut self) -> ClipChainNodeRef {
let previous = self.current.clone();
self.current = match self.current {
Some(ref item) => item.prev.clone(),
None => return None,
};
previous
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct ClipWorkItem {
pub scroll_node_data_index: ClipScrollNodeIndex,
pub clip_sources: ClipSourcesWeakHandle,
pub coordinate_system_id: CoordinateSystemId,
}

Просмотреть файл

@ -5,14 +5,13 @@
use api::{ClipId, DevicePixelScale, ExternalScrollId, LayerPixel, LayerPoint, LayerRect};
use api::{LayerSize, LayerToWorldTransform, LayerTransform, LayerVector2D, LayoutTransform};
use api::{LayoutVector2D, PipelineId, PropertyBinding, ScrollClamping, ScrollEventPhase};
use api::{ScrollLocation, ScrollNodeIdType, ScrollSensitivity, StickyOffsetBounds, WorldPoint};
use clip::{ClipSourcesHandle, ClipStore};
use clip_scroll_tree::{CoordinateSystemId, TransformUpdateState};
use api::{ScrollLocation, ScrollSensitivity, StickyOffsetBounds, WorldPoint};
use clip::{ClipChain, ClipSourcesHandle, ClipStore, ClipWorkItem};
use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId, TransformUpdateState};
use euclid::SideOffsets2D;
use geometry::ray_intersects_rect;
use gpu_cache::GpuCache;
use gpu_types::{ClipScrollNodeIndex, ClipScrollNodeData};
use render_task::{ClipChain, ClipWorkItem};
use resource_cache::ResourceCache;
use scene::SceneProperties;
use spring::{DAMPING, STIFFNESS, Spring};
@ -56,7 +55,10 @@ pub enum NodeType {
ReferenceFrame(ReferenceFrameInfo),
/// Other nodes just do clipping, but no transformation.
Clip(ClipSourcesHandle),
Clip {
handle: ClipSourcesHandle,
clip_chain_index: ClipChainIndex
},
/// Transforms it's content, but doesn't clip it. Can also be adjusted
/// by scroll events or setting scroll offsets.
@ -106,9 +108,6 @@ pub struct ClipScrollNode {
/// The type of this node and any data associated with that node type.
pub node_type: NodeType,
/// The ClipChain that will be used if this node is used as the 'clipping node.'
pub clip_chain: Option<ClipChain>,
/// True if this node is transformed by an invertible transform. If not, display items
/// transformed by this node will not be displayed and display items not transformed by this
/// node will not be clipped by clips that are transformed by this node.
@ -128,7 +127,7 @@ pub struct ClipScrollNode {
}
impl ClipScrollNode {
fn new(
pub fn new(
pipeline_id: PipelineId,
parent_id: Option<ClipId>,
rect: &LayerRect,
@ -142,7 +141,6 @@ impl ClipScrollNode {
children: Vec::new(),
pipeline_id,
node_type: node_type,
clip_chain: None,
invertible: true,
coordinate_system_id: CoordinateSystemId(0),
coordinate_system_relative_transform: TransformOrOffset::zero(),
@ -170,15 +168,6 @@ impl ClipScrollNode {
Self::new(pipeline_id, Some(parent_id), frame_rect, node_type)
}
pub fn new_clip_node(
pipeline_id: PipelineId,
parent_id: ClipId,
handle: ClipSourcesHandle,
clip_rect: LayerRect,
) -> Self {
Self::new(pipeline_id, Some(parent_id), &clip_rect, NodeType::Clip(handle))
}
pub fn new_reference_frame(
parent_id: Option<ClipId>,
frame_rect: &LayerRect,
@ -271,7 +260,6 @@ impl ClipScrollNode {
self.invertible = false;
self.world_content_transform = LayerToWorldTransform::identity();
self.world_viewport_transform = LayerToWorldTransform::identity();
self.clip_chain = None;
}
pub fn push_gpu_node_data(&mut self, node_data: &mut Vec<ClipScrollNodeData>) {
@ -304,6 +292,7 @@ impl ClipScrollNode {
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
scene_properties: &SceneProperties,
clip_chains: &mut Vec<ClipChain>,
) {
// If any of our parents was not rendered, we are not rendered either and can just
// quit here.
@ -331,6 +320,7 @@ impl ClipScrollNode {
clip_store,
resource_cache,
gpu_cache,
clip_chains,
);
}
@ -341,11 +331,11 @@ impl ClipScrollNode {
clip_store: &mut ClipStore,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
clip_chains: &mut Vec<ClipChain>,
) {
let clip_sources_handle = match self.node_type {
NodeType::Clip(ref handle) => handle,
let (clip_sources_handle, clip_chain_index) = match self.node_type {
NodeType::Clip { ref handle, clip_chain_index } => (handle, clip_chain_index),
_ => {
self.clip_chain = Some(state.parent_clip_chain.clone());
self.invertible = true;
return;
}
@ -364,29 +354,22 @@ impl ClipScrollNode {
"Clipping node didn't have outer rect."
);
// If this clip's inner rectangle completely surrounds the existing clip
// chain's outer rectangle, we can discard this clip entirely since it isn't
// going to affect anything.
if screen_inner_rect.contains_rect(&state.parent_clip_chain.combined_outer_screen_rect) {
self.clip_chain = Some(state.parent_clip_chain.clone());
return;
}
let work_item = ClipWorkItem {
scroll_node_data_index: self.node_data_index,
clip_sources: clip_sources_handle.weak(),
coordinate_system_id: state.current_coordinate_system_id,
};
let clip_chain = state.parent_clip_chain.new_with_added_node(
let mut clip_chain = clip_chains[state.parent_clip_chain_index.0].new_with_added_node(
work_item,
self.coordinate_system_relative_transform.apply(&local_outer_rect),
screen_outer_rect,
screen_inner_rect,
);
self.clip_chain = Some(clip_chain.clone());
state.parent_clip_chain = clip_chain;
clip_chain.parent_index = Some(state.parent_clip_chain_index);
clip_chains[clip_chain_index.0] = clip_chain;
state.parent_clip_chain_index = clip_chain_index;
}
pub fn update_transform(
@ -621,7 +604,7 @@ impl ClipScrollNode {
state.nearest_scrolling_ancestor_viewport
.translate(&translation);
}
NodeType::Clip(..) => { }
NodeType::Clip{ .. } => { }
NodeType::ScrollFrame(ref scrolling) => {
state.parent_accumulated_scroll_offset =
scrolling.offset + state.parent_accumulated_scroll_offset;
@ -768,12 +751,7 @@ impl ClipScrollNode {
}
}
pub fn matches_id(&self, node_id: ClipId, id_to_match: ScrollNodeIdType) -> bool {
let external_id = match id_to_match {
ScrollNodeIdType::ExternalScrollId(id) => id,
ScrollNodeIdType::ClipId(clip_id) => return node_id == clip_id,
};
pub fn matches_external_id(&self, external_id: ExternalScrollId) -> bool {
match self.node_type {
NodeType::ScrollFrame(info) if info.external_id == Some(external_id) => true,
_ => false,

Просмотреть файл

@ -2,17 +2,15 @@
* 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 api::{ClipChainId, ClipId, DeviceIntRect, DevicePixelScale, ExternalScrollId, LayerPoint};
use api::{LayerRect, LayerToWorldTransform, LayerVector2D, PipelineId, ScrollClamping};
use api::{ScrollEventPhase, ScrollLocation, ScrollNodeIdType};
use api::{ScrollNodeState, WorldPoint};
use clip::ClipStore;
use api::{ClipId, DeviceIntRect, DevicePixelScale, ExternalScrollId, LayerPoint, LayerRect};
use api::{LayerToWorldTransform, LayerVector2D, PipelineId, ScrollClamping, ScrollEventPhase};
use api::{ScrollLocation, ScrollNodeState, WorldPoint};
use clip::{ClipChain, ClipSourcesHandle, ClipStore};
use clip_scroll_node::{ClipScrollNode, NodeType, ScrollFrameInfo, StickyFrameInfo};
use gpu_cache::GpuCache;
use gpu_types::{ClipScrollNodeIndex, ClipScrollNodeData};
use internal_types::{FastHashMap, FastHashSet};
use print_tree::{PrintTree, PrintTreePrinter};
use render_task::ClipChain;
use resource_cache::ResourceCache;
use scene::SceneProperties;
use util::TransformOrOffset;
@ -44,11 +42,14 @@ impl CoordinateSystemId {
}
pub struct ClipChainDescriptor {
pub id: ClipChainId,
pub parent: Option<ClipChainId>,
pub index: ClipChainIndex,
pub parent: Option<ClipChainIndex>,
pub clips: Vec<ClipId>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ClipChainIndex(pub usize);
pub struct ClipScrollTree {
pub nodes: FastHashMap<ClipId, ClipScrollNode>,
@ -57,10 +58,11 @@ pub struct ClipScrollTree {
/// the children of ClipChains later in the list.
pub clip_chains_descriptors: Vec<ClipChainDescriptor>,
/// A HashMap of built ClipChains that are described by `clip_chains_descriptors`.
pub clip_chains: FastHashMap<ClipChainId, ClipChain>,
/// A vector of all ClipChains in this ClipScrollTree including those from
/// ClipChainDescriptors and also those defined by the clipping node hierarchy.
pub clip_chains: Vec<ClipChain>,
pub pending_scroll_offsets: FastHashMap<ScrollNodeIdType, (LayerPoint, ScrollClamping)>,
pub pending_scroll_offsets: FastHashMap<ExternalScrollId, (LayerPoint, ScrollClamping)>,
/// The ClipId of the currently scrolling node. Used to allow the same
/// node to scroll even if a touch operation leaves the boundaries of that node.
@ -90,7 +92,9 @@ pub struct TransformUpdateState {
pub parent_accumulated_scroll_offset: LayerVector2D,
pub nearest_scrolling_ancestor_offset: LayerVector2D,
pub nearest_scrolling_ancestor_viewport: LayerRect,
pub parent_clip_chain: ClipChain,
/// The index of the current parent's clip chain.
pub parent_clip_chain_index: ClipChainIndex,
/// An id for keeping track of the axis-aligned space of this node. This is used in
/// order to to track what kinds of clip optimizations can be done for a particular
@ -113,7 +117,7 @@ impl ClipScrollTree {
ClipScrollTree {
nodes: FastHashMap::default(),
clip_chains_descriptors: Vec::new(),
clip_chains: FastHashMap::default(),
clip_chains: vec![ClipChain::empty(&DeviceIntRect::zero())],
pending_scroll_offsets: FastHashMap::default(),
currently_scrolling_node_id: None,
root_reference_frame_id: ClipId::root_reference_frame(dummy_pipeline),
@ -211,7 +215,7 @@ impl ClipScrollTree {
}
self.pipelines_to_discard.clear();
self.clip_chains.clear();
self.clip_chains = vec![ClipChain::empty(&DeviceIntRect::zero())];
self.clip_chains_descriptors.clear();
scroll_states
}
@ -219,11 +223,11 @@ impl ClipScrollTree {
pub fn scroll_node(
&mut self,
origin: LayerPoint,
id: ScrollNodeIdType,
id: ExternalScrollId,
clamp: ScrollClamping
) -> bool {
for (clip_id, node) in &mut self.nodes {
if node.matches_id(*clip_id, id) {
for node in &mut self.nodes.values_mut() {
if node.matches_external_id(id) {
return node.set_scroll_origin(&origin, clamp);
}
}
@ -321,6 +325,8 @@ impl ClipScrollTree {
return;
}
self.clip_chains[0] = ClipChain::empty(screen_rect);
let root_reference_frame_id = self.root_reference_frame_id();
let mut state = TransformUpdateState {
parent_reference_frame_transform: LayerToWorldTransform::create_translation(
@ -331,7 +337,7 @@ impl ClipScrollTree {
parent_accumulated_scroll_offset: LayerVector2D::zero(),
nearest_scrolling_ancestor_offset: LayerVector2D::zero(),
nearest_scrolling_ancestor_viewport: LayerRect::zero(),
parent_clip_chain: ClipChain::empty(screen_rect),
parent_clip_chain_index: ClipChainIndex(0),
current_coordinate_system_id: CoordinateSystemId::root(),
coordinate_system_relative_transform: TransformOrOffset::zero(),
invertible: true,
@ -384,6 +390,7 @@ impl ClipScrollTree {
resource_cache,
gpu_cache,
scene_properties,
&mut self.clip_chains,
);
node.push_gpu_node_data(gpu_node_data);
@ -417,21 +424,28 @@ impl ClipScrollTree {
// ClipScrollNode clipping nodes. Here we start the ClipChain with a clone of the
// parent's node, if necessary.
let mut chain = match descriptor.parent {
Some(id) => self.clip_chains[&id].clone(),
Some(index) => self.clip_chains[index.0].clone(),
None => ClipChain::empty(screen_rect),
};
// Now we walk through each ClipScrollNode in the vector of clip nodes and
// extract their ClipChain nodes to construct the final list.
for clip_id in &descriptor.clips {
if let Some(ref node_chain) = self.nodes[&clip_id].clip_chain {
if let Some(ref nodes) = node_chain.nodes {
let node_clip_chain_index = match self.nodes[&clip_id].node_type {
NodeType::Clip { clip_chain_index, .. } => clip_chain_index,
_ => {
warn!("Tried to create a clip chain with non-clipping node.");
continue;
}
};
if let Some(ref nodes) = self.clip_chains[node_clip_chain_index.0].nodes {
chain.add_node((**nodes).clone());
}
}
}
self.clip_chains.insert(descriptor.id, chain);
chain.parent_index = descriptor.parent;
self.clip_chains[descriptor.index.0] = chain;
}
}
@ -442,28 +456,35 @@ impl ClipScrollTree {
}
pub fn finalize_and_apply_pending_scroll_offsets(&mut self, old_states: ScrollStates) {
for (clip_id, node) in &mut self.nodes {
for node in self.nodes.values_mut() {
let external_id = match node.node_type {
NodeType::ScrollFrame(info) => info.external_id,
_ => None,
NodeType::ScrollFrame(ScrollFrameInfo { external_id: Some(id), ..} ) => id,
_ => continue,
};
if let Some(external_id) = external_id {
if let Some(scrolling_state) = old_states.get(&external_id) {
node.apply_old_scrolling_state(scrolling_state);
}
let id = external_id.into();
if let Some((offset, clamping)) = self.pending_scroll_offsets.remove(&id) {
if let Some((offset, clamping)) = self.pending_scroll_offsets.remove(&external_id) {
node.set_scroll_origin(&offset, clamping);
}
}
}
if let Some((offset, clamping)) = self.pending_scroll_offsets.remove(&clip_id.into()) {
node.set_scroll_origin(&offset, clamping);
}
}
pub fn add_clip_node(
&mut self,
id: ClipId,
parent_id: ClipId,
handle: ClipSourcesHandle,
clip_rect: LayerRect,
) -> ClipChainIndex {
let clip_chain_index = self.allocate_clip_chain();
let node_type = NodeType::Clip { handle, clip_chain_index };
let node = ClipScrollNode::new(id.pipeline_id(), Some(parent_id), &clip_rect, node_type);
self.add_node(node, id);
clip_chain_index
}
pub fn add_sticky_frame(
@ -484,11 +505,12 @@ impl ClipScrollTree {
pub fn add_clip_chain_descriptor(
&mut self,
id: ClipChainId,
parent: Option<ClipChainId>,
parent: Option<ClipChainIndex>,
clips: Vec<ClipId>
) {
self.clip_chains_descriptors.push(ClipChainDescriptor { id, parent, clips });
) -> ClipChainIndex {
let index = self.allocate_clip_chain();
self.clip_chains_descriptors.push(ClipChainDescriptor { index, parent, clips });
index
}
pub fn add_node(&mut self, node: ClipScrollNode, id: ClipId) {
@ -515,11 +537,11 @@ impl ClipScrollTree {
let node = self.nodes.get(id).unwrap();
match node.node_type {
NodeType::Clip(ref clip_sources_handle) => {
NodeType::Clip { ref handle, .. } => {
pt.new_level("Clip".to_owned());
pt.add_item(format!("id: {:?}", id));
let clips = clip_store.get(&clip_sources_handle).clips();
let clips = clip_store.get(&handle).clips();
pt.new_level(format!("Clip Sources [{}]", clips.len()));
for source in clips {
pt.add_item(format!("{:?}", source));
@ -581,11 +603,15 @@ impl ClipScrollTree {
}
}
pub fn get_clip_chain(&self, id: &ClipId) -> Option<&ClipChain> {
match id {
&ClipId::ClipChain(clip_chain_id) => Some(&self.clip_chains[&clip_chain_id]),
_ => self.nodes[id].clip_chain.as_ref(),
pub fn allocate_clip_chain(&mut self) -> ClipChainIndex {
debug_assert!(!self.clip_chains.is_empty());
let new_clip_chain =self.clip_chains[0].clone();
self.clip_chains.push(new_clip_chain);
ClipChainIndex(self.clip_chains.len() - 1)
}
pub fn get_clip_chain(&self, index: ClipChainIndex) -> &ClipChain {
&self.clip_chains[index.0]
}
}

Просмотреть файл

@ -438,6 +438,7 @@ pub struct Texture {
render_target: Option<RenderTargetInfo>,
fbo_ids: Vec<FBOId>,
depth_rb: Option<RBOId>,
last_frame_used: FrameId,
}
impl Texture {
@ -473,6 +474,10 @@ impl Texture {
self.render_target.as_ref()
}
pub fn used_in_frame(&self, frame_id: FrameId) -> bool {
self.last_frame_used == frame_id
}
#[cfg(feature = "replay")]
pub fn into_external(mut self) -> ExternalTexture {
let ext = ExternalTexture {
@ -940,6 +945,7 @@ impl Device {
render_target: None,
fbo_ids: vec![],
depth_rb: None,
last_frame_used: self.frame_id,
}
}
@ -1019,6 +1025,7 @@ impl Device {
texture.filter = filter;
texture.layer_count = layer_count;
texture.render_target = render_target;
texture.last_frame_used = self.frame_id;
self.bind_texture(DEFAULT_TEXTURE, texture);
self.set_texture_parameters(texture.target, filter);

Просмотреть файл

@ -3,21 +3,22 @@
* 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 api::{BuiltDisplayListIter, ClipAndScrollInfo, ClipId, ColorF, ComplexClipRegion};
use api::{DevicePixelScale, DeviceUintRect, DeviceUintSize, DisplayItemRef, DocumentLayer, Epoch};
use api::{ExternalScrollId, FilterOp, IframeDisplayItem, ImageDisplayItem, ItemRange, LayerPoint};
use api::{BuiltDisplayListIter, ClipId, ColorF, ComplexClipRegion, DevicePixelScale};
use api::{DeviceUintRect, DeviceUintSize, DisplayItemRef, DocumentLayer, Epoch, ExternalScrollId};
use api::{FilterOp, IframeDisplayItem, ImageDisplayItem, ItemRange, LayerPoint};
use api::{LayerPrimitiveInfo, LayerRect, LayerSize, LayerVector2D, LayoutSize, PipelineId};
use api::{ScrollClamping, ScrollEventPhase, ScrollFrameDisplayItem, ScrollLocation};
use api::{ScrollNodeIdType, ScrollNodeState, ScrollPolicy, ScrollSensitivity, SpecificDisplayItem};
use api::{StackingContext, TileOffset, TransformStyle, WorldPoint};
use api::{ScrollNodeState, ScrollPolicy, ScrollSensitivity, SpecificDisplayItem, StackingContext};
use api::{TileOffset, TransformStyle, WorldPoint};
use clip::ClipRegion;
use clip_scroll_node::StickyFrameInfo;
use clip_scroll_tree::{ClipScrollTree, ScrollStates};
use clip_scroll_tree::{ClipChainIndex, ClipScrollTree, ScrollStates};
use euclid::rect;
use frame_builder::{FrameBuilder, FrameBuilderConfig, ScrollbarInfo};
use gpu_cache::GpuCache;
use hit_test::HitTester;
use internal_types::{FastHashMap, FastHashSet, RenderedDocument};
use prim_store::ScrollNodeAndClipChain;
use profiler::{GpuCacheProfileCounters, TextureCacheProfileCounters};
use resource_cache::{FontInstanceMap,ResourceCache, TiledImageMap};
use scene::{Scene, StackingContextHelpers, ScenePipeline, SceneProperties};
@ -36,6 +37,53 @@ static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF {
a: 0.6,
};
/// A data structure that keeps track of mapping between API clip ids and the indices
/// used internally in the ClipScrollTree to avoid having to do HashMap lookups. This
/// also includes a small LRU cache. Currently the cache is small (1 entry), but in the
/// future we could use uluru here to do something more involved.
pub struct ClipIdToIndexMapper {
map: FastHashMap<ClipId, ClipChainIndex>,
cached_index: Option<(ClipId, ClipChainIndex)>,
}
impl ClipIdToIndexMapper {
fn new() -> ClipIdToIndexMapper {
ClipIdToIndexMapper {
map: FastHashMap::default(),
cached_index: None,
}
}
pub fn add(&mut self, id: ClipId, index: ClipChainIndex) {
debug_assert!(!self.map.contains_key(&id));
self.map.insert(id, index);
}
pub fn map_to_parent_clip_chain(&mut self, id: ClipId, parent_id: &ClipId) {
let parent_chain_index = self.map_clip_id(parent_id);
self.add(id, parent_chain_index);
}
pub fn map_clip_id(&mut self, id: &ClipId) -> ClipChainIndex {
match self.cached_index {
Some((cached_id, cached_index)) if cached_id == *id => return cached_index,
_ => {}
}
self.map[id]
}
pub fn map_clip_id_and_cache_result(&mut self, id: &ClipId) -> ClipChainIndex {
let index = self.map_clip_id(id);
self.cached_index = Some((*id, index));
index
}
pub fn simple_scroll_and_clip_chain(&mut self, id: &ClipId) -> ScrollNodeAndClipChain {
ScrollNodeAndClipChain::new(*id, self.map_clip_id(&id))
}
}
struct FlattenContext<'a> {
scene: &'a Scene,
builder: FrameBuilder,
@ -45,6 +93,7 @@ struct FlattenContext<'a> {
pipeline_epochs: Vec<(PipelineId, Epoch)>,
replacements: Vec<(ClipId, ClipId)>,
output_pipelines: &'a FastHashSet<PipelineId>,
id_to_index_mapper: ClipIdToIndexMapper,
}
impl<'a> FlattenContext<'a> {
@ -104,13 +153,23 @@ impl<'a> FlattenContext<'a> {
let root_reference_frame_id = ClipId::root_reference_frame(pipeline_id);
let root_scroll_frame_id = ClipId::root_scroll_node(pipeline_id);
let root_clip_chain_index =
self.id_to_index_mapper.map_clip_id_and_cache_result(&root_reference_frame_id);
let root_reference_frame_clip_and_scroll = ScrollNodeAndClipChain::new(
root_reference_frame_id,
root_clip_chain_index,
);
self.builder.push_stacking_context(
pipeline_id,
CompositeOps::default(),
TransformStyle::Flat,
true,
true,
ClipAndScrollInfo::simple(root_scroll_frame_id),
ScrollNodeAndClipChain::new(
ClipId::root_scroll_node(pipeline_id),
root_clip_chain_index,
),
self.output_pipelines,
);
@ -122,7 +181,7 @@ impl<'a> FlattenContext<'a> {
let root_bounds = LayerRect::new(LayerPoint::zero(), *frame_size);
let info = LayerPrimitiveInfo::new(root_bounds);
self.builder.add_solid_rectangle(
ClipAndScrollInfo::simple(root_reference_frame_id),
root_reference_frame_clip_and_scroll,
&info,
bg_color,
None,
@ -142,7 +201,7 @@ impl<'a> FlattenContext<'a> {
let scrollbar_rect = LayerRect::new(LayerPoint::zero(), LayerSize::new(10.0, 70.0));
let container_rect = LayerRect::new(LayerPoint::zero(), *frame_size);
self.builder.add_scroll_bar(
ClipAndScrollInfo::simple(root_reference_frame_id),
root_reference_frame_clip_and_scroll,
&LayerPrimitiveInfo::new(scrollbar_rect),
DEFAULT_SCROLLBAR_COLOR,
ScrollbarInfo(root_scroll_frame_id, container_rect),
@ -184,19 +243,13 @@ impl<'a> FlattenContext<'a> {
}
}
fn flatten_clip(
&mut self,
pipeline_id: PipelineId,
parent_id: &ClipId,
new_clip_id: &ClipId,
clip_region: ClipRegion,
) {
fn flatten_clip(&mut self, parent_id: &ClipId, new_clip_id: &ClipId, clip_region: ClipRegion) {
self.builder.add_clip_node(
*new_clip_id,
*parent_id,
pipeline_id,
clip_region,
self.clip_scroll_tree,
&mut self.id_to_index_mapper,
);
}
@ -205,7 +258,7 @@ impl<'a> FlattenContext<'a> {
item: &DisplayItemRef,
info: &ScrollFrameDisplayItem,
pipeline_id: PipelineId,
clip_and_scroll: &ClipAndScrollInfo,
clip_and_scroll: &ScrollNodeAndClipChain,
reference_frame_relative_offset: &LayerVector2D,
) {
let complex_clips = self.get_complex_clips(pipeline_id, item.complex_clip().0);
@ -229,9 +282,9 @@ impl<'a> FlattenContext<'a> {
self.builder.add_clip_node(
info.clip_id,
clip_and_scroll.scroll_node_id,
pipeline_id,
clip_region,
self.clip_scroll_tree,
&mut self.id_to_index_mapper,
);
self.builder.add_scroll_frame(
@ -243,6 +296,7 @@ impl<'a> FlattenContext<'a> {
&content_rect.size,
info.scroll_sensitivity,
self.clip_scroll_tree,
&mut self.id_to_index_mapper,
);
}
@ -250,7 +304,8 @@ impl<'a> FlattenContext<'a> {
&mut self,
traversal: &mut BuiltDisplayListIter<'a>,
pipeline_id: PipelineId,
context_scroll_node_id: ClipId,
unreplaced_scroll_id: ClipId,
clip_and_scroll: ScrollNodeAndClipChain,
mut reference_frame_relative_offset: LayerVector2D,
bounds: &LayerRect,
stacking_context: &StackingContext,
@ -279,7 +334,7 @@ impl<'a> FlattenContext<'a> {
if stacking_context.scroll_policy == ScrollPolicy::Fixed {
self.replacements.push((
context_scroll_node_id,
unreplaced_scroll_id,
self.builder.current_reference_frame_id(),
));
}
@ -296,30 +351,33 @@ impl<'a> FlattenContext<'a> {
);
let reference_frame_bounds = LayerRect::new(LayerPoint::zero(), bounds.size);
let mut parent_id = self.apply_scroll_frame_id_replacement(context_scroll_node_id);
self.builder.push_reference_frame(
reference_frame_id,
Some(parent_id),
Some(clip_and_scroll.scroll_node_id),
pipeline_id,
&reference_frame_bounds,
stacking_context.transform,
stacking_context.perspective,
reference_frame_relative_offset,
self.clip_scroll_tree,
&mut self.id_to_index_mapper,
);
self.replacements.push((context_scroll_node_id, reference_frame_id));
self.replacements.push((unreplaced_scroll_id, reference_frame_id));
reference_frame_relative_offset = LayerVector2D::zero();
}
let sc_scroll_node_id = self.apply_scroll_frame_id_replacement(context_scroll_node_id);
// We apply the replacements one more time in case we need to set it to a replacement
// that we just pushed above.
let new_scroll_node = self.apply_scroll_frame_id_replacement(unreplaced_scroll_id);
let stacking_context_clip_and_scroll =
self.id_to_index_mapper.simple_scroll_and_clip_chain(&new_scroll_node);
self.builder.push_stacking_context(
pipeline_id,
composition_operations,
stacking_context.transform_style,
is_backface_visible,
false,
ClipAndScrollInfo::simple(sc_scroll_node_id),
stacking_context_clip_and_scroll,
self.output_pipelines,
);
@ -345,8 +403,7 @@ impl<'a> FlattenContext<'a> {
&mut self,
item: &DisplayItemRef,
info: &IframeDisplayItem,
parent_pipeline_id: PipelineId,
clip_and_scroll: &ClipAndScrollInfo,
clip_and_scroll: &ScrollNodeAndClipChain,
reference_frame_relative_offset: &LayerVector2D,
) {
let iframe_pipeline_id = info.pipeline_id;
@ -358,12 +415,12 @@ impl<'a> FlattenContext<'a> {
self.builder.add_clip_node(
info.clip_id,
clip_and_scroll.scroll_node_id,
parent_pipeline_id,
ClipRegion::create_for_clip_node_with_local_clip(
&item.local_clip(),
&reference_frame_relative_offset
),
self.clip_scroll_tree,
&mut self.id_to_index_mapper,
);
self.pipeline_epochs.push((iframe_pipeline_id, pipeline.epoch));
@ -380,6 +437,7 @@ impl<'a> FlattenContext<'a> {
None,
origin,
self.clip_scroll_tree,
&mut self.id_to_index_mapper,
);
self.builder.add_scroll_frame(
@ -391,6 +449,7 @@ impl<'a> FlattenContext<'a> {
&pipeline.content_size,
ScrollSensitivity::ScriptAndInputEvents,
self.clip_scroll_tree,
&mut self.id_to_index_mapper,
);
self.flatten_root(&mut pipeline.display_list.iter(), iframe_pipeline_id, &iframe_rect.size);
@ -404,7 +463,11 @@ impl<'a> FlattenContext<'a> {
pipeline_id: PipelineId,
reference_frame_relative_offset: LayerVector2D,
) -> Option<BuiltDisplayListIter<'a>> {
let mut clip_and_scroll = item.clip_and_scroll();
let clip_and_scroll = item.clip_and_scroll();
let mut clip_and_scroll = ScrollNodeAndClipChain::new(
clip_and_scroll.scroll_node_id,
self.id_to_index_mapper.map_clip_id_and_cache_result(&clip_and_scroll.clip_node_id()),
);
let unreplaced_scroll_id = clip_and_scroll.scroll_node_id;
clip_and_scroll.scroll_node_id =
@ -557,6 +620,7 @@ impl<'a> FlattenContext<'a> {
&mut subtraversal,
pipeline_id,
unreplaced_scroll_id,
clip_and_scroll,
reference_frame_relative_offset,
&item.rect(),
&info.stacking_context,
@ -569,7 +633,6 @@ impl<'a> FlattenContext<'a> {
self.flatten_iframe(
&item,
info,
pipeline_id,
&clip_and_scroll,
&reference_frame_relative_offset
);
@ -582,16 +645,16 @@ impl<'a> FlattenContext<'a> {
info.image_mask,
&reference_frame_relative_offset,
);
self.flatten_clip(
pipeline_id,
&clip_and_scroll.scroll_node_id,
&info.id,
clip_region,
);
self.flatten_clip(&clip_and_scroll.scroll_node_id, &info.id, clip_region);
}
SpecificDisplayItem::ClipChain(ref info) => {
let items = self.get_clip_chain_items(pipeline_id, item.clip_chain_items());
self.clip_scroll_tree.add_clip_chain_descriptor(info.id, info.parent, items);
let parent = info.parent.map(|id|
self.id_to_index_mapper.map_clip_id(&ClipId::ClipChain(id))
);
let clip_chain_index =
self.clip_scroll_tree.add_clip_chain_descriptor(parent, items);
self.id_to_index_mapper.add(ClipId::ClipChain(info.id), clip_chain_index);
},
SpecificDisplayItem::ScrollFrame(ref info) => {
self.flatten_scroll_frame(
@ -610,12 +673,14 @@ impl<'a> FlattenContext<'a> {
info.horizontal_offset_bounds,
info.previously_applied_offset,
);
let parent_id = clip_and_scroll.scroll_node_id;
self.clip_scroll_tree.add_sticky_frame(
info.id,
clip_and_scroll.scroll_node_id, /* parent id */
parent_id,
frame_rect,
sticky_frame_info
);
self.id_to_index_mapper.map_to_parent_clip_chain(info.id, &parent_id);
}
// Do nothing; these are dummy items for the display list parser
@ -650,7 +715,7 @@ impl<'a> FlattenContext<'a> {
/// takes care of the decomposition required by the internal tiling of the image.
fn decompose_image(
&mut self,
clip_and_scroll: ClipAndScrollInfo,
clip_and_scroll: ScrollNodeAndClipChain,
prim_info: &LayerPrimitiveInfo,
info: &ImageDisplayItem,
image_size: DeviceUintSize,
@ -696,7 +761,7 @@ impl<'a> FlattenContext<'a> {
fn decompose_image_row(
&mut self,
clip_and_scroll: ClipAndScrollInfo,
clip_and_scroll: ScrollNodeAndClipChain,
prim_info: &LayerPrimitiveInfo,
info: &ImageDisplayItem,
image_size: DeviceUintSize,
@ -742,7 +807,7 @@ impl<'a> FlattenContext<'a> {
fn decompose_tiled_image(
&mut self,
clip_and_scroll: ClipAndScrollInfo,
clip_and_scroll: ScrollNodeAndClipChain,
prim_info: &LayerPrimitiveInfo,
info: &ImageDisplayItem,
image_size: DeviceUintSize,
@ -878,7 +943,7 @@ impl<'a> FlattenContext<'a> {
fn add_tile_primitive(
&mut self,
clip_and_scroll: ClipAndScrollInfo,
clip_and_scroll: ScrollNodeAndClipChain,
prim_info: &LayerPrimitiveInfo,
info: &ImageDisplayItem,
tile_offset: TileOffset,
@ -983,7 +1048,7 @@ impl FrameContext {
pub fn scroll_node(
&mut self,
origin: LayerPoint,
id: ScrollNodeIdType,
id: ExternalScrollId,
clamp: ScrollClamping
) -> bool {
self.clip_scroll_tree.scroll_node(origin, id, clamp)
@ -1056,6 +1121,7 @@ impl FrameContext {
pipeline_epochs: Vec::new(),
replacements: Vec::new(),
output_pipelines,
id_to_index_mapper: ClipIdToIndexMapper::new(),
};
roller.builder.push_root(
@ -1063,6 +1129,7 @@ impl FrameContext {
&root_pipeline.viewport_size,
&root_pipeline.content_size,
roller.clip_scroll_tree,
&mut roller.id_to_index_mapper,
);
roller.builder.setup_viewport_offset(

Просмотреть файл

@ -2,35 +2,34 @@
* 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 api::{AlphaType, BorderDetails, BorderDisplayItem, BuiltDisplayList, ClipAndScrollInfo};
use api::{ClipId, ColorF, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixelScale};
use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentLayer, Epoch, ExtendMode};
use api::{ExternalScrollId, FontRenderMode, GlyphInstance, GlyphOptions, GradientStop, ImageKey};
use api::{ImageRendering, ItemRange, LayerPoint, LayerPrimitiveInfo, LayerRect, LayerSize};
use api::{LayerTransform, LayerVector2D, LayoutTransform, LayoutVector2D, LineOrientation};
use api::{LineStyle, LocalClip, PipelineId, PremultipliedColorF, PropertyBinding, RepeatMode};
use api::{ScrollSensitivity, Shadow, TexelRect, TileOffset, TransformStyle, WorldPoint};
use api::{WorldToLayerTransform, YuvColorSpace, YuvData};
use api::{AlphaType, BorderDetails, BorderDisplayItem, BuiltDisplayList, ClipId, ColorF};
use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixelScale, DeviceUintPoint};
use api::{DeviceUintRect, DeviceUintSize, DocumentLayer, Epoch, ExtendMode, ExternalScrollId};
use api::{FontRenderMode, GlyphInstance, GlyphOptions, GradientStop, ImageKey, ImageRendering};
use api::{ItemRange, LayerPoint, LayerPrimitiveInfo, LayerRect, LayerSize, LayerTransform};
use api::{LayerVector2D, LayoutTransform, LayoutVector2D, LineOrientation, LineStyle, LocalClip};
use api::{PipelineId, PremultipliedColorF, PropertyBinding, RepeatMode, ScrollSensitivity, Shadow};
use api::{TexelRect, TileOffset, TransformStyle, WorldPoint, WorldToLayerTransform, YuvColorSpace};
use api::YuvData;
use app_units::Au;
use border::ImageBorderSegment;
use clip::{ClipRegion, ClipSource, ClipSources, ClipStore};
use clip::{ClipChain, ClipRegion, ClipSource, ClipSources, ClipStore};
use clip_scroll_node::{ClipScrollNode, NodeType};
use clip_scroll_tree::ClipScrollTree;
use clip_scroll_tree::{ClipScrollTree, ClipChainIndex};
use euclid::{SideOffsets2D, vec2};
use frame::FrameId;
use frame::{FrameId, ClipIdToIndexMapper};
use glyph_rasterizer::FontInstance;
use gpu_cache::GpuCache;
use gpu_cache::{GpuCache, GpuCacheHandle};
use gpu_types::{ClipChainRectIndex, ClipScrollNodeData, PictureType};
use hit_test::{HitTester, HitTestingItem, HitTestingRun};
use internal_types::{FastHashMap, FastHashSet, RenderPassIndex};
use internal_types::{FastHashMap, FastHashSet};
use picture::{ContentOrigin, PictureCompositeMode, PictureKind, PicturePrimitive, PictureSurface};
use prim_store::{BrushKind, BrushPrimitive, ImageCacheKey, YuvImagePrimitiveCpu};
use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, ImageSource, PrimitiveKind};
use prim_store::{PrimitiveContainer, PrimitiveIndex};
use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu};
use prim_store::{BrushSegmentDescriptor, PrimitiveRun, TextRunPrimitiveCpu};
use prim_store::{BrushKind, BrushPrimitive, BrushSegmentDescriptor, GradientPrimitiveCpu};
use prim_store::{ImageCacheKey, ImagePrimitiveCpu, ImageSource, PrimitiveContainer};
use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveRun, PrimitiveStore};
use prim_store::{ScrollNodeAndClipChain, TextRunPrimitiveCpu};
use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
use render_task::{ClearMode, ClipChain, RenderTask, RenderTaskId, RenderTaskLocation, RenderTaskTree};
use render_task::{ClearMode, RenderTask, RenderTaskId, RenderTaskLocation, RenderTaskTree};
use resource_cache::{ImageRequest, ResourceCache};
use scene::{ScenePipeline, SceneProperties};
use std::{mem, usize, f32};
@ -90,10 +89,10 @@ pub struct FrameBuilder {
// A stack of the current shadow primitives.
// The sub-Vec stores a buffer of fast-path primitives to be appended on pop.
shadow_prim_stack: Vec<(PrimitiveIndex, Vec<(PrimitiveIndex, ClipAndScrollInfo)>)>,
shadow_prim_stack: Vec<(PrimitiveIndex, Vec<(PrimitiveIndex, ScrollNodeAndClipChain)>)>,
// If we're doing any fast-path shadows, we buffer the "real"
// content here, to be appended when the shadow stack is empty.
pending_shadow_contents: Vec<(PrimitiveIndex, ClipAndScrollInfo, LayerPrimitiveInfo)>,
pending_shadow_contents: Vec<(PrimitiveIndex, ScrollNodeAndClipChain, LayerPrimitiveInfo)>,
scrollbar_prims: Vec<ScrollbarPrimitive>,
@ -150,14 +149,14 @@ impl PictureState {
}
pub struct PrimitiveRunContext<'a> {
pub clip_chain: Option<&'a ClipChain>,
pub clip_chain: &'a ClipChain,
pub scroll_node: &'a ClipScrollNode,
pub clip_chain_rect_index: ClipChainRectIndex,
}
impl<'a> PrimitiveRunContext<'a> {
pub fn new(
clip_chain: Option<&'a ClipChain>,
clip_chain: &'a ClipChain,
scroll_node: &'a ClipScrollNode,
clip_chain_rect_index: ClipChainRectIndex,
) -> Self {
@ -252,7 +251,7 @@ impl FrameBuilder {
pub fn add_primitive_to_hit_testing_list(
&mut self,
info: &LayerPrimitiveInfo,
clip_and_scroll: ClipAndScrollInfo
clip_and_scroll: ScrollNodeAndClipChain
) {
let tag = match info.tag {
Some(tag) => tag,
@ -276,7 +275,7 @@ impl FrameBuilder {
pub fn add_primitive_to_draw_list(
&mut self,
prim_index: PrimitiveIndex,
clip_and_scroll: ClipAndScrollInfo,
clip_and_scroll: ScrollNodeAndClipChain,
) {
// Add primitive to the top-most Picture on the stack.
// TODO(gw): Let's consider removing the extra indirection
@ -294,7 +293,7 @@ impl FrameBuilder {
/// to the draw list.
pub fn add_primitive(
&mut self,
clip_and_scroll: ClipAndScrollInfo,
clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
clip_sources: Vec<ClipSource>,
container: PrimitiveContainer,
@ -313,7 +312,7 @@ impl FrameBuilder {
transform_style: TransformStyle,
is_backface_visible: bool,
is_pipeline_root: bool,
clip_and_scroll: ClipAndScrollInfo,
clip_and_scroll: ScrollNodeAndClipChain,
output_pipelines: &FastHashSet<PipelineId>,
) {
// Construct the necessary set of Picture primitives
@ -640,6 +639,7 @@ impl FrameBuilder {
source_perspective: Option<LayoutTransform>,
origin_in_parent_reference_frame: LayerVector2D,
clip_scroll_tree: &mut ClipScrollTree,
id_to_index_mapper: &mut ClipIdToIndexMapper,
) {
let node = ClipScrollNode::new_reference_frame(
parent_id,
@ -651,6 +651,12 @@ impl FrameBuilder {
);
clip_scroll_tree.add_node(node, reference_frame_id);
self.reference_frame_stack.push(reference_frame_id);
match parent_id {
Some(ref parent_id) =>
id_to_index_mapper.map_to_parent_clip_chain(reference_frame_id, parent_id),
_ => id_to_index_mapper.add(reference_frame_id, ClipChainIndex(0)),
}
}
pub fn current_reference_frame_id(&self) -> ClipId {
@ -682,6 +688,7 @@ impl FrameBuilder {
viewport_size: &LayerSize,
content_size: &LayerSize,
clip_scroll_tree: &mut ClipScrollTree,
id_to_index_mapper: &mut ClipIdToIndexMapper,
) -> ClipId {
let viewport_rect = LayerRect::new(LayerPoint::zero(), *viewport_size);
self.push_reference_frame(
@ -693,6 +700,7 @@ impl FrameBuilder {
None,
LayerVector2D::zero(),
clip_scroll_tree,
id_to_index_mapper,
);
let topmost_scrolling_node_id = ClipId::root_scroll_node(pipeline_id);
@ -707,6 +715,7 @@ impl FrameBuilder {
content_size,
ScrollSensitivity::ScriptAndInputEvents,
clip_scroll_tree,
id_to_index_mapper,
);
topmost_scrolling_node_id
@ -716,18 +725,23 @@ impl FrameBuilder {
&mut self,
new_node_id: ClipId,
parent_id: ClipId,
pipeline_id: PipelineId,
clip_region: ClipRegion,
clip_scroll_tree: &mut ClipScrollTree,
id_to_index_mapper: &mut ClipIdToIndexMapper,
) {
let clip_rect = clip_region.main;
let clip_sources = ClipSources::from(clip_region);
debug_assert!(clip_sources.has_clips());
debug_assert!(clip_sources.has_clips());
let handle = self.clip_store.insert(clip_sources);
let node = ClipScrollNode::new_clip_node(pipeline_id, parent_id, handle, clip_rect);
clip_scroll_tree.add_node(node, new_node_id);
let clip_chain_index = clip_scroll_tree.add_clip_node(
new_node_id,
parent_id,
handle,
clip_rect
);
id_to_index_mapper.add(new_node_id, clip_chain_index);
}
pub fn add_scroll_frame(
@ -740,6 +754,7 @@ impl FrameBuilder {
content_size: &LayerSize,
scroll_sensitivity: ScrollSensitivity,
clip_scroll_tree: &mut ClipScrollTree,
id_to_index_mapper: &mut ClipIdToIndexMapper,
) {
let node = ClipScrollNode::new_scroll_frame(
pipeline_id,
@ -751,6 +766,7 @@ impl FrameBuilder {
);
clip_scroll_tree.add_node(node, new_node_id);
id_to_index_mapper.map_to_parent_clip_chain(new_node_id, &parent_id);
}
pub fn pop_reference_frame(&mut self) {
@ -760,7 +776,7 @@ impl FrameBuilder {
pub fn push_shadow(
&mut self,
shadow: Shadow,
clip_and_scroll: ClipAndScrollInfo,
clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
) {
let pipeline_id = self.sc_stack.last().unwrap().pipeline_id;
@ -804,7 +820,7 @@ impl FrameBuilder {
pub fn add_solid_rectangle(
&mut self,
clip_and_scroll: ClipAndScrollInfo,
clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
color: ColorF,
segments: Option<BrushSegmentDescriptor>,
@ -833,7 +849,7 @@ impl FrameBuilder {
pub fn add_clear_rectangle(
&mut self,
clip_and_scroll: ClipAndScrollInfo,
clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
) {
let prim = BrushPrimitive::new(
@ -851,7 +867,7 @@ impl FrameBuilder {
pub fn add_scroll_bar(
&mut self,
clip_and_scroll: ClipAndScrollInfo,
clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
color: ColorF,
scrollbar_info: ScrollbarInfo,
@ -883,7 +899,7 @@ impl FrameBuilder {
pub fn add_line(
&mut self,
clip_and_scroll: ClipAndScrollInfo,
clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
wavy_line_thickness: f32,
orientation: LineOrientation,
@ -970,7 +986,7 @@ impl FrameBuilder {
pub fn add_border(
&mut self,
clip_and_scroll: ClipAndScrollInfo,
clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
border_item: &BorderDisplayItem,
gradient_stops: ItemRange<GradientStop>,
@ -1222,7 +1238,7 @@ impl FrameBuilder {
pub fn add_gradient(
&mut self,
clip_and_scroll: ClipAndScrollInfo,
clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
start_point: LayerPoint,
end_point: LayerPoint,
@ -1290,9 +1306,43 @@ impl FrameBuilder {
self.add_primitive(clip_and_scroll, info, Vec::new(), prim);
}
fn add_radial_gradient_impl(
&mut self,
clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
start_center: LayerPoint,
start_radius: f32,
end_center: LayerPoint,
end_radius: f32,
ratio_xy: f32,
stops: ItemRange<GradientStop>,
extend_mode: ExtendMode,
) {
let prim = BrushPrimitive::new(
BrushKind::RadialGradient {
stops_range: stops,
extend_mode,
stops_handle: GpuCacheHandle::new(),
start_center,
end_center,
start_radius,
end_radius,
ratio_xy,
},
None,
);
self.add_primitive(
clip_and_scroll,
info,
Vec::new(),
PrimitiveContainer::Brush(prim),
);
}
pub fn add_radial_gradient(
&mut self,
clip_and_scroll: ClipAndScrollInfo,
clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
start_center: LayerPoint,
start_radius: f32,
@ -1304,40 +1354,44 @@ impl FrameBuilder {
tile_size: LayerSize,
tile_spacing: LayerSize,
) {
let tile_repeat = tile_size + tile_spacing;
let prim_infos = info.decompose(
tile_size,
tile_spacing,
64 * 64,
);
let radial_gradient_cpu = RadialGradientPrimitiveCpu {
stops_range: stops,
extend_mode,
gpu_data_count: 0,
gpu_blocks: [
[start_center.x, start_center.y, end_center.x, end_center.y].into(),
[
start_radius,
end_radius,
ratio_xy,
pack_as_float(extend_mode as u32),
].into(),
[
tile_size.width,
tile_size.height,
tile_repeat.width,
tile_repeat.height,
].into(),
],
};
self.add_primitive(
if prim_infos.is_empty() {
self.add_radial_gradient_impl(
clip_and_scroll,
info,
Vec::new(),
PrimitiveContainer::RadialGradient(radial_gradient_cpu),
start_center,
start_radius,
end_center,
end_radius,
ratio_xy,
stops,
extend_mode,
);
} else {
for prim_info in prim_infos {
self.add_radial_gradient_impl(
clip_and_scroll,
&prim_info,
start_center,
start_radius,
end_center,
end_radius,
ratio_xy,
stops,
extend_mode,
);
}
}
}
pub fn add_text(
&mut self,
clip_and_scroll: ClipAndScrollInfo,
clip_and_scroll: ScrollNodeAndClipChain,
run_offset: LayoutVector2D,
info: &LayerPrimitiveInfo,
font: &FontInstance,
@ -1493,7 +1547,7 @@ impl FrameBuilder {
pub fn add_image(
&mut self,
clip_and_scroll: ClipAndScrollInfo,
clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
stretch_size: LayerSize,
mut tile_spacing: LayerSize,
@ -1573,7 +1627,7 @@ impl FrameBuilder {
pub fn add_yuv_image(
&mut self,
clip_and_scroll: ClipAndScrollInfo,
clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
yuv_data: YuvData,
color_space: YuvColorSpace,
@ -1586,19 +1640,21 @@ impl FrameBuilder {
YuvData::InterleavedYCbCr(plane_0) => [plane_0, ImageKey::DUMMY, ImageKey::DUMMY],
};
let prim_cpu = YuvImagePrimitiveCpu {
let prim = BrushPrimitive::new(
BrushKind::YuvImage {
yuv_key,
format,
color_space,
image_rendering,
gpu_block: [info.rect.size.width, info.rect.size.height, 0.0, 0.0].into(),
};
},
None,
);
self.add_primitive(
clip_and_scroll,
info,
Vec::new(),
PrimitiveContainer::YuvImage(prim_cpu),
PrimitiveContainer::Brush(prim),
);
}
@ -1807,7 +1863,7 @@ impl FrameBuilder {
let use_dual_source_blending = self.config.dual_source_blending_is_enabled &&
self.config.dual_source_blending_is_supported;
for (pass_index, pass) in passes.iter_mut().enumerate() {
for pass in &mut passes {
let ctx = RenderTargetContext {
device_pixel_scale,
prim_store: &self.prim_store,
@ -1823,7 +1879,6 @@ impl FrameBuilder {
&mut render_tasks,
&mut deferred_resolves,
&self.clip_store,
RenderPassIndex(pass_index),
);
if let RenderPassKind::OffScreen { ref texture_cache, .. } = pass.kind {
@ -1863,3 +1918,67 @@ impl FrameBuilder {
)
}
}
trait PrimitiveInfoTiler {
fn decompose(
&self,
tile_size: LayerSize,
tile_spacing: LayerSize,
max_prims: usize,
) -> Vec<LayerPrimitiveInfo>;
}
impl PrimitiveInfoTiler for LayerPrimitiveInfo {
fn decompose(
&self,
tile_size: LayerSize,
tile_spacing: LayerSize,
max_prims: usize,
) -> Vec<LayerPrimitiveInfo> {
let mut prims = Vec::new();
let tile_repeat = tile_size + tile_spacing;
if tile_repeat.width <= 0.0 ||
tile_repeat.height <= 0.0 {
return prims;
}
if tile_repeat.width < self.rect.size.width ||
tile_repeat.height < self.rect.size.height {
let local_clip = self.local_clip.clip_by(&self.rect);
let rect_p0 = self.rect.origin;
let rect_p1 = self.rect.bottom_right();
let mut y0 = rect_p0.y;
while y0 < rect_p1.y {
let mut x0 = rect_p0.x;
while x0 < rect_p1.x {
prims.push(LayerPrimitiveInfo {
rect: LayerRect::new(
LayerPoint::new(x0, y0),
tile_size,
),
local_clip,
is_backface_visible: self.is_backface_visible,
tag: self.tag,
});
// Mostly a safety against a crazy number of primitives
// being generated. If we exceed that amount, just bail
// out and only draw the maximum amount.
if prims.len() > max_prims {
warn!("too many prims found due to repeat/tile. dropping extra prims!");
return prims;
}
x0 += tile_repeat.width;
}
y0 += tile_repeat.height;
}
}
prims
}
}

Просмотреть файл

@ -628,16 +628,17 @@ fn rasterize_200_glyphs() {
// This test loads a font from disc, the renders 4 requests containing
// 50 glyphs each, deletes the font and waits for the result.
use rayon::Configuration;
use rayon::ThreadPoolBuilder;
use std::fs::File;
use std::io::Read;
let worker_config = Configuration::new()
let worker = ThreadPoolBuilder::new()
.thread_name(|idx|{ format!("WRWorker#{}", idx) })
.start_handler(move |idx| {
register_thread_with_profiler(format!("WRWorker#{}", idx));
});
let workers = Arc::new(ThreadPool::new(worker_config).unwrap());
})
.build();
let workers = Arc::new(worker.unwrap());
let mut glyph_rasterizer = GlyphRasterizer::new(workers).unwrap();
let mut glyph_cache = GlyphCache::new();
let mut gpu_cache = GpuCache::new();

Просмотреть файл

@ -147,6 +147,14 @@ impl From<CompositePrimitiveInstance> for PrimitiveInstance {
}
}
bitflags! {
/// Flags that define how the common brush shader
/// code should process this instance.
pub struct BrushFlags: u8 {
const PERSPECTIVE_INTERPOLATION = 0x1;
}
}
// TODO(gw): While we are comverting things over, we
// need to have the instance be the same
// size as an old PrimitiveInstance. In the
@ -164,6 +172,7 @@ pub struct BrushInstance {
pub z: i32,
pub segment_index: i32,
pub edge_flags: EdgeAaSegmentMask,
pub brush_flags: BrushFlags,
pub user_data: [i32; 3],
}
@ -175,7 +184,9 @@ impl From<BrushInstance> for PrimitiveInstance {
instance.prim_address.as_int(),
((instance.clip_chain_rect_index.0 as i32) << 16) | instance.scroll_id.0 as i32,
instance.z,
instance.segment_index | ((instance.edge_flags.bits() as i32) << 16),
instance.segment_index |
((instance.edge_flags.bits() as i32) << 16) |
((instance.brush_flags.bits() as i32) << 24),
instance.user_data[0],
instance.user_data[1],
instance.user_data[2],

Просмотреть файл

@ -2,13 +2,14 @@
* 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 api::{BorderRadius, ClipAndScrollInfo, ClipId, ClipMode, HitTestFlags, HitTestItem};
use api::{HitTestResult, ItemTag, LayerPoint, LayerPrimitiveInfo, LayerRect};
use api::{LayerToWorldTransform, LocalClip, PipelineId, WorldPoint};
use api::{BorderRadius, ClipId, ClipMode, HitTestFlags, HitTestItem, HitTestResult, ItemTag};
use api::{LayerPoint, LayerPrimitiveInfo, LayerRect, LayerToWorldTransform, LocalClip, PipelineId};
use api::WorldPoint;
use clip::{ClipSource, ClipStore, Contains, rounded_rectangle_contains_point};
use clip_scroll_node::{ClipScrollNode, NodeType};
use clip_scroll_tree::ClipScrollTree;
use clip_scroll_tree::{ClipChainIndex, ClipScrollTree};
use internal_types::FastHashMap;
use prim_store::ScrollNodeAndClipChain;
/// A copy of important clip scroll node data to use during hit testing. This a copy of
/// data from the ClipScrollTree that will persist as a new frame is under construction,
@ -33,11 +34,21 @@ pub struct HitTestClipScrollNode {
/// handled the same way during hit testing. Once we represent all ClipChains
/// using ClipChainDescriptors, we can get rid of this and just use the
/// ClipChainDescriptor here.
#[derive(Clone)]
struct HitTestClipChainDescriptor {
parent: Option<ClipId>,
parent: Option<ClipChainIndex>,
clips: Vec<ClipId>,
}
impl HitTestClipChainDescriptor {
fn empty() -> HitTestClipChainDescriptor {
HitTestClipChainDescriptor {
parent: None,
clips: Vec::new(),
}
}
}
#[derive(Clone)]
pub struct HitTestingItem {
rect: LayerRect,
@ -56,7 +67,7 @@ impl HitTestingItem {
}
#[derive(Clone)]
pub struct HitTestingRun(pub Vec<HitTestingItem>, pub ClipAndScrollInfo);
pub struct HitTestingRun(pub Vec<HitTestingItem>, pub ScrollNodeAndClipChain);
enum HitTestRegion {
Rectangle(LayerRect),
@ -78,7 +89,7 @@ impl HitTestRegion {
pub struct HitTester {
runs: Vec<HitTestingRun>,
nodes: FastHashMap<ClipId, HitTestClipScrollNode>,
clip_chains: FastHashMap<ClipId, HitTestClipChainDescriptor>,
clip_chains: Vec<HitTestClipChainDescriptor>,
}
impl HitTester {
@ -90,7 +101,7 @@ impl HitTester {
let mut hit_tester = HitTester {
runs: runs.clone(),
nodes: FastHashMap::default(),
clip_chains: FastHashMap::default(),
clip_chains: Vec::new(),
};
hit_tester.read_clip_scroll_tree(clip_scroll_tree, clip_store);
hit_tester
@ -102,6 +113,11 @@ impl HitTester {
clip_store: &ClipStore
) {
self.nodes.clear();
self.clip_chains.clear();
self.clip_chains.resize(
clip_scroll_tree.clip_chains.len(),
HitTestClipChainDescriptor::empty()
);
for (id, node) in &clip_scroll_tree.nodes {
self.nodes.insert(*id, HitTestClipScrollNode {
@ -111,52 +127,50 @@ impl HitTester {
node_origin: node.local_viewport_rect.origin,
});
self.clip_chains.insert(*id, HitTestClipChainDescriptor {
parent: node.parent,
clips: vec![*id],
});
if let NodeType::Clip { clip_chain_index, .. } = node.node_type {
let clip_chain = self.clip_chains.get_mut(clip_chain_index.0).unwrap();
clip_chain.parent =
clip_scroll_tree.get_clip_chain(clip_chain_index).parent_index;
clip_chain.clips = vec![*id];
}
}
for descriptor in &clip_scroll_tree.clip_chains_descriptors {
self.clip_chains.insert(
ClipId::ClipChain(descriptor.id),
HitTestClipChainDescriptor {
parent: descriptor.parent.map(|id| ClipId::ClipChain(id)),
clips: descriptor.clips.clone(),
}
);
let clip_chain = self.clip_chains.get_mut(descriptor.index.0).unwrap();
clip_chain.parent = clip_scroll_tree.get_clip_chain(descriptor.index).parent_index;
clip_chain.clips = descriptor.clips.clone();
}
}
fn is_point_clipped_in_for_clip_chain(
&self,
point: WorldPoint,
chain_id: &ClipId,
clip_chain_index: ClipChainIndex,
test: &mut HitTest
) -> bool {
if let Some(result) = test.clip_chain_cache.get(&chain_id) {
return *result;
if let Some(result) = test.get_from_clip_chain_cache(clip_chain_index) {
return result;
}
let descriptor = &self.clip_chains[&chain_id];
let descriptor = &self.clip_chains[clip_chain_index.0];
let parent_clipped_in = match descriptor.parent {
None => true,
Some(ref parent) => self.is_point_clipped_in_for_clip_chain(point, parent, test),
Some(parent) => self.is_point_clipped_in_for_clip_chain(point, parent, test),
};
if !parent_clipped_in {
test.clip_chain_cache.insert(*chain_id, false);
test.set_in_clip_chain_cache(clip_chain_index, false);
return false;
}
for clip_node in &descriptor.clips {
if !self.is_point_clipped_in_for_node(point, clip_node, test) {
test.clip_chain_cache.insert(*chain_id, false);
test.set_in_clip_chain_cache(clip_chain_index, false);
return false;
}
}
test.clip_chain_cache.insert(*chain_id, true);
test.set_in_clip_chain_cache(clip_chain_index, true);
true
}
@ -197,7 +211,9 @@ impl HitTester {
let mut result = HitTestResult::default();
for &HitTestingRun(ref items, ref clip_and_scroll) in self.runs.iter().rev() {
let scroll_node = &self.nodes[&clip_and_scroll.scroll_node_id];
let scroll_node_id = clip_and_scroll.scroll_node_id;
let scroll_node = &self.nodes[&scroll_node_id];
let pipeline_id = scroll_node_id.pipeline_id();
match (test.pipeline_id, clip_and_scroll.scroll_node_id.pipeline_id()) {
(Some(id), node_id) if node_id != id => continue,
_ => {},
@ -215,20 +231,19 @@ impl HitTester {
continue;
}
let clip_id = &clip_and_scroll.clip_node_id();
if !clipped_in {
clipped_in = self.is_point_clipped_in_for_clip_chain(point, clip_id, &mut test);
let clip_chain_index = clip_and_scroll.clip_chain_index;
clipped_in |=
self.is_point_clipped_in_for_clip_chain(point, clip_chain_index, &mut test);
if !clipped_in {
break;
}
}
// We need to trigger a lookup against the root reference frame here, because
// items that are clipped by clip chains won't test against that part of the
// hierarchy. If we don't have a valid point for this test, we are likely
// in a situation where the reference frame has an univertible transform, but the
// item's clip does not.
let root_reference_frame = ClipId::root_reference_frame(clip_id.pipeline_id());
let root_reference_frame = ClipId::root_reference_frame(pipeline_id);
if !self.is_point_clipped_in_for_node(point, &root_reference_frame, &mut test) {
continue;
}
@ -238,7 +253,7 @@ impl HitTester {
};
result.items.push(HitTestItem {
pipeline: clip_and_scroll.clip_node_id().pipeline_id(),
pipeline: pipeline_id,
tag: item.tag,
point_in_viewport,
point_relative_to_item: point_in_layer - item.rect.origin.to_vector(),
@ -259,7 +274,7 @@ fn get_regions_for_clip_scroll_node(
clip_store: &ClipStore
) -> Vec<HitTestRegion> {
let clips = match node.node_type {
NodeType::Clip(ref handle) => clip_store.get(handle).clips(),
NodeType::Clip{ ref handle, .. } => clip_store.get(handle).clips(),
_ => return Vec::new(),
};
@ -280,7 +295,7 @@ pub struct HitTest {
point: WorldPoint,
flags: HitTestFlags,
node_cache: FastHashMap<ClipId, Option<LayerPoint>>,
clip_chain_cache: FastHashMap<ClipId, bool>,
clip_chain_cache: Vec<Option<bool>>,
}
impl HitTest {
@ -294,10 +309,25 @@ impl HitTest {
point,
flags,
node_cache: FastHashMap::default(),
clip_chain_cache: FastHashMap::default(),
clip_chain_cache: Vec::new(),
}
}
pub fn get_from_clip_chain_cache(&mut self, index: ClipChainIndex) -> Option<bool> {
if index.0 >= self.clip_chain_cache.len() {
None
} else {
self.clip_chain_cache[index.0]
}
}
pub fn set_in_clip_chain_cache(&mut self, index: ClipChainIndex, value: bool) {
if index.0 >= self.clip_chain_cache.len() {
self.clip_chain_cache.resize(index.0 + 1, None);
}
self.clip_chain_cache[index.0] = Some(value);
}
pub fn get_absolute_point(&self, hit_tester: &HitTester) -> WorldPoint {
if !self.flags.contains(HitTestFlags::POINT_RELATIVE_TO_PIPELINE_VIEWPORT) {
return self.point;

Просмотреть файл

@ -44,7 +44,11 @@ pub struct CacheTextureId(pub usize);
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct RenderPassIndex(pub usize);
pub struct SavedTargetIndex(pub usize);
impl SavedTargetIndex {
pub const PENDING: Self = SavedTargetIndex(!0);
}
// Represents the source for a texture.
// These are passed from throughout the
@ -61,10 +65,7 @@ pub enum SourceTexture {
External(ExternalImageData),
CacheA8,
CacheRGBA8,
// XXX Remove this once RenderTaskCacheA8 is used.
#[allow(dead_code)]
RenderTaskCacheA8(RenderPassIndex),
RenderTaskCacheRGBA8(RenderPassIndex),
RenderTaskCache(SavedTargetIndex),
}
pub const ORTHO_NEAR_PLANE: f32 = -1000000.0;

Просмотреть файл

@ -2,15 +2,15 @@
* 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 api::{ColorF, ClipAndScrollInfo, FilterOp, MixBlendMode};
use api::{DeviceIntPoint, DeviceIntRect, LayerToWorldScale, PipelineId};
use api::{BoxShadowClipMode, LayerPoint, LayerRect, LayerVector2D, Shadow};
use api::{ClipId, PremultipliedColorF};
use api::{BoxShadowClipMode, ClipId, ColorF, DeviceIntPoint, DeviceIntRect, FilterOp, LayerPoint};
use api::{LayerRect, LayerToWorldScale, LayerVector2D, MixBlendMode, PipelineId};
use api::{PremultipliedColorF, Shadow};
use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowCacheKey};
use frame_builder::{FrameContext, FrameState, PictureState};
use gpu_cache::GpuDataRequest;
use gpu_types::{BrushImageKind, PictureType};
use prim_store::{BrushKind, BrushPrimitive, PrimitiveIndex, PrimitiveRun, PrimitiveRunLocalRect};
use prim_store::ScrollNodeAndClipChain;
use render_task::{ClearMode, RenderTask, RenderTaskCacheKey};
use render_task::{RenderTaskCacheKeyKind, RenderTaskId, RenderTaskLocation};
use resource_cache::CacheItem;
@ -230,7 +230,7 @@ impl PicturePrimitive {
pub fn add_primitive(
&mut self,
prim_index: PrimitiveIndex,
clip_and_scroll: ClipAndScrollInfo
clip_and_scroll: ScrollNodeAndClipChain
) {
if let Some(ref mut run) = self.runs.last_mut() {
if run.clip_and_scroll == clip_and_scroll &&
@ -368,7 +368,7 @@ impl PicturePrimitive {
}
Some(PictureCompositeMode::Filter(FilterOp::DropShadow(offset, blur_radius, color))) => {
let rect = (prim_local_rect.translate(&-offset) * content_scale).round().to_i32();
let picture_task = RenderTask::new_picture(
let mut picture_task = RenderTask::new_picture(
RenderTaskLocation::Dynamic(None, rect.size),
prim_index,
RenderTargetKind::Color,
@ -378,6 +378,7 @@ impl PicturePrimitive {
pic_state_for_children.tasks,
PictureType::Image,
);
picture_task.mark_for_saving();
let blur_std_deviation = blur_radius * frame_context.device_pixel_scale.0;
let picture_task_id = frame_state.render_tasks.add(picture_task);
@ -601,7 +602,7 @@ impl PicturePrimitive {
match composite_mode {
Some(PictureCompositeMode::Filter(FilterOp::ColorMatrix(m))) => {
for i in 0..5 {
request.push([m[i], m[i+5], m[i+10], m[i+15]]);
request.push([m[i*4], m[i*4+1], m[i*4+2], m[i*4+3]]);
}
}
Some(PictureCompositeMode::Filter(filter)) => {

Просмотреть файл

@ -2,25 +2,24 @@
* 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 api::{AlphaType, BorderRadius, BuiltDisplayList, ClipAndScrollInfo, ClipMode};
use api::{ColorF, DeviceIntRect, DeviceIntSize, DevicePixelScale, Epoch};
use api::{ComplexClipRegion, ExtendMode, FontRenderMode};
use api::{AlphaType, BorderRadius, BuiltDisplayList, ClipId, ClipMode, ColorF, ComplexClipRegion};
use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, Epoch, ExtendMode, FontRenderMode};
use api::{GlyphInstance, GlyphKey, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag};
use api::{LayerPoint, LayerRect, LayerSize, LayerToWorldTransform, LayerVector2D, LineOrientation};
use api::{LineStyle, PremultipliedColorF};
use api::{WorldToLayerTransform, YuvColorSpace, YuvFormat};
use api::{LineStyle, PremultipliedColorF, WorldToLayerTransform, YuvColorSpace, YuvFormat};
use border::{BorderCornerInstance, BorderEdgeKind};
use clip_scroll_tree::{CoordinateSystemId};
use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId};
use clip_scroll_node::ClipScrollNode;
use clip::{ClipSource, ClipSourcesHandle};
use clip::{ClipChain, ClipChainNode, ClipChainNodeIter, ClipChainNodeRef, ClipSource};
use clip::{ClipSourcesHandle, ClipWorkItem};
use frame_builder::{FrameContext, FrameState, PictureContext, PictureState, PrimitiveRunContext};
use glyph_rasterizer::{FontInstance, FontTransform};
use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
ToGpuBlocks};
use gpu_types::{ClipChainRectIndex};
use picture::{PictureKind, PicturePrimitive};
use render_task::{BlitSource, ClipChain, ClipChainNode, ClipChainNodeIter, ClipChainNodeRef, ClipWorkItem};
use render_task::{RenderTask, RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskId};
use render_task::{BlitSource, RenderTask, RenderTaskCacheKey, RenderTaskCacheKeyKind};
use render_task::RenderTaskId;
use renderer::{MAX_VERTEX_TEXTURE_WIDTH};
use resource_cache::{CacheItem, ImageProperties, ImageRequest, ResourceCache};
use segment::SegmentBuilder;
@ -32,11 +31,23 @@ use util::recycle_vec;
const MIN_BRUSH_SPLIT_AREA: f32 = 128.0 * 128.0;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ScrollNodeAndClipChain {
pub scroll_node_id: ClipId,
pub clip_chain_index: ClipChainIndex,
}
impl ScrollNodeAndClipChain {
pub fn new(scroll_node_id: ClipId, clip_chain_index: ClipChainIndex) -> ScrollNodeAndClipChain {
ScrollNodeAndClipChain { scroll_node_id, clip_chain_index }
}
}
#[derive(Debug)]
pub struct PrimitiveRun {
pub base_prim_index: PrimitiveIndex,
pub count: usize,
pub clip_and_scroll: ClipAndScrollInfo,
pub clip_and_scroll: ScrollNodeAndClipChain,
}
#[derive(Debug, Copy, Clone)]
@ -112,11 +123,9 @@ pub struct PrimitiveIndex(pub usize);
pub enum PrimitiveKind {
TextRun,
Image,
YuvImage,
Border,
AlignedGradient,
AngleGradient,
RadialGradient,
Picture,
Brush,
}
@ -195,6 +204,22 @@ pub enum BrushKind {
current_epoch: Epoch,
alpha_type: AlphaType,
},
YuvImage {
yuv_key: [ImageKey; 3],
format: YuvFormat,
color_space: YuvColorSpace,
image_rendering: ImageRendering,
},
RadialGradient {
stops_range: ItemRange<GradientStop>,
extend_mode: ExtendMode,
stops_handle: GpuCacheHandle,
start_center: LayerPoint,
end_center: LayerPoint,
start_radius: f32,
end_radius: f32,
ratio_xy: f32,
}
}
impl BrushKind {
@ -202,7 +227,9 @@ impl BrushKind {
match *self {
BrushKind::Solid { .. } |
BrushKind::Picture |
BrushKind::Image { .. } => true,
BrushKind::Image { .. } |
BrushKind::YuvImage { .. } |
BrushKind::RadialGradient { .. } => true,
BrushKind::Mask { .. } |
BrushKind::Clear |
@ -284,7 +311,8 @@ impl BrushPrimitive {
// has to match VECS_PER_SPECIFIC_BRUSH
match self.kind {
BrushKind::Picture |
BrushKind::Image { .. } => {
BrushKind::Image { .. } |
BrushKind::YuvImage { .. } => {
}
BrushKind::Solid { color } => {
request.push(color.premultiplied());
@ -331,6 +359,20 @@ impl BrushPrimitive {
0.0,
]);
}
BrushKind::RadialGradient { start_center, end_center, start_radius, end_radius, ratio_xy, extend_mode, .. } => {
request.push([
start_center.x,
start_center.y,
end_center.x,
end_center.y,
]);
request.push([
start_radius,
end_radius,
ratio_xy,
pack_as_float(extend_mode as u32),
]);
}
}
}
}
@ -377,24 +419,6 @@ impl ToGpuBlocks for ImagePrimitiveCpu {
}
}
#[derive(Debug)]
pub struct YuvImagePrimitiveCpu {
pub yuv_key: [ImageKey; 3],
pub format: YuvFormat,
pub color_space: YuvColorSpace,
pub image_rendering: ImageRendering,
// TODO(gw): Generate on demand
pub gpu_block: GpuBlockData,
}
impl ToGpuBlocks for YuvImagePrimitiveCpu {
fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
request.push(self.gpu_block);
}
}
#[derive(Debug)]
pub struct BorderPrimitiveCpu {
pub corner_instances: [BorderCornerInstance; 4],
@ -631,27 +655,6 @@ impl<'a> GradientGpuBlockBuilder<'a> {
}
}
#[derive(Debug)]
pub struct RadialGradientPrimitiveCpu {
pub stops_range: ItemRange<GradientStop>,
pub extend_mode: ExtendMode,
pub gpu_data_count: i32,
pub gpu_blocks: [GpuBlockData; 3],
}
impl RadialGradientPrimitiveCpu {
fn build_gpu_blocks_for_angle_radial(
&self,
display_list: &BuiltDisplayList,
mut request: GpuDataRequest,
) {
request.extend_from_slice(&self.gpu_blocks);
let gradient_builder = GradientGpuBlockBuilder::new(self.stops_range, display_list);
gradient_builder.build(false, &mut request);
}
}
#[derive(Debug, Clone)]
pub struct TextRunPrimitiveCpu {
pub font: FontInstance,
@ -933,11 +936,9 @@ impl ClipData {
pub enum PrimitiveContainer {
TextRun(TextRunPrimitiveCpu),
Image(ImagePrimitiveCpu),
YuvImage(YuvImagePrimitiveCpu),
Border(BorderPrimitiveCpu),
AlignedGradient(GradientPrimitiveCpu),
AngleGradient(GradientPrimitiveCpu),
RadialGradient(RadialGradientPrimitiveCpu),
Picture(PicturePrimitive),
Brush(BrushPrimitive),
}
@ -948,9 +949,7 @@ pub struct PrimitiveStore {
pub cpu_text_runs: Vec<TextRunPrimitiveCpu>,
pub cpu_pictures: Vec<PicturePrimitive>,
pub cpu_images: Vec<ImagePrimitiveCpu>,
pub cpu_yuv_images: Vec<YuvImagePrimitiveCpu>,
pub cpu_gradients: Vec<GradientPrimitiveCpu>,
pub cpu_radial_gradients: Vec<RadialGradientPrimitiveCpu>,
pub cpu_metadata: Vec<PrimitiveMetadata>,
pub cpu_borders: Vec<BorderPrimitiveCpu>,
}
@ -963,9 +962,7 @@ impl PrimitiveStore {
cpu_text_runs: Vec::new(),
cpu_pictures: Vec::new(),
cpu_images: Vec::new(),
cpu_yuv_images: Vec::new(),
cpu_gradients: Vec::new(),
cpu_radial_gradients: Vec::new(),
cpu_borders: Vec::new(),
}
}
@ -977,9 +974,7 @@ impl PrimitiveStore {
cpu_text_runs: recycle_vec(self.cpu_text_runs),
cpu_pictures: recycle_vec(self.cpu_pictures),
cpu_images: recycle_vec(self.cpu_images),
cpu_yuv_images: recycle_vec(self.cpu_yuv_images),
cpu_gradients: recycle_vec(self.cpu_gradients),
cpu_radial_gradients: recycle_vec(self.cpu_radial_gradients),
cpu_borders: recycle_vec(self.cpu_borders),
}
}
@ -1018,6 +1013,8 @@ impl PrimitiveStore {
BrushKind::Mask { .. } => PrimitiveOpacity::translucent(),
BrushKind::Line { .. } => PrimitiveOpacity::translucent(),
BrushKind::Image { .. } => PrimitiveOpacity::translucent(),
BrushKind::YuvImage { .. } => PrimitiveOpacity::opaque(),
BrushKind::RadialGradient { .. } => PrimitiveOpacity::translucent(),
BrushKind::Picture => {
// TODO(gw): This is not currently used. In the future
// we should detect opaque pictures.
@ -1069,17 +1066,6 @@ impl PrimitiveStore {
self.cpu_images.push(image_cpu);
metadata
}
PrimitiveContainer::YuvImage(image_cpu) => {
let metadata = PrimitiveMetadata {
opacity: PrimitiveOpacity::opaque(),
prim_kind: PrimitiveKind::YuvImage,
cpu_prim_index: SpecificPrimitiveIndex(self.cpu_yuv_images.len()),
..base_metadata
};
self.cpu_yuv_images.push(image_cpu);
metadata
}
PrimitiveContainer::Border(border_cpu) => {
let metadata = PrimitiveMetadata {
opacity: PrimitiveOpacity::translucent(),
@ -1114,18 +1100,6 @@ impl PrimitiveStore {
self.cpu_gradients.push(gradient_cpu);
metadata
}
PrimitiveContainer::RadialGradient(radial_gradient_cpu) => {
let metadata = PrimitiveMetadata {
// TODO: calculate if the gradient is actually opaque
opacity: PrimitiveOpacity::translucent(),
prim_kind: PrimitiveKind::RadialGradient,
cpu_prim_index: SpecificPrimitiveIndex(self.cpu_radial_gradients.len()),
..base_metadata
};
self.cpu_radial_gradients.push(radial_gradient_cpu);
metadata
}
};
self.cpu_metadata.push(metadata);
@ -1293,22 +1267,6 @@ impl PrimitiveStore {
}
}
}
PrimitiveKind::YuvImage => {
let image_cpu = &mut self.cpu_yuv_images[metadata.cpu_prim_index.0];
let channel_num = image_cpu.format.get_plane_num();
debug_assert!(channel_num <= 3);
for channel in 0 .. channel_num {
frame_state.resource_cache.request_image(
ImageRequest {
key: image_cpu.yuv_key[channel],
rendering: image_cpu.image_rendering,
tile: None,
},
frame_state.gpu_cache,
);
}
}
PrimitiveKind::Brush => {
let brush = &mut self.cpu_brushes[metadata.cpu_prim_index.0];
@ -1332,6 +1290,32 @@ impl PrimitiveStore {
frame_state.gpu_cache,
);
}
BrushKind::YuvImage { format, yuv_key, image_rendering, .. } => {
let channel_num = format.get_plane_num();
debug_assert!(channel_num <= 3);
for channel in 0 .. channel_num {
frame_state.resource_cache.request_image(
ImageRequest {
key: yuv_key[channel],
rendering: image_rendering,
tile: None,
},
frame_state.gpu_cache,
);
}
}
BrushKind::RadialGradient { ref mut stops_handle, stops_range, .. } => {
if let Some(mut request) = frame_state.gpu_cache.request(stops_handle) {
let gradient_builder = GradientGpuBlockBuilder::new(
stops_range,
pic_context.display_list,
);
gradient_builder.build(
false,
&mut request,
);
}
}
BrushKind::Mask { .. } |
BrushKind::Solid { .. } |
BrushKind::Clear |
@ -1340,8 +1324,7 @@ impl PrimitiveStore {
}
}
PrimitiveKind::AlignedGradient |
PrimitiveKind::AngleGradient |
PrimitiveKind::RadialGradient => {}
PrimitiveKind::AngleGradient => {}
}
// Mark this GPU resource as required for this frame.
@ -1359,10 +1342,6 @@ impl PrimitiveStore {
let image = &self.cpu_images[metadata.cpu_prim_index.0];
image.write_gpu_blocks(request);
}
PrimitiveKind::YuvImage => {
let yuv_image = &self.cpu_yuv_images[metadata.cpu_prim_index.0];
yuv_image.write_gpu_blocks(request);
}
PrimitiveKind::AlignedGradient => {
let gradient = &self.cpu_gradients[metadata.cpu_prim_index.0];
metadata.opacity = gradient.build_gpu_blocks_for_aligned(
@ -1377,13 +1356,6 @@ impl PrimitiveStore {
request,
);
}
PrimitiveKind::RadialGradient => {
let gradient = &self.cpu_radial_gradients[metadata.cpu_prim_index.0];
gradient.build_gpu_blocks_for_angle_radial(
pic_context.display_list,
request,
);
}
PrimitiveKind::TextRun => {
let text = &self.cpu_text_runs[metadata.cpu_prim_index.0];
text.write_gpu_blocks(&mut request);
@ -1634,12 +1606,9 @@ impl PrimitiveStore {
}
};
let mut combined_outer_rect = match prim_run_context.clip_chain {
Some(ref chain) => prim_screen_rect.intersection(&chain.combined_outer_screen_rect),
None => Some(prim_screen_rect),
};
let clip_chain = prim_run_context.clip_chain.map_or(None, |x| x.nodes.clone());
let mut combined_outer_rect =
prim_screen_rect.intersection(&prim_run_context.clip_chain.combined_outer_screen_rect);
let clip_chain = prim_run_context.clip_chain.nodes.clone();
let prim_coordinate_system_id = prim_run_context.scroll_node.coordinate_system_id;
let transform = &prim_run_context.scroll_node.world_content_transform;
@ -1860,12 +1829,8 @@ impl PrimitiveStore {
frame_context.device_pixel_scale,
);
let clip_bounds = match prim_run_context.clip_chain {
Some(ref node) => node.combined_outer_screen_rect,
None => frame_context.screen_rect,
};
metadata.screen_rect = screen_bounding_rect
.intersection(&clip_bounds)
.intersection(&prim_run_context.clip_chain.combined_outer_screen_rect)
.map(|clipped| {
ScreenRect {
clipped,
@ -1935,7 +1900,7 @@ impl PrimitiveStore {
.nodes[&run.clip_and_scroll.scroll_node_id];
let clip_chain = frame_context
.clip_scroll_tree
.get_clip_chain(&run.clip_and_scroll.clip_node_id());
.get_clip_chain(run.clip_and_scroll.clip_chain_index);
if pic_context.perform_culling {
if !scroll_node.invertible {
@ -1943,15 +1908,11 @@ impl PrimitiveStore {
continue;
}
match clip_chain {
Some(ref chain) if chain.combined_outer_screen_rect.is_empty() => {
if clip_chain.combined_outer_screen_rect.is_empty() {
debug!("{:?} {:?}: clipped out", run.base_prim_index, pic_context.pipeline_id);
continue;
}
_ => {},
}
}
let parent_relative_transform = pic_context
.inv_world_transform
@ -2082,14 +2043,9 @@ fn convert_clip_chain_to_clip_vector(
fn get_local_clip_rect_for_nodes(
scroll_node: &ClipScrollNode,
clip_chain: Option<&ClipChain>,
clip_chain: &ClipChain,
) -> Option<LayerRect> {
let clip_chain_nodes = match clip_chain {
Some(ref clip_chain) => clip_chain.nodes.clone(),
None => return None,
};
let local_rect = ClipChainNodeIter { current: clip_chain_nodes }.fold(
let local_rect = ClipChainNodeIter { current: clip_chain.nodes.clone() }.fold(
None,
|combined_local_clip_rect: Option<LayerRect>, node| {
if node.work_item.coordinate_system_id != scroll_node.coordinate_system_id {

Просмотреть файл

@ -300,6 +300,9 @@ impl ProfileCounter for AverageTimeProfileCounter {
pub struct FrameProfileCounters {
pub total_primitives: IntProfileCounter,
pub visible_primitives: IntProfileCounter,
pub targets_used: IntProfileCounter,
pub targets_changed: IntProfileCounter,
pub targets_created: IntProfileCounter,
}
impl FrameProfileCounters {
@ -307,8 +310,16 @@ impl FrameProfileCounters {
FrameProfileCounters {
total_primitives: IntProfileCounter::new("Total Primitives"),
visible_primitives: IntProfileCounter::new("Visible Primitives"),
targets_used: IntProfileCounter::new("Used targets"),
targets_changed: IntProfileCounter::new("Changed targets"),
targets_created: IntProfileCounter::new("Created targets"),
}
}
pub fn reset_targets(&mut self) {
self.targets_used.reset();
self.targets_changed.reset();
self.targets_created.reset();
}
}
#[derive(Clone)]
@ -840,7 +851,7 @@ impl Profiler {
}
}
fn draw_gpu_cache_bar(
fn draw_bar(
&mut self,
label: &str,
label_color: ColorU,
@ -898,9 +909,9 @@ impl Profiler {
value: counters.allocated_rows.value * MAX_VERTEX_TEXTURE_WIDTH,
};
let rect0 = self.draw_gpu_cache_bar(
let rect0 = self.draw_bar(
&format!("GPU cache rows ({}):", counters.allocated_rows.value),
ColorU::new(255, 255, 255, 255),
ColorU::new(0xFF, 0xFF, 0xFF, 0xFF),
&[
(color_updated, &counters.updated_rows),
(color_free, &counters.allocated_rows),
@ -908,14 +919,53 @@ impl Profiler {
debug_renderer,
);
let rect1 = self.draw_gpu_cache_bar(
let rect1 = self.draw_bar(
"GPU cache blocks",
ColorU::new(255, 255, 0, 255),
ColorU::new(0xFF, 0xFF, 0, 0xFF),
&[
(color_updated, &counters.updated_blocks),
(color_saved, &requested_blocks),
(color_free, &counters.allocated_blocks),
(ColorU::new(0, 0, 0, 255), &total_blocks),
(ColorU::new(0, 0, 0, 0xFF), &total_blocks),
],
debug_renderer,
);
let total_rect = rect0.union(&rect1).inflate(10.0, 10.0);
debug_renderer.add_quad(
total_rect.origin.x,
total_rect.origin.y,
total_rect.origin.x + total_rect.size.width,
total_rect.origin.y + total_rect.size.height,
ColorF::new(0.1, 0.1, 0.1, 0.8).into(),
ColorF::new(0.2, 0.2, 0.2, 0.8).into(),
);
self.draw_state.y_left = total_rect.origin.y + total_rect.size.height + 30.0;
}
fn draw_frame_bars(
&mut self,
counters: &FrameProfileCounters,
debug_renderer: &mut DebugRenderer,
) {
let rect0 = self.draw_bar(
&format!("primitives ({}):", counters.total_primitives.value),
ColorU::new(0xFF, 0xFF, 0xFF, 0xFF),
&[
(ColorU::new(0, 0, 0xFF, 0xFF), &counters.visible_primitives),
(ColorU::new(0, 0, 0, 0xFF), &counters.total_primitives),
],
debug_renderer,
);
let rect1 = self.draw_bar(
&format!("GPU targets ({}):", &counters.targets_used.value),
ColorU::new(0xFF, 0xFF, 0, 0xFF),
&[
(ColorU::new(0, 0, 0xFF, 0xFF), &counters.targets_created),
(ColorU::new(0xFF, 0, 0, 0xFF), &counters.targets_changed),
(ColorU::new(0, 0xFF, 0, 0xFF), &counters.targets_used),
],
debug_renderer,
);
@ -1017,15 +1067,7 @@ impl Profiler {
);
for frame_profile in frame_profiles {
Profiler::draw_counters(
&[
&frame_profile.total_primitives,
&frame_profile.visible_primitives,
],
debug_renderer,
true,
&mut self.draw_state
);
self.draw_frame_bars(frame_profile, debug_renderer);
}
Profiler::draw_counters(

Просмотреть файл

@ -2,22 +2,21 @@
* 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 api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize};
use api::{ImageDescriptor, ImageFormat, LayerRect, PremultipliedColorF};
use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, ImageDescriptor, ImageFormat};
use api::PremultipliedColorF;
use box_shadow::BoxShadowCacheKey;
use clip::{ClipSourcesWeakHandle};
use clip::ClipWorkItem;
use clip_scroll_tree::CoordinateSystemId;
use device::TextureFilter;
use gpu_cache::GpuCache;
use gpu_types::{ClipScrollNodeIndex, PictureType};
use internal_types::{FastHashMap, RenderPassIndex, SourceTexture};
use gpu_types::PictureType;
use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
use picture::ContentOrigin;
use prim_store::{PrimitiveIndex, ImageCacheKey};
#[cfg(feature = "debugger")]
use print_tree::{PrintTreePrinter};
use resource_cache::CacheItem;
use std::{cmp, ops, usize, f32, i32};
use std::rc::Rc;
use texture_cache::{TextureCache, TextureCacheHandle};
use tiling::{RenderPass, RenderTargetIndex};
use tiling::{RenderTargetKind};
@ -43,91 +42,7 @@ pub struct RenderTaskAddress(pub u32);
pub struct RenderTaskTree {
pub tasks: Vec<RenderTask>,
pub task_data: Vec<RenderTaskData>,
}
pub type ClipChainNodeRef = Option<Rc<ClipChainNode>>;
#[derive(Debug, Clone)]
pub struct ClipChainNode {
pub work_item: ClipWorkItem,
pub local_clip_rect: LayerRect,
pub screen_outer_rect: DeviceIntRect,
pub screen_inner_rect: DeviceIntRect,
pub prev: ClipChainNodeRef,
}
#[derive(Debug, Clone)]
pub struct ClipChain {
pub combined_outer_screen_rect: DeviceIntRect,
pub combined_inner_screen_rect: DeviceIntRect,
pub nodes: ClipChainNodeRef,
}
impl ClipChain {
pub fn empty(screen_rect: &DeviceIntRect) -> ClipChain {
ClipChain {
combined_inner_screen_rect: *screen_rect,
combined_outer_screen_rect: *screen_rect,
nodes: None,
}
}
pub fn new_with_added_node(
&self,
work_item: ClipWorkItem,
local_clip_rect: LayerRect,
screen_outer_rect: DeviceIntRect,
screen_inner_rect: DeviceIntRect,
) -> ClipChain {
let new_node = ClipChainNode {
work_item,
local_clip_rect,
screen_outer_rect,
screen_inner_rect,
prev: None,
};
let mut new_chain = self.clone();
new_chain.add_node(new_node);
new_chain
}
pub fn add_node(&mut self, mut new_node: ClipChainNode) {
new_node.prev = self.nodes.clone();
// If this clip's outer rectangle is completely enclosed by the clip
// chain's inner rectangle, then the only clip that matters from this point
// on is this clip. We can disconnect this clip from the parent clip chain.
if self.combined_inner_screen_rect.contains_rect(&new_node.screen_outer_rect) {
new_node.prev = None;
}
self.combined_outer_screen_rect =
self.combined_outer_screen_rect.intersection(&new_node.screen_outer_rect)
.unwrap_or_else(DeviceIntRect::zero);
self.combined_inner_screen_rect =
self.combined_inner_screen_rect.intersection(&new_node.screen_inner_rect)
.unwrap_or_else(DeviceIntRect::zero);
self.nodes = Some(Rc::new(new_node));
}
}
pub struct ClipChainNodeIter {
pub current: ClipChainNodeRef,
}
impl Iterator for ClipChainNodeIter {
type Item = Rc<ClipChainNode>;
fn next(&mut self) -> ClipChainNodeRef {
let previous = self.current.clone();
self.current = match self.current {
Some(ref item) => item.prev.clone(),
None => return None,
};
previous
}
next_saved: SavedTargetIndex,
}
impl RenderTaskTree {
@ -135,6 +50,7 @@ impl RenderTaskTree {
RenderTaskTree {
tasks: Vec::new(),
task_data: Vec::new(),
next_saved: SavedTargetIndex(0),
}
}
@ -195,10 +111,16 @@ impl RenderTaskTree {
}
pub fn build(&mut self) {
for task in &mut self.tasks {
for task in &self.tasks {
self.task_data.push(task.write_task_data());
}
}
pub fn save_target(&mut self) -> SavedTargetIndex {
let id = self.next_saved;
self.next_saved.0 += 1;
id
}
}
impl ops::Index<RenderTaskId> for RenderTaskTree {
@ -223,15 +145,6 @@ pub enum RenderTaskLocation {
TextureCache(SourceTexture, i32, DeviceIntRect),
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct ClipWorkItem {
pub scroll_node_data_index: ClipScrollNodeIndex,
pub clip_sources: ClipSourcesWeakHandle,
pub coordinate_system_id: CoordinateSystemId,
}
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
@ -331,7 +244,7 @@ pub struct RenderTask {
pub children: Vec<RenderTaskId>,
pub kind: RenderTaskKind,
pub clear_mode: ClearMode,
pub pass_index: Option<RenderPassIndex>,
pub saved_index: Option<SavedTargetIndex>,
}
impl RenderTask {
@ -356,7 +269,7 @@ impl RenderTask {
pic_type,
}),
clear_mode,
pass_index: None,
saved_index: None,
}
}
@ -366,7 +279,7 @@ impl RenderTask {
location: RenderTaskLocation::Dynamic(None, screen_rect.size),
kind: RenderTaskKind::Readback(screen_rect),
clear_mode: ClearMode::Transparent,
pass_index: None,
saved_index: None,
}
}
@ -392,7 +305,7 @@ impl RenderTask {
source,
}),
clear_mode: ClearMode::Transparent,
pass_index: None,
saved_index: None,
}
}
@ -400,7 +313,7 @@ impl RenderTask {
outer_rect: DeviceIntRect,
clips: Vec<ClipWorkItem>,
prim_coordinate_system_id: CoordinateSystemId,
) -> RenderTask {
) -> Self {
RenderTask {
children: Vec::new(),
location: RenderTaskLocation::Dynamic(None, outer_rect.size),
@ -410,7 +323,7 @@ impl RenderTask {
coordinate_system_id: prim_coordinate_system_id,
}),
clear_mode: ClearMode::One,
pass_index: None,
saved_index: None,
}
}
@ -473,7 +386,7 @@ impl RenderTask {
scale_factor,
}),
clear_mode,
pass_index: None,
saved_index: None,
};
let blur_task_v_id = render_tasks.add(blur_task_v);
@ -488,7 +401,7 @@ impl RenderTask {
scale_factor,
}),
clear_mode,
pass_index: None,
saved_index: None,
};
(blur_task_h, scale_factor)
@ -507,7 +420,7 @@ impl RenderTask {
RenderTargetKind::Color => ClearMode::Transparent,
RenderTargetKind::Alpha => ClearMode::One,
},
pass_index: None,
saved_index: None,
}
}
@ -674,7 +587,6 @@ impl RenderTask {
RenderTaskKind::HorizontalBlur(..) |
RenderTaskKind::Scaling(..) |
RenderTaskKind::Blit(..) => false,
RenderTaskKind::CacheMask(..) => true,
}
}
@ -723,6 +635,19 @@ impl RenderTask {
pt.end_level();
true
}
/// Mark this render task for keeping the results alive up until the end of the frame.
pub fn mark_for_saving(&mut self) {
match self.location {
RenderTaskLocation::Fixed(..) |
RenderTaskLocation::Dynamic(..) => {
self.saved_index = Some(SavedTargetIndex::PENDING);
}
RenderTaskLocation::TextureCache(..) => {
panic!("Unable to mark a permanently cached task for saving!");
}
}
}
}
#[derive(Debug, Hash, PartialEq, Eq)]

Просмотреть файл

@ -40,15 +40,15 @@ use glyph_rasterizer::GlyphFormat;
use gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList};
use gpu_types::PrimitiveInstance;
use internal_types::{SourceTexture, ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE, ResourceCacheError};
use internal_types::{CacheTextureId, FastHashMap, RenderedDocument, ResultMsg, TextureUpdateOp};
use internal_types::{DebugOutput, RenderPassIndex, RenderTargetInfo, TextureUpdateList, TextureUpdateSource};
use internal_types::{CacheTextureId, DebugOutput, FastHashMap, RenderedDocument, ResultMsg};
use internal_types::{TextureUpdateList, TextureUpdateOp, TextureUpdateSource};
use internal_types::{RenderTargetInfo, SavedTargetIndex};
use picture::ContentOrigin;
use prim_store::DeferredResolve;
use profiler::{BackendProfileCounters, Profiler};
use profiler::{BackendProfileCounters, FrameProfileCounters, Profiler};
use profiler::{GpuProfileTag, RendererProfileCounters, RendererProfileTimers};
use query::{GpuProfiler, GpuTimer};
use rayon::Configuration as ThreadPoolConfig;
use rayon::ThreadPool;
use rayon::{ThreadPool, ThreadPoolBuilder};
use record::ApiRecordingReceiver;
use render_backend::RenderBackend;
use render_task::{RenderTaskKind, RenderTaskTree};
@ -83,6 +83,14 @@ const GPU_CACHE_RESIZE_TEST: bool = false;
/// Number of GPU blocks per UV rectangle provided for an image.
pub const BLOCKS_PER_UV_RECT: usize = 2;
const GPU_TAG_BRUSH_RADIAL_GRADIENT: GpuProfileTag = GpuProfileTag {
label: "B_RadialGradient",
color: debug_colors::LIGHTPINK,
};
const GPU_TAG_BRUSH_YUV_IMAGE: GpuProfileTag = GpuProfileTag {
label: "B_YuvImage",
color: debug_colors::DARKGREEN,
};
const GPU_TAG_BRUSH_MIXBLEND: GpuProfileTag = GpuProfileTag {
label: "B_MixBlend",
color: debug_colors::MAGENTA,
@ -131,10 +139,6 @@ const GPU_TAG_PRIM_IMAGE: GpuProfileTag = GpuProfileTag {
label: "Image",
color: debug_colors::GREEN,
};
const GPU_TAG_PRIM_YUV_IMAGE: GpuProfileTag = GpuProfileTag {
label: "YuvImage",
color: debug_colors::DARKGREEN,
};
const GPU_TAG_PRIM_HW_COMPOSITE: GpuProfileTag = GpuProfileTag {
label: "HwComposite",
color: debug_colors::DODGERBLUE,
@ -155,10 +159,6 @@ const GPU_TAG_PRIM_ANGLE_GRADIENT: GpuProfileTag = GpuProfileTag {
label: "AngleGradient",
color: debug_colors::POWDERBLUE,
};
const GPU_TAG_PRIM_RADIAL_GRADIENT: GpuProfileTag = GpuProfileTag {
label: "RadialGradient",
color: debug_colors::LIGHTPINK,
};
const GPU_TAG_PRIM_BORDER_CORNER: GpuProfileTag = GpuProfileTag {
label: "BorderCorner",
color: debug_colors::DARKSLATEGREY,
@ -200,10 +200,8 @@ impl TransformBatchKind {
ImageBufferKind::TextureExternal => "Image (External)",
ImageBufferKind::Texture2DArray => "Image (Array)",
},
TransformBatchKind::YuvImage(..) => "YuvImage",
TransformBatchKind::AlignedGradient => "AlignedGradient",
TransformBatchKind::AngleGradient => "AngleGradient",
TransformBatchKind::RadialGradient => "RadialGradient",
TransformBatchKind::BorderCorner => "BorderCorner",
TransformBatchKind::BorderEdge => "BorderEdge",
}
@ -213,12 +211,10 @@ impl TransformBatchKind {
match *self {
TransformBatchKind::TextRun(..) => GPU_TAG_PRIM_TEXT_RUN,
TransformBatchKind::Image(..) => GPU_TAG_PRIM_IMAGE,
TransformBatchKind::YuvImage(..) => GPU_TAG_PRIM_YUV_IMAGE,
TransformBatchKind::BorderCorner => GPU_TAG_PRIM_BORDER_CORNER,
TransformBatchKind::BorderEdge => GPU_TAG_PRIM_BORDER_EDGE,
TransformBatchKind::AlignedGradient => GPU_TAG_PRIM_GRADIENT,
TransformBatchKind::AngleGradient => GPU_TAG_PRIM_ANGLE_GRADIENT,
TransformBatchKind::RadialGradient => GPU_TAG_PRIM_RADIAL_GRADIENT,
}
}
}
@ -237,6 +233,8 @@ impl BatchKind {
BrushBatchKind::Image(..) => "Brush (Image)",
BrushBatchKind::Blend => "Brush (Blend)",
BrushBatchKind::MixBlend { .. } => "Brush (Composite)",
BrushBatchKind::YuvImage(..) => "Brush (YuvImage)",
BrushBatchKind::RadialGradient => "Brush (RadialGradient)",
}
}
BatchKind::Transformable(_, batch_kind) => batch_kind.debug_name(),
@ -255,6 +253,8 @@ impl BatchKind {
BrushBatchKind::Image(..) => GPU_TAG_BRUSH_IMAGE,
BrushBatchKind::Blend => GPU_TAG_BRUSH_BLEND,
BrushBatchKind::MixBlend { .. } => GPU_TAG_BRUSH_MIXBLEND,
BrushBatchKind::YuvImage(..) => GPU_TAG_BRUSH_YUV_IMAGE,
BrushBatchKind::RadialGradient => GPU_TAG_BRUSH_RADIAL_GRADIENT,
}
}
BatchKind::Transformable(_, batch_kind) => batch_kind.gpu_sampler_tag(),
@ -598,7 +598,11 @@ impl CpuProfile {
}
}
struct RenderTargetPoolId(usize);
struct ActiveTexture {
texture: Texture,
saved_index: Option<SavedTargetIndex>,
is_shared: bool,
}
struct SourceTextureResolver {
/// A vector for fast resolves of texture cache IDs to
@ -620,12 +624,17 @@ struct SourceTextureResolver {
dummy_cache_texture: Texture,
/// The current cache textures.
cache_rgba8_texture: Option<Texture>,
cache_a8_texture: Option<Texture>,
cache_rgba8_texture: Option<ActiveTexture>,
cache_a8_texture: Option<ActiveTexture>,
pass_rgba8_textures: FastHashMap<RenderPassIndex, RenderTargetPoolId>,
pass_a8_textures: FastHashMap<RenderPassIndex, RenderTargetPoolId>,
/// An alpha texture shared between all passes.
//TODO: just use the standard texture saving logic instead.
shared_alpha_texture: Option<Texture>,
/// Saved cache textures that are to be re-used.
saved_textures: Vec<Texture>,
/// General pool of render targets.
render_target_pool: Vec<Texture>,
}
@ -649,8 +658,8 @@ impl SourceTextureResolver {
dummy_cache_texture,
cache_a8_texture: None,
cache_rgba8_texture: None,
pass_rgba8_textures: FastHashMap::default(),
pass_a8_textures: FastHashMap::default(),
shared_alpha_texture: None,
saved_textures: Vec::default(),
render_target_pool: Vec::new(),
}
}
@ -670,34 +679,47 @@ impl SourceTextureResolver {
fn begin_frame(&mut self) {
assert!(self.cache_rgba8_texture.is_none());
assert!(self.cache_a8_texture.is_none());
self.pass_rgba8_textures.clear();
self.pass_a8_textures.clear();
assert!(self.saved_textures.is_empty());
}
fn end_frame(&mut self, pass_index: RenderPassIndex) {
fn end_frame(&mut self) {
// return the cached targets to the pool
self.end_pass(None, None, pass_index)
self.end_pass(None, None);
// return the global alpha texture
self.render_target_pool.extend(self.shared_alpha_texture.take());
// return the saved targets as well
self.render_target_pool.extend(self.saved_textures.drain(..));
}
fn end_pass(
&mut self,
a8_texture: Option<Texture>,
rgba8_texture: Option<Texture>,
pass_index: RenderPassIndex,
a8_texture: Option<ActiveTexture>,
rgba8_texture: Option<ActiveTexture>,
) {
// If we have cache textures from previous pass, return them to the pool.
// Also assign the pool index of those cache textures to last pass's index because this is
// the result of last pass.
if let Some(texture) = self.cache_rgba8_texture.take() {
self.pass_rgba8_textures.insert(
RenderPassIndex(pass_index.0 - 1), RenderTargetPoolId(self.render_target_pool.len()));
self.render_target_pool.push(texture);
// Note: the order here is important, needs to match the logic in `RenderPass::build()`.
if let Some(at) = self.cache_rgba8_texture.take() {
assert!(!at.is_shared);
if let Some(index) = at.saved_index {
assert_eq!(self.saved_textures.len(), index.0);
self.saved_textures.push(at.texture);
} else {
self.render_target_pool.push(at.texture);
}
}
if let Some(at) = self.cache_a8_texture.take() {
if let Some(index) = at.saved_index {
assert!(!at.is_shared);
assert_eq!(self.saved_textures.len(), index.0);
self.saved_textures.push(at.texture);
} else if at.is_shared {
assert!(self.shared_alpha_texture.is_none());
self.shared_alpha_texture = Some(at.texture);
} else {
self.render_target_pool.push(at.texture);
}
if let Some(texture) = self.cache_a8_texture.take() {
self.pass_a8_textures.insert(
RenderPassIndex(pass_index.0 - 1), RenderTargetPoolId(self.render_target_pool.len()));
self.render_target_pool.push(texture);
}
// We have another pass to process, make these textures available
@ -711,15 +733,17 @@ impl SourceTextureResolver {
match *texture_id {
SourceTexture::Invalid => {}
SourceTexture::CacheA8 => {
let texture = self.cache_a8_texture
.as_ref()
.unwrap_or(&self.dummy_cache_texture);
let texture = match self.cache_a8_texture {
Some(ref at) => &at.texture,
None => &self.dummy_cache_texture,
};
device.bind_texture(sampler, texture);
}
SourceTexture::CacheRGBA8 => {
let texture = self.cache_rgba8_texture
.as_ref()
.unwrap_or(&self.dummy_cache_texture);
let texture = match self.cache_rgba8_texture {
Some(ref at) => &at.texture,
None => &self.dummy_cache_texture,
};
device.bind_texture(sampler, texture);
}
SourceTexture::External(external_image) => {
@ -732,17 +756,9 @@ impl SourceTextureResolver {
let texture = &self.cache_texture_map[index.0];
device.bind_texture(sampler, texture);
}
SourceTexture::RenderTaskCacheRGBA8(pass_index) => {
let pool_index = self.pass_rgba8_textures
.get(&pass_index)
.expect("BUG: pass_index doesn't map to pool_index");
device.bind_texture(sampler, &self.render_target_pool[pool_index.0])
}
SourceTexture::RenderTaskCacheA8(pass_index) => {
let pool_index = self.pass_a8_textures
.get(&pass_index)
.expect("BUG: pass_index doesn't map to pool_index");
device.bind_texture(sampler, &self.render_target_pool[pool_index.0])
SourceTexture::RenderTaskCache(saved_index) => {
let texture = &self.saved_textures[saved_index.0];
device.bind_texture(sampler, texture)
}
}
}
@ -754,31 +770,26 @@ impl SourceTextureResolver {
match *texture_id {
SourceTexture::Invalid => None,
SourceTexture::CacheA8 => Some(
self.cache_a8_texture
.as_ref()
.unwrap_or(&self.dummy_cache_texture),
match self.cache_a8_texture {
Some(ref at) => &at.texture,
None => &self.dummy_cache_texture,
}
),
SourceTexture::CacheRGBA8 => Some(
self.cache_rgba8_texture
.as_ref()
.unwrap_or(&self.dummy_cache_texture),
match self.cache_rgba8_texture {
Some(ref at) => &at.texture,
None => &self.dummy_cache_texture,
}
),
SourceTexture::External(..) => {
panic!("BUG: External textures cannot be resolved, they can only be bound.");
}
SourceTexture::TextureCache(index) => Some(&self.cache_texture_map[index.0]),
SourceTexture::RenderTaskCacheRGBA8(pass_index) => {
let pool_index = self.pass_rgba8_textures
.get(&pass_index)
.expect("BUG: pass_index doesn't map to pool_index");
Some(&self.render_target_pool[pool_index.0])
},
SourceTexture::RenderTaskCacheA8(pass_index) => {
let pool_index = self.pass_a8_textures
.get(&pass_index)
.expect("BUG: pass_index doesn't map to pool_index");
Some(&self.render_target_pool[pool_index.0])
},
SourceTexture::TextureCache(index) => {
Some(&self.cache_texture_map[index.0])
}
SourceTexture::RenderTaskCache(saved_index) => {
Some(&self.saved_textures[saved_index.0])
}
}
}
}
@ -1605,6 +1616,8 @@ pub struct Renderer {
brush_image: Vec<Option<BrushShader>>,
brush_blend: BrushShader,
brush_mix_blend: BrushShader,
brush_yuv_image: Vec<Option<BrushShader>>,
brush_radial_gradient: BrushShader,
/// These are "cache clip shaders". These shaders are used to
/// draw clip instances into the cached clip mask. The results
@ -1623,12 +1636,10 @@ pub struct Renderer {
ps_text_run: TextShader,
ps_text_run_dual_source: TextShader,
ps_image: Vec<Option<PrimitiveShader>>,
ps_yuv_image: Vec<Option<PrimitiveShader>>,
ps_border_corner: PrimitiveShader,
ps_border_edge: PrimitiveShader,
ps_gradient: PrimitiveShader,
ps_angle_gradient: PrimitiveShader,
ps_radial_gradient: PrimitiveShader,
ps_hw_composite: LazilyCompiledShader,
ps_split_composite: LazilyCompiledShader,
@ -1745,6 +1756,7 @@ impl Renderer {
let (payload_tx, payload_rx) = try!{ channel::payload_channel() };
let (result_tx, result_rx) = channel();
let gl_type = gl.get_type();
let dithering_feature = ["DITHERING"];
let debug_server = DebugServer::new(api_tx.clone());
@ -1861,6 +1873,17 @@ impl Renderer {
options.precache_shaders)
};
let brush_radial_gradient = try!{
BrushShader::new("brush_radial_gradient",
&mut device,
if options.enable_dithering {
&dithering_feature
} else {
&[]
},
options.precache_shaders)
};
let cs_blur_a8 = try!{
LazilyCompiledShader::new(ShaderKind::Cache(VertexArrayKind::Blur),
"cs_blur",
@ -1952,10 +1975,10 @@ impl Renderer {
// All yuv_image configuration.
let mut yuv_features = Vec::new();
let yuv_shader_num = IMAGE_BUFFER_KINDS.len() * YUV_FORMATS.len() * YUV_COLOR_SPACES.len();
let mut ps_yuv_image: Vec<Option<PrimitiveShader>> = Vec::new();
let mut brush_yuv_image = Vec::new();
// PrimitiveShader is not clonable. Use push() to initialize the vec.
for _ in 0 .. yuv_shader_num {
ps_yuv_image.push(None);
brush_yuv_image.push(None);
}
for buffer_kind in 0 .. IMAGE_BUFFER_KINDS.len() {
if IMAGE_BUFFER_KINDS[buffer_kind].has_platform_support(&gl_type) {
@ -1976,7 +1999,7 @@ impl Renderer {
}
let shader = try!{
PrimitiveShader::new("ps_yuv_image",
BrushShader::new("brush_yuv_image",
&mut device,
&yuv_features,
options.precache_shaders)
@ -1986,7 +2009,7 @@ impl Renderer {
YUV_FORMATS[format_kind],
YUV_COLOR_SPACES[color_space_kind],
);
ps_yuv_image[index] = Some(shader);
brush_yuv_image[index] = Some(shader);
yuv_features.clear();
}
}
@ -2007,8 +2030,6 @@ impl Renderer {
options.precache_shaders)
};
let dithering_feature = ["DITHERING"];
let ps_gradient = try!{
PrimitiveShader::new("ps_gradient",
&mut device,
@ -2031,17 +2052,6 @@ impl Renderer {
options.precache_shaders)
};
let ps_radial_gradient = try!{
PrimitiveShader::new("ps_radial_gradient",
&mut device,
if options.enable_dithering {
&dithering_feature
} else {
&[]
},
options.precache_shaders)
};
let ps_hw_composite = try!{
LazilyCompiledShader::new(ShaderKind::Primitive,
"ps_hardware_composite",
@ -2215,7 +2225,7 @@ impl Renderer {
.workers
.take()
.unwrap_or_else(|| {
let worker_config = ThreadPoolConfig::new()
let worker = ThreadPoolBuilder::new()
.thread_name(|idx|{ format!("WRWorker#{}", idx) })
.start_handler(move |idx| {
register_thread_with_profiler(format!("WRWorker#{}", idx));
@ -2227,8 +2237,9 @@ impl Renderer {
if let Some(ref thread_listener) = *thread_listener_for_rayon_end {
thread_listener.thread_stopped(&format!("WRWorker#{}", idx));
}
});
Arc::new(ThreadPool::new(worker_config).unwrap())
})
.build();
Arc::new(worker.unwrap())
});
let enable_render_on_scroll = options.enable_render_on_scroll;
@ -2290,18 +2301,18 @@ impl Renderer {
brush_image,
brush_blend,
brush_mix_blend,
brush_yuv_image,
brush_radial_gradient,
cs_clip_rectangle,
cs_clip_border,
cs_clip_image,
ps_text_run,
ps_text_run_dual_source,
ps_image,
ps_yuv_image,
ps_border_corner,
ps_border_edge,
ps_gradient,
ps_angle_gradient,
ps_radial_gradient,
ps_hw_composite,
ps_split_composite,
debug: debug_renderer,
@ -2873,18 +2884,13 @@ impl Renderer {
}
}
// Re-use whatever targets possible from the pool, before
// they get changed/re-allocated by the rendered frames.
for doc_with_id in &mut active_documents {
self.prepare_tile_frame(&mut doc_with_id.1.frame);
}
#[cfg(feature = "replay")]
self.texture_resolver.external_images.extend(
self.owned_external_images.iter().map(|(key, value)| (*key, value.clone()))
);
for &mut (_, RenderedDocument { ref mut frame, .. }) in &mut active_documents {
frame.profile_counters.reset_targets();
self.prepare_gpu_cache(frame);
assert!(frame.gpu_cache_frame_id <= self.gpu_cache_frame_id);
@ -3257,6 +3263,29 @@ impl Renderer {
&mut self.renderer_errors,
);
}
BrushBatchKind::RadialGradient => {
self.brush_radial_gradient.bind(
&mut self.device,
key.blend_mode,
projection,
0,
&mut self.renderer_errors,
);
}
BrushBatchKind::YuvImage(image_buffer_kind, format, color_space) => {
let shader_index =
Renderer::get_yuv_shader_index(image_buffer_kind, format, color_space);
self.brush_yuv_image[shader_index]
.as_mut()
.expect("Unsupported YUV shader kind")
.bind(
&mut self.device,
key.blend_mode,
projection,
0,
&mut self.renderer_errors,
);
}
}
}
BatchKind::Transformable(transform_kind, batch_kind) => match batch_kind {
@ -3275,20 +3304,6 @@ impl Renderer {
&mut self.renderer_errors,
);
}
TransformBatchKind::YuvImage(image_buffer_kind, format, color_space) => {
let shader_index =
Renderer::get_yuv_shader_index(image_buffer_kind, format, color_space);
self.ps_yuv_image[shader_index]
.as_mut()
.expect("Unsupported YUV shader kind")
.bind(
&mut self.device,
transform_kind,
projection,
0,
&mut self.renderer_errors,
);
}
TransformBatchKind::BorderCorner => {
self.ps_border_corner.bind(
&mut self.device,
@ -3325,15 +3340,6 @@ impl Renderer {
&mut self.renderer_errors,
);
}
TransformBatchKind::RadialGradient => {
self.ps_radial_gradient.bind(
&mut self.device,
transform_kind,
projection,
0,
&mut self.renderer_errors,
);
}
},
};
@ -4250,47 +4256,52 @@ impl Renderer {
}
}
fn prepare_target_list<T: RenderTarget>(
fn allocate_target_texture<T: RenderTarget>(
&mut self,
list: &mut RenderTargetList<T>,
perfect_only: bool,
) {
counters: &mut FrameProfileCounters,
frame_id: FrameId,
) -> Option<ActiveTexture> {
debug_assert_ne!(list.max_size, DeviceUintSize::zero());
if list.targets.is_empty() {
return;
return None
}
let mut texture = if perfect_only {
debug_assert!(list.texture.is_none());
counters.targets_used.inc();
// First, try finding a perfect match
let selector = TargetSelector {
size: list.max_size,
num_layers: list.targets.len() as _,
format: list.format,
};
let index = self.texture_resolver.render_target_pool
let mut index = self.texture_resolver.render_target_pool
.iter()
.position(|texture| {
//TODO: re-use a part of a larger target, if available
selector == TargetSelector {
size: texture.get_dimensions(),
num_layers: texture.get_render_target_layer_count(),
format: texture.get_format(),
}
});
match index {
Some(pos) => self.texture_resolver.render_target_pool.swap_remove(pos),
None => return,
}
} else {
if list.texture.is_some() {
list.check_ready();
return
}
let index = self.texture_resolver.render_target_pool
// Next, try at least finding a matching format
if index.is_none() {
counters.targets_changed.inc();
index = self.texture_resolver.render_target_pool
.iter()
.position(|texture| texture.get_format() == list.format);
match index {
Some(pos) => self.texture_resolver.render_target_pool.swap_remove(pos),
None => self.device.create_texture(TextureTarget::Array, list.format),
.position(|texture| texture.get_format() == list.format && !texture.used_in_frame(frame_id));
}
let mut texture = match index {
Some(pos) => {
self.texture_resolver.render_target_pool.swap_remove(pos)
}
None => {
counters.targets_created.inc();
// finally, give up and create a new one
self.device.create_texture(TextureTarget::Array, list.format)
}
};
@ -4305,34 +4316,19 @@ impl Renderer {
list.targets.len() as _,
None,
);
list.texture = Some(texture);
list.check_ready();
}
fn prepare_tile_frame(&mut self, frame: &mut Frame) {
// Init textures and render targets to match this scene.
// First pass grabs all the perfectly matching targets from the pool.
for pass in &mut frame.passes {
if let RenderPassKind::OffScreen { ref mut alpha, ref mut color, .. } = pass.kind {
self.prepare_target_list(alpha, true);
self.prepare_target_list(color, true);
}
}
list.check_ready(&texture);
Some(ActiveTexture {
texture,
saved_index: list.saved_index.clone(),
is_shared: list.is_shared,
})
}
fn bind_frame_data(&mut self, frame: &mut Frame) {
let _timer = self.gpu_profile.start_timer(GPU_TAG_SETUP_DATA);
self.device.set_device_pixel_ratio(frame.device_pixel_ratio);
// Some of the textures are already assigned by `prepare_frame`.
// Now re-allocate the space for the rest of the target textures.
for pass in &mut frame.passes {
if let RenderPassKind::OffScreen { ref mut alpha, ref mut color, .. } = pass.kind {
self.prepare_target_list(alpha, false);
self.prepare_target_list(color, false);
}
}
self.node_data_texture.update(&mut self.device, &mut frame.node_data);
self.device.bind_texture(TextureSampler::ClipScrollNodes, &self.node_data_texture.texture);
@ -4424,8 +4420,8 @@ impl Renderer {
(None, None)
}
RenderPassKind::OffScreen { ref mut alpha, ref mut color, ref mut texture_cache } => {
alpha.check_ready();
color.check_ready();
let alpha_tex = self.allocate_target_texture(alpha, &mut frame.profile_counters, frame_id);
let color_tex = self.allocate_target_texture(color, &mut frame.profile_counters, frame_id);
// If this frame has already been drawn, then any texture
// cache targets have already been updated and can be
@ -4455,7 +4451,7 @@ impl Renderer {
);
self.draw_alpha_target(
(alpha.texture.as_ref().unwrap(), target_index as i32),
(&alpha_tex.as_ref().unwrap().texture, target_index as i32),
target,
alpha.max_size,
&projection,
@ -4477,7 +4473,7 @@ impl Renderer {
);
self.draw_color_target(
Some((color.texture.as_ref().unwrap(), target_index as i32)),
Some((&color_tex.as_ref().unwrap().texture, target_index as i32)),
target,
frame.inner_rect,
color.max_size,
@ -4490,29 +4486,23 @@ impl Renderer {
);
}
(alpha.texture.take(), color.texture.take())
(alpha_tex, color_tex)
}
};
//Note: the `end_pass` will make sure this texture is not recycled this frame
if let Some(ActiveTexture { ref texture, is_shared: true, .. }) = cur_alpha {
self.device
.bind_texture(TextureSampler::SharedCacheA8, texture);
}
self.texture_resolver.end_pass(
cur_alpha,
cur_color,
RenderPassIndex(pass_index),
);
// After completing the first pass, make the A8 target available as an
// input to any subsequent passes.
if pass_index == 0 {
if let Some(shared_alpha_texture) =
self.texture_resolver.resolve(&SourceTexture::CacheA8)
{
self.device
.bind_texture(TextureSampler::SharedCacheA8, shared_alpha_texture);
}
}
}
self.texture_resolver.end_frame(RenderPassIndex(frame.passes.len()));
self.texture_resolver.end_frame();
if let Some(framebuffer_size) = framebuffer_size {
self.draw_render_target_debug(framebuffer_size);
self.draw_texture_cache_debug(framebuffer_size);
@ -4750,6 +4740,7 @@ impl Renderer {
self.brush_line.deinit(&mut self.device);
self.brush_blend.deinit(&mut self.device);
self.brush_mix_blend.deinit(&mut self.device);
self.brush_radial_gradient.deinit(&mut self.device);
self.cs_clip_rectangle.deinit(&mut self.device);
self.cs_clip_image.deinit(&mut self.device);
self.cs_clip_border.deinit(&mut self.device);
@ -4765,7 +4756,7 @@ impl Renderer {
shader.deinit(&mut self.device);
}
}
for shader in self.ps_yuv_image {
for shader in self.brush_yuv_image {
if let Some(shader) = shader {
shader.deinit(&mut self.device);
}
@ -4777,7 +4768,6 @@ impl Renderer {
self.ps_border_edge.deinit(&mut self.device);
self.ps_gradient.deinit(&mut self.device);
self.ps_angle_gradient.deinit(&mut self.device);
self.ps_radial_gradient.deinit(&mut self.device);
self.ps_hw_composite.deinit(&mut self.device);
self.ps_split_composite.deinit(&mut self.device);
#[cfg(feature = "capture")]

Просмотреть файл

@ -197,10 +197,11 @@ impl FilterOpHelpers for FilterOp {
offset.x == 0.0 && offset.y == 0.0 && blur == 0.0
},
FilterOp::ColorMatrix(matrix) => {
matrix == [1.0, 0.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 0.0, 1.0, 0.0]
matrix == [1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
0.0, 0.0, 0.0, 0.0]
}
}
}

Просмотреть файл

@ -11,10 +11,10 @@ use clip::{ClipStore};
use clip_scroll_tree::{ClipScrollTree};
use device::{FrameId, Texture};
use gpu_cache::{GpuCache};
use gpu_types::{BlurDirection, BlurInstance, BrushInstance, ClipChainRectIndex};
use gpu_types::{BlurDirection, BlurInstance, BrushFlags, BrushInstance, ClipChainRectIndex};
use gpu_types::{ClipScrollNodeData, ClipScrollNodeIndex};
use gpu_types::{PrimitiveInstance};
use internal_types::{FastHashMap, RenderPassIndex, SourceTexture};
use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
use picture::{PictureKind};
use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveStore};
use prim_store::{BrushMaskKind, BrushKind, DeferredResolve, EdgeAaSegmentMask};
@ -137,8 +137,8 @@ pub struct RenderTargetList<T> {
pub format: ImageFormat,
pub max_size: DeviceUintSize,
pub targets: Vec<T>,
#[cfg_attr(feature = "serde", serde(skip))]
pub texture: Option<Texture>,
pub saved_index: Option<SavedTargetIndex>,
pub is_shared: bool,
}
impl<T: RenderTarget> RenderTargetList<T> {
@ -151,7 +151,8 @@ impl<T: RenderTarget> RenderTargetList<T> {
format,
max_size: DeviceUintSize::new(MIN_TARGET_SIZE, MIN_TARGET_SIZE),
targets: Vec::new(),
texture: None,
saved_index: None,
is_shared: false,
}
}
@ -161,7 +162,11 @@ impl<T: RenderTarget> RenderTargetList<T> {
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
deferred_resolves: &mut Vec<DeferredResolve>,
saved_index: Option<SavedTargetIndex>,
) {
debug_assert_eq!(None, self.saved_index);
self.saved_index = saved_index;
for target in &mut self.targets {
target.build(ctx, gpu_cache, render_tasks, deferred_resolves);
}
@ -214,9 +219,7 @@ impl<T: RenderTarget> RenderTargetList<T> {
self.targets.iter().any(|target| target.needs_depth())
}
pub fn check_ready(&self) {
match self.texture {
Some(ref t) => {
pub fn check_ready(&self, t: &Texture) {
assert_eq!(t.get_dimensions(), self.max_size);
assert_eq!(t.get_format(), self.format);
assert_eq!(t.get_render_target_layer_count(), self.targets.len());
@ -224,11 +227,6 @@ impl<T: RenderTarget> RenderTargetList<T> {
assert_eq!(t.has_depth(), t.get_rt_info().unwrap().has_depth);
assert_eq!(t.has_depth(), self.needs_depth());
}
None => {
assert!(self.targets.is_empty())
}
}
}
}
/// Frame output information for a given pipeline ID.
@ -589,6 +587,7 @@ impl RenderTarget for AlphaRenderTarget {
clip_task_address: RenderTaskAddress(0),
z: 0,
segment_index: 0,
brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
edge_flags: EdgeAaSegmentMask::empty(),
user_data: [0; 3],
};
@ -598,6 +597,8 @@ impl RenderTarget for AlphaRenderTarget {
BrushKind::Clear |
BrushKind::Picture |
BrushKind::Line { .. } |
BrushKind::YuvImage { .. } |
BrushKind::RadialGradient { .. } |
BrushKind::Image { .. } => {
unreachable!("bug: unexpected brush here");
}
@ -784,7 +785,6 @@ impl RenderPass {
render_tasks: &mut RenderTaskTree,
deferred_resolves: &mut Vec<DeferredResolve>,
clip_store: &ClipStore,
pass_index: RenderPassIndex,
) {
profile_scope!("RenderPass::build");
@ -792,7 +792,6 @@ impl RenderPass {
RenderPassKind::MainFramebuffer(ref mut target) => {
for &task_id in &self.tasks {
assert_eq!(render_tasks[task_id].target_kind(), RenderTargetKind::Color);
render_tasks[task_id].pass_index = Some(pass_index);
target.add_task(
task_id,
ctx,
@ -805,16 +804,38 @@ impl RenderPass {
target.build(ctx, gpu_cache, render_tasks, deferred_resolves);
}
RenderPassKind::OffScreen { ref mut color, ref mut alpha, ref mut texture_cache } => {
let is_shared_alpha = self.tasks.iter().any(|&task_id| {
match render_tasks[task_id].kind {
RenderTaskKind::CacheMask(..) => true,
_ => false,
}
});
let saved_color = if self.tasks.iter().any(|&task_id| {
let t = &render_tasks[task_id];
t.target_kind() == RenderTargetKind::Color && t.saved_index.is_some()
}) {
Some(render_tasks.save_target())
} else {
None
};
let saved_alpha = if self.tasks.iter().any(|&task_id| {
let t = &render_tasks[task_id];
t.target_kind() == RenderTargetKind::Alpha && t.saved_index.is_some()
}) {
Some(render_tasks.save_target())
} else {
None
};
// Step through each task, adding to batches as appropriate.
for &task_id in &self.tasks {
let (target_kind, texture_target) = {
let task = &mut render_tasks[task_id];
task.pass_index = Some(pass_index);
let target_kind = task.target_kind();
// Find a target to assign this task to, or create a new
// one if required.
match task.location {
let (target_kind, texture_target) = match task.location {
RenderTaskLocation::TextureCache(texture_id, layer, _) => {
// TODO(gw): When we support caching color items, we will
// need to calculate that here to get the
@ -834,7 +855,18 @@ impl RenderPass {
(target_kind, None)
}
};
// Replace the pending saved index with a real one
if let Some(index) = task.saved_index {
assert_eq!(index, SavedTargetIndex::PENDING);
task.saved_index = match target_kind {
RenderTargetKind::Color => saved_color,
RenderTargetKind::Alpha => saved_alpha,
};
}
(target_kind, texture_target)
};
match texture_target {
@ -869,8 +901,9 @@ impl RenderPass {
}
}
color.build(ctx, gpu_cache, render_tasks, deferred_resolves);
alpha.build(ctx, gpu_cache, render_tasks, deferred_resolves);
color.build(ctx, gpu_cache, render_tasks, deferred_resolves, saved_color);
alpha.build(ctx, gpu_cache, render_tasks, deferred_resolves, saved_alpha);
alpha.is_shared = is_shared_alpha;
}
}
}

Просмотреть файл

@ -62,10 +62,6 @@ const SHADERS: &[Shader] = &[
name: "ps_angle_gradient",
features: PRIM_FEATURES,
},
Shader {
name: "ps_radial_gradient",
features: PRIM_FEATURES,
},
Shader {
name: "ps_hardware_composite",
features: PRIM_FEATURES,
@ -78,15 +74,15 @@ const SHADERS: &[Shader] = &[
name: "ps_image",
features: PRIM_FEATURES,
},
Shader {
name: "ps_yuv_image",
features: PRIM_FEATURES,
},
Shader {
name: "ps_text_run",
features: PRIM_FEATURES,
},
// Brush shaders
Shader {
name: "brush_yuv_image",
features: &["", "YUV_NV12", "YUV_PLANAR", "YUV_INTERLEAVED"],
},
Shader {
name: "brush_mask",
features: &[],
@ -111,6 +107,10 @@ const SHADERS: &[Shader] = &[
name: "brush_line",
features: &[],
},
Shader {
name: "brush_radial_gradient",
features: &[],
},
];
const VERSION_STRING: &str = "#version 300 es\n";

Просмотреть файл

@ -9,7 +9,7 @@ use std::fmt;
use std::marker::PhantomData;
use std::path::PathBuf;
use std::u32;
use {BuiltDisplayList, BuiltDisplayListDescriptor, ClipId, ColorF, DeviceIntPoint, DeviceUintRect};
use {BuiltDisplayList, BuiltDisplayListDescriptor, ColorF, DeviceIntPoint, DeviceUintRect};
use {DeviceUintSize, ExternalScrollId, FontInstanceKey, FontInstanceOptions};
use {FontInstancePlatformOptions, FontKey, FontVariation, GlyphDimensions, GlyphKey, ImageData};
use {ImageDescriptor, ImageKey, ItemTag, LayoutPoint, LayoutSize, LayoutTransform, LayoutVector2D};
@ -254,7 +254,7 @@ impl Transaction {
pub fn scroll_node_with_id(
&mut self,
origin: LayoutPoint,
id: ScrollNodeIdType,
id: ExternalScrollId,
clamp: ScrollClamping,
) {
self.ops.push(DocumentMsg::ScrollNodeWithId(origin, id, clamp));
@ -384,37 +384,13 @@ pub enum DocumentMsg {
device_pixel_ratio: f32,
},
Scroll(ScrollLocation, WorldPoint, ScrollEventPhase),
ScrollNodeWithId(LayoutPoint, ScrollNodeIdType, ScrollClamping),
ScrollNodeWithId(LayoutPoint, ExternalScrollId, ScrollClamping),
TickScrollingBounce,
GetScrollNodeState(MsgSender<Vec<ScrollNodeState>>),
GenerateFrame,
UpdateDynamicProperties(DynamicProperties),
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum ScrollNodeIdType {
ExternalScrollId(ExternalScrollId),
ClipId(ClipId),
}
impl From<ExternalScrollId> for ScrollNodeIdType {
fn from(id: ExternalScrollId) -> Self {
ScrollNodeIdType::ExternalScrollId(id)
}
}
impl From<ClipId> for ScrollNodeIdType {
fn from(id: ClipId) -> Self {
ScrollNodeIdType::ClipId(id)
}
}
impl<'a> From<&'a ClipId> for ScrollNodeIdType {
fn from(id: &'a ClipId) -> Self {
ScrollNodeIdType::ClipId(*id)
}
}
impl fmt::Debug for DocumentMsg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match *self {

Просмотреть файл

@ -601,9 +601,9 @@ impl YuvFormat {
pub fn get_feature_string(&self) -> &'static str {
match *self {
YuvFormat::NV12 => "NV12",
YuvFormat::PlanarYCbCr => "",
YuvFormat::InterleavedYCbCr => "INTERLEAVED_Y_CB_CR",
YuvFormat::NV12 => "YUV_NV12",
YuvFormat::PlanarYCbCr => "YUV_PLANAR",
YuvFormat::InterleavedYCbCr => "YUV_INTERLEAVED",
}
}
}
@ -649,6 +649,22 @@ impl LocalClip {
),
}
}
pub fn clip_by(&self, rect: &LayoutRect) -> LocalClip {
match *self {
LocalClip::Rect(clip_rect) => {
LocalClip::Rect(
clip_rect.intersection(rect).unwrap_or(LayoutRect::zero())
)
}
LocalClip::RoundedRect(clip_rect, complex) => {
LocalClip::RoundedRect(
clip_rect.intersection(rect).unwrap_or(LayoutRect::zero()),
complex,
)
}
}
}
}
#[repr(C)]

Просмотреть файл

@ -5,7 +5,7 @@ authors = ["The Mozilla Project Developers"]
license = "MPL-2.0"
[dependencies]
rayon = "0.8"
rayon = "1"
thread_profiler = "0.1.1"
euclid = "0.16"
app_units = "0.6"

Просмотреть файл

@ -1 +1 @@
4af31b8aa79d5a1f3c0242dea6be4876c71075c5
e8d2ffb404a85651fe08a6d09abbece9bd2b9182

Просмотреть файл

@ -672,7 +672,7 @@ pub struct WrThreadPool(Arc<rayon::ThreadPool>);
#[no_mangle]
pub unsafe extern "C" fn wr_thread_pool_new() -> *mut WrThreadPool {
let worker_config = rayon::Configuration::new()
let worker = rayon::ThreadPoolBuilder::new()
.thread_name(|idx|{ format!("WRWorker#{}", idx) })
.start_handler(|idx| {
let name = format!("WRWorker#{}", idx);
@ -681,9 +681,10 @@ pub unsafe extern "C" fn wr_thread_pool_new() -> *mut WrThreadPool {
})
.exit_handler(|_idx| {
gecko_profiler_unregister_thread();
});
})
.build();
let workers = Arc::new(rayon::ThreadPool::new(worker_config).unwrap());
let workers = Arc::new(worker.unwrap());
Box::into_raw(Box::new(WrThreadPool(workers)))
}
@ -1007,7 +1008,7 @@ pub extern "C" fn wr_transaction_scroll_layer(
new_scroll_origin: LayoutPoint
) {
assert!(unsafe { is_in_compositor_thread() });
let scroll_id = ScrollNodeIdType::from(ExternalScrollId(scroll_id, pipeline_id));
let scroll_id = ExternalScrollId(scroll_id, pipeline_id);
txn.scroll_node_with_id(new_scroll_origin, scroll_id, ScrollClamping::NoClamping);
}

Просмотреть файл

@ -502,7 +502,7 @@ impl Wrench {
&mut self,
frame_number: u32,
display_lists: Vec<(PipelineId, LayerSize, BuiltDisplayList)>,
scroll_offsets: &HashMap<ClipId, LayerPoint>,
scroll_offsets: &HashMap<ExternalScrollId, LayerPoint>,
) {
let root_background_color = Some(ColorF::new(1.0, 1.0, 1.0, 1.0));
@ -523,7 +523,7 @@ impl Wrench {
let mut txn = Transaction::new();
for (id, offset) in scroll_offsets {
txn.scroll_node_with_id(*offset, id.into(), ScrollClamping::NoClamping);
txn.scroll_node_with_id(*offset, *id, ScrollClamping::NoClamping);
}
// TODO(nical) - Wrench does not notify frames when there was scrolling
// in the transaction (See RenderNotifier implementations). If we don't

Просмотреть файл

@ -193,7 +193,7 @@ pub struct YamlFrameReader {
/// A HashMap of offsets which specify what scroll offsets particular
/// scroll layers should be initialized with.
scroll_offsets: HashMap<ClipId, LayerPoint>,
scroll_offsets: HashMap<ExternalScrollId, LayerPoint>,
image_map: HashMap<(PathBuf, Option<i64>), (ImageKey, LayoutSize)>,
@ -1273,7 +1273,12 @@ impl YamlFrameReader {
wrench: &mut Wrench,
yaml: &Yaml,
) {
let full_clip = LayoutRect::new(LayoutPoint::zero(), wrench.window_size_f32());
// A very large number (but safely far away from finite limits of f32)
let big_number = 1.0e30;
// A rect that should in practical terms serve as a no-op for clipping
let full_clip = LayoutRect::new(
LayoutPoint::new(-big_number / 2.0, -big_number / 2.0),
LayoutSize::new(big_number, big_number));
for item in yaml.as_vec().unwrap() {
// an explicit type can be skipped with some shorthand
@ -1362,7 +1367,12 @@ impl YamlFrameReader {
let complex_clips = self.to_complex_clip_regions(&yaml["complex"]);
let image_mask = self.to_image_mask(&yaml["image-mask"], wrench);
let external_id = numeric_id.map(|id| ExternalScrollId(id as u64, dl.pipeline_id));
let external_id = yaml["scroll-offset"].as_point().map(|size| {
let id = ExternalScrollId((self.scroll_offsets.len() + 1) as u64, dl.pipeline_id);
self.scroll_offsets.insert(id, LayerPoint::new(size.x, size.y));
id
});
let real_id = dl.define_scroll_frame(
external_id,
content_rect,
@ -1375,10 +1385,6 @@ impl YamlFrameReader {
self.clip_id_map.insert(numeric_id, real_id);
}
if let Some(size) = yaml["scroll-offset"].as_point() {
self.scroll_offsets.insert(real_id, LayerPoint::new(size.x, size.y));
}
if !yaml["items"].is_badvalue() {
dl.push_clip_id(real_id);
self.add_display_list_items_from_yaml(dl, wrench, &yaml["items"]);
@ -1542,9 +1548,8 @@ impl YamlFrameReader {
if is_root {
if let Some(size) = yaml["scroll-offset"].as_point() {
let id = ClipId::root_scroll_node(dl.pipeline_id);
self.scroll_offsets
.insert(id, LayerPoint::new(size.x, size.y));
let external_id = ExternalScrollId(0, dl.pipeline_id);
self.scroll_offsets.insert(external_id, LayerPoint::new(size.x, size.y));
}
}

Просмотреть файл

@ -0,0 +1,18 @@
<!DOCTYPE html>
<div id="host">
<div slot="slot1">content</div>
</div>
<script>
// NOTE(emilio): the failure mode for this crashtest is asserting whenever the
// shell goes away.
let shadowRoot = document.querySelector('#host').attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<div id="slot1-container">
<slot id="slot1" name="slot1"></slot>
<button id="button1">Click here</button>
</div>
`;
document.body.offsetTop;
shadowRoot.querySelector('#slot1-container').remove();
</script>

Просмотреть файл

@ -524,3 +524,4 @@ pref(dom.webcomponents.shadowdom.enabled,true) load 1429088.html
load 1429961.html
load 1435015.html
load 1429962.html
pref(dom.webcomponents.shadowdom.enabled,true) load 1439016.html

Просмотреть файл

@ -126,6 +126,8 @@ public:
nsPresContext* mPresContext;
};
#ifdef MOZ_OLD_STYLE
namespace {
class CharSetChangingRunnable : public Runnable
@ -152,6 +154,8 @@ private:
} // namespace
#endif
nscolor
nsPresContext::MakeColorPref(const nsString& aColor)
{
@ -1162,9 +1166,16 @@ nsPresContext::UpdateCharSet(NotNull<const Encoding*> aCharSet)
void
nsPresContext::DispatchCharSetChange(NotNull<const Encoding*> aEncoding)
{
#ifdef MOZ_OLD_STYLE
if (!Document()->IsStyledByServo()) {
RefPtr<CharSetChangingRunnable> runnable =
new CharSetChangingRunnable(this, aEncoding);
Document()->Dispatch(TaskCategory::Other, runnable.forget());
return;
}
#endif
// In Servo RebuildAllStyleData is async, so no need to do the runnable dance.
DoChangeCharSet(aEncoding);
}
nsPresContext*

Просмотреть файл

@ -23,12 +23,12 @@ fuzzy-if(skiaContent,1,342) == percent-2.html percent-2-ref.html
fuzzy-if(skiaContent,1,343) == percent-3.html percent-3-ref.html
# more serious tests, using SVG reference
fuzzy-if(skiaContent,17,58) fuzzy-if(webrender,41-41,66-66) == border-circle-2.html border-circle-2-ref.xhtml
fuzzy-if(gtkWidget,14,280) fuzzy-if(cocoaWidget,4,582) fuzzy-if(Android,36,264) fuzzy-if(d2d,51,323) fuzzy-if(winWidget&&!d2d,16,377) fuzzy-if(skiaContent,63,398) fuzzy-if(webrender,72-72,906-906) == curved-stripe-border.html curved-stripe-border-ref.svg # bug 459945
fuzzy-if(skiaContent,17,58) fuzzy-if(webrender,42-42,66-66) == border-circle-2.html border-circle-2-ref.xhtml
fuzzy-if(gtkWidget,14,280) fuzzy-if(cocoaWidget,4,582) fuzzy-if(Android,36,264) fuzzy-if(d2d,51,323) fuzzy-if(winWidget&&!d2d,16,377) fuzzy-if(skiaContent,63,398) fuzzy-if(webrender,64-64,897-897) == curved-stripe-border.html curved-stripe-border-ref.svg # bug 459945
# Corners
fuzzy-if(skiaContent,17,47) fuzzy-if(webrender,41-41,52-52) == corner-1.html corner-1-ref.svg # bottom corners different radius than top corners
fuzzy-if(gtkWidget,23,5) fuzzy-if(winWidget&&!d2d,23,5) fuzzy-if(d2d,32,8) fuzzy-if(Android,10,8) fuzzy-if(skiaContent,18,49) fuzzy-if(webrender,41-41,51-51) == corner-2.html corner-2-ref.svg # right corners different radius than left corners; see bug 500804
fuzzy-if(skiaContent,17,47) fuzzy-if(webrender,42-42,52-52) == corner-1.html corner-1-ref.svg # bottom corners different radius than top corners
fuzzy-if(gtkWidget,23,5) fuzzy-if(winWidget&&!d2d,23,5) fuzzy-if(d2d,32,8) fuzzy-if(Android,10,8) fuzzy-if(skiaContent,18,49) fuzzy-if(webrender,42-42,51-51) == corner-2.html corner-2-ref.svg # right corners different radius than left corners; see bug 500804
fuzzy-if(gtkWidget,3,10) fuzzy-if(winWidget&&!d2d,3,10) fuzzy-if(d2d,15,32) fuzzy-if(Android,3,15) fuzzy-if(skiaContent,18,90) fails-if(webrender) == corner-3.html corner-3-ref.svg
fuzzy-if(skiaContent,12,83) fuzzy-if(webrender,19-19,96-96) == corner-4.html corner-4-ref.svg
@ -39,7 +39,7 @@ fuzzy-if(skiaContent,12,83) fuzzy-if(webrender,19-19,96-96) == corner-4.html cor
fails == clipping-1.html clipping-1-ref.html # background color should completely fill box; bug 466572
!= clipping-2.html about:blank # background color clipped to inner/outer border, can't get
# great tests for this due to antialiasing problems described in bug 466572
fuzzy-if(skiaContent,17,62) fuzzy-if(webrender,41-41,66-66) == clipping-3.html clipping-3-ref.xhtml # edge of border-radius clips an underlying object's background
fuzzy-if(skiaContent,17,62) fuzzy-if(webrender,42-42,66-66) == clipping-3.html clipping-3-ref.xhtml # edge of border-radius clips an underlying object's background
# Tests for clipping the contents of replaced elements and overflow!=visible
!= clipping-4-ref.html clipping-4-notref.html

Просмотреть файл

@ -14,7 +14,7 @@ fails-if(Android) == boxshadow-button.html boxshadow-button-ref.html
fuzzy-if(OSX==1010,1,24) fuzzy-if(d2d,16,908) fuzzy-if(webrender,47-47,1080-1080) == boxshadow-large-border-radius.html boxshadow-large-border-radius-ref.html # Bug 1209649
fails-if(Android) == boxshadow-fileupload.html boxshadow-fileupload-ref.html
fuzzy-if(skiaContent,13,28) fuzzy-if(webrender,25,48) == boxshadow-inner-basic.html boxshadow-inner-basic-ref.svg
fuzzy-if(skiaContent,13,28) fuzzy-if(webrender,25,49) == boxshadow-inner-basic.html boxshadow-inner-basic-ref.svg
random-if(layersGPUAccelerated) == boxshadow-mixed.html boxshadow-mixed-ref.html
== boxshadow-mixed-2.html boxshadow-mixed-2-ref.html
random-if(d2d) fuzzy-if(skiaContent,1,100) fuzzy-if(webrender,127,3528) == boxshadow-rounded-spread.html boxshadow-rounded-spread-ref.html
@ -40,7 +40,7 @@ fuzzy(13,9445) fuzzy-if(d2d,13,10926) fails-if(webrender) == boxshadow-inset-lar
== overflow-not-scrollable-2.html overflow-not-scrollable-2-ref.html
fuzzy-if(webrender,1,655) == 611574-1.html 611574-1-ref.html
fuzzy-if(webrender,4,144) == 611574-2.html 611574-2-ref.html
fuzzy-if(winWidget,5,30) fuzzy-if(skiaContent,16,10) fuzzy-if(webrender,51-51,80-80) == fieldset.html fieldset-ref.html # minor anti-aliasing problem on Windows
fuzzy-if(winWidget,5,30) fuzzy-if(skiaContent,16,10) fuzzy-if(webrender,53-53,80-80) == fieldset.html fieldset-ref.html # minor anti-aliasing problem on Windows
fuzzy-if(winWidget,5,30) fuzzy-if(skiaContent,16,10) fails-if(webrender) == fieldset-inset.html fieldset-inset-ref.html # minor anti-aliasing problem on Windows
== 1178575.html 1178575-ref.html
== 1178575-2.html 1178575-2-ref.html

Просмотреть файл

@ -46,8 +46,8 @@ fuzzy-if(Android,8,771) == radial-shape-farthest-corner-1a.html radial-shape-far
fails-if(gtkWidget&&/x86_64-/.test(xulRuntime.XPCOMABI)) fuzzy(1,1569) fuzzy-if(cocoaWidget,2,41281) fuzzy-if(Android,8,1091) fuzzy-if(skiaContent,2,500) == radial-shape-farthest-corner-1b.html radial-shape-farthest-corner-1-ref.html
fuzzy-if(Android,17,13320) == radial-shape-farthest-side-1a.html radial-shape-farthest-side-1-ref.html
fuzzy-if(Android,17,13320) == radial-shape-farthest-side-1b.html radial-shape-farthest-side-1-ref.html
fuzzy-if(webrender,1-1,4-4) == radial-size-1a.html radial-size-1-ref.html
fuzzy-if(webrender,1-1,4-4) == radial-size-1b.html radial-size-1-ref.html
== radial-size-1a.html radial-size-1-ref.html
== radial-size-1b.html radial-size-1-ref.html
fuzzy-if(Android,4,248) == radial-zero-length-1a.html radial-zero-length-1-ref.html
fuzzy-if(Android,4,248) == radial-zero-length-1b.html radial-zero-length-1-ref.html
fuzzy-if(Android,4,248) == radial-zero-length-1c.html radial-zero-length-1-ref.html

Просмотреть файл

@ -21,13 +21,13 @@ fuzzy(11,4) == input-type-invalid.html input-ref.html
== input-disabled-fieldset-1.html input-fieldset-ref.html
== input-disabled-fieldset-2.html input-fieldset-ref.html
== input-fieldset-legend.html input-fieldset-legend-ref.html
== input-radio-required.html input-radio-ref.html
fuzzy-if(skiaContent,2,10) == input-radio-customerror.html input-radio-ref.html
fuzzy-if(skiaContent,2,10) == input-radio-dyn-valid-1.html input-radio-checked-ref.html
fuzzy-if(skiaContent,2,10) == input-radio-dyn-valid-2.html input-radio-ref.html
fuzzy-if(skiaContent,2,10) == input-radio-nogroup-required-valid.html input-radio-ref.html
fuzzy-if(skiaContent,2,10) == input-radio-nogroup-required-invalid.html input-radio-checked-ref.html
fuzzy-if(skiaContent,2,10) == input-radio-focus-click.html input-radio-ref.html
fuzzy-if(webrender,0-14,0-8) == input-radio-required.html input-radio-ref.html
fuzzy-if(skiaContent,2,10) fuzzy-if(webrender,0-14,0-8) == input-radio-customerror.html input-radio-ref.html
fuzzy-if(skiaContent,2,10) fuzzy-if(webrender,0-14,0-8) == input-radio-dyn-valid-1.html input-radio-checked-ref.html
fuzzy-if(skiaContent,2,10) fuzzy-if(webrender,0-14,0-8) == input-radio-dyn-valid-2.html input-radio-ref.html
fuzzy-if(skiaContent,2,10) fuzzy-if(webrender,0-14,0-8) == input-radio-nogroup-required-valid.html input-radio-ref.html
fuzzy-if(skiaContent,2,10) fuzzy-if(webrender,0-14,0-8) == input-radio-nogroup-required-invalid.html input-radio-checked-ref.html
fuzzy-if(skiaContent,2,10) fuzzy-if(webrender,0-14,0-8) == input-radio-focus-click.html input-radio-ref.html
== input-submit.html input-submit-ref.html
== input-image.html input-image-ref.html
# input type='hidden' shouldn't show

Просмотреть файл

@ -3,7 +3,7 @@
== menuitem-key.xul menuitem-key-ref.xul
# these random-if(Android) are due to differences between Android Native & Xul, see bug 732569
random-if(Android) == menulist-shrinkwrap-1.xul menulist-shrinkwrap-1-ref.xul
random-if(Android) fails-if(winWidget) == menulist-shrinkwrap-2.xul menulist-shrinkwrap-2-ref.xul
random-if(Android) == menulist-shrinkwrap-2.xul menulist-shrinkwrap-2-ref.xul
== textbox-overflow-1.xul textbox-overflow-1-ref.xul # for bug 749658
# accesskeys are not normally displayed on Mac, so skip this test
skip-if(cocoaWidget) == accesskey.xul accesskey-ref.xul

Просмотреть файл

@ -2303,7 +2303,8 @@ arena_run_reg_dalloc(arena_run_t* run, arena_bin_t* bin, void* ptr, size_t size)
run->mRegionsMinElement = elm;
}
bit = regind - (elm << (LOG2(sizeof(int)) + 3));
MOZ_DIAGNOSTIC_ASSERT((run->mRegionsMask[elm] & (1U << bit)) == 0);
MOZ_RELEASE_ASSERT((run->mRegionsMask[elm] & (1U << bit)) == 0,
"Double-free?");
run->mRegionsMask[elm] |= (1U << bit);
#undef SIZE_INV
#undef SIZE_INV_SHIFT
@ -3497,7 +3498,7 @@ arena_dalloc(void* aPtr, size_t aOffset, arena_t* aArena)
MutexAutoLock lock(arena->mLock);
size_t pageind = aOffset >> gPageSize2Pow;
arena_chunk_map_t* mapelm = &chunk->map[pageind];
MOZ_DIAGNOSTIC_ASSERT((mapelm->bits & CHUNK_MAP_ALLOCATED) != 0);
MOZ_RELEASE_ASSERT((mapelm->bits & CHUNK_MAP_ALLOCATED) != 0, "Double-free?");
if ((mapelm->bits & CHUNK_MAP_LARGE) == 0) {
// Small allocation.
arena->DallocSmall(chunk, aPtr, mapelm);
@ -3896,7 +3897,7 @@ huge_dalloc(void* aPtr, arena_t* aArena)
// Extract from tree of huge allocations.
key.mAddr = aPtr;
node = huge.Search(&key);
MOZ_ASSERT(node);
MOZ_RELEASE_ASSERT(node, "Double-free?");
MOZ_ASSERT(node->mAddr == aPtr);
MOZ_RELEASE_ASSERT(!aArena || node->mArena == aArena);
huge.Remove(node);

Просмотреть файл

@ -6,10 +6,8 @@
this.EXPORTED_SYMBOLS = ["Accounts"];
ChromeUtils.import("resource://gre/modules/Deprecated.jsm"); /* global Deprecated */
ChromeUtils.import("resource://gre/modules/Messaging.jsm"); /* global Messaging */
ChromeUtils.import("resource://gre/modules/Promise.jsm"); /* global Promise */
ChromeUtils.import("resource://gre/modules/Services.jsm"); /* global Services */
ChromeUtils.import("resource://gre/modules/Messaging.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
/**
* A promise-based API for querying the existence of Sync accounts,
@ -42,13 +40,6 @@ var Accounts = Object.freeze({
return this._accountsExist("fxa");
},
syncAccountsExist: function() {
Deprecated.warning("The legacy Sync account type has been removed from Firefox for Android. " +
"Please use `firefoxAccountsExist` instead.",
"https://developer.mozilla.org/en-US/Add-ons/Firefox_for_Android/API/Accounts.jsm");
return Promise.resolve(false);
},
anySyncAccountsExist: function() {
return this._accountsExist("any");
},

Просмотреть файл

@ -9,7 +9,6 @@ this.EXPORTED_SYMBOLS = [ "HomeProvider" ];
ChromeUtils.import("resource://gre/modules/Messaging.jsm");
ChromeUtils.import("resource://gre/modules/osfile.jsm");
ChromeUtils.import("resource://gre/modules/Promise.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/Sqlite.jsm");
ChromeUtils.import("resource://gre/modules/Task.jsm");

Просмотреть файл

@ -16,16 +16,12 @@ Migrated from Robocop: https://bugzilla.mozilla.org/show_bug.cgi?id=1184186
ChromeUtils.import("resource://gre/modules/Accounts.jsm");
add_task(function* () {
let syncExists = yield Accounts.syncAccountsExist();
info("Sync account exists? " + syncExists + "\n");
let firefoxExists = yield Accounts.firefoxAccountsExist();
info("Firefox account exists? " + firefoxExists + "\n");
let anyExists = yield Accounts.anySyncAccountsExist();
info("Any accounts exist? " + anyExists + "\n");
// Only one account should exist.
ok(!syncExists || !firefoxExists, "at least one account does not exist");
is(anyExists, firefoxExists || syncExists, "sync/firefox account existence consistent with any existence");
is(anyExists, firefoxExists, "sync/firefox account existence consistent with any existence");
// TODO: How can this be cleaned up?
// info("Launching setup.\n");

Просмотреть файл

@ -497,10 +497,13 @@ pref("media.getusermedia.browser.enabled", false);
pref("media.getusermedia.channels", 0);
#if defined(ANDROID)
pref("media.getusermedia.camera.off_while_disabled.enabled", false);
pref("media.getusermedia.microphone.off_while_disabled.enabled", false);
#else
pref("media.getusermedia.camera.off_while_disabled.enabled", true);
pref("media.getusermedia.microphone.off_while_disabled.enabled", true);
#endif
pref("media.getusermedia.camera.off_while_disabled.delay_ms", 3000);
pref("media.getusermedia.microphone.off_while_disabled.delay_ms", 3000);
// Desktop is typically VGA capture or more; and qm_select will not drop resolution
// below 1/2 in each dimension (or so), so QVGA (320x200) is the lowest here usually.
pref("media.peerconnection.video.min_bitrate", 0);
@ -5863,12 +5866,8 @@ pref("layout.css.servo.enabled", false);
// If Stylo is not enabled, this pref doesn't take any effect.
// Note that this pref is only read once when requested. Changing it
// at runtime may have no effect.
#ifdef MOZ_OLD_STYLE
pref("layout.css.servo.chrome.enabled", false);
#else
pref("layout.css.servo.chrome.enabled", true);
#endif
#endif
// TODO: Bug 1324406: Treat 'data:' documents as unique, opaque origins
// If true, data: URIs will be treated as unique opaque origins, hence will use

Просмотреть файл

@ -122,8 +122,7 @@ NS_IMETHODIMP
TRR::Run()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mTRRService);
if (NS_FAILED(SendHTTPRequest())) {
if ((gTRRService == nullptr) || NS_FAILED(SendHTTPRequest())) {
FailData();
// The dtor will now be run
}
@ -146,7 +145,7 @@ TRR::SendHTTPRequest()
nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv));
NS_ENSURE_SUCCESS(rv, rv);
bool useGet = mTRRService->UseGET();
bool useGet = gTRRService->UseGET();
nsAutoCString body;
nsCOMPtr<nsIURI> dnsURI;
@ -164,7 +163,7 @@ TRR::SendHTTPRequest()
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString uri;
mTRRService->GetURI(uri);
gTRRService->GetURI(uri);
uri.Append(NS_LITERAL_CSTRING("?ct&dns="));
uri.Append(body);
rv = NS_NewURI(getter_AddRefs(dnsURI), uri);
@ -173,7 +172,7 @@ TRR::SendHTTPRequest()
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString uri;
mTRRService->GetURI(uri);
gTRRService->GetURI(uri);
rv = NS_NewURI(getter_AddRefs(dnsURI), uri);
}
if (NS_FAILED(rv)) {
@ -206,7 +205,7 @@ TRR::SendHTTPRequest()
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString cred;
mTRRService->GetCredentials(cred);
gTRRService->GetCredentials(cred);
if (!cred.IsEmpty()){
rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Authorization"), cred, false);
NS_ENSURE_SUCCESS(rv, rv);
@ -224,6 +223,8 @@ TRR::SendHTTPRequest()
rv = internalChannel->SetTrr(true);
NS_ENSURE_SUCCESS(rv, rv);
mAllowRFC1918 = gTRRService->AllowRFC1918();
if (useGet) {
rv = httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("GET"));
NS_ENSURE_SUCCESS(rv, rv);
@ -252,7 +253,7 @@ TRR::SendHTTPRequest()
}
if (NS_SUCCEEDED(httpChannel->AsyncOpen2(this))) {
NS_NewTimerWithCallback(getter_AddRefs(mTimeout),
this, mTRRService->GetRequestTimeout(),
this, gTRRService->GetRequestTimeout(),
nsITimer::TYPE_ONE_SHOT);
return NS_OK;
}
@ -597,7 +598,7 @@ TRR::DohDecode()
return NS_ERROR_UNEXPECTED;
}
rv = mDNS.Add(TTL, mResponse, index, RDLENGTH,
mTRRService->AllowRFC1918());
mAllowRFC1918);
if (NS_FAILED(rv)) {
LOG(("TRR:DohDecode failed: local IP addresses or unknown IP family\n"));
return rv;
@ -609,7 +610,7 @@ TRR::DohDecode()
return NS_ERROR_UNEXPECTED;
}
rv = mDNS.Add(TTL, mResponse, index, RDLENGTH,
mTRRService->AllowRFC1918());
mAllowRFC1918);
if (NS_FAILED(rv)) {
LOG(("TRR got unique/local IPv6 address!\n"));
return rv;

Просмотреть файл

@ -73,11 +73,11 @@ public:
: mozilla::Runnable("TRR")
, mRec(aRec)
, mHostResolver(aResolver)
, mTRRService(gTRRService)
, mType(aType)
, mBodySize(0)
, mFailed(false)
, mCnameLoop(kCnameChaseMax)
, mAllowRFC1918(false)
{
mHost = aRec->host;
mPB = aRec->pb;
@ -94,12 +94,12 @@ public:
, mHost(aHost)
, mRec(aRec)
, mHostResolver(aResolver)
, mTRRService(gTRRService)
, mType(aType)
, mBodySize(0)
, mFailed(false)
, mPB(aPB)
, mCnameLoop(aLoopCount)
, mAllowRFC1918(false)
{
}
@ -108,11 +108,11 @@ public:
explicit TRR(AHostResolver *aResolver, bool aPB)
: mozilla::Runnable("TRR")
, mHostResolver(aResolver)
, mTRRService(gTRRService)
, mBodySize(0)
, mFailed(false)
, mPB(aPB)
, mCnameLoop(kCnameChaseMax)
, mAllowRFC1918(false)
{ }
// to verify a domain
@ -123,12 +123,12 @@ public:
: mozilla::Runnable("TRR")
, mHost(aHost)
, mHostResolver(aResolver)
, mTRRService(gTRRService)
, mType(aType)
, mBodySize(0)
, mFailed(false)
, mPB(aPB)
, mCnameLoop(kCnameChaseMax)
, mAllowRFC1918(false)
{ }
NS_IMETHOD Run() override;
@ -137,7 +137,6 @@ public:
nsCString mHost;
RefPtr<nsHostRecord> mRec;
RefPtr<AHostResolver> mHostResolver;
TRRService *mTRRService;
private:
~TRR() = default;
@ -162,6 +161,7 @@ private:
nsCOMPtr<nsITimer> mTimeout;
nsCString mCname;
uint32_t mCnameLoop; // loop detection counter
bool mAllowRFC1918;
};
} // namespace net

Просмотреть файл

@ -1493,14 +1493,20 @@ nsHostResolver::CompleteLookup(nsHostRecord* rec, nsresult status, AddrInfo* aNe
// or deleted on early return.
nsAutoPtr<AddrInfo> newRRSet(aNewRRSet);
bool trrResult = newRRSet && newRRSet->IsTRR();
if (rec->mResolveAgain && (status != NS_ERROR_ABORT) && !trrResult) {
LOG(("nsHostResolver record %p resolve again due to flushcache\n", rec));
rec->mResolveAgain = false;
return LOOKUP_RESOLVEAGAIN;
}
MOZ_ASSERT(rec->mResolving);
rec->mResolving--;
LOG(("nsHostResolver::CompleteLookup %s %p %X trr=%d stillResolving=%d\n",
rec->host.get(), aNewRRSet, (unsigned int)status,
aNewRRSet ? aNewRRSet->IsTRR() : 0, rec->mResolving));
bool trrResult = newRRSet && newRRSet->IsTRR();
if (trrResult) {
LOG(("TRR lookup Complete (%d) %s %s\n",
newRRSet->IsTRR(), newRRSet->mHostName,
@ -1584,12 +1590,6 @@ nsHostResolver::CompleteLookup(nsHostRecord* rec, nsresult status, AddrInfo* aNe
}
}
if (rec->mResolveAgain && (status != NS_ERROR_ABORT)) {
LOG(("nsHostResolver record %p resolve again due to flushcache\n", rec));
rec->mResolveAgain = false;
return LOOKUP_RESOLVEAGAIN;
}
// update record fields. We might have a rec->addr_info already if a
// previous lookup result expired and we're reresolving it or we get
// a late second TRR response.

Просмотреть файл

@ -117,7 +117,7 @@ function *testSteps() {
let startTime = Date.now();
channel.asyncOpen2(new ChannelListener(checkContent, null));
yield undefined;
greater(Date.now() - startTime, 200, "Check that timer works properly");
greaterOrEqual(Date.now() - startTime, 200, "Check that timer works properly");
equal(gResponseCounter, 3);
equal(g200Counter, 1, "check number of 200 responses");
equal(g304Counter, 2, "check number of 304 responses");

Просмотреть файл

@ -4,7 +4,6 @@
this.EXPORTED_SYMBOLS = [
"EngineManager",
"Engine",
"SyncEngine",
"Tracker",
"Store",
@ -314,7 +313,7 @@ Store.prototype = {
try {
await this.applyIncoming(record);
} catch (ex) {
if (ex.code == Engine.prototype.eEngineAbortApplyIncoming) {
if (ex.code == SyncEngine.prototype.eEngineAbortApplyIncoming) {
// This kind of exception should have a 'cause' attribute, which is an
// originating exception.
// ex.cause will carry its stack with it when rethrown.
@ -610,7 +609,7 @@ EngineManager.prototype = {
async unregister(val) {
let name = val;
if (val instanceof Engine) {
if (val instanceof SyncEngine) {
name = val.name;
}
if (name in this._engines) {
@ -629,9 +628,9 @@ EngineManager.prototype = {
},
};
this.Engine = function Engine(name, service) {
this.SyncEngine = function SyncEngine(name, service) {
if (!service) {
throw new Error("Engine must be associated with a Service instance.");
throw new Error("SyncEngine must be associated with a Service instance.");
}
this.Name = name || "Unnamed";
@ -646,124 +645,6 @@ this.Engine = function Engine(name, service) {
this._tracker; // initialize tracker to load previously changed IDs
this._log.debug("Engine constructed");
XPCOMUtils.defineLazyPreferenceGetter(this, "_enabled",
`services.sync.engine.${this.prefName}`, false,
(data, previous, latest) =>
// We do not await on the promise onEngineEnabledChanged returns.
this._tracker.onEngineEnabledChanged(latest));
};
Engine.prototype = {
// _storeObj, and _trackerObj should to be overridden in subclasses
_storeObj: Store,
_trackerObj: Tracker,
// Override this method to return a new changeset type.
emptyChangeset() {
return new Changeset();
},
// Local 'constant'.
// Signal to the engine that processing further records is pointless.
eEngineAbortApplyIncoming: "error.engine.abort.applyincoming",
// Should we keep syncing if we find a record that cannot be uploaded (ever)?
// If this is false, we'll throw, otherwise, we'll ignore the record and
// continue. This currently can only happen due to the record being larger
// than the record upload limit.
allowSkippedRecord: true,
get prefName() {
return this.name;
},
get enabled() {
return this._enabled;
},
set enabled(val) {
if (!!val != this._enabled) {
Svc.Prefs.set("engine." + this.prefName, !!val);
}
},
get score() {
return this._tracker.score;
},
get _store() {
let store = new this._storeObj(this.Name, this);
this.__defineGetter__("_store", () => store);
return store;
},
get _tracker() {
let tracker = new this._trackerObj(this.Name, this);
this.__defineGetter__("_tracker", () => tracker);
return tracker;
},
startTracking() {
this._tracker.start();
},
// Returns a promise
stopTracking() {
return this._tracker.stop();
},
async sync() {
if (!this.enabled) {
return false;
}
if (!this._sync) {
throw new Error("engine does not implement _sync method");
}
return this._notify("sync", this.name, this._sync)();
},
/**
* Get rid of any local meta-data.
*/
async resetClient() {
if (!this._resetClient) {
throw new Error("engine does not implement _resetClient method");
}
return this._notify("reset-client", this.name, this._resetClient)();
},
async _wipeClient() {
await this.resetClient();
this._log.debug("Deleting all local data");
this._tracker.ignoreAll = true;
await this._store.wipe();
this._tracker.ignoreAll = false;
await this._tracker.clearChangedIDs();
},
async wipeClient() {
return this._notify("wipe-client", this.name, this._wipeClient)();
},
/**
* If one exists, initialize and return a validator for this engine (which
* must have a `validate(engine)` method that returns a promise to an object
* with a getSummary method). Otherwise return null.
*/
getValidator() {
return null;
},
async finalize() {
await this._tracker.finalize();
},
};
this.SyncEngine = function SyncEngine(name, service) {
Engine.call(this, name || "SyncEngine", service);
this._toFetchStorage = new JSONFile({
path: Utils.jsonFilePath("toFetch/" + this.name),
dataPostProcessor: json => this._metadataPostProcessor(json),
@ -777,6 +658,11 @@ this.SyncEngine = function SyncEngine(name, service) {
});
Utils.defineLazyIDProperty(this, "syncID", `services.sync.${this.name}.syncID`);
XPCOMUtils.defineLazyPreferenceGetter(this, "_enabled",
`services.sync.engine.${this.prefName}`, false,
(data, previous, latest) =>
// We do not await on the promise onEngineEnabledChanged returns.
this._tracker.onEngineEnabledChanged(latest));
XPCOMUtils.defineLazyPreferenceGetter(this, "_lastSync",
`services.sync.${this.name}.lastSync`,
"0", null,
@ -825,10 +711,22 @@ SyncEngine.kRecoveryStrategy = {
};
SyncEngine.prototype = {
__proto__: Engine.prototype,
_recordObj: CryptoWrapper,
// _storeObj, and _trackerObj should to be overridden in subclasses
_storeObj: Store,
_trackerObj: Tracker,
version: 1,
// Local 'constant'.
// Signal to the engine that processing further records is pointless.
eEngineAbortApplyIncoming: "error.engine.abort.applyincoming",
// Should we keep syncing if we find a record that cannot be uploaded (ever)?
// If this is false, we'll throw, otherwise, we'll ignore the record and
// continue. This currently can only happen due to the record being larger
// than the record upload limit.
allowSkippedRecord: true,
// Which sortindex to use when retrieving records for this engine.
_defaultSort: undefined,
@ -875,6 +773,36 @@ SyncEngine.prototype = {
this._log.debug("SyncEngine initialized", this.name);
},
get prefName() {
return this.name;
},
get enabled() {
return this._enabled;
},
set enabled(val) {
if (!!val != this._enabled) {
Svc.Prefs.set("engine." + this.prefName, !!val);
}
},
get score() {
return this._tracker.score;
},
get _store() {
let store = new this._storeObj(this.Name, this);
this.__defineGetter__("_store", () => store);
return store;
},
get _tracker() {
let tracker = new this._trackerObj(this.Name, this);
this.__defineGetter__("_tracker", () => tracker);
return tracker;
},
get storageURL() {
return this.service.storageURL;
},
@ -891,6 +819,32 @@ SyncEngine.prototype = {
return this.storageURL + "meta/global";
},
startTracking() {
this._tracker.start();
},
// Returns a promise
stopTracking() {
return this._tracker.stop();
},
async sync() {
if (!this.enabled) {
return false;
}
if (!this._sync) {
throw new Error("engine does not implement _sync method");
}
return this._notify("sync", this.name, this._sync)();
},
// Override this method to return a new changeset type.
emptyChangeset() {
return new Changeset();
},
/*
* lastSync is a timestamp in server time.
*/
@ -1343,7 +1297,7 @@ SyncEngine.prototype = {
try {
shouldApply = await this._reconcile(item);
} catch (ex) {
if (ex.code == Engine.prototype.eEngineAbortApplyIncoming) {
if (ex.code == SyncEngine.prototype.eEngineAbortApplyIncoming) {
this._log.warn("Reconciliation failed: aborting incoming processing.");
throw ex.cause;
} else if (!Async.isShutdownException(ex)) {
@ -1922,8 +1876,41 @@ SyncEngine.prototype = {
}
},
/**
* Get rid of any local meta-data.
*/
async resetClient() {
if (!this._resetClient) {
throw new Error("engine does not implement _resetClient method");
}
return this._notify("reset-client", this.name, this._resetClient)();
},
async wipeClient() {
return this._notify("wipe-client", this.name, this._wipeClient)();
},
async _wipeClient() {
await this.resetClient();
this._log.debug("Deleting all local data");
this._tracker.ignoreAll = true;
await this._store.wipe();
this._tracker.ignoreAll = false;
await this._tracker.clearChangedIDs();
},
/**
* If one exists, initialize and return a validator for this engine (which
* must have a `validate(engine)` method that returns a promise to an object
* with a getSummary method). Otherwise return null.
*/
getValidator() {
return null;
},
async finalize() {
await super.finalize();
await this._tracker.finalize();
await this._toFetchStorage.finalize();
await this._previousFailedStorage.finalize();
},

Просмотреть файл

@ -565,7 +565,7 @@ BookmarksEngine.prototype = {
}
this._log.warn("Error while building GUID map, skipping all other incoming items", ex);
// eslint-disable-next-line no-throw-literal
throw {code: Engine.prototype.eEngineAbortApplyIncoming,
throw {code: SyncEngine.prototype.eEngineAbortApplyIncoming,
cause: ex};
}
},

Просмотреть файл

@ -542,7 +542,7 @@ add_task(async function test_bookmark_guidMap_fail() {
} catch (ex) {
err = ex;
}
Assert.equal(err.code, Engine.prototype.eEngineAbortApplyIncoming);
Assert.equal(err.code, SyncEngine.prototype.eEngineAbortApplyIncoming);
Assert.equal(err.cause, "Nooo");
_("We get an error and abort during processIncoming.");

Просмотреть файл

@ -17,7 +17,7 @@ function DummyEngine() {}
DummyEngine.prototype.name = "dummy";
function ActualEngine() {}
ActualEngine.prototype = {__proto__: Engine.prototype,
ActualEngine.prototype = {__proto__: SyncEngine.prototype,
name: "actual"};
function getEngineManager() {

Просмотреть файл

@ -29,12 +29,12 @@ SteamTracker.prototype = {
};
function SteamEngine(name, service) {
Engine.call(this, name, service);
SyncEngine.call(this, name, service);
this.wasReset = false;
this.wasSynced = false;
}
SteamEngine.prototype = {
__proto__: Engine.prototype,
__proto__: SyncEngine.prototype,
_storeObj: SteamStore,
_trackerObj: SteamTracker,

Просмотреть файл

@ -30,7 +30,7 @@ add_task(async function test_processIncoming_abort() {
syncID: engine.syncID}};
_("Fake applyIncoming to abort.");
engine._store.applyIncoming = async function(record) {
let ex = {code: Engine.prototype.eEngineAbortApplyIncoming,
let ex = {code: SyncEngine.prototype.eEngineAbortApplyIncoming,
cause: "Nooo"};
_("Throwing: " + JSON.stringify(ex));
throw ex;

Просмотреть файл

@ -16,9 +16,11 @@ function DummyEngine() {}
DummyEngine.prototype.name = "dummy";
DummyEngine.prototype.finalize = async function() {};
function ActualEngine() {}
ActualEngine.prototype = {__proto__: Engine.prototype,
name: "actual"};
class ActualEngine extends SyncEngine {
constructor(service) {
super("Actual", service);
}
}
add_task(async function test_basics() {
_("We start out with a clean slate");
@ -103,7 +105,7 @@ add_task(async function test_basics() {
await manager.register(ActualEngine);
let actual = await manager.get("actual");
Assert.ok(actual instanceof ActualEngine);
Assert.ok(actual instanceof Engine);
Assert.ok(actual instanceof SyncEngine);
await manager.unregister(actual);
Assert.equal((await manager.get("actual")), undefined);

Просмотреть файл

@ -36,11 +36,11 @@ SteamTracker.prototype = {
};
function SteamEngine(service) {
Engine.call(this, "steam", service);
SyncEngine.call(this, "steam", service);
}
SteamEngine.prototype = {
__proto__: Engine.prototype,
__proto__: SyncEngine.prototype,
_storeObj: SteamStore,
_trackerObj: SteamTracker,
_errToThrow: null,
@ -52,7 +52,7 @@ SteamEngine.prototype = {
};
function BogusEngine(service) {
Engine.call(this, "bogus", service);
SyncEngine.call(this, "bogus", service);
}
BogusEngine.prototype = Object.create(SteamEngine.prototype);

Просмотреть файл

@ -1009,15 +1009,16 @@ fn inner_text_collection_steps<N: LayoutNode>(node: N,
},
Display::TableRow if !is_last_table_row() => {
// Step 7.
items.push(InnerTextItem::Text(String::from("\u{000A}" /* line feed */)));
items.push(InnerTextItem::Text(String::from(
"\u{000A}", /* line feed */
)));
},
_ => (),
}
Display::Block | Display::Flex | Display::TableCaption | Display::Table => {
// Step 9.
if is_block_level_or_table_caption(&display) {
items.insert(0, InnerTextItem::RequiredLineBreakCount(1));
items.push(InnerTextItem::RequiredLineBreakCount(1));
},
_ => {},
}
}
@ -1033,11 +1034,3 @@ fn is_last_table_row() -> bool {
// FIXME(ferjm) Implement this.
false
}
fn is_block_level_or_table_caption(display: &Display) -> bool {
match *display {
Display::Block | Display::Flex |
Display::TableCaption | Display::Table => true,
_ => false,
}
}

Просмотреть файл

@ -478,8 +478,7 @@ fn append_text_node_to_fragment(
text: String
) {
let text = Text::new(DOMString::from(text), document);
let node = DomRoot::upcast::<Node>(text);
fragment.upcast::<Node>().AppendChild(&node).unwrap();
fragment.upcast::<Node>().AppendChild(&text.upcast()).unwrap();
}
// https://html.spec.whatwg.org/multipage/#attr-data-*

Просмотреть файл

@ -207,11 +207,13 @@ impl fmt::Debug for PropertyDeclarationBlock {
impl PropertyDeclarationBlock {
/// Returns the number of declarations in the block.
#[inline]
pub fn len(&self) -> usize {
self.declarations.len()
}
/// Create an empty block
#[inline]
pub fn new() -> Self {
PropertyDeclarationBlock {
declarations: Vec::new(),
@ -229,31 +231,36 @@ impl PropertyDeclarationBlock {
PropertyDeclarationBlock {
declarations: vec![declaration],
declarations_importance: SmallBitVec::from_elem(1, importance.important()),
longhands: longhands,
longhands,
}
}
/// The declarations in this block
#[inline]
pub fn declarations(&self) -> &[PropertyDeclaration] {
&self.declarations
}
/// The `important` flags for declarations in this block
#[inline]
pub fn declarations_importance(&self) -> &SmallBitVec {
&self.declarations_importance
}
/// Iterate over `(PropertyDeclaration, Importance)` pairs
#[inline]
pub fn declaration_importance_iter(&self) -> DeclarationImportanceIterator {
DeclarationImportanceIterator::new(&self.declarations, &self.declarations_importance)
}
/// Iterate over `PropertyDeclaration` for Importance::Normal
#[inline]
pub fn normal_declaration_iter(&self) -> NormalDeclarationIterator {
NormalDeclarationIterator::new(&self.declarations, &self.declarations_importance)
}
/// Return an iterator of (AnimatableLonghand, AnimationValue).
#[inline]
pub fn to_animation_value_iter<'a, 'cx, 'cx_a:'cx>(
&'a self,
context: &'cx mut Context<'cx_a>,
@ -267,6 +274,7 @@ impl PropertyDeclarationBlock {
///
/// This is based on the `declarations_importance` bit-vector,
/// which should be maintained whenever `declarations` is changed.
#[inline]
pub fn any_important(&self) -> bool {
!self.declarations_importance.all_false()
}
@ -275,11 +283,13 @@ impl PropertyDeclarationBlock {
///
/// This is based on the `declarations_importance` bit-vector,
/// which should be maintained whenever `declarations` is changed.
#[inline]
pub fn any_normal(&self) -> bool {
!self.declarations_importance.all_true()
}
/// Returns whether this block contains a declaration of a given longhand.
#[inline]
pub fn contains(&self, id: LonghandId) -> bool {
self.longhands.contains(id)
}
@ -292,8 +302,16 @@ impl PropertyDeclarationBlock {
/// Get a declaration for a given property.
///
/// NOTE: This is linear time.
/// NOTE: This is linear time in the case of custom properties or in the
/// case the longhand is actually in the declaration block.
#[inline]
pub fn get(&self, property: PropertyDeclarationId) -> Option<(&PropertyDeclaration, Importance)> {
if let PropertyDeclarationId::Longhand(id) = property {
if !self.contains(id) {
return None;
}
}
self.declarations.iter().enumerate().find(|&(_, decl)| decl.id() == property).map(|(i, decl)| {
let importance = if self.declarations_importance.get(i as u32) {
Importance::Important
@ -408,7 +426,7 @@ impl PropertyDeclarationBlock {
let all_shorthand_len = match drain.all_shorthand {
AllShorthand::NotSet => 0,
AllShorthand::CSSWideKeyword(_) |
AllShorthand::WithVariables(_) => ShorthandId::All.longhands().len()
AllShorthand::WithVariables(_) => shorthands::ALL_SHORTHAND_MAX_LEN,
};
let push_calls_count =
drain.declarations.len() + all_shorthand_len;
@ -766,8 +784,6 @@ impl PropertyDeclarationBlock {
// Step 3.3.2
for &shorthand in declaration.shorthands() {
let properties = shorthand.longhands();
// Substep 2 & 3
let mut current_longhands = SmallVec::<[_; 10]>::new();
let mut important_count = 0;
@ -793,21 +809,24 @@ impl PropertyDeclarationBlock {
}
}
} else {
for (longhand, importance) in self.declaration_importance_iter() {
if longhand.id().is_longhand_of(shorthand) {
current_longhands.push(longhand);
let mut contains_all_longhands = true;
for &longhand in shorthand.longhands() {
match self.get(PropertyDeclarationId::Longhand(longhand)) {
Some((declaration, importance)) => {
current_longhands.push(declaration);
if importance.important() {
important_count += 1;
}
}
None => {
contains_all_longhands = false;
break;
}
}
}
// Substep 1:
//
// Assuming that the PropertyDeclarationBlock contains no
// duplicate entries, if the current_longhands length is
// equal to the properties length, it means that the
// properties that map to shorthand are present in longhands
if current_longhands.len() != properties.len() {
if !contains_all_longhands {
continue;
}
}

Просмотреть файл

@ -193,6 +193,9 @@ pub mod shorthands {
spec="https://drafts.csswg.org/css-cascade-3/#all-shorthand"
)
%>
/// The max amount of longhands that the `all` shorthand will ever contain.
pub const ALL_SHORTHAND_MAX_LEN: usize = ${len(logical_longhands + other_longhands)};
}
<%

Просмотреть файл

@ -639,19 +639,21 @@ win64-devedition-nightly/opt:
- win64-rust
- win64-sccache
win32-mingw32/debug:
description: "Win32 MinGW Debug"
win32-mingw32/opt:
description: "Win32 MinGW Opt"
index:
product: firefox
job-name: win32-mingw32-debug
job-name: win32-mingw32-opt
treeherder:
platform: windows-mingw32-32/debug
symbol: B
platform: windows-mingw32/all
symbol: WM32(Bo)
tier: 2
worker-type: aws-provisioner-v1/gecko-{level}-b-linux
worker:
docker-image: {in-tree: mingw32-build}
max-run-time: 7200
env:
PERFHERDER_EXTRA_OPTIONS: "opt"
run:
using: mozharness
actions: [build]
@ -668,3 +670,36 @@ win32-mingw32/debug:
- linux64-mingw32-gcc
- linux64-mingw32-nsis
- linux64-mingw32-fxc2
win32-mingw32/debug:
description: "Win32 MinGW Debug"
index:
product: firefox
job-name: win32-mingw32-debug
treeherder:
platform: windows-mingw32/all
symbol: WM32(Bd)
tier: 2
worker-type: aws-provisioner-v1/gecko-{level}-b-linux
worker:
docker-image: {in-tree: mingw32-build}
max-run-time: 7200
env:
PERFHERDER_EXTRA_OPTIONS: "debug"
run:
using: mozharness
actions: [build]
script: mozharness/scripts/fx_desktop_build.py
config:
- builds/releng_base_firefox.py
- builds/releng_base_windows_32_mingw_builds.py
- builds/releng_sub_windows_configs/32_mingw_debug.py
need-xvfb: false
toolchains:
- mingw32-rust
- linux64-upx
- linux64-wine
- linux64-sccache
- linux64-mingw32-gcc
- linux64-mingw32-nsis
- linux64-mingw32-fxc2

2
taskcluster/ci/config.yml Normal file → Executable file
Просмотреть файл

@ -49,6 +49,8 @@ treeherder:
'TMW': 'Toolchain builds for Windows MinGW'
'TW32': 'Toolchain builds for Windows 32-bits'
'TW64': 'Toolchain builds for Windows 64-bits'
'WM32': 'MinGW builds for Windows 32-bits'
'WM64': 'MinGW builds for Windows 64-bits'
'Searchfox': 'Searchfox builds'
'SM': 'Spidermonkey builds'
'pub': 'APK publishing'

Просмотреть файл

@ -0,0 +1,3 @@
config = {
'mozconfig_variant': 'mingw32-debug',
}

1
third_party/rust/coco/.cargo-checksum.json поставляемый
Просмотреть файл

@ -1 +0,0 @@
{"files":{".travis.yml":"b4ea42f2ade2f287c4b0b6eee0e34437ec7cad7462832c18c397372b2a18aef1","CHANGELOG.md":"255242d56d5ce66921e03665a7b4b87be94c4b2ca7c4333f6569abe45321f992","Cargo.toml":"3aeb19f8e670699b19d0627c2466e8a859a02d3b8697c2054ac1ce8f82876c3e","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"0621878e61f0d0fda054bcbe02df75192c28bde1ecc8289cbd86aeba2dd72720","README.md":"7c3ce82aaba8e7bb81a62e1c99eb4c62c0116cd0832e343be5a52ec5e20942cb","benches/bench.rs":"ab1b7a1db73425735405fc214606c9ec783b350001f1be376ebf43cd4a540b67","ci/script.sh":"878f8b0a1d77d51834c152b299e6ef7b9c7d24a7ca2fbefe5070e9d2a72532c9","src/deque.rs":"5eaa6bec7c61435abebb35d52e9e02a6bb164c92d6c078f634e2b941f03e033d","src/epoch/atomic.rs":"1b7ed6f5abc0860a71a2d07f9099a4c0c7f274f7fe2a09733b64bf9f1a72fcd1","src/epoch/garbage.rs":"b1b35659796008001a8cb4a9edad7c101091f5ba45515cc5d64ef1ec862d36af","src/epoch/mod.rs":"0c83566f179b125ce37d40d5ba1c8731b3baa29fc0c46f966eeb44d1cb41502c","src/epoch/thread.rs":"cb8d17c75763004f4d3b227a7b710b1c8cbf3c5adc87d8346db57b2f8af59b27","src/lib.rs":"4b01d1e4bea889496b8c22713caaf34c65339aa8582e8b903fd3e0395c830a4a","src/stack.rs":"c1186eadfce0b83c3df2211cf15e0d2426b3a8fc3cd7726eca4e73851a502b60"},"package":"c06169f5beb7e31c7c67ebf5540b8b472d23e3eade3b2ec7d1f5b504a85f91bd"}

27
third_party/rust/coco/.travis.yml поставляемый
Просмотреть файл

@ -1,27 +0,0 @@
language: rust
rust:
- stable
- beta
- nightly
env:
global:
- RUST_MIN_STACK=33554432
matrix:
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-precise
- llvm-toolchain-precise-3.8
packages:
- llvm-3.8
- llvm-3.8-dev
- clang-3.8
- clang-3.8-dev
script:
- ./ci/script.sh

4
third_party/rust/coco/CHANGELOG.md поставляемый
Просмотреть файл

@ -1,4 +0,0 @@
# Release 0.1.0
* Implemented a lock-free stack.
* Implemented a lock-free work-stealing deque.

18
third_party/rust/coco/README.md поставляемый
Просмотреть файл

@ -1,18 +0,0 @@
# Concurrent collections
[![Build Status](https://travis-ci.org/stjepang/coco.svg?branch=master)](https://travis-ci.org/stjepang/coco)
[![License](https://img.shields.io/badge/license-Apache--2.0%2FMIT-blue.svg)](https://github.com/stjepang/coco)
[![Cargo](https://img.shields.io/crates/v/coco.svg)](https://crates.io/crates/coco)
[![Documentation](https://docs.rs/coco/badge.svg)](https://docs.rs/coco)
This crate offers several collections that are designed for performance in multithreaded
contexts. They can be freely shared among multiple threads running in parallel, and concurrently
modified without the overhead of locking.
<!-- Some of these data structures are lock-free. Others are not strictly speaking lock-free, but -->
<!-- still scale well with respect to the number of threads accessing them. -->
The following collections are available:
* `Stack`: A lock-free stack.
* `deque`: A lock-free work-stealing deque.

12
third_party/rust/coco/benches/bench.rs поставляемый
Просмотреть файл

@ -1,12 +0,0 @@
#![feature(test)]
extern crate coco;
extern crate test;
use coco::epoch;
use test::Bencher;
#[bench]
fn pin_empty(b: &mut Bencher) {
b.iter(|| epoch::pin(|_| ()))
}

20
third_party/rust/coco/ci/script.sh поставляемый
Просмотреть файл

@ -1,20 +0,0 @@
#!/bin/bash
set -ex
cargo test
cargo test --features strict_gc
if [[ "$TRAVIS_RUST_VERSION" == "nightly" ]]; then
cd sanitize
cargo test
cargo test --features coco/strict_gc
for _ in {1..10}; do
cargo test --release
done
for _ in {1..10}; do
cargo test --release --features coco/strict_gc
done
fi

813
third_party/rust/coco/src/deque.rs поставляемый
Просмотреть файл

@ -1,813 +0,0 @@
//! A lock-free work-stealing deque.
//!
//! There is one worker and possibly multiple stealers per deque. The worker has exclusive access
//! to one side of the deque and may push and pop elements. Stealers can only steal (i.e. pop)
//! elements from the other side.
//!
//! The implementation is based on the following papers:
//!
//! 1. Dynamic Circular Work-Stealing Deque
//! <sup>[pdf][chase-lev]</sup>
//! 2. Correct and Efficient Work-Stealing for Weak Memory Models
//! <sup>[pdf][weak-mem]</sup>
//! 3. CDSChecker: Checking Concurrent Data Structures Written with C/C++ Atomics
//! <sup>[pdf][checker] [code][code]</sup>
//!
//! [chase-lev]: https://pdfs.semanticscholar.org/3771/77bb82105c35e6e26ebad1698a20688473bd.pdf
//! [weak-mem]: http://www.di.ens.fr/~zappa/readings/ppopp13.pdf
//! [checker]: http://plrg.eecs.uci.edu/publications/c11modelcheck.pdf
//! [code]: https://github.com/computersforpeace/model-checker-benchmarks/tree/master/chase-lev-deque-bugfix
//!
//! # Examples
//!
//! ```
//! use coco::deque;
//!
//! let (w, s) = deque::new();
//!
//! // Create some work.
//! for i in 0..1000 {
//! w.push(i);
//! }
//!
//! let threads = (0..4).map(|_| {
//! let s = s.clone();
//! std::thread::spawn(move || {
//! while let Some(x) = s.steal() {
//! // Do something with `x`...
//! }
//! })
//! }).collect::<Vec<_>>();
//!
//! while let Some(x) = w.pop() {
//! // Do something with `x`...
//! // Or create even more work...
//! if x > 1 {
//! w.push(x / 2);
//! w.push(x / 2);
//! }
//! }
//!
//! for t in threads {
//! t.join().unwrap();
//! }
//! ```
use std::cmp;
use std::fmt;
use std::marker::PhantomData;
use std::mem;
use std::ptr;
use std::sync::Arc;
use std::sync::atomic::{AtomicIsize, fence};
use std::sync::atomic::Ordering::{Acquire, Release, Relaxed, SeqCst};
use epoch::{self, Atomic};
/// Minimum buffer capacity for a deque.
const MIN_CAP: usize = 16;
/// A buffer where deque elements are stored.
struct Buffer<T> {
/// Pointer to the allocated memory.
ptr: *mut T,
/// Capacity of the buffer. Always a power of two.
cap: usize,
}
impl<T> Buffer<T> {
/// Returns a new buffe with the specified capacity.
fn new(cap: usize) -> Self {
let mut v = Vec::with_capacity(cap);
let ptr = v.as_mut_ptr();
mem::forget(v);
Buffer {
ptr: ptr,
cap: cap,
}
}
/// Returns a pointer to the element at the specified `index`.
unsafe fn at(&self, index: isize) -> *mut T {
// `self.len` is always a power of two.
self.ptr.offset(index & (self.cap - 1) as isize)
}
/// Writes `value` into the specified `index`.
unsafe fn write(&self, index: isize, value: T) {
ptr::write(self.at(index), value)
}
/// Reads the value from the specified `index`.
unsafe fn read(&self, index: isize) -> T {
ptr::read(self.at(index))
}
}
struct Deque<T> {
bottom: AtomicIsize,
top: AtomicIsize,
buffer: Atomic<Buffer<T>>,
}
/// A work-stealing deque.
impl<T> Deque<T> {
/// Returns a new, empty deque.
fn new() -> Self {
Deque {
bottom: AtomicIsize::new(0),
top: AtomicIsize::new(0),
buffer: Atomic::new(Buffer::new(MIN_CAP), 0),
}
}
/// Returns the number of elements in the deque.
///
/// If used concurrently with other operations, the returned number is just an estimate.
fn len(&self) -> usize {
let b = self.bottom.load(Relaxed);
let t = self.top.load(Relaxed);
// The length can be negative because `b` and `t` were loaded without synchronization.
cmp::max(b.wrapping_sub(t), 0) as usize
}
/// Resizes the buffer with new capacity of `new_cap`.
#[cold]
unsafe fn resize(&self, new_cap: usize) {
// Load the bottom, top, and buffer.
let b = self.bottom.load(Relaxed);
let t = self.top.load(Relaxed);
let buffer = self.buffer.load_raw(Relaxed).0;
// Allocate a new buffer.
let new = Buffer::new(new_cap);
// Copy data from the old buffer to the new one.
let mut i = t;
while i != b {
ptr::copy_nonoverlapping((*buffer).at(i), new.at(i), 1);
i = i.wrapping_add(1);
}
epoch::pin(|pin| {
// Replace the old buffer with the new one.
self.buffer.store_box(Box::new(new), 0, pin).as_raw();
let ptr = (*buffer).ptr;
let cap = (*buffer).cap;
// Destroy the old buffer later.
epoch::defer_free(ptr, cap, pin);
epoch::defer_free(buffer, 1, pin);
// If the size of the buffer at least than 1KB, then flush the thread-local garbage in
// order to destroy it sooner.
if mem::size_of::<T>() * cap >= 1 << 10 {
epoch::flush(pin);
}
})
}
/// Pushes an element onto the bottom of the deque.
fn push(&self, value: T) {
unsafe {
// Load the bottom, top, and buffer. The buffer doesn't have to be epoch-protected
// because the current thread (the worker) is the only one that grows and shrinks it.
let b = self.bottom.load(Relaxed);
let t = self.top.load(Acquire);
let mut buffer = self.buffer.load_raw(Relaxed).0;
// Calculate the length of the deque.
let len = b.wrapping_sub(t);
// Is the deque full?
let cap = (*buffer).cap;
if len >= cap as isize {
// Yes. Grow the underlying buffer.
self.resize(2 * cap);
buffer = self.buffer.load_raw(Relaxed).0;
}
// Write `value` into the right slot and increment `b`.
(*buffer).write(b, value);
fence(Release);
self.bottom.store(b.wrapping_add(1), Relaxed);
}
}
/// Pops an element from the bottom of the deque.
fn pop(&self) -> Option<T> {
// Load the bottom.
let b = self.bottom.load(Relaxed);
// If the deque is empty, return early without incurring the cost of a SeqCst fence.
let t = self.top.load(Relaxed);
if b.wrapping_sub(t) <= 0 {
return None;
}
// Decrement the bottom.
let b = b.wrapping_sub(1);
self.bottom.store(b, Relaxed);
// Load the buffer. The buffer doesn't have to be epoch-protected because the current
// thread (the worker) is the only one that grows and shrinks it.
let buffer = self.buffer.load_raw(Relaxed).0;
fence(SeqCst);
// Load the top.
let t = self.top.load(Relaxed);
// Compute the length after the bottom was decremented.
let len = b.wrapping_sub(t);
if len < 0 {
// The deque is empty. Restore the bottom back to the original value.
self.bottom.store(b.wrapping_add(1), Relaxed);
None
} else {
// Read the value to be popped.
let mut value = unsafe { Some((*buffer).read(b)) };
// Are we popping the last element from the deque?
if len == 0 {
// Try incrementing the top.
if self.top.compare_exchange(t, t.wrapping_add(1), SeqCst, Relaxed).is_err() {
// Failed. We didn't pop anything.
mem::forget(value.take());
}
// Restore the bottom back to the original value.
self.bottom.store(b.wrapping_add(1), Relaxed);
} else {
// Shrink the buffer if `len` is less than one fourth of `cap`.
unsafe {
let cap = (*buffer).cap;
if cap > MIN_CAP && len < cap as isize / 4 {
self.resize(cap / 2);
}
}
}
value
}
}
/// Steals an element from the top of the deque.
fn steal(&self) -> Option<T> {
// Load the top.
let mut t = self.top.load(Acquire);
// A SeqCst fence is needed here.
// If the current thread is already pinned (reentrantly), we must manually issue the fence.
// Otherwise, the following pinning will issue the fence anyway, so we don't have to.
if epoch::is_pinned() {
fence(SeqCst);
}
epoch::pin(|pin| {
// Loop until we successfully steal an element or find the deque empty.
loop {
// Load the bottom.
let b = self.bottom.load(Acquire);
// Is the deque empty?
if b.wrapping_sub(t) <= 0 {
return None;
}
// Load the buffer and read the value at the top.
let a = self.buffer.load(pin).unwrap();
let value = unsafe { a.read(t) };
// Try incrementing the top to steal the value.
if self.top.compare_exchange(t, t.wrapping_add(1), SeqCst, Relaxed).is_ok() {
return Some(value);
}
// We didn't steal this value, forget it.
mem::forget(value);
// Before every iteration of the loop we must load the top, issue a SeqCst fence,
// and then load the bottom. Now reload the top and issue the fence.
t = self.top.load(Acquire);
fence(SeqCst);
}
})
}
/// Steals an element from the top of the deque, but only the worker may call this method.
fn steal_as_worker(&self) -> Option<T> {
let b = self.bottom.load(Relaxed);
let a = self.buffer.load_raw(Relaxed).0;
// Loop until we successfully steal an element or find the deque empty.
loop {
let t = self.top.load(Relaxed);
// Is the deque empty?
if b.wrapping_sub(t) <= 0 {
return None;
}
// Try incrementing the top to steal the value.
if self.top.compare_exchange(t, t.wrapping_add(1), SeqCst, Relaxed).is_ok() {
return unsafe { Some((*a).read(t)) };
}
}
}
}
impl<T> Drop for Deque<T> {
fn drop(&mut self) {
// Load the bottom, top, and buffer.
let b = self.bottom.load(Relaxed);
let t = self.top.load(Relaxed);
let buffer = self.buffer.load_raw(Relaxed).0;
unsafe {
// Go through the buffer from top to bottom and drop all elements in the deque.
let mut i = t;
while i != b {
ptr::drop_in_place((*buffer).at(i));
i = i.wrapping_add(1);
}
// Free the memory allocated by the buffer.
drop(Vec::from_raw_parts((*buffer).ptr, 0, (*buffer).cap));
drop(Vec::from_raw_parts(buffer, 0, 1));
}
}
}
/// Worker side of a work-stealing deque.
///
/// There is only one worker per deque.
pub struct Worker<T> {
deque: Arc<Deque<T>>,
_marker: PhantomData<*mut ()>, // !Send + !Sync
}
unsafe impl<T: Send> Send for Worker<T> {}
impl<T> Worker<T> {
/// Returns the number of elements in the deque.
///
/// If used concurrently with other operations, the returned number is just an estimate.
///
/// # Examples
///
/// ```
/// use coco::deque;
///
/// let (w, _) = deque::new();
/// for i in 0..30 {
/// w.push(i);
/// }
/// assert_eq!(w.len(), 30);
/// ```
pub fn len(&self) -> usize {
self.deque.len()
}
/// Pushes an element onto the bottom of the deque.
///
/// # Examples
///
/// ```
/// use coco::deque;
///
/// let (w, _) = deque::new();
/// w.push(1);
/// w.push(2);
/// ```
pub fn push(&self, value: T) {
self.deque.push(value);
}
/// Pops an element from the bottom of the deque.
///
/// # Examples
///
/// ```
/// use coco::deque;
///
/// let (w, _) = deque::new();
/// w.push(1);
/// w.push(2);
///
/// assert_eq!(w.pop(), Some(2));
/// assert_eq!(w.pop(), Some(1));
/// assert_eq!(w.pop(), None);
/// ```
pub fn pop(&self) -> Option<T> {
self.deque.pop()
}
/// Steals an element from the top of the deque.
///
/// # Examples
///
/// ```
/// use coco::deque;
///
/// let (w, _) = deque::new();
/// w.push(1);
/// w.push(2);
///
/// assert_eq!(w.steal(), Some(1));
/// assert_eq!(w.steal(), Some(2));
/// assert_eq!(w.steal(), None);
/// ```
pub fn steal(&self) -> Option<T> {
self.deque.steal_as_worker()
}
}
impl<T> fmt::Debug for Worker<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Worker {{ ... }}")
}
}
/// Stealer side of a work-stealing deque.
///
/// Stealers may be cloned in order to create more stealers for the same deque.
pub struct Stealer<T> {
deque: Arc<Deque<T>>,
_marker: PhantomData<*mut ()>, // !Send + !Sync
}
unsafe impl<T: Send> Send for Stealer<T> {}
unsafe impl<T: Send> Sync for Stealer<T> {}
impl<T> Stealer<T> {
/// Returns the number of elements in the deque.
///
/// If used concurrently with other operations, the returned number is just an estimate.
///
/// # Examples
///
/// ```
/// use coco::deque;
///
/// let (w, _) = deque::new();
/// for i in 0..30 {
/// w.push(i);
/// }
/// assert_eq!(w.len(), 30);
/// ```
pub fn len(&self) -> usize {
self.deque.len()
}
/// Steals an element from the top of the deque.
///
/// # Examples
///
/// ```
/// use coco::deque;
///
/// let (w, s) = deque::new();
/// w.push(1);
/// w.push(2);
///
/// assert_eq!(s.steal(), Some(1));
/// assert_eq!(s.steal(), Some(2));
/// assert_eq!(s.steal(), None);
/// ```
pub fn steal(&self) -> Option<T> {
self.deque.steal()
}
}
impl<T> Clone for Stealer<T> {
fn clone(&self) -> Self {
Stealer {
deque: self.deque.clone(),
_marker: PhantomData,
}
}
}
impl<T> fmt::Debug for Stealer<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Stealer {{ ... }}")
}
}
/// Returns a new work-stealing deque.
///
/// The worker is unique, while stealers can be cloned and distributed among multiple threads.
///
/// The deque will be destructed as soon as it's worker and all it's stealers get dropped.
///
/// # Examples
///
/// ```
/// use coco::deque;
///
/// let (w, s1) = deque::new();
/// let s2 = s1.clone();
///
/// w.push('a');
/// w.push('b');
/// w.push('c');
///
/// assert_eq!(w.pop(), Some('c'));
/// assert_eq!(s1.steal(), Some('a'));
/// assert_eq!(s2.steal(), Some('b'));
/// ```
pub fn new<T>() -> (Worker<T>, Stealer<T>) {
let d = Arc::new(Deque::new());
let worker = Worker {
deque: d.clone(),
_marker: PhantomData,
};
let stealer = Stealer {
deque: d,
_marker: PhantomData,
};
(worker, stealer)
}
#[cfg(test)]
mod tests {
extern crate rand;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicBool, AtomicUsize};
use std::sync::atomic::Ordering::SeqCst;
use std::thread;
use epoch;
use self::rand::Rng;
#[test]
fn smoke() {
let (w, s) = super::new();
assert_eq!(w.pop(), None);
assert_eq!(s.steal(), None);
assert_eq!(w.len(), 0);
assert_eq!(s.len(), 0);
w.push(1);
assert_eq!(w.len(), 1);
assert_eq!(s.len(), 1);
assert_eq!(w.pop(), Some(1));
assert_eq!(w.pop(), None);
assert_eq!(s.steal(), None);
assert_eq!(w.len(), 0);
assert_eq!(s.len(), 0);
w.push(2);
assert_eq!(s.steal(), Some(2));
assert_eq!(s.steal(), None);
assert_eq!(w.pop(), None);
w.push(3);
w.push(4);
w.push(5);
assert_eq!(w.steal(), Some(3));
assert_eq!(s.steal(), Some(4));
assert_eq!(w.steal(), Some(5));
assert_eq!(w.steal(), None);
}
#[test]
fn steal_push() {
const STEPS: usize = 50_000;
let (w, s) = super::new();
let t = thread::spawn(move || {
for i in 0..STEPS {
loop {
if let Some(v) = s.steal() {
assert_eq!(i, v);
break;
}
}
}
});
for i in 0..STEPS {
w.push(i);
}
t.join().unwrap();
}
#[test]
fn stampede() {
const COUNT: usize = 50_000;
let (w, s) = super::new();
for i in 0..COUNT {
w.push(Box::new(i + 1));
}
let remaining = Arc::new(AtomicUsize::new(COUNT));
let threads = (0..8).map(|_| {
let s = s.clone();
let remaining = remaining.clone();
thread::spawn(move || {
let mut last = 0;
while remaining.load(SeqCst) > 0 {
if let Some(x) = s.steal() {
assert!(last < *x);
last = *x;
remaining.fetch_sub(1, SeqCst);
}
}
})
}).collect::<Vec<_>>();
let mut last = COUNT + 1;
while remaining.load(SeqCst) > 0 {
if let Some(x) = w.pop() {
assert!(last > *x);
last = *x;
remaining.fetch_sub(1, SeqCst);
}
}
for t in threads {
t.join().unwrap();
}
}
fn run_stress() {
const COUNT: usize = 50_000;
let (w, s) = super::new();
let done = Arc::new(AtomicBool::new(false));
let hits = Arc::new(AtomicUsize::new(0));
let threads = (0..8).map(|_| {
let s = s.clone();
let done = done.clone();
let hits = hits.clone();
thread::spawn(move || {
while !done.load(SeqCst) {
if let Some(_) = s.steal() {
hits.fetch_add(1, SeqCst);
}
}
})
}).collect::<Vec<_>>();
let mut rng = rand::thread_rng();
let mut expected = 0;
while expected < COUNT {
if rng.gen_range(0, 3) == 0 {
if w.pop().is_some() {
hits.fetch_add(1, SeqCst);
}
} else {
w.push(expected);
expected += 1;
}
}
while hits.load(SeqCst) < COUNT {
if w.pop().is_some() {
hits.fetch_add(1, SeqCst);
}
}
done.store(true, SeqCst);
for t in threads {
t.join().unwrap();
}
}
#[test]
fn stress() {
run_stress();
}
#[test]
fn stress_pinned() {
epoch::pin(|_| run_stress());
}
#[test]
fn no_starvation() {
const COUNT: usize = 50_000;
let (w, s) = super::new();
let done = Arc::new(AtomicBool::new(false));
let (threads, hits): (Vec<_>, Vec<_>) = (0..8).map(|_| {
let s = s.clone();
let done = done.clone();
let hits = Arc::new(AtomicUsize::new(0));
let t = {
let hits = hits.clone();
thread::spawn(move || {
while !done.load(SeqCst) {
if let Some(_) = s.steal() {
hits.fetch_add(1, SeqCst);
}
}
})
};
(t, hits)
}).unzip();
let mut rng = rand::thread_rng();
let mut my_hits = 0;
loop {
for i in 0..rng.gen_range(0, COUNT) {
if rng.gen_range(0, 3) == 0 && my_hits == 0 {
if w.pop().is_some() {
my_hits += 1;
}
} else {
w.push(i);
}
}
if my_hits > 0 && hits.iter().all(|h| h.load(SeqCst) > 0) {
break;
}
}
done.store(true, SeqCst);
for t in threads {
t.join().unwrap();
}
}
#[test]
fn destructors() {
const COUNT: usize = 50_000;
struct Elem(usize, Arc<Mutex<Vec<usize>>>);
impl Drop for Elem {
fn drop(&mut self) {
self.1.lock().unwrap().push(self.0);
}
}
let (w, s) = super::new();
let dropped = Arc::new(Mutex::new(Vec::new()));
let remaining = Arc::new(AtomicUsize::new(COUNT));
for i in 0..COUNT {
w.push(Elem(i, dropped.clone()));
}
let threads = (0..8).map(|_| {
let s = s.clone();
let remaining = remaining.clone();
thread::spawn(move || {
for _ in 0..1000 {
if s.steal().is_some() {
remaining.fetch_sub(1, SeqCst);
}
}
})
}).collect::<Vec<_>>();
for _ in 0..1000 {
if w.pop().is_some() {
remaining.fetch_sub(1, SeqCst);
}
}
for t in threads {
t.join().unwrap();
}
let rem = remaining.load(SeqCst);
assert!(rem > 0);
assert_eq!(w.len(), rem);
assert_eq!(s.len(), rem);
{
let mut v = dropped.lock().unwrap();
assert_eq!(v.len(), COUNT - rem);
v.clear();
}
drop(w);
drop(s);
{
let mut v = dropped.lock().unwrap();
assert_eq!(v.len(), rem);
v.sort();
for w in v.windows(2) {
assert_eq!(w[0] + 1, w[1]);
}
}
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше