Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(7)

Side by Side Diff: ppapi/examples/video_decode/video_decode.cc

Issue 270213004: Implement Pepper PPB_VideoDecoder interface. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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 #include "ppapi/examples/video_decode/testdata.h"
24 #include "ppapi/lib/gl/include/GLES2/gl2.h"
25 #include "ppapi/lib/gl/include/GLES2/gl2ext.h"
26 #include "ppapi/utility/completion_callback_factory.h"
27
28 // Use assert as a poor-man's CHECK, even in non-debug mode.
29 // Since <assert.h> redefines assert on every inclusion (it doesn't use
30 // include-guards), make sure this is the last file #include'd in this file.
31 #undef NDEBUG
32 #include <assert.h>
33
34 // Assert |context_| isn't holding any GL Errors. Done as a macro instead of a
35 // function to preserve line number information in the failure message.
36 #define assertNoGLError() \
37 assert(!gles2_if_->GetError(context_->pp_resource()));
38
39 namespace {
40
41 struct Shader {
42 Shader() : program(0), texcoord_scale_location(0) {}
43 ~Shader() {}
44
45 GLuint program;
46 GLint texcoord_scale_location;
47 };
48
49 class Decoder;
50 class MyInstance;
51
52 class MyInstance : public pp::Instance, public pp::Graphics3DClient {
53 public:
54 MyInstance(PP_Instance instance, pp::Module* module);
55 virtual ~MyInstance();
56
57 // pp::Instance implementation.
58 virtual void DidChangeView(const pp::Rect& position,
59 const pp::Rect& clip_ignored);
60
61 // pp::Graphics3DClient implementation.
62 virtual void Graphics3DContextLost() {
63 // TODO(vrk/fischman): Properly reset after a lost graphics context. In
64 // particular need to delete context_ and re-create textures.
65 // Probably have to recreate the decoder from scratch, because old textures
66 // can still be outstanding in the decoder!
67 assert(false && "Unexpectedly lost graphics context");
68 }
69
70 void PaintPicture(Decoder* decoder, const PP_VideoPicture& picture);
71
72 private:
73 // Log an error to the developer console and stderr by creating a temporary
74 // object of this type and streaming to it. Example usage:
75 // LogError(this).s() << "Hello world: " << 42;
76 class LogError {
77 public:
78 LogError(MyInstance* instance) : instance_(instance) {}
79 ~LogError() {
80 const std::string& msg = stream_.str();
81 instance_->console_if_->Log(
82 instance_->pp_instance(), PP_LOGLEVEL_ERROR, pp::Var(msg).pp_var());
83 std::cerr << msg << std::endl;
84 }
85 // Impl note: it would have been nicer to have LogError derive from
86 // std::ostringstream so that it can be streamed to directly, but lookup
87 // rules turn streamed string literals to hex pointers on output.
88 std::ostringstream& s() { return stream_; }
89
90 private:
91 MyInstance* instance_;
92 std::ostringstream stream_;
93 };
94
95 void InitializeDecoders();
96
97 // GL-related functions.
98 void InitGL();
99 void CreateGLObjects();
100 void Create2DProgramOnce();
101 void CreateRectangleARBProgramOnce();
102 Shader CreateProgram(const char* vertex_shader, const char* fragment_shader);
103 void CreateShader(GLuint program, GLenum type, const char* source, int size);
104 void PaintFinished(int32_t result,
105 Decoder* decoder,
106 PP_VideoPicture picture);
107
108 pp::Size plugin_size_;
109 bool is_painting_;
110 // When decode outpaces render, we queue up decoded pictures for later
111 // painting. Elements are <decoder,picture>.
112 typedef std::queue<std::pair<Decoder*, PP_VideoPicture> > PictureQueue;
113 PictureQueue pictures_pending_paint_;
114
115 int num_frames_rendered_;
116 PP_TimeTicks first_frame_delivered_ticks_;
117 PP_TimeTicks last_swap_request_ticks_;
118 PP_TimeTicks swap_ticks_;
119 pp::CompletionCallbackFactory<MyInstance> callback_factory_;
120
121 // Unowned pointers.
122 const PPB_Console* console_if_;
123 const PPB_Core* core_if_;
124 const PPB_OpenGLES2* gles2_if_;
125
126 // Owned data.
127 pp::Graphics3D* context_;
128 typedef std::vector<Decoder*> DecoderList;
129 DecoderList video_decoders_;
130
131 // Shader program to draw GL_TEXTURE_2D target.
132 Shader shader_2d_;
133 // Shader program to draw GL_TEXTURE_RECTANGLE_ARB target.
134 Shader shader_rectangle_arb_;
135 };
136
137 class Decoder {
138 public:
139 Decoder(MyInstance* instance, int id, const pp::Graphics3D& graphics_3d);
140 ~Decoder();
141
142 int id() const { return id_; }
143 bool ready() const { return !flushing_ && !resetting_; }
144
145 void Seek(int frame);
146 void RecyclePicture(const PP_VideoPicture& picture);
147
148 private:
149 void InitializeDone(int32_t result);
150 void Start(int frame);
151 void DecodeNextFrames();
152 void DecodeDone(int32_t result);
153 void PictureReady(int32_t result, PP_VideoPicture picture);
154 void FlushDone(int32_t result);
155 void ResetDone(int32_t result);
156
157 MyInstance* instance_;
158 int id_;
159
160 pp::VideoDecoder* decoder_;
161 pp::CompletionCallbackFactory<Decoder> callback_factory_;
162
163 size_t encoded_data_next_pos_to_decode_;
164 int next_picture_id_;
165 int seek_frame_;
166 bool flushing_;
167 bool resetting_;
168 };
169
170 // Returns true if the current position is at the start of a NAL unit.
171 static bool LookingAtNAL(const unsigned char* encoded, size_t pos) {
Tom Sepez 2014/05/08 20:35:07 I'd be happier if you passed in the max length as
bbudge 2014/05/14 19:35:04 The test data is actually compiled into the exampl
172 return pos + 3 < kDataLen && encoded[pos] == 0 && encoded[pos + 1] == 0 &&
173 encoded[pos + 2] == 0 && encoded[pos + 3] == 1;
Tom Sepez 2014/05/08 20:35:07 Theoretically, |pos + 3 < kDataLen| could overflow
bbudge 2014/05/14 19:35:04 pos should always be in the range [0, kDataLen + 1
174 }
175
176 // Find the start and end of the next frame.
177 static void GetNextFrame(size_t *start_pos, size_t* end_pos) {
Tom Sepez 2014/05/08 20:35:07 why pass start_pos by pointer? I didn't see it mo
bbudge 2014/05/14 19:35:04 True. It's here because it will be needed in the f
178 // H264 frames start with 0, 0, 0, 1.
Tom Sepez 2014/05/08 20:35:07 nit: this comment might move up to LookingAtNAL().
bbudge 2014/05/14 19:35:04 Done.
179 assert(LookingAtNAL(kData, *start_pos));
180 *end_pos = *start_pos;
181 *end_pos += 4;
182 while (*end_pos + 3 < kDataLen && !LookingAtNAL(kData, *end_pos)) {
Tom Sepez 2014/05/08 20:35:07 I suppose if LookingAtNAL is bullet-proof against
bbudge 2014/05/14 19:35:04 Indeed. Done.
183 ++*end_pos;
184 }
185 if (*end_pos + 3 >= kDataLen) {
186 *end_pos = kDataLen;
187 }
188 }
189
190 Decoder::Decoder(MyInstance* instance,
191 int id,
192 const pp::Graphics3D& graphics_3d)
193 : instance_(instance),
194 id_(id),
195 decoder_(new pp::VideoDecoder(instance)),
196 callback_factory_(this),
197 encoded_data_next_pos_to_decode_(0),
198 next_picture_id_(0),
199 seek_frame_(0),
200 flushing_(false),
201 resetting_(false) {
202 assert(!decoder_->is_null());
203 const PP_VideoProfile profile = PP_VIDEOPROFILE_H264MAIN;
204 decoder_->Initialize(graphics_3d,
205 profile,
206 PP_TRUE /* allow_software_fallback */,
207 callback_factory_.NewCallback(&Decoder::Start));
208 }
209
210 Decoder::~Decoder() {
211 }
212
213 void Decoder::InitializeDone(int32_t result) {
214 assert(decoder_);
215 assert(result == PP_OK);
216 assert(ready());
217 Start(0);
218 }
219
220 void Decoder::Start(int frame) {
221 assert(decoder_);
222
223 // Skip to |frame|.
224 size_t start_pos = 0;
225 size_t end_pos = 0;
226 for (int i = frame; i > 0; i--)
227 GetNextFrame(&start_pos, &end_pos);
228 encoded_data_next_pos_to_decode_ = end_pos;
229
230 DecodeNextFrames();
231 // Register callback to get the first picture. We call GetPicture
232 // again in PictureReady to keep getting pictures.
233 decoder_->GetPicture(
234 callback_factory_.NewCallbackWithOutput(&Decoder::PictureReady));
235 }
236
237 void Decoder::Seek(int frame) {
238 assert(decoder_);
239 seek_frame_ = frame;
240 resetting_ = true;
241 decoder_->Reset(callback_factory_.NewCallback(&Decoder::ResetDone));
242 }
243
244 void Decoder::RecyclePicture(const PP_VideoPicture& picture) {
245 assert(decoder_);
246 decoder_->RecyclePicture(picture);
247 }
248
249 void Decoder::DecodeNextFrames() {
250 assert(decoder_);
251 while (encoded_data_next_pos_to_decode_ <= kDataLen) {
252 // If we've just reached the end of the bitstream, flush and wait.
253 if (!flushing_ && encoded_data_next_pos_to_decode_ == kDataLen) {
254 flushing_ = true;
255 decoder_->Flush(callback_factory_.NewCallback(&Decoder::FlushDone));
256 return;
257 }
258
259 // Find the start of the next NALU.
260 size_t start_pos = encoded_data_next_pos_to_decode_;
261 size_t end_pos;
262 GetNextFrame(&start_pos, &end_pos);
263 encoded_data_next_pos_to_decode_ = end_pos;
264 // Decode the NALU.
265 uint32_t size = static_cast<uint32_t>(end_pos - start_pos);
Tom Sepez 2014/05/08 20:35:07 We know this always fits?
bbudge 2014/05/14 19:35:04 The current test data (ppapi/examples/video_decode
266 int32_t result =
267 decoder_->Decode(next_picture_id_++,
268 size,
269 kData + start_pos,
270 callback_factory_.NewCallback(&Decoder::DecodeDone));
271 if (result != PP_OK) {
272 assert(result == PP_OK_COMPLETIONPENDING);
273 break;
274 }
275 }
276 }
277
278 void Decoder::DecodeDone(int32_t result) {
279 assert(decoder_);
280 assert(result == PP_OK || result == PP_ERROR_ABORTED);
281 if (result == PP_OK && ready())
282 DecodeNextFrames();
283 }
284
285 void Decoder::PictureReady(int32_t result, PP_VideoPicture picture) {
286 assert(decoder_);
287 assert(result == PP_OK || result == PP_ERROR_ABORTED);
288 if (result == PP_OK) {
289 decoder_->GetPicture(
290 callback_factory_.NewCallbackWithOutput(&Decoder::PictureReady));
291 instance_->PaintPicture(this, picture);
292 }
293 }
294
295 void Decoder::FlushDone(int32_t result) {
296 assert(decoder_);
297 assert(result == PP_OK || result == PP_ERROR_ABORTED);
298 flushing_ = false;
299 }
300
301 void Decoder::ResetDone(int32_t result) {
302 assert(decoder_);
303 assert(result == PP_OK);
304 resetting_ = false;
305 }
306
307 MyInstance::MyInstance(PP_Instance instance, pp::Module* module)
308 : pp::Instance(instance),
309 pp::Graphics3DClient(this),
310 is_painting_(false),
311 num_frames_rendered_(0),
312 first_frame_delivered_ticks_(-1),
313 swap_ticks_(0),
314 callback_factory_(this),
315 context_(NULL) {
316 assert((console_if_ = static_cast<const PPB_Console*>(
317 module->GetBrowserInterface(PPB_CONSOLE_INTERFACE))));
318 assert((core_if_ = static_cast<const PPB_Core*>(
319 module->GetBrowserInterface(PPB_CORE_INTERFACE))));
320 assert((gles2_if_ = static_cast<const PPB_OpenGLES2*>(
321 module->GetBrowserInterface(PPB_OPENGLES2_INTERFACE))));
322 RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE);
323 }
324
325 MyInstance::~MyInstance() {
326 if (!context_)
327 return;
328
329 PP_Resource graphics_3d = context_->pp_resource();
330 if (shader_2d_.program)
331 gles2_if_->DeleteProgram(graphics_3d, shader_2d_.program);
332 if (shader_rectangle_arb_.program)
333 gles2_if_->DeleteProgram(graphics_3d, shader_rectangle_arb_.program);
334
335 for (DecoderList::iterator it = video_decoders_.begin();
336 it != video_decoders_.end();
337 ++it)
338 delete *it;
339
340 delete context_;
341 }
342
343 void MyInstance::DidChangeView(const pp::Rect& position,
344 const pp::Rect& clip_ignored) {
345 if (position.width() == 0 || position.height() == 0)
346 return;
347 if (plugin_size_.width()) {
348 assert(position.size() == plugin_size_);
349 return;
350 }
351 plugin_size_ = position.size();
352
353 // Initialize graphics.
354 InitGL();
355 InitializeDecoders();
356 }
357
358 void MyInstance::InitializeDecoders() {
359 assert(video_decoders_.empty());
360 // Create two decoders with ids 0 and 1.
361 video_decoders_.push_back(new Decoder(this, 0, *context_));
362 video_decoders_.push_back(new Decoder(this, 1, *context_));
363 }
364
365 void MyInstance::PaintPicture(Decoder* decoder,
366 const PP_VideoPicture& picture) {
367 if (first_frame_delivered_ticks_ == -1)
368 assert((first_frame_delivered_ticks_ = core_if_->GetTimeTicks()) != -1);
369 if (is_painting_) {
370 pictures_pending_paint_.push(std::make_pair(decoder, picture));
371 return;
372 }
373
374 assert(!is_painting_);
375 is_painting_ = true;
376 int x = 0;
377 int y = 0;
378 int half_width = plugin_size_.width() / 2;
379 int half_height = plugin_size_.height() / 2;
380 if (decoder->id() != 0) {
381 x = half_width;
382 y = half_height;
383 }
384
385 PP_Resource graphics_3d = context_->pp_resource();
386 if (picture.texture_target == GL_TEXTURE_2D) {
387 Create2DProgramOnce();
388 gles2_if_->UseProgram(graphics_3d, shader_2d_.program);
389 gles2_if_->Uniform2f(
390 graphics_3d, shader_2d_.texcoord_scale_location, 1.0, 1.0);
391 } else {
392 assert(picture.texture_target == GL_TEXTURE_RECTANGLE_ARB);
393 CreateRectangleARBProgramOnce();
394 gles2_if_->UseProgram(graphics_3d, shader_rectangle_arb_.program);
395 gles2_if_->Uniform2f(graphics_3d,
396 shader_rectangle_arb_.texcoord_scale_location,
397 picture.texture_size.width,
398 picture.texture_size.height);
399 }
400
401 gles2_if_->Viewport(graphics_3d, x, y, half_width, half_height);
402 gles2_if_->ActiveTexture(graphics_3d, GL_TEXTURE0);
403 gles2_if_->BindTexture(
404 graphics_3d, picture.texture_target, picture.texture_id);
405 gles2_if_->DrawArrays(graphics_3d, GL_TRIANGLE_STRIP, 0, 4);
406
407 gles2_if_->UseProgram(graphics_3d, 0);
408
409 last_swap_request_ticks_ = core_if_->GetTimeTicks();
410 assert(PP_OK_COMPLETIONPENDING ==
411 context_->SwapBuffers(callback_factory_.NewCallback(
412 &MyInstance::PaintFinished, decoder, picture)));
413 }
414
415 void MyInstance::PaintFinished(int32_t result,
416 Decoder* decoder,
417 PP_VideoPicture picture) {
418 assert(result == PP_OK);
419 swap_ticks_ += core_if_->GetTimeTicks() - last_swap_request_ticks_;
420 is_painting_ = false;
421 ++num_frames_rendered_;
422 if (num_frames_rendered_ % 50 == 0) {
423 double elapsed = core_if_->GetTimeTicks() - first_frame_delivered_ticks_;
424 double fps = (elapsed > 0) ? num_frames_rendered_ / elapsed : 1000;
425 double ms_per_swap = (swap_ticks_ * 1e3) / num_frames_rendered_;
426 LogError(this).s() << "Rendered frames: " << num_frames_rendered_
427 << ", fps: " << fps
428 << ", with average ms/swap of: " << ms_per_swap;
429 }
430 decoder->RecyclePicture(picture);
431 // Keep painting as long as we have pictures.
432 if (!pictures_pending_paint_.empty()) {
433 std::pair<Decoder*, PP_VideoPicture> pending =
434 pictures_pending_paint_.front();
435 pictures_pending_paint_.pop();
436 PaintPicture(pending.first, pending.second);
437 }
438 }
439
440 void MyInstance::InitGL() {
441 assert(plugin_size_.width() && plugin_size_.height());
442 is_painting_ = false;
443
444 assert(!context_);
445 int32_t context_attributes[] = {
446 PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8,
447 PP_GRAPHICS3DATTRIB_BLUE_SIZE, 8,
448 PP_GRAPHICS3DATTRIB_GREEN_SIZE, 8,
449 PP_GRAPHICS3DATTRIB_RED_SIZE, 8,
450 PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 0,
451 PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 0,
452 PP_GRAPHICS3DATTRIB_SAMPLES, 0,
453 PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0,
454 PP_GRAPHICS3DATTRIB_WIDTH, plugin_size_.width(),
455 PP_GRAPHICS3DATTRIB_HEIGHT, plugin_size_.height(),
456 PP_GRAPHICS3DATTRIB_NONE, };
457 context_ = new pp::Graphics3D(this, context_attributes);
458 assert(!context_->is_null());
459 assert(BindGraphics(*context_));
460
461 // Clear color bit.
462 gles2_if_->ClearColor(context_->pp_resource(), 1, 0, 0, 1);
463 gles2_if_->Clear(context_->pp_resource(), GL_COLOR_BUFFER_BIT);
464
465 assertNoGLError();
466
467 CreateGLObjects();
468 }
469
470 void MyInstance::CreateGLObjects() {
471 // Assign vertex positions and texture coordinates to buffers for use in
472 // shader program.
473 static const float kVertices[] = {
474 -1, 1, -1, -1, 1, 1, 1, -1, // Position coordinates.
475 0, 1, 0, 0, 1, 1, 1, 0, // Texture coordinates.
476 };
477
478 GLuint buffer;
479 gles2_if_->GenBuffers(context_->pp_resource(), 1, &buffer);
480 gles2_if_->BindBuffer(context_->pp_resource(), GL_ARRAY_BUFFER, buffer);
481
482 gles2_if_->BufferData(context_->pp_resource(),
483 GL_ARRAY_BUFFER,
484 sizeof(kVertices),
485 kVertices,
486 GL_STATIC_DRAW);
487 assertNoGLError();
488 }
489
490 static const char kVertexShader[] =
491 "varying vec2 v_texCoord; \n"
492 "attribute vec4 a_position; \n"
493 "attribute vec2 a_texCoord; \n"
494 "uniform vec2 v_scale; \n"
495 "void main() \n"
496 "{ \n"
497 " v_texCoord = v_scale * a_texCoord; \n"
498 " gl_Position = a_position; \n"
499 "}";
500
501 void MyInstance::Create2DProgramOnce() {
502 if (shader_2d_.program)
503 return;
504 static const char kFragmentShader2D[] =
505 "precision mediump float; \n"
506 "varying vec2 v_texCoord; \n"
507 "uniform sampler2D s_texture; \n"
508 "void main() \n"
509 "{"
510 " gl_FragColor = texture2D(s_texture, v_texCoord); \n"
511 "}";
512 shader_2d_ = CreateProgram(kVertexShader, kFragmentShader2D);
513 assertNoGLError();
514 }
515
516 void MyInstance::CreateRectangleARBProgramOnce() {
517 if (shader_rectangle_arb_.program)
518 return;
519 static const char kFragmentShaderRectangle[] =
520 "#extension GL_ARB_texture_rectangle : require\n"
521 "precision mediump float; \n"
522 "varying vec2 v_texCoord; \n"
523 "uniform sampler2DRect s_texture; \n"
524 "void main() \n"
525 "{"
526 " gl_FragColor = texture2DRect(s_texture, v_texCoord).rgba; \n"
527 "}";
528 shader_rectangle_arb_ =
529 CreateProgram(kVertexShader, kFragmentShaderRectangle);
530 }
531
532 Shader MyInstance::CreateProgram(const char* vertex_shader,
533 const char* fragment_shader) {
534 Shader shader;
535
536 // Create shader program.
537 shader.program = gles2_if_->CreateProgram(context_->pp_resource());
538 CreateShader(
539 shader.program, GL_VERTEX_SHADER, vertex_shader, strlen(vertex_shader));
540 CreateShader(shader.program,
541 GL_FRAGMENT_SHADER,
542 fragment_shader,
543 strlen(fragment_shader));
544 gles2_if_->LinkProgram(context_->pp_resource(), shader.program);
545 gles2_if_->UseProgram(context_->pp_resource(), shader.program);
546 gles2_if_->Uniform1i(
547 context_->pp_resource(),
548 gles2_if_->GetUniformLocation(
549 context_->pp_resource(), shader.program, "s_texture"),
550 0);
551 assertNoGLError();
552
553 shader.texcoord_scale_location = gles2_if_->GetUniformLocation(
554 context_->pp_resource(), shader.program, "v_scale");
555
556 GLint pos_location = gles2_if_->GetAttribLocation(
557 context_->pp_resource(), shader.program, "a_position");
558 GLint tc_location = gles2_if_->GetAttribLocation(
559 context_->pp_resource(), shader.program, "a_texCoord");
560 assertNoGLError();
561
562 gles2_if_->EnableVertexAttribArray(context_->pp_resource(), pos_location);
563 gles2_if_->VertexAttribPointer(
564 context_->pp_resource(), pos_location, 2, GL_FLOAT, GL_FALSE, 0, 0);
565 gles2_if_->EnableVertexAttribArray(context_->pp_resource(), tc_location);
566 gles2_if_->VertexAttribPointer(
567 context_->pp_resource(),
568 tc_location,
569 2,
570 GL_FLOAT,
571 GL_FALSE,
572 0,
573 static_cast<float*>(0) + 8); // Skip position coordinates.
574
575 gles2_if_->UseProgram(context_->pp_resource(), 0);
576 assertNoGLError();
577 return shader;
578 }
579
580 void MyInstance::CreateShader(GLuint program,
581 GLenum type,
582 const char* source,
583 int size) {
584 GLuint shader = gles2_if_->CreateShader(context_->pp_resource(), type);
585 gles2_if_->ShaderSource(context_->pp_resource(), shader, 1, &source, &size);
586 gles2_if_->CompileShader(context_->pp_resource(), shader);
587 gles2_if_->AttachShader(context_->pp_resource(), program, shader);
588 gles2_if_->DeleteShader(context_->pp_resource(), shader);
589 }
590
591 // This object is the global object representing this plugin library as long
592 // as it is loaded.
593 class MyModule : public pp::Module {
594 public:
595 MyModule() : pp::Module() {}
596 virtual ~MyModule() {}
597
598 virtual pp::Instance* CreateInstance(PP_Instance instance) {
599 return new MyInstance(instance, this);
600 }
601 };
602
603 } // anonymous namespace
604
605 namespace pp {
606 // Factory function for your specialization of the Module object.
607 Module* CreateModule() { return new MyModule(); }
608 } // namespace pp
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698