Files
renderdoc/renderdoc/driver/metal/metal_device.cpp
T
Jake Turner 97efa7cf04 Capture support for the presented MTLDrawable
Generate the capture thumbnail image from the presented `MTLTexture`
Serialize the capture `PresentedImage` ID in the `CaptureEnd` chunk.
2022-07-21 10:32:32 +01:00

661 lines
21 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2022 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "metal_device.h"
#include "metal_buffer.h"
#include "metal_command_queue.h"
#include "metal_function.h"
#include "metal_helpers_bridge.h"
#include "metal_library.h"
#include "metal_manager.h"
#include "metal_render_pipeline_state.h"
#include "metal_texture.h"
WrappedMTLDevice::WrappedMTLDevice(MTL::Device *realMTLDevice, ResourceId objId)
: WrappedMTLObject(realMTLDevice, objId, this, GetStateRef()), m_Capturer(*this)
{
AllocateObjCBridge(this);
m_Device = this;
if(RenderDoc::Inst().IsReplayApp())
{
}
else
{
m_State = CaptureState::BackgroundCapturing;
}
m_SectionVersion = MetalInitParams::CurrentVersion;
threadSerialiserTLSSlot = Threading::AllocateTLSSlot();
m_ResourceManager = new MetalResourceManager(m_State, this);
if(!RenderDoc::Inst().IsReplayApp())
{
m_FrameCaptureRecord = GetResourceManager()->AddResourceRecord(ResourceIDGen::GetNewUniqueID());
m_FrameCaptureRecord->DataInSerialiser = false;
m_FrameCaptureRecord->Length = 0;
m_FrameCaptureRecord->InternalResource = true;
}
else
{
m_FrameCaptureRecord = NULL;
ResourceIDGen::SetReplayResourceIDs();
}
RDCASSERT(m_Device == this);
GetResourceManager()->AddCurrentResource(objId, this);
if(IsCaptureMode(m_State))
{
Chunk *chunk = NULL;
{
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CHUNK(MetalChunk::MTLCreateSystemDefaultDevice);
Serialise_MTLCreateSystemDefaultDevice(ser);
chunk = scope.Get();
}
MetalResourceRecord *record = GetResourceManager()->AddResourceRecord(this);
record->AddChunk(chunk);
}
else
{
// TODO: implement RD MTL replay
}
RenderDoc::Inst().AddDeviceFrameCapturer(this, &m_Capturer);
m_mtlCommandQueue = Unwrap(this)->newCommandQueue();
FirstFrame();
}
IMP WrappedMTLDevice::g_real_CAMetalLayer_nextDrawable;
uint64_t WrappedMTLDevice::g_nextDrawableTLSSlot;
MTL::Drawable *hooked_CAMetalLayer_nextDrawable(id self, SEL _cmd)
{
CA::MetalLayer *mtlLayer = (CA::MetalLayer *)self;
MTL::Device *mtlDevice = mtlLayer->device();
RDCASSERT(object_getClass(mtlDevice) == objc_getClass("ObjCBridgeMTLDevice"));
GetWrapped(mtlDevice)->RegisterMetalLayer(mtlLayer);
mtlLayer->setFramebufferOnly(false);
RDCASSERTEQUAL(Threading::GetTLSValue(WrappedMTLDevice::g_nextDrawableTLSSlot), 0);
Threading::SetTLSValue(WrappedMTLDevice::g_nextDrawableTLSSlot, (void *)(uintptr_t) true);
MTL::Drawable *drawable =
((MTL::Drawable * (*)(id, SEL))WrappedMTLDevice::g_real_CAMetalLayer_nextDrawable)(self, _cmd);
Threading::SetTLSValue(WrappedMTLDevice::g_nextDrawableTLSSlot, (void *)(uintptr_t) false);
return drawable;
}
void WrappedMTLDevice::MTLHookObjcMethods()
{
static bool s_hookObjcMethods = false;
if(s_hookObjcMethods)
return;
g_nextDrawableTLSSlot = Threading::AllocateTLSSlot();
Threading::SetTLSValue(WrappedMTLDevice::g_nextDrawableTLSSlot, (void *)(uintptr_t) false);
Method m =
class_getInstanceMethod(objc_lookUpClass("CAMetalLayer"), sel_registerName("nextDrawable"));
g_real_CAMetalLayer_nextDrawable =
method_setImplementation(m, (IMP)hooked_CAMetalLayer_nextDrawable);
s_hookObjcMethods = true;
}
void WrappedMTLDevice::MTLFixupForMetalDriverAssert()
{
static bool s_fixupMetalDriverAssert = false;
if(s_fixupMetalDriverAssert)
return;
RDCLOG(
"Fixup for Metal Driver debug assert. Adding protocol `MTLTextureImplementation` to "
"`ObjCBridgeMTLTexture`");
class_addProtocol(objc_lookUpClass("ObjCBridgeMTLTexture"),
objc_getProtocol("MTLTextureImplementation"));
s_fixupMetalDriverAssert = true;
}
// Serialised MTLDevice APIs
template <typename SerialiserType>
bool WrappedMTLDevice::Serialise_MTLCreateSystemDefaultDevice(SerialiserType &ser)
{
SERIALISE_ELEMENT_LOCAL(Device, GetResID(this)).TypedAs("MTLDevice"_lit);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
// TODO: implement RD MTL replay
}
return true;
}
WrappedMTLDevice *WrappedMTLDevice::MTLCreateSystemDefaultDevice(MTL::Device *realMTLDevice)
{
MTLFixupForMetalDriverAssert();
MTLHookObjcMethods();
ResourceId objId = ResourceIDGen::GetNewUniqueID();
WrappedMTLDevice *wrappedMTLDevice = new WrappedMTLDevice(realMTLDevice, objId);
return wrappedMTLDevice;
}
template <typename SerialiserType>
bool WrappedMTLDevice::Serialise_newCommandQueue(SerialiserType &ser, WrappedMTLCommandQueue *queue)
{
SERIALISE_ELEMENT_LOCAL(Device, this);
SERIALISE_ELEMENT_LOCAL(CommandQueue, GetResID(queue)).TypedAs("MTLCommandQueue"_lit);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
// TODO: implement RD MTL replay
}
return true;
}
WrappedMTLCommandQueue *WrappedMTLDevice::newCommandQueue()
{
MTL::CommandQueue *realMTLCommandQueue;
SERIALISE_TIME_CALL(realMTLCommandQueue = Unwrap(this)->newCommandQueue());
WrappedMTLCommandQueue *wrappedMTLCommandQueue;
ResourceId id = GetResourceManager()->WrapResource(realMTLCommandQueue, wrappedMTLCommandQueue);
if(IsCaptureMode(m_State))
{
Chunk *chunk = NULL;
{
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CHUNK(MetalChunk::MTLDevice_newCommandQueue);
Serialise_newCommandQueue(ser, wrappedMTLCommandQueue);
chunk = scope.Get();
}
MetalResourceRecord *record = GetResourceManager()->AddResourceRecord(wrappedMTLCommandQueue);
record->AddChunk(chunk);
}
else
{
// TODO: implement RD MTL replay
// GetResourceManager()->AddLiveResource(id, wrappedMTLCommandQueue);
}
return wrappedMTLCommandQueue;
}
template <typename SerialiserType>
bool WrappedMTLDevice::Serialise_newDefaultLibrary(SerialiserType &ser, WrappedMTLLibrary *library)
{
bytebuf data;
if(ser.IsWriting())
{
ObjC::Get_defaultLibraryData(data);
}
SERIALISE_ELEMENT_LOCAL(Device, this);
SERIALISE_ELEMENT_LOCAL(Library, GetResID(library)).TypedAs("MTLLibrary"_lit);
SERIALISE_ELEMENT(data);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
// TODO: implement RD MTL replay
}
return true;
}
WrappedMTLLibrary *WrappedMTLDevice::newDefaultLibrary()
{
MTL::Library *realMTLLibrary;
SERIALISE_TIME_CALL(realMTLLibrary = Unwrap(this)->newDefaultLibrary());
WrappedMTLLibrary *wrappedMTLLibrary;
ResourceId id = GetResourceManager()->WrapResource(realMTLLibrary, wrappedMTLLibrary);
if(IsCaptureMode(m_State))
{
Chunk *chunk = NULL;
{
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CHUNK(MetalChunk::MTLDevice_newDefaultLibrary);
Serialise_newDefaultLibrary(ser, wrappedMTLLibrary);
chunk = scope.Get();
}
MetalResourceRecord *record = GetResourceManager()->AddResourceRecord(wrappedMTLLibrary);
record->AddChunk(chunk);
}
else
{
// TODO: implement RD MTL replay
// GetResourceManager()->AddLiveResource(id, wrappedMTLLibrary);
}
return wrappedMTLLibrary;
}
template <typename SerialiserType>
bool WrappedMTLDevice::Serialise_newLibraryWithSource(SerialiserType &ser,
WrappedMTLLibrary *library, NS::String *source,
MTL::CompileOptions *options, NS::Error **error)
{
SERIALISE_ELEMENT_LOCAL(Device, this);
SERIALISE_ELEMENT_LOCAL(Library, GetResID(library)).TypedAs("MTLLibrary"_lit);
SERIALISE_ELEMENT(source);
// TODO:SERIALISE_ELEMENT(options);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
// TODO: implement RD MTL replay
}
return true;
}
WrappedMTLLibrary *WrappedMTLDevice::newLibraryWithSource(NS::String *source,
MTL::CompileOptions *options,
NS::Error **error)
{
MTL::Library *realMTLLibrary;
SERIALISE_TIME_CALL(realMTLLibrary = Unwrap(this)->newLibrary(source, options, error));
WrappedMTLLibrary *wrappedMTLLibrary;
ResourceId id = GetResourceManager()->WrapResource(realMTLLibrary, wrappedMTLLibrary);
if(IsCaptureMode(m_State))
{
Chunk *chunk = NULL;
{
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CHUNK(MetalChunk::MTLDevice_newLibraryWithSource);
Serialise_newLibraryWithSource(ser, wrappedMTLLibrary, source, options, error);
chunk = scope.Get();
}
MetalResourceRecord *record = GetResourceManager()->AddResourceRecord(wrappedMTLLibrary);
record->AddChunk(chunk);
}
else
{
// TODO: implement RD MTL replay
// GetResourceManager()->AddLiveResource(id, wrappedMTLLibrary);
}
return wrappedMTLLibrary;
}
template <typename SerialiserType>
bool WrappedMTLDevice::Serialise_newBufferWithBytes(SerialiserType &ser, WrappedMTLBuffer *buffer,
const void *pointer, NS::UInteger length,
MTL::ResourceOptions options)
{
SERIALISE_ELEMENT_LOCAL(Buffer, GetResID(buffer)).TypedAs("MTLBuffer"_lit);
bytebuf initialData;
if(pointer)
{
initialData.assign((byte *)pointer, length);
}
SERIALISE_ELEMENT(initialData);
SERIALISE_ELEMENT(length).Important();
SERIALISE_ELEMENT(options);
SERIALISE_CHECK_READ_ERRORS();
// TODO: implement RD MTL replay
if(IsReplayingAndReading())
{
}
return true;
}
WrappedMTLBuffer *WrappedMTLDevice::newBufferWithBytes(const void *pointer, NS::UInteger length,
MTL::ResourceOptions options)
{
return Common_NewBuffer(true, pointer, length, options);
}
WrappedMTLBuffer *WrappedMTLDevice::newBufferWithLength(NS::UInteger length,
MTL::ResourceOptions options)
{
return Common_NewBuffer(false, NULL, length, options);
}
template <typename SerialiserType>
bool WrappedMTLDevice::Serialise_newRenderPipelineStateWithDescriptor(
SerialiserType &ser, WrappedMTLRenderPipelineState *pipelineState,
RDMTL::RenderPipelineDescriptor &descriptor, NS::Error **error)
{
SERIALISE_ELEMENT_LOCAL(RenderPipelineState, GetResID(pipelineState))
.TypedAs("MTLRenderPipelineState"_lit);
SERIALISE_ELEMENT(descriptor);
SERIALISE_CHECK_READ_ERRORS();
// TODO: implement RD MTL replay
if(IsReplayingAndReading())
{
}
return true;
}
WrappedMTLRenderPipelineState *WrappedMTLDevice::newRenderPipelineStateWithDescriptor(
RDMTL::RenderPipelineDescriptor &descriptor, NS::Error **error)
{
MTL::RenderPipelineDescriptor *realDescriptor(descriptor);
MTL::RenderPipelineState *realMTLRenderPipelineState;
SERIALISE_TIME_CALL(realMTLRenderPipelineState =
Unwrap(this)->newRenderPipelineState(realDescriptor, error));
realDescriptor->release();
WrappedMTLRenderPipelineState *wrappedMTLRenderPipelineState;
ResourceId id =
GetResourceManager()->WrapResource(realMTLRenderPipelineState, wrappedMTLRenderPipelineState);
if(IsCaptureMode(m_State))
{
Chunk *chunk = NULL;
{
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CHUNK(MetalChunk::MTLDevice_newRenderPipelineStateWithDescriptor);
Serialise_newRenderPipelineStateWithDescriptor(ser, wrappedMTLRenderPipelineState, descriptor,
error);
chunk = scope.Get();
}
MetalResourceRecord *record =
GetResourceManager()->AddResourceRecord(wrappedMTLRenderPipelineState);
record->AddChunk(chunk);
if(descriptor.vertexFunction)
{
record->AddParent(GetRecord(descriptor.vertexFunction));
}
if(descriptor.fragmentFunction)
{
record->AddParent(GetRecord(descriptor.fragmentFunction));
}
}
else
{
// TODO: implement RD MTL replay
// GetResourceManager()->AddLiveResource(id, *wrappedMTLRenderPipelineState);
}
return wrappedMTLRenderPipelineState;
}
template <typename SerialiserType>
bool WrappedMTLDevice::Serialise_newTextureWithDescriptor(SerialiserType &ser,
WrappedMTLTexture *texture,
RDMTL::TextureDescriptor &descriptor)
{
SERIALISE_ELEMENT_LOCAL(Texture, GetResID(texture)).TypedAs("MTLTexture"_lit);
SERIALISE_ELEMENT(descriptor);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
}
return true;
}
WrappedMTLTexture *WrappedMTLDevice::newTextureWithDescriptor(RDMTL::TextureDescriptor &descriptor)
{
return Common_NewTexture(descriptor, MetalChunk::MTLDevice_newTextureWithDescriptor, false, NULL,
0);
}
WrappedMTLTexture *WrappedMTLDevice::newTextureWithDescriptor(RDMTL::TextureDescriptor &descriptor,
IOSurfaceRef iosurface,
NS::UInteger plane)
{
bool nextDrawable = (bool)(uintptr_t)Threading::GetTLSValue(g_nextDrawableTLSSlot);
return Common_NewTexture(descriptor,
nextDrawable ? MetalChunk::MTLDevice_newTextureWithDescriptor_nextDrawable
: MetalChunk::MTLDevice_newTextureWithDescriptor_iosurface,
true, iosurface, plane);
}
// Non-Serialised MTLDevice APIs
bool WrappedMTLDevice::isDepth24Stencil8PixelFormatSupported()
{
return Unwrap(this)->depth24Stencil8PixelFormatSupported();
}
MTL::ReadWriteTextureTier WrappedMTLDevice::readWriteTextureSupport()
{
return Unwrap(this)->readWriteTextureSupport();
}
MTL::ArgumentBuffersTier WrappedMTLDevice::argumentBuffersSupport()
{
return Unwrap(this)->argumentBuffersSupport();
}
bool WrappedMTLDevice::areRasterOrderGroupsSupported()
{
return Unwrap(this)->rasterOrderGroupsSupported();
}
bool WrappedMTLDevice::supports32BitFloatFiltering()
{
return Unwrap(this)->supports32BitFloatFiltering();
}
bool WrappedMTLDevice::supports32BitMSAA()
{
return Unwrap(this)->supports32BitMSAA();
}
bool WrappedMTLDevice::supportsQueryTextureLOD()
{
return Unwrap(this)->supportsQueryTextureLOD();
}
bool WrappedMTLDevice::supportsBCTextureCompression()
{
return Unwrap(this)->supportsBCTextureCompression();
}
bool WrappedMTLDevice::supportsPullModelInterpolation()
{
return Unwrap(this)->supportsPullModelInterpolation();
}
bool WrappedMTLDevice::areBarycentricCoordsSupported()
{
return Unwrap(this)->barycentricCoordsSupported();
}
bool WrappedMTLDevice::supportsShaderBarycentricCoordinates()
{
return Unwrap(this)->supportsShaderBarycentricCoordinates();
}
bool WrappedMTLDevice::supportsFeatureSet(MTL::FeatureSet featureSet)
{
return Unwrap(this)->supportsFeatureSet(featureSet);
}
bool WrappedMTLDevice::supportsFamily(MTL::GPUFamily gpuFamily)
{
return Unwrap(this)->supportsFamily(gpuFamily);
}
bool WrappedMTLDevice::supportsTextureSampleCount(NS::UInteger sampleCount)
{
return Unwrap(this)->supportsTextureSampleCount(sampleCount);
}
bool WrappedMTLDevice::areProgrammableSamplePositionsSupported()
{
return Unwrap(this)->programmableSamplePositionsSupported();
}
bool WrappedMTLDevice::supportsRasterizationRateMapWithLayerCount(NS::UInteger layerCount)
{
return Unwrap(this)->supportsRasterizationRateMap(layerCount);
}
bool WrappedMTLDevice::supportsCounterSampling(MTL::CounterSamplingPoint samplingPoint)
{
return Unwrap(this)->supportsCounterSampling(samplingPoint);
}
bool WrappedMTLDevice::supportsVertexAmplificationCount(NS::UInteger count)
{
return Unwrap(this)->supportsVertexAmplificationCount(count);
}
bool WrappedMTLDevice::supportsDynamicLibraries()
{
return Unwrap(this)->supportsDynamicLibraries();
}
bool WrappedMTLDevice::supportsRenderDynamicLibraries()
{
return Unwrap(this)->supportsRenderDynamicLibraries();
}
bool WrappedMTLDevice::supportsRaytracing()
{
// RD device does not support ray tracing
return false;
}
bool WrappedMTLDevice::supportsFunctionPointers()
{
return Unwrap(this)->supportsFunctionPointers();
}
bool WrappedMTLDevice::supportsFunctionPointersFromRender()
{
return Unwrap(this)->supportsFunctionPointersFromRender();
}
bool WrappedMTLDevice::supportsRaytracingFromRender()
{
// RD device does not support ray tracing
return false;
}
bool WrappedMTLDevice::supportsPrimitiveMotionBlur()
{
return Unwrap(this)->supportsPrimitiveMotionBlur();
}
// End of MTLDevice APIs
WrappedMTLTexture *WrappedMTLDevice::Common_NewTexture(RDMTL::TextureDescriptor &descriptor,
MetalChunk chunkType, bool ioSurfaceTexture,
IOSurfaceRef iosurface, NS::UInteger plane)
{
MTL::Texture *realMTLTexture;
MTL::TextureDescriptor *realDescriptor(descriptor);
SERIALISE_TIME_CALL(realMTLTexture = !ioSurfaceTexture ? Unwrap(this)->newTexture(realDescriptor)
: Unwrap(this)->newTexture(
realDescriptor, iosurface, plane));
realDescriptor->release();
WrappedMTLTexture *wrappedMTLTexture;
ResourceId id = GetResourceManager()->WrapResource(realMTLTexture, wrappedMTLTexture);
if(IsCaptureMode(m_State))
{
RDMTL::TextureDescriptor rdDescriptor(descriptor);
Chunk *chunk = NULL;
{
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CHUNK(chunkType);
Serialise_newTextureWithDescriptor(ser, wrappedMTLTexture, rdDescriptor);
chunk = scope.Get();
}
MetalResourceRecord *textureRecord = GetResourceManager()->AddResourceRecord(wrappedMTLTexture);
textureRecord->AddChunk(chunk);
}
if(ioSurfaceTexture)
{
if(IsCaptureMode(m_State))
{
{
SCOPED_LOCK(m_CapturePotentialBackBuffersLock);
m_CapturePotentialBackBuffers.insert(wrappedMTLTexture);
}
}
}
return wrappedMTLTexture;
}
WrappedMTLBuffer *WrappedMTLDevice::Common_NewBuffer(bool withBytes, const void *pointer,
NS::UInteger length,
MTL::ResourceOptions options)
{
MTL::Buffer *realMTLBuffer;
SERIALISE_TIME_CALL(realMTLBuffer = withBytes ? Unwrap(this)->newBuffer(pointer, length, options)
: Unwrap(this)->newBuffer(length, options));
WrappedMTLBuffer *wrappedMTLBuffer;
ResourceId id = GetResourceManager()->WrapResource(realMTLBuffer, wrappedMTLBuffer);
if(IsCaptureMode(m_State))
{
Chunk *chunk = NULL;
{
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CHUNK(withBytes ? MetalChunk::MTLDevice_newBufferWithBytes
: MetalChunk::MTLDevice_newBufferWithLength);
Serialise_newBufferWithBytes(ser, wrappedMTLBuffer, pointer, length, options);
chunk = scope.Get();
}
MetalResourceRecord *record = GetResourceManager()->AddResourceRecord(wrappedMTLBuffer);
record->AddChunk(chunk);
}
else
{
// TODO: implement RD MTL replay
}
return wrappedMTLBuffer;
}
INSTANTIATE_FUNCTION_SERIALISED(WrappedMTLDevice, bool, MTLCreateSystemDefaultDevice);
INSTANTIATE_FUNCTION_WITH_RETURN_SERIALISED(WrappedMTLDevice, WrappedMTLCommandQueue *,
newCommandQueue);
INSTANTIATE_FUNCTION_WITH_RETURN_SERIALISED(WrappedMTLDevice, WrappedMTLLibrary *, newDefaultLibrary);
INSTANTIATE_FUNCTION_WITH_RETURN_SERIALISED(WrappedMTLDevice, WrappedMTLLibrary *,
newLibraryWithSource, NS::String *source,
MTL::CompileOptions *options, NS::Error **error);
INSTANTIATE_FUNCTION_WITH_RETURN_SERIALISED(WrappedMTLDevice,
WrappedMTLRenderPipelineState *renderPipelineState,
newRenderPipelineStateWithDescriptor,
RDMTL::RenderPipelineDescriptor &descriptor,
NS::Error **error);
INSTANTIATE_FUNCTION_WITH_RETURN_SERIALISED(WrappedMTLDevice, WrappedMTLTexture *,
newTextureWithDescriptor,
RDMTL::TextureDescriptor &descriptor);
INSTANTIATE_FUNCTION_WITH_RETURN_SERIALISED(WrappedMTLDevice, WrappedMTLBuffer *,
newBufferWithBytes, const void *pointer,
NS::UInteger length, MTL::ResourceOptions options);