diff --git a/Client/cefweb/CWebView.cpp b/Client/cefweb/CWebView.cpp index b421ff567e..28b6100d97 100644 --- a/Client/cefweb/CWebView.cpp +++ b/Client/cefweb/CWebView.cpp @@ -14,6 +14,11 @@ #include #include "CWebDevTools.h" +namespace +{ + const int CEF_PIXEL_STRIDE = 4; +} + CWebView::CWebView(bool bIsLocal, CWebBrowserItem* pWebBrowserRenderItem, bool bTransparent) { m_bIsLocal = bIsLocal; @@ -39,9 +44,6 @@ CWebView::~CWebView() // Ensure that CefRefPtr::~CefRefPtr doesn't try to release it twice (it has already been released in CWebView::OnBeforeClose) m_pWebView = nullptr; - // Make sure we don't dead lock the CEF render thread - m_RenderData.cv.notify_all(); - OutputDebugLine("CWebView::~CWebView"); } @@ -75,9 +77,6 @@ void CWebView::CloseBrowser() // CefBrowserHost::CloseBrowser calls the destructor after the browser has been destroyed m_bBeingDestroyed = true; - // Make sure we don't dead lock the CEF render thread - m_RenderData.cv.notify_all(); - if (m_pWebView) m_pWebView->GetHost()->CloseBrowser(true); } @@ -182,15 +181,17 @@ void CWebView::ClearTexture() IDirect3DSurface9* pD3DSurface = m_pWebBrowserRenderItem->m_pD3DRenderTargetSurface; if (!pD3DSurface) return; - - D3DLOCKED_RECT LockedRect; + D3DSURFACE_DESC SurfaceDesc; + if (FAILED(pD3DSurface->GetDesc(&SurfaceDesc))) + return; - pD3DSurface->GetDesc(&SurfaceDesc); - pD3DSurface->LockRect(&LockedRect, NULL, 0); - - memset(LockedRect.pBits, 0xFF, SurfaceDesc.Width * SurfaceDesc.Height * 4); - pD3DSurface->UnlockRect(); + D3DLOCKED_RECT LockedRect; + if (SUCCEEDED(pD3DSurface->LockRect(&LockedRect, NULL, D3DLOCK_DISCARD))) + { + memset(LockedRect.pBits, 0xFF, SurfaceDesc.Height * LockedRect.Pitch); + pD3DSurface->UnlockRect(); + } } void CWebView::UpdateTexture() @@ -210,61 +211,76 @@ void CWebView::UpdateTexture() { // Lock surface D3DLOCKED_RECT LockedRect; - pSurface->LockRect(&LockedRect, nullptr, 0); - - // Dirty rect implementation, don't use this as loops are significantly slower than memcpy - auto surfaceData = static_cast(LockedRect.pBits); - auto sourceData = static_cast(m_RenderData.buffer); - auto pitch = LockedRect.Pitch; - - // Update view area - if (m_RenderData.changed) + if (SUCCEEDED(pSurface->LockRect(&LockedRect, nullptr, 0))) { - // Update changed state - m_RenderData.changed = false; - - if (m_RenderData.dirtyRects.size() > 0 && m_RenderData.dirtyRects[0].width == m_RenderData.width && - m_RenderData.dirtyRects[0].height == m_RenderData.height) + // Dirty rect implementation, don't use this as loops are significantly slower than memcpy + const auto destData = static_cast(LockedRect.pBits); + const auto sourceData = static_cast(m_RenderData.buffer); + const auto destPitch = LockedRect.Pitch; + const auto sourcePitch = m_RenderData.width * CEF_PIXEL_STRIDE; + + // Update view area + if (m_RenderData.changed) { - // Update whole texture - memcpy(surfaceData, sourceData, m_RenderData.width * m_RenderData.height * 4); - } - else - { - // Update dirty rects - for (auto& rect : m_RenderData.dirtyRects) + // Update changed state + m_RenderData.changed = false; + + if (m_RenderData.dirtyRects.size() > 0 && m_RenderData.dirtyRects[0].width == m_RenderData.width && + m_RenderData.dirtyRects[0].height == m_RenderData.height) { - for (int y = rect.y; y < rect.y + rect.height; ++y) + // Note that D3D texture size can be hardware dependent(especially with dynamic texture) + // When destination and source pitches differ we must copy pixels row by row + if (destPitch == sourcePitch) + memcpy(destData, sourceData, destPitch * m_RenderData.height); + else { - int index = y * pitch + rect.x * 4; - memcpy(&surfaceData[index], &sourceData[index], rect.width * 4); + for (int y = 0; y < m_RenderData.height; ++y) + { + const int sourceIndex = y * sourcePitch; + const int destIndex = y * destPitch; + + memcpy(&destData[destIndex], &sourceData[sourceIndex], std::min(sourcePitch, destPitch)); + } + } + } + else + { + // Update dirty rects + for (const auto& rect : m_RenderData.dirtyRects) + { + for (int y = rect.y; y < rect.y + rect.height; ++y) + { + // Note that D3D texture size can be hardware dependent(especially with dynamic texture) + // We cannot be sure that source and destination pitches are the same + const int sourceIndex = y * sourcePitch + rect.x * CEF_PIXEL_STRIDE; + const int destIndex = y * destPitch + rect.x * CEF_PIXEL_STRIDE; + + memcpy(&destData[destIndex], &sourceData[sourceIndex], rect.width * CEF_PIXEL_STRIDE); + } } } } - } - // Update popup area (override certain areas of the view texture) - bool popupSizeMismatches = m_RenderData.popupRect.x + m_RenderData.popupRect.width >= (int)m_pWebBrowserRenderItem->m_uiSizeX || - m_RenderData.popupRect.y + m_RenderData.popupRect.height >= (int)m_pWebBrowserRenderItem->m_uiSizeY; + // Update popup area (override certain areas of the view texture) + const bool popupSizeMismatches = m_RenderData.popupRect.x + m_RenderData.popupRect.width >= (int)m_pWebBrowserRenderItem->m_uiSizeX || + m_RenderData.popupRect.y + m_RenderData.popupRect.height >= (int)m_pWebBrowserRenderItem->m_uiSizeY; - if (m_RenderData.popupShown && !popupSizeMismatches) - { - auto popupPitch = m_RenderData.popupRect.width * 4; - for (int y = 0; y < m_RenderData.popupRect.height; ++y) + if (m_RenderData.popupShown && !popupSizeMismatches) { - int sourceIndex = y * popupPitch; - int destIndex = (y + m_RenderData.popupRect.y) * pitch + m_RenderData.popupRect.x * 4; + const auto popupPitch = m_RenderData.popupRect.width * CEF_PIXEL_STRIDE; + for (int y = 0; y < m_RenderData.popupRect.height; ++y) + { + const int sourceIndex = y * popupPitch; + const int destIndex = (y + m_RenderData.popupRect.y) * destPitch + m_RenderData.popupRect.x * CEF_PIXEL_STRIDE; - memcpy(&surfaceData[destIndex], &m_RenderData.popupBuffer[sourceIndex], popupPitch); + memcpy(&destData[destIndex], &m_RenderData.popupBuffer[sourceIndex], popupPitch); + } } - } - // Unlock surface - pSurface->UnlockRect(); + // Unlock surface + pSurface->UnlockRect(); + } } - - // Resume CEF render thread - m_RenderData.cv.notify_all(); } void CWebView::ExecuteJavascript(const SString& strJavascriptCode) @@ -431,9 +447,6 @@ void CWebView::Resize(const CVector2D& size) // Send resize event to CEF if (m_pWebView) m_pWebView->GetHost()->WasResized(); - - // Tell CEF to render a new frame - m_RenderData.cv.notify_all(); } CVector2D CWebView::GetSize() @@ -661,7 +674,7 @@ void CWebView::OnPopupSize(CefRefPtr browser, const CefRect& rect) m_RenderData.popupRect = rect; // Resize buffer - m_RenderData.popupBuffer.reset(new byte[rect.width * rect.height * 4]); + m_RenderData.popupBuffer.reset(new byte[rect.width * rect.height * CEF_PIXEL_STRIDE]); } //////////////////////////////////////////////////////////////////// @@ -677,31 +690,25 @@ void CWebView::OnPaint(CefRefPtr browser, CefRenderHandler::PaintEle if (m_bBeingDestroyed) return; + std::unique_lock lock(m_RenderData.dataMutex); + + // Copy popup buffer + if (paintType == PET_POPUP) { - std::lock_guard lock(m_RenderData.dataMutex); - - // Copy popup buffer - if (paintType == PET_POPUP) + if (m_RenderData.popupBuffer) { - if (m_RenderData.popupBuffer) - { - memcpy(m_RenderData.popupBuffer.get(), buffer, width * height * 4); - } - - return; // We don't have to wait as we've copied the buffer already + memcpy(m_RenderData.popupBuffer.get(), buffer, width * height * CEF_PIXEL_STRIDE); } - // Store render data - m_RenderData.buffer = buffer; - m_RenderData.width = width; - m_RenderData.height = height; - m_RenderData.dirtyRects = dirtyRects; - m_RenderData.changed = true; + return; // We don't have to wait as we've copied the buffer already } - // Wait for the main thread to handle drawing the texture - std::unique_lock lock(m_RenderData.cvMutex); - m_RenderData.cv.wait(lock); + // Store render data + m_RenderData.buffer = buffer; + m_RenderData.width = width; + m_RenderData.height = height; + m_RenderData.dirtyRects = dirtyRects; + m_RenderData.changed = true; } //////////////////////////////////////////////////////////////////// diff --git a/Client/cefweb/CWebView.h b/Client/cefweb/CWebView.h index 11555608b3..2583163e50 100644 --- a/Client/cefweb/CWebView.h +++ b/Client/cefweb/CWebView.h @@ -176,7 +176,7 @@ class CWebView : public CWebViewInterface, CefRefPtr m_pWebView; CWebBrowserItem* m_pWebBrowserRenderItem; - bool m_bBeingDestroyed; + std::atomic_bool m_bBeingDestroyed; bool m_bIsLocal; bool m_bIsRenderingPaused; bool m_bIsTransparent; @@ -192,8 +192,6 @@ class CWebView : public CWebViewInterface, { bool changed = false; std::mutex dataMutex; - std::mutex cvMutex; - std::condition_variable cv; const void* buffer; int width, height; diff --git a/Client/core/Graphics/CRenderItem.WebBrowser.cpp b/Client/core/Graphics/CRenderItem.WebBrowser.cpp index 70687eb88a..61af412024 100644 --- a/Client/core/Graphics/CRenderItem.WebBrowser.cpp +++ b/Client/core/Graphics/CRenderItem.WebBrowser.cpp @@ -87,11 +87,16 @@ void CWebBrowserItem::CreateUnderlyingData() assert(!m_pD3DRenderTargetSurface); assert(!m_pD3DTexture); - D3DXCreateTexture(m_pDevice, m_uiSizeX, m_uiSizeY, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, (IDirect3DTexture9**)&m_pD3DTexture); + // Check if texture is actually created. It can be failed in some conditions(e.g. lack of memory). + if (FAILED(D3DXCreateTexture(m_pDevice, m_uiSizeX, m_uiSizeY, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, (IDirect3DTexture9**)&m_pD3DTexture)) || !m_pD3DTexture) + return; - // Check texture created - if (!m_pD3DTexture) + // Get the render target surface here for convenience + if (FAILED(((IDirect3DTexture9*)m_pD3DTexture)->GetSurfaceLevel(0, &m_pD3DRenderTargetSurface)) || !m_pD3DRenderTargetSurface) + { + SAFE_RELEASE(m_pD3DTexture); return; + } // D3DXCreateTexture sets width and height to 1 if the argument value was 0 // See: https://docs.microsoft.com/en-us/windows/desktop/direct3d9/d3dxcreatetexture @@ -99,10 +104,7 @@ void CWebBrowserItem::CreateUnderlyingData() m_uiSizeX = 1; if (m_uiSizeY == 0) - m_uiSizeY = 1; - - // Get the render target surface here for convenience - ((IDirect3DTexture9*)m_pD3DTexture)->GetSurfaceLevel(0, &m_pD3DRenderTargetSurface); + m_uiSizeY = 1; // Update surface size, although it probably will be unchanged | Todo: Remove this D3DSURFACE_DESC desc;