Merge "Routine to upload hardware bitmaps"
This commit is contained in:
committed by
Android (Google) Code Review
commit
97cc85fd4b
@@ -82,6 +82,12 @@ void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool for
|
||||
void Texture::deleteTexture() {
|
||||
mCaches.textureState().deleteTexture(mId);
|
||||
mId = 0;
|
||||
mTarget = GL_NONE;
|
||||
if (mEglImageHandle != EGL_NO_IMAGE_KHR) {
|
||||
EGLDisplay eglDisplayHandle = eglGetCurrentDisplay();
|
||||
eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle);
|
||||
mEglImageHandle = EGL_NO_IMAGE_KHR;
|
||||
}
|
||||
}
|
||||
|
||||
bool Texture::updateSize(uint32_t width, uint32_t height, GLint internalFormat,
|
||||
@@ -129,6 +135,17 @@ void Texture::upload(GLint internalFormat, uint32_t width, uint32_t height,
|
||||
GL_CHECKPOINT(MODERATE);
|
||||
}
|
||||
|
||||
void Texture::uploadHardwareBitmapToTexture(GraphicBuffer* buffer) {
|
||||
EGLDisplay eglDisplayHandle = eglGetCurrentDisplay();
|
||||
if (mEglImageHandle != EGL_NO_IMAGE_KHR) {
|
||||
eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle);
|
||||
mEglImageHandle = EGL_NO_IMAGE_KHR;
|
||||
}
|
||||
mEglImageHandle = eglCreateImageKHR(eglDisplayHandle, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
|
||||
buffer->getNativeBuffer(), 0);
|
||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, mEglImageHandle);
|
||||
}
|
||||
|
||||
static void uploadToTexture(bool resize, GLint internalFormat, GLenum format, GLenum type,
|
||||
GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, const GLvoid * data) {
|
||||
|
||||
@@ -173,7 +190,7 @@ static void uploadToTexture(bool resize, GLint internalFormat, GLenum format, GL
|
||||
}
|
||||
}
|
||||
|
||||
static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType,
|
||||
void Texture::colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType,
|
||||
bool needSRGB, GLint* outInternalFormat, GLint* outFormat, GLint* outType) {
|
||||
switch (colorType) {
|
||||
case kAlpha_8_SkColorType:
|
||||
@@ -214,6 +231,24 @@ static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorTy
|
||||
}
|
||||
}
|
||||
|
||||
SkBitmap Texture::uploadToN32(const SkBitmap& bitmap, bool hasSRGB, sk_sp<SkColorSpace> sRGB) {
|
||||
SkBitmap rgbaBitmap;
|
||||
rgbaBitmap.allocPixels(SkImageInfo::MakeN32(bitmap.width(), bitmap.height(),
|
||||
bitmap.info().alphaType(), hasSRGB ? sRGB : nullptr));
|
||||
rgbaBitmap.eraseColor(0);
|
||||
SkCanvas canvas(rgbaBitmap);
|
||||
canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr);
|
||||
return rgbaBitmap;
|
||||
}
|
||||
|
||||
bool Texture::hasUnsupportedColorType(const SkImageInfo& info, bool hasSRGB, SkColorSpace* sRGB) {
|
||||
bool needSRGB = info.colorSpace() == sRGB;
|
||||
return info.colorType() == kARGB_4444_SkColorType
|
||||
|| info.colorType() == kIndex_8_SkColorType
|
||||
|| (info.colorType() == kRGB_565_SkColorType && hasSRGB && needSRGB);
|
||||
}
|
||||
|
||||
|
||||
void Texture::upload(Bitmap& bitmap) {
|
||||
if (!bitmap.readyToDraw()) {
|
||||
ALOGE("Cannot generate texture from bitmap");
|
||||
@@ -243,33 +278,23 @@ void Texture::upload(Bitmap& bitmap) {
|
||||
GLint internalFormat, format, type;
|
||||
colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), needSRGB, &internalFormat, &format, &type);
|
||||
|
||||
if (updateSize(bitmap.width(), bitmap.height(), internalFormat, format, GL_TEXTURE_2D)) {
|
||||
needsAlloc = true;
|
||||
}
|
||||
GLenum target = bitmap.isHardware() ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
|
||||
needsAlloc |= updateSize(bitmap.width(), bitmap.height(), internalFormat, format, target);
|
||||
|
||||
blend = !bitmap.isOpaque();
|
||||
mCaches.textureState().bindTexture(mId);
|
||||
mCaches.textureState().bindTexture(mTarget, mId);
|
||||
|
||||
// TODO: Handle sRGB gray bitmaps
|
||||
bool hasSRGB = mCaches.extensions().hasSRGB();
|
||||
if (CC_UNLIKELY(bitmap.colorType() == kARGB_4444_SkColorType
|
||||
|| bitmap.colorType() == kIndex_8_SkColorType
|
||||
|| (bitmap.colorType() == kRGB_565_SkColorType && hasSRGB && needSRGB))) {
|
||||
|
||||
SkBitmap rgbaBitmap;
|
||||
rgbaBitmap.allocPixels(SkImageInfo::MakeN32(
|
||||
mWidth, mHeight, bitmap.info().alphaType(), hasSRGB ? sRGB : nullptr));
|
||||
rgbaBitmap.eraseColor(0);
|
||||
|
||||
SkCanvas canvas(rgbaBitmap);
|
||||
if (CC_UNLIKELY(hasUnsupportedColorType(bitmap.info(), hasSRGB, sRGB.get()))) {
|
||||
SkBitmap skBitmap;
|
||||
bitmap.getSkBitmap(&skBitmap);
|
||||
canvas.drawBitmap(skBitmap, 0.0f, 0.0f, nullptr);
|
||||
|
||||
SkBitmap rgbaBitmap = uploadToN32(skBitmap, hasSRGB, std::move(sRGB));
|
||||
uploadToTexture(needsAlloc, internalFormat, format, type, rgbaBitmap.rowBytesAsPixels(),
|
||||
rgbaBitmap.bytesPerPixel(), rgbaBitmap.width(),
|
||||
rgbaBitmap.height(), rgbaBitmap.getPixels());
|
||||
|
||||
} else if (bitmap.isHardware()) {
|
||||
uploadHardwareBitmapToTexture(bitmap.graphicBuffer());
|
||||
} else {
|
||||
uploadToTexture(needsAlloc, internalFormat, format, type, bitmap.rowBytesAsPixels(),
|
||||
bitmap.info().bytesPerPixel(), bitmap.width(), bitmap.height(), bitmap.pixels());
|
||||
|
||||
@@ -21,8 +21,14 @@
|
||||
#include "hwui/Bitmap.h"
|
||||
|
||||
#include <GLES2/gl2.h>
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <SkBitmap.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class GraphicBuffer;
|
||||
|
||||
namespace uirenderer {
|
||||
|
||||
class Caches;
|
||||
@@ -34,6 +40,11 @@ class Layer;
|
||||
*/
|
||||
class Texture : public GpuMemoryTracker {
|
||||
public:
|
||||
static SkBitmap uploadToN32(const SkBitmap& bitmap, bool hasSRGB, sk_sp<SkColorSpace> sRGB);
|
||||
static bool hasUnsupportedColorType(const SkImageInfo& info, bool hasSRGB, SkColorSpace* sRGB);
|
||||
static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType,
|
||||
bool needSRGB, GLint* outInternalFormat, GLint* outFormat, GLint* outType);
|
||||
|
||||
explicit Texture(Caches& caches)
|
||||
: GpuMemoryTracker(GpuObjectType::Texture)
|
||||
, mCaches(caches)
|
||||
@@ -147,7 +158,6 @@ public:
|
||||
* the current frame. This is reset at the start of a new frame.
|
||||
*/
|
||||
void* isInUse = nullptr;
|
||||
|
||||
private:
|
||||
// TODO: Temporarily grant private access to Layer, remove once
|
||||
// Layer can be de-tangled from being a dual-purpose render target
|
||||
@@ -157,6 +167,7 @@ private:
|
||||
// Returns true if the size changed, false if it was the same
|
||||
bool updateSize(uint32_t width, uint32_t height, GLint internalFormat,
|
||||
GLint format, GLenum target);
|
||||
void uploadHardwareBitmapToTexture(GraphicBuffer* buffer);
|
||||
void resetCachedParams();
|
||||
|
||||
GLuint mId = 0;
|
||||
@@ -165,6 +176,7 @@ private:
|
||||
GLint mFormat = 0;
|
||||
GLint mInternalFormat = 0;
|
||||
GLenum mTarget = GL_NONE;
|
||||
EGLImageKHR mEglImageHandle = EGL_NO_IMAGE_KHR;
|
||||
|
||||
/* See GLES spec section 3.8.14
|
||||
* "In the initial state, the value assigned to TEXTURE_MIN_FILTER is
|
||||
|
||||
@@ -16,11 +16,27 @@
|
||||
#include "Bitmap.h"
|
||||
|
||||
#include "Caches.h"
|
||||
#include "renderthread/RenderThread.h"
|
||||
#include "renderthread/RenderProxy.h"
|
||||
|
||||
#include <cutils/log.h>
|
||||
#include <sys/mman.h>
|
||||
#include <cutils/ashmem.h>
|
||||
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
|
||||
#include <gui/IGraphicBufferAlloc.h>
|
||||
#include <gui/ISurfaceComposer.h>
|
||||
#include <private/gui/ComposerService.h>
|
||||
#include <binder/IServiceManager.h>
|
||||
#include <ui/PixelFormat.h>
|
||||
|
||||
#include <SkCanvas.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
static bool computeAllocationSize(size_t rowBytes, int height, size_t* size) {
|
||||
@@ -76,6 +92,167 @@ static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& info, si
|
||||
return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes, ctable));
|
||||
}
|
||||
|
||||
#define FENCE_TIMEOUT 2000000000
|
||||
|
||||
// TODO: handle SRGB sanely
|
||||
static PixelFormat internalFormatToPixelFormat(GLint internalFormat) {
|
||||
switch (internalFormat) {
|
||||
case GL_ALPHA:
|
||||
return PIXEL_FORMAT_TRANSPARENT;
|
||||
case GL_LUMINANCE:
|
||||
return PIXEL_FORMAT_RGBA_8888;
|
||||
case GL_SRGB8_ALPHA8:
|
||||
return PIXEL_FORMAT_RGBA_8888;
|
||||
case GL_RGBA:
|
||||
return PIXEL_FORMAT_RGBA_8888;
|
||||
default:
|
||||
LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", internalFormat);
|
||||
return PIXEL_FORMAT_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
class AutoEglFence {
|
||||
public:
|
||||
AutoEglFence(EGLDisplay display)
|
||||
: mDisplay(display) {
|
||||
fence = eglCreateSyncKHR(mDisplay, EGL_SYNC_FENCE_KHR, NULL);
|
||||
}
|
||||
|
||||
~AutoEglFence() {
|
||||
if (fence != EGL_NO_SYNC_KHR) {
|
||||
eglDestroySyncKHR(mDisplay, fence);
|
||||
}
|
||||
}
|
||||
|
||||
EGLSyncKHR fence = EGL_NO_SYNC_KHR;
|
||||
private:
|
||||
EGLDisplay mDisplay = EGL_NO_DISPLAY;
|
||||
};
|
||||
|
||||
class AutoEglImage {
|
||||
public:
|
||||
AutoEglImage(EGLDisplay display, EGLClientBuffer clientBuffer)
|
||||
: mDisplay(display) {
|
||||
EGLint imageAttrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE };
|
||||
image = eglCreateImageKHR(display, EGL_NO_CONTEXT,
|
||||
EGL_NATIVE_BUFFER_ANDROID, clientBuffer, imageAttrs);
|
||||
}
|
||||
|
||||
~AutoEglImage() {
|
||||
if (image != EGL_NO_IMAGE_KHR) {
|
||||
eglDestroyImageKHR(mDisplay, image);
|
||||
}
|
||||
}
|
||||
|
||||
EGLImageKHR image = EGL_NO_IMAGE_KHR;
|
||||
private:
|
||||
EGLDisplay mDisplay = EGL_NO_DISPLAY;
|
||||
};
|
||||
|
||||
static bool uploadBitmapToGraphicBuffer(uirenderer::Caches& caches, SkBitmap& bitmap,
|
||||
GraphicBuffer& buffer, GLint format, GLint type) {
|
||||
SkAutoLockPixels alp(bitmap);
|
||||
EGLDisplay display = eglGetCurrentDisplay();
|
||||
LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY,
|
||||
"Failed to get EGL_DEFAULT_DISPLAY! err=%s",
|
||||
uirenderer::renderthread::EglManager::eglErrorString());
|
||||
// These objects are initialized below but the default "null"
|
||||
// values are used to cleanup properly at any point in the
|
||||
// initialization sequenc
|
||||
GLuint texture = 0;
|
||||
// We use an EGLImage to access the content of the GraphicBuffer
|
||||
// The EGL image is later bound to a 2D texture
|
||||
EGLClientBuffer clientBuffer = (EGLClientBuffer) buffer.getNativeBuffer();
|
||||
AutoEglImage autoImage(display, clientBuffer);
|
||||
if (autoImage.image == EGL_NO_IMAGE_KHR) {
|
||||
ALOGW("Could not create EGL image, err =%s",
|
||||
uirenderer::renderthread::EglManager::eglErrorString());
|
||||
return false;
|
||||
}
|
||||
glGenTextures(1, &texture);
|
||||
caches.textureState().bindTexture(texture);
|
||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image);
|
||||
|
||||
GL_CHECKPOINT(MODERATE);
|
||||
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(),
|
||||
format, type, bitmap.getPixels());
|
||||
|
||||
GL_CHECKPOINT(MODERATE);
|
||||
|
||||
// The fence is used to wait for the texture upload to finish
|
||||
// properly. We cannot rely on glFlush() and glFinish() as
|
||||
// some drivers completely ignore these API calls
|
||||
AutoEglFence autoFence(display);
|
||||
if (autoFence.fence == EGL_NO_SYNC_KHR) {
|
||||
LOG_ALWAYS_FATAL("Could not create sync fence %#x", eglGetError());
|
||||
return false;
|
||||
}
|
||||
// The flag EGL_SYNC_FLUSH_COMMANDS_BIT_KHR will trigger a
|
||||
// pipeline flush (similar to what a glFlush() would do.)
|
||||
EGLint waitStatus = eglClientWaitSyncKHR(display, autoFence.fence,
|
||||
EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, FENCE_TIMEOUT);
|
||||
if (waitStatus != EGL_CONDITION_SATISFIED_KHR) {
|
||||
LOG_ALWAYS_FATAL("Failed to wait for the fence %#x", eglGetError());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(uirenderer::renderthread::RenderThread& renderThread,
|
||||
SkBitmap& skBitmap) {
|
||||
renderThread.eglManager().initialize();
|
||||
uirenderer::Caches& caches = uirenderer::Caches::getInstance();
|
||||
|
||||
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
|
||||
sp<IGraphicBufferAlloc> alloc(composer->createGraphicBufferAlloc());
|
||||
if (alloc == NULL) {
|
||||
ALOGW("createGraphicBufferAlloc() failed in GraphicBuffer.create()");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const SkImageInfo& info = skBitmap.info();
|
||||
if (info.colorType() == kUnknown_SkColorType) {
|
||||
ALOGW("unable to create hardware bitmap of configuration");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sk_sp<SkColorSpace> sRGB = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
|
||||
bool needSRGB = skBitmap.info().colorSpace() == sRGB.get();
|
||||
bool hasSRGB = caches.extensions().hasSRGB();
|
||||
GLint format, type, internalFormat;
|
||||
uirenderer::Texture::colorTypeToGlFormatAndType(caches, skBitmap.colorType(),
|
||||
needSRGB, &internalFormat, &format, &type);
|
||||
|
||||
PixelFormat pixelFormat = internalFormatToPixelFormat(internalFormat);
|
||||
status_t error;
|
||||
sp<GraphicBuffer> buffer = alloc->createGraphicBuffer(info.width(), info.height(), pixelFormat,
|
||||
GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER
|
||||
| GraphicBuffer::USAGE_SW_READ_NEVER , &error);
|
||||
|
||||
if (!buffer.get()) {
|
||||
ALOGW("createGraphicBuffer() failed in GraphicBuffer.create()");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SkBitmap bitmap;
|
||||
if (CC_UNLIKELY(uirenderer::Texture::hasUnsupportedColorType(skBitmap.info(),
|
||||
hasSRGB, sRGB.get()))) {
|
||||
bitmap = uirenderer::Texture::uploadToN32(skBitmap, hasSRGB, std::move(sRGB));
|
||||
} else {
|
||||
bitmap = skBitmap;
|
||||
}
|
||||
|
||||
if (!uploadBitmapToGraphicBuffer(caches, bitmap, *buffer, format, type)) {
|
||||
return nullptr;
|
||||
}
|
||||
return sk_sp<Bitmap>(new Bitmap(std::move(buffer), info));
|
||||
}
|
||||
|
||||
sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(SkBitmap& bitmap) {
|
||||
return uirenderer::renderthread::RenderProxy::allocateHardwareBitmap(bitmap);
|
||||
}
|
||||
|
||||
sk_sp<Bitmap> Bitmap::allocateHeapBitmap(SkBitmap* bitmap, SkColorTable* ctable) {
|
||||
return allocateBitmap(bitmap, ctable, &android::allocateHeapBitmap);
|
||||
}
|
||||
@@ -183,6 +360,16 @@ Bitmap::Bitmap(void* address, int fd, size_t mappedSize,
|
||||
reconfigure(info, rowBytes, ctable);
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(sp<GraphicBuffer>&& buffer, const SkImageInfo& info)
|
||||
: SkPixelRef(info)
|
||||
, mPixelStorageType(PixelStorageType::Hardware) {
|
||||
auto rawBuffer = buffer.get();
|
||||
mPixelStorage.hardware.buffer = rawBuffer;
|
||||
if (rawBuffer) {
|
||||
rawBuffer->incStrong(rawBuffer);
|
||||
}
|
||||
mRowBytes = bytesPerPixel(buffer->getPixelFormat()) * buffer->getStride();
|
||||
}
|
||||
Bitmap::~Bitmap() {
|
||||
switch (mPixelStorageType) {
|
||||
case PixelStorageType::External:
|
||||
@@ -196,6 +383,12 @@ Bitmap::~Bitmap() {
|
||||
case PixelStorageType::Heap:
|
||||
free(mPixelStorage.heap.address);
|
||||
break;
|
||||
case PixelStorageType::Hardware:
|
||||
auto buffer = mPixelStorage.hardware.buffer;
|
||||
buffer->decStrong(buffer);
|
||||
mPixelStorage.hardware.buffer = nullptr;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (android::uirenderer::Caches::hasInstance()) {
|
||||
@@ -219,6 +412,9 @@ void* Bitmap::getStorage() const {
|
||||
return mPixelStorage.ashmem.address;
|
||||
case PixelStorageType::Heap:
|
||||
return mPixelStorage.heap.address;
|
||||
case PixelStorageType::Hardware:
|
||||
LOG_ALWAYS_FATAL_IF("Can't get address for hardware bitmap");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,6 +460,11 @@ void Bitmap::setAlphaType(SkAlphaType alphaType) {
|
||||
}
|
||||
|
||||
void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
|
||||
if (isHardware()) {
|
||||
//TODO: use readback to get pixels
|
||||
LOG_ALWAYS_FATAL("Not implemented");
|
||||
return;
|
||||
}
|
||||
outBitmap->setInfo(info(), rowBytes());
|
||||
outBitmap->setPixelRef(this);
|
||||
outBitmap->setHasHardwareMipMap(mHasHardwareMipMap);
|
||||
@@ -274,4 +475,11 @@ void Bitmap::getBounds(SkRect* bounds) const {
|
||||
bounds->set(0, 0, SkIntToScalar(info().width()), SkIntToScalar(info().height()));
|
||||
}
|
||||
|
||||
GraphicBuffer* Bitmap::graphicBuffer() {
|
||||
if (isHardware()) {
|
||||
return mPixelStorage.hardware.buffer;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <SkImageInfo.h>
|
||||
#include <SkPixelRef.h>
|
||||
#include <cutils/compiler.h>
|
||||
#include <ui/GraphicBuffer.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
@@ -27,8 +28,17 @@ enum class PixelStorageType {
|
||||
External,
|
||||
Heap,
|
||||
Ashmem,
|
||||
Hardware,
|
||||
};
|
||||
|
||||
namespace uirenderer {
|
||||
namespace renderthread {
|
||||
class RenderThread;
|
||||
}
|
||||
}
|
||||
|
||||
class PixelStorage;
|
||||
|
||||
typedef void (*FreeFunc)(void* addr, void* context);
|
||||
|
||||
class ANDROID_API Bitmap : public SkPixelRef {
|
||||
@@ -36,17 +46,24 @@ public:
|
||||
static sk_sp<Bitmap> allocateHeapBitmap(SkBitmap* bitmap, SkColorTable* ctable);
|
||||
static sk_sp<Bitmap> allocateHeapBitmap(const SkImageInfo& info);
|
||||
|
||||
static sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& bitmap);
|
||||
|
||||
static sk_sp<Bitmap> allocateAshmemBitmap(SkBitmap* bitmap, SkColorTable* ctable);
|
||||
static sk_sp<Bitmap> allocateAshmemBitmap(size_t allocSize, const SkImageInfo& info,
|
||||
size_t rowBytes, SkColorTable* ctable);
|
||||
|
||||
static sk_sp<Bitmap> createFrom(const SkImageInfo&, SkPixelRef&);
|
||||
|
||||
static sk_sp<Bitmap> allocateHardwareBitmap(uirenderer::renderthread::RenderThread&,
|
||||
SkBitmap& bitmap);
|
||||
|
||||
Bitmap(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes,
|
||||
SkColorTable* ctable);
|
||||
Bitmap(void* address, void* context, FreeFunc freeFunc,
|
||||
const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
|
||||
Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info,
|
||||
size_t rowBytes, SkColorTable* ctable);
|
||||
Bitmap(sp<GraphicBuffer>&& buffer, const SkImageInfo& info);
|
||||
|
||||
int width() const { return info().width(); }
|
||||
int height() const { return info().height(); }
|
||||
@@ -81,13 +98,18 @@ public:
|
||||
bool readyToDraw() const {
|
||||
return this->colorType() != kIndex_8_SkColorType || mColorTable;
|
||||
}
|
||||
|
||||
bool isHardware() const {
|
||||
return mPixelStorageType == PixelStorageType::Hardware;
|
||||
}
|
||||
|
||||
GraphicBuffer* graphicBuffer();
|
||||
protected:
|
||||
virtual bool onNewLockPixels(LockRec* rec) override;
|
||||
virtual void onUnlockPixels() override { };
|
||||
virtual size_t getAllocatedSizeInBytes() const override;
|
||||
private:
|
||||
virtual ~Bitmap();
|
||||
void doFreePixels();
|
||||
void* getStorage() const;
|
||||
|
||||
PixelStorageType mPixelStorageType;
|
||||
@@ -111,6 +133,9 @@ private:
|
||||
void* address;
|
||||
size_t size;
|
||||
} heap;
|
||||
struct {
|
||||
GraphicBuffer* buffer;
|
||||
} hardware;
|
||||
} mPixelStorage;
|
||||
};
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "EglManager.h"
|
||||
|
||||
#include "Texture.h"
|
||||
#include "Caches.h"
|
||||
#include "DeviceInfo.h"
|
||||
#include "Properties.h"
|
||||
@@ -60,7 +61,7 @@ static const char* egl_error_str(EGLint error) {
|
||||
return "Unknown error";
|
||||
}
|
||||
}
|
||||
static const char* egl_error_str() {
|
||||
const char* EglManager::eglErrorString() {
|
||||
return egl_error_str(eglGetError());
|
||||
}
|
||||
|
||||
@@ -103,11 +104,11 @@ void EglManager::initialize() {
|
||||
|
||||
mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
|
||||
"Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str());
|
||||
"Failed to get EGL_DEFAULT_DISPLAY! err=%s", eglErrorString());
|
||||
|
||||
EGLint major, minor;
|
||||
LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE,
|
||||
"Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str());
|
||||
"Failed to initialize display %p! err=%s", mEglDisplay, eglErrorString());
|
||||
|
||||
ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor);
|
||||
|
||||
@@ -186,7 +187,7 @@ void EglManager::loadConfig() {
|
||||
loadConfig();
|
||||
} else {
|
||||
// Failed to get a valid config
|
||||
LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str());
|
||||
LOG_ALWAYS_FATAL("Failed to choose config, error = %s", eglErrorString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,7 +199,7 @@ void EglManager::createContext() {
|
||||
};
|
||||
mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs);
|
||||
LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT,
|
||||
"Failed to create context, error = %s", egl_error_str());
|
||||
"Failed to create context, error = %s", eglErrorString());
|
||||
}
|
||||
|
||||
void EglManager::createPBufferSurface() {
|
||||
@@ -225,12 +226,12 @@ EGLSurface EglManager::createSurface(EGLNativeWindowType window) {
|
||||
EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, attribs);
|
||||
LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
|
||||
"Failed to create EGLSurface for window %p, eglErr = %s",
|
||||
(void*) window, egl_error_str());
|
||||
(void*) window, eglErrorString());
|
||||
|
||||
if (mSwapBehavior != SwapBehavior::Preserved) {
|
||||
LOG_ALWAYS_FATAL_IF(eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED) == EGL_FALSE,
|
||||
"Failed to set swap behavior to destroyed for window %p, eglErr = %s",
|
||||
(void*) window, egl_error_str());
|
||||
(void*) window, eglErrorString());
|
||||
}
|
||||
|
||||
return surface;
|
||||
@@ -241,7 +242,7 @@ void EglManager::destroySurface(EGLSurface surface) {
|
||||
makeCurrent(EGL_NO_SURFACE);
|
||||
}
|
||||
if (!eglDestroySurface(mEglDisplay, surface)) {
|
||||
ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, egl_error_str());
|
||||
ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, eglErrorString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,7 +277,7 @@ bool EglManager::makeCurrent(EGLSurface surface, EGLint* errOut) {
|
||||
(void*)surface, egl_error_str(*errOut));
|
||||
} else {
|
||||
LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s",
|
||||
(void*)surface, egl_error_str());
|
||||
(void*)surface, eglErrorString());
|
||||
}
|
||||
}
|
||||
mCurrentSurface = surface;
|
||||
@@ -317,7 +318,7 @@ void EglManager::damageFrame(const Frame& frame, const SkRect& dirty) {
|
||||
frame.map(dirty, rects);
|
||||
if (!eglSetDamageRegionKHR(mEglDisplay, frame.mSurface, rects, 1)) {
|
||||
LOG_ALWAYS_FATAL("Failed to set damage region on surface %p, error=%s",
|
||||
(void*)frame.mSurface, egl_error_str());
|
||||
(void*)frame.mSurface, eglErrorString());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -371,14 +372,14 @@ bool EglManager::setPreserveBuffer(EGLSurface surface, bool preserve) {
|
||||
preserve ? EGL_BUFFER_PRESERVED : EGL_BUFFER_DESTROYED);
|
||||
if (!preserved) {
|
||||
ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s",
|
||||
(void*) surface, egl_error_str());
|
||||
(void*) surface, eglErrorString());
|
||||
// Maybe it's already set?
|
||||
EGLint swapBehavior;
|
||||
if (eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &swapBehavior)) {
|
||||
preserved = (swapBehavior == EGL_BUFFER_PRESERVED);
|
||||
} else {
|
||||
ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p",
|
||||
(void*) surface, egl_error_str());
|
||||
(void*) surface, eglErrorString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ private:
|
||||
// and EGLConfig, which are re-used by CanvasContext
|
||||
class EglManager {
|
||||
public:
|
||||
static const char* eglErrorString();
|
||||
// Returns true on success, false on failure
|
||||
void initialize();
|
||||
|
||||
@@ -83,7 +84,6 @@ public:
|
||||
|
||||
private:
|
||||
friend class RenderThread;
|
||||
|
||||
explicit EglManager(RenderThread& thread);
|
||||
// EglContext is never destroyed, method is purposely not implemented
|
||||
~EglManager();
|
||||
|
||||
@@ -650,6 +650,19 @@ void RenderProxy::prepareToDraw(Bitmap& bitmap) {
|
||||
}
|
||||
}
|
||||
|
||||
CREATE_BRIDGE2(allocateHardwareBitmap, RenderThread* thread, SkBitmap* bitmap) {
|
||||
sk_sp<Bitmap> hardwareBitmap = Bitmap::allocateHardwareBitmap(*args->thread, *args->bitmap);
|
||||
return hardwareBitmap.release();
|
||||
}
|
||||
|
||||
sk_sp<Bitmap> RenderProxy::allocateHardwareBitmap(SkBitmap& bitmap) {
|
||||
SETUP_TASK(allocateHardwareBitmap);
|
||||
args->bitmap = &bitmap;
|
||||
args->thread = &RenderThread::getInstance();
|
||||
sk_sp<Bitmap> hardwareBitmap(reinterpret_cast<Bitmap*>(staticPostAndWait(task)));
|
||||
return hardwareBitmap;
|
||||
}
|
||||
|
||||
void RenderProxy::post(RenderTask* task) {
|
||||
mRenderThread.queue(task);
|
||||
}
|
||||
|
||||
@@ -130,6 +130,7 @@ public:
|
||||
int left, int top, int right, int bottom, SkBitmap* bitmap);
|
||||
ANDROID_API static void prepareToDraw(Bitmap& bitmap);
|
||||
|
||||
static sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& bitmap);
|
||||
private:
|
||||
RenderThread& mRenderThread;
|
||||
CanvasContext* mContext;
|
||||
|
||||
74
libs/hwui/tests/common/BitmapAllocationTestUtils.h
Normal file
74
libs/hwui/tests/common/BitmapAllocationTestUtils.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TestScene.h"
|
||||
|
||||
#include <SkBitmap.h>
|
||||
#include <string>
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
namespace test {
|
||||
|
||||
class BitmapAllocationTestUtils {
|
||||
public:
|
||||
static sk_sp<Bitmap> allocateHeapBitmap(int width, int height,
|
||||
SkColorType colorType, std::function<void(SkBitmap& bitmap)> setup) {
|
||||
sk_sp<Bitmap> bitmap = TestUtils::createBitmap(width, height, colorType);
|
||||
SkBitmap skBitmap;
|
||||
bitmap->getSkBitmap(&skBitmap);
|
||||
setup(skBitmap);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
static sk_sp<Bitmap> allocateHardwareBitmap(int width, int height,
|
||||
SkColorType colorType, std::function<void(SkBitmap& bitmap)> setup) {
|
||||
SkBitmap skBitmap;
|
||||
sk_sp<Bitmap> heapBitmap(TestUtils::createBitmap(width, height, &skBitmap));
|
||||
setup(skBitmap);
|
||||
return Bitmap::allocateHardwareBitmap(skBitmap);
|
||||
}
|
||||
|
||||
typedef sk_sp<Bitmap> (*BitmapAllocator) (int, int, SkColorType,
|
||||
std::function<void(SkBitmap& bitmap)> setup);
|
||||
|
||||
template <class T, BitmapAllocator allocator>
|
||||
static test::TestScene* createBitmapAllocationScene(const TestScene::Options&) {
|
||||
return new T(allocator);
|
||||
}
|
||||
|
||||
template <class BaseScene>
|
||||
static bool registerBitmapAllocationScene(std::string name, std::string description) {
|
||||
TestScene::registerScene({
|
||||
name + "GlTex",
|
||||
description + " (GlTex version).",
|
||||
createBitmapAllocationScene<BaseScene, &allocateHeapBitmap>
|
||||
});
|
||||
|
||||
TestScene::registerScene({
|
||||
name + "EglImage",
|
||||
description + " (EglImage version).",
|
||||
createBitmapAllocationScene<BaseScene, &allocateHardwareBitmap>
|
||||
});
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace uirenderer
|
||||
} // namespace android
|
||||
74
libs/hwui/tests/common/scenes/BitmapFillrate.cpp
Normal file
74
libs/hwui/tests/common/scenes/BitmapFillrate.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "TestSceneBase.h"
|
||||
#include "tests/common/BitmapAllocationTestUtils.h"
|
||||
#include "utils/Color.h"
|
||||
|
||||
#include <SkBitmap.h>
|
||||
|
||||
using namespace android;
|
||||
using namespace android::uirenderer;
|
||||
|
||||
class BitmapFillrate;
|
||||
|
||||
static bool _BitmapFillrate(
|
||||
BitmapAllocationTestUtils::registerBitmapAllocationScene<BitmapFillrate>(
|
||||
"bitmapFillrate", "Draws multiple large half transparent bitmaps."));
|
||||
|
||||
class BitmapFillrate : public TestScene {
|
||||
public:
|
||||
BitmapFillrate(BitmapAllocationTestUtils::BitmapAllocator allocator)
|
||||
: TestScene()
|
||||
, mAllocator(allocator) { }
|
||||
|
||||
void createContent(int width, int height, Canvas& canvas) override {
|
||||
canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
|
||||
createNode(canvas, 0x909C27B0, 0, 0, width, height);
|
||||
createNode(canvas, 0xA0CDDC39, width / 3, height / 3, width, height);
|
||||
createNode(canvas, 0x90009688, width / 3, 0, width, height);
|
||||
createNode(canvas, 0xA0FF5722, 0, height / 3, width, height);
|
||||
createNode(canvas, 0x9000796B, width / 6, height/6, width, height);
|
||||
createNode(canvas, 0xA0FFC107, width / 6, 0, width, height);
|
||||
}
|
||||
|
||||
void doFrame(int frameNr) override {
|
||||
for (size_t ci = 0; ci < mNodes.size(); ci++) {
|
||||
mNodes[ci]->mutateStagingProperties().setTranslationX(frameNr % 200);
|
||||
mNodes[ci]->mutateStagingProperties().setTranslationY(frameNr % 200);
|
||||
mNodes[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
|
||||
}
|
||||
}
|
||||
private:
|
||||
void createNode(Canvas& canvas, SkColor color, int left, int top,
|
||||
int width, int height) {
|
||||
int itemWidth = 2 * width / 3;
|
||||
int itemHeight = 2 * height / 3;
|
||||
auto card = TestUtils::createNode(left, top, left + itemWidth , top + itemHeight,
|
||||
[this, itemWidth, itemHeight, color](RenderProperties& props, Canvas& canvas) {
|
||||
sk_sp<Bitmap> bitmap = mAllocator(itemWidth, itemHeight, kRGBA_8888_SkColorType,
|
||||
[color](SkBitmap& skBitmap) {
|
||||
skBitmap.eraseColor(color);
|
||||
});
|
||||
canvas.drawBitmap(*bitmap, 0, 0, nullptr);
|
||||
});
|
||||
canvas.drawRenderNode(card.get());
|
||||
mNodes.push_back(card);
|
||||
}
|
||||
|
||||
BitmapAllocationTestUtils::BitmapAllocator mAllocator;
|
||||
std::vector< sp<RenderNode> > mNodes;
|
||||
};
|
||||
@@ -27,7 +27,7 @@ namespace uirenderer {
|
||||
#if DEBUG_OPENGL
|
||||
#define GL_CHECKPOINT(LEVEL) \
|
||||
do { if (DEBUG_OPENGL >= DEBUG_LEVEL_##LEVEL) {\
|
||||
LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(),\
|
||||
LOG_ALWAYS_FATAL_IF(android::uirenderer::GLUtils::dumpGLErrors(),\
|
||||
"GL errors! %s:%d", __FILE__, __LINE__);\
|
||||
} } while (0)
|
||||
#else
|
||||
|
||||
Reference in New Issue
Block a user