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