OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 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 #include "gpu/command_buffer/service/texture_definition.h" | |
6 | |
7 #include "gpu/command_buffer/service/texture_manager.h" | |
8 #include "ui/gl/gl_image.h" | |
9 #include "ui/gl/gl_implementation.h" | |
10 #include "ui/gl/scoped_binders.h" | |
11 | |
12 #if !defined(OS_MACOSX) | |
13 #include "ui/gl/gl_surface_egl.h" | |
14 #endif | |
15 | |
16 namespace gpu { | |
17 namespace gles2 { | |
18 | |
19 namespace { | |
20 | |
21 class GLImageSync : public gfx::GLImage { | |
22 public: | |
23 explicit GLImageSync( | |
24 const scoped_refptr<NativeImageBuffer>& buffer); | |
25 | |
26 // Implement GLImage. | |
27 virtual void Destroy() OVERRIDE; | |
28 virtual gfx::Size GetSize() OVERRIDE; | |
29 virtual bool BindTexImage(unsigned target) OVERRIDE; | |
30 virtual void ReleaseTexImage(unsigned target) OVERRIDE; | |
31 virtual void WillUseTexImage() OVERRIDE; | |
32 virtual void WillModifyTexImage() OVERRIDE; | |
33 virtual void DidModifyTexImage() OVERRIDE; | |
34 | |
35 virtual void DidUseTexImage() OVERRIDE; | |
36 virtual void SetReleaseAfterUse() OVERRIDE; | |
37 | |
38 protected: | |
39 virtual ~GLImageSync(); | |
40 | |
41 private: | |
42 scoped_refptr<NativeImageBuffer> buffer_; | |
43 | |
44 DISALLOW_COPY_AND_ASSIGN(GLImageSync); | |
45 }; | |
46 | |
47 GLImageSync::GLImageSync(const scoped_refptr<NativeImageBuffer>& buffer) | |
48 : buffer_(buffer) { | |
49 if (buffer) | |
50 buffer->AddClient(this); | |
51 } | |
52 | |
53 GLImageSync::~GLImageSync() { | |
54 if (buffer_) | |
55 buffer_->RemoveClient(this); | |
56 } | |
57 | |
58 void GLImageSync::Destroy() {} | |
59 | |
60 gfx::Size GLImageSync::GetSize() { | |
61 NOTREACHED(); | |
62 return gfx::Size(); | |
63 } | |
64 | |
65 bool GLImageSync::BindTexImage(unsigned target) { | |
66 NOTREACHED(); | |
67 return false; | |
68 } | |
69 | |
70 void GLImageSync::ReleaseTexImage(unsigned target) { | |
71 NOTREACHED(); | |
72 } | |
73 | |
74 void GLImageSync::WillUseTexImage() { | |
75 if (buffer_) | |
76 buffer_->WillRead(this); | |
77 } | |
78 | |
79 void GLImageSync::DidUseTexImage() { | |
80 if (buffer_) | |
81 buffer_->DidRead(this); | |
82 } | |
83 | |
84 void GLImageSync::WillModifyTexImage() { | |
85 if (buffer_) | |
86 buffer_->WillWrite(this); | |
87 } | |
88 | |
89 void GLImageSync::DidModifyTexImage() { | |
90 if (buffer_) | |
91 buffer_->DidWrite(this); | |
92 } | |
93 | |
94 void GLImageSync::SetReleaseAfterUse() { | |
95 NOTREACHED(); | |
96 } | |
97 | |
98 #if !defined(OS_MACOSX) | |
99 class NativeImageBufferEGL : public NativeImageBuffer { | |
100 public: | |
101 static NativeImageBufferEGL* Create(GLuint texture_id); | |
102 | |
103 private: | |
104 explicit NativeImageBufferEGL(EGLDisplay display, EGLImageKHR image); | |
105 virtual ~NativeImageBufferEGL(); | |
106 virtual void BindToTexture(GLenum target) OVERRIDE; | |
107 | |
108 EGLDisplay egl_display_; | |
109 EGLImageKHR egl_image_; | |
110 | |
111 DISALLOW_COPY_AND_ASSIGN(NativeImageBufferEGL); | |
112 }; | |
113 | |
114 NativeImageBufferEGL* NativeImageBufferEGL::Create(GLuint texture_id) { | |
piman
2014/03/13 04:41:58
nit: return a scoped_refptr<NativeImageBufferEGL>
no sievers
2014/03/13 20:50:15
Done.
| |
115 EGLDisplay egl_display = gfx::GLSurfaceEGL::GetHardwareDisplay(); | |
116 EGLContext egl_context = eglGetCurrentContext(); | |
117 | |
118 if (egl_context == EGL_NO_CONTEXT || egl_display == EGL_NO_DISPLAY || | |
119 !glIsTexture(texture_id)) { | |
120 return NULL; | |
121 } | |
piman
2014/03/13 04:41:58
Can these conditions happen? Should they just be D
no sievers
2014/03/13 20:50:15
You mean no context or display, right?
piman
2014/03/13 23:29:06
Yes, thanks.
| |
122 | |
123 // TODO: Need to generate and check EGL_KHR_gl_texture_2D_image | |
124 if (!gfx::g_driver_egl.ext.b_EGL_KHR_image_base || | |
125 !gfx::g_driver_gl.ext.b_GL_OES_EGL_image) { | |
126 return NULL; | |
127 } | |
128 | |
129 const EGLint egl_attrib_list[] = { | |
130 EGL_GL_TEXTURE_LEVEL_KHR, 0, EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE}; | |
131 EGLClientBuffer egl_buffer = reinterpret_cast<EGLClientBuffer>(texture_id); | |
132 EGLenum egl_target = EGL_GL_TEXTURE_2D_KHR; // TODO | |
133 | |
134 EGLImageKHR egl_image = eglCreateImageKHR( | |
135 egl_display, egl_context, egl_target, egl_buffer, egl_attrib_list); | |
136 | |
137 if (egl_image == EGL_NO_IMAGE_KHR) | |
138 return NULL; | |
139 | |
140 return new NativeImageBufferEGL(egl_display, egl_image); | |
141 } | |
142 | |
143 NativeImageBufferEGL::NativeImageBufferEGL(EGLDisplay display, | |
144 EGLImageKHR image) | |
145 : egl_display_(display), egl_image_(image) { | |
146 DCHECK(egl_display_ != EGL_NO_DISPLAY); | |
147 DCHECK(egl_image_ != EGL_NO_IMAGE_KHR); | |
148 } | |
149 | |
150 NativeImageBufferEGL::~NativeImageBufferEGL() { | |
151 if (egl_image_ != EGL_NO_IMAGE_KHR) | |
152 eglDestroyImageKHR(egl_display_, egl_image_); | |
153 } | |
154 | |
155 void NativeImageBufferEGL::BindToTexture(GLenum target) { | |
156 DCHECK(egl_image_ != EGL_NO_IMAGE_KHR); | |
157 glEGLImageTargetTexture2DOES(target, egl_image_); | |
158 DCHECK_EQ(EGL_SUCCESS, (EGLint)eglGetError()); | |
159 DCHECK_EQ(GL_NO_ERROR, (GLint)glGetError()); | |
piman
2014/03/13 04:41:58
nit: no c-style casts. Prefer static_cast or reint
no sievers
2014/03/13 20:50:15
Done.
| |
160 } | |
161 #endif | |
162 | |
163 class NativeImageBufferStub : public NativeImageBuffer { | |
164 public: | |
165 NativeImageBufferStub() {} | |
166 | |
167 private: | |
168 virtual ~NativeImageBufferStub() {} | |
169 virtual void BindToTexture(GLenum target) OVERRIDE {} | |
170 | |
171 DISALLOW_COPY_AND_ASSIGN(NativeImageBufferStub); | |
172 }; | |
173 | |
174 } // anonymous namespace | |
175 | |
176 // static | |
177 NativeImageBuffer* NativeImageBuffer::Create(GLuint texture_id) { | |
178 switch (gfx::GetGLImplementation()) { | |
179 #if !defined(OS_MACOSX) | |
180 case gfx::kGLImplementationEGLGLES2: | |
181 return NativeImageBufferEGL::Create(texture_id); | |
182 #endif | |
183 case gfx::kGLImplementationMockGL: | |
184 return new NativeImageBufferStub; | |
185 default: | |
186 NOTREACHED(); | |
187 return NULL; | |
188 } | |
189 } | |
190 | |
191 NativeImageBuffer::ClientInfo::ClientInfo(gfx::GLImage* client) | |
192 : client(client), needs_wait_before_read(true) {} | |
193 | |
194 NativeImageBuffer::ClientInfo::~ClientInfo() {} | |
195 | |
196 NativeImageBuffer::NativeImageBuffer() : write_client_(NULL) { | |
197 write_fence_.reset(gfx::GLFence::Create()); | |
198 } | |
199 | |
200 NativeImageBuffer::~NativeImageBuffer() { | |
201 DCHECK(client_infos_.empty()); | |
202 } | |
203 | |
204 void NativeImageBuffer::AddClient(gfx::GLImage* client) { | |
205 base::AutoLock lock(lock_); | |
206 client_infos_.push_back(ClientInfo(client)); | |
207 } | |
208 | |
209 void NativeImageBuffer::RemoveClient(gfx::GLImage* client) { | |
210 base::AutoLock lock(lock_); | |
211 if (write_client_ == client) | |
212 write_client_ = NULL; | |
213 for (std::list<ClientInfo>::iterator it = client_infos_.begin(); | |
214 it != client_infos_.end(); | |
215 it++) { | |
216 if (it->client == client) { | |
217 client_infos_.erase(it); | |
218 return; | |
219 } | |
220 } | |
221 NOTREACHED(); | |
222 } | |
223 | |
224 bool NativeImageBuffer::IsClient(gfx::GLImage* client) { | |
225 base::AutoLock lock(lock_); | |
226 for (std::list<ClientInfo>::iterator it = client_infos_.begin(); | |
227 it != client_infos_.end(); | |
228 it++) { | |
229 if (it->client == client) | |
230 return true; | |
231 } | |
232 return false; | |
233 } | |
234 | |
235 void NativeImageBuffer::WillRead(gfx::GLImage* client) { | |
236 base::AutoLock lock(lock_); | |
237 if (!write_fence_.get() || write_client_ == client) | |
238 return; | |
239 | |
240 for (std::list<ClientInfo>::iterator it = client_infos_.begin(); | |
241 it != client_infos_.end(); | |
242 it++) { | |
243 if (it->client == client) { | |
244 if (it->needs_wait_before_read) { | |
245 it->needs_wait_before_read = false; | |
246 write_fence_->ServerWait(); | |
247 } | |
248 return; | |
249 } | |
250 } | |
251 NOTREACHED(); | |
252 } | |
253 | |
254 void NativeImageBuffer::WillWrite(gfx::GLImage* client) { | |
255 base::AutoLock lock(lock_); | |
256 if (write_client_ != client) | |
257 write_fence_->ServerWait(); | |
258 | |
259 for (std::list<ClientInfo>::iterator it = client_infos_.begin(); | |
260 it != client_infos_.end(); | |
261 it++) { | |
262 if (it->read_fence.get() && it->client != client) | |
263 it->read_fence->ServerWait(); | |
264 } | |
265 } | |
266 | |
267 void NativeImageBuffer::DidRead(gfx::GLImage* client) { | |
268 base::AutoLock lock(lock_); | |
269 for (std::list<ClientInfo>::iterator it = client_infos_.begin(); | |
270 it != client_infos_.end(); | |
271 it++) { | |
272 if (it->client == client) { | |
273 it->read_fence = make_linked_ptr(gfx::GLFence::Create()); | |
274 return; | |
275 } | |
276 } | |
277 NOTREACHED(); | |
278 } | |
279 | |
280 void NativeImageBuffer::DidWrite(gfx::GLImage* client) { | |
281 base::AutoLock lock(lock_); | |
282 // TODO(sievers): This is super-risky. We need to somehow find out | |
283 // about when the current context gets flushed, so that we will only | |
284 // ever wait on the write fence (esp. from another context) if it was | |
285 // flushed and is guaranteed to clear. | |
286 // On the other hand, proactively flushing here is not feasible in terms | |
287 // of perf when there are multiple draw calls per frame. | |
piman
2014/03/13 04:41:58
So, the risk is that if we're missing a sync point
no sievers
2014/03/13 20:50:15
Can I owe this as an immediate follow-up?
I was al
piman
2014/03/13 23:29:06
Maybe file a bug and reference here... It's not so
no sievers
2014/03/13 23:51:13
Done.
| |
288 write_fence_.reset(gfx::GLFence::CreateWithoutFlush()); | |
289 write_client_ = client; | |
290 for (std::list<ClientInfo>::iterator it = client_infos_.begin(); | |
291 it != client_infos_.end(); | |
292 it++) { | |
293 it->needs_wait_before_read = true; | |
294 } | |
295 } | |
296 | |
297 TextureDefinition::LevelInfo::LevelInfo(GLenum target, | |
298 GLenum internal_format, | |
299 GLsizei width, | |
300 GLsizei height, | |
301 GLsizei depth, | |
302 GLint border, | |
303 GLenum format, | |
304 GLenum type, | |
305 bool cleared) | |
306 : target(target), | |
307 internal_format(internal_format), | |
308 width(width), | |
309 height(height), | |
310 depth(depth), | |
311 border(border), | |
312 format(format), | |
313 type(type), | |
314 cleared(cleared) {} | |
315 | |
316 TextureDefinition::LevelInfo::~LevelInfo() {} | |
317 | |
318 TextureDefinition::TextureDefinition(GLenum target, | |
319 Texture* texture, | |
320 unsigned int version, | |
321 NativeImageBuffer* image) | |
322 : version_(version), | |
323 target_(target), | |
324 image_(image ? image : NativeImageBuffer::Create(texture->service_id())), | |
325 min_filter_(texture->min_filter()), | |
326 mag_filter_(texture->mag_filter()), | |
327 wrap_s_(texture->wrap_s()), | |
328 wrap_t_(texture->wrap_t()), | |
329 usage_(texture->usage()), | |
330 immutable_(texture->IsImmutable()) { | |
331 | |
332 // TODO | |
333 DCHECK(!texture->level_infos_.empty()); | |
334 DCHECK(!texture->level_infos_[0].empty()); | |
335 DCHECK(!texture->NeedsMips()); | |
336 DCHECK(texture->level_infos_[0][0].width); | |
337 DCHECK(texture->level_infos_[0][0].height); | |
338 | |
339 scoped_refptr<gfx::GLImage> gl_image(new GLImageSync(image_)); | |
340 texture->SetLevelImage(NULL, target, 0, gl_image); | |
341 | |
342 // TODO: all levels | |
343 level_infos_.clear(); | |
344 const Texture::LevelInfo& level = texture->level_infos_[0][0]; | |
345 LevelInfo info(level.target, | |
346 level.internal_format, | |
347 level.width, | |
348 level.height, | |
349 level.depth, | |
350 level.border, | |
351 level.format, | |
352 level.type, | |
353 level.cleared); | |
354 std::vector<LevelInfo> infos; | |
355 infos.push_back(info); | |
356 level_infos_.push_back(infos); | |
357 | |
358 } | |
359 | |
360 TextureDefinition::~TextureDefinition() { | |
361 } | |
362 | |
363 Texture* TextureDefinition::CreateTexture() const { | |
364 if (!image_) | |
365 return NULL; | |
366 | |
367 GLuint texture_id; | |
368 glGenTextures(1, &texture_id); | |
369 | |
370 Texture* texture(new Texture(texture_id)); | |
371 UpdateTexture(texture); | |
372 | |
373 return texture; | |
374 } | |
375 | |
376 void TextureDefinition::UpdateTexture(Texture* texture) const { | |
377 gfx::ScopedTextureBinder texture_binder(target_, texture->service_id()); | |
378 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter_); | |
379 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter_); | |
380 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s_); | |
381 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t_); | |
382 DCHECK(image_); | |
383 if (image_) | |
piman
2014/03/13 04:41:58
DCHECK, or if? Chose one.
no sievers
2014/03/13 20:50:15
Done. Should eventually handle incomplete textures
| |
384 image_->BindToTexture(target_); | |
385 glFlush(); | |
piman
2014/03/13 04:41:58
DO you need this glFlush?
no sievers
2014/03/13 20:50:15
Most of the time probably not. There is one case I
piman
2014/03/13 23:29:06
I see... arguably, ctx2a needs to flush after the
no sievers
2014/03/13 23:51:13
Done. Maybe we could be smarter about when the flu
| |
386 | |
387 texture->level_infos_.resize(1); | |
388 for (size_t i = 0; i < level_infos_.size(); i++) { | |
389 const LevelInfo& base_info = level_infos_[i][0]; | |
390 const size_t levels_needed = TextureManager::ComputeMipMapCount( | |
391 base_info.target, base_info.width, base_info.height, base_info.depth); | |
392 DCHECK(level_infos_.size() <= levels_needed); | |
393 texture->level_infos_[0].resize(levels_needed); | |
394 for (size_t n = 0; n < level_infos_.size(); n++) { | |
395 const LevelInfo& info = level_infos_[i][n]; | |
396 texture->SetLevelInfo(NULL, | |
397 info.target, | |
398 i, | |
399 info.internal_format, | |
400 info.width, | |
401 info.height, | |
402 info.depth, | |
403 info.border, | |
404 info.format, | |
405 info.type, | |
406 info.cleared); | |
407 } | |
408 } | |
409 if (image_) | |
410 texture->SetLevelImage(NULL, target_, 0, new GLImageSync(image_)); | |
411 | |
412 texture->target_ = target_; | |
413 texture->SetImmutable(immutable_); | |
414 texture->min_filter_ = min_filter_; | |
415 texture->mag_filter_ = mag_filter_; | |
416 texture->wrap_s_ = wrap_s_; | |
417 texture->wrap_t_ = wrap_t_; | |
418 texture->usage_ = usage_; | |
419 } | |
420 | |
421 bool TextureDefinition::Matches(const Texture* texture) const { | |
422 DCHECK(target_ == texture->target()); | |
423 if (texture->min_filter_ != min_filter_ || | |
424 texture->mag_filter_ != mag_filter_ || | |
425 texture->wrap_s_ != wrap_s_ || | |
426 texture->wrap_t_ != wrap_t_) { | |
427 return false; | |
428 } | |
429 | |
430 // All structural changes should have orphaned the texture. | |
431 if (image_ && !texture->GetLevelImage(texture->target(), 0)) | |
432 return false; | |
433 | |
434 return true; | |
435 } | |
436 | |
437 } // namespace gles2 | |
438 } // namespace gpu | |
OLD | NEW |