Bug 1946657 - Re-add workaround of video overlay's SwapChain Present() r=gfx-reviewers,lsalzman

New video SwapChain workaround has a workaround of  Bug 1940023.

Differential Revision: https://phabricator.services.mozilla.com/D238720
This commit is contained in:
sotaro
2025-02-20 00:17:18 +00:00
parent 35439876cf
commit cea7a33050
4 changed files with 137 additions and 56 deletions

View File

@@ -660,8 +660,6 @@ void DCLayerTree::CompositorEndFrame() {
// Disable video overlay if mCompositionDevice->Commit() with video overlay is
// too slow. It drops fps.
const auto maxCommitWaitDurationMs = 20;
const auto maxSlowCommitCount = 5;
const auto commitDurationMs =
static_cast<uint32_t>((end - start).ToMilliseconds());
@@ -669,36 +667,12 @@ void DCLayerTree::CompositorEndFrame() {
(uint8_t)mUsedOverlayTypesInFrame, commitDurationMs);
PROFILER_MARKER_TEXT("CommitWait", GRAPHICS, {}, marker);
if (mUsedOverlayTypesInFrame != DCompOverlayTypes::NO_OVERLAY &&
commitDurationMs > maxCommitWaitDurationMs) {
mSlowCommitCount++;
} else {
mSlowCommitCount = 0;
}
if (mSlowCommitCount <= maxSlowCommitCount) {
return;
}
for (auto it = mDCSurfaces.begin(); it != mDCSurfaces.end(); it++) {
auto* surfaceVideo = it->second->AsDCSurfaceVideo();
if (surfaceVideo) {
surfaceVideo->DisableVideoOverlay();
surfaceVideo->OnCompositorEndFrame(mCurrentFrame, commitDurationMs);
}
}
if (mUsedOverlayTypesInFrame & DCompOverlayTypes::SOFTWARE_DECODED_VIDEO) {
gfxCriticalNoteOnce << "Sw video swapchain present is slow";
nsPrintfCString marker("Sw video swapchain present is slow");
PROFILER_MARKER_TEXT("DisableOverlay", GRAPHICS, {}, marker);
}
if (mUsedOverlayTypesInFrame & DCompOverlayTypes::HARDWARE_DECODED_VIDEO) {
gfxCriticalNoteOnce << "Hw video swapchain present is slow";
nsPrintfCString marker("Hw video swapchain present is slow");
PROFILER_MARKER_TEXT("DisableOverlay", GRAPHICS, {}, marker);
}
}
void DCLayerTree::BindSwapChain(wr::NativeSurfaceId aId) {
@@ -1701,9 +1675,65 @@ void DCSurfaceVideo::PresentVideo() {
return;
}
const auto device = mDCLayerTree->GetDevice();
HRESULT hr;
if (mFirstPresent) {
mFirstPresent = false;
UINT flags = DXGI_PRESENT_USE_DURATION;
// DirectComposition can display black for a swap chain between the first
// and second time it's presented to - maybe the first Present can get lost
// somehow and it shows the wrong buffer. In that case copy the buffers so
// all have the correct contents, which seems to help. The first Present()
// after this needs to have SyncInterval > 0, or else the workaround doesn't
// help.
for (size_t i = 0; i < mSwapChainBufferCount - 1; ++i) {
hr = mVideoSwapChain->Present(0, flags);
// Ignore DXGI_STATUS_OCCLUDED since that's not an error but only
// indicates that the window is occluded and we can stop rendering.
if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) {
gfxCriticalNoteOnce << "video Present failed during first present: "
<< gfx::hexa(hr);
return;
}
RefPtr<ID3D11Texture2D> destTexture;
mVideoSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
(void**)getter_AddRefs(destTexture));
MOZ_ASSERT(destTexture);
RefPtr<ID3D11Texture2D> srcTexture;
hr = mVideoSwapChain->GetBuffer(1, __uuidof(ID3D11Texture2D),
(void**)getter_AddRefs(srcTexture));
MOZ_ASSERT(srcTexture);
RefPtr<ID3D11DeviceContext> context;
device->GetImmediateContext(getter_AddRefs(context));
MOZ_ASSERT(context);
context->CopyResource(destTexture, srcTexture);
}
// Additionally wait for the GPU to finish executing its commands, or
// there still may be a black flicker when presenting expensive content
// (e.g. 4k video).
RefPtr<IDXGIDevice2> dxgiDevice2;
device->QueryInterface((IDXGIDevice2**)getter_AddRefs(dxgiDevice2));
MOZ_ASSERT(dxgiDevice2);
HANDLE event = ::CreateEvent(nullptr, false, false, nullptr);
hr = dxgiDevice2->EnqueueSetEvent(event);
if (SUCCEEDED(hr)) {
DebugOnly<DWORD> result = ::WaitForSingleObject(event, INFINITE);
MOZ_ASSERT(result == WAIT_OBJECT_0);
} else {
gfxCriticalNoteOnce << "EnqueueSetEvent failed: " << gfx::hexa(hr);
}
::CloseHandle(event);
}
UINT flags = DXGI_PRESENT_USE_DURATION;
UINT interval = 1;
if (StaticPrefs::gfx_webrender_dcomp_video_swap_chain_present_interval_0()) {
interval = 0;
}
auto start = TimeStamp::Now();
hr = mVideoSwapChain->Present(interval, flags);
@@ -1722,8 +1752,6 @@ void DCSurfaceVideo::PresentVideo() {
return;
}
const auto maxPresentWaitDurationMs = 2;
const auto maxSlowPresentCount = 5;
const auto presentDurationMs =
static_cast<uint32_t>((end - start).ToMilliseconds());
const auto overlayType = mRenderTextureHost->IsSoftwareDecodedVideo()
@@ -1734,36 +1762,20 @@ void DCSurfaceVideo::PresentVideo() {
presentDurationMs);
PROFILER_MARKER_TEXT("PresentWait", GRAPHICS, {}, marker);
if (presentDurationMs > maxPresentWaitDurationMs) {
mSlowPresentCount++;
} else {
mSlowPresentCount = 0;
if (mRenderTextureHostUsageInfo) {
mRenderTextureHostUsageInfo->OnVideoPresent(mDCLayerTree->GetFrameId(),
presentDurationMs);
}
if (mSlowPresentCount <= maxSlowPresentCount) {
return;
}
DisableVideoOverlay();
if (overlayType == DCompOverlayTypes::SOFTWARE_DECODED_VIDEO) {
gfxCriticalNoteOnce << "Sw video swapchain present is slow";
nsPrintfCString marker("Sw video swapchain present is slow");
PROFILER_MARKER_TEXT("DisableOverlay", GRAPHICS, {}, marker);
} else {
gfxCriticalNoteOnce << "Hw video swapchain present is slow";
nsPrintfCString marker("Hw video swapchain present is slow");
PROFILER_MARKER_TEXT("DisableOverlay", GRAPHICS, {}, marker);
}
// printf_stderr("DCSurfaceVideo::PresentVideo() 5 presentDurationMs %u\n",
// presentDurationMs);
}
void DCSurfaceVideo::DisableVideoOverlay() {
void DCSurfaceVideo::OnCompositorEndFrame(int aFrameId, uint32_t aDurationMs) {
if (!mRenderTextureHostUsageInfo) {
return;
}
mRenderTextureHostUsageInfo->DisableVideoOverlay();
mRenderTextureHostUsageInfo->OnCompositorEndFrame(aFrameId, aDurationMs);
}
DXGI_FORMAT DCSurfaceVideo::GetSwapChainFormat(bool aUseVpAutoHDR) {
@@ -1779,6 +1791,8 @@ DXGI_FORMAT DCSurfaceVideo::GetSwapChainFormat(bool aUseVpAutoHDR) {
bool DCSurfaceVideo::CreateVideoSwapChain(DXGI_FORMAT aSwapChainFormat) {
MOZ_ASSERT(mRenderTextureHost);
mFirstPresent = true;
const auto device = mDCLayerTree->GetDevice();
RefPtr<IDXGIDevice> dxgiDevice;

View File

@@ -185,6 +185,8 @@ class DCLayerTree {
void SetUsedOverlayTypeInFrame(DCompOverlayTypes aTypes);
int GetFrameId() { return mCurrentFrame; }
protected:
bool Initialize(HWND aHwnd, nsACString& aError);
bool InitializeVideoOverlaySupport();
@@ -265,7 +267,6 @@ class DCLayerTree {
mutable Maybe<color::ColorProfileDesc> mOutputColorProfile;
DCompOverlayTypes mUsedOverlayTypesInFrame = DCompOverlayTypes::NO_OVERLAY;
int mSlowCommitCount = 0;
public:
const color::ColorProfileDesc& OutputColorProfile() const {
@@ -423,11 +424,10 @@ class DCSurfaceVideo : public DCSurface {
void AttachExternalImage(wr::ExternalImageId aExternalImage) override;
bool CalculateSwapChainSize(gfx::Matrix& aTransform);
void PresentVideo();
void OnCompositorEndFrame(int aFrameId, uint32_t aDurationMs);
DCSurfaceVideo* AsDCSurfaceVideo() override { return this; }
void DisableVideoOverlay();
protected:
virtual ~DCSurfaceVideo();
@@ -449,7 +449,7 @@ class DCSurfaceVideo : public DCSurface {
RefPtr<RenderTextureHost> mRenderTextureHost;
RefPtr<RenderTextureHost> mPrevTexture;
RefPtr<RenderTextureHostUsageInfo> mRenderTextureHostUsageInfo;
int mSlowPresentCount = 0;
bool mFirstPresent = true;
const UINT mSwapChainBufferCount;
bool mUseVpAutoHDR = false;
bool mVpAutoHDRFailed = false;

View File

@@ -9,6 +9,8 @@
#include "GLContext.h"
#include "mozilla/layers/CompositorThread.h"
#include "mozilla/layers/TextureHost.h"
#include "mozilla/ProfilerMarkers.h"
#include "mozilla/webrender/RenderThread.h"
#include "RenderThread.h"
namespace mozilla {
@@ -25,6 +27,63 @@ void ActivateBindAndTexParameteri(gl::GLContext* aGL, GLenum aActiveTexture,
LOCAL_GL_LINEAR);
}
void RenderTextureHostUsageInfo::OnVideoPresent(int aFrameId,
uint32_t aDurationMs) {
MOZ_ASSERT(RenderThread::IsInRenderThread());
const auto maxPresentWaitDurationMs = 2;
const auto maxSlowPresentCount = 5;
mVideoPresentFrameId = aFrameId;
if (aDurationMs < maxPresentWaitDurationMs) {
mSlowPresentCount = 0;
return;
}
mSlowPresentCount++;
if (mSlowPresentCount <= maxSlowPresentCount) {
return;
}
DisableVideoOverlay();
gfxCriticalNoteOnce << "Video swapchain present is slow";
nsPrintfCString marker("Video swapchain present is slow");
PROFILER_MARKER_TEXT("DisableOverlay", GRAPHICS, {}, marker);
}
void RenderTextureHostUsageInfo::OnCompositorEndFrame(int aFrameId,
uint32_t aDurationMs) {
MOZ_ASSERT(RenderThread::IsInRenderThread());
const auto maxCommitWaitDurationMs = 20;
const auto maxSlowCommitCount = 5;
// Check if video was presented in current frame.
if (mVideoPresentFrameId != aFrameId) {
return;
}
if (aDurationMs < maxCommitWaitDurationMs) {
mSlowCommitCount = 0;
return;
}
mSlowCommitCount++;
if (mSlowCommitCount <= maxSlowCommitCount) {
return;
}
gfxCriticalNoteOnce << "Video swapchain is slow";
nsPrintfCString marker("Video swapchain is slow");
PROFILER_MARKER_TEXT("DisableOverlay", GRAPHICS, {}, marker);
}
RenderTextureHost::RenderTextureHost() : mIsFromDRMSource(false) {
MOZ_COUNT_CTOR(RenderTextureHost);
}

View File

@@ -13,6 +13,7 @@
#include "mozilla/Atomics.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/layers/LayersSurfaces.h"
#include "mozilla/layers/OverlayInfo.h"
#include "mozilla/RefPtr.h"
#include "mozilla/webrender/webrender_ffi.h"
#include "mozilla/webrender/WebRenderTypes.h"
@@ -57,6 +58,9 @@ class RenderTextureHostUsageInfo final {
bool VideoOverlayDisabled() { return mVideoOverlayDisabled; }
void DisableVideoOverlay() { mVideoOverlayDisabled = true; }
void OnVideoPresent(int aFrameId, uint32_t aDurationMs);
void OnCompositorEndFrame(int aFrameId, uint32_t aDurationMs);
const TimeStamp mCreationTimeStamp;
protected:
@@ -64,6 +68,10 @@ class RenderTextureHostUsageInfo final {
// RenderTextureHost prefers to disable video overlay.
Atomic<bool> mVideoOverlayDisabled{false};
int mVideoPresentFrameId = 0;
int mSlowPresentCount = 0;
int mSlowCommitCount = 0;
};
class RenderTextureHost {