| /*------------------------------------------------------------------------- |
| * drawElements Quality Program EGL Module |
| * --------------------------------------- |
| * |
| * Copyright 2014 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. |
| * |
| *//*! |
| * \file |
| * \brief Android-specific operations. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "teglAndroidUtil.hpp" |
| |
| #include "deStringUtil.hpp" |
| #include "tcuTextureUtil.hpp" |
| #include "gluTextureUtil.hpp" |
| #include "glwEnums.hpp" |
| #include "eglwLibrary.hpp" |
| #include "eglwEnums.hpp" |
| |
| namespace deqp |
| { |
| namespace egl |
| { |
| namespace Image |
| { |
| |
| using std::string; |
| using de::MovePtr; |
| using tcu::PixelBufferAccess; |
| using tcu::TextureFormat; |
| using tcu::Texture2D; |
| using eglu::AttribMap; |
| using namespace glw; |
| using namespace eglw; |
| |
| #if (DE_OS != DE_OS_ANDROID) |
| |
| MovePtr<ImageSource> createAndroidNativeImageSource (GLenum format, deUint32 numLayers, bool isYUV) |
| { |
| DE_UNREF(numLayers); |
| return createUnsupportedImageSource("Not Android platform", format, isYUV); |
| } |
| |
| #else // DE_OS == DE_OS_ANDROID |
| |
| #if defined(__ANDROID_API_O__) && (DE_ANDROID_API >= __ANDROID_API_O__) |
| # define BUILT_WITH_ANDROID_HARDWARE_BUFFER 1 |
| #endif |
| |
| #if defined(__ANDROID_API_P__) && (DE_ANDROID_API >= __ANDROID_API_P__) |
| # define BUILT_WITH_ANDROID_P_HARDWARE_BUFFER 1 |
| #endif |
| |
| #if !defined(BUILT_WITH_ANDROID_HARDWARE_BUFFER) |
| |
| MovePtr<ImageSource> createAndroidNativeImageSource (GLenum format, deUint32 numLayers, bool isYUV) |
| { |
| DE_UNREF(numLayers); |
| return createUnsupportedImageSource("AHB API not supported", format, isYUV); |
| } |
| |
| #else // defined(BUILT_WITH_ANDROID_HARDWARE_BUFFER) |
| |
| namespace |
| { |
| |
| #include <sys/system_properties.h> |
| #include <android/hardware_buffer.h> |
| #include "deDynamicLibrary.hpp" |
| |
| const deUint32 AHB_FORMAT_Y8Cb8Cr8_420 = 0x23; |
| |
| deInt32 androidGetSdkVersion (void) |
| { |
| static deInt32 sdkVersion = -1; |
| if (sdkVersion < 0) |
| { |
| char value[128] = {0}; |
| __system_property_get("ro.build.version.sdk", value); |
| sdkVersion = static_cast<deInt32>(strtol(value, DE_NULL, 10)); |
| printf("SDK Version is %d\n", sdkVersion); |
| } |
| return sdkVersion; |
| } |
| |
| typedef int (*pfnAHardwareBuffer_allocate)(const AHardwareBuffer_Desc* desc, AHardwareBuffer** outBuffer); |
| typedef void (*pfnAHardwareBuffer_describe)(const AHardwareBuffer* buffer, AHardwareBuffer_Desc* outDesc); |
| typedef void (*pfnAHardwareBuffer_acquire)(AHardwareBuffer* buffer); |
| typedef void (*pfnAHardwareBuffer_release)(AHardwareBuffer* buffer); |
| typedef int (*pfnAHardwareBuffer_isSupported)(const AHardwareBuffer_Desc* desc); |
| |
| struct AhbFunctions |
| { |
| pfnAHardwareBuffer_allocate allocate; |
| pfnAHardwareBuffer_describe describe; |
| pfnAHardwareBuffer_acquire acquire; |
| pfnAHardwareBuffer_release release; |
| pfnAHardwareBuffer_isSupported isSupported; |
| }; |
| |
| AhbFunctions ahbFunctions; |
| |
| bool ahbFunctionsLoaded (AhbFunctions* pAhbFunctions, deInt32 sdkVersion) |
| { |
| static bool ahbApiLoaded = false; |
| if (ahbApiLoaded || |
| ((pAhbFunctions->allocate != DE_NULL) && |
| (pAhbFunctions->describe != DE_NULL) && |
| (pAhbFunctions->acquire != DE_NULL) && |
| (pAhbFunctions->release != DE_NULL) && |
| (pAhbFunctions->isSupported != DE_NULL || sdkVersion < 29))) |
| { |
| ahbApiLoaded = true; |
| return true; |
| } |
| return false; |
| } |
| |
| bool loadAhbDynamicApis (deInt32 sdkVersion) |
| { |
| if (!ahbFunctionsLoaded(&ahbFunctions, sdkVersion)) |
| { |
| static de::DynamicLibrary libnativewindow("libnativewindow.so"); |
| ahbFunctions.allocate = reinterpret_cast<pfnAHardwareBuffer_allocate>(libnativewindow.getFunction("AHardwareBuffer_allocate")); |
| ahbFunctions.describe = reinterpret_cast<pfnAHardwareBuffer_describe>(libnativewindow.getFunction("AHardwareBuffer_describe")); |
| ahbFunctions.acquire = reinterpret_cast<pfnAHardwareBuffer_acquire>(libnativewindow.getFunction("AHardwareBuffer_acquire")); |
| ahbFunctions.release = reinterpret_cast<pfnAHardwareBuffer_release>(libnativewindow.getFunction("AHardwareBuffer_release")); |
| if (sdkVersion >= 29) |
| ahbFunctions.isSupported = reinterpret_cast<pfnAHardwareBuffer_isSupported>(libnativewindow.getFunction("AHardwareBuffer_isSupported")); |
| else |
| ahbFunctions.isSupported = DE_NULL; |
| |
| return ahbFunctionsLoaded(&ahbFunctions, sdkVersion); |
| } |
| |
| return true; |
| } |
| |
| deUint32 getPixelFormat (GLenum format) |
| { |
| switch (format) |
| { |
| case GL_RGB565: return AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM; |
| case GL_RGB8: return AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM; |
| case GL_RGBA8: return AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; |
| case GL_DEPTH_COMPONENT16: return AHARDWAREBUFFER_FORMAT_D16_UNORM; |
| case GL_DEPTH_COMPONENT24: return AHARDWAREBUFFER_FORMAT_D24_UNORM; |
| case GL_DEPTH24_STENCIL8: return AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT; |
| case GL_DEPTH_COMPONENT32F: return AHARDWAREBUFFER_FORMAT_D32_FLOAT; |
| case GL_DEPTH32F_STENCIL8: return AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT; |
| case GL_RGB10_A2: return AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM; |
| case GL_RGBA16F: return AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT; |
| case GL_STENCIL_INDEX8: return AHARDWAREBUFFER_FORMAT_S8_UINT; |
| |
| default: TCU_THROW(NotSupportedError, "Texture format unsupported by Android"); |
| } |
| } |
| |
| class AndroidNativeClientBuffer : public ClientBuffer |
| { |
| public: |
| AndroidNativeClientBuffer (const Library& egl, GLenum format, deUint32 numLayers, bool isYUV); |
| ~AndroidNativeClientBuffer (void); |
| EGLClientBuffer get (void) const; |
| void lock (void** data); |
| void unlock (void); |
| AHardwareBuffer_Desc describe (void); |
| |
| private: |
| const Library& m_egl; |
| AHardwareBuffer* m_hardwareBuffer; |
| }; |
| |
| AndroidNativeClientBuffer::AndroidNativeClientBuffer (const Library& egl, GLenum format, deUint32 numLayers, bool isYUV) |
| : m_egl(egl) |
| { |
| deInt32 sdkVersion = androidGetSdkVersion(); |
| |
| #if defined(BUILT_WITH_ANDROID_P_HARDWARE_BUFFER) |
| // When testing AHB on Android-P and newer the CTS must be compiled against API28 or newer. |
| DE_TEST_ASSERT(sdkVersion >= 28); /*__ANDROID_API_P__ */ |
| #else |
| // When testing AHB on Android-O and newer the CTS must be compiled against API26 or newer. |
| DE_TEST_ASSERT(sdkVersion >= 26); /* __ANDROID_API_O__ */ |
| #endif // !defined(BUILT_WITH_ANDROID_P_HARDWARE_BUFFER) |
| |
| if (!loadAhbDynamicApis(sdkVersion)) |
| { |
| // Couldn't load Android AHB system APIs. |
| DE_TEST_ASSERT(false); |
| } |
| |
| AHardwareBuffer_Desc hbufferdesc = { |
| 64u, |
| 64u, |
| numLayers, |
| isYUV ? AHB_FORMAT_Y8Cb8Cr8_420 : getPixelFormat(format), |
| AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | |
| AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY | |
| AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | |
| AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT, |
| 0u, // Stride in pixels, ignored for AHardwareBuffer_allocate() |
| 0u, // Initialize to zero, reserved for future use |
| 0u // Initialize to zero, reserved for future use |
| }; |
| |
| // If we have AHardwareBuffer_isSupported use that before trying the allocation. |
| if (ahbFunctions.isSupported != DE_NULL) |
| { |
| if (!ahbFunctions.isSupported(&hbufferdesc)) |
| TCU_THROW(NotSupportedError, "Texture format unsupported"); |
| } |
| |
| if (ahbFunctions.allocate(&hbufferdesc, &m_hardwareBuffer) != 0) |
| { |
| // Throw unsupported instead of failing the test as the texture format or the number |
| // of layers might be unsupported. |
| TCU_THROW(NotSupportedError, "AHB allocation failed"); |
| } |
| } |
| |
| AndroidNativeClientBuffer::~AndroidNativeClientBuffer (void) |
| { |
| ahbFunctions.release(m_hardwareBuffer); |
| } |
| |
| EGLClientBuffer AndroidNativeClientBuffer::get (void) const |
| { |
| typedef EGLW_APICALL EGLClientBuffer (EGLW_APIENTRY* eglGetNativeClientBufferANDROIDFunc) (const struct AHardwareBuffer *buffer); |
| return ((eglGetNativeClientBufferANDROIDFunc)m_egl.getProcAddress("eglGetNativeClientBufferANDROID"))(m_hardwareBuffer); |
| } |
| |
| void AndroidNativeClientBuffer::lock (void** data) |
| { |
| const int status = AHardwareBuffer_lock(m_hardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY, -1, DE_NULL, data); |
| |
| if (status != 0) |
| TCU_FAIL(("AHardwareBuffer_lock failed with error: " + de::toString(status)).c_str()); |
| } |
| |
| void AndroidNativeClientBuffer::unlock (void) |
| { |
| const int status = AHardwareBuffer_unlock(m_hardwareBuffer, DE_NULL); |
| |
| if (status != 0) |
| TCU_FAIL(("AHardwareBuffer_unlock failed with error: " + de::toString(status)).c_str()); |
| } |
| |
| AHardwareBuffer_Desc AndroidNativeClientBuffer::describe (void) |
| { |
| AHardwareBuffer_Desc ret; |
| ahbFunctions.describe(m_hardwareBuffer, &ret); |
| return ret; |
| } |
| |
| class AndroidNativeImageSource : public ImageSource |
| { |
| public: |
| AndroidNativeImageSource (GLenum format, deUint32 numLayers, bool isYUV) : m_format(format), m_numLayers(numLayers), m_isY8Cb8Cr8_420(isYUV) {} |
| ~AndroidNativeImageSource (void); |
| MovePtr<ClientBuffer> createBuffer (const Library& egl, const glw::Functions&, Texture2D*) const; |
| string getRequiredExtension (void) const { return "EGL_ANDROID_get_native_client_buffer"; } |
| EGLImageKHR createImage (const Library& egl, EGLDisplay dpy, EGLContext ctx, EGLClientBuffer clientBuffer) const; |
| GLenum getEffectiveFormat (void) const { return m_format; } |
| bool isYUVFormatImage (void) const { return m_isY8Cb8Cr8_420; } |
| protected: |
| GLenum m_format; |
| deUint32 m_numLayers; |
| bool m_isY8Cb8Cr8_420; |
| }; |
| |
| AndroidNativeImageSource::~AndroidNativeImageSource (void) |
| { |
| } |
| |
| MovePtr<ClientBuffer> AndroidNativeImageSource::createBuffer (const Library& egl, const glw::Functions&, Texture2D* ref) const |
| { |
| MovePtr<AndroidNativeClientBuffer> buffer (new AndroidNativeClientBuffer(egl, m_format, m_numLayers, m_isY8Cb8Cr8_420)); |
| |
| if (ref != DE_NULL) |
| { |
| const TextureFormat texFormat = glu::mapGLInternalFormat(m_format); |
| void* bufferData = DE_NULL; |
| |
| *ref = Texture2D(texFormat, 64, 64); |
| ref->m_yuvTextureUsed = m_isY8Cb8Cr8_420; |
| ref->allocLevel(0); |
| tcu::fillWithComponentGradients(ref->getLevel(0), |
| tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f), |
| tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f)); |
| |
| // AHB doesn't allow locking a layered image. In that case the data |
| // will be initialized later using OpenGL API. |
| // YUV format texture will be initialized by glClear. |
| |
| if (m_numLayers == 1u && !m_isY8Cb8Cr8_420) |
| { |
| buffer->lock(&bufferData); |
| { |
| AHardwareBuffer_Desc desc = buffer->describe(); |
| const int rowPitch = texFormat.getPixelSize() * desc.stride; |
| const int slicePitch = rowPitch * desc.height; |
| PixelBufferAccess nativeBuffer (texFormat, desc.width, desc.height, 1, rowPitch, slicePitch, bufferData); |
| |
| tcu::copy(nativeBuffer, ref->getLevel(0)); |
| } |
| buffer->unlock(); |
| } |
| } |
| return MovePtr<ClientBuffer>(buffer); |
| } |
| |
| EGLImageKHR AndroidNativeImageSource::createImage (const Library& egl, EGLDisplay dpy, EGLContext, EGLClientBuffer clientBuffer) const |
| { |
| static const EGLint attribs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE }; |
| const EGLImageKHR image = egl.createImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attribs); |
| |
| EGLU_CHECK_MSG(egl, "eglCreateImageKHR()"); |
| return image; |
| } |
| |
| } // anonymous |
| |
| MovePtr<ImageSource> createAndroidNativeImageSource (GLenum format, deUint32 numLayers, bool isYUV) |
| { |
| try |
| { |
| return MovePtr<ImageSource>(new AndroidNativeImageSource(format, numLayers, isYUV)); |
| } |
| catch (const std::runtime_error& exc) |
| { |
| return createUnsupportedImageSource(string("Android native buffers unsupported: ") + exc.what(), format, isYUV); |
| } |
| } |
| |
| #endif // defined(BUILT_WITH_ANDROID_HARDWARE_BUFFER) |
| |
| #endif // DE_OS == DE_OS_ANDROID |
| |
| } // Image |
| } // egl |
| } // deqp |