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 bool CopyTexImage(unsigned target
) OVERRIDE
;
38 virtual void WillUseTexImage() OVERRIDE
;
39 virtual void WillModifyTexImage() OVERRIDE
;
40 virtual void DidModifyTexImage() OVERRIDE
;
41 virtual void DidUseTexImage() OVERRIDE
;
42 virtual bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget
,
44 gfx::OverlayTransform transform
,
45 const gfx::Rect
& bounds_rect
,
46 const gfx::RectF
& crop_rect
) OVERRIDE
;
47 virtual void SetReleaseAfterUse() OVERRIDE
;
50 virtual ~GLImageSync();
53 scoped_refptr
<NativeImageBuffer
> buffer_
;
56 DISALLOW_COPY_AND_ASSIGN(GLImageSync
);
59 GLImageSync::GLImageSync(const scoped_refptr
<NativeImageBuffer
>& buffer
,
60 const gfx::Size
& size
)
61 : buffer_(buffer
), size_(size
) {
63 buffer
->AddClient(this);
66 GLImageSync::~GLImageSync() {
68 buffer_
->RemoveClient(this);
71 void GLImageSync::Destroy(bool have_context
) {
74 gfx::Size
GLImageSync::GetSize() {
78 bool GLImageSync::BindTexImage(unsigned target
) {
83 void GLImageSync::ReleaseTexImage(unsigned target
) {
87 bool GLImageSync::CopyTexImage(unsigned target
) {
91 void GLImageSync::WillUseTexImage() {
93 buffer_
->WillRead(this);
96 void GLImageSync::DidUseTexImage() {
98 buffer_
->DidRead(this);
101 void GLImageSync::WillModifyTexImage() {
103 buffer_
->WillWrite(this);
106 void GLImageSync::DidModifyTexImage() {
108 buffer_
->DidWrite(this);
111 bool GLImageSync::ScheduleOverlayPlane(gfx::AcceleratedWidget widget
,
113 gfx::OverlayTransform transform
,
114 const gfx::Rect
& bounds_rect
,
115 const gfx::RectF
& crop_rect
) {
120 void GLImageSync::SetReleaseAfterUse() {
124 #if !defined(OS_MACOSX)
125 class NativeImageBufferEGL
: public NativeImageBuffer
{
127 static scoped_refptr
<NativeImageBufferEGL
> Create(GLuint texture_id
);
130 NativeImageBufferEGL(EGLDisplay display
, EGLImageKHR image
);
131 virtual ~NativeImageBufferEGL();
132 virtual void AddClient(gfx::GLImage
* client
) OVERRIDE
;
133 virtual void RemoveClient(gfx::GLImage
* client
) OVERRIDE
;
134 virtual bool IsClient(gfx::GLImage
* client
) OVERRIDE
;
135 virtual void BindToTexture(GLenum target
) OVERRIDE
;
136 virtual void WillRead(gfx::GLImage
* client
) OVERRIDE
;
137 virtual void WillWrite(gfx::GLImage
* client
) OVERRIDE
;
138 virtual void DidRead(gfx::GLImage
* client
) OVERRIDE
;
139 virtual void DidWrite(gfx::GLImage
* client
) OVERRIDE
;
141 EGLDisplay egl_display_
;
142 EGLImageKHR egl_image_
;
147 ClientInfo(gfx::GLImage
* client
);
150 gfx::GLImage
* client
;
151 bool needs_wait_before_read
;
152 linked_ptr
<gfx::GLFence
> read_fence
;
154 std::list
<ClientInfo
> client_infos_
;
155 scoped_ptr
<gfx::GLFence
> write_fence_
;
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
&&
173 gfx::g_driver_egl
.ext
.b_EGL_KHR_fence_sync
);
175 const EGLint egl_attrib_list
[] = {
176 EGL_GL_TEXTURE_LEVEL_KHR
, 0, EGL_IMAGE_PRESERVED_KHR
, EGL_TRUE
, EGL_NONE
};
177 EGLClientBuffer egl_buffer
= reinterpret_cast<EGLClientBuffer
>(texture_id
);
178 EGLenum egl_target
= EGL_GL_TEXTURE_2D_KHR
; // TODO
180 EGLImageKHR egl_image
= eglCreateImageKHR(
181 egl_display
, egl_context
, egl_target
, egl_buffer
, egl_attrib_list
);
183 if (egl_image
== EGL_NO_IMAGE_KHR
)
186 return new NativeImageBufferEGL(egl_display
, egl_image
);
189 NativeImageBufferEGL::ClientInfo::ClientInfo(gfx::GLImage
* client
)
190 : client(client
), needs_wait_before_read(true) {}
192 NativeImageBufferEGL::ClientInfo::~ClientInfo() {}
194 NativeImageBufferEGL::NativeImageBufferEGL(EGLDisplay display
,
196 : NativeImageBuffer(),
197 egl_display_(display
),
199 write_fence_(new gfx::GLFenceEGL(true)),
200 write_client_(NULL
) {
201 DCHECK(egl_display_
!= EGL_NO_DISPLAY
);
202 DCHECK(egl_image_
!= EGL_NO_IMAGE_KHR
);
205 NativeImageBufferEGL::~NativeImageBufferEGL() {
206 DCHECK(client_infos_
.empty());
207 if (egl_image_
!= EGL_NO_IMAGE_KHR
)
208 eglDestroyImageKHR(egl_display_
, egl_image_
);
211 void NativeImageBufferEGL::AddClient(gfx::GLImage
* client
) {
212 base::AutoLock
lock(lock_
);
213 client_infos_
.push_back(ClientInfo(client
));
216 void NativeImageBufferEGL::RemoveClient(gfx::GLImage
* client
) {
217 base::AutoLock
lock(lock_
);
218 if (write_client_
== client
)
219 write_client_
= NULL
;
220 for (std::list
<ClientInfo
>::iterator it
= client_infos_
.begin();
221 it
!= client_infos_
.end();
223 if (it
->client
== client
) {
224 client_infos_
.erase(it
);
231 bool NativeImageBufferEGL::IsClient(gfx::GLImage
* client
) {
232 base::AutoLock
lock(lock_
);
233 for (std::list
<ClientInfo
>::iterator it
= client_infos_
.begin();
234 it
!= client_infos_
.end();
236 if (it
->client
== client
)
242 void NativeImageBufferEGL::BindToTexture(GLenum target
) {
243 DCHECK(egl_image_
!= EGL_NO_IMAGE_KHR
);
244 glEGLImageTargetTexture2DOES(target
, egl_image_
);
245 DCHECK_EQ(static_cast<EGLint
>(EGL_SUCCESS
), eglGetError());
246 DCHECK_EQ(static_cast<GLenum
>(GL_NO_ERROR
), glGetError());
249 void NativeImageBufferEGL::WillRead(gfx::GLImage
* client
) {
250 base::AutoLock
lock(lock_
);
251 if (!write_fence_
.get() || write_client_
== client
)
254 for (std::list
<ClientInfo
>::iterator it
= client_infos_
.begin();
255 it
!= client_infos_
.end();
257 if (it
->client
== client
) {
258 if (it
->needs_wait_before_read
) {
259 it
->needs_wait_before_read
= false;
260 write_fence_
->ServerWait();
268 void NativeImageBufferEGL::WillWrite(gfx::GLImage
* client
) {
269 base::AutoLock
lock(lock_
);
270 if (write_client_
!= client
)
271 write_fence_
->ServerWait();
273 for (std::list
<ClientInfo
>::iterator it
= client_infos_
.begin();
274 it
!= client_infos_
.end();
276 if (it
->read_fence
.get() && it
->client
!= client
)
277 it
->read_fence
->ServerWait();
281 void NativeImageBufferEGL::DidRead(gfx::GLImage
* client
) {
282 base::AutoLock
lock(lock_
);
283 for (std::list
<ClientInfo
>::iterator it
= client_infos_
.begin();
284 it
!= client_infos_
.end();
286 if (it
->client
== client
) {
287 it
->read_fence
= make_linked_ptr(new gfx::GLFenceEGL(true));
294 void NativeImageBufferEGL::DidWrite(gfx::GLImage
* client
) {
295 base::AutoLock
lock(lock_
);
296 // Sharing semantics require the client to flush in order to make changes
297 // visible to other clients.
298 write_fence_
.reset(new gfx::GLFenceEGL(false));
299 write_client_
= client
;
300 for (std::list
<ClientInfo
>::iterator it
= client_infos_
.begin();
301 it
!= client_infos_
.end();
303 it
->needs_wait_before_read
= true;
309 class NativeImageBufferStub
: public NativeImageBuffer
{
311 NativeImageBufferStub() : NativeImageBuffer() {}
314 virtual ~NativeImageBufferStub() {}
315 virtual void AddClient(gfx::GLImage
* client
) OVERRIDE
{}
316 virtual void RemoveClient(gfx::GLImage
* client
) OVERRIDE
{}
317 virtual bool IsClient(gfx::GLImage
* client
) OVERRIDE
{ return true; }
318 virtual void BindToTexture(GLenum target
) OVERRIDE
{}
319 virtual void WillRead(gfx::GLImage
* client
) OVERRIDE
{}
320 virtual void WillWrite(gfx::GLImage
* client
) OVERRIDE
{}
321 virtual void DidRead(gfx::GLImage
* client
) OVERRIDE
{}
322 virtual void DidWrite(gfx::GLImage
* client
) OVERRIDE
{}
324 DISALLOW_COPY_AND_ASSIGN(NativeImageBufferStub
);
327 } // anonymous namespace
330 scoped_refptr
<NativeImageBuffer
> NativeImageBuffer::Create(GLuint texture_id
) {
331 switch (gfx::GetGLImplementation()) {
332 #if !defined(OS_MACOSX)
333 case gfx::kGLImplementationEGLGLES2
:
334 return NativeImageBufferEGL::Create(texture_id
);
336 case gfx::kGLImplementationMockGL
:
337 return new NativeImageBufferStub
;
344 TextureDefinition::LevelInfo::LevelInfo(GLenum target
,
345 GLenum internal_format
,
354 internal_format(internal_format
),
363 TextureDefinition::LevelInfo::~LevelInfo() {}
365 TextureDefinition::TextureDefinition(
368 unsigned int version
,
369 const scoped_refptr
<NativeImageBuffer
>& image_buffer
)
372 image_buffer_(image_buffer
.get()
374 : NativeImageBuffer::Create(texture
->service_id())),
375 min_filter_(texture
->min_filter()),
376 mag_filter_(texture
->mag_filter()),
377 wrap_s_(texture
->wrap_s()),
378 wrap_t_(texture
->wrap_t()),
379 usage_(texture
->usage()),
380 immutable_(texture
->IsImmutable()) {
382 DCHECK(!texture
->level_infos_
.empty());
383 DCHECK(!texture
->level_infos_
[0].empty());
384 DCHECK(!texture
->NeedsMips());
385 DCHECK(texture
->level_infos_
[0][0].width
);
386 DCHECK(texture
->level_infos_
[0][0].height
);
388 scoped_refptr
<gfx::GLImage
> gl_image(
389 new GLImageSync(image_buffer_
,
390 gfx::Size(texture
->level_infos_
[0][0].width
,
391 texture
->level_infos_
[0][0].height
)));
392 texture
->SetLevelImage(NULL
, target
, 0, gl_image
.get());
395 level_infos_
.clear();
396 const Texture::LevelInfo
& level
= texture
->level_infos_
[0][0];
397 LevelInfo
info(level
.target
,
398 level
.internal_format
,
406 std::vector
<LevelInfo
> infos
;
407 infos
.push_back(info
);
408 level_infos_
.push_back(infos
);
411 TextureDefinition::~TextureDefinition() {
414 Texture
* TextureDefinition::CreateTexture() const {
415 if (!image_buffer_
.get())
419 glGenTextures(1, &texture_id
);
421 Texture
* texture(new Texture(texture_id
));
422 UpdateTexture(texture
);
427 void TextureDefinition::UpdateTexture(Texture
* texture
) const {
428 gfx::ScopedTextureBinder
texture_binder(target_
, texture
->service_id());
429 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, min_filter_
);
430 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, mag_filter_
);
431 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, wrap_s_
);
432 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, wrap_t_
);
433 if (image_buffer_
.get())
434 image_buffer_
->BindToTexture(target_
);
435 // We have to make sure the changes are visible to other clients in this share
436 // group. As far as the clients are concerned, the mailbox semantics only
437 // demand a single flush from the client after changes are first made,
438 // and it is not visible to them when another share group boundary is crossed.
439 // We could probably track this and be a bit smarter about when to flush
443 texture
->level_infos_
.resize(1);
444 for (size_t i
= 0; i
< level_infos_
.size(); i
++) {
445 const LevelInfo
& base_info
= level_infos_
[i
][0];
446 const size_t levels_needed
= TextureManager::ComputeMipMapCount(
447 base_info
.target
, base_info
.width
, base_info
.height
, base_info
.depth
);
448 DCHECK(level_infos_
.size() <= levels_needed
);
449 texture
->level_infos_
[0].resize(levels_needed
);
450 for (size_t n
= 0; n
< level_infos_
.size(); n
++) {
451 const LevelInfo
& info
= level_infos_
[i
][n
];
452 texture
->SetLevelInfo(NULL
,
455 info
.internal_format
,
465 if (image_buffer_
.get()) {
466 texture
->SetLevelImage(
472 gfx::Size(level_infos_
[0][0].width
, level_infos_
[0][0].height
)));
475 texture
->target_
= target_
;
476 texture
->SetImmutable(immutable_
);
477 texture
->min_filter_
= min_filter_
;
478 texture
->mag_filter_
= mag_filter_
;
479 texture
->wrap_s_
= wrap_s_
;
480 texture
->wrap_t_
= wrap_t_
;
481 texture
->usage_
= usage_
;
484 bool TextureDefinition::Matches(const Texture
* texture
) const {
485 DCHECK(target_
== texture
->target());
486 if (texture
->min_filter_
!= min_filter_
||
487 texture
->mag_filter_
!= mag_filter_
||
488 texture
->wrap_s_
!= wrap_s_
||
489 texture
->wrap_t_
!= wrap_t_
) {
493 // All structural changes should have orphaned the texture.
494 if (image_buffer_
.get() && !texture
->GetLevelImage(texture
->target(), 0))