blob: 66f3479546928a831dd791e3d4a7885ca3a676e3 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/viz/service/display_embedder/skia_output_device_x11.h"
#include <utility>
#include <vector>
#include "base/logging.h"
#include "base/memory/ref_counted_memory.h"
#include "third_party/skia/include/core/SkImageInfo.h"
#include "ui/base/x/x11_util.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/x/xproto.h"
namespace viz {
namespace {
x11::GraphicsContext CreateGC(x11::Connection* connection, x11::Window window) {
auto gc = connection->GenerateId<x11::GraphicsContext>();
connection->CreateGC({gc, window});
return gc;
}
} // namespace
// static
std::unique_ptr<SkiaOutputDeviceX11> SkiaOutputDeviceX11::Create(
scoped_refptr<gpu::SharedContextState> context_state,
gfx::AcceleratedWidget widget,
gpu::MemoryTracker* memory_tracker,
DidSwapBufferCompleteCallback did_swap_buffer_complete_callback) {
auto window = static_cast<x11::Window>(widget);
auto attributes =
x11::Connection::Get()->GetWindowAttributes({window}).Sync();
if (!attributes) {
DLOG(ERROR) << "Failed to get attributes for window "
<< static_cast<uint32_t>(window);
return {};
}
return std::make_unique<SkiaOutputDeviceX11>(
base::PassKey<SkiaOutputDeviceX11>(), std::move(context_state), window,
attributes->visual, memory_tracker, did_swap_buffer_complete_callback);
}
SkiaOutputDeviceX11::SkiaOutputDeviceX11(
base::PassKey<SkiaOutputDeviceX11> pass_key,
scoped_refptr<gpu::SharedContextState> context_state,
x11::Window window,
x11::VisualId visual,
gpu::MemoryTracker* memory_tracker,
DidSwapBufferCompleteCallback did_swap_buffer_complete_callback)
: SkiaOutputDeviceOffscreen(context_state,
gfx::SurfaceOrigin::kTopLeft,
true /* has_alpha */,
memory_tracker,
did_swap_buffer_complete_callback),
connection_(x11::Connection::Get()),
window_(window),
visual_(visual),
gc_(CreateGC(connection_, window_)) {
// |capabilities_| should be set by SkiaOutputDeviceOffscreen.
DCHECK_EQ(capabilities_.output_surface_origin, gfx::SurfaceOrigin::kTopLeft);
DCHECK(capabilities_.supports_post_sub_buffer);
}
SkiaOutputDeviceX11::~SkiaOutputDeviceX11() {
connection_->FreeGC({gc_});
}
bool SkiaOutputDeviceX11::Reshape(const SkImageInfo& image_info,
const gfx::ColorSpace& color_space,
int sample_count,
float device_scale_factor,
gfx::OverlayTransform transform) {
if (!SkiaOutputDeviceOffscreen::Reshape(image_info, color_space, sample_count,
device_scale_factor, transform)) {
return false;
}
auto ii = SkImageInfo::MakeN32(image_info.width(), image_info.height(),
kOpaque_SkAlphaType);
std::vector<uint8_t> mem(ii.computeMinByteSize());
pixels_ = base::RefCountedBytes::TakeVector(&mem);
return true;
}
void SkiaOutputDeviceX11::Present(const absl::optional<gfx::Rect>& update_rect,
BufferPresentedCallback feedback,
OutputSurfaceFrame frame) {
gfx::Rect rect = update_rect.value_or(
gfx::Rect(0, 0, sk_surface_->width(), sk_surface_->height()));
StartSwapBuffers(std::move(feedback));
if (!rect.IsEmpty()) {
auto ii =
SkImageInfo::MakeN32(rect.width(), rect.height(), kOpaque_SkAlphaType);
DCHECK_GE(pixels_->size(), ii.computeMinByteSize());
SkPixmap sk_pixmap(ii, pixels_->data(), ii.minRowBytes());
bool result = sk_surface_->readPixels(sk_pixmap, rect.x(), rect.y());
LOG_IF(FATAL, !result) << "Failed to read pixels from offscreen SkSurface.";
// TODO(penghuang): Switch to XShmPutImage.
ui::DrawPixmap(x11::Connection::Get(), visual_, window_, gc_, sk_pixmap,
0 /* src_x */, 0 /* src_y */, rect.x() /* dst_x */,
rect.y() /* dst_y */, rect.width(), rect.height());
connection_->Flush();
}
FinishSwapBuffers(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_ACK),
gfx::Size(sk_surface_->width(), sk_surface_->height()),
std::move(frame));
}
} // namespace viz