#include "RenderAPI.h" #include "PlatformBase.h" // Metal implementation of RenderAPI. #if SUPPORT_METAL #include "Unity/IUnityGraphicsMetal.h" #import #import #import class RenderAPI_Metal : public RenderAPI { public: RenderAPI_Metal(); virtual ~RenderAPI_Metal() { } virtual void processDeviceEvent(UnityGfxDeviceEventType type, IUnityInterfaces* interfaces); virtual bool getUsesReverseZ() { return true; } virtual void initSupportFeature(); virtual void spatialUpScale(void*data) override; virtual void enableVRS(void* data) override; virtual void disableVRS() override; private: id mfx_spatial_scaler; id commandQueue; id outTexture; IUnityGraphicsMetal* metal_graphics; }; RenderAPI* CreateRenderAPI_Metal() { return new RenderAPI_Metal(); } RenderAPI_Metal::RenderAPI_Metal() { } void RenderAPI_Metal::processDeviceEvent(UnityGfxDeviceEventType type, IUnityInterfaces* interfaces) { if (type == kUnityGfxDeviceEventInitialize) { metal_graphics = interfaces->Get(); initSupportFeature(); } else if (type == kUnityGfxDeviceEventShutdown) { //@TODO: release resources } } void RenderAPI_Metal::initSupportFeature() { if (@available(iOS 16, macOS 13, *)) { support_features[GraphicsFeature::METAL_FX_SPATIAL_SR] = true; support_features[GraphicsFeature::METAL_FX_TEMPORAL_SR] = true; id device = metal_graphics->MetalDevice(); if(device) { support_features[GraphicsFeature::METAL_VRR] = [device supportsRasterizationRateMapWithLayerCount:1]; } } } void RenderAPI_Metal::spatialUpScale(void* data) { if (@available(iOS 16, macOS 13, *)) { struct DataPack { void* src; void* dst; bool qulityChange; }; DataPack* data_pack = static_cast(data); id srctex = (__bridge id)data_pack->src; id dsttex = (__bridge id)data_pack->dst; id device = metal_graphics->MetalDevice(); id cmd = (id)metal_graphics->CurrentCommandBuffer(); metal_graphics->EndCurrentCommandEncoder(); cmd.label = @"Upscale Command Buffer"; if (mfx_spatial_scaler == nil ) { MTLFXSpatialScalerDescriptor* desc = [[MTLFXSpatialScalerDescriptor alloc]init]; desc.inputWidth = [srctex width]; desc.inputHeight = [srctex height]; desc.outputWidth = [dsttex width]; desc.outputHeight = [dsttex height]; desc.colorTextureFormat = [srctex pixelFormat]; desc.outputTextureFormat = [dsttex pixelFormat]; desc.colorProcessingMode = MTLFXSpatialScalerColorProcessingModeLinear; mfx_spatial_scaler = [desc newSpatialScalerWithDevice:device]; commandQueue = [device newCommandQueue]; MTLTextureDescriptor *texdesc = [[MTLTextureDescriptor alloc] init]; texdesc.width = (int)desc.outputWidth; texdesc.height = (int)desc.outputHeight; texdesc.storageMode = MTLStorageModePrivate; texdesc.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite; texdesc.pixelFormat = desc.outputTextureFormat; outTexture = [device newTextureWithDescriptor:texdesc]; } if (mfx_spatial_scaler == nil || commandQueue == nil) { return; } if(!(srctex && dsttex)) { return; } mfx_spatial_scaler.colorTexture = srctex; mfx_spatial_scaler.outputTexture = outTexture; mfx_spatial_scaler.inputContentWidth = [srctex width]; mfx_spatial_scaler.inputContentHeight = [srctex height]; // if(!cmd) // { // id upscaleCommandBuffer = [commandQueue commandBuffer]; // upscaleCommandBuffer.label = @"Upscale Command Buffer"; // [mfx_spatial_scaler encodeToCommandBuffer:upscaleCommandBuffer]; // [upscaleCommandBuffer commit]; // id textureCommandBuffer = [commandQueue commandBuffer]; // id _mfxSpatialEncoder =[textureCommandBuffer blitCommandEncoder]; // [_mfxSpatialEncoder copyFromTexture: outTexture toTexture: dsttex]; // [_mfxSpatialEncoder endEncoding]; // [textureCommandBuffer commit]; // } // else { [mfx_spatial_scaler encodeToCommandBuffer:cmd]; // [cmd commit]; // id textureCommandBuffer = [commandQueue commandBuffer]; id _mfxSpatialEncoder =[textureCommandBuffer blitCommandEncoder]; [_mfxSpatialEncoder copyFromTexture: outTexture toTexture: dsttex]; [_mfxSpatialEncoder endEncoding]; // [textureCommandBuffer commit]; } } } void RenderAPI_Metal::enableVRS(void* data) { struct DataPack { float width; float height; float ratex; float ratey; bool cfgChange; }; id device = metal_graphics->MetalDevice(); DataPack* data_pack = static_cast(data); MTLRasterizationRateMapDescriptor *descriptor = [[MTLRasterizationRateMapDescriptor alloc] init]; descriptor.label = @"raster rate map"; descriptor.screenSize = MTLSizeMake(data_pack->width, data_pack->height, 1); MTLSize zone_counts = MTLSizeMake(8, 4, 1); MTLRasterizationRateLayerDescriptor *layer_descriptor = [[MTLRasterizationRateLayerDescriptor alloc] initWithSampleCount:zone_counts]; for (int row = 0; row < zone_counts.height; row++) { layer_descriptor.verticalSampleStorage[row] = data_pack->ratey; } for (int column = 0; column < zone_counts.width; column++) { layer_descriptor.horizontalSampleStorage[column] = data_pack->ratex; } [descriptor setLayer:layer_descriptor atIndex:0]; id rate_map = [device newRasterizationRateMapWithDescriptor: descriptor]; MTLRenderPassDescriptor* current_pass_descriptor= metal_graphics->CurrentRenderPassDescriptor(); current_pass_descriptor.rasterizationRateMap = rate_map; } void RenderAPI_Metal::disableVRS() { } #endif // #if SUPPORT_METAL