Bug 1677929 - Add a way to specify an ID for a generated frame, and propagate the ID to the APZSampler. r=gw,kats

This ID allows the compositor to track per-frame information from frame
generation, through APZ sampling, to the NotifyDidRender notification.

Differential Revision: https://phabricator.services.mozilla.com/D97535
This commit is contained in:
Markus Stange
2020-12-09 03:35:50 +00:00
parent a867907ff8
commit 46ea354af1
24 changed files with 113 additions and 55 deletions

View File

@@ -57,11 +57,13 @@ class APZSampler {
*/
static void SetSamplerThread(const wr::WrWindowId& aWindowId);
static void SampleForWebRender(
const wr::WrWindowId& aWindowId, wr::Transaction* aTxn,
const wr::WrWindowId& aWindowId, const uint64_t* aGeneratedFrameId,
wr::Transaction* aTransaction,
const wr::WrPipelineIdEpochs* aEpochsBeingRendered);
void SetSampleTime(const SampleTime& aSampleTime);
void SampleForWebRender(wr::TransactionWrapper& aTxn,
void SampleForWebRender(const Maybe<VsyncId>& aGeneratedFrameId,
wr::TransactionWrapper& aTxn,
const wr::WrPipelineIdEpochs* aEpochsBeingRendered);
bool AdvanceAnimations(const SampleTime& aSampleTime);

View File

@@ -706,7 +706,8 @@ void APZCTreeManager::UpdateHitTestingTree(
}
void APZCTreeManager::SampleForWebRender(
wr::TransactionWrapper& aTxn, const SampleTime& aSampleTime,
const Maybe<VsyncId>& aVsyncId, wr::TransactionWrapper& aTxn,
const SampleTime& aSampleTime,
const wr::WrPipelineIdEpochs* aEpochsBeingRendered) {
AssertOnSamplerThread();
MutexAutoLock lock(mMapLock);

View File

@@ -196,7 +196,8 @@ class APZCTreeManager : public IAPZCTreeManager, public APZInputBridge {
* In effect it is the webrender equivalent of (part of) the code in
* AsyncCompositionManager.
*/
void SampleForWebRender(wr::TransactionWrapper& aTxn,
void SampleForWebRender(const Maybe<VsyncId>& aVsyncId,
wr::TransactionWrapper& aTxn,
const SampleTime& aSampleTime,
const wr::WrPipelineIdEpochs* aEpochsBeingRendered);

View File

@@ -66,11 +66,14 @@ void APZSampler::SetSamplerThread(const wr::WrWindowId& aWindowId) {
/*static*/
void APZSampler::SampleForWebRender(
const wr::WrWindowId& aWindowId, wr::Transaction* aTransaction,
const wr::WrWindowId& aWindowId, const uint64_t* aGeneratedFrameId,
wr::Transaction* aTransaction,
const wr::WrPipelineIdEpochs* aEpochsBeingRendered) {
if (RefPtr<APZSampler> sampler = GetSampler(aWindowId)) {
wr::TransactionWrapper txn(aTransaction);
sampler->SampleForWebRender(txn, aEpochsBeingRendered);
Maybe<VsyncId> vsyncId =
aGeneratedFrameId ? Some(VsyncId{*aGeneratedFrameId}) : Nothing();
sampler->SampleForWebRender(vsyncId, txn, aEpochsBeingRendered);
}
}
@@ -84,7 +87,7 @@ void APZSampler::SetSampleTime(const SampleTime& aSampleTime) {
}
void APZSampler::SampleForWebRender(
wr::TransactionWrapper& aTxn,
const Maybe<VsyncId>& aVsyncId, wr::TransactionWrapper& aTxn,
const wr::WrPipelineIdEpochs* aEpochsBeingRendered) {
AssertOnSamplerThread();
SampleTime sampleTime;
@@ -121,7 +124,7 @@ void APZSampler::SampleForWebRender(
? now
: mSampleTime;
}
mApz->SampleForWebRender(aTxn, sampleTime, aEpochsBeingRendered);
mApz->SampleForWebRender(aVsyncId, aTxn, sampleTime, aEpochsBeingRendered);
}
bool APZSampler::AdvanceAnimations(const SampleTime& aSampleTime) {
@@ -331,10 +334,11 @@ void apz_register_sampler(mozilla::wr::WrWindowId aWindowId) {
}
void apz_sample_transforms(
mozilla::wr::WrWindowId aWindowId, mozilla::wr::Transaction* aTransaction,
mozilla::wr::WrWindowId aWindowId, const uint64_t* aGeneratedFrameId,
mozilla::wr::Transaction* aTransaction,
const mozilla::wr::WrPipelineIdEpochs* aEpochsBeingRendered) {
mozilla::layers::APZSampler::SampleForWebRender(aWindowId, aTransaction,
aEpochsBeingRendered);
mozilla::layers::APZSampler::SampleForWebRender(
aWindowId, aGeneratedFrameId, aTransaction, aEpochsBeingRendered);
}
void apz_deregister_sampler(mozilla::wr::WrWindowId aWindowId) {}

View File

@@ -2091,7 +2091,7 @@ void WebRenderBridgeParent::MaybeGenerateFrame(VsyncId aId,
#endif
MOZ_ASSERT(generateFrame);
fastTxn.GenerateFrame();
fastTxn.GenerateFrame(aId);
mApi->SendTransaction(fastTxn);
#if defined(MOZ_WIDGET_ANDROID)

View File

@@ -252,8 +252,8 @@ void TransactionBuilder::ClearDisplayList(Epoch aEpoch,
wr_transaction_clear_display_list(mTxn, aEpoch, aPipelineId);
}
void TransactionBuilder::GenerateFrame() {
wr_transaction_generate_frame(mTxn);
void TransactionBuilder::GenerateFrame(const VsyncId& aVsyncId) {
wr_transaction_generate_frame(mTxn, aVsyncId.mId);
}
void TransactionBuilder::InvalidateRenderedFrame() {

View File

@@ -111,7 +111,7 @@ class TransactionBuilder final {
void ClearDisplayList(Epoch aEpoch, wr::WrPipelineId aPipeline);
void GenerateFrame();
void GenerateFrame(const VsyncId& aVsyncId);
void InvalidateRenderedFrame();

View File

@@ -917,6 +917,7 @@ extern "C" {
fn apz_register_sampler(window_id: WrWindowId);
fn apz_sample_transforms(
window_id: WrWindowId,
generated_frame_id: *const u64,
transaction: &mut Transaction,
epochs_being_rendered: &WrPipelineIdEpochs,
);
@@ -1014,8 +1015,17 @@ impl AsyncPropertySampler for SamplerCallback {
fn sample(
&self,
_document_id: DocumentId,
generated_frame_id: Option<u64>,
epochs_being_rendered: &FastHashMap<PipelineId, Epoch>,
) -> Vec<FrameMsg> {
let generated_frame_id_value;
let generated_frame_id: *const u64 = match generated_frame_id {
Some(id) => {
generated_frame_id_value = id;
&generated_frame_id_value
}
None => ptr::null_mut(),
};
let mut transaction = Transaction::new();
unsafe {
// XXX: When we implement scroll-linked animations, we will probably
@@ -1023,6 +1033,7 @@ impl AsyncPropertySampler for SamplerCallback {
omta_sample(self.window_id, &mut transaction);
apz_sample_transforms(
self.window_id,
generated_frame_id,
&mut transaction,
&epochs_being_rendered.iter().map(WrPipelineIdAndEpoch::from).collect(),
)
@@ -1823,8 +1834,8 @@ pub extern "C" fn wr_transaction_set_document_view(txn: &mut Transaction, doc_re
}
#[no_mangle]
pub extern "C" fn wr_transaction_generate_frame(txn: &mut Transaction) {
txn.generate_frame();
pub extern "C" fn wr_transaction_generate_frame(txn: &mut Transaction, id: u64) {
txn.generate_frame(id);
}
#[no_mangle]

View File

@@ -92,7 +92,8 @@ void apz_deregister_updater(mozilla::wr::WrWindowId aWindowId);
void apz_register_sampler(mozilla::wr::WrWindowId aWindowId);
void apz_sample_transforms(
mozilla::wr::WrWindowId aWindowId, mozilla::wr::Transaction* aTransaction,
mozilla::wr::WrWindowId aWindowId, const uint64_t* aGeneratedFrameId,
mozilla::wr::Transaction* aTransaction,
const mozilla::wr::WrPipelineIdEpochs* aPipelineEpochs);
void apz_deregister_sampler(mozilla::wr::WrWindowId aWindowId);

View File

@@ -169,7 +169,7 @@ impl Rectangle {
true,
);
transaction.set_root_pipeline(pipeline_id);
transaction.generate_frame();
transaction.generate_frame(0);
self.api.send_transaction(self.document_id, transaction);
rx.recv().unwrap();
let renderer = self.renderer.as_mut().unwrap();

View File

@@ -417,7 +417,7 @@ fn main() {
);
}
txn.generate_frame();
txn.generate_frame(0);
api.send_transaction(document_id, txn);
// Tick the compositor (in this sample, we don't block on UI events)
@@ -464,7 +464,7 @@ fn main() {
}
}
txn.generate_frame();
txn.generate_frame(0);
api.send_transaction(document_id, txn);
current_epoch.0 += 1;
time += 0.001;

View File

@@ -195,7 +195,7 @@ impl Example for App {
colors: vec![],
},
);
txn.generate_frame();
txn.generate_frame(0);
api.send_transaction(document_id, txn);
}
_ => (),

View File

@@ -313,7 +313,7 @@ impl Example for App {
}
if !txn.is_empty() {
txn.generate_frame();
txn.generate_frame(0);
api.send_transaction(document_id, txn);
}

View File

@@ -215,7 +215,7 @@ pub fn main_wrapper<E: Example>(
true,
);
txn.set_root_pipeline(pipeline_id);
txn.generate_frame();
txn.generate_frame(0);
api.send_transaction(document_id, txn);
println!("Entering event loop");
@@ -312,7 +312,7 @@ pub fn main_wrapper<E: Example>(
builder.finalize(),
true,
);
txn.generate_frame();
txn.generate_frame(0);
}
api.send_transaction(document_id, txn);

View File

@@ -129,7 +129,7 @@ impl Example for App {
builder.finalize(),
true,
);
txn.generate_frame();
txn.generate_frame(0);
api.send_transaction(doc.id, txn);
}
}

View File

@@ -104,7 +104,7 @@ impl Example for App {
&DirtyRect::All,
);
let mut txn = Transaction::new();
txn.generate_frame();
txn.generate_frame(0);
api.send_transaction(document_id, txn);
}
_ => {}

View File

@@ -285,7 +285,7 @@ impl Window {
true,
);
txn.set_root_pipeline(self.pipeline_id);
txn.generate_frame();
txn.generate_frame(0);
api.send_transaction(self.document_id, txn);
renderer.update();

View File

@@ -182,11 +182,11 @@ impl Example for App {
ExternalScrollId(0, PipelineId::dummy()),
ScrollClamping::ToContentBounds,
);
txn.generate_frame();
txn.generate_frame(0);
}
if let Some(zoom) = zoom {
txn.set_pinch_zoom(ZoomFactor::new(zoom));
txn.generate_frame();
txn.generate_frame(0);
}
}
winit::WindowEvent::CursorMoved { position: LogicalPosition { x, y }, .. } => {
@@ -207,7 +207,7 @@ impl Example for App {
ScrollClamping::ToContentBounds,
);
txn.generate_frame();
txn.generate_frame(0);
}
winit::WindowEvent::MouseInput { .. } => {
let results = api.hit_test(

View File

@@ -106,6 +106,38 @@ impl fmt::Debug for ResourceUpdate {
}
}
/// Whether to generate a frame, and if so, an id that allows tracking this
/// transaction through the various frame stages.
#[derive(Clone, Debug)]
pub enum GenerateFrame {
/// Generate a frame if something changed.
Yes {
/// An id that allows tracking the frame transaction through the various
/// frame stages. Specified by the caller of generate_frame().
id: u64,
},
/// Don't generate a frame even if something has changed.
No,
}
impl GenerateFrame {
///
pub fn as_bool(&self) -> bool {
match self {
GenerateFrame::Yes { .. } => true,
GenerateFrame::No => false,
}
}
/// Return the frame ID, if a frame is generated.
pub fn id(&self) -> Option<u64> {
match self {
GenerateFrame::Yes { id } => Some(*id),
GenerateFrame::No => None,
}
}
}
/// A Transaction is a group of commands to apply atomically to a document.
///
/// This mechanism ensures that:
@@ -127,8 +159,10 @@ pub struct Transaction {
/// it will be applied directly on the render backend.
use_scene_builder_thread: bool,
///
generate_frame: bool,
/// Whether to generate a frame, and if so, an id that allows tracking this
/// transaction through the various frame stages. Specified by the caller of
/// generate_frame().
generate_frame: GenerateFrame,
/// Set to true in order to force re-rendering even if WebRender can't internally
/// detect that something has changed.
@@ -146,7 +180,7 @@ impl Transaction {
resource_updates: Vec::new(),
notifications: Vec::new(),
use_scene_builder_thread: true,
generate_frame: false,
generate_frame: GenerateFrame::No,
invalidate_rendered_frame: false,
low_priority: false,
}
@@ -171,7 +205,7 @@ impl Transaction {
/// Returns true if the transaction has no effect.
pub fn is_empty(&self) -> bool {
!self.generate_frame &&
!self.generate_frame.as_bool() &&
!self.invalidate_rendered_frame &&
self.scene_ops.is_empty() &&
self.frame_ops.is_empty() &&
@@ -342,8 +376,8 @@ impl Transaction {
/// as to when happened.
///
/// [notifier]: trait.RenderNotifier.html#tymethod.new_frame_ready
pub fn generate_frame(&mut self) {
self.generate_frame = true;
pub fn generate_frame(&mut self, id: u64) {
self.generate_frame = GenerateFrame::Yes{ id };
}
/// Invalidate rendered frame. It ensure that frame will be rendered during
@@ -556,7 +590,7 @@ pub struct TransactionMsg {
/// Updates to resources that persist across display lists.
pub resource_updates: Vec<ResourceUpdate>,
/// Whether to trigger frame building and rendering if something has changed.
pub generate_frame: bool,
pub generate_frame: GenerateFrame,
/// Whether to force frame building and rendering even if no changes are internally
/// observed.
pub invalidate_rendered_frame: bool,
@@ -579,7 +613,7 @@ pub struct TransactionMsg {
impl fmt::Debug for TransactionMsg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "threaded={}, genframe={}, invalidate={}, low_priority={}",
writeln!(f, "threaded={}, genframe={:?}, invalidate={}, low_priority={}",
self.use_scene_builder_thread,
self.generate_frame,
self.invalidate_rendered_frame,
@@ -603,7 +637,7 @@ impl fmt::Debug for TransactionMsg {
impl TransactionMsg {
/// Returns true if this transaction has no effect.
pub fn is_empty(&self) -> bool {
!self.generate_frame &&
!self.generate_frame.as_bool() &&
!self.invalidate_rendered_frame &&
self.scene_ops.is_empty() &&
self.frame_ops.is_empty() &&
@@ -1230,7 +1264,7 @@ impl RenderApi {
frame_ops: vec![msg],
resource_updates: Vec::new(),
notifications: Vec::new(),
generate_frame: false,
generate_frame: GenerateFrame::No,
invalidate_rendered_frame: false,
use_scene_builder_thread: false,
low_priority: false,
@@ -1249,7 +1283,7 @@ impl RenderApi {
frame_ops: Vec::new(),
resource_updates: Vec::new(),
notifications: Vec::new(),
generate_frame: false,
generate_frame: GenerateFrame::No,
invalidate_rendered_frame: false,
use_scene_builder_thread: false,
low_priority: false,
@@ -1287,7 +1321,7 @@ impl RenderApi {
self.resources.update(&mut transaction);
transaction.use_scene_builder_thread |= !transaction.scene_ops.is_empty();
if transaction.generate_frame {
if transaction.generate_frame.as_bool() {
transaction.profile.start_time(profiler::API_SEND_TIME);
transaction.profile.start_time(profiler::TOTAL_FRAME_CPU_TIME);
}
@@ -1312,7 +1346,7 @@ impl RenderApi {
.map(|(txn, id)| {
let mut txn = txn.finalize(id);
self.resources.update(&mut txn);
if txn.generate_frame {
if txn.generate_frame.as_bool() {
txn.profile.start_time(profiler::API_SEND_TIME);
txn.profile.start_time(profiler::TOTAL_FRAME_CPU_TIME);
}

View File

@@ -1010,6 +1010,7 @@ impl RenderBackend {
txn.frame_ops.take(),
txn.notifications.take(),
txn.render_frame,
None,
txn.invalidate_rendered_frame,
frame_counter,
has_built_scene,
@@ -1347,7 +1348,7 @@ impl RenderBackend {
let mut built_frame = false;
for mut txn in txns {
if txn.generate_frame {
if txn.generate_frame.as_bool() {
txn.profile.end_time(profiler::API_SEND_TIME);
}
@@ -1358,7 +1359,8 @@ impl RenderBackend {
txn.resource_updates.take(),
txn.frame_ops.take(),
txn.notifications.take(),
txn.generate_frame,
txn.generate_frame.as_bool(),
txn.generate_frame.id(),
txn.invalidate_rendered_frame,
frame_counter,
false
@@ -1395,6 +1397,7 @@ impl RenderBackend {
Vec::default(),
Vec::default(),
false,
None,
false,
frame_counter,
false);
@@ -1414,6 +1417,7 @@ impl RenderBackend {
mut frame_ops: Vec<FrameMsg>,
mut notifications: Vec<NotificationRequest>,
mut render_frame: bool,
generated_frame_id: Option<u64>,
invalidate_rendered_frame: bool,
frame_counter: &mut u32,
has_built_scene: bool,
@@ -1430,7 +1434,7 @@ impl RenderBackend {
// async transforms.
if requested_frame || has_built_scene {
if let Some(ref sampler) = self.sampler {
frame_ops.append(&mut sampler.sample(document_id,
frame_ops.append(&mut sampler.sample(document_id, generated_frame_id,
&doc.scene.pipeline_epochs));
}
}

View File

@@ -6907,7 +6907,7 @@ pub trait AsyncPropertySampler {
/// This is called for each transaction with the generate_frame flag set
/// (i.e. that will trigger a render). The list of frame messages returned
/// are processed as though they were part of the original transaction.
fn sample(&self, document_id: DocumentId,
fn sample(&self, document_id: DocumentId, generated_frame_id: Option<u64>,
doc: &FastHashMap<PipelineId, Epoch>) -> Vec<FrameMsg>;
/// This is called exactly once, when the render backend thread is about to
/// terminate.

View File

@@ -687,7 +687,7 @@ impl SceneBuilderThread {
Box::new(BuiltTransaction {
document_id: txn.document_id,
render_frame: txn.generate_frame,
render_frame: txn.generate_frame.as_bool(),
invalidate_rendered_frame: txn.invalidate_rendered_frame,
built_scene,
view: doc.view,

View File

@@ -107,7 +107,7 @@ impl<'a> RawtestHarness<'a> {
);
epoch.0 += 1;
txn.generate_frame();
txn.generate_frame(0);
self.wrench.api.send_transaction(self.wrench.document_id, txn);
}
@@ -1239,7 +1239,7 @@ impl<'a> RawtestHarness<'a> {
builder.finalize(),
false,
);
txn.generate_frame();
txn.generate_frame(0);
self.wrench.api.send_transaction(self.wrench.document_id, txn);
@@ -1274,7 +1274,7 @@ impl<'a> RawtestHarness<'a> {
// 6. rebuild the scene and compare again
let mut txn = Transaction::new();
txn.set_root_pipeline(captured.root_pipeline_id.unwrap());
txn.generate_frame();
txn.generate_frame(0);
self.wrench.api.send_transaction(captured.document_id, txn);
let pixels2 = self.render_and_get_pixels(window_rect);
self.compare_pixels(pixels0, pixels2, window_rect.size);
@@ -1305,7 +1305,7 @@ impl<'a> RawtestHarness<'a> {
builder.finalize(),
false,
);
txn.generate_frame();
txn.generate_frame(0);
self.wrench.api.send_transaction(doc_id, txn);
// Ensure we get a notification from rendering the above, even though

View File

@@ -606,7 +606,7 @@ impl Wrench {
txn.scroll_node_with_id(*offset, *id, ScrollClamping::NoClamping);
}
txn.generate_frame();
txn.generate_frame(0);
self.api.send_transaction(self.document_id, txn);
}
@@ -627,7 +627,7 @@ impl Wrench {
pub fn refresh(&mut self) {
self.begin_frame();
let mut txn = Transaction::new();
txn.generate_frame();
txn.generate_frame(0);
self.api.send_transaction(self.document_id, txn);
}