Infobar material design refresh: bg color
[chromium-blink-merge.git] / ppapi / examples / video_decode / video_decode.cc
blobe15ef42f82a730f52550e303502ef29e24cea9e0
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,
260 callback_factory_.NewCallback(&Decoder::InitializeDone));
263 Decoder::~Decoder() {
264 delete decoder_;
267 void Decoder::InitializeDone(int32_t result) {
268 assert(decoder_);
269 assert(result == PP_OK);
270 Start();
273 void Decoder::Start() {
274 assert(decoder_);
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.
284 DecodeNextFrame();
287 void Decoder::Reset() {
288 assert(decoder_);
289 assert(!resetting_);
290 resetting_ = true;
291 decoder_->Reset(callback_factory_.NewCallback(&Decoder::ResetDone));
294 void Decoder::RecyclePicture(const PP_VideoPicture& picture) {
295 assert(decoder_);
296 decoder_->RecyclePicture(picture);
299 void Decoder::DecodeNextFrame() {
300 assert(decoder_);
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) {
304 flushing_ = true;
305 decoder_->Flush(callback_factory_.NewCallback(&Decoder::FlushDone));
306 return;
309 // Find the start of the next frame.
310 size_t start_pos = encoded_data_next_pos_to_decode_;
311 size_t end_pos;
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_++,
319 size,
320 kData + start_pos,
321 callback_factory_.NewCallback(&Decoder::DecodeDone));
325 void Decoder::DecodeDone(int32_t result) {
326 assert(decoder_);
327 // Break out of the decode loop on abort.
328 if (result == PP_ERROR_ABORTED)
329 return;
330 assert(result == PP_OK);
331 if (!flushing_ && !resetting_)
332 DecodeNextFrame();
335 void Decoder::PictureReady(int32_t result, PP_VideoPicture picture) {
336 assert(decoder_);
337 // Break out of the get picture loop on abort.
338 if (result == PP_ERROR_ABORTED)
339 return;
340 assert(result == PP_OK);
342 num_pictures_++;
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) {
353 assert(decoder_);
354 assert(result == PP_OK || result == PP_ERROR_ABORTED);
355 assert(flushing_);
356 flushing_ = false;
359 void Decoder::ResetDone(int32_t result) {
360 assert(decoder_);
361 assert(result == PP_OK);
362 assert(resetting_);
363 resetting_ = false;
365 Start();
368 MyInstance::MyInstance(PP_Instance instance, pp::Module* module)
369 : pp::Instance(instance),
370 pp::Graphics3DClient(this),
371 is_painting_(false),
372 num_frames_rendered_(0),
373 first_frame_delivered_ticks_(-1),
374 last_swap_request_ticks_(-1),
375 swap_ticks_(0),
376 callback_factory_(this),
377 context_(NULL) {
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() {
389 if (!context_)
390 return;
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();
402 ++it)
403 delete *it;
405 delete context_;
408 void MyInstance::DidChangeView(const pp::Rect& position,
409 const pp::Rect& clip_ignored) {
410 if (position.width() == 0 || position.height() == 0)
411 return;
412 if (plugin_size_.width()) {
413 assert(position.size() == plugin_size_);
414 return;
416 plugin_size_ = position.size();
418 // Initialize graphics.
419 InitGL();
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) {
429 // Reset decoders.
430 for (size_t i = 0; i < video_decoders_.size(); i++) {
431 if (!video_decoders_[i]->resetting())
432 video_decoders_[i]->Reset();
435 return true;
438 default:
439 return false;
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));
456 if (!is_painting_)
457 PaintNextPicture();
460 void MyInstance::PaintNextPicture() {
461 assert(!is_painting_);
462 is_painting_ = true;
464 const PendingPicture& next = pending_pictures_.front();
465 Decoder* decoder = next.decoder;
466 const PP_VideoPicture& picture = next.picture;
468 int x = 0;
469 int y = 0;
470 int half_width = plugin_size_.width() / 2;
471 int half_height = plugin_size_.height() / 2;
472 if (decoder->id() != 0) {
473 x = half_width;
474 y = half_height;
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));
490 } else {
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();
523 ++it)
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_
528 << ", fps: " << fps
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())
536 return;
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())
546 PaintNextPicture();
549 void MyInstance::InitGL() {
550 assert(plugin_size_.width() && plugin_size_.height());
551 is_painting_ = false;
553 assert(!context_);
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_));
571 // Clear color bit.
572 gles2_if_->ClearColor(context_->pp_resource(), 1, 0, 0, 1);
573 gles2_if_->Clear(context_->pp_resource(), GL_COLOR_BUFFER_BIT);
575 assertNoGLError();
577 CreateGLObjects();
580 void MyInstance::CreateGLObjects() {
581 // Assign vertex positions and texture coordinates to buffers for use in
582 // shader program.
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.
588 GLuint buffer;
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(),
593 GL_ARRAY_BUFFER,
594 sizeof(kVertices),
595 kVertices,
596 GL_STATIC_DRAW);
597 assertNoGLError();
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"
605 "void main() \n"
606 "{ \n"
607 " v_texCoord = v_scale * a_texCoord; \n"
608 " gl_Position = a_position; \n"
609 "}";
611 void MyInstance::Create2DProgramOnce() {
612 if (shader_2d_.program)
613 return;
614 static const char kFragmentShader2D[] =
615 "precision mediump float; \n"
616 "varying vec2 v_texCoord; \n"
617 "uniform sampler2D s_texture; \n"
618 "void main() \n"
620 " gl_FragColor = texture2D(s_texture, v_texCoord); \n"
621 "}";
622 shader_2d_ = CreateProgram(kVertexShader, kFragmentShader2D);
623 assertNoGLError();
626 void MyInstance::CreateRectangleARBProgramOnce() {
627 if (shader_rectangle_arb_.program)
628 return;
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"
634 "void main() \n"
636 " gl_FragColor = texture2DRect(s_texture, v_texCoord).rgba; \n"
637 "}";
638 shader_rectangle_arb_ =
639 CreateProgram(kVertexShader, kFragmentShaderRectangle);
640 assertNoGLError();
643 void MyInstance::CreateExternalOESProgramOnce() {
644 if (shader_external_oes_.program)
645 return;
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"
651 "void main() \n"
653 " gl_FragColor = texture2D(s_texture, v_texCoord); \n"
654 "}";
655 shader_external_oes_ = CreateProgram(kVertexShader, kFragmentShaderExternal);
656 assertNoGLError();
659 Shader MyInstance::CreateProgram(const char* vertex_shader,
660 const char* fragment_shader) {
661 Shader shader;
663 // Create shader program.
664 shader.program = gles2_if_->CreateProgram(context_->pp_resource());
665 CreateShader(
666 shader.program, GL_VERTEX_SHADER, vertex_shader,
667 static_cast<uint32_t>(strlen(vertex_shader)));
668 CreateShader(shader.program,
669 GL_FRAGMENT_SHADER,
670 fragment_shader,
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"),
679 assertNoGLError();
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");
688 assertNoGLError();
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(),
696 tc_location,
698 GL_FLOAT,
699 GL_FALSE,
701 static_cast<float*>(0) + 8); // Skip position coordinates.
703 gles2_if_->UseProgram(context_->pp_resource(), 0);
704 assertNoGLError();
705 return shader;
708 void MyInstance::CreateShader(GLuint program,
709 GLenum type,
710 const char* source,
711 int size) {
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
720 // as it is loaded.
721 class MyModule : public pp::Module {
722 public:
723 MyModule() : pp::Module() {}
724 virtual ~MyModule() {}
726 virtual pp::Instance* CreateInstance(PP_Instance instance) {
727 return new MyInstance(instance, this);
731 } // anonymous namespace
733 namespace pp {
734 // Factory function for your specialization of the Module object.
735 Module* CreateModule() {
736 return new MyModule();
738 } // namespace pp