Skip to content

Commit

Permalink
Add synchronous SkImage readback through RasterInterface
Browse files Browse the repository at this point in the history
This change sets up PaintImage to lazily trigger a readback of SkImage
via MailboxTextureBacking. It also implements the readback API itself in
RasterImplementation. This API is required for OOPR-Canvas as certain
PaintImage users need image pixels in CPU memory.

More details about overall PaintImage effort: crbug.com/1023259
Info about the OOPR-Canvas2D project: crbug.com/1018894

Bug: 1023281, 1031050
Change-Id: Ib1a1ee3c8ceef72b045277540393a5f55f3135ac
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2280391
Reviewed-by: Dan Sanders <sandersd@chromium.org>
Reviewed-by: Khushal <khushalsagar@chromium.org>
Commit-Queue: Jonah Chin <jochin@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#789697}
  • Loading branch information
Jonah Chin authored and Commit Bot committed Jul 17, 2020
1 parent 1cd8fb9 commit d9d7a73
Show file tree
Hide file tree
Showing 25 changed files with 586 additions and 49 deletions.
42 changes: 42 additions & 0 deletions cc/paint/oop_pixeltest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -894,6 +894,7 @@ class TestMailboxBacking : public TextureBacking {
const SkImageInfo& GetSkImageInfo() override { return info_; }
gpu::Mailbox GetMailbox() const override { return mailbox_; }
sk_sp<SkImage> GetAcceleratedSkImage() override { return nullptr; }
sk_sp<SkImage> GetSkImageViaReadback() override { return nullptr; }

private:
gpu::Mailbox mailbox_;
Expand Down Expand Up @@ -1870,6 +1871,47 @@ TEST_F(OopPixelTest, ConvertYUVToRGB) {
sii->DestroySharedImage(sync_token, v_mailbox);
}

TEST_F(OopPixelTest, ReadbackImagePixels) {
RasterOptions options(gfx::Size(16, 16));
SkImageInfo dest_info = SkImageInfo::MakeN32Premul(
options.resource_size.width(), options.resource_size.height(),
gfx::ColorSpace::CreateSRGB().ToSkColorSpace());

SkBitmap expected_bitmap;
expected_bitmap.allocPixels(dest_info);

SkCanvas canvas(expected_bitmap);
canvas.drawColor(SK_ColorMAGENTA);
SkPaint green;
green.setColor(SK_ColorGREEN);
canvas.drawRect(SkRect::MakeXYWH(1, 2, 3, 4), green);

auto* ri = raster_context_provider_->RasterInterface();
auto* sii = raster_context_provider_->SharedImageInterface();
gpu::Mailbox mailbox = CreateMailboxSharedImage(
ri, sii, options, viz::ResourceFormat::RGBA_8888);
ri->OrderingBarrierCHROMIUM();

gpu::gles2::GLES2Interface* gl = gles2_context_provider_->ContextGL();
UploadPixels(gl, mailbox, options.resource_size, GL_RGBA, GL_UNSIGNED_BYTE,
expected_bitmap.getPixels());
gl->OrderingBarrierCHROMIUM();

SkBitmap actual_bitmap;
actual_bitmap.allocPixels(dest_info);

ri->ReadbackImagePixels(mailbox, dest_info, dest_info.minRowBytes(), 0, 0,
actual_bitmap.getPixels());
EXPECT_EQ(ri->GetError(), static_cast<unsigned>(GL_NO_ERROR));
ri->OrderingBarrierCHROMIUM();

ExpectEquals(actual_bitmap, expected_bitmap);

gpu::SyncToken sync_token;
gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
sii->DestroySharedImage(sync_token, mailbox);
}

// A workaround on Android that forces the use of GLES 2.0 instead of 3.0
// prevents the use of the GL_RG textures required for NV12 format. This
// test will be reactiviated on Android once the workaround is removed.
Expand Down
8 changes: 6 additions & 2 deletions cc/paint/paint_image.cc
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,12 @@ const sk_sp<SkImage>& PaintImage::GetSkImage() const {
return cached_sk_image_;
}

const sk_sp<SkImage>& PaintImage::GetRasterSkImage() const {
return cached_sk_image_;
sk_sp<SkImage> PaintImage::GetRasterSkImage() const {
if (cached_sk_image_)
return cached_sk_image_;
else if (texture_backing_)
return texture_backing_->GetSkImageViaReadback();
return nullptr;
}

SkImageInfo PaintImage::GetSkImageInfo() const {
Expand Down
2 changes: 1 addition & 1 deletion cc/paint/paint_image.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ class CC_PAINT_EXPORT PaintImage {
// Avoid using this API unless actual pixels are needed.
// For other cases, prefer using PaintImage APIs directly or use
// GetSkImageInfo() for metadata about the SkImage.
const sk_sp<SkImage>& GetRasterSkImage() const;
sk_sp<SkImage> GetRasterSkImage() const;

SkImageInfo GetSkImageInfo() const;

Expand Down
8 changes: 6 additions & 2 deletions cc/paint/texture_backing.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,13 @@ class CC_PAINT_EXPORT TextureBacking : public SkRefCnt {
// Returns the shared image mailbox backing for this texture.
virtual gpu::Mailbox GetMailbox() const = 0;

// Returns a texture backed SkImage wrapping the mailbox. Only supported if
// the context used to create this image has a valid GrContext.
// Returns a texture backed SkImage. Only supported if the context used to
// create this image has a valid GrContext.
virtual sk_sp<SkImage> GetAcceleratedSkImage() = 0;

// Gets SkImage via a readback from GPU memory. Use this when actual SkImage
// pixel data is required in software.
virtual sk_sp<SkImage> GetSkImageViaReadback() = 0;
};

} // namespace cc
Expand Down
8 changes: 8 additions & 0 deletions gpu/command_buffer/build_raster_cmd_buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,14 @@
'unit_test': False,
'trace_level': 2,
},
'ReadbackImagePixelsINTERNAL': {
'decoder_func': 'DoReadbackImagePixelsINTERNAL',
'internal': True,
'type': 'PUT',
'count': 16, # GL_MAILBOX_SIZE_CHROMIUM
'unit_test': False,
'trace_level': 2,
},
'ConvertYUVMailboxesToRGBINTERNAL': {
'decoder_func': 'DoConvertYUVMailboxesToRGBINTERNAL',
'internal': True,
Expand Down
22 changes: 22 additions & 0 deletions gpu/command_buffer/client/raster_cmd_helper_autogen.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,28 @@ void WritePixelsINTERNALImmediate(GLint x_offset,
}
}

void ReadbackImagePixelsINTERNALImmediate(GLint src_x,
GLint src_y,
GLuint dst_width,
GLuint dst_height,
GLuint row_bytes,
GLuint dst_sk_color_type,
GLuint dst_sk_alpha_type,
GLint shm_id,
GLuint shm_offset,
GLuint pixels_offset,
const GLbyte* mailbox) {
const uint32_t size =
raster::cmds::ReadbackImagePixelsINTERNALImmediate::ComputeSize();
raster::cmds::ReadbackImagePixelsINTERNALImmediate* c =
GetImmediateCmdSpaceTotalSize<
raster::cmds::ReadbackImagePixelsINTERNALImmediate>(size);
if (c) {
c->Init(src_x, src_y, dst_width, dst_height, row_bytes, dst_sk_color_type,
dst_sk_alpha_type, shm_id, shm_offset, pixels_offset, mailbox);
}
}

void ConvertYUVMailboxesToRGBINTERNALImmediate(GLenum planes_yuv_color_space,
GLboolean is_nv12,
const GLbyte* mailboxes) {
Expand Down
39 changes: 39 additions & 0 deletions gpu/command_buffer/client/raster_implementation.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1306,6 +1306,45 @@ void RasterImplementation::ReadbackYUVPixelsAsync(
NOTREACHED();
}

void RasterImplementation::ReadbackImagePixels(
const gpu::Mailbox& source_mailbox,
const SkImageInfo& dst_info,
GLuint dst_row_bytes,
int src_x,
int src_y,
void* dst_pixels) {
DCHECK_GE(dst_row_bytes, dst_info.minRowBytes());

// Get the size of the SkColorSpace while maintaining 8-byte alignment.
GLuint pixels_offset = 0;
if (dst_info.colorSpace()) {
pixels_offset = base::bits::Align(
dst_info.colorSpace()->writeToMemory(nullptr), sizeof(uint64_t));
}

GLuint dst_size = dst_info.computeByteSize(dst_row_bytes);
GLuint total_size =
pixels_offset + base::bits::Align(dst_size, sizeof(uint64_t));

ScopedSharedMemoryPtr scoped_shared_memory(total_size, transfer_buffer_,
mapped_memory_.get(), helper());
GLint shm_id = scoped_shared_memory.shm_id();
GLuint shm_offset = scoped_shared_memory.offset();
void* address = scoped_shared_memory.address();

if (dst_info.colorSpace()) {
size_t bytes_written = dst_info.colorSpace()->writeToMemory(address);
DCHECK_LE(bytes_written, pixels_offset);
}

helper_->ReadbackImagePixelsINTERNALImmediate(
src_x, src_y, dst_info.width(), dst_info.height(), dst_row_bytes,
dst_info.colorType(), dst_info.alphaType(), shm_id, shm_offset,
pixels_offset, source_mailbox.name);
WaitForCmd();
memcpy(dst_pixels, static_cast<uint8_t*>(address) + pixels_offset, dst_size);
}

void RasterImplementation::IssueImageDecodeCacheEntryCreation(
base::span<const uint8_t> encoded_data,
const gfx::Size& output_size,
Expand Down
6 changes: 6 additions & 0 deletions gpu/command_buffer/client/raster_implementation.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,12 @@ class RASTER_EXPORT RasterImplementation : public RasterInterface,
const gfx::Point& paste_location,
base::OnceCallback<void()> release_mailbox,
base::OnceCallback<void(bool)> readback_done) override;
void ReadbackImagePixels(const gpu::Mailbox& source_mailbox,
const SkImageInfo& dst_info,
GLuint dst_row_bytes,
int src_x,
int src_y,
void* dst_pixels) override;
GLuint CreateAndConsumeForGpuRaster(const gpu::Mailbox& mailbox) override;
void DeleteGpuRasterTexture(GLuint texture) override;
void BeginGpuRaster() override;
Expand Down
10 changes: 10 additions & 0 deletions gpu/command_buffer/client/raster_implementation_gles.cc
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,16 @@ void RasterImplementationGLES::OnReleaseMailbox(
std::move(release_mailbox).Run();
}

void RasterImplementationGLES::ReadbackImagePixels(
const gpu::Mailbox& source_mailbox,
const SkImageInfo& dst_info,
GLuint dst_row_bytes,
int src_x,
int src_y,
void* dst_pixels) {
NOTREACHED();
}

GLuint RasterImplementationGLES::CreateAndConsumeForGpuRaster(
const gpu::Mailbox& mailbox) {
return mailbox.IsSharedImage()
Expand Down
7 changes: 7 additions & 0 deletions gpu/command_buffer/client/raster_implementation_gles.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,13 @@ class RASTER_EXPORT RasterImplementationGLES : public RasterInterface {
base::OnceCallback<void()> release_mailbox,
base::OnceCallback<void(bool)> readback_done) override;

void ReadbackImagePixels(const gpu::Mailbox& source_mailbox,
const SkImageInfo& dst_info,
GLuint dst_row_bytes,
int src_x,
int src_y,
void* dst_pixels) override;

// Raster via GrContext.
GLuint CreateAndConsumeForGpuRaster(const gpu::Mailbox& mailbox) override;
void DeleteGpuRasterTexture(GLuint texture) override;
Expand Down
9 changes: 9 additions & 0 deletions gpu/command_buffer/client/raster_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,15 @@ class RasterInterface : public InterfaceBase {
base::OnceCallback<void()> release_mailbox,
base::OnceCallback<void(bool)> readback_done) = 0;

// Synchronously does a readback of SkImage pixels from |source_mailbox| into
// caller-owned memory |dst_pixels|.
virtual void ReadbackImagePixels(const gpu::Mailbox& source_mailbox,
const SkImageInfo& dst_info,
GLuint dst_row_bytes,
int src_x,
int src_y,
void* dst_pixels) = 0;

// Raster via GrContext.
virtual GLuint CreateAndConsumeForGpuRaster(const gpu::Mailbox& mailbox) = 0;
virtual void DeleteGpuRasterTexture(GLuint texture) = 0;
Expand Down
112 changes: 112 additions & 0 deletions gpu/command_buffer/common/raster_cmd_format_autogen.h
Original file line number Diff line number Diff line change
Expand Up @@ -985,6 +985,118 @@ static_assert(
offsetof(WritePixelsINTERNALImmediate, pixels_offset) == 40,
"offset of WritePixelsINTERNALImmediate pixels_offset should be 40");

struct ReadbackImagePixelsINTERNALImmediate {
typedef ReadbackImagePixelsINTERNALImmediate ValueType;
static const CommandId kCmdId = kReadbackImagePixelsINTERNALImmediate;
static const cmd::ArgFlags kArgFlags = cmd::kAtLeastN;
static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(2);

static uint32_t ComputeDataSize() {
return static_cast<uint32_t>(sizeof(GLbyte) * 16);
}

static uint32_t ComputeSize() {
return static_cast<uint32_t>(sizeof(ValueType) + ComputeDataSize());
}

void SetHeader() { header.SetCmdByTotalSize<ValueType>(ComputeSize()); }

void Init(GLint _src_x,
GLint _src_y,
GLuint _dst_width,
GLuint _dst_height,
GLuint _row_bytes,
GLuint _dst_sk_color_type,
GLuint _dst_sk_alpha_type,
GLint _shm_id,
GLuint _shm_offset,
GLuint _pixels_offset,
const GLbyte* _mailbox) {
SetHeader();
src_x = _src_x;
src_y = _src_y;
dst_width = _dst_width;
dst_height = _dst_height;
row_bytes = _row_bytes;
dst_sk_color_type = _dst_sk_color_type;
dst_sk_alpha_type = _dst_sk_alpha_type;
shm_id = _shm_id;
shm_offset = _shm_offset;
pixels_offset = _pixels_offset;
memcpy(ImmediateDataAddress(this), _mailbox, ComputeDataSize());
}

void* Set(void* cmd,
GLint _src_x,
GLint _src_y,
GLuint _dst_width,
GLuint _dst_height,
GLuint _row_bytes,
GLuint _dst_sk_color_type,
GLuint _dst_sk_alpha_type,
GLint _shm_id,
GLuint _shm_offset,
GLuint _pixels_offset,
const GLbyte* _mailbox) {
static_cast<ValueType*>(cmd)->Init(
_src_x, _src_y, _dst_width, _dst_height, _row_bytes, _dst_sk_color_type,
_dst_sk_alpha_type, _shm_id, _shm_offset, _pixels_offset, _mailbox);
const uint32_t size = ComputeSize();
return NextImmediateCmdAddressTotalSize<ValueType>(cmd, size);
}

gpu::CommandHeader header;
int32_t src_x;
int32_t src_y;
uint32_t dst_width;
uint32_t dst_height;
uint32_t row_bytes;
uint32_t dst_sk_color_type;
uint32_t dst_sk_alpha_type;
int32_t shm_id;
uint32_t shm_offset;
uint32_t pixels_offset;
};

static_assert(sizeof(ReadbackImagePixelsINTERNALImmediate) == 44,
"size of ReadbackImagePixelsINTERNALImmediate should be 44");
static_assert(
offsetof(ReadbackImagePixelsINTERNALImmediate, header) == 0,
"offset of ReadbackImagePixelsINTERNALImmediate header should be 0");
static_assert(
offsetof(ReadbackImagePixelsINTERNALImmediate, src_x) == 4,
"offset of ReadbackImagePixelsINTERNALImmediate src_x should be 4");
static_assert(
offsetof(ReadbackImagePixelsINTERNALImmediate, src_y) == 8,
"offset of ReadbackImagePixelsINTERNALImmediate src_y should be 8");
static_assert(
offsetof(ReadbackImagePixelsINTERNALImmediate, dst_width) == 12,
"offset of ReadbackImagePixelsINTERNALImmediate dst_width should be 12");
static_assert(
offsetof(ReadbackImagePixelsINTERNALImmediate, dst_height) == 16,
"offset of ReadbackImagePixelsINTERNALImmediate dst_height should be 16");
static_assert(
offsetof(ReadbackImagePixelsINTERNALImmediate, row_bytes) == 20,
"offset of ReadbackImagePixelsINTERNALImmediate row_bytes should be 20");
static_assert(offsetof(ReadbackImagePixelsINTERNALImmediate,
dst_sk_color_type) == 24,
"offset of ReadbackImagePixelsINTERNALImmediate "
"dst_sk_color_type should be 24");
static_assert(offsetof(ReadbackImagePixelsINTERNALImmediate,
dst_sk_alpha_type) == 28,
"offset of ReadbackImagePixelsINTERNALImmediate "
"dst_sk_alpha_type should be 28");
static_assert(
offsetof(ReadbackImagePixelsINTERNALImmediate, shm_id) == 32,
"offset of ReadbackImagePixelsINTERNALImmediate shm_id should be 32");
static_assert(
offsetof(ReadbackImagePixelsINTERNALImmediate, shm_offset) == 36,
"offset of ReadbackImagePixelsINTERNALImmediate shm_offset should be 36");
static_assert(offsetof(ReadbackImagePixelsINTERNALImmediate, pixels_offset) ==
40,
"offset of ReadbackImagePixelsINTERNALImmediate pixels_offset "
"should be 40");

struct ConvertYUVMailboxesToRGBINTERNALImmediate {
typedef ConvertYUVMailboxesToRGBINTERNALImmediate ValueType;
static const CommandId kCmdId = kConvertYUVMailboxesToRGBINTERNALImmediate;
Expand Down
Loading

0 comments on commit d9d7a73

Please sign in to comment.