Hash :
7ef6452d
Author :
Date :
2023-06-14T16:02:46
Metal: Refactor hasValidRenderTarget checks Separate the computation of hasValidRenderTarget into a function and only call it if the cheaper check to allowRenderpassWithoutAttachment returns false. Bug: chromium:1329376 Change-Id: Id20a33913b7133a5aea82f53abb0a86664b4f2c6 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4615188 Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org> Commit-Queue: Geoff Lang <geofflang@chromium.org>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
//
// Copyright 2023 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// mtl_pipeline_cache.mm:
// Defines classes for caching of mtl pipelines
//
#include "libANGLE/renderer/metal/mtl_pipeline_cache.h"
#include "libANGLE/renderer/metal/ContextMtl.h"
namespace rx
{
namespace mtl
{
namespace
{
bool HasDefaultAttribs(const RenderPipelineDesc &rpdesc)
{
const VertexDesc &desc = rpdesc.vertexDescriptor;
for (uint8_t i = 0; i < desc.numAttribs; ++i)
{
if (desc.attributes[i].bufferIndex == kDefaultAttribsBindingIndex)
{
return true;
}
}
return false;
}
bool HasValidRenderTarget(const mtl::ContextDevice &device,
const MTLRenderPipelineDescriptor *descriptor)
{
const NSUInteger maxColorRenderTargets = GetMaxNumberOfRenderTargetsForDevice(device);
for (NSUInteger i = 0; i < maxColorRenderTargets; ++i)
{
auto colorAttachment = descriptor.colorAttachments[i];
if (colorAttachment && colorAttachment.pixelFormat != MTLPixelFormatInvalid)
{
return true;
}
}
if (descriptor.depthAttachmentPixelFormat != MTLPixelFormatInvalid)
{
return true;
}
if (descriptor.stencilAttachmentPixelFormat != MTLPixelFormatInvalid)
{
return true;
}
return false;
}
angle::Result ValidateRenderPipelineState(ContextMtl *context,
const MTLRenderPipelineDescriptor *descriptor)
{
const mtl::ContextDevice &device = context->getMetalDevice();
if (!context->getDisplay()->getFeatures().allowRenderpassWithoutAttachment.enabled &&
!HasValidRenderTarget(device, descriptor))
{
ANGLE_MTL_HANDLE_ERROR(
context, "Render pipeline requires at least one render target for this device.",
GL_INVALID_OPERATION);
return angle::Result::Stop;
}
// Ensure the device can support the storage requirement for render targets.
if (DeviceHasMaximumRenderTargetSize(device))
{
NSUInteger maxSize = GetMaxRenderTargetSizeForDeviceInBytes(device);
NSUInteger renderTargetSize =
ComputeTotalSizeUsedForMTLRenderPipelineDescriptor(descriptor, context, device);
if (renderTargetSize > maxSize)
{
std::stringstream errorStream;
errorStream << "This set of render targets requires " << renderTargetSize
<< " bytes of pixel storage. This device supports " << maxSize << " bytes.";
ANGLE_MTL_HANDLE_ERROR(context, errorStream.str().c_str(), GL_INVALID_OPERATION);
return angle::Result::Stop;
}
}
return angle::Result::Continue;
}
angle::Result CreateRenderPipelineState(ContextMtl *context,
const RenderPipelineKey &key,
AutoObjCPtr<id<MTLRenderPipelineState>> *outRenderPipeline)
{
ANGLE_MTL_OBJC_SCOPE
{
if (!key.vertexShader)
{
// Render pipeline without vertex shader is invalid.
ANGLE_MTL_HANDLE_ERROR(context, "Render pipeline without vertex shader is invalid.",
GL_INVALID_OPERATION);
return angle::Result::Stop;
}
const mtl::ContextDevice &metalDevice = context->getMetalDevice();
auto objCDesc = key.pipelineDesc.createMetalDesc(key.vertexShader, key.fragmentShader);
ANGLE_TRY(ValidateRenderPipelineState(context, objCDesc));
// Special attribute slot for default attribute
if (HasDefaultAttribs(key.pipelineDesc))
{
auto defaultAttribLayoutObjCDesc = adoptObjCObj<MTLVertexBufferLayoutDescriptor>(
[[MTLVertexBufferLayoutDescriptor alloc] init]);
defaultAttribLayoutObjCDesc.get().stepFunction = MTLVertexStepFunctionConstant;
defaultAttribLayoutObjCDesc.get().stepRate = 0;
defaultAttribLayoutObjCDesc.get().stride = kDefaultAttributeSize * kMaxVertexAttribs;
[objCDesc.get().vertexDescriptor.layouts setObject:defaultAttribLayoutObjCDesc
atIndexedSubscript:kDefaultAttribsBindingIndex];
}
// Create pipeline state
NSError *err = nil;
auto newState = metalDevice.newRenderPipelineStateWithDescriptor(objCDesc, &err);
if (err)
{
ANGLE_MTL_HANDLE_ERROR(context, mtl::FormatMetalErrorMessage(err).c_str(),
GL_INVALID_OPERATION);
return angle::Result::Stop;
}
*outRenderPipeline = newState;
return angle::Result::Continue;
}
}
} // namespace
bool RenderPipelineKey::operator==(const RenderPipelineKey &rhs) const
{
return std::tie(vertexShader, fragmentShader, pipelineDesc) ==
std::tie(rhs.vertexShader, rhs.fragmentShader, rhs.pipelineDesc);
}
size_t RenderPipelineKey::hash() const
{
return angle::HashMultiple(vertexShader.get(), fragmentShader.get(), pipelineDesc);
}
PipelineCache::PipelineCache() : mRenderPiplineCache(kMaxPipelines) {}
angle::Result PipelineCache::getRenderPipeline(
ContextMtl *context,
id<MTLFunction> vertexShader,
id<MTLFunction> fragmentShader,
const RenderPipelineDesc &desc,
AutoObjCPtr<id<MTLRenderPipelineState>> *outRenderPipeline)
{
RenderPipelineKey key;
key.vertexShader.retainAssign(vertexShader);
key.fragmentShader.retainAssign(fragmentShader);
key.pipelineDesc = desc;
auto iter = mRenderPiplineCache.Get(key);
if (iter != mRenderPiplineCache.end())
{
*outRenderPipeline = iter->second;
return angle::Result::Continue;
}
angle::TrimCache(kMaxPipelines, kGCLimit, "render pipeline", &mRenderPiplineCache);
AutoObjCPtr<id<MTLRenderPipelineState>> newPipeline;
ANGLE_TRY(CreateRenderPipelineState(context, key, &newPipeline));
iter = mRenderPiplineCache.Put(std::move(key), std::move(newPipeline));
*outRenderPipeline = iter->second;
return angle::Result::Continue;
}
} // namespace mtl
} // namespace rx