blob: 2bd5606c712c038f53fdd64b1ddf369ce26d395b [file] [log] [blame]
//
// Copyright 2021 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.
//
// ProvokingVertexHelper.mm:
// Implements the class methods for ProvokingVertexHelper.
//
#include "libANGLE/renderer/metal/ProvokingVertexHelper.h"
#import <Foundation/Foundation.h>
#include "libANGLE/Display.h"
#include "libANGLE/renderer/metal/ContextMtl.h"
#include "libANGLE/renderer/metal/DisplayMtl.h"
#include "libANGLE/renderer/metal/mtl_common.h"
#include "libANGLE/renderer/metal/shaders/rewrite_indices_shared.h"
namespace rx
{
namespace
{
constexpr size_t kInitialIndexBufferSize = 0xFFFF; // Initial 64k pool.
}
static inline uint primCountForIndexCount(const uint fixIndexBufferKey, const uint indexCount)
{
const uint fixIndexBufferMode =
(fixIndexBufferKey >> MtlFixIndexBufferKeyModeShift) & MtlFixIndexBufferKeyModeMask;
switch (fixIndexBufferMode)
{
case MtlFixIndexBufferKeyPoints:
return indexCount;
case MtlFixIndexBufferKeyLines:
return indexCount / 2;
case MtlFixIndexBufferKeyLineStrip:
return (uint)MAX(0, (int)indexCount - 1);
case MtlFixIndexBufferKeyLineLoop:
return (uint)MAX(0, (int)indexCount);
case MtlFixIndexBufferKeyTriangles:
return indexCount / 3;
case MtlFixIndexBufferKeyTriangleStrip:
return (uint)MAX(0, (int)indexCount - 2);
case MtlFixIndexBufferKeyTriangleFan:
return (uint)MAX(0, (int)indexCount - 2);
default:
ASSERT(false);
return 0;
}
}
static inline uint indexCountForPrimCount(const uint fixIndexBufferKey, const uint primCount)
{
const uint fixIndexBufferMode =
(fixIndexBufferKey >> MtlFixIndexBufferKeyModeShift) & MtlFixIndexBufferKeyModeMask;
switch (fixIndexBufferMode)
{
case MtlFixIndexBufferKeyPoints:
return primCount;
case MtlFixIndexBufferKeyLines:
return primCount * 2;
case MtlFixIndexBufferKeyLineStrip:
return primCount * 2;
case MtlFixIndexBufferKeyLineLoop:
return primCount * 2;
case MtlFixIndexBufferKeyTriangles:
return primCount * 3;
case MtlFixIndexBufferKeyTriangleStrip:
return primCount * 3;
case MtlFixIndexBufferKeyTriangleFan:
return primCount * 3;
default:
ASSERT(false);
return 0;
}
}
static inline gl::PrimitiveMode getNewPrimitiveMode(const uint fixIndexBufferKey)
{
const uint fixIndexBufferMode =
(fixIndexBufferKey >> MtlFixIndexBufferKeyModeShift) & MtlFixIndexBufferKeyModeMask;
switch (fixIndexBufferMode)
{
case MtlFixIndexBufferKeyPoints:
return gl::PrimitiveMode::Points;
case MtlFixIndexBufferKeyLines:
return gl::PrimitiveMode::Lines;
case MtlFixIndexBufferKeyLineStrip:
return gl::PrimitiveMode::Lines;
case MtlFixIndexBufferKeyLineLoop:
return gl::PrimitiveMode::Lines;
case MtlFixIndexBufferKeyTriangles:
return gl::PrimitiveMode::Triangles;
case MtlFixIndexBufferKeyTriangleStrip:
return gl::PrimitiveMode::Triangles;
case MtlFixIndexBufferKeyTriangleFan:
return gl::PrimitiveMode::Triangles;
default:
ASSERT(false);
return gl::PrimitiveMode::InvalidEnum;
}
}
ProvokingVertexHelper::ProvokingVertexHelper(ContextMtl *context)
: mIndexBuffers(false), mPipelineCache(this)
{
mIndexBuffers.initialize(context, kInitialIndexBufferSize, mtl::kIndexBufferOffsetAlignment, 0);
}
void ProvokingVertexHelper::onDestroy(ContextMtl *context)
{
mIndexBuffers.destroy(context);
mPipelineCache.clear();
}
void ProvokingVertexHelper::releaseInFlightBuffers(ContextMtl *contextMtl)
{
mIndexBuffers.releaseInFlightBuffers(contextMtl);
}
static uint buildIndexBufferKey(const mtl::ProvokingVertexComputePipelineDesc &pipelineDesc)
{
uint indexBufferKey = 0;
gl::DrawElementsType elementType = (gl::DrawElementsType)pipelineDesc.elementType;
bool doPrimPrestart = pipelineDesc.primitiveRestartEnabled;
gl::PrimitiveMode primMode = pipelineDesc.primitiveMode;
switch (elementType)
{
case gl::DrawElementsType::UnsignedShort:
indexBufferKey |= MtlFixIndexBufferKeyUint16 << MtlFixIndexBufferKeyInShift;
indexBufferKey |= MtlFixIndexBufferKeyUint16 << MtlFixIndexBufferKeyOutShift;
break;
case gl::DrawElementsType::UnsignedInt:
indexBufferKey |= MtlFixIndexBufferKeyUint32 << MtlFixIndexBufferKeyInShift;
indexBufferKey |= MtlFixIndexBufferKeyUint32 << MtlFixIndexBufferKeyOutShift;
break;
default:
ASSERT(false); // Index type should only be short or int.
break;
}
indexBufferKey |= (uint)primMode << MtlFixIndexBufferKeyModeShift;
indexBufferKey |= doPrimPrestart ? MtlFixIndexBufferKeyPrimRestart : 0;
// We only rewrite indices if we're switching the provoking vertex mode.
indexBufferKey |= MtlFixIndexBufferKeyProvokingVertexLast;
return indexBufferKey;
}
bool ProvokingVertexHelper::hasSpecializedShader(
gl::ShaderType shaderType,
const mtl::ProvokingVertexComputePipelineDesc &renderPipelineDesc)
{
return true;
}
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);
auto fcValues = mtl::adoptObjCObj([[MTLFunctionConstantValues alloc] init]);
[fcValues setConstantValue:&indexBufferKey type:MTLDataTypeUInt withName:@"fixIndexBufferKey"];
if (pipelineDesc.generateIndices)
{
return CreateMslShader(context, provokingVertexLibrary, @"genIndexBuffer", fcValues.get(),
shaderOut);
}
else
{
return CreateMslShader(context, provokingVertexLibrary, @"fixIndexBuffer", fcValues.get(),
shaderOut);
}
}
void ProvokingVertexHelper::prepareCommandEncoderForDescriptor(
ContextMtl *context,
mtl::ComputeCommandEncoder *encoder,
mtl::ProvokingVertexComputePipelineDesc desc)
{
auto pipelineState = mPipelineCache.getComputePipelineState(context, desc);
encoder->setComputePipelineState(pipelineState);
mCachedDesc = desc;
}
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)
{
// Get specialized program
// Upload index buffer
// dispatch per-primitive?
mtl::ProvokingVertexComputePipelineDesc pipelineDesc;
pipelineDesc.elementType = (uint8_t)elementsType;
pipelineDesc.primitiveMode = primitiveMode;
pipelineDesc.primitiveRestartEnabled = primitiveRestartEnabled;
pipelineDesc.generateIndices = false;
uint indexBufferKey = buildIndexBufferKey(pipelineDesc);
uint primCount = primCountForIndexCount(indexBufferKey, (uint32_t)indexCount);
uint newIndexCount = indexCountForPrimCount(indexBufferKey, primCount);
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;
}
uint indexCountEncoded = (uint)indexCount;
auto threadsPerThreadgroup = MTLSizeMake(MIN(primCount, 64u), 1, 1);
mtl::ComputeCommandEncoder *encoder =
context->getComputeCommandEncoderWithoutEndingRenderEncoder();
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);
encoder->setData(&indexCountEncoded, 2);
encoder->setData(&primCount, 3);
encoder->dispatch(
MTLSizeMake((primCount + threadsPerThreadgroup.width - 1) / threadsPerThreadgroup.width, 1,
1),
threadsPerThreadgroup);
outIndexCount = newIndexCount;
outIndexOffset = newOffset;
outPrimitiveMode = getNewPrimitiveMode(indexBufferKey);
return newBuffer;
}
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)
{
// Get specialized program
// Upload index buffer
// dispatch per-primitive?
mtl::ProvokingVertexComputePipelineDesc pipelineDesc;
pipelineDesc.elementType = (uint8_t)elementsType;
pipelineDesc.primitiveMode = primitiveMode;
pipelineDesc.primitiveRestartEnabled = false;
pipelineDesc.generateIndices = true;
uint indexBufferKey = buildIndexBufferKey(pipelineDesc);
uint primCount = primCountForIndexCount(indexBufferKey, (uint32_t)indexCount);
uint newIndexCount = indexCountForPrimCount(indexBufferKey, primCount);
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;
}
uint indexCountEncoded = static_cast<uint>(indexCount);
uint firstVertexEncoded = static_cast<uint>(first);
uint indexOffsetEncoded = static_cast<uint>(newIndexOffset);
auto threadsPerThreadgroup = MTLSizeMake(MIN(primCount, 64u), 1, 1);
mtl::ComputeCommandEncoder *encoder =
context->getComputeCommandEncoderWithoutEndingRenderEncoder();
prepareCommandEncoderForDescriptor(context, encoder, pipelineDesc);
encoder->setBufferForWrite(newBuffer, indexOffsetEncoded, 1);
encoder->setData(indexCountEncoded, 2);
encoder->setData(primCount, 3);
encoder->setData(firstVertexEncoded, 4);
encoder->dispatch(
MTLSizeMake((primCount + threadsPerThreadgroup.width - 1) / threadsPerThreadgroup.width, 1,
1),
threadsPerThreadgroup);
outIndexCount = newIndexCount;
outIndexOffset = newIndexOffset;
outPrimitiveMode = getNewPrimitiveMode(indexBufferKey);
return newBuffer;
}
} // namespace rx