video_decode.cc revision 116680a4aac90f2aa7413d9095a592090648e557
1// Copyright (c) 2014 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 <GLES2/gl2.h>
6#include <GLES2/gl2ext.h>
7#include <stdio.h>
8#include <string.h>
9
10#include <iostream>
11#include <queue>
12#include <sstream>
13
14#include "ppapi/c/pp_errors.h"
15#include "ppapi/c/ppb_console.h"
16#include "ppapi/c/ppb_opengles2.h"
17#include "ppapi/cpp/graphics_3d.h"
18#include "ppapi/cpp/graphics_3d_client.h"
19#include "ppapi/cpp/input_event.h"
20#include "ppapi/cpp/instance.h"
21#include "ppapi/cpp/module.h"
22#include "ppapi/cpp/rect.h"
23#include "ppapi/cpp/var.h"
24#include "ppapi/cpp/video_decoder.h"
25#include "ppapi/utility/completion_callback_factory.h"
26
27// VP8 is more likely to work on different versions of Chrome. Undefine this
28// to decode H264.
29#define USE_VP8_TESTDATA_INSTEAD_OF_H264
30#include "testdata.h"
31
32// Use assert as a poor-man's CHECK, even in non-debug mode.
33// Since <assert.h> redefines assert on every inclusion (it doesn't use
34// include-guards), make sure this is the last file #include'd in this file.
35#undef NDEBUG
36#include <assert.h>
37
38// Assert |context_| isn't holding any GL Errors.  Done as a macro instead of a
39// function to preserve line number information in the failure message.
40#define assertNoGLError() assert(!gles2_if_->GetError(context_->pp_resource()));
41
42namespace {
43
44struct Shader {
45  Shader() : program(0), texcoord_scale_location(0) {}
46  ~Shader() {}
47
48  GLuint program;
49  GLint texcoord_scale_location;
50};
51
52class Decoder;
53class MyInstance;
54
55struct PendingPicture {
56  PendingPicture(Decoder* decoder, const PP_VideoPicture& picture)
57      : decoder(decoder), picture(picture) {}
58  ~PendingPicture() {}
59
60  Decoder* decoder;
61  PP_VideoPicture picture;
62};
63
64class MyInstance : public pp::Instance, public pp::Graphics3DClient {
65 public:
66  MyInstance(PP_Instance instance, pp::Module* module);
67  virtual ~MyInstance();
68
69  // pp::Instance implementation.
70  virtual void DidChangeView(const pp::Rect& position,
71                             const pp::Rect& clip_ignored);
72  virtual bool HandleInputEvent(const pp::InputEvent& event);
73
74  // pp::Graphics3DClient implementation.
75  virtual void Graphics3DContextLost() {
76    // TODO(vrk/fischman): Properly reset after a lost graphics context.  In
77    // particular need to delete context_ and re-create textures.
78    // Probably have to recreate the decoder from scratch, because old textures
79    // can still be outstanding in the decoder!
80    assert(false && "Unexpectedly lost graphics context");
81  }
82
83  void PaintPicture(Decoder* decoder, const PP_VideoPicture& picture);
84
85 private:
86  // Log an error to the developer console and stderr by creating a temporary
87  // object of this type and streaming to it.  Example usage:
88  // LogError(this).s() << "Hello world: " << 42;
89  class LogError {
90   public:
91    LogError(MyInstance* instance) : instance_(instance) {}
92    ~LogError() {
93      const std::string& msg = stream_.str();
94      instance_->console_if_->Log(
95          instance_->pp_instance(), PP_LOGLEVEL_ERROR, pp::Var(msg).pp_var());
96      std::cerr << msg << std::endl;
97    }
98    // Impl note: it would have been nicer to have LogError derive from
99    // std::ostringstream so that it can be streamed to directly, but lookup
100    // rules turn streamed string literals to hex pointers on output.
101    std::ostringstream& s() { return stream_; }
102
103   private:
104    MyInstance* instance_;
105    std::ostringstream stream_;
106  };
107
108  void InitializeDecoders();
109
110  // GL-related functions.
111  void InitGL();
112  void CreateGLObjects();
113  void Create2DProgramOnce();
114  void CreateRectangleARBProgramOnce();
115  void CreateExternalOESProgramOnce();
116  Shader CreateProgram(const char* vertex_shader, const char* fragment_shader);
117  void CreateShader(GLuint program, GLenum type, const char* source, int size);
118  void PaintNextPicture();
119  void PaintFinished(int32_t result);
120
121  pp::Size plugin_size_;
122  bool is_painting_;
123  // When decode outpaces render, we queue up decoded pictures for later
124  // painting.
125  typedef std::queue<PendingPicture> PendingPictureQueue;
126  PendingPictureQueue pending_pictures_;
127
128  int num_frames_rendered_;
129  PP_TimeTicks first_frame_delivered_ticks_;
130  PP_TimeTicks last_swap_request_ticks_;
131  PP_TimeTicks swap_ticks_;
132  pp::CompletionCallbackFactory<MyInstance> callback_factory_;
133
134  // Unowned pointers.
135  const PPB_Console* console_if_;
136  const PPB_Core* core_if_;
137  const PPB_OpenGLES2* gles2_if_;
138
139  // Owned data.
140  pp::Graphics3D* context_;
141  typedef std::vector<Decoder*> DecoderList;
142  DecoderList video_decoders_;
143
144  // Shader program to draw GL_TEXTURE_2D target.
145  Shader shader_2d_;
146  // Shader program to draw GL_TEXTURE_RECTANGLE_ARB target.
147  Shader shader_rectangle_arb_;
148  // Shader program to draw GL_TEXTURE_EXTERNAL_OES target.
149  Shader shader_external_oes_;
150};
151
152class Decoder {
153 public:
154  Decoder(MyInstance* instance, int id, const pp::Graphics3D& graphics_3d);
155  ~Decoder();
156
157  int id() const { return id_; }
158  bool flushing() const { return flushing_; }
159  bool resetting() const { return resetting_; }
160
161  void Reset();
162  void RecyclePicture(const PP_VideoPicture& picture);
163
164  PP_TimeTicks GetAverageLatency() {
165    return num_pictures_ ? total_latency_ / num_pictures_ : 0;
166  }
167
168 private:
169  void InitializeDone(int32_t result);
170  void Start();
171  void DecodeNextFrame();
172  void DecodeDone(int32_t result);
173  void PictureReady(int32_t result, PP_VideoPicture picture);
174  void FlushDone(int32_t result);
175  void ResetDone(int32_t result);
176
177  MyInstance* instance_;
178  int id_;
179
180  pp::VideoDecoder* decoder_;
181  pp::CompletionCallbackFactory<Decoder> callback_factory_;
182
183  size_t encoded_data_next_pos_to_decode_;
184  int next_picture_id_;
185  bool flushing_;
186  bool resetting_;
187
188  const PPB_Core* core_if_;
189  static const int kMaxDecodeDelay = 128;
190  PP_TimeTicks decode_time_[kMaxDecodeDelay];
191  PP_TimeTicks total_latency_;
192  int num_pictures_;
193};
194
195#if defined USE_VP8_TESTDATA_INSTEAD_OF_H264
196
197// VP8 is stored in an IVF container.
198// Helpful description: http://wiki.multimedia.cx/index.php?title=IVF
199
200static void GetNextFrame(size_t* start_pos, size_t* end_pos) {
201  size_t current_pos = *start_pos;
202  if (current_pos == 0)
203    current_pos = 32;  // Skip stream header.
204  uint32_t frame_size = kData[current_pos] + (kData[current_pos + 1] << 8) +
205                        (kData[current_pos + 2] << 16) +
206                        (kData[current_pos + 3] << 24);
207  current_pos += 12;  // Skip frame header.
208  *start_pos = current_pos;
209  *end_pos = current_pos + frame_size;
210}
211
212#else  // !USE_VP8_TESTDATA_INSTEAD_OF_H264
213
214// Returns true if the current position is at the start of a NAL unit.
215static bool LookingAtNAL(const unsigned char* encoded, size_t pos) {
216  // H264 frames start with 0, 0, 0, 1 in our test data.
217  return pos + 3 < kDataLen && encoded[pos] == 0 && encoded[pos + 1] == 0 &&
218         encoded[pos + 2] == 0 && encoded[pos + 3] == 1;
219}
220
221static void GetNextFrame(size_t* start_pos, size_t* end_pos) {
222  assert(LookingAtNAL(kData, *start_pos));
223  *end_pos = *start_pos;
224  *end_pos += 4;
225  while (*end_pos < kDataLen && !LookingAtNAL(kData, *end_pos)) {
226    ++*end_pos;
227  }
228}
229
230#endif  // USE_VP8_TESTDATA_INSTEAD_OF_H264
231
232Decoder::Decoder(MyInstance* instance,
233                 int id,
234                 const pp::Graphics3D& graphics_3d)
235    : instance_(instance),
236      id_(id),
237      decoder_(new pp::VideoDecoder(instance)),
238      callback_factory_(this),
239      encoded_data_next_pos_to_decode_(0),
240      next_picture_id_(0),
241      flushing_(false),
242      resetting_(false),
243      total_latency_(0.0),
244      num_pictures_(0) {
245  core_if_ = static_cast<const PPB_Core*>(
246      pp::Module::Get()->GetBrowserInterface(PPB_CORE_INTERFACE));
247
248#if defined USE_VP8_TESTDATA_INSTEAD_OF_H264
249  const PP_VideoProfile kBitstreamProfile = PP_VIDEOPROFILE_VP8MAIN;
250#else
251  const PP_VideoProfile kBitstreamProfile = PP_VIDEOPROFILE_H264MAIN;
252#endif
253
254  assert(!decoder_->is_null());
255  decoder_->Initialize(graphics_3d,
256                       kBitstreamProfile,
257                       PP_TRUE /* allow_software_fallback */,
258                       callback_factory_.NewCallback(&Decoder::InitializeDone));
259}
260
261Decoder::~Decoder() {
262  delete decoder_;
263}
264
265void Decoder::InitializeDone(int32_t result) {
266  assert(decoder_);
267  assert(result == PP_OK);
268  Start();
269}
270
271void Decoder::Start() {
272  assert(decoder_);
273
274  encoded_data_next_pos_to_decode_ = 0;
275
276  // Register callback to get the first picture. We call GetPicture again in
277  // PictureReady to continuously receive pictures as they're decoded.
278  decoder_->GetPicture(
279      callback_factory_.NewCallbackWithOutput(&Decoder::PictureReady));
280
281  // Start the decode loop.
282  DecodeNextFrame();
283}
284
285void Decoder::Reset() {
286  assert(decoder_);
287  assert(!resetting_);
288  resetting_ = true;
289  decoder_->Reset(callback_factory_.NewCallback(&Decoder::ResetDone));
290}
291
292void Decoder::RecyclePicture(const PP_VideoPicture& picture) {
293  assert(decoder_);
294  decoder_->RecyclePicture(picture);
295}
296
297void Decoder::DecodeNextFrame() {
298  assert(decoder_);
299  if (encoded_data_next_pos_to_decode_ <= kDataLen) {
300    // If we've just reached the end of the bitstream, flush and wait.
301    if (!flushing_ && encoded_data_next_pos_to_decode_ == kDataLen) {
302      flushing_ = true;
303      decoder_->Flush(callback_factory_.NewCallback(&Decoder::FlushDone));
304      return;
305    }
306
307    // Find the start of the next frame.
308    size_t start_pos = encoded_data_next_pos_to_decode_;
309    size_t end_pos;
310    GetNextFrame(&start_pos, &end_pos);
311    encoded_data_next_pos_to_decode_ = end_pos;
312    // Decode the frame. On completion, DecodeDone will call DecodeNextFrame
313    // to implement a decode loop.
314    uint32_t size = static_cast<uint32_t>(end_pos - start_pos);
315    decode_time_[next_picture_id_ % kMaxDecodeDelay] = core_if_->GetTimeTicks();
316    decoder_->Decode(next_picture_id_++,
317                     size,
318                     kData + start_pos,
319                     callback_factory_.NewCallback(&Decoder::DecodeDone));
320  }
321}
322
323void Decoder::DecodeDone(int32_t result) {
324  assert(decoder_);
325  // Break out of the decode loop on abort.
326  if (result == PP_ERROR_ABORTED)
327    return;
328  assert(result == PP_OK);
329  if (!flushing_ && !resetting_)
330    DecodeNextFrame();
331}
332
333void Decoder::PictureReady(int32_t result, PP_VideoPicture picture) {
334  assert(decoder_);
335  // Break out of the get picture loop on abort.
336  if (result == PP_ERROR_ABORTED)
337    return;
338  assert(result == PP_OK);
339
340  num_pictures_++;
341  PP_TimeTicks latency = core_if_->GetTimeTicks() -
342                         decode_time_[picture.decode_id % kMaxDecodeDelay];
343  total_latency_ += latency;
344
345  decoder_->GetPicture(
346      callback_factory_.NewCallbackWithOutput(&Decoder::PictureReady));
347  instance_->PaintPicture(this, picture);
348}
349
350void Decoder::FlushDone(int32_t result) {
351  assert(decoder_);
352  assert(result == PP_OK || result == PP_ERROR_ABORTED);
353  assert(flushing_);
354  flushing_ = false;
355}
356
357void Decoder::ResetDone(int32_t result) {
358  assert(decoder_);
359  assert(result == PP_OK);
360  assert(resetting_);
361  resetting_ = false;
362
363  Start();
364}
365
366MyInstance::MyInstance(PP_Instance instance, pp::Module* module)
367    : pp::Instance(instance),
368      pp::Graphics3DClient(this),
369      is_painting_(false),
370      num_frames_rendered_(0),
371      first_frame_delivered_ticks_(-1),
372      last_swap_request_ticks_(-1),
373      swap_ticks_(0),
374      callback_factory_(this),
375      context_(NULL) {
376  console_if_ = static_cast<const PPB_Console*>(
377      pp::Module::Get()->GetBrowserInterface(PPB_CONSOLE_INTERFACE));
378  core_if_ = static_cast<const PPB_Core*>(
379      pp::Module::Get()->GetBrowserInterface(PPB_CORE_INTERFACE));
380  gles2_if_ = static_cast<const PPB_OpenGLES2*>(
381      pp::Module::Get()->GetBrowserInterface(PPB_OPENGLES2_INTERFACE));
382
383  RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE);
384}
385
386MyInstance::~MyInstance() {
387  if (!context_)
388    return;
389
390  PP_Resource graphics_3d = context_->pp_resource();
391  if (shader_2d_.program)
392    gles2_if_->DeleteProgram(graphics_3d, shader_2d_.program);
393  if (shader_rectangle_arb_.program)
394    gles2_if_->DeleteProgram(graphics_3d, shader_rectangle_arb_.program);
395  if (shader_external_oes_.program)
396    gles2_if_->DeleteProgram(graphics_3d, shader_external_oes_.program);
397
398  for (DecoderList::iterator it = video_decoders_.begin();
399       it != video_decoders_.end();
400       ++it)
401    delete *it;
402
403  delete context_;
404}
405
406void MyInstance::DidChangeView(const pp::Rect& position,
407                               const pp::Rect& clip_ignored) {
408  if (position.width() == 0 || position.height() == 0)
409    return;
410  if (plugin_size_.width()) {
411    assert(position.size() == plugin_size_);
412    return;
413  }
414  plugin_size_ = position.size();
415
416  // Initialize graphics.
417  InitGL();
418  InitializeDecoders();
419}
420
421bool MyInstance::HandleInputEvent(const pp::InputEvent& event) {
422  switch (event.GetType()) {
423    case PP_INPUTEVENT_TYPE_MOUSEDOWN: {
424      pp::MouseInputEvent mouse_event(event);
425      // Reset all decoders on mouse down.
426      if (mouse_event.GetButton() == PP_INPUTEVENT_MOUSEBUTTON_LEFT) {
427        // Reset decoders.
428        for (size_t i = 0; i < video_decoders_.size(); i++) {
429          if (!video_decoders_[i]->resetting())
430            video_decoders_[i]->Reset();
431        }
432      }
433      return true;
434    }
435
436    default:
437      return false;
438  }
439}
440
441void MyInstance::InitializeDecoders() {
442  assert(video_decoders_.empty());
443  // Create two decoders with ids 0 and 1.
444  video_decoders_.push_back(new Decoder(this, 0, *context_));
445  video_decoders_.push_back(new Decoder(this, 1, *context_));
446}
447
448void MyInstance::PaintPicture(Decoder* decoder,
449                              const PP_VideoPicture& picture) {
450  if (first_frame_delivered_ticks_ == -1)
451    assert((first_frame_delivered_ticks_ = core_if_->GetTimeTicks()) != -1);
452
453  pending_pictures_.push(PendingPicture(decoder, picture));
454  if (!is_painting_)
455    PaintNextPicture();
456}
457
458void MyInstance::PaintNextPicture() {
459  assert(!is_painting_);
460  is_painting_ = true;
461
462  const PendingPicture& next = pending_pictures_.front();
463  Decoder* decoder = next.decoder;
464  const PP_VideoPicture& picture = next.picture;
465
466  int x = 0;
467  int y = 0;
468  int half_width = plugin_size_.width() / 2;
469  int half_height = plugin_size_.height() / 2;
470  if (decoder->id() != 0) {
471    x = half_width;
472    y = half_height;
473  }
474
475  PP_Resource graphics_3d = context_->pp_resource();
476  if (picture.texture_target == GL_TEXTURE_2D) {
477    Create2DProgramOnce();
478    gles2_if_->UseProgram(graphics_3d, shader_2d_.program);
479    gles2_if_->Uniform2f(
480        graphics_3d, shader_2d_.texcoord_scale_location, 1.0, 1.0);
481  } else if (picture.texture_target == GL_TEXTURE_RECTANGLE_ARB) {
482    CreateRectangleARBProgramOnce();
483    gles2_if_->UseProgram(graphics_3d, shader_rectangle_arb_.program);
484    gles2_if_->Uniform2f(graphics_3d,
485                         shader_rectangle_arb_.texcoord_scale_location,
486                         picture.texture_size.width,
487                         picture.texture_size.height);
488  } else {
489    assert(picture.texture_target == GL_TEXTURE_EXTERNAL_OES);
490    CreateExternalOESProgramOnce();
491    gles2_if_->UseProgram(graphics_3d, shader_external_oes_.program);
492    gles2_if_->Uniform2f(
493        graphics_3d, shader_external_oes_.texcoord_scale_location, 1.0, 1.0);
494  }
495
496  gles2_if_->Viewport(graphics_3d, x, y, half_width, half_height);
497  gles2_if_->ActiveTexture(graphics_3d, GL_TEXTURE0);
498  gles2_if_->BindTexture(
499      graphics_3d, picture.texture_target, picture.texture_id);
500  gles2_if_->DrawArrays(graphics_3d, GL_TRIANGLE_STRIP, 0, 4);
501
502  gles2_if_->UseProgram(graphics_3d, 0);
503
504  last_swap_request_ticks_ = core_if_->GetTimeTicks();
505  context_->SwapBuffers(
506      callback_factory_.NewCallback(&MyInstance::PaintFinished));
507}
508
509void MyInstance::PaintFinished(int32_t result) {
510  assert(result == PP_OK);
511  swap_ticks_ += core_if_->GetTimeTicks() - last_swap_request_ticks_;
512  is_painting_ = false;
513  ++num_frames_rendered_;
514  if (num_frames_rendered_ % 50 == 0) {
515    double elapsed = core_if_->GetTimeTicks() - first_frame_delivered_ticks_;
516    double fps = (elapsed > 0) ? num_frames_rendered_ / elapsed : 1000;
517    double ms_per_swap = (swap_ticks_ * 1e3) / num_frames_rendered_;
518    double secs_average_latency = 0;
519    for (DecoderList::iterator it = video_decoders_.begin();
520         it != video_decoders_.end();
521         ++it)
522      secs_average_latency += (*it)->GetAverageLatency();
523    secs_average_latency /= video_decoders_.size();
524    double ms_average_latency = 1000 * secs_average_latency;
525    LogError(this).s() << "Rendered frames: " << num_frames_rendered_
526                       << ", fps: " << fps
527                       << ", with average ms/swap of: " << ms_per_swap
528                       << ", with average latency (ms) of: "
529                       << ms_average_latency;
530  }
531
532  // If the decoders were reset, this will be empty.
533  if (pending_pictures_.empty())
534    return;
535
536  const PendingPicture& next = pending_pictures_.front();
537  Decoder* decoder = next.decoder;
538  const PP_VideoPicture& picture = next.picture;
539  decoder->RecyclePicture(picture);
540  pending_pictures_.pop();
541
542  // Keep painting as long as we have pictures.
543  if (!pending_pictures_.empty())
544    PaintNextPicture();
545}
546
547void MyInstance::InitGL() {
548  assert(plugin_size_.width() && plugin_size_.height());
549  is_painting_ = false;
550
551  assert(!context_);
552  int32_t context_attributes[] = {
553      PP_GRAPHICS3DATTRIB_ALPHA_SIZE,     8,
554      PP_GRAPHICS3DATTRIB_BLUE_SIZE,      8,
555      PP_GRAPHICS3DATTRIB_GREEN_SIZE,     8,
556      PP_GRAPHICS3DATTRIB_RED_SIZE,       8,
557      PP_GRAPHICS3DATTRIB_DEPTH_SIZE,     0,
558      PP_GRAPHICS3DATTRIB_STENCIL_SIZE,   0,
559      PP_GRAPHICS3DATTRIB_SAMPLES,        0,
560      PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0,
561      PP_GRAPHICS3DATTRIB_WIDTH,          plugin_size_.width(),
562      PP_GRAPHICS3DATTRIB_HEIGHT,         plugin_size_.height(),
563      PP_GRAPHICS3DATTRIB_NONE,
564  };
565  context_ = new pp::Graphics3D(this, context_attributes);
566  assert(!context_->is_null());
567  assert(BindGraphics(*context_));
568
569  // Clear color bit.
570  gles2_if_->ClearColor(context_->pp_resource(), 1, 0, 0, 1);
571  gles2_if_->Clear(context_->pp_resource(), GL_COLOR_BUFFER_BIT);
572
573  assertNoGLError();
574
575  CreateGLObjects();
576}
577
578void MyInstance::CreateGLObjects() {
579  // Assign vertex positions and texture coordinates to buffers for use in
580  // shader program.
581  static const float kVertices[] = {
582      -1, -1, -1, 1, 1, -1, 1, 1,  // Position coordinates.
583      0,  1,  0,  0, 1, 1,  1, 0,  // Texture coordinates.
584  };
585
586  GLuint buffer;
587  gles2_if_->GenBuffers(context_->pp_resource(), 1, &buffer);
588  gles2_if_->BindBuffer(context_->pp_resource(), GL_ARRAY_BUFFER, buffer);
589
590  gles2_if_->BufferData(context_->pp_resource(),
591                        GL_ARRAY_BUFFER,
592                        sizeof(kVertices),
593                        kVertices,
594                        GL_STATIC_DRAW);
595  assertNoGLError();
596}
597
598static const char kVertexShader[] =
599    "varying vec2 v_texCoord;            \n"
600    "attribute vec4 a_position;          \n"
601    "attribute vec2 a_texCoord;          \n"
602    "uniform vec2 v_scale;               \n"
603    "void main()                         \n"
604    "{                                   \n"
605    "    v_texCoord = v_scale * a_texCoord; \n"
606    "    gl_Position = a_position;       \n"
607    "}";
608
609void MyInstance::Create2DProgramOnce() {
610  if (shader_2d_.program)
611    return;
612  static const char kFragmentShader2D[] =
613      "precision mediump float;            \n"
614      "varying vec2 v_texCoord;            \n"
615      "uniform sampler2D s_texture;        \n"
616      "void main()                         \n"
617      "{"
618      "    gl_FragColor = texture2D(s_texture, v_texCoord); \n"
619      "}";
620  shader_2d_ = CreateProgram(kVertexShader, kFragmentShader2D);
621  assertNoGLError();
622}
623
624void MyInstance::CreateRectangleARBProgramOnce() {
625  if (shader_rectangle_arb_.program)
626    return;
627  static const char kFragmentShaderRectangle[] =
628      "#extension GL_ARB_texture_rectangle : require\n"
629      "precision mediump float;            \n"
630      "varying vec2 v_texCoord;            \n"
631      "uniform sampler2DRect s_texture;    \n"
632      "void main()                         \n"
633      "{"
634      "    gl_FragColor = texture2DRect(s_texture, v_texCoord).rgba; \n"
635      "}";
636  shader_rectangle_arb_ =
637      CreateProgram(kVertexShader, kFragmentShaderRectangle);
638  assertNoGLError();
639}
640
641void MyInstance::CreateExternalOESProgramOnce() {
642  if (shader_external_oes_.program)
643    return;
644  static const char kFragmentShaderExternal[] =
645      "#extension GL_OES_EGL_image_external : require\n"
646      "precision mediump float;            \n"
647      "varying vec2 v_texCoord;            \n"
648      "uniform samplerExternalOES s_texture; \n"
649      "void main()                         \n"
650      "{"
651      "    gl_FragColor = texture2D(s_texture, v_texCoord); \n"
652      "}";
653  shader_external_oes_ = CreateProgram(kVertexShader, kFragmentShaderExternal);
654  assertNoGLError();
655}
656
657Shader MyInstance::CreateProgram(const char* vertex_shader,
658                                 const char* fragment_shader) {
659  Shader shader;
660
661  // Create shader program.
662  shader.program = gles2_if_->CreateProgram(context_->pp_resource());
663  CreateShader(
664      shader.program, GL_VERTEX_SHADER, vertex_shader, strlen(vertex_shader));
665  CreateShader(shader.program,
666               GL_FRAGMENT_SHADER,
667               fragment_shader,
668               strlen(fragment_shader));
669  gles2_if_->LinkProgram(context_->pp_resource(), shader.program);
670  gles2_if_->UseProgram(context_->pp_resource(), shader.program);
671  gles2_if_->Uniform1i(
672      context_->pp_resource(),
673      gles2_if_->GetUniformLocation(
674          context_->pp_resource(), shader.program, "s_texture"),
675      0);
676  assertNoGLError();
677
678  shader.texcoord_scale_location = gles2_if_->GetUniformLocation(
679      context_->pp_resource(), shader.program, "v_scale");
680
681  GLint pos_location = gles2_if_->GetAttribLocation(
682      context_->pp_resource(), shader.program, "a_position");
683  GLint tc_location = gles2_if_->GetAttribLocation(
684      context_->pp_resource(), shader.program, "a_texCoord");
685  assertNoGLError();
686
687  gles2_if_->EnableVertexAttribArray(context_->pp_resource(), pos_location);
688  gles2_if_->VertexAttribPointer(
689      context_->pp_resource(), pos_location, 2, GL_FLOAT, GL_FALSE, 0, 0);
690  gles2_if_->EnableVertexAttribArray(context_->pp_resource(), tc_location);
691  gles2_if_->VertexAttribPointer(
692      context_->pp_resource(),
693      tc_location,
694      2,
695      GL_FLOAT,
696      GL_FALSE,
697      0,
698      static_cast<float*>(0) + 8);  // Skip position coordinates.
699
700  gles2_if_->UseProgram(context_->pp_resource(), 0);
701  assertNoGLError();
702  return shader;
703}
704
705void MyInstance::CreateShader(GLuint program,
706                              GLenum type,
707                              const char* source,
708                              int size) {
709  GLuint shader = gles2_if_->CreateShader(context_->pp_resource(), type);
710  gles2_if_->ShaderSource(context_->pp_resource(), shader, 1, &source, &size);
711  gles2_if_->CompileShader(context_->pp_resource(), shader);
712  gles2_if_->AttachShader(context_->pp_resource(), program, shader);
713  gles2_if_->DeleteShader(context_->pp_resource(), shader);
714}
715
716// This object is the global object representing this plugin library as long
717// as it is loaded.
718class MyModule : public pp::Module {
719 public:
720  MyModule() : pp::Module() {}
721  virtual ~MyModule() {}
722
723  virtual pp::Instance* CreateInstance(PP_Instance instance) {
724    return new MyInstance(instance, this);
725  }
726};
727
728}  // anonymous namespace
729
730namespace pp {
731// Factory function for your specialization of the Module object.
732Module* CreateModule() {
733  return new MyModule();
734}
735}  // namespace pp
736