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