Index: ppapi/examples/compositor/compositor.cc |
diff --git a/ppapi/examples/compositor/compositor.cc b/ppapi/examples/compositor/compositor.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..49163a61fa29411134795146fffc440c1a49e97a |
--- /dev/null |
+++ b/ppapi/examples/compositor/compositor.cc |
@@ -0,0 +1,428 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include <math.h> |
+ |
+#include <vector> |
+ |
+#include "ppapi/c/pp_errors.h" |
+#include "ppapi/c/pp_input_event.h" |
+#include "ppapi/cpp/compositor.h" |
+#include "ppapi/cpp/compositor_layer.h" |
+#include "ppapi/cpp/graphics_3d.h" |
+#include "ppapi/cpp/graphics_3d_client.h" |
+#include "ppapi/cpp/image_data.h" |
+#include "ppapi/cpp/input_event.h" |
+#include "ppapi/cpp/instance.h" |
+#include "ppapi/cpp/module.h" |
+#include "ppapi/cpp/rect.h" |
+#include "ppapi/cpp/var_dictionary.h" |
+#include "ppapi/examples/compositor/spinning_cube.h" |
+#include "ppapi/lib/gl/gles2/gl2ext_ppapi.h" |
+#include "ppapi/lib/gl/include/GLES2/gl2.h" |
+#include "ppapi/lib/gl/include/GLES2/gl2ext.h" |
+#include "ppapi/utility/completion_callback_factory.h" |
+ |
+// Use assert as a poor-man's CHECK, even in non-debug mode. |
+// Since <assert.h> redefines assert on every inclusion (it doesn't use |
+// include-guards), make sure this is the last file #include'd in this file. |
+#undef NDEBUG |
+#include <assert.h> |
+ |
+// Assert |context_| isn't holding any GL Errors. Done as a macro instead of a |
+// function to preserve line number information in the failure message. |
+#define AssertNoGLError() \ |
+ PP_DCHECK(!glGetError()); |
+ |
+namespace { |
+ |
+const int32_t kTextureWidth = 800; |
+const int32_t kTextureHeight = 800; |
+const int32_t kImageWidth = 256; |
+const int32_t kImageHeight = 256; |
+ |
+class DemoInstance : public pp::Instance, public pp::Graphics3DClient { |
+ public: |
+ DemoInstance(PP_Instance instance); |
+ virtual ~DemoInstance(); |
+ |
+ // pp::Instance implementation (see PPP_Instance). |
+ virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]); |
+ virtual void DidChangeView(const pp::Rect& position, |
+ const pp::Rect& clip); |
+ virtual bool HandleInputEvent(const pp::InputEvent& event); |
+ |
+ // pp::Graphics3DClient implementation. |
+ virtual void Graphics3DContextLost(); |
+ |
+ private: |
+ // GL-related functions. |
+ void InitGL(int32_t result); |
+ GLuint PrepareFramebuffer(); |
+ pp::ImageData PrepareImage(); |
+ void Paint(int32_t result, int32_t frame); |
+ void OnTextureReleased(int32_t result, GLuint texture); |
+ void OnImageReleased(int32_t result, const pp::ImageData& image); |
+ |
+ pp::CompletionCallbackFactory<DemoInstance> callback_factory_; |
+ |
+ // Owned data. |
+ pp::Graphics3D* context_; |
+ |
+ GLuint fbo_; |
+ GLuint rbo_; |
+ |
+ std::vector<GLuint> textures_; |
+ std::vector<pp::ImageData> images_; |
+ |
+ pp::Compositor compositor_; |
+ pp::CompositorLayer color_layer_; |
+ pp::CompositorLayer stable_texture_layer_; |
+ pp::CompositorLayer texture_layer_; |
+ pp::CompositorLayer image_layer_; |
+ |
+ bool rebuild_layers_; |
+ int32_t total_resource_; |
+ |
+ SpinningCube* cube_; |
+}; |
+ |
+DemoInstance::DemoInstance(PP_Instance instance) |
+ : pp::Instance(instance), |
+ pp::Graphics3DClient(this), |
+ callback_factory_(this), |
+ context_(NULL), |
+ fbo_(0), |
+ rbo_(0), |
+ compositor_(this), |
+ rebuild_layers_(false), |
+ total_resource_(0), |
+ cube_(new SpinningCube()) { |
+ RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE); |
+} |
+ |
+DemoInstance::~DemoInstance() { |
+ delete cube_; |
+ assert(glTerminatePPAPI()); |
+ delete context_; |
+} |
+ |
+bool DemoInstance::Init(uint32_t /*argc*/, |
+ const char* /*argn*/[], |
+ const char* /*argv*/[]) { |
+ return !!glInitializePPAPI(pp::Module::Get()->get_browser_interface()); |
+} |
+ |
+void DemoInstance::DidChangeView( |
+ const pp::Rect& position, const pp::Rect& /*clip*/) { |
+ if (position.width() == 0 || position.height() == 0) |
+ return; |
+ // Initialize graphics. |
+ InitGL(0); |
+} |
+ |
+bool DemoInstance::HandleInputEvent(const pp::InputEvent& event) { |
+ switch (event.GetType()) { |
+ case PP_INPUTEVENT_TYPE_MOUSEDOWN: |
+ rebuild_layers_ = true; |
+ return true; |
+ default: |
+ break; |
+ } |
+ return false; |
+} |
+ |
+void DemoInstance::Graphics3DContextLost() { |
+ fbo_ = 0; |
+ rbo_ = 0; |
+ compositor_.ResetLayers(); |
+ color_layer_ = pp::CompositorLayer(); |
+ stable_texture_layer_ = pp::CompositorLayer(); |
+ texture_layer_ = pp::CompositorLayer(); |
+ image_layer_ = pp::CompositorLayer(); |
+ total_resource_ -= textures_.size(); |
+ textures_.clear(); |
+ delete context_; |
+ context_ = NULL; |
+ cube_->OnGLContextLost(); |
+ pp::CompletionCallback cb = callback_factory_.NewCallback( |
+ &DemoInstance::InitGL); |
+ pp::Module::Get()->core()->CallOnMainThread(0, cb, 0); |
+} |
+ |
+void DemoInstance::InitGL(int32_t /*result*/) { |
+ if (context_) |
+ return; |
+ int32_t context_attributes[] = { |
+ PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8, |
+ PP_GRAPHICS3DATTRIB_BLUE_SIZE, 8, |
+ PP_GRAPHICS3DATTRIB_GREEN_SIZE, 8, |
+ PP_GRAPHICS3DATTRIB_RED_SIZE, 8, |
+ PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 0, |
+ PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 0, |
+ PP_GRAPHICS3DATTRIB_SAMPLES, 0, |
+ PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0, |
+ PP_GRAPHICS3DATTRIB_WIDTH, 32, |
+ PP_GRAPHICS3DATTRIB_HEIGHT, 32, |
+ PP_GRAPHICS3DATTRIB_NONE, |
+ }; |
+ context_ = new pp::Graphics3D(this, context_attributes); |
+ assert(!context_->is_null()); |
+ assert(BindGraphics(compositor_)); |
+ |
+ glSetCurrentContextPPAPI(context_->pp_resource()); |
+ |
+ cube_->Init(kTextureWidth, kTextureHeight); |
+ |
+ Paint(PP_OK, 0); |
+} |
+ |
+GLuint DemoInstance::PrepareFramebuffer() { |
+ GLuint texture = 0; |
+ if (textures_.empty()) { |
+ total_resource_++; |
+ // Create a texture object |
+ glGenTextures(1, &texture); |
+ glBindTexture(GL_TEXTURE_2D, texture); |
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTextureWidth, kTextureHeight, 0, |
+ GL_RGBA, GL_UNSIGNED_BYTE, NULL); |
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
+ glBindTexture(GL_TEXTURE_2D, 0); |
+ } else { |
+ texture = textures_.back(); |
+ textures_.pop_back(); |
+ } |
+ |
+ if (!rbo_) { |
+ // create a renderbuffer object to store depth info |
+ glGenRenderbuffers(1, &rbo_); |
+ glBindRenderbuffer(GL_RENDERBUFFER, rbo_); |
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, |
+ kTextureWidth, kTextureHeight); |
+ glBindRenderbuffer(GL_RENDERBUFFER, 0); |
+ } |
+ |
+ if (!fbo_) { |
+ // create a framebuffer object |
+ glGenFramebuffers(1, &fbo_); |
+ } |
+ |
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo_); |
+ |
+ // attach the texture to FBO color attachment point |
+ glFramebufferTexture2D(GL_FRAMEBUFFER, |
+ GL_COLOR_ATTACHMENT0, |
+ GL_TEXTURE_2D, |
+ texture, |
+ 0); |
+ |
+ // attach the renderbuffer to depth attachment point |
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, |
+ GL_DEPTH_ATTACHMENT, |
+ GL_RENDERBUFFER, |
+ rbo_); |
+ |
+ // check FBO status |
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); |
+ assert(status == GL_FRAMEBUFFER_COMPLETE); |
+ |
+ AssertNoGLError(); |
+ return texture; |
+} |
+ |
+pp::ImageData DemoInstance::PrepareImage() { |
+ if (images_.empty()) { |
+ total_resource_++; |
+ return pp::ImageData(this, |
+ PP_IMAGEDATAFORMAT_RGBA_PREMUL, |
+ pp::Size(kImageWidth, kImageHeight), |
+ false); |
+ } |
+ pp::ImageData image = images_.back(); |
+ images_.pop_back(); |
+ return image; |
+} |
+ |
+void DemoInstance::Paint(int32_t result, int32_t frame) { |
+ assert(result == PP_OK); |
+ |
+ if (result != PP_OK || !context_) |
+ return; |
+ |
+ int32_t rv; |
+ |
+ if (rebuild_layers_) { |
+ compositor_.ResetLayers(); |
+ color_layer_ = pp::CompositorLayer(); |
+ stable_texture_layer_ = pp::CompositorLayer(); |
+ texture_layer_ = pp::CompositorLayer(); |
+ image_layer_ = pp::CompositorLayer(); |
+ frame = 0; |
+ rebuild_layers_ = false; |
+ } |
+ |
+ float factor_sin = sin(M_PI / 180 * frame); |
+ float factor_cos = cos(M_PI / 180 * frame); |
+ { |
+ // Set the background color layer. |
+ if (color_layer_.is_null()) { |
+ color_layer_ = compositor_.AddLayer(); |
+ assert(!color_layer_.is_null()); |
+ static const float transform[16] = { |
+ 1.0f, 0.0f, 0.0f, 0.0f, |
+ 0.0f, 1.0f, 0.0f, 0.0f, |
+ 0.0f, 0.0f, 1.0f, 0.0f, |
+ 0.0f, 0.0f, 0.0f, 1.0f, |
+ }; |
+ rv = color_layer_.SetTransform(transform); |
+ assert(rv == PP_OK); |
+ } |
+ rv = color_layer_.SetColor(fabs(factor_sin), |
+ fabs(factor_cos), |
+ fabs(factor_sin * factor_cos), |
+ 1.0f, |
+ pp::Size(800, 600)); |
+ assert(rv == PP_OK); |
+ } |
+ |
+ { |
+ // Set the image layer |
+ if (image_layer_.is_null()) { |
+ image_layer_ = compositor_.AddLayer(); |
+ assert(!image_layer_.is_null()); |
+ } |
+ float x = frame % 800; |
+ float y = 200 - 200 * factor_sin; |
+ const float transform[16] = { |
+ fabs(factor_sin) + 0.2f, 0.0f, 0.0f, 0.0f, |
+ 0.0f, fabs(factor_sin) + 0.2f, 0.0f, 0.0f, |
+ 0.0f, 0.0f, 1.0f, 0.0f, |
+ x, y, 0.0f, 1.0f, |
+ }; |
+ rv = image_layer_.SetTransform(transform); |
+ assert(rv == PP_OK); |
+ |
+ pp::ImageData image = PrepareImage(); |
+ uint8_t *p = static_cast<uint8_t*>(image.data()); |
+ for (int x = 0; x < kImageWidth; ++x) { |
+ for (int y = 0; y < kImageHeight; ++y) { |
+ *(p++) = frame; |
+ *(p++) = frame * x; |
+ *(p++) = frame * y; |
+ *(p++) = 255; |
+ } |
+ } |
+ rv = image_layer_.SetImage(image, pp::Size(kImageWidth, kImageHeight), |
+ callback_factory_.NewCallback(&DemoInstance::OnImageReleased, image)); |
+ assert(rv == PP_OK_COMPLETIONPENDING); |
+ } |
+ |
+ { |
+ // Set the stable texture layer |
+ if (stable_texture_layer_.is_null()) { |
+ stable_texture_layer_ = compositor_.AddLayer(); |
+ assert(!stable_texture_layer_.is_null()); |
+ GLuint texture = PrepareFramebuffer(); |
+ cube_->UpdateForTimeDelta(0.02f); |
+ cube_->Draw(); |
+ rv = stable_texture_layer_.SetTexture( |
+ *context_, |
+ texture, pp::Size(600, 600), |
+ callback_factory_.NewCallback(&DemoInstance::OnTextureReleased, |
+ texture)); |
+ assert(rv == PP_OK_COMPLETIONPENDING); |
+ rv = stable_texture_layer_.SetPremultipliedAlpha(PP_FALSE); |
+ assert(rv == PP_OK); |
+ } |
+ |
+ int32_t delta = 200 * fabsf(factor_sin); |
+ if (delta != 0) { |
+ int32_t x_y = 25 + delta; |
+ int32_t w_h = 650 - delta - delta; |
+ rv = stable_texture_layer_.SetClipRect(pp::Rect(x_y, x_y, w_h, w_h)); |
+ } else { |
+ rv = stable_texture_layer_.SetClipRect(pp::Rect()); |
+ } |
+ assert(rv == PP_OK); |
+ |
+ const float transform[16] = { |
+ factor_cos, -factor_sin, 0.0f, 0.0f, |
+ factor_sin, factor_cos, 0.0f, 0.0f, |
+ 0.0f, 0.0f, 1.0f, 0.0f, |
+ 50.0f, 50.0f, 0.0f, 1.0f, |
+ }; |
+ rv = stable_texture_layer_.SetTransform(transform); |
+ assert(rv == PP_OK); |
+ } |
+ |
+ { |
+ // Set the dynamic texture layer. |
+ if (texture_layer_.is_null()) { |
+ texture_layer_ = compositor_.AddLayer(); |
+ assert(!texture_layer_.is_null()); |
+ static const float transform[16] = { |
+ 1.0f, 0.0f, 0.0f, 0.0f, |
+ 0.0f, 1.0f, 0.0f, 0.0f, |
+ 0.0f, 0.0f, 1.0f, 0.0f, |
+ 200.0f, 0.0f, 0.0f, 1.0f, |
+ }; |
+ rv = texture_layer_.SetTransform(transform); |
+ assert(rv == PP_OK); |
+ } |
+ |
+ GLuint texture = PrepareFramebuffer(); |
+ cube_->UpdateForTimeDelta(0.02f); |
+ cube_->Draw(); |
+ rv = texture_layer_.SetTexture(*context_, texture, pp::Size(400, 400), |
+ callback_factory_.NewCallback(&DemoInstance::OnTextureReleased, |
+ texture)); |
+ assert(rv == PP_OK_COMPLETIONPENDING); |
+ rv = texture_layer_.SetPremultipliedAlpha(PP_FALSE); |
+ assert(rv == PP_OK); |
+ } |
+ |
+ rv = compositor_.CommitLayers( |
+ callback_factory_.NewCallback(&DemoInstance::Paint, ++frame)); |
+ assert(rv == PP_OK_COMPLETIONPENDING); |
+ |
+ pp::VarDictionary dict; |
+ dict.Set(pp::Var("total_resource"), pp::Var(total_resource_)); |
+ dict.Set(pp::Var("free_resource"), |
+ pp::Var((int32_t)(textures_.size() + images_.size()))); |
+ PostMessage(dict); |
+} |
+ |
+void DemoInstance::OnTextureReleased(int32_t result, GLuint texture) { |
+ if (result == PP_OK) |
+ textures_.push_back(texture); |
+} |
+ |
+void DemoInstance::OnImageReleased(int32_t result, const pp::ImageData& image) { |
+ if (result == PP_OK) |
+ images_.push_back(image); |
+} |
+ |
+// This object is the global object representing this plugin library as long |
+// as it is loaded. |
+class DemoModule : public pp::Module { |
+ public: |
+ DemoModule() : Module() {} |
+ virtual ~DemoModule() {} |
+ |
+ virtual pp::Instance* CreateInstance(PP_Instance instance) { |
+ return new DemoInstance(instance); |
+ } |
+}; |
+ |
+} // anonymous namespace |
+ |
+namespace pp { |
+// Factory function for your specialization of the Module object. |
+Module* CreateModule() { |
+ return new DemoModule(); |
+} |
+} // namespace pp |