OLD | NEW |
| (Empty) |
1 // Copyright 2015 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 "apps/moterm/gl_helper.h" | |
6 | |
7 #ifndef GL_GLEXT_PROTOTYPES | |
8 #define GL_GLEXT_PROTOTYPES | |
9 #endif | |
10 | |
11 #include <GLES2/gl2.h> | |
12 #include <GLES2/gl2extmojo.h> | |
13 #include <MGL/mgl.h> | |
14 | |
15 #include "base/bind.h" | |
16 #include "base/location.h" | |
17 #include "base/message_loop/message_loop.h" | |
18 #include "base/task_runner.h" | |
19 #include "mojo/public/cpp/application/connect.h" | |
20 #include "mojo/public/interfaces/application/shell.mojom.h" | |
21 #include "mojo/services/geometry/cpp/geometry_util.h" | |
22 #include "mojo/services/surfaces/cpp/surfaces_utils.h" | |
23 | |
24 // Maximum number of (live) textures to keep around. | |
25 const size_t kMaxTextures = 10; | |
26 | |
27 GlHelper::GlHelper(Client* client, | |
28 mojo::Shell* shell, | |
29 GLint texture_format, | |
30 bool flipped, | |
31 const mojo::Size& initial_size) | |
32 : client_(client), | |
33 texture_format_(texture_format), | |
34 flipped_(flipped), | |
35 returner_binding_(this), | |
36 next_surface_size_(initial_size), | |
37 mgl_context_(MGL_NO_CONTEXT), | |
38 next_frame_id_(0), | |
39 frame_texture_(0), | |
40 id_namespace_(0), | |
41 local_id_(0), | |
42 next_resource_id_(0), | |
43 weak_factory_(this) { | |
44 mojo::ServiceProviderPtr native_viewport_service_provider; | |
45 shell->ConnectToApplication("mojo:native_viewport_service", | |
46 GetProxy(&native_viewport_service_provider), | |
47 nullptr); | |
48 mojo::ConnectToService(native_viewport_service_provider.get(), &gpu_); | |
49 | |
50 mojo::ServiceProviderPtr surfaces_service_provider; | |
51 shell->ConnectToApplication("mojo:surfaces_service", | |
52 GetProxy(&surfaces_service_provider), nullptr); | |
53 mojo::ConnectToService(surfaces_service_provider.get(), &surface_); | |
54 surface_->GetIdNamespace(base::Bind(&GlHelper::GetIdNamespaceCallback, | |
55 weak_factory_.GetWeakPtr())); | |
56 mojo::ResourceReturnerPtr returner_ptr; | |
57 returner_binding_.Bind(GetProxy(&returner_ptr)); | |
58 surface_->SetResourceReturner(returner_ptr.Pass()); | |
59 } | |
60 | |
61 GlHelper::~GlHelper() { | |
62 DCHECK(!frame_texture_); | |
63 if (mgl_context_ != MGL_NO_CONTEXT) | |
64 MGLDestroyContext(mgl_context_); | |
65 } | |
66 | |
67 void GlHelper::SetSurfaceSize(const mojo::Size& surface_size) { | |
68 next_surface_size_ = surface_size; | |
69 } | |
70 | |
71 void GlHelper::MakeCurrent() { | |
72 EnsureContext(); | |
73 } | |
74 | |
75 void GlHelper::StartFrame() { | |
76 DCHECK(!frame_texture_); | |
77 | |
78 EnsureContext(); | |
79 EnsureSurface(); | |
80 | |
81 TextureInfo texture_info = GetTexture(); | |
82 DCHECK(texture_info.texture); | |
83 frame_texture_ = texture_info.texture; | |
84 | |
85 // It's already bound. | |
86 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
87 } | |
88 | |
89 uint32_t GlHelper::EndFrame() { | |
90 DCHECK(frame_texture_); | |
91 | |
92 mojo::Rect size_rect; | |
93 size_rect.width = current_surface_size_.width; | |
94 size_rect.height = current_surface_size_.height; | |
95 | |
96 GLbyte mailbox[GL_MAILBOX_SIZE_CHROMIUM]; | |
97 glGenMailboxCHROMIUM(mailbox); | |
98 glProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox); | |
99 GLuint sync_point = glInsertSyncPointCHROMIUM(); | |
100 | |
101 mojo::FramePtr frame = mojo::Frame::New(); | |
102 | |
103 // Frame resources: | |
104 frame->resources.push_back(mojo::TransferableResource::New()); | |
105 mojo::TransferableResource* resource = frame->resources[0].get(); | |
106 resource->id = next_resource_id_++; | |
107 textures_pending_return_.push_back( | |
108 TextureInfo(resource->id, frame_texture_, current_surface_size_)); | |
109 frame_texture_ = 0; | |
110 // TODO(vtl): This is wrong, but doesn't seem to have an effect. | |
111 resource->format = mojo::ResourceFormat::RGBA_8888; | |
112 resource->filter = GL_LINEAR; | |
113 resource->size = current_surface_size_.Clone(); | |
114 mojo::MailboxHolderPtr mailbox_holder = mojo::MailboxHolder::New(); | |
115 mailbox_holder->mailbox = mojo::Mailbox::New(); | |
116 for (int i = 0; i < GL_MAILBOX_SIZE_CHROMIUM; ++i) | |
117 mailbox_holder->mailbox->name.push_back(mailbox[i]); | |
118 mailbox_holder->texture_target = GL_TEXTURE_2D; | |
119 mailbox_holder->sync_point = sync_point; | |
120 resource->mailbox_holder = mailbox_holder.Pass(); | |
121 resource->is_repeated = false; | |
122 resource->is_software = false; | |
123 | |
124 // Frame passes: | |
125 frame->passes.push_back(mojo::CreateDefaultPass(1, size_rect)); | |
126 mojo::Pass* pass = frame->passes[0].get(); | |
127 pass->quads.push_back(mojo::Quad::New()); | |
128 mojo::Quad* quad = pass->quads[0].get(); | |
129 quad->material = mojo::Material::TEXTURE_CONTENT; | |
130 quad->rect = size_rect.Clone(); | |
131 quad->opaque_rect = size_rect.Clone(); | |
132 quad->visible_rect = size_rect.Clone(); | |
133 quad->needs_blending = true; | |
134 quad->shared_quad_state_index = 0; | |
135 quad->texture_quad_state = mojo::TextureQuadState::New(); | |
136 mojo::TextureQuadState* texture_state = quad->texture_quad_state.get(); | |
137 texture_state->resource_id = resource->id; | |
138 texture_state->premultiplied_alpha = true; | |
139 texture_state->uv_top_left = mojo::PointF::New(); | |
140 texture_state->uv_bottom_right = mojo::PointF::New(); | |
141 texture_state->uv_bottom_right->x = 1.f; | |
142 texture_state->uv_bottom_right->y = 1.f; | |
143 texture_state->background_color = mojo::Color::New(); | |
144 texture_state->background_color->rgba = 0; | |
145 for (int i = 0; i < 4; ++i) | |
146 texture_state->vertex_opacity.push_back(1.f); | |
147 texture_state->flipped = flipped_; | |
148 pass->shared_quad_states.push_back( | |
149 mojo::CreateDefaultSQS(current_surface_size_)); | |
150 | |
151 surface_->SubmitFrame(local_id_, frame.Pass(), | |
152 base::Bind(&GlHelper::SubmitFrameCallback, | |
153 weak_factory_.GetWeakPtr(), next_frame_id_)); | |
154 | |
155 return next_frame_id_++; | |
156 } | |
157 | |
158 GLuint GlHelper::GetFrameTexture() { | |
159 DCHECK(frame_texture_); | |
160 return frame_texture_; | |
161 } | |
162 | |
163 void GlHelper::ReturnResources( | |
164 mojo::Array<mojo::ReturnedResourcePtr> resources) { | |
165 DCHECK(!frame_texture_); | |
166 | |
167 if (mgl_context_ == MGL_NO_CONTEXT) { | |
168 DCHECK(textures_pending_return_.empty()); | |
169 return; | |
170 } | |
171 | |
172 MGLMakeCurrent(mgl_context_); | |
173 | |
174 // Note: This quadratic nested loop is OK, since we expect both |resources| | |
175 // and |textures_pending_return_| to be small (and |resources| should | |
176 // usually have just a single element). | |
177 for (size_t i = 0; i < resources.size(); ++i) { | |
178 mojo::ReturnedResourcePtr resource = resources[i].Pass(); | |
179 DCHECK_EQ(resource->count, 1); | |
180 | |
181 bool found = false; | |
182 for (size_t j = 0; j < textures_pending_return_.size(); j++) { | |
183 const TextureInfo& texture_info = textures_pending_return_[j]; | |
184 if (texture_info.resource_id == resource->id) { | |
185 glWaitSyncPointCHROMIUM(resource->sync_point); | |
186 ReturnTexture(texture_info); | |
187 textures_pending_return_.erase(textures_pending_return_.begin() + j); | |
188 found = true; | |
189 break; | |
190 } | |
191 } | |
192 if (!found) { | |
193 // If we don't texture ID for it, assume we lost the context. | |
194 // TODO(vtl): This may leak (but currently we don't know if the texture is | |
195 // still valid). | |
196 DVLOG(1) << "Returned texture not found (context lost?)"; | |
197 } | |
198 } | |
199 } | |
200 | |
201 void GlHelper::EnsureContext() { | |
202 DCHECK(!frame_texture_); | |
203 | |
204 if (mgl_context_ == MGL_NO_CONTEXT) { | |
205 DCHECK(textures_pending_return_.empty()); | |
206 | |
207 mojo::CommandBufferPtr command_buffer; | |
208 gpu_->CreateOffscreenGLES2Context(mojo::GetProxy(&command_buffer)); | |
209 mgl_context_ = MGLCreateContext( | |
210 MGL_API_VERSION_GLES2, | |
211 command_buffer.PassInterface().PassHandle().release().value(), nullptr, | |
212 &GlHelper::OnContextLostThunk, this, | |
213 mojo::Environment::GetDefaultAsyncWaiter()); | |
214 CHECK_NE(mgl_context_, MGL_NO_CONTEXT); | |
215 } | |
216 | |
217 MGLMakeCurrent(mgl_context_); | |
218 } | |
219 | |
220 void GlHelper::EnsureSurface() { | |
221 DCHECK(!frame_texture_); | |
222 DCHECK_NE(mgl_context_, MGL_NO_CONTEXT); | |
223 | |
224 if (local_id_) { | |
225 if (current_surface_size_ == next_surface_size_) | |
226 return; | |
227 | |
228 surface_->DestroySurface(local_id_); | |
229 | |
230 ClearTextures(); | |
231 } | |
232 | |
233 local_id_++; | |
234 surface_->CreateSurface(local_id_); | |
235 current_surface_size_ = next_surface_size_; | |
236 if (id_namespace_) { | |
237 // Don't call the client in the nested context. | |
238 base::MessageLoop::current()->task_runner()->PostTask( | |
239 FROM_HERE, base::Bind(&GlHelper::CallOnSurfaceIdChanged, | |
240 weak_factory_.GetWeakPtr())); | |
241 } | |
242 } | |
243 | |
244 void GlHelper::CallOnSurfaceIdChanged() { | |
245 DCHECK(id_namespace_ && local_id_); | |
246 | |
247 auto qualified_id = mojo::SurfaceId::New(); | |
248 qualified_id->id_namespace = id_namespace_; | |
249 qualified_id->local = local_id_; | |
250 client_->OnSurfaceIdChanged(qualified_id.Pass()); | |
251 } | |
252 | |
253 GlHelper::TextureInfo GlHelper::GetTexture() { | |
254 DCHECK_NE(mgl_context_, MGL_NO_CONTEXT); | |
255 | |
256 if (!textures_.empty()) { | |
257 TextureInfo rv = textures_.front(); | |
258 DCHECK(rv.size == current_surface_size_); | |
259 textures_.pop_front(); | |
260 glBindTexture(GL_TEXTURE_2D, rv.texture); | |
261 return rv; | |
262 } | |
263 | |
264 GLuint texture = 0; | |
265 glGenTextures(1, &texture); | |
266 DCHECK(texture); | |
267 glBindTexture(GL_TEXTURE_2D, texture); | |
268 glTexImage2D(GL_TEXTURE_2D, 0, texture_format_, current_surface_size_.width, | |
269 current_surface_size_.height, 0, texture_format_, | |
270 GL_UNSIGNED_BYTE, nullptr); | |
271 | |
272 return TextureInfo(0, texture, current_surface_size_); | |
273 } | |
274 | |
275 void GlHelper::ReturnTexture(const TextureInfo& texture_info) { | |
276 DCHECK_NE(mgl_context_, MGL_NO_CONTEXT); | |
277 DCHECK_NE(texture_info.texture, 0u); | |
278 | |
279 if (texture_info.size == current_surface_size_ && | |
280 textures_.size() < kMaxTextures) | |
281 textures_.push_back(texture_info); // TODO(vtl): Is |push_front()| better? | |
282 else | |
283 glDeleteTextures(1, &texture_info.texture); | |
284 } | |
285 | |
286 void GlHelper::ClearTextures() { | |
287 DCHECK_NE(mgl_context_, MGL_NO_CONTEXT); | |
288 | |
289 for (const auto& texture_info : textures_) | |
290 glDeleteTextures(1, &texture_info.texture); | |
291 | |
292 textures_.clear(); | |
293 } | |
294 | |
295 void GlHelper::GetIdNamespaceCallback(uint32_t id_namespace) { | |
296 id_namespace_ = id_namespace; | |
297 if (local_id_) { | |
298 // We're in a callback, so we can just call the client directly. | |
299 CallOnSurfaceIdChanged(); | |
300 } | |
301 } | |
302 | |
303 // static | |
304 void GlHelper::OnContextLostThunk(void* self) { | |
305 static_cast<GlHelper*>(self)->OnContextLost(); | |
306 } | |
307 | |
308 void GlHelper::OnContextLost() { | |
309 // We shouldn't get this while we're processing a frame. | |
310 DCHECK(!frame_texture_); | |
311 | |
312 DCHECK_NE(mgl_context_, MGL_NO_CONTEXT); | |
313 MGLDestroyContext(mgl_context_); | |
314 mgl_context_ = MGL_NO_CONTEXT; | |
315 | |
316 // TODO(vtl): We don't know if any of those textures will be valid when | |
317 // returned (if they are), so assume they aren't. | |
318 textures_pending_return_.clear(); | |
319 | |
320 // We're in a callback, so we can just call the client directly. | |
321 client_->OnContextLost(); | |
322 } | |
323 | |
324 void GlHelper::SubmitFrameCallback(uint32_t frame_id) { | |
325 // We're in a callback, so we can just call the client directly. | |
326 client_->OnFrameDisplayed(frame_id); | |
327 } | |
OLD | NEW |