blob: 9f209723a481962ff05d9f9e235cdd59a12755d1 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// 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 "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h"
#include "third_party/skia/include/gpu/vk/GrVkTypes.h"
#include "ui/base/x/x11_util.h"
#include "ui/base/x/x11_util_internal.h"
#include "ui/gfx/geometry/rect.h"
namespace viz {
SkiaOutputDeviceX11::SkiaOutputDeviceX11(
GrContext* gr_context,
gfx::AcceleratedWidget widget,
DidSwapBufferCompleteCallback did_swap_buffer_complete_callback)
: SkiaOutputDeviceOffscreen(gr_context,
true /* flipped */,
true /* has_alpha */,
did_swap_buffer_complete_callback),
display_(gfx::GetXDisplay()),
widget_(widget),
gc_(XCreateGC(display_, widget_, 0, nullptr)) {
int result = XGetWindowAttributes(display_, widget_, &attributes_);
LOG_IF(FATAL, !result) << "XGetWindowAttributes failed for window "
<< widget_;
bpp_ = gfx::BitsPerPixelForPixmapDepth(display_, attributes_.depth);
support_rendr_ = ui::QueryRenderSupport(display_);
// |capabilities_| should be set by SkiaOutputDeviceOffscreen.
DCHECK(capabilities_.flipped_output_surface);
DCHECK(capabilities_.supports_post_sub_buffer);
}
SkiaOutputDeviceX11::~SkiaOutputDeviceX11() {
XFreeGC(display_, gc_);
}
void SkiaOutputDeviceX11::Reshape(const gfx::Size& size,
float device_scale_factor,
const gfx::ColorSpace& color_space,
bool has_alpha) {
SkiaOutputDeviceOffscreen::Reshape(size, device_scale_factor, color_space,
has_alpha);
auto ii =
SkImageInfo::MakeN32(size.width(), size.height(), kOpaque_SkAlphaType);
pixels_.reserve(ii.computeMinByteSize());
}
gfx::SwapResponse SkiaOutputDeviceX11::SwapBuffers(
const GrBackendSemaphore& semaphore,
BufferPresentedCallback feedback) {
return PostSubBuffer(
gfx::Rect(0, 0, draw_surface_->width(), draw_surface_->height()),
semaphore, std::move(feedback));
}
gfx::SwapResponse SkiaOutputDeviceX11::PostSubBuffer(
const gfx::Rect& rect,
const GrBackendSemaphore& semaphore,
BufferPresentedCallback feedback) {
StartSwapBuffers(std::move(feedback));
auto ii =
SkImageInfo::MakeN32(rect.width(), rect.height(), kOpaque_SkAlphaType);
DCHECK_GE(pixels_.capacity(), ii.computeMinByteSize());
SkPixmap sk_pixmap(ii, pixels_.data(), ii.minRowBytes());
bool result = draw_surface_->readPixels(sk_pixmap, rect.x(), rect.y());
LOG_IF(FATAL, !result) << "Failed to read pixels from offscreen SkSurface.";
if (bpp_ == 32 || bpp_ == 16) {
// gfx::PutARGBImage() only supports 16 and 32 bpp.
// TODO(penghuang): Switch to XShmPutImage.
gfx::PutARGBImage(display_, attributes_.visual, attributes_.depth, widget_,
gc_, static_cast<const uint8_t*>(sk_pixmap.addr()),
rect.width(), rect.height(), 0 /* src_x */, 0 /* src_y */,
rect.x() /* dst_x */, rect.y() /* dst_y */, rect.width(),
rect.height());
} else if (support_rendr_) {
Pixmap pixmap =
XCreatePixmap(display_, widget_, rect.width(), rect.height(), 32);
GC gc = XCreateGC(display_, pixmap, 0, nullptr);
XImage image = {};
image.width = rect.width();
image.height = rect.height();
image.depth = 32;
image.bits_per_pixel = 32;
image.format = ZPixmap;
image.byte_order = LSBFirst;
image.bitmap_unit = 8;
image.bitmap_bit_order = LSBFirst;
image.bytes_per_line = sk_pixmap.rowBytes();
image.red_mask = 0xff << SK_R32_SHIFT;
image.green_mask = 0xff << SK_G32_SHIFT;
image.blue_mask = 0xff << SK_B32_SHIFT;
image.data = const_cast<char*>(static_cast<const char*>(sk_pixmap.addr()));
XPutImage(display_, pixmap, gc, &image, 0 /* src_x */, 0 /* src_y */,
0 /* dest_x */, 0 /* dest_y */, rect.width(), rect.height());
XFreeGC(display_, gc);
Picture picture = XRenderCreatePicture(
display_, pixmap, ui::GetRenderARGB32Format(display_), 0, nullptr);
XRenderPictFormat* pictformat =
XRenderFindVisualFormat(display_, attributes_.visual);
Picture dest_picture =
XRenderCreatePicture(display_, widget_, pictformat, 0, nullptr);
XRenderComposite(display_,
PictOpSrc, // op
picture, // src
0, // mask
dest_picture, // dest
0, // src_x
0, // src_y
0, // mask_x
0, // mask_y
rect.x(), // dest_x
rect.y(), // dest_y
rect.width(), // width
rect.height()); // height
XRenderFreePicture(display_, picture);
XRenderFreePicture(display_, dest_picture);
XFreePixmap(display_, pixmap);
} else {
NOTIMPLEMENTED();
}
XFlush(display_);
return FinishSwapBuffers(gfx::SwapResult::SWAP_ACK);
}
} // namespace viz