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