1 // Copyright 2018 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 "components/viz/common/gl_scaler.h"
6 
7 #include <algorithm>
8 #include <array>
9 #include <sstream>
10 #include <string>
11 
12 #include "base/logging.h"
13 #include "components/viz/common/gpu/context_provider.h"
14 #include "gpu/GLES2/gl2chromium.h"
15 #include "gpu/GLES2/gl2extchromium.h"
16 #include "ui/gfx/color_transform.h"
17 #include "ui/gfx/geometry/rect_conversions.h"
18 
19 namespace viz {
20 
21 namespace {
22 
23 // The code in GLScaler that computes the ScalerStages is greatly simplified by
24 // being able to access the X and Y components by index (instead of
25 // Vector2d::x() or Vector2d::y()). Thus, define a helper class to represent the
26 // relative size as a 2-element std::array and convert to/from Vector2d.
27 struct RelativeSize : public std::array<int, 2> {
28   using std::array<int, 2>::operator[];
29 
RelativeSizeviz::__anon72f6ed3a0111::RelativeSize30   RelativeSize(int width, int height) : std::array<int, 2>{{width, height}} {}
RelativeSizeviz::__anon72f6ed3a0111::RelativeSize31   explicit RelativeSize(const gfx::Vector2d& v)
32       : std::array<int, 2>{{v.x(), v.y()}} {}
33 
AsVector2dviz::__anon72f6ed3a0111::RelativeSize34   gfx::Vector2d AsVector2d() const {
35     return gfx::Vector2d((*this)[0], (*this)[1]);
36   }
37 };
38 
operator <<(std::ostream & out,const RelativeSize & size)39 std::ostream& operator<<(std::ostream& out, const RelativeSize& size) {
40   return (out << size[0] << 'x' << size[1]);
41 }
42 
43 }  // namespace
44 
GLScaler(ContextProvider * context_provider)45 GLScaler::GLScaler(ContextProvider* context_provider)
46     : context_provider_(context_provider) {
47   if (context_provider_) {
48     DCHECK(context_provider_->ContextGL());
49     context_provider_->AddObserver(this);
50   }
51 }
52 
~GLScaler()53 GLScaler::~GLScaler() {
54   OnContextLost();  // Ensures destruction in dependency order.
55 }
56 
SupportsPreciseColorManagement() const57 bool GLScaler::SupportsPreciseColorManagement() const {
58   if (!context_provider_) {
59     return false;
60   }
61   if (!supports_half_floats_.has_value()) {
62     supports_half_floats_ = AreAllGLExtensionsPresent(
63         context_provider_->ContextGL(),
64         {"GL_EXT_color_buffer_half_float", "GL_OES_texture_half_float_linear"});
65   }
66   return supports_half_floats_.value();
67 }
68 
GetMaxDrawBuffersSupported() const69 int GLScaler::GetMaxDrawBuffersSupported() const {
70   if (!context_provider_) {
71     return 0;
72   }
73 
74   if (max_draw_buffers_ < 0) {
75     // Query the GL context for the multiple draw buffers extension and, if
76     // present, the actual platform-supported maximum.
77     GLES2Interface* const gl = context_provider_->ContextGL();
78     DCHECK(gl);
79     if (AreAllGLExtensionsPresent(gl, {"GL_EXT_draw_buffers"})) {
80       gl->GetIntegerv(GL_MAX_DRAW_BUFFERS_EXT, &max_draw_buffers_);
81     }
82 
83     if (max_draw_buffers_ < 1) {
84       max_draw_buffers_ = 1;
85     }
86   }
87 
88   return max_draw_buffers_;
89 }
90 
Configure(const Parameters & new_params)91 bool GLScaler::Configure(const Parameters& new_params) {
92   chain_.reset();
93   shader_programs_.clear();
94 
95   if (!context_provider_) {
96     return false;
97   }
98   GLES2Interface* const gl = context_provider_->ContextGL();
99   DCHECK(gl);
100 
101   params_ = new_params;
102 
103   // Ensure the client has provided valid scaling vectors.
104   if (params_.scale_from.x() == 0 || params_.scale_from.y() == 0 ||
105       params_.scale_to.x() == 0 || params_.scale_to.y() == 0) {
106     // The caller computed invalid scale_from and/or scale_to values.
107     DVLOG(1) << __func__ << ": Invalid scaling vectors: scale_from="
108              << params_.scale_from.ToString()
109              << ", scale_to=" << params_.scale_to.ToString();
110     return false;
111   }
112 
113   // Resolve the color spaces according to the rules described in the header
114   // file.
115   if (!params_.source_color_space.IsValid()) {
116     params_.source_color_space = gfx::ColorSpace::CreateSRGB();
117   }
118   if (!params_.output_color_space.IsValid()) {
119     params_.output_color_space = params_.source_color_space;
120   }
121 
122   // Check that 16-bit half floats are supported if precise color management is
123   // being requested.
124   if (params_.enable_precise_color_management) {
125     if (!SupportsPreciseColorManagement()) {
126       DVLOG(1) << __func__
127                << ": GL context does not support the half-floats "
128                   "required for precise color management.";
129       return false;
130     }
131   }
132 
133   // Check that MRT support is available if certain export formats were
134   // specified in the Parameters.
135   if (params_.export_format == Parameters::ExportFormat::NV61 ||
136       params_.export_format ==
137           Parameters::ExportFormat::DEINTERLEAVE_PAIRWISE) {
138     if (GetMaxDrawBuffersSupported() < 2) {
139       DVLOG(1) << __func__ << ": GL context does not support 2+ draw buffers.";
140       return false;
141     }
142   }
143 
144   // Color space transformation is meaningless when using the deinterleaver
145   // because it only deals with two color channels. This also means precise
146   // color management must be disabled.
147   if (params_.export_format ==
148           Parameters::ExportFormat::DEINTERLEAVE_PAIRWISE &&
149       (params_.source_color_space != params_.output_color_space ||
150        params_.enable_precise_color_management)) {
151     NOTIMPLEMENTED();
152     return false;
153   }
154 
155   // Check that one of the two implemented output swizzles has been specified.
156   for (GLenum s : params_.swizzle) {
157     if (s != GL_RGBA && s != GL_BGRA_EXT) {
158       NOTIMPLEMENTED();
159       return false;
160     }
161   }
162 
163   // Create the chain of ScalerStages. If the quality setting is FAST or there
164   // is no scaling to be done, just create a single stage.
165   std::unique_ptr<ScalerStage> chain;
166   if (params_.quality == Parameters::Quality::FAST ||
167       params_.scale_from == params_.scale_to) {
168     chain = std::make_unique<ScalerStage>(gl, Shader::BILINEAR, HORIZONTAL,
169                                           params_.scale_from, params_.scale_to);
170   } else if (params_.quality == Parameters::Quality::GOOD) {
171     chain = CreateAGoodScalingChain(gl, params_.scale_from, params_.scale_to);
172   } else if (params_.quality == Parameters::Quality::BEST) {
173     chain = CreateTheBestScalingChain(gl, params_.scale_from, params_.scale_to);
174   } else {
175     NOTREACHED();
176   }
177   chain = MaybeAppendExportStage(gl, std::move(chain), params_.export_format);
178 
179   // Determine the color space and the data type of the pixels in the
180   // intermediate textures, depending on whether precise color management is
181   // enabled. Note that nothing special need be done here if no scaling will be
182   // performed.
183   GLenum intermediate_texture_type;
184   if (params_.enable_precise_color_management &&
185       params_.scale_from != params_.scale_to) {
186     // Ensure the scaling color space is using a linear transfer function.
187     constexpr auto kLinearFunction = std::make_tuple(1, 0, 1, 0, 0, 0, 1);
188     skcms_TransferFunction fn;
189     if (params_.source_color_space.GetTransferFunction(&fn) &&
190         std::make_tuple(fn.a, fn.b, fn.c, fn.d, fn.e, fn.f, fn.g) ==
191             kLinearFunction) {
192       scaling_color_space_ = params_.source_color_space;
193     } else {
194       // Use the source color space, but with a linear transfer function.
195       skcms_Matrix3x3 to_XYZD50;
196       params_.source_color_space.GetPrimaryMatrix(&to_XYZD50);
197       std::tie(fn.a, fn.b, fn.c, fn.d, fn.e, fn.f, fn.g) = kLinearFunction;
198       scaling_color_space_ = gfx::ColorSpace::CreateCustom(to_XYZD50, fn);
199     }
200     intermediate_texture_type = GL_HALF_FLOAT_OES;
201   } else {
202     scaling_color_space_ = params_.source_color_space;
203     intermediate_texture_type = GL_UNSIGNED_BYTE;
204   }
205 
206   // Set the shader program on the final stage. Include color space
207   // transformation and swizzling, if necessary.
208   std::unique_ptr<gfx::ColorTransform> transform;
209   if (scaling_color_space_ != params_.output_color_space) {
210     transform = gfx::ColorTransform::NewColorTransform(
211         scaling_color_space_, params_.output_color_space,
212         gfx::ColorTransform::Intent::INTENT_PERCEPTUAL);
213   }
214   ScalerStage* const final_stage = chain.get();
215   final_stage->set_shader_program(
216       GetShaderProgram(final_stage->shader(), intermediate_texture_type,
217                        transform.get(), params_.swizzle));
218 
219   // Set the shader program on all prior stages. These stages are all operating
220   // in the same color space, |scaling_color_space_|.
221   static const GLenum kNoSwizzle[2] = {GL_RGBA, GL_RGBA};
222   ScalerStage* input_stage = final_stage;
223   while (input_stage->input_stage()) {
224     input_stage = input_stage->input_stage();
225     input_stage->set_shader_program(GetShaderProgram(
226         input_stage->shader(), intermediate_texture_type, nullptr, kNoSwizzle));
227   }
228   // From this point, |input_stage| points to the first ScalerStage (i.e., the
229   // one that will be reading from the source).
230 
231   // If necessary, prepend an extra "import stage" that color-converts the input
232   // before any scaling occurs. It's important not to merge color space
233   // conversion of the source with any other steps because the texture sampler
234   // must not linearly interpolate until after the colors have been mapped to a
235   // linear color space.
236   if (params_.source_color_space != scaling_color_space_) {
237     input_stage->set_input_stage(std::make_unique<ScalerStage>(
238         gl, Shader::BILINEAR, HORIZONTAL, input_stage->scale_from(),
239         input_stage->scale_from()));
240     input_stage = input_stage->input_stage();
241     transform = gfx::ColorTransform::NewColorTransform(
242         params_.source_color_space, scaling_color_space_,
243         gfx::ColorTransform::Intent::INTENT_PERCEPTUAL);
244     input_stage->set_shader_program(
245         GetShaderProgram(input_stage->shader(), intermediate_texture_type,
246                          transform.get(), kNoSwizzle));
247   }
248 
249   // If the source content is Y-flipped, the input scaler stage will perform
250   // math to account for this. It also will flip the content during scaling so
251   // that all following stages may assume the content is not flipped. Then, the
252   // final stage must ensure the final output is correctly flipped-back (or not)
253   // based on what the first stage did PLUS what is being requested by the
254   // client code.
255   if (params_.is_flipped_source) {
256     input_stage->set_is_flipped_source(true);
257     input_stage->set_flip_output(true);
258   }
259   if (input_stage->flip_output() != params_.flip_output) {
260     final_stage->set_flip_output(!final_stage->flip_output());
261   }
262 
263   chain_ = std::move(chain);
264   VLOG(2) << __func__ << " built this: " << *this;
265   return true;
266 }
267 
ScaleToMultipleOutputs(GLuint src_texture,const gfx::Size & src_texture_size,const gfx::Vector2d & src_offset,GLuint dest_texture_0,GLuint dest_texture_1,const gfx::Rect & output_rect)268 bool GLScaler::ScaleToMultipleOutputs(GLuint src_texture,
269                                       const gfx::Size& src_texture_size,
270                                       const gfx::Vector2d& src_offset,
271                                       GLuint dest_texture_0,
272                                       GLuint dest_texture_1,
273                                       const gfx::Rect& output_rect) {
274   if (!chain_) {
275     return false;
276   }
277 
278   // Bind the vertex attributes used to sweep the entire source area when
279   // executing the shader programs.
280   GLES2Interface* const gl = context_provider_->ContextGL();
281   DCHECK(gl);
282   if (vertex_attributes_buffer_) {
283     gl->BindBuffer(GL_ARRAY_BUFFER, vertex_attributes_buffer_);
284   } else {
285     gl->GenBuffers(1, &vertex_attributes_buffer_);
286     gl->BindBuffer(GL_ARRAY_BUFFER, vertex_attributes_buffer_);
287     gl->BufferData(GL_ARRAY_BUFFER, sizeof(ShaderProgram::kVertexAttributes),
288                    ShaderProgram::kVertexAttributes, GL_STATIC_DRAW);
289   }
290 
291   // Disable GL clipping/blending features that interfere with assumptions made
292   // by the implementation. Only those known to possibly be enabled elsewhere in
293   // Chromium code are disabled here, while the remainder are sanity-DCHECK'ed.
294   gl->Disable(GL_SCISSOR_TEST);
295   gl->Disable(GL_STENCIL_TEST);
296   gl->Disable(GL_BLEND);
297   DCHECK_NE(gl->IsEnabled(GL_CULL_FACE), GL_TRUE);
298   DCHECK_NE(gl->IsEnabled(GL_DEPTH_TEST), GL_TRUE);
299   DCHECK_NE(gl->IsEnabled(GL_POLYGON_OFFSET_FILL), GL_TRUE);
300   DCHECK_NE(gl->IsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE), GL_TRUE);
301   DCHECK_NE(gl->IsEnabled(GL_SAMPLE_COVERAGE), GL_TRUE);
302   DCHECK_NE(gl->IsEnabled(GL_SCISSOR_TEST), GL_TRUE);
303   DCHECK_NE(gl->IsEnabled(GL_STENCIL_TEST), GL_TRUE);
304 
305   chain_->ScaleToMultipleOutputs(src_texture, src_texture_size, src_offset,
306                                  dest_texture_0, dest_texture_1, output_rect);
307 
308   gl->BindBuffer(GL_ARRAY_BUFFER, 0);
309   return true;
310 }
311 
312 // static
ParametersHasSameScaleRatio(const GLScaler::Parameters & params,const gfx::Vector2d & from,const gfx::Vector2d & to)313 bool GLScaler::ParametersHasSameScaleRatio(const GLScaler::Parameters& params,
314                                            const gfx::Vector2d& from,
315                                            const gfx::Vector2d& to) {
316   // Returns true iff a_num/a_denom == b_num/b_denom.
317   const auto AreRatiosEqual = [](int32_t a_num, int32_t a_denom, int32_t b_num,
318                                  int32_t b_denom) -> bool {
319     // The math (for each dimension):
320     //   If: a_num/a_denom == b_num/b_denom
321     //   Then: a_num*b_denom == b_num*a_denom
322     //
323     // ...and cast to int64_t to guarantee no overflow from the multiplications.
324     return (static_cast<int64_t>(a_num) * b_denom) ==
325            (static_cast<int64_t>(b_num) * a_denom);
326   };
327 
328   return AreRatiosEqual(params.scale_from.x(), params.scale_to.x(), from.x(),
329                         to.x()) &&
330          AreRatiosEqual(params.scale_from.y(), params.scale_to.y(), from.y(),
331                         to.y());
332 }
333 
334 // static
ParametersAreEquivalent(const Parameters & a,const Parameters & b)335 bool GLScaler::ParametersAreEquivalent(const Parameters& a,
336                                        const Parameters& b) {
337   if (!ParametersHasSameScaleRatio(a, b.scale_from, b.scale_to) ||
338       a.enable_precise_color_management != b.enable_precise_color_management ||
339       a.quality != b.quality || a.is_flipped_source != b.is_flipped_source ||
340       a.flip_output != b.flip_output || a.export_format != b.export_format ||
341       a.swizzle[0] != b.swizzle[0] || a.swizzle[1] != b.swizzle[1]) {
342     return false;
343   }
344 
345   const gfx::ColorSpace source_color_space_a =
346       a.source_color_space.IsValid() ? a.source_color_space
347                                      : gfx::ColorSpace::CreateSRGB();
348   const gfx::ColorSpace source_color_space_b =
349       b.source_color_space.IsValid() ? b.source_color_space
350                                      : gfx::ColorSpace::CreateSRGB();
351   if (source_color_space_a != source_color_space_b) {
352     return false;
353   }
354 
355   const gfx::ColorSpace output_color_space_a = a.output_color_space.IsValid()
356                                                    ? a.output_color_space
357                                                    : source_color_space_a;
358   const gfx::ColorSpace output_color_space_b = b.output_color_space.IsValid()
359                                                    ? b.output_color_space
360                                                    : source_color_space_b;
361   return output_color_space_a == output_color_space_b;
362 }
363 
OnContextLost()364 void GLScaler::OnContextLost() {
365   // The destruction order here is important due to data dependencies.
366   chain_.reset();
367   shader_programs_.clear();
368   if (vertex_attributes_buffer_) {
369     if (auto* gl = context_provider_->ContextGL()) {
370       gl->DeleteBuffers(1, &vertex_attributes_buffer_);
371     }
372     vertex_attributes_buffer_ = 0;
373   }
374   if (context_provider_) {
375     context_provider_->RemoveObserver(this);
376     context_provider_ = nullptr;
377   }
378 }
379 
GetShaderProgram(Shader shader,GLenum texture_type,const gfx::ColorTransform * color_transform,const GLenum swizzle[2])380 GLScaler::ShaderProgram* GLScaler::GetShaderProgram(
381     Shader shader,
382     GLenum texture_type,
383     const gfx::ColorTransform* color_transform,
384     const GLenum swizzle[2]) {
385   const ShaderCacheKey key{
386       shader,
387       texture_type,
388       color_transform ? color_transform->GetSrcColorSpace() : gfx::ColorSpace(),
389       color_transform ? color_transform->GetDstColorSpace() : gfx::ColorSpace(),
390       swizzle[0],
391       swizzle[1]};
392   auto it = shader_programs_.find(key);
393   if (it == shader_programs_.end()) {
394     GLES2Interface* const gl = context_provider_->ContextGL();
395     DCHECK(gl);
396     it = shader_programs_
397              .emplace(std::piecewise_construct, std::forward_as_tuple(key),
398                       std::forward_as_tuple(gl, shader, texture_type,
399                                             color_transform, swizzle))
400              .first;
401   }
402   return &it->second;
403 }
404 
405 // static
CreateAGoodScalingChain(gpu::gles2::GLES2Interface * gl,const gfx::Vector2d & scale_from,const gfx::Vector2d & scale_to)406 std::unique_ptr<GLScaler::ScalerStage> GLScaler::CreateAGoodScalingChain(
407     gpu::gles2::GLES2Interface* gl,
408     const gfx::Vector2d& scale_from,
409     const gfx::Vector2d& scale_to) {
410   DCHECK(scale_from.x() != 0 && scale_from.y() != 0)
411       << "Bad scale_from: " << scale_from.ToString();
412   DCHECK(scale_to.x() != 0 && scale_to.y() != 0)
413       << "Bad scale_to: " << scale_to.ToString();
414   DCHECK(scale_from != scale_to);
415 
416   // The GOOD quality chain performs one bilinear upscale followed by N bilinear
417   // halvings, and does this is both directions. Exception: No upscale is needed
418   // when |scale_from| is a power of two multiple of |scale_to|.
419   //
420   // Since all shaders use bilinear filtering, the heuristics below attempt to
421   // greedily merge steps wherever possible to minimize GPU memory usage and
422   // processing time. This also means that it will be extremely rare for the
423   // stage doing the initial upscale to actually require a larger output texture
424   // than the source texture (a downscale will be merged into the same stage).
425 
426   // Determine the initial upscaled-to size, as the minimum number of doublings
427   // to make |scale_to| greater than |scale_from|.
428   const RelativeSize from(scale_from);
429   const RelativeSize to(scale_to);
430   RelativeSize upscale_to = to;
431   for (Axis x_or_y : std::array<Axis, 2>{HORIZONTAL, VERTICAL}) {
432     while (upscale_to[x_or_y] < from[x_or_y]) {
433       upscale_to[x_or_y] *= 2;
434     }
435   }
436 
437   // Create the stages in order from first-to-last, taking the greediest path
438   // each time. Something like an A* algorithm would be better for discovering
439   // an optimal sequence of operations, and would allow using the BILINEAR3
440   // shader as well, but the run-time performance to compute the stages would be
441   // too prohibitive.
442   std::unique_ptr<ScalerStage> chain;
443   struct CandidateOp {
444     Shader shader;
445     Axis primary_axis;
446     RelativeSize output_size;
447   };
448   std::vector<CandidateOp> candidates;
449   for (RelativeSize cur = from; cur != to;
450        cur = RelativeSize(chain->scale_to())) {
451     candidates.clear();
452 
453     // Determine whether it's possible to do exactly 2 bilinear passes in both
454     // directions.
455     RelativeSize output_size_2x2 = {0, 0};
456     for (Axis x_or_y : std::array<Axis, 2>{VERTICAL, HORIZONTAL}) {
457       if (cur[x_or_y] == from[x_or_y]) {
458         // For the first stage, the 2 bilinear passes must be the initial
459         // upscale followed by one downscale. If there is no initial upscale,
460         // then the 2 passes must both be downscales.
461         if (upscale_to[x_or_y] != from[x_or_y] &&
462             upscale_to[x_or_y] / 2 >= to[x_or_y]) {
463           output_size_2x2[x_or_y] = upscale_to[x_or_y] / 2;
464         } else if (upscale_to[x_or_y] == from[x_or_y] &&
465                    upscale_to[x_or_y] / 4 >= to[x_or_y]) {
466           output_size_2x2[x_or_y] = cur[x_or_y] / 4;
467         }
468       } else {
469         // For all later stages, the 2 bilinear passes must be 2 halvings.
470         if (cur[x_or_y] / 4 >= to[x_or_y]) {
471           output_size_2x2[x_or_y] = cur[x_or_y] / 4;
472         }
473       }
474     }
475     if (output_size_2x2[HORIZONTAL] != 0 && output_size_2x2[VERTICAL] != 0) {
476       candidates.push_back(
477           CandidateOp{Shader::BILINEAR2X2, HORIZONTAL, output_size_2x2});
478     }
479 
480     // Determine the valid set of Ops that do 1 to 3 bilinear passes in one
481     // direction and 0 or 1 pass in the other direction.
482     for (Axis x_or_y : std::array<Axis, 2>{VERTICAL, HORIZONTAL}) {
483       // The first bilinear pass in x_or_y must be an upscale or a halving.
484       Shader shader = Shader::BILINEAR;
485       RelativeSize output_size = cur;
486       if (cur[x_or_y] == from[x_or_y] && upscale_to[x_or_y] != from[x_or_y]) {
487         output_size[x_or_y] = upscale_to[x_or_y];
488       } else if (cur[x_or_y] / 2 >= to[x_or_y]) {
489         output_size[x_or_y] /= 2;
490       } else {
491         DCHECK_EQ(cur[x_or_y], to[x_or_y]);
492         continue;
493       }
494 
495       // Determine whether 1 or 2 additional passes can be made in the same
496       // direction.
497       if (output_size[x_or_y] / 4 >= to[x_or_y]) {
498         shader = Shader::BILINEAR4;  // 2 more passes == 3 total.
499         output_size[x_or_y] /= 4;
500       } else if (output_size[x_or_y] / 2 >= to[x_or_y]) {
501         shader = Shader::BILINEAR2;  // 1 more pass == 2 total.
502         output_size[x_or_y] /= 2;
503       } else {
504         DCHECK_EQ(output_size[x_or_y], to[x_or_y]);
505       }
506 
507       // Determine whether 0 or 1 bilinear passes can be made in the other
508       // direction at the same time.
509       const Axis y_or_x = TheOtherAxis(x_or_y);
510       if (cur[y_or_x] == from[y_or_x] && upscale_to[y_or_x] != from[y_or_x]) {
511         output_size[y_or_x] = upscale_to[y_or_x];
512       } else if (cur[y_or_x] / 2 >= to[y_or_x]) {
513         output_size[y_or_x] /= 2;
514       } else {
515         DCHECK_EQ(cur[y_or_x], to[y_or_x]);
516       }
517 
518       candidates.push_back(CandidateOp{shader, x_or_y, output_size});
519     }
520 
521     // From the candidates, pick the one that produces the fewest number of
522     // output pixels, and append a new ScalerStage. There are opportunities to
523     // improve the "cost function" here (e.g., pixels in the Y direction
524     // probably cost more to process than pixels in the X direction), but that
525     // would require more research.
526     const auto best_candidate = std::min_element(
527         candidates.begin(), candidates.end(),
528         [](const CandidateOp& a, const CandidateOp& b) {
529           static_assert(sizeof(a.output_size[0]) <= sizeof(int32_t),
530                         "Overflow issue in the math here.");
531           const int64_t cost_of_a =
532               int64_t{a.output_size[HORIZONTAL]} * a.output_size[VERTICAL];
533           const int64_t cost_of_b =
534               int64_t{b.output_size[HORIZONTAL]} * b.output_size[VERTICAL];
535           return cost_of_a < cost_of_b;
536         });
537     DCHECK(best_candidate != candidates.end());
538     DCHECK(cur != best_candidate->output_size)
539         << "Best candidate's output size (" << best_candidate->output_size
540         << ") should not equal the input size.";
541     auto next_stage = std::make_unique<ScalerStage>(
542         gl, best_candidate->shader, best_candidate->primary_axis,
543         cur.AsVector2d(), best_candidate->output_size.AsVector2d());
544     next_stage->set_input_stage(std::move(chain));
545     chain = std::move(next_stage);
546   }
547 
548   return chain;
549 }
550 
551 // static
CreateTheBestScalingChain(gpu::gles2::GLES2Interface * gl,const gfx::Vector2d & scale_from,const gfx::Vector2d & scale_to)552 std::unique_ptr<GLScaler::ScalerStage> GLScaler::CreateTheBestScalingChain(
553     gpu::gles2::GLES2Interface* gl,
554     const gfx::Vector2d& scale_from,
555     const gfx::Vector2d& scale_to) {
556   // The BEST quality chain performs one bicubic upscale followed by N bicubic
557   // halvings, and does this is both directions. Exception: No upscale is needed
558   // when |scale_from| is a power of two multiple of |scale_to|.
559 
560   // Determine the initial upscaled-to size, as the minimum number of doublings
561   // to make |scale_to| greater than |scale_from|.
562   const RelativeSize from(scale_from);
563   const RelativeSize to(scale_to);
564   RelativeSize upscale_to = to;
565   for (Axis x_or_y : std::array<Axis, 2>{HORIZONTAL, VERTICAL}) {
566     while (upscale_to[x_or_y] < from[x_or_y]) {
567       upscale_to[x_or_y] *= 2;
568     }
569   }
570 
571   // Create the stages in order from first-to-last.
572   RelativeSize cur = from;
573   std::unique_ptr<ScalerStage> chain;
574   for (Axis x_or_y : std::array<Axis, 2>{VERTICAL, HORIZONTAL}) {
575     if (upscale_to[x_or_y] != from[x_or_y]) {
576       RelativeSize next = cur;
577       next[x_or_y] = upscale_to[x_or_y];
578       auto upscale_stage =
579           std::make_unique<ScalerStage>(gl, Shader::BICUBIC_UPSCALE, x_or_y,
580                                         cur.AsVector2d(), next.AsVector2d());
581       upscale_stage->set_input_stage(std::move(chain));
582       chain = std::move(upscale_stage);
583       cur = next;
584     }
585     while (cur[x_or_y] > to[x_or_y]) {
586       RelativeSize next = cur;
587       next[x_or_y] /= 2;
588       auto next_stage =
589           std::make_unique<ScalerStage>(gl, Shader::BICUBIC_HALF_1D, x_or_y,
590                                         cur.AsVector2d(), next.AsVector2d());
591       next_stage->set_input_stage(std::move(chain));
592       chain = std::move(next_stage);
593       cur = next;
594     }
595   }
596   DCHECK_EQ(cur, to);
597 
598   return chain;
599 }
600 
601 // static
MaybeAppendExportStage(gpu::gles2::GLES2Interface * gl,std::unique_ptr<GLScaler::ScalerStage> chain,GLScaler::Parameters::ExportFormat export_format)602 std::unique_ptr<GLScaler::ScalerStage> GLScaler::MaybeAppendExportStage(
603     gpu::gles2::GLES2Interface* gl,
604     std::unique_ptr<GLScaler::ScalerStage> chain,
605     GLScaler::Parameters::ExportFormat export_format) {
606   DCHECK(chain);
607 
608   if (export_format == Parameters::ExportFormat::INTERLEAVED_QUADS) {
609     return chain;  // No format change.
610   }
611 
612   // If the final stage uses the BILINEAR shader that is not upscaling, the
613   // export stage can replace it with no change in the results. Otherwise, a
614   // separate export stage will be appended.
615   gfx::Vector2d scale_from = chain->scale_from();
616   const gfx::Vector2d scale_to = chain->scale_to();
617   if (chain->shader() == Shader::BILINEAR && scale_from.x() >= scale_to.x() &&
618       scale_from.y() >= scale_to.y()) {
619     chain = chain->take_input_stage();
620   } else {
621     scale_from = scale_to;
622   }
623 
624   Shader shader = Shader::BILINEAR;
625   scale_from.set_x(scale_from.x() * 4);
626   switch (export_format) {
627     case Parameters::ExportFormat::INTERLEAVED_QUADS:
628       NOTREACHED();
629       break;
630     case Parameters::ExportFormat::CHANNEL_0:
631       shader = Shader::PLANAR_CHANNEL_0;
632       break;
633     case Parameters::ExportFormat::CHANNEL_1:
634       shader = Shader::PLANAR_CHANNEL_1;
635       break;
636     case Parameters::ExportFormat::CHANNEL_2:
637       shader = Shader::PLANAR_CHANNEL_2;
638       break;
639     case Parameters::ExportFormat::CHANNEL_3:
640       shader = Shader::PLANAR_CHANNEL_3;
641       break;
642     case Parameters::ExportFormat::NV61:
643       shader = Shader::I422_NV61_MRT;
644       break;
645     case Parameters::ExportFormat::DEINTERLEAVE_PAIRWISE:
646       shader = Shader::DEINTERLEAVE_PAIRWISE_MRT;
647       // Horizontal scale is only 0.5X, not 0.25X like all the others.
648       scale_from.set_x(scale_from.x() / 2);
649       break;
650   }
651 
652   auto export_stage = std::make_unique<ScalerStage>(gl, shader, HORIZONTAL,
653                                                     scale_from, scale_to);
654   export_stage->set_input_stage(std::move(chain));
655   return export_stage;
656 }
657 
658 // static
TheOtherAxis(GLScaler::Axis x_or_y)659 GLScaler::Axis GLScaler::TheOtherAxis(GLScaler::Axis x_or_y) {
660   return x_or_y == HORIZONTAL ? VERTICAL : HORIZONTAL;
661 }
662 
663 // static
GetShaderName(GLScaler::Shader shader)664 const char* GLScaler::GetShaderName(GLScaler::Shader shader) {
665   switch (shader) {
666 #define CASE_RETURN_SHADER_STR(x) \
667   case Shader::x:                 \
668     return #x
669     CASE_RETURN_SHADER_STR(BILINEAR);
670     CASE_RETURN_SHADER_STR(BILINEAR2);
671     CASE_RETURN_SHADER_STR(BILINEAR3);
672     CASE_RETURN_SHADER_STR(BILINEAR4);
673     CASE_RETURN_SHADER_STR(BILINEAR2X2);
674     CASE_RETURN_SHADER_STR(BICUBIC_UPSCALE);
675     CASE_RETURN_SHADER_STR(BICUBIC_HALF_1D);
676     CASE_RETURN_SHADER_STR(PLANAR_CHANNEL_0);
677     CASE_RETURN_SHADER_STR(PLANAR_CHANNEL_1);
678     CASE_RETURN_SHADER_STR(PLANAR_CHANNEL_2);
679     CASE_RETURN_SHADER_STR(PLANAR_CHANNEL_3);
680     CASE_RETURN_SHADER_STR(I422_NV61_MRT);
681     CASE_RETURN_SHADER_STR(DEINTERLEAVE_PAIRWISE_MRT);
682 #undef CASE_RETURN_SHADER_STR
683   }
684 }
685 
686 // static
AreAllGLExtensionsPresent(gpu::gles2::GLES2Interface * gl,const std::vector<std::string> & names)687 bool GLScaler::AreAllGLExtensionsPresent(
688     gpu::gles2::GLES2Interface* gl,
689     const std::vector<std::string>& names) {
690   DCHECK(gl);
691   if (const auto* extensions = gl->GetString(GL_EXTENSIONS)) {
692     const std::string extensions_string =
693         " " + std::string(reinterpret_cast<const char*>(extensions)) + " ";
694     for (const std::string& name : names) {
695       if (extensions_string.find(" " + name + " ") == std::string::npos) {
696         return false;
697       }
698     }
699     return true;
700   }
701   return false;
702 }
703 
704 GLScaler::Parameters::Parameters() = default;
705 GLScaler::Parameters::Parameters(const Parameters& other) = default;
706 GLScaler::Parameters::~Parameters() = default;
707 
708 // static
709 const GLfloat GLScaler::ShaderProgram::kVertexAttributes[16] = {
710     -1.0f, -1.0f, 0.0f, 0.0f,  // vertex 0
711     1.0f,  -1.0f, 1.0f, 0.0f,  // vertex 1
712     -1.0f, 1.0f,  0.0f, 1.0f,  // vertex 2
713     1.0f,  1.0f,  1.0f, 1.0f,  // vertex 3
714 };
715 
ShaderProgram(gpu::gles2::GLES2Interface * gl,GLScaler::Shader shader,GLenum texture_type,const gfx::ColorTransform * color_transform,const GLenum swizzle[2])716 GLScaler::ShaderProgram::ShaderProgram(
717     gpu::gles2::GLES2Interface* gl,
718     GLScaler::Shader shader,
719     GLenum texture_type,
720     const gfx::ColorTransform* color_transform,
721     const GLenum swizzle[2])
722     : gl_(gl),
723       shader_(shader),
724       texture_type_(texture_type),
725       program_(gl_->CreateProgram()) {
726   DCHECK(program_);
727 
728   std::basic_ostringstream<GLchar> vertex_header;
729   std::basic_ostringstream<GLchar> fragment_directives;
730   std::basic_ostringstream<GLchar> fragment_header;
731   std::basic_ostringstream<GLchar> shared_variables;
732   std::basic_ostringstream<GLchar> vertex_main;
733   std::basic_ostringstream<GLchar> fragment_main;
734 
735   vertex_header
736       << ("precision highp float;\n"
737           "attribute vec2 a_position;\n"
738           "attribute vec2 a_texcoord;\n"
739           "uniform vec4 src_rect;\n");
740 
741   fragment_header << "precision mediump float;\n";
742   switch (texture_type_) {
743     case GL_FLOAT:
744       fragment_header << "precision highp sampler2D;\n";
745       break;
746     case GL_HALF_FLOAT_OES:
747       fragment_header << "precision mediump sampler2D;\n";
748       break;
749     default:
750       fragment_header << "precision lowp sampler2D;\n";
751       break;
752   }
753   fragment_header << "uniform sampler2D s_texture;\n";
754 
755   if (color_transform && shader_ != Shader::PLANAR_CHANNEL_3) {
756     const std::string& source = color_transform->GetShaderSource();
757     // Assumption: gfx::ColorTransform::GetShaderSource() should provide a
758     // function named DoColorConversion() that takes a vec3 argument and returns
759     // a vec3.
760     DCHECK_NE(source.find("DoColorConversion"), std::string::npos);
761     fragment_header << source;
762   }
763 
764   vertex_main
765       << ("  gl_Position = vec4(a_position, 0.0, 1.0);\n"
766           "  vec2 texcoord = src_rect.xy + a_texcoord * src_rect.zw;\n");
767 
768   switch (shader_) {
769     case Shader::BILINEAR:
770       shared_variables << "varying highp vec2 v_texcoord;\n";
771       vertex_main << "  v_texcoord = texcoord;\n";
772       fragment_main << "  vec4 sample = texture2D(s_texture, v_texcoord);\n";
773       if (color_transform) {
774         fragment_main << "  sample.rgb = DoColorConversion(sample.rgb);\n";
775       }
776       if (swizzle[0] == GL_BGRA_EXT) {
777         fragment_main << "  sample.rb = sample.br;\n";
778       }
779       fragment_main << "  gl_FragColor = sample;\n";
780       break;
781 
782     case Shader::BILINEAR2:
783       // This is equivialent to two passes of the BILINEAR shader above. It can
784       // be used to scale an image down 1.0x-2.0x in either dimension, or
785       // exactly 4x.
786       shared_variables << "varying highp vec4 v_texcoords;\n";
787       vertex_header << "uniform vec2 scaling_vector;\n";
788       vertex_main
789           << ("  vec2 step = scaling_vector / 4.0;\n"
790               "  v_texcoords.xy = texcoord + step;\n"
791               "  v_texcoords.zw = texcoord - step;\n");
792       fragment_main
793           << ("  vec4 blended = (texture2D(s_texture, v_texcoords.xy) +\n"
794               "                  texture2D(s_texture, v_texcoords.zw)) /\n"
795               "                 2.0;\n");
796       if (color_transform) {
797         fragment_main << "  blended.rgb = DoColorConversion(blended.rgb);\n";
798       }
799       if (swizzle[0] == GL_BGRA_EXT) {
800         fragment_main << "  blended.rb = blended.br;\n";
801       }
802       fragment_main << "  gl_FragColor = blended;\n";
803       break;
804 
805     case Shader::BILINEAR3:
806       // This is kind of like doing 1.5 passes of the BILINEAR shader. It can be
807       // used to scale an image down 1.5x-3.0x, or exactly 6x.
808       shared_variables
809           << ("varying highp vec4 v_texcoords0;\n"
810               "varying highp vec2 v_texcoords1;\n");
811       vertex_header << "uniform vec2 scaling_vector;\n";
812       vertex_main
813           << ("  vec2 step = scaling_vector / 3.0;\n"
814               "  v_texcoords0.xy = texcoord + step;\n"
815               "  v_texcoords0.zw = texcoord;\n"
816               "  v_texcoords1 = texcoord - step;\n");
817       fragment_main
818           << ("  vec4 blended = (texture2D(s_texture, v_texcoords0.xy) +\n"
819               "                  texture2D(s_texture, v_texcoords0.zw) +\n"
820               "                  texture2D(s_texture, v_texcoords1)) / 3.0;\n");
821       if (color_transform) {
822         fragment_main << "  blended.rgb = DoColorConversion(blended.rgb);\n";
823       }
824       if (swizzle[0] == GL_BGRA_EXT) {
825         fragment_main << "  blended.rb = blended.br;\n";
826       }
827       fragment_main << "  gl_FragColor = blended;\n";
828       break;
829 
830     case Shader::BILINEAR4:
831       // This is equivialent to three passes of the BILINEAR shader above. It
832       // can be used to scale an image down 2.0x-4.0x or exactly 8x.
833       shared_variables << "varying highp vec4 v_texcoords[2];\n";
834       vertex_header << "uniform vec2 scaling_vector;\n";
835       vertex_main
836           << ("  vec2 step = scaling_vector / 8.0;\n"
837               "  v_texcoords[0].xy = texcoord - step * 3.0;\n"
838               "  v_texcoords[0].zw = texcoord - step;\n"
839               "  v_texcoords[1].xy = texcoord + step;\n"
840               "  v_texcoords[1].zw = texcoord + step * 3.0;\n");
841       fragment_main
842           << ("  vec4 blended = (\n"
843               "      texture2D(s_texture, v_texcoords[0].xy) +\n"
844               "      texture2D(s_texture, v_texcoords[0].zw) +\n"
845               "      texture2D(s_texture, v_texcoords[1].xy) +\n"
846               "      texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n");
847       if (color_transform) {
848         fragment_main << "  blended.rgb = DoColorConversion(blended.rgb);\n";
849       }
850       if (swizzle[0] == GL_BGRA_EXT) {
851         fragment_main << "  blended.rb = blended.br;\n";
852       }
853       fragment_main << "  gl_FragColor = blended;\n";
854       break;
855 
856     case Shader::BILINEAR2X2:
857       // This is equivialent to four passes of the BILINEAR shader above, two in
858       // each dimension. It can be used to scale an image down 1.0x-2.0x in both
859       // X and Y directions. Or, it could be used to scale an image down by
860       // exactly 4x in both dimensions.
861       shared_variables << "varying highp vec4 v_texcoords[2];\n";
862       vertex_header << "uniform vec2 scaling_vector;\n";
863       vertex_main
864           << ("  vec2 step = scaling_vector / 4.0;\n"
865               "  v_texcoords[0].xy = texcoord + vec2(step.x, step.y);\n"
866               "  v_texcoords[0].zw = texcoord + vec2(step.x, -step.y);\n"
867               "  v_texcoords[1].xy = texcoord + vec2(-step.x, step.y);\n"
868               "  v_texcoords[1].zw = texcoord + vec2(-step.x, -step.y);\n");
869       fragment_main
870           << ("  vec4 blended = (\n"
871               "      texture2D(s_texture, v_texcoords[0].xy) +\n"
872               "      texture2D(s_texture, v_texcoords[0].zw) +\n"
873               "      texture2D(s_texture, v_texcoords[1].xy) +\n"
874               "      texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n");
875       if (color_transform) {
876         fragment_main << "  blended.rgb = DoColorConversion(blended.rgb);\n";
877       }
878       if (swizzle[0] == GL_BGRA_EXT) {
879         fragment_main << "  blended.rb = blended.br;\n";
880       }
881       fragment_main << "  gl_FragColor = blended;\n";
882       break;
883 
884     case Shader::BICUBIC_UPSCALE:
885       // When scaling up, 4 texture reads are necessary. However, some
886       // instructions can be saved because the parameter passed to the bicubic
887       // function will be in a known range. Also, when sampling the bicubic
888       // function like this, the sum is always exactly one, so normalization can
889       // be skipped as well.
890       shared_variables << "varying highp vec2 v_texcoord;\n";
891       vertex_main << "  v_texcoord = texcoord;\n";
892       fragment_header
893           << ("uniform highp vec2 src_pixelsize;\n"
894               "uniform highp vec2 scaling_vector;\n"
895               "const float a = -0.5;\n"
896               // This function is equivialent to calling the bicubic
897               // function with x-1, x, 1-x and 2-x (assuming
898               // 0 <= x < 1). The following is the Catmull-Rom spline.
899               // See: http://wikipedia.org/wiki/Cubic_Hermite_spline
900               "vec4 filt4(float x) {\n"
901               "  return vec4(x * x * x, x * x, x, 1) *\n"
902               "         mat4(       a,      -2.0 * a,   a, 0.0,\n"
903               "               a + 2.0,      -a - 3.0, 0.0, 1.0,\n"
904               "              -a - 2.0, 3.0 + 2.0 * a,  -a, 0.0,\n"
905               "                    -a,             a, 0.0, 0.0);\n"
906               "}\n"
907               "mat4 pixels_x(highp vec2 pos, highp vec2 step) {\n"
908               "  return mat4(texture2D(s_texture, pos - step),\n"
909               "              texture2D(s_texture, pos),\n"
910               "              texture2D(s_texture, pos + step),\n"
911               "              texture2D(s_texture, pos + step * 2.0));\n"
912               "}\n");
913       fragment_main
914           << ("  highp vec2 pixel_pos = v_texcoord * src_pixelsize - \n"
915               "      scaling_vector / 2.0;\n"
916               "  highp float frac = fract(dot(pixel_pos, scaling_vector));\n"
917               "  highp vec2 base = \n"
918               "      (floor(pixel_pos) + vec2(0.5)) / src_pixelsize;\n"
919               "  highp vec2 step = scaling_vector / src_pixelsize;\n"
920               "  vec4 blended = pixels_x(base, step) * filt4(frac);\n");
921       if (color_transform) {
922         fragment_main << "  blended.rgb = DoColorConversion(blended.rgb);\n";
923       }
924       if (swizzle[0] == GL_BGRA_EXT) {
925         fragment_main << "  blended.rb = blended.br;\n";
926       }
927       fragment_main << "  gl_FragColor = blended;\n";
928       break;
929 
930     case Shader::BICUBIC_HALF_1D:
931       // This scales down an image by exactly half in one dimension. The
932       // bilinear lookup reduces the number of texture reads from 8 to 4.
933       shared_variables << "varying highp vec4 v_texcoords[2];\n";
934       vertex_header
935           << ("uniform vec2 scaling_vector;\n"
936               "const float CenterDist = 99.0 / 140.0;\n"
937               "const float LobeDist = 11.0 / 4.0;\n");
938       vertex_main
939           << ("  vec2 step = scaling_vector / 2.0;\n"
940               "  v_texcoords[0].xy = texcoord - LobeDist * step;\n"
941               "  v_texcoords[0].zw = texcoord - CenterDist * step;\n"
942               "  v_texcoords[1].xy = texcoord + CenterDist * step;\n"
943               "  v_texcoords[1].zw = texcoord + LobeDist * step;\n");
944       fragment_header
945           << ("const float CenterWeight = 35.0 / 64.0;\n"
946               "const float LobeWeight = -3.0 / 64.0;\n");
947       fragment_main
948           << ("  vec4 blended = \n"
949               // Lobe pixels
950               "      (texture2D(s_texture, v_texcoords[0].xy) +\n"
951               "       texture2D(s_texture, v_texcoords[1].zw)) *\n"
952               "          LobeWeight +\n"
953               // Center pixels
954               "      (texture2D(s_texture, v_texcoords[0].zw) +\n"
955               "       texture2D(s_texture, v_texcoords[1].xy)) *\n"
956               "          CenterWeight;\n");
957       if (color_transform) {
958         fragment_main << "  blended.rgb = DoColorConversion(blended.rgb);\n";
959       }
960       if (swizzle[0] == GL_BGRA_EXT) {
961         fragment_main << "  blended.rb = blended.br;\n";
962       }
963       fragment_main << "  gl_FragColor = blended;\n";
964       break;
965 
966     case Shader::PLANAR_CHANNEL_0:
967     case Shader::PLANAR_CHANNEL_1:
968     case Shader::PLANAR_CHANNEL_2:
969     case Shader::PLANAR_CHANNEL_3: {
970       // Select one color channel, and pack 4 pixels into one output quad. See
971       // header file for diagram.
972       shared_variables << "varying highp vec4 v_texcoords[2];\n";
973       vertex_header << "uniform vec2 scaling_vector;\n";
974       vertex_main
975           << ("  vec2 step = scaling_vector / 4.0;\n"
976               "  v_texcoords[0].xy = texcoord - step * 1.5;\n"
977               "  v_texcoords[0].zw = texcoord - step * 0.5;\n"
978               "  v_texcoords[1].xy = texcoord + step * 0.5;\n"
979               "  v_texcoords[1].zw = texcoord + step * 1.5;\n");
980       std::basic_string<GLchar> convert_open;
981       std::basic_string<GLchar> convert_close;
982       if (color_transform && shader_ != Shader::PLANAR_CHANNEL_3) {
983         convert_open = "DoColorConversion(";
984         convert_close = ".rgb)";
985       }
986       const char selector = "rgba"[static_cast<int>(shader_) -
987                                    static_cast<int>(Shader::PLANAR_CHANNEL_0)];
988       fragment_main << "  vec4 packed_quad = vec4(\n"
989                     << "      " << convert_open
990                     << "texture2D(s_texture, v_texcoords[0].xy)"
991                     << convert_close << '.' << selector << ",\n"
992                     << "      " << convert_open
993                     << "texture2D(s_texture, v_texcoords[0].zw)"
994                     << convert_close << '.' << selector << ",\n"
995                     << "      " << convert_open
996                     << "texture2D(s_texture, v_texcoords[1].xy)"
997                     << convert_close << '.' << selector << ",\n"
998                     << "      " << convert_open
999                     << "texture2D(s_texture, v_texcoords[1].zw)"
1000                     << convert_close << '.' << selector << ");\n";
1001       if (swizzle[0] == GL_BGRA_EXT) {
1002         fragment_main << "  packed_quad.rb = packed_quad.br;\n";
1003       }
1004       fragment_main << "  gl_FragColor = packed_quad;\n";
1005       break;
1006     }
1007 
1008     case Shader::I422_NV61_MRT:
1009       // I422 sampling, delivered via two output textures (NV61 format). See
1010       // header file for diagram.
1011       shared_variables << "varying highp vec4 v_texcoords[2];\n";
1012       vertex_header << "uniform vec2 scaling_vector;\n";
1013       vertex_main
1014           << ("  vec2 step = scaling_vector / 4.0;\n"
1015               "  v_texcoords[0].xy = texcoord - step * 1.5;\n"
1016               "  v_texcoords[0].zw = texcoord - step * 0.5;\n"
1017               "  v_texcoords[1].xy = texcoord + step * 0.5;\n"
1018               "  v_texcoords[1].zw = texcoord + step * 1.5;\n");
1019       fragment_directives << "#extension GL_EXT_draw_buffers : enable\n";
1020       fragment_main
1021           << ("  vec3 pixel0 = texture2D(s_texture, v_texcoords[0].xy).rgb;\n"
1022               "  vec3 pixel1 = texture2D(s_texture, v_texcoords[0].zw).rgb;\n"
1023               "  vec3 pixel01 = (pixel0 + pixel1) / 2.0;\n"
1024               "  vec3 pixel2 = texture2D(s_texture, v_texcoords[1].xy).rgb;\n"
1025               "  vec3 pixel3 = texture2D(s_texture, v_texcoords[1].zw).rgb;\n"
1026               "  vec3 pixel23 = (pixel2 + pixel3) / 2.0;\n");
1027       if (color_transform) {
1028         fragment_main
1029             << ("  pixel0 = DoColorConversion(pixel0);\n"
1030                 "  pixel1 = DoColorConversion(pixel1);\n"
1031                 "  pixel01 = DoColorConversion(pixel01);\n"
1032                 "  pixel2 = DoColorConversion(pixel2);\n"
1033                 "  pixel3 = DoColorConversion(pixel3);\n"
1034                 "  pixel23 = DoColorConversion(pixel23);\n");
1035       }
1036       if (swizzle[0] == GL_BGRA_EXT) {
1037         fragment_main
1038             << ("  gl_FragData[0] = \n"
1039                 "      vec4(pixel2.r, pixel1.r, pixel0.r, pixel3.r);\n");
1040       } else {
1041         fragment_main
1042             << ("  gl_FragData[0] = \n"
1043                 "      vec4(pixel0.r, pixel1.r, pixel2.r, pixel3.r);\n");
1044       }
1045       if (swizzle[1] == GL_BGRA_EXT) {
1046         fragment_main
1047             << ("  gl_FragData[1] = \n"
1048                 "      vec4(pixel23.g, pixel01.b, pixel01.g, pixel23.b);\n");
1049       } else {
1050         fragment_main
1051             << ("  gl_FragData[1] = \n"
1052                 "      vec4(pixel01.g, pixel01.b, pixel23.g, pixel23.b);\n");
1053       }
1054       break;
1055 
1056     case Shader::DEINTERLEAVE_PAIRWISE_MRT:
1057       // Sample two pixels and unswizzle them. There's no need to do vertical
1058       // scaling with math, since the bilinear interpolation in the sampler
1059       // takes care of that.
1060       shared_variables << "varying highp vec4 v_texcoords;\n";
1061       vertex_header << "uniform vec2 scaling_vector;\n";
1062       vertex_main
1063           << ("  vec2 step = scaling_vector / 2.0;\n"
1064               "  v_texcoords.xy = texcoord - step * 0.5;\n"
1065               "  v_texcoords.zw = texcoord + step * 0.5;\n");
1066       fragment_directives << "#extension GL_EXT_draw_buffers : enable\n";
1067       DCHECK(!color_transform);
1068       fragment_main
1069           << ("  vec4 lo_uvuv = texture2D(s_texture, v_texcoords.xy);\n"
1070               "  vec4 hi_uvuv = texture2D(s_texture, v_texcoords.zw);\n"
1071               "  vec4 uuuu = vec4(lo_uvuv.rb, hi_uvuv.rb);\n"
1072               "  vec4 vvvv = vec4(lo_uvuv.ga, hi_uvuv.ga);\n");
1073       if (swizzle[0] == GL_BGRA_EXT) {
1074         fragment_main << "  uuuu.rb = uuuu.br;\n";
1075       }
1076       fragment_main << "  gl_FragData[0] = uuuu;\n";
1077       if (swizzle[1] == GL_BGRA_EXT) {
1078         fragment_main << "  vvvv.rb = vvvv.br;\n";
1079       }
1080       fragment_main << "  gl_FragData[1] = vvvv;\n";
1081       break;
1082   }
1083 
1084   // Helper function to compile the shader source and log the GLSL compiler's
1085   // results.
1086   const auto CompileShaderFromSource =
1087       [](GLES2Interface* gl, const std::basic_string<GLchar>& source,
1088          GLenum type) -> GLuint {
1089     VLOG(2) << __func__ << ": Compiling shader " << type
1090             << " with source:" << std::endl
1091             << source;
1092     const GLuint shader = gl->CreateShader(type);
1093     const GLchar* source_data = source.data();
1094     const GLint length = base::checked_cast<GLint>(source.size());
1095     gl->ShaderSource(shader, 1, &source_data, &length);
1096     gl->CompileShader(shader);
1097     GLint compile_status = GL_FALSE;
1098     gl->GetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
1099 
1100     // Fetch logs and forward them to the system logger. If compilation failed,
1101     // clean-up and return 0 for error.
1102     if (compile_status != GL_TRUE || VLOG_IS_ON(2)) {
1103       GLint log_length = 0;
1104       gl->GetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
1105       std::string log;
1106       if (log_length > 0) {
1107         std::unique_ptr<GLchar[]> tmp(new GLchar[log_length]);
1108         GLsizei returned_log_length = 0;
1109         gl->GetShaderInfoLog(shader, log_length, &returned_log_length,
1110                              tmp.get());
1111         log.assign(tmp.get(), returned_log_length);
1112       }
1113       if (log.empty()) {
1114         log = "<<NO LOG>>";
1115       }
1116       if (compile_status != GL_TRUE) {
1117         LOG(ERROR) << __func__ << ": Compilation of shader " << type
1118                    << " failed:" << std::endl
1119                    << log;
1120         gl->DeleteShader(shader);
1121         return 0;
1122       }
1123       VLOG(2) << __func__ << ": Compilation of shader " << type
1124               << " succeeded:" << std::endl
1125               << log;
1126     }
1127     return shader;
1128   };
1129 
1130   // Compile the vertex shader and attach it to the program.
1131   const std::string shared_variables_str = shared_variables.str();
1132   const GLuint vertex_shader =
1133       CompileShaderFromSource(gl_,
1134                               vertex_header.str() + shared_variables_str +
1135                                   "void main() {\n" + vertex_main.str() + "}\n",
1136                               GL_VERTEX_SHADER);
1137   if (vertex_shader == 0) {
1138     return;
1139   }
1140   gl_->AttachShader(program_, vertex_shader);
1141   gl_->DeleteShader(vertex_shader);
1142 
1143   // Compile the fragment shader and attach to |program_|.
1144   const GLuint fragment_shader = CompileShaderFromSource(
1145       gl_,
1146       fragment_directives.str() + fragment_header.str() + shared_variables_str +
1147           "void main() {\n" + fragment_main.str() + "}\n",
1148       GL_FRAGMENT_SHADER);
1149   if (fragment_shader == 0) {
1150     return;
1151   }
1152   gl_->AttachShader(program_, fragment_shader);
1153   gl_->DeleteShader(fragment_shader);
1154 
1155   // Link the program.
1156   gl_->LinkProgram(program_);
1157   GLint link_status = GL_FALSE;
1158   gl_->GetProgramiv(program_, GL_LINK_STATUS, &link_status);
1159   if (link_status != GL_TRUE) {
1160     LOG(ERROR) << "Failed to link shader program.";
1161     return;
1162   }
1163 
1164 #define DCHECK_RESOLVED_LOCATION(member)                                  \
1165   DCHECK(member != -1 || gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR) \
1166       << "Failed to get " #member " in program, or GPU process crashed."
1167 
1168   // Resolve the locations of the global variables.
1169   position_location_ = gl_->GetAttribLocation(program_, "a_position");
1170   DCHECK_RESOLVED_LOCATION(position_location_);
1171   texcoord_location_ = gl_->GetAttribLocation(program_, "a_texcoord");
1172   DCHECK_RESOLVED_LOCATION(texcoord_location_);
1173   texture_location_ = gl_->GetUniformLocation(program_, "s_texture");
1174   DCHECK_RESOLVED_LOCATION(texture_location_);
1175   src_rect_location_ = gl_->GetUniformLocation(program_, "src_rect");
1176   DCHECK_RESOLVED_LOCATION(src_rect_location_);
1177   switch (shader_) {
1178     case Shader::BILINEAR:
1179       break;
1180 
1181     case Shader::BILINEAR2:
1182     case Shader::BILINEAR3:
1183     case Shader::BILINEAR4:
1184     case Shader::BILINEAR2X2:
1185     case Shader::BICUBIC_HALF_1D:
1186     case Shader::PLANAR_CHANNEL_0:
1187     case Shader::PLANAR_CHANNEL_1:
1188     case Shader::PLANAR_CHANNEL_2:
1189     case Shader::PLANAR_CHANNEL_3:
1190     case Shader::I422_NV61_MRT:
1191     case Shader::DEINTERLEAVE_PAIRWISE_MRT:
1192       scaling_vector_location_ =
1193           gl_->GetUniformLocation(program_, "scaling_vector");
1194       DCHECK_RESOLVED_LOCATION(scaling_vector_location_);
1195       break;
1196 
1197     case Shader::BICUBIC_UPSCALE:
1198       src_pixelsize_location_ =
1199           gl_->GetUniformLocation(program_, "src_pixelsize");
1200       DCHECK_RESOLVED_LOCATION(src_pixelsize_location_);
1201       scaling_vector_location_ =
1202           gl_->GetUniformLocation(program_, "scaling_vector");
1203       DCHECK_RESOLVED_LOCATION(scaling_vector_location_);
1204       break;
1205   }
1206 
1207 #undef DCHECK_RESOLVED_LOCATION
1208 }
1209 
~ShaderProgram()1210 GLScaler::ShaderProgram::~ShaderProgram() {
1211   gl_->DeleteProgram(program_);
1212 }
1213 
UseProgram(const gfx::Size & src_texture_size,const gfx::RectF & src_rect,const gfx::Size & dst_size,GLScaler::Axis primary_axis,bool flip_y)1214 void GLScaler::ShaderProgram::UseProgram(const gfx::Size& src_texture_size,
1215                                          const gfx::RectF& src_rect,
1216                                          const gfx::Size& dst_size,
1217                                          GLScaler::Axis primary_axis,
1218                                          bool flip_y) {
1219   gl_->UseProgram(program_);
1220 
1221   // OpenGL defines the last parameter to VertexAttribPointer as type
1222   // "const GLvoid*" even though it is actually an offset into the buffer
1223   // object's data store and not a pointer to the client's address space.
1224   const void* offsets[2] = {nullptr,
1225                             reinterpret_cast<const void*>(2 * sizeof(GLfloat))};
1226 
1227   gl_->VertexAttribPointer(position_location_, 2, GL_FLOAT, GL_FALSE,
1228                            4 * sizeof(GLfloat), offsets[0]);
1229   gl_->EnableVertexAttribArray(position_location_);
1230 
1231   gl_->VertexAttribPointer(texcoord_location_, 2, GL_FLOAT, GL_FALSE,
1232                            4 * sizeof(GLfloat), offsets[1]);
1233   gl_->EnableVertexAttribArray(texcoord_location_);
1234 
1235   // Always sample from the first texture unit.
1236   gl_->Uniform1i(texture_location_, 0);
1237 
1238   // Convert |src_rect| from pixel coordinates to texture coordinates. The
1239   // source texture coordinates are in the range [0.0,1.0] for each dimension,
1240   // but the sampling rect may slightly "spill" outside that range (e.g., for
1241   // scaler overscan).
1242   GLfloat src_rect_texcoord[4] = {
1243       src_rect.x() / src_texture_size.width(),
1244       src_rect.y() / src_texture_size.height(),
1245       src_rect.width() / src_texture_size.width(),
1246       src_rect.height() / src_texture_size.height(),
1247   };
1248   if (flip_y) {
1249     src_rect_texcoord[1] += src_rect_texcoord[3];
1250     src_rect_texcoord[3] *= -1.0f;
1251   }
1252   gl_->Uniform4fv(src_rect_location_, 1, src_rect_texcoord);
1253 
1254   // Set shader-specific uniform inputs. The |scaling_vector| is the ratio of
1255   // the number of source pixels sampled per dest pixels output. It is used by
1256   // the shader programs to locate distinct texels from the source texture, and
1257   // sample them at the appropriate offset to produce each output texel.
1258   switch (shader_) {
1259     case Shader::BILINEAR:
1260       break;
1261 
1262     case Shader::BILINEAR2:
1263     case Shader::BILINEAR3:
1264     case Shader::BILINEAR4:
1265     case Shader::BICUBIC_HALF_1D:
1266     case Shader::PLANAR_CHANNEL_0:
1267     case Shader::PLANAR_CHANNEL_1:
1268     case Shader::PLANAR_CHANNEL_2:
1269     case Shader::PLANAR_CHANNEL_3:
1270     case Shader::I422_NV61_MRT:
1271     case Shader::DEINTERLEAVE_PAIRWISE_MRT:
1272       switch (primary_axis) {
1273         case HORIZONTAL:
1274           gl_->Uniform2f(scaling_vector_location_,
1275                          src_rect_texcoord[2] / dst_size.width(), 0.0);
1276           break;
1277         case VERTICAL:
1278           gl_->Uniform2f(scaling_vector_location_, 0.0,
1279                          src_rect_texcoord[3] / dst_size.height());
1280           break;
1281       }
1282       break;
1283 
1284     case Shader::BILINEAR2X2:
1285       gl_->Uniform2f(scaling_vector_location_,
1286                      src_rect_texcoord[2] / dst_size.width(),
1287                      src_rect_texcoord[3] / dst_size.height());
1288       break;
1289 
1290     case Shader::BICUBIC_UPSCALE:
1291       gl_->Uniform2f(src_pixelsize_location_, src_texture_size.width(),
1292                      src_texture_size.height());
1293       // For this shader program, the |scaling_vector| has an alternate meaning:
1294       // It is only being used to select whether bicubic sampling is stepped in
1295       // the X or the Y direction.
1296       gl_->Uniform2f(scaling_vector_location_,
1297                      primary_axis == HORIZONTAL ? 1.0 : 0.0,
1298                      primary_axis == VERTICAL ? 1.0 : 0.0);
1299       break;
1300   }
1301 }
1302 
ScalerStage(gpu::gles2::GLES2Interface * gl,GLScaler::Shader shader,GLScaler::Axis primary_axis,const gfx::Vector2d & scale_from,const gfx::Vector2d & scale_to)1303 GLScaler::ScalerStage::ScalerStage(gpu::gles2::GLES2Interface* gl,
1304                                    GLScaler::Shader shader,
1305                                    GLScaler::Axis primary_axis,
1306                                    const gfx::Vector2d& scale_from,
1307                                    const gfx::Vector2d& scale_to)
1308     : gl_(gl),
1309       shader_(shader),
1310       primary_axis_(primary_axis),
1311       scale_from_(scale_from),
1312       scale_to_(scale_to) {
1313   DCHECK(gl_);
1314 }
1315 
~ScalerStage()1316 GLScaler::ScalerStage::~ScalerStage() {
1317   if (dest_framebuffer_) {
1318     gl_->DeleteFramebuffers(1, &dest_framebuffer_);
1319   }
1320   if (intermediate_texture_) {
1321     gl_->DeleteTextures(1, &intermediate_texture_);
1322   }
1323 }
1324 
ScaleToMultipleOutputs(GLuint src_texture,gfx::Size src_texture_size,const gfx::Vector2d & src_offset,GLuint dest_texture_0,GLuint dest_texture_1,const gfx::Rect & output_rect)1325 void GLScaler::ScalerStage::ScaleToMultipleOutputs(
1326     GLuint src_texture,
1327     gfx::Size src_texture_size,
1328     const gfx::Vector2d& src_offset,
1329     GLuint dest_texture_0,
1330     GLuint dest_texture_1,
1331     const gfx::Rect& output_rect) {
1332   if (output_rect.IsEmpty())
1333     return;  // No work to do.
1334 
1335   // Make a recursive call to the "input" ScalerStage to produce an intermediate
1336   // texture for this stage to source from. Adjust src_* variables to use the
1337   // intermediate texture as input.
1338   //
1339   // If there is no input stage, simply modify |src_rect| to account for the
1340   // overall |src_offset| and Y-flip.
1341   gfx::RectF src_rect = ToSourceRect(output_rect);
1342   if (input_stage_) {
1343     const gfx::Rect input_rect = ToInputRect(src_rect);
1344     EnsureIntermediateTextureDefined(input_rect.size());
1345     input_stage_->ScaleToMultipleOutputs(src_texture, src_texture_size,
1346                                          src_offset, intermediate_texture_, 0,
1347                                          input_rect);
1348     src_texture = intermediate_texture_;
1349     src_texture_size = intermediate_texture_size_;
1350     DCHECK(!is_flipped_source_);
1351     src_rect -= input_rect.OffsetFromOrigin();
1352   } else {
1353     if (is_flipped_source_) {
1354       src_rect.set_x(src_rect.x() + src_offset.x());
1355       src_rect.set_y(src_texture_size.height() - src_rect.bottom() -
1356                      src_offset.y());
1357     } else {
1358       src_rect += src_offset;
1359     }
1360   }
1361 
1362   // Attach the output texture(s) to the framebuffer.
1363   if (!dest_framebuffer_) {
1364     gl_->GenFramebuffers(1, &dest_framebuffer_);
1365   }
1366   gl_->BindFramebuffer(GL_FRAMEBUFFER, dest_framebuffer_);
1367   gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
1368                             dest_texture_0, 0);
1369   if (dest_texture_1 > 0) {
1370     gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + 1,
1371                               GL_TEXTURE_2D, dest_texture_1, 0);
1372   }
1373 
1374   // Bind to the source texture and set the texture sampler to use bilinear
1375   // filtering and clamp-to-edge, as required by all shader programs.
1376   //
1377   // It would be better to stash the existing parameter values, and restore them
1378   // back later. However, glGetTexParameteriv() currently requires a blocking
1379   // call to the GPU service, which is extremely costly performance-wise.
1380   gl_->ActiveTexture(GL_TEXTURE0);
1381   gl_->BindTexture(GL_TEXTURE_2D, src_texture);
1382   gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1383   gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1384   gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1385   gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1386 
1387   // Prepare the shader program for drawing.
1388   DCHECK(program_);
1389   program_->UseProgram(src_texture_size, src_rect, output_rect.size(),
1390                        primary_axis_, flip_output_);
1391 
1392   // Execute the draw.
1393   gl_->Viewport(0, 0, output_rect.width(), output_rect.height());
1394   const GLenum buffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT0 + 1};
1395   if (dest_texture_1 > 0) {
1396     gl_->DrawBuffersEXT(2, buffers);
1397   }
1398   gl_->DrawArrays(GL_TRIANGLE_STRIP, 0, 4);
1399   if (dest_texture_1 > 0) {
1400     // Set the draw buffers back, to not disrupt external operations.
1401     gl_->DrawBuffersEXT(1, buffers);
1402   }
1403 
1404   gl_->BindTexture(GL_TEXTURE_2D, 0);
1405   gl_->BindFramebuffer(GL_FRAMEBUFFER, 0);
1406 }
1407 
ToSourceRect(const gfx::Rect & output_rect) const1408 gfx::RectF GLScaler::ScalerStage::ToSourceRect(
1409     const gfx::Rect& output_rect) const {
1410   return gfx::ScaleRect(gfx::RectF(output_rect),
1411                         float{scale_from_.x()} / scale_to_.x(),
1412                         float{scale_from_.y()} / scale_to_.y());
1413 }
1414 
ToInputRect(gfx::RectF source_rect) const1415 gfx::Rect GLScaler::ScalerStage::ToInputRect(gfx::RectF source_rect) const {
1416   int overscan_x = 0;
1417   int overscan_y = 0;
1418   switch (shader_) {
1419     case Shader::BILINEAR:
1420     case Shader::BILINEAR2:
1421     case Shader::BILINEAR3:
1422     case Shader::BILINEAR4: {
1423       // These shaders sample 1 or more points along the primary axis, and only
1424       // 1 point in the other direction, in order to produce each output pixel.
1425       // The amount of overscan is always 0 or 1 pixel along the primary axis,
1426       // and this can be determined by looking at the upper-left-most source
1427       // texture sampling point: If this point is to the left of the middle of
1428       // the upper-left-most source pixel, the texture sampler will also read
1429       // the pixel to the left of that (for linear interpolation). Similar
1430       // behavior can occur towards the right, upwards, and downwards at the
1431       // source boundaries.
1432       int threshold;
1433       switch (shader_) {
1434         default:
1435           threshold = 1;
1436           break;
1437         case Shader::BILINEAR2:
1438           threshold = 2;
1439           break;
1440         case Shader::BILINEAR3:
1441           threshold = 3;
1442           break;
1443         case Shader::BILINEAR4:
1444           threshold = 4;
1445           break;
1446       }
1447       switch (primary_axis_) {
1448         case HORIZONTAL:
1449           if (scale_from_.x() < threshold * scale_to_.x()) {
1450             overscan_x = 1;
1451           }
1452           if (scale_from_.y() < scale_to_.y()) {
1453             overscan_y = 1;
1454           }
1455           break;
1456         case VERTICAL:
1457           if (scale_from_.x() < scale_to_.x()) {
1458             overscan_x = 1;
1459           }
1460           if (scale_from_.y() < threshold * scale_to_.y()) {
1461             overscan_y = 1;
1462           }
1463           break;
1464       }
1465       break;
1466     }
1467 
1468     case Shader::BILINEAR2X2:
1469       // This shader samples 2 points along both axes, and the overscan is 0 or
1470       // 1 pixel in both directions (same explanation as for the other BILINEAR
1471       // shaders).
1472       if (scale_from_.x() < 2 * scale_to_.x()) {
1473         overscan_x = 1;
1474       }
1475       if (scale_from_.y() < 2 * scale_to_.y()) {
1476         overscan_y = 1;
1477       }
1478       break;
1479 
1480     case Shader::BICUBIC_UPSCALE:
1481       // For each output pixel, this shader always reads 2 pixels about the
1482       // source position in one dimension, and has no overscan in the other
1483       // dimension.
1484       if (scale_from_.x() < scale_to_.x()) {
1485         DCHECK_EQ(HORIZONTAL, primary_axis_);
1486         overscan_x = 2;
1487       } else if (scale_from_.y() < scale_to_.y()) {
1488         DCHECK_EQ(VERTICAL, primary_axis_);
1489         overscan_y = 2;
1490       } else if (scale_from_ == scale_to_) {
1491         // Special case: When not scaling, the math in the shader will resolve
1492         // to just outputting the value for a single source pixel. The shader
1493         // will sample surrounding pixels, but then apply a zero weight to them
1494         // during convolution. Thus, there is effectively no overscan.
1495         NOTREACHED();  // This is a crazy-expensive way to do a 1:1 copy!
1496       } else {
1497         NOTREACHED();  // Downscaling is meaningless.
1498       }
1499       break;
1500 
1501     case Shader::BICUBIC_HALF_1D: {
1502       // For each output pixel, this shader always reads 4 pixels about the
1503       // source position in one dimension, and has no overscan in the other
1504       // dimension. However, since the source position always has a distance
1505       // >= 1 inside the "logical" bounds, there can never be more than 3 pixels
1506       // of overscan.
1507       if (scale_from_.x() == 2 * scale_to_.x()) {
1508         DCHECK_EQ(HORIZONTAL, primary_axis_);
1509         overscan_x = 3;
1510       } else if (scale_from_.y() == 2 * scale_to_.y()) {
1511         DCHECK_EQ(VERTICAL, primary_axis_);
1512         overscan_y = 3;
1513       } else {
1514         // Anything but a half-downscale in one dimension is meaningless.
1515         NOTREACHED();
1516       }
1517       break;
1518     }
1519 
1520     case Shader::PLANAR_CHANNEL_0:
1521     case Shader::PLANAR_CHANNEL_1:
1522     case Shader::PLANAR_CHANNEL_2:
1523     case Shader::PLANAR_CHANNEL_3:
1524     case Shader::I422_NV61_MRT:
1525       // All of these sample 4x1 source pixels to produce each output "pixel."
1526       // There is no overscan. They can also be combined with a bilinear
1527       // downscale, but not an upscale.
1528       DCHECK_GE(scale_from_.x(), 4 * scale_to_.x());
1529       DCHECK_EQ(HORIZONTAL, primary_axis_);
1530       break;
1531 
1532     case Shader::DEINTERLEAVE_PAIRWISE_MRT:
1533       // This shader samples 2x1 source pixels to produce each output "pixel."
1534       // There is no overscan. It can also be combined with a bilinear
1535       // downscale, but not an upscale.
1536       DCHECK_GE(scale_from_.x(), 2 * scale_to_.x());
1537       DCHECK_EQ(HORIZONTAL, primary_axis_);
1538       break;
1539   }
1540 
1541   source_rect.Inset(-overscan_x, -overscan_y);
1542   return gfx::ToEnclosingRect(source_rect);
1543 }
1544 
EnsureIntermediateTextureDefined(const gfx::Size & size)1545 void GLScaler::ScalerStage::EnsureIntermediateTextureDefined(
1546     const gfx::Size& size) {
1547   // Reallocate a new texture, if needed.
1548   if (!intermediate_texture_) {
1549     gl_->GenTextures(1, &intermediate_texture_);
1550   }
1551   if (intermediate_texture_size_ != size) {
1552     gl_->BindTexture(GL_TEXTURE_2D, intermediate_texture_);
1553     // Note: Not setting the filter or wrap parameters on the texture here
1554     // because that will be done in ScaleToMultipleOutputs() anyway.
1555     gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0,
1556                     GL_RGBA, program_->texture_type(), nullptr);
1557     intermediate_texture_size_ = size;
1558   }
1559 }
1560 
operator <<(std::ostream & out,const GLScaler & scaler)1561 std::ostream& operator<<(std::ostream& out, const GLScaler& scaler) {
1562   if (!scaler.chain_) {
1563     return (out << "[GLScaler NOT configured]");
1564   }
1565 
1566   out << "Output";
1567   const GLScaler::ScalerStage* const final_stage = scaler.chain_.get();
1568   for (auto* stage = final_stage; stage; stage = stage->input_stage()) {
1569     out << u8" ← {" << GLScaler::GetShaderName(stage->shader());
1570     if (stage->shader_program()) {
1571       switch (stage->shader_program()->texture_type()) {
1572         case GL_FLOAT:
1573           out << "/highp";
1574           break;
1575         case GL_HALF_FLOAT_OES:
1576           out << "/mediump";
1577           break;
1578         default:
1579           out << "/lowp";
1580           break;
1581       }
1582     }
1583     if (stage->flip_output()) {
1584       out << "+flip_y";
1585     }
1586     if (stage->scale_from() == stage->scale_to()) {
1587       out << " copy";
1588     } else {
1589       out << ' ' << stage->scale_from().ToString() << " to "
1590           << stage->scale_to().ToString();
1591     }
1592     if (!stage->input_stage() &&
1593         scaler.params_.source_color_space != scaler.scaling_color_space_) {
1594       out << ", with color x-form "
1595           << scaler.params_.source_color_space.ToString() << " to "
1596           << scaler.scaling_color_space_.ToString();
1597     }
1598     if (stage == final_stage) {
1599       if (scaler.params_.output_color_space != scaler.scaling_color_space_) {
1600         out << ", with color x-form to "
1601             << scaler.params_.output_color_space.ToString();
1602       }
1603       for (int i = 0; i < 2; ++i) {
1604         if (scaler.params_.swizzle[i] != GL_RGBA) {
1605           out << ", with swizzle(" << i << ')';
1606         }
1607       }
1608     }
1609     out << '}';
1610   }
1611   out << u8" ← Source";
1612   return out;
1613 }
1614 
1615 }  // namespace viz
1616