|
9 | 9 | // granted to it by virtue of its status as an Intergovernmental Organization |
10 | 10 | // or submit itself to any jurisdiction. |
11 | 11 |
|
12 | | -#include "GPUReconstructionMetal.h" |
| 12 | +#include "GPUReconstructionMetalIncludesHost.h" // must come first |
| 13 | + |
13 | 14 | #include "GPUConstantMem.h" |
14 | 15 | #include "GPUDefParametersLoad.inc" |
15 | | -#include "GPUReconstructionMetalIncludesHost.h" |
| 16 | +#include "GPUReconstructionMetal.h" |
16 | 17 |
|
17 | 18 | #include <cstddef> |
18 | 19 | #include <map> |
|
100 | 101 | if (mInternals->commandQueues[i] == nil) { |
101 | 102 | GPUErrorReturn("Error creating Metal command queue"); |
102 | 103 | } |
103 | | - mInternals->commandBuffers[i] = [mInternals->commandQueues[i] commandBuffer]; |
| 104 | + mInternals->commandBuffers[i] = [[mInternals->commandQueues[i] commandBuffer] retain]; |
104 | 105 | if (mInternals->commandBuffers[i] == nil) { |
105 | 106 | GPUErrorReturn("Error creating Metal command buffer"); |
106 | 107 | } |
|
113 | 114 |
|
114 | 115 | mHostMemoryBase = mInternals->mem_host.contents; |
115 | 116 | mHostMemorySize = mInternals->mem_host.allocatedSize; |
116 | | - mDeviceMemoryBase = mInternals->mem_gpu.contents; |
| 117 | + // The kernels reconstruct device pointers from these bases, so they must be |
| 118 | + // the addresses the GPU sees, not the CPU-visible ones of a shared buffer. |
| 119 | + mDeviceMemoryBase = (void*)(uintptr_t)mInternals->mem_gpu.gpuAddress; |
117 | 120 | mDeviceMemorySize = mInternals->mem_gpu.allocatedSize; |
118 | | - mDeviceConstantMem = (GPUConstantMem*)mInternals->mem_constant.contents; |
| 121 | + mDeviceConstantMem = (GPUConstantMem*)(uintptr_t)mInternals->mem_constant.gpuAddress; |
119 | 122 |
|
120 | 123 | if (GetProcessingSettings().debugLevel >= 1) { |
121 | 124 | GPUInfo("Memory ptrs: GPU (%ld bytes): %p - Host (%ld bytes): %p", (int64_t)mDeviceMemorySize, mDeviceMemoryBase, (int64_t)mHostMemorySize, mHostMemoryBase); |
|
153 | 156 | [mInternals->functions[i] release]; |
154 | 157 | } |
155 | 158 | mInternals->functions.clear(); |
| 159 | + for (uint32_t i = 0; i < mInternals->pipelines.size(); i++) { |
| 160 | + [mInternals->pipelines[i] release]; |
| 161 | + } |
| 162 | + mInternals->pipelines.clear(); |
| 163 | + mInternals->maxThreadsPerGroup.clear(); |
156 | 164 | } |
157 | 165 | if (mHostMemoryBase) { |
158 | 166 | for (int32_t i = 0; i < mNStreams; i++) { |
159 | | - [mInternals->commandQueues[i] release]; |
| 167 | + DrainStream(i); |
160 | 168 | [mInternals->commandBuffers[i] release]; |
| 169 | + [mInternals->commandQueues[i] release]; |
161 | 170 | } |
162 | 171 | [mInternals->mem_host release]; |
163 | 172 | } |
|
172 | 181 | return (0); |
173 | 182 | } |
174 | 183 |
|
| 184 | +namespace |
| 185 | +{ |
| 186 | +// Where a pointer lives as far as the backend is concerned. mDeviceMemoryBase is |
| 187 | +// a GPU address, so the CPU-visible address of owned memory has to be taken from |
| 188 | +// the buffer rather than from the pointer itself. |
| 189 | +struct MetalMemRef { |
| 190 | + id<MTLBuffer> buffer; // nil for memory the backend does not own |
| 191 | + size_t offset; |
| 192 | + char* cpu; |
| 193 | +}; |
| 194 | + |
| 195 | +MetalMemRef metalResolve(const GPUReconstructionMetalInternals* internals, const void* p, const void* hostBase, size_t hostSize, const void* devBase, size_t devSize) |
| 196 | +{ |
| 197 | + if (p >= hostBase && p < ((const char*)hostBase + hostSize)) { |
| 198 | + const size_t o = (const char*)p - (const char*)hostBase; |
| 199 | + return {internals->mem_host, o, (char*)internals->mem_host.contents + o}; |
| 200 | + } |
| 201 | + if (p >= devBase && p < ((const char*)devBase + devSize)) { |
| 202 | + const size_t o = (const char*)p - (const char*)devBase; |
| 203 | + return {internals->mem_gpu, o, (char*)internals->mem_gpu.contents + o}; |
| 204 | + } |
| 205 | + return {nil, 0, (char*)p}; |
| 206 | +} |
| 207 | +} // namespace |
| 208 | + |
175 | 209 | size_t GPUReconstructionMetal::GPUMemCpy(void* dst, const void* src, size_t sizeBytes, int32_t stream, int32_t toGPU, deviceEvent* ev, deviceEvent* evList, int32_t nEvents) |
176 | 210 | { |
177 | 211 | if (evList == nullptr) { |
|
186 | 220 | } |
187 | 221 |
|
188 | 222 | auto realStream = stream == -1 ? 0 : stream; |
189 | | - id<MTLCommandBuffer> cb = mInternals->commandBuffers[realStream]; |
190 | | - id<MTLBlitCommandEncoder> blit = [cb blitCommandEncoder]; |
191 | | - id<MTLBuffer> sourceBuffer = nil; |
192 | | - id<MTLBuffer> destBuffer = nil; |
193 | | - ptrdiff_t sourceOffset = 0; |
194 | | - ptrdiff_t destOffset = 0; |
195 | | - |
196 | | - if (src >= mHostMemoryBase && src < ((char*)mHostMemoryBase + mHostMemorySize)) { |
197 | | - sourceBuffer = mInternals->mem_host; |
198 | | - sourceOffset = (char*)src - (char*)mHostMemoryBase; |
199 | | - } else if (src >= mDeviceMemoryBase && src < ((char*)mDeviceMemoryBase + mDeviceMemorySize)) { |
200 | | - sourceBuffer = mInternals->mem_gpu; |
201 | | - sourceOffset = (char*)src - (char*)mDeviceMemoryBase; |
| 223 | + const MetalMemRef s = metalResolve(mInternals, src, mHostMemoryBase, mHostMemorySize, mDeviceMemoryBase, mDeviceMemorySize); |
| 224 | + const MetalMemRef d = metalResolve(mInternals, dst, mHostMemoryBase, mHostMemorySize, mDeviceMemoryBase, mDeviceMemorySize); |
| 225 | + |
| 226 | + if (s.buffer == nil || d.buffer == nil) { |
| 227 | + // A blit moves data between MTLBuffers only, and the calibration objects |
| 228 | + // live in ordinary memory. Both pools are shared storage on a unified |
| 229 | + // architecture, so the copy can be done on the host once the stream is idle. |
| 230 | + DrainStream(realStream); |
| 231 | + memcpy(d.cpu, s.cpu, sizeBytes); |
202 | 232 | } else { |
203 | | - GPUErrorReturn("Unknown buffer at %p", src); |
| 233 | + id<MTLCommandBuffer> cb = mInternals->commandBuffers[realStream]; |
| 234 | + id<MTLBlitCommandEncoder> blit = [cb blitCommandEncoder]; |
| 235 | + [blit copyFromBuffer:s.buffer |
| 236 | + sourceOffset:s.offset |
| 237 | + toBuffer:d.buffer |
| 238 | + destinationOffset:d.offset |
| 239 | + size:sizeBytes]; |
| 240 | + [blit endEncoding]; |
| 241 | + CommitStream(realStream); |
204 | 242 | } |
205 | 243 |
|
206 | | - if (dst >= mHostMemoryBase && dst < ((char*)mHostMemoryBase + mHostMemorySize)) { |
207 | | - destBuffer = mInternals->mem_host; |
208 | | - destOffset = (char*)dst - (char*)mHostMemoryBase; |
209 | | - } else if (dst >= mDeviceMemoryBase && dst < ((char*)mDeviceMemoryBase + mDeviceMemorySize)) { |
210 | | - destBuffer = mInternals->mem_gpu; |
211 | | - destOffset = (char*)dst - (char*)mDeviceMemoryBase; |
212 | | - } else { |
213 | | - GPUErrorReturn("Unknown buffer at %p", dst); |
214 | | - } |
215 | | - |
216 | | - [blit copyFromBuffer:sourceBuffer |
217 | | - sourceOffset:sourceOffset |
218 | | - toBuffer:destBuffer |
219 | | - destinationOffset:destOffset |
220 | | - size:sizeBytes]; |
221 | | - |
222 | | - [blit endEncoding]; |
223 | | - [cb commit]; |
224 | | - |
225 | 244 | if (GetProcessingSettings().serializeGPU & 2) { |
226 | 245 | GPUDebug(("GPUMemCpy " + std::to_string(toGPU)).c_str(), stream, true); |
227 | 246 | } |
|
235 | 254 | } |
236 | 255 |
|
237 | 256 | auto realStream = stream == -1 ? 0 : stream; |
238 | | - id<MTLCommandBuffer> cb = mInternals->commandBuffers[realStream]; |
239 | | - id<MTLBlitCommandEncoder> blit = [cb blitCommandEncoder]; |
240 | | - id<MTLBuffer> sourceBuffer = nil; |
241 | | - ptrdiff_t sourceOffset = 0; |
242 | | - if (src >= mHostMemoryBase && src < ((char*)mHostMemoryBase + mHostMemorySize)) { |
243 | | - sourceBuffer = mInternals->mem_host; |
244 | | - sourceOffset = (char*)src - (char*)mHostMemoryBase; |
245 | | - } else if (src >= mDeviceMemoryBase && src < ((char*)mDeviceMemoryBase + mDeviceMemorySize)) { |
246 | | - sourceBuffer = mInternals->mem_gpu; |
247 | | - sourceOffset = (char*)src - (char*)mDeviceMemoryBase; |
| 257 | + const MetalMemRef s = metalResolve(mInternals, src, mHostMemoryBase, mHostMemorySize, mDeviceMemoryBase, mDeviceMemorySize); |
| 258 | + |
| 259 | + if (s.buffer == nil) { |
| 260 | + DrainStream(realStream); |
| 261 | + memcpy((char*)mInternals->mem_constant.contents + offset, s.cpu, size); |
248 | 262 | } else { |
249 | | - GPUErrorReturn("Unknown buffer at %p", src); |
| 263 | + id<MTLCommandBuffer> cb = mInternals->commandBuffers[realStream]; |
| 264 | + id<MTLBlitCommandEncoder> blit = [cb blitCommandEncoder]; |
| 265 | + [blit copyFromBuffer:s.buffer |
| 266 | + sourceOffset:s.offset |
| 267 | + toBuffer:mInternals->mem_constant |
| 268 | + destinationOffset:offset |
| 269 | + size:size]; |
| 270 | + [blit endEncoding]; |
| 271 | + CommitStream(realStream); |
250 | 272 | } |
251 | | - [blit copyFromBuffer:sourceBuffer |
252 | | - sourceOffset:sourceOffset |
253 | | - toBuffer:mInternals->mem_constant |
254 | | - destinationOffset:offset |
255 | | - size:size]; |
256 | | - |
257 | | - [blit endEncoding]; |
258 | | - [cb commit]; |
259 | 273 |
|
260 | 274 | if (GetProcessingSettings().serializeGPU & 2) { |
261 | 275 | GPUDebug("WriteToConstantMemory", stream, true); |
|
278 | 292 | // the lifetime of the events outside here. |
279 | 293 | auto mtlEvent = (__bridge id<MTLSharedEvent>)(ev->get<void*>()); |
280 | 294 | [cb encodeSignalEvent:mtlEvent value:1]; |
| 295 | + CommitStream(stream); |
| 296 | +} |
| 297 | + |
| 298 | +void GPUReconstructionMetal::CommitStream(int32_t stream) |
| 299 | +{ |
| 300 | + id<MTLCommandBuffer> cb = mInternals->commandBuffers[stream]; |
281 | 301 | [cb commit]; |
| 302 | + // Hand the committed buffer over to the stream's submission list, which owns |
| 303 | + // it until it is waited for, and open a fresh one to encode the next work in. |
| 304 | + mInternals->submittedBuffers[stream].emplace_back(cb); |
| 305 | + mInternals->commandBuffers[stream] = [[mInternals->commandQueues[stream] commandBuffer] retain]; |
| 306 | +} |
| 307 | + |
| 308 | +void GPUReconstructionMetal::DrainStream(int32_t stream) |
| 309 | +{ |
| 310 | + // Waiting on the buffer that is currently open would block forever: it is not |
| 311 | + // committed, so it never completes. Only what was submitted can be waited for. |
| 312 | + for (id<MTLCommandBuffer> cb : mInternals->submittedBuffers[stream]) { |
| 313 | + [cb waitUntilCompleted]; |
| 314 | + [cb release]; |
| 315 | + } |
| 316 | + mInternals->submittedBuffers[stream].clear(); |
282 | 317 | } |
283 | 318 |
|
284 | 319 | void GPUReconstructionMetal::SynchronizeGPU() |
285 | 320 | { |
286 | 321 | for (int32_t i = 0; i < mNStreams; i++) { |
287 | | - [mInternals->commandBuffers[i] waitUntilCompleted]; |
| 322 | + DrainStream(i); |
288 | 323 | } |
289 | 324 | } |
290 | 325 |
|
291 | 326 | void GPUReconstructionMetal::SynchronizeStream(int32_t stream) |
292 | 327 | { |
293 | | - [mInternals->commandBuffers[stream] waitUntilCompleted]; |
| 328 | + DrainStream(stream); |
294 | 329 | } |
295 | 330 |
|
296 | 331 | void GPUReconstructionMetal::SynchronizeEvents(deviceEvent* evList, int32_t nEvents) |
297 | 332 | { |
298 | 333 | for (int32_t si = 0; si < mNStreams; si++) { |
299 | | - id<MTLCommandBuffer> cb = mInternals->commandBuffers[si]; |
300 | | - [cb waitUntilCompleted]; |
| 334 | + DrainStream(si); |
301 | 335 | } |
302 | 336 | } |
303 | 337 |
|
|
309 | 343 | auto mtlEvent = (__bridge id<MTLSharedEvent>)(evList[ei].get<void*>()); |
310 | 344 | [cb encodeWaitForEvent:mtlEvent value:1]; |
311 | 345 | } |
312 | | - [cb commit]; |
313 | | - [cb waitUntilCompleted]; |
| 346 | + CommitStream(stream); |
| 347 | + DrainStream(stream); |
314 | 348 | } |
315 | 349 |
|
316 | 350 | bool GPUReconstructionMetal::IsEventDone(deviceEvent* evList, int32_t nEvents) |
|
331 | 365 | return (0); |
332 | 366 | } |
333 | 367 | for (int32_t si = 0; si < mNStreams; si++) { |
334 | | - [mInternals->commandBuffers[si] waitUntilCompleted]; |
| 368 | + DrainStream(si); |
335 | 369 | } |
336 | 370 | if (GetProcessingSettings().debugLevel >= 3) { |
337 | 371 | GPUInfo("GPU Sync Done"); |
|
0 commit comments