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 "ui/gl/gl_image_memory.h"
7 #include "base/logging.h"
8 #include "base/trace_event/trace_event.h"
9 #include "ui/gl/gl_bindings.h"
10 #include "ui/gl/scoped_binders.h"
12 #if defined(OS_WIN) || defined(USE_X11) || defined(OS_ANDROID) || \
14 #include "ui/gl/gl_surface_egl.h"
20 bool ValidInternalFormat(unsigned internalformat
) {
21 switch (internalformat
) {
23 case GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD
:
24 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT
:
25 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
:
26 case GL_ETC1_RGB8_OES
:
36 bool ValidFormat(BufferFormat format
) {
38 case BufferFormat::ATC
:
39 case BufferFormat::ATCIA
:
40 case BufferFormat::DXT1
:
41 case BufferFormat::DXT5
:
42 case BufferFormat::ETC1
:
43 case BufferFormat::R_8
:
44 case BufferFormat::RGBA_4444
:
45 case BufferFormat::RGBA_8888
:
46 case BufferFormat::BGRA_8888
:
48 case BufferFormat::BGRX_8888
:
49 case BufferFormat::YUV_420
:
50 case BufferFormat::YUV_420_BIPLANAR
:
51 case BufferFormat::UYVY_422
:
59 bool IsCompressedFormat(BufferFormat format
) {
61 case BufferFormat::ATC
:
62 case BufferFormat::ATCIA
:
63 case BufferFormat::DXT1
:
64 case BufferFormat::DXT5
:
65 case BufferFormat::ETC1
:
67 case BufferFormat::R_8
:
68 case BufferFormat::RGBA_4444
:
69 case BufferFormat::RGBA_8888
:
70 case BufferFormat::BGRA_8888
:
71 case BufferFormat::BGRX_8888
:
72 case BufferFormat::YUV_420
:
73 case BufferFormat::YUV_420_BIPLANAR
:
74 case BufferFormat::UYVY_422
:
82 GLenum
TextureFormat(BufferFormat format
) {
84 case BufferFormat::ATC
:
85 return GL_ATC_RGB_AMD
;
86 case BufferFormat::ATCIA
:
87 return GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD
;
88 case BufferFormat::DXT1
:
89 return GL_COMPRESSED_RGB_S3TC_DXT1_EXT
;
90 case BufferFormat::DXT5
:
91 return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
;
92 case BufferFormat::ETC1
:
93 return GL_ETC1_RGB8_OES
;
94 case BufferFormat::R_8
:
96 case BufferFormat::RGBA_4444
:
97 case BufferFormat::RGBA_8888
:
99 case BufferFormat::BGRA_8888
:
101 case BufferFormat::BGRX_8888
:
102 case BufferFormat::YUV_420
:
103 case BufferFormat::YUV_420_BIPLANAR
:
104 case BufferFormat::UYVY_422
:
113 GLenum
DataFormat(BufferFormat format
) {
114 return TextureFormat(format
);
117 GLenum
DataType(BufferFormat format
) {
119 case BufferFormat::RGBA_4444
:
120 return GL_UNSIGNED_SHORT_4_4_4_4
;
121 case BufferFormat::RGBA_8888
:
122 case BufferFormat::BGRA_8888
:
123 case BufferFormat::R_8
:
124 return GL_UNSIGNED_BYTE
;
125 case BufferFormat::ATC
:
126 case BufferFormat::ATCIA
:
127 case BufferFormat::DXT1
:
128 case BufferFormat::DXT5
:
129 case BufferFormat::ETC1
:
130 case BufferFormat::BGRX_8888
:
131 case BufferFormat::YUV_420
:
132 case BufferFormat::YUV_420_BIPLANAR
:
133 case BufferFormat::UYVY_422
:
142 GLsizei
SizeInBytes(const Size
& size
, BufferFormat format
) {
143 size_t stride_in_bytes
= 0;
144 bool valid_stride
= GLImageMemory::StrideInBytes(
145 size
.width(), format
, &stride_in_bytes
);
146 DCHECK(valid_stride
);
147 return static_cast<GLsizei
>(stride_in_bytes
* size
.height());
152 GLImageMemory::GLImageMemory(const Size
& size
, unsigned internalformat
)
154 internalformat_(internalformat
),
156 format_(BufferFormat::RGBA_8888
),
159 need_do_bind_tex_image_(false)
160 #if defined(OS_WIN) || defined(USE_X11) || defined(OS_ANDROID) || \
164 egl_image_(EGL_NO_IMAGE_KHR
)
169 GLImageMemory::~GLImageMemory() {
170 #if defined(OS_WIN) || defined(USE_X11) || defined(OS_ANDROID) || \
172 DCHECK_EQ(EGL_NO_IMAGE_KHR
, egl_image_
);
173 DCHECK_EQ(0u, egl_texture_id_
);
178 bool GLImageMemory::StrideInBytes(size_t width
,
180 size_t* stride_in_bytes
) {
181 base::CheckedNumeric
<size_t> checked_stride
= width
;
183 case BufferFormat::ATCIA
:
184 case BufferFormat::DXT5
:
185 *stride_in_bytes
= width
;
187 case BufferFormat::ATC
:
188 case BufferFormat::DXT1
:
189 case BufferFormat::ETC1
:
190 DCHECK_EQ(width
% 2, 0u);
191 *stride_in_bytes
= width
/ 2;
193 case BufferFormat::R_8
:
195 if (!checked_stride
.IsValid())
197 *stride_in_bytes
= checked_stride
.ValueOrDie() & ~0x3;
199 case BufferFormat::RGBA_4444
:
201 if (!checked_stride
.IsValid())
203 *stride_in_bytes
= checked_stride
.ValueOrDie();
205 case BufferFormat::RGBA_8888
:
206 case BufferFormat::BGRA_8888
:
208 if (!checked_stride
.IsValid())
210 *stride_in_bytes
= checked_stride
.ValueOrDie();
212 case BufferFormat::BGRX_8888
:
213 case BufferFormat::YUV_420
:
214 case BufferFormat::YUV_420_BIPLANAR
:
215 case BufferFormat::UYVY_422
:
224 bool GLImageMemory::Initialize(const unsigned char* memory
,
225 BufferFormat format
) {
226 if (!ValidInternalFormat(internalformat_
)) {
227 LOG(ERROR
) << "Invalid internalformat: " << internalformat_
;
231 if (!ValidFormat(format
)) {
232 LOG(ERROR
) << "Invalid format: " << static_cast<int>(format
);
238 DCHECK_IMPLIES(IsCompressedFormat(format
), size_
.width() % 4 == 0);
239 DCHECK_IMPLIES(IsCompressedFormat(format
), size_
.height() % 4 == 0);
245 void GLImageMemory::Destroy(bool have_context
) {
246 #if defined(OS_WIN) || defined(USE_X11) || defined(OS_ANDROID) || \
248 if (egl_image_
!= EGL_NO_IMAGE_KHR
) {
249 eglDestroyImageKHR(GLSurfaceEGL::GetHardwareDisplay(), egl_image_
);
250 egl_image_
= EGL_NO_IMAGE_KHR
;
253 if (egl_texture_id_
) {
255 glDeleteTextures(1, &egl_texture_id_
);
256 egl_texture_id_
= 0u;
262 Size
GLImageMemory::GetSize() {
266 unsigned GLImageMemory::GetInternalFormat() {
267 return internalformat_
;
270 bool GLImageMemory::BindTexImage(unsigned target
) {
271 if (target_
&& target_
!= target
) {
272 LOG(ERROR
) << "GLImage can only be bound to one target";
277 // Defer DoBindTexImage if not currently in use.
279 need_do_bind_tex_image_
= true;
283 DoBindTexImage(target
);
287 bool GLImageMemory::CopyTexSubImage(unsigned target
,
290 TRACE_EVENT2("gpu", "GLImageMemory::CopyTexSubImage", "width", rect
.width(),
291 "height", rect
.height());
293 // GL_TEXTURE_EXTERNAL_OES is not a supported CopyTexSubImage target.
294 if (target
== GL_TEXTURE_EXTERNAL_OES
)
297 // Sub width is not supported.
298 if (rect
.width() != size_
.width())
301 // Height must be a multiple of 4 if compressed.
302 if (IsCompressedFormat(format_
) && rect
.height() % 4)
305 size_t stride_in_bytes
= 0;
306 bool rv
= StrideInBytes(size_
.width(), format_
, &stride_in_bytes
);
309 const unsigned char* data
= memory_
+ rect
.y() * stride_in_bytes
;
310 if (IsCompressedFormat(format_
)) {
311 glCompressedTexSubImage2D(target
,
313 offset
.x(), offset
.y(), rect
.width(),
314 rect
.height(), DataFormat(format_
),
315 SizeInBytes(rect
.size(), format_
), data
);
317 glTexSubImage2D(target
, 0, // level
318 offset
.x(), offset
.y(), rect
.width(), rect
.height(),
319 DataFormat(format_
), DataType(format_
), data
);
325 void GLImageMemory::WillUseTexImage() {
329 if (!need_do_bind_tex_image_
)
333 DoBindTexImage(target_
);
336 void GLImageMemory::DidUseTexImage() {
341 bool GLImageMemory::ScheduleOverlayPlane(AcceleratedWidget widget
,
343 OverlayTransform transform
,
344 const Rect
& bounds_rect
,
345 const RectF
& crop_rect
) {
349 void GLImageMemory::DoBindTexImage(unsigned target
) {
350 TRACE_EVENT0("gpu", "GLImageMemory::DoBindTexImage");
352 DCHECK(need_do_bind_tex_image_
);
353 need_do_bind_tex_image_
= false;
356 #if defined(OS_WIN) || defined(USE_X11) || defined(OS_ANDROID) || \
358 if (target
== GL_TEXTURE_EXTERNAL_OES
) {
359 if (egl_image_
== EGL_NO_IMAGE_KHR
) {
360 DCHECK_EQ(0u, egl_texture_id_
);
361 glGenTextures(1, &egl_texture_id_
);
364 ScopedTextureBinder
texture_binder(GL_TEXTURE_2D
, egl_texture_id_
);
366 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
367 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
368 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
369 if (IsCompressedFormat(format_
)) {
370 glCompressedTexImage2D(GL_TEXTURE_2D
,
372 TextureFormat(format_
), size_
.width(),
375 SizeInBytes(size_
, format_
), memory_
);
377 glTexImage2D(GL_TEXTURE_2D
,
379 TextureFormat(format_
),
389 EGLint attrs
[] = {EGL_IMAGE_PRESERVED_KHR
, EGL_TRUE
, EGL_NONE
};
390 // Need to pass current EGL rendering context to eglCreateImageKHR for
391 // target type EGL_GL_TEXTURE_2D_KHR.
393 eglCreateImageKHR(GLSurfaceEGL::GetHardwareDisplay(),
394 eglGetCurrentContext(),
395 EGL_GL_TEXTURE_2D_KHR
,
396 reinterpret_cast<EGLClientBuffer
>(egl_texture_id_
),
398 DCHECK_NE(EGL_NO_IMAGE_KHR
, egl_image_
)
399 << "Error creating EGLImage: " << eglGetError();
401 ScopedTextureBinder
texture_binder(GL_TEXTURE_2D
, egl_texture_id_
);
403 if (IsCompressedFormat(format_
)) {
404 glCompressedTexSubImage2D(GL_TEXTURE_2D
,
408 size_
.width(), size_
.height(),
410 SizeInBytes(size_
, format_
), memory_
);
412 glTexSubImage2D(GL_TEXTURE_2D
,
424 glEGLImageTargetTexture2DOES(target
, egl_image_
);
425 DCHECK_EQ(static_cast<GLenum
>(GL_NO_ERROR
), glGetError());
430 DCHECK_NE(static_cast<GLenum
>(GL_TEXTURE_EXTERNAL_OES
), target
);
431 if (IsCompressedFormat(format_
)) {
432 glCompressedTexImage2D(target
,
434 TextureFormat(format_
), size_
.width(),
437 SizeInBytes(size_
, format_
), memory_
);
441 TextureFormat(format_
),
451 void GLImageMemory::OnMemoryDump(base::trace_event::ProcessMemoryDump
* pmd
,
452 uint64_t process_tracing_id
,
453 const std::string
& dump_name
) {
454 // Note that the following calculation does not consider whether this GLImage
455 // has been un-bound from a texture. It also relies on this GLImage only ever
456 // being bound to a single texture. We could check these conditions more
457 // thoroughly, but at the cost of extra GL queries.
458 bool is_bound_to_texture
= target_
&& !need_do_bind_tex_image_
;
460 #if defined(OS_WIN) || defined(USE_X11) || defined(OS_ANDROID) || \
462 is_bound_to_texture
|= !!egl_texture_id_
;
465 size_t size_in_bytes
= is_bound_to_texture
? SizeInBytes(size_
, format_
) : 0;
467 base::trace_event::MemoryAllocatorDump
* dump
=
468 pmd
->CreateAllocatorDump(dump_name
+ "/texture_memory");
469 dump
->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize
,
470 base::trace_event::MemoryAllocatorDump::kUnitsBytes
,
471 static_cast<uint64_t>(size_in_bytes
));
473 // No need for a global shared edge here. This object in the GPU process is
474 // the sole owner of this texture id.