mirror of
https://github.com/Kbz-8/Pulse.git
synced 2026-01-11 07:23:35 +00:00
adding device selection, buffer creation and pipeline creation to Metal backend
This commit is contained in:
@@ -9,7 +9,22 @@
|
||||
#ifndef PULSE_METAL_H_
|
||||
#define PULSE_METAL_H_
|
||||
|
||||
PulseBackendFlags MetalCheckSupport(PulseBackendFlags candidates, PulseShaderFormatsFlags shader_formats_used); // Return PULSE_BACKEND_METAL in case of success and PULSE_BACKEND_INVALID otherwise
|
||||
#define METAL_RETRIEVE_DRIVER_DATA_AS(handle, cast) ((cast)handle->driver_data)
|
||||
|
||||
#define CHECK_METAL_RETVAL(backend, handle, error, retval) \
|
||||
do { \
|
||||
if(!(handle)) \
|
||||
{ \
|
||||
if(backend != PULSE_NULL_HANDLE && PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(backend)) \
|
||||
PulseLogError(backend, "(Metal) call to a Metal function failed"); \
|
||||
PulseSetInternalError(error); \
|
||||
return retval; \
|
||||
} \
|
||||
} while(0) \
|
||||
|
||||
#define CHECK_METAL(backend, handle, error) CHECK_METAL_RETVAL(backend, handle, error, )
|
||||
|
||||
PulseBackendFlags MetalCheckSupport(PulseBackendFlags candidates, PulseShaderFormatsFlags shader_formats_used); // Returns corresponding PULSE_BACKEND enum in case of success and PULSE_BACKEND_INVALID otherwise
|
||||
|
||||
#endif // PULSE_METAL_H_
|
||||
|
||||
|
||||
@@ -6,20 +6,32 @@
|
||||
#include "../../PulseInternal.h"
|
||||
|
||||
#include "Metal.h"
|
||||
#include "MetalDevice.h"
|
||||
|
||||
PulseBackendFlags MetalCheckSupport(PulseBackendFlags candidates, PulseShaderFormatsFlags shader_formats_used)
|
||||
{
|
||||
if(candidates != PULSE_BACKEND_ANY && (candidates & PULSE_BACKEND_METAL) == 0)
|
||||
return PULSE_BACKEND_INVALID;
|
||||
if((shader_formats_used & PULSE_SHADER_FORMAT_MSL_BIT) == 0 && (shader_formats_used & PULSE_SHADER_FORMAT_METALLIB_BIT) == 0)
|
||||
if((shader_formats_used & (PULSE_SHADER_FORMAT_MSL_BIT | PULSE_SHADER_FORMAT_METALLIB_BIT)) == 0)
|
||||
return PULSE_BACKEND_INVALID;
|
||||
return PULSE_BACKEND_INVALID; // Not supported yet
|
||||
return PULSE_BACKEND_METAL;
|
||||
}
|
||||
|
||||
bool MetalLoadBackend(PulseBackend backend, PulseDebugLevel debug_level)
|
||||
{
|
||||
PULSE_UNUSED(backend);
|
||||
PULSE_UNUSED(debug_level);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MetalUnloadBackend(PulseBackend backend)
|
||||
{
|
||||
}
|
||||
|
||||
PulseBackendHandler MetalDriver = {
|
||||
.PFN_LoadBackend = PULSE_NULLPTR,
|
||||
.PFN_UnloadBackend = PULSE_NULLPTR,
|
||||
.PFN_CreateDevice = PULSE_NULLPTR,
|
||||
.PFN_LoadBackend = MetalLoadBackend,
|
||||
.PFN_UnloadBackend = MetalUnloadBackend,
|
||||
.PFN_CreateDevice = MetalCreateDevice,
|
||||
.backend = PULSE_BACKEND_METAL,
|
||||
.supported_shader_formats = PULSE_SHADER_FORMAT_MSL_BIT | PULSE_SHADER_FORMAT_METALLIB_BIT,
|
||||
.driver_data = PULSE_NULLPTR
|
||||
|
||||
29
Sources/Backends/Metal/MetalBuffer.h
git.filemode.normal_file
29
Sources/Backends/Metal/MetalBuffer.h
git.filemode.normal_file
@@ -0,0 +1,29 @@
|
||||
// Copyright (C) 2025 kanel
|
||||
// This file is part of "Pulse"
|
||||
// For conditions of distribution and use, see copyright notice in LICENSE
|
||||
|
||||
#include <Pulse.h>
|
||||
|
||||
#ifdef PULSE_ENABLE_METAL_BACKEND
|
||||
|
||||
#ifndef PULSE_METAL_BUFFER_H_
|
||||
#define PULSE_METAL_BUFFER_H_
|
||||
|
||||
#include <Metal/Metal.h>
|
||||
#include "Metal.h"
|
||||
|
||||
typedef struct MetalBuffer
|
||||
{
|
||||
id<MTLBuffer> buffer;
|
||||
} MetalBuffer;
|
||||
|
||||
PulseBuffer MetalCreateBuffer(PulseDevice device, const PulseBufferCreateInfo* create_infos);
|
||||
bool MetalMapBuffer(PulseBuffer buffer, PulseMapMode mode, void** data);
|
||||
void MetalUnmapBuffer(PulseBuffer buffer);
|
||||
bool MetalCopyBufferToBuffer(PulseCommandList cmd, const PulseBufferRegion* src, const PulseBufferRegion* dst);
|
||||
bool MetalCopyBufferToImage(PulseCommandList cmd, const PulseBufferRegion* src, const PulseImageRegion* dst);
|
||||
void MetalDestroyBuffer(PulseDevice device, PulseBuffer buffer);
|
||||
|
||||
#endif // PULSE_METAL_BUFFER_H_
|
||||
|
||||
#endif // PULSE_ENABLE_METAL_BACKEND
|
||||
73
Sources/Backends/Metal/MetalBuffer.m
git.filemode.normal_file
73
Sources/Backends/Metal/MetalBuffer.m
git.filemode.normal_file
@@ -0,0 +1,73 @@
|
||||
// Copyright (C) 2025 kanel
|
||||
// This file is part of "Pulse"
|
||||
// For conditions of distribution and use, see copyright notice in LICENSE
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <Pulse.h>
|
||||
#include "../../PulseInternal.h"
|
||||
#include "Metal.h"
|
||||
#include "MetalBuffer.h"
|
||||
#include "MetalDevice.h"
|
||||
#include "MetalCommandList.h"
|
||||
|
||||
PulseBuffer MetalCreateBuffer(PulseDevice device, const PulseBufferCreateInfo* create_infos)
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
MetalDevice* metal_device = (MetalDevice*)METAL_RETRIEVE_DRIVER_DATA_AS(device, MetalDevice*);
|
||||
|
||||
PulseBuffer buffer = (PulseBuffer)calloc(1, sizeof(PulseBufferHandler));
|
||||
PULSE_CHECK_ALLOCATION_RETVAL(buffer, PULSE_NULL_HANDLE);
|
||||
|
||||
MetalBuffer* metal_buffer = (MetalBuffer*)calloc(1, sizeof(MetalBuffer));
|
||||
PULSE_CHECK_ALLOCATION_RETVAL(metal_buffer, PULSE_NULL_HANDLE);
|
||||
|
||||
uint32_t size = PULSE_ALIGN_UP(create_infos->size, 4);
|
||||
|
||||
buffer->device = device;
|
||||
buffer->driver_data = metal_buffer;
|
||||
buffer->size = size;
|
||||
buffer->usage = create_infos->usage;
|
||||
|
||||
MTLResourceOptions options;
|
||||
if((create_infos->usage & (PULSE_BUFFER_USAGE_TRANSFER_UPLOAD | PULSE_BUFFER_USAGE_TRANSFER_DOWNLOAD)) == 0) // Is storage only
|
||||
options = MTLResourceStorageModePrivate;
|
||||
else if((create_infos->usage & PULSE_BUFFER_USAGE_TRANSFER_DOWNLOAD) == 0)
|
||||
options = MTLResourceCPUCacheModeWriteCombined;
|
||||
else
|
||||
options = MTLResourceCPUCacheModeDefaultCache;
|
||||
|
||||
metal_buffer->buffer = [metal_device->device newBufferWithLength:size options:options];
|
||||
CHECK_METAL_RETVAL(device->backend, metal_buffer->buffer, PULSE_ERROR_INITIALIZATION_FAILED, PULSE_NULL_HANDLE);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
bool MetalMapBuffer(PulseBuffer buffer, PulseMapMode mode, void** data)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void MetalUnmapBuffer(PulseBuffer buffer)
|
||||
{
|
||||
}
|
||||
|
||||
bool MetalCopyBufferToBuffer(PulseCommandList cmd, const PulseBufferRegion* src, const PulseBufferRegion* dst)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MetalCopyBufferToImage(PulseCommandList cmd, const PulseBufferRegion* src, const PulseImageRegion* dst)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void MetalDestroyBuffer(PulseDevice device, PulseBuffer buffer)
|
||||
{
|
||||
MetalBuffer* metal_buffer = METAL_RETRIEVE_DRIVER_DATA_AS(buffer, MetalBuffer*);
|
||||
metal_buffer->buffer = nil;
|
||||
free(metal_buffer);
|
||||
free(buffer);
|
||||
}
|
||||
29
Sources/Backends/Metal/MetalCommandList.h
git.filemode.normal_file
29
Sources/Backends/Metal/MetalCommandList.h
git.filemode.normal_file
@@ -0,0 +1,29 @@
|
||||
// Copyright (C) 2025 kanel
|
||||
// This file is part of "Pulse"
|
||||
// For conditions of distribution and use, see copyright notice in LICENSE
|
||||
|
||||
#include <Pulse.h>
|
||||
|
||||
#ifdef PULSE_ENABLE_METAL_BACKEND
|
||||
|
||||
#ifndef PULSE_METAL_COMMAND_LIST_H_
|
||||
#define PULSE_METAL_COMMAND_LIST_H_
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <tinycthread.h>
|
||||
|
||||
#include "Metal.h"
|
||||
#include "MetalFence.h"
|
||||
|
||||
typedef struct MetalCommandList
|
||||
{
|
||||
int dummy;
|
||||
} MetalCommandList;
|
||||
|
||||
PulseCommandList MetalRequestCommandList(PulseDevice device, PulseCommandListUsage usage);
|
||||
bool MetalSubmitCommandList(PulseDevice device, PulseCommandList cmd, PulseFence fence);
|
||||
void MetalReleaseCommandList(PulseDevice device, PulseCommandList cmd);
|
||||
|
||||
#endif // PULSE_METAL_COMMAND_LIST_H_
|
||||
|
||||
#endif // PULSE_ENABLE_METAL_BACKEND
|
||||
29
Sources/Backends/Metal/MetalCommandList.m
git.filemode.normal_file
29
Sources/Backends/Metal/MetalCommandList.m
git.filemode.normal_file
@@ -0,0 +1,29 @@
|
||||
// Copyright (C) 2025 kanel
|
||||
// This file is part of "Pulse"
|
||||
// For conditions of distribution and use, see copyright notice in LICENSE
|
||||
|
||||
#include <Pulse.h>
|
||||
#include "../../PulseInternal.h"
|
||||
#include "Metal.h"
|
||||
#include "MetalFence.h"
|
||||
#include "MetalDevice.h"
|
||||
#include "MetalCommandList.h"
|
||||
#include "MetalComputePass.h"
|
||||
#include "MetalComputePipeline.h"
|
||||
#include "MetalBuffer.h"
|
||||
|
||||
PulseCommandList MetalRequestCommandList(PulseDevice device, PulseCommandListUsage usage)
|
||||
{
|
||||
PULSE_CHECK_HANDLE_RETVAL(device, PULSE_NULL_HANDLE);
|
||||
PulseCommandList cmd = (PulseCommandList)calloc(1, sizeof(PulseCommandListHandler));
|
||||
PULSE_CHECK_ALLOCATION_RETVAL(cmd, PULSE_NULL_HANDLE);
|
||||
return cmd;
|
||||
}
|
||||
|
||||
bool MetalSubmitCommandList(PulseDevice device, PulseCommandList cmd, PulseFence fence)
|
||||
{
|
||||
}
|
||||
|
||||
void MetalReleaseCommandList(PulseDevice device, PulseCommandList cmd)
|
||||
{
|
||||
}
|
||||
27
Sources/Backends/Metal/MetalComputePass.h
git.filemode.normal_file
27
Sources/Backends/Metal/MetalComputePass.h
git.filemode.normal_file
@@ -0,0 +1,27 @@
|
||||
// Copyright (C) 2025 kanel
|
||||
// This file is part of "Pulse"
|
||||
// For conditions of distribution and use, see copyright notice in LICENSE
|
||||
|
||||
#include <Pulse.h>
|
||||
|
||||
#ifdef PULSE_ENABLE_METAL_BACKEND
|
||||
|
||||
#ifndef PULSE_METAL_COMPUTE_PASS_H_
|
||||
#define PULSE_METAL_COMPUTE_PASS_H_
|
||||
|
||||
#include "Metal.h"
|
||||
|
||||
PulseComputePass MetalCreateComputePass(PulseDevice device, PulseCommandList cmd);
|
||||
void MetalDestroyComputePass(PulseDevice device, PulseComputePass pass);
|
||||
|
||||
PulseComputePass MetalBeginComputePass(PulseCommandList cmd);
|
||||
void MetalEndComputePass(PulseComputePass pass);
|
||||
void MetalBindStorageBuffers(PulseComputePass pass, const PulseBuffer* buffers, uint32_t num_buffers);
|
||||
void MetalBindUniformData(PulseComputePass pass, uint32_t slot, const void* data, uint32_t data_size);
|
||||
void MetalBindStorageImages(PulseComputePass pass, const PulseImage* images, uint32_t num_images);
|
||||
void MetalBindComputePipeline(PulseComputePass pass, PulseComputePipeline pipeline);
|
||||
void MetalDispatchComputations(PulseComputePass pass, uint32_t groupcount_x, uint32_t groupcount_y, uint32_t groupcount_z);
|
||||
|
||||
#endif // PULSE_METAL_COMPUTE_PASS_H_
|
||||
|
||||
#endif // PULSE_ENABLE_METAL_BACKEND
|
||||
49
Sources/Backends/Metal/MetalComputePass.m
git.filemode.normal_file
49
Sources/Backends/Metal/MetalComputePass.m
git.filemode.normal_file
@@ -0,0 +1,49 @@
|
||||
// Copyright (C) 2025 kanel
|
||||
// This file is part of "Pulse"
|
||||
// For conditions of distribution and use, see copyright notice in LICENSE
|
||||
|
||||
#include <Pulse.h>
|
||||
#include "../../PulseInternal.h"
|
||||
#include "Metal.h"
|
||||
#include "MetalComputePass.h"
|
||||
#include "MetalCommandList.h"
|
||||
|
||||
PulseComputePass MetalCreateComputePass(PulseDevice device, PulseCommandList cmd)
|
||||
{
|
||||
PULSE_UNUSED(device);
|
||||
PulseComputePass pass = (PulseComputePass)calloc(1, sizeof(PulseComputePassHandler));
|
||||
PULSE_CHECK_ALLOCATION_RETVAL(pass, PULSE_NULL_HANDLE);
|
||||
return pass;
|
||||
}
|
||||
|
||||
void MetalDestroyComputePass(PulseDevice device, PulseComputePass pass)
|
||||
{
|
||||
}
|
||||
|
||||
PulseComputePass MetalBeginComputePass(PulseCommandList cmd)
|
||||
{
|
||||
}
|
||||
|
||||
void MetalEndComputePass(PulseComputePass pass)
|
||||
{
|
||||
}
|
||||
|
||||
void MetalBindStorageBuffers(PulseComputePass pass, const PulseBuffer* buffers, uint32_t num_buffers)
|
||||
{
|
||||
}
|
||||
|
||||
void MetalBindUniformData(PulseComputePass pass, uint32_t slot, const void* data, uint32_t data_size)
|
||||
{
|
||||
}
|
||||
|
||||
void MetalBindStorageImages(PulseComputePass pass, const PulseImage* images, uint32_t num_images)
|
||||
{
|
||||
}
|
||||
|
||||
void MetalBindComputePipeline(PulseComputePass pass, PulseComputePipeline pipeline)
|
||||
{
|
||||
}
|
||||
|
||||
void MetalDispatchComputations(PulseComputePass pass, uint32_t groupcount_x, uint32_t groupcount_y, uint32_t groupcount_z)
|
||||
{
|
||||
}
|
||||
25
Sources/Backends/Metal/MetalComputePipeline.h
git.filemode.normal_file
25
Sources/Backends/Metal/MetalComputePipeline.h
git.filemode.normal_file
@@ -0,0 +1,25 @@
|
||||
// Copyright (C) 2025 kanel
|
||||
// This file is part of "Pulse"
|
||||
// For conditions of distribution and use, see copyright notice in LICENSE
|
||||
|
||||
#include <Pulse.h>
|
||||
|
||||
#ifdef PULSE_ENABLE_METAL_BACKEND
|
||||
|
||||
#ifndef PULSE_METAL_COMPUTE_PIPELINE_H_
|
||||
#define PULSE_METAL_COMPUTE_PIPELINE_H_
|
||||
|
||||
#include <Metal/Metal.h>
|
||||
#include "Metal.h"
|
||||
|
||||
typedef struct MetalComputePipeline
|
||||
{
|
||||
id<MTLComputePipelineState> pipeline;
|
||||
} MetalComputePipeline;
|
||||
|
||||
PulseComputePipeline MetalCreateComputePipeline(PulseDevice device, const PulseComputePipelineCreateInfo* info);
|
||||
void MetalDestroyComputePipeline(PulseDevice device, PulseComputePipeline pipeline);
|
||||
|
||||
#endif // PULSE_METAL_COMPUTE_PIPELINE_H_
|
||||
|
||||
#endif // PULSE_ENABLE_METAL_BACKEND
|
||||
109
Sources/Backends/Metal/MetalComputePipeline.m
git.filemode.normal_file
109
Sources/Backends/Metal/MetalComputePipeline.m
git.filemode.normal_file
@@ -0,0 +1,109 @@
|
||||
// Copyright (C) 2025 kanel
|
||||
// This file is part of "Pulse"
|
||||
// For conditions of distribution and use, see copyright notice in LICENSE
|
||||
|
||||
#include <string.h>
|
||||
#include <Pulse.h>
|
||||
#include "../../PulseInternal.h"
|
||||
#include "Metal.h"
|
||||
#include "MetalDevice.h"
|
||||
#include "MetalComputePipeline.h"
|
||||
|
||||
typedef struct MetalLibraryFunction
|
||||
{
|
||||
id<MTLLibrary> library;
|
||||
id<MTLFunction> function;
|
||||
} MetalLibraryFunction;
|
||||
|
||||
static bool MetalIsValidMetalLibrary(const uint8_t* code, uint32_t code_size)
|
||||
{
|
||||
// Metal libraries have a 4 byte header containing `MTLB`.
|
||||
if(code_size < 4 || code == PULSE_NULLPTR)
|
||||
return false;
|
||||
return memcmp(code, "MTLB", 4) == 0;
|
||||
}
|
||||
|
||||
static MetalLibraryFunction MetalCompileShader(PulseDevice device, const PulseComputePipelineCreateInfo* info)
|
||||
{
|
||||
MetalDevice* metal_device = (MetalDevice*)METAL_RETRIEVE_DRIVER_DATA_AS(device, MetalDevice*);
|
||||
|
||||
MetalLibraryFunction library_function = { nil, nil };
|
||||
id<MTLLibrary> library;
|
||||
NSError* error;
|
||||
|
||||
const char* entrypoint = (info->entrypoint == PULSE_NULLPTR) ? "main0" : info->entrypoint;
|
||||
|
||||
if(info->format == PULSE_SHADER_FORMAT_MSL_BIT)
|
||||
{
|
||||
NSString* code_string = [[NSString alloc] initWithBytes:info->code length:info->code_size encoding:NSUTF8StringEncoding];
|
||||
library = [metal_device->device newLibraryWithSource:code_string options:nil error:&error];
|
||||
}
|
||||
else if(info->format == PULSE_SHADER_FORMAT_METALLIB_BIT)
|
||||
{
|
||||
if(!MetalIsValidMetalLibrary(info->code, info->code_size))
|
||||
{
|
||||
if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(device->backend))
|
||||
PulseLogError(device->backend, "(Metal) provided shader code is not a valid Metal library");
|
||||
return library_function;
|
||||
}
|
||||
dispatch_data_t data = dispatch_data_create(info->code, info->code_size, dispatch_get_global_queue(0, 0), DISPATCH_DATA_DESTRUCTOR_DEFAULT);
|
||||
library = [metal_device->device newLibraryWithData:data error:&error];
|
||||
}
|
||||
|
||||
if(library == nil)
|
||||
{
|
||||
if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(device->backend))
|
||||
PulseLogErrorFmt(device->backend, "(Metal) creating MTLLibrary failed due to %s", [[error description] cStringUsingEncoding:[NSString defaultCStringEncoding]]);
|
||||
return library_function;
|
||||
}
|
||||
else if(error != nil && PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(device->backend))
|
||||
PulseLogWarningFmt(device->backend, "(Metal) creating MTLLibrary failed due to %s", [[error description] cStringUsingEncoding:[NSString defaultCStringEncoding]]);
|
||||
|
||||
id<MTLFunction> function = [library newFunctionWithName:@(entrypoint)];
|
||||
if(function == nil)
|
||||
{
|
||||
PulseLogError(device->backend, "(Metal) creating MTLLibrary failed");
|
||||
return library_function;
|
||||
}
|
||||
|
||||
library_function.library = library;
|
||||
library_function.function = function;
|
||||
return library_function;
|
||||
}
|
||||
|
||||
PulseComputePipeline MetalCreateComputePipeline(PulseDevice device, const PulseComputePipelineCreateInfo* info)
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
PulseComputePipelineHandler* pipeline = (PulseComputePipelineHandler*)calloc(1, sizeof(PulseComputePipelineHandler));
|
||||
PULSE_CHECK_ALLOCATION_RETVAL(pipeline, PULSE_NULL_HANDLE);
|
||||
|
||||
MetalComputePipeline* metal_pipeline = (MetalComputePipeline*)calloc(1, sizeof(MetalComputePipeline));
|
||||
PULSE_CHECK_ALLOCATION_RETVAL(metal_pipeline, PULSE_NULL_HANDLE);
|
||||
|
||||
MetalLibraryFunction library_function = MetalCompileShader(device, info);
|
||||
if(library_function.library == nil || library_function.function == nil)
|
||||
{
|
||||
if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(device->backend))
|
||||
PulseLogError(device->backend, "(Metal) failed to compile shader");
|
||||
PulseSetInternalError(PULSE_ERROR_INITIALIZATION_FAILED);
|
||||
return PULSE_NULL_HANDLE;
|
||||
}
|
||||
|
||||
pipeline->driver_data = metal_pipeline;
|
||||
|
||||
if(PULSE_IS_BACKEND_HIGH_LEVEL_DEBUG(device->backend))
|
||||
PulseLogInfoFmt(device->backend, "(Metal) created new compute pipeline %p", pipeline);
|
||||
return pipeline;
|
||||
}
|
||||
}
|
||||
|
||||
void MetalDestroyComputePipeline(PulseDevice device, PulseComputePipeline pipeline)
|
||||
{
|
||||
MetalComputePipeline* metal_pipeline = METAL_RETRIEVE_DRIVER_DATA_AS(pipeline, MetalComputePipeline*);
|
||||
metal_pipeline->pipeline = nil;
|
||||
free(metal_pipeline);
|
||||
if(PULSE_IS_BACKEND_HIGH_LEVEL_DEBUG(device->backend))
|
||||
PulseLogInfoFmt(device->backend, "(Metal) destroyed compute pipeline %p", pipeline);
|
||||
free(pipeline);
|
||||
}
|
||||
26
Sources/Backends/Metal/MetalDevice.h
git.filemode.normal_file
26
Sources/Backends/Metal/MetalDevice.h
git.filemode.normal_file
@@ -0,0 +1,26 @@
|
||||
// Copyright (C) 2025 kanel
|
||||
// This file is part of "Pulse"
|
||||
// For conditions of distribution and use, see copyright notice in LICENSE
|
||||
|
||||
#include <Pulse.h>
|
||||
|
||||
#ifdef PULSE_ENABLE_METAL_BACKEND
|
||||
|
||||
#ifndef PULSE_METAL_DEVICE_H_
|
||||
#define PULSE_METAL_DEVICE_H_
|
||||
|
||||
#include <Metal/Metal.h>
|
||||
#include "Metal.h"
|
||||
|
||||
typedef struct MetalDevice
|
||||
{
|
||||
id<MTLDevice> device;
|
||||
id<MTLCommandQueue> queue;
|
||||
} MetalDevice;
|
||||
|
||||
PulseDevice MetalCreateDevice(PulseBackend backend, PulseDevice* forbiden_devices, uint32_t forbiden_devices_count);
|
||||
void MetalDestroyDevice(PulseDevice device);
|
||||
|
||||
#endif // PULSE_METAL_DEVICE_H_
|
||||
|
||||
#endif // PULSE_ENABLE_METAL_BACKEND
|
||||
115
Sources/Backends/Metal/MetalDevice.m
git.filemode.normal_file
115
Sources/Backends/Metal/MetalDevice.m
git.filemode.normal_file
@@ -0,0 +1,115 @@
|
||||
// Copyright (C) 2025 kanel
|
||||
// This file is part of "Pulse"
|
||||
// For conditions of distribution and use, see copyright notice in LICENSE
|
||||
|
||||
#include <Pulse.h>
|
||||
#include "../../PulseInternal.h"
|
||||
#include "Metal.h"
|
||||
#include "MetalComputePipeline.h"
|
||||
#include "MetalCommandList.h"
|
||||
#include "MetalDevice.h"
|
||||
#include "MetalFence.h"
|
||||
#include "MetalBuffer.h"
|
||||
#include "MetalImage.h"
|
||||
#include "MetalComputePass.h"
|
||||
|
||||
static uint64_t MetalScoreDevice(id<MTLDevice> device)
|
||||
{
|
||||
uint64_t score = 0;
|
||||
|
||||
if(!device.lowPower)
|
||||
score += 1000;
|
||||
|
||||
// A GPU with dedicated memory is typically a dedicated one
|
||||
if(!device.hasUnifiedMemory)
|
||||
score += 10000;
|
||||
|
||||
score += device.maxThreadsPerThreadgroup.width;
|
||||
score += device.maxThreadsPerThreadgroup.height;
|
||||
score += device.maxThreadsPerThreadgroup.depth;
|
||||
score += device.maxThreadgroupMemoryLength;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
static bool MetalIsDeviceForbidden(id<MTLDevice> device, PulseDevice* forbiden_devices, uint32_t forbiden_devices_count)
|
||||
{
|
||||
if(!device)
|
||||
return true;
|
||||
for(uint32_t i = 0; i < forbiden_devices_count; i++)
|
||||
{
|
||||
if(device.registryID == METAL_RETRIEVE_DRIVER_DATA_AS(forbiden_devices[i], MetalDevice*)->device.registryID)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
PulseDevice MetalCreateDevice(PulseBackend backend, PulseDevice* forbiden_devices, uint32_t forbiden_devices_count)
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
PULSE_UNUSED(forbiden_devices);
|
||||
PULSE_UNUSED(forbiden_devices_count);
|
||||
|
||||
PULSE_CHECK_HANDLE_RETVAL(backend, PULSE_NULLPTR);
|
||||
|
||||
PulseDevice pulse_device = (PulseDeviceHandler*)calloc(1, sizeof(PulseDeviceHandler));
|
||||
PULSE_CHECK_ALLOCATION_RETVAL(pulse_device, PULSE_NULL_HANDLE);
|
||||
|
||||
MetalDevice* metal_device = (MetalDevice*)calloc(1, sizeof(MetalDevice));
|
||||
PULSE_CHECK_ALLOCATION_RETVAL(metal_device, PULSE_NULL_HANDLE);
|
||||
|
||||
#ifdef PULSE_PLAT_MACOS
|
||||
uint64_t best = 0;
|
||||
NSArray<id<MTLDevice>>* devices = MTLCopyAllDevices();
|
||||
for(id<MTLDevice> candidate in devices)
|
||||
{
|
||||
if(MetalIsDeviceForbidden(candidate, forbiden_devices, forbiden_devices_count))
|
||||
continue;
|
||||
uint64_t current = MetalScoreDevice(candidate);
|
||||
if(current > best)
|
||||
{
|
||||
best = current;
|
||||
metal_device->device = candidate;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if(!metal_device->device)
|
||||
{
|
||||
#ifdef PULSE_PLAT_MACOS
|
||||
if(PULSE_IS_BACKEND_HIGH_LEVEL_DEBUG(backend))
|
||||
PulseLogError(backend, "(Metal) failed to select device, falling back on default device");
|
||||
#endif
|
||||
id<MTLDevice> device = MTLCreateSystemDefaultDevice();
|
||||
if(!MetalIsDeviceForbidden(device, forbiden_devices, forbiden_devices_count))
|
||||
metal_device->device = device;
|
||||
}
|
||||
|
||||
if(!metal_device->device)
|
||||
{
|
||||
if(PULSE_IS_BACKEND_LOW_LEVEL_DEBUG(backend))
|
||||
PulseLogError(backend, "(Metal) failed to retrieve default device");
|
||||
return PULSE_NULL_HANDLE;
|
||||
}
|
||||
|
||||
metal_device->queue = [metal_device->device newCommandQueue];
|
||||
|
||||
pulse_device->driver_data = metal_device;
|
||||
pulse_device->backend = backend;
|
||||
PULSE_LOAD_DRIVER_DEVICE(Metal);
|
||||
|
||||
if(PULSE_IS_BACKEND_HIGH_LEVEL_DEBUG(backend))
|
||||
PulseLogInfoFmt(backend, "(Metal) created device from %s", [metal_device->device.name UTF8String]);
|
||||
return pulse_device;
|
||||
}
|
||||
}
|
||||
|
||||
void MetalDestroyDevice(PulseDevice device)
|
||||
{
|
||||
MetalDevice* metal_device = (MetalDevice*)METAL_RETRIEVE_DRIVER_DATA_AS(device, MetalDevice*);
|
||||
if(PULSE_IS_BACKEND_HIGH_LEVEL_DEBUG(device->backend))
|
||||
PulseLogInfoFmt(device->backend, "(Metal) destroyed device created from %s", [metal_device->device.name UTF8String]);
|
||||
metal_device->queue = nil;
|
||||
free(metal_device);
|
||||
free(device);
|
||||
}
|
||||
27
Sources/Backends/Metal/MetalFence.h
git.filemode.normal_file
27
Sources/Backends/Metal/MetalFence.h
git.filemode.normal_file
@@ -0,0 +1,27 @@
|
||||
// Copyright (C) 2025 kanel
|
||||
// This file is part of "Pulse"
|
||||
// For conditions of distribution and use, see copyright notice in LICENSE
|
||||
|
||||
#include <Pulse.h>
|
||||
|
||||
#ifdef PULSE_ENABLE_METAL_BACKEND
|
||||
|
||||
#ifndef PULSE_METAL_FENCE_H_
|
||||
#define PULSE_METAL_FENCE_H_
|
||||
|
||||
#include <Pulse.h>
|
||||
#include "Metal.h"
|
||||
|
||||
typedef struct MetalFence
|
||||
{
|
||||
int dummy;
|
||||
} MetalFence;
|
||||
|
||||
PulseFence MetalCreateFence(PulseDevice device);
|
||||
void MetalDestroyFence(PulseDevice device, PulseFence fence);
|
||||
bool MetalIsFenceReady(PulseDevice device, PulseFence fence);
|
||||
bool MetalWaitForFences(PulseDevice device, const PulseFence* fences, uint32_t fences_count, bool wait_for_all);
|
||||
|
||||
#endif // PULSE_METAL_FENCE_H_
|
||||
|
||||
#endif // PULSE_ENABLE_METAL_BACKEND
|
||||
31
Sources/Backends/Metal/MetalFence.m
git.filemode.normal_file
31
Sources/Backends/Metal/MetalFence.m
git.filemode.normal_file
@@ -0,0 +1,31 @@
|
||||
// Copyright (C) 2025 kanel
|
||||
// This file is part of "Pulse"
|
||||
// For conditions of distribution and use, see copyright notice in LICENSE
|
||||
|
||||
#include <Pulse.h>
|
||||
#include "../../PulseInternal.h"
|
||||
#include "Metal.h"
|
||||
#include "MetalFence.h"
|
||||
#include "MetalCommandList.h"
|
||||
|
||||
PulseFence MetalCreateFence(PulseDevice device)
|
||||
{
|
||||
PulseFence fence = (PulseFence)calloc(1, sizeof(PulseFence));
|
||||
PULSE_CHECK_ALLOCATION_RETVAL(fence, PULSE_NULL_HANDLE);
|
||||
return fence;
|
||||
}
|
||||
|
||||
void MetalDestroyFence(PulseDevice device, PulseFence fence)
|
||||
{
|
||||
free(fence);
|
||||
}
|
||||
|
||||
bool MetalIsFenceReady(PulseDevice device, PulseFence fence)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MetalWaitForFences(PulseDevice device, const PulseFence* fences, uint32_t fences_count, bool wait_for_all)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
27
Sources/Backends/Metal/MetalImage.h
git.filemode.normal_file
27
Sources/Backends/Metal/MetalImage.h
git.filemode.normal_file
@@ -0,0 +1,27 @@
|
||||
// Copyright (C) 2025 kanel
|
||||
// This file is part of "Pulse"
|
||||
// For conditions of distribution and use, see copyright notice in LICENSE
|
||||
|
||||
#include <Pulse.h>
|
||||
|
||||
#ifdef PULSE_ENABLE_METAL_BACKEND
|
||||
|
||||
#ifndef PULSE_METAL_IMAGE_H_
|
||||
#define PULSE_METAL_IMAGE_H_
|
||||
|
||||
#include "Metal.h"
|
||||
|
||||
typedef struct MetalImage
|
||||
{
|
||||
int dummy;
|
||||
} MetalImage;
|
||||
|
||||
PulseImage MetalCreateImage(PulseDevice device, const PulseImageCreateInfo* create_infos);
|
||||
bool MetalIsImageFormatValid(PulseDevice device, PulseImageFormat format, PulseImageType type, PulseImageUsageFlags usage);
|
||||
bool MetalCopyImageToBuffer(PulseCommandList cmd, const PulseImageRegion* src, const PulseBufferRegion* dst);
|
||||
bool MetalBlitImage(PulseCommandList cmd, const PulseImageRegion* src, const PulseImageRegion* dst);
|
||||
void MetalDestroyImage(PulseDevice device, PulseImage image);
|
||||
|
||||
#endif // PULSE_METAL_IMAGE_H_
|
||||
|
||||
#endif // PULSE_ENABLE_METAL_BACKEND
|
||||
28
Sources/Backends/Metal/MetalImage.m
git.filemode.normal_file
28
Sources/Backends/Metal/MetalImage.m
git.filemode.normal_file
@@ -0,0 +1,28 @@
|
||||
// Copyright (C) 2025 kanel
|
||||
// This file is part of "Pulse"
|
||||
// For conditions of distribution and use, see copyright notice in LICENSE
|
||||
|
||||
#include <Pulse.h>
|
||||
#include "../../PulseInternal.h"
|
||||
#include "Metal.h"
|
||||
#include "MetalImage.h"
|
||||
|
||||
PulseImage MetalCreateImage(PulseDevice device, const PulseImageCreateInfo* create_infos)
|
||||
{
|
||||
}
|
||||
|
||||
bool MetalIsImageFormatValid(PulseDevice device, PulseImageFormat format, PulseImageType type, PulseImageUsageFlags usage)
|
||||
{
|
||||
}
|
||||
|
||||
bool MetalCopyImageToBuffer(PulseCommandList cmd, const PulseImageRegion* src, const PulseBufferRegion* dst)
|
||||
{
|
||||
}
|
||||
|
||||
bool MetalBlitImage(PulseCommandList cmd, const PulseImageRegion* src, const PulseImageRegion* dst)
|
||||
{
|
||||
}
|
||||
|
||||
void MetalDestroyImage(PulseDevice device, PulseImage image)
|
||||
{
|
||||
}
|
||||
@@ -167,13 +167,17 @@ PulseDevice VulkanCreateDevice(PulseBackend backend, PulseDevice* forbiden_devic
|
||||
instance->vkGetPhysicalDeviceMemoryProperties(device->physical, &device->memory_properties);
|
||||
instance->vkGetPhysicalDeviceFeatures(device->physical, &device->features);
|
||||
|
||||
const char* extensions[] = {
|
||||
"VK_KHR_portability_subset",
|
||||
};
|
||||
|
||||
VkDeviceCreateInfo create_info = { 0 };
|
||||
create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||||
create_info.queueCreateInfoCount = unique_queues_count;
|
||||
create_info.pQueueCreateInfos = queue_create_infos;
|
||||
create_info.pEnabledFeatures = &device->features;
|
||||
create_info.enabledExtensionCount = 0;
|
||||
create_info.ppEnabledExtensionNames = PULSE_NULLPTR;
|
||||
create_info.enabledExtensionCount = 1;
|
||||
create_info.ppEnabledExtensionNames = extensions;
|
||||
create_info.enabledLayerCount = 0;
|
||||
create_info.ppEnabledLayerNames = PULSE_NULLPTR;
|
||||
create_info.flags = 0;
|
||||
|
||||
@@ -115,7 +115,7 @@ static VkInstance VulkanCreateInstance(PulseBackend backend, const char** extens
|
||||
create_info.pApplicationInfo = &app_info;
|
||||
create_info.enabledExtensionCount = extensions_count;
|
||||
create_info.ppEnabledExtensionNames = extensions_enabled;
|
||||
#ifdef PULSE_PLAT_MACOS
|
||||
#ifdef PULSE_PLAT_APPLE
|
||||
create_info.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
|
||||
#else
|
||||
create_info.flags = 0;
|
||||
@@ -151,7 +151,7 @@ static VkInstance VulkanCreateInstance(PulseBackend backend, const char** extens
|
||||
|
||||
bool VulkanInitInstance(PulseBackend backend, VulkanInstance* instance, PulseDebugLevel debug_level)
|
||||
{
|
||||
#ifdef PULSE_PLAT_MACOS
|
||||
#ifdef PULSE_PLAT_APPLE
|
||||
const char* extensions[] = {
|
||||
VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME,
|
||||
};
|
||||
|
||||
@@ -40,7 +40,7 @@ bool VulkanInitLoader()
|
||||
const char* libnames[] = {
|
||||
"vulkan-1.dll"
|
||||
};
|
||||
#elif defined(PULSE_PLAT_MACOS)
|
||||
#elif defined(PULSE_PLAT_APPLE)
|
||||
const char* libnames[] = {
|
||||
"libvulkan.dylib",
|
||||
"libvulkan.1.dylib",
|
||||
|
||||
@@ -101,6 +101,8 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define PULSE_ALIGN_UP(val, alignment) ((val + alignment - 1) & ~(alignment - 1))
|
||||
|
||||
#define PULSE_LOAD_DRIVER_DEVICE_FUNCTION(fn, _namespace) pulse_device->PFN_##fn = _namespace##fn;
|
||||
#define PULSE_LOAD_DRIVER_DEVICE(_namespace) \
|
||||
PULSE_LOAD_DRIVER_DEVICE_FUNCTION(DestroyDevice, _namespace) \
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
PULSE_IMPORT_API FARPROC __stdcall GetProcAddress(HMODULE, LPCSTR);
|
||||
PULSE_IMPORT_API int __stdcall FreeLibrary(HMODULE);
|
||||
#else
|
||||
#ifdef PULSE_PLAT_MACOS
|
||||
#ifdef PULSE_PLAT_APPLE
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#include <dlfcn.h>
|
||||
|
||||
Reference in New Issue
Block a user