зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to inbound. a=merge CLOSED TREE
This commit is contained in:
Коммит
54668feb23
|
@ -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)) {
|
||||
|
|
|
@ -1,57 +1,57 @@
|
|||
|
||||
# Summary
|
||||
|
||||
* [Getting started](getting-started/README.md)
|
||||
* [Where is the code?](getting-started/where-is-the-code.md)
|
||||
* [Architecture overview](getting-started/architecture-overview.md)
|
||||
* [Set up to build DevTools](getting-started/build.md)
|
||||
* [Development profiles](getting-started/development-profiles.md)
|
||||
* [Contributing](./contributing.md)
|
||||
* [Coding standards](./contributing/coding-standards.md)
|
||||
* [JavaScript](./contributing/javascript.md)
|
||||
* [ESLint](./contributing/eslint.md)
|
||||
* [CSS](./contributing/css.md)
|
||||
* [Creating and sending patches](./contributing/making-prs.md)
|
||||
* [Code reviews](./contributing/code-reviews.md)
|
||||
* [Filing good bugs](./contributing/filing-good-bugs.md)
|
||||
* [Investigating performance issues](./contributing/performance.md)
|
||||
* [Bugs and issue trackers](bugs-issues.md)
|
||||
* [Files and directories](files/README.md)
|
||||
* [Adding New Files](files/adding-files.md)
|
||||
* [Tool Architectures](tools/tools.md)
|
||||
* [Inspector](tools/inspector.md)
|
||||
* [Panel Architecture](tools/inspector-panel.md)
|
||||
* [Highlighters](tools/highlighters.md)
|
||||
* [Memory](tools/memory-panel.md)
|
||||
* [Debugger](tools/debugger-panel.md)
|
||||
* [Responsive Design Mode](tools/responsive-design-mode.md)
|
||||
* [HTTP Inspector](tools/http-inspector.md)
|
||||
* [Console](tools/console-panel.md)
|
||||
* [Frontend](frontend/frontend.md)
|
||||
* [Panel SVGs](frontend/svgs.md)
|
||||
* [React](frontend/react.md)
|
||||
* [Guidelines](frontend/react-guidelines.md)
|
||||
* [Redux](frontend/redux.md)
|
||||
* [Guidelines](frontend/redux-guidelines.md)
|
||||
* [Telemetry](frontend/telemetry.md)
|
||||
* [Backend](backend/backend.md)
|
||||
* [Remote Debugging Protocol](backend/protocol.md)
|
||||
* [Client API](backend/client-api.md)
|
||||
* [Debugger API](backend/debugger-api.md)
|
||||
* [Backward Compatibility](backend/backward-compatibility.md)
|
||||
* Actors
|
||||
* [Actors Organization](backend/actor-hierarchy.md)
|
||||
* [Handling Multi-Processes in Actors](backend/actor-e10s-handling.md)
|
||||
* [Writing Actors With protocol.js](backend/protocol.js.md)
|
||||
* [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)
|
||||
|
||||
# Summary
|
||||
|
||||
* [Getting started](getting-started/README.md)
|
||||
* [Where is the code?](getting-started/where-is-the-code.md)
|
||||
* [Architecture overview](getting-started/architecture-overview.md)
|
||||
* [Set up to build DevTools](getting-started/build.md)
|
||||
* [Development profiles](getting-started/development-profiles.md)
|
||||
* [Contributing](./contributing.md)
|
||||
* [Coding standards](./contributing/coding-standards.md)
|
||||
* [JavaScript](./contributing/javascript.md)
|
||||
* [ESLint](./contributing/eslint.md)
|
||||
* [CSS](./contributing/css.md)
|
||||
* [Creating and sending patches](./contributing/making-prs.md)
|
||||
* [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)
|
||||
* [Tool Architectures](tools/tools.md)
|
||||
* [Inspector](tools/inspector.md)
|
||||
* [Panel Architecture](tools/inspector-panel.md)
|
||||
* [Highlighters](tools/highlighters.md)
|
||||
* [Memory](tools/memory-panel.md)
|
||||
* [Debugger](tools/debugger-panel.md)
|
||||
* [Responsive Design Mode](tools/responsive-design-mode.md)
|
||||
* [HTTP Inspector](tools/http-inspector.md)
|
||||
* [Console](tools/console-panel.md)
|
||||
* [Frontend](frontend/frontend.md)
|
||||
* [Panel SVGs](frontend/svgs.md)
|
||||
* [React](frontend/react.md)
|
||||
* [Guidelines](frontend/react-guidelines.md)
|
||||
* [Redux](frontend/redux.md)
|
||||
* [Guidelines](frontend/redux-guidelines.md)
|
||||
* [Telemetry](frontend/telemetry.md)
|
||||
* [Backend](backend/backend.md)
|
||||
* [Remote Debugging Protocol](backend/protocol.md)
|
||||
* [Client API](backend/client-api.md)
|
||||
* [Debugger API](backend/debugger-api.md)
|
||||
* [Backward Compatibility](backend/backward-compatibility.md)
|
||||
* Actors
|
||||
* [Actors Organization](backend/actor-hierarchy.md)
|
||||
* [Handling Multi-Processes in Actors](backend/actor-e10s-handling.md)
|
||||
* [Writing Actors With protocol.js](backend/protocol.js.md)
|
||||
* [Registering A New Actor](backend/actor-registration.md)
|
||||
* [Actor Best Practices](backend/actor-best-practices.md)
|
||||
* [Preferences](preferences.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 +
|
||||
src_task.common_data.task_rect.p0 -
|
||||
src_task.content_origin) * vi.w;
|
||||
vW = vi.w;
|
||||
vec2 uv = vi.snapped_device_pos +
|
||||
src_task.common_data.task_rect.p0 -
|
||||
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 +
|
||||
src_task.common_data.task_rect.p0 -
|
||||
src_task.content_origin) * vi.w;
|
||||
vec2 src_uv = vi.snapped_device_pos +
|
||||
src_task.common_data.task_rect.p0 -
|
||||
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 +
|
||||
backdrop_task.task_rect.p0 -
|
||||
src_task.content_origin) * vi.w;
|
||||
vec2 backdrop_uv = vi.snapped_device_pos +
|
||||
backdrop_task.task_rect.p0 -
|
||||
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 {
|
||||
chain.add_node((**nodes).clone());
|
||||
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,30 +456,37 @@ 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) {
|
||||
node.set_scroll_origin(&offset, clamping);
|
||||
}
|
||||
if let Some(scrolling_state) = old_states.get(&external_id) {
|
||||
node.apply_old_scrolling_state(scrolling_state);
|
||||
}
|
||||
|
||||
if let Some((offset, clamping)) = self.pending_scroll_offsets.remove(&clip_id.into()) {
|
||||
|
||||
if let Some((offset, clamping)) = self.pending_scroll_offsets.remove(&external_id) {
|
||||
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(
|
||||
&mut self,
|
||||
id: ClipId,
|
||||
|
@ -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(),
|
||||
[
|
||||
if prim_infos.is_empty() {
|
||||
self.add_radial_gradient_impl(
|
||||
clip_and_scroll,
|
||||
info,
|
||||
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,
|
||||
pack_as_float(extend_mode as u32),
|
||||
].into(),
|
||||
[
|
||||
tile_size.width,
|
||||
tile_size.height,
|
||||
tile_repeat.width,
|
||||
tile_repeat.height,
|
||||
].into(),
|
||||
],
|
||||
};
|
||||
|
||||
self.add_primitive(
|
||||
clip_and_scroll,
|
||||
info,
|
||||
Vec::new(),
|
||||
PrimitiveContainer::RadialGradient(radial_gradient_cpu),
|
||||
);
|
||||
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 {
|
||||
yuv_key,
|
||||
format,
|
||||
color_space,
|
||||
image_rendering,
|
||||
gpu_block: [info.rect.size.width, info.rect.size.height, 0.0, 0.0].into(),
|
||||
};
|
||||
let prim = BrushPrimitive::new(
|
||||
BrushKind::YuvImage {
|
||||
yuv_key,
|
||||
format,
|
||||
color_space,
|
||||
image_rendering,
|
||||
},
|
||||
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,12 +231,11 @@ impl HitTester {
|
|||
continue;
|
||||
}
|
||||
|
||||
let clip_id = &clip_and_scroll.clip_node_id();
|
||||
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 {
|
||||
clipped_in = self.is_point_clipped_in_for_clip_chain(point, clip_id, &mut test);
|
||||
if !clipped_in {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// We need to trigger a lookup against the root reference frame here, because
|
||||
|
@ -228,7 +243,7 @@ impl HitTester {
|
|||
// 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,16 +1908,12 @@ impl PrimitiveStore {
|
|||
continue;
|
||||
}
|
||||
|
||||
match clip_chain {
|
||||
Some(ref chain) if chain.combined_outer_screen_rect.is_empty() => {
|
||||
debug!("{:?} {:?}: clipped out", run.base_prim_index, pic_context.pipeline_id);
|
||||
continue;
|
||||
}
|
||||
_ => {},
|
||||
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
|
||||
.map(|inv_parent| {
|
||||
|
@ -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(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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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,17 +1999,17 @@ impl Renderer {
|
|||
}
|
||||
|
||||
let shader = try!{
|
||||
PrimitiveShader::new("ps_yuv_image",
|
||||
&mut device,
|
||||
&yuv_features,
|
||||
options.precache_shaders)
|
||||
BrushShader::new("brush_yuv_image",
|
||||
&mut device,
|
||||
&yuv_features,
|
||||
options.precache_shaders)
|
||||
};
|
||||
let index = Renderer::get_yuv_shader_index(
|
||||
IMAGE_BUFFER_KINDS[buffer_kind],
|
||||
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());
|
||||
|
||||
let selector = TargetSelector {
|
||||
size: list.max_size,
|
||||
num_layers: list.targets.len() as _,
|
||||
format: list.format,
|
||||
};
|
||||
let index = self.texture_resolver.render_target_pool
|
||||
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 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(),
|
||||
}
|
||||
});
|
||||
|
||||
// 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| {
|
||||
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,
|
||||
.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)
|
||||
}
|
||||
} else {
|
||||
if list.texture.is_some() {
|
||||
list.check_ready();
|
||||
return
|
||||
}
|
||||
let 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),
|
||||
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,20 +219,13 @@ impl<T: RenderTarget> RenderTargetList<T> {
|
|||
self.targets.iter().any(|target| target.needs_depth())
|
||||
}
|
||||
|
||||
pub fn check_ready(&self) {
|
||||
match self.texture {
|
||||
Some(ref t) => {
|
||||
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());
|
||||
assert_eq!(t.get_layer_count() as usize, self.targets.len());
|
||||
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())
|
||||
}
|
||||
}
|
||||
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());
|
||||
assert_eq!(t.get_layer_count() as usize, self.targets.len());
|
||||
assert_eq!(t.has_depth(), t.get_rt_info().unwrap().has_depth);
|
||||
assert_eq!(t.has_depth(), self.needs_depth());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
RefPtr<CharSetChangingRunnable> runnable =
|
||||
new CharSetChangingRunnable(this, aEncoding);
|
||||
Document()->Dispatch(TaskCategory::Other, runnable.forget());
|
||||
#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 */
|
||||
)));
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
// Step 9.
|
||||
if is_block_level_or_table_caption(&display) {
|
||||
items.insert(0, InnerTextItem::RequiredLineBreakCount(1));
|
||||
items.push(InnerTextItem::RequiredLineBreakCount(1));
|
||||
Display::Block | Display::Flex | Display::TableCaption | Display::Table => {
|
||||
// Step 9.
|
||||
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);
|
||||
if importance.important() {
|
||||
important_count += 1;
|
||||
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
|
||||
|
|
|
@ -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 +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"}
|
|
@ -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
|
|
@ -1,4 +0,0 @@
|
|||
# Release 0.1.0
|
||||
|
||||
* Implemented a lock-free stack.
|
||||
* Implemented a lock-free work-stealing deque.
|
|
@ -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.
|
|
@ -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(|_| ()))
|
||||
}
|
|
@ -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
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче