blob: c9e862eb155e4e8ff4bdfbf79685cfc0b7afac8e [file] [log] [blame]
//
// Copyright 2019 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.
//
// ProgramMtl.mm:
// Implements the class methods for ProgramMtl.
//
#include "libANGLE/renderer/metal/ProgramMtl.h"
#include <TargetConditionals.h>
#include <sstream>
#include "common/WorkerThread.h"
#include "common/debug.h"
#include "common/system_utils.h"
#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"
#include "libANGLE/renderer/metal/renderermtl_utils.h"
#include "libANGLE/renderer/renderer_utils.h"
#include "libANGLE/trace.h"
#define ANGLE_PARALLEL_LINK_RETURN(X) return std::make_unique<LinkEventDone>(X);
#define ANGLE_PARALLEL_LINK_TRY(EXPR) ANGLE_TRY_TEMPLATE(EXPR, ANGLE_PARALLEL_LINK_RETURN)
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);
}
}
}
bool compareBlockInfo(const sh::BlockMemberInfo &a, const sh::BlockMemberInfo &b)
{
return a.offset < b.offset;
}
// 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;
}
size_t GetAlignmentOfUniformGroup(sh::BlockLayoutMap *blockLayoutMap)
{
size_t align = 1;
for (auto layoutIter = blockLayoutMap->begin(); layoutIter != blockLayoutMap->end();
++layoutIter)
{
align = std::max(mtl::GetMetalAlignmentForGLType(layoutIter->second.type), align);
}
return align;
}
void InitDefaultUniformBlock(const std::vector<sh::Uniform> &uniforms,
sh::BlockLayoutMap *blockLayoutMapOut,
size_t *blockSizeOut)
{
if (uniforms.empty())
{
*blockSizeOut = 0;
return;
}
mtl::BlockLayoutEncoderMTL blockEncoder;
sh::GetActiveUniformBlockInfo(uniforms, "", &blockEncoder, blockLayoutMapOut);
size_t blockAlign = GetAlignmentOfUniformGroup(blockLayoutMapOut);
size_t blockSize = roundUp(blockEncoder.getCurrentOffset(), blockAlign);
// TODO(jmadill): I think we still need a valid block for the pipeline even if zero sized.
if (blockSize == 0)
{
*blockSizeOut = 0;
return;
}
*blockSizeOut = blockSize;
return;
}
inline std::map<std::string, std::string> getDefaultSubstitutionDictionary()
{
return {};
}
template <typename T>
void UpdateDefaultUniformBlock(GLsizei count,
uint32_t arrayIndex,
int componentCount,
const T *v,
const sh::BlockMemberInfo &layoutInfo,
angle::MemoryBuffer *uniformData)
{
UpdateDefaultUniformBlockWithElementSize(count, arrayIndex, componentCount, v, sizeof(T),
layoutInfo, uniformData);
}
template <typename T>
void UpdateDefaultUniformBlockWithElementSize(GLsizei count,
uint32_t arrayIndex,
int componentCount,
const T *v,
size_t baseElementSize,
const sh::BlockMemberInfo &layoutInfo,
angle::MemoryBuffer *uniformData)
{
const int elementSize = (int)(baseElementSize * componentCount);
uint8_t *dst = uniformData->data() + layoutInfo.offset;
if (layoutInfo.arrayStride == 0 || layoutInfo.arrayStride == elementSize)
{
uint32_t arrayOffset = arrayIndex * layoutInfo.arrayStride;
uint8_t *writePtr = dst + arrayOffset;
ASSERT(writePtr + (elementSize * count) <= uniformData->data() + uniformData->size());
memcpy(writePtr, v, elementSize * count);
}
else
{
// Have to respect the arrayStride between each element of the array.
int maxIndex = arrayIndex + count;
for (int writeIndex = arrayIndex, readIndex = 0; writeIndex < maxIndex;
writeIndex++, readIndex++)
{
const int arrayOffset = writeIndex * layoutInfo.arrayStride;
uint8_t *writePtr = dst + arrayOffset;
const T *readPtr = v + (readIndex * componentCount);
ASSERT(writePtr + elementSize <= uniformData->data() + uniformData->size());
memcpy(writePtr, readPtr, elementSize);
}
}
}
template <typename T>
void ReadFromDefaultUniformBlock(int componentCount,
uint32_t arrayIndex,
T *dst,
size_t elementSize,
const sh::BlockMemberInfo &layoutInfo,
const angle::MemoryBuffer *uniformData)
{
ReadFromDefaultUniformBlockWithElementSize(componentCount, arrayIndex, dst, sizeof(T),
layoutInfo, uniformData);
}
void ReadFromDefaultUniformBlockWithElementSize(int componentCount,
uint32_t arrayIndex,
void *dst,
size_t baseElementSize,
const sh::BlockMemberInfo &layoutInfo,
const angle::MemoryBuffer *uniformData)
{
ASSERT(layoutInfo.offset != -1);
const size_t elementSize = (baseElementSize * componentCount);
const uint8_t *source = uniformData->data() + layoutInfo.offset;
if (layoutInfo.arrayStride == 0 || (size_t)layoutInfo.arrayStride == elementSize)
{
const uint8_t *readPtr = source + arrayIndex * layoutInfo.arrayStride;
memcpy(dst, readPtr, elementSize);
}
else
{
// Have to respect the arrayStride between each element of the array.
const int arrayOffset = arrayIndex * layoutInfo.arrayStride;
const uint8_t *readPtr = source + arrayOffset;
memcpy(dst, readPtr, elementSize);
}
}
class Std140BlockLayoutEncoderFactory : public gl::CustomBlockLayoutEncoderFactory
{
public:
sh::BlockLayoutEncoder *makeEncoder() override { return new sh::Std140BlockEncoder(); }
};
class Std430BlockLayoutEncoderFactory : public gl::CustomBlockLayoutEncoderFactory
{
public:
sh::BlockLayoutEncoder *makeEncoder() override { return new sh::Std430BlockEncoder(); }
};
class StdMTLBLockLayoutEncoderFactory : public gl::CustomBlockLayoutEncoderFactory
{
public:
sh::BlockLayoutEncoder *makeEncoder() override { return new mtl::BlockLayoutEncoderMTL(); }
};
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(ContextMtl *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
// TODO(angleproject:7979) Upgrade ANGLE Uniform buffer remapper to compute shaders
void ProgramMtl::initUniformBlocksRemapper(const gl::SharedCompiledShaderState &shader)
{
std::unordered_map<std::string, UBOConversionInfo> conversionMap;
const std::vector<sh::InterfaceBlock> ibs = shader->uniformBlocks;
for (size_t i = 0; i < ibs.size(); ++i)
{
const sh::InterfaceBlock &ib = ibs[i];
if (mUniformBlockConversions.find(ib.name) == mUniformBlockConversions.end())
{
mtl::BlockLayoutEncoderMTL metalEncoder;
sh::BlockLayoutEncoder *encoder;
switch (ib.layout)
{
case sh::BLOCKLAYOUT_PACKED:
case sh::BLOCKLAYOUT_SHARED:
case sh::BLOCKLAYOUT_STD140:
{
Std140BlockLayoutEncoderFactory factory;
encoder = factory.makeEncoder();
}
break;
case sh::BLOCKLAYOUT_STD430:
{
Std430BlockLayoutEncoderFactory factory;
encoder = factory.makeEncoder();
}
break;
}
sh::BlockLayoutMap blockLayoutMapOut, stdMapOut;
sh::GetInterfaceBlockInfo(ib.fields, "", &metalEncoder, &blockLayoutMapOut);
sh::GetInterfaceBlockInfo(ib.fields, "", encoder, &stdMapOut);
auto stdIterator = stdMapOut.begin();
auto mtlIterator = blockLayoutMapOut.begin();
std::vector<sh::BlockMemberInfo> stdConversions, mtlConversions;
while (stdIterator != stdMapOut.end())
{
stdConversions.push_back(stdIterator->second);
mtlConversions.push_back(mtlIterator->second);
stdIterator++;
mtlIterator++;
}
std::sort(stdConversions.begin(), stdConversions.end(), compareBlockInfo);
std::sort(mtlConversions.begin(), mtlConversions.end(), compareBlockInfo);
size_t stdSize = encoder->getCurrentOffset();
size_t metalAlign = GetAlignmentOfUniformGroup(&blockLayoutMapOut);
size_t metalSize = roundUp(metalEncoder.getCurrentOffset(), metalAlign);
conversionMap.insert(
{ib.name, UBOConversionInfo(stdConversions, mtlConversions, stdSize, metalSize)});
SafeDelete(encoder);
}
}
mUniformBlockConversions.insert(conversionMap.begin(), conversionMap.end());
}
// ProgramArgumentBufferEncoderMtl implementation
void ProgramArgumentBufferEncoderMtl::reset(ContextMtl *contextMtl)
{
metalArgBufferEncoder = nil;
bufferPool.destroy(contextMtl);
}
// ProgramShaderObjVariantMtl implementation
void ProgramShaderObjVariantMtl::reset(ContextMtl *contextMtl)
{
metalShader = nil;
uboArgBufferEncoder.reset(contextMtl);
translatedSrcInfo = nullptr;
}
// ProgramMtl implementation
ProgramMtl::DefaultUniformBlock::DefaultUniformBlock() {}
ProgramMtl::DefaultUniformBlock::~DefaultUniformBlock() = default;
ProgramMtl::ProgramMtl(const gl::ProgramState &state)
: ProgramImpl(state),
mProgramHasFlatAttributes(false),
mShadowCompareModes(),
mAuxBufferPool(nullptr)
{}
ProgramMtl::~ProgramMtl() {}
void ProgramMtl::destroy(const gl::Context *context)
{
auto contextMtl = mtl::GetImpl(context);
if (mAuxBufferPool)
{
mAuxBufferPool->destroy(contextMtl);
delete mAuxBufferPool;
mAuxBufferPool = nullptr;
}
reset(contextMtl);
}
void ProgramMtl::reset(ContextMtl *context)
{
mProgramHasFlatAttributes = false;
for (auto &block : mDefaultUniformBlocks)
{
block.uniformLayout.clear();
}
for (gl::ShaderType shaderType : gl::AllShaderTypes())
{
mMslShaderTranslateInfo[shaderType].reset();
mCurrentShaderVariants[shaderType] = nullptr;
}
mMslXfbOnlyVertexShaderInfo.reset();
for (ProgramShaderObjVariantMtl &var : mVertexShaderVariants)
{
var.reset(context);
}
for (ProgramShaderObjVariantMtl &var : mFragmentShaderVariants)
{
var.reset(context);
}
if (mAuxBufferPool)
{
if (mAuxBufferPool->reset(context, mtl::kDefaultUniformsMaxSize * 2,
mtl::kUniformBufferSettingOffsetMinAlignment,
3) != angle::Result::Continue)
{
mAuxBufferPool->destroy(context);
delete mAuxBufferPool;
mAuxBufferPool = nullptr;
}
}
}
void ProgramMtl::saveTranslatedShaders(gl::BinaryOutputStream *stream)
{
auto writeTranslatedSource = [](gl::BinaryOutputStream *stream,
const mtl::TranslatedShaderInfo &shaderInfo) {
const std::string &source =
shaderInfo.metalShaderSource ? *shaderInfo.metalShaderSource : std::string();
stream->writeString(source);
};
// Write out shader sources for all shader types
for (const gl::ShaderType shaderType : gl::AllShaderTypes())
{
writeTranslatedSource(stream, mMslShaderTranslateInfo[shaderType]);
}
writeTranslatedSource(stream, mMslXfbOnlyVertexShaderInfo);
}
void ProgramMtl::loadTranslatedShaders(gl::BinaryInputStream *stream)
{
auto readTranslatedSource = [](gl::BinaryInputStream *stream,
mtl::TranslatedShaderInfo &shaderInfo) {
std::string source = stream->readString();
if (!source.empty())
{
shaderInfo.metalShaderSource = std::make_shared<const std::string>(std::move(source));
}
else
{
shaderInfo.metalShaderSource = nullptr;
}
};
// Read in shader sources for all shader types
for (const gl::ShaderType shaderType : gl::AllShaderTypes())
{
readTranslatedSource(stream, mMslShaderTranslateInfo[shaderType]);
}
readTranslatedSource(stream, mMslXfbOnlyVertexShaderInfo);
}
std::unique_ptr<rx::LinkEvent> ProgramMtl::load(const gl::Context *context,
gl::BinaryInputStream *stream,
gl::InfoLog &infoLog)
{
ContextMtl *contextMtl = mtl::GetImpl(context);
// NOTE(hqle): No transform feedbacks for now, since we only support ES 2.0 atm
reset(contextMtl);
loadTranslatedShaders(stream);
loadShaderInternalInfo(stream);
ANGLE_PARALLEL_LINK_TRY(loadDefaultUniformBlocksInfo(context, stream));
ANGLE_PARALLEL_LINK_TRY(loadInterfaceBlockInfo(context, stream));
return compileMslShaderLibs(context, infoLog);
}
void ProgramMtl::save(const gl::Context *context, gl::BinaryOutputStream *stream)
{
saveTranslatedShaders(stream);
saveShaderInternalInfo(stream);
saveDefaultUniformBlocksInfo(stream);
saveInterfaceBlockInfo(stream);
}
void ProgramMtl::setBinaryRetrievableHint(bool retrievable) {}
void ProgramMtl::setSeparable(bool separable)
{
UNIMPLEMENTED();
}
void ProgramMtl::prepareForLink(const gl::ShaderMap<ShaderImpl *> &shaders)
{
for (gl::ShaderType shaderType : gl::AllShaderTypes())
{
mAttachedShaders[shaderType].reset();
if (shaders[shaderType] != nullptr)
{
const ShaderMtl *shaderMtl = GetAs<ShaderMtl>(shaders[shaderType]);
mAttachedShaders[shaderType] = shaderMtl->getCompiledState();
}
}
}
std::unique_ptr<LinkEvent> ProgramMtl::link(const gl::Context *context,
const gl::ProgramLinkedResources &resources,
gl::InfoLog &infoLog,
const gl::ProgramMergedVaryings &mergedVaryings)
{
ContextMtl *contextMtl = mtl::GetImpl(context);
// Link resources before calling GetShaderSource to make sure they are ready for the set/binding
// assignment done in that function.
linkResources(resources);
reset(contextMtl);
ANGLE_PARALLEL_LINK_TRY(initDefaultUniformBlocks(context));
linkUpdateHasFlatAttributes();
gl::ShaderMap<std::string> shaderSources;
mtl::MSLGetShaderSource(mState, resources, &shaderSources);
ANGLE_PARALLEL_LINK_TRY(mtl::MTLGetMSL(
context, mState, contextMtl->getCaps(), shaderSources, mAttachedShaders,
&mMslShaderTranslateInfo, mState.getExecutable().getTransformFeedbackBufferCount()));
mMslXfbOnlyVertexShaderInfo = mMslShaderTranslateInfo[gl::ShaderType::Vertex];
return compileMslShaderLibs(context, infoLog);
}
void ProgramMtl::linkUpdateHasFlatAttributes()
{
mProgramHasFlatAttributes = false;
const auto &programInputs = mState.getProgramInputs();
for (auto &attribute : programInputs)
{
if (attribute.getInterpolation() == sh::INTERPOLATION_FLAT)
{
mProgramHasFlatAttributes = true;
return;
}
}
const auto &flatVaryings = mState.getAttachedShader(gl::ShaderType::Vertex)->outputVaryings;
for (auto &attribute : flatVaryings)
{
if (attribute.interpolation == sh::INTERPOLATION_FLAT)
{
mProgramHasFlatAttributes = true;
return;
}
}
}
class ProgramMtl::CompileMslTask final : public angle::Closure
{
public:
CompileMslTask(ContextMtl *context,
mtl::TranslatedShaderInfo *translatedMslInfo,
const std::map<std::string, std::string> &substitutionMacros)
: mContext(context),
mTranslatedMslInfo(translatedMslInfo),
mSubstitutionMacros(substitutionMacros)
{}
void operator()() override
{
mResult = CreateMslShaderLib(mContext, mInfoLog, mTranslatedMslInfo, mSubstitutionMacros);
}
angle::Result getResult(gl::InfoLog &infoLog)
{
if (!mInfoLog.empty())
{
infoLog << mInfoLog.str();
}
return mResult;
}
private:
ContextMtl *mContext;
gl::InfoLog mInfoLog;
mtl::TranslatedShaderInfo *mTranslatedMslInfo;
std::map<std::string, std::string> mSubstitutionMacros;
angle::Result mResult = angle::Result::Continue;
};
// The LinkEvent implementation for linking a Metal program
class ProgramMtl::ProgramLinkEvent final : public LinkEvent
{
public:
ProgramLinkEvent(gl::InfoLog &infoLog,
std::shared_ptr<angle::WorkerThreadPool> workerPool,
std::vector<std::shared_ptr<ProgramMtl::CompileMslTask>> &&compileTasks)
: mInfoLog(infoLog), mTasks(std::move(compileTasks))
{
mWaitableEvents.reserve(mTasks.size());
for (const auto &task : mTasks)
{
mWaitableEvents.push_back(workerPool->postWorkerTask(task));
}
}
bool isLinking() override { return !angle::WaitableEvent::AllReady(&mWaitableEvents); }
angle::Result wait(const gl::Context *context) override
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramMtl::ProgramLinkEvent::wait");
angle::WaitableEvent::WaitMany(&mWaitableEvents);
for (const auto &task : mTasks)
{
ANGLE_TRY(task->getResult(mInfoLog));
}
return angle::Result::Continue;
}
private:
gl::InfoLog &mInfoLog;
std::vector<std::shared_ptr<ProgramMtl::CompileMslTask>> mTasks;
std::vector<std::shared_ptr<angle::WaitableEvent>> mWaitableEvents;
};
std::unique_ptr<LinkEvent> ProgramMtl::compileMslShaderLibs(const gl::Context *context,
gl::InfoLog &infoLog)
{
ANGLE_TRACE_EVENT0("gpu.angle", "ProgramMtl::compileMslShaderLibs");
ContextMtl *contextMtl = mtl::GetImpl(context);
bool asyncCompile =
contextMtl->getDisplay()->getFeatures().enableParallelMtlLibraryCompilation.enabled;
mtl::LibraryCache &libraryCache = contextMtl->getDisplay()->getLibraryCache();
std::vector<std::shared_ptr<ProgramMtl::CompileMslTask>> asyncTasks;
for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
{
mtl::TranslatedShaderInfo *translateInfo = &mMslShaderTranslateInfo[shaderType];
std::map<std::string, std::string> macros = getDefaultSubstitutionDictionary();
bool disableFastMath = DisableFastMathForShaderCompilation(contextMtl);
bool usesInvariance = UsesInvariance(translateInfo);
// Check if the shader is already in the cache and use it instead of spawning a new thread
translateInfo->metalLibrary = libraryCache.get(translateInfo->metalShaderSource, macros,
disableFastMath, usesInvariance);
if (!translateInfo->metalLibrary)
{
if (asyncCompile)
{
auto task =
std::make_shared<ProgramMtl::CompileMslTask>(contextMtl, translateInfo, macros);
asyncTasks.push_back(task);
}
else
{
ANGLE_PARALLEL_LINK_TRY(
CreateMslShaderLib(contextMtl, infoLog, translateInfo, macros));
}
}
}
if (asyncTasks.empty())
{
// All shaders were in the cache, no async work to do
return std::make_unique<LinkEventDone>(angle::Result::Continue);
}
return std::make_unique<ProgramMtl::ProgramLinkEvent>(
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;
gl::ProgramLinkedResourcesLinker linker(&std140EncoderFactory);
linker.linkResources(mState, resources);
}
angle::Result ProgramMtl::initDefaultUniformBlocks(const gl::Context *glContext)
{
// Process vertex and fragment uniforms into std140 packing.
gl::ShaderMap<sh::BlockLayoutMap> layoutMap;
gl::ShaderMap<size_t> requiredBufferSize;
requiredBufferSize.fill(0);
for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
{
const gl::SharedCompiledShaderState &shader = mState.getAttachedShader(shaderType);
if (shader)
{
const std::vector<sh::Uniform> &uniforms = shader->uniforms;
InitDefaultUniformBlock(uniforms, &layoutMap[shaderType],
&requiredBufferSize[shaderType]);
// Set up block conversion buffer
initUniformBlocksRemapper(shader);
}
}
// Init the default block layout info.
const auto &uniforms = mState.getUniforms();
const auto &uniformNames = mState.getUniformNames();
const auto &uniformLocations = mState.getUniformLocations();
for (size_t locSlot = 0; locSlot < uniformLocations.size(); ++locSlot)
{
const gl::VariableLocation &location = uniformLocations[locSlot];
gl::ShaderMap<sh::BlockMemberInfo> layoutInfo;
if (location.used() && !location.ignored)
{
const gl::LinkedUniform &uniform = uniforms[location.index];
if (uniform.isInDefaultBlock() && !uniform.isSampler() && !uniform.isImage())
{
std::string uniformName = uniformNames[location.index];
if (uniform.isArray())
{
// Gets the uniform name without the [0] at the end.
uniformName = gl::ParseResourceName(uniformName, nullptr);
}
bool found = false;
for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
{
auto it = layoutMap[shaderType].find(uniformName);
if (it != layoutMap[shaderType].end())
{
found = true;
layoutInfo[shaderType] = it->second;
}
}
ASSERT(found);
}
}
for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
{
mDefaultUniformBlocks[shaderType].uniformLayout.push_back(layoutInfo[shaderType]);
}
}
return resizeDefaultUniformBlocksMemory(glContext, requiredBufferSize);
}
angle::Result ProgramMtl::resizeDefaultUniformBlocksMemory(
const gl::Context *glContext,
const gl::ShaderMap<size_t> &requiredBufferSize)
{
ContextMtl *contextMtl = mtl::GetImpl(glContext);
for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
{
if (requiredBufferSize[shaderType] > 0)
{
ASSERT(requiredBufferSize[shaderType] <= mtl::kDefaultUniformsMaxSize);
if (!mDefaultUniformBlocks[shaderType].uniformData.resize(
requiredBufferSize[shaderType]))
{
ANGLE_MTL_CHECK(contextMtl, false, GL_OUT_OF_MEMORY);
}
// Initialize uniform buffer memory to zero by default.
mDefaultUniformBlocks[shaderType].uniformData.fill(0);
mDefaultUniformBlocksDirty.set(shaderType);
}
}
return angle::Result::Continue;
}
angle::Result ProgramMtl::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(!mState.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;
}
void ProgramMtl::saveInterfaceBlockInfo(gl::BinaryOutputStream *stream)
{
// Serializes the uniformLayout data of mDefaultUniformBlocks
// First, save the number of Ib's to process
stream->writeInt<unsigned int>((unsigned int)mUniformBlockConversions.size());
// Next, iterate through all of the conversions.
for (auto conversion : mUniformBlockConversions)
{
// Write the name of the conversion
stream->writeString(conversion.first);
// Write the number of entries in the conversion
const UBOConversionInfo &conversionInfo = conversion.second;
unsigned int numEntries = (unsigned int)(conversionInfo.stdInfo().size());
stream->writeInt<unsigned int>(numEntries);
for (unsigned int i = 0; i < numEntries; ++i)
{
gl::WriteBlockMemberInfo(stream, conversionInfo.stdInfo()[i]);
}
for (unsigned int i = 0; i < numEntries; ++i)
{
gl::WriteBlockMemberInfo(stream, conversionInfo.metalInfo()[i]);
}
stream->writeInt<size_t>(conversionInfo.stdSize());
stream->writeInt<size_t>(conversionInfo.metalSize());
}
}
angle::Result ProgramMtl::loadInterfaceBlockInfo(const gl::Context *glContext,
gl::BinaryInputStream *stream)
{
mUniformBlockConversions.clear();
// First, load the number of Ib's to process
uint32_t numBlocks = stream->readInt<uint32_t>();
// Next, iterate through all of the conversions.
for (uint32_t nBlocks = 0; nBlocks < numBlocks; ++nBlocks)
{
// Read the name of the conversion
std::string blockName = stream->readString();
// Read the number of entries in the conversion
std::vector<sh::BlockMemberInfo> stdInfo, metalInfo;
uint32_t numEntries = stream->readInt<uint32_t>();
stdInfo.reserve(numEntries);
metalInfo.reserve(numEntries);
for (uint32_t i = 0; i < numEntries; ++i)
{
stdInfo.push_back(sh::BlockMemberInfo());
gl::LoadBlockMemberInfo(stream, &(stdInfo[i]));
}
for (uint32_t i = 0; i < numEntries; ++i)
{
metalInfo.push_back(sh::BlockMemberInfo());
gl::LoadBlockMemberInfo(stream, &(metalInfo[i]));
}
size_t stdSize = stream->readInt<size_t>();
size_t metalSize = stream->readInt<size_t>();
mUniformBlockConversions.insert(
{blockName, UBOConversionInfo(stdInfo, metalInfo, stdSize, metalSize)});
}
return angle::Result::Continue;
}
void ProgramMtl::saveDefaultUniformBlocksInfo(gl::BinaryOutputStream *stream)
{
// Serializes the uniformLayout data of mDefaultUniformBlocks
for (gl::ShaderType shaderType : gl::AllShaderTypes())
{
const size_t uniformCount = mDefaultUniformBlocks[shaderType].uniformLayout.size();
stream->writeInt<size_t>(uniformCount);
for (unsigned int uniformIndex = 0; uniformIndex < uniformCount; ++uniformIndex)
{
sh::BlockMemberInfo &blockInfo =
mDefaultUniformBlocks[shaderType].uniformLayout[uniformIndex];
gl::WriteBlockMemberInfo(stream, blockInfo);
}
}
// Serializes required uniform block memory sizes
for (gl::ShaderType shaderType : gl::AllShaderTypes())
{
stream->writeInt(mDefaultUniformBlocks[shaderType].uniformData.size());
}
}
angle::Result ProgramMtl::loadDefaultUniformBlocksInfo(const gl::Context *glContext,
gl::BinaryInputStream *stream)
{
gl::ShaderMap<size_t> requiredBufferSize;
requiredBufferSize.fill(0);
// Deserializes the uniformLayout data of mDefaultUniformBlocks
for (gl::ShaderType shaderType : gl::AllShaderTypes())
{
const size_t uniformCount = stream->readInt<size_t>();
for (unsigned int uniformIndex = 0; uniformIndex < uniformCount; ++uniformIndex)
{
sh::BlockMemberInfo blockInfo;
gl::LoadBlockMemberInfo(stream, &blockInfo);
mDefaultUniformBlocks[shaderType].uniformLayout.push_back(blockInfo);
}
}
// Deserializes required uniform block memory sizes
for (gl::ShaderType shaderType : gl::AllShaderTypes())
{
requiredBufferSize[shaderType] = stream->readInt<size_t>();
}
return resizeDefaultUniformBlocksMemory(glContext, requiredBufferSize);
}
void ProgramMtl::saveShaderInternalInfo(gl::BinaryOutputStream *stream)
{
for (gl::ShaderType shaderType : gl::AllShaderTypes())
{
stream->writeInt<int>(mMslShaderTranslateInfo[shaderType].hasUBOArgumentBuffer);
for (const mtl::SamplerBinding &binding :
mMslShaderTranslateInfo[shaderType].actualSamplerBindings)
{
stream->writeInt<uint32_t>(binding.textureBinding);
stream->writeInt<uint32_t>(binding.samplerBinding);
}
for (int rwTextureBinding : mMslShaderTranslateInfo[shaderType].actualImageBindings)
{
stream->writeInt<int>(rwTextureBinding);
}
for (uint32_t uboBinding : mMslShaderTranslateInfo[shaderType].actualUBOBindings)
{
stream->writeInt<uint32_t>(uboBinding);
}
stream->writeBool(mMslShaderTranslateInfo[shaderType].hasInvariant);
}
for (size_t xfbBindIndex = 0; xfbBindIndex < mtl::kMaxShaderXFBs; xfbBindIndex++)
{
stream->writeInt(
mMslShaderTranslateInfo[gl::ShaderType::Vertex].actualXFBBindings[xfbBindIndex]);
}
// Write out XFB info.
{
stream->writeInt<int>(mMslXfbOnlyVertexShaderInfo.hasUBOArgumentBuffer);
for (mtl::SamplerBinding &binding : mMslXfbOnlyVertexShaderInfo.actualSamplerBindings)
{
stream->writeInt<uint32_t>(binding.textureBinding);
stream->writeInt<uint32_t>(binding.samplerBinding);
}
for (int rwTextureBinding : mMslXfbOnlyVertexShaderInfo.actualImageBindings)
{
stream->writeInt<int>(rwTextureBinding);
}
for (uint32_t &uboBinding : mMslXfbOnlyVertexShaderInfo.actualUBOBindings)
{
stream->writeInt<uint32_t>(uboBinding);
}
for (size_t xfbBindIndex = 0; xfbBindIndex < mtl::kMaxShaderXFBs; xfbBindIndex++)
{
stream->writeInt(mMslXfbOnlyVertexShaderInfo.actualXFBBindings[xfbBindIndex]);
}
}
stream->writeBool(mProgramHasFlatAttributes);
}
void ProgramMtl::loadShaderInternalInfo(gl::BinaryInputStream *stream)
{
for (gl::ShaderType shaderType : gl::AllShaderTypes())
{
mMslShaderTranslateInfo[shaderType].hasUBOArgumentBuffer = stream->readInt<int>() != 0;
for (mtl::SamplerBinding &binding :
mMslShaderTranslateInfo[shaderType].actualSamplerBindings)
{
binding.textureBinding = stream->readInt<uint32_t>();
binding.samplerBinding = stream->readInt<uint32_t>();
}
for (int &rwTextureBinding : mMslShaderTranslateInfo[shaderType].actualImageBindings)
{
rwTextureBinding = stream->readInt<int>();
}
for (uint32_t &uboBinding : mMslShaderTranslateInfo[shaderType].actualUBOBindings)
{
uboBinding = stream->readInt<uint32_t>();
}
mMslShaderTranslateInfo[shaderType].hasInvariant = stream->readBool();
}
for (size_t xfbBindIndex = 0; xfbBindIndex < mtl::kMaxShaderXFBs; xfbBindIndex++)
{
stream->readInt(
&mMslShaderTranslateInfo[gl::ShaderType::Vertex].actualXFBBindings[xfbBindIndex]);
}
// Load Transform Feedback info
{
mMslXfbOnlyVertexShaderInfo.hasUBOArgumentBuffer = stream->readInt<int>() != 0;
for (mtl::SamplerBinding &binding : mMslXfbOnlyVertexShaderInfo.actualSamplerBindings)
{
binding.textureBinding = stream->readInt<uint32_t>();
binding.samplerBinding = stream->readInt<uint32_t>();
}
for (int &rwTextureBinding : mMslXfbOnlyVertexShaderInfo.actualImageBindings)
{
rwTextureBinding = stream->readInt<int>();
}
for (uint32_t &uboBinding : mMslXfbOnlyVertexShaderInfo.actualUBOBindings)
{
uboBinding = stream->readInt<uint32_t>();
}
for (size_t xfbBindIndex = 0; xfbBindIndex < mtl::kMaxShaderXFBs; xfbBindIndex++)
{
stream->readInt(&mMslXfbOnlyVertexShaderInfo.actualXFBBindings[xfbBindIndex]);
}
mMslXfbOnlyVertexShaderInfo.metalLibrary = nullptr;
}
mProgramHasFlatAttributes = stream->readBool();
}
GLboolean ProgramMtl::validate(const gl::Caps &caps, gl::InfoLog *infoLog)
{
// No-op. The spec is very vague about the behavior of validation.
return GL_TRUE;
}
template <typename T>
void ProgramMtl::setUniformImpl(GLint location, GLsizei count, const T *v, GLenum entryPointType)
{
const gl::VariableLocation &locationInfo = mState.getUniformLocations()[location];
const gl::LinkedUniform &linkedUniform = mState.getUniforms()[locationInfo.index];
if (linkedUniform.isSampler())
{
// Sampler binding has changed.
mSamplerBindingsDirty.set();
return;
}
if (linkedUniform.type == entryPointType)
{
for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
{
DefaultUniformBlock &uniformBlock = mDefaultUniformBlocks[shaderType];
const sh::BlockMemberInfo &layoutInfo = uniformBlock.uniformLayout[location];
// Assume an offset of -1 means the block is unused.
if (layoutInfo.offset == -1)
{
continue;
}
const GLint componentCount = (GLint)linkedUniform.getElementComponents();
const GLint baseComponentSize =
(GLint)mtl::GetMetalSizeForGLType(gl::VariableComponentType(linkedUniform.type));
UpdateDefaultUniformBlockWithElementSize(count, locationInfo.arrayIndex, componentCount,
v, baseComponentSize, layoutInfo,
&uniformBlock.uniformData);
mDefaultUniformBlocksDirty.set(shaderType);
}
}
else
{
for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
{
DefaultUniformBlock &uniformBlock = mDefaultUniformBlocks[shaderType];
const sh::BlockMemberInfo &layoutInfo = uniformBlock.uniformLayout[location];
// Assume an offset of -1 means the block is unused.
if (layoutInfo.offset == -1)
{
continue;
}
const GLint componentCount = linkedUniform.getElementComponents();
ASSERT(linkedUniform.type == gl::VariableBoolVectorType(entryPointType));
GLint initialArrayOffset =
locationInfo.arrayIndex * layoutInfo.arrayStride + layoutInfo.offset;
for (GLint i = 0; i < count; i++)
{
GLint elementOffset = i * layoutInfo.arrayStride + initialArrayOffset;
bool *dest =
reinterpret_cast<bool *>(uniformBlock.uniformData.data() + elementOffset);
const T *source = v + i * componentCount;
for (int c = 0; c < componentCount; c++)
{
dest[c] = (source[c] == static_cast<T>(0)) ? GL_FALSE : GL_TRUE;
}
}
mDefaultUniformBlocksDirty.set(shaderType);
}
}
}
template <typename T>
void ProgramMtl::getUniformImpl(GLint location, T *v, GLenum entryPointType) const
{
const gl::VariableLocation &locationInfo = mState.getUniformLocations()[location];
const gl::LinkedUniform &linkedUniform = mState.getUniforms()[locationInfo.index];
ASSERT(!linkedUniform.isSampler());
const gl::ShaderType shaderType = linkedUniform.getFirstActiveShaderType();
ASSERT(shaderType != gl::ShaderType::InvalidEnum);
const DefaultUniformBlock &uniformBlock = mDefaultUniformBlocks[shaderType];
const sh::BlockMemberInfo &layoutInfo = uniformBlock.uniformLayout[location];
ASSERT(gl::GetUniformTypeInfo(linkedUniform.type).componentType == entryPointType ||
gl::GetUniformTypeInfo(linkedUniform.type).componentType ==
gl::VariableBoolVectorType(entryPointType));
const GLint baseComponentSize =
(GLint)mtl::GetMetalSizeForGLType(gl::VariableComponentType(linkedUniform.type));
if (gl::IsMatrixType(linkedUniform.getType()))
{
const uint8_t *ptrToElement = uniformBlock.uniformData.data() + layoutInfo.offset +
(locationInfo.arrayIndex * layoutInfo.arrayStride);
mtl::GetMatrixUniformMetal(linkedUniform.getType(), v,
reinterpret_cast<const T *>(ptrToElement), false);
}
// Decompress bool from one byte to four bytes because bool values in GLSL
// are uint-sized: ES 3.0 Section 2.12.6.3 "Uniform Buffer Object Storage".
else if (gl::VariableComponentType(linkedUniform.getType()) == GL_BOOL)
{
bool bVals[4] = {0};
ReadFromDefaultUniformBlockWithElementSize(
linkedUniform.getElementComponents(), locationInfo.arrayIndex, bVals, baseComponentSize,
layoutInfo, &uniformBlock.uniformData);
for (int bCol = 0; bCol < linkedUniform.getElementComponents(); ++bCol)
{
unsigned int data = bVals[bCol];
*(v + bCol) = static_cast<T>(data);
}
}
else
{
assert(baseComponentSize == sizeof(T));
ReadFromDefaultUniformBlockWithElementSize(linkedUniform.getElementComponents(),
locationInfo.arrayIndex, v, baseComponentSize,
layoutInfo, &uniformBlock.uniformData);
}
}
void ProgramMtl::setUniform1fv(GLint location, GLsizei count, const GLfloat *v)
{
setUniformImpl(location, count, v, GL_FLOAT);
}
void ProgramMtl::setUniform2fv(GLint location, GLsizei count, const GLfloat *v)
{
setUniformImpl(location, count, v, GL_FLOAT_VEC2);
}
void ProgramMtl::setUniform3fv(GLint location, GLsizei count, const GLfloat *v)
{
setUniformImpl(location, count, v, GL_FLOAT_VEC3);
}
void ProgramMtl::setUniform4fv(GLint location, GLsizei count, const GLfloat *v)
{
setUniformImpl(location, count, v, GL_FLOAT_VEC4);
}
void ProgramMtl::setUniform1iv(GLint startLocation, GLsizei count, const GLint *v)
{
setUniformImpl(startLocation, count, v, GL_INT);
}
void ProgramMtl::setUniform2iv(GLint location, GLsizei count, const GLint *v)
{
setUniformImpl(location, count, v, GL_INT_VEC2);
}
void ProgramMtl::setUniform3iv(GLint location, GLsizei count, const GLint *v)
{
setUniformImpl(location, count, v, GL_INT_VEC3);
}
void ProgramMtl::setUniform4iv(GLint location, GLsizei count, const GLint *v)
{
setUniformImpl(location, count, v, GL_INT_VEC4);
}
void ProgramMtl::setUniform1uiv(GLint location, GLsizei count, const GLuint *v)
{
setUniformImpl(location, count, v, GL_UNSIGNED_INT);
}
void ProgramMtl::setUniform2uiv(GLint location, GLsizei count, const GLuint *v)
{
setUniformImpl(location, count, v, GL_UNSIGNED_INT_VEC2);
}
void ProgramMtl::setUniform3uiv(GLint location, GLsizei count, const GLuint *v)
{
setUniformImpl(location, count, v, GL_UNSIGNED_INT_VEC3);
}
void ProgramMtl::setUniform4uiv(GLint location, GLsizei count, const GLuint *v)
{
setUniformImpl(location, count, v, GL_UNSIGNED_INT_VEC4);
}
template <int cols, int rows>
void ProgramMtl::setUniformMatrixfv(GLint location,
GLsizei count,
GLboolean transpose,
const GLfloat *value)
{
const gl::VariableLocation &locationInfo = mState.getUniformLocations()[location];
const gl::LinkedUniform &linkedUniform = mState.getUniforms()[locationInfo.index];
for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
{
DefaultUniformBlock &uniformBlock = mDefaultUniformBlocks[shaderType];
const sh::BlockMemberInfo &layoutInfo = uniformBlock.uniformLayout[location];
// Assume an offset of -1 means the block is unused.
if (layoutInfo.offset == -1)
{
continue;
}
mtl::SetFloatUniformMatrixMetal<cols, rows>::Run(
locationInfo.arrayIndex, linkedUniform.getBasicTypeElementCount(), count, transpose,
value, uniformBlock.uniformData.data() + layoutInfo.offset);
mDefaultUniformBlocksDirty.set(shaderType);
}
}
void ProgramMtl::setUniformMatrix2fv(GLint location,
GLsizei count,
GLboolean transpose,
const GLfloat *value)
{
setUniformMatrixfv<2, 2>(location, count, transpose, value);
}
void ProgramMtl::setUniformMatrix3fv(GLint location,
GLsizei count,
GLboolean transpose,
const GLfloat *value)
{
setUniformMatrixfv<3, 3>(location, count, transpose, value);
}
void ProgramMtl::setUniformMatrix4fv(GLint location,
GLsizei count,
GLboolean transpose,
const GLfloat *value)
{
setUniformMatrixfv<4, 4>(location, count, transpose, value);
}
void ProgramMtl::setUniformMatrix2x3fv(GLint location,
GLsizei count,
GLboolean transpose,
const GLfloat *value)
{
setUniformMatrixfv<2, 3>(location, count, transpose, value);
}
void ProgramMtl::setUniformMatrix3x2fv(GLint location,
GLsizei count,
GLboolean transpose,
const GLfloat *value)
{
setUniformMatrixfv<3, 2>(location, count, transpose, value);
}
void ProgramMtl::setUniformMatrix2x4fv(GLint location,
GLsizei count,
GLboolean transpose,
const GLfloat *value)
{
setUniformMatrixfv<2, 4>(location, count, transpose, value);
}
void ProgramMtl::setUniformMatrix4x2fv(GLint location,
GLsizei count,
GLboolean transpose,
const GLfloat *value)
{
setUniformMatrixfv<4, 2>(location, count, transpose, value);
}
void ProgramMtl::setUniformMatrix3x4fv(GLint location,
GLsizei count,
GLboolean transpose,
const GLfloat *value)
{
setUniformMatrixfv<3, 4>(location, count, transpose, value);
}
void ProgramMtl::setUniformMatrix4x3fv(GLint location,
GLsizei count,
GLboolean transpose,
const GLfloat *value)
{
setUniformMatrixfv<4, 3>(location, count, transpose, value);
}
void ProgramMtl::getUniformfv(const gl::Context *context, GLint location, GLfloat *params) const
{
getUniformImpl(location, params, GL_FLOAT);
}
void ProgramMtl::getUniformiv(const gl::Context *context, GLint location, GLint *params) const
{
getUniformImpl(location, params, GL_INT);
}
void ProgramMtl::getUniformuiv(const gl::Context *context, GLint location, GLuint *params) const
{
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);
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 ProgramMtl::commitUniforms(ContextMtl *context, mtl::RenderCommandEncoder *cmdEncoder)
{
for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes)
{
if (!mDefaultUniformBlocksDirty[shaderType] || !mCurrentShaderVariants[shaderType])
{
continue;
}
DefaultUniformBlock &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 ProgramMtl::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 < 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.boundTextureUnits.size();
++arrayElement)
{
GLuint textureUnit = samplerBinding.boundTextureUnits[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;
}
// 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 ProgramMtl::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 ProgramMtl::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 ProgramMtl::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 ProgramMtl::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(mState.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