1// Copyright 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// Needed on Windows to get |M_PI| from math.h. 6#ifdef _WIN32 7#define _USE_MATH_DEFINES 8#endif 9 10#include <math.h> 11 12#include <vector> 13 14#include "ppapi/c/pp_errors.h" 15#include "ppapi/c/pp_input_event.h" 16#include "ppapi/cpp/compositor.h" 17#include "ppapi/cpp/compositor_layer.h" 18#include "ppapi/cpp/graphics_3d.h" 19#include "ppapi/cpp/graphics_3d_client.h" 20#include "ppapi/cpp/image_data.h" 21#include "ppapi/cpp/input_event.h" 22#include "ppapi/cpp/instance.h" 23#include "ppapi/cpp/module.h" 24#include "ppapi/cpp/rect.h" 25#include "ppapi/cpp/var_dictionary.h" 26#include "ppapi/examples/compositor/spinning_cube.h" 27#include "ppapi/lib/gl/gles2/gl2ext_ppapi.h" 28#include "ppapi/lib/gl/include/GLES2/gl2.h" 29#include "ppapi/lib/gl/include/GLES2/gl2ext.h" 30#include "ppapi/utility/completion_callback_factory.h" 31 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> 37 38// When compiling natively on Windows, PostMessage can be #define-d to 39// something else. 40#ifdef PostMessage 41#undef PostMessage 42#endif 43 44// Assert |context_| isn't holding any GL Errors. Done as a macro instead of a 45// function to preserve line number information in the failure message. 46#define AssertNoGLError() \ 47 PP_DCHECK(!glGetError()); 48 49namespace { 50 51const int32_t kTextureWidth = 800; 52const int32_t kTextureHeight = 800; 53const int32_t kImageWidth = 256; 54const int32_t kImageHeight = 256; 55 56class DemoInstance : public pp::Instance, public pp::Graphics3DClient { 57 public: 58 DemoInstance(PP_Instance instance); 59 virtual ~DemoInstance(); 60 61 // pp::Instance implementation (see PPP_Instance). 62 virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]); 63 virtual void DidChangeView(const pp::Rect& position, 64 const pp::Rect& clip); 65 virtual bool HandleInputEvent(const pp::InputEvent& event); 66 67 // pp::Graphics3DClient implementation. 68 virtual void Graphics3DContextLost(); 69 70 private: 71 // GL-related functions. 72 void InitGL(int32_t result); 73 GLuint PrepareFramebuffer(); 74 pp::ImageData PrepareImage(); 75 void PrepareLayers(int32_t frame); 76 void Paint(int32_t result, int32_t frame); 77 void OnTextureReleased(int32_t result, GLuint texture); 78 void OnImageReleased(int32_t result, const pp::ImageData& image); 79 80 pp::CompletionCallbackFactory<DemoInstance> callback_factory_; 81 82 // Owned data. 83 pp::Graphics3D* context_; 84 85 GLuint fbo_; 86 GLuint rbo_; 87 88 std::vector<GLuint> textures_; 89 std::vector<pp::ImageData> images_; 90 91 pp::Compositor compositor_; 92 pp::CompositorLayer color_layer_; 93 pp::CompositorLayer stable_texture_layer_; 94 pp::CompositorLayer texture_layer_; 95 pp::CompositorLayer image_layer_; 96 97 bool rebuild_layers_; 98 int32_t total_resource_; 99 100 SpinningCube* cube_; 101}; 102 103DemoInstance::DemoInstance(PP_Instance instance) 104 : pp::Instance(instance), 105 pp::Graphics3DClient(this), 106 callback_factory_(this), 107 context_(NULL), 108 fbo_(0), 109 rbo_(0), 110 rebuild_layers_(true), 111 total_resource_(0), 112 cube_(new SpinningCube()) { 113 RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE); 114} 115 116DemoInstance::~DemoInstance() { 117 delete cube_; 118 assert(glTerminatePPAPI()); 119 delete context_; 120} 121 122bool DemoInstance::Init(uint32_t /*argc*/, 123 const char* /*argn*/[], 124 const char* /*argv*/[]) { 125 return !!glInitializePPAPI(pp::Module::Get()->get_browser_interface()); 126} 127 128void DemoInstance::DidChangeView( 129 const pp::Rect& position, const pp::Rect& /*clip*/) { 130 if (position.width() == 0 || position.height() == 0) 131 return; 132 // Initialize graphics. 133 InitGL(0); 134} 135 136bool DemoInstance::HandleInputEvent(const pp::InputEvent& event) { 137 switch (event.GetType()) { 138 case PP_INPUTEVENT_TYPE_MOUSEDOWN: 139 rebuild_layers_ = true; 140 return true; 141 default: 142 break; 143 } 144 return false; 145} 146 147void DemoInstance::Graphics3DContextLost() { 148 fbo_ = 0; 149 rbo_ = 0; 150 rebuild_layers_ = true; 151 total_resource_ -= static_cast<int32_t>(textures_.size()); 152 textures_.clear(); 153 delete context_; 154 context_ = NULL; 155 cube_->OnGLContextLost(); 156 pp::CompletionCallback cb = callback_factory_.NewCallback( 157 &DemoInstance::InitGL); 158 pp::Module::Get()->core()->CallOnMainThread(0, cb, 0); 159} 160 161void DemoInstance::InitGL(int32_t /*result*/) { 162 if (context_) 163 return; 164 int32_t context_attributes[] = { 165 PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8, 166 PP_GRAPHICS3DATTRIB_BLUE_SIZE, 8, 167 PP_GRAPHICS3DATTRIB_GREEN_SIZE, 8, 168 PP_GRAPHICS3DATTRIB_RED_SIZE, 8, 169 PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 0, 170 PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 0, 171 PP_GRAPHICS3DATTRIB_SAMPLES, 0, 172 PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0, 173 PP_GRAPHICS3DATTRIB_WIDTH, 32, 174 PP_GRAPHICS3DATTRIB_HEIGHT, 32, 175 PP_GRAPHICS3DATTRIB_NONE, 176 }; 177 context_ = new pp::Graphics3D(this, context_attributes); 178 assert(!context_->is_null()); 179 assert(BindGraphics(compositor_)); 180 181 glSetCurrentContextPPAPI(context_->pp_resource()); 182 183 cube_->Init(kTextureWidth, kTextureHeight); 184 185 Paint(PP_OK, 0); 186} 187 188GLuint DemoInstance::PrepareFramebuffer() { 189 GLuint texture = 0; 190 if (textures_.empty()) { 191 total_resource_++; 192 // Create a texture object 193 glGenTextures(1, &texture); 194 glBindTexture(GL_TEXTURE_2D, texture); 195 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTextureWidth, kTextureHeight, 0, 196 GL_RGBA, GL_UNSIGNED_BYTE, NULL); 197 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 198 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 199 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 200 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 201 glBindTexture(GL_TEXTURE_2D, 0); 202 } else { 203 texture = textures_.back(); 204 textures_.pop_back(); 205 } 206 207 if (!rbo_) { 208 // create a renderbuffer object to store depth info 209 glGenRenderbuffers(1, &rbo_); 210 glBindRenderbuffer(GL_RENDERBUFFER, rbo_); 211 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, 212 kTextureWidth, kTextureHeight); 213 glBindRenderbuffer(GL_RENDERBUFFER, 0); 214 } 215 216 if (!fbo_) { 217 // create a framebuffer object 218 glGenFramebuffers(1, &fbo_); 219 } 220 221 glBindFramebuffer(GL_FRAMEBUFFER, fbo_); 222 223 // attach the texture to FBO color attachment point 224 glFramebufferTexture2D(GL_FRAMEBUFFER, 225 GL_COLOR_ATTACHMENT0, 226 GL_TEXTURE_2D, 227 texture, 228 0); 229 230 // attach the renderbuffer to depth attachment point 231 glFramebufferRenderbuffer(GL_FRAMEBUFFER, 232 GL_DEPTH_ATTACHMENT, 233 GL_RENDERBUFFER, 234 rbo_); 235 236 // check FBO status 237 GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); 238 assert(status == GL_FRAMEBUFFER_COMPLETE); 239 240 AssertNoGLError(); 241 return texture; 242} 243 244pp::ImageData DemoInstance::PrepareImage() { 245 if (images_.empty()) { 246 total_resource_++; 247 return pp::ImageData(this, 248 PP_IMAGEDATAFORMAT_RGBA_PREMUL, 249 pp::Size(kImageWidth, kImageHeight), 250 false); 251 } 252 pp::ImageData image = images_.back(); 253 images_.pop_back(); 254 return image; 255} 256 257void DemoInstance::Paint(int32_t result, int32_t frame) { 258 assert(result == PP_OK); 259 if (result != PP_OK || !context_) 260 return; 261 262 if (rebuild_layers_) { 263 compositor_ = pp::Compositor(this); 264 assert(BindGraphics(compositor_)); 265 color_layer_ = pp::CompositorLayer(); 266 stable_texture_layer_ = pp::CompositorLayer(); 267 texture_layer_ = pp::CompositorLayer(); 268 image_layer_ = pp::CompositorLayer(); 269 frame = 0; 270 rebuild_layers_ = false; 271 } 272 273 PrepareLayers(frame); 274 275 int32_t rv = compositor_.CommitLayers( 276 callback_factory_.NewCallback(&DemoInstance::Paint, ++frame)); 277 assert(rv == PP_OK_COMPLETIONPENDING); 278 279 pp::VarDictionary dict; 280 dict.Set("total_resource", total_resource_); 281 size_t free_resource = textures_.size() + images_.size(); 282 dict.Set("free_resource", static_cast<int32_t>(free_resource)); 283 PostMessage(dict); 284} 285 286void DemoInstance::PrepareLayers(int32_t frame) { 287 int32_t rv; 288 float factor_sin = sin(M_PI / 180 * frame); 289 float factor_cos = cos(M_PI / 180 * frame); 290 { 291 // Set the background color layer. 292 if (color_layer_.is_null()) { 293 color_layer_ = compositor_.AddLayer(); 294 assert(!color_layer_.is_null()); 295 static const float transform[16] = { 296 1.0f, 0.0f, 0.0f, 0.0f, 297 0.0f, 1.0f, 0.0f, 0.0f, 298 0.0f, 0.0f, 1.0f, 0.0f, 299 0.0f, 0.0f, 0.0f, 1.0f, 300 }; 301 rv = color_layer_.SetTransform(transform); 302 assert(rv == PP_OK); 303 } 304 rv = color_layer_.SetColor(fabs(factor_sin), 305 fabs(factor_cos), 306 fabs(factor_sin * factor_cos), 307 1.0f, 308 pp::Size(800, 600)); 309 assert(rv == PP_OK); 310 } 311 312 { 313 // Set the image layer 314 if (image_layer_.is_null()) { 315 image_layer_ = compositor_.AddLayer(); 316 assert(!image_layer_.is_null()); 317 } 318 float x = frame % 800; 319 float y = 200 - 200 * factor_sin; 320 const float transform[16] = { 321 fabsf(factor_sin) + 0.2f, 0.0f, 0.0f, 0.0f, 322 0.0f, fabsf(factor_sin) + 0.2f, 0.0f, 0.0f, 323 0.0f, 0.0f, 1.0f, 0.0f, 324 x, y, 0.0f, 1.0f, 325 }; 326 rv = image_layer_.SetTransform(transform); 327 assert(rv == PP_OK); 328 329 pp::ImageData image = PrepareImage(); 330 uint8_t *p = static_cast<uint8_t*>(image.data()); 331 for (int x = 0; x < kImageWidth; ++x) { 332 for (int y = 0; y < kImageHeight; ++y) { 333 *(p++) = frame; 334 *(p++) = frame * x; 335 *(p++) = frame * y; 336 *(p++) = 255; 337 } 338 } 339 rv = image_layer_.SetImage(image, pp::Size(kImageWidth, kImageHeight), 340 callback_factory_.NewCallback(&DemoInstance::OnImageReleased, image)); 341 assert(rv == PP_OK_COMPLETIONPENDING); 342 } 343 344 { 345 // Set the stable texture layer 346 if (stable_texture_layer_.is_null()) { 347 stable_texture_layer_ = compositor_.AddLayer(); 348 assert(!stable_texture_layer_.is_null()); 349 GLuint texture = PrepareFramebuffer(); 350 cube_->UpdateForTimeDelta(0.02f); 351 cube_->Draw(); 352 rv = stable_texture_layer_.SetTexture( 353 *context_, 354 texture, pp::Size(600, 600), 355 callback_factory_.NewCallback(&DemoInstance::OnTextureReleased, 356 texture)); 357 assert(rv == PP_OK_COMPLETIONPENDING); 358 rv = stable_texture_layer_.SetPremultipliedAlpha(PP_FALSE); 359 assert(rv == PP_OK); 360 } 361 362 int32_t delta = 200 * fabsf(factor_sin); 363 if (delta != 0) { 364 int32_t x_y = 25 + delta; 365 int32_t w_h = 650 - delta - delta; 366 rv = stable_texture_layer_.SetClipRect(pp::Rect(x_y, x_y, w_h, w_h)); 367 } else { 368 rv = stable_texture_layer_.SetClipRect(pp::Rect()); 369 } 370 assert(rv == PP_OK); 371 372 const float transform[16] = { 373 factor_cos, -factor_sin, 0.0f, 0.0f, 374 factor_sin, factor_cos, 0.0f, 0.0f, 375 0.0f, 0.0f, 1.0f, 0.0f, 376 50.0f, 50.0f, 0.0f, 1.0f, 377 }; 378 rv = stable_texture_layer_.SetTransform(transform); 379 assert(rv == PP_OK); 380 } 381 382 { 383 // Set the dynamic texture layer. 384 if (texture_layer_.is_null()) { 385 texture_layer_ = compositor_.AddLayer(); 386 assert(!texture_layer_.is_null()); 387 static const float transform[16] = { 388 1.0f, 0.0f, 0.0f, 0.0f, 389 0.0f, 1.0f, 0.0f, 0.0f, 390 0.0f, 0.0f, 1.0f, 0.0f, 391 200.0f, 0.0f, 0.0f, 1.0f, 392 }; 393 rv = texture_layer_.SetTransform(transform); 394 assert(rv == PP_OK); 395 } 396 397 GLuint texture = PrepareFramebuffer(); 398 cube_->UpdateForTimeDelta(0.02f); 399 cube_->Draw(); 400 rv = texture_layer_.SetTexture(*context_, texture, pp::Size(400, 400), 401 callback_factory_.NewCallback(&DemoInstance::OnTextureReleased, 402 texture)); 403 assert(rv == PP_OK_COMPLETIONPENDING); 404 rv = texture_layer_.SetPremultipliedAlpha(PP_FALSE); 405 assert(rv == PP_OK); 406 } 407} 408 409void DemoInstance::OnTextureReleased(int32_t result, GLuint texture) { 410 if (result == PP_OK) { 411 textures_.push_back(texture); 412 } else { 413 glDeleteTextures(1, &texture); 414 total_resource_--; 415 } 416} 417 418void DemoInstance::OnImageReleased(int32_t result, const pp::ImageData& image) { 419 if (result == PP_OK) { 420 images_.push_back(image); 421 } else { 422 total_resource_--; 423 } 424} 425 426// This object is the global object representing this plugin library as long 427// as it is loaded. 428class DemoModule : public pp::Module { 429 public: 430 DemoModule() : Module() {} 431 virtual ~DemoModule() {} 432 433 virtual pp::Instance* CreateInstance(PP_Instance instance) { 434 return new DemoInstance(instance); 435 } 436}; 437 438} // anonymous namespace 439 440namespace pp { 441// Factory function for your specialization of the Module object. 442Module* CreateModule() { 443 return new DemoModule(); 444} 445} // namespace pp 446