Allow only one bookmark to be added for multiple fast starring
[chromium-blink-merge.git] / ppapi / examples / video_decode / video_decode.cc
blob10e0536b741fb4ff628548261935ab5f0e0592df
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.
5 #include <stdio.h>
6 #include <string.h>
8 #include <iostream>
9 #include <queue>
10 #include <sstream>
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
25 // to decode H264.
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.
36 #undef NDEBUG
37 #include <assert.h>
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()));
43 namespace {
45 struct Shader {
46 Shader() : program(0), texcoord_scale_location(0) {}
47 ~Shader() {}
49 GLuint program;
50 GLint texcoord_scale_location;
53 class Decoder;
54 class MyInstance;
56 struct PendingPicture {
57 PendingPicture(Decoder* decoder, const PP_VideoPicture& picture)
58 : decoder(decoder), picture(picture) {}
59 ~PendingPicture() {}
61 Decoder* decoder;
62 PP_VideoPicture picture;
65 class MyInstance : public pp::Instance, public pp::Graphics3DClient {
66 public:
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);
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;
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_; }
104 private:
105 MyInstance* instance_;
106 std::ostringstream stream_;
109 void InitializeDecoders();
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);
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_;
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_;
135 // Unowned pointers.
136 const PPB_Console* console_if_;
137 const PPB_Core* core_if_;
138 const PPB_OpenGLES2* gles2_if_;
140 // Owned data.
141 pp::Graphics3D* context_;
142 typedef std::vector<Decoder*> DecoderList;
143 DecoderList video_decoders_;
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_;
153 class Decoder {
154 public:
155 Decoder(MyInstance* instance, int id, const pp::Graphics3D& graphics_3d);
156 ~Decoder();
158 int id() const { return id_; }
159 bool flushing() const { return flushing_; }
160 bool resetting() const { return resetting_; }
162 void Reset();
163 void RecyclePicture(const PP_VideoPicture& picture);
165 PP_TimeTicks GetAverageLatency() {
166 return num_pictures_ ? total_latency_ / num_pictures_ : 0;
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);
178 MyInstance* instance_;
179 int id_;
181 pp::VideoDecoder* decoder_;
182 pp::CompletionCallbackFactory<Decoder> callback_factory_;
184 size_t encoded_data_next_pos_to_decode_;
185 int next_picture_id_;
186 bool flushing_;
187 bool resetting_;
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_;
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;
225 *end_pos += 4;
226 while (*end_pos < kDataLen && !LookingAtNAL(kData, *end_pos)) {
227 ++*end_pos;
231 #endif // USE_VP8_TESTDATA_INSTEAD_OF_H264
233 Decoder::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));
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
255 assert(!decoder_->is_null());
256 decoder_->Initialize(graphics_3d,
257 kBitstreamProfile,
258 PP_HARDWAREACCELERATION_WITHFALLBACK,
259 callback_factory_.NewCallback(&Decoder::InitializeDone));
262 Decoder::~Decoder() {
263 delete decoder_;
266 void Decoder::InitializeDone(int32_t result) {
267 assert(decoder_);
268 assert(result == PP_OK);
269 Start();
272 void Decoder::Start() {
273 assert(decoder_);
275 encoded_data_next_pos_to_decode_ = 0;
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));
282 // Start the decode loop.
283 DecodeNextFrame();
286 void Decoder::Reset() {
287 assert(decoder_);
288 assert(!resetting_);
289 resetting_ = true;
290 decoder_->Reset(callback_factory_.NewCallback(&Decoder::ResetDone));
293 void Decoder::RecyclePicture(const PP_VideoPicture& picture) {
294 assert(decoder_);
295 decoder_->RecyclePicture(picture);
298 void 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;
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));
324 void 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();
334 void 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);
341 num_pictures_++;
342 PP_TimeTicks latency = core_if_->GetTimeTicks() -
343 decode_time_[picture.decode_id % kMaxDecodeDelay];
344 total_latency_ += latency;
346 decoder_->GetPicture(
347 callback_factory_.NewCallbackWithOutput(&Decoder::PictureReady));
348 instance_->PaintPicture(this, picture);
351 void Decoder::FlushDone(int32_t result) {
352 assert(decoder_);
353 assert(result == PP_OK || result == PP_ERROR_ABORTED);
354 assert(flushing_);
355 flushing_ = false;
358 void Decoder::ResetDone(int32_t result) {
359 assert(decoder_);
360 assert(result == PP_OK);
361 assert(resetting_);
362 resetting_ = false;
364 Start();
367 MyInstance::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));
384 RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE);
387 MyInstance::~MyInstance() {
388 if (!context_)
389 return;
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);
399 for (DecoderList::iterator it = video_decoders_.begin();
400 it != video_decoders_.end();
401 ++it)
402 delete *it;
404 delete context_;
407 void 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;
415 plugin_size_ = position.size();
417 // Initialize graphics.
418 InitGL();
419 InitializeDecoders();
422 bool 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();
434 return true;
437 default:
438 return false;
442 void 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_));
449 void 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);
454 pending_pictures_.push(PendingPicture(decoder, picture));
455 if (!is_painting_)
456 PaintNextPicture();
459 void MyInstance::PaintNextPicture() {
460 assert(!is_painting_);
461 is_painting_ = true;
463 const PendingPicture& next = pending_pictures_.front();
464 Decoder* decoder = next.decoder;
465 const PP_VideoPicture& picture = next.picture;
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;
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 static_cast<GLfloat>(picture.texture_size.width),
488 static_cast<GLfloat>(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);
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);
503 gles2_if_->UseProgram(graphics_3d, 0);
505 last_swap_request_ticks_ = core_if_->GetTimeTicks();
506 context_->SwapBuffers(
507 callback_factory_.NewCallback(&MyInstance::PaintFinished));
510 void 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;
533 // If the decoders were reset, this will be empty.
534 if (pending_pictures_.empty())
535 return;
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();
543 // Keep painting as long as we have pictures.
544 if (!pending_pictures_.empty())
545 PaintNextPicture();
548 void MyInstance::InitGL() {
549 assert(plugin_size_.width() && plugin_size_.height());
550 is_painting_ = false;
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,
566 context_ = new pp::Graphics3D(this, context_attributes);
567 assert(!context_->is_null());
568 assert(BindGraphics(*context_));
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);
574 assertNoGLError();
576 CreateGLObjects();
579 void 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.
587 GLuint buffer;
588 gles2_if_->GenBuffers(context_->pp_resource(), 1, &buffer);
589 gles2_if_->BindBuffer(context_->pp_resource(), GL_ARRAY_BUFFER, buffer);
591 gles2_if_->BufferData(context_->pp_resource(),
592 GL_ARRAY_BUFFER,
593 sizeof(kVertices),
594 kVertices,
595 GL_STATIC_DRAW);
596 assertNoGLError();
599 static 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 "}";
610 void 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"
619 " gl_FragColor = texture2D(s_texture, v_texCoord); \n"
620 "}";
621 shader_2d_ = CreateProgram(kVertexShader, kFragmentShader2D);
622 assertNoGLError();
625 void 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"
635 " gl_FragColor = texture2DRect(s_texture, v_texCoord).rgba; \n"
636 "}";
637 shader_rectangle_arb_ =
638 CreateProgram(kVertexShader, kFragmentShaderRectangle);
639 assertNoGLError();
642 void 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"
652 " gl_FragColor = texture2D(s_texture, v_texCoord); \n"
653 "}";
654 shader_external_oes_ = CreateProgram(kVertexShader, kFragmentShaderExternal);
655 assertNoGLError();
658 Shader MyInstance::CreateProgram(const char* vertex_shader,
659 const char* fragment_shader) {
660 Shader shader;
662 // Create shader program.
663 shader.program = gles2_if_->CreateProgram(context_->pp_resource());
664 CreateShader(
665 shader.program, GL_VERTEX_SHADER, vertex_shader,
666 static_cast<uint32_t>(strlen(vertex_shader)));
667 CreateShader(shader.program,
668 GL_FRAGMENT_SHADER,
669 fragment_shader,
670 static_cast<int>(strlen(fragment_shader)));
671 gles2_if_->LinkProgram(context_->pp_resource(), shader.program);
672 gles2_if_->UseProgram(context_->pp_resource(), shader.program);
673 gles2_if_->Uniform1i(
674 context_->pp_resource(),
675 gles2_if_->GetUniformLocation(
676 context_->pp_resource(), shader.program, "s_texture"),
678 assertNoGLError();
680 shader.texcoord_scale_location = gles2_if_->GetUniformLocation(
681 context_->pp_resource(), shader.program, "v_scale");
683 GLint pos_location = gles2_if_->GetAttribLocation(
684 context_->pp_resource(), shader.program, "a_position");
685 GLint tc_location = gles2_if_->GetAttribLocation(
686 context_->pp_resource(), shader.program, "a_texCoord");
687 assertNoGLError();
689 gles2_if_->EnableVertexAttribArray(context_->pp_resource(), pos_location);
690 gles2_if_->VertexAttribPointer(
691 context_->pp_resource(), pos_location, 2, GL_FLOAT, GL_FALSE, 0, 0);
692 gles2_if_->EnableVertexAttribArray(context_->pp_resource(), tc_location);
693 gles2_if_->VertexAttribPointer(
694 context_->pp_resource(),
695 tc_location,
697 GL_FLOAT,
698 GL_FALSE,
700 static_cast<float*>(0) + 8); // Skip position coordinates.
702 gles2_if_->UseProgram(context_->pp_resource(), 0);
703 assertNoGLError();
704 return shader;
707 void MyInstance::CreateShader(GLuint program,
708 GLenum type,
709 const char* source,
710 int size) {
711 GLuint shader = gles2_if_->CreateShader(context_->pp_resource(), type);
712 gles2_if_->ShaderSource(context_->pp_resource(), shader, 1, &source, &size);
713 gles2_if_->CompileShader(context_->pp_resource(), shader);
714 gles2_if_->AttachShader(context_->pp_resource(), program, shader);
715 gles2_if_->DeleteShader(context_->pp_resource(), shader);
718 // This object is the global object representing this plugin library as long
719 // as it is loaded.
720 class MyModule : public pp::Module {
721 public:
722 MyModule() : pp::Module() {}
723 virtual ~MyModule() {}
725 virtual pp::Instance* CreateInstance(PP_Instance instance) {
726 return new MyInstance(instance, this);
730 } // anonymous namespace
732 namespace pp {
733 // Factory function for your specialization of the Module object.
734 Module* CreateModule() {
735 return new MyModule();
737 } // namespace pp