1/* 2 * Copyright 2021 Google LLC 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "src/gpu/graphite/mtl/MtlResourceProvider.h" 9 10#include "include/gpu/ShaderErrorHandler.h" 11#include "include/gpu/graphite/BackendTexture.h" 12#include "src/sksl/SkSLProgramKind.h" 13 14#include "src/core/SkSLTypeShared.h" 15#include "src/gpu/Blend.h" 16#include "src/gpu/PipelineUtils.h" 17#include "src/gpu/Swizzle.h" 18#include "src/gpu/graphite/ComputePipelineDesc.h" 19#include "src/gpu/graphite/ContextUtils.h" 20#include "src/gpu/graphite/GlobalCache.h" 21#include "src/gpu/graphite/GraphicsPipelineDesc.h" 22#include "src/gpu/graphite/RenderPassDesc.h" 23#include "src/gpu/graphite/Renderer.h" 24#include "src/gpu/graphite/RendererProvider.h" 25#include "src/gpu/graphite/compute/ComputeStep.h" 26#include "src/gpu/graphite/mtl/MtlBuffer.h" 27#include "src/gpu/graphite/mtl/MtlCommandBuffer.h" 28#include "src/gpu/graphite/mtl/MtlComputePipeline.h" 29#include "src/gpu/graphite/mtl/MtlGraphicsPipeline.h" 30#include "src/gpu/graphite/mtl/MtlGraphiteUtilsPriv.h" 31#include "src/gpu/graphite/mtl/MtlSampler.h" 32#include "src/gpu/graphite/mtl/MtlSharedContext.h" 33#include "src/gpu/graphite/mtl/MtlTexture.h" 34#include "src/gpu/mtl/MtlUtilsPriv.h" 35#include "src/sksl/SkSLCompiler.h" 36#include "src/sksl/SkSLProgramSettings.h" 37#include "src/sksl/ir/SkSLProgram.h" 38 39#import <Metal/Metal.h> 40 41namespace skgpu::graphite { 42 43MtlResourceProvider::MtlResourceProvider(SharedContext* sharedContext, 44 SingleOwner* singleOwner, 45 uint32_t recorderID, 46 size_t resourceBudget) 47 : ResourceProvider(sharedContext, singleOwner, recorderID, resourceBudget) {} 48 49const MtlSharedContext* MtlResourceProvider::mtlSharedContext() { 50 return static_cast<const MtlSharedContext*>(fSharedContext); 51} 52 53sk_sp<MtlGraphicsPipeline> MtlResourceProvider::findOrCreateLoadMSAAPipeline( 54 const RenderPassDesc& renderPassDesc) { 55 uint64_t renderPassKey = 56 this->mtlSharedContext()->mtlCaps().getRenderPassDescKey(renderPassDesc); 57 sk_sp<MtlGraphicsPipeline> pipeline = fLoadMSAAPipelines[renderPassKey]; 58 if (!pipeline) { 59 static const char* kLoadMSAAShaderText = R"( 60 #include <metal_stdlib> 61 #include <simd/simd.h> 62 using namespace metal; 63 64 typedef struct { 65 float4 position [[position]]; 66 } VertexOutput; 67 68 vertex VertexOutput vertexMain(uint vertexID [[vertex_id]]) { 69 VertexOutput out; 70 float2 position = float2(float(vertexID >> 1), float(vertexID & 1)); 71 out.position = float4(2.0 * position - 1.0, 0.0, 1.0); 72 return out; 73 } 74 75 fragment float4 fragmentMain(VertexOutput in [[stage_in]], 76 texture2d<half> colorMap [[texture(0)]]) { 77 uint2 coords = uint2(in.position.x, in.position.y); 78 half4 colorSample = colorMap.read(coords); 79 return float4(colorSample); 80 } 81 )"; 82 83 auto mtlLibrary = MtlCompileShaderLibrary(this->mtlSharedContext(), 84 "LoadMSAAFromResolve", 85 kLoadMSAAShaderText, 86 fSharedContext->caps()->shaderErrorHandler()); 87 BlendInfo noBlend{}; // default is equivalent to kSrc blending 88 sk_cfp<id<MTLDepthStencilState>> ignoreDS = 89 this->findOrCreateCompatibleDepthStencilState({}); 90 91 std::string pipelineLabel = "LoadMSAAFromResolve + "; 92 pipelineLabel += renderPassDesc.toString().c_str(); 93 pipeline = MtlGraphicsPipeline::Make(this->mtlSharedContext(), 94 pipelineLabel, 95 {mtlLibrary.get(), "vertexMain"}, 96 /*vertexAttrs=*/{}, 97 /*instanceAttrs=*/{}, 98 {mtlLibrary.get(), "fragmentMain"}, 99 std::move(ignoreDS), 100 /*stencilRefValue=*/0, 101 noBlend, 102 renderPassDesc, 103 /*pipelineInfo=*/nullptr); 104 if (pipeline) { 105 fLoadMSAAPipelines.set(renderPassKey, pipeline); 106 } 107 } 108 109 return pipeline; 110} 111 112sk_sp<GraphicsPipeline> MtlResourceProvider::createGraphicsPipeline( 113 const RuntimeEffectDictionary* runtimeDict, 114 const GraphicsPipelineDesc& pipelineDesc, 115 const RenderPassDesc& renderPassDesc) { 116 std::string vsMSL, fsMSL; 117 SkSL::Program::Interface vsInterface, fsInterface; 118 SkSL::ProgramSettings settings; 119 120 settings.fForceNoRTFlip = true; 121 122 SkSL::Compiler skslCompiler; 123 ShaderErrorHandler* errorHandler = fSharedContext->caps()->shaderErrorHandler(); 124 125 const RenderStep* step = 126 fSharedContext->rendererProvider()->lookup(pipelineDesc.renderStepID()); 127 const bool useStorageBuffers = fSharedContext->caps()->storageBufferPreferred(); 128 129 UniquePaintParamsID paintID = pipelineDesc.paintParamsID(); 130 FragSkSLInfo fsSkSLInfo = BuildFragmentSkSL(fSharedContext->caps(), 131 fSharedContext->shaderCodeDictionary(), 132 runtimeDict, 133 step, 134 paintID, 135 useStorageBuffers, 136 renderPassDesc.fWriteSwizzle); 137 std::string& fsSkSL = fsSkSLInfo.fSkSL; 138 const BlendInfo& blendInfo = fsSkSLInfo.fBlendInfo; 139 const bool localCoordsNeeded = fsSkSLInfo.fRequiresLocalCoords; 140 if (!SkSLToMSL(fSharedContext->caps()->shaderCaps(), 141 fsSkSL, 142 SkSL::ProgramKind::kGraphiteFragment, 143 settings, 144 &fsMSL, 145 &fsInterface, 146 errorHandler)) { 147 return nullptr; 148 } 149 150 VertSkSLInfo vsSkSLInfo = BuildVertexSkSL(fSharedContext->caps()->resourceBindingRequirements(), 151 step, 152 useStorageBuffers, 153 localCoordsNeeded); 154 const std::string& vsSkSL = vsSkSLInfo.fSkSL; 155 if (!SkSLToMSL(fSharedContext->caps()->shaderCaps(), 156 vsSkSL, 157 SkSL::ProgramKind::kGraphiteVertex, 158 settings, 159 &vsMSL, 160 &vsInterface, 161 errorHandler)) { 162 return nullptr; 163 } 164 165 auto vsLibrary = MtlCompileShaderLibrary( 166 this->mtlSharedContext(), vsSkSLInfo.fLabel, vsMSL, errorHandler); 167 auto fsLibrary = MtlCompileShaderLibrary( 168 this->mtlSharedContext(), fsSkSLInfo.fLabel, fsMSL, errorHandler); 169 170 sk_cfp<id<MTLDepthStencilState>> dss = 171 this->findOrCreateCompatibleDepthStencilState(step->depthStencilSettings()); 172 173#if defined(GRAPHITE_TEST_UTILS) 174 GraphicsPipeline::PipelineInfo pipelineInfo = {pipelineDesc.renderStepID(), 175 pipelineDesc.paintParamsID(), 176 std::move(vsSkSL), 177 std::move(fsSkSL), 178 std::move(vsMSL), 179 std::move(fsMSL) }; 180 GraphicsPipeline::PipelineInfo* pipelineInfoPtr = &pipelineInfo; 181#else 182 GraphicsPipeline::PipelineInfo* pipelineInfoPtr = nullptr; 183#endif 184 std::string pipelineLabel = 185 GetPipelineLabel(fSharedContext->shaderCodeDictionary(), renderPassDesc, step, paintID); 186 return MtlGraphicsPipeline::Make(this->mtlSharedContext(), 187 pipelineLabel, 188 {vsLibrary.get(), "vertexMain"}, 189 step->vertexAttributes(), 190 step->instanceAttributes(), 191 {fsLibrary.get(), "fragmentMain"}, 192 std::move(dss), 193 step->depthStencilSettings().fStencilReferenceValue, 194 blendInfo, 195 renderPassDesc, 196 pipelineInfoPtr); 197} 198 199sk_sp<ComputePipeline> MtlResourceProvider::createComputePipeline( 200 const ComputePipelineDesc& pipelineDesc) { 201 sk_cfp<id<MTLLibrary>> library; 202 std::string entryPointName; 203 ShaderErrorHandler* errorHandler = fSharedContext->caps()->shaderErrorHandler(); 204 if (pipelineDesc.computeStep()->supportsNativeShader()) { 205 auto nativeShader = pipelineDesc.computeStep()->nativeShaderSource( 206 ComputeStep::NativeShaderFormat::kMSL); 207 library = MtlCompileShaderLibrary(this->mtlSharedContext(), 208 pipelineDesc.computeStep()->name(), 209 nativeShader.fSource, 210 errorHandler); 211 if (library == nil) { 212 return nullptr; 213 } 214 entryPointName = std::move(nativeShader.fEntryPoint); 215 } else { 216 std::string msl; 217 SkSL::Program::Interface interface; 218 SkSL::ProgramSettings settings; 219 220 SkSL::Compiler skslCompiler; 221 std::string sksl = BuildComputeSkSL(fSharedContext->caps(), pipelineDesc.computeStep()); 222 if (!SkSLToMSL(fSharedContext->caps()->shaderCaps(), 223 sksl, 224 SkSL::ProgramKind::kCompute, 225 settings, 226 &msl, 227 &interface, 228 errorHandler)) { 229 return nullptr; 230 } 231 library = MtlCompileShaderLibrary(this->mtlSharedContext(), 232 pipelineDesc.computeStep()->name(), 233 msl, 234 errorHandler); 235 entryPointName = "computeMain"; 236 } 237 return MtlComputePipeline::Make(this->mtlSharedContext(), 238 pipelineDesc.computeStep()->name(), 239 {library.get(), std::move(entryPointName)}); 240} 241 242sk_sp<Texture> MtlResourceProvider::createTexture(SkISize dimensions, 243 const TextureInfo& info, 244 skgpu::Budgeted budgeted) { 245 return MtlTexture::Make(this->mtlSharedContext(), dimensions, info, budgeted); 246} 247 248sk_sp<Texture> MtlResourceProvider::onCreateWrappedTexture(const BackendTexture& texture) { 249 CFTypeRef mtlHandleTexture = texture.getMtlTexture(); 250 if (!mtlHandleTexture) { 251 return nullptr; 252 } 253 sk_cfp<id<MTLTexture>> mtlTexture = sk_ret_cfp((id<MTLTexture>)mtlHandleTexture); 254 return MtlTexture::MakeWrapped(this->mtlSharedContext(), texture.dimensions(), texture.info(), 255 std::move(mtlTexture)); 256} 257 258sk_sp<Buffer> MtlResourceProvider::createBuffer(size_t size, 259 BufferType type, 260 AccessPattern accessPattern) { 261 return MtlBuffer::Make(this->mtlSharedContext(), size, type, accessPattern); 262} 263 264sk_sp<Sampler> MtlResourceProvider::createSampler(const SamplerDesc& samplerDesc) { 265 return MtlSampler::Make(this->mtlSharedContext(), 266 samplerDesc.samplingOptions(), 267 samplerDesc.tileModeX(), 268 samplerDesc.tileModeY()); 269} 270 271namespace { 272MTLCompareFunction compare_op_to_mtl(CompareOp op) { 273 switch (op) { 274 case CompareOp::kAlways: 275 return MTLCompareFunctionAlways; 276 case CompareOp::kNever: 277 return MTLCompareFunctionNever; 278 case CompareOp::kGreater: 279 return MTLCompareFunctionGreater; 280 case CompareOp::kGEqual: 281 return MTLCompareFunctionGreaterEqual; 282 case CompareOp::kLess: 283 return MTLCompareFunctionLess; 284 case CompareOp::kLEqual: 285 return MTLCompareFunctionLessEqual; 286 case CompareOp::kEqual: 287 return MTLCompareFunctionEqual; 288 case CompareOp::kNotEqual: 289 return MTLCompareFunctionNotEqual; 290 } 291} 292 293MTLStencilOperation stencil_op_to_mtl(StencilOp op) { 294 switch (op) { 295 case StencilOp::kKeep: 296 return MTLStencilOperationKeep; 297 case StencilOp::kZero: 298 return MTLStencilOperationZero; 299 case StencilOp::kReplace: 300 return MTLStencilOperationReplace; 301 case StencilOp::kInvert: 302 return MTLStencilOperationInvert; 303 case StencilOp::kIncWrap: 304 return MTLStencilOperationIncrementWrap; 305 case StencilOp::kDecWrap: 306 return MTLStencilOperationDecrementWrap; 307 case StencilOp::kIncClamp: 308 return MTLStencilOperationIncrementClamp; 309 case StencilOp::kDecClamp: 310 return MTLStencilOperationDecrementClamp; 311 } 312} 313 314MTLStencilDescriptor* stencil_face_to_mtl(DepthStencilSettings::Face face) { 315 MTLStencilDescriptor* result = [[MTLStencilDescriptor alloc] init]; 316 result.stencilCompareFunction = compare_op_to_mtl(face.fCompareOp); 317 result.readMask = face.fReadMask; 318 result.writeMask = face.fWriteMask; 319 result.depthStencilPassOperation = stencil_op_to_mtl(face.fDepthStencilPassOp); 320 result.stencilFailureOperation = stencil_op_to_mtl(face.fStencilFailOp); 321 return result; 322} 323} // anonymous namespace 324 325sk_cfp<id<MTLDepthStencilState>> MtlResourceProvider::findOrCreateCompatibleDepthStencilState( 326 const DepthStencilSettings& depthStencilSettings) { 327 sk_cfp<id<MTLDepthStencilState>>* depthStencilState; 328 depthStencilState = fDepthStencilStates.find(depthStencilSettings); 329 if (!depthStencilState) { 330 MTLDepthStencilDescriptor* desc = [[MTLDepthStencilDescriptor alloc] init]; 331 SkASSERT(depthStencilSettings.fDepthTestEnabled || 332 depthStencilSettings.fDepthCompareOp == CompareOp::kAlways); 333 desc.depthCompareFunction = compare_op_to_mtl(depthStencilSettings.fDepthCompareOp); 334 if (depthStencilSettings.fDepthTestEnabled) { 335 desc.depthWriteEnabled = depthStencilSettings.fDepthWriteEnabled; 336 } 337 if (depthStencilSettings.fStencilTestEnabled) { 338 desc.frontFaceStencil = stencil_face_to_mtl(depthStencilSettings.fFrontStencil); 339 desc.backFaceStencil = stencil_face_to_mtl(depthStencilSettings.fBackStencil); 340 } 341 342 sk_cfp<id<MTLDepthStencilState>> dss( 343 [this->mtlSharedContext()->device() newDepthStencilStateWithDescriptor: desc]); 344 depthStencilState = fDepthStencilStates.set(depthStencilSettings, std::move(dss)); 345 } 346 347 SkASSERT(depthStencilState); 348 return *depthStencilState; 349} 350 351BackendTexture MtlResourceProvider::onCreateBackendTexture(SkISize dimensions, 352 const TextureInfo& info) { 353 sk_cfp<id<MTLTexture>> texture = MtlTexture::MakeMtlTexture(this->mtlSharedContext(), 354 dimensions, 355 info); 356 if (!texture) { 357 return {}; 358 } 359 return BackendTexture(dimensions, (CFTypeRef)texture.release()); 360} 361 362void MtlResourceProvider::onDeleteBackendTexture(const BackendTexture& texture) { 363 SkASSERT(texture.backend() == BackendApi::kMetal); 364 CFTypeRef texHandle = texture.getMtlTexture(); 365 SkCFSafeRelease(texHandle); 366} 367 368} // namespace skgpu::graphite 369