skia2/tests/VkHardwareBufferTest.cpp
Greg Daniel 02497d4016 Update nanobench and skpbench to use flush API for gpu syncing.
This also allows us to remove all the one off Fence code that we
implemented in all the backend TestContexts

Change-Id: I9ff7ba4690cf3f19a180f51fc510991a112bb62c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/272456
Reviewed-by: Chris Dalton <csmartdalton@google.com>
Commit-Queue: Greg Daniel <egdaniel@google.com>
2020-02-24 17:21:35 +00:00

1332 lines
50 KiB
C++

/*
* Copyright 2018 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
// This is a GPU-backend specific test. It relies on static intializers to work
#include "include/core/SkTypes.h"
#if SK_SUPPORT_GPU && defined(SK_BUILD_FOR_ANDROID) && __ANDROID_API__ >= 26 && defined(SK_VULKAN)
#include "include/core/SkCanvas.h"
#include "include/core/SkImage.h"
#include "include/core/SkSurface.h"
#include "include/gpu/GrBackendSemaphore.h"
#include "include/gpu/GrContext.h"
#include "include/gpu/vk/GrVkBackendContext.h"
#include "include/gpu/vk/GrVkExtensions.h"
#include "src/core/SkAutoMalloc.h"
#include "src/gpu/GrContextPriv.h"
#include "src/gpu/GrGpu.h"
#include "src/gpu/GrProxyProvider.h"
#include "src/gpu/SkGr.h"
#include "src/gpu/gl/GrGLDefines.h"
#include "src/gpu/gl/GrGLUtil.h"
#include "tests/Test.h"
#include "tools/gpu/GrContextFactory.h"
#include "tools/gpu/vk/VkTestUtils.h"
#include <android/hardware_buffer.h>
#include <cinttypes>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES/gl.h>
#include <GLES/glext.h>
static const int DEV_W = 16, DEV_H = 16;
class BaseTestHelper {
public:
virtual ~BaseTestHelper() {}
virtual bool init(skiatest::Reporter* reporter) = 0;
virtual void cleanup() = 0;
virtual void releaseImage() = 0;
virtual sk_sp<SkImage> importHardwareBufferForRead(skiatest::Reporter* reporter,
AHardwareBuffer* buffer) = 0;
virtual sk_sp<SkSurface> importHardwareBufferForWrite(skiatest::Reporter* reporter,
AHardwareBuffer* buffer) = 0;
virtual void doClientSync() = 0;
virtual bool flushSurfaceAndSignalSemaphore(skiatest::Reporter* reporter, sk_sp<SkSurface>) = 0;
virtual bool importAndWaitOnSemaphore(skiatest::Reporter* reporter, int fdHandle,
sk_sp<SkSurface>) = 0;
virtual void makeCurrent() = 0;
virtual GrContext* grContext() = 0;
int getFdHandle() { return fFdHandle; }
protected:
BaseTestHelper() {}
int fFdHandle = 0;
};
#ifdef SK_GL
class EGLTestHelper : public BaseTestHelper {
public:
EGLTestHelper(const GrContextOptions& options) : fFactory(options) {}
~EGLTestHelper() override {}
void releaseImage() override {
this->makeCurrent();
if (!fGLCtx) {
return;
}
if (EGL_NO_IMAGE_KHR != fImage) {
fGLCtx->destroyEGLImage(fImage);
fImage = EGL_NO_IMAGE_KHR;
}
if (fTexID) {
GR_GL_CALL(fGLCtx->gl(), DeleteTextures(1, &fTexID));
fTexID = 0;
}
}
void cleanup() override {
this->releaseImage();
}
bool init(skiatest::Reporter* reporter) override;
sk_sp<SkImage> importHardwareBufferForRead(skiatest::Reporter* reporter,
AHardwareBuffer* buffer) override;
sk_sp<SkSurface> importHardwareBufferForWrite(skiatest::Reporter* reporter,
AHardwareBuffer* buffer) override;
void doClientSync() override;
bool flushSurfaceAndSignalSemaphore(skiatest::Reporter* reporter, sk_sp<SkSurface>) override;
bool importAndWaitOnSemaphore(skiatest::Reporter* reporter, int fdHandle,
sk_sp<SkSurface>) override;
void makeCurrent() override { fGLCtx->makeCurrent(); }
GrContext* grContext() override { return fGrContext; }
private:
bool importHardwareBuffer(skiatest::Reporter* reporter, AHardwareBuffer* buffer);
typedef EGLClientBuffer (*EGLGetNativeClientBufferANDROIDProc)(const struct AHardwareBuffer*);
typedef EGLImageKHR (*EGLCreateImageKHRProc)(EGLDisplay, EGLContext, EGLenum, EGLClientBuffer,
const EGLint*);
typedef void (*EGLImageTargetTexture2DOESProc)(EGLenum, void*);
EGLGetNativeClientBufferANDROIDProc fEGLGetNativeClientBufferANDROID;
EGLCreateImageKHRProc fEGLCreateImageKHR;
EGLImageTargetTexture2DOESProc fEGLImageTargetTexture2DOES;
PFNEGLCREATESYNCKHRPROC fEGLCreateSyncKHR;
PFNEGLWAITSYNCKHRPROC fEGLWaitSyncKHR;
PFNEGLGETSYNCATTRIBKHRPROC fEGLGetSyncAttribKHR;
PFNEGLDUPNATIVEFENCEFDANDROIDPROC fEGLDupNativeFenceFDANDROID;
PFNEGLDESTROYSYNCKHRPROC fEGLDestroySyncKHR;
EGLImageKHR fImage = EGL_NO_IMAGE_KHR;
GrGLuint fTexID = 0;
sk_gpu_test::GrContextFactory fFactory;
sk_gpu_test::ContextInfo fGLESContextInfo;
sk_gpu_test::GLTestContext* fGLCtx = nullptr;
GrContext* fGrContext = nullptr;
};
bool EGLTestHelper::init(skiatest::Reporter* reporter) {
fGLESContextInfo = fFactory.getContextInfo(sk_gpu_test::GrContextFactory::kGLES_ContextType);
fGrContext = fGLESContextInfo.grContext();
fGLCtx = fGLESContextInfo.glContext();
if (!fGrContext || !fGLCtx) {
return false;
}
if (kGLES_GrGLStandard != fGLCtx->gl()->fStandard) {
return false;
}
// Confirm we have egl and the needed extensions
if (!fGLCtx->gl()->hasExtension("EGL_KHR_image") ||
!fGLCtx->gl()->hasExtension("EGL_ANDROID_get_native_client_buffer") ||
!fGLCtx->gl()->hasExtension("GL_OES_EGL_image_external") ||
!fGLCtx->gl()->hasExtension("GL_OES_EGL_image") ||
!fGLCtx->gl()->hasExtension("EGL_KHR_fence_sync") ||
!fGLCtx->gl()->hasExtension("EGL_ANDROID_native_fence_sync")) {
return false;
}
fEGLGetNativeClientBufferANDROID =
(EGLGetNativeClientBufferANDROIDProc) eglGetProcAddress("eglGetNativeClientBufferANDROID");
if (!fEGLGetNativeClientBufferANDROID) {
ERRORF(reporter, "Failed to get the eglGetNativeClientBufferAndroid proc");
return false;
}
fEGLCreateImageKHR = (EGLCreateImageKHRProc) eglGetProcAddress("eglCreateImageKHR");
if (!fEGLCreateImageKHR) {
ERRORF(reporter, "Failed to get the proc eglCreateImageKHR");
return false;
}
fEGLImageTargetTexture2DOES =
(EGLImageTargetTexture2DOESProc) eglGetProcAddress("glEGLImageTargetTexture2DOES");
if (!fEGLImageTargetTexture2DOES) {
ERRORF(reporter, "Failed to get the proc EGLImageTargetTexture2DOES");
return false;
}
fEGLCreateSyncKHR = (PFNEGLCREATESYNCKHRPROC) eglGetProcAddress("eglCreateSyncKHR");
if (!fEGLCreateSyncKHR) {
ERRORF(reporter, "Failed to get the proc eglCreateSyncKHR");
return false;
}
fEGLWaitSyncKHR = (PFNEGLWAITSYNCKHRPROC) eglGetProcAddress("eglWaitSyncKHR");
if (!fEGLWaitSyncKHR) {
ERRORF(reporter, "Failed to get the proc eglWaitSyncKHR");
return false;
}
fEGLGetSyncAttribKHR = (PFNEGLGETSYNCATTRIBKHRPROC) eglGetProcAddress("eglGetSyncAttribKHR");
if (!fEGLGetSyncAttribKHR) {
ERRORF(reporter, "Failed to get the proc eglGetSyncAttribKHR");
return false;
}
fEGLDupNativeFenceFDANDROID =
(PFNEGLDUPNATIVEFENCEFDANDROIDPROC) eglGetProcAddress("eglDupNativeFenceFDANDROID");
if (!fEGLDupNativeFenceFDANDROID) {
ERRORF(reporter, "Failed to get the proc eglDupNativeFenceFDANDROID");
return false;
}
fEGLDestroySyncKHR = (PFNEGLDESTROYSYNCKHRPROC) eglGetProcAddress("eglDestroySyncKHR");
if (!fEGLDestroySyncKHR) {
ERRORF(reporter, "Failed to get the proc eglDestroySyncKHR");
return false;
}
return true;
}
bool EGLTestHelper::importHardwareBuffer(skiatest::Reporter* reporter, AHardwareBuffer* buffer) {
GrGLClearErr(fGLCtx->gl());
EGLClientBuffer eglClientBuffer = fEGLGetNativeClientBufferANDROID(buffer);
EGLint eglAttribs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
EGL_NONE };
EGLDisplay eglDisplay = eglGetCurrentDisplay();
fImage = fEGLCreateImageKHR(eglDisplay, EGL_NO_CONTEXT,
EGL_NATIVE_BUFFER_ANDROID,
eglClientBuffer, eglAttribs);
if (EGL_NO_IMAGE_KHR == fImage) {
SkDebugf("Could not create EGL image, err = (%#x)\n", (int) eglGetError() );
return false;
}
GR_GL_CALL(fGLCtx->gl(), GenTextures(1, &fTexID));
if (!fTexID) {
ERRORF(reporter, "Failed to create GL Texture");
return false;
}
GR_GL_CALL_NOERRCHECK(fGLCtx->gl(), BindTexture(GR_GL_TEXTURE_2D, fTexID));
if (GR_GL_GET_ERROR(fGLCtx->gl()) != GR_GL_NO_ERROR) {
ERRORF(reporter, "Failed to bind GL Texture");
return false;
}
fEGLImageTargetTexture2DOES(GL_TEXTURE_2D, fImage);
GLenum status = GL_NO_ERROR;
if ((status = glGetError()) != GL_NO_ERROR) {
ERRORF(reporter, "EGLImageTargetTexture2DOES failed (%#x)", (int) status);
return false;
}
fGrContext->resetContext(kTextureBinding_GrGLBackendState);
return true;
}
sk_sp<SkImage> EGLTestHelper::importHardwareBufferForRead(skiatest::Reporter* reporter,
AHardwareBuffer* buffer) {
if (!this->importHardwareBuffer(reporter, buffer)) {
return nullptr;
}
GrGLTextureInfo textureInfo;
textureInfo.fTarget = GR_GL_TEXTURE_2D;
textureInfo.fID = fTexID;
textureInfo.fFormat = GR_GL_RGBA8;
GrBackendTexture backendTex(DEV_W, DEV_H, GrMipMapped::kNo, textureInfo);
REPORTER_ASSERT(reporter, backendTex.isValid());
sk_sp<SkImage> image = SkImage::MakeFromTexture(fGrContext,
backendTex,
kTopLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType,
kPremul_SkAlphaType,
nullptr);
if (!image) {
ERRORF(reporter, "Failed to make wrapped GL SkImage");
return nullptr;
}
return image;
}
sk_sp<SkSurface> EGLTestHelper::importHardwareBufferForWrite(skiatest::Reporter* reporter,
AHardwareBuffer* buffer) {
if (!this->importHardwareBuffer(reporter, buffer)) {
return nullptr;
}
GrGLTextureInfo textureInfo;
textureInfo.fTarget = GR_GL_TEXTURE_2D;
textureInfo.fID = fTexID;
textureInfo.fFormat = GR_GL_RGBA8;
GrBackendTexture backendTex(DEV_W, DEV_H, GrMipMapped::kNo, textureInfo);
REPORTER_ASSERT(reporter, backendTex.isValid());
sk_sp<SkSurface> surface = SkSurface::MakeFromBackendTexture(fGrContext,
backendTex,
kTopLeft_GrSurfaceOrigin,
0,
kRGBA_8888_SkColorType,
nullptr, nullptr);
if (!surface) {
ERRORF(reporter, "Failed to make wrapped GL SkSurface");
return nullptr;
}
return surface;
}
bool EGLTestHelper::flushSurfaceAndSignalSemaphore(skiatest::Reporter* reporter,
sk_sp<SkSurface> surface) {
EGLDisplay eglDisplay = eglGetCurrentDisplay();
EGLSyncKHR eglsync = fEGLCreateSyncKHR(eglDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
if (EGL_NO_SYNC_KHR == eglsync) {
ERRORF(reporter, "Failed to create EGLSync for EGL_SYNC_NATIVE_FENCE_ANDROID\n");
return false;
}
surface->flush();
GR_GL_CALL(fGLCtx->gl(), Flush());
fFdHandle = fEGLDupNativeFenceFDANDROID(eglDisplay, eglsync);
EGLint result = fEGLDestroySyncKHR(eglDisplay, eglsync);
if (EGL_TRUE != result) {
ERRORF(reporter, "Failed to delete EGLSync, error: %d\n", result);
return false;
}
return true;
}
bool EGLTestHelper::importAndWaitOnSemaphore(skiatest::Reporter* reporter, int fdHandle,
sk_sp<SkSurface> surface) {
EGLDisplay eglDisplay = eglGetCurrentDisplay();
EGLint attr[] = {
EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fdHandle,
EGL_NONE
};
EGLSyncKHR eglsync = fEGLCreateSyncKHR(eglDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attr);
if (EGL_NO_SYNC_KHR == eglsync) {
ERRORF(reporter,
"Failed to create EGLSync when importing EGL_SYNC_NATIVE_FENCE_FD_ANDROID\n");
return false;
}
EGLint result = fEGLWaitSyncKHR(eglDisplay, eglsync, 0);
if (EGL_TRUE != result) {
ERRORF(reporter, "Failed called to eglWaitSyncKHR, error: %d\n", result);
// Don't return false yet, try to delete the sync first
}
result = fEGLDestroySyncKHR(eglDisplay, eglsync);
if (EGL_TRUE != result) {
ERRORF(reporter, "Failed to delete EGLSync, error: %d\n", result);
return false;
}
return true;
}
void EGLTestHelper::doClientSync() {
GrFlushInfo flushInfo;
flushInfo.fFlags = kSyncCpu_GrFlushFlag;
this->grContext()->flush(flushInfo);
}
#endif // SK_GL
#define DECLARE_VK_PROC(name) PFN_vk##name fVk##name
#define ACQUIRE_INST_VK_PROC(name) \
do { \
fVk##name = reinterpret_cast<PFN_vk##name>(getProc("vk" #name, fBackendContext.fInstance,\
VK_NULL_HANDLE)); \
if (fVk##name == nullptr) { \
ERRORF(reporter, "Function ptr for vk%s could not be acquired\n", #name); \
return false; \
} \
} while(false)
#define ACQUIRE_DEVICE_VK_PROC(name) \
do { \
fVk##name = reinterpret_cast<PFN_vk##name>(getProc("vk" #name, VK_NULL_HANDLE, fDevice)); \
if (fVk##name == nullptr) { \
ERRORF(reporter, "Function ptr for vk%s could not be acquired\n", #name); \
return false; \
} \
} while(false)
class VulkanTestHelper : public BaseTestHelper {
public:
VulkanTestHelper() {}
~VulkanTestHelper() override {}
void releaseImage() override {
if (VK_NULL_HANDLE == fDevice) {
return;
}
if (fImage != VK_NULL_HANDLE) {
fVkDestroyImage(fDevice, fImage, nullptr);
fImage = VK_NULL_HANDLE;
}
if (fMemory != VK_NULL_HANDLE) {
fVkFreeMemory(fDevice, fMemory, nullptr);
fMemory = VK_NULL_HANDLE;
}
}
void cleanup() override {
fGrContext.reset();
this->releaseImage();
if (fSignalSemaphore != VK_NULL_HANDLE) {
fVkDestroySemaphore(fDevice, fSignalSemaphore, nullptr);
fSignalSemaphore = VK_NULL_HANDLE;
}
fBackendContext.fMemoryAllocator.reset();
if (fDevice != VK_NULL_HANDLE) {
fVkDeviceWaitIdle(fDevice);
fVkDestroyDevice(fDevice, nullptr);
fDevice = VK_NULL_HANDLE;
}
#ifdef SK_ENABLE_VK_LAYERS
if (fDebugCallback != VK_NULL_HANDLE) {
fDestroyDebugCallback(fBackendContext.fInstance, fDebugCallback, nullptr);
}
#endif
if (fBackendContext.fInstance != VK_NULL_HANDLE) {
fVkDestroyInstance(fBackendContext.fInstance, nullptr);
fBackendContext.fInstance = VK_NULL_HANDLE;
}
delete fExtensions;
sk_gpu_test::FreeVulkanFeaturesStructs(fFeatures);
delete fFeatures;
}
bool init(skiatest::Reporter* reporter) override;
void doClientSync() override {
if (!fGrContext) {
return;
}
fGrContext->priv().getGpu()->testingOnly_flushGpuAndSync();
}
bool flushSurfaceAndSignalSemaphore(skiatest::Reporter* reporter, sk_sp<SkSurface>) override;
bool importAndWaitOnSemaphore(skiatest::Reporter* reporter, int fdHandle,
sk_sp<SkSurface>) override;
sk_sp<SkImage> importHardwareBufferForRead(skiatest::Reporter* reporter,
AHardwareBuffer* buffer) override;
sk_sp<SkSurface> importHardwareBufferForWrite(skiatest::Reporter* reporter,
AHardwareBuffer* buffer) override;
void makeCurrent() override {}
GrContext* grContext() override { return fGrContext.get(); }
private:
bool checkOptimalHardwareBuffer(skiatest::Reporter* reporter);
bool importHardwareBuffer(skiatest::Reporter* reporter, AHardwareBuffer* buffer, bool forWrite,
GrVkImageInfo* outImageInfo);
bool setupSemaphoreForSignaling(skiatest::Reporter* reporter, GrBackendSemaphore*);
bool exportSemaphore(skiatest::Reporter* reporter, const GrBackendSemaphore&);
DECLARE_VK_PROC(DestroyInstance);
DECLARE_VK_PROC(DeviceWaitIdle);
DECLARE_VK_PROC(DestroyDevice);
DECLARE_VK_PROC(GetPhysicalDeviceExternalSemaphoreProperties);
DECLARE_VK_PROC(GetPhysicalDeviceImageFormatProperties2);
DECLARE_VK_PROC(GetPhysicalDeviceMemoryProperties2);
DECLARE_VK_PROC(GetAndroidHardwareBufferPropertiesANDROID);
DECLARE_VK_PROC(CreateImage);
DECLARE_VK_PROC(GetImageMemoryRequirements2);
DECLARE_VK_PROC(DestroyImage);
DECLARE_VK_PROC(AllocateMemory);
DECLARE_VK_PROC(BindImageMemory2);
DECLARE_VK_PROC(FreeMemory);
DECLARE_VK_PROC(CreateSemaphore);
DECLARE_VK_PROC(GetSemaphoreFdKHR);
DECLARE_VK_PROC(ImportSemaphoreFdKHR);
DECLARE_VK_PROC(DestroySemaphore);
VkImage fImage = VK_NULL_HANDLE;
VkDeviceMemory fMemory = VK_NULL_HANDLE;
GrVkExtensions* fExtensions = nullptr;
VkPhysicalDeviceFeatures2* fFeatures = nullptr;
VkDebugReportCallbackEXT fDebugCallback = VK_NULL_HANDLE;
PFN_vkDestroyDebugReportCallbackEXT fDestroyDebugCallback = nullptr;
// We hold on to the semaphore so we can delete once the GPU is done.
VkSemaphore fSignalSemaphore = VK_NULL_HANDLE;
VkDevice fDevice = VK_NULL_HANDLE;
GrVkBackendContext fBackendContext;
sk_sp<GrContext> fGrContext;
};
bool VulkanTestHelper::init(skiatest::Reporter* reporter) {
PFN_vkGetInstanceProcAddr instProc;
PFN_vkGetDeviceProcAddr devProc;
if (!sk_gpu_test::LoadVkLibraryAndGetProcAddrFuncs(&instProc, &devProc)) {
return false;
}
auto getProc = [&instProc, &devProc](const char* proc_name,
VkInstance instance, VkDevice device) {
if (device != VK_NULL_HANDLE) {
return devProc(device, proc_name);
}
return instProc(instance, proc_name);
};
fExtensions = new GrVkExtensions();
fFeatures = new VkPhysicalDeviceFeatures2;
memset(fFeatures, 0, sizeof(VkPhysicalDeviceFeatures2));
fFeatures->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
fFeatures->pNext = nullptr;
fBackendContext.fInstance = VK_NULL_HANDLE;
fBackendContext.fDevice = VK_NULL_HANDLE;
if (!sk_gpu_test::CreateVkBackendContext(getProc, &fBackendContext, fExtensions,
fFeatures, &fDebugCallback)) {
return false;
}
fDevice = fBackendContext.fDevice;
if (fDebugCallback != VK_NULL_HANDLE) {
fDestroyDebugCallback = (PFN_vkDestroyDebugReportCallbackEXT) instProc(
fBackendContext.fInstance, "vkDestroyDebugReportCallbackEXT");
}
ACQUIRE_INST_VK_PROC(DestroyInstance);
ACQUIRE_INST_VK_PROC(DeviceWaitIdle);
ACQUIRE_INST_VK_PROC(DestroyDevice);
if (!fExtensions->hasExtension(VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME,
2)) {
return false;
}
if (!fExtensions->hasExtension(VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME, 1)) {
return false;
}
if (!fExtensions->hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) {
return false;
}
if (!fExtensions->hasExtension(VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME, 1)) {
// return false;
}
ACQUIRE_INST_VK_PROC(GetPhysicalDeviceMemoryProperties2);
ACQUIRE_INST_VK_PROC(GetPhysicalDeviceImageFormatProperties2);
ACQUIRE_INST_VK_PROC(GetPhysicalDeviceExternalSemaphoreProperties);
ACQUIRE_DEVICE_VK_PROC(GetAndroidHardwareBufferPropertiesANDROID);
ACQUIRE_DEVICE_VK_PROC(CreateImage);
ACQUIRE_DEVICE_VK_PROC(GetImageMemoryRequirements2);
ACQUIRE_DEVICE_VK_PROC(DestroyImage);
ACQUIRE_DEVICE_VK_PROC(AllocateMemory);
ACQUIRE_DEVICE_VK_PROC(BindImageMemory2);
ACQUIRE_DEVICE_VK_PROC(FreeMemory);
ACQUIRE_DEVICE_VK_PROC(CreateSemaphore);
ACQUIRE_DEVICE_VK_PROC(GetSemaphoreFdKHR);
ACQUIRE_DEVICE_VK_PROC(ImportSemaphoreFdKHR);
ACQUIRE_DEVICE_VK_PROC(DestroySemaphore);
fGrContext = GrContext::MakeVulkan(fBackendContext);
REPORTER_ASSERT(reporter, fGrContext.get());
if (!fGrContext) {
return false;
}
return this->checkOptimalHardwareBuffer(reporter);
}
bool VulkanTestHelper::checkOptimalHardwareBuffer(skiatest::Reporter* reporter) {
VkResult err;
VkPhysicalDeviceExternalImageFormatInfo externalImageFormatInfo;
externalImageFormatInfo.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO;
externalImageFormatInfo.pNext = nullptr;
externalImageFormatInfo.handleType =
VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID;
//externalImageFormatInfo.handType = 0x80;
// We will create the hardware buffer with gpu sampled so these usages should all be valid
VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT;
VkPhysicalDeviceImageFormatInfo2 imageFormatInfo;
imageFormatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2;
imageFormatInfo.pNext = &externalImageFormatInfo;
imageFormatInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
imageFormatInfo.type = VK_IMAGE_TYPE_2D;
imageFormatInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageFormatInfo.usage = usageFlags;
imageFormatInfo.flags = 0;
VkAndroidHardwareBufferUsageANDROID hwbUsage;
hwbUsage.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID;
hwbUsage.pNext = nullptr;
VkExternalImageFormatProperties externalImgFormatProps;
externalImgFormatProps.sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES;
externalImgFormatProps.pNext = &hwbUsage;
VkImageFormatProperties2 imgFormProps;
imgFormProps.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
imgFormProps.pNext = &externalImgFormatProps;
err = fVkGetPhysicalDeviceImageFormatProperties2(fBackendContext.fPhysicalDevice,
&imageFormatInfo, &imgFormProps);
if (VK_SUCCESS != err) {
ERRORF(reporter, "vkGetPhysicalDeviceImageFormatProperites failed, err: %d", err);
return false;
}
const VkImageFormatProperties& imageFormatProperties = imgFormProps.imageFormatProperties;
REPORTER_ASSERT(reporter, DEV_W <= imageFormatProperties.maxExtent.width);
REPORTER_ASSERT(reporter, DEV_H <= imageFormatProperties.maxExtent.height);
const VkExternalMemoryProperties& externalImageFormatProps =
externalImgFormatProps.externalMemoryProperties;
REPORTER_ASSERT(reporter, SkToBool(VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT &
externalImageFormatProps.externalMemoryFeatures));
REPORTER_ASSERT(reporter, SkToBool(VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT &
externalImageFormatProps.externalMemoryFeatures));
REPORTER_ASSERT(reporter, SkToBool(AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE &
hwbUsage.androidHardwareBufferUsage));
return true;
}
bool VulkanTestHelper::importHardwareBuffer(skiatest::Reporter* reporter,
AHardwareBuffer* buffer,
bool forWrite,
GrVkImageInfo* outImageInfo) {
VkResult err;
VkAndroidHardwareBufferFormatPropertiesANDROID hwbFormatProps;
hwbFormatProps.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID;
hwbFormatProps.pNext = nullptr;
VkAndroidHardwareBufferPropertiesANDROID hwbProps;
hwbProps.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID;
hwbProps.pNext = &hwbFormatProps;
err = fVkGetAndroidHardwareBufferPropertiesANDROID(fDevice, buffer, &hwbProps);
if (VK_SUCCESS != err) {
ERRORF(reporter, "GetAndroidHardwareBufferPropertiesAndroid failed, err: %d", err);
return false;
}
REPORTER_ASSERT(reporter, VK_FORMAT_R8G8B8A8_UNORM == hwbFormatProps.format);
REPORTER_ASSERT(reporter,
SkToBool(VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT & hwbFormatProps.formatFeatures) &&
SkToBool(VK_FORMAT_FEATURE_TRANSFER_SRC_BIT & hwbFormatProps.formatFeatures) &&
SkToBool(VK_FORMAT_FEATURE_TRANSFER_DST_BIT & hwbFormatProps.formatFeatures));
if (forWrite) {
REPORTER_ASSERT(reporter,
SkToBool(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT & hwbFormatProps.formatFeatures));
}
bool useExternalFormat = VK_FORMAT_UNDEFINED == hwbFormatProps.format;
const VkExternalFormatANDROID externalFormatInfo {
VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID, // sType
nullptr, // pNext
useExternalFormat ? hwbFormatProps.externalFormat : 0, // externalFormat
};
const VkExternalMemoryImageCreateInfo externalMemoryImageInfo {
VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, // sType
&externalFormatInfo, // pNext
VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID, // handleTypes
};
VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT;
if (forWrite) {
usageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
}
const VkImageCreateInfo imageCreateInfo = {
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // sType
&externalMemoryImageInfo, // pNext
0, // VkImageCreateFlags
VK_IMAGE_TYPE_2D, // VkImageType
hwbFormatProps.format, // VkFormat
{ DEV_W, DEV_H, 1 }, // VkExtent3D
1, // mipLevels
1, // arrayLayers
VK_SAMPLE_COUNT_1_BIT, // samples
VK_IMAGE_TILING_OPTIMAL, // VkImageTiling
usageFlags, // VkImageUsageFlags
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode
0, // queueFamilyCount
0, // pQueueFamilyIndices
VK_IMAGE_LAYOUT_UNDEFINED, // initialLayout
};
err = fVkCreateImage(fDevice, &imageCreateInfo, nullptr, &fImage);
if (VK_SUCCESS != err) {
ERRORF(reporter, "Create Image failed, err: %d", err);
return false;
}
VkPhysicalDeviceMemoryProperties2 phyDevMemProps;
phyDevMemProps.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2;
phyDevMemProps.pNext = nullptr;
uint32_t typeIndex = 0;
uint32_t heapIndex = 0;
bool foundHeap = false;
fVkGetPhysicalDeviceMemoryProperties2(fBackendContext.fPhysicalDevice, &phyDevMemProps);
uint32_t memTypeCnt = phyDevMemProps.memoryProperties.memoryTypeCount;
for (uint32_t i = 0; i < memTypeCnt && !foundHeap; ++i) {
if (hwbProps.memoryTypeBits & (1 << i)) {
const VkPhysicalDeviceMemoryProperties& pdmp = phyDevMemProps.memoryProperties;
uint32_t supportedFlags = pdmp.memoryTypes[i].propertyFlags &
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
if (supportedFlags == VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
typeIndex = i;
heapIndex = pdmp.memoryTypes[i].heapIndex;
foundHeap = true;
}
}
}
if (!foundHeap) {
ERRORF(reporter, "Failed to find valid heap for imported memory");
return false;
}
VkImportAndroidHardwareBufferInfoANDROID hwbImportInfo;
hwbImportInfo.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID;
hwbImportInfo.pNext = nullptr;
hwbImportInfo.buffer = buffer;
VkMemoryDedicatedAllocateInfo dedicatedAllocInfo;
dedicatedAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO;
dedicatedAllocInfo.pNext = &hwbImportInfo;
dedicatedAllocInfo.image = fImage;
dedicatedAllocInfo.buffer = VK_NULL_HANDLE;
VkMemoryAllocateInfo allocInfo = {
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // sType
&dedicatedAllocInfo, // pNext
hwbProps.allocationSize, // allocationSize
typeIndex, // memoryTypeIndex
};
err = fVkAllocateMemory(fDevice, &allocInfo, nullptr, &fMemory);
if (VK_SUCCESS != err) {
ERRORF(reporter, "AllocateMemory failed for imported buffer, err: %d", err);
return false;
}
VkBindImageMemoryInfo bindImageInfo;
bindImageInfo.sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO;
bindImageInfo.pNext = nullptr;
bindImageInfo.image = fImage;
bindImageInfo.memory = fMemory;
bindImageInfo.memoryOffset = 0;
err = fVkBindImageMemory2(fDevice, 1, &bindImageInfo);
if (VK_SUCCESS != err) {
ERRORF(reporter, "BindImageMemory failed for imported buffer, err: %d", err);
return false;
}
outImageInfo->fImage = fImage;
outImageInfo->fAlloc = GrVkAlloc(fMemory, 0, hwbProps.allocationSize, 0);
outImageInfo->fImageTiling = VK_IMAGE_TILING_OPTIMAL;
outImageInfo->fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
outImageInfo->fFormat = VK_FORMAT_R8G8B8A8_UNORM;
outImageInfo->fLevelCount = 1;
outImageInfo->fCurrentQueueFamily = VK_QUEUE_FAMILY_EXTERNAL;
return true;
}
sk_sp<SkImage> VulkanTestHelper::importHardwareBufferForRead(skiatest::Reporter* reporter,
AHardwareBuffer* buffer) {
GrVkImageInfo imageInfo;
if (!this->importHardwareBuffer(reporter, buffer, false, &imageInfo)) {
return nullptr;
}
GrBackendTexture backendTex(DEV_W, DEV_H, imageInfo);
sk_sp<SkImage> wrappedImage = SkImage::MakeFromTexture(fGrContext.get(),
backendTex,
kTopLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType,
kPremul_SkAlphaType,
nullptr);
if (!wrappedImage.get()) {
ERRORF(reporter, "Failed to create wrapped Vulkan SkImage");
return nullptr;
}
return wrappedImage;
}
bool VulkanTestHelper::flushSurfaceAndSignalSemaphore(skiatest::Reporter* reporter,
sk_sp<SkSurface> surface) {
surface->flush();
surface.reset();
GrBackendSemaphore semaphore;
if (!this->setupSemaphoreForSignaling(reporter, &semaphore)) {
return false;
}
GrFlushInfo info;
info.fNumSemaphores = 1;
info.fSignalSemaphores = &semaphore;
GrSemaphoresSubmitted submitted = fGrContext->flush(info);
if (GrSemaphoresSubmitted::kNo == submitted) {
ERRORF(reporter, "Failing call to flush on GrContext");
return false;
}
SkASSERT(semaphore.isInitialized());
if (!this->exportSemaphore(reporter, semaphore)) {
return false;
}
return true;
}
bool VulkanTestHelper::setupSemaphoreForSignaling(skiatest::Reporter* reporter,
GrBackendSemaphore* beSemaphore) {
// Query supported info
VkPhysicalDeviceExternalSemaphoreInfo exSemInfo;
exSemInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO;
exSemInfo.pNext = nullptr;
exSemInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
VkExternalSemaphoreProperties exSemProps;
exSemProps.sType = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES;
exSemProps.pNext = nullptr;
fVkGetPhysicalDeviceExternalSemaphoreProperties(fBackendContext.fPhysicalDevice, &exSemInfo,
&exSemProps);
if (!SkToBool(exSemProps.exportFromImportedHandleTypes &
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT)) {
ERRORF(reporter, "HANDLE_TYPE_SYNC_FD not listed as exportFromImportedHandleTypes");
return false;
}
if (!SkToBool(exSemProps.compatibleHandleTypes &
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT)) {
ERRORF(reporter, "HANDLE_TYPE_SYNC_FD not listed as compatibleHandleTypes");
return false;
}
if (!SkToBool(exSemProps.externalSemaphoreFeatures &
VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT) ||
!SkToBool(exSemProps.externalSemaphoreFeatures &
VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT)) {
ERRORF(reporter, "HANDLE_TYPE_SYNC_FD doesn't support export and import feature");
return false;
}
VkExportSemaphoreCreateInfo exportInfo;
exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO;
exportInfo.pNext = nullptr;
exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
VkSemaphoreCreateInfo semaphoreInfo;
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
semaphoreInfo.pNext = &exportInfo;
semaphoreInfo.flags = 0;
VkSemaphore semaphore;
VkResult err = fVkCreateSemaphore(fDevice, &semaphoreInfo, nullptr, &semaphore);
if (VK_SUCCESS != err) {
ERRORF(reporter, "Failed to create signal semaphore, err: %d", err);
return false;
}
beSemaphore->initVulkan(semaphore);
return true;
}
bool VulkanTestHelper::exportSemaphore(skiatest::Reporter* reporter,
const GrBackendSemaphore& beSemaphore) {
VkSemaphore semaphore = beSemaphore.vkSemaphore();
if (VK_NULL_HANDLE == semaphore) {
ERRORF(reporter, "Invalid vulkan handle in export call");
return false;
}
VkSemaphoreGetFdInfoKHR getFdInfo;
getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR;
getFdInfo.pNext = nullptr;
getFdInfo.semaphore = semaphore;
getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
VkResult err = fVkGetSemaphoreFdKHR(fDevice, &getFdInfo, &fFdHandle);
if (VK_SUCCESS != err) {
ERRORF(reporter, "Failed to export signal semaphore, err: %d", err);
return false;
}
fSignalSemaphore = semaphore;
return true;
}
bool VulkanTestHelper::importAndWaitOnSemaphore(skiatest::Reporter* reporter, int fdHandle,
sk_sp<SkSurface> surface) {
VkSemaphoreCreateInfo semaphoreInfo;
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
semaphoreInfo.pNext = nullptr;
semaphoreInfo.flags = 0;
VkSemaphore semaphore;
VkResult err = fVkCreateSemaphore(fDevice, &semaphoreInfo, nullptr, &semaphore);
if (VK_SUCCESS != err) {
ERRORF(reporter, "Failed to create import semaphore, err: %d", err);
return false;
}
VkImportSemaphoreFdInfoKHR importInfo;
importInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR;
importInfo.pNext = nullptr;
importInfo.semaphore = semaphore;
importInfo.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT;
importInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
importInfo.fd = fdHandle;
err = fVkImportSemaphoreFdKHR(fDevice, &importInfo);
if (VK_SUCCESS != err) {
ERRORF(reporter, "Failed to import semaphore, err: %d", err);
return false;
}
GrBackendSemaphore beSemaphore;
beSemaphore.initVulkan(semaphore);
if (!surface->wait(1, &beSemaphore)) {
ERRORF(reporter, "Failed to add wait semaphore to surface");
fVkDestroySemaphore(fDevice, semaphore, nullptr);
return false;
}
return true;
}
sk_sp<SkSurface> VulkanTestHelper::importHardwareBufferForWrite(skiatest::Reporter* reporter,
AHardwareBuffer* buffer) {
GrVkImageInfo imageInfo;
if (!this->importHardwareBuffer(reporter, buffer, true, &imageInfo)) {
return nullptr;
}
GrBackendTexture backendTex(DEV_W, DEV_H, imageInfo);
sk_sp<SkSurface> surface = SkSurface::MakeFromBackendTexture(fGrContext.get(),
backendTex,
kTopLeft_GrSurfaceOrigin,
0,
kRGBA_8888_SkColorType,
nullptr, nullptr);
if (!surface.get()) {
ERRORF(reporter, "Failed to create wrapped Vulkan SkSurface");
return nullptr;
}
return surface;
}
static SkPMColor get_src_color(int x, int y) {
SkASSERT(x >= 0 && x < DEV_W);
SkASSERT(y >= 0 && y < DEV_H);
U8CPU r = x;
U8CPU g = y;
U8CPU b = 0xc;
U8CPU a = 0xff;
switch ((x+y) % 5) {
case 0:
a = 0xff;
break;
case 1:
a = 0x80;
break;
case 2:
a = 0xCC;
break;
case 4:
a = 0x01;
break;
case 3:
a = 0x00;
break;
}
a = 0xff;
return SkPremultiplyARGBInline(a, r, g, b);
}
static SkBitmap make_src_bitmap() {
static SkBitmap bmp;
if (bmp.isNull()) {
bmp.allocN32Pixels(DEV_W, DEV_H);
intptr_t pixels = reinterpret_cast<intptr_t>(bmp.getPixels());
for (int y = 0; y < DEV_H; ++y) {
for (int x = 0; x < DEV_W; ++x) {
SkPMColor* pixel = reinterpret_cast<SkPMColor*>(
pixels + y * bmp.rowBytes() + x * bmp.bytesPerPixel());
*pixel = get_src_color(x, y);
}
}
}
return bmp;
}
static bool check_read(skiatest::Reporter* reporter, const SkBitmap& srcBitmap,
const SkBitmap& dstBitmap) {
bool result = true;
for (int y = 0; y < DEV_H && result; ++y) {
for (int x = 0; x < DEV_W && result; ++x) {
const uint32_t srcPixel = *srcBitmap.getAddr32(x, y);
const uint32_t dstPixel = *dstBitmap.getAddr32(x, y);
if (srcPixel != dstPixel) {
ERRORF(reporter, "Expected readback pixel (%d, %d) value 0x%08x, got 0x%08x.",
x, y, srcPixel, dstPixel);
result = false;
} /*else {
ERRORF(reporter, "Got good readback pixel (%d, %d) value 0x%08x, got 0x%08x.",
x, y, srcPixel, dstPixel);
}*/
}
}
return result;
}
static void cleanup_resources(BaseTestHelper* srcHelper, BaseTestHelper* dstHelper,
AHardwareBuffer* buffer) {
if (srcHelper) {
srcHelper->cleanup();
}
if (dstHelper) {
dstHelper->cleanup();
}
if (buffer) {
AHardwareBuffer_release(buffer);
}
}
enum class SrcType {
kCPU,
kEGL,
kVulkan,
};
enum class DstType {
kEGL,
kVulkan,
};
void run_test(skiatest::Reporter* reporter, const GrContextOptions& options,
SrcType srcType, DstType dstType, bool shareSyncs) {
if (SrcType::kCPU == srcType && shareSyncs) {
// We don't currently test this since we don't do any syncs in this case.
return;
}
std::unique_ptr<BaseTestHelper> srcHelper;
std::unique_ptr<BaseTestHelper> dstHelper;
AHardwareBuffer* buffer = nullptr;
if (SrcType::kVulkan == srcType) {
srcHelper.reset(new VulkanTestHelper());
} else if (SrcType::kEGL == srcType) {
#ifdef SK_GL
srcHelper.reset(new EGLTestHelper(options));
#else
SkASSERT(false, "SrcType::kEGL used without OpenGL support.");
#endif
}
if (srcHelper) {
if (!srcHelper->init(reporter)) {
cleanup_resources(srcHelper.get(), dstHelper.get(), buffer);
return;
}
}
if (DstType::kVulkan == dstType) {
dstHelper.reset(new VulkanTestHelper());
} else {
#ifdef SK_GL
SkASSERT(DstType::kEGL == dstType);
dstHelper.reset(new EGLTestHelper(options));
#else
SkASSERT(false, "DstType::kEGL used without OpenGL support.");
#endif
}
if (dstHelper) {
if (!dstHelper->init(reporter)) {
cleanup_resources(srcHelper.get(), dstHelper.get(), buffer);
return;
}
}
///////////////////////////////////////////////////////////////////////////
// Setup SkBitmaps
///////////////////////////////////////////////////////////////////////////
SkBitmap srcBitmap = make_src_bitmap();
SkBitmap dstBitmapSurface;
dstBitmapSurface.allocN32Pixels(DEV_W, DEV_H);
SkBitmap dstBitmapFinal;
dstBitmapFinal.allocN32Pixels(DEV_W, DEV_H);
///////////////////////////////////////////////////////////////////////////
// Setup AHardwareBuffer
///////////////////////////////////////////////////////////////////////////
AHardwareBuffer_Desc hwbDesc;
hwbDesc.width = DEV_W;
hwbDesc.height = DEV_H;
hwbDesc.layers = 1;
if (SrcType::kCPU == srcType) {
hwbDesc.usage = AHARDWAREBUFFER_USAGE_CPU_READ_NEVER |
AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
} else {
hwbDesc.usage = AHARDWAREBUFFER_USAGE_CPU_READ_NEVER |
AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER |
AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT;
}
hwbDesc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
// The following three are not used in the allocate
hwbDesc.stride = 0;
hwbDesc.rfu0= 0;
hwbDesc.rfu1= 0;
if (int error = AHardwareBuffer_allocate(&hwbDesc, &buffer)) {
ERRORF(reporter, "Failed to allocated hardware buffer, error: %d", error);
cleanup_resources(srcHelper.get(), dstHelper.get(), buffer);
return;
}
if (SrcType::kCPU == srcType) {
// Get actual desc for allocated buffer so we know the stride for uploading cpu data.
AHardwareBuffer_describe(buffer, &hwbDesc);
uint32_t* bufferAddr;
if (AHardwareBuffer_lock(buffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, -1, nullptr,
reinterpret_cast<void**>(&bufferAddr))) {
ERRORF(reporter, "Failed to lock hardware buffer");
cleanup_resources(srcHelper.get(), dstHelper.get(), buffer);
return;
}
int bbp = srcBitmap.bytesPerPixel();
uint32_t* src = (uint32_t*)srcBitmap.getPixels();
uint32_t* dst = bufferAddr;
for (int y = 0; y < DEV_H; ++y) {
memcpy(dst, src, DEV_W * bbp);
src += DEV_W;
dst += hwbDesc.stride;
}
for (int y = 0; y < DEV_H; ++y) {
for (int x = 0; x < DEV_W; ++x) {
const uint32_t srcPixel = *srcBitmap.getAddr32(x, y);
uint32_t dstPixel = bufferAddr[y * hwbDesc.stride + x];
if (srcPixel != dstPixel) {
ERRORF(reporter, "CPU HWB Expected readpix (%d, %d) value 0x%08x, got 0x%08x.",
x, y, srcPixel, dstPixel);
}
}
}
AHardwareBuffer_unlock(buffer, nullptr);
} else {
srcHelper->makeCurrent();
sk_sp<SkSurface> surface = srcHelper->importHardwareBufferForWrite(reporter, buffer);
if (!surface) {
cleanup_resources(srcHelper.get(), dstHelper.get(), buffer);
return;
}
sk_sp<SkImage> srcBmpImage = SkImage::MakeFromBitmap(srcBitmap);
surface->getCanvas()->drawImage(srcBmpImage, 0, 0);
// If we are testing sharing of syncs, don't do a read here since it forces sychronization
// to occur.
if (!shareSyncs) {
bool readResult = surface->readPixels(dstBitmapSurface, 0, 0);
if (!readResult) {
ERRORF(reporter, "Read Pixels on surface failed");
surface.reset();
cleanup_resources(srcHelper.get(), dstHelper.get(), buffer);
return;
}
REPORTER_ASSERT(reporter, check_read(reporter, srcBitmap, dstBitmapSurface));
}
///////////////////////////////////////////////////////////////////////////
// Cleanup GL/EGL and add syncs
///////////////////////////////////////////////////////////////////////////
if (shareSyncs) {
if (!srcHelper->flushSurfaceAndSignalSemaphore(reporter, std::move(surface))) {
cleanup_resources(srcHelper.get(), dstHelper.get(), buffer);
return;
}
} else {
surface.reset();
srcHelper->doClientSync();
srcHelper->releaseImage();
}
}
///////////////////////////////////////////////////////////////////////////
// Import the HWB into backend and draw it to a surface
///////////////////////////////////////////////////////////////////////////
dstHelper->makeCurrent();
sk_sp<SkImage> wrappedImage = dstHelper->importHardwareBufferForRead(reporter, buffer);
if (!wrappedImage) {
cleanup_resources(srcHelper.get(), dstHelper.get(), buffer);
return;
}
GrContext* grContext = dstHelper->grContext();
// Make SkSurface to render wrapped HWB into.
SkImageInfo imageInfo = SkImageInfo::Make(DEV_W, DEV_H, kRGBA_8888_SkColorType,
kPremul_SkAlphaType, nullptr);
sk_sp<SkSurface> dstSurf = SkSurface::MakeRenderTarget(grContext,
SkBudgeted::kNo, imageInfo, 0,
kTopLeft_GrSurfaceOrigin,
nullptr, false);
if (!dstSurf.get()) {
ERRORF(reporter, "Failed to create destination SkSurface");
wrappedImage.reset();
cleanup_resources(srcHelper.get(), dstHelper.get(), buffer);
return;
}
if (shareSyncs) {
if (!dstHelper->importAndWaitOnSemaphore(reporter, srcHelper->getFdHandle(), dstSurf)) {
wrappedImage.reset();
cleanup_resources(srcHelper.get(), dstHelper.get(), buffer);
return;
}
}
dstSurf->getCanvas()->drawImage(wrappedImage, 0, 0);
bool readResult = dstSurf->readPixels(dstBitmapFinal, 0, 0);
if (!readResult) {
ERRORF(reporter, "Read Pixels failed");
wrappedImage.reset();
dstSurf.reset();
dstHelper->doClientSync();
cleanup_resources(srcHelper.get(), dstHelper.get(), buffer);
return;
}
REPORTER_ASSERT(reporter, check_read(reporter, srcBitmap, dstBitmapFinal));
dstSurf.reset();
wrappedImage.reset();
dstHelper->doClientSync();
cleanup_resources(srcHelper.get(), dstHelper.get(), buffer);
}
DEF_GPUTEST(VulkanHardwareBuffer_CPU_Vulkan, reporter, options) {
run_test(reporter, options, SrcType::kCPU, DstType::kVulkan, false);
}
DEF_GPUTEST(VulkanHardwareBuffer_Vulkan_Vulkan, reporter, options) {
run_test(reporter, options, SrcType::kVulkan, DstType::kVulkan, false);
}
DEF_GPUTEST(VulkanHardwareBuffer_Vulkan_Vulkan_Syncs, reporter, options) {
run_test(reporter, options, SrcType::kVulkan, DstType::kVulkan, true);
}
#if defined(SK_GL)
DEF_GPUTEST(VulkanHardwareBuffer_EGL_Vulkan, reporter, options) {
run_test(reporter, options, SrcType::kEGL, DstType::kVulkan, false);
}
DEF_GPUTEST(VulkanHardwareBuffer_CPU_EGL, reporter, options) {
run_test(reporter, options, SrcType::kCPU, DstType::kEGL, false);
}
DEF_GPUTEST(VulkanHardwareBuffer_EGL_EGL, reporter, options) {
run_test(reporter, options, SrcType::kEGL, DstType::kEGL, false);
}
DEF_GPUTEST(VulkanHardwareBuffer_Vulkan_EGL, reporter, options) {
run_test(reporter, options, SrcType::kVulkan, DstType::kEGL, false);
}
DEF_GPUTEST(VulkanHardwareBuffer_EGL_EGL_Syncs, reporter, options) {
run_test(reporter, options, SrcType::kEGL, DstType::kEGL, true);
}
DEF_GPUTEST(VulkanHardwareBuffer_Vulkan_EGL_Syncs, reporter, options) {
run_test(reporter, options, SrcType::kVulkan, DstType::kEGL, true);
}
DEF_GPUTEST(VulkanHardwareBuffer_EGL_Vulkan_Syncs, reporter, options) {
run_test(reporter, options, SrcType::kEGL, DstType::kVulkan, true);
}
#endif
#endif // SK_SUPPORT_GPU && defined(SK_BUILD_FOR_ANDROID) &&
// __ANDROID_API__ >= 26 && defined(SK_VULKAN)