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:
86
.github/workflows/metal-test-macos.yml
vendored
git.filemode.normal_file
86
.github/workflows/metal-test-macos.yml
vendored
git.filemode.normal_file
@@ -0,0 +1,86 @@
|
|||||||
|
name: Metal
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
paths-ignore:
|
||||||
|
- '.github/workflows/*.yml'
|
||||||
|
- '!.github/workflows/metal-test-macos.yml'
|
||||||
|
- '.gitignore'
|
||||||
|
- 'LICENSE'
|
||||||
|
- 'CHANGELOG.md'
|
||||||
|
- 'README.md'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [macOS-latest]
|
||||||
|
arch: [x86_64]
|
||||||
|
confs:
|
||||||
|
- { mode: debug, archive: yes }
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
if: ${{ !contains(github.event.head_commit.message, 'ci skip') }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Get current date as package key
|
||||||
|
id: cache_key
|
||||||
|
run: echo "key=$(date +'%W')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
# Force xmake to a specific folder (for cache)
|
||||||
|
- name: Set xmake env
|
||||||
|
run: echo "XMAKE_GLOBALDIR=${{ runner.workspace }}/xmake-global" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
# Install xmake
|
||||||
|
- name: Setup xmake
|
||||||
|
uses: xmake-io/github-action-setup-xmake@v1
|
||||||
|
with:
|
||||||
|
xmake-version: branch@dev
|
||||||
|
actions-cache-folder: .xmake-cache-W${{ steps.cache_key.outputs.key }}
|
||||||
|
|
||||||
|
# Update xmake repository (in order to have the file that will be cached)
|
||||||
|
- name: Update xmake repository
|
||||||
|
run: xmake repo --update
|
||||||
|
|
||||||
|
# Fetch xmake dephash
|
||||||
|
- name: Retrieve dependencies hash
|
||||||
|
id: dep_hash
|
||||||
|
run: echo "hash=$(xmake l utils.ci.packageskey)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
# Cache xmake dependencies
|
||||||
|
- name: Restore cached xmake dependencies
|
||||||
|
id: restore-depcache
|
||||||
|
uses: actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
path: ${{ env.XMAKE_GLOBALDIR }}/.xmake/packages
|
||||||
|
key: MacOS-${{ matrix.arch }}-${{ matrix.confs.mode }}${{ matrix.confs.cache_key }}-${{ steps.dep_hash.outputs.hash }}-W${{ steps.cache_key.outputs.key }}
|
||||||
|
|
||||||
|
# Setup compilation mode and install project dependencies
|
||||||
|
- name: Configure xmake and install dependencies
|
||||||
|
run: xmake config --examples=y --arch=${{ matrix.arch }} --mode=${{ matrix.confs.mode }} ${{ matrix.confs.config }} --ccache=n --yes
|
||||||
|
|
||||||
|
# Save dependencies
|
||||||
|
- name: Save cached xmake dependencies
|
||||||
|
if: ${{ !steps.restore-depcache.outputs.cache-hit }}
|
||||||
|
uses: actions/cache/save@v4
|
||||||
|
with:
|
||||||
|
path: ${{ env.XMAKE_GLOBALDIR }}/.xmake/packages
|
||||||
|
key: ${{ steps.restore-depcache.outputs.cache-primary-key }}
|
||||||
|
|
||||||
|
# Cache assets downloading
|
||||||
|
- name: Restore cached assets
|
||||||
|
id: restore-assets
|
||||||
|
uses: actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
path: assets
|
||||||
|
key: assets-${{ hashFiles('assets/examples_version.txt', 'assets/unittests_version.txt') }}
|
||||||
|
|
||||||
|
- name: Test Metal
|
||||||
|
run: |
|
||||||
|
xmake build --yes MetalExample
|
||||||
|
xmake run --yes MetalExample
|
||||||
63
Examples/Metal/main.c
git.filemode.normal_file
63
Examples/Metal/main.c
git.filemode.normal_file
@@ -0,0 +1,63 @@
|
|||||||
|
#include <Pulse.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
void DebugCallBack(PulseDebugMessageSeverity severity, const char* message)
|
||||||
|
{
|
||||||
|
if(severity == PULSE_DEBUG_MESSAGE_SEVERITY_ERROR)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Pulse Error: %s\n", message);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
else if(severity == PULSE_DEBUG_MESSAGE_SEVERITY_WARNING)
|
||||||
|
fprintf(stderr, "Pulse Warning: %s\n", message);
|
||||||
|
else
|
||||||
|
printf("Pulse: %s\n", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BUFFER_SIZE (256 * sizeof(uint32_t))
|
||||||
|
|
||||||
|
const char* msl_source =
|
||||||
|
"#include <metal_stdlib>\n \
|
||||||
|
using namespace metal;\
|
||||||
|
\
|
||||||
|
kernel void Main(device int* ssbo [[ buffer(0) ]], uint3 grid [[ thread_position_in_grid ]]) \
|
||||||
|
{ \
|
||||||
|
uint idx = grid.x * grid.y; \
|
||||||
|
ssbo[idx] = static_cast<int>(idx); \
|
||||||
|
}";
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
PulseBackend backend = PulseLoadBackend(PULSE_BACKEND_METAL, PULSE_SHADER_FORMAT_MSL_BIT, PULSE_HIGH_DEBUG);
|
||||||
|
PulseSetDebugCallback(backend, DebugCallBack);
|
||||||
|
PulseDevice device = PulseCreateDevice(backend, NULL, 0);
|
||||||
|
|
||||||
|
PulseBufferCreateInfo buffer_create_info = { 0 };
|
||||||
|
buffer_create_info.size = BUFFER_SIZE;
|
||||||
|
buffer_create_info.usage = PULSE_BUFFER_USAGE_STORAGE_READ | PULSE_BUFFER_USAGE_STORAGE_WRITE | PULSE_BUFFER_USAGE_TRANSFER_DOWNLOAD;
|
||||||
|
PulseBuffer buffer = PulseCreateBuffer(device, &buffer_create_info);
|
||||||
|
|
||||||
|
// GPU computations
|
||||||
|
{
|
||||||
|
PulseComputePipelineCreateInfo info = { 0 };
|
||||||
|
info.code_size = strlen(msl_source);
|
||||||
|
info.code = (const uint8_t*)msl_source;
|
||||||
|
info.entrypoint = "Main";
|
||||||
|
info.format = PULSE_SHADER_FORMAT_MSL_BIT;
|
||||||
|
info.num_readwrite_storage_buffers = 1;
|
||||||
|
PulseComputePipeline pipeline = PulseCreateComputePipeline(device, &info);
|
||||||
|
|
||||||
|
PulseDestroyComputePipeline(device, pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
PulseDestroyBuffer(device, buffer);
|
||||||
|
|
||||||
|
PulseDestroyDevice(device);
|
||||||
|
PulseUnloadBackend(backend);
|
||||||
|
puts("Successfully executed Pulse example using Metal !");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
4
Examples/Metal/xmake.lua
git.filemode.normal_file
4
Examples/Metal/xmake.lua
git.filemode.normal_file
@@ -0,0 +1,4 @@
|
|||||||
|
target("MetalExample")
|
||||||
|
add_deps("pulse_gpu")
|
||||||
|
add_files("*.c")
|
||||||
|
target_end()
|
||||||
@@ -2,9 +2,12 @@ option("examples", { description = "Build the examples", default = false })
|
|||||||
|
|
||||||
if has_config("examples") then
|
if has_config("examples") then
|
||||||
set_group("Examples")
|
set_group("Examples")
|
||||||
if not is_plat("wasm") and has_config("vulkan") then
|
if has_config("vulkan") then
|
||||||
includes("Vulkan/xmake.lua")
|
includes("Vulkan/xmake.lua")
|
||||||
end
|
end
|
||||||
|
if has_config("metal") then
|
||||||
|
includes("Metal/xmake.lua")
|
||||||
|
end
|
||||||
if has_config("webgpu") then
|
if has_config("webgpu") then
|
||||||
includes("WebGPU/xmake.lua")
|
includes("WebGPU/xmake.lua")
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -48,8 +48,23 @@ extern "C" {
|
|||||||
#define PULSE_PLAT_LINUX
|
#define PULSE_PLAT_LINUX
|
||||||
#define PULSE_PLAT_POSIX
|
#define PULSE_PLAT_POSIX
|
||||||
#elif defined(__APPLE__) && defined(__MACH__)
|
#elif defined(__APPLE__) && defined(__MACH__)
|
||||||
#define PULSE_PLAT_MACOS
|
#define PULSE_PLAT_APPLE
|
||||||
#define PULSE_PLAT_POSIX
|
#define PULSE_PLAT_POSIX
|
||||||
|
#include <TargetConditionals.h>
|
||||||
|
#if TARGET_IPHONE_SIMULATOR
|
||||||
|
// iOS, tvOS, or watchOS Simulator
|
||||||
|
#define PULSE_PLAT_IOS
|
||||||
|
#elif TARGET_OS_MACCATALYST
|
||||||
|
// Mac's Catalyst (ports iOS API into Mac, like UIKit).
|
||||||
|
#define PULSE_PLAT_IOS
|
||||||
|
#elif TARGET_OS_IPHONE
|
||||||
|
// iOS, tvOS, or watchOS device
|
||||||
|
#define PULSE_PLAT_IOS
|
||||||
|
#elif TARGET_OS_MAC
|
||||||
|
#define PULSE_PLAT_MACOS
|
||||||
|
#else
|
||||||
|
#error "Unknown Apple platform"
|
||||||
|
#endif
|
||||||
#elif defined(unix) || defined(__unix__) || defined(__unix)
|
#elif defined(unix) || defined(__unix__) || defined(__unix)
|
||||||
#define PULSE_PLAT_UNIX
|
#define PULSE_PLAT_UNIX
|
||||||
#define PULSE_PLAT_POSIX
|
#define PULSE_PLAT_POSIX
|
||||||
|
|||||||
@@ -9,7 +9,22 @@
|
|||||||
#ifndef PULSE_METAL_H_
|
#ifndef PULSE_METAL_H_
|
||||||
#define 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_
|
#endif // PULSE_METAL_H_
|
||||||
|
|
||||||
|
|||||||
@@ -6,20 +6,32 @@
|
|||||||
#include "../../PulseInternal.h"
|
#include "../../PulseInternal.h"
|
||||||
|
|
||||||
#include "Metal.h"
|
#include "Metal.h"
|
||||||
|
#include "MetalDevice.h"
|
||||||
|
|
||||||
PulseBackendFlags MetalCheckSupport(PulseBackendFlags candidates, PulseShaderFormatsFlags shader_formats_used)
|
PulseBackendFlags MetalCheckSupport(PulseBackendFlags candidates, PulseShaderFormatsFlags shader_formats_used)
|
||||||
{
|
{
|
||||||
if(candidates != PULSE_BACKEND_ANY && (candidates & PULSE_BACKEND_METAL) == 0)
|
if(candidates != PULSE_BACKEND_ANY && (candidates & PULSE_BACKEND_METAL) == 0)
|
||||||
return PULSE_BACKEND_INVALID;
|
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;
|
||||||
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 = {
|
PulseBackendHandler MetalDriver = {
|
||||||
.PFN_LoadBackend = PULSE_NULLPTR,
|
.PFN_LoadBackend = MetalLoadBackend,
|
||||||
.PFN_UnloadBackend = PULSE_NULLPTR,
|
.PFN_UnloadBackend = MetalUnloadBackend,
|
||||||
.PFN_CreateDevice = PULSE_NULLPTR,
|
.PFN_CreateDevice = MetalCreateDevice,
|
||||||
.backend = PULSE_BACKEND_METAL,
|
.backend = PULSE_BACKEND_METAL,
|
||||||
.supported_shader_formats = PULSE_SHADER_FORMAT_MSL_BIT | PULSE_SHADER_FORMAT_METALLIB_BIT,
|
.supported_shader_formats = PULSE_SHADER_FORMAT_MSL_BIT | PULSE_SHADER_FORMAT_METALLIB_BIT,
|
||||||
.driver_data = PULSE_NULLPTR
|
.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->vkGetPhysicalDeviceMemoryProperties(device->physical, &device->memory_properties);
|
||||||
instance->vkGetPhysicalDeviceFeatures(device->physical, &device->features);
|
instance->vkGetPhysicalDeviceFeatures(device->physical, &device->features);
|
||||||
|
|
||||||
|
const char* extensions[] = {
|
||||||
|
"VK_KHR_portability_subset",
|
||||||
|
};
|
||||||
|
|
||||||
VkDeviceCreateInfo create_info = { 0 };
|
VkDeviceCreateInfo create_info = { 0 };
|
||||||
create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||||||
create_info.queueCreateInfoCount = unique_queues_count;
|
create_info.queueCreateInfoCount = unique_queues_count;
|
||||||
create_info.pQueueCreateInfos = queue_create_infos;
|
create_info.pQueueCreateInfos = queue_create_infos;
|
||||||
create_info.pEnabledFeatures = &device->features;
|
create_info.pEnabledFeatures = &device->features;
|
||||||
create_info.enabledExtensionCount = 0;
|
create_info.enabledExtensionCount = 1;
|
||||||
create_info.ppEnabledExtensionNames = PULSE_NULLPTR;
|
create_info.ppEnabledExtensionNames = extensions;
|
||||||
create_info.enabledLayerCount = 0;
|
create_info.enabledLayerCount = 0;
|
||||||
create_info.ppEnabledLayerNames = PULSE_NULLPTR;
|
create_info.ppEnabledLayerNames = PULSE_NULLPTR;
|
||||||
create_info.flags = 0;
|
create_info.flags = 0;
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ static VkInstance VulkanCreateInstance(PulseBackend backend, const char** extens
|
|||||||
create_info.pApplicationInfo = &app_info;
|
create_info.pApplicationInfo = &app_info;
|
||||||
create_info.enabledExtensionCount = extensions_count;
|
create_info.enabledExtensionCount = extensions_count;
|
||||||
create_info.ppEnabledExtensionNames = extensions_enabled;
|
create_info.ppEnabledExtensionNames = extensions_enabled;
|
||||||
#ifdef PULSE_PLAT_MACOS
|
#ifdef PULSE_PLAT_APPLE
|
||||||
create_info.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
|
create_info.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
|
||||||
#else
|
#else
|
||||||
create_info.flags = 0;
|
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)
|
bool VulkanInitInstance(PulseBackend backend, VulkanInstance* instance, PulseDebugLevel debug_level)
|
||||||
{
|
{
|
||||||
#ifdef PULSE_PLAT_MACOS
|
#ifdef PULSE_PLAT_APPLE
|
||||||
const char* extensions[] = {
|
const char* extensions[] = {
|
||||||
VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME,
|
VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ bool VulkanInitLoader()
|
|||||||
const char* libnames[] = {
|
const char* libnames[] = {
|
||||||
"vulkan-1.dll"
|
"vulkan-1.dll"
|
||||||
};
|
};
|
||||||
#elif defined(PULSE_PLAT_MACOS)
|
#elif defined(PULSE_PLAT_APPLE)
|
||||||
const char* libnames[] = {
|
const char* libnames[] = {
|
||||||
"libvulkan.dylib",
|
"libvulkan.dylib",
|
||||||
"libvulkan.1.dylib",
|
"libvulkan.1.dylib",
|
||||||
|
|||||||
@@ -101,6 +101,8 @@
|
|||||||
#endif
|
#endif
|
||||||
#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_FUNCTION(fn, _namespace) pulse_device->PFN_##fn = _namespace##fn;
|
||||||
#define PULSE_LOAD_DRIVER_DEVICE(_namespace) \
|
#define PULSE_LOAD_DRIVER_DEVICE(_namespace) \
|
||||||
PULSE_LOAD_DRIVER_DEVICE_FUNCTION(DestroyDevice, _namespace) \
|
PULSE_LOAD_DRIVER_DEVICE_FUNCTION(DestroyDevice, _namespace) \
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
PULSE_IMPORT_API FARPROC __stdcall GetProcAddress(HMODULE, LPCSTR);
|
PULSE_IMPORT_API FARPROC __stdcall GetProcAddress(HMODULE, LPCSTR);
|
||||||
PULSE_IMPORT_API int __stdcall FreeLibrary(HMODULE);
|
PULSE_IMPORT_API int __stdcall FreeLibrary(HMODULE);
|
||||||
#else
|
#else
|
||||||
#ifdef PULSE_PLAT_MACOS
|
#ifdef PULSE_PLAT_APPLE
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#endif
|
#endif
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ local backends = {
|
|||||||
end,
|
end,
|
||||||
before_build = function(target, os)
|
before_build = function(target, os)
|
||||||
local gles_dir = target:pkg("opengl-headers"):installdir()
|
local gles_dir = target:pkg("opengl-headers"):installdir()
|
||||||
os.runv("python", {"Scripts/GenerateOpenGLDefs.py", "Sources/Backends/OpenGL/OpenGLFunctions.h", gles_dir .. "/include/GLES3/gl32.h", "Sources/Backends/OpenGL/OpenGLWraps.h"})
|
os.runv("python3", {"Scripts/GenerateOpenGLDefs.py", "Sources/Backends/OpenGL/OpenGLFunctions.h", gles_dir .. "/include/GLES3/gl32.h", "Sources/Backends/OpenGL/OpenGLWraps.h"})
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -157,7 +157,11 @@ target("pulse_gpu")
|
|||||||
add_headerfiles("Sources/Backends/" .. name .. "/**.h", { prefixdir = "private", install = false })
|
add_headerfiles("Sources/Backends/" .. name .. "/**.h", { prefixdir = "private", install = false })
|
||||||
add_headerfiles("Sources/Backends/" .. name .. "/**.inl", { prefixdir = "private", install = false })
|
add_headerfiles("Sources/Backends/" .. name .. "/**.inl", { prefixdir = "private", install = false })
|
||||||
|
|
||||||
add_files("Sources/Backends/" .. name .. "/**.c")
|
-- Checks if there are C files in the backend directory and add them if so
|
||||||
|
local cfiles = os.files("Sources/Backends/" .. name .. "/**.c")
|
||||||
|
if #cfiles > 0 then
|
||||||
|
add_files("Sources/Backends/" .. name .. "/**.c")
|
||||||
|
end
|
||||||
|
|
||||||
if module.custom then
|
if module.custom then
|
||||||
module.custom()
|
module.custom()
|
||||||
|
|||||||
Reference in New Issue
Block a user