Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / native_client_sdk / src / examples / api / video_decode / video_decode.cc
blob9664c9b6407ddb85969923a90fe9c5d87d6440e1
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 <GLES2/gl2.h>
6 #include <GLES2/gl2ext.h>
7 #include <stdio.h>
8 #include <string.h>
10 #include <iostream>
11 #include <queue>
12 #include <sstream>
14 #include "ppapi/c/pp_errors.h"
15 #include "ppapi/c/ppb_console.h"
16 #include "ppapi/c/ppb_opengles2.h"
17 #include "ppapi/cpp/graphics_3d.h"
18 #include "ppapi/cpp/graphics_3d_client.h"
19 #include "ppapi/cpp/input_event.h"
20 #include "ppapi/cpp/instance.h"
21 #include "ppapi/cpp/module.h"
22 #include "ppapi/cpp/rect.h"
23 #include "ppapi/cpp/var.h"
24 #include "ppapi/cpp/video_decoder.h"
25 #include "ppapi/utility/completion_callback_factory.h"
27 // VP8 is more likely to work on different versions of Chrome. Undefine this
28 // to decode H264.
29 #define USE_VP8_TESTDATA_INSTEAD_OF_H264
30 #include "testdata.h"
32 // Use assert as a poor-man's CHECK, even in non-debug mode.
33 // Since <assert.h> redefines assert on every inclusion (it doesn't use
34 // include-guards), make sure this is the last file #include'd in this file.
35 #undef NDEBUG
36 #include <assert.h>
38 // Assert |context_| isn't holding any GL Errors. Done as a macro instead of a
39 // function to preserve line number information in the failure message.
40 #define assertNoGLError() assert(!gles2_if_->GetError(context_->pp_resource()));
42 namespace {
44 struct Shader {
45 Shader() : program(0), texcoord_scale_location(0) {}
46 ~Shader() {}
48 GLuint program;
49 GLint texcoord_scale_location;
52 class Decoder;
53 class MyInstance;
55 struct PendingPicture {
56 PendingPicture(Decoder* decoder, const PP_VideoPicture& picture)
57 : decoder(decoder), picture(picture) {}
58 ~PendingPicture() {}
60 Decoder* decoder;
61 PP_VideoPicture picture;
64 class MyInstance : public pp::Instance, public pp::Graphics3DClient {
65 public:
66 MyInstance(PP_Instance instance, pp::Module* module);
67 virtual ~MyInstance();
69 // pp::Instance implementation.
70 virtual void DidChangeView(const pp::Rect& position,
71 const pp::Rect& clip_ignored);
72 virtual bool HandleInputEvent(const pp::InputEvent& event);
74 // pp::Graphics3DClient implementation.
75 virtual void Graphics3DContextLost() {
76 // TODO(vrk/fischman): Properly reset after a lost graphics context. In
77 // particular need to delete context_ and re-create textures.
78 // Probably have to recreate the decoder from scratch, because old textures
79 // can still be outstanding in the decoder!
80 assert(false && "Unexpectedly lost graphics context");
83 void PaintPicture(Decoder* decoder, const PP_VideoPicture& picture);
85 private:
86 // Log an error to the developer console and stderr by creating a temporary
87 // object of this type and streaming to it. Example usage:
88 // LogError(this).s() << "Hello world: " << 42;
89 class LogError {
90 public:
91 LogError(MyInstance* instance) : instance_(instance) {}
92 ~LogError() {
93 const std::string& msg = stream_.str();
94 instance_->console_if_->Log(
95 instance_->pp_instance(), PP_LOGLEVEL_ERROR, pp::Var(msg).pp_var());
96 std::cerr << msg << std::endl;
98 // Impl note: it would have been nicer to have LogError derive from
99 // std::ostringstream so that it can be streamed to directly, but lookup
100 // rules turn streamed string literals to hex pointers on output.
101 std::ostringstream& s() { return stream_; }
103 private:
104 MyInstance* instance_;
105 std::ostringstream stream_;
108 void InitializeDecoders();
110 // GL-related functions.
111 void InitGL();
112 void CreateGLObjects();
113 void Create2DProgramOnce();
114 void CreateRectangleARBProgramOnce();
115 void CreateExternalOESProgramOnce();
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);
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_;
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_;
134 // Unowned pointers.
135 const PPB_Console* console_if_;
136 const PPB_Core* core_if_;
137 const PPB_OpenGLES2* gles2_if_;
139 // Owned data.
140 pp::Graphics3D* context_;
141 typedef std::vector<Decoder*> DecoderList;
142 DecoderList video_decoders_;
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 // Shader program to draw GL_TEXTURE_EXTERNAL_OES target.
149 Shader shader_external_oes_;
152 class Decoder {
153 public:
154 Decoder(MyInstance* instance, int id, const pp::Graphics3D& graphics_3d);
155 ~Decoder();
157 int id() const { return id_; }
158 bool flushing() const { return flushing_; }
159 bool resetting() const { return resetting_; }
161 void Reset();
162 void RecyclePicture(const PP_VideoPicture& picture);
164 PP_TimeTicks GetAverageLatency() {
165 return num_pictures_ ? total_latency_ / num_pictures_ : 0;
168 private:
169 void InitializeDone(int32_t result);
170 void Start();
171 void DecodeNextFrame();
172 void DecodeDone(int32_t result);
173 void PictureReady(int32_t result, PP_VideoPicture picture);
174 void FlushDone(int32_t result);
175 void ResetDone(int32_t result);
177 MyInstance* instance_;
178 int id_;
180 pp::VideoDecoder* decoder_;
181 pp::CompletionCallbackFactory<Decoder> callback_factory_;
183 size_t encoded_data_next_pos_to_decode_;
184 int next_picture_id_;
185 bool flushing_;
186 bool resetting_;
188 const PPB_Core* core_if_;
189 static const int kMaxDecodeDelay = 128;
190 PP_TimeTicks decode_time_[kMaxDecodeDelay];
191 PP_TimeTicks total_latency_;
192 int num_pictures_;
195 #if defined USE_VP8_TESTDATA_INSTEAD_OF_H264
197 // VP8 is stored in an IVF container.
198 // Helpful description: http://wiki.multimedia.cx/index.php?title=IVF
200 static void GetNextFrame(size_t* start_pos, size_t* end_pos) {
201 size_t current_pos = *start_pos;
202 if (current_pos == 0)
203 current_pos = 32; // Skip stream header.
204 uint32_t frame_size = kData[current_pos] + (kData[current_pos + 1] << 8) +
205 (kData[current_pos + 2] << 16) +
206 (kData[current_pos + 3] << 24);
207 current_pos += 12; // Skip frame header.
208 *start_pos = current_pos;
209 *end_pos = current_pos + frame_size;
212 #else // !USE_VP8_TESTDATA_INSTEAD_OF_H264
214 // Returns true if the current position is at the start of a NAL unit.
215 static bool LookingAtNAL(const unsigned char* encoded, size_t pos) {
216 // H264 frames start with 0, 0, 0, 1 in our test data.
217 return pos + 3 < kDataLen && encoded[pos] == 0 && encoded[pos + 1] == 0 &&
218 encoded[pos + 2] == 0 && encoded[pos + 3] == 1;
221 static void GetNextFrame(size_t* start_pos, size_t* end_pos) {
222 assert(LookingAtNAL(kData, *start_pos));
223 *end_pos = *start_pos;
224 *end_pos += 4;
225 while (*end_pos < kDataLen && !LookingAtNAL(kData, *end_pos)) {
226 ++*end_pos;
230 #endif // USE_VP8_TESTDATA_INSTEAD_OF_H264
232 Decoder::Decoder(MyInstance* instance,
233 int id,
234 const pp::Graphics3D& graphics_3d)
235 : instance_(instance),
236 id_(id),
237 decoder_(new pp::VideoDecoder(instance)),
238 callback_factory_(this),
239 encoded_data_next_pos_to_decode_(0),
240 next_picture_id_(0),
241 flushing_(false),
242 resetting_(false),
243 total_latency_(0.0),
244 num_pictures_(0) {
245 core_if_ = static_cast<const PPB_Core*>(
246 pp::Module::Get()->GetBrowserInterface(PPB_CORE_INTERFACE));
248 #if defined USE_VP8_TESTDATA_INSTEAD_OF_H264
249 const PP_VideoProfile kBitstreamProfile = PP_VIDEOPROFILE_VP8_ANY;
250 #else
251 const PP_VideoProfile kBitstreamProfile = PP_VIDEOPROFILE_H264MAIN;
252 #endif
254 assert(!decoder_->is_null());
255 decoder_->Initialize(graphics_3d,
256 kBitstreamProfile,
257 PP_HARDWAREACCELERATION_WITHFALLBACK,
258 callback_factory_.NewCallback(&Decoder::InitializeDone));
261 Decoder::~Decoder() {
262 delete decoder_;
265 void Decoder::InitializeDone(int32_t result) {
266 assert(decoder_);
267 assert(result == PP_OK);
268 Start();
271 void Decoder::Start() {
272 assert(decoder_);
274 encoded_data_next_pos_to_decode_ = 0;
276 // Register callback to get the first picture. We call GetPicture again in
277 // PictureReady to continuously receive pictures as they're decoded.
278 decoder_->GetPicture(
279 callback_factory_.NewCallbackWithOutput(&Decoder::PictureReady));
281 // Start the decode loop.
282 DecodeNextFrame();
285 void Decoder::Reset() {
286 assert(decoder_);
287 assert(!resetting_);
288 resetting_ = true;
289 decoder_->Reset(callback_factory_.NewCallback(&Decoder::ResetDone));
292 void Decoder::RecyclePicture(const PP_VideoPicture& picture) {
293 assert(decoder_);
294 decoder_->RecyclePicture(picture);
297 void Decoder::DecodeNextFrame() {
298 assert(decoder_);
299 if (encoded_data_next_pos_to_decode_ <= kDataLen) {
300 // If we've just reached the end of the bitstream, flush and wait.
301 if (!flushing_ && encoded_data_next_pos_to_decode_ == kDataLen) {
302 flushing_ = true;
303 decoder_->Flush(callback_factory_.NewCallback(&Decoder::FlushDone));
304 return;
307 // Find the start of the next frame.
308 size_t start_pos = encoded_data_next_pos_to_decode_;
309 size_t end_pos;
310 GetNextFrame(&start_pos, &end_pos);
311 encoded_data_next_pos_to_decode_ = end_pos;
312 // Decode the frame. On completion, DecodeDone will call DecodeNextFrame
313 // to implement a decode loop.
314 uint32_t size = static_cast<uint32_t>(end_pos - start_pos);
315 decode_time_[next_picture_id_ % kMaxDecodeDelay] = core_if_->GetTimeTicks();
316 decoder_->Decode(next_picture_id_++,
317 size,
318 kData + start_pos,
319 callback_factory_.NewCallback(&Decoder::DecodeDone));
323 void Decoder::DecodeDone(int32_t result) {
324 assert(decoder_);
325 // Break out of the decode loop on abort.
326 if (result == PP_ERROR_ABORTED)
327 return;
328 assert(result == PP_OK);
329 if (!flushing_ && !resetting_)
330 DecodeNextFrame();
333 void Decoder::PictureReady(int32_t result, PP_VideoPicture picture) {
334 assert(decoder_);
335 // Break out of the get picture loop on abort.
336 if (result == PP_ERROR_ABORTED)
337 return;
338 assert(result == PP_OK);
340 num_pictures_++;
341 PP_TimeTicks latency = core_if_->GetTimeTicks() -
342 decode_time_[picture.decode_id % kMaxDecodeDelay];
343 total_latency_ += latency;
345 decoder_->GetPicture(
346 callback_factory_.NewCallbackWithOutput(&Decoder::PictureReady));
347 instance_->PaintPicture(this, picture);
350 void Decoder::FlushDone(int32_t result) {
351 assert(decoder_);
352 assert(result == PP_OK || result == PP_ERROR_ABORTED);
353 assert(flushing_);
354 flushing_ = false;
357 void Decoder::ResetDone(int32_t result) {
358 assert(decoder_);
359 assert(result == PP_OK);
360 assert(resetting_);
361 resetting_ = false;
363 Start();
366 MyInstance::MyInstance(PP_Instance instance, pp::Module* module)
367 : pp::Instance(instance),
368 pp::Graphics3DClient(this),
369 is_painting_(false),
370 num_frames_rendered_(0),
371 first_frame_delivered_ticks_(-1),
372 last_swap_request_ticks_(-1),
373 swap_ticks_(0),
374 callback_factory_(this),
375 context_(NULL) {
376 console_if_ = static_cast<const PPB_Console*>(
377 pp::Module::Get()->GetBrowserInterface(PPB_CONSOLE_INTERFACE));
378 core_if_ = static_cast<const PPB_Core*>(
379 pp::Module::Get()->GetBrowserInterface(PPB_CORE_INTERFACE));
380 gles2_if_ = static_cast<const PPB_OpenGLES2*>(
381 pp::Module::Get()->GetBrowserInterface(PPB_OPENGLES2_INTERFACE));
383 RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE);
386 MyInstance::~MyInstance() {
387 if (!context_)
388 return;
390 PP_Resource graphics_3d = context_->pp_resource();
391 if (shader_2d_.program)
392 gles2_if_->DeleteProgram(graphics_3d, shader_2d_.program);
393 if (shader_rectangle_arb_.program)
394 gles2_if_->DeleteProgram(graphics_3d, shader_rectangle_arb_.program);
395 if (shader_external_oes_.program)
396 gles2_if_->DeleteProgram(graphics_3d, shader_external_oes_.program);
398 for (DecoderList::iterator it = video_decoders_.begin();
399 it != video_decoders_.end();
400 ++it)
401 delete *it;
403 delete context_;
406 void MyInstance::DidChangeView(const pp::Rect& position,
407 const pp::Rect& clip_ignored) {
408 if (position.width() == 0 || position.height() == 0)
409 return;
410 if (plugin_size_.width()) {
411 assert(position.size() == plugin_size_);
412 return;
414 plugin_size_ = position.size();
416 // Initialize graphics.
417 InitGL();
418 InitializeDecoders();
421 bool MyInstance::HandleInputEvent(const pp::InputEvent& event) {
422 switch (event.GetType()) {
423 case PP_INPUTEVENT_TYPE_MOUSEDOWN: {
424 pp::MouseInputEvent mouse_event(event);
425 // Reset all decoders on mouse down.
426 if (mouse_event.GetButton() == PP_INPUTEVENT_MOUSEBUTTON_LEFT) {
427 // Reset decoders.
428 for (size_t i = 0; i < video_decoders_.size(); i++) {
429 if (!video_decoders_[i]->resetting())
430 video_decoders_[i]->Reset();
433 return true;
436 default:
437 return false;
441 void MyInstance::InitializeDecoders() {
442 assert(video_decoders_.empty());
443 // Create two decoders with ids 0 and 1.
444 video_decoders_.push_back(new Decoder(this, 0, *context_));
445 video_decoders_.push_back(new Decoder(this, 1, *context_));
448 void MyInstance::PaintPicture(Decoder* decoder,
449 const PP_VideoPicture& picture) {
450 if (first_frame_delivered_ticks_ == -1)
451 assert((first_frame_delivered_ticks_ = core_if_->GetTimeTicks()) != -1);
453 pending_pictures_.push(PendingPicture(decoder, picture));
454 if (!is_painting_)
455 PaintNextPicture();
458 void MyInstance::PaintNextPicture() {
459 assert(!is_painting_);
460 is_painting_ = true;
462 const PendingPicture& next = pending_pictures_.front();
463 Decoder* decoder = next.decoder;
464 const PP_VideoPicture& picture = next.picture;
466 int x = 0;
467 int y = 0;
468 int half_width = plugin_size_.width() / 2;
469 int half_height = plugin_size_.height() / 2;
470 if (decoder->id() != 0) {
471 x = half_width;
472 y = half_height;
475 PP_Resource graphics_3d = context_->pp_resource();
476 if (picture.texture_target == GL_TEXTURE_2D) {
477 Create2DProgramOnce();
478 gles2_if_->UseProgram(graphics_3d, shader_2d_.program);
479 gles2_if_->Uniform2f(
480 graphics_3d, shader_2d_.texcoord_scale_location, 1.0, 1.0);
481 } else if (picture.texture_target == GL_TEXTURE_RECTANGLE_ARB) {
482 CreateRectangleARBProgramOnce();
483 gles2_if_->UseProgram(graphics_3d, shader_rectangle_arb_.program);
484 gles2_if_->Uniform2f(graphics_3d,
485 shader_rectangle_arb_.texcoord_scale_location,
486 picture.texture_size.width,
487 picture.texture_size.height);
488 } else {
489 assert(picture.texture_target == GL_TEXTURE_EXTERNAL_OES);
490 CreateExternalOESProgramOnce();
491 gles2_if_->UseProgram(graphics_3d, shader_external_oes_.program);
492 gles2_if_->Uniform2f(
493 graphics_3d, shader_external_oes_.texcoord_scale_location, 1.0, 1.0);
496 gles2_if_->Viewport(graphics_3d, x, y, half_width, half_height);
497 gles2_if_->ActiveTexture(graphics_3d, GL_TEXTURE0);
498 gles2_if_->BindTexture(
499 graphics_3d, picture.texture_target, picture.texture_id);
500 gles2_if_->DrawArrays(graphics_3d, GL_TRIANGLE_STRIP, 0, 4);
502 gles2_if_->UseProgram(graphics_3d, 0);
504 last_swap_request_ticks_ = core_if_->GetTimeTicks();
505 context_->SwapBuffers(
506 callback_factory_.NewCallback(&MyInstance::PaintFinished));
509 void MyInstance::PaintFinished(int32_t result) {
510 assert(result == PP_OK);
511 swap_ticks_ += core_if_->GetTimeTicks() - last_swap_request_ticks_;
512 is_painting_ = false;
513 ++num_frames_rendered_;
514 if (num_frames_rendered_ % 50 == 0) {
515 double elapsed = core_if_->GetTimeTicks() - first_frame_delivered_ticks_;
516 double fps = (elapsed > 0) ? num_frames_rendered_ / elapsed : 1000;
517 double ms_per_swap = (swap_ticks_ * 1e3) / num_frames_rendered_;
518 double secs_average_latency = 0;
519 for (DecoderList::iterator it = video_decoders_.begin();
520 it != video_decoders_.end();
521 ++it)
522 secs_average_latency += (*it)->GetAverageLatency();
523 secs_average_latency /= video_decoders_.size();
524 double ms_average_latency = 1000 * secs_average_latency;
525 LogError(this).s() << "Rendered frames: " << num_frames_rendered_
526 << ", fps: " << fps
527 << ", with average ms/swap of: " << ms_per_swap
528 << ", with average latency (ms) of: "
529 << ms_average_latency;
532 // If the decoders were reset, this will be empty.
533 if (pending_pictures_.empty())
534 return;
536 const PendingPicture& next = pending_pictures_.front();
537 Decoder* decoder = next.decoder;
538 const PP_VideoPicture& picture = next.picture;
539 decoder->RecyclePicture(picture);
540 pending_pictures_.pop();
542 // Keep painting as long as we have pictures.
543 if (!pending_pictures_.empty())
544 PaintNextPicture();
547 void MyInstance::InitGL() {
548 assert(plugin_size_.width() && plugin_size_.height());
549 is_painting_ = false;
551 assert(!context_);
552 int32_t context_attributes[] = {
553 PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8,
554 PP_GRAPHICS3DATTRIB_BLUE_SIZE, 8,
555 PP_GRAPHICS3DATTRIB_GREEN_SIZE, 8,
556 PP_GRAPHICS3DATTRIB_RED_SIZE, 8,
557 PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 0,
558 PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 0,
559 PP_GRAPHICS3DATTRIB_SAMPLES, 0,
560 PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0,
561 PP_GRAPHICS3DATTRIB_WIDTH, plugin_size_.width(),
562 PP_GRAPHICS3DATTRIB_HEIGHT, plugin_size_.height(),
563 PP_GRAPHICS3DATTRIB_NONE,
565 context_ = new pp::Graphics3D(this, context_attributes);
566 assert(!context_->is_null());
567 assert(BindGraphics(*context_));
569 // Clear color bit.
570 gles2_if_->ClearColor(context_->pp_resource(), 1, 0, 0, 1);
571 gles2_if_->Clear(context_->pp_resource(), GL_COLOR_BUFFER_BIT);
573 assertNoGLError();
575 CreateGLObjects();
578 void MyInstance::CreateGLObjects() {
579 // Assign vertex positions and texture coordinates to buffers for use in
580 // shader program.
581 static const float kVertices[] = {
582 -1, -1, -1, 1, 1, -1, 1, 1, // Position coordinates.
583 0, 1, 0, 0, 1, 1, 1, 0, // Texture coordinates.
586 GLuint buffer;
587 gles2_if_->GenBuffers(context_->pp_resource(), 1, &buffer);
588 gles2_if_->BindBuffer(context_->pp_resource(), GL_ARRAY_BUFFER, buffer);
590 gles2_if_->BufferData(context_->pp_resource(),
591 GL_ARRAY_BUFFER,
592 sizeof(kVertices),
593 kVertices,
594 GL_STATIC_DRAW);
595 assertNoGLError();
598 static const char kVertexShader[] =
599 "varying vec2 v_texCoord; \n"
600 "attribute vec4 a_position; \n"
601 "attribute vec2 a_texCoord; \n"
602 "uniform vec2 v_scale; \n"
603 "void main() \n"
604 "{ \n"
605 " v_texCoord = v_scale * a_texCoord; \n"
606 " gl_Position = a_position; \n"
607 "}";
609 void MyInstance::Create2DProgramOnce() {
610 if (shader_2d_.program)
611 return;
612 static const char kFragmentShader2D[] =
613 "precision mediump float; \n"
614 "varying vec2 v_texCoord; \n"
615 "uniform sampler2D s_texture; \n"
616 "void main() \n"
618 " gl_FragColor = texture2D(s_texture, v_texCoord); \n"
619 "}";
620 shader_2d_ = CreateProgram(kVertexShader, kFragmentShader2D);
621 assertNoGLError();
624 void MyInstance::CreateRectangleARBProgramOnce() {
625 if (shader_rectangle_arb_.program)
626 return;
627 static const char kFragmentShaderRectangle[] =
628 "#extension GL_ARB_texture_rectangle : require\n"
629 "precision mediump float; \n"
630 "varying vec2 v_texCoord; \n"
631 "uniform sampler2DRect s_texture; \n"
632 "void main() \n"
634 " gl_FragColor = texture2DRect(s_texture, v_texCoord).rgba; \n"
635 "}";
636 shader_rectangle_arb_ =
637 CreateProgram(kVertexShader, kFragmentShaderRectangle);
638 assertNoGLError();
641 void MyInstance::CreateExternalOESProgramOnce() {
642 if (shader_external_oes_.program)
643 return;
644 static const char kFragmentShaderExternal[] =
645 "#extension GL_OES_EGL_image_external : require\n"
646 "precision mediump float; \n"
647 "varying vec2 v_texCoord; \n"
648 "uniform samplerExternalOES s_texture; \n"
649 "void main() \n"
651 " gl_FragColor = texture2D(s_texture, v_texCoord); \n"
652 "}";
653 shader_external_oes_ = CreateProgram(kVertexShader, kFragmentShaderExternal);
654 assertNoGLError();
657 Shader MyInstance::CreateProgram(const char* vertex_shader,
658 const char* fragment_shader) {
659 Shader shader;
661 // Create shader program.
662 shader.program = gles2_if_->CreateProgram(context_->pp_resource());
663 CreateShader(
664 shader.program, GL_VERTEX_SHADER, vertex_shader, strlen(vertex_shader));
665 CreateShader(shader.program,
666 GL_FRAGMENT_SHADER,
667 fragment_shader,
668 strlen(fragment_shader));
669 gles2_if_->LinkProgram(context_->pp_resource(), shader.program);
670 gles2_if_->UseProgram(context_->pp_resource(), shader.program);
671 gles2_if_->Uniform1i(
672 context_->pp_resource(),
673 gles2_if_->GetUniformLocation(
674 context_->pp_resource(), shader.program, "s_texture"),
676 assertNoGLError();
678 shader.texcoord_scale_location = gles2_if_->GetUniformLocation(
679 context_->pp_resource(), shader.program, "v_scale");
681 GLint pos_location = gles2_if_->GetAttribLocation(
682 context_->pp_resource(), shader.program, "a_position");
683 GLint tc_location = gles2_if_->GetAttribLocation(
684 context_->pp_resource(), shader.program, "a_texCoord");
685 assertNoGLError();
687 gles2_if_->EnableVertexAttribArray(context_->pp_resource(), pos_location);
688 gles2_if_->VertexAttribPointer(
689 context_->pp_resource(), pos_location, 2, GL_FLOAT, GL_FALSE, 0, 0);
690 gles2_if_->EnableVertexAttribArray(context_->pp_resource(), tc_location);
691 gles2_if_->VertexAttribPointer(
692 context_->pp_resource(),
693 tc_location,
695 GL_FLOAT,
696 GL_FALSE,
698 static_cast<float*>(0) + 8); // Skip position coordinates.
700 gles2_if_->UseProgram(context_->pp_resource(), 0);
701 assertNoGLError();
702 return shader;
705 void MyInstance::CreateShader(GLuint program,
706 GLenum type,
707 const char* source,
708 int size) {
709 GLuint shader = gles2_if_->CreateShader(context_->pp_resource(), type);
710 gles2_if_->ShaderSource(context_->pp_resource(), shader, 1, &source, &size);
711 gles2_if_->CompileShader(context_->pp_resource(), shader);
712 gles2_if_->AttachShader(context_->pp_resource(), program, shader);
713 gles2_if_->DeleteShader(context_->pp_resource(), shader);
716 // This object is the global object representing this plugin library as long
717 // as it is loaded.
718 class MyModule : public pp::Module {
719 public:
720 MyModule() : pp::Module() {}
721 virtual ~MyModule() {}
723 virtual pp::Instance* CreateInstance(PP_Instance instance) {
724 return new MyInstance(instance, this);
728 } // anonymous namespace
730 namespace pp {
731 // Factory function for your specialization of the Module object.
732 Module* CreateModule() {
733 return new MyModule();
735 } // namespace pp