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/lazy_instance.h"
10 #include "base/memory/linked_ptr.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/synchronization/lock.h"
13 #include "base/threading/thread_local.h"
14 #include "gpu/command_buffer/service/texture_manager.h"
15 #include "ui/gl/gl_image.h"
16 #include "ui/gl/gl_implementation.h"
17 #include "ui/gl/scoped_binders.h"
19 #if !defined(OS_MACOSX)
20 #include "ui/gl/gl_surface_egl.h"
28 class GLImageSync
: public gfx::GLImage
{
30 explicit GLImageSync(const scoped_refptr
<NativeImageBuffer
>& buffer
,
31 const gfx::Size
& size
);
34 void Destroy(bool have_context
) override
;
35 gfx::Size
GetSize() override
;
36 unsigned GetInternalFormat() override
;
37 bool BindTexImage(unsigned target
) override
;
38 void ReleaseTexImage(unsigned target
) override
;
39 bool CopyTexSubImage(unsigned target
,
40 const gfx::Point
& offset
,
41 const gfx::Rect
& rect
) override
;
42 void WillUseTexImage() override
;
43 void WillModifyTexImage() override
;
44 void DidModifyTexImage() override
;
45 void DidUseTexImage() override
;
46 bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget
,
48 gfx::OverlayTransform transform
,
49 const gfx::Rect
& bounds_rect
,
50 const gfx::RectF
& crop_rect
) override
;
51 void OnMemoryDump(base::trace_event::ProcessMemoryDump
* pmd
,
52 uint64_t process_tracing_id
,
53 const std::string
& dump_name
) override
;
56 ~GLImageSync() override
;
59 scoped_refptr
<NativeImageBuffer
> buffer_
;
62 DISALLOW_COPY_AND_ASSIGN(GLImageSync
);
65 GLImageSync::GLImageSync(const scoped_refptr
<NativeImageBuffer
>& buffer
,
66 const gfx::Size
& size
)
67 : buffer_(buffer
), size_(size
) {
69 buffer
->AddClient(this);
72 GLImageSync::~GLImageSync() {
74 buffer_
->RemoveClient(this);
77 void GLImageSync::Destroy(bool have_context
) {
80 gfx::Size
GLImageSync::GetSize() {
84 unsigned GLImageSync::GetInternalFormat() {
88 bool GLImageSync::BindTexImage(unsigned target
) {
93 void GLImageSync::ReleaseTexImage(unsigned target
) {
97 bool GLImageSync::CopyTexSubImage(unsigned target
,
98 const gfx::Point
& offset
,
99 const gfx::Rect
& rect
) {
103 void GLImageSync::WillUseTexImage() {
106 void GLImageSync::DidUseTexImage() {
109 void GLImageSync::WillModifyTexImage() {
112 void GLImageSync::DidModifyTexImage() {
115 bool GLImageSync::ScheduleOverlayPlane(gfx::AcceleratedWidget widget
,
117 gfx::OverlayTransform transform
,
118 const gfx::Rect
& bounds_rect
,
119 const gfx::RectF
& crop_rect
) {
124 void GLImageSync::OnMemoryDump(base::trace_event::ProcessMemoryDump
* pmd
,
125 uint64_t process_tracing_id
,
126 const std::string
& dump_name
) {
127 // TODO(ericrk): Implement GLImage OnMemoryDump. crbug.com/514914
130 #if !defined(OS_MACOSX)
131 class NativeImageBufferEGL
: public NativeImageBuffer
{
133 static scoped_refptr
<NativeImageBufferEGL
> Create(GLuint texture_id
);
136 NativeImageBufferEGL(EGLDisplay display
, EGLImageKHR image
);
137 ~NativeImageBufferEGL() override
;
138 void AddClient(gfx::GLImage
* client
) override
;
139 void RemoveClient(gfx::GLImage
* client
) override
;
140 bool IsClient(gfx::GLImage
* client
) override
;
141 void BindToTexture(GLenum target
) const override
;
143 const EGLDisplay egl_display_
;
144 const EGLImageKHR egl_image_
;
149 explicit ClientInfo(gfx::GLImage
* client
);
152 gfx::GLImage
* client
;
153 bool needs_wait_before_read
;
155 std::list
<ClientInfo
> client_infos_
;
156 gfx::GLImage
* write_client_
;
158 DISALLOW_COPY_AND_ASSIGN(NativeImageBufferEGL
);
161 scoped_refptr
<NativeImageBufferEGL
> NativeImageBufferEGL::Create(
163 EGLDisplay egl_display
= gfx::GLSurfaceEGL::GetHardwareDisplay();
164 EGLContext egl_context
= eglGetCurrentContext();
166 DCHECK_NE(EGL_NO_CONTEXT
, egl_context
);
167 DCHECK_NE(EGL_NO_DISPLAY
, egl_display
);
168 DCHECK(glIsTexture(texture_id
));
170 DCHECK(gfx::g_driver_egl
.ext
.b_EGL_KHR_image_base
&&
171 gfx::g_driver_egl
.ext
.b_EGL_KHR_gl_texture_2D_image
&&
172 gfx::g_driver_gl
.ext
.b_GL_OES_EGL_image
);
174 const EGLint egl_attrib_list
[] = {
175 EGL_GL_TEXTURE_LEVEL_KHR
, 0, EGL_IMAGE_PRESERVED_KHR
, EGL_TRUE
, EGL_NONE
};
176 EGLClientBuffer egl_buffer
= reinterpret_cast<EGLClientBuffer
>(texture_id
);
177 EGLenum egl_target
= EGL_GL_TEXTURE_2D_KHR
;
179 EGLImageKHR egl_image
= eglCreateImageKHR(
180 egl_display
, egl_context
, egl_target
, egl_buffer
, egl_attrib_list
);
182 if (egl_image
== EGL_NO_IMAGE_KHR
) {
183 LOG(ERROR
) << "eglCreateImageKHR for cross-thread sharing failed: 0x"
184 << std::hex
<< eglGetError();
188 return new NativeImageBufferEGL(egl_display
, egl_image
);
191 NativeImageBufferEGL::ClientInfo::ClientInfo(gfx::GLImage
* client
)
192 : client(client
), needs_wait_before_read(true) {}
194 NativeImageBufferEGL::ClientInfo::~ClientInfo() {}
196 NativeImageBufferEGL::NativeImageBufferEGL(EGLDisplay display
,
198 : NativeImageBuffer(),
199 egl_display_(display
),
201 write_client_(NULL
) {
202 DCHECK(egl_display_
!= EGL_NO_DISPLAY
);
203 DCHECK(egl_image_
!= EGL_NO_IMAGE_KHR
);
206 NativeImageBufferEGL::~NativeImageBufferEGL() {
207 DCHECK(client_infos_
.empty());
208 if (egl_image_
!= EGL_NO_IMAGE_KHR
)
209 eglDestroyImageKHR(egl_display_
, egl_image_
);
212 void NativeImageBufferEGL::AddClient(gfx::GLImage
* client
) {
213 base::AutoLock
lock(lock_
);
214 client_infos_
.push_back(ClientInfo(client
));
217 void NativeImageBufferEGL::RemoveClient(gfx::GLImage
* client
) {
218 base::AutoLock
lock(lock_
);
219 if (write_client_
== client
)
220 write_client_
= NULL
;
221 for (std::list
<ClientInfo
>::iterator it
= client_infos_
.begin();
222 it
!= client_infos_
.end();
224 if (it
->client
== client
) {
225 client_infos_
.erase(it
);
232 bool NativeImageBufferEGL::IsClient(gfx::GLImage
* client
) {
233 base::AutoLock
lock(lock_
);
234 for (std::list
<ClientInfo
>::iterator it
= client_infos_
.begin();
235 it
!= client_infos_
.end();
237 if (it
->client
== client
)
243 void NativeImageBufferEGL::BindToTexture(GLenum target
) const {
244 DCHECK(egl_image_
!= EGL_NO_IMAGE_KHR
);
245 glEGLImageTargetTexture2DOES(target
, egl_image_
);
246 DCHECK_EQ(static_cast<EGLint
>(EGL_SUCCESS
), eglGetError());
247 DCHECK_EQ(static_cast<GLenum
>(GL_NO_ERROR
), glGetError());
252 class NativeImageBufferStub
: public NativeImageBuffer
{
254 NativeImageBufferStub() : NativeImageBuffer() {}
257 ~NativeImageBufferStub() override
{}
258 void AddClient(gfx::GLImage
* client
) override
{}
259 void RemoveClient(gfx::GLImage
* client
) override
{}
260 bool IsClient(gfx::GLImage
* client
) override
{ return true; }
261 void BindToTexture(GLenum target
) const override
{}
263 DISALLOW_COPY_AND_ASSIGN(NativeImageBufferStub
);
266 bool g_avoid_egl_target_texture_reuse
= false;
269 base::LazyInstance
<base::ThreadLocalBoolean
> g_inside_scoped_update_texture
;
272 } // anonymous namespace
275 scoped_refptr
<NativeImageBuffer
> NativeImageBuffer::Create(GLuint texture_id
) {
276 switch (gfx::GetGLImplementation()) {
277 #if !defined(OS_MACOSX)
278 case gfx::kGLImplementationEGLGLES2
:
279 return NativeImageBufferEGL::Create(texture_id
);
281 case gfx::kGLImplementationMockGL
:
282 return new NativeImageBufferStub
;
290 void TextureDefinition::AvoidEGLTargetTextureReuse() {
291 g_avoid_egl_target_texture_reuse
= true;
294 ScopedUpdateTexture::ScopedUpdateTexture() {
296 DCHECK(!g_inside_scoped_update_texture
.Get().Get());
297 g_inside_scoped_update_texture
.Get().Set(true);
301 ScopedUpdateTexture::~ScopedUpdateTexture() {
303 DCHECK(g_inside_scoped_update_texture
.Get().Get());
304 g_inside_scoped_update_texture
.Get().Set(false);
306 // We have to make sure the changes are visible to other clients in this share
307 // group. As far as the clients are concerned, the mailbox semantics only
308 // demand a single flush from the client after changes are first made,
309 // and it is not visible to them when another share group boundary is crossed.
310 // We could probably track this and be a bit smarter about when to flush
315 TextureDefinition::LevelInfo::LevelInfo()
326 TextureDefinition::LevelInfo::LevelInfo(GLenum target
,
327 GLenum internal_format
,
334 const gfx::Rect
& cleared_rect
)
336 internal_format(internal_format
),
343 cleared_rect(cleared_rect
) {
346 TextureDefinition::LevelInfo::~LevelInfo() {}
348 TextureDefinition::TextureDefinition()
359 TextureDefinition::TextureDefinition(
361 unsigned int version
,
362 const scoped_refptr
<NativeImageBuffer
>& image_buffer
)
364 target_(texture
->target()),
365 image_buffer_(image_buffer
),
366 min_filter_(texture
->min_filter()),
367 mag_filter_(texture
->mag_filter()),
368 wrap_s_(texture
->wrap_s()),
369 wrap_t_(texture
->wrap_t()),
370 usage_(texture
->usage()),
371 immutable_(texture
->IsImmutable()),
372 defined_(texture
->IsDefined()) {
373 DCHECK_IMPLIES(image_buffer_
.get(), defined_
);
374 if (!image_buffer_
.get() && defined_
) {
375 image_buffer_
= NativeImageBuffer::Create(texture
->service_id());
376 DCHECK(image_buffer_
.get());
379 const Texture::FaceInfo
& first_face
= texture
->face_infos_
[0];
380 if (image_buffer_
.get()) {
381 scoped_refptr
<gfx::GLImage
> gl_image(
382 new GLImageSync(image_buffer_
,
383 gfx::Size(first_face
.level_infos
[0].width
,
384 first_face
.level_infos
[0].height
)));
385 texture
->SetLevelImage(NULL
, target_
, 0, gl_image
.get());
388 const Texture::LevelInfo
& level
= first_face
.level_infos
[0];
389 level_info_
= LevelInfo(level
.target
, level
.internal_format
, level
.width
,
390 level
.height
, level
.depth
, level
.border
, level
.format
,
391 level
.type
, level
.cleared_rect
);
394 TextureDefinition::~TextureDefinition() {
397 Texture
* TextureDefinition::CreateTexture() const {
399 glGenTextures(1, &texture_id
);
401 Texture
* texture(new Texture(texture_id
));
402 ScopedUpdateTexture scoped_update_texture
;
403 UpdateTextureInternal(texture
);
408 void TextureDefinition::UpdateTextureInternal(Texture
* texture
) const {
410 DCHECK(g_inside_scoped_update_texture
.Get().Get());
412 gfx::ScopedTextureBinder
texture_binder(target_
, texture
->service_id());
413 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, min_filter_
);
414 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, mag_filter_
);
415 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, wrap_s_
);
416 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, wrap_t_
);
418 if (image_buffer_
.get()) {
419 gfx::GLImage
* existing_image
= texture
->GetLevelImage(target_
, 0);
420 // Don't need to re-bind if already bound before.
421 if (!existing_image
|| !image_buffer_
->IsClient(existing_image
)) {
422 image_buffer_
->BindToTexture(target_
);
427 texture
->face_infos_
.resize(1);
428 texture
->face_infos_
[0].level_infos
.resize(1);
429 texture
->SetLevelInfo(NULL
, level_info_
.target
, 0,
430 level_info_
.internal_format
, level_info_
.width
,
431 level_info_
.height
, level_info_
.depth
,
432 level_info_
.border
, level_info_
.format
,
433 level_info_
.type
, level_info_
.cleared_rect
);
436 if (image_buffer_
.get()) {
437 texture
->SetLevelImage(
443 gfx::Size(level_info_
.width
, level_info_
.height
)));
446 texture
->target_
= target_
;
447 texture
->SetImmutable(immutable_
);
448 texture
->min_filter_
= min_filter_
;
449 texture
->mag_filter_
= mag_filter_
;
450 texture
->wrap_s_
= wrap_s_
;
451 texture
->wrap_t_
= wrap_t_
;
452 texture
->usage_
= usage_
;
455 void TextureDefinition::UpdateTexture(Texture
* texture
) const {
456 GLuint old_service_id
= 0u;
457 if (image_buffer_
.get() && g_avoid_egl_target_texture_reuse
) {
458 GLuint service_id
= 0u;
459 glGenTextures(1, &service_id
);
460 old_service_id
= texture
->service_id();
461 texture
->SetServiceId(service_id
);
463 DCHECK_EQ(static_cast<GLenum
>(GL_TEXTURE_2D
), target_
);
465 glGetIntegerv(GL_TEXTURE_BINDING_2D
, &bound_id
);
466 if (bound_id
== static_cast<GLint
>(old_service_id
)) {
467 glBindTexture(target_
, service_id
);
469 texture
->SetLevelImage(NULL
, target_
, 0, NULL
);
472 UpdateTextureInternal(texture
);
474 if (old_service_id
) {
475 glDeleteTextures(1, &old_service_id
);
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_
||
485 texture
->SafeToRenderFrom() != SafeToRenderFrom()) {
489 // Texture became defined.
490 if (!image_buffer_
.get() && texture
->IsDefined())
493 // All structural changes should have orphaned the texture.
494 if (image_buffer_
.get() && !texture
->GetLevelImage(texture
->target(), 0))
500 bool TextureDefinition::SafeToRenderFrom() const {
501 return level_info_
.cleared_rect
.Contains(
502 gfx::Rect(level_info_
.width
, level_info_
.height
));