moltenvk/Common/MVKOSExtensions.mm

162 lines
4.7 KiB
Plaintext
Raw Normal View History

/*
* MVKOSExtensions.mm
*
2024-01-04 14:51:53 -05:00
* Copyright (c) 2015-2024 The Brenwill Workshop Ltd. (http://www.brenwill.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "MVKOSExtensions.h"
#include "MVKCommonEnvironment.h"
#include <mach/mach_host.h>
#include <mach/mach_time.h>
#include <mach/task.h>
#include <os/proc.h>
#include <sys/sysctl.h>
#import <Foundation/Foundation.h>
using namespace std;
#pragma mark -
#pragma mark Operating System versions
MVKOSVersion mvkOSVersion() {
static MVKOSVersion _mvkOSVersion = 0;
if ( !_mvkOSVersion ) {
NSOperatingSystemVersion osVer = [[NSProcessInfo processInfo] operatingSystemVersion];
_mvkOSVersion = mvkMakeOSVersion((uint32_t)osVer.majorVersion, (uint32_t)osVer.minorVersion, (uint32_t)osVer.patchVersion);
}
return _mvkOSVersion;
}
#pragma mark -
#pragma mark Timestamps
static mach_timebase_info_data_t _mvkMachTimebase;
uint64_t mvkGetTimestamp() { return mach_absolute_time(); }
uint64_t mvkGetRuntimeNanoseconds() { return mach_absolute_time() * _mvkMachTimebase.numer / _mvkMachTimebase.denom; }
uint64_t mvkGetContinuousNanoseconds() { return mach_continuous_time() * _mvkMachTimebase.numer / _mvkMachTimebase.denom; }
Support the `VK_EXT_pipeline_creation_feedback` extension. This provides feedback that indicates: * how long it took to compile each shader stage and the pipeline as a whole; * whether or not the pipeline or any shader stage were found in any supplied pipeline cache; and * whether or not any supplied base pipeline were used to accelerate pipeline creation. This is similar to the performance statistics that MoltenVK already collects. Since we don't use any supplied base pipeline at all, this implementation never sets `VK_PIPELINE_CREATION_FEEDBACK_BASE_PIPELINE_ACCELERATION_BIT`. However, I've identified several places where we could probably use the base pipeline to accelerate pipeline creation. One day, I should probably implement that. Likewise, because we don't yet support using `MTLBinaryArchive`s, `VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT` is never set on the whole pipeline, though it *is* set for individual stages, on the assumption that any shader found in a cache is likely to be found in Metal's own implicit cache. In this implementation, shader stage compilation time includes any time needed to build the `MTLComputePipelineState`s needed for vertex and tessellation control shaders in tessellated pipelines. This patch also changes compilation of the vertex stage `MTLComputePipelineState`s in tessellated pipelines to be eager instead of lazy. We really ought to have been doing this anyway, in order to report pipeline failures at creation time instead of draw time. I'm not happy, though, that we now pay the cost of all three pipeline states all the time, instead of just the ones that are used. This also gets rid of some fields of `MVKGraphicsPipeline` that were only used during pipeline construction, which should save some memory, particularly for apps that create lots of pipelines.
2023-07-07 00:25:37 -07:00
uint64_t mvkGetElapsedNanoseconds(uint64_t startTimestamp, uint64_t endTimestamp) {
if (endTimestamp == 0) { endTimestamp = mvkGetTimestamp(); }
return (endTimestamp - startTimestamp) * _mvkMachTimebase.numer / _mvkMachTimebase.denom;
Support the `VK_EXT_pipeline_creation_feedback` extension. This provides feedback that indicates: * how long it took to compile each shader stage and the pipeline as a whole; * whether or not the pipeline or any shader stage were found in any supplied pipeline cache; and * whether or not any supplied base pipeline were used to accelerate pipeline creation. This is similar to the performance statistics that MoltenVK already collects. Since we don't use any supplied base pipeline at all, this implementation never sets `VK_PIPELINE_CREATION_FEEDBACK_BASE_PIPELINE_ACCELERATION_BIT`. However, I've identified several places where we could probably use the base pipeline to accelerate pipeline creation. One day, I should probably implement that. Likewise, because we don't yet support using `MTLBinaryArchive`s, `VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT` is never set on the whole pipeline, though it *is* set for individual stages, on the assumption that any shader found in a cache is likely to be found in Metal's own implicit cache. In this implementation, shader stage compilation time includes any time needed to build the `MTLComputePipelineState`s needed for vertex and tessellation control shaders in tessellated pipelines. This patch also changes compilation of the vertex stage `MTLComputePipelineState`s in tessellated pipelines to be eager instead of lazy. We really ought to have been doing this anyway, in order to report pipeline failures at creation time instead of draw time. I'm not happy, though, that we now pay the cost of all three pipeline states all the time, instead of just the ones that are used. This also gets rid of some fields of `MVKGraphicsPipeline` that were only used during pipeline construction, which should save some memory, particularly for apps that create lots of pipelines.
2023-07-07 00:25:37 -07:00
}
double mvkGetElapsedMilliseconds(uint64_t startTimestamp, uint64_t endTimestamp) {
return mvkGetElapsedNanoseconds(startTimestamp, endTimestamp) / 1e6;
}
// Initialize timestamp capabilities on app startup.
// Called automatically when the framework is loaded and initialized.
static bool _mvkTimestampsInitialized = false;
__attribute__((constructor)) static void MVKInitTimestamps() {
if (_mvkTimestampsInitialized ) { return; }
_mvkTimestampsInitialized = true;
mach_timebase_info(&_mvkMachTimebase);
}
#pragma mark -
#pragma mark Process environment
bool mvkGetEnvVar(const char* varName, string& evStr) {
@autoreleasepool {
NSDictionary* nsEnv = [[NSProcessInfo processInfo] environment];
NSString* nsStr = nsEnv[@(varName)];
if (nsStr) { evStr = nsStr.UTF8String; }
return nsStr != nil;
}
}
const char* mvkGetEnvVarString(const char* varName, string& evStr, const char* defaultValue) {
return mvkGetEnvVar(varName, evStr) ? evStr.c_str() : defaultValue;
}
double mvkGetEnvVarNumber(const char* varName, double defaultValue) {
string evStr;
return mvkGetEnvVar(varName, evStr) ? strtod(evStr.c_str(), nullptr) : defaultValue;
}
#pragma mark -
#pragma mark System memory
uint64_t mvkGetSystemMemorySize() {
uint64_t host_memsize = 0;
size_t size = sizeof(host_memsize);
if (sysctlbyname("hw.memsize", &host_memsize, &size, NULL, 0) == KERN_SUCCESS) {
return host_memsize;
}
2020-06-18 16:36:45 -07:00
return 0;
}
uint64_t mvkGetAvailableMemorySize() {
2020-06-15 16:33:51 -07:00
#if MVK_IOS_OR_TVOS
if (mvkOSVersionIsAtLeast(13.0)) { return os_proc_available_memory(); }
#endif
mach_port_t host_port;
mach_msg_type_number_t host_size;
vm_size_t pagesize;
host_port = mach_host_self();
host_size = HOST_VM_INFO_COUNT;
host_page_size(host_port, &pagesize);
vm_statistics_data_t vm_stat;
if (host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size) == KERN_SUCCESS ) {
return vm_stat.free_count * pagesize;
}
return 0;
}
uint64_t mvkGetUsedMemorySize() {
task_vm_info_data_t task_vm_info;
mach_msg_type_number_t task_size = TASK_VM_INFO_COUNT;
if (task_info(mach_task_self(), TASK_VM_INFO, (task_info_t)&task_vm_info, &task_size) == KERN_SUCCESS) {
#ifdef TASK_VM_INFO_REV3_COUNT // check for rev3 version of task_vm_info
if (task_size >= TASK_VM_INFO_REV3_COUNT) {
return task_vm_info.ledger_tag_graphics_footprint;
}
else
#endif
return task_vm_info.phys_footprint;
}
return 0;
}
uint64_t mvkGetHostMemoryPageSize() { return sysconf(_SC_PAGESIZE); }
#pragma mark -
#pragma mark Threading
/** Returns the amount of avaliable CPU cores. */
uint32_t mvkGetAvaliableCPUCores() {
return (uint32_t)[[NSProcessInfo processInfo] activeProcessorCount];
}
void mvkDispatchToMainAndWait(dispatch_block_t block) {
if (NSThread.isMainThread) {
block();
} else {
dispatch_sync(dispatch_get_main_queue(), block);
}
}