blob: e0375239bf263cdd097bf966959b9f269bc2cdb4 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/gl/gl_image_memory.h"
#include <stdint.h>
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/trace_event/trace_event.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_version_info.h"
using gfx::BufferFormat;
namespace gl {
namespace {
bool ValidInternalFormat(unsigned internalformat) {
switch (internalformat) {
case GL_ATC_RGB_AMD:
case GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD:
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
case GL_ETC1_RGB8_OES:
case GL_RED:
case GL_RG:
case GL_RGB:
case GL_RGBA:
case GL_BGRA_EXT:
return true;
default:
return false;
}
}
bool ValidFormat(gfx::BufferFormat format) {
switch (format) {
case gfx::BufferFormat::ATC:
case gfx::BufferFormat::ATCIA:
case gfx::BufferFormat::DXT1:
case gfx::BufferFormat::DXT5:
case gfx::BufferFormat::ETC1:
case gfx::BufferFormat::R_8:
case gfx::BufferFormat::RG_88:
case gfx::BufferFormat::BGR_565:
case gfx::BufferFormat::RGBA_4444:
case gfx::BufferFormat::RGBX_8888:
case gfx::BufferFormat::RGBA_8888:
case gfx::BufferFormat::BGRX_8888:
case gfx::BufferFormat::BGRA_8888:
case gfx::BufferFormat::RGBA_F16:
return true;
case gfx::BufferFormat::YVU_420:
case gfx::BufferFormat::YUV_420_BIPLANAR:
case gfx::BufferFormat::UYVY_422:
return false;
}
NOTREACHED();
return false;
}
bool IsCompressedFormat(gfx::BufferFormat format) {
switch (format) {
case gfx::BufferFormat::ATC:
case gfx::BufferFormat::ATCIA:
case gfx::BufferFormat::DXT1:
case gfx::BufferFormat::DXT5:
case gfx::BufferFormat::ETC1:
return true;
case gfx::BufferFormat::R_8:
case gfx::BufferFormat::RG_88:
case gfx::BufferFormat::BGR_565:
case gfx::BufferFormat::RGBA_4444:
case gfx::BufferFormat::RGBX_8888:
case gfx::BufferFormat::RGBA_8888:
case gfx::BufferFormat::BGRX_8888:
case gfx::BufferFormat::BGRA_8888:
case gfx::BufferFormat::RGBA_F16:
return false;
case gfx::BufferFormat::YVU_420:
case gfx::BufferFormat::YUV_420_BIPLANAR:
case gfx::BufferFormat::UYVY_422:
NOTREACHED();
return false;
}
NOTREACHED();
return false;
}
GLenum TextureFormat(gfx::BufferFormat format) {
switch (format) {
case gfx::BufferFormat::ATC:
return GL_ATC_RGB_AMD;
case gfx::BufferFormat::ATCIA:
return GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD;
case gfx::BufferFormat::DXT1:
return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
case gfx::BufferFormat::DXT5:
return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
case gfx::BufferFormat::ETC1:
return GL_ETC1_RGB8_OES;
case gfx::BufferFormat::R_8:
return GL_RED;
case gfx::BufferFormat::RG_88:
return GL_RG;
case gfx::BufferFormat::RGBA_4444:
case gfx::BufferFormat::RGBA_8888:
case gfx::BufferFormat::RGBA_F16:
return GL_RGBA;
case gfx::BufferFormat::BGRA_8888:
return GL_BGRA_EXT;
case gfx::BufferFormat::BGR_565:
case gfx::BufferFormat::RGBX_8888:
case gfx::BufferFormat::BGRX_8888:
return GL_RGB;
case gfx::BufferFormat::YVU_420:
case gfx::BufferFormat::YUV_420_BIPLANAR:
case gfx::BufferFormat::UYVY_422:
NOTREACHED();
return 0;
}
NOTREACHED();
return 0;
}
GLenum DataFormat(gfx::BufferFormat format) {
switch (format) {
case gfx::BufferFormat::RGBX_8888:
return GL_RGBA;
case gfx::BufferFormat::BGRX_8888:
return GL_BGRA_EXT;
case gfx::BufferFormat::BGR_565:
case gfx::BufferFormat::RGBA_4444:
case gfx::BufferFormat::RGBA_8888:
case gfx::BufferFormat::BGRA_8888:
case gfx::BufferFormat::RGBA_F16:
case gfx::BufferFormat::R_8:
case gfx::BufferFormat::RG_88:
case gfx::BufferFormat::ATC:
case gfx::BufferFormat::ATCIA:
case gfx::BufferFormat::DXT1:
case gfx::BufferFormat::DXT5:
case gfx::BufferFormat::ETC1:
return TextureFormat(format);
case gfx::BufferFormat::YVU_420:
case gfx::BufferFormat::YUV_420_BIPLANAR:
case gfx::BufferFormat::UYVY_422:
NOTREACHED();
return 0;
}
NOTREACHED();
return 0;
}
GLenum DataType(gfx::BufferFormat format) {
switch (format) {
case gfx::BufferFormat::BGR_565:
return GL_UNSIGNED_SHORT_5_6_5_REV;
case gfx::BufferFormat::RGBA_4444:
return GL_UNSIGNED_SHORT_4_4_4_4;
case gfx::BufferFormat::RGBX_8888:
case gfx::BufferFormat::RGBA_8888:
case gfx::BufferFormat::BGRX_8888:
case gfx::BufferFormat::BGRA_8888:
case gfx::BufferFormat::R_8:
case gfx::BufferFormat::RG_88:
return GL_UNSIGNED_BYTE;
case gfx::BufferFormat::RGBA_F16:
return GL_HALF_FLOAT_OES;
case gfx::BufferFormat::ATC:
case gfx::BufferFormat::ATCIA:
case gfx::BufferFormat::DXT1:
case gfx::BufferFormat::DXT5:
case gfx::BufferFormat::ETC1:
case gfx::BufferFormat::YVU_420:
case gfx::BufferFormat::YUV_420_BIPLANAR:
case gfx::BufferFormat::UYVY_422:
NOTREACHED();
return 0;
}
NOTREACHED();
return 0;
}
GLint DataRowLength(size_t stride, gfx::BufferFormat format) {
switch (format) {
case gfx::BufferFormat::RG_88:
case gfx::BufferFormat::BGR_565:
case gfx::BufferFormat::RGBA_4444:
return base::checked_cast<GLint>(stride) / 2;
case gfx::BufferFormat::RGBX_8888:
case gfx::BufferFormat::RGBA_8888:
case gfx::BufferFormat::BGRX_8888:
case gfx::BufferFormat::BGRA_8888:
return base::checked_cast<GLint>(stride) / 4;
case gfx::BufferFormat::RGBA_F16:
return base::checked_cast<GLint>(stride) / 8;
case gfx::BufferFormat::R_8:
return base::checked_cast<GLint>(stride);
case gfx::BufferFormat::ATC:
case gfx::BufferFormat::ATCIA:
case gfx::BufferFormat::DXT1:
case gfx::BufferFormat::DXT5:
case gfx::BufferFormat::ETC1:
case gfx::BufferFormat::YVU_420:
case gfx::BufferFormat::YUV_420_BIPLANAR:
case gfx::BufferFormat::UYVY_422:
NOTREACHED();
return 0;
}
NOTREACHED();
return 0;
}
template <typename F>
std::unique_ptr<uint8_t[]> GLES2RGBData(const gfx::Size& size,
size_t stride,
const uint8_t* data,
F const& data_to_rgb,
GLenum* data_format,
GLenum* data_type,
GLint* data_row_length) {
TRACE_EVENT2("gpu", "GLES2RGBData", "width", size.width(), "height",
size.height());
// Four-byte row alignment as specified by glPixelStorei with argument
// GL_UNPACK_ALIGNMENT set to 4.
size_t gles2_rgb_data_stride = (size.width() * 3 + 3) & ~3;
std::unique_ptr<uint8_t[]> gles2_rgb_data(
new uint8_t[gles2_rgb_data_stride * size.height()]);
for (int y = 0; y < size.height(); ++y) {
for (int x = 0; x < size.width(); ++x) {
data_to_rgb(&data[y * stride + x * 4],
&gles2_rgb_data[y * gles2_rgb_data_stride + x * 3]);
}
}
*data_format = GL_RGB;
*data_type = GL_UNSIGNED_BYTE;
*data_row_length = size.width();
return gles2_rgb_data;
}
std::unique_ptr<uint8_t[]> GLES2RGB565Data(const gfx::Size& size,
size_t stride,
const uint8_t* data,
GLenum* data_format,
GLenum* data_type,
GLint* data_row_length) {
TRACE_EVENT2("gpu", "GLES2RGB565Data", "width", size.width(), "height",
size.height());
// Four-byte row alignment as specified by glPixelStorei with argument
// GL_UNPACK_ALIGNMENT set to 4.
size_t gles2_rgb_data_stride = (size.width() * 2 + 3) & ~3;
std::unique_ptr<uint8_t[]> gles2_rgb_data(
new uint8_t[gles2_rgb_data_stride * size.height()]);
for (int y = 0; y < size.height(); ++y) {
for (int x = 0; x < size.width(); ++x) {
const uint16_t* src =
reinterpret_cast<const uint16_t*>(&data[y * stride + x * 2]);
uint16_t* dst = reinterpret_cast<uint16_t*>(
&gles2_rgb_data[y * gles2_rgb_data_stride + x * 2]);
*dst = (((*src & 0x1f) >> 0) << 11) | (((*src & 0x7e0) >> 5) << 5) |
(((*src & 0xf800) >> 11) << 5);
}
}
*data_format = GL_RGB;
*data_type = GL_UNSIGNED_SHORT_5_6_5;
*data_row_length = size.width();
return gles2_rgb_data;
}
std::unique_ptr<uint8_t[]> GLES2Data(const gfx::Size& size,
gfx::BufferFormat format,
size_t stride,
const uint8_t* data,
GLenum* data_format,
GLenum* data_type,
GLint* data_row_length) {
TRACE_EVENT2("gpu", "GLES2Data", "width", size.width(), "height",
size.height());
switch (format) {
case gfx::BufferFormat::RGBX_8888:
return GLES2RGBData(size, stride, data,
[](const uint8_t* src, uint8_t* dst) {
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
},
data_format, data_type, data_row_length);
case gfx::BufferFormat::BGR_565:
return GLES2RGB565Data(size, stride, data, data_format, data_type,
data_row_length);
case gfx::BufferFormat::BGRX_8888:
return GLES2RGBData(size, stride, data,
[](const uint8_t* src, uint8_t* dst) {
dst[0] = src[2];
dst[1] = src[1];
dst[2] = src[0];
},
data_format, data_type, data_row_length);
case gfx::BufferFormat::RGBA_4444:
case gfx::BufferFormat::RGBA_8888:
case gfx::BufferFormat::BGRA_8888:
case gfx::BufferFormat::RGBA_F16:
case gfx::BufferFormat::R_8:
case gfx::BufferFormat::RG_88: {
size_t gles2_data_stride =
RowSizeForBufferFormat(size.width(), format, 0);
if (stride == gles2_data_stride ||
g_current_gl_driver->ext.b_GL_EXT_unpack_subimage)
return nullptr; // No data conversion needed
std::unique_ptr<uint8_t[]> gles2_data(
new uint8_t[gles2_data_stride * size.height()]);
for (int y = 0; y < size.height(); ++y) {
memcpy(&gles2_data[y * gles2_data_stride], &data[y * stride],
gles2_data_stride);
}
*data_row_length = size.width();
return gles2_data;
}
case gfx::BufferFormat::ATC:
case gfx::BufferFormat::ATCIA:
case gfx::BufferFormat::DXT1:
case gfx::BufferFormat::DXT5:
case gfx::BufferFormat::ETC1:
return nullptr; // No data conversion needed
case gfx::BufferFormat::YVU_420:
case gfx::BufferFormat::YUV_420_BIPLANAR:
case gfx::BufferFormat::UYVY_422:
NOTREACHED();
return nullptr;
}
NOTREACHED();
return nullptr;
}
} // namespace
GLImageMemory::GLImageMemory(const gfx::Size& size, unsigned internalformat)
: size_(size),
internalformat_(internalformat),
memory_(nullptr),
format_(gfx::BufferFormat::RGBA_8888),
stride_(0) {}
GLImageMemory::~GLImageMemory() {}
bool GLImageMemory::Initialize(const unsigned char* memory,
gfx::BufferFormat format,
size_t stride) {
if (!ValidInternalFormat(internalformat_)) {
LOG(ERROR) << "Invalid internalformat: " << internalformat_;
return false;
}
if (!ValidFormat(format)) {
LOG(ERROR) << "Invalid format: " << static_cast<int>(format);
return false;
}
if (stride < RowSizeForBufferFormat(size_.width(), format, 0) || stride & 3) {
LOG(ERROR) << "Invalid stride: " << stride;
return false;
}
DCHECK(memory);
DCHECK(!memory_);
DCHECK(!IsCompressedFormat(format) || size_.width() % 4 == 0);
DCHECK(!IsCompressedFormat(format) || size_.height() % 4 == 0);
memory_ = memory;
format_ = format;
stride_ = stride;
return true;
}
gfx::Size GLImageMemory::GetSize() {
return size_;
}
unsigned GLImageMemory::GetInternalFormat() {
return internalformat_;
}
bool GLImageMemory::BindTexImage(unsigned target) {
return false;
}
bool GLImageMemory::CopyTexImage(unsigned target) {
TRACE_EVENT2("gpu", "GLImageMemory::CopyTexImage", "width", size_.width(),
"height", size_.height());
// GL_TEXTURE_EXTERNAL_OES is not a supported target.
if (target == GL_TEXTURE_EXTERNAL_OES)
return false;
if (IsCompressedFormat(format_)) {
glCompressedTexImage2D(
target, 0, TextureFormat(format_), size_.width(), size_.height(), 0,
static_cast<GLsizei>(BufferSizeForBufferFormat(size_, format_)),
memory_);
} else {
GLenum data_format = DataFormat(format_);
GLenum data_type = DataType(format_);
GLint data_row_length = DataRowLength(stride_, format_);
std::unique_ptr<uint8_t[]> gles2_data;
if (GLContext::GetCurrent()->GetVersionInfo()->is_es) {
gles2_data = GLES2Data(size_, format_, stride_, memory_, &data_format,
&data_type, &data_row_length);
}
if (data_row_length != size_.width())
glPixelStorei(GL_UNPACK_ROW_LENGTH, data_row_length);
glTexImage2D(target, 0, TextureFormat(format_), size_.width(),
size_.height(), 0, data_format, data_type,
gles2_data ? gles2_data.get() : memory_);
if (data_row_length != size_.width())
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
}
return true;
}
bool GLImageMemory::CopyTexSubImage(unsigned target,
const gfx::Point& offset,
const gfx::Rect& rect) {
TRACE_EVENT2("gpu", "GLImageMemory::CopyTexSubImage", "width", rect.width(),
"height", rect.height());
// GL_TEXTURE_EXTERNAL_OES is not a supported target.
if (target == GL_TEXTURE_EXTERNAL_OES)
return false;
// Sub width is not supported.
if (rect.width() != size_.width())
return false;
const uint8_t* data = memory_ + rect.y() * stride_;
if (IsCompressedFormat(format_)) {
// Height must be a multiple of 4.
if (rect.height() % 4)
return false;
glCompressedTexSubImage2D(
target, 0, offset.x(), offset.y(), rect.width(), rect.height(),
DataFormat(format_),
static_cast<GLsizei>(BufferSizeForBufferFormat(rect.size(), format_)),
data);
} else {
GLenum data_format = DataFormat(format_);
GLenum data_type = DataType(format_);
GLint data_row_length = DataRowLength(stride_, format_);
std::unique_ptr<uint8_t[]> gles2_data;
if (GLContext::GetCurrent()->GetVersionInfo()->is_es) {
gles2_data = GLES2Data(rect.size(), format_, stride_, data, &data_format,
&data_type, &data_row_length);
}
if (data_row_length != rect.width())
glPixelStorei(GL_UNPACK_ROW_LENGTH, data_row_length);
glTexSubImage2D(target, 0, offset.x(), offset.y(), rect.width(),
rect.height(), data_format, data_type,
gles2_data ? gles2_data.get() : data);
if (data_row_length != rect.width())
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
}
return true;
}
bool GLImageMemory::ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
int z_order,
gfx::OverlayTransform transform,
const gfx::Rect& bounds_rect,
const gfx::RectF& crop_rect) {
return false;
}
// static
unsigned GLImageMemory::GetInternalFormatForTesting(gfx::BufferFormat format) {
DCHECK(ValidFormat(format));
return TextureFormat(format);
}
} // namespace gl