Metal: Implement Uniform buffers

Uniform buffer is implemented in two forms:
- If number of ubo used in shader program is low, each buffer will use
  one discrete Metal buffer slot.
- If number of ubo used is large, they will be embedded into one Metal
  argument buffer. Argument buffer is similar to Vulkan descriptor set.
  This is due to limit of number of Metal's discrete buffer slots which
  is only 31 and over half of them are already used by vertex
  attributes, default uniforms, driver uniforms, etc. The downside is
  that whenever a buffer binding is changed, the argument buffer must be
  updated also.

Added empty TransformFeedbackMtl implementation to enable ES3 context
creation on Metal.

Bug: angleproject:2634
Change-Id: I69325696fac735cb45ab88ab55468c0991abc317
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2408593
Commit-Queue: Le Hoang Quyen <le.hoang.q@gmail.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Jonah Ryan-Davis <jonahr@google.com>
This commit is contained in:
Le Hoang Quyen 2020-09-13 20:14:59 +08:00 коммит произвёл Commit Bot
Родитель 962e347302
Коммит 1677cf141d
17 изменённых файлов: 874 добавлений и 123 удалений

Просмотреть файл

@ -10,8 +10,8 @@ Metal and MacOS, Chrome OS, and Fuchsia support.
| | Direct3D 9 | Direct3D 11 | Desktop GL | GL ES | Vulkan | Metal |
|----------------|:-------------:|:----------------:|:--------------:|:-------------:|:-------------:|:-------------:|
| OpenGL ES 2.0 | complete | complete | complete | complete | complete | in progress |
| OpenGL ES 3.0 | | complete | complete | complete | complete | |
| OpenGL ES 2.0 | complete | complete | complete | complete | complete | complete |
| OpenGL ES 3.0 | | complete | complete | complete | complete | in progress |
| OpenGL ES 3.1 | | in progress | complete | complete | complete | |
| OpenGL ES 3.2 | | | in progress | in progress | in progress | |

Просмотреть файл

@ -35,6 +35,8 @@ _metal_backend_sources = [
"SurfaceMtl.mm",
"TextureMtl.h",
"TextureMtl.mm",
"TransformFeedbackMtl.h",
"TransformFeedbackMtl.mm",
"VertexArrayMtl.h",
"VertexArrayMtl.mm",
"mtl_buffer_pool.h",

Просмотреть файл

@ -63,6 +63,13 @@ struct IndexConversionBufferMtl : public ConversionBufferMtl
const size_t offset;
};
struct UniformConversionBufferMtl : public ConversionBufferMtl
{
UniformConversionBufferMtl(ContextMtl *context, size_t offsetIn);
const size_t offset;
};
class BufferHolderMtl
{
public:
@ -138,6 +145,8 @@ class BufferMtl : public BufferImpl, public BufferHolderMtl
gl::DrawElementsType elemType,
size_t offset);
ConversionBufferMtl *getUniformConversionBuffer(ContextMtl *context, size_t offset);
size_t size() const { return static_cast<size_t>(mState.getSize()); }
private:
@ -166,10 +175,12 @@ class BufferMtl : public BufferImpl, public BufferHolderMtl
// GPU side buffers pool
mtl::BufferPool mBufferPool;
// A cache of converted vertex data.
// A cache of converted buffer data.
std::vector<VertexConversionBufferMtl> mVertexConversionBuffers;
std::vector<IndexConversionBufferMtl> mIndexConversionBuffers;
std::vector<UniformConversionBufferMtl> mUniformConversionBuffers;
};
class SimpleWeakBufferHolderMtl : public BufferHolderMtl

Просмотреть файл

@ -63,6 +63,12 @@ IndexConversionBufferMtl::IndexConversionBufferMtl(ContextMtl *context,
offset(offsetIn)
{}
// UniformConversionBufferMtl implementation
UniformConversionBufferMtl::UniformConversionBufferMtl(ContextMtl *context, size_t offsetIn)
: ConversionBufferMtl(context, 0, mtl::kUniformBufferSettingOffsetMinAlignment),
offset(offsetIn)
{}
// VertexConversionBufferMtl implementation.
VertexConversionBufferMtl::VertexConversionBufferMtl(ContextMtl *context,
angle::FormatID formatIDIn,
@ -339,6 +345,20 @@ IndexConversionBufferMtl *BufferMtl::getIndexConversionBuffer(ContextMtl *contex
return &mIndexConversionBuffers.back();
}
ConversionBufferMtl *BufferMtl::getUniformConversionBuffer(ContextMtl *context, size_t offset)
{
for (UniformConversionBufferMtl &buffer : mUniformConversionBuffers)
{
if (buffer.offset == offset)
{
return &buffer;
}
}
mUniformConversionBuffers.emplace_back(context, offset);
return &mUniformConversionBuffers.back();
}
void BufferMtl::markConversionBuffersDirty()
{
for (VertexConversionBufferMtl &buffer : mVertexConversionBuffers)
@ -354,12 +374,20 @@ void BufferMtl::markConversionBuffersDirty()
buffer.convertedBuffer = nullptr;
buffer.convertedOffset = 0;
}
for (UniformConversionBufferMtl &buffer : mUniformConversionBuffers)
{
buffer.dirty = true;
buffer.convertedBuffer = nullptr;
buffer.convertedOffset = 0;
}
}
void BufferMtl::clearConversionBuffers()
{
mVertexConversionBuffers.clear();
mIndexConversionBuffers.clear();
mUniformConversionBuffers.clear();
}
angle::Result BufferMtl::setDataImpl(const gl::Context *context,

Просмотреть файл

@ -420,7 +420,7 @@ class ContextMtl : public ContextImpl, public mtl::Context
angle::Result handleDirtyDepthBias(const gl::Context *context);
angle::Result checkIfPipelineChanged(const gl::Context *context,
gl::PrimitiveMode primitiveMode,
Optional<mtl::RenderPipelineDesc> *changedPipelineDesc);
bool *isPipelineDescChanged);
angle::Result startOcclusionQueryInRenderPass(QueryMtl *query, bool clearOldValue);
@ -440,6 +440,7 @@ class ContextMtl : public ContextImpl, public mtl::Context
DIRTY_BIT_CULL_MODE,
DIRTY_BIT_WINDING,
DIRTY_BIT_RENDER_PIPELINE,
DIRTY_BIT_UNIFORM_BUFFERS_BINDING,
DIRTY_BIT_MAX,
};

Просмотреть файл

@ -22,6 +22,7 @@
#include "libANGLE/renderer/metal/RenderTargetMtl.h"
#include "libANGLE/renderer/metal/ShaderMtl.h"
#include "libANGLE/renderer/metal/TextureMtl.h"
#include "libANGLE/renderer/metal/TransformFeedbackMtl.h"
#include "libANGLE/renderer/metal/VertexArrayMtl.h"
#include "libANGLE/renderer/metal/mtl_command_buffer.h"
#include "libANGLE/renderer/metal/mtl_format_utils.h"
@ -795,7 +796,7 @@ angle::Result ContextMtl::syncState(const gl::Context *context,
// NOTE(hqle): ES 3.0 feature.
break;
case gl::State::DIRTY_BIT_UNIFORM_BUFFER_BINDINGS:
// NOTE(hqle): ES 3.0 feature.
mDirtyBits.set(DIRTY_BIT_UNIFORM_BUFFERS_BINDING);
break;
case gl::State::DIRTY_BIT_ATOMIC_COUNTER_BUFFER_BINDING:
break;
@ -946,8 +947,7 @@ SyncImpl *ContextMtl::createSync()
TransformFeedbackImpl *ContextMtl::createTransformFeedback(const gl::TransformFeedbackState &state)
{
// NOTE(hqle): ES 3.0
UNIMPLEMENTED();
return nullptr;
return new TransformFeedbackMtl(state);
}
// Sampler object creation
@ -1644,8 +1644,10 @@ angle::Result ContextMtl::setupDraw(const gl::Context *context,
ANGLE_TRY(startOcclusionQueryInRenderPass(mOcclusionQuery, false));
}
Optional<mtl::RenderPipelineDesc> changedPipelineDesc;
ANGLE_TRY(checkIfPipelineChanged(context, mode, &changedPipelineDesc));
bool isPipelineDescChanged;
ANGLE_TRY(checkIfPipelineChanged(context, mode, &isPipelineDescChanged));
bool uniformBuffersDirty = false;
for (size_t bit : mDirtyBits)
{
@ -1692,6 +1694,9 @@ angle::Result ContextMtl::setupDraw(const gl::Context *context,
case DIRTY_BIT_RENDER_PIPELINE:
// Already handled. See checkIfPipelineChanged().
break;
case DIRTY_BIT_UNIFORM_BUFFERS_BINDING:
uniformBuffersDirty = true;
break;
default:
UNREACHABLE();
break;
@ -1700,7 +1705,8 @@ angle::Result ContextMtl::setupDraw(const gl::Context *context,
mDirtyBits.reset();
ANGLE_TRY(mProgram->setupDraw(context, &mRenderEncoder, changedPipelineDesc, textureChanged));
ANGLE_TRY(mProgram->setupDraw(context, &mRenderEncoder, mRenderPipelineDesc,
isPipelineDescChanged, textureChanged, uniformBuffersDirty));
if (mode == gl::PrimitiveMode::LineLoop)
{
@ -1892,10 +1898,9 @@ angle::Result ContextMtl::handleDirtyDepthBias(const gl::Context *context)
return angle::Result::Continue;
}
angle::Result ContextMtl::checkIfPipelineChanged(
const gl::Context *context,
gl::PrimitiveMode primitiveMode,
Optional<mtl::RenderPipelineDesc> *changedPipelineDesc)
angle::Result ContextMtl::checkIfPipelineChanged(const gl::Context *context,
gl::PrimitiveMode primitiveMode,
bool *isPipelineDescChanged)
{
ASSERT(mRenderEncoder.valid());
mtl::PrimitiveTopologyClass topologyClass = mtl::GetPrimitiveTopologyClass(primitiveMode);
@ -1920,10 +1925,10 @@ angle::Result ContextMtl::checkIfPipelineChanged(
mRenderPipelineDesc.outputDescriptor.updateEnabledDrawBuffers(
mDrawFramebuffer->getState().getEnabledDrawBuffers());
*changedPipelineDesc = mRenderPipelineDesc;
}
*isPipelineDescChanged = rppChange;
return angle::Result::Continue;
}
}

Просмотреть файл

@ -521,21 +521,20 @@ void DisplayMtl::ensureCapsInitialized() const
mNativeCaps.fragmentMediumpInt.setTwosComplementInt(32);
mNativeCaps.fragmentLowpInt.setTwosComplementInt(32);
GLuint maxUniformVectors = mtl::kDefaultUniformsMaxSize / (sizeof(GLfloat) * 4);
GLuint maxDefaultUniformVectors = mtl::kDefaultUniformsMaxSize / (sizeof(GLfloat) * 4);
const GLuint maxUniformComponents = maxUniformVectors * 4;
const GLuint maxDefaultUniformComponents = maxDefaultUniformVectors * 4;
// Uniforms are implemented using a uniform buffer, so the max number of uniforms we can
// support is the max buffer range divided by the size of a single uniform (4X float).
mNativeCaps.maxVertexUniformVectors = maxUniformVectors;
mNativeCaps.maxShaderUniformComponents[gl::ShaderType::Vertex] = maxUniformComponents;
mNativeCaps.maxFragmentUniformVectors = maxUniformVectors;
mNativeCaps.maxShaderUniformComponents[gl::ShaderType::Fragment] = maxUniformComponents;
mNativeCaps.maxVertexUniformVectors = maxDefaultUniformVectors;
mNativeCaps.maxShaderUniformComponents[gl::ShaderType::Vertex] = maxDefaultUniformComponents;
mNativeCaps.maxFragmentUniformVectors = maxDefaultUniformVectors;
mNativeCaps.maxShaderUniformComponents[gl::ShaderType::Fragment] = maxDefaultUniformComponents;
// NOTE(hqle): support UBO (ES 3.0 feature)
mNativeCaps.maxShaderUniformBlocks[gl::ShaderType::Vertex] = 0;
mNativeCaps.maxShaderUniformBlocks[gl::ShaderType::Fragment] = 0;
mNativeCaps.maxCombinedUniformBlocks = 0;
mNativeCaps.maxShaderUniformBlocks[gl::ShaderType::Vertex] = mtl::kMaxShaderUBOs;
mNativeCaps.maxShaderUniformBlocks[gl::ShaderType::Fragment] = mtl::kMaxShaderUBOs;
mNativeCaps.maxCombinedUniformBlocks = mtl::kMaxGLUBOBindings;
// Note that we currently implement textures as combined image+samplers, so the limit is
// the minimum of supported samplers and sampled images.
@ -550,18 +549,20 @@ void DisplayMtl::ensureCapsInitialized() const
mNativeCaps.maxCombinedShaderStorageBlocks = maxPerStageStorageBuffers;
// Fill in additional limits for UBOs and SSBOs.
mNativeCaps.maxUniformBufferBindings = 0;
mNativeCaps.maxUniformBlockSize = 0;
mNativeCaps.uniformBufferOffsetAlignment = 0;
mNativeCaps.maxUniformBufferBindings = mNativeCaps.maxCombinedUniformBlocks;
mNativeCaps.maxUniformBlockSize = mtl::kMaxUBOSize; // Default according to GLES 3.0 spec.
mNativeCaps.uniformBufferOffsetAlignment = 1;
mNativeCaps.maxShaderStorageBufferBindings = 0;
mNativeCaps.maxShaderStorageBlockSize = 0;
mNativeCaps.shaderStorageBufferOffsetAlignment = 0;
// NOTE(hqle): support UBO
const uint32_t maxCombinedUniformComponents =
maxDefaultUniformComponents + mtl::kMaxUBOSize * mtl::kMaxShaderUBOs / 4;
for (gl::ShaderType shaderType : gl::kAllGraphicsShaderTypes)
{
mNativeCaps.maxCombinedShaderUniformComponents[shaderType] = maxUniformComponents;
mNativeCaps.maxCombinedShaderUniformComponents[shaderType] = maxCombinedUniformComponents;
}
mNativeCaps.maxCombinedShaderOutputResources = 0;

Просмотреть файл

@ -18,6 +18,7 @@
#include "common/utilities.h"
#include "libANGLE/renderer/ProgramImpl.h"
#include "libANGLE/renderer/glslang_wrapper_utils.h"
#include "libANGLE/renderer/metal/mtl_buffer_pool.h"
#include "libANGLE/renderer/metal/mtl_command_buffer.h"
#include "libANGLE/renderer/metal/mtl_glslang_utils.h"
#include "libANGLE/renderer/metal/mtl_resources.h"
@ -27,15 +28,28 @@ namespace rx
{
class ContextMtl;
struct ProgramArgumentBufferEncoderMtl
{
void reset(ContextMtl *contextMtl);
mtl::AutoObjCPtr<id<MTLArgumentEncoder>> metalArgBufferEncoder;
mtl::BufferPool bufferPool;
};
// Represents a specialized shader variant. For example, a shader variant with fragment coverage
// mask enabled and a shader variant without.
struct ProgramShaderVariantMtl
struct ProgramShaderObjVariantMtl
{
void reset(ContextMtl *contextMtl);
mtl::AutoObjCPtr<id<MTLFunction>> metalShader;
// NOTE(hqle): might need additional info such as uniform buffer encoder, fragment coverage mask
// enabled or not, etc.
// UBO's argument buffer encoder. Used when number of UBOs used exceeds number of allowed
// discrete slots, and thus needs to encode all into one argument buffer.
ProgramArgumentBufferEncoderMtl uboArgBufferEncoder;
// Store reference to the TranslatedShaderInfo to easy querying mapped textures/UBO/XFB
// bindings.
const mtl::TranslatedShaderInfo *translatedSrcInfo;
};
class ProgramMtl : public ProgramImpl, public mtl::RenderPipelineCacheSpecializeShaderFactory
@ -124,8 +138,10 @@ class ProgramMtl : public ProgramImpl, public mtl::RenderPipelineCacheSpecialize
// shader program changed.
angle::Result setupDraw(const gl::Context *glContext,
mtl::RenderCommandEncoder *cmdEncoder,
const Optional<mtl::RenderPipelineDesc> &changedPipelineDesc,
bool forceTexturesSetting);
const mtl::RenderPipelineDesc &pipelineDesc,
bool pipelineDescChanged,
bool forceTexturesSetting,
bool uniformBuffersDirty);
private:
template <int cols, int rows>
@ -146,16 +162,32 @@ class ProgramMtl : public ProgramImpl, public mtl::RenderPipelineCacheSpecialize
mtl::RenderCommandEncoder *cmdEncoder,
bool forceUpdate);
angle::Result updateUniformBuffers(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);
angle::Result linkImpl(const gl::Context *glContext,
const gl::ProgramLinkedResources &resources,
gl::InfoLog &infoLog);
angle::Result createMslShaderLib(const gl::Context *glContext,
angle::Result createMslShaderLib(mtl::Context *context,
gl::ShaderType shaderType,
gl::InfoLog &infoLog,
const std::string &translatedSource);
mtl::TranslatedShaderInfo *translatedMslInfo);
// State for the default uniform blocks.
struct DefaultUniformBlock final : private angle::NonCopyable
@ -175,16 +207,25 @@ class ProgramMtl : public ProgramImpl, public mtl::RenderPipelineCacheSpecialize
gl::ShaderBitSet mSamplerBindingsDirty;
gl::ShaderMap<DefaultUniformBlock> mDefaultUniformBlocks;
gl::ShaderMap<std::string> mTranslatedMslShader;
// Translated metal shaders:
gl::ShaderMap<mtl::TranslatedShaderInfo> mMslShaderTranslateInfo;
gl::ShaderMap<mtl::AutoObjCPtr<id<MTLLibrary>>> mMslShaderLibrary;
// Shader variants:
// - Vertex shader: One variant for now.
std::array<ProgramShaderVariantMtl, 1> mVertexShaderVariants;
// Compiled native shader object variants:
// - Vertex shader: one variant for now.
std::array<ProgramShaderObjVariantMtl, 1> mVertexShaderVariants;
// - Fragment shader: One with sample coverage mask enabled, one with it disabled.
std::array<ProgramShaderVariantMtl, 2> mFragmentShaderVariants;
std::array<ProgramShaderObjVariantMtl, 2> mFragmentShaderVariants;
// Cached references of current shader variants.
gl::ShaderMap<ProgramShaderObjVariantMtl *> mCurrentShaderVariants;
// 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;
mtl::RenderPipelineCache mMetalRenderPipelineCache;
};

Просмотреть файл

@ -16,6 +16,7 @@
#include "common/debug.h"
#include "libANGLE/Context.h"
#include "libANGLE/ProgramLinkedResources.h"
#include "libANGLE/renderer/metal/BufferMtl.h"
#include "libANGLE/renderer/metal/ContextMtl.h"
#include "libANGLE/renderer/metal/DisplayMtl.h"
#include "libANGLE/renderer/metal/TextureMtl.h"
@ -32,6 +33,35 @@ namespace
#define SHADER_ENTRY_NAME @"main0"
constexpr char kSpirvCrossSpecConstSuffix[] = "_tmp";
template <typename T>
class ScopedAutoClearVector
{
public:
ScopedAutoClearVector(std::vector<T> *array) : mArray(*array) {}
~ScopedAutoClearVector() { mArray.clear(); }
private:
std::vector<T> &mArray;
};
angle::Result StreamUniformBufferData(ContextMtl *contextMtl,
mtl::BufferPool *dynamicBuffer,
const uint8_t *sourceData,
size_t bytesToAllocate,
size_t sizeToCopy,
mtl::BufferRef *bufferOut,
size_t *bufferOffsetOut)
{
uint8_t *dst = nullptr;
dynamicBuffer->releaseInFlightBuffers(contextMtl);
ANGLE_TRY(dynamicBuffer->allocate(contextMtl, bytesToAllocate, &dst, bufferOut, bufferOffsetOut,
nullptr));
memcpy(dst, sourceData, sizeToCopy);
ANGLE_TRY(dynamicBuffer->commit(contextMtl));
return angle::Result::Continue;
}
void InitDefaultUniformBlock(const std::vector<sh::Uniform> &uniforms,
gl::Shader *shader,
sh::BlockLayoutMap *blockLayoutMapOut,
@ -125,6 +155,19 @@ class Std140BlockLayoutEncoderFactory : public gl::CustomBlockLayoutEncoderFacto
sh::BlockLayoutEncoder *makeEncoder() override { return new sh::Std140BlockEncoder(); }
};
void InitArgumentBufferEncoder(mtl::Context *context,
id<MTLFunction> function,
uint32_t bufferIndex,
ProgramArgumentBufferEncoderMtl *encoder)
{
encoder->metalArgBufferEncoder = [function newArgumentEncoderWithBufferIndex:bufferIndex];
if (encoder->metalArgBufferEncoder)
{
encoder->bufferPool.initialize(context, encoder->metalArgBufferEncoder.get().encodedLength,
mtl::kArgumentBufferOffsetAlignment, 0);
}
}
angle::Result CreateMslShader(mtl::Context *context,
id<MTLLibrary> shaderLib,
MTLFunctionConstantValues *funcConstants,
@ -133,7 +176,6 @@ angle::Result CreateMslShader(mtl::Context *context,
NSError *nsErr = nil;
id<MTLFunction> mtlShader;
if (funcConstants)
{
mtlShader = [shaderLib newFunctionWithName:SHADER_ENTRY_NAME
@ -164,10 +206,21 @@ angle::Result CreateMslShader(mtl::Context *context,
} // namespace
// ProgramShaderVariantMtl implementation
void ProgramShaderVariantMtl::reset(ContextMtl *contextMtl)
// 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
@ -197,20 +250,14 @@ void ProgramMtl::reset(ContextMtl *context)
for (gl::ShaderType shaderType : gl::AllShaderTypes())
{
mMslShaderLibrary[shaderType] = nil;
for (mtl::SamplerBinding &binding :
mMslShaderTranslateInfo[shaderType].actualSamplerBindings)
{
binding.textureBinding = mtl::kMaxShaderSamplers;
}
mMslShaderTranslateInfo[shaderType].reset();
}
for (ProgramShaderVariantMtl &var : mVertexShaderVariants)
for (ProgramShaderObjVariantMtl &var : mVertexShaderVariants)
{
var.reset(context);
}
for (ProgramShaderVariantMtl &var : mFragmentShaderVariants)
for (ProgramShaderObjVariantMtl &var : mFragmentShaderVariants)
{
var.reset(context);
}
@ -279,14 +326,13 @@ angle::Result ProgramMtl::linkImpl(const gl::Context *glContext,
shaderSources, variableInfoMap, &shaderCodes));
// Convert spirv code to MSL
ANGLE_TRY(mtl::SpirvCodeToMsl(contextMtl, mState, &shaderCodes, &mMslShaderTranslateInfo,
&mTranslatedMslShader));
ANGLE_TRY(mtl::SpirvCodeToMsl(contextMtl, mState, &shaderCodes, &mMslShaderTranslateInfo));
for (gl::ShaderType shaderType : gl::AllGLES2ShaderTypes())
{
// Create actual Metal shader
ANGLE_TRY(
createMslShaderLib(glContext, shaderType, infoLog, mTranslatedMslShader[shaderType]));
ANGLE_TRY(createMslShaderLib(contextMtl, shaderType, infoLog,
&mMslShaderTranslateInfo[shaderType]));
}
return angle::Result::Continue;
@ -390,37 +436,32 @@ angle::Result ProgramMtl::getSpecializedShader(mtl::Context *context,
{
static_assert(YES == 1, "YES should have value of 1");
mtl::AutoObjCPtr<id<MTLLibrary>> mtlShaderLib = mMslShaderLibrary[shaderType];
mtl::TranslatedShaderInfo *translatedMslInfo = &mMslShaderTranslateInfo[shaderType];
ProgramShaderObjVariantMtl *shaderVariant;
MTLFunctionConstantValues *funcConstants = nil;
if (shaderType == gl::ShaderType::Vertex)
{
// NOTE(hqle): Only one vertex shader variant for now. In future, there should be a variant
// with rasterization discard enabled.
ProgramShaderVariantMtl &shaderVariant = mVertexShaderVariants[0];
if (shaderVariant.metalShader)
shaderVariant = &mVertexShaderVariants[0];
if (shaderVariant->metalShader)
{
// Already created.
*shaderOut = shaderVariant.metalShader;
*shaderOut = shaderVariant->metalShader;
return angle::Result::Continue;
}
ANGLE_MTL_OBJC_SCOPE
{
ANGLE_TRY(CreateMslShader(context, mtlShaderLib, nil, &shaderVariant.metalShader));
}
*shaderOut = shaderVariant.metalShader;
}
else if (shaderType == gl::ShaderType::Fragment)
{
// For fragment shader, we need to create 2 variants, one with sample coverage mask
// disabled, one with the mask enabled.
BOOL emulateCoverageMask = renderPipelineDesc.emulateCoverageMask;
ProgramShaderVariantMtl &shaderVariant = mFragmentShaderVariants[emulateCoverageMask];
if (shaderVariant.metalShader)
BOOL emulateCoverageMask = renderPipelineDesc.emulateCoverageMask;
shaderVariant = &mFragmentShaderVariants[emulateCoverageMask];
if (shaderVariant->metalShader)
{
// Already created.
*shaderOut = shaderVariant.metalShader;
*shaderOut = shaderVariant->metalShader;
return angle::Result::Continue;
}
@ -430,17 +471,40 @@ angle::Result ProgramMtl::getSpecializedShader(mtl::Context *context,
[NSString stringWithFormat:@"%s%s", sh::mtl::kCoverageMaskEnabledConstName,
kSpirvCrossSpecConstSuffix];
auto funcConstants = [[[MTLFunctionConstantValues alloc] init] ANGLE_MTL_AUTORELEASE];
funcConstants = [[MTLFunctionConstantValues alloc] init];
[funcConstants setConstantValue:&emulateCoverageMask
type:MTLDataTypeBool
withName:coverageMaskEnabledStr];
ANGLE_TRY(
CreateMslShader(context, mtlShaderLib, funcConstants, &shaderVariant.metalShader));
}
*shaderOut = shaderVariant.metalShader;
} // gl::ShaderType::Fragment
else
{
UNREACHABLE();
return angle::Result::Stop;
}
// Create Metal shader object
ANGLE_MTL_OBJC_SCOPE
{
[funcConstants ANGLE_MTL_AUTORELEASE];
ANGLE_TRY(CreateMslShader(context, translatedMslInfo->metalLibrary, funcConstants,
&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;
}
bool ProgramMtl::hasSpecializedShader(gl::ShaderType shaderType,
@ -449,21 +513,21 @@ bool ProgramMtl::hasSpecializedShader(gl::ShaderType shaderType,
return true;
}
angle::Result ProgramMtl::createMslShaderLib(const gl::Context *glContext,
angle::Result ProgramMtl::createMslShaderLib(mtl::Context *context,
gl::ShaderType shaderType,
gl::InfoLog &infoLog,
const std::string &translatedMsl)
mtl::TranslatedShaderInfo *translatedMslInfo)
{
ANGLE_MTL_OBJC_SCOPE
{
ContextMtl *contextMtl = mtl::GetImpl(glContext);
DisplayMtl *display = contextMtl->getDisplay();
DisplayMtl *display = context->getDisplay();
id<MTLDevice> mtlDevice = display->getMetalDevice();
// Convert to actual binary shader
mtl::AutoObjCPtr<NSError *> err = nil;
mMslShaderLibrary[shaderType] = mtl::CreateShaderLibrary(mtlDevice, translatedMsl, &err);
if (err && !mMslShaderLibrary[shaderType])
translatedMslInfo->metalLibrary =
mtl::CreateShaderLibrary(mtlDevice, translatedMslInfo->metalShaderSource, &err);
if (err && !translatedMslInfo->metalLibrary)
{
std::ostringstream ss;
ss << "Internal error compiling Metal shader:\n"
@ -473,7 +537,7 @@ angle::Result ProgramMtl::createMslShaderLib(const gl::Context *glContext,
infoLog << ss.str();
ANGLE_MTL_CHECK(contextMtl, false, GL_INVALID_OPERATION);
ANGLE_MTL_CHECK(context, false, GL_INVALID_OPERATION);
}
return angle::Result::Continue;
@ -762,16 +826,17 @@ void ProgramMtl::getUniformuiv(const gl::Context *context, GLint location, GLuin
angle::Result ProgramMtl::setupDraw(const gl::Context *glContext,
mtl::RenderCommandEncoder *cmdEncoder,
const Optional<mtl::RenderPipelineDesc> &changedPipelineDescOpt,
bool forceTexturesSetting)
const mtl::RenderPipelineDesc &pipelineDesc,
bool pipelineDescChanged,
bool forceTexturesSetting,
bool uniformBuffersDirty)
{
ContextMtl *context = mtl::GetImpl(glContext);
if (changedPipelineDescOpt.valid())
if (pipelineDescChanged)
{
const auto &changedPipelineDesc = changedPipelineDescOpt.value();
// Render pipeline state needs to be changed
id<MTLRenderPipelineState> pipelineState =
mMetalRenderPipelineCache.getRenderPipelineState(context, changedPipelineDesc);
mMetalRenderPipelineCache.getRenderPipelineState(context, pipelineDesc);
if (!pipelineState)
{
// Error already logged inside getRenderPipelineState()
@ -782,11 +847,21 @@ angle::Result ProgramMtl::setupDraw(const gl::Context *glContext,
// 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[0];
mCurrentShaderVariants[gl::ShaderType::Fragment] =
&mFragmentShaderVariants[pipelineDesc.emulateCoverageMask];
}
ANGLE_TRY(commitUniforms(context, cmdEncoder));
ANGLE_TRY(updateTextures(glContext, cmdEncoder, forceTexturesSetting));
if (uniformBuffersDirty || pipelineDescChanged)
{
ANGLE_TRY(updateUniformBuffers(context, cmdEncoder, pipelineDesc));
}
return angle::Result::Continue;
}
@ -794,7 +869,7 @@ angle::Result ProgramMtl::commitUniforms(ContextMtl *context, mtl::RenderCommand
{
for (gl::ShaderType shaderType : gl::AllGLES2ShaderTypes())
{
if (!mDefaultUniformBlocksDirty[shaderType])
if (!mDefaultUniformBlocksDirty[shaderType] || !mCurrentShaderVariants[shaderType])
{
continue;
}
@ -824,11 +899,15 @@ angle::Result ProgramMtl::updateTextures(const gl::Context *glContext,
for (gl::ShaderType shaderType : gl::AllGLES2ShaderTypes())
{
if (!mSamplerBindingsDirty[shaderType] && !forceUpdate)
if ((!mSamplerBindingsDirty[shaderType] && !forceUpdate) ||
!mCurrentShaderVariants[shaderType])
{
continue;
}
const mtl::TranslatedShaderInfo &shaderInfo =
*mCurrentShaderVariants[shaderType]->translatedSrcInfo;
for (uint32_t textureIndex = 0; textureIndex < mState.getSamplerBindings().size();
++textureIndex)
{
@ -836,8 +915,7 @@ angle::Result ProgramMtl::updateTextures(const gl::Context *glContext,
ASSERT(!samplerBinding.unreferenced);
const mtl::SamplerBinding &mslBinding =
mMslShaderTranslateInfo[shaderType].actualSamplerBindings[textureIndex];
const mtl::SamplerBinding &mslBinding = shaderInfo.actualSamplerBindings[textureIndex];
if (mslBinding.textureBinding >= mtl::kMaxShaderSamplers)
{
// No binding assigned
@ -869,4 +947,226 @@ angle::Result ProgramMtl::updateTextures(const gl::Context *glContext,
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::AllGLES2ShaderTypes())
{
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());
size_t offsetModulo = srcOffset % mtl::kUniformBufferSettingOffsetMinAlignment;
if (offsetModulo)
{
ConversionBufferMtl *conversion =
bufferMtl->getUniformConversionBuffer(context, offsetModulo);
// Has the content of the buffer has changed since last conversion?
if (conversion->dirty)
{
const uint8_t *srcBytes = bufferMtl->getClientShadowCopyData(context);
srcBytes += offsetModulo;
size_t sizeToCopy = bufferMtl->size() - offsetModulo;
size_t bytesToAllocate = roundUp<size_t>(sizeToCopy, 16u);
ANGLE_TRY(StreamUniformBufferData(
context, &conversion->data, srcBytes, bytesToAllocate, sizeToCopy,
&conversion->convertedBuffer, &conversion->convertedOffset));
#ifndef NDEBUG
ANGLE_MTL_OBJC_SCOPE
{
conversion->convertedBuffer->get().label = [NSString
stringWithFormat:@"Converted from %p offset=%zu", bufferMtl, offsetModulo];
}
#endif
conversion->dirty = false;
}
// reuse the converted buffer
mLegalizedOffsetedUniformBuffers[bufferIndex].first = conversion->convertedBuffer;
mLegalizedOffsetedUniformBuffers[bufferIndex].second =
static_cast<uint32_t>(conversion->convertedOffset + srcOffset - offsetModulo);
}
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();
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));
[bufferEncoder.metalArgBufferEncoder setArgumentBuffer:argumentBuffer->get()
offset:argumentBufferOffset];
static_assert(MTLRenderStageVertex == (0x1 << static_cast<uint32_t>(gl::ShaderType::Vertex)),
"Expected gl ShaderType enum and Metal enum to relative to each other");
static_assert(
MTLRenderStageFragment == (0x1 << static_cast<uint32_t>(gl::ShaderType::Fragment)),
"Expected gl ShaderType enum and Metal enum to relative to each other");
auto mtlRenderStage = static_cast<MTLRenderStages>(0x1 << static_cast<uint32_t>(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];
}
ANGLE_TRY(bufferEncoder.bufferPool.commit(context));
cmdEncoder->setBuffer(shaderType, argumentBuffer, static_cast<uint32_t>(argumentBufferOffset),
mtl::kUBOArgumentBufferBindingIndex);
return angle::Result::Continue;
}
} // namespace rx

Просмотреть файл

@ -0,0 +1,43 @@
//
// Copyright 2020 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.
//
// TransformFeedbackMtl.h:
// Defines the class interface for TransformFeedbackMtl, implementing TransformFeedbackImpl.
//
#ifndef LIBANGLE_RENDERER_METAL_TRANSFORMFEEDBACKMTL_H_
#define LIBANGLE_RENDERER_METAL_TRANSFORMFEEDBACKMTL_H_
#include "libANGLE/renderer/TransformFeedbackImpl.h"
namespace gl
{
class ProgramState;
} // namespace gl
namespace rx
{
class TransformFeedbackMtl : public TransformFeedbackImpl
{
public:
TransformFeedbackMtl(const gl::TransformFeedbackState &state);
~TransformFeedbackMtl() override;
angle::Result begin(const gl::Context *context, gl::PrimitiveMode primitiveMode) override;
angle::Result end(const gl::Context *context) override;
angle::Result pause(const gl::Context *context) override;
angle::Result resume(const gl::Context *context) override;
angle::Result bindIndexedBuffer(const gl::Context *context,
size_t index,
const gl::OffsetBindingPointer<gl::Buffer> &binding) override;
private:
};
} // namespace rx
#endif // LIBANGLE_RENDERER_METAL_TRANSFORMFEEDBACKMTL_H_

Просмотреть файл

@ -0,0 +1,63 @@
//
// Copyright 2020 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.
//
// TransformFeedbackMtl.mm:
// Defines the class interface for TransformFeedbackMtl, implementing TransformFeedbackImpl.
//
#include "libANGLE/renderer/metal/TransformFeedbackMtl.h"
#include "libANGLE/Context.h"
#include "libANGLE/Query.h"
#include "common/debug.h"
namespace rx
{
TransformFeedbackMtl::TransformFeedbackMtl(const gl::TransformFeedbackState &state)
: TransformFeedbackImpl(state)
{}
TransformFeedbackMtl::~TransformFeedbackMtl() {}
angle::Result TransformFeedbackMtl::begin(const gl::Context *context,
gl::PrimitiveMode primitiveMode)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
angle::Result TransformFeedbackMtl::end(const gl::Context *context)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
angle::Result TransformFeedbackMtl::pause(const gl::Context *context)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
angle::Result TransformFeedbackMtl::resume(const gl::Context *context)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
angle::Result TransformFeedbackMtl::bindIndexedBuffer(
const gl::Context *context,
size_t index,
const gl::OffsetBindingPointer<gl::Buffer> &binding)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
} // namespace rx

Просмотреть файл

@ -418,6 +418,10 @@ class RenderCommandEncoder final : public CommandEncoder
RenderCommandEncoder &setVisibilityResultMode(MTLVisibilityResultMode mode, size_t offset);
RenderCommandEncoder &useResource(const BufferRef &resource,
MTLResourceUsage usage,
mtl::RenderStages states);
RenderCommandEncoder &setColorStoreAction(MTLStoreAction action, uint32_t colorAttachmentIndex);
// Set store action for every color attachment.
RenderCommandEncoder &setColorStoreAction(MTLStoreAction action);

Просмотреть файл

@ -62,7 +62,8 @@ namespace
PROC(DrawIndexed) \
PROC(DrawIndexedInstanced) \
PROC(DrawIndexedInstancedBaseVertex) \
PROC(SetVisibilityResultMode)
PROC(SetVisibilityResultMode) \
PROC(UseResource)
#define ANGLE_MTL_TYPE_DECL(CMD) CMD,
@ -325,6 +326,25 @@ void SetVisibilityResultModeCmd(id<MTLRenderCommandEncoder> encoder,
[encoder setVisibilityResultMode:mode offset:offset];
}
void UseResourceCmd(id<MTLRenderCommandEncoder> encoder, IntermediateCommandStream *stream)
{
id<MTLResource> resource = stream->fetch<id<MTLResource>>();
MTLResourceUsage usage = stream->fetch<MTLResourceUsage>();
mtl::RenderStages stages = stream->fetch<mtl::RenderStages>();
ANGLE_UNUSED_VARIABLE(stages);
#if defined(__IPHONE_13_0) || defined(__MAC_10_15)
if (ANGLE_APPLE_AVAILABLE_XCI(10.15, 13.0, 13.0))
{
[encoder useResource:resource usage:usage stages:stages];
}
else
#endif
{
[encoder useResource:resource usage:usage];
}
[resource ANGLE_MTL_RELEASE];
}
// Command encoder mapping
#define ANGLE_MTL_CMD_MAP(CMD) CMD##Cmd,
@ -1422,6 +1442,25 @@ RenderCommandEncoder &RenderCommandEncoder::setVisibilityResultMode(MTLVisibilit
return *this;
}
RenderCommandEncoder &RenderCommandEncoder::useResource(const BufferRef &resource,
MTLResourceUsage usage,
mtl::RenderStages states)
{
if (!resource)
{
return *this;
}
cmdBuffer().setReadDependency(resource);
mCommands.push(CmdType::UseResource)
.push([resource->get() ANGLE_MTL_RETAIN])
.push(usage)
.push(states);
return *this;
}
RenderCommandEncoder &RenderCommandEncoder::setColorStoreAction(MTLStoreAction action,
uint32_t colorAttachmentIndex)
{

Просмотреть файл

@ -99,6 +99,9 @@ constexpr uint32_t kMaxVertexAttribs = gl::MAX_VERTEX_ATTRIBS;
// NOTE(hqle): support variable max number of render targets
constexpr uint32_t kMaxRenderTargets = 4;
constexpr uint32_t kMaxShaderUBOs = 12;
constexpr uint32_t kMaxUBOSize = 16384;
// The max size of a buffer that will be allocated in shared memory.
// NOTE(hqle): This is just a hint. There is no official document on what is the max allowed size
// for shared memory.
@ -120,10 +123,12 @@ constexpr uint32_t kUniformBufferSettingOffsetMinAlignment = 256;
constexpr uint32_t kUniformBufferSettingOffsetMinAlignment = 4;
#endif
constexpr uint32_t kIndexBufferOffsetAlignment = 4;
constexpr uint32_t kArgumentBufferOffsetAlignment = kUniformBufferSettingOffsetMinAlignment;
constexpr uint32_t kTextureToBufferBlittingAlignment = 256;
// Front end binding limits
constexpr uint32_t kMaxGLSamplerBindings = 2 * kMaxShaderSamplers;
constexpr uint32_t kMaxGLUBOBindings = 2 * kMaxShaderUBOs;
// Binding index start for vertex data buffers:
constexpr uint32_t kVboBindingIndexStart = 0;
@ -134,6 +139,8 @@ constexpr uint32_t kDefaultAttribsBindingIndex = kVboBindingIndexStart + kMaxVer
constexpr uint32_t kDriverUniformsBindingIndex = kDefaultAttribsBindingIndex + 1;
// Binding index for default uniforms:
constexpr uint32_t kDefaultUniformsBindingIndex = kDefaultAttribsBindingIndex + 3;
// Binding index for UBO's argument buffer or starting discrete slot
constexpr uint32_t kUBOArgumentBufferBindingIndex = kDefaultUniformsBindingIndex + 1;
constexpr uint32_t kStencilMaskAll = 0xff; // Only 8 bits stencil is supported
@ -146,8 +153,7 @@ constexpr float kEmulatedAlphaValue = 1.0f;
constexpr size_t kOcclusionQueryResultSize = sizeof(uint64_t);
// NOTE(hqle): Support ES 3.0.
constexpr gl::Version kMaxSupportedGLVersion = gl::Version(2, 0);
constexpr gl::Version kMaxSupportedGLVersion = gl::Version(3, 0);
// Work-around the enum is not available on macOS
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
@ -156,6 +162,16 @@ constexpr MTLBlitOption kBlitOptionRowLinearPVRTC = MTLBlitOptionNone;
constexpr MTLBlitOption kBlitOptionRowLinearPVRTC = MTLBlitOptionRowLinearPVRTC;
#endif
#if defined(__IPHONE_13_0) || defined(__MAC_10_15)
using RenderStages = MTLRenderStages;
constexpr MTLRenderStages kRenderStageVertex = MTLRenderStageVertex;
constexpr MTLRenderStages kRenderStageFragment = MTLRenderStageFragment;
#else
using RenderStages = int;
constexpr RenderStages kRenderStageVertex = 1;
constexpr RenderStages kRenderStageFragment = 2;
#endif
enum class PixelType
{
Int,

Просмотреть файл

@ -28,8 +28,16 @@ struct SamplerBinding
struct TranslatedShaderInfo
{
void reset();
// Translated Metal source code
std::string metalShaderSource;
// Metal library compiled from source code above. Used by ProgramMtl.
AutoObjCPtr<id<MTLLibrary>> metalLibrary;
std::array<SamplerBinding, kMaxGLSamplerBindings> actualSamplerBindings;
// NOTE(hqle): UBO, XFB bindings.
std::array<uint32_t, kMaxGLUBOBindings> actualUBOBindings;
bool hasUBOArgumentBuffer;
};
void GlslangGetShaderSource(const gl::ProgramState &programState,
@ -48,8 +56,7 @@ angle::Result GlslangGetShaderSpirvCode(ErrorHandler *context,
angle::Result SpirvCodeToMsl(Context *context,
const gl::ProgramState &programState,
gl::ShaderMap<std::vector<uint32_t>> *sprivShaderCode,
gl::ShaderMap<TranslatedShaderInfo> *mslShaderInfoOut,
gl::ShaderMap<std::string> *mslCodeOut);
gl::ShaderMap<TranslatedShaderInfo> *mslShaderInfoOut);
} // namespace mtl
} // namespace rx

Просмотреть файл

@ -78,10 +78,22 @@ spv::ExecutionModel ShaderTypeToSpvExecutionModel(gl::ShaderType shaderType)
void BindBuffers(spirv_cross::CompilerMSL *compiler,
const spirv_cross::SmallVector<spirv_cross::Resource> &resources,
gl::ShaderType shaderType)
gl::ShaderType shaderType,
const std::unordered_map<std::string, uint32_t> &uboOriginalBindings,
std::array<uint32_t, kMaxGLUBOBindings> *uboBindingsRemapOut,
bool *uboArgumentBufferUsed)
{
auto &compilerMsl = *compiler;
uint32_t totalUniformBufferSlots = 0;
struct UniformBufferVar
{
const char *name = nullptr;
spirv_cross::MSLResourceBinding resBinding;
uint32_t arraySize;
};
std::vector<UniformBufferVar> uniformBufferBindings;
for (const spirv_cross::Resource &resource : resources)
{
spirv_cross::MSLResourceBinding resBinding;
@ -113,16 +125,33 @@ void BindBuffers(spirv_cross::CompilerMSL *compiler,
bindingPoint = mtl::kDriverUniformsBindingIndex;
break;
case kGlslangDefaultUniformDescSet:
// NOTE(hqle): Properly handle transform feedbacks binding.
if (shaderType != gl::ShaderType::Vertex || resBinding.binding == 0)
{
bindingPoint = mtl::kDefaultUniformsBindingIndex;
}
else
{
continue;
// NOTE(hqle): support XFB buffer
}
break;
case kGlslangShaderResourceDescSet:
{
UniformBufferVar uboVar;
uboVar.name = resource.name.c_str();
uboVar.resBinding = resBinding;
const spirv_cross::SPIRType &type = compilerMsl.get_type_from_variable(resource.id);
if (!type.array.empty())
{
uboVar.arraySize = type.array[0];
}
else
{
uboVar.arraySize = 1;
}
totalUniformBufferSlots += uboVar.arraySize;
uniformBufferBindings.push_back(uboVar);
}
continue;
default:
// We don't support this descriptor set.
continue;
@ -132,6 +161,47 @@ void BindBuffers(spirv_cross::CompilerMSL *compiler,
compilerMsl.add_msl_resource_binding(resBinding);
} // for (resources)
if (totalUniformBufferSlots == 0)
{
return;
}
// Remap the uniform buffers bindings. glslang allows uniform buffers array to use exactly
// one slot in the descriptor set. However, metal enforces that the uniform buffers array
// use (n) slots where n=array size.
uint32_t currentSlot = 0;
uint32_t maxUBODiscreteSlots = kMaxShaderBuffers - kUBOArgumentBufferBindingIndex;
if (totalUniformBufferSlots > maxUBODiscreteSlots)
{
// If shader uses more than maxUBODiscreteSlots number of UBOs, encode them all into
// an argument buffer. Each buffer will be assigned [[id(n)]] attribute.
*uboArgumentBufferUsed = true;
}
else
{
// Use discrete buffer binding slot for UBOs which translates each slot to [[buffer(n)]]
*uboArgumentBufferUsed = false;
// Discrete buffer binding slot starts at kUBOArgumentBufferBindingIndex
currentSlot += kUBOArgumentBufferBindingIndex;
}
for (UniformBufferVar &uboVar : uniformBufferBindings)
{
spirv_cross::MSLResourceBinding &resBinding = uboVar.resBinding;
resBinding.msl_buffer = currentSlot;
uint32_t originalBinding = uboOriginalBindings.at(uboVar.name);
for (uint32_t i = 0; i < uboVar.arraySize; ++i, ++currentSlot)
{
// Use consecutive slot for member in array
uboBindingsRemapOut->at(originalBinding + i) = currentSlot;
}
compilerMsl.add_msl_resource_binding(resBinding);
}
}
void GetAssignedSamplerBindings(const spirv_cross::CompilerMSL &compilerMsl,
@ -192,9 +262,10 @@ class SpirvToMslCompiler : public spirv_cross::CompilerMSL
public:
SpirvToMslCompiler(std::vector<uint32_t> &&spriv) : spirv_cross::CompilerMSL(spriv) {}
std::string compileEx(gl::ShaderType shaderType,
const OriginalSamplerBindingMap &originalSamplerBindings,
TranslatedShaderInfo *mslShaderInfoOut)
void compileEx(gl::ShaderType shaderType,
const std::unordered_map<std::string, uint32_t> &uboOriginalBindings,
const OriginalSamplerBindingMap &originalSamplerBindings,
TranslatedShaderInfo *mslShaderInfoOut)
{
spirv_cross::CompilerMSL::Options compOpt;
@ -220,23 +291,65 @@ class SpirvToMslCompiler : public spirv_cross::CompilerMSL
// Tell spirv-cross to map default & driver uniform blocks as we want
spirv_cross::ShaderResources mslRes = spirv_cross::CompilerMSL::get_shader_resources();
BindBuffers(this, mslRes.uniform_buffers, shaderType);
BindBuffers(this, mslRes.uniform_buffers, shaderType, uboOriginalBindings,
&mslShaderInfoOut->actualUBOBindings, &mslShaderInfoOut->hasUBOArgumentBuffer);
if (mslShaderInfoOut->hasUBOArgumentBuffer)
{
// Enable argument buffer.
compOpt.argument_buffers = true;
// Force UBO argument buffer binding to start at kUBOArgumentBufferBindingIndex.
spirv_cross::MSLResourceBinding argBufferBinding = {};
argBufferBinding.stage = ShaderTypeToSpvExecutionModel(shaderType);
argBufferBinding.desc_set = kGlslangShaderResourceDescSet;
argBufferBinding.binding =
spirv_cross::kArgumentBufferBinding; // spirv-cross built-in binding.
argBufferBinding.msl_buffer = kUBOArgumentBufferBindingIndex; // Actual binding.
spirv_cross::CompilerMSL::add_msl_resource_binding(argBufferBinding);
// Force discrete slot bindings for textures, default uniforms & driver uniforms
// instead of using argument buffer.
spirv_cross::CompilerMSL::add_discrete_descriptor_set(kGlslangTextureDescSet);
spirv_cross::CompilerMSL::add_discrete_descriptor_set(kGlslangDefaultUniformDescSet);
spirv_cross::CompilerMSL::add_discrete_descriptor_set(kGlslangDriverUniformsDescSet);
}
else
{
// Disable argument buffer generation for uniform buffers
compOpt.argument_buffers = false;
}
spirv_cross::CompilerMSL::set_msl_options(compOpt);
// Actual compilation
std::string translatedMsl = PostProcessTranslatedMsl(spirv_cross::CompilerMSL::compile());
mslShaderInfoOut->metalShaderSource =
PostProcessTranslatedMsl(spirv_cross::CompilerMSL::compile());
// Retrieve automatic texture slot assignments
GetAssignedSamplerBindings(*this, originalSamplerBindings,
&mslShaderInfoOut->actualSamplerBindings);
return translatedMsl;
}
};
} // namespace
void TranslatedShaderInfo::reset()
{
metalShaderSource.clear();
metalLibrary = nil;
hasUBOArgumentBuffer = false;
for (mtl::SamplerBinding &binding : actualSamplerBindings)
{
binding.textureBinding = mtl::kMaxShaderSamplers;
}
for (uint32_t &binding : actualUBOBindings)
{
binding = mtl::kMaxShaderBuffers;
}
}
void GlslangGetShaderSource(const gl::ProgramState &programState,
const gl::ProgramLinkedResources &resources,
gl::ShaderMap<std::string> *shaderSourcesOut,
@ -285,9 +398,19 @@ angle::Result GlslangGetShaderSpirvCode(ErrorHandler *context,
angle::Result SpirvCodeToMsl(Context *context,
const gl::ProgramState &programState,
gl::ShaderMap<std::vector<uint32_t>> *sprivShaderCode,
gl::ShaderMap<TranslatedShaderInfo> *mslShaderInfoOut,
gl::ShaderMap<std::string> *mslCodeOut)
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();
for (uint32_t bufferIdx = 0; bufferIdx < blocks.size(); ++bufferIdx)
{
const gl::InterfaceBlock &block = blocks[bufferIdx];
if (!uboOriginalBindings.count(block.mappedName))
{
uboOriginalBindings[block.mappedName] = bufferIdx;
}
}
// Retrieve original sampler bindings produced by front end.
OriginalSamplerBindingMap originalSamplerBindings;
const std::vector<gl::SamplerBinding> &samplerBindings = programState.getSamplerBindings();
@ -311,14 +434,12 @@ angle::Result SpirvCodeToMsl(Context *context,
// NOTE(hqle): spirv-cross uses exceptions to report error, what should we do here
// in case of error?
std::string translatedMsl = compilerMsl.compileEx(shaderType, originalSamplerBindings,
&mslShaderInfoOut->at(shaderType));
if (translatedMsl.size() == 0)
compilerMsl.compileEx(shaderType, uboOriginalBindings, originalSamplerBindings,
&mslShaderInfoOut->at(shaderType));
if (mslShaderInfoOut->at(shaderType).metalShaderSource.empty())
{
ANGLE_MTL_CHECK(context, false, GL_INVALID_OPERATION);
}
mslCodeOut->at(shaderType) = std::move(translatedMsl);
} // for (gl::ShaderType shaderType
return angle::Result::Continue;

Просмотреть файл

@ -2403,7 +2403,7 @@ TEST_P(UniformBufferTest, UniformBlocksInDiffProgramShareUniformBuffer)
// buffer data correctly.
TEST_P(UniformBufferTest, UniformBlocksSharedSameUniformBuffer)
{
ANGLE_SKIP_TEST_IF(IsIntel() && IsOSX());
ANGLE_SKIP_TEST_IF(IsIntel() && IsOSX() && IsOpenGL());
GLint64 maxUniformBlockSize;
glGetInteger64v(GL_MAX_UNIFORM_BLOCK_SIZE, &maxUniformBlockSize);
@ -2842,7 +2842,7 @@ TEST_P(UniformBufferTest, UniformBlockWithOneLargeVec4Array)
TEST_P(UniformBufferTest, UniformBlockWithOneLargeMatrixArrayWithRowMajorQualifier)
{
// http://anglebug.com/3837 , http://anglebug.com/2273
ANGLE_SKIP_TEST_IF(IsOSX() || IsAndroid() || (IsAMD() && IsOpenGL()) ||
ANGLE_SKIP_TEST_IF((IsOSX() && IsOpenGL()) || IsAndroid() || (IsAMD() && IsOpenGL()) ||
(IsLinux() && IsIntel() && IsOpenGL()));
GLint64 maxUniformBlockSize;
glGetInteger64v(GL_MAX_UNIFORM_BLOCK_SIZE, &maxUniformBlockSize);
@ -3096,7 +3096,7 @@ TEST_P(UniformBufferTest, InstanceArrayUniformBlockWithOneLargeMatrixArray)
TEST_P(UniformBufferTest, Std140UniformBlockWithDynamicallyIndexedRowMajorArray)
{
// http://anglebug.com/3837 , http://anglebug.com/2273
ANGLE_SKIP_TEST_IF((IsLinux() && IsIntel() && IsOpenGL()) || IsOSX());
ANGLE_SKIP_TEST_IF(((IsLinux() && IsIntel()) || IsOSX()) && IsOpenGL());
constexpr char kFS[] =
R"(#version 300 es
@ -3180,9 +3180,78 @@ void main(void){
EXPECT_GL_NO_ERROR();
}
// Test with many uniform buffers work as expected.
TEST_P(UniformBufferTest, ManyBlocks)
{
// http://anglebug.com/5039
ANGLE_SKIP_TEST_IF(IsD3D11());
constexpr char kFS[] =
R"(#version 300 es
precision highp float;
out vec4 my_FragColor;
layout(std140) uniform uboBlock { vec4 color; } blocks[12];
void main()
{
vec4 color = vec4(0, 0, 0, 1);
color += blocks[0].color;
color += blocks[1].color;
color += blocks[2].color;
color += blocks[3].color;
color += blocks[4].color;
color += blocks[5].color;
color += blocks[6].color;
color += blocks[7].color;
color += blocks[8].color;
color += blocks[9].color;
color += blocks[10].color;
color += blocks[11].color;
my_FragColor = vec4(color.rgb, 1.0);
})";
ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
GLBuffer buffers[12];
GLint bufferIndex[12];
bufferIndex[0] = glGetUniformBlockIndex(program, "uboBlock[0]");
bufferIndex[1] = glGetUniformBlockIndex(program, "uboBlock[1]");
bufferIndex[2] = glGetUniformBlockIndex(program, "uboBlock[2]");
bufferIndex[3] = glGetUniformBlockIndex(program, "uboBlock[3]");
bufferIndex[4] = glGetUniformBlockIndex(program, "uboBlock[4]");
bufferIndex[5] = glGetUniformBlockIndex(program, "uboBlock[5]");
bufferIndex[6] = glGetUniformBlockIndex(program, "uboBlock[6]");
bufferIndex[7] = glGetUniformBlockIndex(program, "uboBlock[7]");
bufferIndex[8] = glGetUniformBlockIndex(program, "uboBlock[8]");
bufferIndex[9] = glGetUniformBlockIndex(program, "uboBlock[9]");
bufferIndex[10] = glGetUniformBlockIndex(program, "uboBlock[10]");
bufferIndex[11] = glGetUniformBlockIndex(program, "uboBlock[11]");
std::vector<GLubyte> v(16, 0);
float *vAsFloat = reinterpret_cast<float *>(v.data());
for (int i = 0; i < 12; ++i)
{
glBindBuffer(GL_UNIFORM_BUFFER, buffers[i]);
vAsFloat[0] = (i + 1) / 255.0f;
vAsFloat[1] = (i + 1) / 255.0f;
vAsFloat[2] = (i + 1) / 255.0f;
vAsFloat[3] = 255.0f;
glBufferData(GL_UNIFORM_BUFFER, v.size(), v.data(), GL_STATIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, i, buffers[i]);
glUniformBlockBinding(program, bufferIndex[i], i);
}
drawQuad(program.get(), essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_NEAR(0, 0, 78, 78, 78, 255, 2);
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
ANGLE_INSTANTIATE_TEST_ES3(UniformBufferTest);
ANGLE_INSTANTIATE_TEST_ES3_AND(UniformBufferTest, ES3_METAL());
ANGLE_INSTANTIATE_TEST_ES31(UniformBufferTest31);
} // namespace