/* * 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 "SkTypes.h" #ifdef SKQP_BUILD_HARDWAREBUFFER_TEST #if SK_SUPPORT_GPU && defined(SK_VULKAN) #include "GrBackendSemaphore.h" #include "GrContext.h" #include "GrContextFactory.h" #include "GrContextPriv.h" #include "GrGpu.h" #include "GrProxyProvider.h" #include "GrTest.h" #include "SkAutoMalloc.h" #include "SkCanvas.h" #include "SkGr.h" #include "SkImage.h" #include "SkSurface.h" #include "Test.h" #include "../tools/gpu/vk/VkTestUtils.h" #include "gl/GrGLDefines.h" #include "gl/GrGLUtil.h" #include "vk/GrVkBackendContext.h" #include "vk/GrVkExtensions.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; }; 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")) { 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() { sk_gpu_test::FenceSync* fenceSync = fGLCtx->fenceSync(); sk_gpu_test::PlatformFence fence = fenceSync->insertFence(); fenceSync->waitFence(fence); fenceSync->deleteFence(fence); } #define DECLARE_VK_PROC(name) PFN_vk##name fVk##name #define ACQUIRE_VK_PROC(name, instance, device) \ fVk##name = reinterpret_cast<PFN_vk##name>(getProc("vk" #name, instance, device)); \ if (fVk##name == nullptr) { \ ERRORF(reporter, "Function ptr for vk%s could not be acquired\n", #name); \ return false; \ } #define ACQUIRE_INST_VK_PROC(name) \ fVk##name = reinterpret_cast<PFN_vk##name>(getProc("vk" #name, fInst, VK_NULL_HANDLE)); \ if (fVk##name == nullptr) { \ ERRORF(reporter, "Function ptr for vk%s could not be acquired\n", #name); \ fVkDestroyInstance(fInst, nullptr); \ return false; \ } #define ACQUIRE_DEVICE_VK_PROC(name) \ 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); \ fVkDestroyDevice(fDevice, nullptr); \ fVkDestroyInstance(fInst, nullptr); \ return false; \ } #ifdef SK_ENABLE_VK_LAYERS const char* kMyDebugLayerNames[] = { // elements of VK_LAYER_LUNARG_standard_validation "VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation", "VK_LAYER_LUNARG_object_tracker", "VK_LAYER_LUNARG_image", "VK_LAYER_LUNARG_core_validation", "VK_LAYER_LUNARG_swapchain", "VK_LAYER_GOOGLE_unique_objects", // not included in standard_validation //"VK_LAYER_LUNARG_api_dump", //"VK_LAYER_LUNARG_vktrace", //"VK_LAYER_LUNARG_screenshot", }; #endif class VulkanTestHelper : public BaseTestHelper { public: VulkanTestHelper() {} ~VulkanTestHelper() override {} void releaseImage() override { if (VK_NULL_HANDLE == fDevice) { return; } if (fImage != VK_NULL_HANDLE) { SkASSERT(fVkDestroyImage); fVkDestroyImage(fDevice, fImage, nullptr); fImage = VK_NULL_HANDLE; } if (fMemory != VK_NULL_HANDLE) { SkASSERT(fVkFreeMemory); fVkFreeMemory(fDevice, fMemory, nullptr); fMemory = VK_NULL_HANDLE; } } void cleanup() override { this->releaseImage(); fGrContext.reset(); fBackendContext.reset(); fInst = VK_NULL_HANDLE; fPhysDev = VK_NULL_HANDLE; fDevice = VK_NULL_HANDLE; } bool init(skiatest::Reporter* reporter) override; void doClientSync() override { if (!fGrContext) { return; } fGrContext->contextPriv().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(EnumerateInstanceVersion); DECLARE_VK_PROC(CreateInstance); DECLARE_VK_PROC(DestroyInstance); DECLARE_VK_PROC(EnumeratePhysicalDevices); DECLARE_VK_PROC(GetPhysicalDeviceProperties); DECLARE_VK_PROC(GetPhysicalDeviceMemoryProperties2); DECLARE_VK_PROC(GetPhysicalDeviceQueueFamilyProperties); DECLARE_VK_PROC(GetPhysicalDeviceFeatures); DECLARE_VK_PROC(GetPhysicalDeviceExternalSemaphoreProperties); DECLARE_VK_PROC(CreateDevice); DECLARE_VK_PROC(GetDeviceQueue); DECLARE_VK_PROC(DeviceWaitIdle); DECLARE_VK_PROC(DestroyDevice); DECLARE_VK_PROC(GetPhysicalDeviceImageFormatProperties2); DECLARE_VK_PROC(CreateImage); DECLARE_VK_PROC(GetImageMemoryRequirements2); DECLARE_VK_PROC(GetAndroidHardwareBufferPropertiesANDROID); DECLARE_VK_PROC(AllocateMemory); DECLARE_VK_PROC(BindImageMemory2); DECLARE_VK_PROC(DestroyImage); DECLARE_VK_PROC(FreeMemory); DECLARE_VK_PROC(CreateSemaphore); DECLARE_VK_PROC(GetSemaphoreFdKHR); DECLARE_VK_PROC(ImportSemaphoreFdKHR); DECLARE_VK_PROC(DestroySemaphore); VkInstance fInst = VK_NULL_HANDLE; VkPhysicalDevice fPhysDev = VK_NULL_HANDLE; VkDevice fDevice = VK_NULL_HANDLE; VkImage fImage = VK_NULL_HANDLE; VkDeviceMemory fMemory = VK_NULL_HANDLE; sk_sp<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); }; VkResult err; ACQUIRE_VK_PROC(EnumerateInstanceVersion, VK_NULL_HANDLE, VK_NULL_HANDLE); uint32_t instanceVersion = 0; if (fVkEnumerateInstanceVersion) { err = fVkEnumerateInstanceVersion(&instanceVersion); if (err) { ERRORF(reporter, "failed to enumerate instance version. Err: %d\n", err); return false; } } if (instanceVersion < VK_MAKE_VERSION(1, 1, 0)) { return false; } const VkApplicationInfo app_info = { VK_STRUCTURE_TYPE_APPLICATION_INFO, // sType nullptr, // pNext "vkHWBTest", // pApplicationName 0, // applicationVersion "vkHWBTest", // pEngineName 0, // engineVerison instanceVersion, // apiVersion }; GrVkExtensions extensions(getProc); extensions.initInstance(instanceVersion); SkTArray<const char*> instanceLayerNames; SkTArray<const char*> instanceExtensionNames; uint32_t extensionFlags = 0; #ifdef SK_ENABLE_VK_LAYERS for (size_t i = 0; i < SK_ARRAY_COUNT(kMyDebugLayerNames); ++i) { if (extensions.hasInstanceLayer(kMyDebugLayerNames[i])) { instanceLayerNames.push_back(kMyDebugLayerNames[i]); } } if (extensions.hasInstanceExtension(VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) { instanceExtensionNames.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); extensionFlags |= kEXT_debug_report_GrVkExtensionFlag; } #endif const VkInstanceCreateInfo instance_create = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, // sType nullptr, // pNext 0, // flags &app_info, // pApplicationInfo (uint32_t) instanceLayerNames.count(), // enabledLayerNameCount instanceLayerNames.begin(), // ppEnabledLayerNames (uint32_t) instanceExtensionNames.count(), // enabledExtensionNameCount instanceExtensionNames.begin(), // ppEnabledExtensionNames }; ACQUIRE_VK_PROC(CreateInstance, VK_NULL_HANDLE, VK_NULL_HANDLE); err = fVkCreateInstance(&instance_create, nullptr, &fInst); if (err < 0) { ERRORF(reporter, "vkCreateInstance failed: %d\n", err); return false; } ACQUIRE_VK_PROC(DestroyInstance, fInst, VK_NULL_HANDLE); ACQUIRE_INST_VK_PROC(EnumeratePhysicalDevices); ACQUIRE_INST_VK_PROC(GetPhysicalDeviceProperties); ACQUIRE_INST_VK_PROC(GetPhysicalDeviceQueueFamilyProperties); ACQUIRE_INST_VK_PROC(GetPhysicalDeviceFeatures); ACQUIRE_INST_VK_PROC(CreateDevice); ACQUIRE_INST_VK_PROC(GetDeviceQueue); ACQUIRE_INST_VK_PROC(DeviceWaitIdle); ACQUIRE_INST_VK_PROC(DestroyDevice); uint32_t gpuCount; err = fVkEnumeratePhysicalDevices(fInst, &gpuCount, nullptr); if (err) { ERRORF(reporter, "vkEnumeratePhysicalDevices failed: %d\n", err); fVkDestroyInstance(fInst, nullptr); return false; } if (!gpuCount) { // We can no physical devices so this isn't an error and failure in the test. fVkDestroyInstance(fInst, nullptr); return false; } // Just returning the first physical device instead of getting the whole array. // TODO: find best match for our needs gpuCount = 1; err = fVkEnumeratePhysicalDevices(fInst, &gpuCount, &fPhysDev); if (err) { ERRORF(reporter, "vkEnumeratePhysicalDevices failed: %d\n", err); fVkDestroyInstance(fInst, nullptr); return false; } // query to get the initial queue props size uint32_t queueCount; fVkGetPhysicalDeviceQueueFamilyProperties(fPhysDev, &queueCount, nullptr); if (!queueCount) { ERRORF(reporter, "vkGetPhysicalDeviceQueueFamilyProperties returned no queues.\n"); fVkDestroyInstance(fInst, nullptr); return false; } SkAutoMalloc queuePropsAlloc(queueCount * sizeof(VkQueueFamilyProperties)); // now get the actual queue props VkQueueFamilyProperties* queueProps = (VkQueueFamilyProperties*)queuePropsAlloc.get(); fVkGetPhysicalDeviceQueueFamilyProperties(fPhysDev, &queueCount, queueProps); // iterate to find the graphics queue uint32_t graphicsQueueIndex = queueCount; for (uint32_t i = 0; i < queueCount; i++) { if (queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { graphicsQueueIndex = i; break; } } if (graphicsQueueIndex == queueCount) { ERRORF(reporter, "Could not find any supported graphics queues.\n"); fVkDestroyInstance(fInst, nullptr); return false; } VkPhysicalDeviceProperties physDevProperties; fVkGetPhysicalDeviceProperties(fPhysDev, &physDevProperties); int physDevVersion = physDevProperties.apiVersion; if (physDevVersion < VK_MAKE_VERSION(1, 1, 0)) { return false; } // Physical-Device-level functions added in 1.1 ACQUIRE_INST_VK_PROC(GetPhysicalDeviceMemoryProperties2); ACQUIRE_INST_VK_PROC(GetPhysicalDeviceImageFormatProperties2); ACQUIRE_INST_VK_PROC(GetPhysicalDeviceExternalSemaphoreProperties); extensions.initDevice(physDevVersion, fInst, fPhysDev); SkTArray<const char*> deviceLayerNames; SkTArray<const char*> deviceExtensionNames; #ifdef SK_ENABLE_VK_LAYERS for (size_t i = 0; i < SK_ARRAY_COUNT(kMyDebugLayerNames); ++i) { if (extensions.hasDeviceLayer(kMyDebugLayerNames[i])) { deviceLayerNames.push_back(kMyDebugLayerNames[i]); } } #endif if (extensions.hasDeviceExtension( VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME)) { deviceExtensionNames.push_back( VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME); } else { fVkDestroyInstance(fInst, nullptr); return false; } if (extensions.hasDeviceExtension(VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME)) { deviceExtensionNames.push_back(VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME); } else { ERRORF(reporter, "Has HWB extension, but doesn't not have YCBCR coversion extension"); fVkDestroyInstance(fInst, nullptr); return false; } if (extensions.hasDeviceExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME)) { deviceExtensionNames.push_back(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME); } else { fVkDestroyInstance(fInst, nullptr); return false; } if (extensions.hasDeviceExtension(VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME)) { deviceExtensionNames.push_back(VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME); } else { SkDebugf("We don't have the extension for VK_EXT_QUEUE_FAMILY_FOREIGN\n"); //fVkDestroyInstance(fInst, nullptr); //return false; } // query to get the physical device properties VkPhysicalDeviceFeatures deviceFeatures; fVkGetPhysicalDeviceFeatures(fPhysDev, &deviceFeatures); // this looks like it would slow things down, // and we can't depend on it on all platforms deviceFeatures.robustBufferAccess = VK_FALSE; uint32_t featureFlags = 0; if (deviceFeatures.geometryShader) { featureFlags |= kGeometryShader_GrVkFeatureFlag; } if (deviceFeatures.dualSrcBlend) { featureFlags |= kDualSrcBlend_GrVkFeatureFlag; } if (deviceFeatures.sampleRateShading) { featureFlags |= kSampleRateShading_GrVkFeatureFlag; } float queuePriorities[1] = { 0.0 }; // Here we assume no need for swapchain queue // If one is needed, the client will need its own setup code const VkDeviceQueueCreateInfo queueInfo[1] = { { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType nullptr, // pNext 0, // VkDeviceQueueCreateFlags graphicsQueueIndex, // queueFamilyIndex 1, // queueCount queuePriorities, // pQueuePriorities } }; uint32_t queueInfoCount = 1; const VkDeviceCreateInfo deviceInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, // sType nullptr, // pNext 0, // VkDeviceCreateFlags queueInfoCount, // queueCreateInfoCount queueInfo, // pQueueCreateInfos (uint32_t) deviceLayerNames.count(), // layerCount deviceLayerNames.begin(), // ppEnabledLayerNames (uint32_t) deviceExtensionNames.count(), // extensionCount deviceExtensionNames.begin(), // ppEnabledExtensionNames &deviceFeatures // ppEnabledFeatures }; err = fVkCreateDevice(fPhysDev, &deviceInfo, nullptr, &fDevice); if (err) { ERRORF(reporter, "CreateDevice failed: %d\n", err); fVkDestroyInstance(fInst, nullptr); return false; } ACQUIRE_DEVICE_VK_PROC(CreateImage); ACQUIRE_DEVICE_VK_PROC(GetImageMemoryRequirements2); ACQUIRE_DEVICE_VK_PROC(GetAndroidHardwareBufferPropertiesANDROID); ACQUIRE_DEVICE_VK_PROC(AllocateMemory); ACQUIRE_DEVICE_VK_PROC(BindImageMemory2); ACQUIRE_DEVICE_VK_PROC(DestroyImage); 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); VkQueue queue; fVkGetDeviceQueue(fDevice, graphicsQueueIndex, 0, &queue); // Setting up actual skia things now auto interface = sk_make_sp<GrVkInterface>(getProc, fInst, fDevice, extensionFlags); if (!interface->validate(extensionFlags)) { ERRORF(reporter, "Vulkan interface validation failed\n"); fVkDeviceWaitIdle(fDevice); fVkDestroyDevice(fDevice, nullptr); fVkDestroyInstance(fInst, nullptr); return false; } fBackendContext.reset(new GrVkBackendContext()); fBackendContext->fInstance = fInst; fBackendContext->fPhysicalDevice = fPhysDev; fBackendContext->fDevice = fDevice; fBackendContext->fQueue = queue; fBackendContext->fGraphicsQueueIndex = graphicsQueueIndex; fBackendContext->fMinAPIVersion = instanceVersion; fBackendContext->fExtensions = extensionFlags; fBackendContext->fFeatures = featureFlags; fBackendContext->fInterface.reset(interface.release()); fBackendContext->fOwnsInstanceAndDevice = true; fGrContext = GrContext::MakeVulkan(fBackendContext); REPORTER_ASSERT(reporter, fGrContext.get()); 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(fPhysDev, &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, "GetAndroidHardwareBufferPropertiesAndoird 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 //nullptr, // pNext VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID, // handleTypes //0x80, // 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; } VkImageMemoryRequirementsInfo2 memReqsInfo; memReqsInfo.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2; memReqsInfo.pNext = nullptr; memReqsInfo.image = fImage; VkMemoryDedicatedRequirements dedicatedMemReqs; dedicatedMemReqs.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS; dedicatedMemReqs.pNext = nullptr; VkMemoryRequirements2 memReqs; memReqs.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2; memReqs.pNext = &dedicatedMemReqs; fVkGetImageMemoryRequirements2(fDevice, &memReqsInfo, &memReqs); REPORTER_ASSERT(reporter, VK_TRUE == dedicatedMemReqs.requiresDedicatedAllocation); 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(fPhysDev, &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->fInitialQueueFamily = VK_QUEUE_FAMILY_EXTERNAL; 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) { GrBackendSemaphore semaphore; if (!this->setupSemaphoreForSignaling(reporter, &semaphore)) { return false; } GrSemaphoresSubmitted submitted = surface->flushAndSignalSemaphores(1, &semaphore); if (GrSemaphoresSubmitted::kNo == submitted) { ERRORF(reporter, "Failing call to flushAndSignalSemaphores on SkSurface"); 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(fPhysDev, &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; } fVkDestroySemaphore(fDevice, semaphore, nullptr); 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) { srcHelper.reset(new EGLTestHelper(options)); } if (srcHelper) { if (!srcHelper->init(reporter)) { cleanup_resources(srcHelper.get(), dstHelper.get(), buffer); return; } } if (DstType::kVulkan == dstType) { dstHelper.reset(new VulkanTestHelper()); } else { SkASSERT(DstType::kEGL == dstType); dstHelper.reset(new EGLTestHelper(options)); } 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, surface)) { cleanup_resources(srcHelper.get(), dstHelper.get(), buffer); return; } surface.reset(); } 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(); dstHelper->doClientSync(); cleanup_resources(srcHelper.get(), dstHelper.get(), buffer); return; } REPORTER_ASSERT(reporter, check_read(reporter, srcBitmap, dstBitmapFinal)); 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_EGL_Vulkan, reporter, options) { run_test(reporter, options, SrcType::kEGL, DstType::kVulkan, false); } DEF_GPUTEST(VulkanHardwareBuffer_Vulkan_Vulkan, reporter, options) { run_test(reporter, options, SrcType::kVulkan, 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); } DEF_GPUTEST(VulkanHardwareBuffer_Vulkan_Vulkan_Syncs, reporter, options) { run_test(reporter, options, SrcType::kVulkan, DstType::kVulkan, true); } #endif #endif