1// Copyright (c) 2012 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#include <string.h>
6
7#include <iostream>
8#include <sstream>
9
10#include "ppapi/c/pp_errors.h"
11#include "ppapi/c/ppb_opengles2.h"
12#include "ppapi/cpp/core.h"
13#include "ppapi/cpp/fullscreen.h"
14#include "ppapi/cpp/graphics_3d.h"
15#include "ppapi/cpp/graphics_3d_client.h"
16#include "ppapi/cpp/input_event.h"
17#include "ppapi/cpp/instance.h"
18#include "ppapi/cpp/module.h"
19#include "ppapi/cpp/rect.h"
20#include "ppapi/cpp/var.h"
21#include "ppapi/lib/gl/include/GLES2/gl2.h"
22#include "ppapi/utility/completion_callback_factory.h"
23
24// Use assert as a poor-man's CHECK, even in non-debug mode.
25// Since <assert.h> redefines assert on every inclusion (it doesn't use
26// include-guards), make sure this is the last file #include'd in this file.
27#undef NDEBUG
28#include <assert.h>
29
30// Assert |context_| isn't holding any GL Errors.  Done as a macro instead of a
31// function to preserve line number information in the failure message.
32#define assertNoGLError() \
33  assert(!gles2_if_->GetError(context_->pp_resource()));
34
35namespace {
36
37class GLES2DemoInstance : public pp::Instance,
38                          public pp::Graphics3DClient {
39 public:
40  GLES2DemoInstance(PP_Instance instance, pp::Module* module);
41  virtual ~GLES2DemoInstance();
42
43  // pp::Instance implementation (see PPP_Instance).
44  virtual void DidChangeView(const pp::Rect& position,
45                             const pp::Rect& clip_ignored);
46
47  // pp::Graphics3DClient implementation.
48  virtual void Graphics3DContextLost() {
49    // TODO(jamesr): What's the state of context_? Should we delete the old one
50    // or try to revive it somehow?
51    // For now, just delete it and construct+bind a new context.
52    delete context_;
53    context_ = NULL;
54    pp::CompletionCallback cb = callback_factory_.NewCallback(
55        &GLES2DemoInstance::InitGL);
56    module_->core()->CallOnMainThread(0, cb, 0);
57  }
58
59  virtual bool HandleInputEvent(const pp::InputEvent& event) {
60    if (event.GetType() == PP_INPUTEVENT_TYPE_MOUSEUP) {
61      fullscreen_ = !fullscreen_;
62      pp::Fullscreen(this).SetFullscreen(fullscreen_);
63    }
64    return true;
65  }
66
67 private:
68
69  // GL-related functions.
70  void InitGL(int32_t result);
71  void FlickerAndPaint(int32_t result, bool paint_blue);
72
73  pp::Size plugin_size_;
74  pp::CompletionCallbackFactory<GLES2DemoInstance> callback_factory_;
75
76  // Unowned pointers.
77  const PPB_OpenGLES2* gles2_if_;
78  pp::Module* module_;
79
80  // Owned data.
81  pp::Graphics3D* context_;
82  bool fullscreen_;
83};
84
85GLES2DemoInstance::GLES2DemoInstance(PP_Instance instance, pp::Module* module)
86    : pp::Instance(instance), pp::Graphics3DClient(this),
87      callback_factory_(this),
88      gles2_if_(static_cast<const PPB_OpenGLES2*>(
89          module->GetBrowserInterface(PPB_OPENGLES2_INTERFACE))),
90      module_(module),
91      context_(NULL),
92      fullscreen_(false) {
93  assert(gles2_if_);
94  RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE);
95}
96
97GLES2DemoInstance::~GLES2DemoInstance() {
98  delete context_;
99}
100
101void GLES2DemoInstance::DidChangeView(
102    const pp::Rect& position, const pp::Rect& clip_ignored) {
103  if (position.width() == 0 || position.height() == 0)
104    return;
105  plugin_size_ = position.size();
106
107  // Initialize graphics.
108  InitGL(0);
109}
110
111// This object is the global object representing this plugin library as long
112// as it is loaded.
113class GLES2DemoModule : public pp::Module {
114 public:
115  GLES2DemoModule() : pp::Module() {}
116  virtual ~GLES2DemoModule() {}
117
118  virtual pp::Instance* CreateInstance(PP_Instance instance) {
119    return new GLES2DemoInstance(instance, this);
120  }
121};
122
123void GLES2DemoInstance::InitGL(int32_t result) {
124  assert(plugin_size_.width() && plugin_size_.height());
125
126  if (context_) {
127    context_->ResizeBuffers(plugin_size_.width(), plugin_size_.height());
128    return;
129  }
130  int32_t context_attributes[] = {
131    PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8,
132    PP_GRAPHICS3DATTRIB_BLUE_SIZE, 8,
133    PP_GRAPHICS3DATTRIB_GREEN_SIZE, 8,
134    PP_GRAPHICS3DATTRIB_RED_SIZE, 8,
135    PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 0,
136    PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 0,
137    PP_GRAPHICS3DATTRIB_SAMPLES, 0,
138    PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0,
139    PP_GRAPHICS3DATTRIB_WIDTH, plugin_size_.width(),
140    PP_GRAPHICS3DATTRIB_HEIGHT, plugin_size_.height(),
141    PP_GRAPHICS3DATTRIB_NONE,
142  };
143  context_ = new pp::Graphics3D(this, context_attributes);
144  assert(!context_->is_null());
145  assert(BindGraphics(*context_));
146
147  // Clear color bit.
148  gles2_if_->ClearColor(context_->pp_resource(), 0, 1, 0, 1);
149  gles2_if_->Clear(context_->pp_resource(), GL_COLOR_BUFFER_BIT);
150
151  assertNoGLError();
152
153  FlickerAndPaint(0, true);
154}
155
156void GLES2DemoInstance::FlickerAndPaint(int32_t result, bool paint_blue) {
157  if (result != 0 || !context_)
158    return;
159  float r = paint_blue ? 0 : 1;
160  float g = 0;
161  float b = paint_blue ? 1 : 0;
162  float a = 0.75;
163  gles2_if_->ClearColor(context_->pp_resource(), r, g, b, a);
164  gles2_if_->Clear(context_->pp_resource(), GL_COLOR_BUFFER_BIT);
165  assertNoGLError();
166
167  pp::CompletionCallback cb = callback_factory_.NewCallback(
168      &GLES2DemoInstance::FlickerAndPaint, !paint_blue);
169  context_->SwapBuffers(cb);
170  assertNoGLError();
171}
172
173}  // anonymous namespace
174
175namespace pp {
176// Factory function for your specialization of the Module object.
177Module* CreateModule() {
178  return new GLES2DemoModule();
179}
180}  // namespace pp
181