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 "gpu/command_buffer/client/gl_helper.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <string>
11 #include <utility>
12
13 #include "base/bind.h"
14 #include "base/bind_helpers.h"
15 #include "base/containers/queue.h"
16 #include "base/lazy_instance.h"
17 #include "base/logging.h"
18 #include "base/macros.h"
19 #include "base/memory/ref_counted.h"
20 #include "base/numerics/safe_conversions.h"
21 #include "base/strings/string_util.h"
22 #include "base/time/time.h"
23 #include "base/trace_event/trace_event.h"
24 #include "gpu/GLES2/gl2extchromium.h"
25 #include "gpu/command_buffer/client/context_support.h"
26 #include "gpu/command_buffer/client/gl_helper_scaling.h"
27 #include "ui/gfx/geometry/point.h"
28 #include "ui/gfx/geometry/rect.h"
29 #include "ui/gfx/geometry/size.h"
30 #include "ui/gfx/geometry/vector2d.h"
31
32 namespace gpu {
33
34 using gles2::GLES2Interface;
35
36 namespace {
37
38 class ScopedFlush {
39 public:
ScopedFlush(gles2::GLES2Interface * gl)40 explicit ScopedFlush(gles2::GLES2Interface* gl) : gl_(gl) {}
41
~ScopedFlush()42 ~ScopedFlush() { gl_->Flush(); }
43
44 private:
45 gles2::GLES2Interface* gl_;
46
47 DISALLOW_COPY_AND_ASSIGN(ScopedFlush);
48 };
49
50 // Helper class for allocating and holding an RGBA texture of a given
51 // size.
52 class TextureHolder {
53 public:
TextureHolder(GLES2Interface * gl,gfx::Size size)54 TextureHolder(GLES2Interface* gl, gfx::Size size)
55 : texture_(gl), size_(size) {
56 ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl, texture_);
57 gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0,
58 GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
59 }
60
texture() const61 GLuint texture() const { return texture_.id(); }
size() const62 gfx::Size size() const { return size_; }
63
64 private:
65 ScopedTexture texture_;
66 gfx::Size size_;
67
68 DISALLOW_COPY_AND_ASSIGN(TextureHolder);
69 };
70
71 class I420ConverterImpl : public I420Converter {
72 public:
73 I420ConverterImpl(GLES2Interface* gl,
74 GLHelperScaling* scaler_impl,
75 bool flipped_source,
76 bool flip_output,
77 bool swizzle,
78 bool use_mrt);
79
80 ~I420ConverterImpl() override;
81
82 void Convert(GLuint src_texture,
83 const gfx::Size& src_texture_size,
84 const gfx::Vector2dF& src_offset,
85 GLHelper::ScalerInterface* optional_scaler,
86 const gfx::Rect& output_rect,
87 GLuint y_plane_texture,
88 GLuint u_plane_texture,
89 GLuint v_plane_texture) override;
90
91 bool IsSamplingFlippedSource() const override;
92 bool IsFlippingOutput() const override;
93 GLenum GetReadbackFormat() const override;
94
95 protected:
96 // Returns true if the planerizer should use the faster, two-pass shaders
97 // to generate the YUV planar outputs. If false, the source will be
98 // scanned three times, once for each Y/U/V plane.
use_mrt() const99 bool use_mrt() const { return !v_planerizer_; }
100
101 // Reallocates the intermediate and plane textures, if needed.
102 void EnsureTexturesSizedFor(const gfx::Size& scaler_output_size,
103 const gfx::Size& y_texture_size,
104 const gfx::Size& chroma_texture_size,
105 GLuint y_plane_texture,
106 GLuint u_plane_texture,
107 GLuint v_plane_texture);
108
109 GLES2Interface* const gl_;
110
111 private:
112 // These generate the Y/U/V planes. If MRT is being used, |y_planerizer_|
113 // generates the Y and interim UV plane, |u_planerizer_| generates the
114 // final U and V planes, and |v_planerizer_| is unused. If MRT is not
115 // being used, each of these generates only one of the Y/U/V planes.
116 const std::unique_ptr<GLHelper::ScalerInterface> y_planerizer_;
117 const std::unique_ptr<GLHelper::ScalerInterface> u_planerizer_;
118 const std::unique_ptr<GLHelper::ScalerInterface> v_planerizer_;
119
120 // Intermediate texture, holding the scaler's output.
121 base::Optional<TextureHolder> intermediate_;
122
123 // Intermediate texture, holding the UV interim output (if the MRT shader
124 // is being used).
125 base::Optional<ScopedTexture> uv_;
126
127 DISALLOW_COPY_AND_ASSIGN(I420ConverterImpl);
128 };
129
130 } // namespace
131
132 // Implements texture consumption/readback and encapsulates
133 // the data needed for it.
134 class GLHelper::CopyTextureToImpl
135 : public base::SupportsWeakPtr<GLHelper::CopyTextureToImpl> {
136 public:
CopyTextureToImpl(GLES2Interface * gl,ContextSupport * context_support,GLHelper * helper)137 CopyTextureToImpl(GLES2Interface* gl,
138 ContextSupport* context_support,
139 GLHelper* helper)
140 : gl_(gl),
141 context_support_(context_support),
142 helper_(helper),
143 flush_(gl) {}
~CopyTextureToImpl()144 ~CopyTextureToImpl() { CancelRequests(); }
145
146 void ReadbackTextureAsync(GLuint texture,
147 GLenum texture_target,
148 const gfx::Size& dst_size,
149 unsigned char* out,
150 GLenum format,
151 base::OnceCallback<void(bool)> callback);
152
153 // Reads back bytes from the currently bound frame buffer.
154 // Note that dst_size is specified in bytes, not pixels.
155 void ReadbackAsync(const gfx::Size& dst_size,
156 size_t bytes_per_row, // generally dst_size.width() * 4
157 size_t row_stride_bytes, // generally dst_size.width() * 4
158 unsigned char* out,
159 GLenum format,
160 GLenum type,
161 size_t bytes_per_pixel,
162 base::OnceCallback<void(bool)> callback);
163
164 void ReadbackPlane(const gfx::Size& texture_size,
165 int row_stride_bytes,
166 unsigned char* data,
167 int size_shift,
168 const gfx::Rect& paste_rect,
169 ReadbackSwizzle swizzle,
170 base::OnceCallback<void(bool)> callback);
171
172 std::unique_ptr<ReadbackYUVInterface> CreateReadbackPipelineYUV(
173 bool flip_vertically,
174 bool use_mrt);
175
176 private:
177 // Represents the state of a single readback request.
178 // The main thread can cancel the request, before it's handled by the helper
179 // thread, by resetting the texture and pixels fields. Alternatively, the
180 // thread marks that it handles the request by resetting the pixels field
181 // (meaning it guarantees that the callback with be called).
182 // In either case, the callback must be called exactly once, and the texture
183 // must be deleted by the main thread gl.
184 struct Request {
Requestgpu::GLHelper::CopyTextureToImpl::Request185 Request(const gfx::Size& size_,
186 size_t bytes_per_row_,
187 size_t row_stride_bytes_,
188 unsigned char* pixels_,
189 base::OnceCallback<void(bool)> callback_)
190 : done(false),
191 size(size_),
192 bytes_per_row(bytes_per_row_),
193 row_stride_bytes(row_stride_bytes_),
194 pixels(pixels_),
195 callback(std::move(callback_)),
196 buffer(0),
197 query(0) {}
198
199 bool done;
200 bool result;
201 gfx::Size size;
202 size_t bytes_per_row;
203 size_t row_stride_bytes;
204 unsigned char* pixels;
205 base::OnceCallback<void(bool)> callback;
206 GLuint buffer;
207 GLuint query;
208 };
209
210 // We must take care to call the callbacks last, as they may
211 // end up destroying the gl_helper and make *this invalid.
212 // We stick the finished requests in a stack object that calls
213 // the callbacks when it goes out of scope.
214 class FinishRequestHelper {
215 public:
FinishRequestHelper()216 FinishRequestHelper() {}
~FinishRequestHelper()217 ~FinishRequestHelper() {
218 while (!requests_.empty()) {
219 Request* request = requests_.front();
220 requests_.pop();
221 std::move(request->callback).Run(request->result);
222 delete request;
223 }
224 }
Add(Request * r)225 void Add(Request* r) { requests_.push(r); }
226
227 private:
228 base::queue<Request*> requests_;
229 DISALLOW_COPY_AND_ASSIGN(FinishRequestHelper);
230 };
231
232 // A readback pipeline that also converts the data to YUV before
233 // reading it back.
234 class ReadbackYUVImpl : public I420ConverterImpl,
235 public ReadbackYUVInterface {
236 public:
237 ReadbackYUVImpl(GLES2Interface* gl,
238 CopyTextureToImpl* copy_impl,
239 GLHelperScaling* scaler_impl,
240 bool flip_vertically,
241 ReadbackSwizzle swizzle,
242 bool use_mrt);
243
244 ~ReadbackYUVImpl() override;
245
246 void SetScaler(std::unique_ptr<GLHelper::ScalerInterface> scaler) override;
247
248 GLHelper::ScalerInterface* scaler() const override;
249
250 bool IsFlippingOutput() const override;
251
252 void ReadbackYUV(GLuint texture,
253 const gfx::Size& src_texture_size,
254 const gfx::Rect& output_rect,
255 int y_plane_row_stride_bytes,
256 unsigned char* y_plane_data,
257 int u_plane_row_stride_bytes,
258 unsigned char* u_plane_data,
259 int v_plane_row_stride_bytes,
260 unsigned char* v_plane_data,
261 const gfx::Point& paste_location,
262 base::OnceCallback<void(bool)> callback) override;
263
264 private:
265 GLES2Interface* gl_;
266 CopyTextureToImpl* copy_impl_;
267 ReadbackSwizzle swizzle_;
268
269 // May be null if no scaling is required. This can be changed between
270 // calls to ReadbackYUV().
271 std::unique_ptr<GLHelper::ScalerInterface> scaler_;
272
273 // These are the output textures for each Y/U/V plane.
274 ScopedTexture y_;
275 ScopedTexture u_;
276 ScopedTexture v_;
277
278 // Framebuffers used by ReadbackPlane(). They are cached here so as to not
279 // be re-allocated for every frame of video.
280 ScopedFramebuffer y_readback_framebuffer_;
281 ScopedFramebuffer u_readback_framebuffer_;
282 ScopedFramebuffer v_readback_framebuffer_;
283
284 DISALLOW_COPY_AND_ASSIGN(ReadbackYUVImpl);
285 };
286
287 void ReadbackDone(Request* request, size_t bytes_per_pixel);
288 void FinishRequest(Request* request,
289 bool result,
290 FinishRequestHelper* helper);
291 void CancelRequests();
292
293 bool IsBGRAReadbackSupported();
294
295 GLES2Interface* gl_;
296 ContextSupport* context_support_;
297 GLHelper* helper_;
298
299 // A scoped flush that will ensure all resource deletions are flushed when
300 // this object is destroyed. Must be declared before other Scoped* fields.
301 ScopedFlush flush_;
302
303 base::queue<Request*> request_queue_;
304
305 // Lazily set by IsBGRAReadbackSupported().
306 enum {
307 BGRA_SUPPORT_UNKNOWN,
308 BGRA_SUPPORTED,
309 BGRA_NOT_SUPPORTED
310 } bgra_support_ = BGRA_SUPPORT_UNKNOWN;
311
312 // A run-once test is lazy executed in CreateReadbackPipelineYUV(), to
313 // determine whether the GL_BGRA_EXT format is preferred for readback.
314 enum {
315 BGRA_PREFERENCE_UNKNOWN,
316 BGRA_PREFERRED,
317 BGRA_NOT_PREFERRED
318 } bgra_preference_ = BGRA_PREFERENCE_UNKNOWN;
319 };
320
CreateScaler(ScalerQuality quality,const gfx::Vector2d & scale_from,const gfx::Vector2d & scale_to,bool flipped_source,bool flip_output,bool swizzle)321 std::unique_ptr<GLHelper::ScalerInterface> GLHelper::CreateScaler(
322 ScalerQuality quality,
323 const gfx::Vector2d& scale_from,
324 const gfx::Vector2d& scale_to,
325 bool flipped_source,
326 bool flip_output,
327 bool swizzle) {
328 InitScalerImpl();
329 return scaler_impl_->CreateScaler(quality, scale_from, scale_to,
330 flipped_source, flip_output, swizzle);
331 }
332
ReadbackAsync(const gfx::Size & dst_size,size_t bytes_per_row,size_t row_stride_bytes,unsigned char * out,GLenum format,GLenum type,size_t bytes_per_pixel,base::OnceCallback<void (bool)> callback)333 void GLHelper::CopyTextureToImpl::ReadbackAsync(
334 const gfx::Size& dst_size,
335 size_t bytes_per_row,
336 size_t row_stride_bytes,
337 unsigned char* out,
338 GLenum format,
339 GLenum type,
340 size_t bytes_per_pixel,
341 base::OnceCallback<void(bool)> callback) {
342 TRACE_EVENT0("gpu.capture", "GLHelper::CopyTextureToImpl::ReadbackAsync");
343 Request* request = new Request(dst_size, bytes_per_row, row_stride_bytes, out,
344 std::move(callback));
345 request_queue_.push(request);
346 request->buffer = 0u;
347
348 gl_->GenBuffers(1, &request->buffer);
349 gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, request->buffer);
350 gl_->BufferData(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM,
351 bytes_per_pixel * dst_size.GetArea(), nullptr,
352 GL_STREAM_READ);
353
354 request->query = 0u;
355 gl_->GenQueriesEXT(1, &request->query);
356 gl_->BeginQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM, request->query);
357 gl_->ReadPixels(0, 0, dst_size.width(), dst_size.height(), format, type,
358 nullptr);
359 gl_->EndQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM);
360 gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
361 context_support_->SignalQuery(
362 request->query, base::BindOnce(&CopyTextureToImpl::ReadbackDone,
363 AsWeakPtr(), request, bytes_per_pixel));
364 }
365
ReadbackTextureAsync(GLuint texture,GLenum texture_target,const gfx::Size & dst_size,unsigned char * out,GLenum format,base::OnceCallback<void (bool)> callback)366 void GLHelper::CopyTextureToImpl::ReadbackTextureAsync(
367 GLuint texture,
368 GLenum texture_target,
369 const gfx::Size& dst_size,
370 unsigned char* out,
371 GLenum format,
372 base::OnceCallback<void(bool)> callback) {
373 constexpr size_t kBytesPerPixel = 4;
374
375 // Note: It's possible the GL implementation supports other readback
376 // types. However, as of this writing, no caller of this method will
377 // request a different |color_type| (i.e., requiring using some other GL
378 // format).
379 if (format != GL_RGBA &&
380 (format != GL_BGRA_EXT || !IsBGRAReadbackSupported())) {
381 std::move(callback).Run(false);
382 return;
383 }
384
385 ScopedFramebuffer dst_framebuffer(gl_);
386 ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(gl_,
387 dst_framebuffer);
388 gl_->BindTexture(texture_target, texture);
389 gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
390 texture_target, texture, 0);
391 ReadbackAsync(dst_size, dst_size.width() * kBytesPerPixel,
392 dst_size.width() * kBytesPerPixel, out, format,
393 GL_UNSIGNED_BYTE, kBytesPerPixel, std::move(callback));
394 gl_->BindTexture(texture_target, 0);
395 }
396
ReadbackDone(Request * finished_request,size_t bytes_per_pixel)397 void GLHelper::CopyTextureToImpl::ReadbackDone(Request* finished_request,
398 size_t bytes_per_pixel) {
399 TRACE_EVENT0("gpu.capture",
400 "GLHelper::CopyTextureToImpl::CheckReadbackFramebufferComplete");
401 finished_request->done = true;
402
403 FinishRequestHelper finish_request_helper;
404
405 // We process transfer requests in the order they were received, regardless
406 // of the order we get the callbacks in.
407 while (!request_queue_.empty()) {
408 Request* request = request_queue_.front();
409 if (!request->done) {
410 break;
411 }
412
413 bool result = false;
414 if (request->buffer != 0) {
415 gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, request->buffer);
416 unsigned char* data = static_cast<unsigned char*>(gl_->MapBufferCHROMIUM(
417 GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, GL_READ_ONLY));
418 if (data) {
419 result = true;
420 if (request->bytes_per_row == request->size.width() * bytes_per_pixel &&
421 request->bytes_per_row == request->row_stride_bytes) {
422 memcpy(request->pixels, data,
423 request->size.GetArea() * bytes_per_pixel);
424 } else {
425 unsigned char* out = request->pixels;
426 for (int y = 0; y < request->size.height(); y++) {
427 memcpy(out, data, request->bytes_per_row);
428 out += request->row_stride_bytes;
429 data += request->size.width() * bytes_per_pixel;
430 }
431 }
432 gl_->UnmapBufferCHROMIUM(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM);
433 }
434 gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
435 }
436 FinishRequest(request, result, &finish_request_helper);
437 }
438 }
439
FinishRequest(Request * request,bool result,FinishRequestHelper * finish_request_helper)440 void GLHelper::CopyTextureToImpl::FinishRequest(
441 Request* request,
442 bool result,
443 FinishRequestHelper* finish_request_helper) {
444 TRACE_EVENT0("gpu.capture", "GLHelper::CopyTextureToImpl::FinishRequest");
445 DCHECK(request_queue_.front() == request);
446 request_queue_.pop();
447 request->result = result;
448 ScopedFlush flush(gl_);
449 if (request->query != 0) {
450 gl_->DeleteQueriesEXT(1, &request->query);
451 request->query = 0;
452 }
453 if (request->buffer != 0) {
454 gl_->DeleteBuffers(1, &request->buffer);
455 request->buffer = 0;
456 }
457 finish_request_helper->Add(request);
458 }
459
CancelRequests()460 void GLHelper::CopyTextureToImpl::CancelRequests() {
461 FinishRequestHelper finish_request_helper;
462 while (!request_queue_.empty()) {
463 Request* request = request_queue_.front();
464 FinishRequest(request, false, &finish_request_helper);
465 }
466 }
467
IsBGRAReadbackSupported()468 bool GLHelper::CopyTextureToImpl::IsBGRAReadbackSupported() {
469 if (bgra_support_ == BGRA_PREFERENCE_UNKNOWN) {
470 bgra_support_ = BGRA_NOT_SUPPORTED;
471 if (auto* extensions = gl_->GetString(GL_EXTENSIONS)) {
472 const std::string extensions_string =
473 " " + std::string(reinterpret_cast<const char*>(extensions)) + " ";
474 if (extensions_string.find(" GL_EXT_read_format_bgra ") !=
475 std::string::npos) {
476 bgra_support_ = BGRA_SUPPORTED;
477 }
478 }
479 }
480
481 return bgra_support_ == BGRA_SUPPORTED;
482 }
483
GLHelper(GLES2Interface * gl,ContextSupport * context_support)484 GLHelper::GLHelper(GLES2Interface* gl, ContextSupport* context_support)
485 : gl_(gl), context_support_(context_support) {}
486
~GLHelper()487 GLHelper::~GLHelper() {}
488
ReadbackTextureAsync(GLuint texture,GLenum texture_target,const gfx::Size & dst_size,unsigned char * out,GLenum format,base::OnceCallback<void (bool)> callback)489 void GLHelper::ReadbackTextureAsync(GLuint texture,
490 GLenum texture_target,
491 const gfx::Size& dst_size,
492 unsigned char* out,
493 GLenum format,
494 base::OnceCallback<void(bool)> callback) {
495 InitCopyTextToImpl();
496 copy_texture_to_impl_->ReadbackTextureAsync(texture, texture_target, dst_size,
497 out, format, std::move(callback));
498 }
499
InitCopyTextToImpl()500 void GLHelper::InitCopyTextToImpl() {
501 // Lazily initialize |copy_texture_to_impl_|
502 if (!copy_texture_to_impl_)
503 copy_texture_to_impl_.reset(
504 new CopyTextureToImpl(gl_, context_support_, this));
505 }
506
InitScalerImpl()507 void GLHelper::InitScalerImpl() {
508 // Lazily initialize |scaler_impl_|
509 if (!scaler_impl_)
510 scaler_impl_.reset(new GLHelperScaling(gl_, this));
511 }
512
MaxDrawBuffers()513 GLint GLHelper::MaxDrawBuffers() {
514 if (max_draw_buffers_ < 0) {
515 max_draw_buffers_ = 0;
516 const GLubyte* extensions = gl_->GetString(GL_EXTENSIONS);
517 if (extensions) {
518 const std::string extensions_string =
519 " " + std::string(reinterpret_cast<const char*>(extensions)) + " ";
520 if (extensions_string.find(" GL_EXT_draw_buffers ") !=
521 std::string::npos) {
522 gl_->GetIntegerv(GL_MAX_DRAW_BUFFERS_EXT, &max_draw_buffers_);
523 DCHECK_GE(max_draw_buffers_, 0);
524 }
525 }
526 }
527
528 return max_draw_buffers_;
529 }
530
ReadbackPlane(const gfx::Size & texture_size,int row_stride_bytes,unsigned char * data,int size_shift,const gfx::Rect & paste_rect,ReadbackSwizzle swizzle,base::OnceCallback<void (bool)> callback)531 void GLHelper::CopyTextureToImpl::ReadbackPlane(
532 const gfx::Size& texture_size,
533 int row_stride_bytes,
534 unsigned char* data,
535 int size_shift,
536 const gfx::Rect& paste_rect,
537 ReadbackSwizzle swizzle,
538 base::OnceCallback<void(bool)> callback) {
539 const size_t offset = row_stride_bytes * (paste_rect.y() >> size_shift) +
540 (paste_rect.x() >> size_shift);
541 ReadbackAsync(texture_size, paste_rect.width() >> size_shift,
542 row_stride_bytes, data + offset,
543 (swizzle == kSwizzleBGRA) ? GL_BGRA_EXT : GL_RGBA,
544 GL_UNSIGNED_BYTE, 4, std::move(callback));
545 }
546
547 I420Converter::I420Converter() = default;
548 I420Converter::~I420Converter() = default;
549
550 // static
GetYPlaneTextureSize(const gfx::Size & output_size)551 gfx::Size I420Converter::GetYPlaneTextureSize(const gfx::Size& output_size) {
552 return gfx::Size((output_size.width() + 3) / 4, output_size.height());
553 }
554
555 // static
GetChromaPlaneTextureSize(const gfx::Size & output_size)556 gfx::Size I420Converter::GetChromaPlaneTextureSize(
557 const gfx::Size& output_size) {
558 return gfx::Size((output_size.width() + 7) / 8,
559 (output_size.height() + 1) / 2);
560 }
561
562 namespace {
563
I420ConverterImpl(GLES2Interface * gl,GLHelperScaling * scaler_impl,bool flipped_source,bool flip_output,bool swizzle,bool use_mrt)564 I420ConverterImpl::I420ConverterImpl(GLES2Interface* gl,
565 GLHelperScaling* scaler_impl,
566 bool flipped_source,
567 bool flip_output,
568 bool swizzle,
569 bool use_mrt)
570 : gl_(gl),
571 y_planerizer_(
572 use_mrt ? scaler_impl->CreateI420MrtPass1Planerizer(flipped_source,
573 flip_output,
574 swizzle)
575 : scaler_impl->CreateI420Planerizer(0,
576 flipped_source,
577 flip_output,
578 swizzle)),
579 u_planerizer_(use_mrt ? scaler_impl->CreateI420MrtPass2Planerizer(swizzle)
580 : scaler_impl->CreateI420Planerizer(1,
581 flipped_source,
582 flip_output,
583 swizzle)),
584 v_planerizer_(use_mrt ? nullptr
585 : scaler_impl->CreateI420Planerizer(2,
586 flipped_source,
587 flip_output,
588 swizzle)) {}
589
590 I420ConverterImpl::~I420ConverterImpl() = default;
591
Convert(GLuint src_texture,const gfx::Size & src_texture_size,const gfx::Vector2dF & src_offset,GLHelper::ScalerInterface * optional_scaler,const gfx::Rect & output_rect,GLuint y_plane_texture,GLuint u_plane_texture,GLuint v_plane_texture)592 void I420ConverterImpl::Convert(GLuint src_texture,
593 const gfx::Size& src_texture_size,
594 const gfx::Vector2dF& src_offset,
595 GLHelper::ScalerInterface* optional_scaler,
596 const gfx::Rect& output_rect,
597 GLuint y_plane_texture,
598 GLuint u_plane_texture,
599 GLuint v_plane_texture) {
600 const gfx::Size scaler_output_size =
601 optional_scaler ? output_rect.size() : gfx::Size();
602 const gfx::Size y_texture_size = GetYPlaneTextureSize(output_rect.size());
603 const gfx::Size chroma_texture_size =
604 GetChromaPlaneTextureSize(output_rect.size());
605 EnsureTexturesSizedFor(scaler_output_size, y_texture_size,
606 chroma_texture_size, y_plane_texture, u_plane_texture,
607 v_plane_texture);
608
609 // Scale first, if needed.
610 if (optional_scaler) {
611 // The scaler should not be configured to do any swizzling.
612 DCHECK_EQ(optional_scaler->GetReadbackFormat(),
613 static_cast<GLenum>(GL_RGBA));
614 optional_scaler->Scale(src_texture, src_texture_size, src_offset,
615 intermediate_->texture(), output_rect);
616 }
617
618 // Convert the intermediate (or source) texture into Y, U and V planes.
619 const GLuint texture =
620 optional_scaler ? intermediate_->texture() : src_texture;
621 const gfx::Size texture_size =
622 optional_scaler ? intermediate_->size() : src_texture_size;
623 const gfx::Vector2dF offset = optional_scaler ? gfx::Vector2dF() : src_offset;
624 if (use_mrt()) {
625 y_planerizer_->ScaleToMultipleOutputs(texture, texture_size, offset,
626 y_plane_texture, uv_->id(),
627 gfx::Rect(y_texture_size));
628 u_planerizer_->ScaleToMultipleOutputs(
629 uv_->id(), y_texture_size, gfx::Vector2dF(), u_plane_texture,
630 v_plane_texture, gfx::Rect(chroma_texture_size));
631 } else {
632 y_planerizer_->Scale(texture, texture_size, offset, y_plane_texture,
633 gfx::Rect(y_texture_size));
634 u_planerizer_->Scale(texture, texture_size, offset, u_plane_texture,
635 gfx::Rect(chroma_texture_size));
636 v_planerizer_->Scale(texture, texture_size, offset, v_plane_texture,
637 gfx::Rect(chroma_texture_size));
638 }
639 }
640
IsSamplingFlippedSource() const641 bool I420ConverterImpl::IsSamplingFlippedSource() const {
642 return y_planerizer_->IsSamplingFlippedSource();
643 }
644
IsFlippingOutput() const645 bool I420ConverterImpl::IsFlippingOutput() const {
646 return y_planerizer_->IsFlippingOutput();
647 }
648
GetReadbackFormat() const649 GLenum I420ConverterImpl::GetReadbackFormat() const {
650 return y_planerizer_->GetReadbackFormat();
651 }
652
EnsureTexturesSizedFor(const gfx::Size & scaler_output_size,const gfx::Size & y_texture_size,const gfx::Size & chroma_texture_size,GLuint y_plane_texture,GLuint u_plane_texture,GLuint v_plane_texture)653 void I420ConverterImpl::EnsureTexturesSizedFor(
654 const gfx::Size& scaler_output_size,
655 const gfx::Size& y_texture_size,
656 const gfx::Size& chroma_texture_size,
657 GLuint y_plane_texture,
658 GLuint u_plane_texture,
659 GLuint v_plane_texture) {
660 // Reallocate the intermediate texture, if needed.
661 if (!scaler_output_size.IsEmpty()) {
662 if (!intermediate_ || intermediate_->size() != scaler_output_size)
663 intermediate_.emplace(gl_, scaler_output_size);
664 } else {
665 intermediate_ = base::nullopt;
666 }
667
668 // Size the interim UV plane and the three output planes.
669 const auto SetRGBATextureSize = [this](const gfx::Size& size) {
670 gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0,
671 GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
672 };
673 if (use_mrt()) {
674 uv_.emplace(gl_);
675 gl_->BindTexture(GL_TEXTURE_2D, uv_->id());
676 SetRGBATextureSize(y_texture_size);
677 }
678 gl_->BindTexture(GL_TEXTURE_2D, y_plane_texture);
679 SetRGBATextureSize(y_texture_size);
680 gl_->BindTexture(GL_TEXTURE_2D, u_plane_texture);
681 SetRGBATextureSize(chroma_texture_size);
682 gl_->BindTexture(GL_TEXTURE_2D, v_plane_texture);
683 SetRGBATextureSize(chroma_texture_size);
684 }
685
686 } // namespace
687
ReadbackYUVImpl(GLES2Interface * gl,CopyTextureToImpl * copy_impl,GLHelperScaling * scaler_impl,bool flip_vertically,ReadbackSwizzle swizzle,bool use_mrt)688 GLHelper::CopyTextureToImpl::ReadbackYUVImpl::ReadbackYUVImpl(
689 GLES2Interface* gl,
690 CopyTextureToImpl* copy_impl,
691 GLHelperScaling* scaler_impl,
692 bool flip_vertically,
693 ReadbackSwizzle swizzle,
694 bool use_mrt)
695 : I420ConverterImpl(gl,
696 scaler_impl,
697 false,
698 flip_vertically,
699 swizzle == kSwizzleBGRA,
700 use_mrt),
701 gl_(gl),
702 copy_impl_(copy_impl),
703 swizzle_(swizzle),
704 y_(gl_),
705 u_(gl_),
706 v_(gl_),
707 y_readback_framebuffer_(gl_),
708 u_readback_framebuffer_(gl_),
709 v_readback_framebuffer_(gl_) {}
710
711 GLHelper::CopyTextureToImpl::ReadbackYUVImpl::~ReadbackYUVImpl() = default;
712
SetScaler(std::unique_ptr<GLHelper::ScalerInterface> scaler)713 void GLHelper::CopyTextureToImpl::ReadbackYUVImpl::SetScaler(
714 std::unique_ptr<GLHelper::ScalerInterface> scaler) {
715 scaler_ = std::move(scaler);
716 }
717
718 GLHelper::ScalerInterface*
scaler() const719 GLHelper::CopyTextureToImpl::ReadbackYUVImpl::scaler() const {
720 return scaler_.get();
721 }
722
IsFlippingOutput() const723 bool GLHelper::CopyTextureToImpl::ReadbackYUVImpl::IsFlippingOutput() const {
724 return I420ConverterImpl::IsFlippingOutput();
725 }
726
ReadbackYUV(GLuint texture,const gfx::Size & src_texture_size,const gfx::Rect & output_rect,int y_plane_row_stride_bytes,unsigned char * y_plane_data,int u_plane_row_stride_bytes,unsigned char * u_plane_data,int v_plane_row_stride_bytes,unsigned char * v_plane_data,const gfx::Point & paste_location,base::OnceCallback<void (bool)> callback)727 void GLHelper::CopyTextureToImpl::ReadbackYUVImpl::ReadbackYUV(
728 GLuint texture,
729 const gfx::Size& src_texture_size,
730 const gfx::Rect& output_rect,
731 int y_plane_row_stride_bytes,
732 unsigned char* y_plane_data,
733 int u_plane_row_stride_bytes,
734 unsigned char* u_plane_data,
735 int v_plane_row_stride_bytes,
736 unsigned char* v_plane_data,
737 const gfx::Point& paste_location,
738 base::OnceCallback<void(bool)> callback) {
739 DCHECK(!(paste_location.x() & 1));
740 DCHECK(!(paste_location.y() & 1));
741
742 I420ConverterImpl::Convert(texture, src_texture_size, gfx::Vector2dF(),
743 scaler_.get(), output_rect, y_, u_, v_);
744
745 // Read back planes, one at a time. Keep the video frame alive while doing
746 // the readback.
747 const gfx::Rect paste_rect(paste_location, output_rect.size());
748 const auto SetUpAndBindFramebuffer = [this](GLuint framebuffer,
749 GLuint texture) {
750 gl_->BindFramebuffer(GL_FRAMEBUFFER, framebuffer);
751 gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
752 GL_TEXTURE_2D, texture, 0);
753 };
754 SetUpAndBindFramebuffer(y_readback_framebuffer_, y_);
755 copy_impl_->ReadbackPlane(
756 GetYPlaneTextureSize(output_rect.size()), y_plane_row_stride_bytes,
757 y_plane_data, 0, paste_rect, swizzle_, base::DoNothing::Once<bool>());
758 SetUpAndBindFramebuffer(u_readback_framebuffer_, u_);
759 const gfx::Size chroma_texture_size =
760 GetChromaPlaneTextureSize(output_rect.size());
761 copy_impl_->ReadbackPlane(chroma_texture_size, u_plane_row_stride_bytes,
762 u_plane_data, 1, paste_rect, swizzle_,
763 base::DoNothing::Once<bool>());
764 SetUpAndBindFramebuffer(v_readback_framebuffer_, v_);
765 copy_impl_->ReadbackPlane(chroma_texture_size, v_plane_row_stride_bytes,
766 v_plane_data, 1, paste_rect, swizzle_,
767 std::move(callback));
768 gl_->BindFramebuffer(GL_FRAMEBUFFER, 0);
769 }
770
CreateI420Converter(bool flipped_source,bool flip_output,bool swizzle,bool use_mrt)771 std::unique_ptr<I420Converter> GLHelper::CreateI420Converter(
772 bool flipped_source,
773 bool flip_output,
774 bool swizzle,
775 bool use_mrt) {
776 InitCopyTextToImpl();
777 InitScalerImpl();
778 return std::make_unique<I420ConverterImpl>(
779 gl_, scaler_impl_.get(), flipped_source, flip_output, swizzle,
780 use_mrt && (MaxDrawBuffers() >= 2));
781 }
782
783 std::unique_ptr<ReadbackYUVInterface>
CreateReadbackPipelineYUV(bool flip_vertically,bool use_mrt)784 GLHelper::CopyTextureToImpl::CreateReadbackPipelineYUV(bool flip_vertically,
785 bool use_mrt) {
786 helper_->InitScalerImpl();
787
788 if (bgra_preference_ == BGRA_PREFERENCE_UNKNOWN) {
789 if (IsBGRAReadbackSupported()) {
790 // Test whether GL_BRGA_EXT is preferred for readback by creating a test
791 // texture, binding it to a framebuffer as a color attachment, and then
792 // querying the implementation for the framebuffer's readback format.
793 constexpr int kTestSize = 64;
794 GLuint texture = 0;
795 gl_->GenTextures(1, &texture);
796 gl_->BindTexture(GL_TEXTURE_2D, texture);
797 gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
798 gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
799 gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
800 gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
801 gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTestSize, kTestSize, 0,
802 GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
803 GLuint framebuffer = 0;
804 gl_->GenFramebuffers(1, &framebuffer);
805 gl_->BindFramebuffer(GL_FRAMEBUFFER, framebuffer);
806 gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
807 GL_TEXTURE_2D, texture, 0);
808 GLint readback_format = 0;
809 GLint readback_type = 0;
810 gl_->GetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readback_format);
811 gl_->GetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readback_type);
812 if (readback_format == GL_BGRA_EXT && readback_type == GL_UNSIGNED_BYTE) {
813 bgra_preference_ = BGRA_PREFERRED;
814 } else {
815 bgra_preference_ = BGRA_NOT_PREFERRED;
816 }
817 if (framebuffer != 0)
818 gl_->DeleteFramebuffers(1, &framebuffer);
819 if (texture != 0)
820 gl_->DeleteTextures(1, &texture);
821 } else {
822 bgra_preference_ = BGRA_NOT_PREFERRED;
823 }
824 }
825
826 const ReadbackSwizzle swizzle =
827 (bgra_preference_ == BGRA_PREFERRED) ? kSwizzleBGRA : kSwizzleNone;
828 return std::make_unique<ReadbackYUVImpl>(
829 gl_, this, helper_->scaler_impl_.get(), flip_vertically, swizzle,
830 use_mrt && (helper_->MaxDrawBuffers() >= 2));
831 }
832
CreateReadbackPipelineYUV(bool flip_vertically,bool use_mrt)833 std::unique_ptr<ReadbackYUVInterface> GLHelper::CreateReadbackPipelineYUV(
834 bool flip_vertically,
835 bool use_mrt) {
836 InitCopyTextToImpl();
837 return copy_texture_to_impl_->CreateReadbackPipelineYUV(flip_vertically,
838 use_mrt);
839 }
840
GetReadbackPipelineYUV(bool vertically_flip_texture)841 ReadbackYUVInterface* GLHelper::GetReadbackPipelineYUV(
842 bool vertically_flip_texture) {
843 ReadbackYUVInterface* yuv_reader = nullptr;
844 if (vertically_flip_texture) {
845 if (!shared_readback_yuv_flip_) {
846 shared_readback_yuv_flip_ = CreateReadbackPipelineYUV(
847 vertically_flip_texture, true /* use_mrt */);
848 }
849 yuv_reader = shared_readback_yuv_flip_.get();
850 } else {
851 if (!shared_readback_yuv_noflip_) {
852 shared_readback_yuv_noflip_ = CreateReadbackPipelineYUV(
853 vertically_flip_texture, true /* use_mrt */);
854 }
855 yuv_reader = shared_readback_yuv_noflip_.get();
856 }
857 DCHECK(!yuv_reader->scaler());
858 return yuv_reader;
859 }
860
861 } // namespace gpu
862