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.
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"
24 // VP8 is more likely to work on different versions of Chrome. Undefine this
26 #define USE_VP8_TESTDATA_INSTEAD_OF_H264
27 #include "ppapi/examples/video_decode/testdata.h"
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"
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.
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()));
46 Shader() : program(0), texcoord_scale_location(0) {}
50 GLint texcoord_scale_location
;
56 struct PendingPicture
{
57 PendingPicture(Decoder
* decoder
, const PP_VideoPicture
& picture
)
58 : decoder(decoder
), picture(picture
) {}
62 PP_VideoPicture picture
;
65 class MyInstance
: public pp::Instance
, public pp::Graphics3DClient
{
67 MyInstance(PP_Instance instance
, pp::Module
* module
);
68 virtual ~MyInstance();
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
);
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");
84 void PaintPicture(Decoder
* decoder
, const PP_VideoPicture
& picture
);
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;
92 LogError(MyInstance
* instance
) : instance_(instance
) {}
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
;
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_
; }
105 MyInstance
* instance_
;
106 std::ostringstream stream_
;
109 void InitializeDecoders();
111 // GL-related functions.
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
);
122 pp::Size plugin_size_
;
124 // When decode outpaces render, we queue up decoded pictures for later
126 typedef std::queue
<PendingPicture
> PendingPictureQueue
;
127 PendingPictureQueue pending_pictures_
;
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_
;
136 const PPB_Console
* console_if_
;
137 const PPB_Core
* core_if_
;
138 const PPB_OpenGLES2
* gles2_if_
;
141 pp::Graphics3D
* context_
;
142 typedef std::vector
<Decoder
*> DecoderList
;
143 DecoderList video_decoders_
;
145 // Shader program to draw GL_TEXTURE_2D target.
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_
;
155 Decoder(MyInstance
* instance
, int id
, const pp::Graphics3D
& graphics_3d
);
158 int id() const { return id_
; }
159 bool flushing() const { return flushing_
; }
160 bool resetting() const { return resetting_
; }
163 void RecyclePicture(const PP_VideoPicture
& picture
);
165 PP_TimeTicks
GetAverageLatency() {
166 return num_pictures_
? total_latency_
/ num_pictures_
: 0;
170 void InitializeDone(int32_t result
);
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
);
178 MyInstance
* instance_
;
181 pp::VideoDecoder
* decoder_
;
182 pp::CompletionCallbackFactory
<Decoder
> callback_factory_
;
184 size_t encoded_data_next_pos_to_decode_
;
185 int next_picture_id_
;
189 const PPB_Core
* core_if_
;
190 static const int kMaxDecodeDelay
= 128;
191 PP_TimeTicks decode_time_
[kMaxDecodeDelay
];
192 PP_TimeTicks total_latency_
;
196 #if defined USE_VP8_TESTDATA_INSTEAD_OF_H264
198 // VP8 is stored in an IVF container.
199 // Helpful description: http://wiki.multimedia.cx/index.php?title=IVF
201 static 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
;
213 #else // !USE_VP8_TESTDATA_INSTEAD_OF_H264
215 // Returns true if the current position is at the start of a NAL unit.
216 static 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;
222 static void GetNextFrame(size_t* start_pos
, size_t* end_pos
) {
223 assert(LookingAtNAL(kData
, *start_pos
));
224 *end_pos
= *start_pos
;
226 while (*end_pos
< kDataLen
&& !LookingAtNAL(kData
, *end_pos
)) {
231 #endif // USE_VP8_TESTDATA_INSTEAD_OF_H264
233 Decoder::Decoder(MyInstance
* instance
,
235 const pp::Graphics3D
& graphics_3d
)
236 : instance_(instance
),
238 decoder_(new pp::VideoDecoder(instance
)),
239 callback_factory_(this),
240 encoded_data_next_pos_to_decode_(0),
246 core_if_
= static_cast<const PPB_Core
*>(
247 pp::Module::Get()->GetBrowserInterface(PPB_CORE_INTERFACE
));
249 #if defined USE_VP8_TESTDATA_INSTEAD_OF_H264
250 const PP_VideoProfile kBitstreamProfile
= PP_VIDEOPROFILE_VP8_ANY
;
252 const PP_VideoProfile kBitstreamProfile
= PP_VIDEOPROFILE_H264MAIN
;
255 assert(!decoder_
->is_null());
256 decoder_
->Initialize(graphics_3d
,
258 PP_HARDWAREACCELERATION_WITHFALLBACK
,
260 callback_factory_
.NewCallback(&Decoder::InitializeDone
));
263 Decoder::~Decoder() {
267 void Decoder::InitializeDone(int32_t result
) {
269 assert(result
== PP_OK
);
273 void Decoder::Start() {
276 encoded_data_next_pos_to_decode_
= 0;
278 // Register callback to get the first picture. We call GetPicture again in
279 // PictureReady to continuously receive pictures as they're decoded.
280 decoder_
->GetPicture(
281 callback_factory_
.NewCallbackWithOutput(&Decoder::PictureReady
));
283 // Start the decode loop.
287 void Decoder::Reset() {
291 decoder_
->Reset(callback_factory_
.NewCallback(&Decoder::ResetDone
));
294 void Decoder::RecyclePicture(const PP_VideoPicture
& picture
) {
296 decoder_
->RecyclePicture(picture
);
299 void Decoder::DecodeNextFrame() {
301 if (encoded_data_next_pos_to_decode_
<= kDataLen
) {
302 // If we've just reached the end of the bitstream, flush and wait.
303 if (!flushing_
&& encoded_data_next_pos_to_decode_
== kDataLen
) {
305 decoder_
->Flush(callback_factory_
.NewCallback(&Decoder::FlushDone
));
309 // Find the start of the next frame.
310 size_t start_pos
= encoded_data_next_pos_to_decode_
;
312 GetNextFrame(&start_pos
, &end_pos
);
313 encoded_data_next_pos_to_decode_
= end_pos
;
314 // Decode the frame. On completion, DecodeDone will call DecodeNextFrame
315 // to implement a decode loop.
316 uint32_t size
= static_cast<uint32_t>(end_pos
- start_pos
);
317 decode_time_
[next_picture_id_
% kMaxDecodeDelay
] = core_if_
->GetTimeTicks();
318 decoder_
->Decode(next_picture_id_
++,
321 callback_factory_
.NewCallback(&Decoder::DecodeDone
));
325 void Decoder::DecodeDone(int32_t result
) {
327 // Break out of the decode loop on abort.
328 if (result
== PP_ERROR_ABORTED
)
330 assert(result
== PP_OK
);
331 if (!flushing_
&& !resetting_
)
335 void Decoder::PictureReady(int32_t result
, PP_VideoPicture picture
) {
337 // Break out of the get picture loop on abort.
338 if (result
== PP_ERROR_ABORTED
)
340 assert(result
== PP_OK
);
343 PP_TimeTicks latency
= core_if_
->GetTimeTicks() -
344 decode_time_
[picture
.decode_id
% kMaxDecodeDelay
];
345 total_latency_
+= latency
;
347 decoder_
->GetPicture(
348 callback_factory_
.NewCallbackWithOutput(&Decoder::PictureReady
));
349 instance_
->PaintPicture(this, picture
);
352 void Decoder::FlushDone(int32_t result
) {
354 assert(result
== PP_OK
|| result
== PP_ERROR_ABORTED
);
359 void Decoder::ResetDone(int32_t result
) {
361 assert(result
== PP_OK
);
368 MyInstance::MyInstance(PP_Instance instance
, pp::Module
* module
)
369 : pp::Instance(instance
),
370 pp::Graphics3DClient(this),
372 num_frames_rendered_(0),
373 first_frame_delivered_ticks_(-1),
374 last_swap_request_ticks_(-1),
376 callback_factory_(this),
378 console_if_
= static_cast<const PPB_Console
*>(
379 pp::Module::Get()->GetBrowserInterface(PPB_CONSOLE_INTERFACE
));
380 core_if_
= static_cast<const PPB_Core
*>(
381 pp::Module::Get()->GetBrowserInterface(PPB_CORE_INTERFACE
));
382 gles2_if_
= static_cast<const PPB_OpenGLES2
*>(
383 pp::Module::Get()->GetBrowserInterface(PPB_OPENGLES2_INTERFACE
));
385 RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE
);
388 MyInstance::~MyInstance() {
392 PP_Resource graphics_3d
= context_
->pp_resource();
393 if (shader_2d_
.program
)
394 gles2_if_
->DeleteProgram(graphics_3d
, shader_2d_
.program
);
395 if (shader_rectangle_arb_
.program
)
396 gles2_if_
->DeleteProgram(graphics_3d
, shader_rectangle_arb_
.program
);
397 if (shader_external_oes_
.program
)
398 gles2_if_
->DeleteProgram(graphics_3d
, shader_external_oes_
.program
);
400 for (DecoderList::iterator it
= video_decoders_
.begin();
401 it
!= video_decoders_
.end();
408 void MyInstance::DidChangeView(const pp::Rect
& position
,
409 const pp::Rect
& clip_ignored
) {
410 if (position
.width() == 0 || position
.height() == 0)
412 if (plugin_size_
.width()) {
413 assert(position
.size() == plugin_size_
);
416 plugin_size_
= position
.size();
418 // Initialize graphics.
420 InitializeDecoders();
423 bool MyInstance::HandleInputEvent(const pp::InputEvent
& event
) {
424 switch (event
.GetType()) {
425 case PP_INPUTEVENT_TYPE_MOUSEDOWN
: {
426 pp::MouseInputEvent
mouse_event(event
);
427 // Reset all decoders on mouse down.
428 if (mouse_event
.GetButton() == PP_INPUTEVENT_MOUSEBUTTON_LEFT
) {
430 for (size_t i
= 0; i
< video_decoders_
.size(); i
++) {
431 if (!video_decoders_
[i
]->resetting())
432 video_decoders_
[i
]->Reset();
443 void MyInstance::InitializeDecoders() {
444 assert(video_decoders_
.empty());
445 // Create two decoders with ids 0 and 1.
446 video_decoders_
.push_back(new Decoder(this, 0, *context_
));
447 video_decoders_
.push_back(new Decoder(this, 1, *context_
));
450 void MyInstance::PaintPicture(Decoder
* decoder
,
451 const PP_VideoPicture
& picture
) {
452 if (first_frame_delivered_ticks_
== -1)
453 assert((first_frame_delivered_ticks_
= core_if_
->GetTimeTicks()) != -1);
455 pending_pictures_
.push(PendingPicture(decoder
, picture
));
460 void MyInstance::PaintNextPicture() {
461 assert(!is_painting_
);
464 const PendingPicture
& next
= pending_pictures_
.front();
465 Decoder
* decoder
= next
.decoder
;
466 const PP_VideoPicture
& picture
= next
.picture
;
470 int half_width
= plugin_size_
.width() / 2;
471 int half_height
= plugin_size_
.height() / 2;
472 if (decoder
->id() != 0) {
477 PP_Resource graphics_3d
= context_
->pp_resource();
478 if (picture
.texture_target
== GL_TEXTURE_2D
) {
479 Create2DProgramOnce();
480 gles2_if_
->UseProgram(graphics_3d
, shader_2d_
.program
);
481 gles2_if_
->Uniform2f(
482 graphics_3d
, shader_2d_
.texcoord_scale_location
, 1.0, 1.0);
483 } else if (picture
.texture_target
== GL_TEXTURE_RECTANGLE_ARB
) {
484 CreateRectangleARBProgramOnce();
485 gles2_if_
->UseProgram(graphics_3d
, shader_rectangle_arb_
.program
);
486 gles2_if_
->Uniform2f(graphics_3d
,
487 shader_rectangle_arb_
.texcoord_scale_location
,
488 static_cast<GLfloat
>(picture
.texture_size
.width
),
489 static_cast<GLfloat
>(picture
.texture_size
.height
));
491 assert(picture
.texture_target
== GL_TEXTURE_EXTERNAL_OES
);
492 CreateExternalOESProgramOnce();
493 gles2_if_
->UseProgram(graphics_3d
, shader_external_oes_
.program
);
494 gles2_if_
->Uniform2f(
495 graphics_3d
, shader_external_oes_
.texcoord_scale_location
, 1.0, 1.0);
498 gles2_if_
->Viewport(graphics_3d
, x
, y
, half_width
, half_height
);
499 gles2_if_
->ActiveTexture(graphics_3d
, GL_TEXTURE0
);
500 gles2_if_
->BindTexture(
501 graphics_3d
, picture
.texture_target
, picture
.texture_id
);
502 gles2_if_
->DrawArrays(graphics_3d
, GL_TRIANGLE_STRIP
, 0, 4);
504 gles2_if_
->UseProgram(graphics_3d
, 0);
506 last_swap_request_ticks_
= core_if_
->GetTimeTicks();
507 context_
->SwapBuffers(
508 callback_factory_
.NewCallback(&MyInstance::PaintFinished
));
511 void MyInstance::PaintFinished(int32_t result
) {
512 assert(result
== PP_OK
);
513 swap_ticks_
+= core_if_
->GetTimeTicks() - last_swap_request_ticks_
;
514 is_painting_
= false;
515 ++num_frames_rendered_
;
516 if (num_frames_rendered_
% 50 == 0) {
517 double elapsed
= core_if_
->GetTimeTicks() - first_frame_delivered_ticks_
;
518 double fps
= (elapsed
> 0) ? num_frames_rendered_
/ elapsed
: 1000;
519 double ms_per_swap
= (swap_ticks_
* 1e3
) / num_frames_rendered_
;
520 double secs_average_latency
= 0;
521 for (DecoderList::iterator it
= video_decoders_
.begin();
522 it
!= video_decoders_
.end();
524 secs_average_latency
+= (*it
)->GetAverageLatency();
525 secs_average_latency
/= video_decoders_
.size();
526 double ms_average_latency
= 1000 * secs_average_latency
;
527 LogError(this).s() << "Rendered frames: " << num_frames_rendered_
529 << ", with average ms/swap of: " << ms_per_swap
530 << ", with average latency (ms) of: "
531 << ms_average_latency
;
534 // If the decoders were reset, this will be empty.
535 if (pending_pictures_
.empty())
538 const PendingPicture
& next
= pending_pictures_
.front();
539 Decoder
* decoder
= next
.decoder
;
540 const PP_VideoPicture
& picture
= next
.picture
;
541 decoder
->RecyclePicture(picture
);
542 pending_pictures_
.pop();
544 // Keep painting as long as we have pictures.
545 if (!pending_pictures_
.empty())
549 void MyInstance::InitGL() {
550 assert(plugin_size_
.width() && plugin_size_
.height());
551 is_painting_
= false;
554 int32_t context_attributes
[] = {
555 PP_GRAPHICS3DATTRIB_ALPHA_SIZE
, 8,
556 PP_GRAPHICS3DATTRIB_BLUE_SIZE
, 8,
557 PP_GRAPHICS3DATTRIB_GREEN_SIZE
, 8,
558 PP_GRAPHICS3DATTRIB_RED_SIZE
, 8,
559 PP_GRAPHICS3DATTRIB_DEPTH_SIZE
, 0,
560 PP_GRAPHICS3DATTRIB_STENCIL_SIZE
, 0,
561 PP_GRAPHICS3DATTRIB_SAMPLES
, 0,
562 PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS
, 0,
563 PP_GRAPHICS3DATTRIB_WIDTH
, plugin_size_
.width(),
564 PP_GRAPHICS3DATTRIB_HEIGHT
, plugin_size_
.height(),
565 PP_GRAPHICS3DATTRIB_NONE
,
567 context_
= new pp::Graphics3D(this, context_attributes
);
568 assert(!context_
->is_null());
569 assert(BindGraphics(*context_
));
572 gles2_if_
->ClearColor(context_
->pp_resource(), 1, 0, 0, 1);
573 gles2_if_
->Clear(context_
->pp_resource(), GL_COLOR_BUFFER_BIT
);
580 void MyInstance::CreateGLObjects() {
581 // Assign vertex positions and texture coordinates to buffers for use in
583 static const float kVertices
[] = {
584 -1, -1, -1, 1, 1, -1, 1, 1, // Position coordinates.
585 0, 1, 0, 0, 1, 1, 1, 0, // Texture coordinates.
589 gles2_if_
->GenBuffers(context_
->pp_resource(), 1, &buffer
);
590 gles2_if_
->BindBuffer(context_
->pp_resource(), GL_ARRAY_BUFFER
, buffer
);
592 gles2_if_
->BufferData(context_
->pp_resource(),
600 static const char kVertexShader
[] =
601 "varying vec2 v_texCoord; \n"
602 "attribute vec4 a_position; \n"
603 "attribute vec2 a_texCoord; \n"
604 "uniform vec2 v_scale; \n"
607 " v_texCoord = v_scale * a_texCoord; \n"
608 " gl_Position = a_position; \n"
611 void MyInstance::Create2DProgramOnce() {
612 if (shader_2d_
.program
)
614 static const char kFragmentShader2D
[] =
615 "precision mediump float; \n"
616 "varying vec2 v_texCoord; \n"
617 "uniform sampler2D s_texture; \n"
620 " gl_FragColor = texture2D(s_texture, v_texCoord); \n"
622 shader_2d_
= CreateProgram(kVertexShader
, kFragmentShader2D
);
626 void MyInstance::CreateRectangleARBProgramOnce() {
627 if (shader_rectangle_arb_
.program
)
629 static const char kFragmentShaderRectangle
[] =
630 "#extension GL_ARB_texture_rectangle : require\n"
631 "precision mediump float; \n"
632 "varying vec2 v_texCoord; \n"
633 "uniform sampler2DRect s_texture; \n"
636 " gl_FragColor = texture2DRect(s_texture, v_texCoord).rgba; \n"
638 shader_rectangle_arb_
=
639 CreateProgram(kVertexShader
, kFragmentShaderRectangle
);
643 void MyInstance::CreateExternalOESProgramOnce() {
644 if (shader_external_oes_
.program
)
646 static const char kFragmentShaderExternal
[] =
647 "#extension GL_OES_EGL_image_external : require\n"
648 "precision mediump float; \n"
649 "varying vec2 v_texCoord; \n"
650 "uniform samplerExternalOES s_texture; \n"
653 " gl_FragColor = texture2D(s_texture, v_texCoord); \n"
655 shader_external_oes_
= CreateProgram(kVertexShader
, kFragmentShaderExternal
);
659 Shader
MyInstance::CreateProgram(const char* vertex_shader
,
660 const char* fragment_shader
) {
663 // Create shader program.
664 shader
.program
= gles2_if_
->CreateProgram(context_
->pp_resource());
666 shader
.program
, GL_VERTEX_SHADER
, vertex_shader
,
667 static_cast<uint32_t>(strlen(vertex_shader
)));
668 CreateShader(shader
.program
,
671 static_cast<int>(strlen(fragment_shader
)));
672 gles2_if_
->LinkProgram(context_
->pp_resource(), shader
.program
);
673 gles2_if_
->UseProgram(context_
->pp_resource(), shader
.program
);
674 gles2_if_
->Uniform1i(
675 context_
->pp_resource(),
676 gles2_if_
->GetUniformLocation(
677 context_
->pp_resource(), shader
.program
, "s_texture"),
681 shader
.texcoord_scale_location
= gles2_if_
->GetUniformLocation(
682 context_
->pp_resource(), shader
.program
, "v_scale");
684 GLint pos_location
= gles2_if_
->GetAttribLocation(
685 context_
->pp_resource(), shader
.program
, "a_position");
686 GLint tc_location
= gles2_if_
->GetAttribLocation(
687 context_
->pp_resource(), shader
.program
, "a_texCoord");
690 gles2_if_
->EnableVertexAttribArray(context_
->pp_resource(), pos_location
);
691 gles2_if_
->VertexAttribPointer(
692 context_
->pp_resource(), pos_location
, 2, GL_FLOAT
, GL_FALSE
, 0, 0);
693 gles2_if_
->EnableVertexAttribArray(context_
->pp_resource(), tc_location
);
694 gles2_if_
->VertexAttribPointer(
695 context_
->pp_resource(),
701 static_cast<float*>(0) + 8); // Skip position coordinates.
703 gles2_if_
->UseProgram(context_
->pp_resource(), 0);
708 void MyInstance::CreateShader(GLuint program
,
712 GLuint shader
= gles2_if_
->CreateShader(context_
->pp_resource(), type
);
713 gles2_if_
->ShaderSource(context_
->pp_resource(), shader
, 1, &source
, &size
);
714 gles2_if_
->CompileShader(context_
->pp_resource(), shader
);
715 gles2_if_
->AttachShader(context_
->pp_resource(), program
, shader
);
716 gles2_if_
->DeleteShader(context_
->pp_resource(), shader
);
719 // This object is the global object representing this plugin library as long
721 class MyModule
: public pp::Module
{
723 MyModule() : pp::Module() {}
724 virtual ~MyModule() {}
726 virtual pp::Instance
* CreateInstance(PP_Instance instance
) {
727 return new MyInstance(instance
, this);
731 } // anonymous namespace
734 // Factory function for your specialization of the Module object.
735 Module
* CreateModule() {
736 return new MyModule();