Commit 0be396a2 authored by kylechar's avatar kylechar Committed by Commit Bot

Fix Windows overlay texture lifetime

With SkiaRenderer on Windows overlay textures weren't kept alive after
the client destroys them if still in use. Implement something in
SkiaOutputDeviceGL that is similar SkiaOutputDeviceBufferQueue where we
keep a map of mailboxes to textures to keep alive. Instead of using
SharedImageRepresentationOverlay we have to use GLImage still as not all
Windows video paths are using shared image.

Bug: 1136194
Change-Id: Ibeb5726b914058c0868a90dcc9bca74a8499c660
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2462038Reviewed-by: default avatarSunny Sachanandani <sunnyps@chromium.org>
Reviewed-by: default avatarVasiliy Telezhnikov <vasilyt@chromium.org>
Commit-Queue: kylechar <kylechar@chromium.org>
Cr-Commit-Position: refs/heads/master@{#817164}
parent 5f03bc04
...@@ -4,11 +4,11 @@ ...@@ -4,11 +4,11 @@
#include "components/viz/service/display_embedder/skia_output_device_gl.h" #include "components/viz/service/display_embedder/skia_output_device_gl.h"
#include <tuple>
#include <utility> #include <utility>
#include "base/bind_helpers.h" #include "base/bind_helpers.h"
#include "base/debug/alias.h" #include "base/debug/alias.h"
#include "build/build_config.h"
#include "components/viz/common/gpu/context_lost_reason.h" #include "components/viz/common/gpu/context_lost_reason.h"
#include "components/viz/service/display/dc_layer_overlay.h" #include "components/viz/service/display/dc_layer_overlay.h"
#include "gpu/command_buffer/common/swap_buffers_complete_params.h" #include "gpu/command_buffer/common/swap_buffers_complete_params.h"
...@@ -49,6 +49,51 @@ NOINLINE void CheckForLoopFailures() { ...@@ -49,6 +49,51 @@ NOINLINE void CheckForLoopFailures() {
} // namespace } // namespace
// Holds reference needed to keep overlay textures alive. Can either hold a
// shared image or legacy GL texture.
// TODO(kylechar): Merge with SkiaOutputDeviceBufferQueue::OverlayData when we
// dont need to support TexturePassthrough anymore.
class SkiaOutputDeviceGL::OverlayData {
public:
// TODO(crbug.com/1011555): Remove ability to hold TexturePassthrough after
// all Window video paths use shared image API.
explicit OverlayData(scoped_refptr<gpu::gles2::TexturePassthrough> texture)
: texture_(std::move(texture)) {}
OverlayData(
std::unique_ptr<gpu::SharedImageRepresentationOverlay> representation,
std::unique_ptr<gpu::SharedImageRepresentationOverlay::ScopedReadAccess>
scoped_read_access)
: representation_(std::move(representation)),
scoped_read_access_(std::move(scoped_read_access)) {}
~OverlayData() = default;
OverlayData(OverlayData&& other) = default;
OverlayData& operator=(OverlayData&& other) {
texture_ = std::move(other.texture_);
// Must happen in the same order as destruction to avoid having
// |scoped_read_access_| outlive |representation_|.
scoped_read_access_ = std::move(other.scoped_read_access_);
representation_ = std::move(other.representation_);
return *this;
}
scoped_refptr<gl::GLImage> GetImage() {
if (texture_)
return texture_->GetLevelImage(texture_->target(), 0);
DCHECK(scoped_read_access_);
return scoped_read_access_->gl_image();
}
private:
std::unique_ptr<gpu::SharedImageRepresentationOverlay> representation_;
std::unique_ptr<gpu::SharedImageRepresentationOverlay::ScopedReadAccess>
scoped_read_access_;
scoped_refptr<gpu::gles2::TexturePassthrough> texture_;
};
SkiaOutputDeviceGL::SkiaOutputDeviceGL( SkiaOutputDeviceGL::SkiaOutputDeviceGL(
gpu::MailboxManager* mailbox_manager, gpu::MailboxManager* mailbox_manager,
gpu::SharedImageRepresentationFactory* shared_image_representation_factory, gpu::SharedImageRepresentationFactory* shared_image_representation_factory,
...@@ -247,14 +292,14 @@ void SkiaOutputDeviceGL::SwapBuffers( ...@@ -247,14 +292,14 @@ void SkiaOutputDeviceGL::SwapBuffers(
gfx::Size(sk_surface_->width(), sk_surface_->height()); gfx::Size(sk_surface_->width(), sk_surface_->height());
if (supports_async_swap_) { if (supports_async_swap_) {
auto callback = base::BindOnce(&SkiaOutputDeviceGL::DoFinishSwapBuffers, auto callback = base::BindOnce(
weak_ptr_factory_.GetWeakPtr(), surface_size, &SkiaOutputDeviceGL::DoFinishSwapBuffersAsync,
std::move(latency_info)); weak_ptr_factory_.GetWeakPtr(), surface_size, std::move(latency_info));
gl_surface_->SwapBuffersAsync(std::move(callback), std::move(feedback)); gl_surface_->SwapBuffersAsync(std::move(callback), std::move(feedback));
} else { } else {
gfx::SwapResult result = gl_surface_->SwapBuffers(std::move(feedback)); gfx::SwapResult result = gl_surface_->SwapBuffers(std::move(feedback));
FinishSwapBuffers(gfx::SwapCompletionResult(result), surface_size, DoFinishSwapBuffers(surface_size, std::move(latency_info),
std::move(latency_info)); gfx::SwapCompletionResult(result));
} }
} }
...@@ -268,17 +313,17 @@ void SkiaOutputDeviceGL::PostSubBuffer( ...@@ -268,17 +313,17 @@ void SkiaOutputDeviceGL::PostSubBuffer(
gfx::Size(sk_surface_->width(), sk_surface_->height()); gfx::Size(sk_surface_->width(), sk_surface_->height());
if (supports_async_swap_) { if (supports_async_swap_) {
auto callback = base::BindOnce(&SkiaOutputDeviceGL::DoFinishSwapBuffers, auto callback = base::BindOnce(
weak_ptr_factory_.GetWeakPtr(), surface_size, &SkiaOutputDeviceGL::DoFinishSwapBuffersAsync,
std::move(latency_info)); weak_ptr_factory_.GetWeakPtr(), surface_size, std::move(latency_info));
gl_surface_->PostSubBufferAsync(rect.x(), rect.y(), rect.width(), gl_surface_->PostSubBufferAsync(rect.x(), rect.y(), rect.width(),
rect.height(), std::move(callback), rect.height(), std::move(callback),
std::move(feedback)); std::move(feedback));
} else { } else {
gfx::SwapResult result = gl_surface_->PostSubBuffer( gfx::SwapResult result = gl_surface_->PostSubBuffer(
rect.x(), rect.y(), rect.width(), rect.height(), std::move(feedback)); rect.x(), rect.y(), rect.width(), rect.height(), std::move(feedback));
FinishSwapBuffers(gfx::SwapCompletionResult(result), surface_size, DoFinishSwapBuffers(surface_size, std::move(latency_info),
std::move(latency_info)); gfx::SwapCompletionResult(result));
} }
} }
...@@ -291,24 +336,43 @@ void SkiaOutputDeviceGL::CommitOverlayPlanes( ...@@ -291,24 +336,43 @@ void SkiaOutputDeviceGL::CommitOverlayPlanes(
gfx::Size(sk_surface_->width(), sk_surface_->height()); gfx::Size(sk_surface_->width(), sk_surface_->height());
if (supports_async_swap_) { if (supports_async_swap_) {
auto callback = base::BindOnce(&SkiaOutputDeviceGL::DoFinishSwapBuffers, auto callback = base::BindOnce(
weak_ptr_factory_.GetWeakPtr(), surface_size, &SkiaOutputDeviceGL::DoFinishSwapBuffersAsync,
std::move(latency_info)); weak_ptr_factory_.GetWeakPtr(), surface_size, std::move(latency_info));
gl_surface_->CommitOverlayPlanesAsync(std::move(callback), gl_surface_->CommitOverlayPlanesAsync(std::move(callback),
std::move(feedback)); std::move(feedback));
} else { } else {
FinishSwapBuffers( gfx::SwapResult result =
gfx::SwapCompletionResult( gl_surface_->CommitOverlayPlanes(std::move(feedback));
gl_surface_->CommitOverlayPlanes(std::move(feedback))), DoFinishSwapBuffers(surface_size, std::move(latency_info),
surface_size, std::move(latency_info)); gfx::SwapCompletionResult(result));
} }
} }
void SkiaOutputDeviceGL::DoFinishSwapBuffersAsync(
const gfx::Size& size,
std::vector<ui::LatencyInfo> latency_info,
gfx::SwapCompletionResult result) {
DCHECK(!result.gpu_fence);
FinishSwapBuffers(std::move(result), size, latency_info);
}
void SkiaOutputDeviceGL::DoFinishSwapBuffers( void SkiaOutputDeviceGL::DoFinishSwapBuffers(
const gfx::Size& size, const gfx::Size& size,
std::vector<ui::LatencyInfo> latency_info, std::vector<ui::LatencyInfo> latency_info,
gfx::SwapCompletionResult result) { gfx::SwapCompletionResult result) {
DCHECK(!result.gpu_fence); DCHECK(!result.gpu_fence);
// Remove entries from |overlays_| for textures that weren't scheduled as an
// overlay this frame.
if (!overlays_.empty()) {
base::EraseIf(overlays_, [this](auto& entry) {
const gpu::Mailbox& mailbox = entry.first;
return !scheduled_overlay_mailboxes_.contains(mailbox);
});
scheduled_overlay_mailboxes_.clear();
}
FinishSwapBuffers(std::move(result), size, latency_info); FinishSwapBuffers(std::move(result), size, latency_info);
} }
...@@ -333,15 +397,17 @@ void SkiaOutputDeviceGL::ScheduleOverlays( ...@@ -333,15 +397,17 @@ void SkiaOutputDeviceGL::ScheduleOverlays(
// Get GLImages for DC layer textures. // Get GLImages for DC layer textures.
bool success = true; bool success = true;
for (size_t i = 0; i < DCLayerOverlay::kNumResources; ++i) { for (size_t i = 0; i < DCLayerOverlay::kNumResources; ++i) {
if (i > 0 && dc_layer.mailbox[i].IsZero()) const gpu::Mailbox& mailbox = dc_layer.mailbox[i];
if (i > 0 && mailbox.IsZero())
break; break;
auto image = GetGLImageForMailbox(dc_layer.mailbox[i]); auto image = GetGLImageForMailbox(mailbox);
if (!image) { if (!image) {
success = false; success = false;
break; break;
} }
scheduled_overlay_mailboxes_.insert(mailbox);
image->SetColorSpace(dc_layer.color_space); image->SetColorSpace(dc_layer.color_space);
params.images[i] = std::move(image); params.images[i] = std::move(image);
} }
...@@ -386,38 +452,39 @@ void SkiaOutputDeviceGL::EndPaint() {} ...@@ -386,38 +452,39 @@ void SkiaOutputDeviceGL::EndPaint() {}
scoped_refptr<gl::GLImage> SkiaOutputDeviceGL::GetGLImageForMailbox( scoped_refptr<gl::GLImage> SkiaOutputDeviceGL::GetGLImageForMailbox(
const gpu::Mailbox& mailbox) { const gpu::Mailbox& mailbox) {
auto it = overlays_.find(mailbox);
if (it != overlays_.end())
return it->second.GetImage();
// TODO(crbug.com/1005306): Stop using MailboxManager for lookup once all // TODO(crbug.com/1005306): Stop using MailboxManager for lookup once all
// clients are using SharedImageInterface to create textures. // clients are using SharedImageInterface to create textures.
// For example, the legacy mailbox still uses GL textures (no overlay) // For example, the legacy mailbox still uses GL textures (no overlay)
// and is still used. // and is still used.
auto* texture_base = mailbox_manager_->ConsumeTexture(mailbox); if (!mailbox.IsSharedImage()) {
if (!texture_base) { auto* texture_base = mailbox_manager_->ConsumeTexture(mailbox);
auto overlay = if (!texture_base)
shared_image_representation_factory_->ProduceOverlay(mailbox);
if (!overlay)
return nullptr; return nullptr;
// Return GLImage since the ScopedReadAccess isn't being held by anyone. DCHECK_EQ(texture_base->GetType(), gpu::TextureBase::Type::kPassthrough);
// TODO(crbug.com/1011555): Have SkiaOutputSurfaceImplOnGpu hold on to the std::tie(it, std::ignore) = overlays_.try_emplace(
// ScopedReadAccess for overlays like it does for PromiseImage based mailbox,
// resources. base::WrapRefCounted(
std::unique_ptr<gpu::SharedImageRepresentationOverlay::ScopedReadAccess> static_cast<gpu::gles2::TexturePassthrough*>(texture_base)));
scoped_overlay_read_access = return it->second.GetImage();
overlay->BeginScopedReadAccess(/*need_gl_image=*/true);
DCHECK(scoped_overlay_read_access);
return scoped_overlay_read_access->gl_image();
} }
if (texture_base->GetType() == gpu::TextureBase::Type::kPassthrough) { auto overlay = shared_image_representation_factory_->ProduceOverlay(mailbox);
gpu::gles2::TexturePassthrough* texture = if (!overlay)
static_cast<gpu::gles2::TexturePassthrough*>(texture_base); return nullptr;
return texture->GetLevelImage(texture->target(), 0);
} else { std::unique_ptr<gpu::SharedImageRepresentationOverlay::ScopedReadAccess>
DCHECK_EQ(texture_base->GetType(), gpu::TextureBase::Type::kValidated); scoped_overlay_read_access =
gpu::gles2::Texture* texture = overlay->BeginScopedReadAccess(/*need_gl_image=*/true);
static_cast<gpu::gles2::Texture*>(texture_base); DCHECK(scoped_overlay_read_access);
return texture->GetLevelImage(texture->target(), 0);
} std::tie(it, std::ignore) = overlays_.try_emplace(
mailbox, std::move(overlay), std::move(scoped_overlay_read_access));
return it->second.GetImage();
} }
} // namespace viz } // namespace viz
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
...@@ -67,11 +69,18 @@ class SkiaOutputDeviceGL final : public SkiaOutputDevice { ...@@ -67,11 +69,18 @@ class SkiaOutputDeviceGL final : public SkiaOutputDevice {
void EndPaint() override; void EndPaint() override;
private: private:
// Used as callback for SwapBuffersAsync and PostSubBufferAsync to finish class OverlayData;
// operation
// Use instead of calling FinishSwapBuffers() directly. On Windows this cleans
// up old entries in |overlays_|.
void DoFinishSwapBuffers(const gfx::Size& size, void DoFinishSwapBuffers(const gfx::Size& size,
std::vector<ui::LatencyInfo> latency_info, std::vector<ui::LatencyInfo> latency_info,
gfx::SwapCompletionResult result); gfx::SwapCompletionResult result);
// Used as callback for SwapBuffersAsync and PostSubBufferAsync to finish
// operation
void DoFinishSwapBuffersAsync(const gfx::Size& size,
std::vector<ui::LatencyInfo> latency_info,
gfx::SwapCompletionResult result);
scoped_refptr<gl::GLImage> GetGLImageForMailbox(const gpu::Mailbox& mailbox); scoped_refptr<gl::GLImage> GetGLImageForMailbox(const gpu::Mailbox& mailbox);
...@@ -86,6 +95,12 @@ class SkiaOutputDeviceGL final : public SkiaOutputDevice { ...@@ -86,6 +95,12 @@ class SkiaOutputDeviceGL final : public SkiaOutputDevice {
sk_sp<SkSurface> sk_surface_; sk_sp<SkSurface> sk_surface_;
// Mailboxes of overlays scheduled in the current frame.
base::flat_set<gpu::Mailbox> scheduled_overlay_mailboxes_;
// Holds references to overlay textures so they aren't destroyed while in use.
base::flat_map<gpu::Mailbox, OverlayData> overlays_;
uint64_t backbuffer_estimated_size_ = 0; uint64_t backbuffer_estimated_size_ = 0;
base::WeakPtrFactory<SkiaOutputDeviceGL> weak_ptr_factory_{this}; base::WeakPtrFactory<SkiaOutputDeviceGL> weak_ptr_factory_{this};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment