diff --git a/.github/workflows/win_msvc_dbg_x64_cmake.yml b/.github/workflows/win_msvc_dbg_x64_cmake.yml index a42061317..9cb44e31c 100644 --- a/.github/workflows/win_msvc_dbg_x64_cmake.yml +++ b/.github/workflows/win_msvc_dbg_x64_cmake.yml @@ -28,11 +28,32 @@ jobs: cd vcpkg .\bootstrap-vcpkg.bat + - name: Install depot_tools + shell: cmd + run: | + git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git ..\depot_tools + set "PATH=%CD%\..\depot_tools;%PATH%" + gclient + + - name: Set up Python 3.x + uses: actions/setup-python@v2 + with: + python-version: '3.x' + - uses: actions/checkout@v2 with: path: test fetch-depth: 0 + - name: Sync code for main branch (with patch) + shell: cmd + run: | + set "PATH=%CD%\..\depot_tools;%PATH%" + set "DEPOT_TOOLS_WIN_TOOLCHAIN=0" + cd test + copy scripts\standalone.gclient .gclient + gclient sync + - name: Generate project for main branch (with patch) shell: cmd run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index 17fd6acfb..5b2c0886a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,29 +44,44 @@ set(GPGMM_INCLUDE_DIR "${GPGMM_ROOT_DIR}/src/include") # option(name description default) # If a variable is not already defined with the given name, otherwise the # function does nothing. -# Simplifies customization by projects that use Dawn as a dependency. function (option_if_not_defined name description default) if(NOT DEFINED ${name}) option(${name} ${description} ${default}) endif() endfunction() +# set_if_not_defined(name value description) +# Behaves like: +# set(${name} ${value} CACHE STRING ${description}) +# If a variable is not already defined with the given name, otherwise the +# function does nothing. +function (set_if_not_defined name value description) + if(NOT DEFINED ${name}) + set(${name} ${value} CACHE STRING ${description}) + endif() +endfunction() + # Default values for the backend-enabling options set(ENABLE_D3D12 OFF) -set(ENABLE_VULKAN OFF) +set(ENABLE_VK OFF) if (WIN32) set(ENABLE_D3D12 ON) if (NOT WINDOWS_STORE) # Enable Vulkan in win32 compilation only # since UWP only supports d3d - set(ENABLE_VULKAN ON) + set(ENABLE_VK ON) endif() elseif(UNIX) - set(ENABLE_VULKAN ON) + set(ENABLE_VK ON) endif() option_if_not_defined(GPGMM_ENABLE_D3D12 "Enable compilation of the D3D12 backend" ${ENABLE_D3D12}) -option_if_not_defined(GPGMM_ENABLE_VULKAN "Enable compilation of the Vulkan backend" ${ENABLE_VULKAN}) +option_if_not_defined(GPGMM_ENABLE_VK "Enable compilation of the Vulkan backend" ${ENABLE_VK}) + +set_if_not_defined(GPGMM_THIRD_PARTY_DIR "${GPGMM_SOURCE_DIR}/third_party" "Directory in which to find third-party dependencies.") +set_if_not_defined(GPGMM_VK_DEPS_DIR "${GPGMM_THIRD_PARTY_DIR}/vulkan-deps" "Directory in which to find vulkan-deps") +set_if_not_defined(GPGMM_VK_HEADERS_DIR "${GPGMM_VK_DEPS_DIR}/vulkan-headers/src" "Directory in which to find Vulkan-Headers") +set_if_not_defined(GPGMM_VK_TOOLS_DIR "${GPGMM_VK_DEPS_DIR}/vulkan-tools/src" "Directory in which to find Vulkan-Tools") option_if_not_defined(GPGMM_ALWAYS_ASSERT "Enable assertions on all build types" OFF) @@ -91,8 +106,8 @@ endif() if (GPGMM_ENABLE_D3D12) target_compile_definitions(gpgmm_common_config INTERFACE "GPGMM_ENABLE_D3D12") endif() -if (GPGMM_ENABLE_VULKAN) - target_compile_definitions(gpgmm_common_config INTERFACE "GPGMM_ENABLE_VULKAN") +if (GPGMM_ENABLE_VK) + target_compile_definitions(gpgmm_common_config INTERFACE "GPGMM_ENABLE_VK") endif() if (WIN32) target_compile_definitions(gpgmm_common_config INTERFACE "NOMINMAX" "WIN32_LEAN_AND_MEAN") @@ -103,4 +118,5 @@ set(CMAKE_CXX_STANDARD "14") ################################################################################ # Build subdirectories ################################################################################ +add_subdirectory(third_party) add_subdirectory(src/gpgmm) diff --git a/README.md b/README.md index 1cee07ca5..b3a93f8a2 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ To build with a backend, please set the corresponding argument from following ta | Backend | Build argument | |---------|--------------| | DirectX 12 | `gpgmm_enable_d3d12=true` (default on winos) | -| Vulkan | `gpgmm_enable_vulkan=true` | +| Vulkan | `gpgmm_enable_vk=true` | ### Build diff --git a/build_overrides/gpgmm_features.gni b/build_overrides/gpgmm_features.gni index 9cbb77ddb..0f6bd370f 100644 --- a/build_overrides/gpgmm_features.gni +++ b/build_overrides/gpgmm_features.gni @@ -1,4 +1,4 @@ -# Copyright 2021 The GPMM Authors +# Copyright 2021 The GPGMM Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -37,9 +37,13 @@ declare_args() { # Sets -dGPGMM_FORCE_TRACING gpgmm_force_tracing = false - # Enables the compilation of the D3D12 backend. + # Enables the compilation of the DirectX 12 backend. gpgmm_enable_d3d12 = is_win + # Enables the compilation of the Vulkan backend + gpgmm_enable_vk = is_linux || is_chromeos || (is_win && !gpgmm_is_winuwp) || + is_fuchsia || is_android + # Enables compilation of Dawn's end2end tests. gpgmm_enable_dawn = checkout_dawn @@ -66,3 +70,22 @@ declare_args() { # Sets -dGPGMM_ENABLE_DEVICE_CHECKS gpgmm_enable_device_checks = false } + +# Declare separately since both depend on |gpgmm_enable_vk| and GN does not allow +# args to depend on another args in the same declare_args(). +declare_args() { + # Uses our built version of the Vulkan validation layers + gpgmm_enable_vk_validation_layers = + gpgmm_enable_vk && ((is_linux && !is_chromeos) || is_win || is_mac) + + # Uses our built version of the Vulkan loader on platforms where we can't + # assume to have one present at the system level. + gpgmm_enable_vk_loader = + gpgmm_enable_vk && (is_mac || (is_linux && !is_android)) + + # Configures Vulkan functions by statically importing them (ie. importing the Vulkan loader symbols). + gpgmm_vk_static_functions = true + + # Configures Vulkan functions by dynamically importing them (ie. using vkGetInstanceProcAddr and vkGetDeviceProcAddr). + gpgmm_vk_dynamic_functions = false +} diff --git a/build_overrides/gpgmm_overrides_with_defaults.gni b/build_overrides/gpgmm_overrides_with_defaults.gni index ee23e59a5..6a077f10a 100644 --- a/build_overrides/gpgmm_overrides_with_defaults.gni +++ b/build_overrides/gpgmm_overrides_with_defaults.gni @@ -28,3 +28,29 @@ if (!defined(gpgmm_googletest_dir)) { if (!defined(gpgmm_jsoncpp_dir)) { gpgmm_jsoncpp_dir = "//third_party/jsoncpp" } + +if (!defined(gpgmm_vk_deps_dir)) { + gpgmm_vk_deps_dir = "//third_party/vulkan-deps" + if (gpgmm_standalone) { + gpgmm_vk_deps_dir = "${gpgmm_root_dir}/third_party/vulkan-deps" + } +} + +if (!defined(gpgmm_vk_headers_dir)) { + gpgmm_vk_headers_dir = "${gpgmm_vk_deps_dir}/vulkan-headers/src" +} + +if (!defined(gpgmm_vk_loader_dir)) { + gpgmm_vk_loader_dir = "" + if (gpgmm_standalone) { + gpgmm_vk_loader_dir = "${gpgmm_vk_deps_dir}/vulkan-loader/src" + } +} + +if (!defined(gpgmm_vk_tools_dir)) { + gpgmm_vk_tools_dir = "${gpgmm_vk_deps_dir}/vulkan-tools/src" +} + +if (!defined(gpgmm_vk_validation_layers_dir)) { + gpgmm_vk_validation_layers_dir = "" +} diff --git a/build_overrides/vulkan_loader.gni b/build_overrides/vulkan_loader.gni new file mode 100644 index 000000000..e38e55b0d --- /dev/null +++ b/build_overrides/vulkan_loader.gni @@ -0,0 +1,8 @@ +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build_overrides/vulkan_common.gni") + +# When Vulkan-loader is statically linked, we must use this extension. +vulkan_loader_shared = true diff --git a/build_overrides/vulkan_validation_layers.gni b/build_overrides/vulkan_validation_layers.gni index 3fda96683..2934f4795 100644 --- a/build_overrides/vulkan_validation_layers.gni +++ b/build_overrides/vulkan_validation_layers.gni @@ -4,7 +4,6 @@ import("//build_overrides/vulkan_common.gni") -vulkan_headers_dir = "//third_party/vulkan-deps/vulkan-headers/src" vvl_spirv_tools_dir = "//third_party/vulkan-deps/spirv-tools/src" vvl_glslang_dir = "//third_party/vulkan-deps/glslang/src" diff --git a/scripts/webnn.deps b/scripts/webnn.deps index b53d099c0..dcc7e5439 100644 --- a/scripts/webnn.deps +++ b/scripts/webnn.deps @@ -96,7 +96,7 @@ deps = { 'condition': 'gpgmm_standalone', }, 'third_party/vulkan-deps': { - 'url': '{chromium_git}/vulkan-deps@b2b44c000ee6dd3beaf2a381bbc9a24a270c84a1', + 'url': '{chromium_git}/vulkan-deps@7e9ab0686bf4d4fa9c52eeb8def33b2057624987', 'condition': 'gpgmm_standalone', }, 'third_party/jsoncpp/source': { diff --git a/src/gpgmm/BUILD.gn b/src/gpgmm/BUILD.gn index a05fd062d..ab7925c8e 100644 --- a/src/gpgmm/BUILD.gn +++ b/src/gpgmm/BUILD.gn @@ -30,18 +30,18 @@ if (is_mac) { } # The VVLs are an optional dependency, only use it if the path has been set. -# enable_vulkan_validation_layers = gpgmm_enable_vulkan_validation_layers && -# gpgmm_vulkan_validation_layers_dir != "" -# if (enable_vulkan_validation_layers) { -# import("//build_overrides/vulkan_validation_layers.gni") -# } +enable_vk_validation_layers = + gpgmm_enable_vk_validation_layers && gpgmm_vk_validation_layers_dir != "" +if (enable_vk_validation_layers) { + import("//build_overrides/vulkan_validation_layers.gni") +} # The Vulkan loader is an optional dependency, only use it if the path has been # set. -# if (gpgmm_enable_vulkan) { -# enable_vulkan_loader = -# gpgmm_enable_vulkan_loader && gpgmm_vulkan_loader_dir != "" -# } +if (gpgmm_enable_vk) { + enable_vk_loader = gpgmm_vk_static_functions || + (gpgmm_enable_vk_loader && gpgmm_vk_loader_dir != "") +} config("gpgmm_common_config") { configs = [ "${gpgmm_root_dir}/src/gpgmm/common:gpgmm_common_config" ] @@ -61,10 +61,9 @@ config("gpgmm_common_config") { } } -# Config that adds the @executable_path rpath if needed so that Swiftshader or the Vulkan loader are found. -config("gpgmm_vulkan_rpath") { - if (is_mac && gpgmm_enable_vulkan && - (use_swiftshader || enable_vulkan_loader)) { +# Config that adds the @executable_path rpath if needed so the Vulkan loader are found. +config("vulkan_rpath") { + if (is_mac && gpgmm_enable_vk && enable_vk_loader) { ldflags = [ "-rpath", "@executable_path/", @@ -72,6 +71,10 @@ config("gpgmm_vulkan_rpath") { } } +config("vulkan_tools_include") { + include_dirs = [ "${gpgmm_vk_tools_dir}" ] +} + # Public gpgmm headers so they can be publicly visible for # dependencies of gpgmm source_set("gpgmm_headers") { @@ -116,6 +119,10 @@ source_set("gpgmm_sources") { defines += [ "GPGMM_ENABLE_DEVICE_CHECKS" ] } + if (gpgmm_vk_static_functions) { + defines += [ "GPGMM_STATIC_VULKAN_FUNCTIONS" ] + } + libs = [] data_deps = [] @@ -242,6 +249,42 @@ source_set("gpgmm_sources") { "d3d12/d3d12_platform.h", ] } + + if (gpgmm_enable_vk) { + configs += [ ":vulkan_tools_include" ] + deps += [ "${gpgmm_vk_tools_dir}:vulkan_tools_headers" ] + public_deps = [ "${gpgmm_vk_headers_dir}:vulkan_headers" ] + + sources += [ + "vk/CapsVk.cpp", + "vk/CapsVk.h", + "vk/DeviceMemoryAllocatorVk.cpp", + "vk/DeviceMemoryAllocatorVk.h", + "vk/DeviceMemoryVk.cpp", + "vk/DeviceMemoryVk.h", + "vk/ErrorVk.h", + "vk/FunctionsVk.cpp", + "vk/FunctionsVk.h", + "vk/ResourceAllocatorVk.cpp", + "vk/ResourceAllocatorVk.h", + ] + + if (enable_vk_validation_layers) { + defines += [ + "GPGMM_ENABLE_VK_VALIDATION_LAYERS", + "GPGMM_VK_DATA_DIR=\"$vulkan_data_subdir\"", + ] + } + + if (enable_vk_loader) { + # Loader must be a linked dependency when using importing functions statically. + if (gpgmm_vk_static_functions) { + deps += [ "${gpgmm_vk_loader_dir}:libvulkan" ] + } else { + data_deps += [ "${gpgmm_vk_loader_dir}:libvulkan" ] + } + } + } } # Defines the type of target for GN to build. @@ -258,4 +301,16 @@ if (gpgmm_shared_library) { target(target_type, "gpgmm") { public_deps = [ ":gpgmm_headers" ] deps = [ ":gpgmm_sources" ] + public_configs = [ ":vulkan_rpath" ] + + if (gpgmm_enable_vk) { + if (enable_vk_validation_layers) { + data_deps = + [ "${gpgmm_vk_validation_layers_dir}:vulkan_validation_layers" ] + if (!is_android) { + data_deps += + [ "${gpgmm_vk_validation_layers_dir}:vulkan_gen_json_files" ] + } + } + } } diff --git a/src/gpgmm/CMakeLists.txt b/src/gpgmm/CMakeLists.txt index 155d62dc4..6c48cda3f 100644 --- a/src/gpgmm/CMakeLists.txt +++ b/src/gpgmm/CMakeLists.txt @@ -121,7 +121,26 @@ if (GPGMM_ENABLE_D3D12) target_link_libraries(gpgmm PRIVATE dxguid.lib) endif() -# TODO: GPGMM_ENABLE_VULKAN +if (GPGMM_ENABLE_VK) + target_sources(gpgmm PRIVATE + "vk/CapsVk.cpp" + "vk/CapsVk.h" + "vk/DeviceMemoryAllocatorVk.cpp" + "vk/DeviceMemoryAllocatorVk.h" + "vk/DeviceMemoryVk.cpp" + "vk/DeviceMemoryVk.h" + "vk/ErrorVk.h" + "vk/ResourceAllocatorVk.cpp" + "vk/ResourceAllocatorVk.h" + "vk/FunctionsVk.cpp" + "vk/FunctionsVk.h" + "vk/vk_platform.h" + ) + + target_link_libraries(gpgmm PUBLIC Vulkan-Headers) + target_include_directories(gpgmm PRIVATE ${GPGMM_VK_TOOLS_DIR}) + +endif() ################################################################################ # Build subdirectories diff --git a/src/gpgmm/vk/BackendVk.h b/src/gpgmm/vk/BackendVk.h new file mode 100644 index 000000000..f51f7a588 --- /dev/null +++ b/src/gpgmm/vk/BackendVk.h @@ -0,0 +1,35 @@ +// Copyright 2021 The GPGMM 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 GPGMM_VK_BACKENDVK_H_ +#define GPGMM_VK_BACKENDVK_H_ + +#include "gpgmm/Backend.h" + +namespace gpgmm { namespace vk { + + class DeviceMemory; + + struct BackendTrait { + using MemoryType = DeviceMemory; + }; + + template + auto ToBackend(T&& common) -> decltype(gpgmm::ToBackend(common)) { + return gpgmm::ToBackend(common); + } + +}} // namespace gpgmm::vk + +#endif // GPGMM_VK_BACKENDVK_H_ diff --git a/src/gpgmm/vk/CapsVk.cpp b/src/gpgmm/vk/CapsVk.cpp new file mode 100644 index 000000000..2a1cb3b18 --- /dev/null +++ b/src/gpgmm/vk/CapsVk.cpp @@ -0,0 +1,52 @@ +// Copyright 2021 The GPGMM 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 "gpgmm/vk/CapsVk.h" +#include "gpgmm/vk/FunctionsVk.h" + +#include +#include + +namespace gpgmm { namespace vk { + + // static + VkResult Caps::CreateCaps(VkPhysicalDevice physicalDevice, + const VulkanFunctions& vulkanFunctions, + uint32_t vulkanApiVersion, + Caps** capsOut) { + Caps* caps = new Caps(vulkanApiVersion); + + VkPhysicalDeviceProperties deviceProperties; + vulkanFunctions.GetPhysicalDeviceProperties(physicalDevice, &deviceProperties); + + caps->mMaxDeviceAllocationCount = deviceProperties.limits.maxMemoryAllocationCount; + + *capsOut = caps; + + return VK_SUCCESS; + } + + Caps::Caps(uint32_t vulkanApiVersion) + : mVulkanApiVersion(vulkanApiVersion != 0 ? vulkanApiVersion : VK_API_VERSION_1_0) { + } + + uint64_t Caps::GetMaxDeviceAllocationCount() const { + return mMaxDeviceAllocationCount; + } + + uint32_t Caps::GetVulkanApiVersion() const { + return mVulkanApiVersion; + } + +}} // namespace gpgmm::vk diff --git a/src/gpgmm/vk/CapsVk.h b/src/gpgmm/vk/CapsVk.h new file mode 100644 index 000000000..c0d5dd8c7 --- /dev/null +++ b/src/gpgmm/vk/CapsVk.h @@ -0,0 +1,45 @@ +// Copyright 2021 The GPGMM 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 GPGMM_VK_CAPSVK_H_ +#define GPGMM_VK_CAPSVK_H_ + +#include "gpgmm/vk/vk_platform.h" + +#include + +namespace gpgmm { namespace vk { + + struct VulkanFunctions; + + class Caps { + public: + static VkResult CreateCaps(VkPhysicalDevice physicalDevice, + const VulkanFunctions& vulkanFunctions, + uint32_t vulkanApiVersion, + Caps** capsOut); + + uint64_t GetMaxDeviceAllocationCount() const; + uint32_t GetVulkanApiVersion() const; + + private: + Caps(uint32_t vulkanApiVersion); + + uint64_t mMaxDeviceAllocationCount = 0; + uint32_t mVulkanApiVersion = 0; + }; + +}} // namespace gpgmm::vk + +#endif // GPGMM_VK_CAPSVK_H_ diff --git a/src/gpgmm/vk/DeviceMemoryAllocatorVk.cpp b/src/gpgmm/vk/DeviceMemoryAllocatorVk.cpp new file mode 100644 index 000000000..7cf063af0 --- /dev/null +++ b/src/gpgmm/vk/DeviceMemoryAllocatorVk.cpp @@ -0,0 +1,98 @@ +// Copyright 2019 The Dawn Authors +// Copyright 2021 The GPGMM 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 "gpgmm/vk/DeviceMemoryAllocatorVk.h" + +#include "gpgmm/Debug.h" +#include "gpgmm/vk/BackendVk.h" +#include "gpgmm/vk/CapsVk.h" +#include "gpgmm/vk/DeviceMemoryVk.h" +#include "gpgmm/vk/ResourceAllocatorVk.h" + +namespace gpgmm { namespace vk { + + DeviceMemoryAllocator::DeviceMemoryAllocator(GpResourceAllocator resourceAllocator, + uint32_t memoryTypeIndex, + VkDeviceSize memorySize) + : mResourceAllocator(resourceAllocator), + mMemoryTypeIndex(memoryTypeIndex), + mMemorySize(memorySize) { + } + + std::unique_ptr DeviceMemoryAllocator::TryAllocateMemory( + uint64_t requestSize, + uint64_t alignment, + bool neverAllocate, + bool cacheSize, + bool prefetchMemory) { + TRACE_EVENT0(TraceEventCategory::Default, "DeviceMemoryAllocator.TryAllocateMemory"); + + if (neverAllocate) { + return {}; + } + + const uint64_t maxDeviceMemoryAllocationCount = + mResourceAllocator->GetCaps()->GetMaxDeviceAllocationCount(); + if (mInfo.UsedMemoryCount + 1 >= maxDeviceMemoryAllocationCount) { + DebugEvent("DeviceMemoryAllocator.TryAllocateMemory", + ALLOCATOR_MESSAGE_ID_ALLOCATOR_FAILED) + << "Device exceeded max number of device memory allocations (" + + std::to_string(mInfo.UsedMemoryCount) + " vs " + + std::to_string(maxDeviceMemoryAllocationCount) + ")."; + return {}; + } + + if (requestSize > mMemorySize) { + DebugEvent("DeviceMemoryAllocator.TryAllocateMemory", + ALLOCATOR_MESSAGE_ID_SIZE_EXCEEDED) + << "Allocation size exceeded the memory size (" + std::to_string(requestSize) + + " vs " + std::to_string(mMemorySize) + " bytes)."; + return {}; + } + + VkMemoryAllocateInfo allocateInfo = {}; + allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocateInfo.pNext = nullptr; + allocateInfo.allocationSize = requestSize; + allocateInfo.memoryTypeIndex = mMemoryTypeIndex; + + VkDeviceMemory deviceMemory = VK_NULL_HANDLE; + if (mResourceAllocator->GetFunctions().AllocateMemory(mResourceAllocator->GetDevice(), + &allocateInfo, nullptr, + &deviceMemory) != VK_SUCCESS) { + return {}; + } + + mInfo.UsedMemoryUsage += requestSize; + mInfo.UsedMemoryCount++; + + return std::make_unique( + this, new DeviceMemory(deviceMemory, mMemoryTypeIndex, requestSize)); + } + + void DeviceMemoryAllocator::DeallocateMemory(std::unique_ptr allocation) { + TRACE_EVENT0(TraceEventCategory::Default, "DeviceMemoryAllocator.DeallocateMemory"); + + VkDeviceMemory deviceMemory = ToBackend(allocation->GetMemory())->GetDeviceMemory(); + mResourceAllocator->GetFunctions().FreeMemory(mResourceAllocator->GetDevice(), deviceMemory, + /*allocationCallbacks*/ nullptr); + + mInfo.UsedMemoryUsage -= allocation->GetSize(); + mInfo.UsedMemoryCount--; + + SafeRelease(allocation); + } + +}} // namespace gpgmm::vk diff --git a/src/gpgmm/vk/DeviceMemoryAllocatorVk.h b/src/gpgmm/vk/DeviceMemoryAllocatorVk.h new file mode 100644 index 000000000..090e2ff00 --- /dev/null +++ b/src/gpgmm/vk/DeviceMemoryAllocatorVk.h @@ -0,0 +1,48 @@ +// Copyright 2021 The GPGMM 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 GPGMM_VK_DEVICEMEMORYALLOCATORVK_H_ +#define GPGMM_VK_DEVICEMEMORYALLOCATORVK_H_ + +#include "gpgmm/MemoryAllocator.h" +#include "gpgmm/vk/vk_platform.h" + +namespace gpgmm { namespace vk { + + VK_DEFINE_HANDLE(GpResourceAllocator) + + class DeviceMemoryAllocator final : public MemoryAllocator { + public: + DeviceMemoryAllocator(GpResourceAllocator resourceAllocator, + uint32_t memoryTypeIndex, + VkDeviceSize memorySize); + ~DeviceMemoryAllocator() override = default; + + // MemoryAllocator interface + std::unique_ptr TryAllocateMemory(uint64_t requestSize, + uint64_t alignment, + bool neverAllocate, + bool cacheSize, + bool prefetchMemory) override; + void DeallocateMemory(std::unique_ptr allocation) override; + + private: + GpResourceAllocator mResourceAllocator; + uint32_t mMemoryTypeIndex; + VkDeviceSize mMemorySize; + }; + +}} // namespace gpgmm::vk + +#endif // GPGMM_VK_DEVICEMEMORYALLOCATORVK_H_ diff --git a/src/gpgmm/vk/DeviceMemoryVk.cpp b/src/gpgmm/vk/DeviceMemoryVk.cpp new file mode 100644 index 000000000..91ba4793c --- /dev/null +++ b/src/gpgmm/vk/DeviceMemoryVk.cpp @@ -0,0 +1,31 @@ +// Copyright 2021 The GPGMM 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 "gpgmm/vk/DeviceMemoryVk.h" + +namespace gpgmm { namespace vk { + + DeviceMemory::DeviceMemory(VkDeviceMemory memory, uint32_t memoryTypeIndex, uint64_t size) + : MemoryBase(size), mMemory(memory), mMemoryTypeIndex(memoryTypeIndex) { + } + + VkDeviceMemory DeviceMemory::GetDeviceMemory() const { + return mMemory; + } + + uint32_t DeviceMemory::GetMemoryTypeIndex() const { + return mMemoryTypeIndex; + } + +}} // namespace gpgmm::vk diff --git a/src/gpgmm/vk/DeviceMemoryVk.h b/src/gpgmm/vk/DeviceMemoryVk.h new file mode 100644 index 000000000..27c736919 --- /dev/null +++ b/src/gpgmm/vk/DeviceMemoryVk.h @@ -0,0 +1,39 @@ +// Copyright 2021 The GPGMM 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 GPGMM_VK_DEVICEMEMORYVK_H_ +#define GPGMM_VK_DEVICEMEMORYVK_H_ + +#include "gpgmm/Memory.h" + +#include "gpgmm/vk/vk_platform.h" + +namespace gpgmm { namespace vk { + + class DeviceMemory : public MemoryBase { + public: + DeviceMemory(VkDeviceMemory memory, uint32_t memoryTypeIndex, uint64_t size); + ~DeviceMemory() = default; + + VkDeviceMemory GetDeviceMemory() const; + uint32_t GetMemoryTypeIndex() const; + + private: + VkDeviceMemory mMemory = VK_NULL_HANDLE; + uint32_t mMemoryTypeIndex = 0; + }; + +}} // namespace gpgmm::vk + +#endif // GPGMM_VK_DEVICEMEMORYVK_H_ diff --git a/src/gpgmm/vk/ErrorVk.h b/src/gpgmm/vk/ErrorVk.h new file mode 100644 index 000000000..c431c5fdb --- /dev/null +++ b/src/gpgmm/vk/ErrorVk.h @@ -0,0 +1,43 @@ +// Copyright 2021 The GPGMM 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 GPGMM_VK_ERRORVK_H_ +#define GPGMM_VK_ERRORVK_H_ + +#include "gpgmm/vk/vk_platform.h" + +namespace gpgmm { namespace vk { + +#define ReturnIfFailed(expr) \ + { \ + VkResult result = expr; \ + if (result != VK_SUCCESS) { \ + return result; \ + } \ + } \ + for (;;) \ + break + +#define ReturnIfSuccess(expr) \ + { \ + VkResult result = expr; \ + if (result == VK_SUCCESS) { \ + return result; \ + } \ + } \ + for (;;) \ + break + +}} // namespace gpgmm::vk + +#endif // GPGMM_VK_ERRORVK_H_ diff --git a/src/gpgmm/vk/FunctionsVk.cpp b/src/gpgmm/vk/FunctionsVk.cpp new file mode 100644 index 000000000..57e0febe2 --- /dev/null +++ b/src/gpgmm/vk/FunctionsVk.cpp @@ -0,0 +1,81 @@ +// Copyright 2021 The GPGMM 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 "gpgmm/vk/FunctionsVk.h" + +#include "gpgmm/common/Assert.h" + +#define GPGMM_DYNAMIC_GET_INSTANCE_FUNC(functionName) \ + do { \ + functionName = reinterpret_cast( \ + GetInstanceProcAddr(instance, "vk" #functionName)); \ + } while (0) + +#define GPGMM_DYNAMIC_GET_DEVICE_FUNC(functionName) \ + do { \ + functionName = reinterpret_cast( \ + GetDeviceProcAddr(device, "vk" #functionName)); \ + } while (0) + +#define GPGMM_STATIC_GET_FUNC(functionName) \ + do { \ + functionName = reinterpret_cast(vk##functionName); \ + } while (0) + +namespace gpgmm { namespace vk { + void VulkanFunctions::LoadInstanceFunctions(VkInstance instance) { + GPGMM_DYNAMIC_GET_INSTANCE_FUNC(GetDeviceProcAddr); + GPGMM_DYNAMIC_GET_INSTANCE_FUNC(GetPhysicalDeviceMemoryProperties); + GPGMM_DYNAMIC_GET_INSTANCE_FUNC(GetPhysicalDeviceProperties); + // TODO + } + + void VulkanFunctions::LoadDeviceFunctions(VkDevice device) { + GPGMM_DYNAMIC_GET_DEVICE_FUNC(AllocateMemory); + GPGMM_DYNAMIC_GET_DEVICE_FUNC(FreeMemory); + GPGMM_DYNAMIC_GET_DEVICE_FUNC(BindBufferMemory); + GPGMM_DYNAMIC_GET_DEVICE_FUNC(GetBufferMemoryRequirements); + GPGMM_DYNAMIC_GET_DEVICE_FUNC(CreateBuffer); + GPGMM_DYNAMIC_GET_DEVICE_FUNC(DestroyBuffer); + // TODO + } + + void VulkanFunctions::ImportDeviceFunctions() { + GPGMM_STATIC_GET_FUNC(GetPhysicalDeviceMemoryProperties); + GPGMM_STATIC_GET_FUNC(GetPhysicalDeviceProperties); + GPGMM_STATIC_GET_FUNC(AllocateMemory); + GPGMM_STATIC_GET_FUNC(FreeMemory); + GPGMM_STATIC_GET_FUNC(BindBufferMemory); + GPGMM_STATIC_GET_FUNC(GetBufferMemoryRequirements); + GPGMM_STATIC_GET_FUNC(CreateBuffer); + GPGMM_STATIC_GET_FUNC(DestroyBuffer); + // TODO + } + + void VulkanFunctions::ImportDeviceFunctions(const VulkanFunctions* vulkanFunctions) { + // TODO + } + + void VulkanFunctions::AssertVulkanFunctionsAreValid() { + ASSERT(GetPhysicalDeviceMemoryProperties != nullptr); + ASSERT(GetPhysicalDeviceProperties != nullptr); + ASSERT(AllocateMemory != nullptr); + ASSERT(FreeMemory != nullptr); + ASSERT(BindBufferMemory != nullptr); + ASSERT(GetBufferMemoryRequirements != nullptr); + ASSERT(CreateBuffer != nullptr); + ASSERT(DestroyBuffer != nullptr); + } + +}} // namespace gpgmm::vk diff --git a/src/gpgmm/vk/FunctionsVk.h b/src/gpgmm/vk/FunctionsVk.h new file mode 100644 index 000000000..4a7dd9fd7 --- /dev/null +++ b/src/gpgmm/vk/FunctionsVk.h @@ -0,0 +1,56 @@ +// Copyright 2021 The GPGMM 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 "gpgmm/vk/vk_platform.h" + +namespace gpgmm { namespace vk { + + // Vulkan entrypoints used by GPGMM. + struct VulkanFunctions { + // Used to dynamically set functions. + void LoadInstanceFunctions(VkInstance instance); + void LoadDeviceFunctions(VkDevice device); + + // Used to statically set functions. + void ImportDeviceFunctions(); + + // Used to import functions already specified elsewhere. + void ImportDeviceFunctions(const VulkanFunctions* vulkanFunctions); + + // ASSERTs if any Vulkan function is left unset. + void AssertVulkanFunctionsAreValid(); + + // Order is important: instance must be loaded before device. + PFN_vkGetInstanceProcAddr GetInstanceProcAddr = nullptr; + + // Core Vulkan 1.0 + PFN_vkGetDeviceProcAddr GetDeviceProcAddr = nullptr; + PFN_vkGetPhysicalDeviceProperties GetPhysicalDeviceProperties = nullptr; + PFN_vkGetPhysicalDeviceMemoryProperties GetPhysicalDeviceMemoryProperties = nullptr; + PFN_vkAllocateMemory AllocateMemory = nullptr; + PFN_vkBindBufferMemory BindBufferMemory = nullptr; + PFN_vkFreeMemory FreeMemory = nullptr; + PFN_vkMapMemory MapMemory = nullptr; + PFN_vkUnmapMemory UnmapMemory = nullptr; + PFN_vkFlushMappedMemoryRanges FlushMappedMemoryRanges = nullptr; + PFN_vkInvalidateMappedMemoryRanges InvalidateMappedMemoryRanges = nullptr; + PFN_vkGetBufferMemoryRequirements GetBufferMemoryRequirements = nullptr; + PFN_vkGetImageMemoryRequirements GetImageMemoryRequirements = nullptr; + PFN_vkCreateBuffer CreateBuffer = nullptr; + PFN_vkDestroyBuffer DestroyBuffer = nullptr; + PFN_vkCreateImage CreateImage = nullptr; + PFN_vkDestroyImage DestroyImage = nullptr; + }; + +}} // namespace gpgmm::vk diff --git a/src/gpgmm/vk/ResourceAllocatorVk.cpp b/src/gpgmm/vk/ResourceAllocatorVk.cpp new file mode 100644 index 000000000..aeaea4bb5 --- /dev/null +++ b/src/gpgmm/vk/ResourceAllocatorVk.cpp @@ -0,0 +1,261 @@ +// Copyright 2021 The GPGMM 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 "gpgmm/vk/ResourceAllocatorVk.h" + +#include "gpgmm/Debug.h" +#include "gpgmm/TraceEvent.h" +#include "gpgmm/vk/BackendVk.h" +#include "gpgmm/vk/CapsVk.h" +#include "gpgmm/vk/DeviceMemoryAllocatorVk.h" +#include "gpgmm/vk/DeviceMemoryVk.h" +#include "gpgmm/vk/ErrorVk.h" + +namespace gpgmm { namespace vk { + + VkResult gpCreateResourceAllocator(const GpCreateAllocatorInfo& info, + GpResourceAllocator* allocatorOut) { + return GpResourceAllocator_T::CreateAllocator(info, allocatorOut); + } + + void gpDestroyResourceAllocator(GpResourceAllocator allocator) { + if (allocator == VK_NULL_HANDLE) { + return; + } + SafeDelete(allocator); + } + + VkResult gpCreateBuffer(GpResourceAllocator allocator, + const VkBufferCreateInfo* pBufferCreateInfo, + VkBuffer* bufferOut, + const GpResourceAllocationCreateInfo* pAllocationCreateInfo, + GpResourceAllocation* allocationOut) { + *allocationOut = VK_NULL_HANDLE; + *bufferOut = VK_NULL_HANDLE; + + if (allocator == VK_NULL_HANDLE) { + return VK_INCOMPLETE; + } + + // Create the buffer. + VkBuffer buffer = VK_NULL_HANDLE; + ReturnIfFailed( + allocator->GetFunctions().CreateBuffer(allocator->GetDevice(), pBufferCreateInfo, + /*allocationCallbacks*/ nullptr, &buffer)); + + VkMemoryRequirements requirements = {}; + allocator->GetBufferMemoryRequirements(buffer, &requirements); + + // Create memory for the buffer. + GpResourceAllocation allocation = VK_NULL_HANDLE; + VkResult result = + allocator->TryAllocateMemory(requirements, *pAllocationCreateInfo, &allocation); + if (result != VK_SUCCESS) { + allocator->GetFunctions().DestroyBuffer(allocator->GetDevice(), buffer, + /*allocationCallbacks*/ nullptr); + return result; + } + + // Associate memory with the buffer. + result = allocator->GetFunctions().BindBufferMemory( + allocator->GetDevice(), buffer, ToBackend(allocation->GetMemory())->GetDeviceMemory(), + allocation->GetOffset()); + if (result != VK_SUCCESS) { + allocator->GetFunctions().DestroyBuffer(allocator->GetDevice(), buffer, + /*allocationCallbacks*/ nullptr); + allocator->DeallocateMemory(allocation); + return result; + } + + *allocationOut = allocation; + *bufferOut = buffer; + + return VK_SUCCESS; + } + + void gpDestroyBuffer(GpResourceAllocator allocator, + VkBuffer buffer, + GpResourceAllocation allocation) { + if (allocator == VK_NULL_HANDLE || buffer == VK_NULL_HANDLE) { + return; + } + + allocator->GetFunctions().DestroyBuffer(allocator->GetDevice(), buffer, + /*allocationCallbacks*/ nullptr); + + if (allocation == VK_NULL_HANDLE) { + return; + } + + allocator->DeallocateMemory(allocation); + } + + // GpResourceAllocation_T + + GpResourceAllocation_T::GpResourceAllocation_T(const MemoryAllocation& allocation) + : MemoryAllocation(allocation) { + } + + // GpResourceAllocator_T + + // static + VkResult GpResourceAllocator_T::CreateAllocator(const GpCreateAllocatorInfo& info, + GpResourceAllocator* allocatorOut) { + VulkanFunctions vulkanFunctions = {}; + { + if (info.pVulkanFunctions != nullptr) { + vulkanFunctions.ImportDeviceFunctions(info.pVulkanFunctions); + } else { +#if defined(GPGMM_STATIC_VULKAN_FUNCTIONS) + vulkanFunctions.ImportDeviceFunctions(); +#else // GPGMM_DYNAMIC_VULKAN_FUNCTIONS + vulkanFunctions.LoadInstanceFunctions(info.instance); + vulkanFunctions.LoadDeviceFunctions(info.device); +#endif + } + +#ifndef NDEBUG + vulkanFunctions.AssertVulkanFunctionsAreValid(); +#endif + } + + std::unique_ptr caps; + { + Caps* ptr = nullptr; + ReturnIfFailed(Caps::CreateCaps(info.physicalDevice, vulkanFunctions, + info.vulkanApiVersion, &ptr)); + caps.reset(ptr); + } + + if (allocatorOut != VK_NULL_HANDLE) { + *allocatorOut = new GpResourceAllocator_T(info, vulkanFunctions, std::move(caps)); + } + + return VK_SUCCESS; + } + + GpResourceAllocator_T::GpResourceAllocator_T(const GpCreateAllocatorInfo& info, + const VulkanFunctions& vulkanFunctions, + std::unique_ptr caps) + : mDevice(info.device), mVulkanFunctions(vulkanFunctions), mCaps(std::move(caps)) { + VkPhysicalDeviceMemoryProperties memoryProperties = {}; + mVulkanFunctions.GetPhysicalDeviceMemoryProperties(info.physicalDevice, &memoryProperties); + { + mMemoryTypes.assign(memoryProperties.memoryTypes, + memoryProperties.memoryTypes + memoryProperties.memoryTypeCount); + + std::vector memoryHeaps; + memoryHeaps.assign(memoryProperties.memoryHeaps, + memoryProperties.memoryHeaps + memoryProperties.memoryHeapCount); + + for (uint32_t memoryTypeIndex = 0; memoryTypeIndex < mMemoryTypes.size(); + memoryTypeIndex++) { + mDeviceAllocatorsPerType.emplace_back(std::make_unique( + this, memoryTypeIndex, + memoryHeaps[mMemoryTypes[memoryTypeIndex].heapIndex].size)); + } + } + } + + VkResult GpResourceAllocator_T::FindMemoryTypeIndex( + uint32_t memoryTypeBits, + const GpResourceAllocationCreateInfo& allocationInfo, + uint32_t* memoryTypeIndexOut) { + *memoryTypeIndexOut = UINT32_MAX; + + const VkFlags& requiredPropertyFlags = allocationInfo.requiredPropertyFlags; + + uint32_t bestMemoryTypeIndex = UINT32_MAX; + for (uint32_t memoryTypeIndex = 0; memoryTypeIndex < mMemoryTypes.size(); + ++memoryTypeIndex) { + const VkMemoryPropertyFlags& currPropertyFlags = + mMemoryTypes[memoryTypeIndex].propertyFlags; + + // Memory type must be acceptable for this memoryTypeBits. + if ((memoryTypeBits & (1 << memoryTypeIndex)) == 0) { + continue; + } + + // Memory type must have all the required property flags. + if ((currPropertyFlags & requiredPropertyFlags) != requiredPropertyFlags) { + continue; + } + + // Found the first candidate memory type + if (*memoryTypeIndexOut == UINT32_MAX) { + bestMemoryTypeIndex = memoryTypeIndex; + continue; + } + } + + if (bestMemoryTypeIndex == UINT32_MAX) { + return VK_ERROR_FEATURE_NOT_PRESENT; + } + + *memoryTypeIndexOut = bestMemoryTypeIndex; + + return VK_SUCCESS; + } + + void GpResourceAllocator_T::GetBufferMemoryRequirements(VkBuffer buffer, + VkMemoryRequirements* requirementsOut) { + mVulkanFunctions.GetBufferMemoryRequirements(mDevice, buffer, requirementsOut); + } + + VkResult GpResourceAllocator_T::TryAllocateMemory( + const VkMemoryRequirements& requirements, + const GpResourceAllocationCreateInfo& allocationInfo, + GpResourceAllocation* allocationOut) { + uint32_t memoryTypeIndex; + ReturnIfFailed( + FindMemoryTypeIndex(requirements.memoryTypeBits, allocationInfo, &memoryTypeIndex)); + + MemoryAllocator* allocator = mDeviceAllocatorsPerType[memoryTypeIndex].get(); + std::unique_ptr memoryAllocation = allocator->TryAllocateMemory( + requirements.size, requirements.alignment, + (allocationInfo.flags & GP_ALLOCATION_FLAG_NEVER_ALLOCATE_MEMORY), /*cacheSize*/ false, + (allocationInfo.flags & GP_ALLOCATION_FLAG_ALWAYS_PREFETCH_MEMORY)); + if (memoryAllocation == nullptr) { + InfoEvent("GpResourceAllocator.TryAllocateResource", + ALLOCATOR_MESSAGE_ID_ALLOCATOR_FAILED) + << std::string(allocator->GetTypename()) + + " failed to allocate memory for resource."; + + return VK_ERROR_UNKNOWN; + } + + *allocationOut = new GpResourceAllocation_T(*memoryAllocation); + + return VK_SUCCESS; + } + + void GpResourceAllocator_T::DeallocateMemory(GpResourceAllocation allocation) { + if (allocation == VK_NULL_HANDLE) { + return; + } + allocation->GetAllocator()->DeallocateMemory(std::unique_ptr(allocation)); + } + + VkDevice GpResourceAllocator_T::GetDevice() const { + return mDevice; + } + + VulkanFunctions GpResourceAllocator_T::GetFunctions() const { + return mVulkanFunctions; + } + + Caps* GpResourceAllocator_T::GetCaps() const { + return mCaps.get(); + } +}} // namespace gpgmm::vk diff --git a/src/gpgmm/vk/ResourceAllocatorVk.h b/src/gpgmm/vk/ResourceAllocatorVk.h new file mode 100644 index 000000000..2a74bd975 --- /dev/null +++ b/src/gpgmm/vk/ResourceAllocatorVk.h @@ -0,0 +1,141 @@ +// Copyright 2021 The GPGMM 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 GPGMM_VK_RESOURCEALLOCATORVK_H_ +#define GPGMM_VK_RESOURCEALLOCATORVK_H_ + +#include "gpgmm/MemoryAllocator.h" +#include "gpgmm/common/Flags.h" +#include "gpgmm/vk/FunctionsVk.h" +#include "include/gpgmm_export.h" + +#include + +namespace gpgmm { + class MemoryAllocation; +} // namespace gpgmm + +namespace gpgmm { namespace vk { + + // Opaque handle to a allocator object. + VK_DEFINE_HANDLE(GpResourceAllocator) + + // Opaque handle to a resource allocation object. + VK_DEFINE_HANDLE(GpResourceAllocation) + + struct GpCreateAllocatorInfo { + // Function pointer to Vulkan functions. + const VulkanFunctions* pVulkanFunctions = nullptr; + + // Handle to Vulkan physical device object. + VkPhysicalDevice physicalDevice; + + // Handle to Vulkan device object. + VkDevice device; + + // Handle to Vulkan instance object. + VkInstance instance; + + // Vulkan version return by VK_MAKE_VERSION. + uint32_t vulkanApiVersion; + }; + + enum GpResourceAllocationCreateFlags { + + // Disables all allocation flags. Enabled by default. + GP_ALLOCATION_FLAG_NONE = 0x0, + + // Forbids creating new device memory when creating a resource. The created resource + // must use existing device memory or error. Effectively disables creating + // standalone allocations whose memory cannot be reused. + GP_ALLOCATION_FLAG_NEVER_ALLOCATE_MEMORY = 0x1, + + // Forbids allowing multiple resource allocations to be created from the same device + // memory. The created resource will always be allocated with it's own device memory. + GP_ALLOCATION_FLAG_NEVER_SUBALLOCATE_MEMORY = 0x4, + + // Prefetch memory for the next resource allocation. + // The call to prefetch is deferred to a seperate background thread by GPGMM which runs + // when the current allocation requested is completed. By default, GPGMM will automatically + // trigger prefetching based on heurstics. Prefetching enables more performance when + // allocating for large contiguous allocations. Should not be used with + // ALLOCATION_FLAG_NEVER_ALLOCATE_MEMORY. + GP_ALLOCATION_FLAG_ALWAYS_PREFETCH_MEMORY = 0x8, + }; + + struct GpResourceAllocationCreateInfo { + // Flags used to control how the resource will be allocated. + GpResourceAllocationCreateFlags flags; + + // Bitmask to specify required memory properties for the allocation. + VkMemoryPropertyFlags requiredPropertyFlags; + }; + + GPGMM_EXPORT VkResult gpCreateResourceAllocator(const GpCreateAllocatorInfo& info, + GpResourceAllocator* allocatorOut); + GPGMM_EXPORT void gpDestroyResourceAllocator(GpResourceAllocator allocator); + + GPGMM_EXPORT VkResult + gpCreateBuffer(GpResourceAllocator allocator, + const VkBufferCreateInfo* pBufferCreateInfo, + VkBuffer* pBuffer, + const GpResourceAllocationCreateInfo* pAllocationCreateInfo, + GpResourceAllocation* allocationOut); + + GPGMM_EXPORT void gpDestroyBuffer(GpResourceAllocator allocator, + VkBuffer buffer, + GpResourceAllocation allocation); + + struct GpResourceAllocation_T final : public MemoryAllocation { + GpResourceAllocation_T(const MemoryAllocation& allocation); + }; + + class Caps; + struct GpResourceAllocator_T { + public: + static VkResult CreateAllocator(const GpCreateAllocatorInfo& info, + GpResourceAllocator* allocatorOut); + + VkResult TryAllocateMemory(const VkMemoryRequirements& requirements, + const GpResourceAllocationCreateInfo& allocationInfo, + GpResourceAllocation* allocationOut); + + void DeallocateMemory(GpResourceAllocation allocation); + + void GetBufferMemoryRequirements(VkBuffer buffer, VkMemoryRequirements* requirementsOut); + + VkDevice GetDevice() const; + VulkanFunctions GetFunctions() const; + Caps* GetCaps() const; + + private: + GpResourceAllocator_T(const GpCreateAllocatorInfo& info, + const VulkanFunctions& vulkanFunctions, + std::unique_ptr caps); + + VkResult FindMemoryTypeIndex(uint32_t memoryTypeBits, + const GpResourceAllocationCreateInfo& allocationInfo, + uint32_t* memoryTypeIndexOut); + + VkDevice mDevice; + VulkanFunctions mVulkanFunctions; + std::unique_ptr mCaps; + + std::vector> mDeviceAllocatorsPerType; + std::vector mMemoryTypes; + }; + +}} // namespace gpgmm::vk + +#endif // GPGMM_VK_RESOURCEALLOCATORVK_H_ diff --git a/src/gpgmm/vk/vk_platform.h b/src/gpgmm/vk/vk_platform.h new file mode 100644 index 000000000..8768dad85 --- /dev/null +++ b/src/gpgmm/vk/vk_platform.h @@ -0,0 +1,20 @@ +// Copyright 2021 The GPGMM 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 GPGMM_VK_VKPLATFORM_H_ +#define GPGMM_VK_VKPLATFORM_H_ + +#include + +#endif // GPGMM_VK_VKPLATFORM_H_ diff --git a/src/include/gpgmm_vk.h b/src/include/gpgmm_vk.h new file mode 100644 index 000000000..d9c692d89 --- /dev/null +++ b/src/include/gpgmm_vk.h @@ -0,0 +1,26 @@ +// Copyright 2021 The GPGMM 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 GPGMM_INCLUDE_GPGMM_VK_H_ +#define GPGMM_INCLUDE_GPGMM_VK_H_ + +#include + +// clang-format off + +#include "gpgmm/vk/ResourceAllocatorVk.h" + +// clang-format on + +#endif // GPGMM_INCLUDE_GPGMM_VK_H_ diff --git a/src/tests/BUILD.gn b/src/tests/BUILD.gn index e89c853c5..902d725a6 100644 --- a/src/tests/BUILD.gn +++ b/src/tests/BUILD.gn @@ -159,6 +159,16 @@ source_set("gpgmm_end2end_tests_sources") { "dxgi.lib", ] } + + if (gpgmm_enable_vk) { + sources += [ + "VKTest.cpp", + "VKTest.h", + "end2end/VKResourceAllocatorTests.cpp", + ] + + deps += [ "//third_party/vulkan-deps/vulkan-loader/src:libvulkan" ] + } } test("gpgmm_end2end_tests") { diff --git a/src/tests/VKTest.cpp b/src/tests/VKTest.cpp new file mode 100644 index 000000000..1b774841d --- /dev/null +++ b/src/tests/VKTest.cpp @@ -0,0 +1,142 @@ +// Copyright 2021 The GPGMM 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/tests/VKTest.h" + +#include + +#include + +namespace gpgmm { namespace vk { + + void VKTestBase::SetUp() { + GPGMMTestBase::SetUp(); + + // Setup the instance. + + // TODO + std::vector enabledExtensions = {}; + std::vector instanceLayers = {}; + + VkApplicationInfo appInfo = {}; + appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + appInfo.pNext = nullptr; + appInfo.pApplicationName = "End2End Tests"; + appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); + appInfo.pEngineName = "GPGMM"; + appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); + appInfo.apiVersion = VK_API_VERSION_1_0; + + VkInstanceCreateInfo instanceInfo = {}; + instanceInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instanceInfo.pNext = nullptr; + instanceInfo.pApplicationInfo = &appInfo; + instanceInfo.enabledExtensionCount = static_cast(enabledExtensions.size()); + instanceInfo.ppEnabledExtensionNames = enabledExtensions.data(); + instanceInfo.enabledLayerCount = static_cast(instanceLayers.size()); + instanceInfo.ppEnabledLayerNames = instanceLayers.data(); + + // vkCreateInstance fails if no Vulkan ICD was installed. + // TODO: Consider installing a fall-back CPU-based Vulkan driver for testing. + GPGMM_SKIP_TEST_IF(vkCreateInstance(&instanceInfo, nullptr, &mInstance) != VK_SUCCESS); + + // Setup the physical device + { + uint32_t physicalDeviceCount = 0; + ASSERT_SUCCESS(vkEnumeratePhysicalDevices(mInstance, &physicalDeviceCount, nullptr)); + ASSERT_GT(physicalDeviceCount, 0u); + + std::vector physicalDevices(physicalDeviceCount); + ASSERT_SUCCESS(vkEnumeratePhysicalDevices(mInstance, &physicalDeviceCount, + physicalDevices.data())); + + mPhysicalDevice = physicalDevices[0]; + } + + ASSERT_NE(mPhysicalDevice, VK_NULL_HANDLE); + + // Setup a single (universal) queue. + uint32_t queueFamilyIndex = 0; + { + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueFamilyCount, nullptr); + + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueFamilyCount, + queueFamilies.data()); + + // Note that GRAPHICS and COMPUTE imply TRANSFER so we don't need to check for it. + constexpr uint32_t kUniversalFlags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT; + int universalQueueFamily = -1; + for (unsigned int i = 0; i < queueFamilyCount; ++i) { + if ((queueFamilies[i].queueFlags & kUniversalFlags) == kUniversalFlags) { + universalQueueFamily = i; + break; + } + } + ASSERT_NE(universalQueueFamily, -1); + queueFamilyIndex = static_cast(universalQueueFamily); + } + + std::vector queueCreateInfo = {}; + { + VkDeviceQueueCreateInfo queueInfo = {}; + queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueInfo.pNext = nullptr; + queueInfo.flags = 0; + queueInfo.queueFamilyIndex = static_cast(queueFamilyIndex); + queueInfo.queueCount = 1; + float zero = 0.0f; + queueInfo.pQueuePriorities = &zero; + queueCreateInfo.push_back(queueInfo); + } + + // Setup the device + + VkDeviceCreateInfo deviceInfo = {}; + deviceInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + deviceInfo.pNext = nullptr; + deviceInfo.flags = 0; + deviceInfo.enabledLayerCount = 0; + deviceInfo.ppEnabledLayerNames = nullptr; + deviceInfo.enabledExtensionCount = static_cast(enabledExtensions.size()); + deviceInfo.ppEnabledExtensionNames = + !enabledExtensions.empty() ? enabledExtensions.data() : nullptr; + deviceInfo.queueCreateInfoCount = static_cast(queueCreateInfo.size()); + deviceInfo.pQueueCreateInfos = queueCreateInfo.data(); + + ASSERT_SUCCESS(vkCreateDevice(mPhysicalDevice, &deviceInfo, nullptr, &mDevice)); + } + + void VKTestBase::TearDown() { + GPGMMTestBase::TearDown(); + + if (mDevice != VK_NULL_HANDLE) { + vkDestroyDevice(mDevice, nullptr); + } + + if (mInstance != VK_NULL_HANDLE) { + vkDestroyInstance(mInstance, nullptr); + } + } + + GpCreateAllocatorInfo VKTestBase::CreateBasicAllocatorInfo() const { + GpCreateAllocatorInfo allocatorInfo = {}; + allocatorInfo.device = mDevice; + allocatorInfo.instance = mInstance; + allocatorInfo.physicalDevice = mPhysicalDevice; + return allocatorInfo; + } + +}} // namespace gpgmm::vk diff --git a/src/tests/VKTest.h b/src/tests/VKTest.h new file mode 100644 index 000000000..6dc981faf --- /dev/null +++ b/src/tests/VKTest.h @@ -0,0 +1,44 @@ +// Copyright 2021 The GPGMM 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 TESTS_VKTEST_H_ +#define TESTS_VKTEST_H_ + +#include "tests/GPGMMTest.h" + +#include "gpgmm/vk/vk_platform.h" + +#define ASSERT_FAILED(expr) ASSERT_TRUE((expr) != VK_SUCCESS) +#define ASSERT_SUCCESS(expr) ASSERT_TRUE((expr) == VK_SUCCESS) + +namespace gpgmm { namespace vk { + + struct GpCreateAllocatorInfo; + + class VKTestBase : public GPGMMTestBase { + public: + void SetUp(); + void TearDown(); + + GpCreateAllocatorInfo CreateBasicAllocatorInfo() const; + + protected: + VkDevice mDevice = VK_NULL_HANDLE; + VkInstance mInstance = VK_NULL_HANDLE; + VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE; + }; + +}} // namespace gpgmm::vk + +#endif // TESTS_VKTEST_H_ diff --git a/src/tests/end2end/VKResourceAllocatorTests.cpp b/src/tests/end2end/VKResourceAllocatorTests.cpp new file mode 100644 index 000000000..c76d09afd --- /dev/null +++ b/src/tests/end2end/VKResourceAllocatorTests.cpp @@ -0,0 +1,58 @@ +// Copyright 2021 The GPGMM 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 "tests/VKTest.h" + +#include + +using namespace gpgmm::vk; + +static constexpr uint64_t kDefaultBufferSize = 4ll * 1024ll * 1024ll; // 4MB + +class VKResourceAllocatorTests : public VKTestBase, public ::testing::Test { + protected: + void SetUp() override { + VKTestBase::SetUp(); + } + + void TearDown() override { + VKTestBase::TearDown(); + } +}; + +TEST_F(VKResourceAllocatorTests, CreateAllocator) { + GpResourceAllocator resourceAllocator; + ASSERT_SUCCESS(gpCreateResourceAllocator(CreateBasicAllocatorInfo(), &resourceAllocator)); + gpDestroyResourceAllocator(resourceAllocator); + ASSERT_SUCCESS(gpCreateResourceAllocator(CreateBasicAllocatorInfo(), nullptr)); +} + +TEST_F(VKResourceAllocatorTests, CreateBuffer) { + GpResourceAllocator resourceAllocator; + ASSERT_SUCCESS(gpCreateResourceAllocator(CreateBasicAllocatorInfo(), &resourceAllocator)); + + VkBufferCreateInfo bufferInfo = {}; + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.size = kDefaultBufferSize; + + GpResourceAllocationCreateInfo allocationInfo = {}; + + VkBuffer buffer; + GpResourceAllocation allocation; + ASSERT_SUCCESS( + gpCreateBuffer(resourceAllocator, &bufferInfo, &buffer, &allocationInfo, &allocation)); + + gpDestroyBuffer(resourceAllocator, buffer, allocation); + gpDestroyResourceAllocator(resourceAllocator); +} diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt new file mode 100644 index 000000000..c48210e97 --- /dev/null +++ b/third_party/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright 2021 The GPGMM 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. + +# Check if the target was already added with NOT TARGET. +if (NOT TARGET Vulkan-Headers) + add_subdirectory(${GPGMM_VK_HEADERS_DIR} "${CMAKE_CURRENT_BINARY_DIR}/vulkan-headers") +endif()