1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/common/gpu/client/gl_helper.h"
6
7 #include <queue>
8 #include <string>
9
10 #include "base/bind.h"
11 #include "base/debug/trace_event.h"
12 #include "base/lazy_instance.h"
13 #include "base/logging.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/strings/string_util.h"
17 #include "base/time/time.h"
18 #include "content/common/gpu/client/gl_helper_scaling.h"
19 #include "gpu/command_buffer/client/context_support.h"
20 #include "gpu/command_buffer/common/mailbox.h"
21 #include "media/base/video_frame.h"
22 #include "media/base/video_util.h"
23 #include "third_party/WebKit/public/platform/WebCString.h"
24 #include "third_party/skia/include/core/SkRegion.h"
25 #include "ui/gfx/rect.h"
26 #include "ui/gfx/size.h"
27 #include "ui/gl/gl_bindings.h"
28
29 using blink::WebGLId;
30 using blink::WebGraphicsContext3D;
31
32 namespace {
33
34 // Helper class for allocating and holding an RGBA texture of a given
35 // size and an associated framebuffer.
36 class TextureFrameBufferPair {
37 public:
TextureFrameBufferPair(WebGraphicsContext3D * context,gfx::Size size)38 TextureFrameBufferPair(WebGraphicsContext3D* context,
39 gfx::Size size)
40 : texture_(context, context->createTexture()),
41 framebuffer_(context, context->createFramebuffer()),
42 size_(size) {
43 content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context,
44 texture_);
45 context->texImage2D(GL_TEXTURE_2D,
46 0,
47 GL_RGBA,
48 size.width(),
49 size.height(),
50 0,
51 GL_RGBA,
52 GL_UNSIGNED_BYTE,
53 NULL);
54 content::ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(
55 context,
56 framebuffer_);
57 context->framebufferTexture2D(GL_FRAMEBUFFER,
58 GL_COLOR_ATTACHMENT0,
59 GL_TEXTURE_2D,
60 texture_,
61 0);
62 }
63
texture() const64 WebGLId texture() const { return texture_.id(); }
framebuffer() const65 WebGLId framebuffer() const { return framebuffer_.id(); }
size() const66 gfx::Size size() const { return size_; }
67
68 private:
69 content::ScopedTexture texture_;
70 content::ScopedFramebuffer framebuffer_;
71 gfx::Size size_;
72
73 DISALLOW_COPY_AND_ASSIGN(TextureFrameBufferPair);
74 };
75
76 // Helper class for holding a scaler, a texture for the output of that
77 // scaler and an associated frame buffer. This is inteded to be used
78 // when the output of a scaler is to be sent to a readback.
79 class ScalerHolder {
80 public:
ScalerHolder(WebGraphicsContext3D * context,content::GLHelper::ScalerInterface * scaler)81 ScalerHolder(WebGraphicsContext3D* context,
82 content::GLHelper::ScalerInterface *scaler)
83 : texture_and_framebuffer_(context, scaler->DstSize()),
84 scaler_(scaler) {
85 }
86
Scale(blink::WebGLId src_texture)87 void Scale(blink::WebGLId src_texture) {
88 scaler_->Scale(src_texture, texture_and_framebuffer_.texture());
89 }
90
scaler() const91 content::GLHelper::ScalerInterface* scaler() const { return scaler_.get(); }
texture_and_framebuffer()92 TextureFrameBufferPair *texture_and_framebuffer() {
93 return &texture_and_framebuffer_;
94 }
texture() const95 WebGLId texture() const { return texture_and_framebuffer_.texture(); }
96
97 private:
98 TextureFrameBufferPair texture_and_framebuffer_;
99 scoped_ptr<content::GLHelper::ScalerInterface> scaler_;
100
101 DISALLOW_COPY_AND_ASSIGN(ScalerHolder);
102 };
103
104 } // namespace
105
106 namespace content {
107
108 // Implements GLHelper::CropScaleReadbackAndCleanTexture and encapsulates
109 // the data needed for it.
110 class GLHelper::CopyTextureToImpl :
111 public base::SupportsWeakPtr<GLHelper::CopyTextureToImpl> {
112 public:
CopyTextureToImpl(WebGraphicsContext3D * context,gpu::ContextSupport * context_support,GLHelper * helper)113 CopyTextureToImpl(WebGraphicsContext3D* context,
114 gpu::ContextSupport* context_support,
115 GLHelper* helper)
116 : context_(context),
117 context_support_(context_support),
118 helper_(helper),
119 flush_(context),
120 max_draw_buffers_(0) {
121 std::string extensions_string = " " +
122 UTF16ToASCII(context_->getString(GL_EXTENSIONS)) + " ";
123 if (extensions_string.find(" GL_EXT_draw_buffers ") != std::string::npos) {
124 context_->getIntegerv(GL_MAX_DRAW_BUFFERS, &max_draw_buffers_);
125 }
126 }
~CopyTextureToImpl()127 ~CopyTextureToImpl() {
128 CancelRequests();
129 }
130
ConsumeMailboxToTexture(const gpu::Mailbox & mailbox,uint32 sync_point)131 WebGLId ConsumeMailboxToTexture(const gpu::Mailbox& mailbox,
132 uint32 sync_point) {
133 return helper_->ConsumeMailboxToTexture(mailbox, sync_point);
134 }
135
136 void CropScaleReadbackAndCleanTexture(
137 WebGLId src_texture,
138 const gfx::Size& src_size,
139 const gfx::Rect& src_subrect,
140 const gfx::Size& dst_size,
141 unsigned char* out,
142 const base::Callback<void(bool)>& callback,
143 GLHelper::ScalerQuality quality);
144
145 void ReadbackTextureSync(WebGLId texture,
146 const gfx::Rect& src_rect,
147 unsigned char* out);
148
149 // Reads back bytes from the currently bound frame buffer.
150 // Note that dst_size is specified in bytes, not pixels.
151 void ReadbackAsync(
152 const gfx::Size& dst_size,
153 int32 bytes_per_row, // generally dst_size.width() * 4
154 int32 row_stride_bytes, // generally dst_size.width() * 4
155 unsigned char* out,
156 const base::Callback<void(bool)>& callback);
157
158 void ReadbackPlane(TextureFrameBufferPair* source,
159 const scoped_refptr<media::VideoFrame>& target,
160 int plane,
161 int size_shift,
162 const gfx::Rect& dst_subrect,
163 const base::Callback<void(bool)>& callback);
164
165 blink::WebGLId CopyAndScaleTexture(WebGLId texture,
166 const gfx::Size& src_size,
167 const gfx::Size& dst_size,
168 bool vertically_flip_texture,
169 GLHelper::ScalerQuality quality);
170
171 ReadbackYUVInterface* CreateReadbackPipelineYUV(
172 GLHelper::ScalerQuality quality,
173 const gfx::Size& src_size,
174 const gfx::Rect& src_subrect,
175 const gfx::Size& dst_size,
176 const gfx::Rect& dst_subrect,
177 bool flip_vertically,
178 bool use_mrt);
179
180 // Returns the maximum number of draw buffers available,
181 // 0 if GL_EXT_draw_buffers is not available.
MaxDrawBuffers() const182 blink::WGC3Dint MaxDrawBuffers() const {
183 return max_draw_buffers_;
184 }
185
186 private:
187 // A single request to CropScaleReadbackAndCleanTexture.
188 // The main thread can cancel the request, before it's handled by the helper
189 // thread, by resetting the texture and pixels fields. Alternatively, the
190 // thread marks that it handles the request by resetting the pixels field
191 // (meaning it guarantees that the callback with be called).
192 // In either case, the callback must be called exactly once, and the texture
193 // must be deleted by the main thread context.
194 struct Request {
Requestcontent::GLHelper::CopyTextureToImpl::Request195 Request(const gfx::Size& size_,
196 int32 bytes_per_row_,
197 int32 row_stride_bytes_,
198 unsigned char* pixels_,
199 const base::Callback<void(bool)>& callback_)
200 : done(false),
201 size(size_),
202 bytes_per_row(bytes_per_row_),
203 row_stride_bytes(row_stride_bytes_),
204 pixels(pixels_),
205 callback(callback_),
206 buffer(0),
207 query(0) {
208 }
209
210 bool done;
211 gfx::Size size;
212 int bytes_per_row;
213 int row_stride_bytes;
214 unsigned char* pixels;
215 base::Callback<void(bool)> callback;
216 GLuint buffer;
217 blink::WebGLId query;
218 };
219
220 // A readback pipeline that also converts the data to YUV before
221 // reading it back.
222 class ReadbackYUVImpl : public ReadbackYUVInterface {
223 public:
224 ReadbackYUVImpl(WebGraphicsContext3D* context,
225 CopyTextureToImpl* copy_impl,
226 GLHelperScaling* scaler_impl,
227 GLHelper::ScalerQuality quality,
228 const gfx::Size& src_size,
229 const gfx::Rect& src_subrect,
230 const gfx::Size& dst_size,
231 const gfx::Rect& dst_subrect,
232 bool flip_vertically);
233
234 virtual void ReadbackYUV(
235 const gpu::Mailbox& mailbox,
236 uint32 sync_point,
237 const scoped_refptr<media::VideoFrame>& target,
238 const base::Callback<void(bool)>& callback) OVERRIDE;
239
scaler()240 virtual ScalerInterface* scaler() OVERRIDE {
241 return scaler_.scaler();
242 }
243
244 private:
245 WebGraphicsContext3D* context_;
246 CopyTextureToImpl* copy_impl_;
247 gfx::Size dst_size_;
248 gfx::Rect dst_subrect_;
249 ScalerHolder scaler_;
250 ScalerHolder y_;
251 ScalerHolder u_;
252 ScalerHolder v_;
253
254 DISALLOW_COPY_AND_ASSIGN(ReadbackYUVImpl);
255 };
256
257 // A readback pipeline that also converts the data to YUV before
258 // reading it back. This one uses Multiple Render Targets, which
259 // may not be supported on all platforms.
260 class ReadbackYUV_MRT : public ReadbackYUVInterface {
261 public:
262 ReadbackYUV_MRT(WebGraphicsContext3D* context,
263 CopyTextureToImpl* copy_impl,
264 GLHelperScaling* scaler_impl,
265 GLHelper::ScalerQuality quality,
266 const gfx::Size& src_size,
267 const gfx::Rect& src_subrect,
268 const gfx::Size& dst_size,
269 const gfx::Rect& dst_subrect,
270 bool flip_vertically);
271
272 virtual void ReadbackYUV(
273 const gpu::Mailbox& mailbox,
274 uint32 sync_point,
275 const scoped_refptr<media::VideoFrame>& target,
276 const base::Callback<void(bool)>& callback) OVERRIDE;
277
scaler()278 virtual ScalerInterface* scaler() OVERRIDE {
279 return scaler_.scaler();
280 }
281
282 private:
283 WebGraphicsContext3D* context_;
284 CopyTextureToImpl* copy_impl_;
285 gfx::Size dst_size_;
286 gfx::Rect dst_subrect_;
287 GLHelper::ScalerQuality quality_;
288 ScalerHolder scaler_;
289 scoped_ptr<content::GLHelperScaling::ShaderInterface> pass1_shader_;
290 scoped_ptr<content::GLHelperScaling::ShaderInterface> pass2_shader_;
291 TextureFrameBufferPair y_;
292 ScopedTexture uv_;
293 TextureFrameBufferPair u_;
294 TextureFrameBufferPair v_;
295
296 DISALLOW_COPY_AND_ASSIGN(ReadbackYUV_MRT);
297 };
298
299 // Copies the block of pixels specified with |src_subrect| from |src_texture|,
300 // scales it to |dst_size|, writes it into a texture, and returns its ID.
301 // |src_size| is the size of |src_texture|.
302 WebGLId ScaleTexture(WebGLId src_texture,
303 const gfx::Size& src_size,
304 const gfx::Rect& src_subrect,
305 const gfx::Size& dst_size,
306 bool vertically_flip_texture,
307 bool swizzle,
308 GLHelper::ScalerQuality quality);
309
nullcallback(bool success)310 static void nullcallback(bool success) {}
311 void ReadbackDone(Request *request);
312 void FinishRequest(Request* request, bool result);
313 void CancelRequests();
314
315 static const float kRGBtoYColorWeights[];
316 static const float kRGBtoUColorWeights[];
317 static const float kRGBtoVColorWeights[];
318
319 WebGraphicsContext3D* context_;
320 gpu::ContextSupport* context_support_;
321 GLHelper* helper_;
322
323 // A scoped flush that will ensure all resource deletions are flushed when
324 // this object is destroyed. Must be declared before other Scoped* fields.
325 ScopedFlush flush_;
326
327 std::queue<Request*> request_queue_;
328 blink::WGC3Dint max_draw_buffers_;
329 };
330
CreateScaler(ScalerQuality quality,const gfx::Size & src_size,const gfx::Rect & src_subrect,const gfx::Size & dst_size,bool vertically_flip_texture,bool swizzle)331 GLHelper::ScalerInterface* GLHelper::CreateScaler(
332 ScalerQuality quality,
333 const gfx::Size& src_size,
334 const gfx::Rect& src_subrect,
335 const gfx::Size& dst_size,
336 bool vertically_flip_texture,
337 bool swizzle) {
338 InitScalerImpl();
339 return scaler_impl_->CreateScaler(quality,
340 src_size,
341 src_subrect,
342 dst_size,
343 vertically_flip_texture,
344 swizzle);
345 }
346
ScaleTexture(WebGLId src_texture,const gfx::Size & src_size,const gfx::Rect & src_subrect,const gfx::Size & dst_size,bool vertically_flip_texture,bool swizzle,GLHelper::ScalerQuality quality)347 WebGLId GLHelper::CopyTextureToImpl::ScaleTexture(
348 WebGLId src_texture,
349 const gfx::Size& src_size,
350 const gfx::Rect& src_subrect,
351 const gfx::Size& dst_size,
352 bool vertically_flip_texture,
353 bool swizzle,
354 GLHelper::ScalerQuality quality) {
355 scoped_ptr<ScalerInterface> scaler(
356 helper_->CreateScaler(quality,
357 src_size,
358 src_subrect,
359 dst_size,
360 vertically_flip_texture,
361 swizzle));
362
363 WebGLId dst_texture = context_->createTexture();
364 {
365 ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_, dst_texture);
366 context_->texImage2D(GL_TEXTURE_2D,
367 0,
368 GL_RGBA,
369 dst_size.width(),
370 dst_size.height(),
371 0,
372 GL_RGBA,
373 GL_UNSIGNED_BYTE,
374 NULL);
375 }
376 scaler->Scale(src_texture, dst_texture);
377 return dst_texture;
378 }
379
ReadbackAsync(const gfx::Size & dst_size,int32 bytes_per_row,int32 row_stride_bytes,unsigned char * out,const base::Callback<void (bool)> & callback)380 void GLHelper::CopyTextureToImpl::ReadbackAsync(
381 const gfx::Size& dst_size,
382 int32 bytes_per_row,
383 int32 row_stride_bytes,
384 unsigned char* out,
385 const base::Callback<void(bool)>& callback) {
386 Request* request = new Request(dst_size,
387 bytes_per_row,
388 row_stride_bytes,
389 out,
390 callback);
391 request_queue_.push(request);
392 request->buffer = context_->createBuffer();
393 context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM,
394 request->buffer);
395 context_->bufferData(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM,
396 4 * dst_size.GetArea(),
397 NULL,
398 GL_STREAM_READ);
399
400 request->query = context_->createQueryEXT();
401 context_->beginQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM,
402 request->query);
403 context_->readPixels(0, 0, dst_size.width(), dst_size.height(),
404 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
405 context_->endQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM);
406 context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
407 context_support_->SignalQuery(
408 request->query,
409 base::Bind(&CopyTextureToImpl::ReadbackDone, AsWeakPtr(), request));
410 }
411
412
CropScaleReadbackAndCleanTexture(WebGLId src_texture,const gfx::Size & src_size,const gfx::Rect & src_subrect,const gfx::Size & dst_size,unsigned char * out,const base::Callback<void (bool)> & callback,GLHelper::ScalerQuality quality)413 void GLHelper::CopyTextureToImpl::CropScaleReadbackAndCleanTexture(
414 WebGLId src_texture,
415 const gfx::Size& src_size,
416 const gfx::Rect& src_subrect,
417 const gfx::Size& dst_size,
418 unsigned char* out,
419 const base::Callback<void(bool)>& callback,
420 GLHelper::ScalerQuality quality) {
421 WebGLId texture = ScaleTexture(src_texture,
422 src_size,
423 src_subrect,
424 dst_size,
425 true,
426 #if (SK_R32_SHIFT == 16) && !SK_B32_SHIFT
427 true,
428 #else
429 false,
430 #endif
431 quality);
432 ScopedFramebuffer dst_framebuffer(context_, context_->createFramebuffer());
433 ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(context_,
434 dst_framebuffer);
435 ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_, texture);
436 context_->framebufferTexture2D(GL_FRAMEBUFFER,
437 GL_COLOR_ATTACHMENT0,
438 GL_TEXTURE_2D,
439 texture,
440 0);
441 ReadbackAsync(dst_size,
442 dst_size.width() * 4,
443 dst_size.width() * 4,
444 out,
445 callback);
446 context_->deleteTexture(texture);
447 }
448
ReadbackTextureSync(WebGLId texture,const gfx::Rect & src_rect,unsigned char * out)449 void GLHelper::CopyTextureToImpl::ReadbackTextureSync(
450 WebGLId texture,
451 const gfx::Rect& src_rect,
452 unsigned char* out) {
453 ScopedFramebuffer dst_framebuffer(context_, context_->createFramebuffer());
454 ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(context_,
455 dst_framebuffer);
456 ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_, texture);
457 context_->framebufferTexture2D(GL_FRAMEBUFFER,
458 GL_COLOR_ATTACHMENT0,
459 GL_TEXTURE_2D,
460 texture,
461 0);
462 context_->readPixels(src_rect.x(),
463 src_rect.y(),
464 src_rect.width(),
465 src_rect.height(),
466 GL_RGBA,
467 GL_UNSIGNED_BYTE,
468 out);
469 }
470
CopyAndScaleTexture(WebGLId src_texture,const gfx::Size & src_size,const gfx::Size & dst_size,bool vertically_flip_texture,GLHelper::ScalerQuality quality)471 blink::WebGLId GLHelper::CopyTextureToImpl::CopyAndScaleTexture(
472 WebGLId src_texture,
473 const gfx::Size& src_size,
474 const gfx::Size& dst_size,
475 bool vertically_flip_texture,
476 GLHelper::ScalerQuality quality) {
477 return ScaleTexture(src_texture,
478 src_size,
479 gfx::Rect(src_size),
480 dst_size,
481 vertically_flip_texture,
482 false,
483 quality);
484 }
485
ReadbackDone(Request * finished_request)486 void GLHelper::CopyTextureToImpl::ReadbackDone(Request* finished_request) {
487 TRACE_EVENT0("mirror",
488 "GLHelper::CopyTextureToImpl::CheckReadbackFramebufferComplete");
489 finished_request->done = true;
490
491 // We process transfer requests in the order they were received, regardless
492 // of the order we get the callbacks in.
493 while (!request_queue_.empty()) {
494 Request* request = request_queue_.front();
495 if (!request->done) {
496 break;
497 }
498
499 bool result = false;
500 if (request->buffer != 0) {
501 context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM,
502 request->buffer);
503 unsigned char* data = static_cast<unsigned char *>(
504 context_->mapBufferCHROMIUM(
505 GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, GL_READ_ONLY));
506 if (data) {
507 result = true;
508 if (request->bytes_per_row == request->size.width() * 4 &&
509 request->bytes_per_row == request->row_stride_bytes) {
510 memcpy(request->pixels, data, request->size.GetArea() * 4);
511 } else {
512 unsigned char* out = request->pixels;
513 for (int y = 0; y < request->size.height(); y++) {
514 memcpy(out, data, request->bytes_per_row);
515 out += request->row_stride_bytes;
516 data += request->size.width() * 4;
517 }
518 }
519 context_->unmapBufferCHROMIUM(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM);
520 }
521 context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
522 }
523
524 FinishRequest(request, result);
525 }
526 }
527
FinishRequest(Request * request,bool result)528 void GLHelper::CopyTextureToImpl::FinishRequest(Request* request,
529 bool result) {
530 TRACE_EVENT0("mirror", "GLHelper::CopyTextureToImpl::FinishRequest");
531 DCHECK(request_queue_.front() == request);
532 request_queue_.pop();
533 request->callback.Run(result);
534 ScopedFlush flush(context_);
535 if (request->query != 0) {
536 context_->deleteQueryEXT(request->query);
537 request->query = 0;
538 }
539 if (request->buffer != 0) {
540 context_->deleteBuffer(request->buffer);
541 request->buffer = 0;
542 }
543 delete request;
544 }
545
CancelRequests()546 void GLHelper::CopyTextureToImpl::CancelRequests() {
547 while (!request_queue_.empty()) {
548 Request* request = request_queue_.front();
549 FinishRequest(request, false);
550 }
551 }
552
GLHelper(blink::WebGraphicsContext3D * context,gpu::ContextSupport * context_support)553 GLHelper::GLHelper(blink::WebGraphicsContext3D* context,
554 gpu::ContextSupport* context_support)
555 : context_(context),
556 context_support_(context_support) {
557 }
558
~GLHelper()559 GLHelper::~GLHelper() {
560 }
561
CropScaleReadbackAndCleanTexture(WebGLId src_texture,const gfx::Size & src_size,const gfx::Rect & src_subrect,const gfx::Size & dst_size,unsigned char * out,const base::Callback<void (bool)> & callback)562 void GLHelper::CropScaleReadbackAndCleanTexture(
563 WebGLId src_texture,
564 const gfx::Size& src_size,
565 const gfx::Rect& src_subrect,
566 const gfx::Size& dst_size,
567 unsigned char* out,
568 const base::Callback<void(bool)>& callback) {
569 InitCopyTextToImpl();
570 copy_texture_to_impl_->CropScaleReadbackAndCleanTexture(
571 src_texture,
572 src_size,
573 src_subrect,
574 dst_size,
575 out,
576 callback,
577 GLHelper::SCALER_QUALITY_FAST);
578 }
579
CropScaleReadbackAndCleanMailbox(const gpu::Mailbox & src_mailbox,uint32 sync_point,const gfx::Size & src_size,const gfx::Rect & src_subrect,const gfx::Size & dst_size,unsigned char * out,const base::Callback<void (bool)> & callback)580 void GLHelper::CropScaleReadbackAndCleanMailbox(
581 const gpu::Mailbox& src_mailbox,
582 uint32 sync_point,
583 const gfx::Size& src_size,
584 const gfx::Rect& src_subrect,
585 const gfx::Size& dst_size,
586 unsigned char* out,
587 const base::Callback<void(bool)>& callback) {
588 WebGLId mailbox_texture = ConsumeMailboxToTexture(src_mailbox, sync_point);
589 CropScaleReadbackAndCleanTexture(
590 mailbox_texture, src_size, src_subrect, dst_size, out, callback);
591 context_->deleteTexture(mailbox_texture);
592 }
593
ReadbackTextureSync(blink::WebGLId texture,const gfx::Rect & src_rect,unsigned char * out)594 void GLHelper::ReadbackTextureSync(blink::WebGLId texture,
595 const gfx::Rect& src_rect,
596 unsigned char* out) {
597 InitCopyTextToImpl();
598 copy_texture_to_impl_->ReadbackTextureSync(texture,
599 src_rect,
600 out);
601 }
602
CopyTexture(blink::WebGLId texture,const gfx::Size & size)603 blink::WebGLId GLHelper::CopyTexture(blink::WebGLId texture,
604 const gfx::Size& size) {
605 InitCopyTextToImpl();
606 return copy_texture_to_impl_->CopyAndScaleTexture(
607 texture,
608 size,
609 size,
610 false,
611 GLHelper::SCALER_QUALITY_FAST);
612 }
613
CopyAndScaleTexture(blink::WebGLId texture,const gfx::Size & src_size,const gfx::Size & dst_size,bool vertically_flip_texture,ScalerQuality quality)614 blink::WebGLId GLHelper::CopyAndScaleTexture(
615 blink::WebGLId texture,
616 const gfx::Size& src_size,
617 const gfx::Size& dst_size,
618 bool vertically_flip_texture,
619 ScalerQuality quality) {
620 InitCopyTextToImpl();
621 return copy_texture_to_impl_->CopyAndScaleTexture(texture,
622 src_size,
623 dst_size,
624 vertically_flip_texture,
625 quality);
626 }
627
CompileShaderFromSource(const blink::WGC3Dchar * source,blink::WGC3Denum type)628 WebGLId GLHelper::CompileShaderFromSource(
629 const blink::WGC3Dchar* source,
630 blink::WGC3Denum type) {
631 ScopedShader shader(context_, context_->createShader(type));
632 context_->shaderSource(shader, source);
633 context_->compileShader(shader);
634 blink::WGC3Dint compile_status = 0;
635 context_->getShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
636 if (!compile_status) {
637 LOG(ERROR) << std::string(context_->getShaderInfoLog(shader).utf8());
638 return 0;
639 }
640 return shader.Detach();
641 }
642
InitCopyTextToImpl()643 void GLHelper::InitCopyTextToImpl() {
644 // Lazily initialize |copy_texture_to_impl_|
645 if (!copy_texture_to_impl_)
646 copy_texture_to_impl_.reset(
647 new CopyTextureToImpl(context_, context_support_, this));
648 }
649
InitScalerImpl()650 void GLHelper::InitScalerImpl() {
651 // Lazily initialize |scaler_impl_|
652 if (!scaler_impl_)
653 scaler_impl_.reset(new GLHelperScaling(context_, this));
654 }
655
MaxDrawBuffers()656 blink::WGC3Dint GLHelper::MaxDrawBuffers() {
657 InitCopyTextToImpl();
658 return copy_texture_to_impl_->MaxDrawBuffers();
659 }
660
CopySubBufferDamage(blink::WebGLId texture,blink::WebGLId previous_texture,const SkRegion & new_damage,const SkRegion & old_damage)661 void GLHelper::CopySubBufferDamage(blink::WebGLId texture,
662 blink::WebGLId previous_texture,
663 const SkRegion& new_damage,
664 const SkRegion& old_damage) {
665 SkRegion region(old_damage);
666 if (region.op(new_damage, SkRegion::kDifference_Op)) {
667 ScopedFramebuffer dst_framebuffer(context_,
668 context_->createFramebuffer());
669 ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(context_,
670 dst_framebuffer);
671 ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_, texture);
672 context_->framebufferTexture2D(GL_FRAMEBUFFER,
673 GL_COLOR_ATTACHMENT0,
674 GL_TEXTURE_2D,
675 previous_texture,
676 0);
677 for (SkRegion::Iterator it(region); !it.done(); it.next()) {
678 const SkIRect& rect = it.rect();
679 context_->copyTexSubImage2D(GL_TEXTURE_2D, 0,
680 rect.x(), rect.y(),
681 rect.x(), rect.y(),
682 rect.width(), rect.height());
683 }
684 context_->flush();
685 }
686 }
687
CreateTexture()688 blink::WebGLId GLHelper::CreateTexture() {
689 blink::WebGLId texture = context_->createTexture();
690 content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_,
691 texture);
692 context_->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
693 context_->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
694 context_->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
695 context_->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
696 return texture;
697 }
698
DeleteTexture(blink::WebGLId texture_id)699 void GLHelper::DeleteTexture(blink::WebGLId texture_id) {
700 context_->deleteTexture(texture_id);
701 }
702
InsertSyncPoint()703 uint32 GLHelper::InsertSyncPoint() { return context_->insertSyncPoint(); }
704
WaitSyncPoint(uint32 sync_point)705 void GLHelper::WaitSyncPoint(uint32 sync_point) {
706 context_->waitSyncPoint(sync_point);
707 }
708
ProduceMailboxFromTexture(blink::WebGLId texture_id,uint32 * sync_point)709 gpu::Mailbox GLHelper::ProduceMailboxFromTexture(blink::WebGLId texture_id,
710 uint32* sync_point) {
711 gpu::Mailbox mailbox;
712 context_->genMailboxCHROMIUM(mailbox.name);
713 if (mailbox.IsZero()) {
714 *sync_point = 0;
715 return mailbox;
716 }
717 content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_,
718 texture_id);
719 context_->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
720 *sync_point = context_->insertSyncPoint();
721 return mailbox;
722 }
723
ConsumeMailboxToTexture(const gpu::Mailbox & mailbox,uint32 sync_point)724 blink::WebGLId GLHelper::ConsumeMailboxToTexture(const gpu::Mailbox& mailbox,
725 uint32 sync_point) {
726 if (mailbox.IsZero())
727 return 0;
728 if (sync_point)
729 context_->waitSyncPoint(sync_point);
730 blink::WebGLId texture = CreateTexture();
731 content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_,
732 texture);
733 context_->consumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
734 return texture;
735 }
736
ResizeTexture(blink::WebGLId texture,const gfx::Size & size)737 void GLHelper::ResizeTexture(blink::WebGLId texture, const gfx::Size& size) {
738 content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_, texture);
739 context_->texImage2D(GL_TEXTURE_2D, 0, GL_RGB,
740 size.width(), size.height(), 0,
741 GL_RGB, GL_UNSIGNED_BYTE, NULL);
742 }
743
CopyTextureSubImage(blink::WebGLId texture,const gfx::Rect & rect)744 void GLHelper::CopyTextureSubImage(blink::WebGLId texture,
745 const gfx::Rect& rect) {
746 content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_, texture);
747 context_->copyTexSubImage2D(GL_TEXTURE_2D, 0,
748 rect.x(), rect.y(),
749 rect.x(), rect.y(), rect.width(), rect.height());
750 }
751
CopyTextureFullImage(blink::WebGLId texture,const gfx::Size & size)752 void GLHelper::CopyTextureFullImage(blink::WebGLId texture,
753 const gfx::Size& size) {
754 content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_, texture);
755 context_->copyTexImage2D(GL_TEXTURE_2D, 0,
756 GL_RGB,
757 0, 0,
758 size.width(), size.height(), 0);
759 }
760
ReadbackPlane(TextureFrameBufferPair * source,const scoped_refptr<media::VideoFrame> & target,int plane,int size_shift,const gfx::Rect & dst_subrect,const base::Callback<void (bool)> & callback)761 void GLHelper::CopyTextureToImpl::ReadbackPlane(
762 TextureFrameBufferPair* source,
763 const scoped_refptr<media::VideoFrame>& target,
764 int plane,
765 int size_shift,
766 const gfx::Rect& dst_subrect,
767 const base::Callback<void(bool)>& callback) {
768 context_->bindFramebuffer(GL_FRAMEBUFFER, source->framebuffer());
769 size_t offset = target->stride(plane) * (dst_subrect.y() >> size_shift) +
770 (dst_subrect.x() >> size_shift);
771 ReadbackAsync(
772 source->size(),
773 dst_subrect.width() >> size_shift,
774 target->stride(plane),
775 target->data(plane) + offset,
776 callback);
777 }
778
779 const float GLHelper::CopyTextureToImpl::kRGBtoYColorWeights[] = {
780 0.257f, 0.504f, 0.098f, 0.0625f
781 };
782 const float GLHelper::CopyTextureToImpl::kRGBtoUColorWeights[] = {
783 -0.148f, -0.291f, 0.439f, 0.5f
784 };
785 const float GLHelper::CopyTextureToImpl::kRGBtoVColorWeights[] = {
786 0.439f, -0.368f, -0.071f, 0.5f
787 };
788
789 // YUV readback constructors. Initiates the main scaler pipeline and
790 // one planar scaler for each of the Y, U and V planes.
ReadbackYUVImpl(WebGraphicsContext3D * context,CopyTextureToImpl * copy_impl,GLHelperScaling * scaler_impl,GLHelper::ScalerQuality quality,const gfx::Size & src_size,const gfx::Rect & src_subrect,const gfx::Size & dst_size,const gfx::Rect & dst_subrect,bool flip_vertically)791 GLHelper::CopyTextureToImpl::ReadbackYUVImpl::ReadbackYUVImpl(
792 WebGraphicsContext3D* context,
793 CopyTextureToImpl* copy_impl,
794 GLHelperScaling* scaler_impl,
795 GLHelper::ScalerQuality quality,
796 const gfx::Size& src_size,
797 const gfx::Rect& src_subrect,
798 const gfx::Size& dst_size,
799 const gfx::Rect& dst_subrect,
800 bool flip_vertically)
801 : context_(context),
802 copy_impl_(copy_impl),
803 dst_size_(dst_size),
804 dst_subrect_(dst_subrect),
805 scaler_(context, scaler_impl->CreateScaler(
806 quality,
807 src_size,
808 src_subrect,
809 dst_subrect.size(),
810 flip_vertically,
811 false)),
812 y_(context, scaler_impl->CreatePlanarScaler(
813 dst_subrect.size(),
814 gfx::Rect(0, 0,
815 (dst_subrect.width() + 3) & ~3,
816 dst_subrect.height()),
817 gfx::Size((dst_subrect.width() + 3) / 4,
818 dst_subrect.height()),
819 false,
820 kRGBtoYColorWeights)),
821 u_(context, scaler_impl->CreatePlanarScaler(
822 dst_subrect.size(),
823 gfx::Rect(0, 0,
824 (dst_subrect.width() + 7) & ~7,
825 (dst_subrect.height() + 1) & ~1),
826 gfx::Size((dst_subrect.width() + 7) / 8,
827 (dst_subrect.height() + 1) / 2),
828 false,
829 kRGBtoUColorWeights)),
830 v_(context, scaler_impl->CreatePlanarScaler(
831 dst_subrect.size(),
832 gfx::Rect(0, 0,
833 (dst_subrect.width() + 7) & ~7,
834 (dst_subrect.height() + 1) & ~1),
835 gfx::Size((dst_subrect.width() + 7) / 8,
836 (dst_subrect.height() + 1) / 2),
837 false,
838 kRGBtoVColorWeights)) {
839 DCHECK(!(dst_size.width() & 1));
840 DCHECK(!(dst_size.height() & 1));
841 DCHECK(!(dst_subrect.width() & 1));
842 DCHECK(!(dst_subrect.height() & 1));
843 DCHECK(!(dst_subrect.x() & 1));
844 DCHECK(!(dst_subrect.y() & 1));
845 }
846
CallbackKeepingVideoFrameAlive(scoped_refptr<media::VideoFrame> video_frame,const base::Callback<void (bool)> & callback,bool success)847 static void CallbackKeepingVideoFrameAlive(
848 scoped_refptr<media::VideoFrame> video_frame,
849 const base::Callback<void(bool)>& callback,
850 bool success) {
851 callback.Run(success);
852 }
853
ReadbackYUV(const gpu::Mailbox & mailbox,uint32 sync_point,const scoped_refptr<media::VideoFrame> & target,const base::Callback<void (bool)> & callback)854 void GLHelper::CopyTextureToImpl::ReadbackYUVImpl::ReadbackYUV(
855 const gpu::Mailbox& mailbox,
856 uint32 sync_point,
857 const scoped_refptr<media::VideoFrame>& target,
858 const base::Callback<void(bool)>& callback) {
859 WebGLId mailbox_texture =
860 copy_impl_->ConsumeMailboxToTexture(mailbox, sync_point);
861
862 // Scale texture to right size.
863 scaler_.Scale(mailbox_texture);
864 context_->deleteTexture(mailbox_texture);
865
866 // Convert the scaled texture in to Y, U and V planes.
867 y_.Scale(scaler_.texture());
868 u_.Scale(scaler_.texture());
869 v_.Scale(scaler_.texture());
870
871 if (target->coded_size() != dst_size_) {
872 DCHECK(target->coded_size() == dst_size_);
873 LOG(ERROR) << "ReadbackYUV size error!";
874 callback.Run(false);
875 return;
876 }
877
878 // Read back planes, one at a time. Keep the video frame alive while doing the
879 // readback.
880 copy_impl_->ReadbackPlane(y_.texture_and_framebuffer(),
881 target,
882 media::VideoFrame::kYPlane,
883 0,
884 dst_subrect_,
885 base::Bind(&nullcallback));
886 copy_impl_->ReadbackPlane(u_.texture_and_framebuffer(),
887 target,
888 media::VideoFrame::kUPlane,
889 1,
890 dst_subrect_,
891 base::Bind(&nullcallback));
892 copy_impl_->ReadbackPlane(v_.texture_and_framebuffer(),
893 target,
894 media::VideoFrame::kVPlane,
895 1,
896 dst_subrect_,
897 base::Bind(&CallbackKeepingVideoFrameAlive,
898 target,
899 callback));
900 context_->bindFramebuffer(GL_FRAMEBUFFER, 0);
901 media::LetterboxYUV(target, dst_subrect_);
902 }
903
904 // YUV readback constructors. Initiates the main scaler pipeline and
905 // one planar scaler for each of the Y, U and V planes.
ReadbackYUV_MRT(WebGraphicsContext3D * context,CopyTextureToImpl * copy_impl,GLHelperScaling * scaler_impl,GLHelper::ScalerQuality quality,const gfx::Size & src_size,const gfx::Rect & src_subrect,const gfx::Size & dst_size,const gfx::Rect & dst_subrect,bool flip_vertically)906 GLHelper::CopyTextureToImpl::ReadbackYUV_MRT::ReadbackYUV_MRT(
907 WebGraphicsContext3D* context,
908 CopyTextureToImpl* copy_impl,
909 GLHelperScaling* scaler_impl,
910 GLHelper::ScalerQuality quality,
911 const gfx::Size& src_size,
912 const gfx::Rect& src_subrect,
913 const gfx::Size& dst_size,
914 const gfx::Rect& dst_subrect,
915 bool flip_vertically)
916 : context_(context),
917 copy_impl_(copy_impl),
918 dst_size_(dst_size),
919 dst_subrect_(dst_subrect),
920 quality_(quality),
921 scaler_(context, scaler_impl->CreateScaler(
922 quality,
923 src_size,
924 src_subrect,
925 dst_subrect.size(),
926 false,
927 false)),
928 pass1_shader_(scaler_impl->CreateYuvMrtShader(
929 dst_subrect.size(),
930 gfx::Rect(0, 0,
931 (dst_subrect.width() + 3) & ~3,
932 dst_subrect.height()),
933 gfx::Size((dst_subrect.width() + 3) / 4,
934 dst_subrect.height()),
935 flip_vertically,
936 GLHelperScaling::SHADER_YUV_MRT_PASS1)),
937 pass2_shader_(scaler_impl->CreateYuvMrtShader(
938 gfx::Size((dst_subrect.width() + 3) / 4,
939 dst_subrect.height()),
940 gfx::Rect(0, 0,
941 (dst_subrect.width() + 7) / 8 * 2,
942 dst_subrect.height()),
943 gfx::Size((dst_subrect.width() + 7) / 8,
944 (dst_subrect.height() + 1) / 2),
945 false,
946 GLHelperScaling::SHADER_YUV_MRT_PASS2)),
947 y_(context, gfx::Size((dst_subrect.width() + 3) / 4,
948 dst_subrect.height())),
949 uv_(context, context->createTexture()),
950 u_(context, gfx::Size((dst_subrect.width() + 7) / 8,
951 (dst_subrect.height() + 1) / 2)),
952 v_(context, gfx::Size((dst_subrect.width() + 7) / 8,
953 (dst_subrect.height() + 1) / 2)) {
954
955 content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context, uv_);
956 context->texImage2D(GL_TEXTURE_2D,
957 0,
958 GL_RGBA,
959 (dst_subrect.width() + 3) / 4,
960 dst_subrect.height(),
961 0,
962 GL_RGBA,
963 GL_UNSIGNED_BYTE,
964 NULL);
965
966 DCHECK(!(dst_size.width() & 1));
967 DCHECK(!(dst_size.height() & 1));
968 DCHECK(!(dst_subrect.width() & 1));
969 DCHECK(!(dst_subrect.height() & 1));
970 DCHECK(!(dst_subrect.x() & 1));
971 DCHECK(!(dst_subrect.y() & 1));
972 }
973
ReadbackYUV(const gpu::Mailbox & mailbox,uint32 sync_point,const scoped_refptr<media::VideoFrame> & target,const base::Callback<void (bool)> & callback)974 void GLHelper::CopyTextureToImpl::ReadbackYUV_MRT::ReadbackYUV(
975 const gpu::Mailbox& mailbox,
976 uint32 sync_point,
977 const scoped_refptr<media::VideoFrame>& target,
978 const base::Callback<void(bool)>& callback) {
979 WebGLId mailbox_texture =
980 copy_impl_->ConsumeMailboxToTexture(mailbox, sync_point);
981
982 WebGLId texture;
983 if (quality_ == GLHelper::SCALER_QUALITY_FAST) {
984 // Optimization: SCALER_QUALITY_FAST is just a single bilinear
985 // pass, which pass1_shader_ can do just as well, so let's skip
986 // the actual scaling in that case.
987 texture = mailbox_texture;
988 } else {
989 // Scale texture to right size.
990 scaler_.Scale(mailbox_texture);
991 texture = scaler_.texture();
992 }
993
994
995 std::vector<blink::WebGLId> outputs(2);
996 // Convert the scaled texture in to Y, U and V planes.
997 outputs[0] = y_.texture();
998 outputs[1] = uv_;
999 pass1_shader_->Execute(texture, outputs);
1000
1001 context_->deleteTexture(mailbox_texture);
1002
1003 outputs[0] = u_.texture();
1004 outputs[1] = v_.texture();
1005 pass2_shader_->Execute(uv_, outputs);
1006
1007 if (target->coded_size() != dst_size_) {
1008 DCHECK(target->coded_size() == dst_size_);
1009 LOG(ERROR) << "ReadbackYUV size error!";
1010 callback.Run(false);
1011 return;
1012 }
1013
1014 // Read back planes, one at a time.
1015 copy_impl_->ReadbackPlane(&y_,
1016 target,
1017 media::VideoFrame::kYPlane,
1018 0,
1019 dst_subrect_,
1020 base::Bind(&nullcallback));
1021 copy_impl_->ReadbackPlane(&u_,
1022 target,
1023 media::VideoFrame::kUPlane,
1024 1,
1025 dst_subrect_,
1026 base::Bind(&nullcallback));
1027 copy_impl_->ReadbackPlane(&v_,
1028 target,
1029 media::VideoFrame::kVPlane,
1030 1,
1031 dst_subrect_,
1032 base::Bind(&CallbackKeepingVideoFrameAlive,
1033 target,
1034 callback));
1035 context_->bindFramebuffer(GL_FRAMEBUFFER, 0);
1036 media::LetterboxYUV(target, dst_subrect_);
1037 }
1038
1039 ReadbackYUVInterface*
CreateReadbackPipelineYUV(GLHelper::ScalerQuality quality,const gfx::Size & src_size,const gfx::Rect & src_subrect,const gfx::Size & dst_size,const gfx::Rect & dst_subrect,bool flip_vertically,bool use_mrt)1040 GLHelper::CopyTextureToImpl::CreateReadbackPipelineYUV(
1041 GLHelper::ScalerQuality quality,
1042 const gfx::Size& src_size,
1043 const gfx::Rect& src_subrect,
1044 const gfx::Size& dst_size,
1045 const gfx::Rect& dst_subrect,
1046 bool flip_vertically,
1047 bool use_mrt) {
1048 helper_->InitScalerImpl();
1049 if (max_draw_buffers_ >= 2 && use_mrt) {
1050 return new ReadbackYUV_MRT(
1051 context_,
1052 this,
1053 helper_->scaler_impl_.get(),
1054 quality,
1055 src_size,
1056 src_subrect,
1057 dst_size,
1058 dst_subrect,
1059 flip_vertically);
1060 }
1061 return new ReadbackYUVImpl(
1062 context_,
1063 this,
1064 helper_->scaler_impl_.get(),
1065 quality,
1066 src_size,
1067 src_subrect,
1068 dst_size,
1069 dst_subrect,
1070 flip_vertically);
1071 }
1072
1073 ReadbackYUVInterface*
CreateReadbackPipelineYUV(ScalerQuality quality,const gfx::Size & src_size,const gfx::Rect & src_subrect,const gfx::Size & dst_size,const gfx::Rect & dst_subrect,bool flip_vertically,bool use_mrt)1074 GLHelper::CreateReadbackPipelineYUV(
1075 ScalerQuality quality,
1076 const gfx::Size& src_size,
1077 const gfx::Rect& src_subrect,
1078 const gfx::Size& dst_size,
1079 const gfx::Rect& dst_subrect,
1080 bool flip_vertically,
1081 bool use_mrt) {
1082 InitCopyTextToImpl();
1083 return copy_texture_to_impl_->CreateReadbackPipelineYUV(
1084 quality,
1085 src_size,
1086 src_subrect,
1087 dst_size,
1088 dst_subrect,
1089 flip_vertically,
1090 use_mrt);
1091 }
1092
1093 } // namespace content
1094