|
20 | 20 | namespace |
21 | 21 | { |
22 | 22 | const int CEF_PIXEL_STRIDE = 4; |
23 | | - |
24 | | - // Threshold for switching from dirty rect updates to full frame copy |
25 | | - // If dirty area exceeds this fraction of total area, full copy is more efficient |
26 | | - constexpr float DIRTY_RECT_THRESHOLD = 0.25f; |
27 | | - |
28 | | - // Calculate total area covered by dirty rects (accounting for overlaps approximately) |
29 | | - size_t CalculateDirtyArea(const CefRenderHandler::RectList& dirtyRects, int frameWidth, int frameHeight) |
30 | | - { |
31 | | - if (dirtyRects.empty()) |
32 | | - return 0; |
33 | | - |
34 | | - // For a single rect, just return its area |
35 | | - if (dirtyRects.size() == 1) |
36 | | - { |
37 | | - const auto& rect = dirtyRects[0]; |
38 | | - return static_cast<size_t>(rect.width) * static_cast<size_t>(rect.height); |
39 | | - } |
40 | | - |
41 | | - // For multiple rects, calculate bounding box area as upper bound estimate |
42 | | - // This is faster than precise overlap calculation and gives good results |
43 | | - int minX = frameWidth, minY = frameHeight; |
44 | | - int maxX = 0, maxY = 0; |
45 | | - size_t totalRectArea = 0; |
46 | | - |
47 | | - for (const auto& rect : dirtyRects) |
48 | | - { |
49 | | - if (rect.width <= 0 || rect.height <= 0) |
50 | | - continue; |
51 | | - |
52 | | - minX = std::min(minX, rect.x); |
53 | | - minY = std::min(minY, rect.y); |
54 | | - maxX = std::max(maxX, rect.x + rect.width); |
55 | | - maxY = std::max(maxY, rect.y + rect.height); |
56 | | - totalRectArea += static_cast<size_t>(rect.width) * static_cast<size_t>(rect.height); |
57 | | - } |
58 | | - |
59 | | - // Use the smaller of: sum of rect areas, or bounding box area |
60 | | - // This gives a reasonable estimate without expensive overlap calculation |
61 | | - const size_t boundingArea = static_cast<size_t>(maxX - minX) * static_cast<size_t>(maxY - minY); |
62 | | - return std::min(totalRectArea, boundingArea); |
63 | | - } |
64 | | - |
65 | | - // Check if two rects are adjacent or overlapping (can be merged) |
66 | | - bool RectsAreAdjacent(const CefRect& a, const CefRect& b, int tolerance = 1) |
67 | | - { |
68 | | - // Check if rects overlap or touch within tolerance |
69 | | - return !(a.x + a.width + tolerance < b.x || |
70 | | - b.x + b.width + tolerance < a.x || |
71 | | - a.y + a.height + tolerance < b.y || |
72 | | - b.y + b.height + tolerance < a.y); |
73 | | - } |
74 | | - |
75 | | - // Merge two rects into their bounding box |
76 | | - CefRect MergeRects(const CefRect& a, const CefRect& b) |
77 | | - { |
78 | | - const int minX = std::min(a.x, b.x); |
79 | | - const int minY = std::min(a.y, b.y); |
80 | | - const int maxX = std::max(a.x + a.width, b.x + b.width); |
81 | | - const int maxY = std::max(a.y + a.height, b.y + b.height); |
82 | | - return CefRect(minX, minY, maxX - minX, maxY - minY); |
83 | | - } |
84 | | - |
85 | | - // Merge adjacent dirty rects to reduce number of copy operations |
86 | | - // Returns optimized list with fewer, larger rects |
87 | | - std::vector<CefRect> MergeAdjacentRects(const CefRenderHandler::RectList& dirtyRects) |
88 | | - { |
89 | | - if (dirtyRects.size() <= 1) |
90 | | - return std::vector<CefRect>(dirtyRects.begin(), dirtyRects.end()); |
91 | | - |
92 | | - std::vector<CefRect> merged(dirtyRects.begin(), dirtyRects.end()); |
93 | | - |
94 | | - // Simple greedy merge - keep merging until no more merges possible |
95 | | - // Limited iterations to prevent excessive processing |
96 | | - constexpr int maxIterations = 3; |
97 | | - for (int iter = 0; iter < maxIterations; ++iter) |
98 | | - { |
99 | | - bool mergedAny = false; |
100 | | - |
101 | | - for (size_t i = 0; i < merged.size() && merged.size() > 1; ++i) |
102 | | - { |
103 | | - for (size_t j = i + 1; j < merged.size(); ++j) |
104 | | - { |
105 | | - if (RectsAreAdjacent(merged[i], merged[j])) |
106 | | - { |
107 | | - merged[i] = MergeRects(merged[i], merged[j]); |
108 | | - merged.erase(merged.begin() + j); |
109 | | - mergedAny = true; |
110 | | - break; |
111 | | - } |
112 | | - } |
113 | | - if (mergedAny) |
114 | | - break; |
115 | | - } |
116 | | - |
117 | | - if (!mergedAny) |
118 | | - break; |
119 | | - } |
120 | | - |
121 | | - return merged; |
122 | | - } |
123 | 23 | } |
124 | 24 |
|
125 | 25 | CWebView::CWebView(bool bIsLocal, CWebBrowserItem* pWebBrowserRenderItem, bool bTransparent) |
@@ -389,10 +289,7 @@ void CWebView::SetRenderingPaused(bool bPaused) |
389 | 289 | m_RenderData.popupShown = false; |
390 | 290 | m_RenderData.buffer.reset(); |
391 | 291 | m_RenderData.bufferSize = 0; |
392 | | - m_RenderData.dirtyRects.clear(); |
393 | | - m_RenderData.dirtyRects.shrink_to_fit(); |
394 | 292 | m_RenderData.popupBuffer.reset(); |
395 | | - m_RenderData.needsFullCopy = true; // Force full copy when unpaused |
396 | 293 | } |
397 | 294 | } |
398 | 295 | } |
@@ -468,7 +365,20 @@ void CWebView::UpdateTexture() |
468 | 365 | if (m_RenderData.changed && (m_pWebBrowserRenderItem->m_uiSizeX != m_RenderData.width || m_pWebBrowserRenderItem->m_uiSizeY != m_RenderData.height)) |
469 | 366 | { |
470 | 367 | m_RenderData.changed = false; |
471 | | - m_RenderData.needsFullCopy = true; // Force full copy after size change |
| 368 | + } |
| 369 | + |
| 370 | + // After device reset (minimize/restore), force full copy from our buffer to new texture |
| 371 | + if (m_pWebBrowserRenderItem->m_bTextureWasRecreated) |
| 372 | + { |
| 373 | + m_pWebBrowserRenderItem->m_bTextureWasRecreated = false; |
| 374 | + |
| 375 | + // If we have valid buffer data matching texture size, trigger full update |
| 376 | + if (m_RenderData.buffer && m_RenderData.bufferSize > 0 && |
| 377 | + m_RenderData.width == static_cast<int>(m_pWebBrowserRenderItem->m_uiSizeX) && |
| 378 | + m_RenderData.height == static_cast<int>(m_pWebBrowserRenderItem->m_uiSizeY)) |
| 379 | + { |
| 380 | + m_RenderData.changed = true; |
| 381 | + } |
472 | 382 | } |
473 | 383 |
|
474 | 384 | if (m_RenderData.changed || m_RenderData.popupShown) [[likely]] |
@@ -516,109 +426,48 @@ void CWebView::UpdateTexture() |
516 | 426 | { |
517 | 427 | m_RenderData.changed = false; |
518 | 428 |
|
519 | | - const auto& dirtyRects = m_RenderData.dirtyRects; |
520 | | - const auto frameArea = static_cast<size_t>(m_RenderData.width) * static_cast<size_t>(m_RenderData.height); |
521 | | - |
522 | | - // Determine if we should do full frame copy or partial dirty rect update |
523 | | - // Always do full copy on first update (texture may have garbage data) |
524 | | - // Full copy is also more efficient when dirty area exceeds threshold |
525 | | - bool doFullCopy = m_RenderData.needsFullCopy || dirtyRects.empty() || |
526 | | - (dirtyRects.size() == 1 && dirtyRects[0].width == m_RenderData.width && dirtyRects[0].height == m_RenderData.height); |
527 | | - |
528 | | - if (!doFullCopy && frameArea > 0) |
| 429 | + // Always do full frame copy since D3DLOCK_DISCARD invalidates entire texture |
| 430 | + // Our buffer contains the complete frame from OnPaint's full memcpy |
| 431 | + if (destPitch == sourcePitch) [[likely]] |
529 | 432 | { |
530 | | - const auto dirtyArea = CalculateDirtyArea(dirtyRects, m_RenderData.width, m_RenderData.height); |
531 | | - doFullCopy = (static_cast<float>(dirtyArea) / static_cast<float>(frameArea)) > DIRTY_RECT_THRESHOLD; |
532 | | - } |
533 | | - |
534 | | - if (doFullCopy) |
535 | | - { |
536 | | - // Clear the needsFullCopy flag after we do a full copy |
537 | | - m_RenderData.needsFullCopy = false; |
538 | | - |
539 | | - // Full frame update - copy entire buffer |
540 | | - if (destPitch == sourcePitch) [[likely]] |
541 | | - { |
542 | | - if (m_RenderData.height > 0 && |
543 | | - static_cast<size_t>(m_RenderData.height) > SIZE_MAX / static_cast<size_t>(destPitch)) [[unlikely]] |
544 | | - { |
545 | | - pSurface->UnlockRect(); |
546 | | - m_RenderData.changed = false; |
547 | | - m_RenderData.popupShown = false; |
548 | | - return; |
549 | | - } |
550 | | - std::memcpy(destData, sourceData, static_cast<size_t>(destPitch) * static_cast<size_t>(m_RenderData.height)); |
551 | | - } |
552 | | - else |
| 433 | + if (m_RenderData.height > 0 && |
| 434 | + static_cast<size_t>(m_RenderData.height) > SIZE_MAX / static_cast<size_t>(destPitch)) [[unlikely]] |
553 | 435 | { |
554 | | - // Row-by-row copy when pitches differ |
555 | | - if (destPitch <= 0 || sourcePitch <= 0) [[unlikely]] |
556 | | - { |
557 | | - pSurface->UnlockRect(); |
558 | | - m_RenderData.changed = false; |
559 | | - m_RenderData.popupShown = false; |
560 | | - return; |
561 | | - } |
562 | | - |
563 | | - if (m_RenderData.height > 0 && |
564 | | - (static_cast<size_t>(m_RenderData.height) > SIZE_MAX / static_cast<size_t>(destPitch) || |
565 | | - static_cast<size_t>(m_RenderData.height) > SIZE_MAX / static_cast<size_t>(sourcePitch))) [[unlikely]] |
566 | | - { |
567 | | - pSurface->UnlockRect(); |
568 | | - m_RenderData.changed = false; |
569 | | - m_RenderData.popupShown = false; |
570 | | - return; |
571 | | - } |
572 | | - |
573 | | - for (int y = 0; y < m_RenderData.height; ++y) |
574 | | - { |
575 | | - const auto sourceIndex = static_cast<size_t>(y) * static_cast<size_t>(sourcePitch); |
576 | | - const auto destIndex = static_cast<size_t>(y) * static_cast<size_t>(destPitch); |
577 | | - const auto copySize = std::min(static_cast<size_t>(sourcePitch), static_cast<size_t>(destPitch)); |
578 | | - |
579 | | - std::memcpy(&destData[destIndex], &sourceData[sourceIndex], copySize); |
580 | | - } |
| 436 | + pSurface->UnlockRect(); |
| 437 | + m_RenderData.changed = false; |
| 438 | + m_RenderData.popupShown = false; |
| 439 | + return; |
581 | 440 | } |
| 441 | + std::memcpy(destData, sourceData, static_cast<size_t>(destPitch) * static_cast<size_t>(m_RenderData.height)); |
582 | 442 | } |
583 | 443 | else |
584 | 444 | { |
585 | | - // Partial update using optimized dirty rects |
586 | | - if (m_RenderData.height > 0 && |
587 | | - static_cast<size_t>(m_RenderData.height) > SIZE_MAX / static_cast<size_t>(destPitch)) [[unlikely]] |
| 445 | + // Row-by-row copy when pitches differ |
| 446 | + if (destPitch <= 0 || sourcePitch <= 0) [[unlikely]] |
588 | 447 | { |
589 | 448 | pSurface->UnlockRect(); |
590 | 449 | m_RenderData.changed = false; |
591 | 450 | m_RenderData.popupShown = false; |
592 | 451 | return; |
593 | 452 | } |
594 | 453 |
|
595 | | - // Merge adjacent rects to reduce number of copy operations |
596 | | - const auto mergedRects = MergeAdjacentRects(dirtyRects); |
597 | | - |
598 | | - for (const auto& rect : mergedRects) |
| 454 | + if (m_RenderData.height > 0 && |
| 455 | + (static_cast<size_t>(m_RenderData.height) > SIZE_MAX / static_cast<size_t>(destPitch) || |
| 456 | + static_cast<size_t>(m_RenderData.height) > SIZE_MAX / static_cast<size_t>(sourcePitch))) [[unlikely]] |
599 | 457 | { |
600 | | - if (rect.x < 0 || rect.y < 0 || rect.width <= 0 || rect.height <= 0) [[unlikely]] |
601 | | - continue; |
602 | | - |
603 | | - if (rect.x >= m_RenderData.width || rect.y >= m_RenderData.height || |
604 | | - rect.width > m_RenderData.width || rect.height > m_RenderData.height || |
605 | | - rect.x > m_RenderData.width - rect.width || rect.y > m_RenderData.height - rect.height) [[unlikely]] |
606 | | - continue; |
607 | | - |
608 | | - const auto rectEndY = rect.y + rect.height; |
609 | | - |
610 | | - if (static_cast<size_t>(destPitch) < static_cast<size_t>(rect.x + rect.width) * CEF_PIXEL_STRIDE) [[unlikely]] |
611 | | - continue; |
| 458 | + pSurface->UnlockRect(); |
| 459 | + m_RenderData.changed = false; |
| 460 | + m_RenderData.popupShown = false; |
| 461 | + return; |
| 462 | + } |
612 | 463 |
|
613 | | - for (int y = rect.y; y < rectEndY; ++y) |
614 | | - { |
615 | | - const auto sourceIndex = static_cast<size_t>(y) * static_cast<size_t>(sourcePitch) + |
616 | | - static_cast<size_t>(rect.x) * CEF_PIXEL_STRIDE; |
617 | | - const auto destIndex = static_cast<size_t>(y) * static_cast<size_t>(destPitch) + |
618 | | - static_cast<size_t>(rect.x) * CEF_PIXEL_STRIDE; |
| 464 | + for (int y = 0; y < m_RenderData.height; ++y) |
| 465 | + { |
| 466 | + const auto sourceIndex = static_cast<size_t>(y) * static_cast<size_t>(sourcePitch); |
| 467 | + const auto destIndex = static_cast<size_t>(y) * static_cast<size_t>(destPitch); |
| 468 | + const auto copySize = std::min(static_cast<size_t>(sourcePitch), static_cast<size_t>(destPitch)); |
619 | 469 |
|
620 | | - std::memcpy(&destData[destIndex], &sourceData[sourceIndex], static_cast<size_t>(rect.width) * CEF_PIXEL_STRIDE); |
621 | | - } |
| 470 | + std::memcpy(&destData[destIndex], &sourceData[sourceIndex], copySize); |
622 | 471 | } |
623 | 472 | } |
624 | 473 | } |
@@ -672,10 +521,6 @@ void CWebView::UpdateTexture() |
672 | 521 | m_RenderData.changed = false; |
673 | 522 | m_RenderData.popupShown = false; |
674 | 523 | } |
675 | | - |
676 | | - // Clear dirty rects to prevent memory accumulation |
677 | | - m_RenderData.dirtyRects.clear(); |
678 | | - m_RenderData.dirtyRects.shrink_to_fit(); |
679 | 524 | } |
680 | 525 | } |
681 | 526 |
|
@@ -1218,8 +1063,6 @@ void CWebView::OnPaint(CefRefPtr<CefBrowser> browser, CefRenderHandler::PaintEle |
1218 | 1063 |
|
1219 | 1064 | m_RenderData.width = width; |
1220 | 1065 | m_RenderData.height = height; |
1221 | | - m_RenderData.dirtyRects = dirtyRects; |
1222 | | - m_RenderData.dirtyRects.shrink_to_fit(); |
1223 | 1066 | m_RenderData.changed = true; |
1224 | 1067 | } |
1225 | 1068 |
|
|
0 commit comments