Home | History | Annotate | Download | only in video_decode
      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       return true;
    430     }
    431 
    432     default:
    433       return false;
    434   }
    435 }
    436 
    437 void MyInstance::InitializeDecoders() {
    438   assert(video_decoders_.empty());
    439   // Create two decoders with ids 0 and 1.
    440   video_decoders_.push_back(new Decoder(this, 0, *context_));
    441   video_decoders_.push_back(new Decoder(this, 1, *context_));
    442 }
    443 
    444 void MyInstance::PaintPicture(Decoder* decoder,
    445                               const PP_VideoPicture& picture) {
    446   if (first_frame_delivered_ticks_ == -1)
    447     assert((first_frame_delivered_ticks_ = core_if_->GetTimeTicks()) != -1);
    448 
    449   pending_pictures_.push(PendingPicture(decoder, picture));
    450   if (!is_painting_)
    451     PaintNextPicture();
    452 }
    453 
    454 void MyInstance::PaintNextPicture() {
    455   assert(!is_painting_);
    456   is_painting_ = true;
    457 
    458   const PendingPicture& next = pending_pictures_.front();
    459   Decoder* decoder = next.decoder;
    460   const PP_VideoPicture& picture = next.picture;
    461 
    462   int x = 0;
    463   int y = 0;
    464   int half_width = plugin_size_.width() / 2;
    465   int half_height = plugin_size_.height() / 2;
    466   if (decoder->id() != 0) {
    467     x = half_width;
    468     y = half_height;
    469   }
    470 
    471   PP_Resource graphics_3d = context_->pp_resource();
    472   if (picture.texture_target == GL_TEXTURE_2D) {
    473     Create2DProgramOnce();
    474     gles2_if_->UseProgram(graphics_3d, shader_2d_.program);
    475     gles2_if_->Uniform2f(
    476         graphics_3d, shader_2d_.texcoord_scale_location, 1.0, 1.0);
    477   } else {
    478     assert(picture.texture_target == GL_TEXTURE_RECTANGLE_ARB);
    479     CreateRectangleARBProgramOnce();
    480     gles2_if_->UseProgram(graphics_3d, shader_rectangle_arb_.program);
    481     gles2_if_->Uniform2f(graphics_3d,
    482                          shader_rectangle_arb_.texcoord_scale_location,
    483                          picture.texture_size.width,
    484                          picture.texture_size.height);
    485   }
    486 
    487   gles2_if_->Viewport(graphics_3d, x, y, half_width, half_height);
    488   gles2_if_->ActiveTexture(graphics_3d, GL_TEXTURE0);
    489   gles2_if_->BindTexture(
    490       graphics_3d, picture.texture_target, picture.texture_id);
    491   gles2_if_->DrawArrays(graphics_3d, GL_TRIANGLE_STRIP, 0, 4);
    492 
    493   gles2_if_->UseProgram(graphics_3d, 0);
    494 
    495   last_swap_request_ticks_ = core_if_->GetTimeTicks();
    496   context_->SwapBuffers(
    497       callback_factory_.NewCallback(&MyInstance::PaintFinished));
    498 }
    499 
    500 void MyInstance::PaintFinished(int32_t result) {
    501   assert(result == PP_OK);
    502   swap_ticks_ += core_if_->GetTimeTicks() - last_swap_request_ticks_;
    503   is_painting_ = false;
    504   ++num_frames_rendered_;
    505   if (num_frames_rendered_ % 50 == 0) {
    506     double elapsed = core_if_->GetTimeTicks() - first_frame_delivered_ticks_;
    507     double fps = (elapsed > 0) ? num_frames_rendered_ / elapsed : 1000;
    508     double ms_per_swap = (swap_ticks_ * 1e3) / num_frames_rendered_;
    509     double secs_average_latency = 0;
    510     for (DecoderList::iterator it = video_decoders_.begin();
    511          it != video_decoders_.end();
    512          ++it)
    513       secs_average_latency += (*it)->GetAverageLatency();
    514     secs_average_latency /= video_decoders_.size();
    515     double ms_average_latency = 1000 * secs_average_latency;
    516     LogError(this).s() << "Rendered frames: " << num_frames_rendered_
    517                        << ", fps: " << fps
    518                        << ", with average ms/swap of: " << ms_per_swap
    519                        << ", with average latency (ms) of: "
    520                        << ms_average_latency;
    521   }
    522 
    523   // If the decoders were reset, this will be empty.
    524   if (pending_pictures_.empty())
    525     return;
    526 
    527   const PendingPicture& next = pending_pictures_.front();
    528   Decoder* decoder = next.decoder;
    529   const PP_VideoPicture& picture = next.picture;
    530   decoder->RecyclePicture(picture);
    531   pending_pictures_.pop();
    532 
    533   // Keep painting as long as we have pictures.
    534   if (!pending_pictures_.empty())
    535     PaintNextPicture();
    536 }
    537 
    538 void MyInstance::InitGL() {
    539   assert(plugin_size_.width() && plugin_size_.height());
    540   is_painting_ = false;
    541 
    542   assert(!context_);
    543   int32_t context_attributes[] = {
    544       PP_GRAPHICS3DATTRIB_ALPHA_SIZE,     8,
    545       PP_GRAPHICS3DATTRIB_BLUE_SIZE,      8,
    546       PP_GRAPHICS3DATTRIB_GREEN_SIZE,     8,
    547       PP_GRAPHICS3DATTRIB_RED_SIZE,       8,
    548       PP_GRAPHICS3DATTRIB_DEPTH_SIZE,     0,
    549       PP_GRAPHICS3DATTRIB_STENCIL_SIZE,   0,
    550       PP_GRAPHICS3DATTRIB_SAMPLES,        0,
    551       PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0,
    552       PP_GRAPHICS3DATTRIB_WIDTH,          plugin_size_.width(),
    553       PP_GRAPHICS3DATTRIB_HEIGHT,         plugin_size_.height(),
    554       PP_GRAPHICS3DATTRIB_NONE,
    555   };
    556   context_ = new pp::Graphics3D(this, context_attributes);
    557   assert(!context_->is_null());
    558   assert(BindGraphics(*context_));
    559 
    560   // Clear color bit.
    561   gles2_if_->ClearColor(context_->pp_resource(), 1, 0, 0, 1);
    562   gles2_if_->Clear(context_->pp_resource(), GL_COLOR_BUFFER_BIT);
    563 
    564   assertNoGLError();
    565 
    566   CreateGLObjects();
    567 }
    568 
    569 void MyInstance::CreateGLObjects() {
    570   // Assign vertex positions and texture coordinates to buffers for use in
    571   // shader program.
    572   static const float kVertices[] = {
    573       -1, -1, -1, 1, 1, -1, 1, 1,  // Position coordinates.
    574       0,  1,  0,  0, 1, 1,  1, 0,  // Texture coordinates.
    575   };
    576 
    577   GLuint buffer;
    578   gles2_if_->GenBuffers(context_->pp_resource(), 1, &buffer);
    579   gles2_if_->BindBuffer(context_->pp_resource(), GL_ARRAY_BUFFER, buffer);
    580 
    581   gles2_if_->BufferData(context_->pp_resource(),
    582                         GL_ARRAY_BUFFER,
    583                         sizeof(kVertices),
    584                         kVertices,
    585                         GL_STATIC_DRAW);
    586   assertNoGLError();
    587 }
    588 
    589 static const char kVertexShader[] =
    590     "varying vec2 v_texCoord;            \n"
    591     "attribute vec4 a_position;          \n"
    592     "attribute vec2 a_texCoord;          \n"
    593     "uniform vec2 v_scale;               \n"
    594     "void main()                         \n"
    595     "{                                   \n"
    596     "    v_texCoord = v_scale * a_texCoord; \n"
    597     "    gl_Position = a_position;       \n"
    598     "}";
    599 
    600 void MyInstance::Create2DProgramOnce() {
    601   if (shader_2d_.program)
    602     return;
    603   static const char kFragmentShader2D[] =
    604       "precision mediump float;            \n"
    605       "varying vec2 v_texCoord;            \n"
    606       "uniform sampler2D s_texture;        \n"
    607       "void main()                         \n"
    608       "{"
    609       "    gl_FragColor = texture2D(s_texture, v_texCoord); \n"
    610       "}";
    611   shader_2d_ = CreateProgram(kVertexShader, kFragmentShader2D);
    612   assertNoGLError();
    613 }
    614 
    615 void MyInstance::CreateRectangleARBProgramOnce() {
    616   if (shader_rectangle_arb_.program)
    617     return;
    618   static const char kFragmentShaderRectangle[] =
    619       "#extension GL_ARB_texture_rectangle : require\n"
    620       "precision mediump float;            \n"
    621       "varying vec2 v_texCoord;            \n"
    622       "uniform sampler2DRect s_texture;    \n"
    623       "void main()                         \n"
    624       "{"
    625       "    gl_FragColor = texture2DRect(s_texture, v_texCoord).rgba; \n"
    626       "}";
    627   shader_rectangle_arb_ =
    628       CreateProgram(kVertexShader, kFragmentShaderRectangle);
    629 }
    630 
    631 Shader MyInstance::CreateProgram(const char* vertex_shader,
    632                                  const char* fragment_shader) {
    633   Shader shader;
    634 
    635   // Create shader program.
    636   shader.program = gles2_if_->CreateProgram(context_->pp_resource());
    637   CreateShader(
    638       shader.program, GL_VERTEX_SHADER, vertex_shader, strlen(vertex_shader));
    639   CreateShader(shader.program,
    640                GL_FRAGMENT_SHADER,
    641                fragment_shader,
    642                strlen(fragment_shader));
    643   gles2_if_->LinkProgram(context_->pp_resource(), shader.program);
    644   gles2_if_->UseProgram(context_->pp_resource(), shader.program);
    645   gles2_if_->Uniform1i(
    646       context_->pp_resource(),
    647       gles2_if_->GetUniformLocation(
    648           context_->pp_resource(), shader.program, "s_texture"),
    649       0);
    650   assertNoGLError();
    651 
    652   shader.texcoord_scale_location = gles2_if_->GetUniformLocation(
    653       context_->pp_resource(), shader.program, "v_scale");
    654 
    655   GLint pos_location = gles2_if_->GetAttribLocation(
    656       context_->pp_resource(), shader.program, "a_position");
    657   GLint tc_location = gles2_if_->GetAttribLocation(
    658       context_->pp_resource(), shader.program, "a_texCoord");
    659   assertNoGLError();
    660 
    661   gles2_if_->EnableVertexAttribArray(context_->pp_resource(), pos_location);
    662   gles2_if_->VertexAttribPointer(
    663       context_->pp_resource(), pos_location, 2, GL_FLOAT, GL_FALSE, 0, 0);
    664   gles2_if_->EnableVertexAttribArray(context_->pp_resource(), tc_location);
    665   gles2_if_->VertexAttribPointer(
    666       context_->pp_resource(),
    667       tc_location,
    668       2,
    669       GL_FLOAT,
    670       GL_FALSE,
    671       0,
    672       static_cast<float*>(0) + 8);  // Skip position coordinates.
    673 
    674   gles2_if_->UseProgram(context_->pp_resource(), 0);
    675   assertNoGLError();
    676   return shader;
    677 }
    678 
    679 void MyInstance::CreateShader(GLuint program,
    680                               GLenum type,
    681                               const char* source,
    682                               int size) {
    683   GLuint shader = gles2_if_->CreateShader(context_->pp_resource(), type);
    684   gles2_if_->ShaderSource(context_->pp_resource(), shader, 1, &source, &size);
    685   gles2_if_->CompileShader(context_->pp_resource(), shader);
    686   gles2_if_->AttachShader(context_->pp_resource(), program, shader);
    687   gles2_if_->DeleteShader(context_->pp_resource(), shader);
    688 }
    689 
    690 // This object is the global object representing this plugin library as long
    691 // as it is loaded.
    692 class MyModule : public pp::Module {
    693  public:
    694   MyModule() : pp::Module() {}
    695   virtual ~MyModule() {}
    696 
    697   virtual pp::Instance* CreateInstance(PP_Instance instance) {
    698     return new MyInstance(instance, this);
    699   }
    700 };
    701 
    702 }  // anonymous namespace
    703 
    704 namespace pp {
    705 // Factory function for your specialization of the Module object.
    706 Module* CreateModule() {
    707   return new MyModule();
    708 }
    709 }  // namespace pp
    710