| /* |
| * Copyright 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| #define LOG_TAG "VulkanPreTransformTestHelpers" |
| |
| #ifndef VK_USE_PLATFORM_ANDROID_KHR |
| #define VK_USE_PLATFORM_ANDROID_KHR |
| #endif |
| |
| #include <android/log.h> |
| #include <cstring> |
| |
| #include "VulkanPreTransformTestHelpers.h" |
| |
| #define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) |
| #define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) |
| #define ASSERT(a) \ |
| if (!(a)) { \ |
| ALOGE("Failure: " #a " at " __FILE__ ":%d", __LINE__); \ |
| return VK_TEST_ERROR; \ |
| } |
| #define VK_CALL(a) ASSERT(VK_SUCCESS == (a)) |
| |
| #define TIMEOUT_30_SEC 30000000000 |
| |
| static const float vertexData[] = { |
| // L:left, T:top, R:right, B:bottom, C:center |
| -1.0f, -1.0f, 0.0f, // LT |
| -1.0f, 0.0f, 0.0f, // LC |
| 0.0f, -1.0f, 0.0f, // CT |
| 0.0f, 0.0f, 0.0f, // CC |
| 1.0f, -1.0f, 0.0f, // RT |
| 1.0f, 0.0f, 0.0f, // RC |
| -1.0f, 0.0f, 0.0f, // LC |
| -1.0f, 1.0f, 0.0f, // LB |
| 0.0f, 0.0f, 0.0f, // CC |
| 0.0f, 1.0f, 0.0f, // CB |
| 1.0f, 0.0f, 0.0f, // RC |
| 1.0f, 1.0f, 0.0f, // RB |
| }; |
| |
| static const float fragData[] = { |
| 1.0f, 0.0f, 0.0f, // Red |
| 0.0f, 1.0f, 0.0f, // Green |
| 0.0f, 0.0f, 1.0f, // Blue |
| 1.0f, 1.0f, 0.0f, // Yellow |
| }; |
| |
| static const char* requiredInstanceExtensions[] = { |
| "VK_KHR_surface", |
| "VK_KHR_android_surface", |
| }; |
| |
| static const char* requiredDeviceExtensions[] = { |
| "VK_KHR_swapchain", |
| }; |
| |
| static const char* blacklistedDeviceExtensions[] = { |
| "VK_KHR_performance_query", |
| }; |
| |
| static bool enumerateInstanceExtensions(std::vector<VkExtensionProperties>* extensions) { |
| VkResult result; |
| |
| uint32_t count = 0; |
| result = vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr); |
| if (result != VK_SUCCESS) return false; |
| |
| extensions->resize(count); |
| result = vkEnumerateInstanceExtensionProperties(nullptr, &count, extensions->data()); |
| if (result != VK_SUCCESS) return false; |
| |
| return true; |
| } |
| |
| static bool enumerateDeviceExtensions(VkPhysicalDevice device, |
| std::vector<VkExtensionProperties>* extensions) { |
| VkResult result; |
| |
| uint32_t count = 0; |
| result = vkEnumerateDeviceExtensionProperties(device, nullptr, &count, nullptr); |
| if (result != VK_SUCCESS) return false; |
| |
| extensions->resize(count); |
| result = vkEnumerateDeviceExtensionProperties(device, nullptr, &count, extensions->data()); |
| if (result != VK_SUCCESS) return false; |
| |
| return true; |
| } |
| |
| static bool hasExtension(const char* extension_name, |
| const std::vector<VkExtensionProperties>& extensions) { |
| return std::find_if(extensions.cbegin(), extensions.cend(), |
| [extension_name](const VkExtensionProperties& extension) { |
| return strcmp(extension.extensionName, extension_name) == 0; |
| }) != extensions.cend(); |
| } |
| |
| DeviceInfo::DeviceInfo() |
| : mInstance(VK_NULL_HANDLE), |
| mGpu(VK_NULL_HANDLE), |
| mWindow(nullptr), |
| mSurface(VK_NULL_HANDLE), |
| mQueueFamilyIndex(0), |
| mDevice(VK_NULL_HANDLE), |
| mQueue(VK_NULL_HANDLE) {} |
| |
| DeviceInfo::~DeviceInfo() { |
| if (mDevice) { |
| vkDeviceWaitIdle(mDevice); |
| vkDestroyDevice(mDevice, nullptr); |
| mDevice = VK_NULL_HANDLE; |
| } |
| if (mInstance) { |
| vkDestroySurfaceKHR(mInstance, mSurface, nullptr); |
| vkDestroyInstance(mInstance, nullptr); |
| mInstance = VK_NULL_HANDLE; |
| } |
| if (mWindow) { |
| ANativeWindow_release(mWindow); |
| mWindow = nullptr; |
| } |
| } |
| |
| VkTestResult DeviceInfo::init(JNIEnv* env, jobject jSurface) { |
| ASSERT(jSurface); |
| |
| mWindow = ANativeWindow_fromSurface(env, jSurface); |
| ASSERT(mWindow); |
| |
| std::vector<VkExtensionProperties> supportedInstanceExtensions; |
| ASSERT(enumerateInstanceExtensions(&supportedInstanceExtensions)); |
| |
| std::vector<const char*> enabledInstanceExtensions; |
| for (const auto extension : requiredInstanceExtensions) { |
| ASSERT(hasExtension(extension, supportedInstanceExtensions)); |
| enabledInstanceExtensions.push_back(extension); |
| } |
| |
| const VkApplicationInfo appInfo = { |
| .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, |
| .pNext = nullptr, |
| .pApplicationName = "VulkanPreTransformTest", |
| .applicationVersion = VK_MAKE_VERSION(1, 0, 0), |
| .pEngineName = "", |
| .engineVersion = VK_MAKE_VERSION(1, 0, 0), |
| .apiVersion = VK_MAKE_VERSION(1, 0, 0), |
| }; |
| const VkInstanceCreateInfo instanceInfo = { |
| .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = 0, |
| .pApplicationInfo = &appInfo, |
| .enabledLayerCount = 0, |
| .ppEnabledLayerNames = nullptr, |
| .enabledExtensionCount = static_cast<uint32_t>(enabledInstanceExtensions.size()), |
| .ppEnabledExtensionNames = enabledInstanceExtensions.data(), |
| }; |
| VK_CALL(vkCreateInstance(&instanceInfo, nullptr, &mInstance)); |
| |
| uint32_t gpuCount = 0; |
| VK_CALL(vkEnumeratePhysicalDevices(mInstance, &gpuCount, nullptr)); |
| if (gpuCount == 0) { |
| ALOGD("No physical device available"); |
| return VK_TEST_PHYSICAL_DEVICE_NOT_EXISTED; |
| } |
| |
| std::vector<VkPhysicalDevice> gpus(gpuCount, VK_NULL_HANDLE); |
| VK_CALL(vkEnumeratePhysicalDevices(mInstance, &gpuCount, gpus.data())); |
| |
| mGpu = gpus[0]; |
| |
| const VkAndroidSurfaceCreateInfoKHR surfaceInfo = { |
| .sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR, |
| .pNext = nullptr, |
| .flags = 0, |
| .window = mWindow, |
| }; |
| VK_CALL(vkCreateAndroidSurfaceKHR(mInstance, &surfaceInfo, nullptr, &mSurface)); |
| |
| std::vector<VkExtensionProperties> supportedDeviceExtensions; |
| ASSERT(enumerateDeviceExtensions(mGpu, &supportedDeviceExtensions)); |
| |
| // Fail if the blacklisted extensions are advertised as supported |
| for (const auto extension : blacklistedDeviceExtensions) { |
| ASSERT(!hasExtension(extension, supportedDeviceExtensions)); |
| } |
| |
| std::vector<const char*> enabledDeviceExtensions; |
| for (const auto extension : requiredDeviceExtensions) { |
| ASSERT(hasExtension(extension, supportedDeviceExtensions)); |
| enabledDeviceExtensions.push_back(extension); |
| } |
| |
| uint32_t queueFamilyCount = 0; |
| vkGetPhysicalDeviceQueueFamilyProperties(mGpu, &queueFamilyCount, nullptr); |
| ASSERT(queueFamilyCount); |
| |
| std::vector<VkQueueFamilyProperties> queueFamilyProperties(queueFamilyCount); |
| vkGetPhysicalDeviceQueueFamilyProperties(mGpu, &queueFamilyCount, queueFamilyProperties.data()); |
| |
| uint32_t queueFamilyIndex; |
| for (queueFamilyIndex = 0; queueFamilyIndex < queueFamilyCount; ++queueFamilyIndex) { |
| if (queueFamilyProperties[queueFamilyIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) { |
| break; |
| } |
| } |
| ASSERT(queueFamilyIndex < queueFamilyCount); |
| mQueueFamilyIndex = queueFamilyIndex; |
| |
| const float priority = 1.0f; |
| const VkDeviceQueueCreateInfo queueCreateInfo = { |
| .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = 0, |
| .queueFamilyIndex = mQueueFamilyIndex, |
| .queueCount = 1, |
| .pQueuePriorities = &priority, |
| }; |
| const VkDeviceCreateInfo deviceCreateInfo = { |
| .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, |
| .pNext = nullptr, |
| .queueCreateInfoCount = 1, |
| .pQueueCreateInfos = &queueCreateInfo, |
| .enabledLayerCount = 0, |
| .ppEnabledLayerNames = nullptr, |
| .enabledExtensionCount = static_cast<uint32_t>(enabledDeviceExtensions.size()), |
| .ppEnabledExtensionNames = enabledDeviceExtensions.data(), |
| .pEnabledFeatures = nullptr, |
| }; |
| VK_CALL(vkCreateDevice(mGpu, &deviceCreateInfo, nullptr, &mDevice)); |
| |
| vkGetDeviceQueue(mDevice, mQueueFamilyIndex, 0, &mQueue); |
| |
| return VK_TEST_SUCCESS; |
| } |
| |
| SwapchainInfo::SwapchainInfo(const DeviceInfo* const deviceInfo) |
| : mDeviceInfo(deviceInfo), |
| mFormat(VK_FORMAT_UNDEFINED), |
| mSurfaceSize({0, 0}), |
| mImageSize({0, 0}), |
| mSwapchain(VK_NULL_HANDLE), |
| mSwapchainLength(0) {} |
| |
| SwapchainInfo::~SwapchainInfo() { |
| if (mDeviceInfo->device()) { |
| vkDeviceWaitIdle(mDeviceInfo->device()); |
| vkDestroySwapchainKHR(mDeviceInfo->device(), mSwapchain, nullptr); |
| } |
| } |
| |
| VkTestResult SwapchainInfo::init(bool setPreTransform, int* outPreTransformHint) { |
| VkSurfaceCapabilitiesKHR surfaceCapabilities; |
| VK_CALL(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(mDeviceInfo->gpu(), mDeviceInfo->surface(), |
| &surfaceCapabilities)); |
| ALOGD("Vulkan Surface Capabilities:\n"); |
| ALOGD("\timage count: %u - %u\n", surfaceCapabilities.minImageCount, |
| surfaceCapabilities.maxImageCount); |
| ALOGD("\tarray layers: %u\n", surfaceCapabilities.maxImageArrayLayers); |
| ALOGD("\timage size (now): %dx%d\n", surfaceCapabilities.currentExtent.width, |
| surfaceCapabilities.currentExtent.height); |
| ALOGD("\timage size (extent): %dx%d - %dx%d\n", surfaceCapabilities.minImageExtent.width, |
| surfaceCapabilities.minImageExtent.height, surfaceCapabilities.maxImageExtent.width, |
| surfaceCapabilities.maxImageExtent.height); |
| ALOGD("\tusage: %x\n", surfaceCapabilities.supportedUsageFlags); |
| ALOGD("\tcurrent transform: %u\n", surfaceCapabilities.currentTransform); |
| ALOGD("\tallowed transforms: %x\n", surfaceCapabilities.supportedTransforms); |
| ALOGD("\tcomposite alpha flags: %u\n", surfaceCapabilities.supportedCompositeAlpha); |
| |
| uint32_t formatCount = 0; |
| VK_CALL(vkGetPhysicalDeviceSurfaceFormatsKHR(mDeviceInfo->gpu(), mDeviceInfo->surface(), |
| &formatCount, nullptr)); |
| |
| std::vector<VkSurfaceFormatKHR> formats(formatCount); |
| VK_CALL(vkGetPhysicalDeviceSurfaceFormatsKHR(mDeviceInfo->gpu(), mDeviceInfo->surface(), |
| &formatCount, formats.data())); |
| |
| uint32_t formatIndex; |
| for (formatIndex = 0; formatIndex < formatCount; ++formatIndex) { |
| if (formats[formatIndex].format == VK_FORMAT_R8G8B8A8_UNORM) { |
| break; |
| } |
| } |
| ASSERT(formatIndex < formatCount); |
| |
| mFormat = formats[formatIndex].format; |
| mImageSize = mSurfaceSize = surfaceCapabilities.currentExtent; |
| |
| VkSurfaceTransformFlagBitsKHR preTransform = |
| (setPreTransform ? surfaceCapabilities.currentTransform |
| : VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR); |
| ALOGD("currentTransform = %u, preTransform = %u", |
| static_cast<uint32_t>(surfaceCapabilities.currentTransform), |
| static_cast<uint32_t>(preTransform)); |
| |
| if ((preTransform & |
| (VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR | |
| VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR | |
| VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR)) != 0) { |
| std::swap(mImageSize.width, mImageSize.height); |
| } |
| |
| if (outPreTransformHint) { |
| *outPreTransformHint = surfaceCapabilities.currentTransform; |
| } |
| |
| const uint32_t queueFamilyIndex = mDeviceInfo->queueFamilyIndex(); |
| const VkSwapchainCreateInfoKHR swapchainCreateInfo = { |
| .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, |
| .pNext = nullptr, |
| .flags = 0, |
| .surface = mDeviceInfo->surface(), |
| .minImageCount = surfaceCapabilities.minImageCount, |
| .imageFormat = mFormat, |
| .imageColorSpace = formats[formatIndex].colorSpace, |
| .imageExtent = mImageSize, |
| .imageArrayLayers = 1, |
| .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, |
| .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, |
| .queueFamilyIndexCount = 1, |
| .pQueueFamilyIndices = &queueFamilyIndex, |
| .preTransform = preTransform, |
| .compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR, |
| .presentMode = VK_PRESENT_MODE_FIFO_KHR, |
| .clipped = VK_FALSE, |
| }; |
| VK_CALL(vkCreateSwapchainKHR(mDeviceInfo->device(), &swapchainCreateInfo, nullptr, |
| &mSwapchain)); |
| |
| VK_CALL(vkGetSwapchainImagesKHR(mDeviceInfo->device(), mSwapchain, &mSwapchainLength, nullptr)); |
| ALOGD("Swapchain length = %u", mSwapchainLength); |
| |
| return VK_TEST_SUCCESS; |
| } |
| |
| Renderer::Renderer(const DeviceInfo* const deviceInfo, const SwapchainInfo* const swapchainInfo) |
| : mDeviceInfo(deviceInfo), |
| mSwapchainInfo(swapchainInfo), |
| mDeviceMemory(VK_NULL_HANDLE), |
| mVertexBuffer(VK_NULL_HANDLE), |
| mRenderPass(VK_NULL_HANDLE), |
| mVertexShader(VK_NULL_HANDLE), |
| mFragmentShader(VK_NULL_HANDLE), |
| mPipelineLayout(VK_NULL_HANDLE), |
| mPipeline(VK_NULL_HANDLE), |
| mCommandPool(VK_NULL_HANDLE), |
| mSemaphore(VK_NULL_HANDLE), |
| mFence(VK_NULL_HANDLE) {} |
| |
| Renderer::~Renderer() { |
| if (mDeviceInfo->device()) { |
| vkDeviceWaitIdle(mDeviceInfo->device()); |
| vkDestroyShaderModule(mDeviceInfo->device(), mVertexShader, nullptr); |
| vkDestroyShaderModule(mDeviceInfo->device(), mFragmentShader, nullptr); |
| vkDestroyFence(mDeviceInfo->device(), mFence, nullptr); |
| vkDestroySemaphore(mDeviceInfo->device(), mSemaphore, nullptr); |
| if (!mCommandBuffers.empty()) { |
| vkFreeCommandBuffers(mDeviceInfo->device(), mCommandPool, mCommandBuffers.size(), |
| mCommandBuffers.data()); |
| } |
| vkDestroyCommandPool(mDeviceInfo->device(), mCommandPool, nullptr); |
| vkDestroyPipeline(mDeviceInfo->device(), mPipeline, nullptr); |
| vkDestroyPipelineLayout(mDeviceInfo->device(), mPipelineLayout, nullptr); |
| vkDestroyBuffer(mDeviceInfo->device(), mVertexBuffer, nullptr); |
| vkFreeMemory(mDeviceInfo->device(), mDeviceMemory, nullptr); |
| vkDestroyRenderPass(mDeviceInfo->device(), mRenderPass, nullptr); |
| for (auto& framebuffer : mFramebuffers) { |
| vkDestroyFramebuffer(mDeviceInfo->device(), framebuffer, nullptr); |
| } |
| for (auto& imageView : mImageViews) { |
| vkDestroyImageView(mDeviceInfo->device(), imageView, nullptr); |
| } |
| } |
| } |
| |
| VkTestResult Renderer::createRenderPass() { |
| const VkAttachmentDescription attachmentDescription = { |
| .flags = 0, |
| .format = mSwapchainInfo->format(), |
| .samples = VK_SAMPLE_COUNT_1_BIT, |
| .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, |
| .storeOp = VK_ATTACHMENT_STORE_OP_STORE, |
| .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, |
| .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, |
| .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, |
| .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, |
| }; |
| const VkAttachmentReference attachmentReference = { |
| .attachment = 0, |
| .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, |
| }; |
| const VkSubpassDescription subpassDescription = { |
| .flags = 0, |
| .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, |
| .inputAttachmentCount = 0, |
| .pInputAttachments = nullptr, |
| .colorAttachmentCount = 1, |
| .pColorAttachments = &attachmentReference, |
| .pResolveAttachments = nullptr, |
| .pDepthStencilAttachment = nullptr, |
| .preserveAttachmentCount = 0, |
| .pPreserveAttachments = nullptr, |
| }; |
| const VkRenderPassCreateInfo renderPassCreateInfo = { |
| .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = 0, |
| .attachmentCount = 1, |
| .pAttachments = &attachmentDescription, |
| .subpassCount = 1, |
| .pSubpasses = &subpassDescription, |
| .dependencyCount = 0, |
| .pDependencies = nullptr, |
| }; |
| VK_CALL(vkCreateRenderPass(mDeviceInfo->device(), &renderPassCreateInfo, nullptr, |
| &mRenderPass)); |
| |
| return VK_TEST_SUCCESS; |
| } |
| |
| VkTestResult Renderer::createFrameBuffers() { |
| uint32_t swapchainLength = mSwapchainInfo->swapchainLength(); |
| std::vector<VkImage> images(swapchainLength, VK_NULL_HANDLE); |
| VK_CALL(vkGetSwapchainImagesKHR(mDeviceInfo->device(), mSwapchainInfo->swapchain(), |
| &swapchainLength, images.data())); |
| |
| mImageViews.resize(swapchainLength, VK_NULL_HANDLE); |
| for (uint32_t i = 0; i < swapchainLength; ++i) { |
| const VkImageViewCreateInfo imageViewCreateInfo = { |
| .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = 0, |
| .image = images[i], |
| .viewType = VK_IMAGE_VIEW_TYPE_2D, |
| .format = mSwapchainInfo->format(), |
| .components = |
| { |
| .r = VK_COMPONENT_SWIZZLE_R, |
| .g = VK_COMPONENT_SWIZZLE_G, |
| .b = VK_COMPONENT_SWIZZLE_B, |
| .a = VK_COMPONENT_SWIZZLE_A, |
| }, |
| .subresourceRange = |
| { |
| .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, |
| .baseMipLevel = 0, |
| .levelCount = 1, |
| .baseArrayLayer = 0, |
| .layerCount = 1, |
| }, |
| }; |
| VK_CALL(vkCreateImageView(mDeviceInfo->device(), &imageViewCreateInfo, nullptr, |
| &mImageViews[i])); |
| } |
| |
| mFramebuffers.resize(swapchainLength, VK_NULL_HANDLE); |
| for (uint32_t i = 0; i < swapchainLength; ++i) { |
| const VkFramebufferCreateInfo framebufferCreateInfo = { |
| .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = 0, |
| .renderPass = mRenderPass, |
| .attachmentCount = 1, |
| .pAttachments = &mImageViews[i], |
| .width = mSwapchainInfo->imageSize().width, |
| .height = mSwapchainInfo->imageSize().height, |
| .layers = 1, |
| }; |
| VK_CALL(vkCreateFramebuffer(mDeviceInfo->device(), &framebufferCreateInfo, nullptr, |
| &mFramebuffers[i])); |
| } |
| |
| return VK_TEST_SUCCESS; |
| } |
| |
| VkTestResult Renderer::createVertexBuffers() { |
| const uint32_t queueFamilyIndex = mDeviceInfo->queueFamilyIndex(); |
| const VkBufferCreateInfo bufferCreateInfo = { |
| .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = 0, |
| .size = sizeof(vertexData), |
| .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, |
| .sharingMode = VK_SHARING_MODE_EXCLUSIVE, |
| .queueFamilyIndexCount = 1, |
| .pQueueFamilyIndices = &queueFamilyIndex, |
| }; |
| VK_CALL(vkCreateBuffer(mDeviceInfo->device(), &bufferCreateInfo, nullptr, &mVertexBuffer)); |
| |
| VkMemoryRequirements memoryRequirements; |
| vkGetBufferMemoryRequirements(mDeviceInfo->device(), mVertexBuffer, &memoryRequirements); |
| |
| VkPhysicalDeviceMemoryProperties memoryProperties; |
| vkGetPhysicalDeviceMemoryProperties(mDeviceInfo->gpu(), &memoryProperties); |
| |
| int32_t typeIndex = -1; |
| for (int32_t i = 0, typeBits = memoryRequirements.memoryTypeBits; i < 32; ++i) { |
| if ((typeBits & 1) == 1) { |
| if ((memoryProperties.memoryTypes[i].propertyFlags & |
| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { |
| typeIndex = i; |
| break; |
| } |
| } |
| typeBits >>= 1; |
| } |
| ASSERT(typeIndex != -1); |
| |
| VkMemoryAllocateInfo memoryAllocateInfo = { |
| .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, |
| .pNext = nullptr, |
| .allocationSize = memoryRequirements.size, |
| .memoryTypeIndex = static_cast<uint32_t>(typeIndex), |
| }; |
| VK_CALL(vkAllocateMemory(mDeviceInfo->device(), &memoryAllocateInfo, nullptr, &mDeviceMemory)); |
| |
| void* data; |
| VK_CALL(vkMapMemory(mDeviceInfo->device(), mDeviceMemory, 0, sizeof(vertexData), 0, &data)); |
| |
| memcpy(data, vertexData, sizeof(vertexData)); |
| vkUnmapMemory(mDeviceInfo->device(), mDeviceMemory); |
| |
| VK_CALL(vkBindBufferMemory(mDeviceInfo->device(), mVertexBuffer, mDeviceMemory, 0)); |
| |
| return VK_TEST_SUCCESS; |
| } |
| |
| VkTestResult Renderer::loadShaderFromFile(const char* filePath, VkShaderModule* const outShader) { |
| ASSERT(filePath); |
| |
| AAsset* file = AAssetManager_open(mAssetManager, filePath, AASSET_MODE_BUFFER); |
| ASSERT(file); |
| |
| size_t fileLength = AAsset_getLength(file); |
| std::vector<char> fileContent(fileLength); |
| AAsset_read(file, fileContent.data(), fileLength); |
| AAsset_close(file); |
| |
| const VkShaderModuleCreateInfo shaderModuleCreateInfo = { |
| .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = 0, |
| .codeSize = fileLength, |
| .pCode = (const uint32_t*)(fileContent.data()), |
| }; |
| VK_CALL(vkCreateShaderModule(mDeviceInfo->device(), &shaderModuleCreateInfo, nullptr, |
| outShader)); |
| |
| return VK_TEST_SUCCESS; |
| } |
| |
| VkTestResult Renderer::createGraphicsPipeline() { |
| const VkPushConstantRange pushConstantRange = { |
| .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, |
| .offset = 0, |
| .size = 3 * sizeof(float), |
| }; |
| const VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = { |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = 0, |
| .setLayoutCount = 0, |
| .pSetLayouts = nullptr, |
| .pushConstantRangeCount = 1, |
| .pPushConstantRanges = &pushConstantRange, |
| }; |
| VK_CALL(vkCreatePipelineLayout(mDeviceInfo->device(), &pipelineLayoutCreateInfo, nullptr, |
| &mPipelineLayout)); |
| |
| ASSERT(!loadShaderFromFile("shaders/tri.vert.spv", &mVertexShader)); |
| ASSERT(!loadShaderFromFile("shaders/tri.frag.spv", &mFragmentShader)); |
| |
| const VkPipelineShaderStageCreateInfo shaderStages[2] = |
| {{ |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = 0, |
| .stage = VK_SHADER_STAGE_VERTEX_BIT, |
| .module = mVertexShader, |
| .pName = "main", |
| .pSpecializationInfo = nullptr, |
| }, |
| { |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = 0, |
| .stage = VK_SHADER_STAGE_FRAGMENT_BIT, |
| .module = mFragmentShader, |
| .pName = "main", |
| .pSpecializationInfo = nullptr, |
| }}; |
| const VkViewport viewports = { |
| .x = 0.0f, |
| .y = 0.0f, |
| .width = (float)mSwapchainInfo->imageSize().width, |
| .height = (float)mSwapchainInfo->imageSize().height, |
| .minDepth = 0.0f, |
| .maxDepth = 1.0f, |
| }; |
| const VkRect2D scissor = { |
| .offset = |
| { |
| .x = 0, |
| .y = 0, |
| }, |
| .extent = mSwapchainInfo->imageSize(), |
| }; |
| const VkPipelineViewportStateCreateInfo viewportInfo = { |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = 0, |
| .viewportCount = 1, |
| .pViewports = &viewports, |
| .scissorCount = 1, |
| .pScissors = &scissor, |
| }; |
| VkSampleMask sampleMask = ~0u; |
| const VkPipelineMultisampleStateCreateInfo multisampleInfo = { |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = 0, |
| .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, |
| .sampleShadingEnable = VK_FALSE, |
| .minSampleShading = 0, |
| .pSampleMask = &sampleMask, |
| .alphaToCoverageEnable = VK_FALSE, |
| .alphaToOneEnable = VK_FALSE, |
| }; |
| const VkPipelineColorBlendAttachmentState attachmentStates = { |
| .blendEnable = VK_FALSE, |
| .srcColorBlendFactor = (VkBlendFactor)0, |
| .dstColorBlendFactor = (VkBlendFactor)0, |
| .colorBlendOp = (VkBlendOp)0, |
| .srcAlphaBlendFactor = (VkBlendFactor)0, |
| .dstAlphaBlendFactor = (VkBlendFactor)0, |
| .alphaBlendOp = (VkBlendOp)0, |
| .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | |
| VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, |
| }; |
| const VkPipelineColorBlendStateCreateInfo colorBlendInfo = { |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = 0, |
| .logicOpEnable = VK_FALSE, |
| .logicOp = VK_LOGIC_OP_COPY, |
| .attachmentCount = 1, |
| .pAttachments = &attachmentStates, |
| .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, |
| }; |
| const VkPipelineRasterizationStateCreateInfo rasterInfo = { |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = 0, |
| .depthClampEnable = VK_FALSE, |
| .rasterizerDiscardEnable = VK_FALSE, |
| .polygonMode = VK_POLYGON_MODE_FILL, |
| .cullMode = VK_CULL_MODE_NONE, |
| .frontFace = VK_FRONT_FACE_CLOCKWISE, |
| .depthBiasEnable = VK_FALSE, |
| .depthBiasConstantFactor = 0, |
| .depthBiasClamp = 0, |
| .depthBiasSlopeFactor = 0, |
| .lineWidth = 1, |
| }; |
| const VkPipelineInputAssemblyStateCreateInfo inputAssemblyInfo = { |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = 0, |
| .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, |
| .primitiveRestartEnable = VK_FALSE, |
| }; |
| const VkVertexInputBindingDescription vertexInputBindingDescription = { |
| .binding = 0, |
| .stride = 3 * sizeof(float), |
| .inputRate = VK_VERTEX_INPUT_RATE_VERTEX, |
| }; |
| const VkVertexInputAttributeDescription vertexInputAttributeDescription = { |
| .location = 0, |
| .binding = 0, |
| .format = VK_FORMAT_R32G32B32_SFLOAT, |
| .offset = 0, |
| }; |
| const VkPipelineVertexInputStateCreateInfo vertexInputInfo = { |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = 0, |
| .vertexBindingDescriptionCount = 1, |
| .pVertexBindingDescriptions = &vertexInputBindingDescription, |
| .vertexAttributeDescriptionCount = 1, |
| .pVertexAttributeDescriptions = &vertexInputAttributeDescription, |
| }; |
| const VkGraphicsPipelineCreateInfo pipelineCreateInfo = { |
| .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = 0, |
| .stageCount = 2, |
| .pStages = shaderStages, |
| .pVertexInputState = &vertexInputInfo, |
| .pInputAssemblyState = &inputAssemblyInfo, |
| .pTessellationState = nullptr, |
| .pViewportState = &viewportInfo, |
| .pRasterizationState = &rasterInfo, |
| .pMultisampleState = &multisampleInfo, |
| .pDepthStencilState = nullptr, |
| .pColorBlendState = &colorBlendInfo, |
| .pDynamicState = nullptr, |
| .layout = mPipelineLayout, |
| .renderPass = mRenderPass, |
| .subpass = 0, |
| .basePipelineHandle = VK_NULL_HANDLE, |
| .basePipelineIndex = 0, |
| }; |
| VK_CALL(vkCreateGraphicsPipelines(mDeviceInfo->device(), VK_NULL_HANDLE, 1, &pipelineCreateInfo, |
| nullptr, &mPipeline)); |
| |
| vkDestroyShaderModule(mDeviceInfo->device(), mVertexShader, nullptr); |
| vkDestroyShaderModule(mDeviceInfo->device(), mFragmentShader, nullptr); |
| mVertexShader = VK_NULL_HANDLE; |
| mFragmentShader = VK_NULL_HANDLE; |
| |
| return VK_TEST_SUCCESS; |
| } |
| |
| VkTestResult Renderer::init(JNIEnv* env, jobject jAssetManager) { |
| mAssetManager = AAssetManager_fromJava(env, jAssetManager); |
| ASSERT(mAssetManager); |
| |
| ASSERT(!createRenderPass()); |
| |
| ASSERT(!createFrameBuffers()); |
| |
| ASSERT(!createVertexBuffers()); |
| |
| ASSERT(!createGraphicsPipeline()); |
| |
| const VkCommandPoolCreateInfo commandPoolCreateInfo = { |
| .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, |
| .queueFamilyIndex = mDeviceInfo->queueFamilyIndex(), |
| }; |
| VK_CALL(vkCreateCommandPool(mDeviceInfo->device(), &commandPoolCreateInfo, nullptr, |
| &mCommandPool)); |
| |
| uint32_t swapchainLength = mSwapchainInfo->swapchainLength(); |
| mCommandBuffers.resize(swapchainLength, VK_NULL_HANDLE); |
| const VkCommandBufferAllocateInfo commandBufferAllocateInfo = { |
| .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, |
| .pNext = nullptr, |
| .commandPool = mCommandPool, |
| .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, |
| .commandBufferCount = swapchainLength, |
| }; |
| VK_CALL(vkAllocateCommandBuffers(mDeviceInfo->device(), &commandBufferAllocateInfo, |
| mCommandBuffers.data())); |
| |
| for (uint32_t i = 0; i < swapchainLength; ++i) { |
| const VkCommandBufferBeginInfo commandBufferBeginInfo = { |
| .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, |
| .pNext = nullptr, |
| .flags = 0, |
| .pInheritanceInfo = nullptr, |
| }; |
| VK_CALL(vkBeginCommandBuffer(mCommandBuffers[i], &commandBufferBeginInfo)); |
| |
| const VkClearValue clearVals = { |
| .color.float32[0] = 0.0f, |
| .color.float32[1] = 0.0f, |
| .color.float32[2] = 0.0f, |
| .color.float32[3] = 1.0f, |
| }; |
| const VkRenderPassBeginInfo renderPassBeginInfo = { |
| .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, |
| .pNext = nullptr, |
| .renderPass = mRenderPass, |
| .framebuffer = mFramebuffers[i], |
| .renderArea = |
| { |
| .offset = |
| { |
| .x = 0, |
| .y = 0, |
| }, |
| .extent = mSwapchainInfo->imageSize(), |
| }, |
| .clearValueCount = 1, |
| .pClearValues = &clearVals, |
| }; |
| vkCmdBeginRenderPass(mCommandBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); |
| |
| vkCmdBindPipeline(mCommandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, mPipeline); |
| |
| VkDeviceSize offset = 0; |
| vkCmdBindVertexBuffers(mCommandBuffers[i], 0, 1, &mVertexBuffer, &offset); |
| |
| vkCmdPushConstants(mCommandBuffers[i], mPipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, |
| 3 * sizeof(float), &fragData[0]); |
| vkCmdDraw(mCommandBuffers[i], 4, 1, 0, 0); |
| |
| vkCmdPushConstants(mCommandBuffers[i], mPipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, |
| 3 * sizeof(float), &fragData[3]); |
| vkCmdDraw(mCommandBuffers[i], 4, 1, 2, 0); |
| |
| vkCmdPushConstants(mCommandBuffers[i], mPipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, |
| 3 * sizeof(float), &fragData[6]); |
| vkCmdDraw(mCommandBuffers[i], 4, 1, 6, 0); |
| |
| vkCmdPushConstants(mCommandBuffers[i], mPipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, |
| 3 * sizeof(float), &fragData[9]); |
| vkCmdDraw(mCommandBuffers[i], 4, 1, 8, 0); |
| |
| vkCmdEndRenderPass(mCommandBuffers[i]); |
| |
| VK_CALL(vkEndCommandBuffer(mCommandBuffers[i])); |
| } |
| |
| const VkFenceCreateInfo fenceCreateInfo = { |
| .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = 0, |
| }; |
| VK_CALL(vkCreateFence(mDeviceInfo->device(), &fenceCreateInfo, nullptr, &mFence)); |
| |
| const VkSemaphoreCreateInfo semaphoreCreateInfo = { |
| .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = 0, |
| }; |
| VK_CALL(vkCreateSemaphore(mDeviceInfo->device(), &semaphoreCreateInfo, nullptr, &mSemaphore)); |
| |
| return VK_TEST_SUCCESS; |
| } |
| |
| VkTestResult Renderer::drawFrame() { |
| uint32_t nextIndex; |
| VK_CALL(vkAcquireNextImageKHR(mDeviceInfo->device(), mSwapchainInfo->swapchain(), UINT64_MAX, |
| mSemaphore, VK_NULL_HANDLE, &nextIndex)); |
| |
| VK_CALL(vkResetFences(mDeviceInfo->device(), 1, &mFence)); |
| |
| VkPipelineStageFlags waitStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; |
| const VkSubmitInfo submitInfo = { |
| .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, |
| .pNext = nullptr, |
| .waitSemaphoreCount = 1, |
| .pWaitSemaphores = &mSemaphore, |
| .pWaitDstStageMask = &waitStageMask, |
| .commandBufferCount = 1, |
| .pCommandBuffers = &mCommandBuffers[nextIndex], |
| .signalSemaphoreCount = 0, |
| .pSignalSemaphores = nullptr, |
| }; |
| VK_CALL(vkQueueSubmit(mDeviceInfo->queue(), 1, &submitInfo, mFence)) |
| |
| VK_CALL(vkWaitForFences(mDeviceInfo->device(), 1, &mFence, VK_TRUE, TIMEOUT_30_SEC)); |
| |
| const VkSwapchainKHR swapchain = mSwapchainInfo->swapchain(); |
| const VkPresentInfoKHR presentInfo = { |
| .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, |
| .pNext = nullptr, |
| .waitSemaphoreCount = 0, |
| .pWaitSemaphores = nullptr, |
| .swapchainCount = 1, |
| .pSwapchains = &swapchain, |
| .pImageIndices = &nextIndex, |
| .pResults = nullptr, |
| }; |
| VkResult ret = vkQueuePresentKHR(mDeviceInfo->queue(), &presentInfo); |
| if (ret == VK_SUBOPTIMAL_KHR) { |
| return VK_TEST_SUCCESS_SUBOPTIMAL; |
| } |
| |
| return ret == VK_SUCCESS ? VK_TEST_SUCCESS : VK_TEST_ERROR; |
| } |