| /* |
| * 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 |
| |