зеркало из https://github.com/AvaloniaUI/angle.git
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:
Родитель
962e347302
Коммит
1677cf141d
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче