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_surface_egl.h"
19 #endif
20
21 namespace gpu {
22 namespace gles2 {
23
24 namespace {
25
26 class GLImageSync : public gfx::GLImage {
27 public:
28 explicit GLImageSync(const scoped_refptr<NativeImageBuffer>& buffer,
29 const gfx::Size& size);
30
31 // Implement GLImage.
32 virtual void Destroy(bool have_context) OVERRIDE;
33 virtual gfx::Size GetSize() OVERRIDE;
34 virtual bool BindTexImage(unsigned target) OVERRIDE;
35 virtual void ReleaseTexImage(unsigned target) OVERRIDE;
36 virtual bool CopyTexImage(unsigned target) OVERRIDE;
37 virtual void WillUseTexImage() OVERRIDE;
38 virtual void WillModifyTexImage() OVERRIDE;
39 virtual void DidModifyTexImage() OVERRIDE;
40 virtual void DidUseTexImage() OVERRIDE;
41 virtual bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
42 int z_order,
43 gfx::OverlayTransform transform,
44 const gfx::Rect& bounds_rect,
45 const gfx::RectF& crop_rect) OVERRIDE;
46 virtual void SetReleaseAfterUse() OVERRIDE;
47
48 protected:
49 virtual ~GLImageSync();
50
51 private:
52 scoped_refptr<NativeImageBuffer> buffer_;
53 gfx::Size size_;
54
55 DISALLOW_COPY_AND_ASSIGN(GLImageSync);
56 };
57
GLImageSync(const scoped_refptr<NativeImageBuffer> & buffer,const gfx::Size & size)58 GLImageSync::GLImageSync(const scoped_refptr<NativeImageBuffer>& buffer,
59 const gfx::Size& size)
60 : buffer_(buffer), size_(size) {
61 if (buffer.get())
62 buffer->AddClient(this);
63 }
64
~GLImageSync()65 GLImageSync::~GLImageSync() {
66 if (buffer_.get())
67 buffer_->RemoveClient(this);
68 }
69
Destroy(bool have_context)70 void GLImageSync::Destroy(bool have_context) {
71 }
72
GetSize()73 gfx::Size GLImageSync::GetSize() {
74 return size_;
75 }
76
BindTexImage(unsigned target)77 bool GLImageSync::BindTexImage(unsigned target) {
78 NOTREACHED();
79 return false;
80 }
81
ReleaseTexImage(unsigned target)82 void GLImageSync::ReleaseTexImage(unsigned target) {
83 NOTREACHED();
84 }
85
CopyTexImage(unsigned target)86 bool GLImageSync::CopyTexImage(unsigned target) {
87 return false;
88 }
89
WillUseTexImage()90 void GLImageSync::WillUseTexImage() {
91 }
92
DidUseTexImage()93 void GLImageSync::DidUseTexImage() {
94 }
95
WillModifyTexImage()96 void GLImageSync::WillModifyTexImage() {
97 }
98
DidModifyTexImage()99 void GLImageSync::DidModifyTexImage() {
100 }
101
ScheduleOverlayPlane(gfx::AcceleratedWidget widget,int z_order,gfx::OverlayTransform transform,const gfx::Rect & bounds_rect,const gfx::RectF & crop_rect)102 bool GLImageSync::ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
103 int z_order,
104 gfx::OverlayTransform transform,
105 const gfx::Rect& bounds_rect,
106 const gfx::RectF& crop_rect) {
107 NOTREACHED();
108 return false;
109 }
110
SetReleaseAfterUse()111 void GLImageSync::SetReleaseAfterUse() {
112 NOTREACHED();
113 }
114
115 #if !defined(OS_MACOSX)
116 class NativeImageBufferEGL : public NativeImageBuffer {
117 public:
118 static scoped_refptr<NativeImageBufferEGL> Create(GLuint texture_id);
119
120 private:
121 NativeImageBufferEGL(EGLDisplay display, EGLImageKHR image);
122 virtual ~NativeImageBufferEGL();
123 virtual void AddClient(gfx::GLImage* client) OVERRIDE;
124 virtual void RemoveClient(gfx::GLImage* client) OVERRIDE;
125 virtual bool IsClient(gfx::GLImage* client) OVERRIDE;
126 virtual void BindToTexture(GLenum target) OVERRIDE;
127
128 EGLDisplay egl_display_;
129 EGLImageKHR egl_image_;
130
131 base::Lock lock_;
132
133 struct ClientInfo {
134 ClientInfo(gfx::GLImage* client);
135 ~ClientInfo();
136
137 gfx::GLImage* client;
138 bool needs_wait_before_read;
139 };
140 std::list<ClientInfo> client_infos_;
141 gfx::GLImage* write_client_;
142
143 DISALLOW_COPY_AND_ASSIGN(NativeImageBufferEGL);
144 };
145
Create(GLuint texture_id)146 scoped_refptr<NativeImageBufferEGL> NativeImageBufferEGL::Create(
147 GLuint texture_id) {
148 EGLDisplay egl_display = gfx::GLSurfaceEGL::GetHardwareDisplay();
149 EGLContext egl_context = eglGetCurrentContext();
150
151 DCHECK_NE(EGL_NO_CONTEXT, egl_context);
152 DCHECK_NE(EGL_NO_DISPLAY, egl_display);
153 DCHECK(glIsTexture(texture_id));
154
155 DCHECK(gfx::g_driver_egl.ext.b_EGL_KHR_image_base &&
156 gfx::g_driver_egl.ext.b_EGL_KHR_gl_texture_2D_image &&
157 gfx::g_driver_gl.ext.b_GL_OES_EGL_image);
158
159 const EGLint egl_attrib_list[] = {
160 EGL_GL_TEXTURE_LEVEL_KHR, 0, EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
161 EGLClientBuffer egl_buffer = reinterpret_cast<EGLClientBuffer>(texture_id);
162 EGLenum egl_target = EGL_GL_TEXTURE_2D_KHR; // TODO
163
164 EGLImageKHR egl_image = eglCreateImageKHR(
165 egl_display, egl_context, egl_target, egl_buffer, egl_attrib_list);
166
167 if (egl_image == EGL_NO_IMAGE_KHR)
168 return NULL;
169
170 return new NativeImageBufferEGL(egl_display, egl_image);
171 }
172
ClientInfo(gfx::GLImage * client)173 NativeImageBufferEGL::ClientInfo::ClientInfo(gfx::GLImage* client)
174 : client(client), needs_wait_before_read(true) {}
175
~ClientInfo()176 NativeImageBufferEGL::ClientInfo::~ClientInfo() {}
177
NativeImageBufferEGL(EGLDisplay display,EGLImageKHR image)178 NativeImageBufferEGL::NativeImageBufferEGL(EGLDisplay display,
179 EGLImageKHR image)
180 : NativeImageBuffer(),
181 egl_display_(display),
182 egl_image_(image),
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 client_infos_.erase(it);
208 return;
209 }
210 }
211 NOTREACHED();
212 }
213
IsClient(gfx::GLImage * client)214 bool NativeImageBufferEGL::IsClient(gfx::GLImage* client) {
215 base::AutoLock lock(lock_);
216 for (std::list<ClientInfo>::iterator it = client_infos_.begin();
217 it != client_infos_.end();
218 it++) {
219 if (it->client == client)
220 return true;
221 }
222 return false;
223 }
224
BindToTexture(GLenum target)225 void NativeImageBufferEGL::BindToTexture(GLenum target) {
226 DCHECK(egl_image_ != EGL_NO_IMAGE_KHR);
227 glEGLImageTargetTexture2DOES(target, egl_image_);
228 DCHECK_EQ(static_cast<EGLint>(EGL_SUCCESS), eglGetError());
229 DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
230 }
231
232 #endif
233
234 class NativeImageBufferStub : public NativeImageBuffer {
235 public:
NativeImageBufferStub()236 NativeImageBufferStub() : NativeImageBuffer() {}
237
238 private:
~NativeImageBufferStub()239 virtual ~NativeImageBufferStub() {}
AddClient(gfx::GLImage * client)240 virtual void AddClient(gfx::GLImage* client) OVERRIDE {}
RemoveClient(gfx::GLImage * client)241 virtual void RemoveClient(gfx::GLImage* client) OVERRIDE {}
IsClient(gfx::GLImage * client)242 virtual bool IsClient(gfx::GLImage* client) OVERRIDE { return true; }
BindToTexture(GLenum target)243 virtual void BindToTexture(GLenum target) OVERRIDE {}
244
245 DISALLOW_COPY_AND_ASSIGN(NativeImageBufferStub);
246 };
247
248 } // anonymous namespace
249
250 // static
Create(GLuint texture_id)251 scoped_refptr<NativeImageBuffer> NativeImageBuffer::Create(GLuint texture_id) {
252 switch (gfx::GetGLImplementation()) {
253 #if !defined(OS_MACOSX)
254 case gfx::kGLImplementationEGLGLES2:
255 return NativeImageBufferEGL::Create(texture_id);
256 #endif
257 case gfx::kGLImplementationMockGL:
258 return new NativeImageBufferStub;
259 default:
260 NOTREACHED();
261 return NULL;
262 }
263 }
264
LevelInfo(GLenum target,GLenum internal_format,GLsizei width,GLsizei height,GLsizei depth,GLint border,GLenum format,GLenum type,bool cleared)265 TextureDefinition::LevelInfo::LevelInfo(GLenum target,
266 GLenum internal_format,
267 GLsizei width,
268 GLsizei height,
269 GLsizei depth,
270 GLint border,
271 GLenum format,
272 GLenum type,
273 bool cleared)
274 : target(target),
275 internal_format(internal_format),
276 width(width),
277 height(height),
278 depth(depth),
279 border(border),
280 format(format),
281 type(type),
282 cleared(cleared) {}
283
~LevelInfo()284 TextureDefinition::LevelInfo::~LevelInfo() {}
285
TextureDefinition(GLenum target,Texture * texture,unsigned int version,const scoped_refptr<NativeImageBuffer> & image_buffer)286 TextureDefinition::TextureDefinition(
287 GLenum target,
288 Texture* texture,
289 unsigned int version,
290 const scoped_refptr<NativeImageBuffer>& image_buffer)
291 : version_(version),
292 target_(target),
293 image_buffer_(image_buffer.get()
294 ? image_buffer
295 : NativeImageBuffer::Create(texture->service_id())),
296 min_filter_(texture->min_filter()),
297 mag_filter_(texture->mag_filter()),
298 wrap_s_(texture->wrap_s()),
299 wrap_t_(texture->wrap_t()),
300 usage_(texture->usage()),
301 immutable_(texture->IsImmutable()) {
302 // TODO
303 DCHECK(!texture->level_infos_.empty());
304 DCHECK(!texture->level_infos_[0].empty());
305 DCHECK(!texture->NeedsMips());
306 DCHECK(texture->level_infos_[0][0].width);
307 DCHECK(texture->level_infos_[0][0].height);
308
309 scoped_refptr<gfx::GLImage> gl_image(
310 new GLImageSync(image_buffer_,
311 gfx::Size(texture->level_infos_[0][0].width,
312 texture->level_infos_[0][0].height)));
313 texture->SetLevelImage(NULL, target, 0, gl_image.get());
314
315 // TODO: all levels
316 level_infos_.clear();
317 const Texture::LevelInfo& level = texture->level_infos_[0][0];
318 LevelInfo info(level.target,
319 level.internal_format,
320 level.width,
321 level.height,
322 level.depth,
323 level.border,
324 level.format,
325 level.type,
326 level.cleared);
327 std::vector<LevelInfo> infos;
328 infos.push_back(info);
329 level_infos_.push_back(infos);
330 }
331
~TextureDefinition()332 TextureDefinition::~TextureDefinition() {
333 }
334
CreateTexture() const335 Texture* TextureDefinition::CreateTexture() const {
336 if (!image_buffer_.get())
337 return NULL;
338
339 GLuint texture_id;
340 glGenTextures(1, &texture_id);
341
342 Texture* texture(new Texture(texture_id));
343 UpdateTexture(texture);
344
345 return texture;
346 }
347
UpdateTexture(Texture * texture) const348 void TextureDefinition::UpdateTexture(Texture* texture) const {
349 gfx::ScopedTextureBinder texture_binder(target_, texture->service_id());
350 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter_);
351 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter_);
352 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s_);
353 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t_);
354 if (image_buffer_.get())
355 image_buffer_->BindToTexture(target_);
356 // We have to make sure the changes are visible to other clients in this share
357 // group. As far as the clients are concerned, the mailbox semantics only
358 // demand a single flush from the client after changes are first made,
359 // and it is not visible to them when another share group boundary is crossed.
360 // We could probably track this and be a bit smarter about when to flush
361 // though.
362 glFlush();
363
364 texture->level_infos_.resize(1);
365 for (size_t i = 0; i < level_infos_.size(); i++) {
366 const LevelInfo& base_info = level_infos_[i][0];
367 const size_t levels_needed = TextureManager::ComputeMipMapCount(
368 base_info.target, base_info.width, base_info.height, base_info.depth);
369 DCHECK(level_infos_.size() <= levels_needed);
370 texture->level_infos_[0].resize(levels_needed);
371 for (size_t n = 0; n < level_infos_.size(); n++) {
372 const LevelInfo& info = level_infos_[i][n];
373 texture->SetLevelInfo(NULL,
374 info.target,
375 i,
376 info.internal_format,
377 info.width,
378 info.height,
379 info.depth,
380 info.border,
381 info.format,
382 info.type,
383 info.cleared);
384 }
385 }
386 if (image_buffer_.get()) {
387 texture->SetLevelImage(
388 NULL,
389 target_,
390 0,
391 new GLImageSync(
392 image_buffer_,
393 gfx::Size(level_infos_[0][0].width, level_infos_[0][0].height)));
394 }
395
396 texture->target_ = target_;
397 texture->SetImmutable(immutable_);
398 texture->min_filter_ = min_filter_;
399 texture->mag_filter_ = mag_filter_;
400 texture->wrap_s_ = wrap_s_;
401 texture->wrap_t_ = wrap_t_;
402 texture->usage_ = usage_;
403 }
404
Matches(const Texture * texture) const405 bool TextureDefinition::Matches(const Texture* texture) const {
406 DCHECK(target_ == texture->target());
407 if (texture->min_filter_ != min_filter_ ||
408 texture->mag_filter_ != mag_filter_ ||
409 texture->wrap_s_ != wrap_s_ ||
410 texture->wrap_t_ != wrap_t_) {
411 return false;
412 }
413
414 // All structural changes should have orphaned the texture.
415 if (image_buffer_.get() && !texture->GetLevelImage(texture->target(), 0))
416 return false;
417
418 return true;
419 }
420
421 } // namespace gles2
422 } // namespace gpu
423