OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 The Native Client 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 "flocking_geese_app.h" | |
6 | |
7 #include <algorithm> | |
8 #include <sstream> | |
9 #include <string> | |
10 #include "nacl_app/scoped_pixel_lock.h" | |
11 #include "ppapi/c/pp_errors.h" | |
12 #include "ppapi/cpp/completion_callback.h" | |
13 #include "ppapi/cpp/module.h" | |
14 #include "threading/scoped_mutex_lock.h" | |
15 #include "url_io/url_request.h" | |
16 #include "url_io/web_resource_loader.h" | |
17 | |
18 namespace { | |
19 // Input method and parameter Ids. These correspond to C++ calls. | |
20 const char* const kResetFlockMethodId = "resetFlock"; | |
21 const char* const kRunSimulationMethodId = "runSimulation"; | |
22 const char* const kPauseSimulationMethodId = "pauseSimulation"; | |
23 const char* const kSetAttractorLocMethodId = "setAttractorLocation"; | |
24 | |
25 // Output method and parameter Ids. These correspond to methods in the | |
26 // browser. | |
27 const char* const kSetSimulationInfoMethodId = "setSimulationInfo"; | |
28 const char* const kFrameRateParamId = "frameRate"; | |
29 | |
30 const int kSimulationTickInterval = 10; // Measured in msec. | |
31 const uint32_t kBackgroundColor = 0xFFFFFFFF; // Opaque white. | |
32 | |
33 // Return the value of parameter named |param_name| from |parameters|. If | |
34 // |param_name| doesn't exist, then return an empty string. | |
35 std::string GetParameterNamed( | |
36 const std::string& param_name, | |
37 const scripting::MethodParameter& parameters) { | |
38 scripting::MethodParameter::const_iterator i = | |
39 parameters.find(param_name); | |
40 if (i == parameters.end()) { | |
41 return ""; | |
42 } | |
43 return i->second; | |
44 } | |
45 | |
46 // Return the int32_t equivalent of |str|. All the usual C++ rounding rules | |
47 // apply. | |
48 int32_t StringAsInt32(const std::string& str) { | |
49 std::istringstream cvt_stream(str); | |
50 double double_val; | |
51 cvt_stream >> double_val; | |
52 return static_cast<int32_t>(double_val); | |
53 } | |
54 | |
55 // Called from the browser when the delay time has elapsed. This routine | |
56 // runs a simulation update, then reschedules itself to run the next | |
57 // simulation tick. | |
58 void SimulationTickCallback(void* data, int32_t result) { | |
59 flocking_geese::FlockingGeeseApp* geese_app = | |
60 static_cast<flocking_geese::FlockingGeeseApp*>(data); | |
61 // Flush the graphics so far and post the current frame rate. | |
62 geese_app->PostSimulationInfo(); | |
63 geese_app->Update(); | |
64 if (geese_app->is_running()) { | |
65 pp::Module::Get()->core()->CallOnMainThread( | |
66 kSimulationTickInterval, | |
67 pp::CompletionCallback(&SimulationTickCallback, data), | |
68 PP_OK); | |
69 } | |
70 } | |
71 | |
72 // Called from the browser when the 2D graphics have been flushed out to the | |
73 // device. | |
74 void FlushCallback(void* data, int32_t result) { | |
75 static_cast<flocking_geese::FlockingGeeseApp*>(data)-> | |
76 set_flush_pending(false); | |
77 } | |
78 } // namespace | |
79 | |
80 using scripting::ScriptingBridge; | |
81 | |
82 namespace flocking_geese { | |
83 | |
84 FlockingGeeseApp::FlockingGeeseApp(PP_Instance instance) | |
85 : pp::Instance(instance), | |
86 graphics_2d_context_(NULL), | |
87 flush_pending_(false), | |
88 view_changed_size_(true) { | |
89 } | |
90 | |
91 FlockingGeeseApp::~FlockingGeeseApp() { | |
92 flock_simulation_.set_is_simulation_running(false); | |
93 DestroyContext(); | |
94 } | |
95 | |
96 bool FlockingGeeseApp::Init(uint32_t /* argc */, | |
97 const char* /* argn */[], | |
98 const char* /* argv */[]) { | |
99 // Add all the methods to the scripting bridge. | |
100 ScriptingBridge::SharedMethodCallbackExecutor | |
101 reset_flock_method(new scripting::MethodCallback<FlockingGeeseApp>( | |
102 this, &FlockingGeeseApp::ResetFlock)); | |
103 scripting_bridge_.AddMethodNamed(kResetFlockMethodId, reset_flock_method); | |
104 | |
105 ScriptingBridge::SharedMethodCallbackExecutor | |
106 run_sim_method(new scripting::MethodCallback<FlockingGeeseApp>( | |
107 this, &FlockingGeeseApp::RunSimulation)); | |
108 scripting_bridge_.AddMethodNamed(kRunSimulationMethodId, run_sim_method); | |
109 | |
110 ScriptingBridge::SharedMethodCallbackExecutor | |
111 pause_sim_method(new scripting::MethodCallback<FlockingGeeseApp>( | |
112 this, &FlockingGeeseApp::PauseSimulation)); | |
113 scripting_bridge_.AddMethodNamed(kPauseSimulationMethodId, pause_sim_method); | |
114 | |
115 ScriptingBridge::SharedMethodCallbackExecutor | |
116 set_attractor_method(new scripting::MethodCallback<FlockingGeeseApp>( | |
117 this, &FlockingGeeseApp::SetAttractorLocation)); | |
118 scripting_bridge_.AddMethodNamed(kSetAttractorLocMethodId, | |
119 set_attractor_method); | |
120 | |
121 // Load the goose sprite. |sprite_loader| is deleted by its delegate when | |
122 // the download is complete (or gets an error). The PNG data is checked for | |
123 // validity at each Update(); when the PNG data has all arrived, the sprite | |
124 // can be used for drawing. | |
125 url_io::WebResourceLoader* sprite_loader = | |
126 new url_io::WebResourceLoader(this, &png_loader_); | |
127 url_io::URLRequest request("images/Flock32_Green.png"); | |
128 sprite_loader->LoadURL(request); | |
129 | |
130 flock_simulation_.StartSimulation(); | |
131 return true; | |
132 } | |
133 | |
134 void FlockingGeeseApp::DidChangeView(const pp::Rect& position, | |
135 const pp::Rect& /* clip */) { | |
136 if (position.size().width() == width() && | |
137 position.size().height() == height()) | |
138 return; // Size didn't change, no need to update anything. | |
139 // Indicate that all the buffers need to be resized at the next Update() | |
140 // call. | |
141 view_changed_size_ = true; | |
142 view_size_ = position.size(); | |
143 // Make sure the buffers get changed if the simulation isn't running. | |
144 if (!is_running()) | |
145 Update(); | |
146 } | |
147 | |
148 void FlockingGeeseApp::HandleMessage(const pp::Var& var_message) { | |
149 if (!var_message.is_string()) | |
150 return; | |
151 scripting_bridge_.InvokeMethod(var_message.AsString()); | |
152 } | |
153 | |
154 void FlockingGeeseApp::ResetFlock( | |
155 const scripting::ScriptingBridge& bridge, | |
156 const scripting::MethodParameter& parameters) { | |
157 std::string flock_size_str = GetParameterNamed("size", parameters); | |
158 if (flock_size_str.length()) { | |
159 Flock::SimulationMode sim_mode = flock_simulation_.simulation_mode(); | |
160 flock_simulation_.set_simulation_mode(flocking_geese::Flock::kPaused); | |
161 size_t flock_size = static_cast<size_t>(StringAsInt32(flock_size_str)); | |
162 Vector2 initialLocation(view_size_.width(), view_size_.height()); | |
163 initialLocation.Scale(0.5); | |
164 flock_simulation_.ResetFlock(flock_size, initialLocation); | |
165 flock_simulation_.set_simulation_mode(sim_mode); | |
166 } | |
167 } | |
168 | |
169 void FlockingGeeseApp::RunSimulation( | |
170 const scripting::ScriptingBridge& bridge, | |
171 const scripting::MethodParameter& parameters) { | |
172 flock_simulation_.set_simulation_mode(flocking_geese::Flock::kRunning); | |
173 // Schedule a simulation tick to get things going. | |
174 pp::Module::Get()->core()->CallOnMainThread( | |
175 kSimulationTickInterval, | |
176 pp::CompletionCallback(&SimulationTickCallback, this), | |
177 PP_OK); | |
178 } | |
179 | |
180 void FlockingGeeseApp::PauseSimulation( | |
181 const scripting::ScriptingBridge& bridge, | |
182 const scripting::MethodParameter& parameters) { | |
183 flock_simulation_.set_simulation_mode(flocking_geese::Flock::kPaused); | |
184 } | |
185 | |
186 void FlockingGeeseApp::SetAttractorLocation( | |
187 const scripting::ScriptingBridge& bridge, | |
188 const scripting::MethodParameter& parameters) { | |
189 std::string loc_x_str = GetParameterNamed("x", parameters); | |
190 std::string loc_y_str = GetParameterNamed("y", parameters); | |
191 if (loc_x_str.length() && loc_y_str.length()) { | |
192 Vector2 attractor_loc(StringAsInt32(loc_x_str), StringAsInt32(loc_y_str)); | |
193 flock_simulation_.SetAttractorAtIndex(attractor_loc, 0); | |
194 } | |
195 } | |
196 | |
197 void FlockingGeeseApp::PostSimulationInfo() { | |
198 double frame_rate = flock_simulation_.FrameRate(); | |
199 std::ostringstream method_invocation(std::ostringstream::out); | |
200 method_invocation << kSetSimulationInfoMethodId << " "; | |
201 method_invocation << kFrameRateParamId << ":" << frame_rate; | |
202 pp::Var var_message(method_invocation.str()); | |
203 PostMessage(var_message); | |
204 } | |
205 | |
206 void FlockingGeeseApp::Update() { | |
207 if (flush_pending()) | |
208 return; // Don't attempt to flush if one is pending. | |
209 | |
210 // See if the goose sprite is ready to be used. | |
211 if (png_loader_.is_valid() && !flock_simulation_.has_goose_sprite()) { | |
212 pp::Size pixel_buffer_size = png_loader_.png_image_size(); | |
213 uint32_t* pixel_buffer = | |
214 static_cast<uint32_t*>(malloc(pixel_buffer_size.width() * | |
215 pixel_buffer_size.height() * | |
216 sizeof(uint32_t))); | |
217 png_loader_.FillPixelBuffer(pixel_buffer); | |
218 flock_simulation_.set_goose_sprite( | |
219 new Sprite(pixel_buffer, png_loader_.png_image_size(), 0)); | |
220 } | |
221 | |
222 if (view_changed_size_) { | |
223 // Delete the old pixel buffer and create a new one. | |
224 // Pause the simulation before changing all the buffer sizes. | |
225 Flock::SimulationMode sim_mode = flock_simulation_.simulation_mode(); | |
226 flock_simulation_.set_simulation_mode(flocking_geese::Flock::kPaused); | |
227 // Create a new device context with the new size. | |
228 // Note: DestroyContext() releases the simulation's copy of the shared | |
229 // pixel buffer. *This has to happen before the reset() below!* If the | |
230 // simulation's copy of hte shared pointer is released last, then the | |
231 // underlying ImageData is released off the main thread, which is not | |
232 // supported in Pepper. | |
233 DestroyContext(); | |
234 CreateContext(view_size_); | |
235 threading::ScopedMutexLock scoped_mutex( | |
236 flock_simulation_.simulation_mutex()); | |
237 if (!scoped_mutex.is_valid()) | |
238 // This potentially leaves the simulation in a paused state, but getting | |
239 // here means something is very wrong and the simulation probably can't | |
240 // run anyways. | |
241 return; | |
242 if (graphics_2d_context_ != NULL) { | |
243 shared_pixel_buffer_.reset( | |
244 new LockingImageData(this, | |
245 PP_IMAGEDATAFORMAT_BGRA_PREMUL, | |
246 graphics_2d_context_->size(), | |
247 false)); | |
248 set_flush_pending(false); | |
249 // Ok to get a non-locked version because the simulation is guraranteed | |
250 // to be paused here. | |
251 uint32_t* pixels = shared_pixel_buffer_->PixelBufferNoLock(); | |
252 if (pixels) { | |
253 const size_t size = width() * height(); | |
254 std::fill(pixels, pixels + size, kBackgroundColor); | |
255 } | |
256 flock_simulation_.Resize(pp::Size(width(), height())); | |
257 flock_simulation_.set_pixel_buffer(shared_pixel_buffer_); | |
258 flock_simulation_.set_simulation_mode(sim_mode); | |
259 } | |
260 view_changed_size_ = false; | |
261 } | |
262 flock_simulation_.Render(); | |
263 FlushPixelBuffer(); | |
264 } | |
265 | |
266 void FlockingGeeseApp::CreateContext(const pp::Size& size) { | |
267 if (IsContextValid()) | |
268 return; | |
269 graphics_2d_context_ = new pp::Graphics2D(this, size, false); | |
270 if (!BindGraphics(*graphics_2d_context_)) { | |
271 PostMessage("Couldn't bind the device context"); | |
272 } | |
273 } | |
274 | |
275 void FlockingGeeseApp::DestroyContext() { | |
276 threading::ScopedMutexLock scoped_mutex(flock_simulation_.simulation_mutex()); | |
277 if (!scoped_mutex.is_valid()) { | |
278 return; | |
279 } | |
280 ScopedPixelLock scoped_pixel_lock(shared_pixel_buffer_); | |
281 shared_pixel_buffer_.reset(); | |
282 flock_simulation_.set_pixel_buffer(shared_pixel_buffer_); | |
283 if (!IsContextValid()) | |
284 return; | |
285 delete graphics_2d_context_; | |
286 graphics_2d_context_ = NULL; | |
287 } | |
288 | |
289 void FlockingGeeseApp::FlushPixelBuffer() { | |
290 if (!IsContextValid() || shared_pixel_buffer_ == NULL) | |
291 return; | |
292 set_flush_pending(true); | |
293 graphics_2d_context_->PaintImageData(*shared_pixel_buffer_, pp::Point()); | |
294 graphics_2d_context_->Flush(pp::CompletionCallback(&FlushCallback, this)); | |
295 } | |
296 } // namespace flocking_geese | |
297 | |
OLD | NEW |