summaryrefslogtreecommitdiff
path: root/proposals/VK_EXT_map_memory_placed.adoc
blob: a35b69fcfbc4952e7cca8e7189a2cf60a3dc40d5 (plain)
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// Copyright 2022-2024 The Khronos Group, Inc.
// SPDX-License-Identifier: CC-BY-4.0

# VK_EXT_map_memory_placed
:toc: left
:refpage: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/
:sectnums:

This document proposes adding support client-controlled virtual address
placement of `VkDeviceMemory` maps.

## Problem Statement

There are certain cases in which it can be useful to directly control the
address used by a memory map operation.
One example of this is in emulation environments when the application under
emulation is 32-bit but the host userspace is 64-bit.
In order to avoid additional copies or address translation, the emulator
can choose an address in the lower 32 bits of the address space and request
that the map be placed there.

This functionality is already supported by the standard UNIX `mmap()`
system call which provides an address hint flag as its first parameter as
well as a `MAP_FIXED` flag which causes the `mmap()` to fail if it cannot
place the map in exactly that location.
This extension proposes to add similar functionality to `vkMapMemory()`.


## Solution Space

It is possible to emulate this functionality using
VK_EXT_external_memory_host by creating a placed memory mapping in some
other way (such as a Linux memfd combined with conventional `mmap()`) and
then importing the resulting map via VK_EXT_external_memory_host.
However, this requires every mappable Vulkan memory allocation to be a host
pointer import which places restrictions available memory types and heaps
and may have significant performance costs.

One alternative approach to the emulation use-case would be to add a
`VK_MEMORY_MAP_32BIT_ADDRESS_BIT_EXT` which does exactly what emulation
wants and places the map in the lower 32 bits.
This is equivalent to the Linux `MAP_32BIT` flag.
However, this is less flexible and may be difficult to implement on Arm
platforms where Linux `mmap()` does not support `MAP_32BIT` since drivers
are likely relying on `mmap()` to implement `vkMapMemory()`.

## Proposal

### Dependencies

 - VK_EXT_map_memory2

### API Features

The following features are exposed by this extension:

[source,c]
----
typedef struct VkPhysicalDeviceMapMemoryPlacedFeaturesEXT {
    VkStructureType    sType;
    void*              pNext;
    VkBool32           memoryMapPlaced;
    VkBool32           memoryMapRangePlaced;
    VkBool32           memoryUnmapReserve;
} VkPhysicalDeviceMapMemoryPlacedFeaturesEXT;

typedef struct VkPhysicalDeviceMapMemoryPlacedPropertiesEXT {
    VkStructureType    sType;
    void*              pNext;
    VkDeviceSize       minPlacedMemoryMapAlignment;
} VkPhysicalDeviceMapMemoryPlacedPropertiesEXT;

typedef enum VkMemoryMapFlagBits {
    VK_MEMORY_MAP_PLACED_BIT_EXT = 0x00000001,
} VkMemoryMapFlagBits;

typedef struct VkMemoryMapPlacedInfoEXT {
    VkStructureType    sType;
    const void*        pNext;
    void*              pPlacedAddress;
} VkMemoryMapPlacedInfoEXT;

typedef enum VkMemoryUnmapFlagBitsKHR {
    VK_MEMORY_UNMAP_RESERVE_BIT_EXT = 0x00000001,
} VkMemoryMapFlagBitsKHR;
----

 . `memoryMapPlaced` indicates support for placed memory maps
 . `memoryMapRangePlaced` indicates support for placed memory maps of
   subranges of the `VkDeviceMemory` object.
 . `memoryUnmapReserve` indicates support for
   `VK_MEMORY_UNMAP_RESERVE_BIT_EXT`.

When `VK_MEMORY_MAP_PLACED_BIT_EXT` is set in the `flags` member of
`VkMemoryMapInfoKHR` and a `VkMapMemoryPlacedInfoEXT` structure is included
in the `pNext` chain of `VkMemoryMapInfoKHR`, the implementation attempts
to place the map at the address specified by
`VkMapMemoryPlacedInfoEXT::pPlacedAddress`.
If the map cannot be placed exactly at the specified virtual address,
`vkMapMemory2KHR()` returns `VK_ERROR_MEMORY_MAP_FAILED`.

When `VK_MEMORY_UNMAP_RESERVE_BIT_EXT` is set in the `flags` member of
`VkMemoryUnmapInfoEXT`, the memory object will be unmapped but the
address range will remain reserved so that another call to `mmap()` or
`vkMapMemory()` will not reuse that address range without an address hint
or `VK_MEMORY_MAP_PLACED_BIT_EXT`, respectively.

## Examples

The following example allocates a memory object and maps it in the lower 32
bit address space, using `mmap()` to select the address.

[source,c]
----
VkDeviceMemory mem;
const VkMemoryAllocateInfo allocInfo = {
    .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
   .allocationSize = /* compute size */,
   .memoryTypeIndex = /* compute memory type */,
};
VK_CHECK(vkAllocateMemory(device, &allocInfo, NULL, &mem));

void *reserved = mmap(NULL, allocInfo.allocationSize, PROT_NONE,
                      MAP_ANONYMOUS | MAP_PRIVATE | MAP_32BIT, -1, 0);
CHECK(reserved != MAP_FAILED);

const VkMemoryMapPlacedInfoEXT mapPlacedInfo = {
    .sType = VK_STRUCTURE_TYPE_MEMORY_MAP_PLACED_INFO_EXT,
    .pPlacedAddress = reserved,
};
const VkMemoryMapInfoKHR mapInfo = {
    .sType = VK_STRUCTURE_TYPE_MEMORY_MAP_INFO_KHR,
    .pNext = &mapPlacedInfo,
    .memory = mem,
    .offset = 0,
    .size = VK_WHOLE_SIZE,
    .flags = VK_MEMORY_MAP_PLACED_BIT_EXT,
};
void *map;
VK_CHECK(vkMapMemory2KHR(device, &mapInfo, &map));
CHECK(map == reserved);
----

## Issues

1) How should the requested address be passed to `vkMapMemory2KHR()`?

*RESOLVED*: When `VK_MEMORY_MAP_PLACED_BIT_EXT` is passed to
`vkMapMemory2KHR()` and a `VkMemoryMapPlacedInfoEXT` structure is present
in the `pNext` chain, the implementation attempts to map to whatever
address is provided by `VkMemoryMapPlacedInfoEXT::pPlacedAddress`.

2) What should happen if the implementation cannot place the memory map at
the requested address?

*RESOLVED*: The memory object should be left unmapped and
flink:vkMapMemory2KHR should return ename:VK_ERROR_MEMORY_MAP_FAILED.

3) How can a client atomically re-reserve the address range on unmap?

*RESOLVED*: When `VK_MEMORY_UNMAP_RESERVE_BIT_EXT` is passed to
`vkUnmapMemory2KHR()`, the implementation unmaps the memory range in such a
way that the range is automatically re-reserved.
With `mmap()`, this is accomplished by simply mapping over the range with
another anonymous mapping.
However, allowing this with `vkMapMemory()` would break Vulkan's concept of
when a memory object is or is not mapped.

4) Should a placed memory map replace existing maps in the specified
range or fail if a map exists?

*RESOLVED*: It should replace existing maps.
If a client wants try-map behavior, it can get that by using mmap with
`MAP_ANONYMOUS` and an address and only call `vkMapMemory2KHR()` to do a
placed map if that succeeds.
Without replacement, there is no way for the application to atomically
exchange maps and prevent races between reservation and the final map.
Also, any attempt at a middle ground in which it might replace would make
the spec less well-defined.