merge autoland to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2016-12-14 16:35:55 +01:00
commit 2e12354e97
136 changed files with 2017 additions and 2224 deletions

View File

@ -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");

View File

@ -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) {

View File

@ -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.");

View File

@ -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.
-->

View File

@ -100,9 +100,9 @@ fileExecutableSecurityWarning=“%S” is an executable file. Executable files m
fileExecutableSecurityWarningTitle=Open Executable File?
fileExecutableSecurityWarningDontAsk=Dont 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

View File

@ -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, "

View File

@ -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");
}

View File

@ -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 {

View File

@ -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);
};

View File

@ -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();
}

View File

@ -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;

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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);

View File

@ -265,6 +265,8 @@ private:
class DecodingFirstFrameState;
class DecodingState;
class SeekingState;
class AccurateSeekingState;
class NextFrameSeekingState;
class BufferingState;
class CompletedState;
class ShutdownState;

View File

@ -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);
},

View File

@ -55,13 +55,6 @@ NextFrameSeekTask::Discard()
mIsDiscarded = true;
}
bool
NextFrameSeekTask::NeedToResetMDSM() const
{
AssertOwnerThread();
return false;
}
int64_t
NextFrameSeekTask::CalculateNewCurrentTime() const
{

View File

@ -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();

View File

@ -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);

View File

@ -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) {

View File

@ -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()));
{

View File

@ -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);
},

View File

@ -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',

View File

@ -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;
};

View File

@ -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.

View File

@ -28,9 +28,6 @@ protocol PPluginBackgroundDestroyer {
parent:
async __delete__();
state DESTROYING:
recv __delete__;
};
} // namespace plugins

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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));
}
}
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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.
*/

View File

@ -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>";
}

View File

@ -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);
/**

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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,

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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 {}

View File

@ -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*)
{

View File

@ -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();

View File

@ -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;

View File

@ -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;
}

View File

@ -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() {}

View File

@ -1819,5 +1819,11 @@ PerUnitTexturePoolOGL::DestroyTextures()
mTextures.SetLength(0);
}
bool
CompositorOGL::SupportsLayerGeometry() const
{
return gfxPrefs::OGLLayerGeometry();
}
} // namespace layers
} // namespace mozilla

View File

@ -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;

View File

@ -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),

View File

@ -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>

View File

@ -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),

View File

@ -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));
}

View File

@ -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);

View File

@ -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

View File

@ -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)

View File

@ -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))

View File

@ -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

View File

@ -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

View File

@ -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
})

View File

@ -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

View File

@ -15,10 +15,12 @@ child:
parent:
async __delete__();
/*
state START:
send Start goto DEAD;
state DEAD:
recv __delete__;
*/
};

View File

@ -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__;
*/
};

View File

@ -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__;
*/
};

View File

@ -12,10 +12,12 @@ child:
intr DIEDIEDIE();
async __delete__();
/*
state ALIVE:
call DIEDIEDIE goto CRASH;
state CRASH:
send __delete__;
*/
};
}

View File

@ -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

View File

@ -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__;
*/
};
}

View File

@ -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__;
*/
};

View File

@ -9,10 +9,12 @@ child:
intr PTestFailedCtorSub();
async __delete__();
/*
state CONSTRUCT:
call PTestFailedCtorSub goto DEAD;
state DEAD:
send __delete__;
*/
};
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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__;
*/
};

View File

@ -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

View File

@ -10,8 +10,10 @@ protocol PTestMultiMgrsBottom {
child:
async __delete__();
/*
state DOA:
send __delete__;
*/
};
} // namespace _ipdltest

View File

@ -13,11 +13,13 @@ child:
async PTestMultiMgrsBottom();
async __delete__();
/*
state START:
send PTestMultiMgrsBottom goto DONE;
state DONE:
send __delete__;
*/
};
} // namespace _ipdltest

View File

@ -13,11 +13,13 @@ child:
async PTestMultiMgrsBottom();
async __delete__();
/*
state START:
send PTestMultiMgrsBottom goto DONE;
state DONE:
send __delete__;
*/
};
} // namespace _ipdltest

View File

@ -13,7 +13,7 @@ child:
parent:
async Nonce();
/*
state START:
send Start goto RACE;
@ -27,6 +27,7 @@ state RACE2:
state DEAD:
send __delete__;
*/
};

View File

@ -14,10 +14,12 @@ child:
parent:
async __delete__();
/*
state START:
send Start goto DEAD;
state DEAD:
recv __delete__;
*/
};

View File

@ -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__;
*/
};

View File

@ -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

View File

@ -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__;
*/
};

View File

@ -12,9 +12,11 @@ child:
async PTestSelfManage();
async __delete__();
/*
state LIVE:
send PTestSelfManage goto LIVE;
send __delete__;
*/
};

View File

@ -11,11 +11,13 @@ child:
async PTestSelfManage();
async __delete__();
/*
state LIVE:
send PTestSelfManage goto DEAD;
state DEAD:
send __delete__;
*/
};

View File

@ -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__;
*/
};
}

View File

@ -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

View File

@ -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

View File

@ -9,8 +9,10 @@ sync protocol PTestShutdownSubsub {
parent:
sync __delete__();
/*
state LIVE:
recv __delete__;
*/
};
} // namespace _ipdltest

View File

@ -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