1 // Copyright 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 <list>
8
9 #include "base/memory/linked_ptr.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/synchronization/lock.h"
12 #include "gpu/command_buffer/service/texture_manager.h"
13 #include "ui/gl/gl_image.h"
14 #include "ui/gl/gl_implementation.h"
15 #include "ui/gl/scoped_binders.h"
16
17 #if !defined(OS_MACOSX)
18 #include "ui/gl/gl_fence_egl.h"
19 #include "ui/gl/gl_surface_egl.h"
20 #endif
21
22 namespace gpu {
23 namespace gles2 {
24
25 namespace {
26
27 class GLImageSync : public gfx::GLImage {
28 public:
29 explicit GLImageSync(const scoped_refptr<NativeImageBuffer>& buffer,
30 const gfx::Size& size);
31
32 // Implement GLImage.
33 virtual void Destroy() OVERRIDE;
34 virtual gfx::Size GetSize() OVERRIDE;
35 virtual bool BindTexImage(unsigned target) OVERRIDE;
36 virtual void ReleaseTexImage(unsigned target) OVERRIDE;
37 virtual void WillUseTexImage() OVERRIDE;
38 virtual void WillModifyTexImage() OVERRIDE;
39 virtual void DidModifyTexImage() OVERRIDE;
40
41 virtual void DidUseTexImage() OVERRIDE;
42 virtual void SetReleaseAfterUse() OVERRIDE;
43
44 protected:
45 virtual ~GLImageSync();
46
47 private:
48 scoped_refptr<NativeImageBuffer> buffer_;
49 gfx::Size size_;
50
51 DISALLOW_COPY_AND_ASSIGN(GLImageSync);
52 };
53
GLImageSync(const scoped_refptr<NativeImageBuffer> & buffer,const gfx::Size & size)54 GLImageSync::GLImageSync(const scoped_refptr<NativeImageBuffer>& buffer,
55 const gfx::Size& size)
56 : buffer_(buffer), size_(size) {
57 if (buffer)
58 buffer->AddClient(this);
59 }
60
~GLImageSync()61 GLImageSync::~GLImageSync() {
62 if (buffer_)
63 buffer_->RemoveClient(this);
64 }
65
Destroy()66 void GLImageSync::Destroy() {}
67
GetSize()68 gfx::Size GLImageSync::GetSize() {
69 return size_;
70 }
71
BindTexImage(unsigned target)72 bool GLImageSync::BindTexImage(unsigned target) {
73 NOTREACHED();
74 return false;
75 }
76
ReleaseTexImage(unsigned target)77 void GLImageSync::ReleaseTexImage(unsigned target) {
78 NOTREACHED();
79 }
80
WillUseTexImage()81 void GLImageSync::WillUseTexImage() {
82 if (buffer_)
83 buffer_->WillRead(this);
84 }
85
DidUseTexImage()86 void GLImageSync::DidUseTexImage() {
87 if (buffer_)
88 buffer_->DidRead(this);
89 }
90
WillModifyTexImage()91 void GLImageSync::WillModifyTexImage() {
92 if (buffer_)
93 buffer_->WillWrite(this);
94 }
95
DidModifyTexImage()96 void GLImageSync::DidModifyTexImage() {
97 if (buffer_)
98 buffer_->DidWrite(this);
99 }
100
SetReleaseAfterUse()101 void GLImageSync::SetReleaseAfterUse() {
102 NOTREACHED();
103 }
104
105 #if !defined(OS_MACOSX)
106 class NativeImageBufferEGL : public NativeImageBuffer {
107 public:
108 static scoped_refptr<NativeImageBufferEGL> Create(GLuint texture_id);
109
110 private:
111 NativeImageBufferEGL(EGLDisplay display, EGLImageKHR image);
112 virtual ~NativeImageBufferEGL();
113 virtual void AddClient(gfx::GLImage* client) OVERRIDE;
114 virtual void RemoveClient(gfx::GLImage* client) OVERRIDE;
115 virtual bool IsClient(gfx::GLImage* client) OVERRIDE;
116 virtual void BindToTexture(GLenum target) OVERRIDE;
117 virtual void WillRead(gfx::GLImage* client) OVERRIDE;
118 virtual void WillWrite(gfx::GLImage* client) OVERRIDE;
119 virtual void DidRead(gfx::GLImage* client) OVERRIDE;
120 virtual void DidWrite(gfx::GLImage* client) OVERRIDE;
121
122 void ClearCompletedReadFencesLocked();
123
124 EGLDisplay egl_display_;
125 EGLImageKHR egl_image_;
126
127 base::Lock lock_;
128
129 struct ClientInfo {
130 ClientInfo(gfx::GLImage* client);
131 ~ClientInfo();
132
133 gfx::GLImage* client;
134 bool needs_wait_before_read;
135 linked_ptr<gfx::GLFence> read_fence;
136 };
137 std::list<ClientInfo> client_infos_;
138 scoped_ptr<gfx::GLFence> write_fence_;
139 gfx::GLImage* write_client_;
140
141 DISALLOW_COPY_AND_ASSIGN(NativeImageBufferEGL);
142 };
143
Create(GLuint texture_id)144 scoped_refptr<NativeImageBufferEGL> NativeImageBufferEGL::Create(
145 GLuint texture_id) {
146 EGLDisplay egl_display = gfx::GLSurfaceEGL::GetHardwareDisplay();
147 EGLContext egl_context = eglGetCurrentContext();
148
149 DCHECK_NE(EGL_NO_CONTEXT, egl_context);
150 DCHECK_NE(EGL_NO_DISPLAY, egl_display);
151 DCHECK(glIsTexture(texture_id));
152
153 DCHECK(gfx::g_driver_egl.ext.b_EGL_KHR_image_base &&
154 gfx::g_driver_egl.ext.b_EGL_KHR_gl_texture_2D_image &&
155 gfx::g_driver_gl.ext.b_GL_OES_EGL_image &&
156 gfx::g_driver_egl.ext.b_EGL_KHR_fence_sync);
157
158 const EGLint egl_attrib_list[] = {
159 EGL_GL_TEXTURE_LEVEL_KHR, 0, EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
160 EGLClientBuffer egl_buffer = reinterpret_cast<EGLClientBuffer>(texture_id);
161 EGLenum egl_target = EGL_GL_TEXTURE_2D_KHR; // TODO
162
163 EGLImageKHR egl_image = eglCreateImageKHR(
164 egl_display, egl_context, egl_target, egl_buffer, egl_attrib_list);
165
166 if (egl_image == EGL_NO_IMAGE_KHR)
167 return NULL;
168
169 return new NativeImageBufferEGL(egl_display, egl_image);
170 }
171
ClientInfo(gfx::GLImage * client)172 NativeImageBufferEGL::ClientInfo::ClientInfo(gfx::GLImage* client)
173 : client(client), needs_wait_before_read(true) {}
174
~ClientInfo()175 NativeImageBufferEGL::ClientInfo::~ClientInfo() {}
176
NativeImageBufferEGL(EGLDisplay display,EGLImageKHR image)177 NativeImageBufferEGL::NativeImageBufferEGL(EGLDisplay display,
178 EGLImageKHR image)
179 : NativeImageBuffer(),
180 egl_display_(display),
181 egl_image_(image),
182 write_fence_(new gfx::GLFenceEGL(true)),
183 write_client_(NULL) {
184 DCHECK(egl_display_ != EGL_NO_DISPLAY);
185 DCHECK(egl_image_ != EGL_NO_IMAGE_KHR);
186 }
187
~NativeImageBufferEGL()188 NativeImageBufferEGL::~NativeImageBufferEGL() {
189 DCHECK(client_infos_.empty());
190 if (egl_image_ != EGL_NO_IMAGE_KHR)
191 eglDestroyImageKHR(egl_display_, egl_image_);
192 }
193
AddClient(gfx::GLImage * client)194 void NativeImageBufferEGL::AddClient(gfx::GLImage* client) {
195 base::AutoLock lock(lock_);
196 client_infos_.push_back(ClientInfo(client));
197 }
198
RemoveClient(gfx::GLImage * client)199 void NativeImageBufferEGL::RemoveClient(gfx::GLImage* client) {
200 base::AutoLock lock(lock_);
201 if (write_client_ == client)
202 write_client_ = NULL;
203 for (std::list<ClientInfo>::iterator it = client_infos_.begin();
204 it != client_infos_.end();
205 it++) {
206 if (it->client == client) {
207 if (it->read_fence.get()) {
208 it->client = NULL;
209 } else {
210 client_infos_.erase(it);
211 }
212 ClearCompletedReadFencesLocked();
213 return;
214 }
215 }
216 NOTREACHED();
217 }
218
IsClient(gfx::GLImage * client)219 bool NativeImageBufferEGL::IsClient(gfx::GLImage* client) {
220 base::AutoLock lock(lock_);
221 for (std::list<ClientInfo>::iterator it = client_infos_.begin();
222 it != client_infos_.end();
223 it++) {
224 if (it->client == client)
225 return true;
226 }
227 return false;
228 }
229
BindToTexture(GLenum target)230 void NativeImageBufferEGL::BindToTexture(GLenum target) {
231 DCHECK(egl_image_ != EGL_NO_IMAGE_KHR);
232 glEGLImageTargetTexture2DOES(target, egl_image_);
233 DCHECK_EQ(static_cast<EGLint>(EGL_SUCCESS), eglGetError());
234 DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
235 }
236
WillRead(gfx::GLImage * client)237 void NativeImageBufferEGL::WillRead(gfx::GLImage* client) {
238 base::AutoLock lock(lock_);
239 if (!write_fence_.get() || write_client_ == client)
240 return;
241
242 for (std::list<ClientInfo>::iterator it = client_infos_.begin();
243 it != client_infos_.end();
244 it++) {
245 if (it->client == client) {
246 if (it->needs_wait_before_read) {
247 it->needs_wait_before_read = false;
248 write_fence_->ServerWait();
249 }
250 return;
251 }
252 }
253 NOTREACHED();
254 }
255
WillWrite(gfx::GLImage * client)256 void NativeImageBufferEGL::WillWrite(gfx::GLImage* client) {
257 base::AutoLock lock(lock_);
258 if (write_client_ != client)
259 write_fence_->ServerWait();
260
261 ClearCompletedReadFencesLocked();
262 for (std::list<ClientInfo>::iterator it = client_infos_.begin();
263 it != client_infos_.end();
264 it++) {
265 if (it->read_fence.get() && it->client != client)
266 it->read_fence->ServerWait();
267 }
268 }
269
DidRead(gfx::GLImage * client)270 void NativeImageBufferEGL::DidRead(gfx::GLImage* client) {
271 base::AutoLock lock(lock_);
272 ClearCompletedReadFencesLocked();
273 for (std::list<ClientInfo>::iterator it = client_infos_.begin();
274 it != client_infos_.end();
275 it++) {
276 if (it->client == client) {
277 it->read_fence = make_linked_ptr(new gfx::GLFenceEGL(true));
278 return;
279 }
280 }
281 NOTREACHED();
282 }
283
DidWrite(gfx::GLImage * client)284 void NativeImageBufferEGL::DidWrite(gfx::GLImage* client) {
285 base::AutoLock lock(lock_);
286 // Sharing semantics require the client to flush in order to make changes
287 // visible to other clients.
288 write_fence_.reset(new gfx::GLFenceEGL(false));
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
ClearCompletedReadFencesLocked()297 void NativeImageBufferEGL::ClearCompletedReadFencesLocked() {
298 lock_.AssertAcquired();
299 std::list<ClientInfo>::iterator it = client_infos_.begin();
300 while (it != client_infos_.end()) {
301 if (!it->client && it->read_fence->HasCompleted()) {
302 it = client_infos_.erase(it);
303 } else {
304 it++;
305 }
306 }
307 }
308
309 #endif
310
311 class NativeImageBufferStub : public NativeImageBuffer {
312 public:
NativeImageBufferStub()313 NativeImageBufferStub() : NativeImageBuffer() {}
314
315 private:
~NativeImageBufferStub()316 virtual ~NativeImageBufferStub() {}
AddClient(gfx::GLImage * client)317 virtual void AddClient(gfx::GLImage* client) OVERRIDE {}
RemoveClient(gfx::GLImage * client)318 virtual void RemoveClient(gfx::GLImage* client) OVERRIDE {}
IsClient(gfx::GLImage * client)319 virtual bool IsClient(gfx::GLImage* client) OVERRIDE { return true; }
BindToTexture(GLenum target)320 virtual void BindToTexture(GLenum target) OVERRIDE {}
WillRead(gfx::GLImage * client)321 virtual void WillRead(gfx::GLImage* client) OVERRIDE {}
WillWrite(gfx::GLImage * client)322 virtual void WillWrite(gfx::GLImage* client) OVERRIDE {}
DidRead(gfx::GLImage * client)323 virtual void DidRead(gfx::GLImage* client) OVERRIDE {}
DidWrite(gfx::GLImage * client)324 virtual void DidWrite(gfx::GLImage* client) OVERRIDE {}
325
326 DISALLOW_COPY_AND_ASSIGN(NativeImageBufferStub);
327 };
328
329 } // anonymous namespace
330
331 // static
Create(GLuint texture_id)332 scoped_refptr<NativeImageBuffer> NativeImageBuffer::Create(GLuint texture_id) {
333 switch (gfx::GetGLImplementation()) {
334 #if !defined(OS_MACOSX)
335 case gfx::kGLImplementationEGLGLES2:
336 return NativeImageBufferEGL::Create(texture_id);
337 #endif
338 case gfx::kGLImplementationMockGL:
339 return new NativeImageBufferStub;
340 default:
341 NOTREACHED();
342 return NULL;
343 }
344 }
345
LevelInfo(GLenum target,GLenum internal_format,GLsizei width,GLsizei height,GLsizei depth,GLint border,GLenum format,GLenum type,bool cleared)346 TextureDefinition::LevelInfo::LevelInfo(GLenum target,
347 GLenum internal_format,
348 GLsizei width,
349 GLsizei height,
350 GLsizei depth,
351 GLint border,
352 GLenum format,
353 GLenum type,
354 bool cleared)
355 : target(target),
356 internal_format(internal_format),
357 width(width),
358 height(height),
359 depth(depth),
360 border(border),
361 format(format),
362 type(type),
363 cleared(cleared) {}
364
~LevelInfo()365 TextureDefinition::LevelInfo::~LevelInfo() {}
366
TextureDefinition(GLenum target,Texture * texture,unsigned int version,const scoped_refptr<NativeImageBuffer> & image_buffer)367 TextureDefinition::TextureDefinition(
368 GLenum target,
369 Texture* texture,
370 unsigned int version,
371 const scoped_refptr<NativeImageBuffer>& image_buffer)
372 : version_(version),
373 target_(target),
374 image_buffer_(image_buffer ? image_buffer : NativeImageBuffer::Create(
375 texture->service_id())),
376 min_filter_(texture->min_filter()),
377 mag_filter_(texture->mag_filter()),
378 wrap_s_(texture->wrap_s()),
379 wrap_t_(texture->wrap_t()),
380 usage_(texture->usage()),
381 immutable_(texture->IsImmutable()) {
382
383 // TODO
384 DCHECK(!texture->level_infos_.empty());
385 DCHECK(!texture->level_infos_[0].empty());
386 DCHECK(!texture->NeedsMips());
387 DCHECK(texture->level_infos_[0][0].width);
388 DCHECK(texture->level_infos_[0][0].height);
389
390 scoped_refptr<gfx::GLImage> gl_image(
391 new GLImageSync(image_buffer_,
392 gfx::Size(texture->level_infos_[0][0].width,
393 texture->level_infos_[0][0].height)));
394 texture->SetLevelImage(NULL, target, 0, gl_image);
395
396 // TODO: all levels
397 level_infos_.clear();
398 const Texture::LevelInfo& level = texture->level_infos_[0][0];
399 LevelInfo info(level.target,
400 level.internal_format,
401 level.width,
402 level.height,
403 level.depth,
404 level.border,
405 level.format,
406 level.type,
407 level.cleared);
408 std::vector<LevelInfo> infos;
409 infos.push_back(info);
410 level_infos_.push_back(infos);
411 }
412
~TextureDefinition()413 TextureDefinition::~TextureDefinition() {
414 }
415
CreateTexture() const416 Texture* TextureDefinition::CreateTexture() const {
417 if (!image_buffer_)
418 return NULL;
419
420 GLuint texture_id;
421 glGenTextures(1, &texture_id);
422
423 Texture* texture(new Texture(texture_id));
424 UpdateTexture(texture);
425
426 return texture;
427 }
428
UpdateTexture(Texture * texture) const429 void TextureDefinition::UpdateTexture(Texture* texture) const {
430 gfx::ScopedTextureBinder texture_binder(target_, texture->service_id());
431 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter_);
432 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter_);
433 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s_);
434 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t_);
435 if (image_buffer_)
436 image_buffer_->BindToTexture(target_);
437 // We have to make sure the changes are visible to other clients in this share
438 // group. As far as the clients are concerned, the mailbox semantics only
439 // demand a single flush from the client after changes are first made,
440 // and it is not visible to them when another share group boundary is crossed.
441 // We could probably track this and be a bit smarter about when to flush
442 // though.
443 glFlush();
444
445 texture->level_infos_.resize(1);
446 for (size_t i = 0; i < level_infos_.size(); i++) {
447 const LevelInfo& base_info = level_infos_[i][0];
448 const size_t levels_needed = TextureManager::ComputeMipMapCount(
449 base_info.target, base_info.width, base_info.height, base_info.depth);
450 DCHECK(level_infos_.size() <= levels_needed);
451 texture->level_infos_[0].resize(levels_needed);
452 for (size_t n = 0; n < level_infos_.size(); n++) {
453 const LevelInfo& info = level_infos_[i][n];
454 texture->SetLevelInfo(NULL,
455 info.target,
456 i,
457 info.internal_format,
458 info.width,
459 info.height,
460 info.depth,
461 info.border,
462 info.format,
463 info.type,
464 info.cleared);
465 }
466 }
467 if (image_buffer_) {
468 texture->SetLevelImage(
469 NULL,
470 target_,
471 0,
472 new GLImageSync(
473 image_buffer_,
474 gfx::Size(level_infos_[0][0].width, level_infos_[0][0].height)));
475 }
476
477 texture->target_ = target_;
478 texture->SetImmutable(immutable_);
479 texture->min_filter_ = min_filter_;
480 texture->mag_filter_ = mag_filter_;
481 texture->wrap_s_ = wrap_s_;
482 texture->wrap_t_ = wrap_t_;
483 texture->usage_ = usage_;
484 }
485
Matches(const Texture * texture) const486 bool TextureDefinition::Matches(const Texture* texture) const {
487 DCHECK(target_ == texture->target());
488 if (texture->min_filter_ != min_filter_ ||
489 texture->mag_filter_ != mag_filter_ ||
490 texture->wrap_s_ != wrap_s_ ||
491 texture->wrap_t_ != wrap_t_) {
492 return false;
493 }
494
495 // All structural changes should have orphaned the texture.
496 if (image_buffer_ && !texture->GetLevelImage(texture->target(), 0))
497 return false;
498
499 return true;
500 }
501
502 } // namespace gles2
503 } // namespace gpu
504