Bug 1890689 accumulate input in LargerReceiverBlockSizeThanDesiredBuffering GTest...
[gecko.git] / widget / windows / RemoteBackbuffer.cpp
blob2d53570c3ea9d6fb868f4885db4c917c56f4b19b
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "RemoteBackbuffer.h"
7 #include "GeckoProfiler.h"
8 #include "nsThreadUtils.h"
9 #include "mozilla/Span.h"
10 #include "mozilla/gfx/Point.h"
11 #include "WinUtils.h"
12 #include <algorithm>
13 #include <type_traits>
15 namespace mozilla {
16 namespace widget {
17 namespace remote_backbuffer {
19 // This number can be adjusted as a time-memory tradeoff
20 constexpr uint8_t kMaxDirtyRects = 8;
22 struct IpcSafeRect {
23 explicit IpcSafeRect(const gfx::IntRect& aRect)
24 : x(aRect.x), y(aRect.y), width(aRect.width), height(aRect.height) {}
25 int32_t x;
26 int32_t y;
27 int32_t width;
28 int32_t height;
31 enum class ResponseResult {
32 Unknown,
33 Error,
34 BorrowSuccess,
35 BorrowSameBuffer,
36 PresentSuccess
39 enum class SharedDataType {
40 BorrowRequest,
41 BorrowRequestAllowSameBuffer,
42 BorrowResponse,
43 PresentRequest,
44 PresentResponse
47 struct BorrowResponseData {
48 ResponseResult result;
49 int32_t width;
50 int32_t height;
51 HANDLE fileMapping;
54 struct PresentRequestData {
55 uint8_t lenDirtyRects;
56 IpcSafeRect dirtyRects[kMaxDirtyRects];
59 struct PresentResponseData {
60 ResponseResult result;
63 struct SharedData {
64 SharedDataType dataType;
65 union {
66 BorrowResponseData borrowResponse;
67 PresentRequestData presentRequest;
68 PresentResponseData presentResponse;
69 } data;
72 static_assert(std::is_trivially_copyable<SharedData>::value &&
73 std::is_standard_layout<SharedData>::value,
74 "SharedData must be safe to pass over IPC boundaries");
76 class SharedImage {
77 public:
78 SharedImage()
79 : mWidth(0), mHeight(0), mFileMapping(nullptr), mPixelData(nullptr) {}
81 ~SharedImage() {
82 if (mPixelData) {
83 MOZ_ALWAYS_TRUE(::UnmapViewOfFile(mPixelData));
86 if (mFileMapping) {
87 MOZ_ALWAYS_TRUE(::CloseHandle(mFileMapping));
91 bool Initialize(int32_t aWidth, int32_t aHeight) {
92 MOZ_ASSERT(aWidth);
93 MOZ_ASSERT(aHeight);
94 MOZ_ASSERT(aWidth > 0);
95 MOZ_ASSERT(aHeight > 0);
97 mWidth = aWidth;
98 mHeight = aHeight;
100 DWORD bufferSize = static_cast<DWORD>(mHeight * GetStride());
102 mFileMapping = ::CreateFileMappingW(
103 INVALID_HANDLE_VALUE, nullptr /*secattr*/, PAGE_READWRITE,
104 0 /*sizeHigh*/, bufferSize, nullptr /*name*/);
105 if (!mFileMapping) {
106 return false;
109 void* mappedFilePtr =
110 ::MapViewOfFile(mFileMapping, FILE_MAP_ALL_ACCESS, 0 /*offsetHigh*/,
111 0 /*offsetLow*/, 0 /*bytesToMap*/);
112 if (!mappedFilePtr) {
113 return false;
116 mPixelData = reinterpret_cast<unsigned char*>(mappedFilePtr);
118 return true;
121 bool InitializeRemote(int32_t aWidth, int32_t aHeight, HANDLE aFileMapping) {
122 MOZ_ASSERT(aWidth > 0);
123 MOZ_ASSERT(aHeight > 0);
124 MOZ_ASSERT(aFileMapping);
126 mWidth = aWidth;
127 mHeight = aHeight;
128 mFileMapping = aFileMapping;
130 void* mappedFilePtr =
131 ::MapViewOfFile(mFileMapping, FILE_MAP_ALL_ACCESS, 0 /*offsetHigh*/,
132 0 /*offsetLow*/, 0 /*bytesToMap*/);
133 if (!mappedFilePtr) {
134 return false;
137 mPixelData = reinterpret_cast<unsigned char*>(mappedFilePtr);
139 return true;
142 HBITMAP CreateDIBSection() {
143 BITMAPINFO bitmapInfo = {};
144 bitmapInfo.bmiHeader.biSize = sizeof(bitmapInfo.bmiHeader);
145 bitmapInfo.bmiHeader.biWidth = mWidth;
146 bitmapInfo.bmiHeader.biHeight = -mHeight;
147 bitmapInfo.bmiHeader.biPlanes = 1;
148 bitmapInfo.bmiHeader.biBitCount = 32;
149 bitmapInfo.bmiHeader.biCompression = BI_RGB;
150 void* dummy = nullptr;
151 return ::CreateDIBSection(nullptr /*paletteDC*/, &bitmapInfo,
152 DIB_RGB_COLORS, &dummy, mFileMapping,
153 0 /*offset*/);
156 HANDLE CreateRemoteFileMapping(HANDLE aTargetProcess) {
157 MOZ_ASSERT(aTargetProcess);
159 HANDLE fileMapping = nullptr;
160 if (!::DuplicateHandle(GetCurrentProcess(), mFileMapping, aTargetProcess,
161 &fileMapping, 0 /*desiredAccess*/,
162 FALSE /*inheritHandle*/, DUPLICATE_SAME_ACCESS)) {
163 return nullptr;
165 return fileMapping;
168 already_AddRefed<gfx::DrawTarget> CreateDrawTarget() {
169 return gfx::Factory::CreateDrawTargetForData(
170 gfx::BackendType::CAIRO, mPixelData, gfx::IntSize(mWidth, mHeight),
171 GetStride(), gfx::SurfaceFormat::B8G8R8A8);
174 void CopyPixelsFrom(const SharedImage& other) {
175 const unsigned char* src = other.mPixelData;
176 unsigned char* dst = mPixelData;
178 int32_t width = std::min(mWidth, other.mWidth);
179 int32_t height = std::min(mHeight, other.mHeight);
181 for (int32_t row = 0; row < height; ++row) {
182 memcpy(dst, src, static_cast<uint32_t>(width * kBytesPerPixel));
183 src += other.GetStride();
184 dst += GetStride();
188 int32_t GetWidth() const { return mWidth; }
190 int32_t GetHeight() const { return mHeight; }
192 SharedImage(const SharedImage&) = delete;
193 SharedImage(SharedImage&&) = delete;
194 SharedImage& operator=(const SharedImage&) = delete;
195 SharedImage& operator=(SharedImage&&) = delete;
197 private:
198 static constexpr int32_t kBytesPerPixel = 4;
200 int32_t GetStride() const {
201 // DIB requires 32-bit row alignment
202 return (((mWidth * kBytesPerPixel) + 3) / 4) * 4;
205 int32_t mWidth;
206 int32_t mHeight;
207 HANDLE mFileMapping;
208 unsigned char* mPixelData;
211 class PresentableSharedImage {
212 public:
213 PresentableSharedImage()
214 : mSharedImage(),
215 mDeviceContext(nullptr),
216 mDIBSection(nullptr),
217 mSavedObject(nullptr) {}
219 ~PresentableSharedImage() {
220 if (mSavedObject) {
221 MOZ_ALWAYS_TRUE(::SelectObject(mDeviceContext, mSavedObject));
224 if (mDIBSection) {
225 MOZ_ALWAYS_TRUE(::DeleteObject(mDIBSection));
228 if (mDeviceContext) {
229 MOZ_ALWAYS_TRUE(::DeleteDC(mDeviceContext));
233 bool Initialize(int32_t aWidth, int32_t aHeight) {
234 if (!mSharedImage.Initialize(aWidth, aHeight)) {
235 return false;
238 mDeviceContext = ::CreateCompatibleDC(nullptr);
239 if (!mDeviceContext) {
240 return false;
243 mDIBSection = mSharedImage.CreateDIBSection();
244 if (!mDIBSection) {
245 return false;
248 mSavedObject = ::SelectObject(mDeviceContext, mDIBSection);
249 if (!mSavedObject) {
250 return false;
253 return true;
256 bool PresentToWindow(HWND aWindowHandle, TransparencyMode aTransparencyMode,
257 Span<const IpcSafeRect> aDirtyRects) {
258 if (aTransparencyMode == TransparencyMode::Transparent) {
259 // If our window is a child window or a child-of-a-child, the window
260 // that needs to be updated is the top level ancestor of the tree
261 HWND topLevelWindow = WinUtils::GetTopLevelHWND(aWindowHandle, true);
262 MOZ_ASSERT(::GetWindowLongPtr(topLevelWindow, GWL_EXSTYLE) &
263 WS_EX_LAYERED);
265 BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
266 POINT srcPos = {0, 0};
267 RECT clientRect = {};
268 if (!::GetClientRect(aWindowHandle, &clientRect)) {
269 return false;
271 MOZ_ASSERT(clientRect.left == 0);
272 MOZ_ASSERT(clientRect.top == 0);
273 int32_t width = clientRect.right;
274 int32_t height = clientRect.bottom;
275 SIZE winSize = {width, height};
276 // Window resize could cause the client area to be different than
277 // mSharedImage's size. If the client area doesn't match,
278 // PresentToWindow() returns false without calling UpdateLayeredWindow().
279 // Another call to UpdateLayeredWindow() will follow shortly, since the
280 // resize will eventually force the backbuffer to repaint itself again.
281 // When client area is larger than mSharedImage's size,
282 // UpdateLayeredWindow() draws the window completely invisible. But it
283 // does not return false.
284 if (width != mSharedImage.GetWidth() ||
285 height != mSharedImage.GetHeight()) {
286 return false;
289 return !!::UpdateLayeredWindow(
290 topLevelWindow, nullptr /*paletteDC*/, nullptr /*newPos*/, &winSize,
291 mDeviceContext, &srcPos, 0 /*colorKey*/, &bf, ULW_ALPHA);
294 gfx::IntRect sharedImageRect{0, 0, mSharedImage.GetWidth(),
295 mSharedImage.GetHeight()};
297 bool result = true;
299 HDC windowDC = ::GetDC(aWindowHandle);
300 if (!windowDC) {
301 return false;
304 for (auto& ipcDirtyRect : aDirtyRects) {
305 gfx::IntRect dirtyRect{ipcDirtyRect.x, ipcDirtyRect.y, ipcDirtyRect.width,
306 ipcDirtyRect.height};
307 gfx::IntRect bltRect = dirtyRect.Intersect(sharedImageRect);
309 if (!::BitBlt(windowDC, bltRect.x /*dstX*/, bltRect.y /*dstY*/,
310 bltRect.width, bltRect.height, mDeviceContext,
311 bltRect.x /*srcX*/, bltRect.y /*srcY*/, SRCCOPY)) {
312 result = false;
313 break;
317 MOZ_ALWAYS_TRUE(::ReleaseDC(aWindowHandle, windowDC));
319 return result;
322 HANDLE CreateRemoteFileMapping(HANDLE aTargetProcess) {
323 return mSharedImage.CreateRemoteFileMapping(aTargetProcess);
326 already_AddRefed<gfx::DrawTarget> CreateDrawTarget() {
327 return mSharedImage.CreateDrawTarget();
330 void CopyPixelsFrom(const PresentableSharedImage& other) {
331 mSharedImage.CopyPixelsFrom(other.mSharedImage);
334 int32_t GetWidth() { return mSharedImage.GetWidth(); }
336 int32_t GetHeight() { return mSharedImage.GetHeight(); }
338 PresentableSharedImage(const PresentableSharedImage&) = delete;
339 PresentableSharedImage(PresentableSharedImage&&) = delete;
340 PresentableSharedImage& operator=(const PresentableSharedImage&) = delete;
341 PresentableSharedImage& operator=(PresentableSharedImage&&) = delete;
343 private:
344 SharedImage mSharedImage;
345 HDC mDeviceContext;
346 HBITMAP mDIBSection;
347 HGDIOBJ mSavedObject;
350 Provider::Provider()
351 : mWindowHandle(nullptr),
352 mTargetProcess(nullptr),
353 mFileMapping(nullptr),
354 mRequestReadyEvent(nullptr),
355 mResponseReadyEvent(nullptr),
356 mSharedDataPtr(nullptr),
357 mStopServiceThread(false),
358 mServiceThread(nullptr),
359 mBackbuffer() {}
361 Provider::~Provider() {
362 mBackbuffer.reset();
364 if (mServiceThread) {
365 mStopServiceThread = true;
366 MOZ_ALWAYS_TRUE(::SetEvent(mRequestReadyEvent));
367 MOZ_ALWAYS_TRUE(PR_JoinThread(mServiceThread) == PR_SUCCESS);
370 if (mSharedDataPtr) {
371 MOZ_ALWAYS_TRUE(::UnmapViewOfFile(mSharedDataPtr));
374 if (mResponseReadyEvent) {
375 MOZ_ALWAYS_TRUE(::CloseHandle(mResponseReadyEvent));
378 if (mRequestReadyEvent) {
379 MOZ_ALWAYS_TRUE(::CloseHandle(mRequestReadyEvent));
382 if (mFileMapping) {
383 MOZ_ALWAYS_TRUE(::CloseHandle(mFileMapping));
386 if (mTargetProcess) {
387 MOZ_ALWAYS_TRUE(::CloseHandle(mTargetProcess));
391 bool Provider::Initialize(HWND aWindowHandle, DWORD aTargetProcessId,
392 TransparencyMode aTransparencyMode) {
393 MOZ_ASSERT(aWindowHandle);
394 MOZ_ASSERT(aTargetProcessId);
396 mWindowHandle = aWindowHandle;
398 mTargetProcess = ::OpenProcess(PROCESS_DUP_HANDLE, FALSE /*inheritHandle*/,
399 aTargetProcessId);
400 if (!mTargetProcess) {
401 return false;
404 mFileMapping = ::CreateFileMappingW(
405 INVALID_HANDLE_VALUE, nullptr /*secattr*/, PAGE_READWRITE, 0 /*sizeHigh*/,
406 static_cast<DWORD>(sizeof(SharedData)), nullptr /*name*/);
407 if (!mFileMapping) {
408 return false;
411 mRequestReadyEvent =
412 ::CreateEventW(nullptr /*secattr*/, FALSE /*manualReset*/,
413 FALSE /*initialState*/, nullptr /*name*/);
414 if (!mRequestReadyEvent) {
415 return false;
418 mResponseReadyEvent =
419 ::CreateEventW(nullptr /*secattr*/, FALSE /*manualReset*/,
420 FALSE /*initialState*/, nullptr /*name*/);
421 if (!mResponseReadyEvent) {
422 return false;
425 void* mappedFilePtr =
426 ::MapViewOfFile(mFileMapping, FILE_MAP_ALL_ACCESS, 0 /*offsetHigh*/,
427 0 /*offsetLow*/, 0 /*bytesToMap*/);
428 if (!mappedFilePtr) {
429 return false;
432 mSharedDataPtr = reinterpret_cast<SharedData*>(mappedFilePtr);
434 mStopServiceThread = false;
436 // Use a raw NSPR OS-level thread here instead of nsThread because we are
437 // performing low-level synchronization across processes using Win32 Events,
438 // and nsThread is designed around an incompatible "in-process task queue"
439 // model
440 mServiceThread = PR_CreateThread(
441 PR_USER_THREAD, [](void* p) { static_cast<Provider*>(p)->ThreadMain(); },
442 this, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD,
443 0 /*default stack size*/);
444 if (!mServiceThread) {
445 return false;
448 mTransparencyMode = uint32_t(aTransparencyMode);
450 return true;
453 Maybe<RemoteBackbufferHandles> Provider::CreateRemoteHandles() {
454 return Some(
455 RemoteBackbufferHandles(ipc::FileDescriptor(mFileMapping),
456 ipc::FileDescriptor(mRequestReadyEvent),
457 ipc::FileDescriptor(mResponseReadyEvent)));
460 void Provider::UpdateTransparencyMode(TransparencyMode aTransparencyMode) {
461 mTransparencyMode = uint32_t(aTransparencyMode);
464 void Provider::ThreadMain() {
465 AUTO_PROFILER_REGISTER_THREAD("RemoteBackbuffer");
466 NS_SetCurrentThreadName("RemoteBackbuffer");
468 while (true) {
470 AUTO_PROFILER_THREAD_SLEEP;
471 MOZ_ALWAYS_TRUE(::WaitForSingleObject(mRequestReadyEvent, INFINITE) ==
472 WAIT_OBJECT_0);
475 if (mStopServiceThread) {
476 break;
479 switch (mSharedDataPtr->dataType) {
480 case SharedDataType::BorrowRequest:
481 case SharedDataType::BorrowRequestAllowSameBuffer: {
482 BorrowResponseData responseData = {};
484 HandleBorrowRequest(&responseData,
485 mSharedDataPtr->dataType ==
486 SharedDataType::BorrowRequestAllowSameBuffer);
488 mSharedDataPtr->dataType = SharedDataType::BorrowResponse;
489 mSharedDataPtr->data.borrowResponse = responseData;
491 MOZ_ALWAYS_TRUE(::SetEvent(mResponseReadyEvent));
493 break;
495 case SharedDataType::PresentRequest: {
496 PresentRequestData requestData = mSharedDataPtr->data.presentRequest;
497 PresentResponseData responseData = {};
499 HandlePresentRequest(requestData, &responseData);
501 mSharedDataPtr->dataType = SharedDataType::PresentResponse;
502 mSharedDataPtr->data.presentResponse = responseData;
504 MOZ_ALWAYS_TRUE(::SetEvent(mResponseReadyEvent));
506 break;
508 default:
509 break;
514 void Provider::HandleBorrowRequest(BorrowResponseData* aResponseData,
515 bool aAllowSameBuffer) {
516 MOZ_ASSERT(aResponseData);
518 aResponseData->result = ResponseResult::Error;
520 RECT clientRect{};
521 if (!::GetClientRect(mWindowHandle, &clientRect)) {
522 return;
525 MOZ_ASSERT(clientRect.left == 0);
526 MOZ_ASSERT(clientRect.top == 0);
528 const int32_t width = std::max(int32_t(clientRect.right), 1);
529 const int32_t height = std::max(int32_t(clientRect.bottom), 1);
531 bool needNewBackbuffer = !aAllowSameBuffer || !mBackbuffer ||
532 mBackbuffer->GetWidth() != width ||
533 mBackbuffer->GetHeight() != height;
535 if (!needNewBackbuffer) {
536 aResponseData->result = ResponseResult::BorrowSameBuffer;
537 return;
540 auto newBackbuffer = std::make_unique<PresentableSharedImage>();
541 if (!newBackbuffer->Initialize(width, height)) {
542 return;
545 // Preserve the contents of the old backbuffer (if it exists)
546 if (mBackbuffer) {
547 newBackbuffer->CopyPixelsFrom(*mBackbuffer);
548 mBackbuffer.reset();
551 HANDLE remoteFileMapping =
552 newBackbuffer->CreateRemoteFileMapping(mTargetProcess);
553 if (!remoteFileMapping) {
554 return;
557 aResponseData->result = ResponseResult::BorrowSuccess;
558 aResponseData->width = width;
559 aResponseData->height = height;
560 aResponseData->fileMapping = remoteFileMapping;
562 mBackbuffer = std::move(newBackbuffer);
565 void Provider::HandlePresentRequest(const PresentRequestData& aRequestData,
566 PresentResponseData* aResponseData) {
567 MOZ_ASSERT(aResponseData);
569 Span rectSpan(aRequestData.dirtyRects, kMaxDirtyRects);
571 aResponseData->result = ResponseResult::Error;
573 if (!mBackbuffer) {
574 return;
577 if (!mBackbuffer->PresentToWindow(
578 mWindowHandle, GetTransparencyMode(),
579 rectSpan.First(aRequestData.lenDirtyRects))) {
580 return;
583 aResponseData->result = ResponseResult::PresentSuccess;
586 Client::Client()
587 : mFileMapping(nullptr),
588 mRequestReadyEvent(nullptr),
589 mResponseReadyEvent(nullptr),
590 mSharedDataPtr(nullptr),
591 mBackbuffer() {}
593 Client::~Client() {
594 mBackbuffer.reset();
596 if (mSharedDataPtr) {
597 MOZ_ALWAYS_TRUE(::UnmapViewOfFile(mSharedDataPtr));
600 if (mResponseReadyEvent) {
601 MOZ_ALWAYS_TRUE(::CloseHandle(mResponseReadyEvent));
604 if (mRequestReadyEvent) {
605 MOZ_ALWAYS_TRUE(::CloseHandle(mRequestReadyEvent));
608 if (mFileMapping) {
609 MOZ_ALWAYS_TRUE(::CloseHandle(mFileMapping));
613 bool Client::Initialize(const RemoteBackbufferHandles& aRemoteHandles) {
614 MOZ_ASSERT(aRemoteHandles.fileMapping().IsValid());
615 MOZ_ASSERT(aRemoteHandles.requestReadyEvent().IsValid());
616 MOZ_ASSERT(aRemoteHandles.responseReadyEvent().IsValid());
618 // FIXME: Due to PCompositorWidget using virtual Recv methods,
619 // RemoteBackbufferHandles is passed by const reference, and cannot have its
620 // signature customized, meaning that we need to clone the handles here.
622 // Once PCompositorWidget is migrated to use direct call semantics, the
623 // signature can be changed to accept RemoteBackbufferHandles by rvalue
624 // reference or value, and the DuplicateHandle calls here can be avoided.
625 mFileMapping = aRemoteHandles.fileMapping().ClonePlatformHandle().release();
626 mRequestReadyEvent =
627 aRemoteHandles.requestReadyEvent().ClonePlatformHandle().release();
628 mResponseReadyEvent =
629 aRemoteHandles.responseReadyEvent().ClonePlatformHandle().release();
631 void* mappedFilePtr =
632 ::MapViewOfFile(mFileMapping, FILE_MAP_ALL_ACCESS, 0 /*offsetHigh*/,
633 0 /*offsetLow*/, 0 /*bytesToMap*/);
634 if (!mappedFilePtr) {
635 return false;
638 mSharedDataPtr = reinterpret_cast<SharedData*>(mappedFilePtr);
640 return true;
643 already_AddRefed<gfx::DrawTarget> Client::BorrowDrawTarget() {
644 mSharedDataPtr->dataType = mBackbuffer
645 ? SharedDataType::BorrowRequestAllowSameBuffer
646 : SharedDataType::BorrowRequest;
648 MOZ_ALWAYS_TRUE(::SetEvent(mRequestReadyEvent));
649 MOZ_ALWAYS_TRUE(::WaitForSingleObject(mResponseReadyEvent, INFINITE) ==
650 WAIT_OBJECT_0);
652 if (mSharedDataPtr->dataType != SharedDataType::BorrowResponse) {
653 return nullptr;
656 BorrowResponseData responseData = mSharedDataPtr->data.borrowResponse;
658 if ((responseData.result != ResponseResult::BorrowSameBuffer) &&
659 (responseData.result != ResponseResult::BorrowSuccess)) {
660 return nullptr;
663 if (responseData.result == ResponseResult::BorrowSuccess) {
664 mBackbuffer.reset();
666 auto newBackbuffer = std::make_unique<SharedImage>();
667 if (!newBackbuffer->InitializeRemote(responseData.width,
668 responseData.height,
669 responseData.fileMapping)) {
670 return nullptr;
673 mBackbuffer = std::move(newBackbuffer);
676 MOZ_ASSERT(mBackbuffer);
678 return mBackbuffer->CreateDrawTarget();
681 bool Client::PresentDrawTarget(gfx::IntRegion aDirtyRegion) {
682 mSharedDataPtr->dataType = SharedDataType::PresentRequest;
684 // Simplify the region until it has <= kMaxDirtyRects
685 aDirtyRegion.SimplifyOutward(kMaxDirtyRects);
687 Span rectSpan(mSharedDataPtr->data.presentRequest.dirtyRects, kMaxDirtyRects);
689 uint8_t rectIndex = 0;
690 for (auto iter = aDirtyRegion.RectIter(); !iter.Done(); iter.Next()) {
691 rectSpan[rectIndex] = IpcSafeRect(iter.Get());
692 ++rectIndex;
695 mSharedDataPtr->data.presentRequest.lenDirtyRects = rectIndex;
697 MOZ_ALWAYS_TRUE(::SetEvent(mRequestReadyEvent));
698 MOZ_ALWAYS_TRUE(::WaitForSingleObject(mResponseReadyEvent, INFINITE) ==
699 WAIT_OBJECT_0);
701 if (mSharedDataPtr->dataType != SharedDataType::PresentResponse) {
702 return false;
705 if (mSharedDataPtr->data.presentResponse.result !=
706 ResponseResult::PresentSuccess) {
707 return false;
710 return true;
713 } // namespace remote_backbuffer
714 } // namespace widget
715 } // namespace mozilla