WebRenderImageHost.cpp (14371B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "WebRenderImageHost.h" 8 9 #include <utility> 10 11 #include "mozilla/ScopeExit.h" 12 #include "mozilla/gfx/gfxVars.h" 13 #include "mozilla/layers/AsyncImagePipelineManager.h" 14 #include "mozilla/layers/CompositorThread.h" 15 #include "mozilla/layers/CompositorVsyncScheduler.h" // for CompositorVsyncScheduler 16 #include "mozilla/layers/KnowsCompositor.h" 17 #include "mozilla/layers/RemoteTextureHostWrapper.h" 18 #include "mozilla/layers/RemoteTextureMap.h" 19 #include "mozilla/layers/WebRenderBridgeParent.h" 20 #include "mozilla/layers/WebRenderTextureHost.h" 21 #include "mozilla/ProfilerMarkers.h" 22 #include "mozilla/StaticPrefs_gfx.h" 23 #include "mozilla/StaticPrefs_webgl.h" 24 #include "nsAString.h" 25 #include "nsDebug.h" // for NS_WARNING, NS_ASSERTION 26 #include "nsPrintfCString.h" // for nsPrintfCString 27 #include "nsString.h" // for nsAutoCString 28 29 #if XP_WIN 30 # include "mozilla/layers/GpuProcessD3D11TextureMap.h" 31 # include "mozilla/layers/TextureHostWrapperD3D11.h" 32 #endif 33 34 namespace mozilla { 35 36 using namespace gfx; 37 38 namespace layers { 39 40 class ISurfaceAllocator; 41 42 WebRenderImageHost::WebRenderImageHost(const TextureInfo& aTextureInfo) 43 : CompositableHost(aTextureInfo), mCurrentAsyncImageManager(nullptr) {} 44 45 WebRenderImageHost::~WebRenderImageHost() { 46 MOZ_ASSERT(mPendingRemoteTextureWrappers.empty()); 47 MOZ_ASSERT(mWrBridges.empty()); 48 } 49 50 void WebRenderImageHost::OnReleased() { 51 ImageComposite::ClearImages(); 52 if (!mPendingRemoteTextureWrappers.empty()) { 53 mPendingRemoteTextureWrappers.clear(); 54 } 55 SetCurrentTextureHost(nullptr); 56 } 57 58 void WebRenderImageHost::UseTextureHost( 59 const nsTArray<TimedTexture>& aTextures) { 60 CompositableHost::UseTextureHost(aTextures); 61 MOZ_ASSERT(aTextures.Length() >= 1); 62 63 if (!mPendingRemoteTextureWrappers.empty()) { 64 mPendingRemoteTextureWrappers.clear(); 65 } 66 67 if (mCurrentTextureHost && 68 mCurrentTextureHost->AsRemoteTextureHostWrapper()) { 69 mCurrentTextureHost = nullptr; 70 } 71 72 nsTArray<TimedImage> newImages; 73 74 for (uint32_t i = 0; i < aTextures.Length(); ++i) { 75 const TimedTexture& t = aTextures[i]; 76 MOZ_ASSERT(t.mTexture); 77 if (i + 1 < aTextures.Length() && t.mProducerID == mLastProducerID && 78 t.mFrameID < mLastFrameID) { 79 // Ignore frames before a frame that we already composited. We don't 80 // ever want to display these frames. This could be important if 81 // the frame producer adjusts timestamps (e.g. to track the audio clock) 82 // and the new frame times are earlier. 83 continue; 84 } 85 TimedImage& img = *newImages.AppendElement(); 86 img.mTextureHost = t.mTexture; 87 img.mTimeStamp = t.mTimeStamp; 88 img.mPictureRect = t.mPictureRect; 89 img.mFrameID = t.mFrameID; 90 img.mProducerID = t.mProducerID; 91 img.mTextureHost->SetCropRect(img.mPictureRect); 92 } 93 94 SetImages(std::move(newImages)); 95 96 if (GetAsyncRef()) { 97 for (const auto& it : mWrBridges) { 98 RefPtr<WebRenderBridgeParent> wrBridge = it.second->WrBridge(); 99 if (wrBridge && wrBridge->CompositorScheduler()) { 100 wrBridge->CompositorScheduler()->ScheduleComposition( 101 wr::RenderReasons::ASYNC_IMAGE); 102 } 103 } 104 } 105 106 // Video producers generally send replacement images with the same frameID but 107 // slightly different timestamps in order to sync with the audio clock. This 108 // means that any CompositeUntil() call we made in Composite() may no longer 109 // guarantee that we'll composite until the next frame is ready. Fix that 110 // here. 111 if (mLastFrameID >= 0 && !mWrBridges.empty()) { 112 for (const auto& img : Images()) { 113 bool frameComesAfter = 114 img.mFrameID > mLastFrameID || img.mProducerID != mLastProducerID; 115 if (frameComesAfter && !img.mTimeStamp.IsNull()) { 116 for (const auto& it : mWrBridges) { 117 RefPtr<WebRenderBridgeParent> wrBridge = it.second->WrBridge(); 118 if (wrBridge) { 119 wrBridge->AsyncImageManager()->CompositeUntil( 120 img.mTimeStamp + TimeDuration::FromMilliseconds(BIAS_TIME_MS)); 121 } 122 } 123 break; 124 } 125 } 126 } 127 } 128 129 void WebRenderImageHost::PushPendingRemoteTexture( 130 const RemoteTextureId aTextureId, const RemoteTextureOwnerId aOwnerId, 131 const base::ProcessId aForPid, const gfx::IntSize aSize, 132 const TextureFlags aFlags) { 133 // Ensure aOwnerId is the same as RemoteTextureOwnerId of pending 134 // RemoteTextures. 135 if (!mPendingRemoteTextureWrappers.empty()) { 136 auto* wrapper = 137 mPendingRemoteTextureWrappers.front()->AsRemoteTextureHostWrapper(); 138 MOZ_ASSERT(wrapper); 139 if (wrapper->mOwnerId != aOwnerId || wrapper->mForPid != aForPid) { 140 // Clear when RemoteTextureOwner is different. 141 mPendingRemoteTextureWrappers.clear(); 142 mWaitingReadyCallback = false; 143 mWaitForRemoteTextureOwner = true; 144 } 145 } 146 147 // Check if waiting for remote texture owner is allowed. 148 if (!(aFlags & TextureFlags::WAIT_FOR_REMOTE_TEXTURE_OWNER)) { 149 mWaitForRemoteTextureOwner = false; 150 } 151 152 RefPtr<TextureHost> texture = 153 RemoteTextureMap::Get()->GetOrCreateRemoteTextureHostWrapper( 154 aTextureId, aOwnerId, aForPid, aSize, aFlags); 155 MOZ_ASSERT(texture); 156 mPendingRemoteTextureWrappers.push_back( 157 CompositableTextureHostRef(texture.get())); 158 } 159 160 void WebRenderImageHost::UseRemoteTexture() { 161 if (mPendingRemoteTextureWrappers.empty()) { 162 return; 163 } 164 165 const bool useReadyCallback = bool(GetAsyncRef()); 166 CompositableTextureHostRef texture; 167 168 if (useReadyCallback) { 169 if (mWaitingReadyCallback) { 170 return; 171 } 172 MOZ_ASSERT(!mWaitingReadyCallback); 173 174 auto readyCallback = [self = RefPtr<WebRenderImageHost>(this)]( 175 const RemoteTextureInfo aInfo) { 176 RefPtr<nsIRunnable> runnable = NS_NewRunnableFunction( 177 "WebRenderImageHost::UseRemoteTexture", 178 [self = std::move(self), aInfo]() { 179 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); 180 181 if (self->mPendingRemoteTextureWrappers.empty()) { 182 return; 183 } 184 185 auto* wrapper = self->mPendingRemoteTextureWrappers.front() 186 ->AsRemoteTextureHostWrapper(); 187 MOZ_ASSERT(wrapper); 188 if (wrapper->mOwnerId != aInfo.mOwnerId || 189 wrapper->mForPid != aInfo.mForPid) { 190 // obsoleted callback 191 return; 192 } 193 194 self->mWaitingReadyCallback = false; 195 self->UseRemoteTexture(); 196 }); 197 198 CompositorThread()->Dispatch(runnable.forget()); 199 }; 200 201 // Check which of the pending remote textures is the most recent and ready. 202 while (!mPendingRemoteTextureWrappers.empty()) { 203 auto* wrapper = 204 mPendingRemoteTextureWrappers.front()->AsRemoteTextureHostWrapper(); 205 206 if (mWaitForRemoteTextureOwner) { 207 // XXX remove sync wait 208 RemoteTextureMap::Get()->WaitForRemoteTextureOwner(wrapper); 209 } 210 mWaitingReadyCallback = !RemoteTextureMap::Get()->CheckRemoteTextureReady( 211 wrapper->GetRemoteTextureInfo(), readyCallback); 212 if (mWaitingReadyCallback) { 213 break; 214 } 215 RemoteTextureMap::Get()->GetRemoteTexture(wrapper); 216 texture = mPendingRemoteTextureWrappers.front(); 217 mPendingRemoteTextureWrappers.pop_front(); 218 } 219 } else { 220 texture = mPendingRemoteTextureWrappers.front(); 221 auto* wrapper = texture->AsRemoteTextureHostWrapper(); 222 mPendingRemoteTextureWrappers.pop_front(); 223 MOZ_ASSERT(mPendingRemoteTextureWrappers.empty()); 224 225 if (mWaitForRemoteTextureOwner) { 226 if (StaticPrefs::gfx_remote_texture_wait_owner_at_image_host()) { 227 RemoteTextureMap::Get()->WaitForRemoteTextureOwner(wrapper); 228 } else { 229 wrapper->EnableWaitForRemoteTextureOwner(true); 230 } 231 } 232 mWaitForRemoteTextureOwner = false; 233 } 234 235 if (!texture || 236 (GetAsyncRef() && 237 !texture->AsRemoteTextureHostWrapper()->IsReadyForRendering())) { 238 return; 239 } 240 241 SetCurrentTextureHost(texture); 242 243 if (GetAsyncRef()) { 244 for (const auto& it : mWrBridges) { 245 RefPtr<WebRenderBridgeParent> wrBridge = it.second->WrBridge(); 246 if (wrBridge && wrBridge->CompositorScheduler()) { 247 wrBridge->CompositorScheduler()->ScheduleComposition( 248 wr::RenderReasons::ASYNC_IMAGE); 249 } 250 } 251 } 252 } 253 254 void WebRenderImageHost::CleanupResources() { 255 ImageComposite::ClearImages(); 256 SetCurrentTextureHost(nullptr); 257 } 258 259 void WebRenderImageHost::RemoveTextureHost(TextureHost* aTexture) { 260 RemoveImagesWithTextureHost(aTexture); 261 } 262 263 void WebRenderImageHost::ClearImages(ClearImagesType aType) { 264 ImageComposite::ClearImages(); 265 if (aType == ClearImagesType::All) { 266 if (!mPendingRemoteTextureWrappers.empty()) { 267 mPendingRemoteTextureWrappers.clear(); 268 } 269 SetCurrentTextureHost(nullptr); 270 271 if (GetAsyncRef()) { 272 for (const auto& it : mWrBridges) { 273 RefPtr<WebRenderBridgeParent> wrBridge = it.second->WrBridge(); 274 if (wrBridge && wrBridge->CompositorScheduler()) { 275 wrBridge->CompositorScheduler()->ScheduleComposition( 276 wr::RenderReasons::ASYNC_IMAGE); 277 } 278 } 279 } 280 } 281 } 282 283 TimeStamp WebRenderImageHost::GetCompositionTime() const { 284 TimeStamp time; 285 286 MOZ_ASSERT(mCurrentAsyncImageManager); 287 if (mCurrentAsyncImageManager) { 288 time = mCurrentAsyncImageManager->GetCompositionTime(); 289 } 290 return time; 291 } 292 293 CompositionOpportunityId WebRenderImageHost::GetCompositionOpportunityId() 294 const { 295 CompositionOpportunityId id; 296 297 MOZ_ASSERT(mCurrentAsyncImageManager); 298 if (mCurrentAsyncImageManager) { 299 id = mCurrentAsyncImageManager->GetCompositionOpportunityId(); 300 } 301 return id; 302 } 303 304 void WebRenderImageHost::AppendImageCompositeNotification( 305 const ImageCompositeNotificationInfo& aInfo) const { 306 if (mCurrentAsyncImageManager) { 307 mCurrentAsyncImageManager->AppendImageCompositeNotification(aInfo); 308 } 309 } 310 311 TextureHost* WebRenderImageHost::GetAsTextureHostForComposite( 312 AsyncImagePipelineManager* aAsyncImageManager) { 313 MOZ_ASSERT(aAsyncImageManager); 314 315 if (mCurrentTextureHost && 316 mCurrentTextureHost->AsRemoteTextureHostWrapper()) { 317 return mCurrentTextureHost; 318 } 319 320 mCurrentAsyncImageManager = aAsyncImageManager; 321 const auto onExit = 322 mozilla::MakeScopeExit([&]() { mCurrentAsyncImageManager = nullptr; }); 323 324 int imageIndex = ChooseImageIndex(); 325 if (imageIndex < 0) { 326 SetCurrentTextureHost(nullptr); 327 return nullptr; 328 } 329 330 if (uint32_t(imageIndex) + 1 < ImagesCount()) { 331 mCurrentAsyncImageManager->CompositeUntil( 332 GetImage(imageIndex + 1)->mTimeStamp + 333 TimeDuration::FromMilliseconds(BIAS_TIME_MS)); 334 } 335 336 const TimedImage* img = GetImage(imageIndex); 337 338 RefPtr<TextureHost> texture = img->mTextureHost.get(); 339 #if XP_WIN 340 // Convert YUV BufferTextureHost to TextureHostWrapperD3D11 if possible 341 if (texture->AsBufferTextureHost()) { 342 auto identifier = aAsyncImageManager->GetTextureFactoryIdentifier(); 343 const bool tryConvertToNV12 = 344 StaticPrefs::gfx_video_convert_yuv_to_nv12_image_host_win() && 345 identifier.mSupportsD3D11NV12 && 346 KnowsCompositor::SupportsD3D11(identifier) && 347 texture->GetFormat() == gfx::SurfaceFormat::YUV420; 348 if (tryConvertToNV12) { 349 PROFILER_MARKER_TEXT("WebRenderImageHost", GRAPHICS, {}, 350 "Try ConvertToNV12"_ns); 351 352 if (!mTextureAllocator) { 353 mTextureAllocator = new TextureWrapperD3D11Allocator(); 354 } 355 RefPtr<TextureHost> textureWrapper = 356 TextureHostWrapperD3D11::CreateFromBufferTexture(mTextureAllocator, 357 texture); 358 if (textureWrapper) { 359 texture = textureWrapper; 360 } 361 } else if (profiler_thread_is_being_profiled_for_markers() && 362 StaticPrefs::gfx_video_convert_yuv_to_nv12_image_host_win() && 363 texture->GetFormat() == gfx::SurfaceFormat::YUV420) { 364 nsPrintfCString str("No ConvertToNV12 D3D11 %d NV12 %d", 365 KnowsCompositor::SupportsD3D11(identifier), 366 identifier.mSupportsD3D11NV12); 367 PROFILER_MARKER_TEXT("WebRenderImageHost", GRAPHICS, {}, str); 368 } 369 } 370 #endif 371 SetCurrentTextureHost(texture); 372 373 if (mCurrentAsyncImageManager->GetCompositionTime()) { 374 // We are in a composition. Send ImageCompositeNotifications. 375 OnFinishRendering(imageIndex, img, mAsyncRef.mProcessId, mAsyncRef.mHandle); 376 } 377 378 return mCurrentTextureHost; 379 } 380 381 void WebRenderImageHost::SetCurrentTextureHost(TextureHost* aTexture) { 382 if (aTexture == mCurrentTextureHost.get()) { 383 return; 384 } 385 mCurrentTextureHost = aTexture; 386 } 387 388 void WebRenderImageHost::Dump(std::stringstream& aStream, const char* aPrefix, 389 bool aDumpHtml) { 390 for (const auto& img : Images()) { 391 aStream << aPrefix; 392 aStream << (aDumpHtml ? "<ul><li>TextureHost: " : "TextureHost: "); 393 DumpTextureHost(aStream, img.mTextureHost); 394 aStream << (aDumpHtml ? " </li></ul> " : " "); 395 } 396 } 397 398 void WebRenderImageHost::SetWrBridge(const wr::PipelineId& aPipelineId, 399 WebRenderBridgeParent* aWrBridge) { 400 MOZ_ASSERT(aWrBridge); 401 MOZ_ASSERT(!mCurrentAsyncImageManager); 402 #ifdef DEBUG 403 const auto it = mWrBridges.find(wr::AsUint64(aPipelineId)); 404 MOZ_ASSERT(it == mWrBridges.end()); 405 #endif 406 RefPtr<WebRenderBridgeParentRef> ref = 407 aWrBridge->GetWebRenderBridgeParentRef(); 408 mWrBridges.emplace(wr::AsUint64(aPipelineId), ref); 409 } 410 411 void WebRenderImageHost::ClearWrBridge(const wr::PipelineId& aPipelineId, 412 WebRenderBridgeParent* aWrBridge) { 413 MOZ_ASSERT(aWrBridge); 414 MOZ_ASSERT(!mCurrentAsyncImageManager); 415 416 const auto it = mWrBridges.find(wr::AsUint64(aPipelineId)); 417 MOZ_ASSERT(it != mWrBridges.end()); 418 if (it == mWrBridges.end()) { 419 gfxCriticalNote << "WrBridge mismatch happened"; 420 return; 421 } 422 mWrBridges.erase(it); 423 SetCurrentTextureHost(nullptr); 424 } 425 426 } // namespace layers 427 } // namespace mozilla