summaryrefslogtreecommitdiffstats
path: root/gfx/webrender_bindings/RendererOGL.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /gfx/webrender_bindings/RendererOGL.cpp
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/webrender_bindings/RendererOGL.cpp')
-rw-r--r--gfx/webrender_bindings/RendererOGL.cpp430
1 files changed, 430 insertions, 0 deletions
diff --git a/gfx/webrender_bindings/RendererOGL.cpp b/gfx/webrender_bindings/RendererOGL.cpp
new file mode 100644
index 0000000000..3c4d13e376
--- /dev/null
+++ b/gfx/webrender_bindings/RendererOGL.cpp
@@ -0,0 +1,430 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RendererOGL.h"
+
+#include "base/task.h"
+#include "GLContext.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/Types.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/ProfilerScreenshots.h"
+#include "mozilla/webrender/RenderCompositor.h"
+#include "mozilla/webrender/RenderTextureHost.h"
+#include "mozilla/widget/CompositorWidget.h"
+
+namespace mozilla {
+namespace wr {
+
+class RendererRecordedFrame final : public layers::RecordedFrame {
+ public:
+ RendererRecordedFrame(const TimeStamp& aTimeStamp, wr::Renderer* aRenderer,
+ const wr::RecordedFrameHandle aHandle,
+ const gfx::IntSize& aSize)
+ : RecordedFrame(aTimeStamp),
+ mRenderer(aRenderer),
+ mSize(aSize),
+ mHandle(aHandle) {}
+
+ already_AddRefed<gfx::DataSourceSurface> GetSourceSurface() override {
+ if (!mSurface) {
+ mSurface = gfx::Factory::CreateDataSourceSurface(
+ mSize, gfx::SurfaceFormat::B8G8R8A8, /* aZero = */ false);
+
+ gfx::DataSourceSurface::ScopedMap map(mSurface,
+ gfx::DataSourceSurface::WRITE);
+
+ if (!wr_renderer_map_recorded_frame(mRenderer, mHandle, map.GetData(),
+ map.GetStride() * mSize.height,
+ map.GetStride())) {
+ return nullptr;
+ }
+ }
+
+ return do_AddRef(mSurface);
+ }
+
+ private:
+ wr::Renderer* mRenderer;
+ RefPtr<gfx::DataSourceSurface> mSurface;
+ gfx::IntSize mSize;
+ wr::RecordedFrameHandle mHandle;
+};
+
+wr::WrExternalImage wr_renderer_lock_external_image(void* aObj,
+ wr::ExternalImageId aId,
+ uint8_t aChannelIndex) {
+ RendererOGL* renderer = reinterpret_cast<RendererOGL*>(aObj);
+ RenderTextureHost* texture = renderer->GetRenderTexture(aId);
+ MOZ_ASSERT(texture);
+ if (!texture) {
+ gfxCriticalNoteOnce << "Failed to lock ExternalImage for extId:"
+ << AsUint64(aId);
+ return InvalidToWrExternalImage();
+ }
+ if (auto* gl = renderer->gl()) {
+ return texture->Lock(aChannelIndex, gl);
+ } else if (auto* swgl = renderer->swgl()) {
+ return texture->LockSWGL(aChannelIndex, swgl, renderer->GetCompositor());
+ } else {
+ gfxCriticalNoteOnce
+ << "No GL or SWGL context available to lock ExternalImage for extId:"
+ << AsUint64(aId);
+ return InvalidToWrExternalImage();
+ }
+}
+
+void wr_renderer_unlock_external_image(void* aObj, wr::ExternalImageId aId,
+ uint8_t aChannelIndex) {
+ RendererOGL* renderer = reinterpret_cast<RendererOGL*>(aObj);
+ RenderTextureHost* texture = renderer->GetRenderTexture(aId);
+ MOZ_ASSERT(texture);
+ if (!texture) {
+ return;
+ }
+ if (renderer->gl()) {
+ texture->Unlock();
+ } else if (renderer->swgl()) {
+ texture->UnlockSWGL();
+ }
+}
+
+RendererOGL::RendererOGL(RefPtr<RenderThread>&& aThread,
+ UniquePtr<RenderCompositor> aCompositor,
+ wr::WindowId aWindowId, wr::Renderer* aRenderer,
+ layers::CompositorBridgeParent* aBridge)
+ : mThread(aThread),
+ mCompositor(std::move(aCompositor)),
+ mRenderer(aRenderer),
+ mBridge(aBridge),
+ mWindowId(aWindowId),
+ mDisableNativeCompositor(false),
+ mLastPipelineInfo(new WebRenderPipelineInfo) {
+ MOZ_ASSERT(mThread);
+ MOZ_ASSERT(mCompositor);
+ MOZ_ASSERT(mRenderer);
+ MOZ_ASSERT(mBridge);
+ MOZ_COUNT_CTOR(RendererOGL);
+}
+
+RendererOGL::~RendererOGL() {
+ MOZ_COUNT_DTOR(RendererOGL);
+ if (!mCompositor->MakeCurrent()) {
+ gfxCriticalNote
+ << "Failed to make render context current during destroying.";
+ // Leak resources!
+ } else {
+ wr_renderer_delete(mRenderer);
+ }
+}
+
+wr::WrExternalImageHandler RendererOGL::GetExternalImageHandler() {
+ return wr::WrExternalImageHandler{
+ this,
+ };
+}
+
+void RendererOGL::SetFramePublishId(FramePublishId aPublishId) {
+ wr_renderer_set_target_frame_publish_id(mRenderer, aPublishId);
+}
+
+void RendererOGL::Update() {
+ mCompositor->Update();
+ if (mCompositor->MakeCurrent()) {
+ wr_renderer_update(mRenderer);
+ FlushPipelineInfo();
+ }
+}
+
+static void DoWebRenderDisableNativeCompositor(
+ layers::CompositorBridgeParent* aBridge) {
+ aBridge->NotifyWebRenderDisableNativeCompositor();
+}
+
+RenderedFrameId RendererOGL::UpdateAndRender(
+ const Maybe<gfx::IntSize>& aReadbackSize,
+ const Maybe<wr::ImageFormat>& aReadbackFormat,
+ const Maybe<Range<uint8_t>>& aReadbackBuffer, bool* aNeedsYFlip,
+ RendererStats* aOutStats) {
+ mozilla::widget::WidgetRenderingContext widgetContext;
+
+#if defined(XP_MACOSX)
+ widgetContext.mGL = mCompositor->gl();
+#endif
+
+ if (!mCompositor->GetWidget()->PreRender(&widgetContext)) {
+ // XXX This could cause oom in webrender since pending_texture_updates is
+ // not handled. It needs to be addressed.
+ return RenderedFrameId();
+ }
+ // XXX set clear color if MOZ_WIDGET_ANDROID is defined.
+
+ if (mThread->IsHandlingDeviceReset() || !mCompositor->BeginFrame()) {
+ CheckGraphicsResetStatus("BeginFrame", /* aForce */ true);
+ mCompositor->GetWidget()->PostRender(&widgetContext);
+ return RenderedFrameId();
+ }
+
+ auto size = mCompositor->GetBufferSize();
+ auto bufferAge = mCompositor->GetBufferAge();
+
+ wr_renderer_update(mRenderer);
+
+ bool fullRender = mCompositor->RequestFullRender();
+ // When we're rendering to an external target, we want to render everything.
+ if (mCompositor->UsePartialPresent() &&
+ (aReadbackBuffer.isSome() || layers::ProfilerScreenshots::IsEnabled())) {
+ fullRender = true;
+ }
+ if (fullRender) {
+ wr_renderer_force_redraw(mRenderer);
+ }
+
+ nsTArray<DeviceIntRect> dirtyRects;
+ bool rendered = wr_renderer_render(mRenderer, size.width, size.height,
+ bufferAge, aOutStats, &dirtyRects);
+ FlushPipelineInfo();
+ if (!rendered) {
+ mCompositor->CancelFrame();
+ RenderThread::Get()->HandleWebRenderError(WebRenderError::RENDER);
+ mCompositor->GetWidget()->PostRender(&widgetContext);
+ return RenderedFrameId();
+ }
+
+ if (aReadbackBuffer.isSome()) {
+ MOZ_ASSERT(aReadbackSize.isSome());
+ MOZ_ASSERT(aReadbackFormat.isSome());
+ if (!mCompositor->MaybeReadback(aReadbackSize.ref(), aReadbackFormat.ref(),
+ aReadbackBuffer.ref(), aNeedsYFlip)) {
+ wr_renderer_readback(mRenderer, aReadbackSize.ref().width,
+ aReadbackSize.ref().height, aReadbackFormat.ref(),
+ &aReadbackBuffer.ref()[0],
+ aReadbackBuffer.ref().length());
+ if (aNeedsYFlip) {
+ *aNeedsYFlip = !mCompositor->SurfaceOriginIsTopLeft();
+ }
+ }
+ }
+
+ if (size.Width() != 0 && size.Height() != 0) {
+ if (!mCompositor->MaybeGrabScreenshot(size.ToUnknownSize())) {
+ mScreenshotGrabber.MaybeGrabScreenshot(this, size.ToUnknownSize());
+ }
+ }
+
+ // Frame recording must happen before EndFrame, as we must ensure we read the
+ // contents of the back buffer before any calls to SwapBuffers which might
+ // invalidate it.
+ MaybeRecordFrame(mLastPipelineInfo);
+
+ RenderedFrameId frameId = mCompositor->EndFrame(dirtyRects);
+
+ mCompositor->GetWidget()->PostRender(&widgetContext);
+
+#if defined(ENABLE_FRAME_LATENCY_LOG)
+ if (mFrameStartTime) {
+ uint32_t latencyMs =
+ round((TimeStamp::Now() - mFrameStartTime).ToMilliseconds());
+ printf_stderr("generate frame latencyMs latencyMs %d\n", latencyMs);
+ }
+ // Clear frame start time
+ mFrameStartTime = TimeStamp();
+#endif
+
+ if (!mCompositor->MaybeProcessScreenshotQueue()) {
+ mScreenshotGrabber.MaybeProcessQueue(this);
+ }
+
+ // TODO: Flush pending actions such as texture deletions/unlocks and
+ // textureHosts recycling.
+
+ return frameId;
+}
+
+bool RendererOGL::EnsureAsyncScreenshot() {
+ if (mCompositor->SupportAsyncScreenshot()) {
+ return true;
+ }
+ if (!mDisableNativeCompositor) {
+ layers::CompositorThread()->Dispatch(
+ NewRunnableFunction("DoWebRenderDisableNativeCompositorRunnable",
+ &DoWebRenderDisableNativeCompositor, mBridge));
+
+ mDisableNativeCompositor = true;
+ gfxCriticalNote << "Disable native compositor for async screenshot";
+ }
+ return false;
+}
+
+void RendererOGL::CheckGraphicsResetStatus(const char* aCaller, bool aForce) {
+ if (mCompositor) {
+ auto reason = mCompositor->IsContextLost(aForce);
+ if (reason != LOCAL_GL_NO_ERROR) {
+ RenderThread::Get()->HandleDeviceReset(aCaller, reason);
+ }
+ }
+}
+
+void RendererOGL::WaitForGPU() {
+ if (!mCompositor->WaitForGPU()) {
+ CheckGraphicsResetStatus("WaitForGPU", /* aForce */ true);
+ }
+}
+
+ipc::FileDescriptor RendererOGL::GetAndResetReleaseFence() {
+ return mCompositor->GetAndResetReleaseFence();
+}
+
+RenderedFrameId RendererOGL::GetLastCompletedFrameId() {
+ return mCompositor->GetLastCompletedFrameId();
+}
+
+RenderedFrameId RendererOGL::UpdateFrameId() {
+ return mCompositor->UpdateFrameId();
+}
+
+void RendererOGL::Pause() { mCompositor->Pause(); }
+
+bool RendererOGL::Resume() { return mCompositor->Resume(); }
+
+bool RendererOGL::IsPaused() { return mCompositor->IsPaused(); }
+
+layers::SyncObjectHost* RendererOGL::GetSyncObject() const {
+ return mCompositor->GetSyncObject();
+}
+
+gl::GLContext* RendererOGL::gl() const { return mCompositor->gl(); }
+
+void* RendererOGL::swgl() const { return mCompositor->swgl(); }
+
+void RendererOGL::SetFrameStartTime(const TimeStamp& aTime) {
+ if (mFrameStartTime) {
+ // frame start time is already set. This could happen when multiple
+ // generate frame requests are merged by webrender.
+ return;
+ }
+ mFrameStartTime = aTime;
+}
+
+void RendererOGL::BeginRecording(const TimeStamp& aRecordingStart,
+ wr::PipelineId aRootPipelineId) {
+ MOZ_ASSERT(!mCompositionRecorder);
+
+ mRootPipelineId = aRootPipelineId;
+ mCompositionRecorder =
+ MakeUnique<layers::CompositionRecorder>(aRecordingStart);
+ mCompositor->MaybeRequestAllowFrameRecording(true);
+}
+
+void RendererOGL::MaybeRecordFrame(const WebRenderPipelineInfo* aPipelineInfo) {
+ if (!mCompositionRecorder || !EnsureAsyncScreenshot()) {
+ return;
+ }
+
+ if (!mRenderer || !aPipelineInfo || !DidPaintContent(aPipelineInfo)) {
+ return;
+ }
+
+ if (mCompositor->MaybeRecordFrame(*mCompositionRecorder)) {
+ return;
+ }
+
+ wr::RecordedFrameHandle handle{0};
+ gfx::IntSize size(0, 0);
+
+ if (wr_renderer_record_frame(mRenderer, wr::ImageFormat::BGRA8, &handle,
+ &size.width, &size.height)) {
+ RefPtr<layers::RecordedFrame> frame =
+ new RendererRecordedFrame(TimeStamp::Now(), mRenderer, handle, size);
+
+ mCompositionRecorder->RecordFrame(frame);
+ }
+}
+
+bool RendererOGL::DidPaintContent(const WebRenderPipelineInfo* aFrameEpochs) {
+ const wr::WrPipelineInfo& info = aFrameEpochs->Raw();
+ bool didPaintContent = false;
+
+ // Check if a non-root pipeline has updated to a new epoch.
+ // We treat all non-root pipelines as "content" pipelines, even if they're
+ // not fed by content paints, such as videos (see bug 1665512).
+ for (const auto& epoch : info.epochs) {
+ const wr::PipelineId pipelineId = epoch.pipeline_id;
+
+ if (pipelineId == mRootPipelineId) {
+ continue;
+ }
+
+ const auto it = mContentPipelineEpochs.find(AsUint64(pipelineId));
+ if (it == mContentPipelineEpochs.end() || it->second != epoch.epoch) {
+ // This pipeline has updated since last render or has newly rendered.
+ didPaintContent = true;
+ mContentPipelineEpochs[AsUint64(pipelineId)] = epoch.epoch;
+ }
+ }
+
+ for (const auto& removedPipeline : info.removed_pipelines) {
+ const wr::PipelineId pipelineId = removedPipeline.pipeline_id;
+ if (pipelineId == mRootPipelineId) {
+ continue;
+ }
+ mContentPipelineEpochs.erase(AsUint64(pipelineId));
+ }
+
+ return didPaintContent;
+}
+
+Maybe<layers::FrameRecording> RendererOGL::EndRecording() {
+ if (!mCompositionRecorder) {
+ MOZ_DIAGNOSTIC_ASSERT(
+ false, "Attempted to get frames from a window that was not recording.");
+ return Nothing();
+ }
+
+ auto maybeRecording = mCompositionRecorder->GetRecording();
+
+ wr_renderer_release_composition_recorder_structures(mRenderer);
+
+ mCompositor->MaybeRequestAllowFrameRecording(false);
+ mCompositionRecorder = nullptr;
+
+ return maybeRecording;
+}
+
+void RendererOGL::FlushPipelineInfo() {
+ RefPtr<WebRenderPipelineInfo> info = new WebRenderPipelineInfo;
+ wr_renderer_flush_pipeline_info(mRenderer, &info->Raw());
+ mLastPipelineInfo = info;
+}
+
+RenderTextureHost* RendererOGL::GetRenderTexture(
+ wr::ExternalImageId aExternalImageId) {
+ return mThread->GetRenderTexture(aExternalImageId);
+}
+
+void RendererOGL::AccumulateMemoryReport(MemoryReport* aReport) {
+ wr_renderer_accumulate_memory_report(GetRenderer(), aReport, swgl());
+
+ LayoutDeviceIntSize size = mCompositor->GetBufferSize();
+
+ // Assume BGRA8 for the format since it's not exposed anywhere,
+ // and all compositor backends should be using that.
+ uintptr_t swapChainSize = size.width * size.height *
+ BytesPerPixel(gfx::SurfaceFormat::B8G8R8A8) *
+ (mCompositor->UseTripleBuffering() ? 3 : 2);
+ aReport->swap_chain += swapChainSize;
+}
+
+void RendererOGL::SetProfilerUI(const nsACString& aUI) {
+ wr_renderer_set_profiler_ui(GetRenderer(), (const uint8_t*)aUI.BeginReading(),
+ aUI.Length());
+}
+
+} // namespace wr
+} // namespace mozilla