Metal: Cache compute pipelines for provoking vertex emulation

Remove ProvokingVertexComputePipelineCache and update
ProvokingVertexHelper to use the PipelineCache.

Bug: chromium:1329376
Change-Id: Ifca89fbb572d850c806b24f124fb86c65eec4f11
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4637204
Reviewed-by: Quyen Le <lehoangquyen@chromium.org>
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
Commit-Queue: Geoff Lang <geofflang@chromium.org>
This commit is contained in:
Geoff Lang
2023-06-22 15:07:11 -04:00
committed by Angle LUCI CQ
parent 927410a8c1
commit 3a0da09d8b
5 changed files with 99 additions and 296 deletions

View File

@@ -681,12 +681,13 @@ angle::Result ContextMtl::drawArraysProvokingVertexImpl(const gl::Context *conte
gl::DrawElementsType convertedType = gl::DrawElementsType::UnsignedInt;
gl::PrimitiveMode outIndexMode = gl::PrimitiveMode::InvalidEnum;
mtl::BufferRef drawIdxBuffer = mProvokingVertexHelper.generateIndexBuffer(
mtl::BufferRef drawIdxBuffer;
ANGLE_TRY(mProvokingVertexHelper.generateIndexBuffer(
mtl::GetImpl(context), first, count, mode, convertedType, outIndexCount, outIndexOffset,
outIndexMode);
outIndexMode, drawIdxBuffer));
GLsizei outIndexCounti32 = static_cast<GLsizei>(outIndexCount);
const uint8_t *mappedIndices = drawIdxBuffer->mapReadOnly(this);
if (!drawIdxBuffer || !mappedIndices)
if (!mappedIndices)
{
return angle::Result::Stop;
}
@@ -804,14 +805,10 @@ angle::Result ContextMtl::drawElementsImpl(const gl::Context *context,
{
size_t outIndexCount = 0;
gl::PrimitiveMode newMode = gl::PrimitiveMode::InvalidEnum;
drawIdxBuffer = mProvokingVertexHelper.preconditionIndexBuffer(
ANGLE_TRY(mProvokingVertexHelper.preconditionIndexBuffer(
mtl::GetImpl(context), idxBuffer, count, convertedOffset,
mState.isPrimitiveRestartEnabled(), mode, convertedType, outIndexCount,
provokingVertexAdditionalOffset, newMode);
if (!drawIdxBuffer)
{
return angle::Result::Stop;
}
provokingVertexAdditionalOffset, newMode, drawIdxBuffer));
// Line strips and triangle strips are rewritten to flat line arrays and tri arrays.
convertedCounti32 = (uint32_t)outIndexCount;
mode = newMode;

View File

@@ -22,29 +22,31 @@ namespace rx
{
class ContextMtl;
class ProvokingVertexHelper : public mtl::ProvokingVertexCacheSpecializeShaderFactory
class ProvokingVertexHelper : angle::NonCopyable
{
public:
ProvokingVertexHelper(ContextMtl *context);
mtl::BufferRef preconditionIndexBuffer(ContextMtl *context,
mtl::BufferRef indexBuffer,
size_t indexCount,
size_t indexOffset,
bool primitiveRestartEnabled,
gl::PrimitiveMode primitiveMode,
gl::DrawElementsType elementsType,
size_t &outIndexCount,
size_t &outIndexOffset,
gl::PrimitiveMode &outPrimitiveMode);
angle::Result preconditionIndexBuffer(ContextMtl *context,
mtl::BufferRef indexBuffer,
size_t indexCount,
size_t indexOffset,
bool primitiveRestartEnabled,
gl::PrimitiveMode primitiveMode,
gl::DrawElementsType elementsType,
size_t &outIndexCount,
size_t &outIndexOffset,
gl::PrimitiveMode &outPrimitiveMode,
mtl::BufferRef &outNewBuffer);
mtl::BufferRef generateIndexBuffer(ContextMtl *context,
size_t first,
size_t indexCount,
gl::PrimitiveMode primitiveMode,
gl::DrawElementsType elementsType,
size_t &outIndexCount,
size_t &outIndexOffset,
gl::PrimitiveMode &outPrimitiveMode);
angle::Result generateIndexBuffer(ContextMtl *context,
size_t first,
size_t indexCount,
gl::PrimitiveMode primitiveMode,
gl::DrawElementsType elementsType,
size_t &outIndexCount,
size_t &outIndexOffset,
gl::PrimitiveMode &outPrimitiveMode,
mtl::BufferRef &outNewBuffer);
void releaseInFlightBuffers(ContextMtl *contextMtl);
void ensureCommandBufferReady();
@@ -52,24 +54,19 @@ class ProvokingVertexHelper : public mtl::ProvokingVertexCacheSpecializeShaderFa
mtl::ComputeCommandEncoder *getComputeCommandEncoder();
private:
angle::Result getComputePipleineState(
ContextMtl *context,
const mtl::ProvokingVertexComputePipelineDesc &desc,
mtl::AutoObjCPtr<id<MTLComputePipelineState>> *outComputePipeline);
angle::Result prepareCommandEncoderForDescriptor(ContextMtl *context,
mtl::ComputeCommandEncoder *encoder,
mtl::ProvokingVertexComputePipelineDesc desc);
mtl::BufferPool mIndexBuffers;
mtl::ProvokingVertexComputePipelineCache mPipelineCache;
mtl::ProvokingVertexComputePipelineDesc mCachedDesc;
// Program cache
virtual angle::Result getSpecializedShader(
rx::mtl::Context *context,
gl::ShaderType shaderType,
const mtl::ProvokingVertexComputePipelineDesc &renderPipelineDesc,
id<MTLFunction> *shaderOut) override;
// Private command buffer
virtual bool hasSpecializedShader(
gl::ShaderType shaderType,
const mtl::ProvokingVertexComputePipelineDesc &renderPipelineDesc) override;
void prepareCommandEncoderForDescriptor(ContextMtl *context,
mtl::ComputeCommandEncoder *encoder,
mtl::ProvokingVertexComputePipelineDesc desc);
std::unordered_map<mtl::ProvokingVertexComputePipelineDesc, mtl::AutoObjCPtr<id<MTLFunction>>>
mComputeFunctions;
};
} // namespace rx
#endif /* LIBANGLE_RENDERER_METAL_PROVOKINGVERTEXHELPER_H */

View File

@@ -99,8 +99,7 @@ static inline gl::PrimitiveMode getNewPrimitiveMode(const uint fixIndexBufferKey
return gl::PrimitiveMode::InvalidEnum;
}
}
ProvokingVertexHelper::ProvokingVertexHelper(ContextMtl *context)
: mIndexBuffers(false), mPipelineCache(this)
ProvokingVertexHelper::ProvokingVertexHelper(ContextMtl *context) : mIndexBuffers(false)
{
mIndexBuffers.initialize(context, kInitialIndexBufferSize, mtl::kIndexBufferOffsetAlignment, 0);
}
@@ -108,7 +107,6 @@ ProvokingVertexHelper::ProvokingVertexHelper(ContextMtl *context)
void ProvokingVertexHelper::onDestroy(ContextMtl *context)
{
mIndexBuffers.destroy(context);
mPipelineCache.clear();
}
void ProvokingVertexHelper::releaseInFlightBuffers(ContextMtl *contextMtl)
@@ -143,55 +141,64 @@ static uint buildIndexBufferKey(const mtl::ProvokingVertexComputePipelineDesc &p
return indexBufferKey;
}
bool ProvokingVertexHelper::hasSpecializedShader(
gl::ShaderType shaderType,
const mtl::ProvokingVertexComputePipelineDesc &renderPipelineDesc)
angle::Result ProvokingVertexHelper::getComputePipleineState(
ContextMtl *context,
const mtl::ProvokingVertexComputePipelineDesc &desc,
mtl::AutoObjCPtr<id<MTLComputePipelineState>> *outComputePipeline)
{
return true;
}
auto iter = mComputeFunctions.find(desc);
if (iter != mComputeFunctions.end())
{
return context->getPipelineCache().getComputePipeline(context, iter->second,
outComputePipeline);
}
angle::Result ProvokingVertexHelper::getSpecializedShader(
rx::mtl::Context *context,
gl::ShaderType shaderType,
const mtl::ProvokingVertexComputePipelineDesc &pipelineDesc,
id<MTLFunction> *shaderOut)
{
id<MTLLibrary> provokingVertexLibrary = context->getDisplay()->getDefaultShadersLib();
uint indexBufferKey = buildIndexBufferKey(pipelineDesc);
uint indexBufferKey = buildIndexBufferKey(desc);
auto fcValues = mtl::adoptObjCObj([[MTLFunctionConstantValues alloc] init]);
[fcValues setConstantValue:&indexBufferKey type:MTLDataTypeUInt withName:@"fixIndexBufferKey"];
if (pipelineDesc.generateIndices)
mtl::AutoObjCPtr<id<MTLFunction>> computeShader;
if (desc.generateIndices)
{
return CreateMslShader(context, provokingVertexLibrary, @"genIndexBuffer", fcValues.get(),
shaderOut);
ANGLE_TRY(CreateMslShader(context, provokingVertexLibrary, @"genIndexBuffer",
fcValues.get(), &computeShader));
}
else
{
return CreateMslShader(context, provokingVertexLibrary, @"fixIndexBuffer", fcValues.get(),
shaderOut);
ANGLE_TRY(CreateMslShader(context, provokingVertexLibrary, @"fixIndexBuffer",
fcValues.get(), &computeShader));
}
mComputeFunctions[desc] = computeShader;
return context->getPipelineCache().getComputePipeline(context, computeShader,
outComputePipeline);
}
void ProvokingVertexHelper::prepareCommandEncoderForDescriptor(
angle::Result ProvokingVertexHelper::prepareCommandEncoderForDescriptor(
ContextMtl *context,
mtl::ComputeCommandEncoder *encoder,
mtl::ProvokingVertexComputePipelineDesc desc)
{
auto pipelineState = mPipelineCache.getComputePipelineState(context, desc);
mtl::AutoObjCPtr<id<MTLComputePipelineState>> pipelineState;
ANGLE_TRY(getComputePipleineState(context, desc, &pipelineState));
encoder->setComputePipelineState(pipelineState);
mCachedDesc = desc;
return angle::Result::Continue;
}
mtl::BufferRef ProvokingVertexHelper::preconditionIndexBuffer(ContextMtl *context,
mtl::BufferRef indexBuffer,
size_t indexCount,
size_t indexOffset,
bool primitiveRestartEnabled,
gl::PrimitiveMode primitiveMode,
gl::DrawElementsType elementsType,
size_t &outIndexCount,
size_t &outIndexOffset,
gl::PrimitiveMode &outPrimitiveMode)
angle::Result ProvokingVertexHelper::preconditionIndexBuffer(ContextMtl *context,
mtl::BufferRef indexBuffer,
size_t indexCount,
size_t indexOffset,
bool primitiveRestartEnabled,
gl::PrimitiveMode primitiveMode,
gl::DrawElementsType elementsType,
size_t &outIndexCount,
size_t &outIndexOffset,
gl::PrimitiveMode &outPrimitiveMode,
mtl::BufferRef &outNewBuffer)
{
// Get specialized program
// Upload index buffer
@@ -207,17 +214,14 @@ mtl::BufferRef ProvokingVertexHelper::preconditionIndexBuffer(ContextMtl *contex
size_t indexSize = gl::GetDrawElementsTypeSize(elementsType);
size_t newOffset = 0;
mtl::BufferRef newBuffer;
if (mIndexBuffers.allocate(context, newIndexCount * indexSize + indexOffset, nullptr,
&newBuffer, &newOffset) == angle::Result::Stop)
{
return nullptr;
}
ANGLE_TRY(mIndexBuffers.allocate(context, newIndexCount * indexSize + indexOffset, nullptr,
&newBuffer, &newOffset));
uint indexCountEncoded = (uint)indexCount;
auto threadsPerThreadgroup = MTLSizeMake(MIN(primCount, 64u), 1, 1);
mtl::ComputeCommandEncoder *encoder =
context->getComputeCommandEncoderWithoutEndingRenderEncoder();
prepareCommandEncoderForDescriptor(context, encoder, pipelineDesc);
ANGLE_TRY(prepareCommandEncoderForDescriptor(context, encoder, pipelineDesc));
encoder->setBuffer(indexBuffer, static_cast<uint32_t>(indexOffset), 0);
encoder->setBufferForWrite(
newBuffer, static_cast<uint32_t>(indexOffset) + static_cast<uint32_t>(newOffset), 1);
@@ -230,17 +234,19 @@ mtl::BufferRef ProvokingVertexHelper::preconditionIndexBuffer(ContextMtl *contex
outIndexCount = newIndexCount;
outIndexOffset = newOffset;
outPrimitiveMode = getNewPrimitiveMode(indexBufferKey);
return newBuffer;
outNewBuffer = newBuffer;
return angle::Result::Continue;
}
mtl::BufferRef ProvokingVertexHelper::generateIndexBuffer(ContextMtl *context,
size_t first,
size_t indexCount,
gl::PrimitiveMode primitiveMode,
gl::DrawElementsType elementsType,
size_t &outIndexCount,
size_t &outIndexOffset,
gl::PrimitiveMode &outPrimitiveMode)
angle::Result ProvokingVertexHelper::generateIndexBuffer(ContextMtl *context,
size_t first,
size_t indexCount,
gl::PrimitiveMode primitiveMode,
gl::DrawElementsType elementsType,
size_t &outIndexCount,
size_t &outIndexOffset,
gl::PrimitiveMode &outPrimitiveMode,
mtl::BufferRef &outNewBuffer)
{
// Get specialized program
// Upload index buffer
@@ -256,11 +262,8 @@ mtl::BufferRef ProvokingVertexHelper::generateIndexBuffer(ContextMtl *context,
size_t indexSize = gl::GetDrawElementsTypeSize(elementsType);
size_t newIndexOffset = 0;
mtl::BufferRef newBuffer;
if (mIndexBuffers.allocate(context, newIndexCount * indexSize, nullptr, &newBuffer,
&newIndexOffset) == angle::Result::Stop)
{
return nullptr;
}
ANGLE_TRY(mIndexBuffers.allocate(context, newIndexCount * indexSize, nullptr, &newBuffer,
&newIndexOffset));
uint indexCountEncoded = static_cast<uint>(indexCount);
uint firstVertexEncoded = static_cast<uint>(first);
uint indexOffsetEncoded = static_cast<uint>(newIndexOffset);
@@ -268,7 +271,7 @@ mtl::BufferRef ProvokingVertexHelper::generateIndexBuffer(ContextMtl *context,
mtl::ComputeCommandEncoder *encoder =
context->getComputeCommandEncoderWithoutEndingRenderEncoder();
prepareCommandEncoderForDescriptor(context, encoder, pipelineDesc);
ANGLE_TRY(prepareCommandEncoderForDescriptor(context, encoder, pipelineDesc));
encoder->setBufferForWrite(newBuffer, indexOffsetEncoded, 1);
encoder->setData(indexCountEncoded, 2);
encoder->setData(primCount, 3);
@@ -280,7 +283,8 @@ mtl::BufferRef ProvokingVertexHelper::generateIndexBuffer(ContextMtl *context,
outIndexCount = newIndexCount;
outIndexOffset = newIndexOffset;
outPrimitiveMode = getNewPrimitiveMode(indexBufferKey);
return newBuffer;
outNewBuffer = newBuffer;
return angle::Result::Continue;
}
} // namespace rx

View File

@@ -298,7 +298,7 @@ struct alignas(4) ProvokingVertexComputePipelineDesc
{
ProvokingVertexComputePipelineDesc();
ProvokingVertexComputePipelineDesc(const ProvokingVertexComputePipelineDesc &src);
ProvokingVertexComputePipelineDesc(const ProvokingVertexComputePipelineDesc &&src);
ProvokingVertexComputePipelineDesc(ProvokingVertexComputePipelineDesc &&src);
ProvokingVertexComputePipelineDesc &operator=(const ProvokingVertexComputePipelineDesc &src);
@@ -451,72 +451,6 @@ namespace rx
namespace mtl
{
// Abstract factory to create specialized provoking vertex compute shaders based off of
// compute shader pipeline descs
class ProvokingVertexCacheSpecializeShaderFactory
{
public:
virtual ~ProvokingVertexCacheSpecializeShaderFactory() = default;
// Get specialized shader for the render pipeline cache.
virtual angle::Result getSpecializedShader(
Context *context,
gl::ShaderType shaderType,
const ProvokingVertexComputePipelineDesc &renderPipelineDesc,
id<MTLFunction> *shaderOut) = 0;
// Check whether specialized shaders is required for the specified RenderPipelineDesc.
// If not, the render pipeline cache will use the supplied non-specialized shaders.
virtual bool hasSpecializedShader(
gl::ShaderType shaderType,
const ProvokingVertexComputePipelineDesc &renderPipelineDesc) = 0;
};
// render pipeline state cache per shader program
class ProvokingVertexComputePipelineCache final : angle::NonCopyable
{
public:
ProvokingVertexComputePipelineCache();
ProvokingVertexComputePipelineCache(
ProvokingVertexCacheSpecializeShaderFactory *specializedShaderFactory);
~ProvokingVertexComputePipelineCache();
// Set non-specialized vertex/fragment shader to be used by render pipeline cache to create
// render pipeline state. If the internal
// RenderPipelineCacheSpecializeShaderFactory.hasSpecializedShader() returns false for a
// particular RenderPipelineDesc, the render pipeline cache will use the non-specialized
// shaders.
void setComputeShader(ContextMtl *context, id<MTLFunction> shader);
id<MTLFunction> getComputeShader() { return mComputeShader; }
AutoObjCPtr<id<MTLComputePipelineState>> getComputePipelineState(
ContextMtl *context,
const ProvokingVertexComputePipelineDesc &desc);
void clear();
protected:
// Non-specialized compute shader
AutoObjCPtr<id<MTLFunction>> mComputeShader;
private:
void clearPipelineStates();
void recreatePipelineStates(ContextMtl *context);
AutoObjCPtr<id<MTLComputePipelineState>> insertComputePipelineState(
ContextMtl *context,
const ProvokingVertexComputePipelineDesc &desc);
AutoObjCPtr<id<MTLComputePipelineState>> createComputePipelineState(
ContextMtl *context,
const ProvokingVertexComputePipelineDesc &desc);
bool hasDefaultAttribs(const RenderPipelineDesc &desc) const;
// One table with default attrib and one table without.
std::unordered_map<ProvokingVertexComputePipelineDesc, AutoObjCPtr<id<MTLComputePipelineState>>>
mComputePipelineStates;
ProvokingVertexCacheSpecializeShaderFactory *mSpecializedShaderFactory;
};
class StateCache final : angle::NonCopyable
{
public:

View File

@@ -873,7 +873,7 @@ ProvokingVertexComputePipelineDesc::ProvokingVertexComputePipelineDesc(
memcpy(this, &src, sizeof(*this));
}
ProvokingVertexComputePipelineDesc::ProvokingVertexComputePipelineDesc(
const ProvokingVertexComputePipelineDesc &&src)
ProvokingVertexComputePipelineDesc &&src)
{
memcpy(this, &src, sizeof(*this));
}
@@ -898,135 +898,6 @@ size_t ProvokingVertexComputePipelineDesc::hash() const
return angle::ComputeGenericHash(*this);
}
ProvokingVertexComputePipelineCache::ProvokingVertexComputePipelineCache() : mComputeShader(nullptr)
{}
ProvokingVertexComputePipelineCache::ProvokingVertexComputePipelineCache(
ProvokingVertexCacheSpecializeShaderFactory *specializedShaderFactory)
: mComputeShader(nullptr), mSpecializedShaderFactory(specializedShaderFactory)
{}
void ProvokingVertexComputePipelineCache::setComputeShader(ContextMtl *context,
id<MTLFunction> shader)
{
mComputeShader.retainAssign(shader);
if (!shader)
{
clearPipelineStates();
return;
}
recreatePipelineStates(context);
}
void ProvokingVertexComputePipelineCache::clearPipelineStates()
{
mComputePipelineStates.clear();
}
void ProvokingVertexComputePipelineCache::clear()
{
clearPipelineStates();
}
AutoObjCPtr<id<MTLComputePipelineState>>
ProvokingVertexComputePipelineCache::getComputePipelineState(
ContextMtl *context,
const ProvokingVertexComputePipelineDesc &desc)
{
auto &table = mComputePipelineStates;
auto ite = table.find(desc);
if (ite == table.end())
{
return insertComputePipelineState(context, desc);
}
return ite->second;
}
AutoObjCPtr<id<MTLComputePipelineState>>
ProvokingVertexComputePipelineCache::insertComputePipelineState(
ContextMtl *context,
const ProvokingVertexComputePipelineDesc &desc)
{
AutoObjCPtr<id<MTLComputePipelineState>> newState = createComputePipelineState(context, desc);
auto re = mComputePipelineStates.insert(std::make_pair(desc, newState));
if (!re.second)
{
return nil;
}
return re.first->second;
}
void ProvokingVertexComputePipelineCache::recreatePipelineStates(ContextMtl *context)
{
for (auto &ite : mComputePipelineStates)
{
if (ite.second == nil)
{
continue;
}
ite.second = createComputePipelineState(context, ite.first);
}
}
AutoObjCPtr<id<MTLComputePipelineState>>
ProvokingVertexComputePipelineCache::createComputePipelineState(
ContextMtl *context,
const ProvokingVertexComputePipelineDesc &originalDesc)
{
ANGLE_MTL_OBJC_SCOPE
{
// Disable coverage if the render pipeline's sample count is only 1.
ProvokingVertexComputePipelineDesc desc = originalDesc;
id<MTLFunction> computeFunction = nil;
// Special case for transform feedback shader, we've already created it! See
// getTransformFeedbackRenderPipeline
if (mSpecializedShaderFactory &&
mSpecializedShaderFactory->hasSpecializedShader(gl::ShaderType::Compute, desc))
{
if (IsError(mSpecializedShaderFactory->getSpecializedShader(
context, gl::ShaderType::Compute, desc, &computeFunction)))
{
return nil;
}
}
else
{
// Non-specialized version
computeFunction = mComputeShader;
}
if (!computeFunction)
{
ANGLE_MTL_HANDLE_ERROR(context, "Render pipeline without vertex shader is invalid.",
GL_INVALID_OPERATION);
return nil;
}
const mtl::ContextDevice &metalDevice = context->getMetalDevice();
// Convert to Objective-C desc:
NSError *err = nil;
auto newState = metalDevice.newComputePipelineStateWithFunction(computeFunction, &err);
if (err)
{
ANGLE_MTL_HANDLE_ERROR(context, mtl::FormatMetalErrorMessage(err).c_str(),
GL_INVALID_OPERATION);
return nil;
}
return newState;
}
}
ProvokingVertexComputePipelineCache::~ProvokingVertexComputePipelineCache() {}
// StateCache implementation
StateCache::StateCache(const angle::FeaturesMtl &features) : mFeatures(features) {}