diff options
-rw-r--r-- | samples/CMakeLists.txt | 1 | ||||
-rw-r--r-- | samples/config_helper_vulkan.cc | 146 | ||||
-rw-r--r-- | samples/config_helper_vulkan.h | 5 | ||||
-rw-r--r-- | samples/log.cc | 42 | ||||
-rw-r--r-- | samples/log.h | 27 | ||||
-rw-r--r-- | src/amber.cc | 5 | ||||
-rw-r--r-- | src/vulkan/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/vulkan/buffer.cc | 20 | ||||
-rw-r--r-- | src/vulkan/buffer_descriptor.cc | 10 | ||||
-rw-r--r-- | src/vulkan/command.cc | 10 | ||||
-rw-r--r-- | src/vulkan/device.cc | 161 | ||||
-rw-r--r-- | src/vulkan/device.h | 2 | ||||
-rw-r--r-- | src/vulkan/engine_vulkan.cc | 17 | ||||
-rw-r--r-- | src/vulkan/frame_buffer.cc | 5 | ||||
-rw-r--r-- | src/vulkan/graphics_pipeline.cc | 4 | ||||
-rw-r--r-- | src/vulkan/image.cc | 13 | ||||
-rw-r--r-- | src/vulkan/log.cc | 44 | ||||
-rw-r--r-- | src/vulkan/log.h | 29 | ||||
-rw-r--r-- | src/vulkan/pipeline.cc | 24 | ||||
-rw-r--r-- | src/vulkan/resource.cc | 10 | ||||
-rw-r--r-- | tests/cases/compute_in_spirv_generated_by_clspv.amber | 164 |
21 files changed, 524 insertions, 216 deletions
diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 73a617a..58243d7 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -17,6 +17,7 @@ include_directories("${PROJECT_SOURCE_DIR}/include") set(AMBER_SOURCES amber.cc config_helper.cc + log.cc ${CMAKE_BINARY_DIR}/src/build-versions.h.fake ) diff --git a/samples/config_helper_vulkan.cc b/samples/config_helper_vulkan.cc index 625d665..14d91db 100644 --- a/samples/config_helper_vulkan.cc +++ b/samples/config_helper_vulkan.cc @@ -16,11 +16,56 @@ #include <algorithm> #include <cassert> +#include <cstring> #include <set> +#include "samples/log.h" + namespace sample { 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; +} + // Convert required features given as a string array to // VkPhysicalDeviceFeatures. VkPhysicalDeviceFeatures NamesToVulkanFeatures( @@ -255,6 +300,62 @@ VkPhysicalDeviceFeatures NamesToVulkanFeatures( return required_vulkan_features; } +bool AreAllValidationLayersSupported() { + std::vector<VkLayerProperties> available_layer_properties; + uint32_t available_layer_count = 0; + if (vkEnumerateInstanceLayerProperties(&available_layer_count, nullptr) != + VK_SUCCESS) { + return false; + } + available_layer_properties.resize(available_layer_count); + if (vkEnumerateInstanceLayerProperties(&available_layer_count, + available_layer_properties.data()) != + VK_SUCCESS) { + return false; + } + + std::set<std::string> 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 true; + + std::string missing_layers; + for (const auto& missing_layer : required_layer_set) + missing_layers = missing_layers + missing_layer + ",\n\t\t"; + LogError("Vulkan: missing validation layers:\n\t\t" + missing_layers); + return true; +} + +bool AreAllValidationExtensionsSupported() { + for (const auto& layer : kRequiredValidationLayers) { + uint32_t available_extension_count = 0; + std::vector<VkExtensionProperties> 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; +} + // Check if |physical_device| supports all required features given // in |required_features|. bool AreAllRequiredFeaturesSupported( @@ -561,14 +662,46 @@ void ConfigHelperVulkan::CreateVulkanInstance() { app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; app_info.apiVersion = VK_MAKE_VERSION(1, 0, 0); + if (!AreAllValidationLayersSupported()) { + assert(false && "Sample: not all validation layers are supported"); + } + + if (!AreAllValidationExtensionsSupported()) { + assert(false && + "Sample: 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; assert(vkCreateInstance(&instance_info, nullptr, &vulkan_instance_) == VK_SUCCESS); } +void ConfigHelperVulkan::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<PFN_vkCreateDebugReportCallbackEXT>( + vkGetInstanceProcAddr(vulkan_instance_, + "vkCreateDebugReportCallbackEXT")); + if (!vkCreateDebugReportCallbackEXT) + assert(false && "Sample: vkCreateDebugReportCallbackEXT is nullptr"); + + if (vkCreateDebugReportCallbackEXT(vulkan_instance_, &info, nullptr, + &vulkan_callback_) != VK_SUCCESS) { + assert(false && "Sample: vkCreateDebugReportCallbackEXT fail"); + } +} + void ConfigHelperVulkan::ChooseVulkanPhysicalDevice( const VkPhysicalDeviceFeatures& required_features, const std::vector<std::string>& required_extensions) { @@ -607,7 +740,7 @@ void ConfigHelperVulkan::ChooseVulkanPhysicalDevice( void ConfigHelperVulkan::CreateVulkanDevice( const VkPhysicalDeviceFeatures& required_features, const std::vector<std::string>& required_extensions) { - VkDeviceQueueCreateInfo queue_info; + VkDeviceQueueCreateInfo queue_info = {}; const float priorities[] = {1.0f}; queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; @@ -640,6 +773,7 @@ std::unique_ptr<amber::EngineConfig> ConfigHelperVulkan::CreateConfig( auto required_vulkan_features = NamesToVulkanFeatures(required_features); CreateVulkanInstance(); + CreateDebugReportCallback(); ChooseVulkanPhysicalDevice(required_vulkan_features, required_extensions); CreateVulkanDevice(required_vulkan_features, required_extensions); vkGetDeviceQueue(vulkan_device_, vulkan_queue_family_index_, 0, @@ -662,6 +796,16 @@ void ConfigHelperVulkan::Shutdown() { if (vulkan_device_ != VK_NULL_HANDLE) vkDestroyDevice(vulkan_device_, nullptr); + auto vkDestroyDebugReportCallbackEXT = + reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>( + vkGetInstanceProcAddr(vulkan_instance_, + "vkDestroyDebugReportCallbackEXT")); + assert(vkDestroyDebugReportCallbackEXT && + "Sample: vkDestroyDebugReportCallbackEXT is nullptr"); + if (vulkan_callback_ != VK_NULL_HANDLE) + vkDestroyDebugReportCallbackEXT(vulkan_instance_, vulkan_callback_, + nullptr); + if (vulkan_instance_ != VK_NULL_HANDLE) vkDestroyInstance(vulkan_instance_, nullptr); } diff --git a/samples/config_helper_vulkan.h b/samples/config_helper_vulkan.h index 61c4c40..a6ad9de 100644 --- a/samples/config_helper_vulkan.h +++ b/samples/config_helper_vulkan.h @@ -47,6 +47,10 @@ class ConfigHelperVulkan : public ConfigHelperImpl { // Create Vulkan instance. void CreateVulkanInstance(); + // Create |vulkan_callback_| that reports validation layer errors + // via debugCallback() function in config_helper_vulkan.cc. + void CreateDebugReportCallback(); + // Choose Vulkan physical device that supports both // |required_features| and |required_extensions|. void ChooseVulkanPhysicalDevice( @@ -59,6 +63,7 @@ class ConfigHelperVulkan : public ConfigHelperImpl { const std::vector<std::string>& required_extensions); VkInstance vulkan_instance_ = VK_NULL_HANDLE; + VkDebugReportCallbackEXT vulkan_callback_ = VK_NULL_HANDLE; VkPhysicalDevice vulkan_physical_device_ = VK_NULL_HANDLE; VkPhysicalDeviceFeatures available_features_ = {}; std::vector<std::string> available_extensions_; diff --git a/samples/log.cc b/samples/log.cc new file mode 100644 index 0000000..66d365d --- /dev/null +++ b/samples/log.cc @@ -0,0 +1,42 @@ +// Copyright 2019 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 "samples/log.h" + +#include <string> + +#ifdef __ANDROID__ +#include <android/log.h> +#else // __ANDROID__ +#include <iostream> +#endif // __ANDROID__ + +namespace sample { +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 sample diff --git a/samples/log.h b/samples/log.h new file mode 100644 index 0000000..ff9b316 --- /dev/null +++ b/samples/log.h @@ -0,0 +1,27 @@ +// Copyright 2019 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 SAMPLES_LOG_H_ +#define SAMPLES_LOG_H_ + +#include <string> + +namespace sample { + +// This method is used for debug reports from Vulkan validation layers. +void LogError(const std::string& msg); + +} // namespace sample + +#endif // SAMPLES_LOG_H_ 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 <cassert> +#include <cstring> #include <memory> #include <set> #include <vector> #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<Feature>& required_features) { VkPhysicalDeviceFeatures requested_features = {}; @@ -491,6 +536,61 @@ bool AreAllExtensionsSupported( return required_extension_set.empty(); } +Result AreAllValidationLayersSupported() { + std::vector<VkLayerProperties> 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<std::string> 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<VkExtensionProperties> 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<PFN_vkDestroyDebugReportCallbackEXT>( + vkGetInstanceProcAddr(instance_, + "vkDestroyDebugReportCallbackEXT")); + assert(vkDestroyDebugReportCallbackEXT); + vkDestroyDebugReportCallbackEXT(instance_, callback_, nullptr); + vkDestroyInstance(instance_, nullptr); } } @@ -523,6 +631,10 @@ Result Device::Initialize(const std::vector<Feature>& 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<PFN_vkCreateDebugReportCallbackEXT>( + 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<Feature>& required_features, const std::vector<std::string>& 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<std::string>& 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 <string> + +#ifdef __ANDROID__ +#include <android/log.h> +#else // __ANDROID__ +#include <iostream> +#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 <string> + +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() { diff --git a/tests/cases/compute_in_spirv_generated_by_clspv.amber b/tests/cases/compute_in_spirv_generated_by_clspv.amber deleted file mode 100644 index 82251f2..0000000 --- a/tests/cases/compute_in_spirv_generated_by_clspv.amber +++ /dev/null @@ -1,164 +0,0 @@ -[comment] -Source code in OpenCL C: - -typedef struct { int arr[2]; } S1; -typedef struct { S1 s1; int a; S1 s2; } S2; - -S2 make_s2(int n) { - S2 s2; - s2.s1.arr[0] = n; - s2.s1.arr[1] = n; - s2.a = n; - s2.s2.arr[0] = n; - s2.s2.arr[1] = n; - return s2; -} - -S2 choose(int n) { - if (n > 0) return make_s2(n - 5); - return make_s2(0); -} - -kernel void foo(global S2 *data, int n) { - *data = choose(n); -} - -The following SPIR-V code was generated by CLSPV with -cl-opt-disable option. -GIT_COMMIT = 8638849b2167fcba90dbff0ee4cfe7dbf0380d24 - -[compute shader spirv] -; SPIR-V -; Version: 1.0 -; Generator: Codeplay; 0 -; Bound: 71 -; Schema: 0 - OpCapability Shader - OpCapability VariablePointers - OpExtension "SPV_KHR_storage_buffer_storage_class" - OpExtension "SPV_KHR_variable_pointers" - OpMemoryModel Logical GLSL450 - OpEntryPoint GLCompute %52 "main" - OpSource OpenCL_C 120 - OpDecorate %24 SpecId 0 - OpDecorate %25 SpecId 1 - OpDecorate %26 SpecId 2 - OpMemberDecorate %_struct_4 0 Offset 0 - OpMemberDecorate %_struct_5 0 Offset 0 - OpMemberDecorate %_struct_5 1 Offset 8 - OpMemberDecorate %_struct_5 2 Offset 12 - OpDecorate %_runtimearr__struct_5 ArrayStride 20 - OpMemberDecorate %_struct_7 0 Offset 0 - OpDecorate %_struct_7 Block - OpMemberDecorate %_struct_9 0 Offset 0 - OpDecorate %_struct_9 Block - OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize - OpDecorate %29 DescriptorSet 0 - OpDecorate %29 Binding 0 - OpDecorate %30 DescriptorSet 0 - OpDecorate %30 Binding 1 - OpDecorate %_arr_uint_uint_2 ArrayStride 4 - %uint = OpTypeInt 32 0 - %uint_2 = OpConstant %uint 2 -%_arr_uint_uint_2 = OpTypeArray %uint %uint_2 - %_struct_4 = OpTypeStruct %_arr_uint_uint_2 - %_struct_5 = OpTypeStruct %_struct_4 %uint %_struct_4 -%_runtimearr__struct_5 = OpTypeRuntimeArray %_struct_5 - %_struct_7 = OpTypeStruct %_runtimearr__struct_5 -%_ptr_StorageBuffer__struct_7 = OpTypePointer StorageBuffer %_struct_7 - %_struct_9 = OpTypeStruct %uint -%_ptr_StorageBuffer__struct_9 = OpTypePointer StorageBuffer %_struct_9 - %void = OpTypeVoid - %12 = OpTypeFunction %void -%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint - %14 = OpTypeFunction %_struct_5 %uint - %bool = OpTypeBool - %v3uint = OpTypeVector %uint 3 -%_ptr_Private_v3uint = OpTypePointer Private %v3uint - %uint_0 = OpConstant %uint 0 - %uint_1 = OpConstant %uint 1 - %20 = OpUndef %_struct_5 - %false = OpConstantFalse %bool - %true = OpConstantTrue %bool -%uint_4294967291 = OpConstant %uint 4294967291 - %24 = OpSpecConstant %uint 1 - %25 = OpSpecConstant %uint 1 - %26 = OpSpecConstant %uint 1 -%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %24 %25 %26 - %28 = OpVariable %_ptr_Private_v3uint Private %gl_WorkGroupSize - %29 = OpVariable %_ptr_StorageBuffer__struct_7 StorageBuffer - %30 = OpVariable %_ptr_StorageBuffer__struct_9 StorageBuffer - %31 = OpFunction %_struct_5 Const %14 - %32 = OpFunctionParameter %uint - %33 = OpLabel - %34 = OpCompositeConstruct %_arr_uint_uint_2 %32 %32 - %35 = OpCompositeConstruct %_struct_4 %34 - %36 = OpCompositeConstruct %_struct_5 %35 %32 %35 - OpReturnValue %36 - OpFunctionEnd - %37 = OpFunction %_struct_5 Const %14 - %38 = OpFunctionParameter %uint - %39 = OpLabel - %40 = OpSGreaterThan %bool %38 %uint_0 - %41 = OpLogicalNot %bool %40 - OpSelectionMerge %44 None - OpBranchConditional %41 %42 %44 - %42 = OpLabel - %43 = OpFunctionCall %_struct_5 %31 %uint_0 - OpBranch %44 - %44 = OpLabel - %46 = OpPhi %bool %false %42 %true %39 - %45 = OpPhi %_struct_5 %43 %42 %20 %39 - OpSelectionMerge %47 None - OpBranchConditional %46 %49 %47 - %47 = OpLabel - %48 = OpPhi %_struct_5 %45 %44 %51 %49 - OpReturnValue %48 - %49 = OpLabel - %50 = OpIAdd %uint %38 %uint_4294967291 - %51 = OpFunctionCall %_struct_5 %31 %50 - OpBranch %47 - OpFunctionEnd - %52 = OpFunction %void None %12 - %53 = OpLabel - %54 = OpAccessChain %_ptr_StorageBuffer_uint %30 %uint_0 - %55 = OpLoad %uint %54 - %56 = OpFunctionCall %_struct_5 %37 %55 - %57 = OpCompositeExtract %_struct_4 %56 0 - %58 = OpCompositeExtract %_arr_uint_uint_2 %57 0 - %59 = OpCompositeExtract %uint %58 0 - %60 = OpCompositeExtract %uint %58 1 - %61 = OpCompositeExtract %uint %56 1 - %62 = OpCompositeExtract %_struct_4 %56 2 - %63 = OpCompositeExtract %_arr_uint_uint_2 %62 0 - %64 = OpCompositeExtract %uint %63 0 - %65 = OpCompositeExtract %uint %63 1 - %66 = OpAccessChain %_ptr_StorageBuffer_uint %29 %uint_0 %uint_0 %uint_0 %uint_0 %uint_0 - OpStore %66 %59 - %67 = OpAccessChain %_ptr_StorageBuffer_uint %29 %uint_0 %uint_0 %uint_0 %uint_0 %uint_1 - OpStore %67 %60 - %68 = OpAccessChain %_ptr_StorageBuffer_uint %29 %uint_0 %uint_0 %uint_1 - OpStore %68 %61 - %69 = OpAccessChain %_ptr_StorageBuffer_uint %29 %uint_0 %uint_0 %uint_2 %uint_0 %uint_0 - OpStore %69 %64 - %70 = OpAccessChain %_ptr_StorageBuffer_uint %29 %uint_0 %uint_0 %uint_2 %uint_0 %uint_1 - OpStore %70 %65 - OpReturn - OpFunctionEnd -[require] -VK_KHR_storage_buffer_storage_class -VK_KHR_get_physical_device_properties2 -VK_KHR_variable_pointers - -[test] -# ssbo [bind] subdata [type] [offset] [values ...] -ssbo 0 subdata int 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -ssbo 1 subdata int 0 13 - -compute 3 1 1 - -# probe ssbo [type] [bind] [offset] == [values ...] -probe ssbo int 0 0 == 8 -probe ssbo int 0 4 == 8 -probe ssbo int 0 8 == 8 -probe ssbo int 0 12 == 8 -probe ssbo int 0 16 == 8 |