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.
5 #include "gpu/command_buffer/service/texture_definition.h"
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"
17 #if !defined(OS_MACOSX)
18 #include "ui/gl/gl_fence_egl.h"
19 #include "ui/gl/gl_surface_egl.h"
27 class GLImageSync
: public gfx::GLImage
{
29 explicit GLImageSync(const scoped_refptr
<NativeImageBuffer
>& buffer
,
30 const gfx::Size
& size
);
33 virtual void Destroy(bool have_context
) 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 virtual void DidUseTexImage() OVERRIDE
;
41 virtual bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget
,
43 gfx::OverlayTransform transform
,
44 const gfx::Rect
& bounds_rect
,
45 const gfx::RectF
& crop_rect
) OVERRIDE
;
46 virtual void SetReleaseAfterUse() OVERRIDE
;
49 virtual ~GLImageSync();
52 scoped_refptr
<NativeImageBuffer
> buffer_
;
55 DISALLOW_COPY_AND_ASSIGN(GLImageSync
);
58 GLImageSync::GLImageSync(const scoped_refptr
<NativeImageBuffer
>& buffer
,
59 const gfx::Size
& size
)
60 : buffer_(buffer
), size_(size
) {
62 buffer
->AddClient(this);
65 GLImageSync::~GLImageSync() {
67 buffer_
->RemoveClient(this);
70 void GLImageSync::Destroy(bool have_context
) {
73 gfx::Size
GLImageSync::GetSize() {
77 bool GLImageSync::BindTexImage(unsigned target
) {
82 void GLImageSync::ReleaseTexImage(unsigned target
) {
86 void GLImageSync::WillUseTexImage() {
88 buffer_
->WillRead(this);
91 void GLImageSync::DidUseTexImage() {
93 buffer_
->DidRead(this);
96 void GLImageSync::WillModifyTexImage() {
98 buffer_
->WillWrite(this);
101 void GLImageSync::DidModifyTexImage() {
103 buffer_
->DidWrite(this);
106 bool GLImageSync::ScheduleOverlayPlane(gfx::AcceleratedWidget widget
,
108 gfx::OverlayTransform transform
,
109 const gfx::Rect
& bounds_rect
,
110 const gfx::RectF
& crop_rect
) {
115 void GLImageSync::SetReleaseAfterUse() {
119 #if !defined(OS_MACOSX)
120 class NativeImageBufferEGL
: public NativeImageBuffer
{
122 static scoped_refptr
<NativeImageBufferEGL
> Create(GLuint texture_id
);
125 NativeImageBufferEGL(EGLDisplay display
, EGLImageKHR image
);
126 virtual ~NativeImageBufferEGL();
127 virtual void AddClient(gfx::GLImage
* client
) OVERRIDE
;
128 virtual void RemoveClient(gfx::GLImage
* client
) OVERRIDE
;
129 virtual bool IsClient(gfx::GLImage
* client
) OVERRIDE
;
130 virtual void BindToTexture(GLenum target
) OVERRIDE
;
131 virtual void WillRead(gfx::GLImage
* client
) OVERRIDE
;
132 virtual void WillWrite(gfx::GLImage
* client
) OVERRIDE
;
133 virtual void DidRead(gfx::GLImage
* client
) OVERRIDE
;
134 virtual void DidWrite(gfx::GLImage
* client
) OVERRIDE
;
136 EGLDisplay egl_display_
;
137 EGLImageKHR egl_image_
;
142 ClientInfo(gfx::GLImage
* client
);
145 gfx::GLImage
* client
;
146 bool needs_wait_before_read
;
147 linked_ptr
<gfx::GLFence
> read_fence
;
149 std::list
<ClientInfo
> client_infos_
;
150 scoped_ptr
<gfx::GLFence
> write_fence_
;
151 gfx::GLImage
* write_client_
;
153 DISALLOW_COPY_AND_ASSIGN(NativeImageBufferEGL
);
156 scoped_refptr
<NativeImageBufferEGL
> NativeImageBufferEGL::Create(
158 EGLDisplay egl_display
= gfx::GLSurfaceEGL::GetHardwareDisplay();
159 EGLContext egl_context
= eglGetCurrentContext();
161 DCHECK_NE(EGL_NO_CONTEXT
, egl_context
);
162 DCHECK_NE(EGL_NO_DISPLAY
, egl_display
);
163 DCHECK(glIsTexture(texture_id
));
165 DCHECK(gfx::g_driver_egl
.ext
.b_EGL_KHR_image_base
&&
166 gfx::g_driver_egl
.ext
.b_EGL_KHR_gl_texture_2D_image
&&
167 gfx::g_driver_gl
.ext
.b_GL_OES_EGL_image
&&
168 gfx::g_driver_egl
.ext
.b_EGL_KHR_fence_sync
);
170 const EGLint egl_attrib_list
[] = {
171 EGL_GL_TEXTURE_LEVEL_KHR
, 0, EGL_IMAGE_PRESERVED_KHR
, EGL_TRUE
, EGL_NONE
};
172 EGLClientBuffer egl_buffer
= reinterpret_cast<EGLClientBuffer
>(texture_id
);
173 EGLenum egl_target
= EGL_GL_TEXTURE_2D_KHR
; // TODO
175 EGLImageKHR egl_image
= eglCreateImageKHR(
176 egl_display
, egl_context
, egl_target
, egl_buffer
, egl_attrib_list
);
178 if (egl_image
== EGL_NO_IMAGE_KHR
)
181 return new NativeImageBufferEGL(egl_display
, egl_image
);
184 NativeImageBufferEGL::ClientInfo::ClientInfo(gfx::GLImage
* client
)
185 : client(client
), needs_wait_before_read(true) {}
187 NativeImageBufferEGL::ClientInfo::~ClientInfo() {}
189 NativeImageBufferEGL::NativeImageBufferEGL(EGLDisplay display
,
191 : NativeImageBuffer(),
192 egl_display_(display
),
194 write_fence_(new gfx::GLFenceEGL(true)),
195 write_client_(NULL
) {
196 DCHECK(egl_display_
!= EGL_NO_DISPLAY
);
197 DCHECK(egl_image_
!= EGL_NO_IMAGE_KHR
);
200 NativeImageBufferEGL::~NativeImageBufferEGL() {
201 DCHECK(client_infos_
.empty());
202 if (egl_image_
!= EGL_NO_IMAGE_KHR
)
203 eglDestroyImageKHR(egl_display_
, egl_image_
);
206 void NativeImageBufferEGL::AddClient(gfx::GLImage
* client
) {
207 base::AutoLock
lock(lock_
);
208 client_infos_
.push_back(ClientInfo(client
));
211 void NativeImageBufferEGL::RemoveClient(gfx::GLImage
* client
) {
212 base::AutoLock
lock(lock_
);
213 if (write_client_
== client
)
214 write_client_
= NULL
;
215 for (std::list
<ClientInfo
>::iterator it
= client_infos_
.begin();
216 it
!= client_infos_
.end();
218 if (it
->client
== client
) {
219 client_infos_
.erase(it
);
226 bool NativeImageBufferEGL::IsClient(gfx::GLImage
* client
) {
227 base::AutoLock
lock(lock_
);
228 for (std::list
<ClientInfo
>::iterator it
= client_infos_
.begin();
229 it
!= client_infos_
.end();
231 if (it
->client
== client
)
237 void NativeImageBufferEGL::BindToTexture(GLenum target
) {
238 DCHECK(egl_image_
!= EGL_NO_IMAGE_KHR
);
239 glEGLImageTargetTexture2DOES(target
, egl_image_
);
240 DCHECK_EQ(static_cast<EGLint
>(EGL_SUCCESS
), eglGetError());
241 DCHECK_EQ(static_cast<GLenum
>(GL_NO_ERROR
), glGetError());
244 void NativeImageBufferEGL::WillRead(gfx::GLImage
* client
) {
245 base::AutoLock
lock(lock_
);
246 if (!write_fence_
.get() || write_client_
== client
)
249 for (std::list
<ClientInfo
>::iterator it
= client_infos_
.begin();
250 it
!= client_infos_
.end();
252 if (it
->client
== client
) {
253 if (it
->needs_wait_before_read
) {
254 it
->needs_wait_before_read
= false;
255 write_fence_
->ServerWait();
263 void NativeImageBufferEGL::WillWrite(gfx::GLImage
* client
) {
264 base::AutoLock
lock(lock_
);
265 if (write_client_
!= client
)
266 write_fence_
->ServerWait();
268 for (std::list
<ClientInfo
>::iterator it
= client_infos_
.begin();
269 it
!= client_infos_
.end();
271 if (it
->read_fence
.get() && it
->client
!= client
)
272 it
->read_fence
->ServerWait();
276 void NativeImageBufferEGL::DidRead(gfx::GLImage
* client
) {
277 base::AutoLock
lock(lock_
);
278 for (std::list
<ClientInfo
>::iterator it
= client_infos_
.begin();
279 it
!= client_infos_
.end();
281 if (it
->client
== client
) {
282 it
->read_fence
= make_linked_ptr(new gfx::GLFenceEGL(true));
289 void NativeImageBufferEGL::DidWrite(gfx::GLImage
* client
) {
290 base::AutoLock
lock(lock_
);
291 // Sharing semantics require the client to flush in order to make changes
292 // visible to other clients.
293 write_fence_
.reset(new gfx::GLFenceEGL(false));
294 write_client_
= client
;
295 for (std::list
<ClientInfo
>::iterator it
= client_infos_
.begin();
296 it
!= client_infos_
.end();
298 it
->needs_wait_before_read
= true;
304 class NativeImageBufferStub
: public NativeImageBuffer
{
306 NativeImageBufferStub() : NativeImageBuffer() {}
309 virtual ~NativeImageBufferStub() {}
310 virtual void AddClient(gfx::GLImage
* client
) OVERRIDE
{}
311 virtual void RemoveClient(gfx::GLImage
* client
) OVERRIDE
{}
312 virtual bool IsClient(gfx::GLImage
* client
) OVERRIDE
{ return true; }
313 virtual void BindToTexture(GLenum target
) OVERRIDE
{}
314 virtual void WillRead(gfx::GLImage
* client
) OVERRIDE
{}
315 virtual void WillWrite(gfx::GLImage
* client
) OVERRIDE
{}
316 virtual void DidRead(gfx::GLImage
* client
) OVERRIDE
{}
317 virtual void DidWrite(gfx::GLImage
* client
) OVERRIDE
{}
319 DISALLOW_COPY_AND_ASSIGN(NativeImageBufferStub
);
322 } // anonymous namespace
325 scoped_refptr
<NativeImageBuffer
> NativeImageBuffer::Create(GLuint texture_id
) {
326 switch (gfx::GetGLImplementation()) {
327 #if !defined(OS_MACOSX)
328 case gfx::kGLImplementationEGLGLES2
:
329 return NativeImageBufferEGL::Create(texture_id
);
331 case gfx::kGLImplementationMockGL
:
332 return new NativeImageBufferStub
;
339 TextureDefinition::LevelInfo::LevelInfo(GLenum target
,
340 GLenum internal_format
,
349 internal_format(internal_format
),
358 TextureDefinition::LevelInfo::~LevelInfo() {}
360 TextureDefinition::TextureDefinition(
363 unsigned int version
,
364 const scoped_refptr
<NativeImageBuffer
>& image_buffer
)
367 image_buffer_(image_buffer
.get()
369 : NativeImageBuffer::Create(texture
->service_id())),
370 min_filter_(texture
->min_filter()),
371 mag_filter_(texture
->mag_filter()),
372 wrap_s_(texture
->wrap_s()),
373 wrap_t_(texture
->wrap_t()),
374 usage_(texture
->usage()),
375 immutable_(texture
->IsImmutable()) {
377 DCHECK(!texture
->level_infos_
.empty());
378 DCHECK(!texture
->level_infos_
[0].empty());
379 DCHECK(!texture
->NeedsMips());
380 DCHECK(texture
->level_infos_
[0][0].width
);
381 DCHECK(texture
->level_infos_
[0][0].height
);
383 scoped_refptr
<gfx::GLImage
> gl_image(
384 new GLImageSync(image_buffer_
,
385 gfx::Size(texture
->level_infos_
[0][0].width
,
386 texture
->level_infos_
[0][0].height
)));
387 texture
->SetLevelImage(NULL
, target
, 0, gl_image
.get());
390 level_infos_
.clear();
391 const Texture::LevelInfo
& level
= texture
->level_infos_
[0][0];
392 LevelInfo
info(level
.target
,
393 level
.internal_format
,
401 std::vector
<LevelInfo
> infos
;
402 infos
.push_back(info
);
403 level_infos_
.push_back(infos
);
406 TextureDefinition::~TextureDefinition() {
409 Texture
* TextureDefinition::CreateTexture() const {
410 if (!image_buffer_
.get())
414 glGenTextures(1, &texture_id
);
416 Texture
* texture(new Texture(texture_id
));
417 UpdateTexture(texture
);
422 void TextureDefinition::UpdateTexture(Texture
* texture
) const {
423 gfx::ScopedTextureBinder
texture_binder(target_
, texture
->service_id());
424 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, min_filter_
);
425 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, mag_filter_
);
426 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, wrap_s_
);
427 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, wrap_t_
);
428 if (image_buffer_
.get())
429 image_buffer_
->BindToTexture(target_
);
430 // We have to make sure the changes are visible to other clients in this share
431 // group. As far as the clients are concerned, the mailbox semantics only
432 // demand a single flush from the client after changes are first made,
433 // and it is not visible to them when another share group boundary is crossed.
434 // We could probably track this and be a bit smarter about when to flush
438 texture
->level_infos_
.resize(1);
439 for (size_t i
= 0; i
< level_infos_
.size(); i
++) {
440 const LevelInfo
& base_info
= level_infos_
[i
][0];
441 const size_t levels_needed
= TextureManager::ComputeMipMapCount(
442 base_info
.target
, base_info
.width
, base_info
.height
, base_info
.depth
);
443 DCHECK(level_infos_
.size() <= levels_needed
);
444 texture
->level_infos_
[0].resize(levels_needed
);
445 for (size_t n
= 0; n
< level_infos_
.size(); n
++) {
446 const LevelInfo
& info
= level_infos_
[i
][n
];
447 texture
->SetLevelInfo(NULL
,
450 info
.internal_format
,
460 if (image_buffer_
.get()) {
461 texture
->SetLevelImage(
467 gfx::Size(level_infos_
[0][0].width
, level_infos_
[0][0].height
)));
470 texture
->target_
= target_
;
471 texture
->SetImmutable(immutable_
);
472 texture
->min_filter_
= min_filter_
;
473 texture
->mag_filter_
= mag_filter_
;
474 texture
->wrap_s_
= wrap_s_
;
475 texture
->wrap_t_
= wrap_t_
;
476 texture
->usage_
= usage_
;
479 bool TextureDefinition::Matches(const Texture
* texture
) const {
480 DCHECK(target_
== texture
->target());
481 if (texture
->min_filter_
!= min_filter_
||
482 texture
->mag_filter_
!= mag_filter_
||
483 texture
->wrap_s_
!= wrap_s_
||
484 texture
->wrap_t_
!= wrap_t_
) {
488 // All structural changes should have orphaned the texture.
489 if (image_buffer_
.get() && !texture
->GetLevelImage(texture
->target(), 0))