Metal: Move setupDraw and associated code to executable

Last bits of state left in ProgramMtl that are now moved to
ProgramExecutableMtl, and now ContextMtl does not reference the program
at all.

This fix was necessary for a follow up change that allows the program to
be modified while the executable is installed, and the metal backend was
crashing after a failed relink due its direct access to the program.

Bug: angleproject:8297
Change-Id: Iadf623bf6baf612767ff372adee2e7f4eeedb593
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4833624
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
This commit is contained in:
Shahbaz Youssefi
2023-09-01 23:11:59 -04:00
committed by Angle LUCI CQ
parent 0ff71d5ecd
commit d664543f3e
8 changed files with 987 additions and 995 deletions

View File

@@ -595,7 +595,6 @@ class ContextMtl : public ContextImpl, public mtl::Context
// Cached back-end objects
FramebufferMtl *mDrawFramebuffer = nullptr;
VertexArrayMtl *mVertexArray = nullptr;
ProgramMtl *mProgram = nullptr;
ProgramExecutableMtl *mExecutable = nullptr;
QueryMtl *mOcclusionQuery = nullptr;
mtl::TextureRef mWorkTexture;

View File

@@ -1294,8 +1294,7 @@ angle::Result ContextMtl::syncState(const gl::Context *context,
case gl::state::DIRTY_BIT_DISPATCH_INDIRECT_BUFFER_BINDING:
break;
case gl::state::DIRTY_BIT_PROGRAM_BINDING:
mProgram = mtl::GetImpl(glState.getProgram());
mExecutable = mProgram->getExecutable();
mExecutable = mtl::GetImpl(mState.getProgramExecutable());
break;
case gl::state::DIRTY_BIT_PROGRAM_EXECUTABLE:
updateProgramExecutable(context);
@@ -2628,8 +2627,9 @@ angle::Result ContextMtl::setupDrawImpl(const gl::Context *context,
}
else
{
ANGLE_TRY(mProgram->setupDraw(context, &mRenderEncoder, mRenderPipelineDesc,
isPipelineDescChanged, textureChanged, uniformBuffersDirty));
ANGLE_TRY(mExecutable->setupDraw(context, &mRenderEncoder, mRenderPipelineDesc,
isPipelineDescChanged, textureChanged,
uniformBuffersDirty));
}
return angle::Result::Continue;

View File

@@ -12,6 +12,7 @@
#include "libANGLE/ProgramExecutable.h"
#include "libANGLE/renderer/ProgramExecutableImpl.h"
#include "libANGLE/renderer/metal/mtl_buffer_pool.h"
#include "libANGLE/renderer/metal/mtl_command_buffer.h"
#include "libANGLE/renderer/metal/mtl_common.h"
#include "libANGLE/renderer/metal/mtl_msl_utils.h"
#include "libANGLE/renderer/metal/mtl_resources.h"
@@ -129,6 +130,15 @@ class ProgramExecutableMtl : public ProgramExecutableImpl
bool hasFlatAttribute() const { return mProgramHasFlatAttributes; }
// Calls this before drawing, changedPipelineDesc is passed when vertex attributes desc and/or
// shader program changed.
angle::Result setupDraw(const gl::Context *glContext,
mtl::RenderCommandEncoder *cmdEncoder,
const mtl::RenderPipelineDesc &pipelineDesc,
bool pipelineDescChanged,
bool forceTexturesSetting,
bool uniformBuffersDirty);
private:
friend class ProgramMtl;
@@ -156,6 +166,37 @@ class ProgramExecutableMtl : public ProgramExecutableImpl
const gl::ShaderMap<size_t> &requiredBufferSize);
void initUniformBlocksRemapper(const gl::SharedCompiledShaderState &shader);
mtl::BufferPool *getBufferPool(ContextMtl *context);
angle::Result getSpecializedShader(ContextMtl *context,
gl::ShaderType shaderType,
const mtl::RenderPipelineDesc &renderPipelineDesc,
id<MTLFunction> *shaderOut);
angle::Result commitUniforms(ContextMtl *context, mtl::RenderCommandEncoder *cmdEncoder);
angle::Result updateTextures(const gl::Context *glContext,
mtl::RenderCommandEncoder *cmdEncoder,
bool forceUpdate);
angle::Result updateUniformBuffers(ContextMtl *context,
mtl::RenderCommandEncoder *cmdEncoder,
const mtl::RenderPipelineDesc &pipelineDesc);
angle::Result updateXfbBuffers(ContextMtl *context,
mtl::RenderCommandEncoder *cmdEncoder,
const mtl::RenderPipelineDesc &pipelineDesc);
angle::Result legalizeUniformBufferOffsets(ContextMtl *context,
const std::vector<gl::InterfaceBlock> &blocks);
angle::Result bindUniformBuffersToDiscreteSlots(ContextMtl *context,
mtl::RenderCommandEncoder *cmdEncoder,
const std::vector<gl::InterfaceBlock> &blocks,
gl::ShaderType shaderType);
angle::Result encodeUniformBuffersInfoArgumentBuffer(
ContextMtl *context,
mtl::RenderCommandEncoder *cmdEncoder,
const std::vector<gl::InterfaceBlock> &blocks,
gl::ShaderType shaderType);
bool mProgramHasFlatAttributes;
gl::ShaderMap<DefaultUniformBlockMtl> mDefaultUniformBlocks;
@@ -182,8 +223,24 @@ class ProgramExecutableMtl : public ProgramExecutableImpl
gl::ShaderBitSet mDefaultUniformBlocksDirty;
gl::ShaderBitSet mSamplerBindingsDirty;
// Scratch data:
// Legalized buffers and their offsets. For example, uniform buffer's offset=1 is not a valid
// offset, it will be converted to legal offset and the result is stored in this array.
std::vector<std::pair<mtl::BufferRef, uint32_t>> mLegalizedOffsetedUniformBuffers;
// Stores the render stages usage of each uniform buffer. Only used if the buffers are encoded
// into an argument buffer.
std::vector<uint32_t> mArgumentBufferRenderStageUsages;
uint32_t mShadowCompareModes[mtl::kMaxShaderSamplers];
mtl::BufferPool *mAuxBufferPool;
};
angle::Result CreateMslShaderLib(ContextMtl *context,
gl::InfoLog &infoLog,
mtl::TranslatedShaderInfo *translatedMslInfo,
const std::map<std::string, std::string> &substitutionMacros);
} // namespace rx
#endif // LIBANGLE_RENDERER_MTL_PROGRAMEXECUTABLEMTL_H_

View File

@@ -7,13 +7,17 @@
#include "libANGLE/renderer/metal/ProgramExecutableMtl.h"
#include "libANGLE/renderer/metal/BufferMtl.h"
#include "libANGLE/renderer/metal/ContextMtl.h"
#include "libANGLE/renderer/metal/TextureMtl.h"
#include "libANGLE/renderer/metal/blocklayoutMetal.h"
namespace rx
{
namespace
{
#define SHADER_ENTRY_NAME @"main0"
bool CompareBlockInfo(const sh::BlockMemberInfo &a, const sh::BlockMemberInfo &b)
{
return a.offset < b.offset;
@@ -56,6 +60,205 @@ void InitDefaultUniformBlock(const std::vector<sh::Uniform> &uniforms,
return;
}
template <typename T>
class [[nodiscard]] ScopedAutoClearVector
{
public:
ScopedAutoClearVector(std::vector<T> *array) : mArray(*array) {}
~ScopedAutoClearVector() { mArray.clear(); }
private:
std::vector<T> &mArray;
};
inline void memcpy_guarded(void *dst, const void *src, const void *maxSrcPtr, size_t size)
{
size_t bytesAvailable = maxSrcPtr > src ? (const uint8_t *)maxSrcPtr - (const uint8_t *)src : 0;
size_t bytesToCopy = std::min(size, bytesAvailable);
size_t bytesToZero = size - bytesToCopy;
if (bytesToCopy)
memcpy(dst, src, bytesToCopy);
if (bytesToZero)
memset((uint8_t *)dst + bytesToCopy, 0, bytesToZero);
}
// Copy matrix one column at a time
inline void copy_matrix(void *dst,
const void *src,
const void *maxSrcPtr,
size_t srcStride,
size_t dstStride,
GLenum type)
{
size_t elemSize = mtl::GetMetalSizeForGLType(gl::VariableComponentType(type));
const size_t dstRows = gl::VariableRowCount(type);
const size_t dstCols = gl::VariableColumnCount(type);
for (size_t col = 0; col < dstCols; col++)
{
size_t srcOffset = col * srcStride;
memcpy_guarded(((uint8_t *)dst) + dstStride * col, (const uint8_t *)src + srcOffset,
maxSrcPtr, elemSize * dstRows);
}
}
// Copy matrix one element at a time to transpose.
inline void copy_matrix_row_major(void *dst,
const void *src,
const void *maxSrcPtr,
size_t srcStride,
size_t dstStride,
GLenum type)
{
size_t elemSize = mtl::GetMetalSizeForGLType(gl::VariableComponentType(type));
const size_t dstRows = gl::VariableRowCount(type);
const size_t dstCols = gl::VariableColumnCount(type);
for (size_t col = 0; col < dstCols; col++)
{
for (size_t row = 0; row < dstRows; row++)
{
size_t srcOffset = row * srcStride + col * elemSize;
memcpy_guarded((uint8_t *)dst + dstStride * col + row * elemSize,
(const uint8_t *)src + srcOffset, maxSrcPtr, elemSize);
}
}
}
// TODO(angleproject:7979) Upgrade ANGLE Uniform buffer remapper to compute shaders
angle::Result ConvertUniformBufferData(ContextMtl *contextMtl,
const UBOConversionInfo &blockConversionInfo,
mtl::BufferPool *dynamicBuffer,
const uint8_t *sourceData,
size_t sizeToCopy,
mtl::BufferRef *bufferOut,
size_t *bufferOffsetOut)
{
uint8_t *dst = nullptr;
const uint8_t *maxSrcPtr = sourceData + sizeToCopy;
dynamicBuffer->releaseInFlightBuffers(contextMtl);
// When converting a UBO buffer, we convert all of the data
// supplied in a buffer at once (sizeToCopy = bufferMtl->size() - initial offset).
// It's possible that a buffer could represent multiple instances of
// a uniform block, so we loop over the number of block conversions we intend
// to do.
size_t numBlocksToCopy =
(sizeToCopy + blockConversionInfo.stdSize() - 1) / blockConversionInfo.stdSize();
size_t bytesToAllocate = numBlocksToCopy * blockConversionInfo.metalSize();
ANGLE_TRY(dynamicBuffer->allocate(contextMtl, bytesToAllocate, &dst, bufferOut, bufferOffsetOut,
nullptr));
const std::vector<sh::BlockMemberInfo> &stdConversions = blockConversionInfo.stdInfo();
const std::vector<sh::BlockMemberInfo> &mtlConversions = blockConversionInfo.metalInfo();
for (size_t i = 0; i < numBlocksToCopy; ++i)
{
auto stdIterator = stdConversions.begin();
auto mtlIterator = mtlConversions.begin();
while (stdIterator != stdConversions.end())
{
for (int arraySize = 0; arraySize < stdIterator->arraySize; ++arraySize)
{
// For every entry in an array, calculate the offset based off of the
// array element size.
// Offset of a single entry is
// blockIndex*blockSize + arrayOffset*arraySize + offset of field in base struct.
// Fields are copied per block, per member, per array entry of member.
size_t stdArrayOffset = stdIterator->arrayStride * arraySize;
size_t mtlArrayOffset = mtlIterator->arrayStride * arraySize;
if (gl::IsMatrixType(mtlIterator->type))
{
void *dstMat = dst + mtlIterator->offset + mtlArrayOffset +
blockConversionInfo.metalSize() * i;
const void *srcMat = sourceData + stdIterator->offset + stdArrayOffset +
blockConversionInfo.stdSize() * i;
// Transpose matricies into column major order, if they're row major encoded.
if (stdIterator->isRowMajorMatrix)
{
copy_matrix_row_major(dstMat, srcMat, maxSrcPtr, stdIterator->matrixStride,
mtlIterator->matrixStride, mtlIterator->type);
}
else
{
copy_matrix(dstMat, srcMat, maxSrcPtr, stdIterator->matrixStride,
mtlIterator->matrixStride, mtlIterator->type);
}
}
// Compress bool from four bytes to one byte because bool values in GLSL
// are uint-sized: ES 3.0 Section 2.12.6.3 "Uniform Buffer Object Storage".
// Bools in metal are byte-sized. (Metal shading language spec Table 2.2)
else if (gl::VariableComponentType(mtlIterator->type) == GL_BOOL)
{
for (int boolCol = 0; boolCol < gl::VariableComponentCount(mtlIterator->type);
boolCol++)
{
const uint8_t *srcBool =
(sourceData + stdIterator->offset + stdArrayOffset +
blockConversionInfo.stdSize() * i +
gl::VariableComponentSize(GL_BOOL) * boolCol);
unsigned int srcValue =
srcBool < maxSrcPtr ? *((unsigned int *)(srcBool)) : 0;
uint8_t *dstBool = dst + mtlIterator->offset + mtlArrayOffset +
blockConversionInfo.metalSize() * i +
sizeof(bool) * boolCol;
*dstBool = (srcValue != 0);
}
}
else
{
memcpy_guarded(dst + mtlIterator->offset + mtlArrayOffset +
blockConversionInfo.metalSize() * i,
sourceData + stdIterator->offset + stdArrayOffset +
blockConversionInfo.stdSize() * i,
maxSrcPtr, mtl::GetMetalSizeForGLType(mtlIterator->type));
}
}
++stdIterator;
++mtlIterator;
}
}
ANGLE_TRY(dynamicBuffer->commit(contextMtl));
return angle::Result::Continue;
}
constexpr size_t PipelineParametersToFragmentShaderVariantIndex(bool multisampledRendering,
bool allowFragDepthWrite)
{
const size_t index = (allowFragDepthWrite << 1) | multisampledRendering;
ASSERT(index < kFragmentShaderVariants);
return index;
}
void InitArgumentBufferEncoder(mtl::Context *context,
id<MTLFunction> function,
uint32_t bufferIndex,
ProgramArgumentBufferEncoderMtl *encoder)
{
encoder->metalArgBufferEncoder =
mtl::adoptObjCObj([function newArgumentEncoderWithBufferIndex:bufferIndex]);
if (encoder->metalArgBufferEncoder)
{
encoder->bufferPool.initialize(context, encoder->metalArgBufferEncoder.get().encodedLength,
mtl::kArgumentBufferOffsetAlignment, 0);
}
}
bool DisableFastMathForShaderCompilation(mtl::Context *context)
{
return context->getDisplay()->getFeatures().intelDisableFastMath.enabled;
}
bool UsesInvariance(const mtl::TranslatedShaderInfo *translatedMslInfo)
{
return translatedMslInfo->hasInvariant;
}
class Std140BlockLayoutEncoderFactory : public gl::CustomBlockLayoutEncoderFactory
{
public:
@@ -74,19 +277,64 @@ class StdMTLBLockLayoutEncoderFactory : public gl::CustomBlockLayoutEncoderFacto
sh::BlockLayoutEncoder *makeEncoder() override { return new mtl::BlockLayoutEncoderMTL(); }
};
} // anonymous namespace
angle::Result CreateMslShaderLib(ContextMtl *context,
gl::InfoLog &infoLog,
mtl::TranslatedShaderInfo *translatedMslInfo,
const std::map<std::string, std::string> &substitutionMacros)
{
ANGLE_MTL_OBJC_SCOPE
{
mtl::LibraryCache &libraryCache = context->getDisplay()->getLibraryCache();
// Convert to actual binary shader
mtl::AutoObjCPtr<NSError *> err = nil;
bool disableFastMath = DisableFastMathForShaderCompilation(context);
bool usesInvariance = UsesInvariance(translatedMslInfo);
translatedMslInfo->metalLibrary = libraryCache.getOrCompileShaderLibrary(
context, translatedMslInfo->metalShaderSource, substitutionMacros, disableFastMath,
usesInvariance, &err);
if (err && !translatedMslInfo->metalLibrary)
{
std::ostringstream ss;
ss << "Internal error compiling shader with Metal backend.\n";
ss << err.get().localizedDescription.UTF8String << "\n";
ss << "-----\n";
ss << *(translatedMslInfo->metalShaderSource);
ss << "-----\n";
infoLog << ss.str();
ANGLE_MTL_HANDLE_ERROR(context, ss.str().c_str(), GL_INVALID_OPERATION);
return angle::Result::Stop;
}
return angle::Result::Continue;
}
}
DefaultUniformBlockMtl::DefaultUniformBlockMtl() {}
DefaultUniformBlockMtl::~DefaultUniformBlockMtl() = default;
ProgramExecutableMtl::ProgramExecutableMtl(const gl::ProgramExecutable *executable)
: ProgramExecutableImpl(executable), mProgramHasFlatAttributes(false)
: ProgramExecutableImpl(executable),
mProgramHasFlatAttributes(false),
mShadowCompareModes{},
mAuxBufferPool(nullptr)
{}
ProgramExecutableMtl::~ProgramExecutableMtl() {}
void ProgramExecutableMtl::destroy(const gl::Context *context)
{
reset(mtl::GetImpl(context));
auto contextMtl = mtl::GetImpl(context);
if (mAuxBufferPool)
{
mAuxBufferPool->destroy(contextMtl);
delete mAuxBufferPool;
mAuxBufferPool = nullptr;
}
reset(contextMtl);
}
void ProgramExecutableMtl::reset(ContextMtl *context)
@@ -113,6 +361,17 @@ void ProgramExecutableMtl::reset(ContextMtl *context)
{
var.reset(context);
}
if (mAuxBufferPool)
{
if (mAuxBufferPool->reset(context, mtl::kDefaultUniformsMaxSize * 2,
mtl::kUniformBufferSettingOffsetMinAlignment,
3) != angle::Result::Continue)
{
mAuxBufferPool->destroy(context);
SafeDelete(mAuxBufferPool);
}
}
}
angle::Result ProgramExecutableMtl::load(ContextMtl *contextMtl, gl::BinaryInputStream *stream)
@@ -568,4 +827,630 @@ void ProgramExecutableMtl::initUniformBlocksRemapper(const gl::SharedCompiledSha
mUniformBlockConversions.insert(conversionMap.begin(), conversionMap.end());
}
mtl::BufferPool *ProgramExecutableMtl::getBufferPool(ContextMtl *context)
{
if (mAuxBufferPool == nullptr)
{
mAuxBufferPool = new mtl::BufferPool(true);
mAuxBufferPool->initialize(context, mtl::kDefaultUniformsMaxSize * 2,
mtl::kUniformBufferSettingOffsetMinAlignment, 3);
}
return mAuxBufferPool;
}
angle::Result ProgramExecutableMtl::setupDraw(const gl::Context *glContext,
mtl::RenderCommandEncoder *cmdEncoder,
const mtl::RenderPipelineDesc &pipelineDesc,
bool pipelineDescChanged,
bool forceTexturesSetting,
bool uniformBuffersDirty)
{
ContextMtl *context = mtl::GetImpl(glContext);
if (pipelineDescChanged)
{
id<MTLFunction> vertexShader = nil;
ANGLE_TRY(
getSpecializedShader(context, gl::ShaderType::Vertex, pipelineDesc, &vertexShader));
id<MTLFunction> fragmentShader = nil;
ANGLE_TRY(
getSpecializedShader(context, gl::ShaderType::Fragment, pipelineDesc, &fragmentShader));
mtl::AutoObjCPtr<id<MTLRenderPipelineState>> pipelineState;
ANGLE_TRY(context->getPipelineCache().getRenderPipeline(
context, vertexShader, fragmentShader, pipelineDesc, &pipelineState));
cmdEncoder->setRenderPipelineState(pipelineState);
// We need to rebind uniform buffers & textures also
mDefaultUniformBlocksDirty.set();
mSamplerBindingsDirty.set();
// Cache current shader variant references for easier querying.
mCurrentShaderVariants[gl::ShaderType::Vertex] =
&mVertexShaderVariants[pipelineDesc.rasterizationType];
const bool multisampledRendering = pipelineDesc.outputDescriptor.sampleCount > 1;
const bool allowFragDepthWrite =
pipelineDesc.outputDescriptor.depthAttachmentPixelFormat != 0;
mCurrentShaderVariants[gl::ShaderType::Fragment] =
pipelineDesc.rasterizationEnabled()
? &mFragmentShaderVariants[PipelineParametersToFragmentShaderVariantIndex(
multisampledRendering, allowFragDepthWrite)]
: nullptr;
}
ANGLE_TRY(commitUniforms(context, cmdEncoder));
ANGLE_TRY(updateTextures(glContext, cmdEncoder, forceTexturesSetting));
if (uniformBuffersDirty || pipelineDescChanged)
{
ANGLE_TRY(updateUniformBuffers(context, cmdEncoder, pipelineDesc));
}
if (pipelineDescChanged)
{
ANGLE_TRY(updateXfbBuffers(context, cmdEncoder, pipelineDesc));
}
return angle::Result::Continue;
}
angle::Result ProgramExecutableMtl::getSpecializedShader(
ContextMtl *context,
gl::ShaderType shaderType,
const mtl::RenderPipelineDesc &renderPipelineDesc,
id<MTLFunction> *shaderOut)
{
static_assert(YES == 1, "YES should have value of 1");
mtl::TranslatedShaderInfo *translatedMslInfo = &mMslShaderTranslateInfo[shaderType];
ProgramShaderObjVariantMtl *shaderVariant;
mtl::AutoObjCObj<MTLFunctionConstantValues> funcConstants;
if (shaderType == gl::ShaderType::Vertex)
{
// For vertex shader, we need to create 3 variants, one with emulated rasterization
// discard, one with true rasterization discard and one without.
shaderVariant = &mVertexShaderVariants[renderPipelineDesc.rasterizationType];
if (shaderVariant->metalShader)
{
// Already created.
*shaderOut = shaderVariant->metalShader;
return angle::Result::Continue;
}
if (renderPipelineDesc.rasterizationType == mtl::RenderPipelineRasterization::Disabled)
{
// Special case: XFB output only vertex shader.
ASSERT(!mExecutable->getLinkedTransformFeedbackVaryings().empty());
translatedMslInfo = &mMslXfbOnlyVertexShaderInfo;
if (!translatedMslInfo->metalLibrary)
{
// Lazily compile XFB only shader
gl::InfoLog infoLog;
ANGLE_TRY(CreateMslShaderLib(context, infoLog, &mMslXfbOnlyVertexShaderInfo,
{{"TRANSFORM_FEEDBACK_ENABLED", "1"}}));
translatedMslInfo->metalLibrary.get().label = @"TransformFeedback";
}
}
ANGLE_MTL_OBJC_SCOPE
{
BOOL emulateDiscard = renderPipelineDesc.rasterizationType ==
mtl::RenderPipelineRasterization::EmulatedDiscard;
NSString *discardEnabledStr =
[NSString stringWithUTF8String:sh::mtl::kRasterizerDiscardEnabledConstName];
funcConstants = mtl::adoptObjCObj([[MTLFunctionConstantValues alloc] init]);
[funcConstants setConstantValue:&emulateDiscard
type:MTLDataTypeBool
withName:discardEnabledStr];
}
} // if (shaderType == gl::ShaderType::Vertex)
else if (shaderType == gl::ShaderType::Fragment)
{
// For fragment shader, we need to create 4 variants,
// combining multisampled rendering and depth write enabled states.
const bool multisampledRendering = renderPipelineDesc.outputDescriptor.sampleCount > 1;
const bool allowFragDepthWrite =
renderPipelineDesc.outputDescriptor.depthAttachmentPixelFormat != 0;
shaderVariant = &mFragmentShaderVariants[PipelineParametersToFragmentShaderVariantIndex(
multisampledRendering, allowFragDepthWrite)];
if (shaderVariant->metalShader)
{
// Already created.
*shaderOut = shaderVariant->metalShader;
return angle::Result::Continue;
}
ANGLE_MTL_OBJC_SCOPE
{
NSString *multisampledRenderingStr =
[NSString stringWithUTF8String:sh::mtl::kMultisampledRenderingConstName];
NSString *depthWriteEnabledStr =
[NSString stringWithUTF8String:sh::mtl::kDepthWriteEnabledConstName];
funcConstants = mtl::adoptObjCObj([[MTLFunctionConstantValues alloc] init]);
[funcConstants setConstantValue:&multisampledRendering
type:MTLDataTypeBool
withName:multisampledRenderingStr];
[funcConstants setConstantValue:&allowFragDepthWrite
type:MTLDataTypeBool
withName:depthWriteEnabledStr];
}
} // gl::ShaderType::Fragment
else
{
UNREACHABLE();
return angle::Result::Stop;
}
[funcConstants
setConstantValue:&(context->getDisplay()->getFeatures().allowSamplerCompareGradient.enabled)
type:MTLDataTypeBool
withName:@"ANGLEUseSampleCompareGradient"];
[funcConstants
setConstantValue:&(context->getDisplay()->getFeatures().allowSamplerCompareLod.enabled)
type:MTLDataTypeBool
withName:@"ANGLEUseSampleCompareLod"];
[funcConstants
setConstantValue:&(context->getDisplay()->getFeatures().emulateAlphaToCoverage.enabled)
type:MTLDataTypeBool
withName:@"ANGLEEmulateAlphaToCoverage"];
// Create Metal shader object
ANGLE_MTL_OBJC_SCOPE
{
ANGLE_TRY(CreateMslShader(context, translatedMslInfo->metalLibrary, SHADER_ENTRY_NAME,
funcConstants.get(), &shaderVariant->metalShader));
}
// Store reference to the translated source for easily querying mapped bindings later.
shaderVariant->translatedSrcInfo = translatedMslInfo;
// Initialize argument buffer encoder if required
if (translatedMslInfo->hasUBOArgumentBuffer)
{
InitArgumentBufferEncoder(context, shaderVariant->metalShader,
mtl::kUBOArgumentBufferBindingIndex,
&shaderVariant->uboArgBufferEncoder);
}
*shaderOut = shaderVariant->metalShader;
return angle::Result::Continue;
}
angle::Result ProgramExecutableMtl::commitUniforms(ContextMtl *context,
mtl::RenderCommandEncoder *cmdEncoder)
{
for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
{
if (!mDefaultUniformBlocksDirty[shaderType] || !mCurrentShaderVariants[shaderType])
{
continue;
}
DefaultUniformBlockMtl &uniformBlock = mDefaultUniformBlocks[shaderType];
if (!uniformBlock.uniformData.size())
{
continue;
}
if (mAuxBufferPool)
{
mAuxBufferPool->releaseInFlightBuffers(context);
}
// If we exceed the default inline max size, try to allocate a buffer
bool needsCommitUniform = true;
if (needsCommitUniform && uniformBlock.uniformData.size() <= mtl::kInlineConstDataMaxSize)
{
ASSERT(uniformBlock.uniformData.size() <= mtl::kInlineConstDataMaxSize);
cmdEncoder->setBytes(shaderType, uniformBlock.uniformData.data(),
uniformBlock.uniformData.size(),
mtl::kDefaultUniformsBindingIndex);
}
else if (needsCommitUniform)
{
ASSERT(uniformBlock.uniformData.size() <= mtl::kDefaultUniformsMaxSize);
mtl::BufferRef mtlBufferOut;
size_t offsetOut;
uint8_t *ptrOut;
// Allocate a new Uniform buffer
ANGLE_TRY(getBufferPool(context)->allocate(context, uniformBlock.uniformData.size(),
&ptrOut, &mtlBufferOut, &offsetOut));
// Copy the uniform result
memcpy(ptrOut, uniformBlock.uniformData.data(), uniformBlock.uniformData.size());
// Commit
ANGLE_TRY(getBufferPool(context)->commit(context));
// Set buffer
cmdEncoder->setBuffer(shaderType, mtlBufferOut, (uint32_t)offsetOut,
mtl::kDefaultUniformsBindingIndex);
}
mDefaultUniformBlocksDirty.reset(shaderType);
}
return angle::Result::Continue;
}
angle::Result ProgramExecutableMtl::updateTextures(const gl::Context *glContext,
mtl::RenderCommandEncoder *cmdEncoder,
bool forceUpdate)
{
ContextMtl *contextMtl = mtl::GetImpl(glContext);
const auto &glState = glContext->getState();
const gl::ActiveTexturesCache &completeTextures = glState.getActiveTexturesCache();
for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
{
if ((!mSamplerBindingsDirty[shaderType] && !forceUpdate) ||
!mCurrentShaderVariants[shaderType])
{
continue;
}
const mtl::TranslatedShaderInfo &shaderInfo =
mCurrentShaderVariants[shaderType]->translatedSrcInfo
? *mCurrentShaderVariants[shaderType]->translatedSrcInfo
: mMslShaderTranslateInfo[shaderType];
bool hasDepthSampler = false;
for (uint32_t textureIndex = 0; textureIndex < mExecutable->getSamplerBindings().size();
++textureIndex)
{
const gl::SamplerBinding &samplerBinding =
mExecutable->getSamplerBindings()[textureIndex];
const mtl::SamplerBinding &mslBinding = shaderInfo.actualSamplerBindings[textureIndex];
if (mslBinding.textureBinding >= mtl::kMaxShaderSamplers)
{
// No binding assigned
continue;
}
gl::TextureType textureType = samplerBinding.textureType;
for (uint32_t arrayElement = 0; arrayElement < samplerBinding.textureUnitsCount;
++arrayElement)
{
GLuint textureUnit = samplerBinding.getTextureUnit(
mExecutable->getSamplerBoundTextureUnits(), arrayElement);
gl::Texture *texture = completeTextures[textureUnit];
gl::Sampler *sampler = contextMtl->getState().getSampler(textureUnit);
uint32_t textureSlot = mslBinding.textureBinding + arrayElement;
uint32_t samplerSlot = mslBinding.samplerBinding + arrayElement;
if (!texture)
{
ANGLE_TRY(contextMtl->getIncompleteTexture(glContext, textureType,
samplerBinding.format, &texture));
}
const gl::SamplerState *samplerState =
sampler ? &sampler->getSamplerState() : &texture->getSamplerState();
TextureMtl *textureMtl = mtl::GetImpl(texture);
if (samplerBinding.format == gl::SamplerFormat::Shadow)
{
hasDepthSampler = true;
mShadowCompareModes[textureSlot] = mtl::MslGetShaderShadowCompareMode(
samplerState->getCompareMode(), samplerState->getCompareFunc());
}
ANGLE_TRY(textureMtl->bindToShader(glContext, cmdEncoder, shaderType, sampler,
textureSlot, samplerSlot));
} // for array elements
} // for sampler bindings
if (hasDepthSampler)
{
cmdEncoder->setData(shaderType, mShadowCompareModes,
mtl::kShadowSamplerCompareModesBindingIndex);
}
for (const gl::ImageBinding &imageBinding : mExecutable->getImageBindings())
{
if (imageBinding.boundImageUnits.size() != 1)
{
UNIMPLEMENTED();
continue;
}
int glslImageBinding = imageBinding.boundImageUnits[0];
int mtlRWTextureBinding = shaderInfo.actualImageBindings[glslImageBinding];
ASSERT(mtlRWTextureBinding < static_cast<int>(mtl::kMaxShaderImages));
if (mtlRWTextureBinding < 0)
{
continue; // The program does not have an image bound at this unit.
}
const gl::ImageUnit &imageUnit = glState.getImageUnit(glslImageBinding);
TextureMtl *textureMtl = mtl::GetImpl(imageUnit.texture.get());
ANGLE_TRY(textureMtl->bindToShaderImage(
glContext, cmdEncoder, shaderType, static_cast<uint32_t>(mtlRWTextureBinding),
imageUnit.level, imageUnit.layer, imageUnit.format));
}
} // for shader types
return angle::Result::Continue;
}
angle::Result ProgramExecutableMtl::updateUniformBuffers(
ContextMtl *context,
mtl::RenderCommandEncoder *cmdEncoder,
const mtl::RenderPipelineDesc &pipelineDesc)
{
const std::vector<gl::InterfaceBlock> &blocks = mExecutable->getUniformBlocks();
if (blocks.empty())
{
return angle::Result::Continue;
}
// This array is only used inside this function and its callees.
ScopedAutoClearVector<uint32_t> scopeArrayClear(&mArgumentBufferRenderStageUsages);
ScopedAutoClearVector<std::pair<mtl::BufferRef, uint32_t>> scopeArrayClear2(
&mLegalizedOffsetedUniformBuffers);
mArgumentBufferRenderStageUsages.resize(blocks.size());
mLegalizedOffsetedUniformBuffers.resize(blocks.size());
ANGLE_TRY(legalizeUniformBufferOffsets(context, blocks));
const gl::State &glState = context->getState();
for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
{
if (!mCurrentShaderVariants[shaderType])
{
continue;
}
if (mCurrentShaderVariants[shaderType]->translatedSrcInfo->hasUBOArgumentBuffer)
{
ANGLE_TRY(
encodeUniformBuffersInfoArgumentBuffer(context, cmdEncoder, blocks, shaderType));
}
else
{
ANGLE_TRY(bindUniformBuffersToDiscreteSlots(context, cmdEncoder, blocks, shaderType));
}
} // for shader types
// After encode the uniform buffers into an argument buffer, we need to tell Metal that
// the buffers are being used by what shader stages.
for (uint32_t bufferIndex = 0; bufferIndex < blocks.size(); ++bufferIndex)
{
const gl::InterfaceBlock &block = blocks[bufferIndex];
const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding =
glState.getIndexedUniformBuffer(block.binding);
if (bufferBinding.get() == nullptr)
{
continue;
}
// Remove any other stages other than vertex and fragment.
uint32_t stages = mArgumentBufferRenderStageUsages[bufferIndex] &
(mtl::kRenderStageVertex | mtl::kRenderStageFragment);
if (stages == 0)
{
continue;
}
cmdEncoder->useResource(mLegalizedOffsetedUniformBuffers[bufferIndex].first,
MTLResourceUsageRead, static_cast<mtl::RenderStages>(stages));
}
return angle::Result::Continue;
}
angle::Result ProgramExecutableMtl::legalizeUniformBufferOffsets(
ContextMtl *context,
const std::vector<gl::InterfaceBlock> &blocks)
{
const gl::State &glState = context->getState();
for (uint32_t bufferIndex = 0; bufferIndex < blocks.size(); ++bufferIndex)
{
const gl::InterfaceBlock &block = blocks[bufferIndex];
const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding =
glState.getIndexedUniformBuffer(block.binding);
if (bufferBinding.get() == nullptr)
{
continue;
}
BufferMtl *bufferMtl = mtl::GetImpl(bufferBinding.get());
size_t srcOffset = std::min<size_t>(bufferBinding.getOffset(), bufferMtl->size());
ASSERT(mUniformBlockConversions.find(block.name) != mUniformBlockConversions.end());
const UBOConversionInfo &conversionInfo = mUniformBlockConversions.at(block.name);
if (conversionInfo.needsConversion())
{
UniformConversionBufferMtl *conversion =
(UniformConversionBufferMtl *)bufferMtl->getUniformConversionBuffer(
context, std::pair<size_t, size_t>(bufferIndex, srcOffset),
conversionInfo.stdSize());
// Has the content of the buffer has changed since last conversion?
if (conversion->dirty)
{
const uint8_t *srcBytes = bufferMtl->getBufferDataReadOnly(context);
srcBytes += srcOffset;
size_t sizeToCopy = bufferMtl->size() - srcOffset;
ANGLE_TRY(ConvertUniformBufferData(
context, conversionInfo, &conversion->data, srcBytes, sizeToCopy,
&conversion->convertedBuffer, &conversion->convertedOffset));
conversion->dirty = false;
}
// Calculate offset in new block.
size_t dstOffsetSource = srcOffset - conversion->initialSrcOffset();
assert(dstOffsetSource % conversionInfo.stdSize() == 0);
unsigned int numBlocksToOffset =
(unsigned int)(dstOffsetSource / conversionInfo.stdSize());
size_t bytesToOffset = numBlocksToOffset * conversionInfo.metalSize();
mLegalizedOffsetedUniformBuffers[bufferIndex].first = conversion->convertedBuffer;
mLegalizedOffsetedUniformBuffers[bufferIndex].second =
static_cast<uint32_t>(conversion->convertedOffset + bytesToOffset);
}
else
{
mLegalizedOffsetedUniformBuffers[bufferIndex].first = bufferMtl->getCurrentBuffer();
mLegalizedOffsetedUniformBuffers[bufferIndex].second =
static_cast<uint32_t>(bufferBinding.getOffset());
}
}
return angle::Result::Continue;
}
angle::Result ProgramExecutableMtl::bindUniformBuffersToDiscreteSlots(
ContextMtl *context,
mtl::RenderCommandEncoder *cmdEncoder,
const std::vector<gl::InterfaceBlock> &blocks,
gl::ShaderType shaderType)
{
const gl::State &glState = context->getState();
const mtl::TranslatedShaderInfo &shaderInfo =
*mCurrentShaderVariants[shaderType]->translatedSrcInfo;
for (uint32_t bufferIndex = 0; bufferIndex < blocks.size(); ++bufferIndex)
{
const gl::InterfaceBlock &block = blocks[bufferIndex];
const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding =
glState.getIndexedUniformBuffer(block.binding);
if (bufferBinding.get() == nullptr || !block.activeShaders().test(shaderType))
{
continue;
}
uint32_t actualBufferIdx = shaderInfo.actualUBOBindings[bufferIndex];
if (actualBufferIdx >= mtl::kMaxShaderBuffers)
{
continue;
}
mtl::BufferRef mtlBuffer = mLegalizedOffsetedUniformBuffers[bufferIndex].first;
uint32_t offset = mLegalizedOffsetedUniformBuffers[bufferIndex].second;
cmdEncoder->setBuffer(shaderType, mtlBuffer, offset, actualBufferIdx);
}
return angle::Result::Continue;
}
angle::Result ProgramExecutableMtl::encodeUniformBuffersInfoArgumentBuffer(
ContextMtl *context,
mtl::RenderCommandEncoder *cmdEncoder,
const std::vector<gl::InterfaceBlock> &blocks,
gl::ShaderType shaderType)
{
const gl::State &glState = context->getState();
ASSERT(mCurrentShaderVariants[shaderType]->translatedSrcInfo);
const mtl::TranslatedShaderInfo &shaderInfo =
*mCurrentShaderVariants[shaderType]->translatedSrcInfo;
// Encode all uniform buffers into an argument buffer.
ProgramArgumentBufferEncoderMtl &bufferEncoder =
mCurrentShaderVariants[shaderType]->uboArgBufferEncoder;
mtl::BufferRef argumentBuffer;
size_t argumentBufferOffset;
bufferEncoder.bufferPool.releaseInFlightBuffers(context);
ANGLE_TRY(bufferEncoder.bufferPool.allocate(
context, bufferEncoder.metalArgBufferEncoder.get().encodedLength, nullptr, &argumentBuffer,
&argumentBufferOffset));
// MTLArgumentEncoder is modifying the buffer indirectly on CPU. We need to call map()
// so that the buffer's data changes could be flushed to the GPU side later.
ANGLE_UNUSED_VARIABLE(argumentBuffer->mapWithOpt(context, /*readonly=*/false, /*noSync=*/true));
[bufferEncoder.metalArgBufferEncoder setArgumentBuffer:argumentBuffer->get()
offset:argumentBufferOffset];
constexpr gl::ShaderMap<MTLRenderStages> kShaderStageMap = {
{gl::ShaderType::Vertex, mtl::kRenderStageVertex},
{gl::ShaderType::Fragment, mtl::kRenderStageFragment},
};
auto mtlRenderStage = kShaderStageMap[shaderType];
for (uint32_t bufferIndex = 0; bufferIndex < blocks.size(); ++bufferIndex)
{
const gl::InterfaceBlock &block = blocks[bufferIndex];
const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding =
glState.getIndexedUniformBuffer(block.binding);
if (bufferBinding.get() == nullptr || !block.activeShaders().test(shaderType))
{
continue;
}
mArgumentBufferRenderStageUsages[bufferIndex] |= mtlRenderStage;
uint32_t actualBufferIdx = shaderInfo.actualUBOBindings[bufferIndex];
if (actualBufferIdx >= mtl::kMaxShaderBuffers)
{
continue;
}
mtl::BufferRef mtlBuffer = mLegalizedOffsetedUniformBuffers[bufferIndex].first;
uint32_t offset = mLegalizedOffsetedUniformBuffers[bufferIndex].second;
[bufferEncoder.metalArgBufferEncoder setBuffer:mtlBuffer->get()
offset:offset
atIndex:actualBufferIdx];
}
// Flush changes made by MTLArgumentEncoder to GPU.
argumentBuffer->unmapAndFlushSubset(context, argumentBufferOffset,
bufferEncoder.metalArgBufferEncoder.get().encodedLength);
cmdEncoder->setBuffer(shaderType, argumentBuffer, static_cast<uint32_t>(argumentBufferOffset),
mtl::kUBOArgumentBufferBindingIndex);
return angle::Result::Continue;
}
angle::Result ProgramExecutableMtl::updateXfbBuffers(ContextMtl *context,
mtl::RenderCommandEncoder *cmdEncoder,
const mtl::RenderPipelineDesc &pipelineDesc)
{
const gl::State &glState = context->getState();
gl::TransformFeedback *transformFeedback = glState.getCurrentTransformFeedback();
if (pipelineDesc.rasterizationEnabled() || !glState.isTransformFeedbackActiveUnpaused() ||
ANGLE_UNLIKELY(!transformFeedback))
{
// XFB output can only be used with rasterization disabled.
return angle::Result::Continue;
}
size_t xfbBufferCount = glState.getProgramExecutable()->getTransformFeedbackBufferCount();
ASSERT(xfbBufferCount > 0);
ASSERT(mExecutable->getTransformFeedbackBufferMode() != GL_INTERLEAVED_ATTRIBS ||
xfbBufferCount == 1);
for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
{
uint32_t actualBufferIdx = mMslXfbOnlyVertexShaderInfo.actualXFBBindings[bufferIndex];
if (actualBufferIdx >= mtl::kMaxShaderBuffers)
{
continue;
}
const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding =
transformFeedback->getIndexedBuffer(bufferIndex);
gl::Buffer *buffer = bufferBinding.get();
ASSERT((bufferBinding.getOffset() % 4) == 0);
ASSERT(buffer != nullptr);
BufferMtl *bufferMtl = mtl::GetImpl(buffer);
// Use offset=0, actual offset will be set in Driver Uniform inside ContextMtl.
cmdEncoder->setBufferForWrite(gl::ShaderType::Vertex, bufferMtl->getCurrentBuffer(), 0,
actualBufferIdx);
}
return angle::Result::Continue;
}
} // namespace rx

View File

@@ -20,7 +20,6 @@
#include "libANGLE/renderer/ProgramImpl.h"
#include "libANGLE/renderer/metal/ProgramExecutableMtl.h"
#include "libANGLE/renderer/metal/ShaderMtl.h"
#include "libANGLE/renderer/metal/mtl_command_buffer.h"
#include "libANGLE/renderer/metal/mtl_context_device.h"
namespace rx
@@ -101,20 +100,6 @@ class ProgramMtl : public ProgramImpl
void getUniformiv(const gl::Context *context, GLint location, GLint *params) const override;
void getUniformuiv(const gl::Context *context, GLint location, GLuint *params) const override;
angle::Result getSpecializedShader(ContextMtl *context,
gl::ShaderType shaderType,
const mtl::RenderPipelineDesc &renderPipelineDesc,
id<MTLFunction> *shaderOut);
// Calls this before drawing, changedPipelineDesc is passed when vertex attributes desc and/or
// shader program changed.
angle::Result setupDraw(const gl::Context *glContext,
mtl::RenderCommandEncoder *cmdEncoder,
const mtl::RenderPipelineDesc &pipelineDesc,
bool pipelineDescChanged,
bool forceTexturesSetting,
bool uniformBuffersDirty);
const ProgramExecutableMtl *getExecutable() const
{
return mtl::GetImpl(&mState.getExecutable());
@@ -136,50 +121,12 @@ class ProgramMtl : public ProgramImpl
template <typename T>
void setUniformImpl(GLint location, GLsizei count, const T *v, GLenum entryPointType);
angle::Result commitUniforms(ContextMtl *context, mtl::RenderCommandEncoder *cmdEncoder);
angle::Result updateTextures(const gl::Context *glContext,
mtl::RenderCommandEncoder *cmdEncoder,
bool forceUpdate);
angle::Result updateUniformBuffers(ContextMtl *context,
mtl::RenderCommandEncoder *cmdEncoder,
const mtl::RenderPipelineDesc &pipelineDesc);
angle::Result updateXfbBuffers(ContextMtl *context,
mtl::RenderCommandEncoder *cmdEncoder,
const mtl::RenderPipelineDesc &pipelineDesc);
angle::Result legalizeUniformBufferOffsets(ContextMtl *context,
const std::vector<gl::InterfaceBlock> &blocks);
angle::Result bindUniformBuffersToDiscreteSlots(ContextMtl *context,
mtl::RenderCommandEncoder *cmdEncoder,
const std::vector<gl::InterfaceBlock> &blocks,
gl::ShaderType shaderType);
angle::Result encodeUniformBuffersInfoArgumentBuffer(
ContextMtl *context,
mtl::RenderCommandEncoder *cmdEncoder,
const std::vector<gl::InterfaceBlock> &blocks,
gl::ShaderType shaderType);
void reset(ContextMtl *context);
void linkResources(const gl::ProgramLinkedResources &resources);
std::unique_ptr<LinkEvent> compileMslShaderLibs(const gl::Context *context);
mtl::BufferPool *getBufferPool(ContextMtl *context);
gl::ShaderMap<SharedCompiledShaderStateMtl> mAttachedShaders;
// Scratch data:
// Legalized buffers and their offsets. For example, uniform buffer's offset=1 is not a valid
// offset, it will be converted to legal offset and the result is stored in this array.
std::vector<std::pair<mtl::BufferRef, uint32_t>> mLegalizedOffsetedUniformBuffers;
// Stores the render stages usage of each uniform buffer. Only used if the buffers are encoded
// into an argument buffer.
std::vector<uint32_t> mArgumentBufferRenderStageUsages;
uint32_t mShadowCompareModes[mtl::kMaxShaderSamplers];
mtl::BufferPool *mAuxBufferPool;
};
} // namespace rx

View File

@@ -19,11 +19,9 @@
#include "libANGLE/Context.h"
#include "libANGLE/ProgramLinkedResources.h"
#include "libANGLE/renderer/metal/BufferMtl.h"
#include "libANGLE/renderer/metal/CompilerMtl.h"
#include "libANGLE/renderer/metal/ContextMtl.h"
#include "libANGLE/renderer/metal/DisplayMtl.h"
#include "libANGLE/renderer/metal/TextureMtl.h"
#include "libANGLE/renderer/metal/blocklayoutMetal.h"
#include "libANGLE/renderer/metal/mtl_msl_utils.h"
#include "libANGLE/renderer/metal/mtl_utils.h"
@@ -39,183 +37,21 @@ namespace rx
namespace
{
// Block Encoder Information
#define SHADER_ENTRY_NAME @"main0"
template <typename T>
class [[nodiscard]] ScopedAutoClearVector
{
public:
ScopedAutoClearVector(std::vector<T> *array) : mArray(*array) {}
~ScopedAutoClearVector() { mArray.clear(); }
private:
std::vector<T> &mArray;
};
inline void memcpy_guarded(void *dst, const void *src, const void *maxSrcPtr, size_t size)
{
size_t bytesAvailable = maxSrcPtr > src ? (const uint8_t *)maxSrcPtr - (const uint8_t *)src : 0;
size_t bytesToCopy = std::min(size, bytesAvailable);
size_t bytesToZero = size - bytesToCopy;
if (bytesToCopy)
memcpy(dst, src, bytesToCopy);
if (bytesToZero)
memset((uint8_t *)dst + bytesToCopy, 0, bytesToZero);
}
// Copy matrix one column at a time
inline void copy_matrix(void *dst,
const void *src,
const void *maxSrcPtr,
size_t srcStride,
size_t dstStride,
GLenum type)
{
size_t elemSize = mtl::GetMetalSizeForGLType(gl::VariableComponentType(type));
const size_t dstRows = gl::VariableRowCount(type);
const size_t dstCols = gl::VariableColumnCount(type);
for (size_t col = 0; col < dstCols; col++)
{
size_t srcOffset = col * srcStride;
memcpy_guarded(((uint8_t *)dst) + dstStride * col, (const uint8_t *)src + srcOffset,
maxSrcPtr, elemSize * dstRows);
}
}
// Copy matrix one element at a time to transpose.
inline void copy_matrix_row_major(void *dst,
const void *src,
const void *maxSrcPtr,
size_t srcStride,
size_t dstStride,
GLenum type)
{
size_t elemSize = mtl::GetMetalSizeForGLType(gl::VariableComponentType(type));
const size_t dstRows = gl::VariableRowCount(type);
const size_t dstCols = gl::VariableColumnCount(type);
for (size_t col = 0; col < dstCols; col++)
{
for (size_t row = 0; row < dstRows; row++)
{
size_t srcOffset = row * srcStride + col * elemSize;
memcpy_guarded((uint8_t *)dst + dstStride * col + row * elemSize,
(const uint8_t *)src + srcOffset, maxSrcPtr, elemSize);
}
}
}
// TODO(angleproject:7979) Upgrade ANGLE Uniform buffer remapper to compute shaders
angle::Result ConvertUniformBufferData(ContextMtl *contextMtl,
const UBOConversionInfo &blockConversionInfo,
mtl::BufferPool *dynamicBuffer,
const uint8_t *sourceData,
size_t sizeToCopy,
mtl::BufferRef *bufferOut,
size_t *bufferOffsetOut)
{
uint8_t *dst = nullptr;
const uint8_t *maxSrcPtr = sourceData + sizeToCopy;
dynamicBuffer->releaseInFlightBuffers(contextMtl);
// When converting a UBO buffer, we convert all of the data
// supplied in a buffer at once (sizeToCopy = bufferMtl->size() - initial offset).
// It's possible that a buffer could represent multiple instances of
// a uniform block, so we loop over the number of block conversions we intend
// to do.
size_t numBlocksToCopy =
(sizeToCopy + blockConversionInfo.stdSize() - 1) / blockConversionInfo.stdSize();
size_t bytesToAllocate = numBlocksToCopy * blockConversionInfo.metalSize();
ANGLE_TRY(dynamicBuffer->allocate(contextMtl, bytesToAllocate, &dst, bufferOut, bufferOffsetOut,
nullptr));
const std::vector<sh::BlockMemberInfo> &stdConversions = blockConversionInfo.stdInfo();
const std::vector<sh::BlockMemberInfo> &mtlConversions = blockConversionInfo.metalInfo();
for (size_t i = 0; i < numBlocksToCopy; ++i)
{
auto stdIterator = stdConversions.begin();
auto mtlIterator = mtlConversions.begin();
while (stdIterator != stdConversions.end())
{
for (int arraySize = 0; arraySize < stdIterator->arraySize; ++arraySize)
{
// For every entry in an array, calculate the offset based off of the
// array element size.
// Offset of a single entry is
// blockIndex*blockSize + arrayOffset*arraySize + offset of field in base struct.
// Fields are copied per block, per member, per array entry of member.
size_t stdArrayOffset = stdIterator->arrayStride * arraySize;
size_t mtlArrayOffset = mtlIterator->arrayStride * arraySize;
if (gl::IsMatrixType(mtlIterator->type))
{
void *dstMat = dst + mtlIterator->offset + mtlArrayOffset +
blockConversionInfo.metalSize() * i;
const void *srcMat = sourceData + stdIterator->offset + stdArrayOffset +
blockConversionInfo.stdSize() * i;
// Transpose matricies into column major order, if they're row major encoded.
if (stdIterator->isRowMajorMatrix)
{
copy_matrix_row_major(dstMat, srcMat, maxSrcPtr, stdIterator->matrixStride,
mtlIterator->matrixStride, mtlIterator->type);
}
else
{
copy_matrix(dstMat, srcMat, maxSrcPtr, stdIterator->matrixStride,
mtlIterator->matrixStride, mtlIterator->type);
}
}
// Compress bool from four bytes to one byte because bool values in GLSL
// are uint-sized: ES 3.0 Section 2.12.6.3 "Uniform Buffer Object Storage".
// Bools in metal are byte-sized. (Metal shading language spec Table 2.2)
else if (gl::VariableComponentType(mtlIterator->type) == GL_BOOL)
{
for (int boolCol = 0; boolCol < gl::VariableComponentCount(mtlIterator->type);
boolCol++)
{
const uint8_t *srcBool =
(sourceData + stdIterator->offset + stdArrayOffset +
blockConversionInfo.stdSize() * i +
gl::VariableComponentSize(GL_BOOL) * boolCol);
unsigned int srcValue =
srcBool < maxSrcPtr ? *((unsigned int *)(srcBool)) : 0;
uint8_t *dstBool = dst + mtlIterator->offset + mtlArrayOffset +
blockConversionInfo.metalSize() * i +
sizeof(bool) * boolCol;
*dstBool = (srcValue != 0);
}
}
else
{
memcpy_guarded(dst + mtlIterator->offset + mtlArrayOffset +
blockConversionInfo.metalSize() * i,
sourceData + stdIterator->offset + stdArrayOffset +
blockConversionInfo.stdSize() * i,
maxSrcPtr, mtl::GetMetalSizeForGLType(mtlIterator->type));
}
}
++stdIterator;
++mtlIterator;
}
}
ANGLE_TRY(dynamicBuffer->commit(contextMtl));
return angle::Result::Continue;
}
inline std::map<std::string, std::string> GetDefaultSubstitutionDictionary()
{
return {};
}
bool DisableFastMathForShaderCompilation(mtl::Context *context)
{
return context->getDisplay()->getFeatures().intelDisableFastMath.enabled;
}
bool UsesInvariance(const mtl::TranslatedShaderInfo *translatedMslInfo)
{
return translatedMslInfo->hasInvariant;
}
template <typename T>
void UpdateDefaultUniformBlockWithElementSize(GLsizei count,
uint32_t arrayIndex,
@@ -293,73 +129,6 @@ class Std140BlockLayoutEncoderFactory : public gl::CustomBlockLayoutEncoderFacto
public:
sh::BlockLayoutEncoder *makeEncoder() override { return new sh::Std140BlockEncoder(); }
};
void InitArgumentBufferEncoder(mtl::Context *context,
id<MTLFunction> function,
uint32_t bufferIndex,
ProgramArgumentBufferEncoderMtl *encoder)
{
encoder->metalArgBufferEncoder =
mtl::adoptObjCObj([function newArgumentEncoderWithBufferIndex:bufferIndex]);
if (encoder->metalArgBufferEncoder)
{
encoder->bufferPool.initialize(context, encoder->metalArgBufferEncoder.get().encodedLength,
mtl::kArgumentBufferOffsetAlignment, 0);
}
}
constexpr size_t PipelineParametersToFragmentShaderVariantIndex(bool multisampledRendering,
bool allowFragDepthWrite)
{
const size_t index = (allowFragDepthWrite << 1) | multisampledRendering;
ASSERT(index < kFragmentShaderVariants);
return index;
}
bool DisableFastMathForShaderCompilation(mtl::Context *context)
{
return context->getDisplay()->getFeatures().intelDisableFastMath.enabled;
}
bool UsesInvariance(const mtl::TranslatedShaderInfo *translatedMslInfo)
{
return translatedMslInfo->hasInvariant;
}
angle::Result CreateMslShaderLib(ContextMtl *context,
gl::InfoLog &infoLog,
mtl::TranslatedShaderInfo *translatedMslInfo,
const std::map<std::string, std::string> &substitutionMacros)
{
ANGLE_MTL_OBJC_SCOPE
{
mtl::LibraryCache &libraryCache = context->getDisplay()->getLibraryCache();
// Convert to actual binary shader
mtl::AutoObjCPtr<NSError *> err = nil;
bool disableFastMath = DisableFastMathForShaderCompilation(context);
bool usesInvariance = UsesInvariance(translatedMslInfo);
translatedMslInfo->metalLibrary = libraryCache.getOrCompileShaderLibrary(
context, translatedMslInfo->metalShaderSource, substitutionMacros, disableFastMath,
usesInvariance, &err);
if (err && !translatedMslInfo->metalLibrary)
{
std::ostringstream ss;
ss << "Internal error compiling shader with Metal backend.\n";
ss << err.get().localizedDescription.UTF8String << "\n";
ss << "-----\n";
ss << *(translatedMslInfo->metalShaderSource);
ss << "-----\n";
infoLog << ss.str();
ANGLE_MTL_HANDLE_ERROR(context, ss.str().c_str(), GL_INVALID_OPERATION);
return angle::Result::Stop;
}
return angle::Result::Continue;
}
}
} // namespace
// ProgramArgumentBufferEncoderMtl implementation
@@ -380,39 +149,18 @@ void ProgramShaderObjVariantMtl::reset(ContextMtl *contextMtl)
}
// ProgramMtl implementation
ProgramMtl::ProgramMtl(const gl::ProgramState &state)
: ProgramImpl(state), mShadowCompareModes(), mAuxBufferPool(nullptr)
{}
ProgramMtl::ProgramMtl(const gl::ProgramState &state) : ProgramImpl(state) {}
ProgramMtl::~ProgramMtl() = default;
void ProgramMtl::destroy(const gl::Context *context)
{
auto contextMtl = mtl::GetImpl(context);
if (mAuxBufferPool)
{
mAuxBufferPool->destroy(contextMtl);
delete mAuxBufferPool;
mAuxBufferPool = nullptr;
}
reset(contextMtl);
reset(mtl::GetImpl(context));
}
void ProgramMtl::reset(ContextMtl *context)
{
getExecutable()->reset(context);
if (mAuxBufferPool)
{
if (mAuxBufferPool->reset(context, mtl::kDefaultUniformsMaxSize * 2,
mtl::kUniformBufferSettingOffsetMinAlignment,
3) != angle::Result::Continue)
{
mAuxBufferPool->destroy(context);
delete mAuxBufferPool;
mAuxBufferPool = nullptr;
}
}
}
std::unique_ptr<rx::LinkEvent> ProgramMtl::load(const gl::Context *context,
@@ -474,10 +222,9 @@ std::unique_ptr<LinkEvent> ProgramMtl::link(const gl::Context *context,
gl::ShaderMap<std::string> shaderSources;
mtl::MSLGetShaderSource(mState, resources, &shaderSources);
ANGLE_PARALLEL_LINK_TRY(
mtl::MTLGetMSL(contextMtl, mState, contextMtl->getCaps(), shaderSources, mAttachedShaders,
&executableMtl->mMslShaderTranslateInfo,
mState.getExecutable().getTransformFeedbackBufferCount()));
ANGLE_PARALLEL_LINK_TRY(mtl::MTLGetMSL(contextMtl, mState.getExecutable(),
contextMtl->getCaps(), shaderSources, mAttachedShaders,
&executableMtl->mMslShaderTranslateInfo));
executableMtl->mMslXfbOnlyVertexShaderInfo =
executableMtl->mMslShaderTranslateInfo[gl::ShaderType::Vertex];
@@ -605,16 +352,6 @@ std::unique_ptr<LinkEvent> ProgramMtl::compileMslShaderLibs(const gl::Context *c
infoLog, context->getShaderCompileThreadPool(), std::move(asyncTasks));
}
mtl::BufferPool *ProgramMtl::getBufferPool(ContextMtl *context)
{
if (mAuxBufferPool == nullptr)
{
mAuxBufferPool = new mtl::BufferPool(true);
mAuxBufferPool->initialize(context, mtl::kDefaultUniformsMaxSize * 2,
mtl::kUniformBufferSettingOffsetMinAlignment, 3);
}
return mAuxBufferPool;
}
void ProgramMtl::linkResources(const gl::ProgramLinkedResources &resources)
{
Std140BlockLayoutEncoderFactory std140EncoderFactory;
@@ -623,137 +360,6 @@ void ProgramMtl::linkResources(const gl::ProgramLinkedResources &resources)
linker.linkResources(mState, resources);
}
angle::Result ProgramMtl::getSpecializedShader(ContextMtl *context,
gl::ShaderType shaderType,
const mtl::RenderPipelineDesc &renderPipelineDesc,
id<MTLFunction> *shaderOut)
{
ProgramExecutableMtl *executableMtl = getExecutable();
static_assert(YES == 1, "YES should have value of 1");
mtl::TranslatedShaderInfo *translatedMslInfo =
&executableMtl->mMslShaderTranslateInfo[shaderType];
ProgramShaderObjVariantMtl *shaderVariant;
mtl::AutoObjCObj<MTLFunctionConstantValues> funcConstants;
if (shaderType == gl::ShaderType::Vertex)
{
// For vertex shader, we need to create 3 variants, one with emulated rasterization
// discard, one with true rasterization discard and one without.
shaderVariant = &executableMtl->mVertexShaderVariants[renderPipelineDesc.rasterizationType];
if (shaderVariant->metalShader)
{
// Already created.
*shaderOut = shaderVariant->metalShader;
return angle::Result::Continue;
}
if (renderPipelineDesc.rasterizationType == mtl::RenderPipelineRasterization::Disabled)
{
// Special case: XFB output only vertex shader.
ASSERT(!mState.getLinkedTransformFeedbackVaryings().empty());
translatedMslInfo = &executableMtl->mMslXfbOnlyVertexShaderInfo;
if (!translatedMslInfo->metalLibrary)
{
// Lazily compile XFB only shader
gl::InfoLog infoLog;
ANGLE_TRY(CreateMslShaderLib(context, infoLog,
&executableMtl->mMslXfbOnlyVertexShaderInfo,
{{"TRANSFORM_FEEDBACK_ENABLED", "1"}}));
translatedMslInfo->metalLibrary.get().label = @"TransformFeedback";
}
}
ANGLE_MTL_OBJC_SCOPE
{
BOOL emulateDiscard = renderPipelineDesc.rasterizationType ==
mtl::RenderPipelineRasterization::EmulatedDiscard;
NSString *discardEnabledStr =
[NSString stringWithUTF8String:sh::mtl::kRasterizerDiscardEnabledConstName];
funcConstants = mtl::adoptObjCObj([[MTLFunctionConstantValues alloc] init]);
[funcConstants setConstantValue:&emulateDiscard
type:MTLDataTypeBool
withName:discardEnabledStr];
}
} // if (shaderType == gl::ShaderType::Vertex)
else if (shaderType == gl::ShaderType::Fragment)
{
// For fragment shader, we need to create 4 variants,
// combining multisampled rendering and depth write enabled states.
const bool multisampledRendering = renderPipelineDesc.outputDescriptor.sampleCount > 1;
const bool allowFragDepthWrite =
renderPipelineDesc.outputDescriptor.depthAttachmentPixelFormat != 0;
shaderVariant =
&executableMtl->mFragmentShaderVariants[PipelineParametersToFragmentShaderVariantIndex(
multisampledRendering, allowFragDepthWrite)];
if (shaderVariant->metalShader)
{
// Already created.
*shaderOut = shaderVariant->metalShader;
return angle::Result::Continue;
}
ANGLE_MTL_OBJC_SCOPE
{
NSString *multisampledRenderingStr =
[NSString stringWithUTF8String:sh::mtl::kMultisampledRenderingConstName];
NSString *depthWriteEnabledStr =
[NSString stringWithUTF8String:sh::mtl::kDepthWriteEnabledConstName];
funcConstants = mtl::adoptObjCObj([[MTLFunctionConstantValues alloc] init]);
[funcConstants setConstantValue:&multisampledRendering
type:MTLDataTypeBool
withName:multisampledRenderingStr];
[funcConstants setConstantValue:&allowFragDepthWrite
type:MTLDataTypeBool
withName:depthWriteEnabledStr];
}
} // gl::ShaderType::Fragment
else
{
UNREACHABLE();
return angle::Result::Stop;
}
[funcConstants
setConstantValue:&(context->getDisplay()->getFeatures().allowSamplerCompareGradient.enabled)
type:MTLDataTypeBool
withName:@"ANGLEUseSampleCompareGradient"];
[funcConstants
setConstantValue:&(context->getDisplay()->getFeatures().allowSamplerCompareLod.enabled)
type:MTLDataTypeBool
withName:@"ANGLEUseSampleCompareLod"];
[funcConstants
setConstantValue:&(context->getDisplay()->getFeatures().emulateAlphaToCoverage.enabled)
type:MTLDataTypeBool
withName:@"ANGLEEmulateAlphaToCoverage"];
// Create Metal shader object
ANGLE_MTL_OBJC_SCOPE
{
ANGLE_TRY(CreateMslShader(context, translatedMslInfo->metalLibrary, SHADER_ENTRY_NAME,
funcConstants.get(), &shaderVariant->metalShader));
}
// Store reference to the translated source for easily querying mapped bindings later.
shaderVariant->translatedSrcInfo = translatedMslInfo;
// Initialize argument buffer encoder if required
if (translatedMslInfo->hasUBOArgumentBuffer)
{
InitArgumentBufferEncoder(context, shaderVariant->metalShader,
mtl::kUBOArgumentBufferBindingIndex,
&shaderVariant->uboArgBufferEncoder);
}
*shaderOut = shaderVariant->metalShader;
return angle::Result::Continue;
}
GLboolean ProgramMtl::validate(const gl::Caps &caps)
{
// No-op. The spec is very vague about the behavior of validation.
@@ -1064,505 +670,4 @@ void ProgramMtl::getUniformuiv(const gl::Context *context, GLint location, GLuin
getUniformImpl(location, params, GL_UNSIGNED_INT);
}
angle::Result ProgramMtl::setupDraw(const gl::Context *glContext,
mtl::RenderCommandEncoder *cmdEncoder,
const mtl::RenderPipelineDesc &pipelineDesc,
bool pipelineDescChanged,
bool forceTexturesSetting,
bool uniformBuffersDirty)
{
ContextMtl *context = mtl::GetImpl(glContext);
ProgramExecutableMtl *executableMtl = getExecutable();
if (pipelineDescChanged)
{
id<MTLFunction> vertexShader = nil;
ANGLE_TRY(
getSpecializedShader(context, gl::ShaderType::Vertex, pipelineDesc, &vertexShader));
id<MTLFunction> fragmentShader = nil;
ANGLE_TRY(
getSpecializedShader(context, gl::ShaderType::Fragment, pipelineDesc, &fragmentShader));
mtl::AutoObjCPtr<id<MTLRenderPipelineState>> pipelineState;
ANGLE_TRY(context->getPipelineCache().getRenderPipeline(
context, vertexShader, fragmentShader, pipelineDesc, &pipelineState));
cmdEncoder->setRenderPipelineState(pipelineState);
// We need to rebind uniform buffers & textures also
executableMtl->mDefaultUniformBlocksDirty.set();
executableMtl->mSamplerBindingsDirty.set();
// Cache current shader variant references for easier querying.
executableMtl->mCurrentShaderVariants[gl::ShaderType::Vertex] =
&executableMtl->mVertexShaderVariants[pipelineDesc.rasterizationType];
const bool multisampledRendering = pipelineDesc.outputDescriptor.sampleCount > 1;
const bool allowFragDepthWrite =
pipelineDesc.outputDescriptor.depthAttachmentPixelFormat != 0;
executableMtl->mCurrentShaderVariants[gl::ShaderType::Fragment] =
pipelineDesc.rasterizationEnabled()
? &executableMtl
->mFragmentShaderVariants[PipelineParametersToFragmentShaderVariantIndex(
multisampledRendering, allowFragDepthWrite)]
: nullptr;
}
ANGLE_TRY(commitUniforms(context, cmdEncoder));
ANGLE_TRY(updateTextures(glContext, cmdEncoder, forceTexturesSetting));
if (uniformBuffersDirty || pipelineDescChanged)
{
ANGLE_TRY(updateUniformBuffers(context, cmdEncoder, pipelineDesc));
}
if (pipelineDescChanged)
{
ANGLE_TRY(updateXfbBuffers(context, cmdEncoder, pipelineDesc));
}
return angle::Result::Continue;
}
angle::Result ProgramMtl::commitUniforms(ContextMtl *context, mtl::RenderCommandEncoder *cmdEncoder)
{
ProgramExecutableMtl *executableMtl = getExecutable();
for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
{
if (!executableMtl->mDefaultUniformBlocksDirty[shaderType] ||
!executableMtl->mCurrentShaderVariants[shaderType])
{
continue;
}
DefaultUniformBlockMtl &uniformBlock = executableMtl->mDefaultUniformBlocks[shaderType];
if (!uniformBlock.uniformData.size())
{
continue;
}
if (mAuxBufferPool)
{
mAuxBufferPool->releaseInFlightBuffers(context);
}
// If we exceed the default inline max size, try to allocate a buffer
bool needsCommitUniform = true;
if (needsCommitUniform && uniformBlock.uniformData.size() <= mtl::kInlineConstDataMaxSize)
{
ASSERT(uniformBlock.uniformData.size() <= mtl::kInlineConstDataMaxSize);
cmdEncoder->setBytes(shaderType, uniformBlock.uniformData.data(),
uniformBlock.uniformData.size(),
mtl::kDefaultUniformsBindingIndex);
}
else if (needsCommitUniform)
{
ASSERT(uniformBlock.uniformData.size() <= mtl::kDefaultUniformsMaxSize);
mtl::BufferRef mtlBufferOut;
size_t offsetOut;
uint8_t *ptrOut;
// Allocate a new Uniform buffer
ANGLE_TRY(getBufferPool(context)->allocate(context, uniformBlock.uniformData.size(),
&ptrOut, &mtlBufferOut, &offsetOut));
// Copy the uniform result
memcpy(ptrOut, uniformBlock.uniformData.data(), uniformBlock.uniformData.size());
// Commit
ANGLE_TRY(getBufferPool(context)->commit(context));
// Set buffer
cmdEncoder->setBuffer(shaderType, mtlBufferOut, (uint32_t)offsetOut,
mtl::kDefaultUniformsBindingIndex);
}
executableMtl->mDefaultUniformBlocksDirty.reset(shaderType);
}
return angle::Result::Continue;
}
angle::Result ProgramMtl::updateTextures(const gl::Context *glContext,
mtl::RenderCommandEncoder *cmdEncoder,
bool forceUpdate)
{
ContextMtl *contextMtl = mtl::GetImpl(glContext);
const auto &glState = glContext->getState();
ProgramExecutableMtl *executableMtl = getExecutable();
const gl::ActiveTexturesCache &completeTextures = glState.getActiveTexturesCache();
for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
{
if ((!executableMtl->mSamplerBindingsDirty[shaderType] && !forceUpdate) ||
!executableMtl->mCurrentShaderVariants[shaderType])
{
continue;
}
const mtl::TranslatedShaderInfo &shaderInfo =
executableMtl->mCurrentShaderVariants[shaderType]->translatedSrcInfo
? *executableMtl->mCurrentShaderVariants[shaderType]->translatedSrcInfo
: executableMtl->mMslShaderTranslateInfo[shaderType];
bool hasDepthSampler = false;
for (uint32_t textureIndex = 0; textureIndex < mState.getSamplerBindings().size();
++textureIndex)
{
const gl::SamplerBinding &samplerBinding = mState.getSamplerBindings()[textureIndex];
const mtl::SamplerBinding &mslBinding = shaderInfo.actualSamplerBindings[textureIndex];
if (mslBinding.textureBinding >= mtl::kMaxShaderSamplers)
{
// No binding assigned
continue;
}
gl::TextureType textureType = samplerBinding.textureType;
for (uint32_t arrayElement = 0; arrayElement < samplerBinding.textureUnitsCount;
++arrayElement)
{
GLuint textureUnit = samplerBinding.getTextureUnit(
mState.getSamplerBoundTextureUnits(), arrayElement);
gl::Texture *texture = completeTextures[textureUnit];
gl::Sampler *sampler = contextMtl->getState().getSampler(textureUnit);
uint32_t textureSlot = mslBinding.textureBinding + arrayElement;
uint32_t samplerSlot = mslBinding.samplerBinding + arrayElement;
if (!texture)
{
ANGLE_TRY(contextMtl->getIncompleteTexture(glContext, textureType,
samplerBinding.format, &texture));
}
const gl::SamplerState *samplerState =
sampler ? &sampler->getSamplerState() : &texture->getSamplerState();
TextureMtl *textureMtl = mtl::GetImpl(texture);
if (samplerBinding.format == gl::SamplerFormat::Shadow)
{
hasDepthSampler = true;
mShadowCompareModes[textureSlot] = mtl::MslGetShaderShadowCompareMode(
samplerState->getCompareMode(), samplerState->getCompareFunc());
}
ANGLE_TRY(textureMtl->bindToShader(glContext, cmdEncoder, shaderType, sampler,
textureSlot, samplerSlot));
} // for array elements
} // for sampler bindings
if (hasDepthSampler)
{
cmdEncoder->setData(shaderType, mShadowCompareModes,
mtl::kShadowSamplerCompareModesBindingIndex);
}
for (const gl::ImageBinding &imageBinding : mState.getImageBindings())
{
if (imageBinding.boundImageUnits.size() != 1)
{
UNIMPLEMENTED();
continue;
}
int glslImageBinding = imageBinding.boundImageUnits[0];
int mtlRWTextureBinding = shaderInfo.actualImageBindings[glslImageBinding];
ASSERT(mtlRWTextureBinding < static_cast<int>(mtl::kMaxShaderImages));
if (mtlRWTextureBinding < 0)
{
continue; // The program does not have an image bound at this unit.
}
const gl::ImageUnit &imageUnit = glState.getImageUnit(glslImageBinding);
TextureMtl *textureMtl = mtl::GetImpl(imageUnit.texture.get());
ANGLE_TRY(textureMtl->bindToShaderImage(
glContext, cmdEncoder, shaderType, static_cast<uint32_t>(mtlRWTextureBinding),
imageUnit.level, imageUnit.layer, imageUnit.format));
}
} // for shader types
return angle::Result::Continue;
}
angle::Result ProgramMtl::updateUniformBuffers(ContextMtl *context,
mtl::RenderCommandEncoder *cmdEncoder,
const mtl::RenderPipelineDesc &pipelineDesc)
{
const std::vector<gl::InterfaceBlock> &blocks = mState.getUniformBlocks();
if (blocks.empty())
{
return angle::Result::Continue;
}
ProgramExecutableMtl *executableMtl = getExecutable();
// This array is only used inside this function and its callees.
ScopedAutoClearVector<uint32_t> scopeArrayClear(&mArgumentBufferRenderStageUsages);
ScopedAutoClearVector<std::pair<mtl::BufferRef, uint32_t>> scopeArrayClear2(
&mLegalizedOffsetedUniformBuffers);
mArgumentBufferRenderStageUsages.resize(blocks.size());
mLegalizedOffsetedUniformBuffers.resize(blocks.size());
ANGLE_TRY(legalizeUniformBufferOffsets(context, blocks));
const gl::State &glState = context->getState();
for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
{
if (!executableMtl->mCurrentShaderVariants[shaderType])
{
continue;
}
if (executableMtl->mCurrentShaderVariants[shaderType]
->translatedSrcInfo->hasUBOArgumentBuffer)
{
ANGLE_TRY(
encodeUniformBuffersInfoArgumentBuffer(context, cmdEncoder, blocks, shaderType));
}
else
{
ANGLE_TRY(bindUniformBuffersToDiscreteSlots(context, cmdEncoder, blocks, shaderType));
}
} // for shader types
// After encode the uniform buffers into an argument buffer, we need to tell Metal that
// the buffers are being used by what shader stages.
for (uint32_t bufferIndex = 0; bufferIndex < blocks.size(); ++bufferIndex)
{
const gl::InterfaceBlock &block = blocks[bufferIndex];
const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding =
glState.getIndexedUniformBuffer(block.binding);
if (bufferBinding.get() == nullptr)
{
continue;
}
// Remove any other stages other than vertex and fragment.
uint32_t stages = mArgumentBufferRenderStageUsages[bufferIndex] &
(mtl::kRenderStageVertex | mtl::kRenderStageFragment);
if (stages == 0)
{
continue;
}
cmdEncoder->useResource(mLegalizedOffsetedUniformBuffers[bufferIndex].first,
MTLResourceUsageRead, static_cast<mtl::RenderStages>(stages));
}
return angle::Result::Continue;
}
angle::Result ProgramMtl::legalizeUniformBufferOffsets(
ContextMtl *context,
const std::vector<gl::InterfaceBlock> &blocks)
{
ProgramExecutableMtl *executableMtl = getExecutable();
const gl::State &glState = context->getState();
for (uint32_t bufferIndex = 0; bufferIndex < blocks.size(); ++bufferIndex)
{
const gl::InterfaceBlock &block = blocks[bufferIndex];
const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding =
glState.getIndexedUniformBuffer(block.binding);
if (bufferBinding.get() == nullptr)
{
continue;
}
BufferMtl *bufferMtl = mtl::GetImpl(bufferBinding.get());
size_t srcOffset = std::min<size_t>(bufferBinding.getOffset(), bufferMtl->size());
ASSERT(executableMtl->mUniformBlockConversions.find(block.name) !=
executableMtl->mUniformBlockConversions.end());
const UBOConversionInfo &conversionInfo =
executableMtl->mUniformBlockConversions.at(block.name);
if (conversionInfo.needsConversion())
{
UniformConversionBufferMtl *conversion =
(UniformConversionBufferMtl *)bufferMtl->getUniformConversionBuffer(
context, std::pair<size_t, size_t>(bufferIndex, srcOffset),
conversionInfo.stdSize());
// Has the content of the buffer has changed since last conversion?
if (conversion->dirty)
{
const uint8_t *srcBytes = bufferMtl->getBufferDataReadOnly(context);
srcBytes += srcOffset;
size_t sizeToCopy = bufferMtl->size() - srcOffset;
ANGLE_TRY(ConvertUniformBufferData(
context, conversionInfo, &conversion->data, srcBytes, sizeToCopy,
&conversion->convertedBuffer, &conversion->convertedOffset));
conversion->dirty = false;
}
// Calculate offset in new block.
size_t dstOffsetSource = srcOffset - conversion->initialSrcOffset();
assert(dstOffsetSource % conversionInfo.stdSize() == 0);
unsigned int numBlocksToOffset =
(unsigned int)(dstOffsetSource / conversionInfo.stdSize());
size_t bytesToOffset = numBlocksToOffset * conversionInfo.metalSize();
mLegalizedOffsetedUniformBuffers[bufferIndex].first = conversion->convertedBuffer;
mLegalizedOffsetedUniformBuffers[bufferIndex].second =
static_cast<uint32_t>(conversion->convertedOffset + bytesToOffset);
}
else
{
mLegalizedOffsetedUniformBuffers[bufferIndex].first = bufferMtl->getCurrentBuffer();
mLegalizedOffsetedUniformBuffers[bufferIndex].second =
static_cast<uint32_t>(bufferBinding.getOffset());
}
}
return angle::Result::Continue;
}
angle::Result ProgramMtl::bindUniformBuffersToDiscreteSlots(
ContextMtl *context,
mtl::RenderCommandEncoder *cmdEncoder,
const std::vector<gl::InterfaceBlock> &blocks,
gl::ShaderType shaderType)
{
const gl::State &glState = context->getState();
ProgramExecutableMtl *executableMtl = getExecutable();
const mtl::TranslatedShaderInfo &shaderInfo =
*executableMtl->mCurrentShaderVariants[shaderType]->translatedSrcInfo;
for (uint32_t bufferIndex = 0; bufferIndex < blocks.size(); ++bufferIndex)
{
const gl::InterfaceBlock &block = blocks[bufferIndex];
const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding =
glState.getIndexedUniformBuffer(block.binding);
if (bufferBinding.get() == nullptr || !block.activeShaders().test(shaderType))
{
continue;
}
uint32_t actualBufferIdx = shaderInfo.actualUBOBindings[bufferIndex];
if (actualBufferIdx >= mtl::kMaxShaderBuffers)
{
continue;
}
mtl::BufferRef mtlBuffer = mLegalizedOffsetedUniformBuffers[bufferIndex].first;
uint32_t offset = mLegalizedOffsetedUniformBuffers[bufferIndex].second;
cmdEncoder->setBuffer(shaderType, mtlBuffer, offset, actualBufferIdx);
}
return angle::Result::Continue;
}
angle::Result ProgramMtl::encodeUniformBuffersInfoArgumentBuffer(
ContextMtl *context,
mtl::RenderCommandEncoder *cmdEncoder,
const std::vector<gl::InterfaceBlock> &blocks,
gl::ShaderType shaderType)
{
const gl::State &glState = context->getState();
ProgramExecutableMtl *executableMtl = getExecutable();
ASSERT(executableMtl->mCurrentShaderVariants[shaderType]->translatedSrcInfo);
const mtl::TranslatedShaderInfo &shaderInfo =
*executableMtl->mCurrentShaderVariants[shaderType]->translatedSrcInfo;
// Encode all uniform buffers into an argument buffer.
ProgramArgumentBufferEncoderMtl &bufferEncoder =
executableMtl->mCurrentShaderVariants[shaderType]->uboArgBufferEncoder;
mtl::BufferRef argumentBuffer;
size_t argumentBufferOffset;
bufferEncoder.bufferPool.releaseInFlightBuffers(context);
ANGLE_TRY(bufferEncoder.bufferPool.allocate(
context, bufferEncoder.metalArgBufferEncoder.get().encodedLength, nullptr, &argumentBuffer,
&argumentBufferOffset));
// MTLArgumentEncoder is modifying the buffer indirectly on CPU. We need to call map()
// so that the buffer's data changes could be flushed to the GPU side later.
ANGLE_UNUSED_VARIABLE(argumentBuffer->mapWithOpt(context, /*readonly=*/false, /*noSync=*/true));
[bufferEncoder.metalArgBufferEncoder setArgumentBuffer:argumentBuffer->get()
offset:argumentBufferOffset];
constexpr gl::ShaderMap<MTLRenderStages> kShaderStageMap = {
{gl::ShaderType::Vertex, mtl::kRenderStageVertex},
{gl::ShaderType::Fragment, mtl::kRenderStageFragment},
};
auto mtlRenderStage = kShaderStageMap[shaderType];
for (uint32_t bufferIndex = 0; bufferIndex < blocks.size(); ++bufferIndex)
{
const gl::InterfaceBlock &block = blocks[bufferIndex];
const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding =
glState.getIndexedUniformBuffer(block.binding);
if (bufferBinding.get() == nullptr || !block.activeShaders().test(shaderType))
{
continue;
}
mArgumentBufferRenderStageUsages[bufferIndex] |= mtlRenderStage;
uint32_t actualBufferIdx = shaderInfo.actualUBOBindings[bufferIndex];
if (actualBufferIdx >= mtl::kMaxShaderBuffers)
{
continue;
}
mtl::BufferRef mtlBuffer = mLegalizedOffsetedUniformBuffers[bufferIndex].first;
uint32_t offset = mLegalizedOffsetedUniformBuffers[bufferIndex].second;
[bufferEncoder.metalArgBufferEncoder setBuffer:mtlBuffer->get()
offset:offset
atIndex:actualBufferIdx];
}
// Flush changes made by MTLArgumentEncoder to GPU.
argumentBuffer->unmapAndFlushSubset(context, argumentBufferOffset,
bufferEncoder.metalArgBufferEncoder.get().encodedLength);
cmdEncoder->setBuffer(shaderType, argumentBuffer, static_cast<uint32_t>(argumentBufferOffset),
mtl::kUBOArgumentBufferBindingIndex);
return angle::Result::Continue;
}
angle::Result ProgramMtl::updateXfbBuffers(ContextMtl *context,
mtl::RenderCommandEncoder *cmdEncoder,
const mtl::RenderPipelineDesc &pipelineDesc)
{
const gl::State &glState = context->getState();
ProgramExecutableMtl *executableMtl = getExecutable();
gl::TransformFeedback *transformFeedback = glState.getCurrentTransformFeedback();
if (pipelineDesc.rasterizationEnabled() || !glState.isTransformFeedbackActiveUnpaused() ||
ANGLE_UNLIKELY(!transformFeedback))
{
// XFB output can only be used with rasterization disabled.
return angle::Result::Continue;
}
size_t xfbBufferCount = glState.getProgramExecutable()->getTransformFeedbackBufferCount();
ASSERT(xfbBufferCount > 0);
ASSERT(mState.getTransformFeedbackBufferMode() != GL_INTERLEAVED_ATTRIBS ||
xfbBufferCount == 1);
for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
{
uint32_t actualBufferIdx =
executableMtl->mMslXfbOnlyVertexShaderInfo.actualXFBBindings[bufferIndex];
if (actualBufferIdx >= mtl::kMaxShaderBuffers)
{
continue;
}
const gl::OffsetBindingPointer<gl::Buffer> &bufferBinding =
transformFeedback->getIndexedBuffer(bufferIndex);
gl::Buffer *buffer = bufferBinding.get();
ASSERT((bufferBinding.getOffset() % 4) == 0);
ASSERT(buffer != nullptr);
BufferMtl *bufferMtl = mtl::GetImpl(buffer);
// Use offset=0, actual offset will be set in Driver Uniform inside ContextMtl.
cmdEncoder->setBufferForWrite(gl::ShaderType::Vertex, bufferMtl->getCurrentBuffer(), 0,
actualBufferIdx);
}
return angle::Result::Continue;
}
} // namespace rx

View File

@@ -52,12 +52,11 @@ void MSLGetShaderSource(const gl::ProgramState &programState,
gl::ShaderMap<std::string> *shaderSourcesOut);
angle::Result MTLGetMSL(Context *context,
const gl::ProgramState &programState,
const gl::ProgramExecutable &executable,
const gl::Caps &glCaps,
const gl::ShaderMap<std::string> &shaderSources,
const gl::ShaderMap<SharedCompiledShaderStateMtl> &shadersState,
gl::ShaderMap<TranslatedShaderInfo> *mslShaderInfoOut,
size_t xfbBufferCount);
gl::ShaderMap<TranslatedShaderInfo> *mslShaderInfoOut);
// Get equivalent shadow compare mode that is used in translated msl shader.
uint MslGetShaderShadowCompareMode(GLenum mode, GLenum func);

View File

@@ -158,10 +158,11 @@ void GetAssignedSamplerBindings(const sh::TranslatorMetalReflection *reflection,
}
}
std::string updateShaderAttributes(std::string shaderSourceIn, const gl::ProgramState &programState)
std::string updateShaderAttributes(std::string shaderSourceIn,
const gl::ProgramExecutable &executable)
{
// Build string to attrib map.
const auto &programAttributes = programState.getProgramInputs();
const auto &programAttributes = executable.getProgramInputs();
std::ostringstream stream;
std::unordered_map<std::string, uint32_t> attributeBindings;
for (auto &attribute : programAttributes)
@@ -204,12 +205,12 @@ std::string updateShaderAttributes(std::string shaderSourceIn, const gl::Program
}
std::string UpdateFragmentShaderOutputs(std::string shaderSourceIn,
const gl::ProgramState &programState,
const gl::ProgramExecutable &executable,
bool defineAlpha0)
{
std::ostringstream stream;
std::string outputSource = shaderSourceIn;
const auto &outputVariables = programState.getOutputVariables();
const auto &outputVariables = executable.getOutputVariables();
// For alpha-to-coverage emulation, a reference to the alpha channel
// of color output 0 is needed. For ESSL 1.00, it is gl_FragColor or
@@ -266,8 +267,8 @@ std::string UpdateFragmentShaderOutputs(std::string shaderSourceIn,
}
}
};
assignLocations(programState.getOutputLocations(), false);
assignLocations(programState.getSecondaryOutputLocations(), true);
assignLocations(executable.getOutputLocations(), false);
assignLocations(executable.getSecondaryOutputLocations(), true);
if (defineAlpha0)
{
@@ -401,15 +402,15 @@ std::string GenerateTransformFeedbackVaryingOutput(const gl::TransformFeedbackVa
}
void GenerateTransformFeedbackEmulationOutputs(
const gl::ProgramState &programState,
const gl::ProgramExecutable &executable,
std::string *vertexShader,
std::array<uint32_t, kMaxShaderXFBs> *xfbBindingRemapOut)
{
const std::vector<gl::TransformFeedbackVarying> &varyings =
programState.getLinkedTransformFeedbackVaryings();
const std::vector<GLsizei> &bufferStrides = programState.getTransformFeedbackStrides();
executable.getLinkedTransformFeedbackVaryings();
const std::vector<GLsizei> &bufferStrides = executable.getTransformFeedbackStrides();
const bool isInterleaved =
programState.getTransformFeedbackBufferMode() == GL_INTERLEAVED_ATTRIBS;
executable.getTransformFeedbackBufferMode() == GL_INTERLEAVED_ATTRIBS;
const size_t bufferCount = isInterleaved ? 1 : varyings.size();
std::vector<std::string> xfbIndices(bufferCount);
@@ -456,16 +457,15 @@ void GenerateTransformFeedbackEmulationOutputs(
}
angle::Result MTLGetMSL(Context *context,
const gl::ProgramState &programState,
const gl::ProgramExecutable &executable,
const gl::Caps &glCaps,
const gl::ShaderMap<std::string> &shaderSources,
const gl::ShaderMap<SharedCompiledShaderStateMtl> &shadersState,
gl::ShaderMap<TranslatedShaderInfo> *mslShaderInfoOut,
size_t xfbBufferCount)
gl::ShaderMap<TranslatedShaderInfo> *mslShaderInfoOut)
{
// Retrieve original uniform buffer bindings generated by front end. We will need to do a remap.
std::unordered_map<std::string, uint32_t> uboOriginalBindings;
const std::vector<gl::InterfaceBlock> &blocks = programState.getUniformBlocks();
const std::vector<gl::InterfaceBlock> &blocks = executable.getUniformBlocks();
for (uint32_t bufferIdx = 0; bufferIdx < blocks.size(); ++bufferIdx)
{
const gl::InterfaceBlock &block = blocks[bufferIdx];
@@ -476,15 +476,15 @@ angle::Result MTLGetMSL(Context *context,
}
// Retrieve original sampler bindings produced by front end.
OriginalSamplerBindingMap originalSamplerBindings;
const std::vector<gl::SamplerBinding> &samplerBindings = programState.getSamplerBindings();
const std::vector<gl::SamplerBinding> &samplerBindings = executable.getSamplerBindings();
std::unordered_set<std::string> structSamplers = {};
for (uint32_t textureIndex = 0; textureIndex < samplerBindings.size(); ++textureIndex)
{
const gl::SamplerBinding &samplerBinding = samplerBindings[textureIndex];
uint32_t uniformIndex = programState.getUniformIndexFromSamplerIndex(textureIndex);
const std::string &uniformName = programState.getUniformNames()[uniformIndex];
const std::string &uniformMappedName = programState.getUniformMappedNames()[uniformIndex];
uint32_t uniformIndex = executable.getUniformIndexFromSamplerIndex(textureIndex);
const std::string &uniformName = executable.getUniformNames()[uniformIndex];
const std::string &uniformMappedName = executable.getUniformMappedNames()[uniformIndex];
bool isSamplerInStruct = uniformName.find('.') != std::string::npos;
std::string mappedSamplerName = isSamplerInStruct
? MSLGetMappedSamplerName(uniformName)
@@ -500,18 +500,18 @@ angle::Result MTLGetMSL(Context *context,
std::string source;
if (type == gl::ShaderType::Vertex)
{
source = updateShaderAttributes(shaderSources[type], programState);
source = updateShaderAttributes(shaderSources[type], executable);
// Write transform feedback output code.
if (!source.empty())
{
if (programState.getLinkedTransformFeedbackVaryings().empty())
if (executable.getLinkedTransformFeedbackVaryings().empty())
{
source = SubstituteTransformFeedbackMarkers(source, "", "");
}
else
{
GenerateTransformFeedbackEmulationOutputs(
programState, &source, &(*mslShaderInfoOut)[type].actualXFBBindings);
executable, &source, &(*mslShaderInfoOut)[type].actualXFBBindings);
}
}
}
@@ -521,7 +521,7 @@ angle::Result MTLGetMSL(Context *context,
bool defineAlpha0 =
context->getDisplay()->getFeatures().emulateAlphaToCoverage.enabled ||
context->getDisplay()->getFeatures().generateShareableShaders.enabled;
source = UpdateFragmentShaderOutputs(shaderSources[type], programState, defineAlpha0);
source = UpdateFragmentShaderOutputs(shaderSources[type], executable, defineAlpha0);
}
(*mslShaderInfoOut)[type].metalShaderSource =
std::make_shared<const std::string>(std::move(source));