1 /* 2 * Copyright 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 */ 17 18 #include <android/log.h> 19 #include <jni.h> 20 #include <unistd.h> 21 22 #include "ImageReaderTestHelpers.h" 23 #include "MediaTestHelpers.h" 24 #include "NativeTestHelpers.h" 25 #include "VulkanTestHelpers.h" 26 27 namespace { 28 29 static constexpr uint32_t kTestImageWidth = 1920; 30 static constexpr uint32_t kTestImageHeight = 1080; 31 static constexpr uint32_t kTestImageFormat = AIMAGE_FORMAT_YUV_420_888; 32 static constexpr uint64_t kTestImageUsage = 33 AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE; 34 static constexpr uint32_t kTestImageCount = 3; 35 36 // Confirms that the two values match, allowing for an error of tolerance per 37 // channel. 38 bool fuzzyMatch(uint32_t value1, uint32_t value2, int32_t tolerance) { 39 for (size_t i = 0; i < 4; ++i) { 40 size_t shift = 8 * i; 41 uint32_t mask = 0x000000FF << shift; 42 43 int32_t value1Masked = static_cast<int32_t>((value1 & mask) >> shift); 44 int32_t value2Masked = static_cast<int32_t>((value2 & mask) >> shift); 45 46 if (std::abs(value1Masked - value2Masked) > tolerance) 47 return false; 48 } 49 50 return true; 51 } 52 53 uint32_t swizzleBgraToRgba(uint32_t bgra) { 54 uint32_t result = 0; 55 result |= (bgra & 0xFF000000) >> 0; // Alpha 56 result |= (bgra & 0x00FF0000) >> 16; // Red 57 result |= (bgra & 0x0000FF00) >> 0; // Green 58 result |= (bgra & 0x000000FF) << 16; // Blue 59 return result; 60 } 61 62 } // namespace 63 64 // A Vulkan media import test which does the following: 65 // 1) Reads the first frame from a video as an AHardwareBuffer. 66 // 2) Creates a VkImage from this AHardwareBuffer. 67 // 3) Renders the AHardwareBuffer to a Vulkan RGBA intermediate. 68 // 4) Reads back the intermediate into a CPU accessible VkBuffer. 69 // 5) Validates that the values are as expected. 70 static void loadMediaAndVerifyFrameImport(JNIEnv *env, jclass, jobject assetMgr, 71 jstring jfilename, 72 jintArray referencePixels) { 73 // Set up Vulkan. 74 VkInit init; 75 if (!init.init()) { 76 // Could not initialize Vulkan due to lack of device support, skip test. 77 return; 78 } 79 VkImageRenderer renderer(&init, kTestImageWidth, kTestImageHeight, 80 VK_FORMAT_R8G8B8A8_UNORM, 4); 81 ASSERT(renderer.init(env, assetMgr), "Could not init VkImageRenderer."); 82 83 // Set up the image reader and media helpers used to get a frames from video. 84 ImageReaderHelper imageReader(kTestImageWidth, kTestImageHeight, 85 kTestImageFormat, kTestImageUsage, 86 kTestImageCount); 87 ASSERT(imageReader.initImageReader() >= 0, 88 "Failed to initialize image reader."); 89 MediaHelper media; 90 ASSERT(media.init(env, assetMgr, jfilename, imageReader.getNativeWindow()), 91 "Failed to initialize media codec."); 92 93 // Get an AHardwareBuffer for the first frame of the video. 94 ASSERT(media.processOneFrame(), 95 "Could not get a media frame to import into Vulkan."); 96 AHardwareBuffer *buffer; 97 int ret = imageReader.getBufferFromCurrentImage(&buffer); 98 while (ret != 0) { 99 usleep(1000); 100 ret = imageReader.getBufferFromCurrentImage(&buffer); 101 } 102 103 // Import the AHardwareBuffer into Vulkan. 104 VkAHardwareBufferImage vkImage(&init); 105 ASSERT(vkImage.init(buffer, true /* useExternalFormat */), "Could not init VkAHardwareBufferImage."); 106 107 // Render the AHardwareBuffer using Vulkan and read back the result. 108 std::vector<uint32_t> framePixels; 109 ASSERT(renderer.renderImageAndReadback( 110 vkImage.image(), vkImage.sampler(), vkImage.view(), 111 vkImage.semaphore(), vkImage.isSamplerImmutable(), &framePixels), 112 "Could not get frame pixels from Vulkan."); 113 ASSERT(framePixels.size() == kTestImageWidth * kTestImageHeight, 114 "Unexpected number of pixels in frame"); 115 116 // Ensure that the data we read back matches our reference image. 117 size_t referenceSize = 118 static_cast<size_t>(env->GetArrayLength(referencePixels)); 119 ASSERT(framePixels.size() == referenceSize, 120 "Unexpected number of pixels in frame."); 121 uint32_t *referenceData = reinterpret_cast<uint32_t *>( 122 env->GetIntArrayElements(referencePixels, 0)); 123 for (uint32_t x = 0; x < kTestImageWidth; ++x) { 124 for (uint32_t y = 0; y < kTestImageHeight; ++y) { 125 size_t offset = y * kTestImageWidth + x; 126 static const int32_t kTolerance = 0x30; 127 uint32_t value1 = framePixels[offset]; 128 // Reference data is BGRA, Vk data is BGRA. 129 uint32_t value2 = swizzleBgraToRgba(referenceData[offset]); 130 ASSERT(fuzzyMatch(value1, value2, kTolerance), 131 "Expected ~0x%08X at (%i,%i), got 0x%08X", value2, x, y, value1); 132 } 133 } 134 } 135 136 static JNINativeMethod gMethods[] = { 137 {"loadMediaAndVerifyFrameImport", 138 "(Landroid/content/res/AssetManager;Ljava/lang/String;[I)V", 139 (void *)loadMediaAndVerifyFrameImport}, 140 }; 141 142 int register_android_graphics_cts_MediaVulkanGpuTest(JNIEnv *env) { 143 jclass clazz = env->FindClass("android/graphics/cts/MediaVulkanGpuTest"); 144 return env->RegisterNatives(clazz, gMethods, 145 sizeof(gMethods) / sizeof(JNINativeMethod)); 146 } 147