From 610296cc045b094509a6bd2f420ebdef1f98edf9 Mon Sep 17 00:00:00 2001 From: Jaebaek Seo Date: Thu, 10 Jan 2019 15:47:32 -0500 Subject: Vulkan: enable validation layer (#184) Fixes #153 --- src/amber.cc | 5 +- src/vulkan/CMakeLists.txt | 1 + src/vulkan/buffer.cc | 20 +++-- src/vulkan/buffer_descriptor.cc | 10 ++- src/vulkan/command.cc | 10 ++- src/vulkan/device.cc | 161 +++++++++++++++++++++++++++++++++++++--- src/vulkan/device.h | 2 + src/vulkan/engine_vulkan.cc | 17 ++++- src/vulkan/frame_buffer.cc | 5 +- src/vulkan/graphics_pipeline.cc | 4 +- src/vulkan/image.cc | 13 ++-- src/vulkan/log.cc | 44 +++++++++++ src/vulkan/log.h | 29 ++++++++ src/vulkan/pipeline.cc | 24 ++++-- src/vulkan/resource.cc | 10 ++- 15 files changed, 304 insertions(+), 51 deletions(-) create mode 100644 src/vulkan/log.cc create mode 100644 src/vulkan/log.h (limited to 'src') diff --git a/src/amber.cc b/src/amber.cc index 65ef22d..021b4ef 100644 --- a/src/amber.cc +++ b/src/amber.cc @@ -81,8 +81,11 @@ amber::Result Amber::ExecuteWithShaderData(const amber::Recipe* recipe, Executor executor; r = executor.Execute(engine.get(), script, shader_data); - if (!r.IsSuccess()) + if (!r.IsSuccess()) { + // Clean up Vulkan/Dawn objects + engine->Shutdown(); return r; + } return engine->Shutdown(); } diff --git a/src/vulkan/CMakeLists.txt b/src/vulkan/CMakeLists.txt index 46ec379..aa8bc74 100644 --- a/src/vulkan/CMakeLists.txt +++ b/src/vulkan/CMakeLists.txt @@ -23,6 +23,7 @@ set(VULKAN_ENGINE_SOURCES engine_vulkan.cc format_data.cc frame_buffer.cc + log.cc resource.cc image.cc pipeline.cc diff --git a/src/vulkan/buffer.cc b/src/vulkan/buffer.cc index c6ce26e..8276c09 100644 --- a/src/vulkan/buffer.cc +++ b/src/vulkan/buffer.cc @@ -99,20 +99,18 @@ void Buffer::CopyFromBuffer(VkCommandBuffer command, const Buffer& src) { } void Buffer::Shutdown() { - // TODO(jaebaek): Doublecheck what happens if |view_| is VK_NULL_HANDLE on - // Android and Windows. - if (view_ != VK_NULL_HANDLE) { + if (view_ != VK_NULL_HANDLE) vkDestroyBufferView(GetDevice(), view_, nullptr); - view_ = VK_NULL_HANDLE; - } - if (is_buffer_host_accessible_) - UnMapMemory(memory_); + if (memory_ != VK_NULL_HANDLE) { + if (is_buffer_host_accessible_) + UnMapMemory(memory_); + + vkFreeMemory(GetDevice(), memory_, nullptr); + } - vkDestroyBuffer(GetDevice(), buffer_, nullptr); - vkFreeMemory(GetDevice(), memory_, nullptr); - buffer_ = VK_NULL_HANDLE; - memory_ = VK_NULL_HANDLE; + if (buffer_ != VK_NULL_HANDLE) + vkDestroyBuffer(GetDevice(), buffer_, nullptr); if (!is_buffer_host_accessible_) Resource::Shutdown(); diff --git a/src/vulkan/buffer_descriptor.cc b/src/vulkan/buffer_descriptor.cc index baf736c..b4d11c6 100644 --- a/src/vulkan/buffer_descriptor.cc +++ b/src/vulkan/buffer_descriptor.cc @@ -173,9 +173,13 @@ ResourceInfo BufferDescriptor::GetResourceInfo() { } void BufferDescriptor::Shutdown() { - buffer_->Shutdown(); - for (auto& buffer : not_destroyed_buffers_) - buffer->Shutdown(); + if (buffer_) + buffer_->Shutdown(); + + for (auto& buffer : not_destroyed_buffers_) { + if (buffer) + buffer->Shutdown(); + } } } // namespace vulkan diff --git a/src/vulkan/command.cc b/src/vulkan/command.cc index 24abe12..02159d8 100644 --- a/src/vulkan/command.cc +++ b/src/vulkan/command.cc @@ -39,7 +39,8 @@ Result CommandPool::Initialize(uint32_t queue_family_index) { } void CommandPool::Shutdown() { - vkDestroyCommandPool(device_, pool_, nullptr); + if (pool_ != VK_NULL_HANDLE) + vkDestroyCommandPool(device_, pool_, nullptr); } CommandBuffer::CommandBuffer(VkDevice device, VkCommandPool pool, VkQueue queue) @@ -123,8 +124,11 @@ Result CommandBuffer::SubmitAndReset(uint32_t timeout_ms) { } void CommandBuffer::Shutdown() { - vkDestroyFence(device_, fence_, nullptr); - vkFreeCommandBuffers(device_, pool_, 1, &command_); + if (fence_ != VK_NULL_HANDLE) + vkDestroyFence(device_, fence_, nullptr); + + if (command_ != VK_NULL_HANDLE) + vkFreeCommandBuffers(device_, pool_, 1, &command_); } } // namespace vulkan diff --git a/src/vulkan/device.cc b/src/vulkan/device.cc index e211e4e..c83e9a1 100644 --- a/src/vulkan/device.cc +++ b/src/vulkan/device.cc @@ -14,16 +14,61 @@ #include "src/vulkan/device.h" +#include +#include #include #include #include #include "src/make_unique.h" +#include "src/vulkan/log.h" namespace amber { namespace vulkan { namespace { +const char* const kRequiredValidationLayers[] = { +#ifdef __ANDROID__ + // Note that the order of enabled layers is important. It is + // based on Android NDK Vulkan document. + "VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation", + "VK_LAYER_LUNARG_object_tracker", "VK_LAYER_LUNARG_core_validation", + "VK_LAYER_GOOGLE_unique_objects", +#else // __ANDROID__ + "VK_LAYER_LUNARG_standard_validation", +#endif // __ANDROID__ +}; + +const size_t kNumberOfRequiredValidationLayers = + sizeof(kRequiredValidationLayers) / sizeof(const char*); + +const char* kExtensionForValidationLayer = "VK_EXT_debug_report"; + +VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugReportFlagsEXT flag, + VkDebugReportObjectTypeEXT, + uint64_t, + size_t, + int32_t, + const char* layerPrefix, + const char* msg, + void*) { + std::string flag_message; + switch (flag) { + case VK_DEBUG_REPORT_ERROR_BIT_EXT: + flag_message = "[ERROR]"; + break; + case VK_DEBUG_REPORT_WARNING_BIT_EXT: + flag_message = "[WARNING]"; + break; + default: + flag_message = "[UNKNOWN]"; + break; + } + + LogError(flag_message + " validation layer (" + layerPrefix + "):\n" + msg); + return VK_FALSE; +} + VkPhysicalDeviceFeatures RequestedFeatures( const std::vector& required_features) { VkPhysicalDeviceFeatures requested_features = {}; @@ -491,6 +536,61 @@ bool AreAllExtensionsSupported( return required_extension_set.empty(); } +Result AreAllValidationLayersSupported() { + std::vector available_layer_properties; + uint32_t available_layer_count = 0; + if (vkEnumerateInstanceLayerProperties(&available_layer_count, nullptr) != + VK_SUCCESS) { + return Result("Vulkan: vkEnumerateInstanceLayerProperties fail"); + } + available_layer_properties.resize(available_layer_count); + if (vkEnumerateInstanceLayerProperties(&available_layer_count, + available_layer_properties.data()) != + VK_SUCCESS) { + return Result("Vulkan: vkEnumerateInstanceLayerProperties fail"); + } + + std::set required_layer_set( + kRequiredValidationLayers, + &kRequiredValidationLayers[kNumberOfRequiredValidationLayers]); + for (const auto& property : available_layer_properties) { + required_layer_set.erase(property.layerName); + } + + if (required_layer_set.empty()) + return {}; + + std::string missing_layers; + for (const auto& missing_layer : required_layer_set) + missing_layers = missing_layers + missing_layer + ",\n\t\t"; + return Result("Vulkan: missing validation layers:\n\t\t" + missing_layers); +} + +bool AreAllValidationExtensionsSupported() { + for (const auto& layer : kRequiredValidationLayers) { + uint32_t available_extension_count = 0; + std::vector extension_properties; + + if (vkEnumerateInstanceExtensionProperties( + layer, &available_extension_count, nullptr) != VK_SUCCESS) { + return false; + } + extension_properties.resize(available_extension_count); + if (vkEnumerateInstanceExtensionProperties( + layer, &available_extension_count, extension_properties.data()) != + VK_SUCCESS) { + return false; + } + + for (const auto& ext : extension_properties) { + if (!strcmp(kExtensionForValidationLayer, ext.extensionName)) + return true; + } + } + + return false; +} + } // namespace Device::Device() = default; @@ -512,6 +612,14 @@ Device::~Device() = default; void Device::Shutdown() { if (destroy_device_) { vkDestroyDevice(device_, nullptr); + + auto vkDestroyDebugReportCallbackEXT = + reinterpret_cast( + vkGetInstanceProcAddr(instance_, + "vkDestroyDebugReportCallbackEXT")); + assert(vkDestroyDebugReportCallbackEXT); + vkDestroyDebugReportCallbackEXT(instance_, callback_, nullptr); + vkDestroyInstance(instance_, nullptr); } } @@ -523,6 +631,10 @@ Result Device::Initialize(const std::vector& required_features, if (!r.IsSuccess()) return r; + r = CreateDebugReportCallback(); + if (!r.IsSuccess()) + return r; + r = ChoosePhysicalDevice(required_features, required_extensions); if (!r.IsSuccess()) return r; @@ -577,21 +689,50 @@ bool Device::ChooseQueueFamilyIndex(const VkPhysicalDevice& physical_device) { } Result Device::CreateInstance() { - VkApplicationInfo appInfo = {}; - appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - appInfo.apiVersion = VK_MAKE_VERSION(1, 0, 0); - - VkInstanceCreateInfo instInfo = {}; - instInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - instInfo.pApplicationInfo = &appInfo; - // TODO(jaebaek): Enable layers, extensions - - if (vkCreateInstance(&instInfo, nullptr, &instance_) != VK_SUCCESS) + VkApplicationInfo app_info = {}; + app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app_info.apiVersion = VK_MAKE_VERSION(1, 0, 0); + + Result r = AreAllValidationLayersSupported(); + if (!r.IsSuccess()) + return r; + + if (!AreAllValidationExtensionsSupported()) + return Result("Vulkan: extensions of validation layers are not supported"); + + VkInstanceCreateInfo instance_info = {}; + instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instance_info.pApplicationInfo = &app_info; + instance_info.enabledLayerCount = kNumberOfRequiredValidationLayers; + instance_info.ppEnabledLayerNames = kRequiredValidationLayers; + instance_info.enabledExtensionCount = 1U; + instance_info.ppEnabledExtensionNames = &kExtensionForValidationLayer; + + if (vkCreateInstance(&instance_info, nullptr, &instance_) != VK_SUCCESS) return Result("Vulkan::Calling vkCreateInstance Fail"); return {}; } +Result Device::CreateDebugReportCallback() { + VkDebugReportCallbackCreateInfoEXT info = {}; + info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; + info.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; + info.pfnCallback = debugCallback; + + auto vkCreateDebugReportCallbackEXT = + reinterpret_cast( + vkGetInstanceProcAddr(instance_, "vkCreateDebugReportCallbackEXT")); + if (!vkCreateDebugReportCallbackEXT) + return Result("Vulkan: vkCreateDebugReportCallbackEXT is nullptr"); + + if (vkCreateDebugReportCallbackEXT(instance_, &info, nullptr, &callback_) != + VK_SUCCESS) { + return Result("Vulkan: vkCreateDebugReportCallbackEXT fail"); + } + return {}; +} + Result Device::ChoosePhysicalDevice( const std::vector& required_features, const std::vector& required_extensions) { diff --git a/src/vulkan/device.h b/src/vulkan/device.h index df161da..5afc550 100644 --- a/src/vulkan/device.h +++ b/src/vulkan/device.h @@ -54,6 +54,7 @@ class Device { private: Result CreateInstance(); + Result CreateDebugReportCallback(); // Get a physical device by checking if the physical device has a proper // queue family, required features, and required extensions. Note that @@ -77,6 +78,7 @@ class Device { const std::vector& required_extensions); VkInstance instance_ = VK_NULL_HANDLE; + VkDebugReportCallbackEXT callback_ = VK_NULL_HANDLE; VkPhysicalDevice physical_device_ = VK_NULL_HANDLE; VkPhysicalDeviceMemoryProperties physical_memory_properties_ = {}; VkPhysicalDeviceFeatures available_physical_device_features_ = {}; diff --git a/src/vulkan/engine_vulkan.cc b/src/vulkan/engine_vulkan.cc index 0dfe51c..1533abb 100644 --- a/src/vulkan/engine_vulkan.cc +++ b/src/vulkan/engine_vulkan.cc @@ -144,15 +144,24 @@ Result EngineVulkan::InitializeWithConfig( } Result EngineVulkan::Shutdown() { - for (auto it = modules_.begin(); it != modules_.end(); ++it) - vkDestroyShaderModule(device_->GetDevice(), it->second, nullptr); + if (!device_) + return {}; + + for (auto it = modules_.begin(); it != modules_.end(); ++it) { + auto vk_device = device_->GetDevice(); + if (vk_device != VK_NULL_HANDLE && it->second != VK_NULL_HANDLE) + vkDestroyShaderModule(vk_device, it->second, nullptr); + } - pipeline_->Shutdown(); + if (pipeline_) + pipeline_->Shutdown(); if (vertex_buffer_) vertex_buffer_->Shutdown(); - pool_->Shutdown(); + if (pool_) + pool_->Shutdown(); + device_->Shutdown(); return {}; } diff --git a/src/vulkan/frame_buffer.cc b/src/vulkan/frame_buffer.cc index 2cc4a1a..e05a1a5 100644 --- a/src/vulkan/frame_buffer.cc +++ b/src/vulkan/frame_buffer.cc @@ -133,9 +133,12 @@ Result FrameBuffer::ChangeFrameImageLayout(VkCommandBuffer command, } void FrameBuffer::Shutdown() { - vkDestroyFramebuffer(device_, frame_, nullptr); + if (frame_ != VK_NULL_HANDLE) + vkDestroyFramebuffer(device_, frame_, nullptr); + if (color_image_) color_image_->Shutdown(); + if (depth_image_) depth_image_->Shutdown(); } diff --git a/src/vulkan/graphics_pipeline.cc b/src/vulkan/graphics_pipeline.cc index 2b9a9e6..fad10d8 100644 --- a/src/vulkan/graphics_pipeline.cc +++ b/src/vulkan/graphics_pipeline.cc @@ -597,7 +597,9 @@ void GraphicsPipeline::Shutdown() { Pipeline::Shutdown(); frame_->Shutdown(); - vkDestroyRenderPass(device_, render_pass_, nullptr); + + if (render_pass_ != VK_NULL_HANDLE) + vkDestroyRenderPass(device_, render_pass_, nullptr); } } // namespace vulkan diff --git a/src/vulkan/image.cc b/src/vulkan/image.cc index 7b64daf..256be35 100644 --- a/src/vulkan/image.cc +++ b/src/vulkan/image.cc @@ -112,13 +112,14 @@ Result Image::CreateVkImageView() { } void Image::Shutdown() { - vkDestroyImageView(GetDevice(), view_, nullptr); - vkDestroyImage(GetDevice(), image_, nullptr); - vkFreeMemory(GetDevice(), memory_, nullptr); + if (view_ != VK_NULL_HANDLE) + vkDestroyImageView(GetDevice(), view_, nullptr); - view_ = VK_NULL_HANDLE; - image_ = VK_NULL_HANDLE; - memory_ = VK_NULL_HANDLE; + if (image_ != VK_NULL_HANDLE) + vkDestroyImage(GetDevice(), image_, nullptr); + + if (memory_ != VK_NULL_HANDLE) + vkFreeMemory(GetDevice(), memory_, nullptr); Resource::Shutdown(); } diff --git a/src/vulkan/log.cc b/src/vulkan/log.cc new file mode 100644 index 0000000..32ae55e --- /dev/null +++ b/src/vulkan/log.cc @@ -0,0 +1,44 @@ +// Copyright 2018 The Amber Authors. +// +// 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. + +#include "src/vulkan/log.h" + +#include + +#ifdef __ANDROID__ +#include +#else // __ANDROID__ +#include +#endif // __ANDROID__ + +namespace amber { +namespace vulkan { +namespace { + +#ifdef __ANDROID__ +const char* kTAG = "Amber"; +#endif // __ANDROID__ + +} // namespace + +void LogError(const std::string& msg) { +#ifdef __ANDROID__ + ((void)__android_log_print(ANDROID_LOG_ERROR, kTAG, "%s", msg.c_str())); +#else // __ANDROID__ + std::cerr << msg << std::endl << std::flush; +#endif // __ANDROID__ +} + +} // namespace vulkan +} // namespace amber diff --git a/src/vulkan/log.h b/src/vulkan/log.h new file mode 100644 index 0000000..c624590 --- /dev/null +++ b/src/vulkan/log.h @@ -0,0 +1,29 @@ +// Copyright 2018 The Amber Authors. +// +// 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. + +#ifndef SRC_VULKAN_LOG_H_ +#define SRC_VULKAN_LOG_H_ + +#include + +namespace amber { +namespace vulkan { + +// This method is used for debug reports from Vulkan validation layers. +void LogError(const std::string& msg); + +} // namespace vulkan +} // namespace amber + +#endif // SRC_VULKAN_LOG_H_ diff --git a/src/vulkan/pipeline.cc b/src/vulkan/pipeline.cc index c5edbc5..1e8f917 100644 --- a/src/vulkan/pipeline.cc +++ b/src/vulkan/pipeline.cc @@ -66,24 +66,32 @@ Result Pipeline::InitializeCommandBuffer(VkCommandPool pool, VkQueue queue) { } void Pipeline::Shutdown() { - Result r = command_->End(); - if (r.IsSuccess()) - command_->SubmitAndReset(fence_timeout_ms_); - command_->Shutdown(); + if (command_) { + Result r = command_->End(); + if (r.IsSuccess()) + command_->SubmitAndReset(fence_timeout_ms_); + + command_->Shutdown(); + } DestroyVkDescriptorRelatedObjects(); } void Pipeline::DestroyVkDescriptorRelatedObjects() { for (auto& info : descriptor_set_info_) { - vkDestroyDescriptorSetLayout(device_, info.layout, nullptr); + if (info.layout != VK_NULL_HANDLE) + vkDestroyDescriptorSetLayout(device_, info.layout, nullptr); + if (info.empty) continue; - vkDestroyDescriptorPool(device_, info.pool, nullptr); + if (info.pool != VK_NULL_HANDLE) + vkDestroyDescriptorPool(device_, info.pool, nullptr); - for (auto& desc : info.descriptors_) - desc->Shutdown(); + for (auto& desc : info.descriptors_) { + if (desc) + desc->Shutdown(); + } } if (pipeline_layout_ != VK_NULL_HANDLE) diff --git a/src/vulkan/resource.cc b/src/vulkan/resource.cc index be9bd0f..92a1462 100644 --- a/src/vulkan/resource.cc +++ b/src/vulkan/resource.cc @@ -56,9 +56,13 @@ Resource::Resource(VkDevice device, Resource::~Resource() = default; void Resource::Shutdown() { - UnMapMemory(host_accessible_memory_); - vkDestroyBuffer(device_, host_accessible_buffer_, nullptr); - vkFreeMemory(device_, host_accessible_memory_, nullptr); + if (host_accessible_memory_ != VK_NULL_HANDLE) { + UnMapMemory(host_accessible_memory_); + vkFreeMemory(device_, host_accessible_memory_, nullptr); + } + + if (host_accessible_buffer_ != VK_NULL_HANDLE) + vkDestroyBuffer(device_, host_accessible_buffer_, nullptr); } Result Resource::Initialize() { -- cgit v1.2.3