summaryrefslogtreecommitdiffstats
path: root/src/video_core/renderer_vulkan/vk_memory_manager.h
blob: cd00bb91b146c098cf24f54d3f293268c8dadc60 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

#pragma once

#include <memory>
#include <utility>
#include <vector>
#include "common/common_types.h"
#include "video_core/renderer_vulkan/declarations.h"

namespace Vulkan {

class MemoryMap;
class VKDevice;
class VKMemoryAllocation;
class VKMemoryCommitImpl;

using VKMemoryCommit = std::unique_ptr<VKMemoryCommitImpl>;

class VKMemoryManager final {
public:
    explicit VKMemoryManager(const VKDevice& device);
    VKMemoryManager(const VKMemoryManager&) = delete;
    ~VKMemoryManager();

    /**
     * Commits a memory with the specified requeriments.
     * @param requirements Requirements returned from a Vulkan call.
     * @param host_visible Signals the allocator that it *must* use host visible and coherent
     *                     memory. When passing false, it will try to allocate device local memory.
     * @returns A memory commit.
     */
    VKMemoryCommit Commit(const vk::MemoryRequirements& reqs, bool host_visible);

    /// Commits memory required by the buffer and binds it.
    VKMemoryCommit Commit(vk::Buffer buffer, bool host_visible);

    /// Commits memory required by the image and binds it.
    VKMemoryCommit Commit(vk::Image image, bool host_visible);

    /// Returns true if the memory allocations are done always in host visible and coherent memory.
    bool IsMemoryUnified() const {
        return is_memory_unified;
    }

private:
    /// Allocates a chunk of memory.
    bool AllocMemory(vk::MemoryPropertyFlags wanted_properties, u32 type_mask, u64 size);

    /// Tries to allocate a memory commit.
    VKMemoryCommit TryAllocCommit(const vk::MemoryRequirements& requirements,
                                  vk::MemoryPropertyFlags wanted_properties);

    /// Returns true if the device uses an unified memory model.
    static bool GetMemoryUnified(const vk::PhysicalDeviceMemoryProperties& properties);

    const VKDevice& device;                              ///< Device handler.
    const vk::PhysicalDeviceMemoryProperties properties; ///< Physical device properties.
    const bool is_memory_unified;                        ///< True if memory model is unified.
    std::vector<std::unique_ptr<VKMemoryAllocation>> allocations; ///< Current allocations.
};

class VKMemoryCommitImpl final {
    friend VKMemoryAllocation;
    friend MemoryMap;

public:
    explicit VKMemoryCommitImpl(const VKDevice& device, VKMemoryAllocation* allocation,
                                vk::DeviceMemory memory, u64 begin, u64 end);
    ~VKMemoryCommitImpl();

    /// Maps a memory region and returns a pointer to it.
    /// It's illegal to have more than one memory map at the same time.
    MemoryMap Map(u64 size, u64 offset = 0) const;

    /// Maps the whole commit and returns a pointer to it.
    /// It's illegal to have more than one memory map at the same time.
    MemoryMap Map() const;

    /// Returns the Vulkan memory handler.
    vk::DeviceMemory GetMemory() const {
        return memory;
    }

    /// Returns the start position of the commit relative to the allocation.
    vk::DeviceSize GetOffset() const {
        return static_cast<vk::DeviceSize>(interval.first);
    }

private:
    /// Unmaps memory.
    void Unmap() const;

    const VKDevice& device;           ///< Vulkan device.
    std::pair<u64, u64> interval{};   ///< Interval where the commit exists.
    vk::DeviceMemory memory;          ///< Vulkan device memory handler.
    VKMemoryAllocation* allocation{}; ///< Pointer to the large memory allocation.
};

/// Holds ownership of a memory map.
class MemoryMap final {
public:
    explicit MemoryMap(const VKMemoryCommitImpl* commit, u8* address)
        : commit{commit}, address{address} {}

    ~MemoryMap() {
        if (commit) {
            commit->Unmap();
        }
    }

    /// Prematurely releases the memory map.
    void Release() {
        commit->Unmap();
        commit = nullptr;
    }

    /// Returns the address of the memory map.
    u8* GetAddress() const {
        return address;
    }

    /// Returns the address of the memory map;
    operator u8*() const {
        return address;
    }

private:
    const VKMemoryCommitImpl* commit{}; ///< Mapped memory commit.
    u8* address{};                      ///< Address to the mapped memory.
};

} // namespace Vulkan