How to compile two versions of metal files - ios

I want to support both 10.13 and 10.14 however I want to support fast math on 10.14. I am only able to compile project if I force #define __CIKERNEL_METAL_VERSION__ 200 but this means on 10.13 it will crash. How do I configure the project so it creates 2 metal libraries? So far the result file is default.metallib (compiling using Xcode)
BOOL supportsMetal;
#if TARGET_OS_IOS
supportsMetal = MTLCreateSystemDefaultDevice() != nil; //this forces GPU on macbook to switch immediatelly
#else
supportsMetal = [MTLCopyAllDevices() count] >= 1;
#endif
if (#available(macOS 10.13, *)) {
//only 10.14 fully supports metal with fast math, however there are hackintoshes etc...
if (supportsMetal) {
_kernel = [self metalKernel];
} else {
_kernel = [self GLSLKernel];
}
} else {
_kernel = [self GLSLKernel];
}
if (_kernel == nil) return nil;
METAL file
#include <metal_stdlib>
using namespace metal;
//https://en.wikipedia.org/wiki/List_of_monochrome_and_RGB_palettes
//https://en.wikipedia.org/wiki/Relative_luminance
//https://en.wikipedia.org/wiki/Grayscale
//<CoreImage/CIKernelMetalLib.h>
//only if you enable fast math (macOS10.14 or iOS12) otherwise fall back to float4 instead of half4
//forcing compilation for macOS 10.14+//iOS12+
#define __CIKERNEL_METAL_VERSION__ 200
constant half3 kRec709Luma = half3(0.2126, 0.7152, 0.0722);
constant half3 kRec601Luma = half3(0.299 , 0.587 , 0.114);
//constant float3 kRec2100Luma = float3(0.2627, 0.6780, 0.0593);
#include <CoreImage/CoreImage.h>
extern "C" { namespace coreimage {
float lumin601(half3 p)
{
return dot(p.rgb, kRec601Luma);
}
float lumin709(half3 p)
{
return dot(p.rgb, kRec709Luma);
}
half4 thresholdFilter(sample_h image, float threshold)
{
half4 pix = unpremultiply(image);
float luma = lumin601(pix.rgb);
pix.rgb = half3(step(threshold, luma));
return premultiply(pix);
}
}}

XCode 11 supports Metal libraries.
Add a new build target to your project.
Add metal files in compile sources
If you use Core Image add these linker flags. Change deployment targets (ios12+ ) and check for fast math.
To your original project target add new dependencies and copy script
cp "${BUILT_PRODUCTS_DIR}"/*.metallib "${METAL_LIBRARY_OUTPUT_DIR}"
Optional:
Avoiding hard coded strings everywhere in project. Add xconfig file to project
MY_METAL_LIBRARY_NAME_10_13 = Metal_10_13_aaa
MY_METAL_LIBRARY_NAME_10_14 = Metal_10_14_bbb
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) MY_METAL_LIBRARY_NAME_10_13='#"$(MY_METAL_LIBRARY_NAME_10_13)"' MY_METAL_LIBRARY_NAME_10_14='#"$(MY_METAL_LIBRARY_NAME_10_14)"'
Add xconfig as configuration (don't set it for project cause you will end up with double import)
Change PRODUCT_NAME variable of each metal library to a variable
Use preprocesor variables in code
static NSString *const kMetallibExtension = #"metallib";
NSString *const kMetalLibraryOldTarget = MY_METAL_LIBRARY_NAME_10_13; //#"Metal_10_13";
NSString *const kMetalLibraryFastMathTarget = MY_METAL_LIBRARY_NAME_10_14; //#"Metal_10_14";
+ (NSString *)metalLibraryName
{
if (#available(macOS 10.14, *)) {
return kMetalLibraryFastMathTarget;
} else {
return kMetalLibraryOldTarget;
}
//use default
//return #"default";
}

Related

WebAssembly.instantiate fails when wasm file was compiled with `clang++`, but works with `clang`

When C/C++ .wasm code is compiled with clang (C) - it loads in Chrome and works well, but when with clang++ (C++) - wasm load fails with error (in JS console):
Uncaught (in promise) LinkError: WebAssembly.instantiate(): Import #1 module="wasi_snapshot_preview1" function="fd_close" error: function import requires a callable
Why?
WASM compile arguments:
"clang", <=== I only changed this to "clang++" - and it fails
"-O0",
// "-std=c++14",
"--target=wasm32-unknown-wasi",
"--sysroot C:\\OpenGL\\wasi-sdk-11.0-mingw.tar\\wasi-sdk-11.0\\share\\wasi-sysroot",
"-fno-exceptions",
"-nostartfiles",
"-Wl,--import-memory",
"-Wl,--no-entry",
"-Wl,--export-all",
"-o templates/my-app/public/hello_wasm.wasm",
"wasm/hello_wasm.cpp"
JS wasm load code:
const response = await fetch("./hello_wasm.wasm");
const bytes = await response.arrayBuffer();
const { instance } = await WebAssembly.instantiate(bytes, {
env: { memory: this.memory },
},
});
this.instance = instance;
console.log("c" + instance);
})();
hello_wasm.cpp (compilation was without an error):
#include <math.h>
// extern "C"
// {
int int_sqrt(int x)
{
return sqrt(x);
};
float *run_sin(float x[], int n)
{
// float *a = new float[n];
float *a = (float *)malloc(sizeof(float) * n);
for (int i = 0; i < n; i++)
{
a[i] = x[i] * 2;
}
return a;
}
LLVM v10
I use wasi sysroot from https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-11/wasi-sdk-11.0-mingw.tar.gz
Also discussing this issue here https://github.com/WebAssembly/wasi-sdk/issues/145
In order to run a WASI binary on the web you need to provide an implementation of the WASI APIs. The web platform does not natively support WASI. There are some polyfills out there that try to emulate some/all of the WASI APIs which might work for your case.

HLSL min16float fails to compile

I have a shader:
float4 Test_PixelShader(float2 inTex:TEXCOORD,
uniform int mode ):COLOR
{
if(mode) // half
{
min16float2 t=(min16float2)inTex;
min16float2 r=1;
for(int i=0; i<256; i++)
{
r+=t*r;
}
return float4(r.x, r.y, mode, 1);
}else
{
float2 t=inTex;
float2 r=1;
for(int i=0; i<256; i++)
{
r+=t*r;
}
return float4(r.x, r.y, mode, 1);
}
}
#define TECHNIQUE5(name, vs, ps) technique11 name{pass p0{SetVertexShader(CompileShader(vs_5_0, vs)); SetPixelShader(CompileShader(ps_5_0, ps));}}
TECHNIQUE5(Test , Draw_VertexShader(), Test_PixelShader(0));
TECHNIQUE5(Test1, Draw_VertexShader(), Test_PixelShader(1));
Which I'm compiling using:
#define FLAGS_DX11 (D3DCOMPILE_ENABLE_BACKWARDS_COMPATIBILITY|D3DCOMPILE_OPTIMIZATION_LEVEL3|D3DCOMPILE_NO_PRESHADER)
D3DCompile(data.data(), data.elms(), null, d3d_macros.data(), &Include11(src), null, "fx_5_0", FLAGS_DX11, 0, &buffer, &error);
Compilation fails, and the only error message I get is:
warning X4717: Effects deprecated for D3DCompiler_47
No errors, just a warning, yet the shader blob is null.
However if I replace all min16float2 with float2, then compilation works OK.
How to get min16float working?
I've read the https://gpuopen.com/first-steps-implementing-fp16/ article, and it mentions it should work OK.
Do I need to use https://github.com/microsoft/DirectXShaderCompiler instead of D3DCompile from Win10SDK?
I have Windows 10, using latest Windows SDK
Looks like it fails because Microsoft abandoned "fx_*" targets, and have to use "vs/ps" targets instead.

How can I use `-rewrite-objc` to transform the block in `.c` to the right `.cpp` file in ARC?

In the book, Pro multithreading and memory management for iOS and OS X, section 2.3, the author said the following code can be transformed to a .cpp code:
typedef int (^blk_t)(int);
blk_t func(int rate) {
return ^(int count){return rate * count;};
}
.cpp code:
blk func(int rate) {
blk_t tmp = &__func_block_impl_0 (
__func_block_func_0, &__func_block_desc_0_DATA, rate
);
tmp = objc_retainBlock(tmp);
return objc_autoreleaseReturnValue(tmp);
}
But I can't get it by using clang -rewrite-objc -fobjc-arc block.c, and this is my result, following:
So, how can I get the right result as the book description? And what's the right clang command which can transform c/cpp/objc to cpp in ARC?
use clang -rewrite-objc fileName.c/m to try.

Determine if iOS device is 32- or 64-bit

Does anyone know of an easy way to tell if an iOS7 device has 32- or 64-bit hardware? I don't mean programmatically, I just mean via settings, model number, 3rd-party app, etc.
I'm having a problem that I suspect is 64-bit related. Apple's advice is to test on the 64-bit simulator but also on an actual 64-bit device, but then doesn't say anything about how to determine that. I can write a test app to check sizeof(int) or whatever, but there's got to be some way for, say, tech support to know what they're working with.
Eric
There is no other "official" way to determine it. You can determine it using this code:
if (sizeof(void*) == 4) {
NSLog(#"32-bit App");
} else if (sizeof(void*) == 8) {
NSLog(#"64-bit App");
}
Below is the method is64bitHardware. It returns YES if the hardware is a 64-bit hardware and works on a real iOS device and in an iOS Simulator. Here is source.
#include <mach/mach.h>
+ (BOOL) is64bitHardware
{
#if __LP64__
// The app has been compiled for 64-bit intel and runs as 64-bit intel
return YES;
#endif
// Use some static variables to avoid performing the tasks several times.
static BOOL sHardwareChecked = NO;
static BOOL sIs64bitHardware = NO;
if(!sHardwareChecked)
{
sHardwareChecked = YES;
#if TARGET_IPHONE_SIMULATOR
// The app was compiled as 32-bit for the iOS Simulator.
// We check if the Simulator is a 32-bit or 64-bit simulator using the function is64bitSimulator()
// See http://blog.timac.org/?p=886
sIs64bitHardware = is64bitSimulator();
#else
// The app runs on a real iOS device: ask the kernel for the host info.
struct host_basic_info host_basic_info;
unsigned int count;
kern_return_t returnValue = host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)(&host_basic_info), &count);
if(returnValue != KERN_SUCCESS)
{
sIs64bitHardware = NO;
}
sIs64bitHardware = (host_basic_info.cpu_type == CPU_TYPE_ARM64);
#endif // TARGET_IPHONE_SIMULATOR
}
return sIs64bitHardware;
}
Totally untested, but you should be able to get the CPU via sysctl like this:
#include <sys/types.h>
#include <sys/sysctl.h>
#include <mach/machine.h>
void foo() {
size_t size;
cpu_type_t type;
size = sizeof(type);
sysctlbyname("hw.cputype", &type, &size, NULL, 0);
if (type == CPU_TYPE_ARM64) {
// ARM 64-bit CPU
} else if (type == CPU_TYPE_ARM) {
// ARM 32-bit CPU
} else {
// Something else.
}
}
In the iOS 7 SDK, CPU_TYPE_ARM64 is defined in <mach/machine.h> as:
#define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64)
A different way seems to be:
#include <mach/mach_host.h>
void foo() {
host_basic_info_data_t hostInfo;
mach_msg_type_number_t infoCount;
infoCount = HOST_BASIC_INFO_COUNT;
host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)&hostInfo, &infoCount);
if (hostInfo.cpu_type == CPU_TYPE_ARM64) {
// ARM 64-bit CPU
} else if (hostInfo.cpu_type == CPU_TYPE_ARM) {
// ARM 32-bit CPU
} else {
// Something else.
}
}
If you are compiling with clang, there is another way: just check if __arm__ or __arm64__ is defined.
The example code below is not tested but it should illustrate what I mean by that:
#if defined(__arm__)
NSLog(#"32-bit App");
#elif defined(__arm64__)
NSLog(#"64-bit App");
#else
NSLog(#"Not running ARM");
#endif
Note that this relies on the fact that current iOS application binaries contain both, 32bit and 64bit binaries in a single container and they will be correctly selected depending on whether your app supports executing 64bit.
You can use bitWidth on Int
https://developer.apple.com/documentation/swift/int/2885648-bitwidth
static var is32Bit: Bool {
return Int.bitWidth == 32
}
static var is64Bit: Bool {
return Int.bitWidth == 64
}
I use this in swift 4, not sure if it's the best solution but it works.
func getCPUArch()
{
#if arch(arm)
print("this is a 32bit system")
#elseif arch(arm64)
print("this is a 64bit system")
#endif
}
In runtime you can use something like this
extension UIDevice {
static let is64Bit = MemoryLayout<Int>.size == MemoryLayout<Int64>.size
}

What is the simplest way to retrieve the device serial number of an iOS device using MonoTouch?

Does MonoTouch have a simple mechanism for retrieving the device serial number (not UDID) of an iOS device? Is there a third-party library which I can use to obtain this?
In case it matters, I'm looking to use this functionality in an in-house application and am not concerned with the App Store approval process.
UPDATE: from iOS 8, we cannot retrieve the serial number of our iDevice.
To retrieve iphone serial number from Monotouch, you can use this technic:
Create a static library .a from XCode that have a function to get serial number
In MonoDevelop, create a binding project to bind you .a library into C# classes/functions (http://docs.xamarin.com/guides/ios/advanced_topics/binding_objective-c_libraries)
In your application, you call this binding library (in step 2).
For detail:
STEP 1. In my library.a, I have a class DeviceInfo, here is the implementation to get Serial number
#import "DeviceInfo.h"
#import <dlfcn.h>
#import <mach/port.h>
#import <mach/kern_return.h>
#implementation DeviceInfo
- (NSString *) serialNumber
{
NSString *serialNumber = nil;
void *IOKit = dlopen("/System/Library/Frameworks/IOKit.framework/IOKit", RTLD_NOW);
if (IOKit)
{
mach_port_t *kIOMasterPortDefault = dlsym(IOKit, "kIOMasterPortDefault");
CFMutableDictionaryRef (*IOServiceMatching)(const char *name) = dlsym(IOKit, "IOServiceMatching");
mach_port_t (*IOServiceGetMatchingService)(mach_port_t masterPort, CFDictionaryRef matching) = dlsym(IOKit, "IOServiceGetMatchingService");
CFTypeRef (*IORegistryEntryCreateCFProperty)(mach_port_t entry, CFStringRef key, CFAllocatorRef allocator, uint32_t options) = dlsym(IOKit, "IORegistryEntryCreateCFProperty");
kern_return_t (*IOObjectRelease)(mach_port_t object) = dlsym(IOKit, "IOObjectRelease");
if (kIOMasterPortDefault && IOServiceGetMatchingService && IORegistryEntryCreateCFProperty && IOObjectRelease)
{
mach_port_t platformExpertDevice = IOServiceGetMatchingService(*kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
if (platformExpertDevice)
{
CFTypeRef platformSerialNumber = IORegistryEntryCreateCFProperty(platformExpertDevice, CFSTR("IOPlatformSerialNumber"), kCFAllocatorDefault, 0);
if (CFGetTypeID(platformSerialNumber) == CFStringGetTypeID())
{
serialNumber = [NSString stringWithString:(__bridge NSString*)platformSerialNumber];
CFRelease(platformSerialNumber);
}
IOObjectRelease(platformExpertDevice);
}
}
dlclose(IOKit);
}
return serialNumber;
}
#end
STEP 2. In ApiDefinition.cs of my Binding Library project in Monotouch, I add this binding:
[BaseType (typeof (NSObject))]
public interface DeviceInfo {
[Export ("serialNumber")]
NSString GetSerialNumber ();
}
STEP 3. In my application, I import Reference to Binding library project in step 2, then add
using MyBindingProject;
...
string serialNumber = "";
try {
DeviceInfo nativeDeviceInfo = new DeviceInfo ();
NSString temp = nativeDeviceInfo.GetSerialNumber();
serialNumber = temp.ToString();
} catch (Exception ex) {
Console.WriteLine("Cannot get serial number {0} - {1}",ex.Message, ex.StackTrace);
}
Hope that helps. Don't hesitate if you have any question.

Resources