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"
7 #include "gpu/command_buffer/service/texture_manager.h"
8 #include "ui/gl/gl_image.h"
9 #include "ui/gl/gl_implementation.h"
10 #include "ui/gl/scoped_binders.h"
12 #if !defined(OS_MACOSX)
13 #include "ui/gl/gl_surface_egl.h"
21 class GLImageSync
: public gfx::GLImage
{
24 const scoped_refptr
<NativeImageBuffer
>& buffer
);
27 virtual void Destroy() OVERRIDE
;
28 virtual gfx::Size
GetSize() OVERRIDE
;
29 virtual bool BindTexImage(unsigned target
) OVERRIDE
;
30 virtual void ReleaseTexImage(unsigned target
) OVERRIDE
;
31 virtual void WillUseTexImage() OVERRIDE
;
32 virtual void WillModifyTexImage() OVERRIDE
;
33 virtual void DidModifyTexImage() OVERRIDE
;
35 virtual void DidUseTexImage() OVERRIDE
;
36 virtual void SetReleaseAfterUse() OVERRIDE
;
39 virtual ~GLImageSync();
42 scoped_refptr
<NativeImageBuffer
> buffer_
;
44 DISALLOW_COPY_AND_ASSIGN(GLImageSync
);
47 GLImageSync::GLImageSync(const scoped_refptr
<NativeImageBuffer
>& buffer
)
50 buffer
->AddClient(this);
53 GLImageSync::~GLImageSync() {
55 buffer_
->RemoveClient(this);
58 void GLImageSync::Destroy() {}
60 gfx::Size
GLImageSync::GetSize() {
65 bool GLImageSync::BindTexImage(unsigned target
) {
70 void GLImageSync::ReleaseTexImage(unsigned target
) {
74 void GLImageSync::WillUseTexImage() {
76 buffer_
->WillRead(this);
79 void GLImageSync::DidUseTexImage() {
81 buffer_
->DidRead(this);
84 void GLImageSync::WillModifyTexImage() {
86 buffer_
->WillWrite(this);
89 void GLImageSync::DidModifyTexImage() {
91 buffer_
->DidWrite(this);
94 void GLImageSync::SetReleaseAfterUse() {
98 #if !defined(OS_MACOSX)
99 class NativeImageBufferEGL
: public NativeImageBuffer
{
101 static scoped_refptr
<NativeImageBufferEGL
> Create(GLuint texture_id
);
104 NativeImageBufferEGL(scoped_ptr
<gfx::GLFence
> write_fence
,
107 virtual ~NativeImageBufferEGL();
108 virtual void BindToTexture(GLenum target
) OVERRIDE
;
110 EGLDisplay egl_display_
;
111 EGLImageKHR egl_image_
;
113 DISALLOW_COPY_AND_ASSIGN(NativeImageBufferEGL
);
116 scoped_refptr
<NativeImageBufferEGL
> NativeImageBufferEGL::Create(
118 EGLDisplay egl_display
= gfx::GLSurfaceEGL::GetHardwareDisplay();
119 EGLContext egl_context
= eglGetCurrentContext();
121 DCHECK_NE(EGL_NO_CONTEXT
, egl_context
);
122 DCHECK_NE(EGL_NO_DISPLAY
, egl_display
);
123 DCHECK(glIsTexture(texture_id
));
125 DCHECK(gfx::g_driver_egl
.ext
.b_EGL_KHR_image_base
&&
126 gfx::g_driver_egl
.ext
.b_EGL_KHR_gl_texture_2D_image
&&
127 gfx::g_driver_gl
.ext
.b_GL_OES_EGL_image
&&
128 gfx::g_driver_egl
.ext
.b_EGL_KHR_fence_sync
);
130 const EGLint egl_attrib_list
[] = {
131 EGL_GL_TEXTURE_LEVEL_KHR
, 0, EGL_IMAGE_PRESERVED_KHR
, EGL_TRUE
, EGL_NONE
};
132 EGLClientBuffer egl_buffer
= reinterpret_cast<EGLClientBuffer
>(texture_id
);
133 EGLenum egl_target
= EGL_GL_TEXTURE_2D_KHR
; // TODO
135 EGLImageKHR egl_image
= eglCreateImageKHR(
136 egl_display
, egl_context
, egl_target
, egl_buffer
, egl_attrib_list
);
138 if (egl_image
== EGL_NO_IMAGE_KHR
)
141 return new NativeImageBufferEGL(
142 make_scoped_ptr(gfx::GLFence::Create()), egl_display
, egl_image
);
145 NativeImageBufferEGL::NativeImageBufferEGL(scoped_ptr
<gfx::GLFence
> write_fence
,
148 : NativeImageBuffer(write_fence
.Pass()),
149 egl_display_(display
),
151 DCHECK(egl_display_
!= EGL_NO_DISPLAY
);
152 DCHECK(egl_image_
!= EGL_NO_IMAGE_KHR
);
155 NativeImageBufferEGL::~NativeImageBufferEGL() {
156 if (egl_image_
!= EGL_NO_IMAGE_KHR
)
157 eglDestroyImageKHR(egl_display_
, egl_image_
);
160 void NativeImageBufferEGL::BindToTexture(GLenum target
) {
161 DCHECK(egl_image_
!= EGL_NO_IMAGE_KHR
);
162 glEGLImageTargetTexture2DOES(target
, egl_image_
);
163 DCHECK_EQ(static_cast<EGLint
>(EGL_SUCCESS
), eglGetError());
164 DCHECK_EQ(static_cast<GLenum
>(GL_NO_ERROR
), glGetError());
168 class NativeImageBufferStub
: public NativeImageBuffer
{
170 NativeImageBufferStub() : NativeImageBuffer(scoped_ptr
<gfx::GLFence
>()) {}
173 virtual ~NativeImageBufferStub() {}
174 virtual void BindToTexture(GLenum target
) OVERRIDE
{}
176 DISALLOW_COPY_AND_ASSIGN(NativeImageBufferStub
);
179 } // anonymous namespace
182 scoped_refptr
<NativeImageBuffer
> NativeImageBuffer::Create(GLuint texture_id
) {
183 switch (gfx::GetGLImplementation()) {
184 #if !defined(OS_MACOSX)
185 case gfx::kGLImplementationEGLGLES2
:
186 return NativeImageBufferEGL::Create(texture_id
);
188 case gfx::kGLImplementationMockGL
:
189 return new NativeImageBufferStub
;
196 NativeImageBuffer::ClientInfo::ClientInfo(gfx::GLImage
* client
)
197 : client(client
), needs_wait_before_read(true) {}
199 NativeImageBuffer::ClientInfo::~ClientInfo() {}
201 NativeImageBuffer::NativeImageBuffer(scoped_ptr
<gfx::GLFence
> write_fence
)
202 : write_fence_(write_fence
.Pass()), write_client_(NULL
) {
205 NativeImageBuffer::~NativeImageBuffer() {
206 DCHECK(client_infos_
.empty());
209 void NativeImageBuffer::AddClient(gfx::GLImage
* client
) {
210 base::AutoLock
lock(lock_
);
211 client_infos_
.push_back(ClientInfo(client
));
214 void NativeImageBuffer::RemoveClient(gfx::GLImage
* client
) {
215 base::AutoLock
lock(lock_
);
216 if (write_client_
== client
)
217 write_client_
= NULL
;
218 for (std::list
<ClientInfo
>::iterator it
= client_infos_
.begin();
219 it
!= client_infos_
.end();
221 if (it
->client
== client
) {
222 client_infos_
.erase(it
);
229 bool NativeImageBuffer::IsClient(gfx::GLImage
* client
) {
230 base::AutoLock
lock(lock_
);
231 for (std::list
<ClientInfo
>::iterator it
= client_infos_
.begin();
232 it
!= client_infos_
.end();
234 if (it
->client
== client
)
240 void NativeImageBuffer::WillRead(gfx::GLImage
* client
) {
241 base::AutoLock
lock(lock_
);
242 if (!write_fence_
.get() || write_client_
== client
)
245 for (std::list
<ClientInfo
>::iterator it
= client_infos_
.begin();
246 it
!= client_infos_
.end();
248 if (it
->client
== client
) {
249 if (it
->needs_wait_before_read
) {
250 it
->needs_wait_before_read
= false;
251 write_fence_
->ServerWait();
259 void NativeImageBuffer::WillWrite(gfx::GLImage
* client
) {
260 base::AutoLock
lock(lock_
);
261 if (write_client_
!= client
)
262 write_fence_
->ServerWait();
264 for (std::list
<ClientInfo
>::iterator it
= client_infos_
.begin();
265 it
!= client_infos_
.end();
267 if (it
->read_fence
.get() && it
->client
!= client
)
268 it
->read_fence
->ServerWait();
272 void NativeImageBuffer::DidRead(gfx::GLImage
* client
) {
273 base::AutoLock
lock(lock_
);
274 for (std::list
<ClientInfo
>::iterator it
= client_infos_
.begin();
275 it
!= client_infos_
.end();
277 if (it
->client
== client
) {
278 it
->read_fence
= make_linked_ptr(gfx::GLFence::Create());
285 void NativeImageBuffer::DidWrite(gfx::GLImage
* client
) {
286 base::AutoLock
lock(lock_
);
287 // Sharing semantics require the client to flush in order to make changes
288 // visible to other clients.
289 write_fence_
.reset(gfx::GLFence::CreateWithoutFlush());
290 write_client_
= client
;
291 for (std::list
<ClientInfo
>::iterator it
= client_infos_
.begin();
292 it
!= client_infos_
.end();
294 it
->needs_wait_before_read
= true;
298 TextureDefinition::LevelInfo::LevelInfo(GLenum target
,
299 GLenum internal_format
,
308 internal_format(internal_format
),
317 TextureDefinition::LevelInfo::~LevelInfo() {}
319 TextureDefinition::TextureDefinition(
322 unsigned int version
,
323 const scoped_refptr
<NativeImageBuffer
>& image_buffer
)
326 image_buffer_(image_buffer
? image_buffer
: NativeImageBuffer::Create(
327 texture
->service_id())),
328 min_filter_(texture
->min_filter()),
329 mag_filter_(texture
->mag_filter()),
330 wrap_s_(texture
->wrap_s()),
331 wrap_t_(texture
->wrap_t()),
332 usage_(texture
->usage()),
333 immutable_(texture
->IsImmutable()) {
336 DCHECK(!texture
->level_infos_
.empty());
337 DCHECK(!texture
->level_infos_
[0].empty());
338 DCHECK(!texture
->NeedsMips());
339 DCHECK(texture
->level_infos_
[0][0].width
);
340 DCHECK(texture
->level_infos_
[0][0].height
);
342 scoped_refptr
<gfx::GLImage
> gl_image(new GLImageSync(image_buffer_
));
343 texture
->SetLevelImage(NULL
, target
, 0, gl_image
);
346 level_infos_
.clear();
347 const Texture::LevelInfo
& level
= texture
->level_infos_
[0][0];
348 LevelInfo
info(level
.target
,
349 level
.internal_format
,
357 std::vector
<LevelInfo
> infos
;
358 infos
.push_back(info
);
359 level_infos_
.push_back(infos
);
363 TextureDefinition::~TextureDefinition() {
366 Texture
* TextureDefinition::CreateTexture() const {
371 glGenTextures(1, &texture_id
);
373 Texture
* texture(new Texture(texture_id
));
374 UpdateTexture(texture
);
379 void TextureDefinition::UpdateTexture(Texture
* texture
) const {
380 gfx::ScopedTextureBinder
texture_binder(target_
, texture
->service_id());
381 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, min_filter_
);
382 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, mag_filter_
);
383 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, wrap_s_
);
384 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, wrap_t_
);
386 image_buffer_
->BindToTexture(target_
);
387 // We have to make sure the changes are visible to other clients in this share
388 // group. As far as the clients are concerned, the mailbox semantics only
389 // demand a single flush from the client after changes are first made,
390 // and it is not visible to them when another share group boundary is crossed.
391 // We could probably track this and be a bit smarter about when to flush
395 texture
->level_infos_
.resize(1);
396 for (size_t i
= 0; i
< level_infos_
.size(); i
++) {
397 const LevelInfo
& base_info
= level_infos_
[i
][0];
398 const size_t levels_needed
= TextureManager::ComputeMipMapCount(
399 base_info
.target
, base_info
.width
, base_info
.height
, base_info
.depth
);
400 DCHECK(level_infos_
.size() <= levels_needed
);
401 texture
->level_infos_
[0].resize(levels_needed
);
402 for (size_t n
= 0; n
< level_infos_
.size(); n
++) {
403 const LevelInfo
& info
= level_infos_
[i
][n
];
404 texture
->SetLevelInfo(NULL
,
407 info
.internal_format
,
418 texture
->SetLevelImage(NULL
, target_
, 0, new GLImageSync(image_buffer_
));
420 texture
->target_
= target_
;
421 texture
->SetImmutable(immutable_
);
422 texture
->min_filter_
= min_filter_
;
423 texture
->mag_filter_
= mag_filter_
;
424 texture
->wrap_s_
= wrap_s_
;
425 texture
->wrap_t_
= wrap_t_
;
426 texture
->usage_
= usage_
;
429 bool TextureDefinition::Matches(const Texture
* texture
) const {
430 DCHECK(target_
== texture
->target());
431 if (texture
->min_filter_
!= min_filter_
||
432 texture
->mag_filter_
!= mag_filter_
||
433 texture
->wrap_s_
!= wrap_s_
||
434 texture
->wrap_t_
!= wrap_t_
) {
438 // All structural changes should have orphaned the texture.
439 if (image_buffer_
&& !texture
->GetLevelImage(texture
->target(), 0))