6658fd1584
We've run into drivers a few times now that have bugs where they only expose vkGetInstaneProcAddr and not vkGetDeviceProcAddr. The latest being swiftshader (which is getting fixed). To avoid this issue in the future we can just have our tests use vkGetInstanceProcAddr to get vkGetDeviceProcAddr. Change-Id: I6d73abde507519c145b873042393f50ce6c4527c Reviewed-on: https://skia-review.googlesource.com/c/skia/+/494822 Reviewed-by: Jim Van Verth <jvanverth@google.com> Commit-Queue: Greg Daniel <egdaniel@google.com>
1332 lines
50 KiB
C++
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/SkBitmap.h"
|
|
#include "include/core/SkCanvas.h"
|
|
#include "include/core/SkImage.h"
|
|
#include "include/core/SkSurface.h"
|
|
#include "include/gpu/GrBackendSemaphore.h"
|
|
#include "include/gpu/GrDirectContext.h"
|
|
#include "include/gpu/vk/GrVkBackendContext.h"
|
|
#include "include/gpu/vk/GrVkExtensions.h"
|
|
#include "src/core/SkAutoMalloc.h"
|
|
#include "src/gpu/GrDirectContextPriv.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 GrDirectContext* directContext() = 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(); }
|
|
|
|
GrDirectContext* directContext() override { return fDirectContext; }
|
|
|
|
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;
|
|
GrDirectContext* fDirectContext = nullptr;
|
|
};
|
|
|
|
bool EGLTestHelper::init(skiatest::Reporter* reporter) {
|
|
fGLESContextInfo = fFactory.getContextInfo(sk_gpu_test::GrContextFactory::kGLES_ContextType);
|
|
fDirectContext = fGLESContextInfo.directContext();
|
|
fGLCtx = fGLESContextInfo.glContext();
|
|
if (!fDirectContext || !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) {
|
|
while (fGLCtx->gl()->fFunctions.fGetError() != GR_GL_NO_ERROR) {}
|
|
|
|
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 (fGLCtx->gl()->fFunctions.fGetError() != GR_GL_NO_ERROR) {
|
|
ERRORF(reporter, "Failed to bind GL Texture");
|
|
return false;
|
|
}
|
|
|
|
fEGLImageTargetTexture2DOES(GL_TEXTURE_2D, fImage);
|
|
if (GrGLenum error = fGLCtx->gl()->fFunctions.fGetError(); error != GR_GL_NO_ERROR) {
|
|
ERRORF(reporter, "EGLImageTargetTexture2DOES failed (%#x)", (int) error);
|
|
return false;
|
|
}
|
|
|
|
fDirectContext->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(fDirectContext,
|
|
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(fDirectContext,
|
|
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->flushAndSubmit();
|
|
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() {
|
|
this->directContext()->flush();
|
|
this->directContext()->submit(true);
|
|
}
|
|
#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 {
|
|
fDirectContext.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 (!fDirectContext) {
|
|
return;
|
|
}
|
|
|
|
fDirectContext->submit(true);
|
|
}
|
|
|
|
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 {}
|
|
|
|
GrDirectContext* directContext() override { return fDirectContext.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<GrDirectContext> fDirectContext;
|
|
};
|
|
|
|
bool VulkanTestHelper::init(skiatest::Reporter* reporter) {
|
|
PFN_vkGetInstanceProcAddr instProc;
|
|
if (!sk_gpu_test::LoadVkLibraryAndGetProcAddrFuncs(&instProc)) {
|
|
return false;
|
|
}
|
|
|
|
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(instProc, &fBackendContext, fExtensions,
|
|
fFeatures, &fDebugCallback)) {
|
|
return false;
|
|
}
|
|
fDevice = fBackendContext.fDevice;
|
|
auto getProc = fBackendContext.fGetProc;
|
|
|
|
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);
|
|
|
|
fDirectContext = GrDirectContext::MakeVulkan(fBackendContext);
|
|
REPORTER_ASSERT(reporter, fDirectContext.get());
|
|
if (!fDirectContext) {
|
|
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;
|
|
}
|
|
|
|
GrVkAlloc alloc;
|
|
alloc.fMemory = fMemory;
|
|
alloc.fOffset = 0;
|
|
alloc.fSize = hwbProps.allocationSize;
|
|
alloc.fFlags = 0;
|
|
|
|
outImageInfo->fImage = fImage;
|
|
outImageInfo->fAlloc = alloc;
|
|
outImageInfo->fImageTiling = VK_IMAGE_TILING_OPTIMAL;
|
|
outImageInfo->fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
outImageInfo->fFormat = VK_FORMAT_R8G8B8A8_UNORM;
|
|
outImageInfo->fImageUsageFlags = usageFlags;
|
|
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(fDirectContext.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->flushAndSubmit();
|
|
surface.reset();
|
|
GrBackendSemaphore semaphore;
|
|
if (!this->setupSemaphoreForSignaling(reporter, &semaphore)) {
|
|
return false;
|
|
}
|
|
GrFlushInfo info;
|
|
info.fNumSemaphores = 1;
|
|
info.fSignalSemaphores = &semaphore;
|
|
GrSemaphoresSubmitted submitted = fDirectContext->flush(info);
|
|
fDirectContext->submit();
|
|
if (GrSemaphoresSubmitted::kNo == submitted) {
|
|
ERRORF(reporter, "Failing call to flush on GrDirectContext");
|
|
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(fDirectContext.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;
|
|
}
|
|
|
|
auto direct = dstHelper->directContext();
|
|
|
|
// 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(direct,
|
|
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)
|
|
|