mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 14:22:01 +00:00
merge autoland to mozilla-central a=merge
This commit is contained in:
commit
2e12354e97
@ -5204,6 +5204,7 @@
|
||||
if (!tab.hasAttribute("blocked")) {
|
||||
tab.setAttribute("blocked", true);
|
||||
this._tabAttrModified(tab, ["blocked"]);
|
||||
tab.startMediaBlockTimer();
|
||||
}
|
||||
]]>
|
||||
</handler>
|
||||
@ -5217,6 +5218,9 @@
|
||||
if (tab.hasAttribute("blocked")) {
|
||||
tab.removeAttribute("blocked");
|
||||
this._tabAttrModified(tab, ["blocked"]);
|
||||
let hist = Services.telemetry.getHistogramById("TAB_AUDIO_INDICATOR_USED");
|
||||
hist.add(2 /* unblockByVisitingTab */);
|
||||
tab.finishMediaBlockTimer();
|
||||
}
|
||||
]]>
|
||||
</handler>
|
||||
@ -6924,6 +6928,18 @@
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="startMediaBlockTimer">
|
||||
<body><![CDATA[
|
||||
TelemetryStopwatch.start("TAB_MEDIA_BLOCKING_TIME_MS", this);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="finishMediaBlockTimer">
|
||||
<body><![CDATA[
|
||||
TelemetryStopwatch.finish("TAB_MEDIA_BLOCKING_TIME_MS", this);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="toggleMuteAudio">
|
||||
<parameter name="aMuteReason"/>
|
||||
<body>
|
||||
@ -6931,6 +6947,8 @@
|
||||
let tabContainer = this.parentNode;
|
||||
let browser = this.linkedBrowser;
|
||||
let modifiedAttrs = [];
|
||||
let hist = Services.telemetry.getHistogramById("TAB_AUDIO_INDICATOR_USED");
|
||||
|
||||
if (browser.audioBlocked) {
|
||||
this.removeAttribute("blocked");
|
||||
modifiedAttrs.push("blocked");
|
||||
@ -6944,15 +6962,19 @@
|
||||
}
|
||||
|
||||
browser.resumeMedia();
|
||||
hist.add(3 /* unblockByClickingIcon */);
|
||||
this.finishMediaBlockTimer();
|
||||
} else {
|
||||
if (browser.audioMuted) {
|
||||
browser.unmute();
|
||||
this.removeAttribute("muted");
|
||||
BrowserUITelemetry.countTabMutingEvent("unmute", aMuteReason);
|
||||
hist.add(1 /* unmute */);
|
||||
} else {
|
||||
browser.mute();
|
||||
this.setAttribute("muted", "true");
|
||||
BrowserUITelemetry.countTabMutingEvent("mute", aMuteReason);
|
||||
hist.add(0 /* mute */);
|
||||
}
|
||||
this.muteReason = aMuteReason || null;
|
||||
modifiedAttrs.push("muted");
|
||||
|
@ -89,7 +89,7 @@ const kDownloadsStringsRequiringFormatting = {
|
||||
};
|
||||
|
||||
const kDownloadsStringsRequiringPluralForm = {
|
||||
otherDownloads2: true
|
||||
otherDownloads3: true
|
||||
};
|
||||
|
||||
const kPartialDownloadSuffix = ".part";
|
||||
@ -1520,12 +1520,11 @@ DownloadsSummaryData.prototype = {
|
||||
DownloadsCommon.summarizeDownloads(this._downloadsForSummary());
|
||||
|
||||
this._description = DownloadsCommon.strings
|
||||
.otherDownloads2(summary.numActive);
|
||||
.otherDownloads3(summary.numDownloading);
|
||||
this._percentComplete = summary.percentComplete;
|
||||
|
||||
// If all downloads are paused, show the progress indicator as paused.
|
||||
this._showingProgress = summary.numDownloading > 0 ||
|
||||
summary.numPaused > 0;
|
||||
// Only show the downloading items.
|
||||
this._showingProgress = summary.numDownloading > 0;
|
||||
|
||||
// Display the estimated time left, if present.
|
||||
if (summary.rawTimeLeft == -1) {
|
||||
|
@ -296,14 +296,17 @@ add_task(function* test_favicon_cache_firstParty() {
|
||||
.getService(Ci.nsICacheStorageService);
|
||||
networkCache.clear();
|
||||
|
||||
// Open the tab for the first site.
|
||||
let tabInfoA = yield openTab(TEST_SITE_ONE + TEST_CACHE_PAGE);
|
||||
|
||||
// Start to observer the event of that favicon has been fully loaded and cached.
|
||||
let promiseForFaviconLoaded = waitOnFaviconLoaded(THIRD_PARTY_SITE + TEST_FAVICON_CACHE_URI);
|
||||
|
||||
// Wait for the favicon response of the first tab.
|
||||
let response = yield waitOnFaviconResponse(THIRD_PARTY_SITE + TEST_FAVICON_CACHE_URI);
|
||||
// Start to observer for the favicon response of the first tab.
|
||||
let responsePromise = waitOnFaviconResponse(THIRD_PARTY_SITE + TEST_FAVICON_CACHE_URI);
|
||||
|
||||
// Open the tab for the first site.
|
||||
let tabInfoA = yield openTab(TEST_SITE_ONE + TEST_CACHE_PAGE);
|
||||
|
||||
// Waiting for the favicon response.
|
||||
let response = yield responsePromise;
|
||||
|
||||
// Make sure the favicon is loaded through the network and its first party domain is correct.
|
||||
is(response.topic, "http-on-examine-response", "The favicon image should be loaded through network.");
|
||||
|
@ -30,11 +30,11 @@
|
||||
which is displayed at the bottom of the Downloads Panel if the
|
||||
number of downloads exceeds the limit that the panel can display.
|
||||
|
||||
A good rule of thumb here is to look at the otherDownloads2 string
|
||||
A good rule of thumb here is to look at the otherDownloads3 string
|
||||
in downloads.properties, and make a reasonable estimate of its
|
||||
maximum length. For English, this seems like a reasonable limit:
|
||||
|
||||
+ 999 other downloads
|
||||
999 files downloading
|
||||
|
||||
that's 21 characters, so we set the minimum width to 21ch.
|
||||
-->
|
||||
|
@ -100,9 +100,9 @@ fileExecutableSecurityWarning=“%S” is an executable file. Executable files m
|
||||
fileExecutableSecurityWarningTitle=Open Executable File?
|
||||
fileExecutableSecurityWarningDontAsk=Don’t ask me this again
|
||||
|
||||
# LOCALIZATION NOTE (otherDownloads2):
|
||||
# LOCALIZATION NOTE (otherDownloads3):
|
||||
# This is displayed in an item at the bottom of the Downloads Panel when
|
||||
# there are more downloads than can fit in the list in the panel. Use a
|
||||
# semi-colon list of plural forms.
|
||||
# See: http://developer.mozilla.org/en/Localization_and_Plurals
|
||||
otherDownloads2=+ %1$S other download; + %1$S other downloads
|
||||
otherDownloads3=%1$S file downloading;%1$S files downloading
|
||||
|
@ -191,7 +191,7 @@ AudioChannelAgent::InitInternal(nsPIDOMWindowInner* aWindow,
|
||||
|
||||
NS_IMETHODIMP
|
||||
AudioChannelAgent::NotifyStartedPlaying(AudioPlaybackConfig* aConfig,
|
||||
bool aAudible)
|
||||
uint8_t aAudible)
|
||||
{
|
||||
if (NS_WARN_IF(!aConfig)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
@ -203,8 +203,9 @@ AudioChannelAgent::NotifyStartedPlaying(AudioPlaybackConfig* aConfig,
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(AudioChannelService::AudibleState::eAudible == true &&
|
||||
AudioChannelService::AudibleState::eNotAudible == false);
|
||||
MOZ_ASSERT(AudioChannelService::AudibleState::eNotAudible == 0 &&
|
||||
AudioChannelService::AudibleState::eMaybeAudible == 1 &&
|
||||
AudioChannelService::AudibleState::eAudible == 2);
|
||||
service->RegisterAudioChannelAgent(this,
|
||||
static_cast<AudioChannelService::AudibleState>(aAudible));
|
||||
|
||||
@ -242,7 +243,7 @@ AudioChannelAgent::NotifyStoppedPlaying()
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AudioChannelAgent::NotifyStartedAudible(bool aAudible, uint32_t aReason)
|
||||
AudioChannelAgent::NotifyStartedAudible(uint8_t aAudible, uint32_t aReason)
|
||||
{
|
||||
MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
|
||||
("AudioChannelAgent, NotifyStartedAudible, this = %p, "
|
||||
|
@ -1227,14 +1227,14 @@ AudioChannelService::AudioChannelWindow::AppendAgent(AudioChannelAgent* aAgent,
|
||||
RequestAudioFocus(aAgent);
|
||||
AppendAgentAndIncreaseAgentsNum(aAgent);
|
||||
AudioCapturedChanged(aAgent, AudioCaptureState::eCapturing);
|
||||
if (aAudible) {
|
||||
if (aAudible == AudibleState::eAudible) {
|
||||
AudioAudibleChanged(aAgent,
|
||||
AudibleState::eAudible,
|
||||
AudibleChangedReasons::eDataAudibleChanged);
|
||||
} else if (IsEnableAudioCompetingForAllAgents() && !aAudible) {
|
||||
} else if (IsEnableAudioCompetingForAllAgents() &&
|
||||
aAudible != AudibleState::eAudible) {
|
||||
NotifyAudioCompetingChanged(aAgent, true);
|
||||
}
|
||||
MaybeNotifyMediaBlocked(aAgent);
|
||||
}
|
||||
|
||||
void
|
||||
@ -1305,13 +1305,16 @@ AudioChannelService::AudioChannelWindow::AudioAudibleChanged(AudioChannelAgent*
|
||||
{
|
||||
MOZ_ASSERT(aAgent);
|
||||
|
||||
if (aAudible) {
|
||||
if (aAudible == AudibleState::eAudible) {
|
||||
AppendAudibleAgentIfNotContained(aAgent, aReason);
|
||||
} else {
|
||||
RemoveAudibleAgentIfContained(aAgent, aReason);
|
||||
}
|
||||
|
||||
NotifyAudioCompetingChanged(aAgent, aAudible);
|
||||
NotifyAudioCompetingChanged(aAgent, aAudible == AudibleState::eAudible);
|
||||
if (aAudible != AudibleState::eNotAudible) {
|
||||
MaybeNotifyMediaBlocked(aAgent);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -1368,7 +1371,9 @@ AudioChannelService::AudioChannelWindow::NotifyAudioAudibleChanged(nsPIDOMWindow
|
||||
AudibleChangedReasons aReason)
|
||||
{
|
||||
RefPtr<AudioPlaybackRunnable> runnable =
|
||||
new AudioPlaybackRunnable(aWindow, aAudible, aReason);
|
||||
new AudioPlaybackRunnable(aWindow,
|
||||
aAudible == AudibleState::eAudible,
|
||||
aReason);
|
||||
DebugOnly<nsresult> rv = NS_DispatchToCurrentThread(runnable);
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToCurrentThread failed");
|
||||
}
|
||||
|
@ -65,9 +65,15 @@ public:
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSIAUDIOCHANNELSERVICE
|
||||
|
||||
enum AudibleState : bool {
|
||||
eAudible = true,
|
||||
eNotAudible = false
|
||||
/**
|
||||
* eNotAudible : agent is not audible
|
||||
* eMaybeAudible : agent is not audible now, but it might be audible later
|
||||
* eAudible : agent is audible now
|
||||
*/
|
||||
enum AudibleState : uint8_t {
|
||||
eNotAudible = 0,
|
||||
eMaybeAudible = 1,
|
||||
eAudible = 2
|
||||
};
|
||||
|
||||
enum AudioCaptureState : bool {
|
||||
|
@ -163,7 +163,7 @@ interface nsIAudioChannelAgent : nsISupports
|
||||
* @param config
|
||||
* It contains the playback related states (volume/mute/suspend)
|
||||
*/
|
||||
void notifyStartedPlaying(in AudioPlaybackConfig config, in bool audible);
|
||||
void notifyStartedPlaying(in AudioPlaybackConfig config, in uint8_t audible);
|
||||
|
||||
/**
|
||||
* Notify the agent we no longer want to play.
|
||||
@ -183,5 +183,5 @@ interface nsIAudioChannelAgent : nsISupports
|
||||
* notify the actually audible state to other services which want to know
|
||||
* about that, ex. tab sound indicator.
|
||||
*/
|
||||
void notifyStartedAudible(in bool audible, in uint32_t reason);
|
||||
void notifyStartedAudible(in uint8_t audible, in uint32_t reason);
|
||||
};
|
||||
|
@ -753,7 +753,7 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
bool newAudibleState = IsOwnerAudible();
|
||||
AudibleState newAudibleState = IsOwnerAudible();
|
||||
if (mIsOwnerAudible == newAudibleState) {
|
||||
return;
|
||||
}
|
||||
@ -976,25 +976,25 @@ private:
|
||||
mSuspended == nsISuspendedTypes::SUSPENDED_BLOCK);
|
||||
}
|
||||
|
||||
bool
|
||||
AudibleState
|
||||
IsOwnerAudible() const
|
||||
{
|
||||
// Muted or the volume should not be ~0
|
||||
if (mOwner->Muted() || (std::fabs(mOwner->Volume()) <= 1e-7)) {
|
||||
return false;
|
||||
return AudioChannelService::AudibleState::eNotAudible;
|
||||
}
|
||||
|
||||
// No sound can be heard during suspending.
|
||||
if (IsSuspended()) {
|
||||
return false;
|
||||
// No audio track.
|
||||
if (!mOwner->HasAudio()) {
|
||||
return AudioChannelService::AudibleState::eNotAudible;
|
||||
}
|
||||
|
||||
// Silent audio track.
|
||||
if (!mOwner->mIsAudioTrackAudible) {
|
||||
return false;
|
||||
// Might be audible but not yet.
|
||||
if (mOwner->HasAudio() && !mOwner->mIsAudioTrackAudible) {
|
||||
return AudioChannelService::AudibleState::eMaybeAudible;
|
||||
}
|
||||
|
||||
return true;
|
||||
return AudioChannelService::AudibleState::eAudible;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -1064,8 +1064,8 @@ private:
|
||||
// and stop the audio channel agent. MediaElement can only be restarted by
|
||||
// play().
|
||||
SuspendTypes mSuspended;
|
||||
// True if media element is audible for users.
|
||||
bool mIsOwnerAudible;
|
||||
// Indicate whether media element is audible for users.
|
||||
AudibleState mIsOwnerAudible;
|
||||
bool mIsShutDown;
|
||||
};
|
||||
|
||||
@ -6976,7 +6976,12 @@ HTMLMediaElement::ShouldElementBePaused()
|
||||
void
|
||||
HTMLMediaElement::SetMediaInfo(const MediaInfo& aInfo)
|
||||
{
|
||||
const bool oldHasAudio = mMediaInfo.HasAudio();
|
||||
mMediaInfo = aInfo;
|
||||
if (aInfo.HasAudio() != oldHasAudio) {
|
||||
NotifyAudioPlaybackChanged(
|
||||
AudioChannelService::AudibleChangedReasons::eDataAudibleChanged);
|
||||
}
|
||||
if (mAudioChannelWrapper) {
|
||||
mAudioChannelWrapper->AudioCaptureStreamChangeIfNeeded();
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ typedef uint16_t nsMediaNetworkState;
|
||||
typedef uint16_t nsMediaReadyState;
|
||||
typedef uint32_t SuspendTypes;
|
||||
typedef uint32_t AudibleChangedReasons;
|
||||
typedef uint8_t AudibleState;
|
||||
|
||||
namespace mozilla {
|
||||
class DecoderDoctorDiagnostics;
|
||||
|
@ -1,463 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 "AccurateSeekTask.h"
|
||||
#include "MediaDecoderReaderWrapper.h"
|
||||
#include "mozilla/AbstractThread.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "nsPrintfCString.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
extern LazyLogModule gMediaDecoderLog;
|
||||
extern LazyLogModule gMediaSampleLog;
|
||||
|
||||
// avoid redefined macro in unified build
|
||||
#undef FMT
|
||||
#undef DECODER_LOG
|
||||
#undef SAMPLE_LOG
|
||||
#undef DECODER_WARN
|
||||
|
||||
#define FMT(x, ...) "[AccurateSeekTask] Decoder=%p " x, mDecoderID, ##__VA_ARGS__
|
||||
#define DECODER_LOG(...) MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, (FMT(__VA_ARGS__)))
|
||||
#define SAMPLE_LOG(...) MOZ_LOG(gMediaSampleLog, LogLevel::Debug, (FMT(__VA_ARGS__)))
|
||||
#define DECODER_WARN(...) NS_WARNING(nsPrintfCString(FMT(__VA_ARGS__)).get())
|
||||
|
||||
AccurateSeekTask::AccurateSeekTask(const void* aDecoderID,
|
||||
AbstractThread* aThread,
|
||||
MediaDecoderReaderWrapper* aReader,
|
||||
const SeekTarget& aTarget,
|
||||
const MediaInfo& aInfo,
|
||||
const media::TimeUnit& aEnd,
|
||||
int64_t aCurrentMediaTime)
|
||||
: SeekTask(aDecoderID, aThread, aReader, aTarget)
|
||||
, mCurrentTimeBeforeSeek(media::TimeUnit::FromMicroseconds(aCurrentMediaTime))
|
||||
, mAudioRate(aInfo.mAudio.mRate)
|
||||
, mDoneAudioSeeking(!aInfo.HasAudio() || aTarget.IsVideoOnly())
|
||||
, mDoneVideoSeeking(!aInfo.HasVideo())
|
||||
{
|
||||
AssertOwnerThread();
|
||||
}
|
||||
|
||||
AccurateSeekTask::~AccurateSeekTask()
|
||||
{
|
||||
AssertOwnerThread();
|
||||
MOZ_ASSERT(mIsDiscarded);
|
||||
}
|
||||
|
||||
void
|
||||
AccurateSeekTask::Discard()
|
||||
{
|
||||
AssertOwnerThread();
|
||||
|
||||
// Disconnect MDSM.
|
||||
RejectIfExist(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
|
||||
|
||||
// Disconnect MediaDecoderReaderWrapper.
|
||||
mSeekRequest.DisconnectIfExists();
|
||||
|
||||
mIsDiscarded = true;
|
||||
}
|
||||
|
||||
bool
|
||||
AccurateSeekTask::NeedToResetMDSM() const
|
||||
{
|
||||
AssertOwnerThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t
|
||||
AccurateSeekTask::CalculateNewCurrentTime() const
|
||||
{
|
||||
AssertOwnerThread();
|
||||
|
||||
const int64_t seekTime = mTarget.GetTime().ToMicroseconds();
|
||||
|
||||
// For the accurate seek, we always set the newCurrentTime = seekTime so that
|
||||
// the updated HTMLMediaElement.currentTime will always be the seek target;
|
||||
// we rely on the MediaSink to handles the gap between the newCurrentTime and
|
||||
// the real decoded samples' start time.
|
||||
if (mTarget.IsAccurate()) {
|
||||
return seekTime;
|
||||
}
|
||||
|
||||
// For the fast seek, we update the newCurrentTime with the decoded audio and
|
||||
// video samples, set it to be the one which is closet to the seekTime.
|
||||
if (mTarget.IsFast()) {
|
||||
|
||||
// A situation that both audio and video approaches the end.
|
||||
if (!mSeekedAudioData && !mSeekedVideoData) {
|
||||
return seekTime;
|
||||
}
|
||||
|
||||
const int64_t audioStart = mSeekedAudioData ? mSeekedAudioData->mTime : INT64_MAX;
|
||||
const int64_t videoStart = mSeekedVideoData ? mSeekedVideoData->mTime : INT64_MAX;
|
||||
const int64_t audioGap = std::abs(audioStart - seekTime);
|
||||
const int64_t videoGap = std::abs(videoStart - seekTime);
|
||||
return audioGap <= videoGap ? audioStart : videoStart;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(false, "AccurateSeekTask doesn't handle other seek types.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
AccurateSeekTask::HandleAudioDecoded(MediaData* aAudio)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
MOZ_ASSERT(!mSeekTaskPromise.IsEmpty(), "Seek shouldn't be finished");
|
||||
|
||||
RefPtr<MediaData> audio(aAudio);
|
||||
MOZ_ASSERT(audio);
|
||||
|
||||
// The MDSM::mDecodedAudioEndTime will be updated once the whole SeekTask is
|
||||
// resolved.
|
||||
|
||||
SAMPLE_LOG("OnAudioDecoded [%lld,%lld]", audio->mTime, audio->GetEndTime());
|
||||
|
||||
// Video-only seek doesn't reset audio decoder. There might be pending audio
|
||||
// requests when AccurateSeekTask::Seek() begins. We will just store the data
|
||||
// without checking |mDiscontinuity| or calling DropAudioUpToSeekTarget().
|
||||
if (mTarget.IsVideoOnly()) {
|
||||
mSeekedAudioData = audio.forget();
|
||||
return;
|
||||
}
|
||||
|
||||
AdjustFastSeekIfNeeded(audio);
|
||||
|
||||
if (mTarget.IsFast()) {
|
||||
// Non-precise seek; we can stop the seek at the first sample.
|
||||
mSeekedAudioData = audio;
|
||||
mDoneAudioSeeking = true;
|
||||
} else {
|
||||
nsresult rv = DropAudioUpToSeekTarget(audio);
|
||||
if (NS_FAILED(rv)) {
|
||||
RejectIfExist(rv, __func__);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mDoneAudioSeeking) {
|
||||
RequestAudioData();
|
||||
return;
|
||||
}
|
||||
MaybeFinishSeek();
|
||||
}
|
||||
|
||||
void
|
||||
AccurateSeekTask::HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
MOZ_ASSERT(!mSeekTaskPromise.IsEmpty(), "Seek shouldn't be finished");
|
||||
|
||||
RefPtr<MediaData> video(aVideo);
|
||||
MOZ_ASSERT(video);
|
||||
|
||||
// The MDSM::mDecodedVideoEndTime will be updated once the whole SeekTask is
|
||||
// resolved.
|
||||
|
||||
SAMPLE_LOG("OnVideoDecoded [%lld,%lld]", video->mTime, video->GetEndTime());
|
||||
|
||||
AdjustFastSeekIfNeeded(video);
|
||||
|
||||
if (mTarget.IsFast()) {
|
||||
// Non-precise seek. We can stop the seek at the first sample.
|
||||
mSeekedVideoData = video;
|
||||
mDoneVideoSeeking = true;
|
||||
} else {
|
||||
nsresult rv = DropVideoUpToSeekTarget(video.get());
|
||||
if (NS_FAILED(rv)) {
|
||||
RejectIfExist(rv, __func__);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mDoneVideoSeeking) {
|
||||
RequestVideoData();
|
||||
return;
|
||||
}
|
||||
MaybeFinishSeek();
|
||||
}
|
||||
|
||||
void
|
||||
AccurateSeekTask::HandleNotDecoded(MediaData::Type aType, const MediaResult& aError)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
MOZ_ASSERT(!mSeekTaskPromise.IsEmpty(), "Seek shouldn't be finished");
|
||||
|
||||
SAMPLE_LOG("OnNotDecoded type=%d reason=%u", aType, aError.Code());
|
||||
|
||||
// Ignore pending requests from video-only seek.
|
||||
if (aType == MediaData::AUDIO_DATA && mTarget.IsVideoOnly()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the decoder is waiting for data, we tell it to call us back when the
|
||||
// data arrives.
|
||||
if (aError == NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA) {
|
||||
mReader->WaitForData(aType);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aError == NS_ERROR_DOM_MEDIA_CANCELED) {
|
||||
if (aType == MediaData::AUDIO_DATA) {
|
||||
RequestAudioData();
|
||||
} else {
|
||||
RequestVideoData();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (aError == NS_ERROR_DOM_MEDIA_END_OF_STREAM) {
|
||||
if (aType == MediaData::AUDIO_DATA) {
|
||||
mIsAudioQueueFinished = true;
|
||||
mDoneAudioSeeking = true;
|
||||
} else {
|
||||
mIsVideoQueueFinished = true;
|
||||
mDoneVideoSeeking = true;
|
||||
if (mFirstVideoFrameAfterSeek) {
|
||||
// Hit the end of stream. Move mFirstVideoFrameAfterSeek into
|
||||
// mSeekedVideoData so we have something to display after seeking.
|
||||
mSeekedVideoData = mFirstVideoFrameAfterSeek.forget();
|
||||
}
|
||||
}
|
||||
MaybeFinishSeek();
|
||||
return;
|
||||
}
|
||||
|
||||
// This is a decode error, delegate to the generic error path.
|
||||
RejectIfExist(aError, __func__);
|
||||
}
|
||||
|
||||
void
|
||||
AccurateSeekTask::HandleAudioWaited(MediaData::Type aType)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
|
||||
// Ignore pending requests from video-only seek.
|
||||
if (mTarget.IsVideoOnly()) {
|
||||
return;
|
||||
}
|
||||
RequestAudioData();
|
||||
}
|
||||
|
||||
void
|
||||
AccurateSeekTask::HandleVideoWaited(MediaData::Type aType)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
RequestVideoData();
|
||||
}
|
||||
|
||||
void
|
||||
AccurateSeekTask::HandleNotWaited(const WaitForDataRejectValue& aRejection)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
}
|
||||
|
||||
RefPtr<AccurateSeekTask::SeekTaskPromise>
|
||||
AccurateSeekTask::Seek(const media::TimeUnit& aDuration)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
|
||||
// Do the seek.
|
||||
mSeekRequest.Begin(mReader->Seek(mTarget, aDuration)
|
||||
->Then(OwnerThread(), __func__, this,
|
||||
&AccurateSeekTask::OnSeekResolved, &AccurateSeekTask::OnSeekRejected));
|
||||
|
||||
return mSeekTaskPromise.Ensure(__func__);
|
||||
}
|
||||
|
||||
void
|
||||
AccurateSeekTask::RequestAudioData()
|
||||
{
|
||||
AssertOwnerThread();
|
||||
MOZ_ASSERT(!mDoneAudioSeeking);
|
||||
MOZ_ASSERT(!mReader->IsRequestingAudioData());
|
||||
MOZ_ASSERT(!mReader->IsWaitingAudioData());
|
||||
mReader->RequestAudioData();
|
||||
}
|
||||
|
||||
void
|
||||
AccurateSeekTask::RequestVideoData()
|
||||
{
|
||||
AssertOwnerThread();
|
||||
MOZ_ASSERT(!mDoneVideoSeeking);
|
||||
MOZ_ASSERT(!mReader->IsRequestingVideoData());
|
||||
MOZ_ASSERT(!mReader->IsWaitingVideoData());
|
||||
mReader->RequestVideoData(false, media::TimeUnit());
|
||||
}
|
||||
|
||||
nsresult
|
||||
AccurateSeekTask::DropAudioUpToSeekTarget(MediaData* aSample)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
|
||||
RefPtr<AudioData> audio(aSample->As<AudioData>());
|
||||
MOZ_ASSERT(audio && mTarget.IsAccurate());
|
||||
|
||||
CheckedInt64 sampleDuration = FramesToUsecs(audio->mFrames, mAudioRate);
|
||||
if (!sampleDuration.isValid()) {
|
||||
return NS_ERROR_DOM_MEDIA_OVERFLOW_ERR;
|
||||
}
|
||||
|
||||
if (audio->mTime + sampleDuration.value() <= mTarget.GetTime().ToMicroseconds()) {
|
||||
// Our seek target lies after the frames in this AudioData. Don't
|
||||
// push it onto the audio queue, and keep decoding forwards.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (audio->mTime > mTarget.GetTime().ToMicroseconds()) {
|
||||
// The seek target doesn't lie in the audio block just after the last
|
||||
// audio frames we've seen which were before the seek target. This
|
||||
// could have been the first audio data we've seen after seek, i.e. the
|
||||
// seek terminated after the seek target in the audio stream. Just
|
||||
// abort the audio decode-to-target, the state machine will play
|
||||
// silence to cover the gap. Typically this happens in poorly muxed
|
||||
// files.
|
||||
DECODER_WARN("Audio not synced after seek, maybe a poorly muxed file?");
|
||||
mSeekedAudioData = audio;
|
||||
mDoneAudioSeeking = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// The seek target lies somewhere in this AudioData's frames, strip off
|
||||
// any frames which lie before the seek target, so we'll begin playback
|
||||
// exactly at the seek target.
|
||||
NS_ASSERTION(mTarget.GetTime().ToMicroseconds() >= audio->mTime,
|
||||
"Target must at or be after data start.");
|
||||
NS_ASSERTION(mTarget.GetTime().ToMicroseconds() < audio->mTime + sampleDuration.value(),
|
||||
"Data must end after target.");
|
||||
|
||||
CheckedInt64 framesToPrune =
|
||||
UsecsToFrames(mTarget.GetTime().ToMicroseconds() - audio->mTime, mAudioRate);
|
||||
if (!framesToPrune.isValid()) {
|
||||
return NS_ERROR_DOM_MEDIA_OVERFLOW_ERR;
|
||||
}
|
||||
if (framesToPrune.value() > audio->mFrames) {
|
||||
// We've messed up somehow. Don't try to trim frames, the |frames|
|
||||
// variable below will overflow.
|
||||
DECODER_WARN("Can't prune more frames that we have!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
uint32_t frames = audio->mFrames - static_cast<uint32_t>(framesToPrune.value());
|
||||
uint32_t channels = audio->mChannels;
|
||||
AlignedAudioBuffer audioData(frames * channels);
|
||||
if (!audioData) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
memcpy(audioData.get(),
|
||||
audio->mAudioData.get() + (framesToPrune.value() * channels),
|
||||
frames * channels * sizeof(AudioDataValue));
|
||||
CheckedInt64 duration = FramesToUsecs(frames, mAudioRate);
|
||||
if (!duration.isValid()) {
|
||||
return NS_ERROR_DOM_MEDIA_OVERFLOW_ERR;
|
||||
}
|
||||
RefPtr<AudioData> data(new AudioData(audio->mOffset,
|
||||
mTarget.GetTime().ToMicroseconds(),
|
||||
duration.value(),
|
||||
frames,
|
||||
Move(audioData),
|
||||
channels,
|
||||
audio->mRate));
|
||||
MOZ_ASSERT(!mSeekedAudioData, "Should be the 1st sample after seeking");
|
||||
mSeekedAudioData = data;
|
||||
mDoneAudioSeeking = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
AccurateSeekTask::DropVideoUpToSeekTarget(MediaData* aSample)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
|
||||
RefPtr<VideoData> video(aSample->As<VideoData>());
|
||||
MOZ_ASSERT(video);
|
||||
DECODER_LOG("DropVideoUpToSeekTarget() frame [%lld, %lld]",
|
||||
video->mTime, video->GetEndTime());
|
||||
const int64_t target = mTarget.GetTime().ToMicroseconds();
|
||||
|
||||
// If the frame end time is less than the seek target, we won't want
|
||||
// to display this frame after the seek, so discard it.
|
||||
if (target >= video->GetEndTime()) {
|
||||
DECODER_LOG("DropVideoUpToSeekTarget() pop video frame [%lld, %lld] target=%lld",
|
||||
video->mTime, video->GetEndTime(), target);
|
||||
mFirstVideoFrameAfterSeek = video;
|
||||
} else {
|
||||
if (target >= video->mTime && video->GetEndTime() >= target) {
|
||||
// The seek target lies inside this frame's time slice. Adjust the frame's
|
||||
// start time to match the seek target. We do this by replacing the
|
||||
// first frame with a shallow copy which has the new timestamp.
|
||||
RefPtr<VideoData> temp = VideoData::ShallowCopyUpdateTimestamp(video.get(), target);
|
||||
video = temp;
|
||||
}
|
||||
mFirstVideoFrameAfterSeek = nullptr;
|
||||
|
||||
DECODER_LOG("DropVideoUpToSeekTarget() found video frame [%lld, %lld] containing target=%lld",
|
||||
video->mTime, video->GetEndTime(), target);
|
||||
|
||||
MOZ_ASSERT(!mSeekedVideoData, "Should be the 1st sample after seeking");
|
||||
mSeekedVideoData = video;
|
||||
mDoneVideoSeeking = true;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
AccurateSeekTask::MaybeFinishSeek()
|
||||
{
|
||||
AssertOwnerThread();
|
||||
if (mDoneAudioSeeking && mDoneVideoSeeking) {
|
||||
Resolve(__func__); // Call to MDSM::SeekCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AccurateSeekTask::OnSeekResolved(media::TimeUnit)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
|
||||
mSeekRequest.Complete();
|
||||
// We must decode the first samples of active streams, so we can determine
|
||||
// the new stream time. So dispatch tasks to do that.
|
||||
if (!mDoneVideoSeeking) {
|
||||
RequestVideoData();
|
||||
}
|
||||
if (!mDoneAudioSeeking) {
|
||||
RequestAudioData();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AccurateSeekTask::OnSeekRejected(nsresult aResult)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
|
||||
mSeekRequest.Complete();
|
||||
MOZ_ASSERT(NS_FAILED(aResult), "Cancels should also disconnect mSeekRequest");
|
||||
RejectIfExist(aResult, __func__);
|
||||
}
|
||||
|
||||
void
|
||||
AccurateSeekTask::AdjustFastSeekIfNeeded(MediaData* aSample)
|
||||
{
|
||||
AssertOwnerThread();
|
||||
if (mTarget.IsFast() &&
|
||||
mTarget.GetTime() > mCurrentTimeBeforeSeek &&
|
||||
aSample->mTime < mCurrentTimeBeforeSeek.ToMicroseconds()) {
|
||||
// We are doing a fastSeek, but we ended up *before* the previous
|
||||
// playback position. This is surprising UX, so switch to an accurate
|
||||
// seek and decode to the seek target. This is not conformant to the
|
||||
// spec, fastSeek should always be fast, but until we get the time to
|
||||
// change all Readers to seek to the keyframe after the currentTime
|
||||
// in this case, we'll just decode forward. Bug 1026330.
|
||||
mTarget.SetType(SeekTarget::Accurate);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
@ -1,87 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef ACCURATE_SEEK_TASK_H
|
||||
#define ACCURATE_SEEK_TASK_H
|
||||
|
||||
#include "SeekTask.h"
|
||||
#include "MediaDecoderReader.h"
|
||||
#include "SeekJob.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class AccurateSeekTask final : public SeekTask {
|
||||
public:
|
||||
AccurateSeekTask(const void* aDecoderID,
|
||||
AbstractThread* aThread,
|
||||
MediaDecoderReaderWrapper* aReader,
|
||||
const SeekTarget& aTarget,
|
||||
const MediaInfo& aInfo,
|
||||
const media::TimeUnit& aEnd,
|
||||
int64_t aCurrentMediaTime);
|
||||
|
||||
void Discard() override;
|
||||
|
||||
RefPtr<SeekTaskPromise> Seek(const media::TimeUnit& aDuration) override;
|
||||
|
||||
bool NeedToResetMDSM() const override;
|
||||
|
||||
int64_t CalculateNewCurrentTime() const override;
|
||||
|
||||
void HandleAudioDecoded(MediaData* aAudio) override;
|
||||
|
||||
void HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) override;
|
||||
|
||||
void HandleNotDecoded(MediaData::Type aType, const MediaResult& aError) override;
|
||||
|
||||
void HandleAudioWaited(MediaData::Type aType) override;
|
||||
|
||||
void HandleVideoWaited(MediaData::Type aType) override;
|
||||
|
||||
void HandleNotWaited(const WaitForDataRejectValue& aRejection) override;
|
||||
|
||||
private:
|
||||
~AccurateSeekTask();
|
||||
|
||||
void RequestVideoData();
|
||||
|
||||
void RequestAudioData();
|
||||
|
||||
nsresult DropAudioUpToSeekTarget(MediaData* aSample);
|
||||
|
||||
nsresult DropVideoUpToSeekTarget(MediaData* aSample);
|
||||
|
||||
void MaybeFinishSeek();
|
||||
|
||||
void OnSeekResolved(media::TimeUnit);
|
||||
|
||||
void OnSeekRejected(nsresult aResult);
|
||||
|
||||
void AdjustFastSeekIfNeeded(MediaData* aSample);
|
||||
|
||||
/*
|
||||
* Internal state.
|
||||
*/
|
||||
const media::TimeUnit mCurrentTimeBeforeSeek;
|
||||
const uint32_t mAudioRate; // Audio sample rate.
|
||||
bool mDoneAudioSeeking;
|
||||
bool mDoneVideoSeeking;
|
||||
|
||||
// This temporarily stores the first frame we decode after we seek.
|
||||
// This is so that if we hit end of stream while we're decoding to reach
|
||||
// the seek target, we will still have a frame that we can display as the
|
||||
// last frame in the media.
|
||||
RefPtr<MediaData> mFirstVideoFrameAfterSeek;
|
||||
|
||||
/*
|
||||
* Track the current seek promise made by the reader.
|
||||
*/
|
||||
MozPromiseRequestHolder<MediaDecoderReader::SeekPromise> mSeekRequest;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* ACCURATE_SEEK_TASK_H */
|
@ -33,9 +33,9 @@ MediaDecoderReaderWrapper::ReadMetadata()
|
||||
MOZ_ASSERT(!mShutdown);
|
||||
return InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__,
|
||||
&MediaDecoderReader::AsyncReadMetadata)
|
||||
->ThenPromise(mOwnerThread, __func__, this,
|
||||
&MediaDecoderReaderWrapper::OnMetadataRead,
|
||||
&MediaDecoderReaderWrapper::OnMetadataNotRead);
|
||||
->Then(mOwnerThread, __func__, this,
|
||||
&MediaDecoderReaderWrapper::OnMetadataRead,
|
||||
&MediaDecoderReaderWrapper::OnMetadataNotRead);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -37,7 +37,6 @@
|
||||
#include "nsDeque.h"
|
||||
#include "prenv.h"
|
||||
|
||||
#include "AccurateSeekTask.h"
|
||||
#include "AudioSegment.h"
|
||||
#include "DOMMediaStream.h"
|
||||
#include "ImageContainer.h"
|
||||
@ -86,6 +85,7 @@ using namespace mozilla::media;
|
||||
#define SLOG(x, ...) MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, (SFMT(x, ##__VA_ARGS__)))
|
||||
#define SWARN(x, ...) NS_WARNING(nsPrintfCString(SFMT(x, ##__VA_ARGS__)).get())
|
||||
#define SDUMP(x, ...) NS_DebugBreak(NS_DEBUG_WARNING, nsPrintfCString(SFMT(x, ##__VA_ARGS__)).get(), nullptr, nullptr, -1)
|
||||
#define SSAMPLELOG(x, ...) MOZ_LOG(gMediaSampleLog, LogLevel::Debug, (SFMT(x, ##__VA_ARGS__)))
|
||||
|
||||
// Certain constants get stored as member variables and then adjusted by various
|
||||
// scale factors on a per-decoder basis. We want to make sure to avoid using these
|
||||
@ -268,6 +268,9 @@ protected:
|
||||
return s->Enter(Move(aArgs)...);
|
||||
}
|
||||
|
||||
RefPtr<MediaDecoder::SeekPromise>
|
||||
SetSeekingState(SeekJob&& aSeekJob, EventVisibility aVisibility);
|
||||
|
||||
// Take a raw pointer in order not to change the life cycle of MDSM.
|
||||
// It is guaranteed to be valid by MDSM.
|
||||
Master* mMaster;
|
||||
@ -770,20 +773,7 @@ public:
|
||||
Reader()->SetVideoBlankDecode(false);
|
||||
}
|
||||
|
||||
// Create a new SeekTask instance for the incoming seek task.
|
||||
if (mSeekJob.mTarget.IsAccurate() ||
|
||||
mSeekJob.mTarget.IsFast()) {
|
||||
mSeekTask = new AccurateSeekTask(
|
||||
mMaster->mDecoderID, OwnerThread(), Reader(), mSeekJob.mTarget,
|
||||
Info(), mMaster->Duration(), mMaster->GetMediaTime());
|
||||
} else if (mSeekJob.mTarget.IsNextFrame()) {
|
||||
mSeekTask = new NextFrameSeekTask(
|
||||
mMaster->mDecoderID, OwnerThread(), Reader(), mSeekJob.mTarget,
|
||||
Info(), mMaster->Duration(),mMaster->GetMediaTime(),
|
||||
AudioQueue(), VideoQueue());
|
||||
} else {
|
||||
MOZ_DIAGNOSTIC_ASSERT(false, "Cannot handle this seek task.");
|
||||
}
|
||||
CreateSeekTask();
|
||||
|
||||
// Don't stop playback for a video-only seek since audio is playing.
|
||||
if (!mSeekJob.mTarget.IsVideoOnly()) {
|
||||
@ -799,29 +789,14 @@ public:
|
||||
mMaster->UpdateNextFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING);
|
||||
}
|
||||
|
||||
// Reset our state machine and decoding pipeline before seeking.
|
||||
if (mSeekTask->NeedToResetMDSM()) {
|
||||
if (mSeekJob.mTarget.IsVideoOnly()) {
|
||||
mMaster->Reset(TrackInfo::kVideoTrack);
|
||||
} else {
|
||||
mMaster->Reset();
|
||||
}
|
||||
}
|
||||
ResetMDSM();
|
||||
|
||||
// Do the seek.
|
||||
mSeekTaskRequest.Begin(mSeekTask->Seek(mMaster->Duration())
|
||||
->Then(OwnerThread(), __func__,
|
||||
[this] (const SeekTaskResolveValue& aValue) {
|
||||
OnSeekTaskResolved(aValue);
|
||||
},
|
||||
[this] (const SeekTaskRejectValue& aValue) {
|
||||
OnSeekTaskRejected(aValue);
|
||||
}));
|
||||
DoSeek();
|
||||
|
||||
return mSeekJob.mPromise.Ensure(__func__);
|
||||
}
|
||||
|
||||
void Exit() override
|
||||
virtual void Exit() override
|
||||
{
|
||||
mSeekTaskRequest.DisconnectIfExists();
|
||||
mSeekJob.RejectIfExists(__func__);
|
||||
@ -874,7 +849,535 @@ public:
|
||||
MOZ_ASSERT(false, "Shouldn't have suspended video decoding.");
|
||||
}
|
||||
|
||||
protected:
|
||||
SeekJob mSeekJob;
|
||||
RefPtr<SeekTask> mSeekTask;
|
||||
MozPromiseRequestHolder<SeekTask::SeekTaskPromise> mSeekTaskRequest;
|
||||
|
||||
void SeekCompleted();
|
||||
|
||||
private:
|
||||
virtual void CreateSeekTask() = 0;
|
||||
|
||||
virtual void ResetMDSM() = 0;
|
||||
|
||||
virtual void DoSeek() = 0;
|
||||
|
||||
virtual int64_t CalculateNewCurrentTime() const = 0;
|
||||
};
|
||||
|
||||
class MediaDecoderStateMachine::AccurateSeekingState
|
||||
: public MediaDecoderStateMachine::SeekingState
|
||||
{
|
||||
public:
|
||||
explicit AccurateSeekingState(Master* aPtr) : SeekingState(aPtr)
|
||||
{
|
||||
}
|
||||
|
||||
RefPtr<MediaDecoder::SeekPromise> Enter(SeekJob aSeekJob,
|
||||
EventVisibility aVisibility)
|
||||
{
|
||||
MOZ_ASSERT(aSeekJob.mTarget.IsAccurate() || aSeekJob.mTarget.IsFast());
|
||||
return SeekingState::Enter(Move(aSeekJob), aVisibility);
|
||||
}
|
||||
|
||||
void Exit() override
|
||||
{
|
||||
// Disconnect MediaDecoder.
|
||||
mSeekJob.RejectIfExists(__func__);
|
||||
|
||||
// Disconnect MediaDecoderReaderWrapper.
|
||||
mSeekRequest.DisconnectIfExists();
|
||||
}
|
||||
|
||||
void HandleAudioDecoded(MediaData* aAudio) override
|
||||
{
|
||||
MOZ_ASSERT(!mDoneAudioSeeking || !mDoneVideoSeeking, "Seek shouldn't be finished");
|
||||
|
||||
RefPtr<MediaData> audio(aAudio);
|
||||
MOZ_ASSERT(audio);
|
||||
|
||||
// The MDSM::mDecodedAudioEndTime will be updated once the whole SeekTask is
|
||||
// resolved.
|
||||
|
||||
SSAMPLELOG("HandleAudioDecoded [%lld,%lld]", audio->mTime, audio->GetEndTime());
|
||||
|
||||
// Video-only seek doesn't reset audio decoder. There might be pending audio
|
||||
// requests when AccurateSeekTask::Seek() begins. We will just store the data
|
||||
// without checking |mDiscontinuity| or calling DropAudioUpToSeekTarget().
|
||||
if (mSeekJob.mTarget.IsVideoOnly()) {
|
||||
mSeekedAudioData = audio.forget();
|
||||
return;
|
||||
}
|
||||
|
||||
AdjustFastSeekIfNeeded(audio);
|
||||
|
||||
if (mSeekJob.mTarget.IsFast()) {
|
||||
// Non-precise seek; we can stop the seek at the first sample.
|
||||
mSeekedAudioData = audio;
|
||||
mDoneAudioSeeking = true;
|
||||
} else {
|
||||
nsresult rv = DropAudioUpToSeekTarget(audio);
|
||||
if (NS_FAILED(rv)) {
|
||||
OnSeekTaskRejected(rv);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mDoneAudioSeeking) {
|
||||
RequestAudioData();
|
||||
return;
|
||||
}
|
||||
MaybeFinishSeek();
|
||||
}
|
||||
|
||||
void HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) override
|
||||
{
|
||||
MOZ_ASSERT(!mDoneAudioSeeking || !mDoneVideoSeeking, "Seek shouldn't be finished");
|
||||
|
||||
RefPtr<MediaData> video(aVideo);
|
||||
MOZ_ASSERT(video);
|
||||
|
||||
// The MDSM::mDecodedVideoEndTime will be updated once the whole SeekTask is
|
||||
// resolved.
|
||||
|
||||
SSAMPLELOG("HandleVideoDecoded [%lld,%lld]", video->mTime, video->GetEndTime());
|
||||
|
||||
AdjustFastSeekIfNeeded(video);
|
||||
|
||||
if (mSeekJob.mTarget.IsFast()) {
|
||||
// Non-precise seek. We can stop the seek at the first sample.
|
||||
mSeekedVideoData = video;
|
||||
mDoneVideoSeeking = true;
|
||||
} else {
|
||||
nsresult rv = DropVideoUpToSeekTarget(video.get());
|
||||
if (NS_FAILED(rv)) {
|
||||
OnSeekTaskRejected(rv);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mDoneVideoSeeking) {
|
||||
RequestVideoData();
|
||||
return;
|
||||
}
|
||||
MaybeFinishSeek();
|
||||
}
|
||||
|
||||
void HandleNotDecoded(MediaData::Type aType, const MediaResult& aError) override
|
||||
{
|
||||
MOZ_ASSERT(!mDoneAudioSeeking || !mDoneVideoSeeking, "Seek shouldn't be finished");
|
||||
|
||||
SSAMPLELOG("OnNotDecoded type=%d reason=%u", aType, aError.Code());
|
||||
|
||||
// Ignore pending requests from video-only seek.
|
||||
if (aType == MediaData::AUDIO_DATA && mSeekJob.mTarget.IsVideoOnly()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the decoder is waiting for data, we tell it to call us back when the
|
||||
// data arrives.
|
||||
if (aError == NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA) {
|
||||
Reader()->WaitForData(aType);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aError == NS_ERROR_DOM_MEDIA_CANCELED) {
|
||||
if (aType == MediaData::AUDIO_DATA) {
|
||||
RequestAudioData();
|
||||
} else {
|
||||
RequestVideoData();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (aError == NS_ERROR_DOM_MEDIA_END_OF_STREAM) {
|
||||
if (aType == MediaData::AUDIO_DATA) {
|
||||
mIsAudioQueueFinished = true;
|
||||
mDoneAudioSeeking = true;
|
||||
} else {
|
||||
mIsVideoQueueFinished = true;
|
||||
mDoneVideoSeeking = true;
|
||||
if (mFirstVideoFrameAfterSeek) {
|
||||
// Hit the end of stream. Move mFirstVideoFrameAfterSeek into
|
||||
// mSeekedVideoData so we have something to display after seeking.
|
||||
mSeekedVideoData = mFirstVideoFrameAfterSeek.forget();
|
||||
}
|
||||
}
|
||||
MaybeFinishSeek();
|
||||
return;
|
||||
}
|
||||
|
||||
// This is a decode error, delegate to the generic error path.
|
||||
OnSeekTaskRejected(aError);
|
||||
}
|
||||
|
||||
void HandleAudioWaited(MediaData::Type aType) override
|
||||
{
|
||||
MOZ_ASSERT(!mDoneAudioSeeking || !mDoneVideoSeeking, "Seek shouldn't be finished");
|
||||
|
||||
// Ignore pending requests from video-only seek.
|
||||
if (mSeekJob.mTarget.IsVideoOnly()) {
|
||||
return;
|
||||
}
|
||||
RequestAudioData();
|
||||
}
|
||||
|
||||
void HandleVideoWaited(MediaData::Type aType) override
|
||||
{
|
||||
MOZ_ASSERT(!mDoneAudioSeeking || !mDoneVideoSeeking, "Seek shouldn't be finished");
|
||||
|
||||
RequestVideoData();
|
||||
}
|
||||
|
||||
void HandleNotWaited(const WaitForDataRejectValue& aRejection) override
|
||||
{
|
||||
MOZ_ASSERT(!mDoneAudioSeeking || !mDoneVideoSeeking, "Seek shouldn't be finished");
|
||||
}
|
||||
|
||||
private:
|
||||
void CreateSeekTask() override
|
||||
{
|
||||
mCurrentTimeBeforeSeek = TimeUnit::FromMicroseconds(mMaster->GetMediaTime());
|
||||
mDoneAudioSeeking = !Info().HasAudio() || mSeekJob.mTarget.IsVideoOnly();
|
||||
mDoneVideoSeeking = !Info().HasVideo();
|
||||
}
|
||||
|
||||
void ResetMDSM() override
|
||||
{
|
||||
if (mSeekJob.mTarget.IsVideoOnly()) {
|
||||
mMaster->Reset(TrackInfo::kVideoTrack);
|
||||
} else {
|
||||
mMaster->Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void DoSeek() override
|
||||
{
|
||||
// Request the demuxer to perform seek.
|
||||
mSeekRequest.Begin(Reader()->Seek(mSeekJob.mTarget, mMaster->Duration())
|
||||
->Then(OwnerThread(), __func__,
|
||||
[this] (media::TimeUnit aUnit) {
|
||||
OnSeekResolved(aUnit);
|
||||
},
|
||||
[this] (nsresult aResult) {
|
||||
OnSeekRejected(aResult);
|
||||
}));
|
||||
}
|
||||
|
||||
int64_t CalculateNewCurrentTime() const override
|
||||
{
|
||||
const int64_t seekTime = mSeekJob.mTarget.GetTime().ToMicroseconds();
|
||||
|
||||
// For the accurate seek, we always set the newCurrentTime = seekTime so that
|
||||
// the updated HTMLMediaElement.currentTime will always be the seek target;
|
||||
// we rely on the MediaSink to handles the gap between the newCurrentTime and
|
||||
// the real decoded samples' start time.
|
||||
if (mSeekJob.mTarget.IsAccurate()) {
|
||||
return seekTime;
|
||||
}
|
||||
|
||||
// For the fast seek, we update the newCurrentTime with the decoded audio and
|
||||
// video samples, set it to be the one which is closet to the seekTime.
|
||||
if (mSeekJob.mTarget.IsFast()) {
|
||||
|
||||
// A situation that both audio and video approaches the end.
|
||||
if (!mSeekedAudioData && !mSeekedVideoData) {
|
||||
return seekTime;
|
||||
}
|
||||
|
||||
const int64_t audioStart = mSeekedAudioData ? mSeekedAudioData->mTime : INT64_MAX;
|
||||
const int64_t videoStart = mSeekedVideoData ? mSeekedVideoData->mTime : INT64_MAX;
|
||||
const int64_t audioGap = std::abs(audioStart - seekTime);
|
||||
const int64_t videoGap = std::abs(videoStart - seekTime);
|
||||
return audioGap <= videoGap ? audioStart : videoStart;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(false, "AccurateSeekTask doesn't handle other seek types.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void OnSeekResolved(media::TimeUnit) {
|
||||
mSeekRequest.Complete();
|
||||
|
||||
// We must decode the first samples of active streams, so we can determine
|
||||
// the new stream time. So dispatch tasks to do that.
|
||||
if (!mDoneVideoSeeking) {
|
||||
RequestVideoData();
|
||||
}
|
||||
if (!mDoneAudioSeeking) {
|
||||
RequestAudioData();
|
||||
}
|
||||
}
|
||||
|
||||
void OnSeekRejected(nsresult aResult) {
|
||||
mSeekRequest.Complete();
|
||||
|
||||
MOZ_ASSERT(NS_FAILED(aResult), "Cancels should also disconnect mSeekRequest");
|
||||
OnSeekTaskRejected(aResult);
|
||||
}
|
||||
|
||||
void RequestAudioData()
|
||||
{
|
||||
MOZ_ASSERT(!mDoneAudioSeeking);
|
||||
MOZ_ASSERT(!Reader()->IsRequestingAudioData());
|
||||
MOZ_ASSERT(!Reader()->IsWaitingAudioData());
|
||||
Reader()->RequestAudioData();
|
||||
}
|
||||
|
||||
void RequestVideoData()
|
||||
{
|
||||
MOZ_ASSERT(!mDoneVideoSeeking);
|
||||
MOZ_ASSERT(!Reader()->IsRequestingVideoData());
|
||||
MOZ_ASSERT(!Reader()->IsWaitingVideoData());
|
||||
Reader()->RequestVideoData(false, media::TimeUnit());
|
||||
}
|
||||
|
||||
void AdjustFastSeekIfNeeded(MediaData* aSample)
|
||||
{
|
||||
if (mSeekJob.mTarget.IsFast() &&
|
||||
mSeekJob.mTarget.GetTime() > mCurrentTimeBeforeSeek &&
|
||||
aSample->mTime < mCurrentTimeBeforeSeek.ToMicroseconds()) {
|
||||
// We are doing a fastSeek, but we ended up *before* the previous
|
||||
// playback position. This is surprising UX, so switch to an accurate
|
||||
// seek and decode to the seek target. This is not conformant to the
|
||||
// spec, fastSeek should always be fast, but until we get the time to
|
||||
// change all Readers to seek to the keyframe after the currentTime
|
||||
// in this case, we'll just decode forward. Bug 1026330.
|
||||
mSeekJob.mTarget.SetType(SeekTarget::Accurate);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult DropAudioUpToSeekTarget(MediaData* aSample)
|
||||
{
|
||||
RefPtr<AudioData> audio(aSample->As<AudioData>());
|
||||
MOZ_ASSERT(audio && mSeekJob.mTarget.IsAccurate());
|
||||
|
||||
CheckedInt64 sampleDuration = FramesToUsecs(audio->mFrames, Info().mAudio.mRate);
|
||||
if (!sampleDuration.isValid()) {
|
||||
return NS_ERROR_DOM_MEDIA_OVERFLOW_ERR;
|
||||
}
|
||||
|
||||
if (audio->mTime + sampleDuration.value() <= mSeekJob.mTarget.GetTime().ToMicroseconds()) {
|
||||
// Our seek target lies after the frames in this AudioData. Don't
|
||||
// push it onto the audio queue, and keep decoding forwards.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (audio->mTime > mSeekJob.mTarget.GetTime().ToMicroseconds()) {
|
||||
// The seek target doesn't lie in the audio block just after the last
|
||||
// audio frames we've seen which were before the seek target. This
|
||||
// could have been the first audio data we've seen after seek, i.e. the
|
||||
// seek terminated after the seek target in the audio stream. Just
|
||||
// abort the audio decode-to-target, the state machine will play
|
||||
// silence to cover the gap. Typically this happens in poorly muxed
|
||||
// files.
|
||||
SWARN("Audio not synced after seek, maybe a poorly muxed file?");
|
||||
mSeekedAudioData = audio;
|
||||
mDoneAudioSeeking = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// The seek target lies somewhere in this AudioData's frames, strip off
|
||||
// any frames which lie before the seek target, so we'll begin playback
|
||||
// exactly at the seek target.
|
||||
NS_ASSERTION(mSeekJob.mTarget.GetTime().ToMicroseconds() >= audio->mTime,
|
||||
"Target must at or be after data start.");
|
||||
NS_ASSERTION(mSeekJob.mTarget.GetTime().ToMicroseconds() < audio->mTime + sampleDuration.value(),
|
||||
"Data must end after target.");
|
||||
|
||||
CheckedInt64 framesToPrune =
|
||||
UsecsToFrames(mSeekJob.mTarget.GetTime().ToMicroseconds() - audio->mTime, Info().mAudio.mRate);
|
||||
if (!framesToPrune.isValid()) {
|
||||
return NS_ERROR_DOM_MEDIA_OVERFLOW_ERR;
|
||||
}
|
||||
if (framesToPrune.value() > audio->mFrames) {
|
||||
// We've messed up somehow. Don't try to trim frames, the |frames|
|
||||
// variable below will overflow.
|
||||
SWARN("Can't prune more frames that we have!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
uint32_t frames = audio->mFrames - static_cast<uint32_t>(framesToPrune.value());
|
||||
uint32_t channels = audio->mChannels;
|
||||
AlignedAudioBuffer audioData(frames * channels);
|
||||
if (!audioData) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
memcpy(audioData.get(),
|
||||
audio->mAudioData.get() + (framesToPrune.value() * channels),
|
||||
frames * channels * sizeof(AudioDataValue));
|
||||
CheckedInt64 duration = FramesToUsecs(frames, Info().mAudio.mRate);
|
||||
if (!duration.isValid()) {
|
||||
return NS_ERROR_DOM_MEDIA_OVERFLOW_ERR;
|
||||
}
|
||||
RefPtr<AudioData> data(new AudioData(audio->mOffset,
|
||||
mSeekJob.mTarget.GetTime().ToMicroseconds(),
|
||||
duration.value(),
|
||||
frames,
|
||||
Move(audioData),
|
||||
channels,
|
||||
audio->mRate));
|
||||
MOZ_ASSERT(!mSeekedAudioData, "Should be the 1st sample after seeking");
|
||||
mSeekedAudioData = data;
|
||||
mDoneAudioSeeking = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult DropVideoUpToSeekTarget(MediaData* aSample)
|
||||
{
|
||||
RefPtr<VideoData> video(aSample->As<VideoData>());
|
||||
MOZ_ASSERT(video);
|
||||
SLOG("DropVideoUpToSeekTarget() frame [%lld, %lld]",
|
||||
video->mTime, video->GetEndTime());
|
||||
const int64_t target = mSeekJob.mTarget.GetTime().ToMicroseconds();
|
||||
|
||||
// If the frame end time is less than the seek target, we won't want
|
||||
// to display this frame after the seek, so discard it.
|
||||
if (target >= video->GetEndTime()) {
|
||||
SLOG("DropVideoUpToSeekTarget() pop video frame [%lld, %lld] target=%lld",
|
||||
video->mTime, video->GetEndTime(), target);
|
||||
mFirstVideoFrameAfterSeek = video;
|
||||
} else {
|
||||
if (target >= video->mTime && video->GetEndTime() >= target) {
|
||||
// The seek target lies inside this frame's time slice. Adjust the frame's
|
||||
// start time to match the seek target. We do this by replacing the
|
||||
// first frame with a shallow copy which has the new timestamp.
|
||||
RefPtr<VideoData> temp = VideoData::ShallowCopyUpdateTimestamp(video.get(), target);
|
||||
video = temp;
|
||||
}
|
||||
mFirstVideoFrameAfterSeek = nullptr;
|
||||
|
||||
SLOG("DropVideoUpToSeekTarget() found video frame [%lld, %lld] containing target=%lld",
|
||||
video->mTime, video->GetEndTime(), target);
|
||||
|
||||
MOZ_ASSERT(!mSeekedVideoData, "Should be the 1st sample after seeking");
|
||||
mSeekedVideoData = video;
|
||||
mDoneVideoSeeking = true;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void MaybeFinishSeek()
|
||||
{
|
||||
if (mDoneAudioSeeking && mDoneVideoSeeking) {
|
||||
OnSeekTaskResolved();
|
||||
}
|
||||
}
|
||||
|
||||
void OnSeekTaskResolved()
|
||||
{
|
||||
if (mSeekedAudioData) {
|
||||
mMaster->Push(mSeekedAudioData);
|
||||
mMaster->mDecodedAudioEndTime = std::max(
|
||||
mSeekedAudioData->GetEndTime(), mMaster->mDecodedAudioEndTime);
|
||||
}
|
||||
|
||||
if (mSeekedVideoData) {
|
||||
mMaster->Push(mSeekedVideoData);
|
||||
mMaster->mDecodedVideoEndTime = std::max(
|
||||
mSeekedVideoData->GetEndTime(), mMaster->mDecodedVideoEndTime);
|
||||
}
|
||||
|
||||
if (mIsAudioQueueFinished) {
|
||||
AudioQueue().Finish();
|
||||
}
|
||||
|
||||
if (mIsVideoQueueFinished) {
|
||||
VideoQueue().Finish();
|
||||
}
|
||||
|
||||
SeekCompleted();
|
||||
}
|
||||
|
||||
void OnSeekTaskRejected(MediaResult aError)
|
||||
{
|
||||
if (mIsAudioQueueFinished) {
|
||||
AudioQueue().Finish();
|
||||
}
|
||||
|
||||
if (mIsVideoQueueFinished) {
|
||||
VideoQueue().Finish();
|
||||
}
|
||||
|
||||
mMaster->DecodeError(aError);
|
||||
}
|
||||
|
||||
/*
|
||||
* Track the current seek promise made by the reader.
|
||||
*/
|
||||
MozPromiseRequestHolder<MediaDecoderReader::SeekPromise> mSeekRequest;
|
||||
|
||||
/*
|
||||
* Internal state.
|
||||
*/
|
||||
media::TimeUnit mCurrentTimeBeforeSeek;
|
||||
bool mDoneAudioSeeking = false;
|
||||
bool mDoneVideoSeeking = false;
|
||||
|
||||
// This temporarily stores the first frame we decode after we seek.
|
||||
// This is so that if we hit end of stream while we're decoding to reach
|
||||
// the seek target, we will still have a frame that we can display as the
|
||||
// last frame in the media.
|
||||
RefPtr<MediaData> mFirstVideoFrameAfterSeek;
|
||||
|
||||
/*
|
||||
* Information which are going to be returned to MDSM.
|
||||
*/
|
||||
RefPtr<MediaData> mSeekedAudioData;
|
||||
RefPtr<MediaData> mSeekedVideoData;
|
||||
bool mIsAudioQueueFinished = false;
|
||||
bool mIsVideoQueueFinished = false;
|
||||
};
|
||||
|
||||
class MediaDecoderStateMachine::NextFrameSeekingState
|
||||
: public MediaDecoderStateMachine::SeekingState
|
||||
{
|
||||
public:
|
||||
explicit NextFrameSeekingState(Master* aPtr) : SeekingState(aPtr)
|
||||
{
|
||||
}
|
||||
|
||||
RefPtr<MediaDecoder::SeekPromise> Enter(SeekJob aSeekJob,
|
||||
EventVisibility aVisibility)
|
||||
{
|
||||
MOZ_ASSERT(aSeekJob.mTarget.IsNextFrame());
|
||||
return SeekingState::Enter(Move(aSeekJob), aVisibility);
|
||||
}
|
||||
|
||||
private:
|
||||
void CreateSeekTask() override
|
||||
{
|
||||
mSeekTask = new NextFrameSeekTask(
|
||||
mMaster->mDecoderID, OwnerThread(), Reader(), mSeekJob.mTarget,
|
||||
Info(), mMaster->Duration(),mMaster->GetMediaTime(),
|
||||
AudioQueue(), VideoQueue());
|
||||
}
|
||||
|
||||
void ResetMDSM() override
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
void DoSeek() override
|
||||
{
|
||||
mSeekTaskRequest.Begin(mSeekTask->Seek(mMaster->Duration())
|
||||
->Then(OwnerThread(), __func__,
|
||||
[this] (const SeekTaskResolveValue& aValue) {
|
||||
OnSeekTaskResolved(aValue);
|
||||
},
|
||||
[this] (const SeekTaskRejectValue& aValue) {
|
||||
OnSeekTaskRejected(aValue);
|
||||
}));
|
||||
}
|
||||
|
||||
int64_t CalculateNewCurrentTime() const override
|
||||
{
|
||||
return mSeekTask->CalculateNewCurrentTime();
|
||||
}
|
||||
|
||||
void OnSeekTaskResolved(const SeekTaskResolveValue& aValue)
|
||||
{
|
||||
mSeekTaskRequest.Complete();
|
||||
@ -917,11 +1420,6 @@ private:
|
||||
mMaster->DecodeError(aValue.mError);
|
||||
}
|
||||
|
||||
void SeekCompleted();
|
||||
|
||||
SeekJob mSeekJob;
|
||||
MozPromiseRequestHolder<SeekTask::SeekTaskPromise> mSeekTaskRequest;
|
||||
RefPtr<SeekTask> mSeekTask;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1227,7 +1725,7 @@ StateObject::HandleSeek(SeekTarget aTarget)
|
||||
SLOG("Changed state to SEEKING (to %lld)", aTarget.GetTime().ToMicroseconds());
|
||||
SeekJob seekJob;
|
||||
seekJob.mTarget = aTarget;
|
||||
return SetState<SeekingState>(Move(seekJob), EventVisibility::Observable);
|
||||
return SetSeekingState(Move(seekJob), EventVisibility::Observable);
|
||||
}
|
||||
|
||||
RefPtr<ShutdownPromise>
|
||||
@ -1302,12 +1800,28 @@ StateObject::HandleResumeVideoDecoding()
|
||||
type,
|
||||
true /* aVideoOnly */);
|
||||
|
||||
SetState<SeekingState>(Move(seekJob), EventVisibility::Suppressed)->Then(
|
||||
SetSeekingState(Move(seekJob), EventVisibility::Suppressed)->Then(
|
||||
AbstractThread::MainThread(), __func__,
|
||||
[start, info, hw](){ ReportRecoveryTelemetry(start, info, hw); },
|
||||
[](){});
|
||||
}
|
||||
|
||||
RefPtr<MediaDecoder::SeekPromise>
|
||||
MediaDecoderStateMachine::
|
||||
StateObject::SetSeekingState(SeekJob&& aSeekJob, EventVisibility aVisibility)
|
||||
{
|
||||
if (aSeekJob.mTarget.IsAccurate() || aSeekJob.mTarget.IsFast()) {
|
||||
return SetState<AccurateSeekingState>(Move(aSeekJob), aVisibility);
|
||||
}
|
||||
|
||||
if (aSeekJob.mTarget.IsNextFrame()) {
|
||||
return SetState<NextFrameSeekingState>(Move(aSeekJob), aVisibility);
|
||||
}
|
||||
|
||||
MOZ_ASSERT_UNREACHABLE("Unknown SeekTarget::Type.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderStateMachine::
|
||||
DecodeMetadataState::OnMetadataRead(MetadataHolder* aMetadata)
|
||||
@ -1364,7 +1878,7 @@ DormantState::HandlePlayStateChanged(MediaDecoder::PlayState aPlayState)
|
||||
// Exit dormant when the user wants to play.
|
||||
MOZ_ASSERT(!Info().IsEncrypted() || mMaster->mCDMProxy);
|
||||
MOZ_ASSERT(mMaster->mSentFirstFrameLoadedEvent);
|
||||
SetState<SeekingState>(Move(mPendingSeek), EventVisibility::Suppressed);
|
||||
SetSeekingState(Move(mPendingSeek), EventVisibility::Suppressed);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1373,7 +1887,7 @@ MediaDecoderStateMachine::
|
||||
WaitForCDMState::HandleCDMProxyReady()
|
||||
{
|
||||
if (mPendingSeek.Exists()) {
|
||||
SetState<SeekingState>(Move(mPendingSeek), EventVisibility::Observable);
|
||||
SetSeekingState(Move(mPendingSeek), EventVisibility::Observable);
|
||||
} else {
|
||||
SetState<DecodingFirstFrameState>();
|
||||
}
|
||||
@ -1495,7 +2009,7 @@ void
|
||||
MediaDecoderStateMachine::
|
||||
SeekingState::SeekCompleted()
|
||||
{
|
||||
const int64_t newCurrentTime = mSeekTask->CalculateNewCurrentTime();
|
||||
const int64_t newCurrentTime = CalculateNewCurrentTime();
|
||||
|
||||
bool isLiveStream = Resource()->IsLiveStream();
|
||||
if (newCurrentTime == mMaster->Duration().ToMicroseconds() && !isLiveStream) {
|
||||
@ -1656,7 +2170,7 @@ ShutdownState::Enter()
|
||||
// Shut down the watch manager to stop further notifications.
|
||||
master->mWatchManager.Shutdown();
|
||||
|
||||
return Reader()->Shutdown()->ThenPromise(
|
||||
return Reader()->Shutdown()->Then(
|
||||
OwnerThread(), __func__, master,
|
||||
&MediaDecoderStateMachine::FinishShutdown,
|
||||
&MediaDecoderStateMachine::FinishShutdown);
|
||||
|
@ -265,6 +265,8 @@ private:
|
||||
class DecodingFirstFrameState;
|
||||
class DecodingState;
|
||||
class SeekingState;
|
||||
class AccurateSeekingState;
|
||||
class NextFrameSeekingState;
|
||||
class BufferingState;
|
||||
class CompletedState;
|
||||
class ShutdownState;
|
||||
|
@ -931,7 +931,7 @@ MediaFormatReader::DoDemuxVideo()
|
||||
|
||||
if (mVideo.mFirstDemuxedSampleTime.isNothing()) {
|
||||
RefPtr<MediaFormatReader> self = this;
|
||||
p = p->ThenPromise(OwnerThread(), __func__,
|
||||
p = p->Then(OwnerThread(), __func__,
|
||||
[self] (RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples) {
|
||||
self->OnFirstDemuxCompleted(TrackInfo::kVideoTrack, aSamples);
|
||||
},
|
||||
@ -997,7 +997,7 @@ MediaFormatReader::DoDemuxAudio()
|
||||
|
||||
if (mAudio.mFirstDemuxedSampleTime.isNothing()) {
|
||||
RefPtr<MediaFormatReader> self = this;
|
||||
p = p->ThenPromise(OwnerThread(), __func__,
|
||||
p = p->Then(OwnerThread(), __func__,
|
||||
[self] (RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples) {
|
||||
self->OnFirstDemuxCompleted(TrackInfo::kAudioTrack, aSamples);
|
||||
},
|
||||
|
@ -55,13 +55,6 @@ NextFrameSeekTask::Discard()
|
||||
mIsDiscarded = true;
|
||||
}
|
||||
|
||||
bool
|
||||
NextFrameSeekTask::NeedToResetMDSM() const
|
||||
{
|
||||
AssertOwnerThread();
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t
|
||||
NextFrameSeekTask::CalculateNewCurrentTime() const
|
||||
{
|
||||
|
@ -38,8 +38,6 @@ public:
|
||||
|
||||
RefPtr<SeekTaskPromise> Seek(const media::TimeUnit& aDuration) override;
|
||||
|
||||
bool NeedToResetMDSM() const override;
|
||||
|
||||
int64_t CalculateNewCurrentTime() const override;
|
||||
|
||||
void HandleAudioDecoded(MediaData* aAudio) override;
|
||||
@ -54,7 +52,6 @@ public:
|
||||
|
||||
void HandleNotWaited(const WaitForDataRejectValue& aRejection) override;
|
||||
|
||||
private:
|
||||
~NextFrameSeekTask();
|
||||
|
||||
void RequestVideoData();
|
||||
|
@ -8,6 +8,8 @@
|
||||
#define SEEK_TASK_H
|
||||
|
||||
#include "mozilla/MozPromise.h"
|
||||
#include "MediaData.h" // For MediaData::Type.
|
||||
#include "MediaDecoderReader.h" // For WaitForDataRejectValue.
|
||||
#include "MediaResult.h"
|
||||
#include "SeekTarget.h"
|
||||
|
||||
@ -56,8 +58,6 @@ public:
|
||||
|
||||
virtual RefPtr<SeekTaskPromise> Seek(const media::TimeUnit& aDuration) = 0;
|
||||
|
||||
virtual bool NeedToResetMDSM() const = 0;
|
||||
|
||||
virtual int64_t CalculateNewCurrentTime() const = 0;
|
||||
|
||||
virtual void HandleAudioDecoded(MediaData* aAudio) = 0;
|
||||
@ -80,6 +80,7 @@ protected:
|
||||
|
||||
virtual ~SeekTask();
|
||||
|
||||
public:
|
||||
void Resolve(const char* aCallSite);
|
||||
|
||||
void RejectIfExist(const MediaResult& aError, const char* aCallSite);
|
||||
|
@ -60,7 +60,7 @@ public:
|
||||
// bytes.
|
||||
bool Parse(const uint8_t* aPacket)
|
||||
{
|
||||
mp4_demuxer::BitReader br(aPacket, FLAC_MAX_FRAME_HEADER_SIZE);
|
||||
mp4_demuxer::BitReader br(aPacket, FLAC_MAX_FRAME_HEADER_SIZE * 8);
|
||||
|
||||
// Frame sync code.
|
||||
if ((br.ReadBits(15) & 0x7fff) != 0x7ffc) {
|
||||
|
@ -909,7 +909,7 @@ GeckoMediaPluginServiceParent::AsyncAddPluginDirectory(const nsAString& aDirecto
|
||||
return InvokeAsync<nsString&&>(
|
||||
thread, this, __func__,
|
||||
&GeckoMediaPluginServiceParent::AddOnGMPThread, dir)
|
||||
->ThenPromise(AbstractThread::MainThread(), __func__,
|
||||
->Then(AbstractThread::MainThread(), __func__,
|
||||
[dir, self]() -> void {
|
||||
LOGD(("GeckoMediaPluginServiceParent::AsyncAddPluginDirectory %s succeeded",
|
||||
NS_ConvertUTF16toUTF8(dir).get()));
|
||||
@ -1134,7 +1134,7 @@ GeckoMediaPluginServiceParent::AddOnGMPThread(nsString aDirectory)
|
||||
}
|
||||
|
||||
RefPtr<GeckoMediaPluginServiceParent> self(this);
|
||||
return gmp->Init(this, directory)->ThenPromise(thread, __func__,
|
||||
return gmp->Init(this, directory)->Then(thread, __func__,
|
||||
[gmp, self, dir]() -> void {
|
||||
LOGD(("%s::%s: %s Succeeded", __CLASS__, __FUNCTION__, dir.get()));
|
||||
{
|
||||
|
@ -148,11 +148,11 @@ TEST(MozPromise, CompletionPromises)
|
||||
RefPtr<TaskQueue> queue = atq.Queue();
|
||||
RunOnTaskQueue(queue, [queue, &invokedPass] () -> void {
|
||||
TestPromise::CreateAndResolve(40, __func__)
|
||||
->ThenPromise(queue, __func__,
|
||||
->Then(queue, __func__,
|
||||
[] (int aVal) -> RefPtr<TestPromise> { return TestPromise::CreateAndResolve(aVal + 10, __func__); },
|
||||
DO_FAIL)
|
||||
->ThenPromise(queue, __func__, [&invokedPass] () -> void { invokedPass = true; }, DO_FAIL)
|
||||
->ThenPromise(queue, __func__,
|
||||
->Then(queue, __func__, [&invokedPass] () -> void { invokedPass = true; }, DO_FAIL)
|
||||
->Then(queue, __func__,
|
||||
[queue] (int aVal) -> RefPtr<TestPromise> {
|
||||
RefPtr<TestPromise::Private> p = new TestPromise::Private(__func__);
|
||||
nsCOMPtr<nsIRunnable> resolver = new DelayedResolveOrReject(queue, p, RRValue::MakeResolve(aVal - 8), 10);
|
||||
@ -160,7 +160,7 @@ TEST(MozPromise, CompletionPromises)
|
||||
return RefPtr<TestPromise>(p);
|
||||
},
|
||||
DO_FAIL)
|
||||
->ThenPromise(queue, __func__,
|
||||
->Then(queue, __func__,
|
||||
[queue] (int aVal) -> RefPtr<TestPromise> { return TestPromise::CreateAndReject(double(aVal - 42) + 42.0, __func__); },
|
||||
DO_FAIL)
|
||||
->Then(queue, __func__,
|
||||
@ -228,7 +228,7 @@ TEST(MozPromise, Chaining)
|
||||
auto p = TestPromise::CreateAndResolve(42, __func__);
|
||||
const size_t kIterations = 100;
|
||||
for (size_t i = 0; i < kIterations; ++i) {
|
||||
p = p->ThenPromise(queue, __func__,
|
||||
p = p->Then(queue, __func__,
|
||||
[] (int aVal) {
|
||||
EXPECT_EQ(aVal, 42);
|
||||
},
|
||||
|
@ -77,7 +77,6 @@ XPIDL_MODULE = 'dom_media'
|
||||
|
||||
EXPORTS += [
|
||||
'AbstractMediaDecoder.h',
|
||||
'AccurateSeekTask.h',
|
||||
'ADTSDecoder.h',
|
||||
'ADTSDemuxer.h',
|
||||
'AudioBufferUtils.h',
|
||||
@ -188,7 +187,6 @@ EXPORTS.mozilla.dom += [
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'AccurateSeekTask.cpp',
|
||||
'ADTSDecoder.cpp',
|
||||
'ADTSDemuxer.cpp',
|
||||
'AudioCaptureStream.cpp',
|
||||
|
@ -99,18 +99,6 @@ protected:
|
||||
MediaDataDecoderCallback* mDecoderCallback;
|
||||
};
|
||||
|
||||
struct SampleTime final
|
||||
{
|
||||
SampleTime(int64_t aStart, int64_t aDuration)
|
||||
: mStart(aStart)
|
||||
, mDuration(aDuration)
|
||||
{}
|
||||
|
||||
int64_t mStart;
|
||||
int64_t mDuration;
|
||||
};
|
||||
|
||||
|
||||
class RemoteVideoDecoder final : public RemoteDataDecoder
|
||||
{
|
||||
public:
|
||||
@ -288,18 +276,23 @@ private:
|
||||
class DurationQueue {
|
||||
public:
|
||||
|
||||
DurationQueue() : mMutex("Video duration queue") {}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mValues.clear();
|
||||
}
|
||||
|
||||
void Put(int64_t aDurationUs)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mValues.emplace_back(aDurationUs);
|
||||
}
|
||||
|
||||
Maybe<int64_t> Get()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (mValues.empty()) {
|
||||
return Nothing();
|
||||
}
|
||||
@ -311,6 +304,7 @@ private:
|
||||
}
|
||||
|
||||
private:
|
||||
Mutex mMutex; // To protect mValues.
|
||||
std::deque<int64_t> mValues;
|
||||
};
|
||||
|
||||
|
@ -259,7 +259,7 @@ OmxDataDecoder::DoAsyncShutdown()
|
||||
// Flush to all ports, so all buffers can be returned from component.
|
||||
RefPtr<OmxDataDecoder> self = this;
|
||||
mOmxLayer->SendCommand(OMX_CommandFlush, OMX_ALL, nullptr)
|
||||
->ThenPromise(mOmxTaskQueue, __func__,
|
||||
->Then(mOmxTaskQueue, __func__,
|
||||
[self] () -> RefPtr<OmxCommandPromise> {
|
||||
LOGL("DoAsyncShutdown: flush complete");
|
||||
return self->mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateIdle, nullptr);
|
||||
@ -267,7 +267,7 @@ OmxDataDecoder::DoAsyncShutdown()
|
||||
[self] () {
|
||||
self->mOmxLayer->Shutdown();
|
||||
})
|
||||
->ThenPromise(mOmxTaskQueue, __func__,
|
||||
->Then(mOmxTaskQueue, __func__,
|
||||
[self] () -> RefPtr<OmxCommandPromise> {
|
||||
RefPtr<OmxCommandPromise> p =
|
||||
self->mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateLoaded, nullptr);
|
||||
@ -796,7 +796,7 @@ OmxDataDecoder::PortSettingsChanged()
|
||||
// 1. disable port.
|
||||
LOG("PortSettingsChanged: disable port %d", def.nPortIndex);
|
||||
mOmxLayer->SendCommand(OMX_CommandPortDisable, mPortSettingsChanged, nullptr)
|
||||
->ThenPromise(mOmxTaskQueue, __func__,
|
||||
->Then(mOmxTaskQueue, __func__,
|
||||
[self, def] () -> RefPtr<OmxCommandPromise> {
|
||||
// 3. enable port.
|
||||
// Send enable port command.
|
||||
|
@ -28,9 +28,6 @@ protocol PPluginBackgroundDestroyer {
|
||||
|
||||
parent:
|
||||
async __delete__();
|
||||
|
||||
state DESTROYING:
|
||||
recv __delete__;
|
||||
};
|
||||
|
||||
} // namespace plugins
|
||||
|
@ -73,6 +73,7 @@ child:
|
||||
uint32_t aWrapColumn);
|
||||
async __delete__();
|
||||
|
||||
/*
|
||||
state START:
|
||||
recv Attributes goto MAIN;
|
||||
recv InitFailure goto FAILED;
|
||||
@ -85,6 +86,7 @@ state MAIN:
|
||||
|
||||
state FAILED:
|
||||
send __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -148,7 +148,7 @@ template<class units>
|
||||
IntPointTyped<units> RoundedToInt(const PointTyped<units>& aPoint) {
|
||||
return IntPointTyped<units>::Round(aPoint.x, aPoint.y);
|
||||
}
|
||||
|
||||
|
||||
template<class units>
|
||||
IntPointTyped<units> TruncatedToInt(const PointTyped<units>& aPoint) {
|
||||
return IntPointTyped<units>::Truncate(aPoint.x, aPoint.y);
|
||||
@ -218,6 +218,9 @@ struct Point4DTyped :
|
||||
Point4DTyped() : Super() {}
|
||||
Point4DTyped(F aX, F aY, F aZ, F aW) : Super(aX, aY, aZ, aW) {}
|
||||
|
||||
explicit Point4DTyped(const Point3DTyped<units, F>& aPoint)
|
||||
: Super(aPoint.x, aPoint.y, aPoint.z, 1) {}
|
||||
|
||||
// XXX When all of the code is ported, the following functions to convert to and from
|
||||
// unknown types should be removed.
|
||||
|
||||
@ -229,8 +232,15 @@ struct Point4DTyped :
|
||||
return Point4DTyped<UnknownUnits, F>(this->x, this->y, this->z, this->w);
|
||||
}
|
||||
|
||||
PointTyped<units, F> As2DPoint() {
|
||||
return PointTyped<units, F>(this->x / this->w, this->y / this->w);
|
||||
PointTyped<units, F> As2DPoint() const {
|
||||
return PointTyped<units, F>(this->x / this->w,
|
||||
this->y / this->w);
|
||||
}
|
||||
|
||||
Point3DTyped<units, F> As3DPoint() const {
|
||||
return Point3DTyped<units, F>(this->x / this->w,
|
||||
this->y / this->w,
|
||||
this->z / this->w);
|
||||
}
|
||||
};
|
||||
typedef Point4DTyped<UnknownUnits> Point4D;
|
||||
|
252
gfx/2d/Polygon.h
252
gfx/2d/Polygon.h
@ -17,49 +17,101 @@
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
// Polygon3DTyped stores the points of a convex planar polygon.
|
||||
template<class Units>
|
||||
class Polygon3DTyped {
|
||||
Point4DTyped<Units>
|
||||
CalculateEdgeIntersect(const Point4DTyped<Units>& aFirst,
|
||||
const Point4DTyped<Units>& aSecond)
|
||||
{
|
||||
static const float w = 0.00001f;
|
||||
const float t = (w - aFirst.w) / (aSecond.w - aFirst.w);
|
||||
return aFirst + (aSecond - aFirst) * t;
|
||||
}
|
||||
|
||||
template<class Units>
|
||||
nsTArray<Point4DTyped<Units>>
|
||||
ClipHomogeneous(const nsTArray<Point4DTyped<Units>>& aPoints)
|
||||
{
|
||||
nsTArray<Point4DTyped<Units>> outPoints;
|
||||
|
||||
const size_t pointCount = aPoints.Length();
|
||||
for (size_t i = 0; i < pointCount; ++i) {
|
||||
const Point4DTyped<Units>& first = aPoints[i];
|
||||
const Point4DTyped<Units>& second = aPoints[(i + 1) % pointCount];
|
||||
|
||||
MOZ_ASSERT(first.w != 0.0f || second.w != 0.0f);
|
||||
|
||||
if (first.w > 0.0f) {
|
||||
outPoints.AppendElement(first);
|
||||
}
|
||||
|
||||
if ((first.w <= 0.0f) ^ (second.w <= 0.0f)) {
|
||||
outPoints.AppendElement(CalculateEdgeIntersect(first, second));
|
||||
}
|
||||
}
|
||||
|
||||
return outPoints;
|
||||
}
|
||||
|
||||
template<class Units>
|
||||
nsTArray<Point4DTyped<Units>>
|
||||
ToPoints4D(const nsTArray<Point3DTyped<Units>>& aPoints)
|
||||
{
|
||||
nsTArray<Point4DTyped<Units>> points;
|
||||
|
||||
for (const Point3DTyped<Units>& point : aPoints) {
|
||||
points.AppendElement(Point4DTyped<Units>(point));
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
// PolygonTyped stores the points of a convex planar polygon.
|
||||
template<class Units>
|
||||
class PolygonTyped {
|
||||
typedef Point3DTyped<Units> Point3DType;
|
||||
typedef Point4DTyped<Units> Point4DType;
|
||||
|
||||
public:
|
||||
Polygon3DTyped() {}
|
||||
PolygonTyped() {}
|
||||
|
||||
explicit Polygon3DTyped(const std::initializer_list<Point3DTyped<Units>>& aPoints,
|
||||
Point3DTyped<Units> aNormal =
|
||||
Point3DTyped<Units>(0.0f, 0.0f, 1.0f))
|
||||
: mNormal(aNormal), mPoints(aPoints)
|
||||
explicit PolygonTyped(const std::initializer_list<Point3DType>& aPoints)
|
||||
: mNormal(DefaultNormal()),
|
||||
mPoints(ToPoints4D(nsTArray<Point3DType>(aPoints)))
|
||||
{
|
||||
#ifdef DEBUG
|
||||
EnsurePlanarPolygon();
|
||||
#endif
|
||||
}
|
||||
|
||||
explicit Polygon3DTyped(nsTArray<Point3DTyped<Units>>&& aPoints,
|
||||
Point3DTyped<Units> aNormal =
|
||||
Point3DTyped<Units>(0.0f, 0.0f, 1.0f))
|
||||
explicit PolygonTyped(const nsTArray<Point3DType>& aPoints)
|
||||
: mNormal(DefaultNormal()), mPoints(ToPoints4D(aPoints))
|
||||
{
|
||||
#ifdef DEBUG
|
||||
EnsurePlanarPolygon();
|
||||
#endif
|
||||
}
|
||||
|
||||
explicit PolygonTyped(const nsTArray<Point4DType>& aPoints,
|
||||
const Point4DType& aNormal = DefaultNormal())
|
||||
: mNormal(aNormal), mPoints(aPoints)
|
||||
{}
|
||||
|
||||
explicit PolygonTyped(nsTArray<Point4DType>&& aPoints,
|
||||
const Point4DType& aNormal = DefaultNormal())
|
||||
: mNormal(aNormal), mPoints(Move(aPoints))
|
||||
{
|
||||
#ifdef DEBUG
|
||||
EnsurePlanarPolygon();
|
||||
#endif
|
||||
}
|
||||
|
||||
explicit Polygon3DTyped(const nsTArray<Point3DTyped<Units>>& aPoints,
|
||||
Point3DTyped<Units> aNormal =
|
||||
Point3DTyped<Units>(0.0f, 0.0f, 1.0f))
|
||||
: mNormal(aNormal), mPoints(aPoints)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
EnsurePlanarPolygon();
|
||||
#endif
|
||||
}
|
||||
{}
|
||||
|
||||
RectTyped<Units> BoundingBox() const
|
||||
{
|
||||
if (mPoints.IsEmpty()) {
|
||||
return RectTyped<Units>();
|
||||
}
|
||||
|
||||
float minX, maxX, minY, maxY;
|
||||
minX = maxX = mPoints[0].x;
|
||||
minY = maxY = mPoints[0].y;
|
||||
|
||||
for (const Point3DTyped<Units>& point : mPoints) {
|
||||
for (const Point4DType& point : mPoints) {
|
||||
minX = std::min(point.x, minX);
|
||||
maxX = std::max(point.x, maxX);
|
||||
|
||||
@ -70,21 +122,21 @@ public:
|
||||
return RectTyped<Units>(minX, minY, maxX - minX, maxY - minY);
|
||||
}
|
||||
|
||||
nsTArray<float>
|
||||
CalculateDotProducts(const Polygon3DTyped<Units>& aPlane,
|
||||
size_t& aPos, size_t& aNeg) const
|
||||
nsTArray<float> CalculateDotProducts(const PolygonTyped<Units>& aPlane,
|
||||
size_t& aPos, size_t& aNeg) const
|
||||
{
|
||||
// Point classification might produce incorrect results due to numerical
|
||||
// inaccuracies. Using an epsilon value makes the splitting plane "thicker".
|
||||
const float epsilon = 0.05f;
|
||||
|
||||
MOZ_ASSERT(!aPlane.GetPoints().IsEmpty());
|
||||
const Point3DTyped<Units>& planeNormal = aPlane.GetNormal();
|
||||
const Point3DTyped<Units>& planePoint = aPlane[0];
|
||||
const Point4DType& planeNormal = aPlane.GetNormal();
|
||||
const Point4DType& planePoint = aPlane[0];
|
||||
|
||||
aPos = aNeg = 0;
|
||||
nsTArray<float> dotProducts;
|
||||
for (const Point3DTyped<Units>& point : mPoints) {
|
||||
|
||||
for (const Point4DType& point : mPoints) {
|
||||
float dot = (point - planePoint).DotProduct(planeNormal);
|
||||
|
||||
if (dot > epsilon) {
|
||||
@ -103,45 +155,74 @@ public:
|
||||
}
|
||||
|
||||
// Clips the polygon against the given 2D rectangle.
|
||||
Polygon3DTyped<Units> ClipPolygon(const RectTyped<Units>& aRect) const
|
||||
PolygonTyped<Units> ClipPolygon(const RectTyped<Units>& aRect) const
|
||||
{
|
||||
Polygon3DTyped<Units> polygon(mPoints, mNormal);
|
||||
if (aRect.IsEmpty()) {
|
||||
return PolygonTyped<Units>();
|
||||
}
|
||||
|
||||
// Left edge
|
||||
ClipPolygonWithEdge(polygon, aRect.BottomLeft(), aRect.TopLeft());
|
||||
return ClipPolygon(FromRect(aRect));
|
||||
}
|
||||
|
||||
// Bottom edge
|
||||
ClipPolygonWithEdge(polygon, aRect.BottomRight(), aRect.BottomLeft());
|
||||
// Clips the polygon against the given polygon in 2D.
|
||||
PolygonTyped<Units> ClipPolygon(const PolygonTyped<Units>& aPolygon) const
|
||||
{
|
||||
const nsTArray<Point4DType>& points = aPolygon.GetPoints();
|
||||
|
||||
// Right edge
|
||||
ClipPolygonWithEdge(polygon, aRect.TopRight(), aRect.BottomRight());
|
||||
if (mPoints.IsEmpty() || points.IsEmpty()) {
|
||||
return PolygonTyped<Units>();
|
||||
}
|
||||
|
||||
// Top edge
|
||||
ClipPolygonWithEdge(polygon, aRect.TopLeft(), aRect.TopRight());
|
||||
PolygonTyped<Units> polygon(mPoints, mNormal);
|
||||
|
||||
const size_t pointCount = points.Length();
|
||||
for (size_t i = 0; i < pointCount; ++i) {
|
||||
const Point4DType p1 = points[(i + 1) % pointCount];
|
||||
const Point4DType p2 = points[i];
|
||||
|
||||
const Point4DType normal(p2.y - p1.y, p1.x - p2.x, 0.0f, 0.0f);
|
||||
const PolygonTyped<Units> plane({p1, p2}, normal);
|
||||
|
||||
ClipPolygonWithPlane(polygon, plane);
|
||||
}
|
||||
|
||||
if (polygon.GetPoints().Length() < 3) {
|
||||
return PolygonTyped<Units>();
|
||||
}
|
||||
|
||||
return polygon;
|
||||
}
|
||||
|
||||
const Point3DTyped<Units>& GetNormal() const
|
||||
static PolygonTyped<Units> FromRect(const RectTyped<Units>& aRect)
|
||||
{
|
||||
return PolygonTyped<Units> {
|
||||
Point3DType(aRect.x, aRect.y, 0.0f),
|
||||
Point3DType(aRect.x, aRect.y + aRect.height, 0.0f),
|
||||
Point3DType(aRect.x + aRect.width, aRect.y + aRect.height, 0.0f),
|
||||
Point3DType(aRect.x + aRect.width, aRect.y, 0.0f)
|
||||
};
|
||||
}
|
||||
|
||||
const Point4DType& GetNormal() const
|
||||
{
|
||||
return mNormal;
|
||||
}
|
||||
|
||||
const nsTArray<Point3DTyped<Units>>& GetPoints() const
|
||||
const nsTArray<Point4DType>& GetPoints() const
|
||||
{
|
||||
return mPoints;
|
||||
}
|
||||
|
||||
const Point3DTyped<Units>& operator[](size_t aIndex) const
|
||||
const Point4DType& operator[](size_t aIndex) const
|
||||
{
|
||||
MOZ_ASSERT(mPoints.Length() > aIndex);
|
||||
return mPoints[aIndex];
|
||||
}
|
||||
|
||||
void SplitPolygon(const Polygon3DTyped<Units>& aSplittingPlane,
|
||||
void SplitPolygon(const Point4DType& aNormal,
|
||||
const nsTArray<float>& aDots,
|
||||
nsTArray<Point3DTyped<Units>>& aBackPoints,
|
||||
nsTArray<Point3DTyped<Units>>& aFrontPoints) const
|
||||
nsTArray<Point4DType>& aBackPoints,
|
||||
nsTArray<Point4DType>& aFrontPoints) const
|
||||
{
|
||||
static const auto Sign = [](const float& f) {
|
||||
if (f > 0.0f) return 1;
|
||||
@ -149,14 +230,12 @@ public:
|
||||
return 0;
|
||||
};
|
||||
|
||||
const Point3DTyped<Units>& normal = aSplittingPlane.GetNormal();
|
||||
const size_t pointCount = mPoints.Length();
|
||||
|
||||
for (size_t i = 0; i < pointCount; ++i) {
|
||||
size_t j = (i + 1) % pointCount;
|
||||
|
||||
const Point3DTyped<Units>& a = mPoints[i];
|
||||
const Point3DTyped<Units>& b = mPoints[j];
|
||||
const Point4DType& a = mPoints[i];
|
||||
const Point4DType& b = mPoints[j];
|
||||
const float dotA = aDots[i];
|
||||
const float dotB = aDots[j];
|
||||
|
||||
@ -175,10 +254,10 @@ public:
|
||||
// The case where the polygon edge is within the plane is handled above.
|
||||
if (Sign(dotA) && Sign(dotB) && Sign(dotA) != Sign(dotB)) {
|
||||
// Calculate the line segment and plane intersection point.
|
||||
const Point3DTyped<Units> ab = b - a;
|
||||
const float dotAB = ab.DotProduct(normal);
|
||||
const Point4DType ab = b - a;
|
||||
const float dotAB = ab.DotProduct(aNormal);
|
||||
const float t = -dotA / dotAB;
|
||||
const Point3DTyped<Units> p = a + (ab * t);
|
||||
const Point4DType p = a + (ab * t);
|
||||
|
||||
// Add the intersection point to both polygons.
|
||||
aBackPoints.AppendElement(p);
|
||||
@ -207,36 +286,37 @@ public:
|
||||
|
||||
void TransformToLayerSpace(const Matrix4x4Typed<Units, Units>& aTransform)
|
||||
{
|
||||
TransformPoints(aTransform);
|
||||
mNormal = Point3DTyped<Units>(0.0f, 0.0f, 1.0f);
|
||||
TransformPoints(aTransform, true);
|
||||
mNormal = DefaultNormal();
|
||||
}
|
||||
|
||||
void TransformToScreenSpace(const Matrix4x4Typed<Units, Units>& aTransform)
|
||||
{
|
||||
TransformPoints(aTransform);
|
||||
TransformPoints(aTransform, false);
|
||||
mPoints = ClipHomogeneous(mPoints);
|
||||
|
||||
// Normal vectors should be transformed using inverse transpose.
|
||||
mNormal = aTransform.Inverse().Transpose().TransformPoint(mNormal);
|
||||
}
|
||||
|
||||
private:
|
||||
void ClipPolygonWithEdge(Polygon3DTyped<Units>& aPolygon,
|
||||
const PointTyped<Units>& aFirst,
|
||||
const PointTyped<Units>& aSecond) const
|
||||
void ClipPolygonWithPlane(PolygonTyped<Units>& aPolygon,
|
||||
const PolygonTyped<Units>& aPlane) const
|
||||
{
|
||||
const Point3DTyped<Units> a(aFirst.x, aFirst.y, 0.0f);
|
||||
const Point3DTyped<Units> b(aSecond.x, aSecond.y, 0.0f);
|
||||
const Point3DTyped<Units> normal(b.y - a.y, a.x - b.x, 0.0f);
|
||||
Polygon3DTyped<Units> plane({a, b}, normal);
|
||||
|
||||
size_t pos, neg;
|
||||
nsTArray<float> dots = aPolygon.CalculateDotProducts(plane, pos, neg);
|
||||
const nsTArray<float> dots =
|
||||
aPolygon.CalculateDotProducts(aPlane, pos, neg);
|
||||
|
||||
nsTArray<Point3DTyped<Units>> backPoints, frontPoints;
|
||||
aPolygon.SplitPolygon(plane, dots, backPoints, frontPoints);
|
||||
nsTArray<Point4DType> backPoints, frontPoints;
|
||||
aPolygon.SplitPolygon(aPlane.GetNormal(), dots, backPoints, frontPoints);
|
||||
|
||||
// Only use the points that are behind the clipping plane.
|
||||
aPolygon = Polygon3DTyped<Units>(Move(backPoints), aPolygon.GetNormal());
|
||||
aPolygon = PolygonTyped<Units>(Move(backPoints), aPolygon.GetNormal());
|
||||
}
|
||||
|
||||
static Point4DType DefaultNormal()
|
||||
{
|
||||
return Point4DType(0.0f, 0.0f, 1.0f, 0.0f);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -251,11 +331,14 @@ private:
|
||||
// The resulting normal vector will point towards the viewer when the
|
||||
// polygon has a counter-clockwise winding order from the perspective
|
||||
// of the viewer.
|
||||
Point3DTyped<Units> normal;
|
||||
Point3DType normal;
|
||||
const Point3DType p0 = mPoints[0].As3DPoint();
|
||||
|
||||
for (size_t i = 1; i < mPoints.Length() - 1; ++i) {
|
||||
normal +=
|
||||
(mPoints[i] - mPoints[0]).CrossProduct(mPoints[i + 1] - mPoints[0]);
|
||||
const Point3DType p1 = mPoints[i].As3DPoint();
|
||||
const Point3DType p2 = mPoints[i + 1].As3DPoint();
|
||||
|
||||
normal += (p1 - p0).CrossProduct(p2 - p0);
|
||||
}
|
||||
|
||||
// Ensure that at least one component is greater than zero.
|
||||
@ -263,6 +346,7 @@ private:
|
||||
bool hasNonZeroComponent = std::abs(normal.x) > 0.0f ||
|
||||
std::abs(normal.y) > 0.0f ||
|
||||
std::abs(normal.z) > 0.0f;
|
||||
|
||||
MOZ_ASSERT(hasNonZeroComponent);
|
||||
|
||||
normal.Normalize();
|
||||
@ -270,24 +354,32 @@ private:
|
||||
// Ensure that the polygon is planar.
|
||||
// http://mathworld.wolfram.com/Point-PlaneDistance.html
|
||||
const float epsilon = 0.01f;
|
||||
for (const Point3DTyped<Units>& point : mPoints) {
|
||||
float d = normal.DotProduct(point - mPoints[0]);
|
||||
for (const Point4DType& point : mPoints) {
|
||||
const Point3DType p1 = point.As3DPoint();
|
||||
const float d = normal.DotProduct(p1 - p0);
|
||||
|
||||
MOZ_ASSERT(std::abs(d) < epsilon);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
void TransformPoints(const Matrix4x4Typed<Units, Units>& aTransform)
|
||||
|
||||
void TransformPoints(const Matrix4x4Typed<Units, Units>& aTransform,
|
||||
const bool aDivideByW)
|
||||
{
|
||||
for (Point3DTyped<Units>& point : mPoints) {
|
||||
for (Point4DType& point : mPoints) {
|
||||
point = aTransform.TransformPoint(point);
|
||||
|
||||
if (aDivideByW && point.w > 0.0f) {
|
||||
point = point / point.w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Point3DTyped<Units> mNormal;
|
||||
nsTArray<Point3DTyped<Units>> mPoints;
|
||||
Point4DType mNormal;
|
||||
nsTArray<Point4DType> mPoints;
|
||||
};
|
||||
|
||||
typedef Polygon3DTyped<UnknownUnits> Polygon3D;
|
||||
typedef PolygonTyped<UnknownUnits> Polygon;
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
@ -20,7 +20,7 @@ void
|
||||
BSPTree::BuildDrawOrder(const UniquePtr<BSPTreeNode>& aNode,
|
||||
nsTArray<LayerPolygon>& aLayers) const
|
||||
{
|
||||
const gfx::Point3D& normal = aNode->First().GetNormal();
|
||||
const gfx::Point4D& normal = aNode->First().GetNormal();
|
||||
|
||||
UniquePtr<BSPTreeNode> *front = &aNode->front;
|
||||
UniquePtr<BSPTreeNode> *back = &aNode->back;
|
||||
@ -58,11 +58,11 @@ BSPTree::BuildTree(UniquePtr<BSPTreeNode>& aRoot,
|
||||
return;
|
||||
}
|
||||
|
||||
const gfx::Polygon3D& plane = aRoot->First();
|
||||
const gfx::Polygon& plane = aRoot->First();
|
||||
std::deque<LayerPolygon> backLayers, frontLayers;
|
||||
|
||||
for (LayerPolygon& layerPolygon : aLayers) {
|
||||
const Maybe<gfx::Polygon3D>& geometry = layerPolygon.geometry;
|
||||
const Maybe<gfx::Polygon>& geometry = layerPolygon.geometry;
|
||||
|
||||
size_t pos = 0, neg = 0;
|
||||
nsTArray<float> dots = geometry->CalculateDotProducts(plane, pos, neg);
|
||||
@ -81,14 +81,19 @@ BSPTree::BuildTree(UniquePtr<BSPTreeNode>& aRoot,
|
||||
}
|
||||
// Polygon intersects with the splitting plane.
|
||||
else if (pos > 0 && neg > 0) {
|
||||
nsTArray<gfx::Point3D> backPoints, frontPoints;
|
||||
geometry->SplitPolygon(plane, dots, backPoints, frontPoints);
|
||||
nsTArray<gfx::Point4D> backPoints, frontPoints;
|
||||
geometry->SplitPolygon(plane.GetNormal(), dots, backPoints, frontPoints);
|
||||
|
||||
const gfx::Point3D& normal = geometry->GetNormal();
|
||||
const gfx::Point4D& normal = geometry->GetNormal();
|
||||
Layer *layer = layerPolygon.layer;
|
||||
|
||||
backLayers.push_back(LayerPolygon(layer, Move(backPoints), normal));
|
||||
frontLayers.push_back(LayerPolygon(layer, Move(frontPoints), normal));
|
||||
if (backPoints.Length() >= 3) {
|
||||
backLayers.push_back(LayerPolygon(layer, Move(backPoints), normal));
|
||||
}
|
||||
|
||||
if (frontPoints.Length() >= 3) {
|
||||
frontLayers.push_back(LayerPolygon(layer, Move(frontPoints), normal));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,15 +24,16 @@ struct LayerPolygon {
|
||||
: layer(aLayer) {}
|
||||
|
||||
LayerPolygon(Layer *aLayer,
|
||||
gfx::Polygon3D&& aGeometry)
|
||||
gfx::Polygon&& aGeometry)
|
||||
: layer(aLayer), geometry(Some(aGeometry)) {}
|
||||
|
||||
LayerPolygon(Layer *aLayer,
|
||||
nsTArray<gfx::Point3D>&& aPoints, const gfx::Point3D& aNormal)
|
||||
: layer(aLayer), geometry(Some(gfx::Polygon3D(Move(aPoints), aNormal))) {}
|
||||
nsTArray<gfx::Point4D>&& aPoints,
|
||||
const gfx::Point4D& aNormal)
|
||||
: layer(aLayer), geometry(Some(gfx::Polygon(Move(aPoints), aNormal))) {}
|
||||
|
||||
Layer *layer;
|
||||
Maybe<gfx::Polygon3D> geometry;
|
||||
Maybe<gfx::Polygon> geometry;
|
||||
};
|
||||
|
||||
LayerPolygon PopFront(std::deque<LayerPolygon>& aLayers);
|
||||
@ -46,7 +47,7 @@ struct BSPTreeNode {
|
||||
layers.push_back(Move(layer));
|
||||
}
|
||||
|
||||
const gfx::Polygon3D& First() const
|
||||
const gfx::Polygon& First() const
|
||||
{
|
||||
MOZ_ASSERT(layers[0].geometry);
|
||||
return *layers[0].geometry;
|
||||
|
@ -227,7 +227,7 @@ static void
|
||||
UpdateTextureCoordinates(gfx::TexturedTriangle& aTriangle,
|
||||
const gfx::Rect& aRect,
|
||||
const gfx::Rect& aIntersection,
|
||||
gfx::Rect aTextureCoords)
|
||||
const gfx::Rect& aTextureCoords)
|
||||
{
|
||||
// Calculate the relative offset of the intersection within the layer.
|
||||
float dx = (aIntersection.x - aRect.x) / aRect.width;
|
||||
@ -241,10 +241,8 @@ UpdateTextureCoordinates(gfx::TexturedTriangle& aTriangle,
|
||||
float w = aTextureCoords.width * aIntersection.width / aRect.width;
|
||||
float h = aTextureCoords.height * aIntersection.height / aRect.height;
|
||||
|
||||
static const auto ValidateAndClamp = [](float& f) {
|
||||
// Allow some numerical inaccuracy.
|
||||
MOZ_ASSERT(f >= -0.0001f && f <= 1.0001f);
|
||||
|
||||
static const auto Clamp = [](float& f)
|
||||
{
|
||||
if (f >= 1.0f) f = 1.0f;
|
||||
if (f <= 0.0f) f = 0.0f;
|
||||
};
|
||||
@ -254,8 +252,8 @@ UpdateTextureCoordinates(gfx::TexturedTriangle& aTriangle,
|
||||
t.x = x + (p.x - aIntersection.x) / aIntersection.width * w;
|
||||
t.y = y + (p.y - aIntersection.y) / aIntersection.height * h;
|
||||
|
||||
ValidateAndClamp(t.x);
|
||||
ValidateAndClamp(t.y);
|
||||
Clamp(t.x);
|
||||
Clamp(t.y);
|
||||
};
|
||||
|
||||
UpdatePoint(aTriangle.p1, aTriangle.textureCoords.p1);
|
||||
@ -270,9 +268,9 @@ Compositor::DrawGeometry(const gfx::Rect& aRect,
|
||||
gfx::Float aOpacity,
|
||||
const gfx::Matrix4x4& aTransform,
|
||||
const gfx::Rect& aVisibleRect,
|
||||
const Maybe<gfx::Polygon3D>& aGeometry)
|
||||
const Maybe<gfx::Polygon>& aGeometry)
|
||||
{
|
||||
if (!aGeometry) {
|
||||
if (!aGeometry || !SupportsLayerGeometry()) {
|
||||
DrawQuad(aRect, aClipRect, aEffectChain,
|
||||
aOpacity, aTransform, aVisibleRect);
|
||||
return;
|
||||
@ -283,11 +281,10 @@ Compositor::DrawGeometry(const gfx::Rect& aRect,
|
||||
return;
|
||||
}
|
||||
|
||||
gfx::Polygon3D clipped = aGeometry->ClipPolygon(aRect);
|
||||
nsTArray<gfx::Triangle> triangles = clipped.ToTriangles();
|
||||
const gfx::Polygon clipped = aGeometry->ClipPolygon(aRect);
|
||||
|
||||
for (gfx::Triangle& geometry : triangles) {
|
||||
const gfx::Rect intersection = aRect.Intersect(geometry.BoundingBox());
|
||||
for (gfx::Triangle& triangle : clipped.ToTriangles()) {
|
||||
const gfx::Rect intersection = aRect.Intersect(triangle.BoundingBox());
|
||||
|
||||
// Cull invisible triangles.
|
||||
if (intersection.IsEmpty()) {
|
||||
@ -297,21 +294,24 @@ Compositor::DrawGeometry(const gfx::Rect& aRect,
|
||||
MOZ_ASSERT(aRect.width > 0.0f && aRect.height > 0.0f);
|
||||
MOZ_ASSERT(intersection.width > 0.0f && intersection.height > 0.0f);
|
||||
|
||||
gfx::TexturedTriangle triangle(Move(geometry));
|
||||
triangle.width = aRect.width;
|
||||
triangle.height = aRect.height;
|
||||
gfx::TexturedTriangle texturedTriangle(Move(triangle));
|
||||
texturedTriangle.width = aRect.width;
|
||||
texturedTriangle.height = aRect.height;
|
||||
|
||||
// Since the texture was created for non-split geometry, we need to
|
||||
// update the texture coordinates to account for the split.
|
||||
if (aEffectChain.mPrimaryEffect->mType == EffectTypes::RGB) {
|
||||
const EffectTypes type = aEffectChain.mPrimaryEffect->mType;
|
||||
|
||||
if (type == EffectTypes::RGB || type == EffectTypes::YCBCR ||
|
||||
type == EffectTypes::NV12 || type == EffectTypes::RENDER_TARGET) {
|
||||
TexturedEffect* texturedEffect =
|
||||
static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
|
||||
|
||||
UpdateTextureCoordinates(triangle, aRect, intersection,
|
||||
UpdateTextureCoordinates(texturedTriangle, aRect, intersection,
|
||||
texturedEffect->mTextureCoords);
|
||||
}
|
||||
|
||||
DrawTriangle(triangle, aClipRect, aEffectChain,
|
||||
DrawTriangle(texturedTriangle, aClipRect, aEffectChain,
|
||||
aOpacity, aTransform, aVisibleRect);
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "mozilla/gfx/2D.h" // for DrawTarget
|
||||
#include "mozilla/gfx/MatrixFwd.h" // for Matrix4x4
|
||||
#include "mozilla/gfx/Point.h" // for IntSize, Point
|
||||
#include "mozilla/gfx/Polygon.h" // for Polygon3D
|
||||
#include "mozilla/gfx/Polygon.h" // for Polygon
|
||||
#include "mozilla/gfx/Rect.h" // for Rect, IntRect
|
||||
#include "mozilla/gfx/Types.h" // for Float
|
||||
#include "mozilla/gfx/Triangle.h" // for Triangle, TexturedTriangle
|
||||
@ -312,14 +312,14 @@ public:
|
||||
gfx::Float aOpacity,
|
||||
const gfx::Matrix4x4& aTransform,
|
||||
const gfx::Rect& aVisibleRect,
|
||||
const Maybe<gfx::Polygon3D>& aGeometry);
|
||||
const Maybe<gfx::Polygon>& aGeometry);
|
||||
|
||||
void DrawGeometry(const gfx::Rect& aRect,
|
||||
const gfx::IntRect& aClipRect,
|
||||
const EffectChain &aEffectChain,
|
||||
gfx::Float aOpacity,
|
||||
const gfx::Matrix4x4& aTransform,
|
||||
const Maybe<gfx::Polygon3D>& aGeometry)
|
||||
const Maybe<gfx::Polygon>& aGeometry)
|
||||
{
|
||||
DrawGeometry(aRect, aClipRect, aEffectChain, aOpacity,
|
||||
aTransform, aRect, aGeometry);
|
||||
@ -359,6 +359,11 @@ public:
|
||||
MOZ_CRASH("Compositor::DrawTriangle is not implemented for the current platform!");
|
||||
}
|
||||
|
||||
virtual bool SupportsLayerGeometry() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw an unfilled solid color rect. Typically used for debugging overlays.
|
||||
*/
|
||||
|
@ -28,7 +28,9 @@
|
||||
#include "mozilla/gfx/2D.h" // for DrawTarget
|
||||
#include "mozilla/gfx/BaseSize.h" // for BaseSize
|
||||
#include "mozilla/gfx/Matrix.h" // for Matrix4x4
|
||||
#include "mozilla/gfx/Polygon.h" // for Polygon
|
||||
#include "mozilla/layers/AsyncCanvasRenderer.h"
|
||||
#include "mozilla/layers/BSPTree.h" // for BSPTree
|
||||
#include "mozilla/layers/CompositableClient.h" // for CompositableClient
|
||||
#include "mozilla/layers/Compositor.h" // for Compositor
|
||||
#include "mozilla/layers/CompositorTypes.h"
|
||||
@ -47,6 +49,9 @@
|
||||
#include "mozilla/Compression.h"
|
||||
#include "TreeTraversal.h" // for ForEachNode
|
||||
|
||||
#include <deque>
|
||||
#include <set>
|
||||
|
||||
uint8_t gLayerManagerLayerBuilder;
|
||||
|
||||
namespace mozilla {
|
||||
@ -1337,34 +1342,122 @@ ContainerLayer::Collect3DContextLeaves(nsTArray<Layer*>& aToSort)
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
ContainerLayer::SortChildrenBy3DZOrder(nsTArray<Layer*>& aArray)
|
||||
static nsTArray<LayerPolygon>
|
||||
SortLayersWithBSPTree(nsTArray<Layer*>& aArray)
|
||||
{
|
||||
AutoTArray<Layer*, 10> toSort;
|
||||
std::deque<LayerPolygon> inputLayers;
|
||||
nsTArray<LayerPolygon> orderedLayers;
|
||||
|
||||
for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
|
||||
ContainerLayer* container = l->AsContainerLayer();
|
||||
if (container && container->Extend3DContext() &&
|
||||
!container->UseIntermediateSurface()) {
|
||||
container->Collect3DContextLeaves(toSort);
|
||||
} else {
|
||||
if (toSort.Length() > 0) {
|
||||
SortLayersBy3DZOrder(toSort);
|
||||
aArray.AppendElements(Move(toSort));
|
||||
// XXX The move analysis gets confused here, because toSort gets moved
|
||||
// here, and then gets used again outside of the loop. To clarify that
|
||||
// we realize that the array is going to be empty to the move checker,
|
||||
// we clear it again here. (This method renews toSort for the move
|
||||
// analysis)
|
||||
toSort.ClearAndRetainStorage();
|
||||
}
|
||||
aArray.AppendElement(l);
|
||||
// Build a list of polygons to be sorted.
|
||||
for (Layer* layer : aArray) {
|
||||
// Ignore invisible layers.
|
||||
if (!layer->IsVisible()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const gfx::IntRect& bounds =
|
||||
layer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds();
|
||||
const gfx::Matrix4x4& transform = layer->GetEffectiveTransform();
|
||||
|
||||
gfx::Polygon polygon = gfx::Polygon::FromRect(gfx::Rect(bounds));
|
||||
|
||||
// Transform the polygon to screen space.
|
||||
polygon.TransformToScreenSpace(transform);
|
||||
|
||||
if (polygon.GetPoints().Length() >= 3) {
|
||||
inputLayers.push_back(LayerPolygon(layer, Move(polygon)));
|
||||
}
|
||||
}
|
||||
if (toSort.Length() > 0) {
|
||||
SortLayersBy3DZOrder(toSort);
|
||||
aArray.AppendElements(Move(toSort));
|
||||
|
||||
if (inputLayers.empty()) {
|
||||
return orderedLayers;
|
||||
}
|
||||
|
||||
// Build a BSP tree from the list of polygons.
|
||||
BSPTree tree(inputLayers);
|
||||
orderedLayers = Move(tree.GetDrawOrder());
|
||||
|
||||
// Transform the polygons back to layer space.
|
||||
for (LayerPolygon& layerPolygon : orderedLayers) {
|
||||
gfx::Matrix4x4 inverse =
|
||||
layerPolygon.layer->GetEffectiveTransform().Inverse();
|
||||
|
||||
MOZ_ASSERT(layerPolygon.geometry);
|
||||
layerPolygon.geometry->TransformToLayerSpace(inverse);
|
||||
}
|
||||
|
||||
return orderedLayers;
|
||||
}
|
||||
|
||||
static nsTArray<LayerPolygon>
|
||||
StripLayerGeometry(const nsTArray<LayerPolygon>& aLayers)
|
||||
{
|
||||
nsTArray<LayerPolygon> layers;
|
||||
std::set<Layer*> uniqueLayers;
|
||||
|
||||
for (const LayerPolygon& layerPolygon : aLayers) {
|
||||
auto result = uniqueLayers.insert(layerPolygon.layer);
|
||||
|
||||
if (result.second) {
|
||||
// Layer was added to the set.
|
||||
layers.AppendElement(LayerPolygon(layerPolygon.layer));
|
||||
}
|
||||
}
|
||||
|
||||
return layers;
|
||||
}
|
||||
|
||||
nsTArray<LayerPolygon>
|
||||
ContainerLayer::SortChildrenBy3DZOrder(SortMode aSortMode)
|
||||
{
|
||||
AutoTArray<Layer*, 10> toSort;
|
||||
nsTArray<LayerPolygon> drawOrder;
|
||||
|
||||
for (Layer* layer = GetFirstChild(); layer; layer = layer->GetNextSibling()) {
|
||||
ContainerLayer* container = layer->AsContainerLayer();
|
||||
|
||||
if (container && container->Extend3DContext() &&
|
||||
!container->UseIntermediateSurface()) {
|
||||
|
||||
// Collect 3D layers in toSort array.
|
||||
container->Collect3DContextLeaves(toSort);
|
||||
|
||||
// Sort the 3D layers.
|
||||
if (toSort.Length() > 0) {
|
||||
nsTArray<LayerPolygon> sorted = SortLayersWithBSPTree(toSort);
|
||||
drawOrder.AppendElements(Move(sorted));
|
||||
|
||||
toSort.ClearAndRetainStorage();
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
drawOrder.AppendElement(LayerPolygon(layer));
|
||||
}
|
||||
|
||||
if (aSortMode == SortMode::WITHOUT_GEOMETRY) {
|
||||
// Compositor does not support arbitrary layers, strip the layer geometry
|
||||
// and duplicate layers.
|
||||
return StripLayerGeometry(drawOrder);
|
||||
}
|
||||
|
||||
return drawOrder;
|
||||
}
|
||||
|
||||
bool
|
||||
ContainerLayer::AnyAncestorOrThisIs3DContextLeaf()
|
||||
{
|
||||
Layer* parent = this;
|
||||
while (parent != nullptr) {
|
||||
if (parent->Is3DContextLeaf()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
parent = parent->GetParent();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
@ -1397,7 +1490,8 @@ ContainerLayer::DefaultComputeEffectiveTransforms(const Matrix4x4& aTransformToS
|
||||
((opacity != 1.0f && !Extend3DContext()) ||
|
||||
(blendMode != CompositionOp::OP_OVER))) {
|
||||
useIntermediateSurface = true;
|
||||
} else if (!idealTransform.Is2D() && Creates3DContextWithExtendingChildren()) {
|
||||
} else if ((!idealTransform.Is2D() || AnyAncestorOrThisIs3DContextLeaf()) &&
|
||||
Creates3DContextWithExtendingChildren()) {
|
||||
useIntermediateSurface = true;
|
||||
} else {
|
||||
useIntermediateSurface = false;
|
||||
@ -1709,7 +1803,7 @@ void WriteSnapshotToDumpFile(Compositor* aCompositor, DrawTarget* aTarget)
|
||||
|
||||
void
|
||||
Layer::Dump(std::stringstream& aStream, const char* aPrefix,
|
||||
bool aDumpHtml, bool aSorted)
|
||||
bool aDumpHtml, bool aSorted, const Maybe<gfx::Polygon>& aGeometry)
|
||||
{
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
bool dumpCompositorTexture = gfxEnv::DumpCompositorTextures() && AsHostLayer() &&
|
||||
@ -1729,7 +1823,7 @@ Layer::Dump(std::stringstream& aStream, const char* aPrefix,
|
||||
#endif
|
||||
aStream << ">";
|
||||
}
|
||||
DumpSelf(aStream, aPrefix);
|
||||
DumpSelf(aStream, aPrefix, aGeometry);
|
||||
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
if (dumpCompositorTexture) {
|
||||
@ -1777,12 +1871,13 @@ Layer::Dump(std::stringstream& aStream, const char* aPrefix,
|
||||
#endif
|
||||
|
||||
if (ContainerLayer* container = AsContainerLayer()) {
|
||||
AutoTArray<Layer*, 12> children;
|
||||
nsTArray<LayerPolygon> children;
|
||||
if (aSorted) {
|
||||
container->SortChildrenBy3DZOrder(children);
|
||||
children =
|
||||
container->SortChildrenBy3DZOrder(ContainerLayer::SortMode::WITH_GEOMETRY);
|
||||
} else {
|
||||
for (Layer* l = container->GetFirstChild(); l; l = l->GetNextSibling()) {
|
||||
children.AppendElement(l);
|
||||
children.AppendElement(LayerPolygon(l));
|
||||
}
|
||||
}
|
||||
nsAutoCString pfx(aPrefix);
|
||||
@ -1791,8 +1886,8 @@ Layer::Dump(std::stringstream& aStream, const char* aPrefix,
|
||||
aStream << "<ul>";
|
||||
}
|
||||
|
||||
for (Layer* child : children) {
|
||||
child->Dump(aStream, pfx.get(), aDumpHtml, aSorted);
|
||||
for (LayerPolygon& child : children) {
|
||||
child.layer->Dump(aStream, pfx.get(), aDumpHtml, aSorted, child.geometry);
|
||||
}
|
||||
|
||||
if (aDumpHtml) {
|
||||
@ -1805,10 +1900,31 @@ Layer::Dump(std::stringstream& aStream, const char* aPrefix,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
DumpGeometry(std::stringstream& aStream, const Maybe<gfx::Polygon>& aGeometry)
|
||||
{
|
||||
aStream << " [geometry=[";
|
||||
|
||||
const nsTArray<gfx::Point4D>& points = aGeometry->GetPoints();
|
||||
for (size_t i = 0; i < points.Length(); ++i) {
|
||||
const gfx::IntPoint point = TruncatedToInt(points[i].As2DPoint());
|
||||
const char* sfx = (i != points.Length() - 1) ? "," : "";
|
||||
AppendToString(aStream, point, "", sfx);
|
||||
}
|
||||
|
||||
aStream << "]]";
|
||||
}
|
||||
|
||||
void
|
||||
Layer::DumpSelf(std::stringstream& aStream, const char* aPrefix)
|
||||
Layer::DumpSelf(std::stringstream& aStream, const char* aPrefix,
|
||||
const Maybe<gfx::Polygon>& aGeometry)
|
||||
{
|
||||
PrintInfo(aStream, aPrefix);
|
||||
|
||||
if (aGeometry) {
|
||||
DumpGeometry(aStream, aGeometry);
|
||||
}
|
||||
|
||||
aStream << "\n";
|
||||
}
|
||||
|
||||
@ -2389,7 +2505,7 @@ LayerManager::Dump(std::stringstream& aStream, const char* aPrefix,
|
||||
if (aDumpHtml) {
|
||||
aStream << "<ul>";
|
||||
}
|
||||
GetRoot()->Dump(aStream, pfx.get(), aDumpHtml);
|
||||
GetRoot()->Dump(aStream, pfx.get(), aDumpHtml, aSorted);
|
||||
if (aDumpHtml) {
|
||||
aStream << "</ul></li></ul>";
|
||||
}
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "mozilla/gfx/TiledRegion.h" // for TiledIntRegion
|
||||
#include "mozilla/gfx/Types.h" // for SurfaceFormat
|
||||
#include "mozilla/gfx/UserData.h" // for UserData, etc
|
||||
#include "mozilla/layers/BSPTree.h" // for LayerPolygon
|
||||
#include "mozilla/layers/LayersTypes.h"
|
||||
#include "mozilla/mozalloc.h" // for operator delete, etc
|
||||
#include "nsAutoPtr.h" // for nsAutoPtr, nsRefPtr, etc
|
||||
@ -1691,11 +1692,13 @@ public:
|
||||
* aStream.
|
||||
*/
|
||||
void Dump(std::stringstream& aStream, const char* aPrefix="",
|
||||
bool aDumpHtml=false, bool aSorted=false);
|
||||
bool aDumpHtml=false, bool aSorted=false,
|
||||
const Maybe<gfx::Polygon>& aGeometry=Nothing());
|
||||
/**
|
||||
* Dump information about just this layer manager itself to aStream.
|
||||
*/
|
||||
void DumpSelf(std::stringstream& aStream, const char* aPrefix="");
|
||||
void DumpSelf(std::stringstream& aStream, const char* aPrefix="",
|
||||
const Maybe<gfx::Polygon>& aGeometry=Nothing());
|
||||
|
||||
/**
|
||||
* Dump information about this layer and its child & sibling layers to
|
||||
@ -2153,13 +2156,17 @@ public:
|
||||
|
||||
virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override;
|
||||
|
||||
void SortChildrenBy3DZOrder(nsTArray<Layer*>& aArray);
|
||||
enum class SortMode {
|
||||
WITH_GEOMETRY,
|
||||
WITHOUT_GEOMETRY,
|
||||
};
|
||||
|
||||
// These getters can be used anytime.
|
||||
nsTArray<LayerPolygon> SortChildrenBy3DZOrder(SortMode aSortMode);
|
||||
|
||||
virtual ContainerLayer* AsContainerLayer() override { return this; }
|
||||
virtual const ContainerLayer* AsContainerLayer() const override { return this; }
|
||||
|
||||
// These getters can be used anytime.
|
||||
virtual Layer* GetFirstChild() const override { return mFirstChild; }
|
||||
virtual Layer* GetLastChild() const override { return mLastChild; }
|
||||
float GetPreXScale() const { return mPreXScale; }
|
||||
@ -2241,8 +2248,29 @@ protected:
|
||||
void DidInsertChild(Layer* aLayer);
|
||||
void DidRemoveChild(Layer* aLayer);
|
||||
|
||||
bool AnyAncestorOrThisIs3DContextLeaf();
|
||||
|
||||
void Collect3DContextLeaves(nsTArray<Layer*>& aToSort);
|
||||
|
||||
// Collects child layers that do not extend 3D context. For ContainerLayers
|
||||
// that do extend 3D context, the 3D context leaves are collected.
|
||||
nsTArray<Layer*> CollectChildren() {
|
||||
nsTArray<Layer*> children;
|
||||
|
||||
for (Layer* layer = GetFirstChild(); layer; layer = layer->GetNextSibling()) {
|
||||
ContainerLayer* container = layer->AsContainerLayer();
|
||||
|
||||
if (container && container->Extend3DContext() &&
|
||||
!container->UseIntermediateSurface()) {
|
||||
container->Collect3DContextLeaves(children);
|
||||
} else {
|
||||
children.AppendElement(layer);
|
||||
}
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
ContainerLayer(LayerManager* aManager, void* aImplData);
|
||||
|
||||
/**
|
||||
|
@ -720,10 +720,12 @@ BasicLayerManager::PaintSelfOrChildren(PaintLayerContext& aPaintContext,
|
||||
} else {
|
||||
ContainerLayer* container =
|
||||
static_cast<ContainerLayer*>(aPaintContext.mLayer);
|
||||
AutoTArray<Layer*, 12> children;
|
||||
container->SortChildrenBy3DZOrder(children);
|
||||
|
||||
nsTArray<LayerPolygon> children =
|
||||
container->SortChildrenBy3DZOrder(ContainerLayer::SortMode::WITHOUT_GEOMETRY);
|
||||
|
||||
for (uint32_t i = 0; i < children.Length(); i++) {
|
||||
Layer* layer = children.ElementAt(i);
|
||||
Layer* layer = children.ElementAt(i).layer;
|
||||
if (layer->IsBackfaceHidden()) {
|
||||
continue;
|
||||
}
|
||||
|
@ -47,15 +47,13 @@ public:
|
||||
virtual void RenderLayer() override
|
||||
{
|
||||
RenderMaskLayers(this);
|
||||
|
||||
DefaultComputeSupportsComponentAlphaChildren();
|
||||
|
||||
AutoTArray<Layer*, 12> children;
|
||||
SortChildrenBy3DZOrder(children);
|
||||
DefaultComputeSupportsComponentAlphaChildren();
|
||||
|
||||
ReadbackProcessor readback;
|
||||
readback.BuildUpdates(this);
|
||||
|
||||
nsTArray<Layer*> children = CollectChildren();
|
||||
for (uint32_t i = 0; i < children.Length(); i++) {
|
||||
Layer* child = children.ElementAt(i);
|
||||
|
||||
|
@ -78,7 +78,8 @@ CanvasLayerComposite::GetRenderState()
|
||||
}
|
||||
|
||||
void
|
||||
CanvasLayerComposite::RenderLayer(const IntRect& aClipRect)
|
||||
CanvasLayerComposite::RenderLayer(const IntRect& aClipRect,
|
||||
const Maybe<gfx::Polygon>& aGeometry)
|
||||
{
|
||||
if (!mCompositableHost || !mCompositableHost->IsAttached()) {
|
||||
return;
|
||||
|
@ -51,7 +51,8 @@ public:
|
||||
virtual void SetLayerManager(HostLayerManager* aManager) override;
|
||||
|
||||
virtual Layer* GetLayer() override;
|
||||
virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
|
||||
virtual void RenderLayer(const gfx::IntRect& aClipRect,
|
||||
const Maybe<gfx::Polygon>& aGeometry) override;
|
||||
|
||||
virtual void CleanupResources() override;
|
||||
|
||||
|
@ -20,16 +20,19 @@ namespace layers {
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
void
|
||||
ColorLayerComposite::RenderLayer(const IntRect& aClipRect)
|
||||
ColorLayerComposite::RenderLayer(const gfx::IntRect& aClipRect,
|
||||
const Maybe<gfx::Polygon>& aGeometry)
|
||||
{
|
||||
Rect rect(GetBounds());
|
||||
|
||||
const Matrix4x4& transform = GetEffectiveTransform();
|
||||
|
||||
RenderWithAllMasks(this, mCompositor, aClipRect,
|
||||
[&](EffectChain& effectChain, const IntRect& clipRect) {
|
||||
GenEffectChain(effectChain);
|
||||
mCompositor->DrawQuad(rect, clipRect, effectChain, GetEffectiveOpacity(),
|
||||
transform);
|
||||
|
||||
mCompositor->DrawGeometry(rect, clipRect, effectChain,
|
||||
GetEffectiveOpacity(), transform, aGeometry);
|
||||
});
|
||||
|
||||
mCompositor->DrawDiagnostics(DiagnosticFlags::COLOR, rect, aClipRect,
|
||||
|
@ -47,7 +47,9 @@ public:
|
||||
|
||||
virtual void Destroy() override { mDestroyed = true; }
|
||||
|
||||
virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
|
||||
virtual void RenderLayer(const gfx::IntRect& aClipRect,
|
||||
const Maybe<gfx::Polygon>& aGeometry) override;
|
||||
|
||||
virtual void CleanupResources() override {};
|
||||
|
||||
virtual void GenEffectChain(EffectChain& aEffect) override;
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "mozilla/RefPtr.h" // for RefPtr, RefCounted, etc
|
||||
#include "mozilla/gfx/MatrixFwd.h" // for Matrix4x4
|
||||
#include "mozilla/gfx/Point.h" // for Point
|
||||
#include "mozilla/gfx/Polygon.h" // for Polygon
|
||||
#include "mozilla/gfx/Rect.h" // for Rect
|
||||
#include "mozilla/gfx/Types.h" // for SamplingFilter
|
||||
#include "mozilla/ipc/ProtocolUtils.h"
|
||||
@ -83,7 +84,8 @@ public:
|
||||
const gfx::Matrix4x4& aTransform,
|
||||
const gfx::SamplingFilter aSamplingFilter,
|
||||
const gfx::IntRect& aClipRect,
|
||||
const nsIntRegion* aVisibleRegion = nullptr) = 0;
|
||||
const nsIntRegion* aVisibleRegion = nullptr,
|
||||
const Maybe<gfx::Polygon>& aGeometry = Nothing()) = 0;
|
||||
|
||||
/**
|
||||
* Update the content host.
|
||||
@ -291,7 +293,7 @@ private:
|
||||
* CompositableMap must be global because the image bridge doesn't have any
|
||||
* reference to whatever we have created with PLayerTransaction. So, the only way to
|
||||
* actually connect these two worlds is to have something global that they can
|
||||
* both query (in the same thread). The map is not allocated the map on the
|
||||
* both query (in the same thread). The map is not allocated the map on the
|
||||
* stack to avoid the badness of static initialization.
|
||||
*
|
||||
* Also, we have a compositor/PLayerTransaction protocol/etc. per layer manager, and the
|
||||
|
@ -52,11 +52,11 @@ namespace layers {
|
||||
|
||||
using namespace gfx;
|
||||
|
||||
static void DrawLayerInfo(const RenderTargetIntRect& aClipRect,
|
||||
LayerManagerComposite* aManager,
|
||||
Layer* aLayer)
|
||||
static void
|
||||
DrawLayerInfo(const RenderTargetIntRect& aClipRect,
|
||||
LayerManagerComposite* aManager,
|
||||
Layer* aLayer)
|
||||
{
|
||||
|
||||
if (aLayer->GetType() == Layer::LayerType::TYPE_CONTAINER) {
|
||||
// XXX - should figure out a way to render this, but for now this
|
||||
// is hard to do, since it will often get superimposed over the first
|
||||
@ -77,14 +77,8 @@ static void DrawLayerInfo(const RenderTargetIntRect& aClipRect,
|
||||
maxWidth);
|
||||
}
|
||||
|
||||
template<class ContainerT>
|
||||
static gfx::IntRect ContainerVisibleRect(ContainerT* aContainer)
|
||||
{
|
||||
gfx::IntRect surfaceRect = aContainer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds();
|
||||
return surfaceRect;
|
||||
}
|
||||
|
||||
static void PrintUniformityInfo(Layer* aLayer)
|
||||
static void
|
||||
PrintUniformityInfo(Layer* aLayer)
|
||||
{
|
||||
#ifdef MOZ_ENABLE_PROFILER_SPS
|
||||
if (!profiler_is_active()) {
|
||||
@ -108,13 +102,69 @@ static void PrintUniformityInfo(Layer* aLayer)
|
||||
#endif
|
||||
}
|
||||
|
||||
static Maybe<gfx::Polygon>
|
||||
SelectLayerGeometry(const Maybe<gfx::Polygon>& aParentGeometry,
|
||||
const Maybe<gfx::Polygon>& aChildGeometry)
|
||||
{
|
||||
// Both the parent and the child layer were split.
|
||||
if (aParentGeometry && aChildGeometry) {
|
||||
// As we use intermediate surface in these cases, this branch should never
|
||||
// get executed.
|
||||
MOZ_ASSERT(false,
|
||||
"Both parent and child geometry present in nested 3D context!");
|
||||
return Some(aParentGeometry->ClipPolygon(*aChildGeometry));
|
||||
}
|
||||
|
||||
// The parent layer was split.
|
||||
if (aParentGeometry) {
|
||||
return aParentGeometry;
|
||||
}
|
||||
|
||||
// The child layer was split.
|
||||
if(aChildGeometry) {
|
||||
return aChildGeometry;
|
||||
}
|
||||
|
||||
// No split.
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
static void
|
||||
TransformLayerGeometry(Layer* aLayer, Maybe<gfx::Polygon>& aGeometry)
|
||||
{
|
||||
Layer* parent = aLayer->GetParent();
|
||||
gfx::Matrix4x4 transform;
|
||||
|
||||
// Collect all parent transforms.
|
||||
while (parent != nullptr && !parent->Is3DContextLeaf()) {
|
||||
transform = transform * parent->GetLocalTransform();
|
||||
parent = parent->GetParent();
|
||||
}
|
||||
|
||||
// Transform the geometry to the parent 3D context leaf coordinate space.
|
||||
aGeometry->TransformToScreenSpace(transform.ProjectTo2D().Inverse());
|
||||
}
|
||||
|
||||
|
||||
template<class ContainerT>
|
||||
static gfx::IntRect ContainerVisibleRect(ContainerT* aContainer)
|
||||
{
|
||||
gfx::IntRect surfaceRect = aContainer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds();
|
||||
return surfaceRect;
|
||||
}
|
||||
|
||||
|
||||
/* all of the per-layer prepared data we need to maintain */
|
||||
struct PreparedLayer
|
||||
{
|
||||
PreparedLayer(LayerComposite *aLayer, RenderTargetIntRect aClipRect) :
|
||||
mLayer(aLayer), mClipRect(aClipRect) {}
|
||||
PreparedLayer(LayerComposite *aLayer,
|
||||
RenderTargetIntRect aClipRect,
|
||||
Maybe<gfx::Polygon> aGeometry)
|
||||
: mLayer(aLayer), mClipRect(aClipRect), mGeometry(aGeometry) {}
|
||||
|
||||
LayerComposite* mLayer;
|
||||
RenderTargetIntRect mClipRect;
|
||||
Maybe<Polygon> mGeometry;
|
||||
};
|
||||
|
||||
/* all of the prepared data that we need in RenderLayer() */
|
||||
@ -134,17 +184,20 @@ ContainerPrepare(ContainerT* aContainer,
|
||||
aContainer->mPrepared = MakeUnique<PreparedData>();
|
||||
aContainer->mPrepared->mNeedsSurfaceCopy = false;
|
||||
|
||||
/**
|
||||
* Determine which layers to draw.
|
||||
*/
|
||||
AutoTArray<Layer*, 12> children;
|
||||
aContainer->SortChildrenBy3DZOrder(children);
|
||||
const ContainerLayerComposite::SortMode sortMode =
|
||||
aManager->GetCompositor()->SupportsLayerGeometry()
|
||||
? ContainerLayerComposite::SortMode::WITH_GEOMETRY
|
||||
: ContainerLayerComposite::SortMode::WITHOUT_GEOMETRY;
|
||||
|
||||
for (uint32_t i = 0; i < children.Length(); i++) {
|
||||
LayerComposite* layerToRender = static_cast<LayerComposite*>(children.ElementAt(i)->AsHostLayer());
|
||||
const nsTArray<LayerPolygon> polygons =
|
||||
aContainer->SortChildrenBy3DZOrder(sortMode);
|
||||
|
||||
RenderTargetIntRect clipRect = layerToRender->GetLayer()->
|
||||
CalculateScissorRect(aClipRect);
|
||||
for (const LayerPolygon& layer : polygons) {
|
||||
LayerComposite* layerToRender =
|
||||
static_cast<LayerComposite*>(layer.layer->ImplData());
|
||||
|
||||
RenderTargetIntRect clipRect =
|
||||
layerToRender->GetLayer()->CalculateScissorRect(aClipRect);
|
||||
|
||||
if (layerToRender->GetLayer()->IsBackfaceHidden()) {
|
||||
continue;
|
||||
@ -168,7 +221,7 @@ ContainerPrepare(ContainerT* aContainer,
|
||||
CULLING_LOG("Preparing sublayer %p\n", layerToRender->GetLayer());
|
||||
|
||||
layerToRender->Prepare(clipRect);
|
||||
aContainer->mPrepared->mLayers.AppendElement(PreparedLayer(layerToRender, clipRect));
|
||||
aContainer->mPrepared->mLayers.AppendElement(PreparedLayer(layerToRender, clipRect, layer.geometry));
|
||||
}
|
||||
|
||||
CULLING_LOG("Preparing container layer %p\n", aContainer->GetLayer());
|
||||
@ -334,18 +387,20 @@ RenderMinimap(ContainerT* aContainer, LayerManagerComposite* aManager,
|
||||
compositor->SlowDrawRect(r, viewPortColor, clipRect, aContainer->GetEffectiveTransform(), 2);
|
||||
}
|
||||
|
||||
|
||||
template<class ContainerT> void
|
||||
RenderLayers(ContainerT* aContainer,
|
||||
LayerManagerComposite* aManager,
|
||||
const RenderTargetIntRect& aClipRect)
|
||||
RenderLayers(ContainerT* aContainer, LayerManagerComposite* aManager,
|
||||
const RenderTargetIntRect& aClipRect,
|
||||
const Maybe<gfx::Polygon>& aGeometry)
|
||||
{
|
||||
Compositor* compositor = aManager->GetCompositor();
|
||||
|
||||
for (size_t i = 0u; i < aContainer->mPrepared->mLayers.Length(); i++) {
|
||||
PreparedLayer& preparedData = aContainer->mPrepared->mLayers[i];
|
||||
|
||||
const gfx::IntRect clipRect = preparedData.mClipRect.ToUnknownRect();
|
||||
LayerComposite* layerToRender = preparedData.mLayer;
|
||||
const RenderTargetIntRect& clipRect = preparedData.mClipRect;
|
||||
const Maybe<gfx::Polygon>& childGeometry = preparedData.mGeometry;
|
||||
|
||||
Layer* layer = layerToRender->GetLayer();
|
||||
|
||||
if (layerToRender->HasStaleCompositor()) {
|
||||
@ -372,11 +427,9 @@ RenderLayers(ContainerT* aContainer,
|
||||
// intersect areas in different coordinate spaces. So we do this a little more permissively
|
||||
// and only fill in the background when we know there is checkerboard, which in theory
|
||||
// should only occur transiently.
|
||||
gfx::IntRect layerBounds = layer->GetLayerBounds();
|
||||
EffectChain effectChain(layer);
|
||||
effectChain.mPrimaryEffect = new EffectSolidColor(color);
|
||||
aManager->GetCompositor()->DrawQuad(gfx::Rect(layerBounds.x, layerBounds.y, layerBounds.width, layerBounds.height),
|
||||
clipRect.ToUnknownRect(),
|
||||
aManager->GetCompositor()->DrawQuad(gfx::Rect(layer->GetLayerBounds()), clipRect,
|
||||
effectChain, layer->GetEffectiveOpacity(),
|
||||
layer->GetEffectiveTransform());
|
||||
}
|
||||
@ -393,7 +446,16 @@ RenderLayers(ContainerT* aContainer,
|
||||
layerToRender->SetClearRect(gfx::IntRect(0, 0, 0, 0));
|
||||
}
|
||||
} else {
|
||||
layerToRender->RenderLayer(clipRect.ToUnknownRect());
|
||||
Maybe<gfx::Polygon> geometry =
|
||||
SelectLayerGeometry(aGeometry, childGeometry);
|
||||
|
||||
// If we are dealing with a nested 3D context, we might need to transform
|
||||
// the geometry to the coordinate space of the parent 3D context leaf.
|
||||
if (geometry && !layer->Is3DContextLeaf()) {
|
||||
TransformLayerGeometry(layer, geometry);
|
||||
}
|
||||
|
||||
layerToRender->RenderLayer(clipRect, geometry);
|
||||
}
|
||||
|
||||
if (gfxPrefs::UniformityInfo()) {
|
||||
@ -401,7 +463,7 @@ RenderLayers(ContainerT* aContainer,
|
||||
}
|
||||
|
||||
if (gfxPrefs::DrawLayerInfo()) {
|
||||
DrawLayerInfo(clipRect, aManager, layer);
|
||||
DrawLayerInfo(preparedData.mClipRect, aManager, layer);
|
||||
}
|
||||
|
||||
// Draw a border around scrollable layers.
|
||||
@ -502,7 +564,10 @@ RenderIntermediate(ContainerT* aContainer,
|
||||
|
||||
compositor->SetRenderTarget(surface);
|
||||
// pre-render all of the layers into our temporary
|
||||
RenderLayers(aContainer, aManager, RenderTargetIntRect::FromUnknownRect(aClipRect));
|
||||
RenderLayers(aContainer, aManager,
|
||||
RenderTargetIntRect::FromUnknownRect(aClipRect),
|
||||
Nothing());
|
||||
|
||||
// Unbind the current surface and rebind the previous one.
|
||||
compositor->SetRenderTarget(previousTarget);
|
||||
}
|
||||
@ -510,7 +575,8 @@ RenderIntermediate(ContainerT* aContainer,
|
||||
template<class ContainerT> void
|
||||
ContainerRender(ContainerT* aContainer,
|
||||
LayerManagerComposite* aManager,
|
||||
const gfx::IntRect& aClipRect)
|
||||
const gfx::IntRect& aClipRect,
|
||||
const Maybe<gfx::Polygon>& aGeometry)
|
||||
{
|
||||
MOZ_ASSERT(aContainer->mPrepared);
|
||||
|
||||
@ -532,6 +598,7 @@ ContainerRender(ContainerT* aContainer,
|
||||
}
|
||||
|
||||
gfx::Rect visibleRect(aContainer->GetLocalVisibleRegion().ToUnknownRegion().GetBounds());
|
||||
|
||||
RefPtr<Compositor> compositor = aManager->GetCompositor();
|
||||
#ifdef MOZ_DUMP_PAINTING
|
||||
if (gfxEnv::DumpCompositorTextures()) {
|
||||
@ -546,14 +613,17 @@ ContainerRender(ContainerT* aContainer,
|
||||
RenderWithAllMasks(aContainer, compositor, aClipRect,
|
||||
[&, surface, compositor, container](EffectChain& effectChain, const IntRect& clipRect) {
|
||||
effectChain.mPrimaryEffect = new EffectRenderTarget(surface);
|
||||
compositor->DrawQuad(visibleRect, clipRect, effectChain,
|
||||
container->GetEffectiveOpacity(),
|
||||
container->GetEffectiveTransform());
|
||||
|
||||
compositor->DrawGeometry(visibleRect, clipRect, effectChain,
|
||||
container->GetEffectiveOpacity(),
|
||||
container->GetEffectiveTransform(), aGeometry);
|
||||
});
|
||||
|
||||
} else {
|
||||
RenderLayers(aContainer, aManager, RenderTargetIntRect::FromUnknownRect(aClipRect));
|
||||
RenderLayers(aContainer, aManager,
|
||||
RenderTargetIntRect::FromUnknownRect(aClipRect),
|
||||
aGeometry);
|
||||
}
|
||||
aContainer->mPrepared = nullptr;
|
||||
|
||||
// If it is a scrollable container layer with no child layers, and one of the APZCs
|
||||
// attached to it has a nonempty async transform, then that transform is not applied
|
||||
@ -623,9 +693,16 @@ ContainerLayerComposite::GetFirstChildComposite()
|
||||
}
|
||||
|
||||
void
|
||||
ContainerLayerComposite::RenderLayer(const gfx::IntRect& aClipRect)
|
||||
ContainerLayerComposite::Cleanup()
|
||||
{
|
||||
ContainerRender(this, mCompositeManager, aClipRect);
|
||||
mPrepared = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
ContainerLayerComposite::RenderLayer(const gfx::IntRect& aClipRect,
|
||||
const Maybe<gfx::Polygon>& aGeometry)
|
||||
{
|
||||
ContainerRender(this, mCompositeManager, aClipRect, aGeometry);
|
||||
}
|
||||
|
||||
void
|
||||
@ -674,9 +751,10 @@ RefLayerComposite::GetFirstChildComposite()
|
||||
}
|
||||
|
||||
void
|
||||
RefLayerComposite::RenderLayer(const gfx::IntRect& aClipRect)
|
||||
RefLayerComposite::RenderLayer(const gfx::IntRect& aClipRect,
|
||||
const Maybe<gfx::Polygon>& aGeometry)
|
||||
{
|
||||
ContainerRender(this, mCompositeManager, aClipRect);
|
||||
ContainerRender(this, mCompositeManager, aClipRect, aGeometry);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -29,11 +29,13 @@ class ContainerLayerComposite : public ContainerLayer,
|
||||
template<class ContainerT>
|
||||
friend void ContainerRender(ContainerT* aContainer,
|
||||
LayerManagerComposite* aManager,
|
||||
const RenderTargetIntRect& aClipRect);
|
||||
const RenderTargetIntRect& aClipRect,
|
||||
const Maybe<gfx::Polygon>& aGeometry);
|
||||
template<class ContainerT>
|
||||
friend void RenderLayers(ContainerT* aContainer,
|
||||
LayerManagerComposite* aManager,
|
||||
const RenderTargetIntRect& aClipRect);
|
||||
const RenderTargetIntRect& aClipRect,
|
||||
const Maybe<gfx::Polygon>& aGeometry);
|
||||
template<class ContainerT>
|
||||
friend void RenderIntermediate(ContainerT* aContainer,
|
||||
LayerManagerComposite* aManager,
|
||||
@ -79,7 +81,11 @@ public:
|
||||
|
||||
LayerComposite* GetFirstChildComposite() override;
|
||||
|
||||
virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
|
||||
virtual void Cleanup() override;
|
||||
|
||||
virtual void RenderLayer(const gfx::IntRect& aClipRect,
|
||||
const Maybe<gfx::Polygon>& aGeometry) override;
|
||||
|
||||
virtual void Prepare(const RenderTargetIntRect& aClipRect) override;
|
||||
|
||||
virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
|
||||
@ -128,11 +134,13 @@ class RefLayerComposite : public RefLayer,
|
||||
template<class ContainerT>
|
||||
friend void ContainerRender(ContainerT* aContainer,
|
||||
LayerManagerComposite* aManager,
|
||||
const gfx::IntRect& aClipRect);
|
||||
const gfx::IntRect& aClipRect,
|
||||
const Maybe<gfx::Polygon>& aGeometry);
|
||||
template<class ContainerT>
|
||||
friend void RenderLayers(ContainerT* aContainer,
|
||||
LayerManagerComposite* aManager,
|
||||
const gfx::IntRect& aClipRect);
|
||||
const gfx::IntRect& aClipRect,
|
||||
const Maybe<gfx::Polygon>& aGeometry);
|
||||
template<class ContainerT>
|
||||
friend void RenderIntermediate(ContainerT* aContainer,
|
||||
LayerManagerComposite* aManager,
|
||||
@ -163,7 +171,9 @@ public:
|
||||
|
||||
LayerComposite* GetFirstChildComposite() override;
|
||||
|
||||
virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
|
||||
virtual void RenderLayer(const gfx::IntRect& aClipRect,
|
||||
const Maybe<gfx::Polygon>& aGeometry) override;
|
||||
|
||||
virtual void Prepare(const RenderTargetIntRect& aClipRect) override;
|
||||
|
||||
virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
|
||||
|
@ -38,7 +38,8 @@ ContentHostTexture::Composite(LayerComposite* aLayer,
|
||||
const gfx::Matrix4x4& aTransform,
|
||||
const SamplingFilter aSamplingFilter,
|
||||
const IntRect& aClipRect,
|
||||
const nsIntRegion* aVisibleRegion)
|
||||
const nsIntRegion* aVisibleRegion,
|
||||
const Maybe<gfx::Polygon>& aGeometry)
|
||||
{
|
||||
NS_ASSERTION(aVisibleRegion, "Requires a visible region");
|
||||
|
||||
@ -180,7 +181,10 @@ ContentHostTexture::Composite(LayerComposite* aLayer,
|
||||
Float(tileRegionRect.y) / texRect.height,
|
||||
Float(tileRegionRect.width) / texRect.width,
|
||||
Float(tileRegionRect.height) / texRect.height);
|
||||
GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform);
|
||||
|
||||
GetCompositor()->DrawGeometry(rect, aClipRect, aEffectChain,
|
||||
aOpacity, aTransform, aGeometry);
|
||||
|
||||
if (usingTiles) {
|
||||
DiagnosticFlags diagnostics = DiagnosticFlags::CONTENT | DiagnosticFlags::BIGIMAGE;
|
||||
if (iterOnWhite) {
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "mozilla/gfx/BasePoint.h" // for BasePoint
|
||||
#include "mozilla/gfx/MatrixFwd.h" // for Matrix4x4
|
||||
#include "mozilla/gfx/Point.h" // for Point
|
||||
#include "mozilla/gfx/Polygon.h" // for Polygon
|
||||
#include "mozilla/gfx/Rect.h" // for Rect
|
||||
#include "mozilla/gfx/Types.h" // for SamplingFilter
|
||||
#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
|
||||
@ -123,7 +124,8 @@ public:
|
||||
const gfx::Matrix4x4& aTransform,
|
||||
const gfx::SamplingFilter aSamplingFilter,
|
||||
const gfx::IntRect& aClipRect,
|
||||
const nsIntRegion* aVisibleRegion = nullptr) override;
|
||||
const nsIntRegion* aVisibleRegion = nullptr,
|
||||
const Maybe<gfx::Polygon>& aGeometry = Nothing()) override;
|
||||
|
||||
virtual void SetCompositor(Compositor* aCompositor) override;
|
||||
|
||||
|
@ -287,7 +287,8 @@ ImageHost::Composite(LayerComposite* aLayer,
|
||||
const gfx::Matrix4x4& aTransform,
|
||||
const gfx::SamplingFilter aSamplingFilter,
|
||||
const gfx::IntRect& aClipRect,
|
||||
const nsIntRegion* aVisibleRegion)
|
||||
const nsIntRegion* aVisibleRegion,
|
||||
const Maybe<gfx::Polygon>& aGeometry)
|
||||
{
|
||||
if (!GetCompositor()) {
|
||||
// should only happen when a tab is dragged to another window and
|
||||
@ -392,8 +393,8 @@ ImageHost::Composite(LayerComposite* aLayer,
|
||||
effect->mTextureCoords.y = effect->mTextureCoords.YMost();
|
||||
effect->mTextureCoords.height = -effect->mTextureCoords.height;
|
||||
}
|
||||
GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain,
|
||||
aOpacity, aTransform);
|
||||
GetCompositor()->DrawGeometry(rect, aClipRect, aEffectChain,
|
||||
aOpacity, aTransform, aGeometry);
|
||||
GetCompositor()->DrawDiagnostics(diagnosticFlags | DiagnosticFlags::BIGIMAGE,
|
||||
rect, aClipRect, aTransform, mFlashCounter);
|
||||
} while (it->NextTile());
|
||||
@ -413,8 +414,8 @@ ImageHost::Composite(LayerComposite* aLayer,
|
||||
effect->mTextureCoords.height = -effect->mTextureCoords.height;
|
||||
}
|
||||
|
||||
GetCompositor()->DrawQuad(pictureRect, aClipRect, aEffectChain,
|
||||
aOpacity, aTransform);
|
||||
GetCompositor()->DrawGeometry(pictureRect, aClipRect, aEffectChain,
|
||||
aOpacity, aTransform, aGeometry);
|
||||
GetCompositor()->DrawDiagnostics(diagnosticFlags,
|
||||
pictureRect, aClipRect,
|
||||
aTransform, mFlashCounter);
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "mozilla/RefPtr.h" // for RefPtr
|
||||
#include "mozilla/gfx/MatrixFwd.h" // for Matrix4x4
|
||||
#include "mozilla/gfx/Point.h" // for Point
|
||||
#include "mozilla/gfx/Polygon.h" // for Polygon
|
||||
#include "mozilla/gfx/Rect.h" // for Rect
|
||||
#include "mozilla/gfx/Types.h" // for SamplingFilter
|
||||
#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
|
||||
@ -48,7 +49,8 @@ public:
|
||||
const gfx::Matrix4x4& aTransform,
|
||||
const gfx::SamplingFilter aSamplingFilter,
|
||||
const gfx::IntRect& aClipRect,
|
||||
const nsIntRegion* aVisibleRegion = nullptr) override;
|
||||
const nsIntRegion* aVisibleRegion = nullptr,
|
||||
const Maybe<gfx::Polygon>& aGeometry = Nothing()) override;
|
||||
|
||||
virtual void UseTextureHost(const nsTArray<TimedTexture>& aTextures) override;
|
||||
|
||||
|
@ -90,7 +90,8 @@ ImageLayerComposite::SetLayerManager(HostLayerManager* aManager)
|
||||
}
|
||||
|
||||
void
|
||||
ImageLayerComposite::RenderLayer(const IntRect& aClipRect)
|
||||
ImageLayerComposite::RenderLayer(const IntRect& aClipRect,
|
||||
const Maybe<gfx::Polygon>& aGeometry)
|
||||
{
|
||||
if (!mImageHost || !mImageHost->IsAttached()) {
|
||||
return;
|
||||
|
@ -45,7 +45,8 @@ public:
|
||||
|
||||
virtual void SetLayerManager(HostLayerManager* aManager) override;
|
||||
|
||||
virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
|
||||
virtual void RenderLayer(const gfx::IntRect& aClipRect,
|
||||
const Maybe<gfx::Polygon>& aGeometry) override;
|
||||
|
||||
virtual void ComputeEffectiveTransforms(const mozilla::gfx::Matrix4x4& aTransformToSurface) override;
|
||||
|
||||
|
@ -573,7 +573,7 @@ LayerManagerComposite::RenderDebugOverlay(const IntRect& aBounds)
|
||||
bool drawFps = gfxPrefs::LayersDrawFPS();
|
||||
bool drawFrameCounter = gfxPrefs::DrawFrameCounter();
|
||||
bool drawFrameColorBars = gfxPrefs::CompositorDrawColorBars();
|
||||
|
||||
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
|
||||
if (drawFps) {
|
||||
@ -926,7 +926,8 @@ LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion, const nsIntRegi
|
||||
|
||||
// Render our layers.
|
||||
RootLayer()->Prepare(ViewAs<RenderTargetPixel>(clipRect, PixelCastJustification::RenderTargetIsParentLayerForRoot));
|
||||
RootLayer()->RenderLayer(clipRect.ToUnknownRect());
|
||||
RootLayer()->RenderLayer(clipRect.ToUnknownRect(), Nothing());
|
||||
RootLayer()->Cleanup();
|
||||
|
||||
if (!mRegionToClear.IsEmpty()) {
|
||||
for (auto iter = mRegionToClear.RectIter(); !iter.Done(); iter.Next()) {
|
||||
@ -1133,7 +1134,7 @@ LayerManagerComposite::RenderToPresentationSurface()
|
||||
const IntRect clipRect = IntRect::Truncate(0, 0, actualWidth, actualHeight);
|
||||
|
||||
RootLayer()->Prepare(RenderTargetIntRect::FromUnknownRect(clipRect));
|
||||
RootLayer()->RenderLayer(clipRect);
|
||||
RootLayer()->RenderLayer(clipRect, Nothing());
|
||||
|
||||
mCompositor->EndFrame();
|
||||
}
|
||||
@ -1170,7 +1171,8 @@ public:
|
||||
|
||||
virtual void Destroy() override { mDestroyed = true; }
|
||||
|
||||
virtual void RenderLayer(const gfx::IntRect& aClipRect) override {}
|
||||
virtual void RenderLayer(const gfx::IntRect& aClipRect,
|
||||
const Maybe<gfx::Polygon>& aGeometry) override {}
|
||||
virtual void CleanupResources() override {};
|
||||
|
||||
virtual void GenEffectChain(EffectChain& aEffect) override {}
|
||||
@ -1213,7 +1215,8 @@ public:
|
||||
|
||||
virtual void Destroy() override { mDestroyed = true; }
|
||||
|
||||
virtual void RenderLayer(const gfx::IntRect& aClipRect) override {}
|
||||
virtual void RenderLayer(const gfx::IntRect& aClipRect,
|
||||
const Maybe<gfx::Polygon>& aGeometry) override {}
|
||||
virtual void CleanupResources() override {};
|
||||
|
||||
virtual void GenEffectChain(EffectChain& aEffect) override {}
|
||||
|
@ -523,7 +523,7 @@ public:
|
||||
gfx::Matrix4x4 GetShadowTransform();
|
||||
bool GetShadowTransformSetByAnimation() { return mShadowTransformSetByAnimation; }
|
||||
bool GetShadowOpacitySetByAnimation() { return mShadowOpacitySetByAnimation; }
|
||||
|
||||
|
||||
/**
|
||||
* Return true if a checkerboarding background color needs to be drawn
|
||||
* for this layer.
|
||||
@ -577,6 +577,7 @@ public:
|
||||
* concrete class destructor
|
||||
*/
|
||||
virtual void Destroy();
|
||||
virtual void Cleanup() {}
|
||||
|
||||
/**
|
||||
* Perform a first pass over the layer tree to render all of the intermediate
|
||||
@ -587,7 +588,8 @@ public:
|
||||
virtual void Prepare(const RenderTargetIntRect& aClipRect) {}
|
||||
|
||||
// TODO: This should also take RenderTargetIntRect like Prepare.
|
||||
virtual void RenderLayer(const gfx::IntRect& aClipRect) = 0;
|
||||
virtual void RenderLayer(const gfx::IntRect& aClipRect,
|
||||
const Maybe<gfx::Polygon>& aGeometry) = 0;
|
||||
|
||||
virtual bool SetCompositableHost(CompositableHost*)
|
||||
{
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
|
||||
#include "mozilla/gfx/Matrix.h" // for Matrix4x4
|
||||
#include "mozilla/gfx/Point.h" // for Point
|
||||
#include "mozilla/gfx/Polygon.h" // for Polygon
|
||||
#include "mozilla/gfx/Rect.h" // for RoundedToInt, Rect
|
||||
#include "mozilla/gfx/Types.h" // for SamplingFilter::LINEAR
|
||||
#include "mozilla/layers/Compositor.h" // for Compositor
|
||||
@ -98,7 +99,8 @@ PaintedLayerComposite::GetRenderState()
|
||||
}
|
||||
|
||||
void
|
||||
PaintedLayerComposite::RenderLayer(const gfx::IntRect& aClipRect)
|
||||
PaintedLayerComposite::RenderLayer(const gfx::IntRect& aClipRect,
|
||||
const Maybe<gfx::Polygon>& aGeometry)
|
||||
{
|
||||
if (!mBuffer || !mBuffer->IsAttached()) {
|
||||
return;
|
||||
@ -123,17 +125,14 @@ PaintedLayerComposite::RenderLayer(const gfx::IntRect& aClipRect)
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
RenderWithAllMasks(this, compositor, aClipRect,
|
||||
[&](EffectChain& effectChain, const gfx::IntRect& clipRect) {
|
||||
[&](EffectChain& effectChain,
|
||||
const gfx::IntRect& clipRect) {
|
||||
mBuffer->SetPaintWillResample(MayResample());
|
||||
|
||||
mBuffer->Composite(this, effectChain,
|
||||
GetEffectiveOpacity(),
|
||||
GetEffectiveTransform(),
|
||||
GetSamplingFilter(),
|
||||
clipRect,
|
||||
&visibleRegion);
|
||||
mBuffer->Composite(this, effectChain, GetEffectiveOpacity(),
|
||||
GetEffectiveTransform(), GetSamplingFilter(),
|
||||
clipRect, &visibleRegion, aGeometry);
|
||||
});
|
||||
|
||||
mBuffer->BumpFlashCounter();
|
||||
|
@ -51,7 +51,8 @@ public:
|
||||
|
||||
virtual void SetLayerManager(HostLayerManager* aManager) override;
|
||||
|
||||
virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
|
||||
virtual void RenderLayer(const gfx::IntRect& aClipRect,
|
||||
const Maybe<gfx::Polygon>& aGeometry) override;
|
||||
|
||||
virtual void CleanupResources() override;
|
||||
|
||||
|
@ -394,7 +394,8 @@ TiledContentHost::Composite(LayerComposite* aLayer,
|
||||
const gfx::Matrix4x4& aTransform,
|
||||
const gfx::SamplingFilter aSamplingFilter,
|
||||
const gfx::IntRect& aClipRect,
|
||||
const nsIntRegion* aVisibleRegion /* = nullptr */)
|
||||
const nsIntRegion* aVisibleRegion /* = nullptr */,
|
||||
const Maybe<gfx::Polygon>& aGeometry)
|
||||
{
|
||||
MOZ_ASSERT(mCompositor);
|
||||
// Reduce the opacity of the low-precision buffer to make it a
|
||||
@ -439,9 +440,10 @@ TiledContentHost::Composite(LayerComposite* aLayer,
|
||||
RenderLayerBuffer(mLowPrecisionTiledBuffer,
|
||||
lowPrecisionOpacityReduction < 1.0f ? &backgroundColor : nullptr,
|
||||
aEffectChain, lowPrecisionOpacityReduction * aOpacity,
|
||||
aSamplingFilter, aClipRect, *renderRegion, aTransform);
|
||||
aSamplingFilter, aClipRect, *renderRegion, aTransform, aGeometry);
|
||||
|
||||
RenderLayerBuffer(mTiledBuffer, nullptr, aEffectChain, aOpacity, aSamplingFilter,
|
||||
aClipRect, *renderRegion, aTransform);
|
||||
aClipRect, *renderRegion, aTransform, aGeometry);
|
||||
}
|
||||
|
||||
|
||||
@ -455,7 +457,8 @@ TiledContentHost::RenderTile(TileHost& aTile,
|
||||
const nsIntRegion& aScreenRegion,
|
||||
const IntPoint& aTextureOffset,
|
||||
const IntSize& aTextureBounds,
|
||||
const gfx::Rect& aVisibleRect)
|
||||
const gfx::Rect& aVisibleRect,
|
||||
const Maybe<gfx::Polygon>& aGeometry)
|
||||
{
|
||||
MOZ_ASSERT(!aTile.IsPlaceholderTile());
|
||||
|
||||
@ -498,8 +501,11 @@ TiledContentHost::RenderTile(TileHost& aTile,
|
||||
textureRect.y / aTextureBounds.height,
|
||||
textureRect.width / aTextureBounds.width,
|
||||
textureRect.height / aTextureBounds.height);
|
||||
mCompositor->DrawQuad(graphicsRect, aClipRect, aEffectChain, opacity, aTransform, aVisibleRect);
|
||||
|
||||
mCompositor->DrawGeometry(graphicsRect, aClipRect, aEffectChain, opacity,
|
||||
aTransform, aVisibleRect, aGeometry);
|
||||
}
|
||||
|
||||
DiagnosticFlags flags = DiagnosticFlags::CONTENT | DiagnosticFlags::TILE;
|
||||
if (aTile.mTextureHostOnWhite) {
|
||||
flags |= DiagnosticFlags::COMPONENT_ALPHA;
|
||||
@ -516,7 +522,8 @@ TiledContentHost::RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer,
|
||||
const gfx::SamplingFilter aSamplingFilter,
|
||||
const gfx::IntRect& aClipRect,
|
||||
nsIntRegion aVisibleRegion,
|
||||
gfx::Matrix4x4 aTransform)
|
||||
gfx::Matrix4x4 aTransform,
|
||||
const Maybe<Polygon>& aGeometry)
|
||||
{
|
||||
if (!mCompositor) {
|
||||
NS_WARNING("Can't render tiled content host - no compositor");
|
||||
@ -572,7 +579,8 @@ TiledContentHost::RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer,
|
||||
for (auto iter = backgroundRegion.RectIter(); !iter.Done(); iter.Next()) {
|
||||
const IntRect& rect = iter.Get();
|
||||
Rect graphicsRect(rect.x, rect.y, rect.width, rect.height);
|
||||
mCompositor->DrawQuad(graphicsRect, aClipRect, effect, 1.0, aTransform);
|
||||
mCompositor->DrawGeometry(graphicsRect, aClipRect, effect,
|
||||
1.0, aTransform, aGeometry);
|
||||
}
|
||||
}
|
||||
|
||||
@ -599,7 +607,9 @@ TiledContentHost::RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer,
|
||||
aTransform, aSamplingFilter, aClipRect, tileDrawRegion,
|
||||
tileOffset * resolution, aLayerBuffer.GetTileSize(),
|
||||
gfx::Rect(visibleRect.x, visibleRect.y,
|
||||
visibleRect.width, visibleRect.height));
|
||||
visibleRect.width, visibleRect.height),
|
||||
aGeometry);
|
||||
|
||||
if (tile.mTextureHostOnWhite) {
|
||||
componentAlphaDiagnostic = DiagnosticFlags::COMPONENT_ALPHA;
|
||||
}
|
||||
|
@ -236,7 +236,8 @@ public:
|
||||
const gfx::Matrix4x4& aTransform,
|
||||
const gfx::SamplingFilter aSamplingFilter,
|
||||
const gfx::IntRect& aClipRect,
|
||||
const nsIntRegion* aVisibleRegion = nullptr) override;
|
||||
const nsIntRegion* aVisibleRegion = nullptr,
|
||||
const Maybe<gfx::Polygon>& aGeometry = Nothing()) override;
|
||||
|
||||
virtual CompositableType GetType() override { return CompositableType::CONTENT_TILED; }
|
||||
|
||||
@ -266,7 +267,8 @@ private:
|
||||
const gfx::SamplingFilter aSamplingFilter,
|
||||
const gfx::IntRect& aClipRect,
|
||||
nsIntRegion aMaskRegion,
|
||||
gfx::Matrix4x4 aTransform);
|
||||
gfx::Matrix4x4 aTransform,
|
||||
const Maybe<gfx::Polygon>& aGeometry);
|
||||
|
||||
// Renders a single given tile.
|
||||
void RenderTile(TileHost& aTile,
|
||||
@ -278,7 +280,8 @@ private:
|
||||
const nsIntRegion& aScreenRegion,
|
||||
const gfx::IntPoint& aTextureOffset,
|
||||
const gfx::IntSize& aTextureBounds,
|
||||
const gfx::Rect& aVisibleRect);
|
||||
const gfx::Rect& aVisibleRect,
|
||||
const Maybe<gfx::Polygon>& aGeometry);
|
||||
|
||||
void EnsureTileStore() {}
|
||||
|
||||
|
@ -1819,5 +1819,11 @@ PerUnitTexturePoolOGL::DestroyTextures()
|
||||
mTextures.SetLength(0);
|
||||
}
|
||||
|
||||
bool
|
||||
CompositorOGL::SupportsLayerGeometry() const
|
||||
{
|
||||
return gfxPrefs::OGLLayerGeometry();
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
@ -171,6 +171,8 @@ public:
|
||||
const gfx::Matrix4x4& aTransform,
|
||||
const gfx::Rect& aVisibleRect) override;
|
||||
|
||||
virtual bool SupportsLayerGeometry() const override;
|
||||
|
||||
virtual void EndFrame() override;
|
||||
|
||||
virtual bool SupportsPartialTextureUpdate() override;
|
||||
|
@ -13,9 +13,19 @@ namespace gfx {
|
||||
const float kEpsilon = 0.001f;
|
||||
|
||||
// Compares two points while allowing some numerical inaccuracy.
|
||||
bool FuzzyEquals(const Point4D& lhs, const Point4D& rhs)
|
||||
{
|
||||
const auto d = lhs - rhs;
|
||||
|
||||
return std::abs(d.x) < kEpsilon &&
|
||||
std::abs(d.y) < kEpsilon &&
|
||||
std::abs(d.z) < kEpsilon &&
|
||||
std::abs(d.w) < kEpsilon;
|
||||
}
|
||||
|
||||
bool FuzzyEquals(const Point3D& lhs, const Point3D& rhs)
|
||||
{
|
||||
const Point3D d = lhs - rhs;
|
||||
const auto d = lhs - rhs;
|
||||
|
||||
return std::abs(d.x) < kEpsilon &&
|
||||
std::abs(d.y) < kEpsilon &&
|
||||
@ -24,7 +34,7 @@ bool FuzzyEquals(const Point3D& lhs, const Point3D& rhs)
|
||||
|
||||
bool FuzzyEquals(const Point& lhs, const Point& rhs)
|
||||
{
|
||||
const Point d = lhs - rhs;
|
||||
const auto d = lhs - rhs;
|
||||
|
||||
return std::abs(d.x) < kEpsilon &&
|
||||
std::abs(d.y) < kEpsilon;
|
||||
@ -39,10 +49,10 @@ bool operator==(const Triangle& lhs, const Triangle& rhs)
|
||||
|
||||
// Compares the points of two polygons and ensures
|
||||
// that the points are in the same winding order.
|
||||
bool operator==(const Polygon3D& lhs, const Polygon3D& rhs)
|
||||
bool operator==(const Polygon& lhs, const Polygon& rhs)
|
||||
{
|
||||
const nsTArray<Point3D>& left = lhs.GetPoints();
|
||||
const nsTArray<Point3D>& right = rhs.GetPoints();
|
||||
const auto& left = lhs.GetPoints();
|
||||
const auto& right = rhs.GetPoints();
|
||||
|
||||
// Polygons do not have the same amount of points.
|
||||
if (left.Length() != right.Length()) {
|
||||
@ -99,7 +109,7 @@ TEST(PolygonTestUtils, TestSanity)
|
||||
EXPECT_FALSE(FuzzyEquals(Point3D(0.01f, 0.01f, 0.01f),
|
||||
Point3D(0.0f, 0.0f, 0.0f)));
|
||||
|
||||
Polygon3D p1 {
|
||||
Polygon p1 {
|
||||
Point3D(0.0f, 0.0f, 1.0f),
|
||||
Point3D(1.0f, 0.0f, 1.0f),
|
||||
Point3D(1.0f, 1.0f, 1.0f),
|
||||
@ -107,21 +117,21 @@ TEST(PolygonTestUtils, TestSanity)
|
||||
};
|
||||
|
||||
// Same points as above shifted forward by one position.
|
||||
Polygon3D shifted {
|
||||
Polygon shifted {
|
||||
Point3D(0.0f, 1.0f, 1.0f),
|
||||
Point3D(0.0f, 0.0f, 1.0f),
|
||||
Point3D(1.0f, 0.0f, 1.0f),
|
||||
Point3D(1.0f, 1.0f, 1.0f)
|
||||
};
|
||||
|
||||
Polygon3D p2 {
|
||||
Polygon p2 {
|
||||
Point3D(0.00001f, 0.00001f, 1.00001f),
|
||||
Point3D(1.00001f, 0.00001f, 1.00001f),
|
||||
Point3D(1.00001f, 1.00001f, 1.00001f),
|
||||
Point3D(0.00001f, 1.00001f, 1.00001f)
|
||||
};
|
||||
|
||||
Polygon3D p3 {
|
||||
Polygon p3 {
|
||||
Point3D(0.01f, 0.01f, 1.01f),
|
||||
Point3D(1.01f, 0.01f, 1.01f),
|
||||
Point3D(1.01f, 1.01f, 1.01f),
|
||||
|
@ -16,11 +16,12 @@
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
bool FuzzyEquals(const Point4D& lhs, const Point4D& rhs);
|
||||
bool FuzzyEquals(const Point3D& lhs, const Point3D& rhs);
|
||||
bool FuzzyEquals(const Point& lhs, const Point& rhs);
|
||||
|
||||
bool operator==(const Triangle& lhs, const Triangle& rhs);
|
||||
bool operator==(const Polygon3D& lhs, const Polygon3D& rhs);
|
||||
bool operator==(const Polygon& lhs, const Polygon& rhs);
|
||||
|
||||
// Compares two arrays with the equality operator.
|
||||
template<typename T>
|
||||
|
@ -16,11 +16,11 @@ using namespace mozilla::layers;
|
||||
|
||||
namespace {
|
||||
|
||||
static void RunTest(std::deque<Polygon3D> aPolygons,
|
||||
std::deque<Polygon3D> aExpected)
|
||||
static void RunTest(std::deque<Polygon> aPolygons,
|
||||
std::deque<Polygon> aExpected)
|
||||
{
|
||||
std::deque<LayerPolygon> layers;
|
||||
for (Polygon3D& polygon : aPolygons) {
|
||||
for (Polygon& polygon : aPolygons) {
|
||||
layers.push_back(LayerPolygon(nullptr, Move(polygon)));
|
||||
}
|
||||
|
||||
@ -39,14 +39,14 @@ static void RunTest(std::deque<Polygon3D> aPolygons,
|
||||
|
||||
TEST(BSPTree, SameNode)
|
||||
{
|
||||
const std::deque<Polygon3D> polygons {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> polygons {
|
||||
Polygon {
|
||||
Point3D(0.0f, 0.0f, 0.0f),
|
||||
Point3D(1.0f, 0.0f, 0.0f),
|
||||
Point3D(1.0f, 1.0f, 0.0f),
|
||||
Point3D(0.0f, 1.0f, 0.0f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(0.0f, 0.0f, 0.0f),
|
||||
Point3D(1.0f, 0.0f, 0.0f),
|
||||
Point3D(1.0f, 1.0f, 0.0f),
|
||||
@ -59,14 +59,14 @@ TEST(BSPTree, SameNode)
|
||||
|
||||
TEST(BSPTree, OneChild)
|
||||
{
|
||||
const Polygon3D p1 {
|
||||
const Polygon p1 {
|
||||
Point3D(0.0f, 0.0f, 0.0f),
|
||||
Point3D(1.0f, 0.0f, 0.0f),
|
||||
Point3D(1.0f, 1.0f, 0.0f),
|
||||
Point3D(0.0f, 1.0f, 0.0f)
|
||||
};
|
||||
|
||||
const Polygon3D p2 {
|
||||
const Polygon p2 {
|
||||
Point3D(0.0f, 0.0f, 1.0f),
|
||||
Point3D(1.0f, 0.0f, 1.0f),
|
||||
Point3D(1.0f, 1.0f, 1.0f),
|
||||
@ -79,14 +79,14 @@ TEST(BSPTree, OneChild)
|
||||
|
||||
TEST(BSPTree, SharedEdge1)
|
||||
{
|
||||
Polygon3D p1 {
|
||||
Polygon p1 {
|
||||
Point3D(1.0f, 0.0f, 1.0f),
|
||||
Point3D(0.0f, 0.0f, 1.0f),
|
||||
Point3D(0.0f, 1.0f, 1.0f),
|
||||
Point3D(1.0f, 1.0f, 1.0f)
|
||||
};
|
||||
|
||||
Polygon3D p2 {
|
||||
Polygon p2 {
|
||||
Point3D(1.0f, 0.0f, 1.0f),
|
||||
Point3D(1.0f, 1.0f, 1.0f),
|
||||
Point3D(2.0f, 2.0f, 1.0f),
|
||||
@ -98,14 +98,14 @@ TEST(BSPTree, SharedEdge1)
|
||||
|
||||
TEST(BSPTree, SharedEdge2)
|
||||
{
|
||||
Polygon3D p1 {
|
||||
Polygon p1 {
|
||||
Point3D(1.0f, 0.0f, 1.0f),
|
||||
Point3D(0.0f, 0.0f, 1.0f),
|
||||
Point3D(0.0f, 1.0f, 1.0f),
|
||||
Point3D(1.0f, 1.0f, 1.0f)
|
||||
};
|
||||
|
||||
Polygon3D p2 {
|
||||
Polygon p2 {
|
||||
Point3D(1.0f, 0.0f, 1.0f),
|
||||
Point3D(1.0f, 1.0f, 1.0f),
|
||||
Point3D(2.0f, 2.0f, 0.0f),
|
||||
@ -117,34 +117,34 @@ TEST(BSPTree, SharedEdge2)
|
||||
|
||||
TEST(BSPTree, SplitSharedEdge)
|
||||
{
|
||||
Polygon3D p1 {
|
||||
Polygon p1 {
|
||||
Point3D(1.0f, 0.0f, 1.0f),
|
||||
Point3D(0.0f, 0.0f, 1.0f),
|
||||
Point3D(0.0f, 1.0f, 1.0f),
|
||||
Point3D(1.0f, 1.0f, 1.0f)
|
||||
};
|
||||
|
||||
Polygon3D p2 {
|
||||
Polygon p2 {
|
||||
Point3D(1.0f, 0.0f, 2.0f),
|
||||
Point3D(1.0f, 1.0f, 2.0f),
|
||||
Point3D(1.0f, 1.0f, 0.0f),
|
||||
Point3D(1.0f, 0.0f, 0.0f)
|
||||
};
|
||||
|
||||
const std::deque<Polygon3D> expected {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> expected {
|
||||
Polygon {
|
||||
Point3D(1.0f, 1.0f, 1.0f),
|
||||
Point3D(1.0f, 1.0f, 0.0f),
|
||||
Point3D(1.0f, 0.0f, 0.0f),
|
||||
Point3D(1.0f, 0.0f, 1.0f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(1.0f, 0.0f, 1.0f),
|
||||
Point3D(0.0f, 0.0f, 1.0f),
|
||||
Point3D(0.0f, 1.0f, 1.0f),
|
||||
Point3D(1.0f, 1.0f, 1.0f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(1.0f, 0.0f, 2.0f),
|
||||
Point3D(1.0f, 1.0f, 2.0f),
|
||||
Point3D(1.0f, 1.0f, 1.0f),
|
||||
@ -157,29 +157,29 @@ TEST(BSPTree, SplitSharedEdge)
|
||||
|
||||
TEST(BSPTree, SplitSimple1)
|
||||
{
|
||||
Polygon3D p1 {
|
||||
Polygon p1 {
|
||||
Point3D(0.0f, 0.0f, 1.0f),
|
||||
Point3D(1.0f, 0.0f, 1.0f),
|
||||
Point3D(1.0f, 1.0f, 1.0f),
|
||||
Point3D(0.0f, 1.0f, 1.0f)
|
||||
};
|
||||
|
||||
Polygon3D p2 {
|
||||
Polygon p2 {
|
||||
Point3D(0.0f, 0.0f, 2.0f),
|
||||
Point3D(1.0f, 0.0f, 2.0f),
|
||||
Point3D(1.0f, 1.0f, 0.0f),
|
||||
Point3D(0.0f, 1.0f, 0.0f)
|
||||
};
|
||||
|
||||
const std::deque<Polygon3D> expected {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> expected {
|
||||
Polygon {
|
||||
Point3D(0.0f, 1.0f, 0.0f),
|
||||
Point3D(0.0f, 0.5f, 1.0f),
|
||||
Point3D(1.0f, 0.5f, 1.0f),
|
||||
Point3D(1.0f, 1.0f, 0.0f)
|
||||
},
|
||||
p1,
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(0.0f, 0.0f, 2.0f),
|
||||
Point3D(1.0f, 0.0f, 2.0f),
|
||||
Point3D(1.0f, 0.5f, 1.0f),
|
||||
@ -191,14 +191,14 @@ TEST(BSPTree, SplitSimple1)
|
||||
}
|
||||
|
||||
TEST(BSPTree, SplitSimple2) {
|
||||
const std::deque<Polygon3D> polygons {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> polygons {
|
||||
Polygon {
|
||||
Point3D(-5.00000f, -5.00000f, 0.00000f),
|
||||
Point3D(-5.00000f, 5.00000f, 0.00000f),
|
||||
Point3D(5.00000f, 5.00000f, 0.00000f),
|
||||
Point3D(5.00000f, -5.00000f, 0.00000f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(0.00000f, -5.00000f, -5.00000f),
|
||||
Point3D(0.00000f, 5.00000f, -5.00000f),
|
||||
Point3D(0.00000f, 5.00000f, 5.00000f),
|
||||
@ -206,20 +206,20 @@ TEST(BSPTree, SplitSimple2) {
|
||||
}
|
||||
};
|
||||
|
||||
const std::deque<Polygon3D> expected {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> expected {
|
||||
Polygon {
|
||||
Point3D(0.00000f, -5.00000f, 0.00000f),
|
||||
Point3D(0.00000f, -5.00000f, -5.00000f),
|
||||
Point3D(0.00000f, 5.00000f, -5.00000f),
|
||||
Point3D(0.00000f, 5.00000f, 0.00000f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(-5.00000f, -5.00000f, 0.00000f),
|
||||
Point3D(-5.00000f, 5.00000f, 0.00000f),
|
||||
Point3D(5.00000f, 5.00000f, 0.00000f),
|
||||
Point3D(5.00000f, -5.00000f, 0.00000f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(0.00000f, 5.00000f, 0.00000f),
|
||||
Point3D(0.00000f, 5.00000f, 5.00000f),
|
||||
Point3D(0.00000f, -5.00000f, 5.00000f),
|
||||
@ -230,20 +230,20 @@ TEST(BSPTree, SplitSimple2) {
|
||||
}
|
||||
|
||||
TEST(BSPTree, NoSplit1) {
|
||||
const std::deque<Polygon3D> polygons {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> polygons {
|
||||
Polygon {
|
||||
Point3D(0.00000f, 10.00000f, 0.00000f),
|
||||
Point3D(0.00000f, 0.00000f, 0.00000f),
|
||||
Point3D(10.00000f, 0.00000f, 0.00000f),
|
||||
Point3D(10.00000f, 10.00000f, 0.00000f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(0.00000f, 10.00000f, -5.00000f),
|
||||
Point3D(0.00000f, 0.00000f, -5.00000f),
|
||||
Point3D(10.00000f, 0.00000f, -5.00000f),
|
||||
Point3D(10.00000f, 10.00000f, -5.00000f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(0.00000f, 10.00000f, 5.00000f),
|
||||
Point3D(0.00000f, 0.00000f, 5.00000f),
|
||||
Point3D(10.00000f, 0.00000f, 5.00000f),
|
||||
@ -251,20 +251,20 @@ TEST(BSPTree, NoSplit1) {
|
||||
}
|
||||
};
|
||||
|
||||
const std::deque<Polygon3D> expected {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> expected {
|
||||
Polygon {
|
||||
Point3D(0.00000f, 10.00000f, -5.00000f),
|
||||
Point3D(0.00000f, 0.00000f, -5.00000f),
|
||||
Point3D(10.00000f, 0.00000f, -5.00000f),
|
||||
Point3D(10.00000f, 10.00000f, -5.00000f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(0.00000f, 10.00000f, 0.00000f),
|
||||
Point3D(0.00000f, 0.00000f, 0.00000f),
|
||||
Point3D(10.00000f, 0.00000f, 0.00000f),
|
||||
Point3D(10.00000f, 10.00000f, 0.00000f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(0.00000f, 10.00000f, 5.00000f),
|
||||
Point3D(0.00000f, 0.00000f, 5.00000f),
|
||||
Point3D(10.00000f, 0.00000f, 5.00000f),
|
||||
@ -275,14 +275,14 @@ TEST(BSPTree, NoSplit1) {
|
||||
}
|
||||
|
||||
TEST(BSPTree, NoSplit2) {
|
||||
const std::deque<Polygon3D> polygons {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> polygons {
|
||||
Polygon {
|
||||
Point3D(-5.00000f, -5.00000f, 0.00000f),
|
||||
Point3D(-5.00000f, 5.00000f, 0.00000f),
|
||||
Point3D(5.00000f, 5.00000f, 0.00000f),
|
||||
Point3D(5.00000f, -5.00000f, 0.00000f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(0.00000f, 5.00000f, -15.00000f),
|
||||
Point3D(0.00000f, -5.00000f, -15.00000f),
|
||||
Point3D(0.00000f, -5.00000f, -10.00000f),
|
||||
@ -290,14 +290,14 @@ TEST(BSPTree, NoSplit2) {
|
||||
}
|
||||
};
|
||||
|
||||
const std::deque<Polygon3D> expected {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> expected {
|
||||
Polygon {
|
||||
Point3D(0.00000f, 5.00000f, -15.00000f),
|
||||
Point3D(0.00000f, -5.00000f, -15.00000f),
|
||||
Point3D(0.00000f, -5.00000f, -10.00000f),
|
||||
Point3D(0.00000f, 5.00000f, -10.00000f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(-5.00000f, -5.00000f, 0.00000f),
|
||||
Point3D(-5.00000f, 5.00000f, 0.00000f),
|
||||
Point3D(5.00000f, 5.00000f, 0.00000f),
|
||||
@ -308,14 +308,14 @@ TEST(BSPTree, NoSplit2) {
|
||||
}
|
||||
|
||||
TEST(BSPTree, TwoPlaneIntersectRotate0degrees) {
|
||||
const std::deque<Polygon3D> polygons {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> polygons {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, 2.00000f, 2.00000f),
|
||||
Point3D(-0.00000f, -2.00000f, 2.00000f),
|
||||
Point3D(0.00010f, -2.00000f, -2.00000f),
|
||||
Point3D(0.00010f, 2.00000f, -2.00000f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, 0.00000f, 2.00000f),
|
||||
Point3D(2.00000f, -0.00000f, -2.00000f),
|
||||
Point3D(-2.00000f, 0.00000f, -2.00000f),
|
||||
@ -323,14 +323,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate0degrees) {
|
||||
}
|
||||
};
|
||||
|
||||
const std::deque<Polygon3D> expected {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> expected {
|
||||
Polygon {
|
||||
Point3D(2.00000f, 0.00000f, 2.00000f),
|
||||
Point3D(2.00000f, -0.00000f, -2.00000f),
|
||||
Point3D(-2.00000f, 0.00000f, -2.00000f),
|
||||
Point3D(-2.00000f, 0.00010f, 2.00000f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, 2.00000f, 2.00000f),
|
||||
Point3D(-0.00000f, -2.00000f, 2.00000f),
|
||||
Point3D(0.00010f, -2.00000f, -2.00000f),
|
||||
@ -341,14 +341,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate0degrees) {
|
||||
}
|
||||
|
||||
TEST(BSPTree, TwoPlaneIntersectRotate20degrees) {
|
||||
const std::deque<Polygon3D> polygons {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> polygons {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, 1.19540f, 2.56350f),
|
||||
Point3D(-0.00000f, -2.56340f, 1.19540f),
|
||||
Point3D(0.00010f, -1.19530f, -2.56340f),
|
||||
Point3D(0.00010f, 2.56350f, -1.19530f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, -0.68400f, 1.87940f),
|
||||
Point3D(2.00000f, 0.68410f, -1.87930f),
|
||||
Point3D(-2.00000f, 0.68410f, -1.87930f),
|
||||
@ -356,14 +356,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate20degrees) {
|
||||
}
|
||||
};
|
||||
|
||||
const std::deque<Polygon3D> expected {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> expected {
|
||||
Polygon {
|
||||
Point3D(2.00000f, -0.68400f, 1.87940f),
|
||||
Point3D(2.00000f, 0.68410f, -1.87930f),
|
||||
Point3D(-2.00000f, 0.68410f, -1.87930f),
|
||||
Point3D(-2.00000f, -0.68400f, 1.87940f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, 1.19540f, 2.56350f),
|
||||
Point3D(-0.00000f, -2.56340f, 1.19540f),
|
||||
Point3D(0.00010f, -1.19530f, -2.56340f),
|
||||
@ -374,14 +374,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate20degrees) {
|
||||
}
|
||||
|
||||
TEST(BSPTree, TwoPlaneIntersectRotate40degrees) {
|
||||
const std::deque<Polygon3D> polygons {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> polygons {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, -0.73200f, 2.73210f),
|
||||
Point3D(-0.00000f, -2.73200f, -0.73200f),
|
||||
Point3D(0.00010f, 0.73210f, -2.73200f),
|
||||
Point3D(0.00010f, 2.73210f, 0.73210f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, -1.73200f, 1.00000f),
|
||||
Point3D(2.00000f, 1.73210f, -0.99990f),
|
||||
Point3D(-2.00000f, 1.73210f, -0.99990f),
|
||||
@ -389,14 +389,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate40degrees) {
|
||||
}
|
||||
};
|
||||
|
||||
const std::deque<Polygon3D> expected {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> expected {
|
||||
Polygon {
|
||||
Point3D(2.00000f, -1.73200f, 1.00000f),
|
||||
Point3D(2.00000f, 1.73210f, -0.99990f),
|
||||
Point3D(-2.00000f, 1.73210f, -0.99990f),
|
||||
Point3D(-2.00000f, -1.73200f, 1.00000f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, -0.73200f, 2.73210f),
|
||||
Point3D(-0.00000f, -2.73200f, -0.73200f),
|
||||
Point3D(0.00010f, 0.73210f, -2.73200f),
|
||||
@ -407,14 +407,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate40degrees) {
|
||||
}
|
||||
|
||||
TEST(BSPTree, TwoPlaneIntersectRotate60degrees) {
|
||||
const std::deque<Polygon3D> polygons {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> polygons {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, -2.73200f, 0.73210f),
|
||||
Point3D(-0.00000f, -0.73200f, -2.73200f),
|
||||
Point3D(0.00010f, 2.73210f, -0.73200f),
|
||||
Point3D(0.00010f, 0.73210f, 2.73210f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, -1.73200f, -1.00000f),
|
||||
Point3D(2.00000f, 1.73210f, 1.00010f),
|
||||
Point3D(-2.00000f, 1.73210f, 1.00010f),
|
||||
@ -422,20 +422,20 @@ TEST(BSPTree, TwoPlaneIntersectRotate60degrees) {
|
||||
}
|
||||
};
|
||||
|
||||
const std::deque<Polygon3D> expected {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> expected {
|
||||
Polygon {
|
||||
Point3D(-2.00000f, 1.26793f, 0.73210f),
|
||||
Point3D(-2.00000f, -1.73200f, -1.00000f),
|
||||
Point3D(2.00000f, -1.73200f, -1.00000f),
|
||||
Point3D(2.00000f, 1.26793f, 0.73210f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, -2.73200f, 0.73210f),
|
||||
Point3D(-0.00000f, -0.73200f, -2.73200f),
|
||||
Point3D(0.00010f, 2.73210f, -0.73200f),
|
||||
Point3D(0.00010f, 0.73210f, 2.73210f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, 1.26793f, 0.73210f),
|
||||
Point3D(2.00000f, 1.73210f, 1.00010f),
|
||||
Point3D(-2.00000f, 1.73210f, 1.00010f),
|
||||
@ -446,14 +446,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate60degrees) {
|
||||
}
|
||||
|
||||
TEST(BSPTree, TwoPlaneIntersectRotate80degrees) {
|
||||
const std::deque<Polygon3D> polygons {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> polygons {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, -1.19530f, -2.56340f),
|
||||
Point3D(-0.00000f, 2.56350f, -1.19530f),
|
||||
Point3D(0.00010f, 1.19540f, 2.56350f),
|
||||
Point3D(0.00010f, -2.56340f, 1.19540f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, 0.68410f, -1.87930f),
|
||||
Point3D(2.00000f, -0.68400f, 1.87940f),
|
||||
Point3D(-2.00000f, -0.68400f, 1.87940f),
|
||||
@ -461,14 +461,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate80degrees) {
|
||||
}
|
||||
};
|
||||
|
||||
const std::deque<Polygon3D> expected {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> expected {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, -1.19530f, -2.56340f),
|
||||
Point3D(-0.00000f, 2.56350f, -1.19530f),
|
||||
Point3D(0.00010f, 1.19540f, 2.56350f),
|
||||
Point3D(0.00010f, -2.56340f, 1.19540f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, 0.68410f, -1.87930f),
|
||||
Point3D(2.00000f, -0.68400f, 1.87940f),
|
||||
Point3D(-2.00000f, -0.68400f, 1.87940f),
|
||||
@ -479,14 +479,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate80degrees) {
|
||||
}
|
||||
|
||||
TEST(BSPTree, TwoPlaneIntersectRotate100degrees) {
|
||||
const std::deque<Polygon3D> polygons {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> polygons {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, 2.73210f, -0.73200f),
|
||||
Point3D(-0.00000f, 0.73210f, 2.73210f),
|
||||
Point3D(0.00010f, -2.73200f, 0.73210f),
|
||||
Point3D(0.00010f, -0.73200f, -2.73200f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, 1.73210f, 1.00010f),
|
||||
Point3D(2.00000f, -1.73200f, -1.00000f),
|
||||
Point3D(-2.00000f, -1.73200f, -1.00000f),
|
||||
@ -494,20 +494,20 @@ TEST(BSPTree, TwoPlaneIntersectRotate100degrees) {
|
||||
}
|
||||
};
|
||||
|
||||
const std::deque<Polygon3D> expected {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> expected {
|
||||
Polygon {
|
||||
Point3D(2.00000f, -1.26783f, -0.73200f),
|
||||
Point3D(2.00000f, -1.73200f, -1.00000f),
|
||||
Point3D(-2.00000f, -1.73200f, -1.00000f),
|
||||
Point3D(-2.00000f, -1.26783f, -0.73200f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, 2.73210f, -0.73200f),
|
||||
Point3D(-0.00000f, 0.73210f, 2.73210f),
|
||||
Point3D(0.00010f, -2.73200f, 0.73210f),
|
||||
Point3D(0.00010f, -0.73200f, -2.73200f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(-2.00000f, -1.26783f, -0.73200f),
|
||||
Point3D(-2.00000f, 1.73210f, 1.00010f),
|
||||
Point3D(2.00000f, 1.73210f, 1.00010f),
|
||||
@ -518,14 +518,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate100degrees) {
|
||||
}
|
||||
|
||||
TEST(BSPTree, TwoPlaneIntersectRotate120degrees) {
|
||||
const std::deque<Polygon3D> polygons {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> polygons {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, -0.73200f, 2.73210f),
|
||||
Point3D(-0.00000f, -2.73200f, -0.73200f),
|
||||
Point3D(0.00010f, 0.73210f, -2.73200f),
|
||||
Point3D(0.00010f, 2.73210f, 0.73210f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, -1.73200f, 1.00000f),
|
||||
Point3D(2.00000f, 1.73210f, -0.99990f),
|
||||
Point3D(-2.00000f, 1.73210f, -0.99990f),
|
||||
@ -533,14 +533,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate120degrees) {
|
||||
}
|
||||
};
|
||||
|
||||
const std::deque<Polygon3D> expected {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> expected {
|
||||
Polygon {
|
||||
Point3D(2.00000f, -1.73200f, 1.00000f),
|
||||
Point3D(2.00000f, 1.73210f, -0.99990f),
|
||||
Point3D(-2.00000f, 1.73210f, -0.99990f),
|
||||
Point3D(-2.00000f, -1.73200f, 1.00000f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, -0.73200f, 2.73210f),
|
||||
Point3D(-0.00000f, -2.73200f, -0.73200f),
|
||||
Point3D(0.00010f, 0.73210f, -2.73200f),
|
||||
@ -551,14 +551,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate120degrees) {
|
||||
}
|
||||
|
||||
TEST(BSPTree, TwoPlaneIntersectRotate140degrees) {
|
||||
const std::deque<Polygon3D> polygons {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> polygons {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, -1.19530f, -2.56340f),
|
||||
Point3D(-0.00000f, 2.56350f, -1.19530f),
|
||||
Point3D(0.00010f, 1.19540f, 2.56350f),
|
||||
Point3D(0.00010f, -2.56340f, 1.19540f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, 0.68410f, -1.87930f),
|
||||
Point3D(2.00000f, -0.68400f, 1.87940f),
|
||||
Point3D(-2.00000f, -0.68400f, 1.87940f),
|
||||
@ -566,14 +566,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate140degrees) {
|
||||
}
|
||||
};
|
||||
|
||||
const std::deque<Polygon3D> expected {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> expected {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, -1.19530f, -2.56340f),
|
||||
Point3D(-0.00000f, 2.56350f, -1.19530f),
|
||||
Point3D(0.00010f, 1.19540f, 2.56350f),
|
||||
Point3D(0.00010f, -2.56340f, 1.19540f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, 0.68410f, -1.87930f),
|
||||
Point3D(2.00000f, -0.68400f, 1.87940f),
|
||||
Point3D(-2.00000f, -0.68400f, 1.87940f),
|
||||
@ -584,14 +584,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate140degrees) {
|
||||
}
|
||||
|
||||
TEST(BSPTree, TwoPlaneIntersectRotate160degrees) {
|
||||
const std::deque<Polygon3D> polygons {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> polygons {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, 2.00000f, 2.00000f),
|
||||
Point3D(-0.00000f, -2.00000f, 2.00000f),
|
||||
Point3D(0.00010f, -2.00000f, -2.00000f),
|
||||
Point3D(0.00010f, 2.00000f, -2.00000f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, -0.00000f, 2.00000f),
|
||||
Point3D(2.00000f, 0.00010f, -2.00000f),
|
||||
Point3D(-2.00000f, 0.00010f, -2.00000f),
|
||||
@ -599,14 +599,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate160degrees) {
|
||||
}
|
||||
};
|
||||
|
||||
const std::deque<Polygon3D> expected {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> expected {
|
||||
Polygon {
|
||||
Point3D(2.00000f, -0.00000f, 2.00000f),
|
||||
Point3D(2.00000f, 0.00010f, -2.00000f),
|
||||
Point3D(-2.00000f, 0.00010f, -2.00000f),
|
||||
Point3D(-2.00000f, -0.00000f, 2.00000f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, 2.00000f, 2.00000f),
|
||||
Point3D(-0.00000f, -2.00000f, 2.00000f),
|
||||
Point3D(0.00010f, -2.00000f, -2.00000f),
|
||||
@ -617,14 +617,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate160degrees) {
|
||||
}
|
||||
|
||||
TEST(BSPTree, TwoPlaneIntersectRotate180degrees) {
|
||||
const std::deque<Polygon3D> polygons {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> polygons {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, -2.00000f, -2.00000f),
|
||||
Point3D(-0.00000f, 2.00000f, -2.00000f),
|
||||
Point3D(0.00010f, 2.00000f, 2.00000f),
|
||||
Point3D(0.00010f, -2.00000f, 2.00000f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, 0.00010f, -2.00000f),
|
||||
Point3D(2.00000f, -0.00000f, 2.00000f),
|
||||
Point3D(-2.00000f, -0.00000f, 2.00000f),
|
||||
@ -632,14 +632,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate180degrees) {
|
||||
}
|
||||
};
|
||||
|
||||
const std::deque<Polygon3D> expected {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> expected {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, -2.00000f, -2.00000f),
|
||||
Point3D(-0.00000f, 2.00000f, -2.00000f),
|
||||
Point3D(0.00010f, 2.00000f, 2.00000f),
|
||||
Point3D(0.00010f, -2.00000f, 2.00000f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, 0.00010f, -2.00000f),
|
||||
Point3D(2.00000f, -0.00000f, 2.00000f),
|
||||
Point3D(-2.00000f, -0.00000f, 2.00000f),
|
||||
@ -650,14 +650,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate180degrees) {
|
||||
}
|
||||
|
||||
TEST(BSPTree, TwoPlaneIntersectRotate200degrees) {
|
||||
const std::deque<Polygon3D> polygons {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> polygons {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, 1.19540f, 2.56350f),
|
||||
Point3D(-0.00000f, -2.56340f, 1.19540f),
|
||||
Point3D(0.00010f, -1.19530f, -2.56340f),
|
||||
Point3D(0.00010f, 2.56350f, -1.19530f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, -0.68400f, 1.87940f),
|
||||
Point3D(2.00000f, 0.68410f, -1.87930f),
|
||||
Point3D(-2.00000f, 0.68410f, -1.87930f),
|
||||
@ -665,14 +665,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate200degrees) {
|
||||
}
|
||||
};
|
||||
|
||||
const std::deque<Polygon3D> expected {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> expected {
|
||||
Polygon {
|
||||
Point3D(2.00000f, -0.68400f, 1.87940f),
|
||||
Point3D(2.00000f, 0.68410f, -1.87930f),
|
||||
Point3D(-2.00000f, 0.68410f, -1.87930f),
|
||||
Point3D(-2.00000f, -0.68400f, 1.87940f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, 1.19540f, 2.56350f),
|
||||
Point3D(-0.00000f, -2.56340f, 1.19540f),
|
||||
Point3D(0.00010f, -1.19530f, -2.56340f),
|
||||
@ -683,14 +683,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate200degrees) {
|
||||
}
|
||||
|
||||
TEST(BSPTree, TwoPlaneIntersectRotate220degrees) {
|
||||
const std::deque<Polygon3D> polygons {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> polygons {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, 0.73210f, -2.73200f),
|
||||
Point3D(-0.00000f, 2.73210f, 0.73210f),
|
||||
Point3D(0.00010f, -0.73200f, 2.73210f),
|
||||
Point3D(0.00010f, -2.73200f, -0.73200f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, 1.73210f, -0.99990f),
|
||||
Point3D(2.00000f, -1.73200f, 1.00000f),
|
||||
Point3D(-2.00000f, -1.73200f, 1.00000f),
|
||||
@ -698,14 +698,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate220degrees) {
|
||||
}
|
||||
};
|
||||
|
||||
const std::deque<Polygon3D> expected {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> expected {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, 0.73210f, -2.73200f),
|
||||
Point3D(-0.00000f, 2.73210f, 0.73210f),
|
||||
Point3D(0.00010f, -0.73200f, 2.73210f),
|
||||
Point3D(0.00010f, -2.73200f, -0.73200f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, 1.73210f, -0.99990f),
|
||||
Point3D(2.00000f, -1.73200f, 1.00000f),
|
||||
Point3D(-2.00000f, -1.73200f, 1.00000f),
|
||||
@ -716,14 +716,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate220degrees) {
|
||||
}
|
||||
|
||||
TEST(BSPTree, TwoPlaneIntersectRotate240degrees) {
|
||||
const std::deque<Polygon3D> polygons {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> polygons {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, -2.73200f, 0.73210f),
|
||||
Point3D(-0.00000f, -0.73200f, -2.73200f),
|
||||
Point3D(0.00010f, 2.73210f, -0.73200f),
|
||||
Point3D(0.00010f, 0.73210f, 2.73210f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, -1.73200f, -1.00000f),
|
||||
Point3D(2.00000f, 1.73210f, 1.00010f),
|
||||
Point3D(-2.00000f, 1.73210f, 1.00010f),
|
||||
@ -731,20 +731,20 @@ TEST(BSPTree, TwoPlaneIntersectRotate240degrees) {
|
||||
}
|
||||
};
|
||||
|
||||
const std::deque<Polygon3D> expected {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> expected {
|
||||
Polygon {
|
||||
Point3D(-2.00000f, 1.26793f, 0.73210f),
|
||||
Point3D(-2.00000f, -1.73200f, -1.00000f),
|
||||
Point3D(2.00000f, -1.73200f, -1.00000f),
|
||||
Point3D(2.00000f, 1.26793f, 0.73210f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, -2.73200f, 0.73210f),
|
||||
Point3D(-0.00000f, -0.73200f, -2.73200f),
|
||||
Point3D(0.00010f, 2.73210f, -0.73200f),
|
||||
Point3D(0.00010f, 0.73210f, 2.73210f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, 1.26793f, 0.73210f),
|
||||
Point3D(2.00000f, 1.73210f, 1.00010f),
|
||||
Point3D(-2.00000f, 1.73210f, 1.00010f),
|
||||
@ -755,14 +755,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate240degrees) {
|
||||
}
|
||||
|
||||
TEST(BSPTree, TwoPlaneIntersectRotate260degrees) {
|
||||
const std::deque<Polygon3D> polygons {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> polygons {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, 1.19540f, 2.56350f),
|
||||
Point3D(-0.00000f, -2.56340f, 1.19540f),
|
||||
Point3D(0.00010f, -1.19530f, -2.56340f),
|
||||
Point3D(0.00010f, 2.56350f, -1.19530f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, -0.68400f, 1.87940f),
|
||||
Point3D(2.00000f, 0.68410f, -1.87930f),
|
||||
Point3D(-2.00000f, 0.68410f, -1.87930f),
|
||||
@ -770,14 +770,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate260degrees) {
|
||||
}
|
||||
};
|
||||
|
||||
const std::deque<Polygon3D> expected {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> expected {
|
||||
Polygon {
|
||||
Point3D(2.00000f, -0.68400f, 1.87940f),
|
||||
Point3D(2.00000f, 0.68410f, -1.87930f),
|
||||
Point3D(-2.00000f, 0.68410f, -1.87930f),
|
||||
Point3D(-2.00000f, -0.68400f, 1.87940f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, 1.19540f, 2.56350f),
|
||||
Point3D(-0.00000f, -2.56340f, 1.19540f),
|
||||
Point3D(0.00010f, -1.19530f, -2.56340f),
|
||||
@ -788,14 +788,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate260degrees) {
|
||||
}
|
||||
|
||||
TEST(BSPTree, TwoPlaneIntersectRotate280degrees) {
|
||||
const std::deque<Polygon3D> polygons {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> polygons {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, 2.73210f, -0.73200f),
|
||||
Point3D(-0.00000f, 0.73210f, 2.73210f),
|
||||
Point3D(0.00010f, -2.73200f, 0.73210f),
|
||||
Point3D(0.00010f, -0.73200f, -2.73200f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, 1.73210f, 1.00010f),
|
||||
Point3D(2.00000f, -1.73200f, -1.00000f),
|
||||
Point3D(-2.00000f, -1.73200f, -1.00000f),
|
||||
@ -803,20 +803,20 @@ TEST(BSPTree, TwoPlaneIntersectRotate280degrees) {
|
||||
}
|
||||
};
|
||||
|
||||
const std::deque<Polygon3D> expected {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> expected {
|
||||
Polygon {
|
||||
Point3D(2.00000f, -1.26783f, -0.73200f),
|
||||
Point3D(2.00000f, -1.73200f, -1.00000f),
|
||||
Point3D(-2.00000f, -1.73200f, -1.00000f),
|
||||
Point3D(-2.00000f, -1.26783f, -0.73200f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, 2.73210f, -0.73200f),
|
||||
Point3D(-0.00000f, 0.73210f, 2.73210f),
|
||||
Point3D(0.00010f, -2.73200f, 0.73210f),
|
||||
Point3D(0.00010f, -0.73200f, -2.73200f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(-2.00000f, -1.26783f, -0.73200f),
|
||||
Point3D(-2.00000f, 1.73210f, 1.00010f),
|
||||
Point3D(2.00000f, 1.73210f, 1.00010f),
|
||||
@ -827,14 +827,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate280degrees) {
|
||||
}
|
||||
|
||||
TEST(BSPTree, TwoPlaneIntersectRotate300degrees) {
|
||||
const std::deque<Polygon3D> polygons {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> polygons {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, 0.73210f, -2.73200f),
|
||||
Point3D(-0.00000f, 2.73210f, 0.73210f),
|
||||
Point3D(0.00010f, -0.73200f, 2.73210f),
|
||||
Point3D(0.00010f, -2.73200f, -0.73200f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, 1.73210f, -0.99990f),
|
||||
Point3D(2.00000f, -1.73200f, 1.00000f),
|
||||
Point3D(-2.00000f, -1.73200f, 1.00000f),
|
||||
@ -842,14 +842,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate300degrees) {
|
||||
}
|
||||
};
|
||||
|
||||
const std::deque<Polygon3D> expected {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> expected {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, 0.73210f, -2.73200f),
|
||||
Point3D(-0.00000f, 2.73210f, 0.73210f),
|
||||
Point3D(0.00010f, -0.73200f, 2.73210f),
|
||||
Point3D(0.00010f, -2.73200f, -0.73200f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, 1.73210f, -0.99990f),
|
||||
Point3D(2.00000f, -1.73200f, 1.00000f),
|
||||
Point3D(-2.00000f, -1.73200f, 1.00000f),
|
||||
@ -860,14 +860,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate300degrees) {
|
||||
}
|
||||
|
||||
TEST(BSPTree, TwoPlaneIntersectRotate320degrees) {
|
||||
const std::deque<Polygon3D> polygons {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> polygons {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, -1.19530f, -2.56340f),
|
||||
Point3D(-0.00000f, 2.56350f, -1.19530f),
|
||||
Point3D(0.00010f, 1.19540f, 2.56350f),
|
||||
Point3D(0.00010f, -2.56340f, 1.19540f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, 0.68410f, -1.87930f),
|
||||
Point3D(2.00000f, -0.68400f, 1.87940f),
|
||||
Point3D(-2.00000f, -0.68400f, 1.87940f),
|
||||
@ -875,14 +875,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate320degrees) {
|
||||
}
|
||||
};
|
||||
|
||||
const std::deque<Polygon3D> expected {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> expected {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, -1.19530f, -2.56340f),
|
||||
Point3D(-0.00000f, 2.56350f, -1.19530f),
|
||||
Point3D(0.00010f, 1.19540f, 2.56350f),
|
||||
Point3D(0.00010f, -2.56340f, 1.19540f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, 0.68410f, -1.87930f),
|
||||
Point3D(2.00000f, -0.68400f, 1.87940f),
|
||||
Point3D(-2.00000f, -0.68400f, 1.87940f),
|
||||
@ -893,14 +893,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate320degrees) {
|
||||
}
|
||||
|
||||
TEST(BSPTree, TwoPlaneIntersectRotate340degrees) {
|
||||
const std::deque<Polygon3D> polygons {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> polygons {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, -2.00000f, -2.00000f),
|
||||
Point3D(-0.00000f, 2.00000f, -2.00000f),
|
||||
Point3D(0.00010f, 2.00000f, 2.00000f),
|
||||
Point3D(0.00010f, -2.00000f, 2.00000f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, 0.00010f, -2.00000f),
|
||||
Point3D(2.00000f, -0.00000f, 2.00000f),
|
||||
Point3D(-2.00000f, -0.00000f, 2.00000f),
|
||||
@ -908,14 +908,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate340degrees) {
|
||||
}
|
||||
};
|
||||
|
||||
const std::deque<Polygon3D> expected {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> expected {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, -2.00000f, -2.00000f),
|
||||
Point3D(-0.00000f, 2.00000f, -2.00000f),
|
||||
Point3D(0.00010f, 2.00000f, 2.00000f),
|
||||
Point3D(0.00010f, -2.00000f, 2.00000f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, 0.00010f, -2.00000f),
|
||||
Point3D(2.00000f, -0.00000f, 2.00000f),
|
||||
Point3D(-2.00000f, -0.00000f, 2.00000f),
|
||||
@ -926,14 +926,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate340degrees) {
|
||||
}
|
||||
|
||||
TEST(BSPTree, TwoPlaneIntersectRotate360degrees) {
|
||||
const std::deque<Polygon3D> polygons {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> polygons {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, -2.00000f, -2.00000f),
|
||||
Point3D(-0.00000f, 2.00000f, -2.00000f),
|
||||
Point3D(0.00010f, 2.00000f, 2.00000f),
|
||||
Point3D(0.00010f, -2.00000f, 2.00000f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, 0.00010f, -2.00000f),
|
||||
Point3D(2.00000f, -0.00000f, 2.00000f),
|
||||
Point3D(-2.00000f, -0.00000f, 2.00000f),
|
||||
@ -941,14 +941,14 @@ TEST(BSPTree, TwoPlaneIntersectRotate360degrees) {
|
||||
}
|
||||
};
|
||||
|
||||
const std::deque<Polygon3D> expected {
|
||||
Polygon3D {
|
||||
const std::deque<Polygon> expected {
|
||||
Polygon {
|
||||
Point3D(-0.00000f, -2.00000f, -2.00000f),
|
||||
Point3D(-0.00000f, 2.00000f, -2.00000f),
|
||||
Point3D(0.00010f, 2.00000f, 2.00000f),
|
||||
Point3D(0.00010f, -2.00000f, 2.00000f)
|
||||
},
|
||||
Polygon3D {
|
||||
Polygon {
|
||||
Point3D(2.00000f, 0.00010f, -2.00000f),
|
||||
Point3D(2.00000f, -0.00000f, 2.00000f),
|
||||
Point3D(-2.00000f, -0.00000f, 2.00000f),
|
||||
|
@ -14,9 +14,9 @@
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
TEST(Polygon3D, TriangulateRectangle)
|
||||
TEST(Polygon, TriangulateRectangle)
|
||||
{
|
||||
const Polygon3D p {
|
||||
const Polygon p {
|
||||
Point3D(0.0f, 0.0f, 1.0f),
|
||||
Point3D(0.0f, 1.0f, 1.0f),
|
||||
Point3D(1.0f, 1.0f, 1.0f),
|
||||
@ -32,9 +32,9 @@ TEST(Polygon3D, TriangulateRectangle)
|
||||
AssertArrayEQ(triangles, expected);
|
||||
}
|
||||
|
||||
TEST(Polygon3D, TriangulatePentagon)
|
||||
TEST(Polygon, TriangulatePentagon)
|
||||
{
|
||||
const Polygon3D p {
|
||||
const Polygon p {
|
||||
Point3D(0.0f, 0.0f, 1.0f),
|
||||
Point3D(0.0f, 1.0f, 1.0f),
|
||||
Point3D(0.5f, 1.5f, 1.0f),
|
||||
@ -52,92 +52,84 @@ TEST(Polygon3D, TriangulatePentagon)
|
||||
AssertArrayEQ(triangles, expected);
|
||||
}
|
||||
|
||||
TEST(Polygon3D, ClipRectangle)
|
||||
void
|
||||
TestClipRect(const Polygon& aPolygon,
|
||||
const Polygon& aExpected,
|
||||
const Rect& aRect)
|
||||
{
|
||||
Polygon3D clipped, expected;
|
||||
const Polygon res = aPolygon.ClipPolygon(Polygon::FromRect(aRect));
|
||||
EXPECT_TRUE(res == aExpected);
|
||||
}
|
||||
|
||||
Polygon3D polygon {
|
||||
TEST(Polygon, ClipRectangle)
|
||||
{
|
||||
Polygon polygon {
|
||||
Point3D(0.0f, 0.0f, 0.0f),
|
||||
Point3D(0.0f, 1.0f, 0.0f),
|
||||
Point3D(1.0f, 1.0f, 0.0f),
|
||||
Point3D(1.0f, 0.0f, 0.0f)
|
||||
};
|
||||
TestClipRect(polygon, polygon, Rect(0.0f, 0.0f, 1.0f, 1.0f));
|
||||
|
||||
clipped = polygon.ClipPolygon(Rect(0.0f, 0.0f, 1.0f, 1.0f));
|
||||
EXPECT_TRUE(clipped == polygon);
|
||||
|
||||
|
||||
clipped = polygon.ClipPolygon(Rect(0.0f, 0.0f, 0.8f, 0.8f));
|
||||
expected = Polygon3D {
|
||||
Polygon expected = Polygon {
|
||||
Point3D(0.0f, 0.0f, 0.0f),
|
||||
Point3D(0.0f, 0.8f, 0.0f),
|
||||
Point3D(0.8f, 0.8f, 0.0f),
|
||||
Point3D(0.8f, 0.0f, 0.0f)
|
||||
};
|
||||
EXPECT_TRUE(clipped == expected);
|
||||
TestClipRect(polygon, expected, Rect(0.0f, 0.0f, 0.8f, 0.8f));
|
||||
|
||||
|
||||
clipped = polygon.ClipPolygon(Rect(0.2f, 0.2f, 0.8f, 0.8f));
|
||||
expected = Polygon3D {
|
||||
expected = Polygon {
|
||||
Point3D(0.2f, 0.2f, 0.0f),
|
||||
Point3D(0.2f, 1.0f, 0.0f),
|
||||
Point3D(1.0f, 1.0f, 0.0f),
|
||||
Point3D(1.0f, 0.2f, 0.0f)
|
||||
};
|
||||
EXPECT_TRUE(clipped == expected);
|
||||
TestClipRect(polygon, expected, Rect(0.2f, 0.2f, 0.8f, 0.8f));
|
||||
|
||||
|
||||
clipped = polygon.ClipPolygon(Rect(0.2f, 0.2f, 0.6f, 0.6f));
|
||||
expected = Polygon3D {
|
||||
expected = Polygon {
|
||||
Point3D(0.2f, 0.2f, 0.0f),
|
||||
Point3D(0.2f, 0.8f, 0.0f),
|
||||
Point3D(0.8f, 0.8f, 0.0f),
|
||||
Point3D(0.8f, 0.2f, 0.0f)
|
||||
};
|
||||
EXPECT_TRUE(clipped == expected);
|
||||
TestClipRect(polygon, expected, Rect(0.2f, 0.2f, 0.6f, 0.6f));
|
||||
}
|
||||
|
||||
TEST(Polygon3D, ClipTriangle)
|
||||
TEST(Polygon, ClipTriangle)
|
||||
{
|
||||
Polygon3D clipped, expected;
|
||||
const Polygon3D polygon {
|
||||
Polygon clipped, expected;
|
||||
const Polygon polygon {
|
||||
Point3D(0.0f, 0.0f, 0.0f),
|
||||
Point3D(0.0f, 1.0f, 0.0f),
|
||||
Point3D(1.0f, 1.0f, 0.0f)
|
||||
};
|
||||
|
||||
clipped = polygon.ClipPolygon(Rect(0.0f, 0.0f, 1.0f, 1.0f));
|
||||
expected = Polygon3D {
|
||||
expected = Polygon {
|
||||
Point3D(0.0f, 0.0f, 0.0f),
|
||||
Point3D(0.0f, 1.0f, 0.0f),
|
||||
Point3D(1.0f, 1.0f, 0.0f)
|
||||
};
|
||||
EXPECT_TRUE(clipped == expected);
|
||||
TestClipRect(polygon, expected, Rect(0.0f, 0.0f, 1.0f, 1.0f));
|
||||
|
||||
|
||||
clipped = polygon.ClipPolygon(Rect(0.0f, 0.0f, 0.8f, 0.8f));
|
||||
expected = Polygon3D {
|
||||
expected = Polygon {
|
||||
Point3D(0.0f, 0.0f, 0.0f),
|
||||
Point3D(0.0f, 0.8f, 0.0f),
|
||||
Point3D(0.8f, 0.8f, 0.0f)
|
||||
};
|
||||
EXPECT_TRUE(clipped == expected);
|
||||
TestClipRect(polygon, expected, Rect(0.0f, 0.0f, 0.8f, 0.8f));
|
||||
|
||||
|
||||
clipped = polygon.ClipPolygon(Rect(0.2f, 0.2f, 0.8f, 0.8f));
|
||||
expected = Polygon3D {
|
||||
expected = Polygon {
|
||||
Point3D(0.2f, 0.2f, 0.0f),
|
||||
Point3D(0.2f, 1.0f, 0.0f),
|
||||
Point3D(1.0f, 1.0f, 0.0f)
|
||||
};
|
||||
EXPECT_TRUE(clipped == expected);
|
||||
TestClipRect(polygon, expected, Rect(0.2f, 0.2f, 0.8f, 0.8f));
|
||||
|
||||
|
||||
clipped = polygon.ClipPolygon(Rect(0.2f, 0.2f, 0.6f, 0.6f));
|
||||
expected = Polygon3D {
|
||||
expected = Polygon {
|
||||
Point3D(0.2f, 0.2f, 0.0f),
|
||||
Point3D(0.2f, 0.8f, 0.0f),
|
||||
Point3D(0.8f, 0.8f, 0.0f)
|
||||
};
|
||||
EXPECT_TRUE(clipped == expected);
|
||||
}
|
||||
TestClipRect(polygon, expected, Rect(0.2f, 0.2f, 0.6f, 0.6f));
|
||||
}
|
@ -527,6 +527,7 @@ private:
|
||||
DECL_GFX_PREF(Once, "layers.uniformity-info", UniformityInfo, bool, false);
|
||||
DECL_GFX_PREF(Once, "layers.use-image-offscreen-surfaces", UseImageOffscreenSurfaces, bool, true);
|
||||
DECL_GFX_PREF(Live, "layers.draw-mask-debug", DrawMaskLayer, bool, false);
|
||||
DECL_GFX_PREF(Live, "layers.geometry.opengl.enabled", OGLLayerGeometry, bool, false);
|
||||
|
||||
DECL_GFX_PREF(Live, "layout.css.scroll-behavior.damping-ratio", ScrollBehaviorDampingRatio, float, 1.0f);
|
||||
DECL_GFX_PREF(Live, "layout.css.scroll-behavior.enabled", ScrollBehaviorEnabled, bool, true);
|
||||
|
@ -81,11 +81,10 @@ VRManager::VRManager()
|
||||
mgr = VRDisplayManagerOpenVR::Create();
|
||||
if (mgr) {
|
||||
mManagers.AppendElement(mgr);
|
||||
}
|
||||
|
||||
controllerMgr = VRControllerManagerOpenVR::Create();
|
||||
if (controllerMgr) {
|
||||
mControllerManagers.AppendElement(controllerMgr);
|
||||
controllerMgr = VRControllerManagerOpenVR::Create();
|
||||
if (controllerMgr) {
|
||||
mControllerManagers.AppendElement(controllerMgr);
|
||||
}
|
||||
}
|
||||
|
||||
// OSVR is cross platform compatible
|
||||
|
@ -68,8 +68,6 @@ class Visitor:
|
||||
managed.accept(self)
|
||||
for msgDecl in p.messageDecls:
|
||||
msgDecl.accept(self)
|
||||
for transitionStmt in p.transitionStmts:
|
||||
transitionStmt.accept(self)
|
||||
|
||||
def visitNamespace(self, ns):
|
||||
pass
|
||||
@ -95,18 +93,6 @@ class Visitor:
|
||||
for outParam in md.outParams:
|
||||
outParam.accept(self)
|
||||
|
||||
def visitTransitionStmt(self, ts):
|
||||
ts.state.accept(self)
|
||||
for trans in ts.transitions:
|
||||
trans.accept(self)
|
||||
|
||||
def visitTransition(self, t):
|
||||
for toState in t.toStates:
|
||||
toState.accept(self)
|
||||
|
||||
def visitState(self, s):
|
||||
pass
|
||||
|
||||
def visitParam(self, decl):
|
||||
pass
|
||||
|
||||
@ -246,8 +232,6 @@ class Protocol(NamespacedNode):
|
||||
self.managers = [ ]
|
||||
self.managesStmts = [ ]
|
||||
self.messageDecls = [ ]
|
||||
self.transitionStmts = [ ]
|
||||
self.startStates = [ ]
|
||||
|
||||
class StructField(Node):
|
||||
def __init__(self, loc, type, name):
|
||||
@ -322,83 +306,6 @@ class MessageDecl(Node):
|
||||
elif modifier != '':
|
||||
raise Exception, "Unexpected message modifier `%s'"% modifier
|
||||
|
||||
class Transition(Node):
|
||||
def __init__(self, loc, trigger, msg, toStates):
|
||||
Node.__init__(self, loc)
|
||||
self.trigger = trigger
|
||||
self.msg = msg
|
||||
self.toStates = toStates
|
||||
|
||||
def __cmp__(self, o):
|
||||
c = cmp(self.msg, o.msg)
|
||||
if c: return c
|
||||
c = cmp(self.trigger, o.trigger)
|
||||
if c: return c
|
||||
|
||||
def __hash__(self): return hash(str(self))
|
||||
def __str__(self): return '%s %s'% (self.trigger, self.msg)
|
||||
|
||||
@staticmethod
|
||||
def nameToTrigger(name):
|
||||
return { 'send': SEND, 'recv': RECV, 'call': CALL, 'answer': ANSWER }[name]
|
||||
|
||||
Transition.NULL = Transition(Loc.NONE, None, None, [ ])
|
||||
|
||||
class TransitionStmt(Node):
|
||||
def __init__(self, loc, state, transitions):
|
||||
Node.__init__(self, loc)
|
||||
self.state = state
|
||||
self.transitions = transitions
|
||||
|
||||
@staticmethod
|
||||
def makeNullStmt(state):
|
||||
return TransitionStmt(Loc.NONE, state, [ Transition.NULL ])
|
||||
|
||||
class SEND:
|
||||
pretty = 'send'
|
||||
@classmethod
|
||||
def __hash__(cls): return hash(cls.pretty)
|
||||
@classmethod
|
||||
def direction(cls): return OUT
|
||||
class RECV:
|
||||
pretty = 'recv'
|
||||
@classmethod
|
||||
def __hash__(cls): return hash(cls.pretty)
|
||||
@classmethod
|
||||
def direction(cls): return IN
|
||||
class CALL:
|
||||
pretty = 'call'
|
||||
@classmethod
|
||||
def __hash__(cls): return hash(cls.pretty)
|
||||
@classmethod
|
||||
def direction(cls): return OUT
|
||||
class ANSWER:
|
||||
pretty = 'answer'
|
||||
@classmethod
|
||||
def __hash__(cls): return hash(cls.pretty)
|
||||
@classmethod
|
||||
def direction(cls): return IN
|
||||
|
||||
class State(Node):
|
||||
def __init__(self, loc, name, start=False):
|
||||
Node.__init__(self, loc)
|
||||
self.name = name
|
||||
self.start = start
|
||||
def __eq__(self, o):
|
||||
return (isinstance(o, State)
|
||||
and o.name == self.name
|
||||
and o.start == self.start)
|
||||
def __hash__(self):
|
||||
return hash(repr(self))
|
||||
def __ne__(self, o):
|
||||
return not (self == o)
|
||||
def __repr__(self): return '<State %r start=%r>'% (self.name, self.start)
|
||||
def __str__(self): return '<State %s start=%s>'% (self.name, self.start)
|
||||
|
||||
State.ANY = State(Loc.NONE, '[any]', start=True)
|
||||
State.DEAD = State(Loc.NONE, '[dead]', start=False)
|
||||
State.DYING = State(Loc.NONE, '[dying]', start=False)
|
||||
|
||||
class Param(Node):
|
||||
def __init__(self, loc, typespec, name):
|
||||
Node.__init__(self, loc)
|
||||
|
@ -51,7 +51,7 @@ Also known as pretty-printing.'''
|
||||
self.println('/* Included file:')
|
||||
IPDLCodeGen(outf=self.outf, indentCols=self.indentCols,
|
||||
printed=self.printed).visitTranslationUnit(inc.tu)
|
||||
|
||||
|
||||
self.println('*/')
|
||||
|
||||
def visitProtocol(self, p):
|
||||
@ -68,8 +68,6 @@ Also known as pretty-printing.'''
|
||||
for msgDecl in p.messageDecls: msgDecl.accept(self)
|
||||
self.println()
|
||||
|
||||
for transStmt in p.transitionStmts: transStmt.accept(self)
|
||||
|
||||
self.dedent()
|
||||
self.println('}')
|
||||
self.write('}\n'* len(p.namespaces))
|
||||
|
@ -1340,10 +1340,6 @@ with some new IPDL/C++ nodes that are tuned for C++ codegen."""
|
||||
md.returns = [ ret.accept(self) for ret in md.outParams ]
|
||||
MessageDecl.upgrade(md)
|
||||
|
||||
def visitTransitionStmt(self, ts):
|
||||
name = ts.state.decl.progname
|
||||
ts.state.decl.cxxname = name
|
||||
ts.state.decl.cxxenum = ExprVar(self.protocolName +'::'+ name)
|
||||
|
||||
##-----------------------------------------------------------------------------
|
||||
|
||||
@ -1550,13 +1546,7 @@ class _GenerateProtocolCode(ipdl.ast.Visitor):
|
||||
stateenum.addId(_errorState().name)
|
||||
if self.protocol.decl.type.hasReentrantDelete:
|
||||
stateenum.addId(_dyingState().name)
|
||||
for ts in p.transitionStmts:
|
||||
stateenum.addId(ts.state.decl.cxxname)
|
||||
if len(p.transitionStmts):
|
||||
startstate = p.transitionStmts[0].state.decl.cxxname
|
||||
else:
|
||||
startstate = _nullState().name
|
||||
stateenum.addId(_startState().name, startstate)
|
||||
stateenum.addId(_startState().name, _nullState().name)
|
||||
|
||||
ns.addstmts([ StmtDecl(Decl(stateenum,'')), Whitespace.NL ])
|
||||
|
||||
@ -1681,28 +1671,12 @@ class _GenerateProtocolCode(ipdl.ast.Visitor):
|
||||
usesend, sendvar = set(), ExprVar('Send__')
|
||||
userecv, recvvar = set(), ExprVar('Recv__')
|
||||
|
||||
def sameTrigger(trigger, actionexpr):
|
||||
if trigger is ipdl.ast.SEND or trigger is ipdl.ast.CALL:
|
||||
usesend.add('yes')
|
||||
return ExprBinary(sendvar, '==', actionexpr)
|
||||
else:
|
||||
userecv.add('yes')
|
||||
return ExprBinary(recvvar, '==',
|
||||
actionexpr)
|
||||
|
||||
def stateEnum(s):
|
||||
if s is ipdl.ast.State.DEAD:
|
||||
return _deadState()
|
||||
else:
|
||||
return ExprVar(s.decl.cxxname)
|
||||
|
||||
# bool Transition(Trigger trigger, State* next)
|
||||
# The state we are transitioning from is stored in *next.
|
||||
fromvar = ExprVar('from')
|
||||
triggervar = ExprVar('trigger')
|
||||
nextvar = ExprVar('next')
|
||||
msgexpr = ExprSelect(triggervar, '.', 'mMessage')
|
||||
actionexpr = ExprSelect(triggervar, '.', 'mAction')
|
||||
|
||||
transitionfunc = FunctionDefn(FunctionDecl(
|
||||
'Transition',
|
||||
@ -1712,39 +1686,6 @@ class _GenerateProtocolCode(ipdl.ast.Visitor):
|
||||
|
||||
fromswitch = StmtSwitch(fromvar)
|
||||
|
||||
for ts in self.protocol.transitionStmts:
|
||||
msgswitch = StmtSwitch(msgexpr)
|
||||
|
||||
msgToTransitions = { }
|
||||
|
||||
for t in ts.transitions:
|
||||
msgid = t.msg._md.msgId()
|
||||
|
||||
ifsametrigger = StmtIf(sameTrigger(t.trigger, actionexpr))
|
||||
# FIXME multi-out states
|
||||
for nextstate in t.toStates: break
|
||||
ifsametrigger.addifstmts([
|
||||
StmtExpr(ExprAssn(ExprDeref(nextvar),
|
||||
stateEnum(nextstate))),
|
||||
StmtReturn(ExprLiteral.TRUE)
|
||||
])
|
||||
|
||||
transitions = msgToTransitions.get(msgid, [ ])
|
||||
transitions.append(ifsametrigger)
|
||||
msgToTransitions[msgid] = transitions
|
||||
|
||||
for msgid, transitions in msgToTransitions.iteritems():
|
||||
block = Block()
|
||||
block.addstmts(transitions +[ StmtBreak() ])
|
||||
msgswitch.addcase(CaseLabel(msgid), block)
|
||||
|
||||
msgblock = Block()
|
||||
msgblock.addstmts([
|
||||
msgswitch,
|
||||
StmtBreak()
|
||||
])
|
||||
fromswitch.addcase(CaseLabel(ts.state.decl.cxxname), msgblock)
|
||||
|
||||
# special cases for Null and Error
|
||||
nullerrorblock = Block()
|
||||
if ptype.hasDelete:
|
||||
@ -1800,13 +1741,6 @@ class _GenerateProtocolCode(ipdl.ast.Visitor):
|
||||
transitionfunc.addstmt(StmtDecl(Decl(Type('State'), fromvar.name),
|
||||
init=ExprDeref(nextvar)))
|
||||
transitionfunc.addstmt(fromswitch)
|
||||
# all --> Error transitions break to here. But only insert this
|
||||
# block if there is any possibility of such transitions.
|
||||
if self.protocol.transitionStmts:
|
||||
transitionfunc.addstmts([
|
||||
StmtExpr(ExprAssn(ExprDeref(nextvar), _errorState())),
|
||||
StmtReturn(ExprLiteral.FALSE),
|
||||
])
|
||||
|
||||
return transitionfunc
|
||||
|
||||
|
@ -105,12 +105,10 @@ def locFromTok(p, num):
|
||||
##-----------------------------------------------------------------------------
|
||||
|
||||
reserved = set((
|
||||
'answer',
|
||||
'as',
|
||||
'async',
|
||||
'both',
|
||||
'bridges',
|
||||
'call',
|
||||
'child',
|
||||
'class',
|
||||
'compress',
|
||||
@ -118,7 +116,6 @@ reserved = set((
|
||||
'__delete__',
|
||||
'delete', # reserve 'delete' to prevent its use
|
||||
'from',
|
||||
'goto',
|
||||
'include',
|
||||
'intr',
|
||||
'manager',
|
||||
@ -131,12 +128,8 @@ reserved = set((
|
||||
'parent',
|
||||
'prio',
|
||||
'protocol',
|
||||
'recv',
|
||||
'returns',
|
||||
'send',
|
||||
'spawns',
|
||||
'start',
|
||||
'state',
|
||||
'struct',
|
||||
'sync',
|
||||
'union',
|
||||
@ -460,9 +453,10 @@ def p_ManagesStmt(p):
|
||||
|
||||
def p_MessageDeclsOpt(p):
|
||||
"""MessageDeclsOpt : MessageDeclThing MessageDeclsOpt
|
||||
| TransitionStmtsOpt"""
|
||||
if 2 == len(p):
|
||||
p[0] = p[1]
|
||||
| """
|
||||
if 1 == len(p):
|
||||
# we fill in |loc| in the Protocol rule
|
||||
p[0] = Protocol(None)
|
||||
else:
|
||||
p[2].messageDecls.insert(0, p[1])
|
||||
p[0] = p[2]
|
||||
@ -560,71 +554,6 @@ def p_MessageCompress(p):
|
||||
| COMPRESSALL"""
|
||||
p[0] = p[1]
|
||||
|
||||
##--------------------
|
||||
## State machine
|
||||
|
||||
def p_TransitionStmtsOpt(p):
|
||||
"""TransitionStmtsOpt : TransitionStmt TransitionStmtsOpt
|
||||
|"""
|
||||
if 1 == len(p):
|
||||
# we fill in |loc| in the Protocol rule
|
||||
p[0] = Protocol(None)
|
||||
else:
|
||||
p[2].transitionStmts.insert(0, p[1])
|
||||
p[0] = p[2]
|
||||
|
||||
def p_TransitionStmt(p):
|
||||
"""TransitionStmt : OptionalStart STATE State ':' Transitions"""
|
||||
p[3].start = p[1]
|
||||
p[0] = TransitionStmt(locFromTok(p, 2), p[3], p[5])
|
||||
|
||||
def p_OptionalStart(p):
|
||||
"""OptionalStart : START
|
||||
| """
|
||||
p[0] = (len(p) == 2) # True iff 'start' specified
|
||||
|
||||
def p_Transitions(p):
|
||||
"""Transitions : Transitions Transition
|
||||
| Transition"""
|
||||
if 3 == len(p):
|
||||
p[1].append(p[2])
|
||||
p[0] = p[1]
|
||||
else:
|
||||
p[0] = [ p[1] ]
|
||||
|
||||
def p_Transition(p):
|
||||
"""Transition : Trigger ID GOTO StateList ';'
|
||||
| Trigger __DELETE__ ';'
|
||||
| Trigger DELETE ';'"""
|
||||
if 'delete' == p[2]:
|
||||
_error(locFromTok(p, 1), "`delete' is a reserved identifier")
|
||||
|
||||
loc, trigger = p[1]
|
||||
if 6 == len(p):
|
||||
nextstates = p[4]
|
||||
else:
|
||||
nextstates = [ State.DEAD ]
|
||||
p[0] = Transition(loc, trigger, p[2], nextstates)
|
||||
|
||||
def p_Trigger(p):
|
||||
"""Trigger : SEND
|
||||
| RECV
|
||||
| CALL
|
||||
| ANSWER"""
|
||||
p[0] = [ locFromTok(p, 1), Transition.nameToTrigger(p[1]) ]
|
||||
|
||||
def p_StateList(p):
|
||||
"""StateList : StateList OR State
|
||||
| State"""
|
||||
if 2 == len(p):
|
||||
p[0] = [ p[1] ]
|
||||
else:
|
||||
p[1].append(p[3])
|
||||
p[0] = p[1]
|
||||
|
||||
def p_State(p):
|
||||
"""State : ID"""
|
||||
p[0] = State(locFromTok(p, 1), p[1])
|
||||
|
||||
##--------------------
|
||||
## Minor stuff
|
||||
|
@ -5,10 +5,10 @@
|
||||
|
||||
import os, sys
|
||||
|
||||
from ipdl.ast import CxxInclude, Decl, Loc, QualifiedId, State, StructDecl, TransitionStmt
|
||||
from ipdl.ast import CxxInclude, Decl, Loc, QualifiedId, StructDecl
|
||||
from ipdl.ast import TypeSpec, UnionDecl, UsingStmt, Visitor
|
||||
from ipdl.ast import ASYNC, SYNC, INTR
|
||||
from ipdl.ast import IN, OUT, INOUT, ANSWER, CALL, RECV, SEND
|
||||
from ipdl.ast import IN, OUT, INOUT
|
||||
from ipdl.ast import NOT_NESTED, INSIDE_SYNC_NESTED, INSIDE_CPOW_NESTED
|
||||
import ipdl.builtin as builtin
|
||||
|
||||
@ -20,12 +20,6 @@ def _otherside(side):
|
||||
elif side == 'child': return 'parent'
|
||||
else: assert 0 and 'unknown side "%s"'% (side)
|
||||
|
||||
def unique_pairs(s):
|
||||
n = len(s)
|
||||
for i, e1 in enumerate(s):
|
||||
for j in xrange(i+1, n):
|
||||
yield (e1, s[j])
|
||||
|
||||
def cartesian_product(s1, s2):
|
||||
for e1 in s1:
|
||||
for e2 in s2:
|
||||
@ -49,9 +43,6 @@ class TypeVisitor:
|
||||
def visitImportedCxxType(self, t, *args):
|
||||
pass
|
||||
|
||||
def visitStateType(self, s, *args):
|
||||
pass
|
||||
|
||||
def visitMessageType(self, m, *args):
|
||||
for param in m.params:
|
||||
param.accept(self, *args)
|
||||
@ -67,7 +58,6 @@ class TypeVisitor:
|
||||
|
||||
def visitActorType(self, a, *args):
|
||||
a.protocol.accept(self, *args)
|
||||
a.state.accept(self, *args)
|
||||
|
||||
def visitStructType(self, s, *args):
|
||||
if s in self.visited:
|
||||
@ -195,7 +185,6 @@ class ImportedCxxType(CxxType):
|
||||
class IPDLType(Type):
|
||||
def isIPDL(self): return True
|
||||
def isVisible(self): return True
|
||||
def isState(self): return False
|
||||
def isMessage(self): return False
|
||||
def isProtocol(self): return False
|
||||
def isActor(self): return False
|
||||
@ -238,17 +227,6 @@ class IPDLType(Type):
|
||||
def needsMoreJuiceThan(self, o):
|
||||
return not IPDLType.convertsTo(self, o)
|
||||
|
||||
class StateType(IPDLType):
|
||||
def __init__(self, protocol, name, start=False):
|
||||
self.protocol = protocol
|
||||
self.name = name
|
||||
self.start = start
|
||||
def isState(self): return True
|
||||
def name(self):
|
||||
return self.name
|
||||
def fullname(self):
|
||||
return self.name()
|
||||
|
||||
class MessageType(IPDLType):
|
||||
def __init__(self, nested, prio, sendSemantics, direction,
|
||||
ctor=False, dtor=False, cdtype=None, compress=False,
|
||||
@ -295,7 +273,7 @@ class Bridge:
|
||||
return hash(self.parent) + hash(self.child)
|
||||
|
||||
class ProtocolType(IPDLType):
|
||||
def __init__(self, qname, nestedRange, sendSemantics, stateless=False):
|
||||
def __init__(self, qname, nestedRange, sendSemantics):
|
||||
self.qname = qname
|
||||
self.nestedRange = nestedRange
|
||||
self.sendSemantics = sendSemantics
|
||||
@ -303,7 +281,6 @@ class ProtocolType(IPDLType):
|
||||
self.opens = set() # ProtocolType
|
||||
self.managers = [] # ProtocolType
|
||||
self.manages = [ ]
|
||||
self.stateless = stateless
|
||||
self.hasDelete = False
|
||||
self.hasReentrantDelete = False
|
||||
def isProtocol(self): return True
|
||||
@ -364,9 +341,8 @@ class ProtocolType(IPDLType):
|
||||
for mgr in self.managers: return mgr
|
||||
|
||||
class ActorType(IPDLType):
|
||||
def __init__(self, protocol, state=None, nullable=0):
|
||||
def __init__(self, protocol, nullable=0):
|
||||
self.protocol = protocol
|
||||
self.state = state
|
||||
self.nullable = nullable
|
||||
def isActor(self): return True
|
||||
|
||||
@ -639,10 +615,6 @@ With this information, it finally type checks the AST.'''
|
||||
and runpass(CheckProcessGraph(self.errors))):
|
||||
return False
|
||||
|
||||
if (tu.protocol
|
||||
and len(tu.protocol.startStates)
|
||||
and not runpass(CheckStateMachine(self.errors))):
|
||||
return False
|
||||
return True
|
||||
|
||||
def reportErrors(self, errout):
|
||||
@ -712,8 +684,7 @@ class GatherDecls(TcheckVisitor):
|
||||
fullname = str(qname)
|
||||
p.decl = self.declare(
|
||||
loc=p.loc,
|
||||
type=ProtocolType(qname, p.nestedRange, p.sendSemantics,
|
||||
stateless=(0 == len(p.transitionStmts))),
|
||||
type=ProtocolType(qname, p.nestedRange, p.sendSemantics),
|
||||
shortname=p.name,
|
||||
fullname=None if 0 == len(qname.quals) else fullname)
|
||||
|
||||
@ -926,94 +897,6 @@ class GatherDecls(TcheckVisitor):
|
||||
"constructor declaration required for managed protocol `%s' (managed by protocol `%s')",
|
||||
mgdname, p.name)
|
||||
|
||||
p.states = { }
|
||||
|
||||
if len(p.transitionStmts):
|
||||
p.startStates = [ ts for ts in p.transitionStmts
|
||||
if ts.state.start ]
|
||||
if 0 == len(p.startStates):
|
||||
p.startStates = [ p.transitionStmts[0] ]
|
||||
|
||||
# declare implicit "any", "dead", and "dying" states
|
||||
self.declare(loc=State.ANY.loc,
|
||||
type=StateType(p.decl.type, State.ANY.name, start=False),
|
||||
progname=State.ANY.name)
|
||||
self.declare(loc=State.DEAD.loc,
|
||||
type=StateType(p.decl.type, State.DEAD.name, start=False),
|
||||
progname=State.DEAD.name)
|
||||
if p.decl.type.hasReentrantDelete:
|
||||
self.declare(loc=State.DYING.loc,
|
||||
type=StateType(p.decl.type, State.DYING.name, start=False),
|
||||
progname=State.DYING.name)
|
||||
|
||||
# declare each state before decorating their mention
|
||||
for trans in p.transitionStmts:
|
||||
p.states[trans.state] = trans
|
||||
trans.state.decl = self.declare(
|
||||
loc=trans.state.loc,
|
||||
type=StateType(p.decl.type, trans.state, trans.state.start),
|
||||
progname=trans.state.name)
|
||||
|
||||
for trans in p.transitionStmts:
|
||||
self.seentriggers = set()
|
||||
trans.accept(self)
|
||||
|
||||
if not (p.decl.type.stateless
|
||||
or (p.decl.type.isToplevel()
|
||||
and None is self.symtab.lookup(_DELETE_MSG))):
|
||||
# add a special state |state DEAD: null goto DEAD;|
|
||||
deadtrans = TransitionStmt.makeNullStmt(State.DEAD)
|
||||
p.states[State.DEAD] = deadtrans
|
||||
if p.decl.type.hasReentrantDelete:
|
||||
dyingtrans = TransitionStmt.makeNullStmt(State.DYING)
|
||||
p.states[State.DYING] = dyingtrans
|
||||
|
||||
# visit the message decls once more and resolve the state names
|
||||
# attached to actor params and returns
|
||||
def resolvestate(loc, actortype):
|
||||
assert actortype.isIPDL() and actortype.isActor()
|
||||
|
||||
# already resolved this guy's state
|
||||
if isinstance(actortype.state, Decl):
|
||||
return
|
||||
|
||||
if actortype.state is None:
|
||||
# we thought this was a C++ type until type checking,
|
||||
# when we realized it was an IPDL actor type. But
|
||||
# that means that the actor wasn't specified to be in
|
||||
# any particular state
|
||||
actortype.state = State.ANY
|
||||
|
||||
statename = actortype.state.name
|
||||
# FIXME/cjones: this is just wrong. we need the symbol table
|
||||
# of the protocol this actor refers to. low priority bug
|
||||
# since nobody's using this feature yet
|
||||
statedecl = self.symtab.lookup(statename)
|
||||
if statedecl is None:
|
||||
self.error(
|
||||
loc,
|
||||
"protocol `%s' does not have the state `%s'",
|
||||
actortype.protocol.name(),
|
||||
statename)
|
||||
elif not statedecl.type.isState():
|
||||
self.error(
|
||||
loc,
|
||||
"tag `%s' is supposed to be of state type, but is instead of type `%s'",
|
||||
statename,
|
||||
statedecl.type.typename())
|
||||
else:
|
||||
actortype.state = statedecl.type
|
||||
|
||||
for msg in p.messageDecls:
|
||||
for iparam in msg.inParams:
|
||||
loc = iparam.loc
|
||||
for actortype in iteractortypes(iparam.type):
|
||||
resolvestate(loc, actortype)
|
||||
for oparam in msg.outParams:
|
||||
loc = oparam.loc
|
||||
for actortype in iteractortypes(oparam.type):
|
||||
resolvestate(loc, actortype)
|
||||
|
||||
# FIXME/cjones declare all the little C++ thingies that will
|
||||
# be generated. they're not relevant to IPDL itself, but
|
||||
# those ("invisible") symbols can clash with others in the
|
||||
@ -1163,56 +1046,6 @@ class GatherDecls(TcheckVisitor):
|
||||
md.decl._md = md
|
||||
|
||||
|
||||
def visitTransitionStmt(self, ts):
|
||||
self.seentriggers = set()
|
||||
TcheckVisitor.visitTransitionStmt(self, ts)
|
||||
|
||||
def visitTransition(self, t):
|
||||
loc = t.loc
|
||||
|
||||
# check the trigger message
|
||||
mname = t.msg
|
||||
if t in self.seentriggers:
|
||||
self.error(loc, "trigger `%s' appears multiple times", t.msg)
|
||||
self.seentriggers.add(t)
|
||||
|
||||
mdecl = self.symtab.lookup(mname)
|
||||
if mdecl is not None and mdecl.type.isIPDL() and mdecl.type.isProtocol():
|
||||
mdecl = self.symtab.lookup(mname +'Constructor')
|
||||
|
||||
if mdecl is None:
|
||||
self.error(loc, "message `%s' has not been declared", mname)
|
||||
elif not mdecl.type.isMessage():
|
||||
self.error(
|
||||
loc,
|
||||
"`%s' should have message type, but instead has type `%s'",
|
||||
mname, mdecl.type.typename())
|
||||
else:
|
||||
t.msg = mdecl
|
||||
|
||||
# check the to-states
|
||||
seenstates = set()
|
||||
for toState in t.toStates:
|
||||
sname = toState.name
|
||||
sdecl = self.symtab.lookup(sname)
|
||||
|
||||
if sname in seenstates:
|
||||
self.error(loc, "to-state `%s' appears multiple times", sname)
|
||||
seenstates.add(sname)
|
||||
|
||||
if sdecl is None:
|
||||
self.error(loc, "state `%s' has not been declared", sname)
|
||||
elif not sdecl.type.isState():
|
||||
self.error(
|
||||
loc, "`%s' should have state type, but instead has type `%s'",
|
||||
sname, sdecl.type.typename())
|
||||
else:
|
||||
toState.decl = sdecl
|
||||
toState.start = sdecl.type.start
|
||||
|
||||
t.toStates = set(t.toStates)
|
||||
|
||||
|
||||
def _canonicalType(self, itype, typespec):
|
||||
loc = typespec.loc
|
||||
if itype.isIPDL():
|
||||
@ -1522,27 +1355,6 @@ class CheckTypes(TcheckVisitor):
|
||||
mname[:-len('constructor')], pname)
|
||||
|
||||
|
||||
def visitTransition(self, t):
|
||||
_YNC = [ ASYNC, SYNC ]
|
||||
|
||||
loc = t.loc
|
||||
impliedDirection, impliedSems = {
|
||||
SEND: [ OUT, _YNC ], RECV: [ IN, _YNC ],
|
||||
CALL: [ OUT, INTR ], ANSWER: [ IN, INTR ],
|
||||
} [t.trigger]
|
||||
|
||||
if (OUT is impliedDirection and t.msg.type.isIn()
|
||||
or IN is impliedDirection and t.msg.type.isOut()
|
||||
or _YNC is impliedSems and t.msg.type.isInterrupt()
|
||||
or INTR is impliedSems and (not t.msg.type.isInterrupt())):
|
||||
mtype = t.msg.type
|
||||
|
||||
self.error(
|
||||
loc, "%s %s message `%s' is not `%s'd",
|
||||
mtype.sendSemantics.pretty, mtype.direction.pretty,
|
||||
t.msg.progname,
|
||||
t.trigger.pretty)
|
||||
|
||||
##-----------------------------------------------------------------------------
|
||||
|
||||
class Process:
|
||||
@ -1868,308 +1680,3 @@ class CheckProcessGraph(TcheckVisitor):
|
||||
for opensList in ProcessGraph.opens.itervalues():
|
||||
for opens in opensList:
|
||||
print ' ', opens
|
||||
|
||||
##-----------------------------------------------------------------------------
|
||||
|
||||
class CheckStateMachine(TcheckVisitor):
|
||||
def __init__(self, errors):
|
||||
# don't need the symbol table, we just want the error reporting
|
||||
TcheckVisitor.__init__(self, None, errors)
|
||||
self.p = None
|
||||
|
||||
def visitProtocol(self, p):
|
||||
self.p = p
|
||||
self.checkReachability(p)
|
||||
for ts in p.transitionStmts:
|
||||
ts.accept(self)
|
||||
|
||||
def visitTransitionStmt(self, ts):
|
||||
# We want to disallow "race conditions" in protocols. These
|
||||
# can occur when a protocol state machine has a state that
|
||||
# allows triggers of opposite direction. That declaration
|
||||
# allows the parent to send the child a message at the
|
||||
# exact instance the child sends the parent a message. One of
|
||||
# those messages would (probably) violate the state machine
|
||||
# and cause the child to be terminated. It's obviously very
|
||||
# nice if we can forbid this at the level of IPDL state
|
||||
# machines, rather than resorting to static or dynamic
|
||||
# checking of C++ implementation code.
|
||||
#
|
||||
# An easy way to avoid this problem in IPDL is to only allow
|
||||
# "unidirectional" protocol states; that is, from each state,
|
||||
# only send or only recv triggers are allowed. This approach
|
||||
# is taken by the Singularity project's "contract-based
|
||||
# message channels." However, this can be something of a
|
||||
# notational burden for stateful protocols.
|
||||
#
|
||||
# If two messages race, the effect is that the parent's and
|
||||
# child's states get temporarily out of sync. Informally,
|
||||
# IPDL allows this *only if* the state machines get out of
|
||||
# sync for only *one* step (state machine transition), then
|
||||
# sync back up. This is a design decision: the states could
|
||||
# be allowd to get out of sync for any constant k number of
|
||||
# steps. (If k is unbounded, there's no point in presenting
|
||||
# the abstraction of parent and child actor states being
|
||||
# "entangled".) The working hypothesis is that the more steps
|
||||
# the states are allowed to be out of sync, the harder it is
|
||||
# to reason about the protocol.
|
||||
#
|
||||
# Slightly less informally, two messages are allowed to race
|
||||
# only if processing them in either order leaves the protocol
|
||||
# in the same state. That is, messages A and B are allowed to
|
||||
# race only if processing A then B leaves the protocol in
|
||||
# state S, *and* processing B then A also leaves the protocol
|
||||
# in state S. Technically, if this holds, then messages A and
|
||||
# B could be called "commutative" wrt to actor state.
|
||||
#
|
||||
# "Formally", state machine definitions must adhere to two
|
||||
# rules.
|
||||
#
|
||||
# *Rule 1*: from a state S, all sync triggers must be of the same
|
||||
# "direction," i.e. only |send| or only |recv|
|
||||
#
|
||||
# (Pairs of sync messages can't commute, because otherwise
|
||||
# deadlock can occur from simultaneously in-flight sync
|
||||
# requests.)
|
||||
#
|
||||
# *Rule 2*: the "Diamond Rule".
|
||||
# from a state S,
|
||||
# for any pair of triggers t1 and t2,
|
||||
# where t1 and t2 have opposite direction,
|
||||
# and t1 transitions to state T1 and t2 to T2,
|
||||
# then the following must be true:
|
||||
# (T2 allows the trigger t1, transitioning to state U)
|
||||
# and
|
||||
# (T1 allows the trigger t2, transitioning to state U)
|
||||
# and
|
||||
# (
|
||||
# (
|
||||
# (all of T1's triggers have the same direction as t2)
|
||||
# and
|
||||
# (all of T2's triggers have the same direction as t1)
|
||||
# )
|
||||
# or
|
||||
# (T1, T2, and U are the same "terminal state")
|
||||
# )
|
||||
#
|
||||
# A "terminal state" S is one from which all triggers
|
||||
# transition back to S itself.
|
||||
#
|
||||
# The presence of triggers with multiple out states complicates
|
||||
# this check slightly, but doesn't fundamentally change it.
|
||||
#
|
||||
# from a state S,
|
||||
# for any pair of triggers t1 and t2,
|
||||
# where t1 and t2 have opposite direction,
|
||||
# for each pair of states (T1, T2) \in t1_out x t2_out,
|
||||
# where t1_out is the set of outstates from t1
|
||||
# t2_out is the set of outstates from t2
|
||||
# t1_out x t2_out is their Cartesian product
|
||||
# and t1 transitions to state T1 and t2 to T2,
|
||||
# then the following must be true:
|
||||
# (T2 allows the trigger t1, with out-state set { U })
|
||||
# and
|
||||
# (T1 allows the trigger t2, with out-state set { U })
|
||||
# and
|
||||
# (
|
||||
# (
|
||||
# (all of T1's triggers have the same direction as t2)
|
||||
# and
|
||||
# (all of T2's triggers have the same direction as t1)
|
||||
# )
|
||||
# or
|
||||
# (T1, T2, and U are the same "terminal state")
|
||||
# )
|
||||
|
||||
# check Rule 1
|
||||
syncdirection = None
|
||||
syncok = True
|
||||
for trans in ts.transitions:
|
||||
if not trans.msg.type.isSync(): continue
|
||||
if syncdirection is None:
|
||||
syncdirection = trans.trigger.direction()
|
||||
elif syncdirection is not trans.trigger.direction():
|
||||
self.error(
|
||||
trans.loc,
|
||||
"sync trigger at state `%s' in protocol `%s' has different direction from earlier sync trigger at same state",
|
||||
ts.state.name, self.p.name)
|
||||
syncok = False
|
||||
# don't check the Diamond Rule if Rule 1 doesn't hold
|
||||
if not syncok:
|
||||
return
|
||||
|
||||
# helper functions
|
||||
def triggerTargets(S, t):
|
||||
'''Return the set of states transitioned to from state |S|
|
||||
upon trigger |t|, or { } if |t| is not a trigger in |S|.'''
|
||||
for trans in self.p.states[S].transitions:
|
||||
if t.trigger is trans.trigger and t.msg is trans.msg:
|
||||
return trans.toStates
|
||||
return set()
|
||||
|
||||
def allTriggersSameDirectionAs(S, t):
|
||||
'''Return true iff all the triggers from state |S| have the same
|
||||
direction as trigger |t|'''
|
||||
direction = t.direction()
|
||||
for trans in self.p.states[S].transitions:
|
||||
if direction != trans.trigger.direction():
|
||||
return False
|
||||
return True
|
||||
|
||||
def terminalState(S):
|
||||
'''Return true iff |S| is a "terminal state".'''
|
||||
for trans in self.p.states[S].transitions:
|
||||
for S_ in trans.toStates:
|
||||
if S_ != S: return False
|
||||
return True
|
||||
|
||||
def sameTerminalState(S1, S2, S3):
|
||||
'''Return true iff states |S1|, |S2|, and |S3| are all the same
|
||||
"terminal state".'''
|
||||
if isinstance(S3, set):
|
||||
assert len(S3) == 1
|
||||
for S3_ in S3: pass
|
||||
S3 = S3_
|
||||
|
||||
return (S1 == S2 == S3) and terminalState(S1)
|
||||
|
||||
S = ts.state.name
|
||||
|
||||
# check the Diamond Rule
|
||||
for (t1, t2) in unique_pairs(ts.transitions):
|
||||
# if the triggers have the same direction, they can't race,
|
||||
# since only one endpoint can initiate either (and delivery
|
||||
# is in-order)
|
||||
if t1.trigger.direction() == t2.trigger.direction():
|
||||
continue
|
||||
|
||||
loc = t1.loc
|
||||
t1_out = t1.toStates
|
||||
t2_out = t2.toStates
|
||||
|
||||
for (T1, T2) in cartesian_product(t1_out, t2_out):
|
||||
# U1 <- { u | T1 --t2--> u }
|
||||
U1 = triggerTargets(T1, t2)
|
||||
# U2 <- { u | T2 --t1--> u }
|
||||
U2 = triggerTargets(T2, t1)
|
||||
|
||||
# don't report more than one Diamond Rule violation
|
||||
# per state. there may be O(n^4) total, way too many
|
||||
# for a human to parse
|
||||
#
|
||||
# XXX/cjones: could set a limit on #printed and stop
|
||||
# after that limit ...
|
||||
raceError = False
|
||||
errT1 = None
|
||||
errT2 = None
|
||||
|
||||
if 0 == len(U1) or 0 == len(U2):
|
||||
print "******* case 1"
|
||||
raceError = True
|
||||
elif 1 < len(U1) or 1 < len(U2):
|
||||
raceError = True
|
||||
# there are potentially many unpaired states; just
|
||||
# pick two
|
||||
print "******* case 2"
|
||||
for u1, u2 in cartesian_product(U1, U2):
|
||||
if u1 != u2:
|
||||
errT1, errT2 = u1, u2
|
||||
break
|
||||
elif U1 != U2:
|
||||
print "******* case 3"
|
||||
raceError = True
|
||||
for errT1 in U1: pass
|
||||
for errT2 in U2: pass
|
||||
|
||||
if raceError:
|
||||
self.reportRaceError(loc, S,
|
||||
[ T1, t1, errT1 ],
|
||||
[ T2, t2, errT2 ])
|
||||
return
|
||||
|
||||
if not ((allTriggersSameDirectionAs(T1, t2.trigger)
|
||||
and allTriggersSameDirectionAs(T2, t1.trigger))
|
||||
or sameTerminalState(T1, T2, U1)):
|
||||
self.reportRunawayError(loc, S, [ T1, t1, None ], [ T2, t2, None ])
|
||||
return
|
||||
|
||||
def checkReachability(self, p):
|
||||
def explore(ts, visited):
|
||||
if ts.state in visited:
|
||||
return
|
||||
visited.add(ts.state)
|
||||
for outedge in ts.transitions:
|
||||
for toState in outedge.toStates:
|
||||
explore(p.states[toState], visited)
|
||||
|
||||
checkfordelete = (State.DEAD in p.states)
|
||||
|
||||
allvisited = set() # set(State)
|
||||
for root in p.startStates:
|
||||
visited = set()
|
||||
|
||||
explore(root, visited)
|
||||
allvisited.update(visited)
|
||||
|
||||
if checkfordelete and State.DEAD not in visited:
|
||||
self.error(
|
||||
root.loc,
|
||||
"when starting from state `%s', actors of protocol `%s' cannot be deleted", root.state.name, p.name)
|
||||
|
||||
for ts in p.states.itervalues():
|
||||
if ts.state is not State.DEAD and ts.state not in allvisited:
|
||||
self.error(ts.loc,
|
||||
"unreachable state `%s' in protocol `%s'",
|
||||
ts.state.name, p.name)
|
||||
|
||||
|
||||
def _normalizeTransitionSequences(self, t1Seq, t2Seq):
|
||||
T1, M1, U1 = t1Seq
|
||||
T2, M2, U2 = t2Seq
|
||||
assert M1 is not None and M2 is not None
|
||||
|
||||
# make sure that T1/M1/U1 is the parent side of the race
|
||||
if M1.trigger is RECV or M1.trigger is ANSWER:
|
||||
T1, M1, U1, T2, M2, U2 = T2, M2, U2, T1, M1, U1
|
||||
|
||||
def stateName(S):
|
||||
if S: return S.name
|
||||
return '[error]'
|
||||
|
||||
T1 = stateName(T1)
|
||||
T2 = stateName(T2)
|
||||
U1 = stateName(U1)
|
||||
U2 = stateName(U2)
|
||||
|
||||
return T1, M1.msg.progname, U1, T2, M2.msg.progname, U2
|
||||
|
||||
|
||||
def reportRaceError(self, loc, S, t1Seq, t2Seq):
|
||||
T1, M1, U1, T2, M2, U2 = self._normalizeTransitionSequences(t1Seq, t2Seq)
|
||||
self.error(
|
||||
loc,
|
||||
"""in protocol `%(P)s', the sequence of events
|
||||
parent: +--`send %(M1)s'-->( state `%(T1)s' )--`recv %(M2)s'-->( state %(U1)s )
|
||||
/
|
||||
( state `%(S)s' )
|
||||
\\
|
||||
child: +--`send %(M2)s'-->( state `%(T2)s' )--`recv %(M1)s'-->( state %(U2)s )
|
||||
results in error(s) or leaves parent/child state out of sync for more than one step and is thus a race hazard; i.e., triggers `%(M1)s' and `%(M2)s' fail to commute in state `%(S)s'"""% {
|
||||
'P': self.p.name, 'S': S, 'M1': M1, 'M2': M2,
|
||||
'T1': T1, 'T2': T2, 'U1': U1, 'U2': U2
|
||||
})
|
||||
|
||||
|
||||
def reportRunawayError(self, loc, S, t1Seq, t2Seq):
|
||||
T1, M1, _, T2, M2, __ = self._normalizeTransitionSequences(t1Seq, t2Seq)
|
||||
self.error(
|
||||
loc,
|
||||
"""in protocol `%(P)s', the sequence of events
|
||||
parent: +--`send %(M1)s'-->( state `%(T1)s' )
|
||||
/
|
||||
( state `%(S)s' )
|
||||
\\
|
||||
child: +--`send %(M2)s'-->( state `%(T2)s' )
|
||||
lead to parent/child states in which parent/child state can become more than one step out of sync (though this divergence might not lead to error conditions)"""% {
|
||||
'P': self.p.name, 'S': S, 'M1': M1, 'M2': M2, 'T1': T1, 'T2': T2
|
||||
})
|
||||
|
@ -21,7 +21,7 @@ parent:
|
||||
async Pun(PTestActorPunningSub a, Bad bad);
|
||||
async __delete__();
|
||||
|
||||
|
||||
/*
|
||||
state PING:
|
||||
send Start goto CONSTRUCTING;
|
||||
|
||||
@ -33,6 +33,7 @@ state CONSTRUCTING:
|
||||
|
||||
state DEAD:
|
||||
recv __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -15,10 +15,12 @@ child:
|
||||
parent:
|
||||
async __delete__();
|
||||
|
||||
/*
|
||||
state START:
|
||||
send Start goto DEAD;
|
||||
state DEAD:
|
||||
recv __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
|
@ -19,6 +19,7 @@ parent:
|
||||
intr HelloRpc();
|
||||
async __delete__();
|
||||
|
||||
/*
|
||||
state START: recv Hello goto HI;
|
||||
state HI: send Hi goto HELLO_SYNC;
|
||||
state HELLO_SYNC: recv HelloSync goto HELLO_RPC;
|
||||
@ -26,6 +27,7 @@ state HELLO_RPC: answer HelloRpc goto HI_RPC;
|
||||
state HI_RPC: call HiRpc goto DEAD;
|
||||
state DEAD:
|
||||
recv __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
|
@ -12,12 +12,14 @@ parent:
|
||||
async BridgeEm();
|
||||
async __delete__();
|
||||
|
||||
/*
|
||||
state START:
|
||||
send Ping goto BRIDGEEM;
|
||||
state BRIDGEEM:
|
||||
recv BridgeEm goto DEAD;
|
||||
state DEAD:
|
||||
recv __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
|
@ -12,10 +12,12 @@ child:
|
||||
intr DIEDIEDIE();
|
||||
async __delete__();
|
||||
|
||||
/*
|
||||
state ALIVE:
|
||||
call DIEDIEDIE goto CRASH;
|
||||
state CRASH:
|
||||
send __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -101,6 +101,7 @@ parent:
|
||||
|
||||
sync Dummy(ShmemUnion su) returns (ShmemUnion rsu);
|
||||
|
||||
/*
|
||||
state CONSTRUCTING:
|
||||
send PTestDataStructuresSub goto CONSTRUCTING;
|
||||
send Start goto TEST1;
|
||||
@ -125,6 +126,7 @@ state TEST18: recv Test18 goto DEAD;
|
||||
|
||||
state DEAD:
|
||||
recv __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
} // namespace _ipdltest
|
||||
|
@ -16,7 +16,7 @@ child:
|
||||
parent:
|
||||
async Ok(PTestDescSubsub a);
|
||||
|
||||
|
||||
/*
|
||||
state CONSTRUCT:
|
||||
call PTestDescSub goto TEST;
|
||||
state TEST:
|
||||
@ -25,6 +25,7 @@ state ACK:
|
||||
recv Ok goto DEAD;
|
||||
state DEAD:
|
||||
send __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ parent:
|
||||
intr HelloRpc();
|
||||
async __delete__();
|
||||
|
||||
/*
|
||||
state START: recv Hello goto HI;
|
||||
state HI: send Hi goto HELLO_SYNC;
|
||||
state HELLO_SYNC: recv HelloSync goto HELLO_RPC;
|
||||
@ -23,6 +24,7 @@ state HELLO_RPC: answer HelloRpc goto HI_RPC;
|
||||
state HI_RPC: call HiRpc goto DEAD;
|
||||
state DEAD:
|
||||
recv __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
|
@ -9,10 +9,12 @@ child:
|
||||
intr PTestFailedCtorSub();
|
||||
async __delete__();
|
||||
|
||||
/*
|
||||
state CONSTRUCT:
|
||||
call PTestFailedCtorSub goto DEAD;
|
||||
state DEAD:
|
||||
send __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ child:
|
||||
intr Hang();
|
||||
async __delete__();
|
||||
|
||||
|
||||
/*
|
||||
state START:
|
||||
send Start goto RACE;
|
||||
|
||||
@ -34,6 +34,7 @@ state HANG: call Hang goto DEATH;
|
||||
|
||||
state DEATH:
|
||||
send __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
} // namespace _ipdltest
|
||||
|
@ -19,6 +19,7 @@ child:
|
||||
intr Child();
|
||||
async __delete__();
|
||||
|
||||
/*
|
||||
state START:
|
||||
send Start goto TEST1;
|
||||
|
||||
@ -76,6 +77,8 @@ state CHECK:
|
||||
|
||||
state DYING:
|
||||
send __delete__;
|
||||
*/
|
||||
|
||||
};
|
||||
|
||||
} // namespace _ipdltest
|
||||
|
@ -11,6 +11,7 @@ child:
|
||||
intr Exit();
|
||||
async __delete__();
|
||||
|
||||
/*
|
||||
state START:
|
||||
send Start goto START_DEATH;
|
||||
|
||||
@ -28,6 +29,7 @@ state QUITTING2:
|
||||
|
||||
state DEAD:
|
||||
send __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
} // namespace _ipdltest
|
||||
|
@ -41,6 +41,7 @@ parent:
|
||||
returns (JSONVariant o);
|
||||
async __delete__();
|
||||
|
||||
/*
|
||||
state START:
|
||||
send Start goto TEST;
|
||||
|
||||
@ -48,6 +49,7 @@ state TEST:
|
||||
recv PTestHandle goto TEST;
|
||||
recv Test goto TEST;
|
||||
recv __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -20,6 +20,7 @@ parent:
|
||||
async Pong();
|
||||
async Pong5();
|
||||
|
||||
/*
|
||||
state START:
|
||||
// if the timing resolution is too low, abort the test
|
||||
send __delete__;
|
||||
@ -68,6 +69,7 @@ state COMPRESSED_SPAM: // compressed spam, mmm
|
||||
|
||||
state DONE:
|
||||
send __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
|
@ -17,6 +17,7 @@ child:
|
||||
async Check();
|
||||
async __delete__();
|
||||
|
||||
/*
|
||||
state START:
|
||||
send PTestMultiMgrsLeft goto CONSTRUCT_RIGHT;
|
||||
state CONSTRUCT_RIGHT:
|
||||
@ -28,6 +29,7 @@ state CHILD_ACK:
|
||||
|
||||
state DONE:
|
||||
send __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
} // namespace _ipdltest
|
||||
|
@ -10,8 +10,10 @@ protocol PTestMultiMgrsBottom {
|
||||
child:
|
||||
async __delete__();
|
||||
|
||||
/*
|
||||
state DOA:
|
||||
send __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
} // namespace _ipdltest
|
||||
|
@ -13,11 +13,13 @@ child:
|
||||
async PTestMultiMgrsBottom();
|
||||
async __delete__();
|
||||
|
||||
/*
|
||||
state START:
|
||||
send PTestMultiMgrsBottom goto DONE;
|
||||
|
||||
state DONE:
|
||||
send __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
} // namespace _ipdltest
|
||||
|
@ -13,11 +13,13 @@ child:
|
||||
async PTestMultiMgrsBottom();
|
||||
async __delete__();
|
||||
|
||||
/*
|
||||
state START:
|
||||
send PTestMultiMgrsBottom goto DONE;
|
||||
|
||||
state DONE:
|
||||
send __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
} // namespace _ipdltest
|
||||
|
@ -13,7 +13,7 @@ child:
|
||||
parent:
|
||||
async Nonce();
|
||||
|
||||
|
||||
/*
|
||||
state START:
|
||||
send Start goto RACE;
|
||||
|
||||
@ -27,6 +27,7 @@ state RACE2:
|
||||
|
||||
state DEAD:
|
||||
send __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
|
@ -14,10 +14,12 @@ child:
|
||||
parent:
|
||||
async __delete__();
|
||||
|
||||
/*
|
||||
state START:
|
||||
send Start goto DEAD;
|
||||
state DEAD:
|
||||
recv __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
|
@ -14,6 +14,7 @@ parent:
|
||||
intr HelloRpc();
|
||||
async __delete__();
|
||||
|
||||
/*
|
||||
state START: recv Hello goto HI;
|
||||
state HI: send Hi goto HELLO_SYNC;
|
||||
state HELLO_SYNC: recv HelloSync goto HELLO_RPC;
|
||||
@ -21,6 +22,7 @@ state HELLO_RPC: answer HelloRpc goto HI_RPC;
|
||||
state HI_RPC: call HiRpc goto DEAD;
|
||||
state DEAD:
|
||||
recv __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
|
@ -12,6 +12,7 @@ parent:
|
||||
intr _R() returns (int replyNum);
|
||||
async A_();
|
||||
|
||||
/*
|
||||
state PARENT_START:
|
||||
call R_ goto PARENT_S1;
|
||||
|
||||
@ -35,6 +36,7 @@ state CHILD_S2:
|
||||
|
||||
state DYING:
|
||||
send __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
} // namespace _ipdltest
|
||||
|
@ -12,7 +12,7 @@ child:
|
||||
parent:
|
||||
async Pong(int one, float zeroPtTwoFive, uint8_t dummy);
|
||||
|
||||
|
||||
/*
|
||||
state PING:
|
||||
send Ping goto PONG;
|
||||
|
||||
@ -21,6 +21,7 @@ state PONG:
|
||||
|
||||
state DEAD:
|
||||
send __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
|
@ -12,9 +12,11 @@ child:
|
||||
async PTestSelfManage();
|
||||
async __delete__();
|
||||
|
||||
/*
|
||||
state LIVE:
|
||||
send PTestSelfManage goto LIVE;
|
||||
send __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
|
@ -11,11 +11,13 @@ child:
|
||||
async PTestSelfManage();
|
||||
async __delete__();
|
||||
|
||||
/*
|
||||
state LIVE:
|
||||
send PTestSelfManage goto DEAD;
|
||||
|
||||
state DEAD:
|
||||
send __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
|
@ -9,13 +9,14 @@ parent:
|
||||
async Take(Shmem mem, Shmem unsafe, size_t expectedSize);
|
||||
async __delete__();
|
||||
|
||||
|
||||
/*
|
||||
state GIVING:
|
||||
send Give goto TAKING;
|
||||
|
||||
state TAKING:
|
||||
recv Take goto TAKING;
|
||||
recv __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ parent:
|
||||
|
||||
async __delete__();
|
||||
|
||||
|
||||
/*
|
||||
state START:
|
||||
send Start goto TESTING;
|
||||
|
||||
@ -31,6 +31,7 @@ state TESTING:
|
||||
|
||||
state DYING:
|
||||
recv __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
} // namespace _ipdltest
|
||||
|
@ -15,6 +15,7 @@ parent:
|
||||
async PTestShutdownSubsub(bool expectParentDeleted);
|
||||
sync __delete__();
|
||||
|
||||
/*
|
||||
state CREATING:
|
||||
recv PTestShutdownSubsub goto CREATING;
|
||||
answer StackFrame goto DUMMYFRAME;
|
||||
@ -24,6 +25,7 @@ state DUMMYFRAME:
|
||||
|
||||
state DEAD:
|
||||
recv __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
} // namespace _ipdltest
|
||||
|
@ -9,8 +9,10 @@ sync protocol PTestShutdownSubsub {
|
||||
parent:
|
||||
sync __delete__();
|
||||
|
||||
/*
|
||||
state LIVE:
|
||||
recv __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
} // namespace _ipdltest
|
||||
|
@ -20,7 +20,7 @@ both:
|
||||
parent:
|
||||
async __delete__();
|
||||
|
||||
|
||||
/*
|
||||
state START:
|
||||
send Start goto TEST1;
|
||||
|
||||
@ -49,6 +49,7 @@ state TEST5_3:
|
||||
|
||||
state DEAD:
|
||||
recv __delete__;
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user