Metal: Implement MSAA default framebuffer.

GL_SAMPLE_COVERAGE_VALUE is implemented by inserting gl_SampleMask
writing logic to fragment shader.

New test added: MultisampleTest.ContentPresevedAfterInterruption.
- Skip on D3D11 (Bug: angleproject:4609)

Bug: angleproject:2634
Change-Id: Ib44daf0baccc36ea320596d81713156047da059c
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2281783
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Jonah Ryan-Davis <jonahr@google.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
This commit is contained in:
Le Hoang Quyen 2020-07-06 12:40:58 +08:00 коммит произвёл Commit Bot
Родитель f9e01f1230
Коммит 9277ee7413
37 изменённых файлов: 11472 добавлений и 11239 удалений

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

@ -26,7 +26,7 @@
// Version number for shader translation API.
// It is incremented every time the API changes.
#define ANGLE_SH_VERSION 230
#define ANGLE_SH_VERSION 231
enum ShShaderSpec
{
@ -790,6 +790,12 @@ extern const char kAtomicCountersBlockName[];
extern const char kLineRasterEmulationPosition[];
} // namespace vk
namespace mtl
{
// Specialization constant to enable GL_SAMPLE_COVERAGE_VALUE emulation.
extern const char kCoverageMaskEnabledConstName[];
} // namespace mtl
} // namespace sh
#endif // GLSLANG_SHADERLANG_H_

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

@ -41,6 +41,10 @@ struct FeaturesMtl : FeatureSetBase
"Some Apple platforms such as iOS allows separate depth & stencil buffers, "
"whereas others such as macOS don't",
&members};
Feature allowMultisampleStoreAndResolve = {
"allow_msaa_store_and_resolve", FeatureCategory::MetalFeatures,
"The renderer supports MSAA store and resolve in the same pass", &members};
};
} // namespace angle

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

@ -1,18 +1,20 @@
{
"src/libANGLE/renderer/metal/shaders/blit.metal":
"9281aba529ceb4ed5131b7f9c0515362",
"1a12b22f56799bd38cf1c6b301b720ee",
"src/libANGLE/renderer/metal/shaders/clear.metal":
"1c231afc6100433a79fce49046aa5965",
"src/libANGLE/renderer/metal/shaders/common.h":
"569171e345ef36dd6a3b12aeebfae4a6",
"7330bd3f7ab21214e4fe16bc526749bb",
"src/libANGLE/renderer/metal/shaders/compiled/mtl_default_shaders.inc":
"c42c37285c375de9a065b925ed54aebd",
"445538e99fb3679c3c8436f565911c69",
"src/libANGLE/renderer/metal/shaders/constants.h":
"9bb6e63bf2b48a7a56978c787bde4850",
"src/libANGLE/renderer/metal/shaders/gen_indices.metal":
"002511e2b980a7fca7e80cbda6a82712",
"src/libANGLE/renderer/metal/shaders/gen_mtl_internal_shaders.py":
"8de75752bb966cdbe575defc04fa7a7a",
"0e599fb113dbc3f714291383d85c39c2",
"src/libANGLE/renderer/metal/shaders/master_source.metal":
"fbe6f4bfb49a48ae87791a4cff5fab0a",
"src/libANGLE/renderer/metal/shaders/mtl_default_shaders_src_autogen.inc":
"8a94beb0d979f472d71686b6f95b624f"
"ee7ff414da20e7b84f1187d2bea1e84d"
}

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

@ -25,9 +25,31 @@
namespace sh
{
namespace mtl
{
/** extern */
const char kCoverageMaskEnabledConstName[] = "ANGLECoverageMaskEnabled";
} // namespace mtl
namespace
{
constexpr ImmutableString kCoverageMaskField = ImmutableString("coverageMask");
constexpr ImmutableString kSampleMaskWriteFuncName = ImmutableString("ANGLEWriteSampleMask");
TIntermBinary *CreateDriverUniformRef(const TVariable *driverUniforms, const char *fieldName)
{
size_t fieldIndex =
FindFieldIndex(driverUniforms->getType().getInterfaceBlock()->fields(), fieldName);
TIntermSymbol *angleUniformsRef = new TIntermSymbol(driverUniforms);
TConstantUnion *uniformIndex = new TConstantUnion;
uniformIndex->setIConst(static_cast<int>(fieldIndex));
TIntermConstantUnion *indexRef =
new TIntermConstantUnion(uniformIndex, *StaticType::GetBasic<EbtInt>());
return new TIntermBinary(EOpIndexDirectInterfaceBlock, angleUniformsRef, indexRef);
}
// Unlike Vulkan having auto viewport flipping extension, in Metal we have to flip gl_Position.y
// manually.
// This operation performs flipping the gl_Position.y using this expression:
@ -89,6 +111,13 @@ bool TranslatorMetal::translate(TIntermBlock *root,
return false;
}
}
else if (getShaderType() == GL_FRAGMENT_SHADER)
{
if (!insertSampleMaskWritingLogic(root, driverUniforms))
{
return false;
}
}
// Write translated shader.
root->traverse(&outputGLSL);
@ -124,4 +153,70 @@ bool TranslatorMetal::transformDepthBeforeCorrection(TIntermBlock *root,
return RunAtTheEndOfShader(this, root, assignment, &getSymbolTable());
}
void TranslatorMetal::createAdditionalGraphicsDriverUniformFields(std::vector<TField *> *fieldsOut)
{
// Add coverage mask to driver uniform. Metal doesn't have built-in GL_SAMPLE_COVERAGE_VALUE
// equivalent functionality, needs to emulate it using fragment shader's [[sample_mask]] output
// value.
TField *coverageMaskField =
new TField(new TType(EbtUInt), kCoverageMaskField, TSourceLoc(), SymbolType::AngleInternal);
fieldsOut->push_back(coverageMaskField);
}
// Add sample_mask writing to main, guarded by the specialization constant
// kCoverageMaskEnabledConstName
ANGLE_NO_DISCARD bool TranslatorMetal::insertSampleMaskWritingLogic(TIntermBlock *root,
const TVariable *driverUniforms)
{
TInfoSinkBase &sink = getInfoSink().obj;
TSymbolTable *symbolTable = &getSymbolTable();
// Insert coverageMaskEnabled specialization constant and sample_mask writing function.
sink << "layout (constant_id=0) const bool " << mtl::kCoverageMaskEnabledConstName;
sink << " = false;\n";
sink << "void " << kSampleMaskWriteFuncName << "(uint mask)\n";
sink << "{\n";
sink << " if (" << mtl::kCoverageMaskEnabledConstName << ")\n";
sink << " {\n";
sink << " gl_SampleMask[0] = int(mask);\n";
sink << " }\n";
sink << "}\n";
// Create kCoverageMaskEnabledConstName and kSampleMaskWriteFuncName variable references.
TType *boolType = new TType(EbtBool);
boolType->setQualifier(EvqConst);
TVariable *coverageMaskEnabledVar =
new TVariable(symbolTable, ImmutableString(mtl::kCoverageMaskEnabledConstName), boolType,
SymbolType::AngleInternal);
TFunction *sampleMaskWriteFunc =
new TFunction(symbolTable, kSampleMaskWriteFuncName, SymbolType::AngleInternal,
StaticType::GetBasic<EbtVoid>(), false);
TType *uintType = new TType(EbtUInt);
TVariable *maskArg =
new TVariable(symbolTable, ImmutableString("mask"), uintType, SymbolType::AngleInternal);
sampleMaskWriteFunc->addParameter(maskArg);
// coverageMask
TIntermBinary *coverageMask = CreateDriverUniformRef(driverUniforms, kCoverageMaskField.data());
// Insert this code to the end of main()
// if (ANGLECoverageMaskEnabled)
// {
// ANGLEWriteSampleMask(ANGLEUniforms.coverageMask);
// }
TIntermSequence *args = new TIntermSequence;
args->push_back(coverageMask);
TIntermAggregate *callSampleMaskWriteFunc =
TIntermAggregate::CreateFunctionCall(*sampleMaskWriteFunc, args);
TIntermBlock *callBlock = new TIntermBlock;
callBlock->appendStatement(callSampleMaskWriteFunc);
TIntermSymbol *coverageMaskEnabled = new TIntermSymbol(coverageMaskEnabledVar);
TIntermIfElse *ifCall = new TIntermIfElse(coverageMaskEnabled, callBlock, nullptr);
return RunAtTheEndOfShader(this, root, ifCall, symbolTable);
}
} // namespace sh

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

@ -32,6 +32,11 @@ class TranslatorMetal : public TranslatorVulkan
ANGLE_NO_DISCARD bool transformDepthBeforeCorrection(TIntermBlock *root,
const TVariable *driverUniforms) override;
void createAdditionalGraphicsDriverUniformFields(std::vector<TField *> *fieldsOut) override;
ANGLE_NO_DISCARD bool insertSampleMaskWritingLogic(TIntermBlock *root,
const TVariable *driverUniforms);
};
} // namespace sh

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

@ -196,19 +196,6 @@ constexpr size_t kNumComputeDriverUniforms
constexpr std::array<const char *, kNumComputeDriverUniforms> kComputeDriverUniformNames = {
{kAcbBufferOffsets}};
size_t FindFieldIndex(const TFieldList &fieldList, const char *fieldName)
{
for (size_t fieldIndex = 0; fieldIndex < fieldList.size(); ++fieldIndex)
{
if (strcmp(fieldList[fieldIndex]->name().data(), fieldName) == 0)
{
return fieldIndex;
}
}
UNREACHABLE();
return 0;
}
TIntermBinary *CreateDriverUniformRef(const TVariable *driverUniforms, const char *fieldName)
{
size_t fieldIndex =
@ -389,7 +376,9 @@ ANGLE_NO_DISCARD bool AppendVertexShaderTransformFeedbackOutputToMain(TCompiler
// variable.
//
// There are Graphics and Compute variations as they require different uniforms.
const TVariable *AddGraphicsDriverUniformsToShader(TIntermBlock *root, TSymbolTable *symbolTable)
const TVariable *AddGraphicsDriverUniformsToShader(TIntermBlock *root,
TSymbolTable *symbolTable,
const std::vector<TField *> &additionalFields)
{
// Init the depth range type.
TFieldList *depthRangeParamsFields = new TFieldList();
@ -445,6 +434,10 @@ const TVariable *AddGraphicsDriverUniformsToShader(TIntermBlock *root, TSymbolTa
driverFieldList->push_back(driverUniformField);
}
// Back-end specific fields
driverFieldList->insert(driverFieldList->end(), additionalFields.begin(),
additionalFields.end());
// Define a driver uniform block "ANGLEUniformBlock" with instance name "ANGLEUniforms".
return DeclareInterfaceBlock(
root, symbolTable, driverFieldList, EvqUniform, TMemoryQualifier::Create(), 0,
@ -865,7 +858,10 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root,
}
else
{
driverUniforms = AddGraphicsDriverUniformsToShader(root, &getSymbolTable());
std::vector<TField *> additionalFields;
createAdditionalGraphicsDriverUniformFields(&additionalFields);
driverUniforms =
AddGraphicsDriverUniformsToShader(root, &getSymbolTable(), additionalFields);
}
if (atomicCounterCount > 0)

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

@ -48,6 +48,9 @@ class TranslatorVulkan : public TCompiler
{
return true;
}
// Back-end specific fields to be added to driver uniform. See TranslatorMetal.cpp.
virtual void createAdditionalGraphicsDriverUniformFields(std::vector<TField *> *fieldsOut) {}
};
} // namespace sh

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

@ -962,4 +962,17 @@ bool IsValidImplicitConversion(sh::ImplicitTypeConversion conversion, TOperator
return false;
}
size_t FindFieldIndex(const TFieldList &fieldList, const char *fieldName)
{
for (size_t fieldIndex = 0; fieldIndex < fieldList.size(); ++fieldIndex)
{
if (strcmp(fieldList[fieldIndex]->name().data(), fieldName) == 0)
{
return fieldIndex;
}
}
UNREACHABLE();
return 0;
}
} // namespace sh

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

@ -88,6 +88,8 @@ bool IsSpecWithFunctionBodyNewScope(ShShaderSpec shaderSpec, int shaderVersion);
ImplicitTypeConversion GetConversion(TBasicType t1, TBasicType t2);
bool IsValidImplicitConversion(ImplicitTypeConversion conversion, TOperator op);
size_t FindFieldIndex(const TFieldList &fieldList, const char *fieldName);
} // namespace sh
#endif // COMPILER_TRANSLATOR_UTIL_H_

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

@ -55,6 +55,7 @@ _metal_backend_sources = [
"mtl_utils.h",
"mtl_utils.mm",
"shaders/compiled/mtl_default_shaders.inc",
"shaders/constants.h",
"shaders/mtl_default_shaders_src_autogen.inc",
]

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

@ -27,6 +27,7 @@ class DisplayMtl;
class FramebufferMtl;
class VertexArrayMtl;
class ProgramMtl;
class WindowSurfaceMtl;
class ContextMtl : public ContextImpl, public mtl::Context
{
@ -266,6 +267,7 @@ class ContextMtl : public ContextImpl, public mtl::Context
// Call this to notify ContextMtl whenever FramebufferMtl's state changed
void onDrawFrameBufferChange(const gl::Context *context, FramebufferMtl *framebuffer);
void onBackbufferResized(const gl::Context *context, WindowSurfaceMtl *backbuffer);
const MTLClearColor &getClearColorValue() const;
MTLColorWriteMask getColorMask() const;
@ -296,13 +298,10 @@ class ContextMtl : public ContextImpl, public mtl::Context
// Check whether compatible render pass has been started.
bool hasStartedRenderPass(const mtl::RenderPassDesc &desc);
bool hasStartedRenderPass(FramebufferMtl *framebuffer);
// Get current render encoder. May be nullptr if no render pass has been started.
mtl::RenderCommandEncoder *getRenderCommandEncoder();
mtl::RenderCommandEncoder *getCurrentFramebufferRenderCommandEncoder();
// Will end current command encoder if it is valid, then start new encoder.
// Unless hasStartedRenderPass(desc) returns true.
mtl::RenderCommandEncoder *getRenderCommandEncoder(const mtl::RenderPassDesc &desc);
@ -448,6 +447,10 @@ class ContextMtl : public ContextImpl, public mtl::Context
// Used to pre-rotate gl_FragCoord for Vulkan swapchain images on Android (a mat2, which is
// padded to the size of two vec4's).
float fragRotation[8];
uint32_t coverageMask;
float padding2[3];
};
struct DefaultAttribute
@ -466,12 +469,6 @@ class ContextMtl : public ContextImpl, public mtl::Context
VertexArrayMtl *mVertexArray = nullptr;
ProgramMtl *mProgram = nullptr;
// Special flag to indicate current draw framebuffer is default framebuffer.
// We need this instead of calling mDrawFramebuffer->getState().isDefault() because
// mDrawFramebuffer might point to a deleted object, ContextMtl only knows about this very late,
// only during syncState() function call.
bool mDrawFramebufferIsDefault = true;
using DirtyBits = angle::BitSet<DIRTY_BIT_MAX>;
gl::AttributesMask mDirtyDefaultAttribsMask;

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

@ -654,19 +654,19 @@ angle::Result ContextMtl::syncState(const gl::Context *context,
invalidateRenderPipeline();
break;
case gl::State::DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE_ENABLED:
// NOTE(hqle): MSAA support
invalidateRenderPipeline();
break;
case gl::State::DIRTY_BIT_SAMPLE_COVERAGE_ENABLED:
// NOTE(hqle): MSAA support
invalidateRenderPipeline();
break;
case gl::State::DIRTY_BIT_SAMPLE_COVERAGE:
// NOTE(hqle): MSAA support
invalidateDriverUniforms();
break;
case gl::State::DIRTY_BIT_SAMPLE_MASK_ENABLED:
// NOTE(hqle): MSAA support
// NOTE(hqle): 3.1 MSAA support
break;
case gl::State::DIRTY_BIT_SAMPLE_MASK:
// NOTE(hqle): MSAA support
// NOTE(hqle): 3.1 MSAA support
break;
case gl::State::DIRTY_BIT_DEPTH_TEST_ENABLED:
mDepthStencilDesc.updateDepthTestEnabled(glState.getDepthStencilState());
@ -801,7 +801,7 @@ angle::Result ContextMtl::syncState(const gl::Context *context,
invalidateCurrentTextures();
break;
case gl::State::DIRTY_BIT_MULTISAMPLING:
// NOTE(hqle): MSAA feature.
// NOTE(hqle): MSAA on/off.
break;
case gl::State::DIRTY_BIT_SAMPLE_ALPHA_TO_ONE:
// NOTE(hqle): this is part of EXT_multisample_compatibility.
@ -896,7 +896,7 @@ ProgramImpl *ContextMtl::createProgram(const gl::ProgramState &state)
// Framebuffer creation
FramebufferImpl *ContextMtl::createFramebuffer(const gl::FramebufferState &state)
{
return new FramebufferMtl(state, false);
return new FramebufferMtl(state, false, nullptr);
}
// Texture creation
@ -1170,16 +1170,10 @@ void ContextMtl::present(const gl::Context *context, id<CAMetalDrawable> present
{
ensureCommandBufferReady();
// Always discard default FBO's depth stencil buffers at the end of the frame:
if (mDrawFramebufferIsDefault && hasStartedRenderPass(mDrawFramebuffer))
FramebufferMtl *currentframebuffer = mtl::GetImpl(getState().getDrawFramebuffer());
if (currentframebuffer)
{
constexpr GLenum dsAttachments[] = {GL_DEPTH, GL_STENCIL};
(void)mDrawFramebuffer->invalidate(context, 2, dsAttachments);
endEncoding(false);
// Reset discard flag by notify framebuffer that a new render pass has started.
mDrawFramebuffer->onStartedDrawingToFrameBuffer(context);
currentframebuffer->onFrameEnd(context);
}
endEncoding(false);
@ -1202,11 +1196,6 @@ bool ContextMtl::hasStartedRenderPass(const mtl::RenderPassDesc &desc)
mRenderEncoder.renderPassDesc().equalIgnoreLoadStoreOptions(desc);
}
bool ContextMtl::hasStartedRenderPass(FramebufferMtl *framebuffer)
{
return framebuffer && hasStartedRenderPass(framebuffer->getRenderPassDesc(this));
}
// Get current render encoder
mtl::RenderCommandEncoder *ContextMtl::getRenderCommandEncoder()
{
@ -1218,16 +1207,6 @@ mtl::RenderCommandEncoder *ContextMtl::getRenderCommandEncoder()
return &mRenderEncoder;
}
mtl::RenderCommandEncoder *ContextMtl::getCurrentFramebufferRenderCommandEncoder()
{
if (!mDrawFramebuffer)
{
return nullptr;
}
return getRenderCommandEncoder(mDrawFramebuffer->getRenderPassDesc(this));
}
mtl::RenderCommandEncoder *ContextMtl::getRenderCommandEncoder(const mtl::RenderPassDesc &desc)
{
if (hasStartedRenderPass(desc))
@ -1256,10 +1235,10 @@ mtl::RenderCommandEncoder *ContextMtl::getRenderCommandEncoder(
mtl::RenderPassDesc rpDesc;
rpDesc.colorAttachments[0].texture = textureTarget;
rpDesc.colorAttachments[0].level = index.getLevelIndex();
rpDesc.colorAttachments[0].slice = index.hasLayer() ? index.getLayerIndex() : 0;
rpDesc.numColorAttachments = 1;
rpDesc.colorAttachments[0].texture = textureTarget;
rpDesc.colorAttachments[0].level = index.getLevelIndex();
rpDesc.colorAttachments[0].sliceOrDepth = index.hasLayer() ? index.getLayerIndex() : 0;
rpDesc.numColorAttachments = 1;
if (clearColor.valid())
{
@ -1415,8 +1394,7 @@ void ContextMtl::updateDrawFrameBufferBinding(const gl::Context *context)
{
const gl::State &glState = getState();
mDrawFramebuffer = mtl::GetImpl(glState.getDrawFramebuffer());
mDrawFramebufferIsDefault = mDrawFramebuffer->getState().isDefault();
mDrawFramebuffer = mtl::GetImpl(glState.getDrawFramebuffer());
mDrawFramebuffer->onStartedDrawingToFrameBuffer(context);
@ -1435,10 +1413,26 @@ void ContextMtl::onDrawFrameBufferChange(const gl::Context *context, Framebuffer
updateFrontFace(glState);
updateScissor(glState);
// End any render encoding using the old render pass.
endEncoding(false);
// Need to re-apply state to RenderCommandEncoder
invalidateState(context);
}
void ContextMtl::onBackbufferResized(const gl::Context *context, WindowSurfaceMtl *backbuffer)
{
const gl::State &glState = getState();
FramebufferMtl *framebuffer = mtl::GetImpl(glState.getDrawFramebuffer());
if (framebuffer->getAttachedBackbuffer() != backbuffer)
{
return;
}
updateViewport(framebuffer, glState.getViewport(), glState.getNearPlane(),
glState.getFarPlane());
updateScissor(glState);
}
void ContextMtl::updateProgramExecutable(const gl::Context *context)
{
// Need to rebind textures
@ -1519,8 +1513,7 @@ angle::Result ContextMtl::setupDraw(const gl::Context *context,
if (mDirtyBits.test(DIRTY_BIT_DRAW_FRAMEBUFFER))
{
// Start new render command encoder
const mtl::RenderPassDesc &rpDesc = mDrawFramebuffer->getRenderPassDesc(this);
ANGLE_MTL_TRY(this, getRenderCommandEncoder(rpDesc));
ANGLE_MTL_TRY(this, mDrawFramebuffer->ensureRenderPassStarted(context));
// re-apply everything
invalidateState(context);
@ -1729,6 +1722,19 @@ angle::Result ContextMtl::handleDirtyDriverUniforms(const gl::Context *context)
mDriverUniforms.fragRotation[6] = 0.0f;
mDriverUniforms.fragRotation[7] = 0.0f;
// Sample coverage mask
uint32_t sampleBitCount = mDrawFramebuffer->getSamples();
uint32_t coverageSampleBitCount =
static_cast<uint32_t>(std::round(mState.getSampleCoverageValue() * sampleBitCount));
ASSERT(sampleBitCount < 32);
uint32_t coverageMask = (1u << coverageSampleBitCount) - 1;
uint32_t sampleMask = (1u << sampleBitCount) - 1;
if (mState.getSampleCoverageInvert())
{
coverageMask = sampleMask & (~coverageMask);
}
mDriverUniforms.coverageMask = coverageMask;
ASSERT(mRenderEncoder.valid());
mRenderEncoder.setFragmentData(mDriverUniforms, mtl::kDriverUniformsBindingIndex);
mRenderEncoder.setVertexData(mDriverUniforms, mtl::kDriverUniformsBindingIndex);
@ -1742,7 +1748,7 @@ angle::Result ContextMtl::handleDirtyDepthStencilState(const gl::Context *contex
// Need to handle the case when render pass doesn't have depth/stencil attachment.
mtl::DepthStencilDesc dsDesc = mDepthStencilDesc;
const mtl::RenderPassDesc &renderPassDesc = mDrawFramebuffer->getRenderPassDesc(this);
const mtl::RenderPassDesc &renderPassDesc = mRenderEncoder.renderPassDesc();
if (!renderPassDesc.depthAttachment.texture)
{
@ -1796,12 +1802,14 @@ angle::Result ContextMtl::checkIfPipelineChanged(
if (rppChange)
{
const mtl::RenderPassDesc &renderPassDesc = mDrawFramebuffer->getRenderPassDesc(this);
const mtl::RenderPassDesc &renderPassDesc = mRenderEncoder.renderPassDesc();
// Obtain RenderPipelineDesc's output descriptor.
renderPassDesc.populateRenderPipelineOutputDesc(mBlendDesc,
&mRenderPipelineDesc.outputDescriptor);
mRenderPipelineDesc.inputPrimitiveTopology = topologyClass;
mRenderPipelineDesc.alphaToCoverageEnabled = mState.isSampleAlphaToCoverageEnabled();
mRenderPipelineDesc.emulateCoverageMask = mState.isSampleCoverageEnabled();
*changedPipelineDesc = mRenderPipelineDesc;
}

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

@ -295,8 +295,13 @@ egl::ConfigSet DisplayMtl::generateConfigs()
config.surfaceType = EGL_WINDOW_BIT;
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
config.minSwapInterval = 0;
config.maxSwapInterval = 1;
#else
config.minSwapInterval = 1;
config.maxSwapInterval = 1;
#endif
config.renderTargetFormat = GL_RGBA8;
config.depthStencilFormat = GL_DEPTH24_STENCIL8;
@ -308,40 +313,52 @@ egl::ConfigSet DisplayMtl::generateConfigs()
config.colorComponentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
// Buffer sizes
config.redSize = 8;
config.greenSize = 8;
config.blueSize = 8;
config.alphaSize = 8;
config.bufferSize = config.redSize + config.greenSize + config.blueSize + config.alphaSize;
constexpr int samplesSupported[] = {0, 4};
// With DS
config.depthSize = 24;
config.stencilSize = 8;
configs.add(config);
for (int samples : samplesSupported)
{
config.samples = samples;
config.sampleBuffers = (samples == 0) ? 0 : 1;
// With D
config.depthSize = 24;
config.stencilSize = 0;
configs.add(config);
// Buffer sizes
config.redSize = 8;
config.greenSize = 8;
config.blueSize = 8;
config.alphaSize = 8;
config.bufferSize = config.redSize + config.greenSize + config.blueSize + config.alphaSize;
// With S
config.depthSize = 0;
config.stencilSize = 8;
configs.add(config);
// With DS
config.depthSize = 24;
config.stencilSize = 8;
// No DS
config.depthSize = 0;
config.stencilSize = 0;
configs.add(config);
configs.add(config);
// With D
config.depthSize = 24;
config.stencilSize = 0;
configs.add(config);
// With S
config.depthSize = 0;
config.stencilSize = 8;
configs.add(config);
// No DS
config.depthSize = 0;
config.stencilSize = 0;
configs.add(config);
}
return configs;
}
bool DisplayMtl::isValidNativeWindow(EGLNativeWindowType window) const
{
NSObject *layer = (__bridge NSObject *)(window);
return [layer isKindOfClass:[CALayer class]];
ANGLE_MTL_OBJC_SCOPE
{
NSObject *layer = (__bridge NSObject *)(window);
return [layer isKindOfClass:[CALayer class]];
}
}
std::string DisplayMtl::getRendererDescription() const
@ -611,7 +628,8 @@ void DisplayMtl::initializeFeatures()
mFeatures.allowSeparatedDepthStencilBuffers.enabled = false;
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
mFeatures.hasDepthTextureFiltering.enabled = true;
mFeatures.hasDepthTextureFiltering.enabled = true;
mFeatures.allowMultisampleStoreAndResolve.enabled = true;
// Texture swizzle is only supported if macos sdk 10.15 is present
# if defined(__MAC_10_15)
@ -630,6 +648,9 @@ void DisplayMtl::initializeFeatures()
ANGLE_FEATURE_CONDITION((&mFeatures), hasNonUniformDispatch,
[getMetalDevice() supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily4_v1]);
ANGLE_FEATURE_CONDITION((&mFeatures), allowMultisampleStoreAndResolve,
[getMetalDevice() supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]);
# if !TARGET_OS_SIMULATOR
mFeatures.allowSeparatedDepthStencilBuffers.enabled = true;
# endif
@ -637,6 +658,8 @@ void DisplayMtl::initializeFeatures()
angle::PlatformMethods *platform = ANGLEPlatformCurrent();
platform->overrideFeaturesMtl(platform, &mFeatures);
ApplyFeatureOverrides(&mFeatures, getState());
}
angle::Result DisplayMtl::initializeShaderLibrary()

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

@ -18,13 +18,17 @@
namespace rx
{
namespace mtl
{
class RenderCommandEncoder;
} // namespace mtl
class ContextMtl;
class SurfaceMtl;
class WindowSurfaceMtl;
class FramebufferMtl : public FramebufferImpl
{
public:
explicit FramebufferMtl(const gl::FramebufferState &state, bool flipY);
FramebufferMtl(const gl::FramebufferState &state, bool flipY, WindowSurfaceMtl *backbuffer);
~FramebufferMtl() override;
void destroy(const gl::Context *context) override;
@ -84,19 +88,26 @@ class FramebufferMtl : public FramebufferImpl
size_t index,
GLfloat *xy) const override;
RenderTargetMtl *getColorReadRenderTarget() const;
RenderTargetMtl *getColorReadRenderTarget(const gl::Context *context) const;
RenderTargetMtl *getDepthRenderTarget() const { return mDepthRenderTarget; }
RenderTargetMtl *getStencilRenderTarget() const { return mStencilRenderTarget; }
bool flipY() const { return mFlipY; }
gl::Rectangle getCompleteRenderArea() const;
int getSamples() const;
WindowSurfaceMtl *getAttachedBackbuffer() const { return mBackbuffer; }
const mtl::RenderPassDesc &getRenderPassDesc(ContextMtl *context);
bool renderPassHasStarted(ContextMtl *contextMtl) const;
mtl::RenderCommandEncoder *ensureRenderPassStarted(const gl::Context *context);
// Call this to notify FramebufferMtl whenever its render pass has started.
void onStartedDrawingToFrameBuffer(const gl::Context *context);
void onFrameEnd(const gl::Context *context);
// The actual area will be adjusted based on framebuffer flipping property.
gl::Rectangle getReadPixelArea(const gl::Rectangle &glArea);
gl::Rectangle getCorrectFlippedReadArea(const gl::Context *context,
const gl::Rectangle &glArea) const;
// NOTE: this method doesn't do the flipping of area. Caller must do it if needed before
// callling this. See getReadPixelsArea().
@ -121,9 +132,17 @@ class FramebufferMtl : public FramebufferImpl
gl::DrawBufferMask clearColorBuffers,
const mtl::ClearRectParams &clearOpts);
angle::Result prepareRenderPass(const gl::Context *context,
gl::DrawBufferMask drawColorBuffers,
mtl::RenderPassDesc *descOut);
// Initialize load store options for a render pass's first start (i.e. not render pass resuming
// from interruptions such as those caused by a conversion compute pass)
void setLoadStoreActionOnRenderPassFirstStart(mtl::RenderPassAttachmentDesc *attachmentOut);
// Fill RenderPassDesc with relevant attachment's info from GL front end.
angle::Result prepareRenderPass(const gl::Context *context, mtl::RenderPassDesc *descOut);
// Check if a render pass specified by the given RenderPassDesc has started or not, if not this
// method will start the render pass and return its render encoder.
mtl::RenderCommandEncoder *ensureRenderPassStarted(const gl::Context *context,
const mtl::RenderPassDesc &desc);
void overrideClearColor(const mtl::TextureRef &texture,
MTLClearColor clearColor,
@ -143,7 +162,13 @@ class FramebufferMtl : public FramebufferImpl
RenderTargetMtl *mDepthRenderTarget = nullptr;
RenderTargetMtl *mStencilRenderTarget = nullptr;
mtl::RenderPassDesc mRenderPassDesc;
const bool mFlipY = false;
// Flag indicating the render pass start is a clean start or a resume from interruption such
// as by a compute pass.
bool mRenderPassCleanStart = false;
WindowSurfaceMtl *mBackbuffer = nullptr;
const bool mFlipY = false;
};
} // namespace rx

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

@ -23,8 +23,10 @@
namespace rx
{
// FramebufferMtl implementation
FramebufferMtl::FramebufferMtl(const gl::FramebufferState &state, bool flipY)
: FramebufferImpl(state), mFlipY(flipY)
FramebufferMtl::FramebufferMtl(const gl::FramebufferState &state,
bool flipY,
WindowSurfaceMtl *backbuffer)
: FramebufferImpl(state), mBackbuffer(backbuffer), mFlipY(flipY)
{
reset();
}
@ -164,7 +166,7 @@ angle::Result FramebufferMtl::readPixels(const gl::Context *context,
// nothing to read
return angle::Result::Continue;
}
gl::Rectangle flippedArea = getReadPixelArea(clippedArea);
gl::Rectangle flippedArea = getCorrectFlippedReadArea(context, clippedArea);
ContextMtl *contextMtl = mtl::GetImpl(context);
@ -190,7 +192,7 @@ angle::Result FramebufferMtl::readPixels(const gl::Context *context,
params.reverseRowOrder = !params.reverseRowOrder;
}
ANGLE_TRY(readPixelsImpl(context, flippedArea, params, getColorReadRenderTarget(),
ANGLE_TRY(readPixelsImpl(context, flippedArea, params, getColorReadRenderTarget(context),
static_cast<uint8_t *>(pixels) + outputSkipBytes));
return angle::Result::Continue;
@ -273,7 +275,7 @@ angle::Result FramebufferMtl::syncState(const gl::Context *context,
auto oldRenderPassDesc = mRenderPassDesc;
ANGLE_TRY(prepareRenderPass(context, mState.getEnabledDrawBuffers(), &mRenderPassDesc));
ANGLE_TRY(prepareRenderPass(context, &mRenderPassDesc));
if (!oldRenderPassDesc.equalIgnoreLoadStoreOptions(mRenderPassDesc))
{
@ -296,62 +298,166 @@ angle::Result FramebufferMtl::getSamplePosition(const gl::Context *context,
return angle::Result::Stop;
}
RenderTargetMtl *FramebufferMtl::getColorReadRenderTarget() const
RenderTargetMtl *FramebufferMtl::getColorReadRenderTarget(const gl::Context *context) const
{
if (mState.getReadIndex() >= mColorRenderTargets.size())
{
return nullptr;
}
if (mBackbuffer)
{
if (IsError(mBackbuffer->ensureCurrentDrawableObtained(context)))
{
return nullptr;
}
}
return mColorRenderTargets[mState.getReadIndex()];
}
int FramebufferMtl::getSamples() const
{
return mRenderPassDesc.sampleCount;
}
gl::Rectangle FramebufferMtl::getCompleteRenderArea() const
{
return gl::Rectangle(0, 0, mState.getDimensions().width, mState.getDimensions().height);
}
const mtl::RenderPassDesc &FramebufferMtl::getRenderPassDesc(ContextMtl *context)
bool FramebufferMtl::renderPassHasStarted(ContextMtl *contextMtl) const
{
return mRenderPassDesc;
return contextMtl->hasStartedRenderPass(mRenderPassDesc);
}
mtl::RenderCommandEncoder *FramebufferMtl::ensureRenderPassStarted(const gl::Context *context)
{
return ensureRenderPassStarted(context, mRenderPassDesc);
}
mtl::RenderCommandEncoder *FramebufferMtl::ensureRenderPassStarted(const gl::Context *context,
const mtl::RenderPassDesc &desc)
{
ContextMtl *contextMtl = mtl::GetImpl(context);
if (renderPassHasStarted(contextMtl))
{
return contextMtl->getRenderCommandEncoder();
}
if (mBackbuffer)
{
// Backbuffer might obtain new drawable, which means it might change the
// the native texture used as the target of the render pass.
// We need to call this before creating render encoder.
if (IsError(mBackbuffer->ensureCurrentDrawableObtained(context)))
{
return nullptr;
}
}
// Only support ensureRenderPassStarted() with different load & store options.
// The texture, level, slice must be the same.
ASSERT(desc.equalIgnoreLoadStoreOptions(mRenderPassDesc));
mtl::RenderCommandEncoder *encoder = contextMtl->getRenderCommandEncoder(desc);
if (mRenderPassCleanStart)
{
// After a clean start we should reset the loadOp to MTLLoadActionLoad in case this render
// pass could be interrupted by a conversion compute shader pass then being resumed later.
mRenderPassCleanStart = false;
for (mtl::RenderPassColorAttachmentDesc &colorAttachment : mRenderPassDesc.colorAttachments)
{
colorAttachment.loadAction = MTLLoadActionLoad;
}
mRenderPassDesc.depthAttachment.loadAction = MTLLoadActionLoad;
mRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
}
return encoder;
}
void FramebufferMtl::setLoadStoreActionOnRenderPassFirstStart(
mtl::RenderPassAttachmentDesc *attachmentOut)
{
ASSERT(mRenderPassCleanStart);
mtl::RenderPassAttachmentDesc &attachment = *attachmentOut;
if (attachment.storeAction == MTLStoreActionDontCare ||
attachment.storeAction == MTLStoreActionMultisampleResolve)
{
// If we previously discarded attachment's content, then don't need to load it.
attachment.loadAction = MTLLoadActionDontCare;
}
else
{
attachment.loadAction = MTLLoadActionLoad;
}
if (attachment.hasImplicitMSTexture())
{
if (mBackbuffer)
{
// Default action for default framebuffer is resolve and keep MS texture's content.
// We only discard MS texture's content at the end of the frame. See onFrameEnd().
attachment.storeAction = MTLStoreActionStoreAndMultisampleResolve;
}
else
{
// Default action is resolve but don't keep MS texture's content.
attachment.storeAction = MTLStoreActionMultisampleResolve;
}
}
else
{
attachment.storeAction = MTLStoreActionStore; // Default action is store
}
}
void FramebufferMtl::onStartedDrawingToFrameBuffer(const gl::Context *context)
{
mRenderPassCleanStart = true;
// Compute loadOp based on previous storeOp and reset storeOp flags:
for (mtl::RenderPassColorAttachmentDesc &colorAttachment : mRenderPassDesc.colorAttachments)
{
if (colorAttachment.storeAction == MTLStoreActionDontCare)
{
// If we previously discarded attachment's content, then don't need to load it.
colorAttachment.loadAction = MTLLoadActionDontCare;
}
else
{
colorAttachment.loadAction = MTLLoadActionLoad;
}
colorAttachment.storeAction = MTLStoreActionStore; // Default action is store
setLoadStoreActionOnRenderPassFirstStart(&colorAttachment);
}
// Depth load/store
if (mRenderPassDesc.depthAttachment.storeAction == MTLStoreActionDontCare)
{
mRenderPassDesc.depthAttachment.loadAction = MTLLoadActionDontCare;
}
else
{
mRenderPassDesc.depthAttachment.loadAction = MTLLoadActionLoad;
}
mRenderPassDesc.depthAttachment.storeAction = MTLStoreActionStore;
setLoadStoreActionOnRenderPassFirstStart(&mRenderPassDesc.depthAttachment);
// Stencil load/store
if (mRenderPassDesc.stencilAttachment.storeAction == MTLStoreActionDontCare)
setLoadStoreActionOnRenderPassFirstStart(&mRenderPassDesc.stencilAttachment);
}
void FramebufferMtl::onFrameEnd(const gl::Context *context)
{
if (!mBackbuffer)
{
mRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionDontCare;
return;
}
else
ContextMtl *contextMtl = mtl::GetImpl(context);
// Always discard default FBO's depth stencil & multisample buffers at the end of the frame:
if (this->renderPassHasStarted(contextMtl))
{
mRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
mtl::RenderCommandEncoder *encoder = contextMtl->getRenderCommandEncoder();
constexpr GLenum dsAttachments[] = {GL_DEPTH, GL_STENCIL};
(void)invalidateImpl(contextMtl, 2, dsAttachments);
if (mBackbuffer->getSamples() > 1)
{
encoder->setColorStoreAction(MTLStoreActionMultisampleResolve, 0);
}
contextMtl->endEncoding(false);
// Reset discard flag.
onStartedDrawingToFrameBuffer(context);
}
mRenderPassDesc.stencilAttachment.storeAction = MTLStoreActionStore;
}
angle::Result FramebufferMtl::updateColorRenderTarget(const gl::Context *context,
@ -394,36 +500,53 @@ angle::Result FramebufferMtl::updateCachedRenderTarget(const gl::Context *contex
}
angle::Result FramebufferMtl::prepareRenderPass(const gl::Context *context,
gl::DrawBufferMask drawColorBuffers,
mtl::RenderPassDesc *pDescOut)
{
auto &desc = *pDescOut;
gl::DrawBufferMask enabledBuffer = mState.getEnabledDrawBuffers();
desc.numColorAttachments = static_cast<uint32_t>(drawColorBuffers.count());
size_t attachmentIdx = 0;
mtl::RenderPassDesc &desc = *pDescOut;
for (size_t colorIndexGL : drawColorBuffers)
uint32_t maxColorAttachments = static_cast<uint32_t>(mState.getColorAttachments().size());
desc.numColorAttachments = 0;
desc.sampleCount = 1;
for (uint32_t colorIndexGL = 0; colorIndexGL < maxColorAttachments; ++colorIndexGL)
{
if (colorIndexGL >= mtl::kMaxRenderTargets)
{
continue;
}
const RenderTargetMtl *colorRenderTarget = mColorRenderTargets[colorIndexGL];
ASSERT(colorRenderTarget);
ASSERT(colorIndexGL < mtl::kMaxRenderTargets);
mtl::RenderPassColorAttachmentDesc &colorAttachment =
desc.colorAttachments[attachmentIdx++];
colorRenderTarget->toRenderPassAttachmentDesc(&colorAttachment);
mtl::RenderPassColorAttachmentDesc &colorAttachment = desc.colorAttachments[colorIndexGL];
const RenderTargetMtl *colorRenderTarget = mColorRenderTargets[colorIndexGL];
if (colorRenderTarget && enabledBuffer.test(colorIndexGL))
{
colorRenderTarget->toRenderPassAttachmentDesc(&colorAttachment);
desc.numColorAttachments = std::max(desc.numColorAttachments, colorIndexGL + 1);
desc.sampleCount = std::max(desc.sampleCount, colorRenderTarget->getRenderSamples());
}
else
{
colorAttachment.reset();
}
}
if (mDepthRenderTarget)
{
mDepthRenderTarget->toRenderPassAttachmentDesc(&desc.depthAttachment);
desc.sampleCount = std::max(desc.sampleCount, mDepthRenderTarget->getRenderSamples());
}
else
{
desc.depthAttachment.reset();
}
if (mStencilRenderTarget)
{
mStencilRenderTarget->toRenderPassAttachmentDesc(&desc.stencilAttachment);
desc.sampleCount = std::max(desc.sampleCount, mStencilRenderTarget->getRenderSamples());
}
else
{
desc.stencilAttachment.reset();
}
return angle::Result::Continue;
@ -519,7 +642,7 @@ angle::Result FramebufferMtl::clearWithLoadOp(const gl::Context *context,
}
// Start new render encoder with loadOp=Clear
contextMtl->getRenderCommandEncoder(tempDesc);
ensureRenderPassStarted(context, tempDesc);
return angle::Result::Continue;
}
@ -532,7 +655,7 @@ angle::Result FramebufferMtl::clearWithDraw(const gl::Context *context,
DisplayMtl *display = contextMtl->getDisplay();
// Start new render encoder if not already.
mtl::RenderCommandEncoder *encoder = contextMtl->getRenderCommandEncoder(mRenderPassDesc);
mtl::RenderCommandEncoder *encoder = ensureRenderPassStarted(context, mRenderPassDesc);
return display->getUtils().clearWithDraw(context, encoder, clearOpts);
}
@ -659,9 +782,10 @@ angle::Result FramebufferMtl::invalidateImpl(ContextMtl *contextMtl,
return angle::Result::Continue;
}
gl::Rectangle FramebufferMtl::getReadPixelArea(const gl::Rectangle &glArea)
gl::Rectangle FramebufferMtl::getCorrectFlippedReadArea(const gl::Context *context,
const gl::Rectangle &glArea) const
{
RenderTargetMtl *colorReadRT = getColorReadRenderTarget();
RenderTargetMtl *colorReadRT = getColorReadRenderTarget(context);
ASSERT(colorReadRT);
gl::Rectangle flippedArea = glArea;
if (mFlipY)
@ -690,11 +814,20 @@ angle::Result FramebufferMtl::readPixelsImpl(const gl::Context *context,
{
return angle::Result::Continue;
}
const mtl::TextureRef &texture = renderTarget->getTexture();
if (!texture)
mtl::Texture *texture;
if (mBackbuffer)
{
return angle::Result::Continue;
// Backbuffer might have MSAA texture as render target, needs to obtain the
// resolved texture to be able to read pixels.
ANGLE_TRY(mBackbuffer->ensureColorTextureReadyForReadPixels(context));
texture = mBackbuffer->getColorTexture().get();
}
else
{
texture = renderTarget->getTexture().get();
// For non-default framebuffer, MSAA read pixels is disallowed.
ANGLE_MTL_CHECK(contextMtl, texture->samples() == 1, GL_INVALID_OPERATION);
}
const mtl::Format &readFormat = *renderTarget->getFormat();

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

@ -27,7 +27,18 @@ namespace rx
{
class ContextMtl;
class ProgramMtl : public ProgramImpl
// Represents a specialized shader variant. For example, a shader variant with fragment coverage
// mask enabled and a shader variant without.
struct ProgramShaderVariantMtl
{
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.
};
class ProgramMtl : public ProgramImpl, public mtl::RenderPipelineCacheSpecializeShaderFactory
{
public:
ProgramMtl(const gl::ProgramState &state);
@ -100,6 +111,14 @@ class ProgramMtl : public ProgramImpl
void getUniformiv(const gl::Context *context, GLint location, GLint *params) const override;
void getUniformuiv(const gl::Context *context, GLint location, GLuint *params) const override;
// Override mtl::RenderPipelineCacheSpecializeShaderFactory
angle::Result getSpecializedShader(mtl::Context *context,
gl::ShaderType shaderType,
const mtl::RenderPipelineDesc &renderPipelineDesc,
id<MTLFunction> *shaderOut) override;
bool hasSpecializedShader(gl::ShaderType shaderType,
const mtl::RenderPipelineDesc &renderPipelineDesc) override;
// Calls this before drawing, changedPipelineDesc is passed when vertex attributes desc and/or
// shader program changed.
angle::Result setupDraw(const gl::Context *glContext,
@ -132,10 +151,10 @@ class ProgramMtl : public ProgramImpl
const gl::ProgramLinkedResources &resources,
gl::InfoLog &infoLog);
angle::Result createMslShader(const gl::Context *glContext,
gl::ShaderType shaderType,
gl::InfoLog &infoLog,
const std::string &translatedSource);
angle::Result createMslShaderLib(const gl::Context *glContext,
gl::ShaderType shaderType,
gl::InfoLog &infoLog,
const std::string &translatedSource);
// State for the default uniform blocks.
struct DefaultUniformBlock final : private angle::NonCopyable
@ -158,6 +177,13 @@ class ProgramMtl : public ProgramImpl
gl::ShaderMap<std::string> mTranslatedMslShader;
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;
// - Fragment shader: One with sample coverage mask enabled, one with it disabled.
std::array<ProgramShaderVariantMtl, 2> mFragmentShaderVariants;
mtl::RenderPipelineCache mMetalRenderPipelineCache;
};

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

@ -30,6 +30,7 @@ namespace
{
#define SHADER_ENTRY_NAME @"main0"
constexpr char kSpirvCrossSpecConstSuffix[] = "_tmp";
void InitDefaultUniformBlock(const std::vector<sh::Uniform> &uniforms,
gl::Shader *shader,
@ -124,14 +125,59 @@ class Std140BlockLayoutEncoderFactory : public gl::CustomBlockLayoutEncoderFacto
sh::BlockLayoutEncoder *makeEncoder() override { return new sh::Std140BlockEncoder(); }
};
angle::Result CreateMslShader(mtl::Context *context,
id<MTLLibrary> shaderLib,
MTLFunctionConstantValues *funcConstants,
mtl::AutoObjCPtr<id<MTLFunction>> *shaderOut)
{
NSError *nsErr = nil;
id<MTLFunction> mtlShader;
if (funcConstants)
{
mtlShader = [shaderLib newFunctionWithName:SHADER_ENTRY_NAME
constantValues:funcConstants
error:&nsErr];
}
else
{
mtlShader = [shaderLib newFunctionWithName:SHADER_ENTRY_NAME];
}
[mtlShader ANGLE_MTL_AUTORELEASE];
if (nsErr && !mtlShader)
{
std::ostringstream ss;
ss << "Internal error compiling Metal shader:\n"
<< nsErr.localizedDescription.UTF8String << "\n";
ERR() << ss.str();
ANGLE_MTL_CHECK(context, false, GL_INVALID_OPERATION);
}
shaderOut->retainAssign(mtlShader);
return angle::Result::Continue;
}
} // namespace
// ProgramShaderVariantMtl implementation
void ProgramShaderVariantMtl::reset(ContextMtl *contextMtl)
{
metalShader = nil;
}
// ProgramMtl implementation
ProgramMtl::DefaultUniformBlock::DefaultUniformBlock() {}
ProgramMtl::DefaultUniformBlock::~DefaultUniformBlock() = default;
ProgramMtl::ProgramMtl(const gl::ProgramState &state) : ProgramImpl(state) {}
ProgramMtl::ProgramMtl(const gl::ProgramState &state)
: ProgramImpl(state), mMetalRenderPipelineCache(this)
{}
ProgramMtl::~ProgramMtl() {}
@ -149,8 +195,10 @@ void ProgramMtl::reset(ContextMtl *context)
block.uniformLayout.clear();
}
for (gl::ShaderType shaderType : gl::AllGLES2ShaderTypes())
for (gl::ShaderType shaderType : gl::AllShaderTypes())
{
mMslShaderLibrary[shaderType] = nil;
for (mtl::SamplerBinding &binding :
mMslShaderTranslateInfo[shaderType].actualSamplerBindings)
{
@ -158,6 +206,15 @@ void ProgramMtl::reset(ContextMtl *context)
}
}
for (ProgramShaderVariantMtl &var : mVertexShaderVariants)
{
var.reset(context);
}
for (ProgramShaderVariantMtl &var : mFragmentShaderVariants)
{
var.reset(context);
}
mMetalRenderPipelineCache.clear();
}
@ -228,7 +285,7 @@ angle::Result ProgramMtl::linkImpl(const gl::Context *glContext,
{
// Create actual Metal shader
ANGLE_TRY(
createMslShader(glContext, shaderType, infoLog, mTranslatedMslShader[shaderType]));
createMslShaderLib(glContext, shaderType, infoLog, mTranslatedMslShader[shaderType]));
}
return angle::Result::Continue;
@ -325,10 +382,76 @@ angle::Result ProgramMtl::initDefaultUniformBlocks(const gl::Context *glContext)
return angle::Result::Continue;
}
angle::Result ProgramMtl::createMslShader(const gl::Context *glContext,
gl::ShaderType shaderType,
gl::InfoLog &infoLog,
const std::string &translatedMsl)
angle::Result ProgramMtl::getSpecializedShader(mtl::Context *context,
gl::ShaderType shaderType,
const mtl::RenderPipelineDesc &renderPipelineDesc,
id<MTLFunction> *shaderOut)
{
static_assert(YES == 1, "YES should have value of 1");
mtl::AutoObjCPtr<id<MTLLibrary>> mtlShaderLib = mMslShaderLibrary[shaderType];
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)
{
// Already created.
*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)
{
// Already created.
*shaderOut = shaderVariant.metalShader;
return angle::Result::Continue;
}
ANGLE_MTL_OBJC_SCOPE
{
NSString *coverageMaskEnabledStr =
[NSString stringWithFormat:@"%s%s", sh::mtl::kCoverageMaskEnabledConstName,
kSpirvCrossSpecConstSuffix];
auto funcConstants = [[[MTLFunctionConstantValues alloc] init] ANGLE_MTL_AUTORELEASE];
[funcConstants setConstantValue:&emulateCoverageMask
type:MTLDataTypeBool
withName:coverageMaskEnabledStr];
ANGLE_TRY(
CreateMslShader(context, mtlShaderLib, funcConstants, &shaderVariant.metalShader));
}
*shaderOut = shaderVariant.metalShader;
} // gl::ShaderType::Fragment
return angle::Result::Continue;
}
bool ProgramMtl::hasSpecializedShader(gl::ShaderType shaderType,
const mtl::RenderPipelineDesc &renderPipelineDesc)
{
return true;
}
angle::Result ProgramMtl::createMslShaderLib(const gl::Context *glContext,
gl::ShaderType shaderType,
gl::InfoLog &infoLog,
const std::string &translatedMsl)
{
ANGLE_MTL_OBJC_SCOPE
{
@ -338,9 +461,8 @@ angle::Result ProgramMtl::createMslShader(const gl::Context *glContext,
// Convert to actual binary shader
mtl::AutoObjCPtr<NSError *> err = nil;
mtl::AutoObjCPtr<id<MTLLibrary>> mtlShaderLib =
mtl::CreateShaderLibrary(mtlDevice, translatedMsl, &err);
if (err && !mtlShaderLib)
mMslShaderLibrary[shaderType] = mtl::CreateShaderLibrary(mtlDevice, translatedMsl, &err);
if (err && !mMslShaderLibrary[shaderType])
{
std::ostringstream ss;
ss << "Internal error compiling Metal shader:\n"
@ -353,18 +475,6 @@ angle::Result ProgramMtl::createMslShader(const gl::Context *glContext,
ANGLE_MTL_CHECK(contextMtl, false, GL_INVALID_OPERATION);
}
auto mtlShader = [mtlShaderLib.get() newFunctionWithName:SHADER_ENTRY_NAME];
[mtlShader ANGLE_MTL_AUTORELEASE];
ASSERT(mtlShader);
if (shaderType == gl::ShaderType::Vertex)
{
mMetalRenderPipelineCache.setVertexShader(contextMtl, mtlShader);
}
else if (shaderType == gl::ShaderType::Fragment)
{
mMetalRenderPipelineCache.setFragmentShader(contextMtl, mtlShader);
}
return angle::Result::Continue;
}
}

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

@ -32,21 +32,33 @@ class RenderTargetMtl final : public FramebufferAttachmentRenderTarget
// Used in std::vector initialization.
RenderTargetMtl(RenderTargetMtl &&other);
void set(const mtl::TextureRef &texture, size_t level, size_t layer, const mtl::Format &format);
void set(const mtl::TextureRef &texture);
void set(const mtl::TextureRef &texture,
uint32_t level,
uint32_t layer,
const mtl::Format &format);
void set(const mtl::TextureRef &texture,
const mtl::TextureRef &implicitMSTexture,
uint32_t level,
uint32_t layer,
const mtl::Format &format);
void setTexture(const mtl::TextureRef &texture);
void setImplicitMSTexture(const mtl::TextureRef &implicitMSTexture);
void reset();
const mtl::TextureRef &getTexture() const { return mTexture; }
size_t getLevelIndex() const { return mLevelIndex; }
size_t getLayerIndex() const { return mLayerIndex; }
mtl::TextureRef getTexture() const { return mTexture; }
mtl::TextureRef getImplicitMSTexture() const { return mImplicitMSTexture; }
uint32_t getLevelIndex() const { return mLevelIndex; }
uint32_t getLayerIndex() const { return mLayerIndex; }
uint32_t getRenderSamples() const;
const mtl::Format *getFormat() const { return mFormat; }
void toRenderPassAttachmentDesc(mtl::RenderPassAttachmentDesc *rpaDescOut) const;
private:
mtl::TextureRef mTexture;
size_t mLevelIndex = 0;
size_t mLayerIndex = 0;
mtl::TextureRef mImplicitMSTexture;
uint32_t mLevelIndex = 0;
uint32_t mLayerIndex = 0;
const mtl::Format *mFormat = nullptr;
};
} // namespace rx

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

@ -20,38 +20,61 @@ RenderTargetMtl::~RenderTargetMtl()
RenderTargetMtl::RenderTargetMtl(RenderTargetMtl &&other)
: mTexture(std::move(other.mTexture)),
mImplicitMSTexture(std::move(other.mImplicitMSTexture)),
mLevelIndex(other.mLevelIndex),
mLayerIndex(other.mLayerIndex)
{}
void RenderTargetMtl::set(const mtl::TextureRef &texture,
size_t level,
size_t layer,
uint32_t level,
uint32_t layer,
const mtl::Format &format)
{
mTexture = texture;
mLevelIndex = level;
mLayerIndex = layer;
mFormat = &format;
set(texture, nullptr, level, layer, format);
}
void RenderTargetMtl::set(const mtl::TextureRef &texture)
void RenderTargetMtl::set(const mtl::TextureRef &texture,
const mtl::TextureRef &implicitMSTexture,
uint32_t level,
uint32_t layer,
const mtl::Format &format)
{
mTexture = texture;
mImplicitMSTexture = implicitMSTexture;
mLevelIndex = level;
mLayerIndex = layer;
mFormat = &format;
}
void RenderTargetMtl::setTexture(const mtl::TextureRef &texture)
{
mTexture = texture;
}
void RenderTargetMtl::setImplicitMSTexture(const mtl::TextureRef &implicitMSTexture)
{
mImplicitMSTexture = implicitMSTexture;
}
void RenderTargetMtl::reset()
{
mTexture.reset();
mImplicitMSTexture.reset();
mLevelIndex = 0;
mLayerIndex = 0;
mFormat = nullptr;
}
uint32_t RenderTargetMtl::getRenderSamples() const
{
return mImplicitMSTexture ? mImplicitMSTexture->samples()
: (mTexture ? mTexture->samples() : 1);
}
void RenderTargetMtl::toRenderPassAttachmentDesc(mtl::RenderPassAttachmentDesc *rpaDescOut) const
{
rpaDescOut->texture = mTexture;
rpaDescOut->level = static_cast<uint32_t>(mLevelIndex);
rpaDescOut->slice = static_cast<uint32_t>(mLayerIndex);
rpaDescOut->texture = mTexture;
rpaDescOut->implicitMSTexture = mImplicitMSTexture;
rpaDescOut->level = mLevelIndex;
rpaDescOut->sliceOrDepth = mLayerIndex;
}
}

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

@ -64,6 +64,10 @@ class SurfaceMtl : public SurfaceImpl
EGLint isPostSubBufferSupported() const override;
EGLint getSwapBehavior() const override;
const mtl::TextureRef &getColorTexture() { return mColorTexture; }
const mtl::Format &getColorFormat() const { return mColorFormat; }
int getSamples() const { return mSamples; }
angle::Result getAttachmentRenderTarget(const gl::Context *context,
GLenum binding,
const gl::ImageIndex &imageIndex,
@ -71,20 +75,29 @@ class SurfaceMtl : public SurfaceImpl
FramebufferAttachmentRenderTarget **rtOut) override;
protected:
// Ensure companion (depth, stencil) textures' size is correct w.r.t color texture.
angle::Result ensureDepthStencilSizeCorrect(const gl::Context *context,
gl::Framebuffer::DirtyBits *fboDirtyBits);
// Ensure companion (MS, depth, stencil) textures' size is correct w.r.t color texture.
angle::Result ensureCompanionTexturesSizeCorrect(const gl::Context *context,
const gl::Extents &size);
angle::Result resolveColorTextureIfNeeded(const gl::Context *context);
// Normal textures
mtl::TextureRef mColorTexture;
mtl::TextureRef mDepthTexture;
mtl::TextureRef mStencilTexture;
// Implicit multisample texture
mtl::TextureRef mMSColorTexture;
bool mUsePackedDepthStencil = false;
// Auto resolve MS texture at the end of render pass or requires a separate blitting pass?
bool mAutoResolveMSColorTexture = false;
mtl::Format mColorFormat;
mtl::Format mDepthFormat;
mtl::Format mStencilFormat;
int mSamples = 0;
RenderTargetMtl mColorRenderTarget;
RenderTargetMtl mDepthRenderTarget;
RenderTargetMtl mStencilRenderTarget;
@ -105,9 +118,9 @@ class WindowSurfaceMtl : public SurfaceMtl
FramebufferImpl *createDefaultFramebuffer(const gl::Context *context,
const gl::FramebufferState &state) override;
egl::Error makeCurrent(const gl::Context *context) override;
egl::Error swap(const gl::Context *context) override;
void setSwapInterval(EGLint interval) override;
EGLint getSwapBehavior() const override;
// width and height can change with client window resizing
@ -120,17 +133,29 @@ class WindowSurfaceMtl : public SurfaceMtl
GLsizei samples,
FramebufferAttachmentRenderTarget **rtOut) override;
angle::Result ensureCurrentDrawableObtained(const gl::Context *context);
// Ensure the the texture returned from getColorTexture() is ready for glReadPixels(). This
// implicitly calls ensureCurrentDrawableObtained().
angle::Result ensureColorTextureReadyForReadPixels(const gl::Context *context);
private:
angle::Result swapImpl(const gl::Context *context);
angle::Result ensureRenderTargetsCreated(const gl::Context *context);
angle::Result obtainNextDrawable(const gl::Context *context);
angle::Result ensureCompanionTexturesSizeCorrect(const gl::Context *context);
CGSize calcExpectedDrawableSize() const;
// Check if metal layer has been resized.
void checkIfLayerResized();
bool checkIfLayerResized(const gl::Context *context);
mtl::AutoObjCObj<CAMetalLayer> mMetalLayer = nil;
CALayer *mLayer;
mtl::AutoObjCPtr<id<CAMetalDrawable>> mCurrentDrawable = nil;
// Cache last known drawable size that is used by GL context. Can be used to detect resize
// event. We don't use mMetalLayer.drawableSize directly since it might be changed internally by
// metal runtime.
CGSize mCurrentKnownDrawableSize;
};
} // namespace rx

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

@ -46,6 +46,30 @@ constexpr angle::FormatID kDefaultFrameBufferStencilFormatId = angle::FormatID::
constexpr angle::FormatID kDefaultFrameBufferDepthStencilFormatId =
angle::FormatID::D24_UNORM_S8_UINT;
angle::Result CreateTexture(const gl::Context *context,
const mtl::Format &format,
uint32_t width,
uint32_t height,
uint32_t samples,
bool renderTargetOnly,
mtl::TextureRef *textureOut)
{
ContextMtl *contextMtl = mtl::GetImpl(context);
if (samples > 1)
{
ANGLE_TRY(mtl::Texture::Make2DMSTexture(contextMtl, format, width, height, samples,
/** renderTargetOnly */ renderTargetOnly,
/** allowFormatView */ false, textureOut));
}
else
{
ANGLE_TRY(mtl::Texture::Make2DTexture(contextMtl, format, width, height, 1,
/** renderTargetOnly */ renderTargetOnly,
/** allowFormatView */ false, textureOut));
}
return angle::Result::Continue;
}
ANGLE_MTL_UNUSED
bool IsFrameCaptureEnabled()
{
@ -194,6 +218,8 @@ SurfaceMtl::SurfaceMtl(DisplayMtl *display,
mColorFormat.intendedFormatId = mColorFormat.actualFormatId = angle::FormatID::B8G8R8A8_UNORM;
mColorFormat.metalFormat = MTLPixelFormatBGRA8Unorm;
mSamples = state.config->samples;
int depthBits = 0;
int stencilBits = 0;
if (state.config)
@ -235,6 +261,8 @@ void SurfaceMtl::destroy(const egl::Display *display)
mDepthTexture = nullptr;
mStencilTexture = nullptr;
mMSColorTexture = nullptr;
mColorRenderTarget.reset();
mDepthRenderTarget.reset();
mStencilRenderTarget.reset();
@ -248,18 +276,25 @@ egl::Error SurfaceMtl::initialize(const egl::Display *display)
FramebufferImpl *SurfaceMtl::createDefaultFramebuffer(const gl::Context *context,
const gl::FramebufferState &state)
{
auto fbo = new FramebufferMtl(state, /* flipY */ false);
auto fbo = new FramebufferMtl(state, /* flipY */ false, /* backbuffer */ nullptr);
return fbo;
}
egl::Error SurfaceMtl::makeCurrent(const gl::Context *context)
{
ContextMtl *contextMtl = mtl::GetImpl(context);
StartFrameCapture(contextMtl);
return egl::NoError();
}
egl::Error SurfaceMtl::unMakeCurrent(const gl::Context *context)
{
ContextMtl *contextMtl = mtl::GetImpl(context);
contextMtl->flushCommandBufer();
StopFrameCapture();
return egl::NoError();
}
@ -376,21 +411,38 @@ angle::Result SurfaceMtl::getAttachmentRenderTarget(const gl::Context *context,
return angle::Result::Continue;
}
angle::Result SurfaceMtl::ensureDepthStencilSizeCorrect(const gl::Context *context,
gl::Framebuffer::DirtyBits *fboDirtyBits)
angle::Result SurfaceMtl::ensureCompanionTexturesSizeCorrect(const gl::Context *context,
const gl::Extents &size)
{
ASSERT(mColorTexture && mColorTexture->get());
ContextMtl *contextMtl = mtl::GetImpl(context);
auto size = mColorTexture->size();
ASSERT(mColorTexture);
if (mSamples > 1 && (!mMSColorTexture || mMSColorTexture->size() != size))
{
mAutoResolveMSColorTexture =
contextMtl->getDisplay()->getFeatures().allowMultisampleStoreAndResolve.enabled;
ANGLE_TRY(CreateTexture(context, mColorFormat, size.width, size.height, mSamples,
/** renderTargetOnly */ mAutoResolveMSColorTexture,
&mMSColorTexture));
if (mAutoResolveMSColorTexture)
{
// Use auto MSAA resolve at the end of render pass.
mColorRenderTarget.setImplicitMSTexture(mMSColorTexture);
}
else
{
mColorRenderTarget.setTexture(mMSColorTexture);
}
}
if (mDepthFormat.valid() && (!mDepthTexture || mDepthTexture->size() != size))
{
ANGLE_TRY(mtl::Texture::Make2DTexture(contextMtl, mDepthFormat, size.width, size.height, 1,
true, false, &mDepthTexture));
ANGLE_TRY(CreateTexture(context, mDepthFormat, size.width, size.height, mSamples,
/** renderTargetOnly */ true, &mDepthTexture));
mDepthRenderTarget.set(mDepthTexture, 0, 0, mDepthFormat);
fboDirtyBits->set(gl::Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT);
}
if (mStencilFormat.valid() && (!mStencilTexture || mStencilTexture->size() != size))
@ -401,24 +453,43 @@ angle::Result SurfaceMtl::ensureDepthStencilSizeCorrect(const gl::Context *conte
}
else
{
ANGLE_TRY(mtl::Texture::Make2DTexture(contextMtl, mStencilFormat, size.width,
size.height, 1, true, false, &mStencilTexture));
ANGLE_TRY(CreateTexture(context, mStencilFormat, size.width, size.height, mSamples,
/** renderTargetOnly */ true, &mStencilTexture));
}
mStencilRenderTarget.set(mStencilTexture, 0, 0, mStencilFormat);
fboDirtyBits->set(gl::Framebuffer::DIRTY_BIT_STENCIL_ATTACHMENT);
}
return angle::Result::Continue;
}
angle::Result SurfaceMtl::resolveColorTextureIfNeeded(const gl::Context *context)
{
ASSERT(mMSColorTexture);
if (!mAutoResolveMSColorTexture)
{
// Manually resolve texture
ContextMtl *contextMtl = mtl::GetImpl(context);
mtl::RenderCommandEncoder *encoder =
contextMtl->getRenderCommandEncoder(mColorTexture, gl::ImageIndex::Make2D(0));
ANGLE_TRY(
contextMtl->getDisplay()->getUtils().blitWithDraw(context, encoder, mMSColorTexture));
contextMtl->endEncoding(true);
}
return angle::Result::Continue;
}
// WindowSurfaceMtl implementation.
WindowSurfaceMtl::WindowSurfaceMtl(DisplayMtl *display,
const egl::SurfaceState &state,
EGLNativeWindowType window,
const egl::AttributeMap &attribs)
: SurfaceMtl(display, state, attribs), mLayer((__bridge CALayer *)(window))
{}
{
// NOTE(hqle): Width and height attributes is ignored for now.
mCurrentKnownDrawableSize = CGSizeMake(0, 0);
}
WindowSurfaceMtl::~WindowSurfaceMtl() {}
@ -470,15 +541,15 @@ egl::Error WindowSurfaceMtl::initialize(const egl::Display *display)
mMetalLayer.get().autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
#endif
// ensure drawableSize is set to correct value:
mMetalLayer.get().drawableSize = mCurrentKnownDrawableSize = calcExpectedDrawableSize();
if (mMetalLayer.get() != mLayer)
{
mMetalLayer.get().contentsScale = mLayer.contentsScale;
[mLayer addSublayer:mMetalLayer.get()];
}
// ensure drawableSize is set to correct value:
checkIfLayerResized();
}
return egl::NoError();
@ -487,18 +558,11 @@ egl::Error WindowSurfaceMtl::initialize(const egl::Display *display)
FramebufferImpl *WindowSurfaceMtl::createDefaultFramebuffer(const gl::Context *context,
const gl::FramebufferState &state)
{
auto fbo = new FramebufferMtl(state, /* flipY */ true);
auto fbo = new FramebufferMtl(state, /* flipY */ true, /* backbuffer */ this);
return fbo;
}
egl::Error WindowSurfaceMtl::makeCurrent(const gl::Context *context)
{
ANGLE_TO_EGL_TRY(obtainNextDrawable(context));
return egl::NoError();
}
egl::Error WindowSurfaceMtl::swap(const gl::Context *context)
{
ANGLE_TO_EGL_TRY(swapImpl(context));
@ -506,31 +570,22 @@ egl::Error WindowSurfaceMtl::swap(const gl::Context *context)
return egl::NoError();
}
void WindowSurfaceMtl::setSwapInterval(EGLint interval)
{
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
mMetalLayer.get().displaySyncEnabled = interval != 0;
#endif
}
// width and height can change with client window resizing
EGLint WindowSurfaceMtl::getWidth() const
{
if (mColorTexture)
{
return static_cast<EGLint>(mColorTexture->width());
}
if (mMetalLayer)
{
return static_cast<EGLint>(mMetalLayer.get().drawableSize.width);
}
return SurfaceMtl::getWidth();
return static_cast<EGLint>(mCurrentKnownDrawableSize.width);
}
EGLint WindowSurfaceMtl::getHeight() const
{
if (mColorTexture)
{
return static_cast<EGLint>(mColorTexture->height());
}
if (mMetalLayer)
{
return static_cast<EGLint>(mMetalLayer.get().drawableSize.height);
}
return SurfaceMtl::getHeight();
return static_cast<EGLint>(mCurrentKnownDrawableSize.height);
}
EGLint WindowSurfaceMtl::getSwapBehavior() const
@ -544,15 +599,15 @@ angle::Result WindowSurfaceMtl::getAttachmentRenderTarget(const gl::Context *con
GLsizei samples,
FramebufferAttachmentRenderTarget **rtOut)
{
// NOTE(hqle): Support MSAA.
ANGLE_TRY(ensureRenderTargetsCreated(context));
ANGLE_TRY(ensureCurrentDrawableObtained(context));
ANGLE_TRY(ensureCompanionTexturesSizeCorrect(context));
return SurfaceMtl::getAttachmentRenderTarget(context, binding, imageIndex, samples, rtOut);
}
angle::Result WindowSurfaceMtl::ensureRenderTargetsCreated(const gl::Context *context)
angle::Result WindowSurfaceMtl::ensureCurrentDrawableObtained(const gl::Context *context)
{
if (!mColorTexture)
if (!mCurrentDrawable)
{
ANGLE_TRY(obtainNextDrawable(context));
}
@ -560,39 +615,81 @@ angle::Result WindowSurfaceMtl::ensureRenderTargetsCreated(const gl::Context *co
return angle::Result::Continue;
}
void WindowSurfaceMtl::checkIfLayerResized()
angle::Result WindowSurfaceMtl::ensureCompanionTexturesSizeCorrect(const gl::Context *context)
{
ASSERT(mMetalLayer);
gl::Extents size(static_cast<int>(mMetalLayer.get().drawableSize.width),
static_cast<int>(mMetalLayer.get().drawableSize.height), 1);
ANGLE_TRY(SurfaceMtl::ensureCompanionTexturesSizeCorrect(context, size));
return angle::Result::Continue;
}
angle::Result WindowSurfaceMtl::ensureColorTextureReadyForReadPixels(const gl::Context *context)
{
ANGLE_TRY(ensureCurrentDrawableObtained(context));
if (mMSColorTexture)
{
if (mMSColorTexture->isCPUReadMemNeedSync())
{
ANGLE_TRY(resolveColorTextureIfNeeded(context));
mMSColorTexture->resetCPUReadMemNeedSync();
}
}
return angle::Result::Continue;
}
CGSize WindowSurfaceMtl::calcExpectedDrawableSize() const
{
CGSize currentDrawableSize = mMetalLayer.get().drawableSize;
CGSize currentLayerSize = mMetalLayer.get().bounds.size;
CGFloat currentLayerContentsScale = mMetalLayer.get().contentsScale;
CGSize expectedDrawableSize = CGSizeMake(currentLayerSize.width * currentLayerContentsScale,
currentLayerSize.height * currentLayerContentsScale);
if (currentDrawableSize.width != expectedDrawableSize.width ||
currentDrawableSize.height != expectedDrawableSize.height)
return expectedDrawableSize;
}
bool WindowSurfaceMtl::checkIfLayerResized(const gl::Context *context)
{
CGSize currentLayerDrawableSize = mMetalLayer.get().drawableSize;
CGSize expectedDrawableSize = calcExpectedDrawableSize();
// NOTE(hqle): We need to compare the size against mCurrentKnownDrawableSize also.
// That is because metal framework might internally change the drawableSize property of
// metal layer, and it might become equal to expectedDrawableSize. If that happens, we cannot
// know whether the layer has been resized or not.
if (currentLayerDrawableSize.width != expectedDrawableSize.width ||
currentLayerDrawableSize.height != expectedDrawableSize.height ||
mCurrentKnownDrawableSize.width != expectedDrawableSize.width ||
mCurrentKnownDrawableSize.height != expectedDrawableSize.height)
{
// Resize the internal drawable texture.
mMetalLayer.get().drawableSize = expectedDrawableSize;
mMetalLayer.get().drawableSize = mCurrentKnownDrawableSize = expectedDrawableSize;
return true;
}
return false;
}
angle::Result WindowSurfaceMtl::obtainNextDrawable(const gl::Context *context)
{
checkIfLayerResized();
ANGLE_MTL_OBJC_SCOPE
{
ContextMtl *contextMtl = mtl::GetImpl(context);
StartFrameCapture(contextMtl);
ANGLE_MTL_TRY(contextMtl, mMetalLayer);
if (mColorTexture)
// Check if layer was resized
if (checkIfLayerResized(context))
{
mColorTexture->set(nil);
contextMtl->onBackbufferResized(context, this);
}
mCurrentDrawable = nil;
mCurrentDrawable.retainAssign([mMetalLayer nextDrawable]);
if (!mCurrentDrawable)
{
@ -607,7 +704,8 @@ angle::Result WindowSurfaceMtl::obtainNextDrawable(const gl::Context *context)
if (!mColorTexture)
{
mColorTexture = mtl::Texture::MakeFromMetal(mCurrentDrawable.get().texture);
mColorRenderTarget.set(mColorTexture, 0, 0, mColorFormat);
ASSERT(!mColorRenderTarget.getTexture());
mColorRenderTarget.set(mColorTexture, mMSColorTexture, 0, 0, mColorFormat);
}
else
{
@ -617,22 +715,8 @@ angle::Result WindowSurfaceMtl::obtainNextDrawable(const gl::Context *context)
ANGLE_MTL_LOG("Current metal drawable size=%d,%d", mColorTexture->width(),
mColorTexture->height());
gl::Framebuffer::DirtyBits fboDirtyBits;
fboDirtyBits.set(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0);
// Now we have to resize depth stencil buffers if necessary.
ANGLE_TRY(ensureDepthStencilSizeCorrect(context, &fboDirtyBits));
// Need to notify default framebuffer to invalidate its render targets.
// Since a new drawable texture has been obtained, also, the depth stencil
// buffers might have been resized.
gl::Framebuffer *defaultFbo =
context->getFramebuffer(gl::Framebuffer::kDefaultDrawFramebufferHandle);
if (defaultFbo)
{
FramebufferMtl *framebufferMtl = mtl::GetImpl(defaultFbo);
ANGLE_TRY(framebufferMtl->syncState(context, GL_FRAMEBUFFER, fboDirtyBits));
}
// Now we have to resize depth stencil buffers if required.
ANGLE_TRY(ensureCompanionTexturesSizeCorrect(context));
return angle::Result::Continue;
}
@ -640,15 +724,26 @@ angle::Result WindowSurfaceMtl::obtainNextDrawable(const gl::Context *context)
angle::Result WindowSurfaceMtl::swapImpl(const gl::Context *context)
{
ANGLE_TRY(ensureRenderTargetsCreated(context));
if (mCurrentDrawable)
{
ASSERT(mColorTexture);
ContextMtl *contextMtl = mtl::GetImpl(context);
ContextMtl *contextMtl = mtl::GetImpl(context);
contextMtl->present(context, mCurrentDrawable);
if (mMSColorTexture)
{
ANGLE_TRY(resolveColorTextureIfNeeded(context));
}
StopFrameCapture();
contextMtl->present(context, mCurrentDrawable);
ANGLE_TRY(obtainNextDrawable(context));
StopFrameCapture();
StartFrameCapture(contextMtl);
// Invalidate current drawable
mColorTexture->set(nil);
mCurrentDrawable = nil;
}
return angle::Result::Continue;
}

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

@ -337,7 +337,7 @@ void TextureMtl::releaseTexture(bool releaseImages)
for (RenderTargetMtl &rt : mLayeredRenderTargets)
{
rt.set(nullptr);
rt.reset();
}
if (releaseImages)
@ -1153,7 +1153,7 @@ angle::Result TextureMtl::copySubImageWithDraw(const gl::Context *context,
DisplayMtl *displayMtl = contextMtl->getDisplay();
FramebufferMtl *framebufferMtl = mtl::GetImpl(source);
RenderTargetMtl *colorReadRT = framebufferMtl->getColorReadRenderTarget();
RenderTargetMtl *colorReadRT = framebufferMtl->getColorReadRenderTarget(context);
if (!colorReadRT || !colorReadRT->getTexture())
{
@ -1192,7 +1192,7 @@ angle::Result TextureMtl::copySubImageCPU(const gl::Context *context,
ContextMtl *contextMtl = mtl::GetImpl(context);
FramebufferMtl *framebufferMtl = mtl::GetImpl(source);
RenderTargetMtl *colorReadRT = framebufferMtl->getColorReadRenderTarget();
RenderTargetMtl *colorReadRT = framebufferMtl->getColorReadRenderTarget(context);
if (!colorReadRT || !colorReadRT->getTexture())
{
@ -1216,10 +1216,10 @@ angle::Result TextureMtl::copySubImageCPU(const gl::Context *context,
PackPixelsParams packParams(srcRowArea, dstFormat, dstRowPitch, false, nullptr, 0);
// Read pixels from framebuffer to memory:
gl::Rectangle flippedSrcRowArea = framebufferMtl->getReadPixelArea(srcRowArea);
gl::Rectangle flippedSrcRowArea =
framebufferMtl->getCorrectFlippedReadArea(context, srcRowArea);
ANGLE_TRY(framebufferMtl->readPixelsImpl(context, flippedSrcRowArea, packParams,
framebufferMtl->getColorReadRenderTarget(),
conversionRow.data()));
colorReadRT, conversionRow.data()));
// Upload to texture
ANGLE_TRY(UploadTextureContents(context, image, dstFormat, mtlDstRowArea, 0, 0,

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

@ -788,6 +788,24 @@ void RenderCommandEncoder::finalizeLoadStoreAction(
return;
}
if (objCRenderPassAttachment.resolveTexture)
{
if (objCRenderPassAttachment.storeAction == MTLStoreActionStore)
{
// NOTE(hqle): Currently if the store action with implicit MS texture is
// MTLStoreActionStore, it is automatically convert to store and resolve action. It
// might introduce unnecessary overhead. Consider an improvement such as only store the
// MS texture, and resolve only at the end of real render pass (not render pass the is
// interrupted by compute pass) or before glBlitFramebuffer operation starts.
objCRenderPassAttachment.storeAction = MTLStoreActionStoreAndMultisampleResolve;
}
else if (objCRenderPassAttachment.storeAction == MTLStoreActionDontCare)
{
// Ignore resolve texture if the store action is not a resolve action.
objCRenderPassAttachment.resolveTexture = nil;
}
}
if (objCRenderPassAttachment.storeAction == MTLStoreActionUnknown)
{
// If storeAction hasn't been set for this attachment, we set to dontcare.

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

@ -171,6 +171,21 @@ void GetAssignedSamplerBindings(const spirv_cross::CompilerMSL &compilerMsl,
}
}
std::string PostProcessTranslatedMsl(const std::string &translatedSource)
{
// Add function_constant attribute to gl_SampleMask.
// Even though this varying is only used when ANGLECoverageMaskEnabled is true,
// the spirv-cross doesn't assign function_constant attribute to it. Thus it won't be dead-code
// removed when ANGLECoverageMaskEnabled=false.
std::string sampleMaskReplaceStr = std::string("[[sample_mask, function_constant(") +
sh::mtl::kCoverageMaskEnabledConstName + ")]]";
// This replaces "gl_SampleMask [[sample_mask]]"
// with "gl_SampleMask [[sample_mask, function_constant(ANGLECoverageMaskEnabled)]]"
std::regex sampleMaskDeclareRegex(R"(\[\s*\[\s*sample_mask\s*\]\s*\])");
return std::regex_replace(translatedSource, sampleMaskDeclareRegex, sampleMaskReplaceStr);
}
// Customized spirv-cross compiler
class SpirvToMslCompiler : public spirv_cross::CompilerMSL
{
@ -210,7 +225,7 @@ class SpirvToMslCompiler : public spirv_cross::CompilerMSL
spirv_cross::CompilerMSL::set_msl_options(compOpt);
// Actual compilation
std::string translatedMsl = spirv_cross::CompilerMSL::compile();
std::string translatedMsl = PostProcessTranslatedMsl(spirv_cross::CompilerMSL::compile());
// Retrieve automatic texture slot assignments
GetAssignedSamplerBindings(*this, originalSamplerBindings,

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

@ -15,6 +15,7 @@
#include "libANGLE/angletypes.h"
#include "libANGLE/renderer/metal/mtl_command_buffer.h"
#include "libANGLE/renderer/metal/mtl_state_cache.h"
#include "libANGLE/renderer/metal/shaders/constants.h"
namespace rx
{
@ -129,6 +130,7 @@ class ColorBlitUtils
// Defer loading of render pipeline state cache.
void ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx,
int alphaPremultiplyType,
int textureType,
RenderPipelineCache *cacheOut);
void setupBlitWithDraw(const gl::Context *context,
@ -139,9 +141,13 @@ class ColorBlitUtils
RenderCommandEncoder *cmdEncoder,
const BlitParams &params);
RenderPipelineCache mBlitRenderPipelineCache;
RenderPipelineCache mBlitPremultiplyAlphaRenderPipelineCache;
RenderPipelineCache mBlitUnmultiplyAlphaRenderPipelineCache;
// Blit with draw pipeline caches:
// - array dimension: source texture type (2d, ms, array, 3d, etc)
using ColorBlitRenderPipelineCacheArray =
std::array<RenderPipelineCache, mtl_shader::kTextureTypeCount>;
ColorBlitRenderPipelineCacheArray mBlitRenderPipelineCache;
ColorBlitRenderPipelineCacheArray mBlitPremultiplyAlphaRenderPipelineCache;
ColorBlitRenderPipelineCacheArray mBlitUnmultiplyAlphaRenderPipelineCache;
};
// util class for generating index buffer
@ -231,6 +237,11 @@ class RenderUtils : public Context, angle::NonCopyable
angle::Result blitWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const BlitParams &params);
// Same as above but blit the whole texture to the whole of current framebuffer.
// This function assumes the framebuffer and the source texture have same size.
angle::Result blitWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const TextureRef &srcTexture);
// See IndexGeneratorUtils
angle::Result convertIndexBufferGPU(ContextMtl *contextMtl,

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

@ -29,6 +29,9 @@ namespace
#define SOURCE_IDX_IS_U8_CONSTANT_NAME @"kSourceIndexIsU8"
#define SOURCE_IDX_IS_U16_CONSTANT_NAME @"kSourceIndexIsU16"
#define SOURCE_IDX_IS_U32_CONSTANT_NAME @"kSourceIndexIsU32"
#define PREMULTIPLY_ALPHA_CONSTANT_NAME @"kPremultiplyAlpha"
#define UNMULTIPLY_ALPHA_CONSTANT_NAME @"kUnmultiplyAlpha"
#define SOURCE_TEXTURE_TYPE_CONSTANT_NAME @"kSourceTextureType"
// See libANGLE/renderer/metal/shaders/clear.metal
struct ClearParamsUniform
@ -44,10 +47,12 @@ struct BlitParamsUniform
// 0: lower left, 1: lower right, 2: upper left
float srcTexCoords[3][2];
int srcLevel = 0;
uint8_t srcLuminance = 0; // source texture is luminance texture
int srcLayer = 0;
uint8_t dstFlipX = 0;
uint8_t dstFlipY = 0;
uint8_t dstLuminance = 0; // dest texture is luminace
uint8_t padding;
uint8_t padding1;
float padding2[3];
};
struct IndexConversionUniform
@ -131,6 +136,31 @@ void GetFirstLastIndicesFromClientElements(GLsizei count,
memcpy(lastOut, indices + count - 1, sizeof(indices[0]));
}
int GetShaderTextureType(const TextureRef &texture)
{
if (!texture)
{
return -1;
}
switch (texture->textureType())
{
case MTLTextureType2D:
return mtl_shader::kTextureType2D;
case MTLTextureType2DArray:
return mtl_shader::kTextureType2DArray;
case MTLTextureType2DMultisample:
return mtl_shader::kTextureType2DMultisample;
case MTLTextureTypeCube:
return mtl_shader::kTextureTypeCube;
case MTLTextureType3D:
return mtl_shader::kTextureType3D;
default:
UNREACHABLE();
}
return 0;
}
ANGLE_INLINE
void EnsureComputePipelineInitialized(DisplayMtl *display,
NSString *functionName,
@ -207,6 +237,15 @@ void EnsureSpecializedComputePipelineInitialized(
}
}
template <typename T>
void ClearRenderPipelineCacheArray(T *pipelineCacheArray)
{
for (RenderPipelineCache &pipelineCache : *pipelineCacheArray)
{
pipelineCache.clear();
}
}
template <typename T>
void ClearPipelineStateArray(T *pipelineCacheArray)
{
@ -353,6 +392,20 @@ angle::Result RenderUtils::blitWithDraw(const gl::Context *context,
return mColorBlitUtils.blitWithDraw(context, cmdEncoder, params);
}
angle::Result RenderUtils::blitWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const TextureRef &srcTexture)
{
if (!srcTexture)
{
return angle::Result::Continue;
}
BlitParams params;
params.src = srcTexture;
params.srcRect = gl::Rectangle(0, 0, srcTexture->width(), srcTexture->height());
return blitWithDraw(context, cmdEncoder, params);
}
angle::Result RenderUtils::convertIndexBufferGPU(ContextMtl *contextMtl,
const IndexConversionParams &params)
{
@ -570,13 +623,14 @@ ColorBlitUtils::ColorBlitUtils() = default;
void ColorBlitUtils::onDestroy()
{
mBlitRenderPipelineCache.clear();
mBlitPremultiplyAlphaRenderPipelineCache.clear();
mBlitUnmultiplyAlphaRenderPipelineCache.clear();
ClearRenderPipelineCacheArray(&mBlitRenderPipelineCache);
ClearRenderPipelineCacheArray(&mBlitPremultiplyAlphaRenderPipelineCache);
ClearRenderPipelineCacheArray(&mBlitUnmultiplyAlphaRenderPipelineCache);
}
void ColorBlitUtils::ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx,
int alphaPremultiplyType,
int textureType,
RenderPipelineCache *cacheOut)
{
RenderPipelineCache &pipelineCache = *cacheOut;
@ -588,24 +642,43 @@ void ColorBlitUtils::ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx,
ANGLE_MTL_OBJC_SCOPE
{
NSString *const fragmentShaderNames[] = {// Normal blit
@"blitFS",
// Blit premultiply-alpha
@"blitPremultiplyAlphaFS",
// Blit unmultiply alpha
@"blitUnmultiplyAlphaFS"};
NSError *err = nil;
id<MTLLibrary> shaderLib = ctx->getDisplay()->getDefaultShadersLib();
id<MTLFunction> vertexShader =
[[shaderLib newFunctionWithName:@"blitVS"] ANGLE_MTL_AUTORELEASE];
id<MTLFunction> fragmentShader = [[shaderLib
newFunctionWithName:fragmentShaderNames[alphaPremultiplyType]] ANGLE_MTL_AUTORELEASE];
MTLFunctionConstantValues *funcConstants =
[[[MTLFunctionConstantValues alloc] init] ANGLE_MTL_AUTORELEASE];
constexpr BOOL multiplyAlphaFlags[][2] = {// premultiply, unmultiply
// Normal blit
{NO, NO},
// Blit premultiply-alpha
{YES, NO},
// Blit unmultiply alpha
{NO, YES}};
// Set alpha multiply flags
[funcConstants setConstantValue:&multiplyAlphaFlags[alphaPremultiplyType][0]
type:MTLDataTypeBool
withName:PREMULTIPLY_ALPHA_CONSTANT_NAME];
[funcConstants setConstantValue:&multiplyAlphaFlags[alphaPremultiplyType][1]
type:MTLDataTypeBool
withName:UNMULTIPLY_ALPHA_CONSTANT_NAME];
// Set texture type constant
[funcConstants setConstantValue:&textureType
type:MTLDataTypeInt
withName:SOURCE_TEXTURE_TYPE_CONSTANT_NAME];
id<MTLFunction> fragmentShader =
[[shaderLib newFunctionWithName:@"blitFS" constantValues:funcConstants
error:&err] ANGLE_MTL_AUTORELEASE];
ASSERT(vertexShader);
ASSERT(fragmentShader);
mBlitRenderPipelineCache.setVertexShader(ctx, vertexShader);
mBlitRenderPipelineCache.setFragmentShader(ctx, fragmentShader);
pipelineCache.setVertexShader(ctx, vertexShader);
pipelineCache.setFragmentShader(ctx, fragmentShader);
}
}
@ -625,23 +698,25 @@ id<MTLRenderPipelineState> ColorBlitUtils::getBlitRenderPipelineState(
RenderPipelineCache *pipelineCache;
int alphaPremultiplyType;
int textureType = GetShaderTextureType(params.src);
if (params.unpackPremultiplyAlpha == params.unpackUnmultiplyAlpha)
{
alphaPremultiplyType = 0;
pipelineCache = &mBlitRenderPipelineCache;
pipelineCache = &mBlitRenderPipelineCache[textureType];
}
else if (params.unpackPremultiplyAlpha)
{
alphaPremultiplyType = 1;
pipelineCache = &mBlitPremultiplyAlphaRenderPipelineCache;
pipelineCache = &mBlitPremultiplyAlphaRenderPipelineCache[textureType];
}
else
{
alphaPremultiplyType = 2;
pipelineCache = &mBlitUnmultiplyAlphaRenderPipelineCache;
pipelineCache = &mBlitUnmultiplyAlphaRenderPipelineCache[textureType];
}
ensureRenderPipelineStateCacheInitialized(contextMtl, alphaPremultiplyType, pipelineCache);
ensureRenderPipelineStateCacheInitialized(contextMtl, alphaPremultiplyType, textureType,
pipelineCache);
return pipelineCache->getRenderPipelineState(contextMtl, pipelineDesc);
}

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

@ -111,6 +111,15 @@ class Texture final : public Resource,
bool allowTextureView,
TextureRef *refOut);
static angle::Result Make2DMSTexture(ContextMtl *context,
const Format &format,
uint32_t width,
uint32_t height,
uint32_t samples,
bool renderTargetOnly,
bool allowTextureView,
TextureRef *refOut);
static TextureRef MakeFromMetal(id<MTLTexture> metalTexture);
// Allow CPU to read & write data directly to this texture?
@ -148,6 +157,8 @@ class Texture final : public Resource,
gl::Extents size(uint32_t level = 0) const;
gl::Extents size(const gl::ImageIndex &index) const;
uint32_t samples() const;
// For render target
MTLColorWriteMask getColorWritableMask() const { return *mColorWritableMask; }
void setColorWritableMask(MTLColorWriteMask mask) { *mColorWritableMask = mask; }

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

@ -147,6 +147,38 @@ angle::Result Texture::MakeCubeTexture(ContextMtl *context,
return angle::Result::Continue;
}
/** static */
angle::Result Texture::Make2DMSTexture(ContextMtl *context,
const Format &format,
uint32_t width,
uint32_t height,
uint32_t samples,
bool renderTargetOnly,
bool allowTextureView,
TextureRef *refOut)
{
ANGLE_MTL_OBJC_SCOPE
{
MTLTextureDescriptor *desc = [[MTLTextureDescriptor new] ANGLE_MTL_AUTORELEASE];
desc.textureType = MTLTextureType2DMultisample;
desc.pixelFormat = format.metalFormat;
desc.width = width;
desc.height = height;
desc.mipmapLevelCount = 1;
desc.sampleCount = samples;
SetTextureSwizzle(context, format, desc);
refOut->reset(new Texture(context, desc, 1, renderTargetOnly, allowTextureView));
} // ANGLE_MTL_OBJC_SCOPE
if (!refOut || !refOut->get())
{
ANGLE_MTL_CHECK(context, false, GL_OUT_OF_MEMORY);
}
return angle::Result::Continue;
}
/** static */
TextureRef Texture::MakeFromMetal(id<MTLTexture> metalTexture)
{
@ -183,7 +215,8 @@ Texture::Texture(ContextMtl *context,
desc.usage |= MTLTextureUsageRenderTarget;
}
if (!Format::FormatCPUReadable(desc.pixelFormat))
if (!Format::FormatCPUReadable(desc.pixelFormat) ||
desc.textureType == MTLTextureType2DMultisample)
{
desc.resourceOptions = MTLResourceStorageModePrivate;
}
@ -405,6 +438,11 @@ gl::Extents Texture::size(const gl::ImageIndex &index) const
return size(index.getLevelIndex());
}
uint32_t Texture::samples() const
{
return static_cast<uint32_t>(get().sampleCount);
}
void Texture::set(id<MTLTexture> metalTexture)
{
ParentClass::set(metalTexture);

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

@ -214,6 +214,7 @@ struct RenderPipelineOutputDesc
static_assert(kMaxRenderTargets <= 4, "kMaxRenderTargets must be <= 4");
uint8_t numColorAttachments : 3;
uint8_t sampleCount : 5;
};
// Some SDK levels don't declare MTLPrimitiveTopologyClass. Needs to do compile time check here:
@ -249,7 +250,13 @@ struct alignas(4) RenderPipelineDesc
// Use uint8_t instead of PrimitiveTopologyClass to compact space.
uint8_t inputPrimitiveTopology : 2;
bool rasterizationEnabled;
bool rasterizationEnabled : 1;
bool alphaToCoverageEnabled : 1;
// These flags are for emulation and do not correspond to any flags in
// MTLRenderPipelineDescriptor descriptor. These flags should be used by
// RenderPipelineCacheSpecializeShaderFactory.
bool emulateCoverageMask : 1;
};
struct RenderPassAttachmentDesc
@ -261,9 +268,15 @@ struct RenderPassAttachmentDesc
bool equalIgnoreLoadStoreOptions(const RenderPassAttachmentDesc &other) const;
bool operator==(const RenderPassAttachmentDesc &other) const;
ANGLE_INLINE bool hasImplicitMSTexture() const { return implicitMSTexture.get(); }
TextureRef texture;
// Implicit multisample texture that will be rendered into and discarded at the end of
// a render pass. Its result will be resolved into normal texture above.
TextureRef implicitMSTexture;
uint32_t level;
uint32_t slice;
uint32_t sliceOrDepth;
MTLLoadAction loadAction;
MTLStoreAction storeAction;
MTLStoreActionOptions storeActionOptions;
@ -333,6 +346,7 @@ struct RenderPassDesc
inline bool operator!=(const RenderPassDesc &other) const { return !(*this == other); }
uint32_t numColorAttachments = 0;
uint32_t sampleCount = 1;
};
} // namespace mtl
@ -365,18 +379,43 @@ namespace rx
{
namespace mtl
{
// render pipeline state cache per shader program
// Abstract factory to create specialized vertex & fragment shaders based on RenderPipelineDesc.
class RenderPipelineCacheSpecializeShaderFactory
{
public:
virtual ~RenderPipelineCacheSpecializeShaderFactory() = default;
// Get specialized shader for the render pipeline cache.
virtual angle::Result getSpecializedShader(Context *context,
gl::ShaderType shaderType,
const RenderPipelineDesc &renderPipelineDesc,
id<MTLFunction> *shaderOut) = 0;
// Check whether specialized shaders is required for the specified RenderPipelineDesc.
// If not, the render pipeline cache will use the supplied non-specialized shaders.
virtual bool hasSpecializedShader(gl::ShaderType shaderType,
const RenderPipelineDesc &renderPipelineDesc) = 0;
};
// Render pipeline state cache per shader program.
class RenderPipelineCache final : angle::NonCopyable
{
public:
RenderPipelineCache();
RenderPipelineCache(RenderPipelineCacheSpecializeShaderFactory *specializedShaderFactory);
~RenderPipelineCache();
// Set non-specialized vertex/fragment shader to be used by render pipeline cache to create
// render pipeline state. If the internal
// RenderPipelineCacheSpecializeShaderFactory.hasSpecializedShader() returns false for a
// particular RenderPipelineDesc, the render pipeline cache will use the non-specialized
// shaders.
void setVertexShader(Context *context, id<MTLFunction> shader);
void setFragmentShader(Context *context, id<MTLFunction> shader);
id<MTLFunction> getVertexShader() { return mVertexShader.get(); }
id<MTLFunction> getFragmentShader() { return mFragmentShader.get(); }
// Get non-specialized shaders supplied via set*Shader().
id<MTLFunction> getVertexShader() { return mVertexShader; }
id<MTLFunction> getFragmentShader() { return mFragmentShader; }
AutoObjCPtr<id<MTLRenderPipelineState>> getRenderPipelineState(ContextMtl *context,
const RenderPipelineDesc &desc);
@ -384,8 +423,10 @@ class RenderPipelineCache final : angle::NonCopyable
void clear();
protected:
AutoObjCPtr<id<MTLFunction>> mVertexShader = nil;
AutoObjCPtr<id<MTLFunction>> mFragmentShader = nil;
// Non-specialized vertex shader
AutoObjCPtr<id<MTLFunction>> mVertexShader;
// Non-specialized fragment shader
AutoObjCPtr<id<MTLFunction>> mFragmentShader;
private:
void clearPipelineStates();
@ -404,6 +445,8 @@ class RenderPipelineCache final : angle::NonCopyable
// One table with default attrib and one table without.
std::unordered_map<RenderPipelineDesc, AutoObjCPtr<id<MTLRenderPipelineState>>>
mRenderPipelineStates[2];
RenderPipelineCacheSpecializeShaderFactory *mSpecializedShaderFactory;
};
class StateCache final : angle::NonCopyable

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

@ -156,11 +156,13 @@ MTLRenderPipelineDescriptor *ToObjC(id<MTLFunction> vertexShader,
}
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc.outputDescriptor, depthAttachmentPixelFormat);
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc.outputDescriptor, stencilAttachmentPixelFormat);
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc.outputDescriptor, sampleCount);
#if ANGLE_MTL_PRIMITIVE_TOPOLOGY_CLASS_AVAILABLE
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, inputPrimitiveTopology);
#endif
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, rasterizationEnabled);
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, alphaToCoverageEnabled);
return [objCDesc ANGLE_MTL_AUTORELEASE];
}
@ -174,9 +176,44 @@ id<MTLTexture> ToObjC(const TextureRef &texture)
void BaseRenderPassAttachmentDescToObjC(const RenderPassAttachmentDesc &src,
MTLRenderPassAttachmentDescriptor *dst)
{
ANGLE_OBJC_CP_PROPERTY(dst, src, texture);
ANGLE_OBJC_CP_PROPERTY(dst, src, level);
ANGLE_OBJC_CP_PROPERTY(dst, src, slice);
const TextureRef &implicitMsTexture = src.implicitMSTexture;
if (implicitMsTexture)
{
dst.texture = ToObjC(implicitMsTexture);
dst.level = 0;
dst.slice = 0;
dst.resolveTexture = ToObjC(src.texture);
dst.resolveLevel = src.level;
if (dst.resolveTexture.textureType == MTLTextureType3D)
{
dst.resolveDepthPlane = src.sliceOrDepth;
dst.resolveSlice = 0;
}
else
{
dst.resolveSlice = src.sliceOrDepth;
dst.resolveDepthPlane = 0;
}
}
else
{
dst.texture = ToObjC(src.texture);
dst.level = src.level;
if (dst.texture.textureType == MTLTextureType3D)
{
dst.depthPlane = src.sliceOrDepth;
dst.slice = 0;
}
else
{
dst.slice = src.sliceOrDepth;
dst.depthPlane = 0;
}
dst.resolveTexture = nil;
dst.resolveLevel = 0;
dst.resolveSlice = 0;
}
ANGLE_OBJC_CP_PROPERTY(dst, src, loadAction);
ANGLE_OBJC_CP_PROPERTY(dst, src, storeAction);
@ -585,7 +622,8 @@ bool RenderPipelineOutputDesc::operator==(const RenderPipelineOutputDesc &rhs) c
RenderPipelineDesc::RenderPipelineDesc()
{
memset(this, 0, sizeof(*this));
rasterizationEnabled = true;
outputDescriptor.sampleCount = 1;
rasterizationEnabled = true;
}
RenderPipelineDesc::RenderPipelineDesc(const RenderPipelineDesc &src)
@ -626,8 +664,9 @@ RenderPassAttachmentDesc::RenderPassAttachmentDesc()
void RenderPassAttachmentDesc::reset()
{
texture.reset();
implicitMSTexture.reset();
level = 0;
slice = 0;
sliceOrDepth = 0;
loadAction = MTLLoadActionLoad;
storeAction = MTLStoreActionStore;
storeActionOptions = MTLStoreActionOptionNone;
@ -636,7 +675,8 @@ void RenderPassAttachmentDesc::reset()
bool RenderPassAttachmentDesc::equalIgnoreLoadStoreOptions(
const RenderPassAttachmentDesc &other) const
{
return texture == other.texture && level == other.level && slice == other.slice;
return texture == other.texture && implicitMSTexture == other.implicitMSTexture &&
level == other.level && sliceOrDepth == other.sliceOrDepth;
}
bool RenderPassAttachmentDesc::operator==(const RenderPassAttachmentDesc &other) const
@ -668,8 +708,9 @@ void RenderPassDesc::populateRenderPipelineOutputDesc(MTLColorWriteMask colorWri
void RenderPassDesc::populateRenderPipelineOutputDesc(const BlendDesc &blendState,
RenderPipelineOutputDesc *outDesc) const
{
auto &outputDescriptor = *outDesc;
outputDescriptor.numColorAttachments = this->numColorAttachments;
RenderPipelineOutputDesc &outputDescriptor = *outDesc;
outputDescriptor.numColorAttachments = this->numColorAttachments;
outputDescriptor.sampleCount = this->sampleCount;
for (uint32_t i = 0; i < this->numColorAttachments; ++i)
{
auto &renderPassColorAttachment = this->colorAttachments[i];
@ -694,6 +735,12 @@ void RenderPassDesc::populateRenderPipelineOutputDesc(const BlendDesc &blendStat
}
}
// Reset the unused output slots to ensure consistent hash value
for (uint32_t i = this->numColorAttachments; i < kMaxRenderTargets; ++i)
{
outputDescriptor.colorAttachments[i].reset();
}
auto depthTexture = this->depthAttachment.texture;
outputDescriptor.depthAttachmentPixelFormat =
depthTexture ? depthTexture->pixelFormat() : MTLPixelFormatInvalid;
@ -770,7 +817,12 @@ void RenderPassDesc::convertToMetalDesc(MTLRenderPassDescriptor *objCDesc) const
}
// RenderPipelineCache implementation
RenderPipelineCache::RenderPipelineCache() {}
RenderPipelineCache::RenderPipelineCache() : RenderPipelineCache(nullptr) {}
RenderPipelineCache::RenderPipelineCache(
RenderPipelineCacheSpecializeShaderFactory *specializedShaderFactory)
: mSpecializedShaderFactory(specializedShaderFactory)
{}
RenderPipelineCache::~RenderPipelineCache() {}
@ -837,6 +889,10 @@ AutoObjCPtr<id<MTLRenderPipelineState>> RenderPipelineCache::insertRenderPipelin
{
AutoObjCPtr<id<MTLRenderPipelineState>> newState =
createRenderPipelineState(context, desc, insertDefaultAttribLayout);
if (!newState)
{
return nil;
}
int tableIdx = insertDefaultAttribLayout ? 1 : 0;
auto re = mRenderPipelineStates[tableIdx].insert(std::make_pair(desc, newState));
@ -850,16 +906,66 @@ AutoObjCPtr<id<MTLRenderPipelineState>> RenderPipelineCache::insertRenderPipelin
AutoObjCPtr<id<MTLRenderPipelineState>> RenderPipelineCache::createRenderPipelineState(
Context *context,
const RenderPipelineDesc &desc,
const RenderPipelineDesc &originalDesc,
bool insertDefaultAttribLayout)
{
ANGLE_MTL_OBJC_SCOPE
{
auto metalDevice = context->getMetalDevice();
AutoObjCObj<MTLRenderPipelineDescriptor> objCDesc =
ToObjC(mVertexShader, mFragmentShader, desc);
// Disable coverage if the render pipeline's sample count is only 1.
RenderPipelineDesc desc = originalDesc;
if (desc.outputDescriptor.sampleCount == 1)
{
// Disable sample coverage if the output is not multisample
desc.emulateCoverageMask = false;
desc.alphaToCoverageEnabled = false;
}
// special attribute slot for default attribute
// Choose shader variant
id<MTLFunction> vertShader = nil;
id<MTLFunction> fragShader = nil;
if (mSpecializedShaderFactory &&
mSpecializedShaderFactory->hasSpecializedShader(gl::ShaderType::Vertex, desc))
{
if (IsError(mSpecializedShaderFactory->getSpecializedShader(
context, gl::ShaderType::Vertex, desc, &vertShader)))
{
return nil;
}
}
else
{
// Non-specialized version
vertShader = mVertexShader;
}
if (mSpecializedShaderFactory &&
mSpecializedShaderFactory->hasSpecializedShader(gl::ShaderType::Fragment, desc))
{
if (IsError(mSpecializedShaderFactory->getSpecializedShader(
context, gl::ShaderType::Fragment, desc, &fragShader)))
{
return nil;
}
}
else
{
// Non-specialized version
fragShader = mFragmentShader;
}
if (!vertShader)
{
// Render pipeline without vertex shader is invalid.
context->handleError(GL_INVALID_OPERATION, __FILE__, ANGLE_FUNCTION, __LINE__);
return nil;
}
id<MTLDevice> metalDevice = context->getMetalDevice();
// Convert to Objective-C desc:
AutoObjCObj<MTLRenderPipelineDescriptor> objCDesc = ToObjC(vertShader, fragShader, desc);
// Special attribute slot for default attribute
if (insertDefaultAttribLayout)
{
MTLVertexBufferLayoutDescriptor *defaultAttribLayoutObjCDesc =
@ -873,8 +979,9 @@ AutoObjCPtr<id<MTLRenderPipelineState>> RenderPipelineCache::createRenderPipelin
atIndexedSubscript:kDefaultAttribsBindingIndex];
}
// Create pipeline state
NSError *err = nil;
auto newState = [metalDevice newRenderPipelineStateWithDescriptor:objCDesc error:&err];
NSError *err = nil;
id<MTLRenderPipelineState> newState =
[metalDevice newRenderPipelineStateWithDescriptor:objCDesc error:&err];
if (err)
{
context->handleError(err, __FILE__, ANGLE_FUNCTION, __LINE__);

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

@ -7,15 +7,28 @@
#include "common.h"
using namespace rx::mtl_shader;
// function_constant(0-3) is already used by gen_indices.metal
constant bool kPremultiplyAlpha [[function_constant(4)]];
constant bool kUnmultiplyAlpha [[function_constant(5)]];
constant int kSourceTextureType [[function_constant(6)]]; // Source texture type.
constant bool kSourceTextureType2D = kSourceTextureType == kTextureType2D;
constant bool kSourceTextureType2DArray = kSourceTextureType == kTextureType2DArray;
constant bool kSourceTextureType2DMS = kSourceTextureType == kTextureType2DMultisample;
constant bool kSourceTextureTypeCube = kSourceTextureType == kTextureTypeCube;
constant bool kSourceTextureType3D = kSourceTextureType == kTextureType3D;
struct BlitParams
{
// 0: lower left, 1: lower right, 2: upper left
float2 srcTexCoords[3];
int srcLevel;
bool srcLuminance; // source texture is luminance texture
int srcLevel; // Source texture level.
int srcLayer; // Source texture layer.
bool dstFlipViewportX;
bool dstFlipViewportY;
bool dstLuminance; // destination texture is luminance;
bool dstLuminance; // destination texture is luminance. Unused by depth & stencil blitting.
};
struct BlitVSOut
@ -24,13 +37,16 @@ struct BlitVSOut
float2 texCoords [[user(locn1)]];
};
vertex BlitVSOut blitVS(unsigned int vid [[ vertex_id ]],
constant BlitParams &options [[buffer(0)]])
vertex BlitVSOut blitVS(unsigned int vid [[vertex_id]], constant BlitParams &options [[buffer(0)]])
{
BlitVSOut output;
output.position = float4(gCorners[vid], 0.0, 1.0);
output.texCoords = options.srcTexCoords[vid];
if (options.dstFlipViewportX)
{
output.position.x = -output.position.x;
}
if (!options.dstFlipViewportY)
{
// If viewport is not flipped, we have to flip Y in normalized device coordinates.
@ -41,58 +57,124 @@ vertex BlitVSOut blitVS(unsigned int vid [[ vertex_id ]],
return output;
}
float4 blitSampleTexture(texture2d<float> srcTexture,
float2 texCoords,
constant BlitParams &options)
static inline float3 cubeTexcoords(float2 texcoords, int face)
{
constexpr sampler textureSampler(mag_filter::linear,
min_filter::linear);
float4 output = srcTexture.sample(textureSampler, texCoords, level(options.srcLevel));
if (options.srcLuminance)
texcoords = 2.0 * texcoords - 1.0;
switch (face)
{
output.gb = float2(output.r, output.r);
case 0:
return float3(1.0, -texcoords.y, -texcoords.x);
case 1:
return float3(-1.0, -texcoords.y, texcoords.x);
case 2:
return float3(texcoords.x, 1.0, texcoords.y);
case 3:
return float3(texcoords.x, -1.0, -texcoords.y);
case 4:
return float3(texcoords.x, -texcoords.y, 1.0);
case 5:
return float3(-texcoords.x, -texcoords.y, -1.0);
}
return float3(texcoords, 0);
}
template <typename T>
static inline vec<T, 4> blitSampleTextureMS(texture2d_ms<T> srcTexture, float2 texCoords)
{
uint2 dimens(srcTexture.get_width(), srcTexture.get_height());
uint2 coords = uint2(texCoords * float2(dimens));
uint samples = srcTexture.get_num_samples();
vec<T, 4> output(0);
for (uint sample = 0; sample < samples; ++sample)
{
output += srcTexture.read(coords, sample);
}
output = output / samples;
return output;
}
template <typename T>
static inline vec<T, 4> blitSampleTexture3D(texture3d<T> srcTexture,
sampler textureSampler,
float2 texCoords,
constant BlitParams &options)
{
uint depth = srcTexture.get_depth(options.srcLevel);
float zCoord = (float(options.srcLayer) + 0.5) / float(depth);
return srcTexture.sample(textureSampler, float3(texCoords, zCoord), level(options.srcLevel));
}
// clang-format off
#define BLIT_COLOR_FS_PARAMS(TYPE) \
BlitVSOut input [[stage_in]], \
texture2d<TYPE> srcTexture2d [[texture(0), function_constant(kSourceTextureType2D)]], \
texture2d_array<TYPE> srcTexture2dArray \
[[texture(0), function_constant(kSourceTextureType2DArray)]], \
texture2d_ms<TYPE> srcTexture2dMS [[texture(0), function_constant(kSourceTextureType2DMS)]], \
texturecube<TYPE> srcTextureCube [[texture(0), function_constant(kSourceTextureTypeCube)]], \
texture3d<TYPE> srcTexture3d [[texture(0), function_constant(kSourceTextureType3D)]], \
sampler textureSampler [[sampler(0)]], \
constant BlitParams &options [[buffer(0)]]
// clang-format on
#define FORWARD_BLIT_COLOR_FS_PARAMS \
input, srcTexture2d, srcTexture2dArray, srcTexture2dMS, srcTextureCube, srcTexture3d, \
textureSampler, options
template <typename T>
static inline vec<T, 4> blitReadTexture(BLIT_COLOR_FS_PARAMS(T))
{
vec<T, 4> output;
switch (kSourceTextureType)
{
case kTextureType2D:
output = srcTexture2d.sample(textureSampler, input.texCoords, level(options.srcLevel));
break;
case kTextureType2DArray:
output = srcTexture2dArray.sample(textureSampler, input.texCoords, options.srcLayer,
level(options.srcLevel));
break;
case kTextureType2DMultisample:
output = blitSampleTextureMS(srcTexture2dMS, input.texCoords);
break;
case kTextureTypeCube:
output = srcTextureCube.sample(textureSampler,
cubeTexcoords(input.texCoords, options.srcLayer),
level(options.srcLevel));
break;
case kTextureType3D:
output = blitSampleTexture3D(srcTexture3d, textureSampler, input.texCoords, options);
break;
}
if (kPremultiplyAlpha)
{
output.xyz *= output.a;
}
else if (kUnmultiplyAlpha)
{
if (output.a != 0.0)
{
output.xyz /= output.a;
}
}
if (options.dstLuminance)
{
output.g = output.b = output.r;
}
return output;
}
float4 blitOutput(float4 color, constant BlitParams &options)
fragment float4 blitFS(BLIT_COLOR_FS_PARAMS(float))
{
float4 ret = color;
if (options.dstLuminance)
{
ret.r = ret.g = ret.b = color.r;
}
return ret;
}
fragment float4 blitFS(BlitVSOut input [[stage_in]],
texture2d<float> srcTexture [[texture(0)]],
constant BlitParams &options [[buffer(0)]])
{
return blitOutput(blitSampleTexture(srcTexture, input.texCoords, options), options);
}
fragment float4 blitPremultiplyAlphaFS(BlitVSOut input [[stage_in]],
texture2d<float> srcTexture [[texture(0)]],
constant BlitParams &options [[buffer(0)]])
{
float4 output = blitSampleTexture(srcTexture, input.texCoords, options);
output.xyz *= output.a;
return blitOutput(output, options);
}
fragment float4 blitUnmultiplyAlphaFS(BlitVSOut input [[stage_in]],
texture2d<float> srcTexture [[texture(0)]],
constant BlitParams &options [[buffer(0)]])
{
float4 output = blitSampleTexture(srcTexture, input.texCoords, options);
if (output.a != 0.0)
{
output.xyz *= 1.0 / output.a;
}
return blitOutput(output, options);
return blitReadTexture(FORWARD_BLIT_COLOR_FS_PARAMS);
}

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

@ -13,6 +13,8 @@
# include <metal_stdlib>
#endif
#include "constants.h"
#define ANGLE_KERNEL_GUARD(IDX, MAX_COUNT) \
if (IDX >= MAX_COUNT) \
{ \

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -0,0 +1,29 @@
//
// Copyright 2020 The ANGLE Project. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// constants.h: Declare some constant values to be used by metal defaultshaders.
#ifndef LIBANGLE_RENDERER_METAL_SHADERS_ENUM_H_
#define LIBANGLE_RENDERER_METAL_SHADERS_ENUM_H_
namespace rx
{
namespace mtl_shader
{
enum
{
kTextureType2D = 0,
kTextureType2DMultisample = 1,
kTextureType2DArray = 2,
kTextureTypeCube = 3,
kTextureType3D = 4,
kTextureTypeCount = 5,
};
} // namespace mtl_shader
} // namespace rx
#endif

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

@ -26,7 +26,8 @@ def main():
# auto_script parameters.
if len(sys.argv) > 1:
inputs = [
'master_source.metal', 'blit.metal', 'clear.metal', 'gen_indices.metal', 'common.h'
'master_source.metal', 'blit.metal', 'clear.metal', 'gen_indices.metal', 'common.h',
'constants.h'
]
outputs = ['compiled/mtl_default_shaders.inc', 'mtl_default_shaders_src_autogen.inc']

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

@ -38,7 +38,34 @@ constexpr char default_metallib_src[] = R"(
# 1 "./common.h" 1
# 22 "./common.h"
# 16 "./common.h"
# 1 "./constants.h" 1
# 11 "./constants.h"
namespace rx
{
namespace mtl_shader
{
enum
{
kTextureType2D = 0,
kTextureType2DMultisample = 1,
kTextureType2DArray = 2,
kTextureTypeCube = 3,
kTextureType3D = 4,
kTextureTypeCount = 5,
};
}
}
# 17 "./common.h" 2
using namespace metal;
@ -68,13 +95,27 @@ fragment float4 clearFS(constant ClearParams &clearParams [[buffer(0)]])
}
# 10 "master_source.metal" 2
# 1 "./blit.metal" 1
# 11 "./blit.metal"
# 10 "./blit.metal"
using namespace rx::mtl_shader;
constant bool kPremultiplyAlpha [[function_constant(4)]];
constant bool kUnmultiplyAlpha [[function_constant(5)]];
constant int kSourceTextureType [[function_constant(6)]];
constant bool kSourceTextureType2D = kSourceTextureType == kTextureType2D;
constant bool kSourceTextureType2DArray = kSourceTextureType == kTextureType2DArray;
constant bool kSourceTextureType2DMS = kSourceTextureType == kTextureType2DMultisample;
constant bool kSourceTextureTypeCube = kSourceTextureType == kTextureTypeCube;
constant bool kSourceTextureType3D = kSourceTextureType == kTextureType3D;
struct BlitParams
{
float2 srcTexCoords[3];
int srcLevel;
bool srcLuminance;
int srcLayer;
bool dstFlipViewportX;
bool dstFlipViewportY;
bool dstLuminance;
};
@ -85,13 +126,16 @@ struct BlitVSOut
float2 texCoords [[user(locn1)]];
};
vertex BlitVSOut blitVS(unsigned int vid [[ vertex_id ]],
constant BlitParams &options [[buffer(0)]])
vertex BlitVSOut blitVS(unsigned int vid [[vertex_id]], constant BlitParams &options [[buffer(0)]])
{
BlitVSOut output;
output.position = float4(gCorners[vid], 0.0, 1.0);
output.texCoords = options.srcTexCoords[vid];
if (options.dstFlipViewportX)
{
output.position.x = -output.position.x;
}
if (!options.dstFlipViewportY)
{
@ -102,60 +146,109 @@ vertex BlitVSOut blitVS(unsigned int vid [[ vertex_id ]],
return output;
}
float4 blitSampleTexture(texture2d<float> srcTexture,
float2 texCoords,
constant BlitParams &options)
static inline float3 cubeTexcoords(float2 texcoords, int face)
{
constexpr sampler textureSampler(mag_filter::linear,
min_filter::linear);
float4 output = srcTexture.sample(textureSampler, texCoords, level(options.srcLevel));
if (options.srcLuminance)
texcoords = 2.0 * texcoords - 1.0;
switch (face)
{
output.gb = float2(output.r, output.r);
case 0:
return float3(1.0, -texcoords.y, -texcoords.x);
case 1:
return float3(-1.0, -texcoords.y, texcoords.x);
case 2:
return float3(texcoords.x, 1.0, texcoords.y);
case 3:
return float3(texcoords.x, -1.0, -texcoords.y);
case 4:
return float3(texcoords.x, -texcoords.y, 1.0);
case 5:
return float3(-texcoords.x, -texcoords.y, -1.0);
}
return float3(texcoords, 0);
}
template <typename T>
static inline vec<T, 4> blitSampleTextureMS(texture2d_ms<T> srcTexture, float2 texCoords)
{
uint2 dimens(srcTexture.get_width(), srcTexture.get_height());
uint2 coords = uint2(texCoords * float2(dimens));
uint samples = srcTexture.get_num_samples();
vec<T, 4> output(0);
for (uint sample = 0; sample < samples; ++sample)
{
output += srcTexture.read(coords, sample);
}
output = output / samples;
return output;
}
template <typename T>
static inline vec<T, 4> blitSampleTexture3D(texture3d<T> srcTexture,
sampler textureSampler,
float2 texCoords,
constant BlitParams &options)
{
uint depth = srcTexture.get_depth(options.srcLevel);
float zCoord = (float(options.srcLayer) + 0.5) / float(depth);
return srcTexture.sample(textureSampler, float3(texCoords, zCoord), level(options.srcLevel));
}
# 130 "./blit.metal"
template <typename T>
static inline vec<T, 4> blitReadTexture(BlitVSOut input [[stage_in]], texture2d<T> srcTexture2d [[texture(0), function_constant(kSourceTextureType2D)]], texture2d_array<T> srcTexture2dArray [[texture(0), function_constant(kSourceTextureType2DArray)]], texture2d_ms<T> srcTexture2dMS [[texture(0), function_constant(kSourceTextureType2DMS)]], texturecube<T> srcTextureCube [[texture(0), function_constant(kSourceTextureTypeCube)]], texture3d<T> srcTexture3d [[texture(0), function_constant(kSourceTextureType3D)]], sampler textureSampler [[sampler(0)]], constant BlitParams &options [[buffer(0)]])
{
vec<T, 4> output;
switch (kSourceTextureType)
{
case kTextureType2D:
output = srcTexture2d.sample(textureSampler, input.texCoords, level(options.srcLevel));
break;
case kTextureType2DArray:
output = srcTexture2dArray.sample(textureSampler, input.texCoords, options.srcLayer,
level(options.srcLevel));
break;
case kTextureType2DMultisample:
output = blitSampleTextureMS(srcTexture2dMS, input.texCoords);
break;
case kTextureTypeCube:
output = srcTextureCube.sample(textureSampler,
cubeTexcoords(input.texCoords, options.srcLayer),
level(options.srcLevel));
break;
case kTextureType3D:
output = blitSampleTexture3D(srcTexture3d, textureSampler, input.texCoords, options);
break;
}
if (kPremultiplyAlpha)
{
output.xyz *= output.a;
}
else if (kUnmultiplyAlpha)
{
if (output.a != 0.0)
{
output.xyz /= output.a;
}
}
if (options.dstLuminance)
{
output.g = output.b = output.r;
}
return output;
}
float4 blitOutput(float4 color, constant BlitParams &options)
fragment float4 blitFS(BlitVSOut input [[stage_in]], texture2d<float> srcTexture2d [[texture(0), function_constant(kSourceTextureType2D)]], texture2d_array<float> srcTexture2dArray [[texture(0), function_constant(kSourceTextureType2DArray)]], texture2d_ms<float> srcTexture2dMS [[texture(0), function_constant(kSourceTextureType2DMS)]], texturecube<float> srcTextureCube [[texture(0), function_constant(kSourceTextureTypeCube)]], texture3d<float> srcTexture3d [[texture(0), function_constant(kSourceTextureType3D)]], sampler textureSampler [[sampler(0)]], constant BlitParams &options [[buffer(0)]])
{
float4 ret = color;
if (options.dstLuminance)
{
ret.r = ret.g = ret.b = color.r;
}
return ret;
}
fragment float4 blitFS(BlitVSOut input [[stage_in]],
texture2d<float> srcTexture [[texture(0)]],
constant BlitParams &options [[buffer(0)]])
{
return blitOutput(blitSampleTexture(srcTexture, input.texCoords, options), options);
}
fragment float4 blitPremultiplyAlphaFS(BlitVSOut input [[stage_in]],
texture2d<float> srcTexture [[texture(0)]],
constant BlitParams &options [[buffer(0)]])
{
float4 output = blitSampleTexture(srcTexture, input.texCoords, options);
output.xyz *= output.a;
return blitOutput(output, options);
}
fragment float4 blitUnmultiplyAlphaFS(BlitVSOut input [[stage_in]],
texture2d<float> srcTexture [[texture(0)]],
constant BlitParams &options [[buffer(0)]])
{
float4 output = blitSampleTexture(srcTexture, input.texCoords, options);
if (output.a != 0.0)
{
output.xyz *= 1.0 / output.a;
}
return blitOutput(output, options);
return blitReadTexture(input, srcTexture2d, srcTexture2dArray, srcTexture2dMS, srcTextureCube, srcTexture3d, textureSampler, options);
}
# 11 "master_source.metal" 2
# 1 "./gen_indices.metal" 1

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

@ -16,15 +16,39 @@ using namespace angle;
namespace
{
class MultisampleTest : public ANGLETest
using MultisampleTestParams = std::tuple<angle::PlatformParameters, bool>;
std::string PrintToStringParamName(const ::testing::TestParamInfo<MultisampleTestParams> &info)
{
::std::stringstream ss;
ss << std::get<0>(info.param);
if (std::get<1>(info.param))
{
ss << "__NoStoreAndResolve";
}
return ss.str();
}
class MultisampleTest : public ANGLETestWithParam<MultisampleTestParams>
{
protected:
void testSetUp() override
{
const angle::PlatformParameters platform = ::testing::get<0>(GetParam());
std::vector<const char *> disabledFeatures;
if (::testing::get<1>(GetParam()))
{
disabledFeatures.push_back("allow_msaa_store_and_resolve");
}
disabledFeatures.push_back(nullptr);
// Get display.
EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(), EGL_NONE};
mDisplay = eglGetPlatformDisplayEXT(
EGL_PLATFORM_ANGLE_ANGLE, reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
EGLAttrib dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, platform.getRenderer(),
EGL_FEATURE_OVERRIDES_DISABLED_ANGLE,
reinterpret_cast<EGLAttrib>(disabledFeatures.data()), EGL_NONE};
mDisplay = eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE,
reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
ASSERT_TRUE(mDisplay != EGL_NO_DISPLAY);
ASSERT_TRUE(eglInitialize(mDisplay, nullptr, nullptr) == EGL_TRUE);
@ -57,9 +81,9 @@ class MultisampleTest : public ANGLETest
EGLint contextAttributes[] = {
EGL_CONTEXT_MAJOR_VERSION_KHR,
GetParam().majorVersion,
platform.majorVersion,
EGL_CONTEXT_MINOR_VERSION_KHR,
GetParam().minorVersion,
platform.minorVersion,
EGL_NONE,
};
@ -267,6 +291,90 @@ TEST_P(MultisampleTest, Triangle)
}
}
// Test polygon rendering on a multisampled surface. And rendering is interrupted by a compute pass
// that converts the index buffer. Make sure the rendering's multisample result is preserved after
// interruption.
TEST_P(MultisampleTest, ContentPresevedAfterInterruption)
{
ANGLE_SKIP_TEST_IF(!mMultisampledConfigExists);
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_rgb8_rgba8"));
// http://anglebug.com/3470
ANGLE_SKIP_TEST_IF(IsAndroid() && IsNVIDIAShield() && IsOpenGLES());
// http://anglebug.com/4609
ANGLE_SKIP_TEST_IF(IsD3D11());
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
glUseProgram(program);
const GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
if (IsGLExtensionEnabled("GL_EXT_discard_framebuffer"))
{
GLenum attachments[] = {GL_COLOR_EXT, GL_DEPTH_EXT, GL_STENCIL_EXT};
glDiscardFramebufferEXT(GL_FRAMEBUFFER, 3, attachments);
}
// Draw triangle
GLBuffer vertexBuffer;
const Vector3 vertices[3] = {{-1.0f, -1.0f, 0.0f}, {-1.0f, 1.0f, 0.0f}, {1.0f, -1.0f, 0.0f}};
prepareVertexBuffer(vertexBuffer, vertices, 3, positionLocation);
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 3);
ASSERT_GL_NO_ERROR();
// Draw a line
GLBuffer vertexBuffer2;
GLBuffer indexBuffer2;
const Vector3 vertices2[2] = {{-1.0f, -0.3f, 0.0f}, {1.0f, 0.3f, 0.0f}};
const GLubyte indices2[] = {0, 1};
prepareVertexBuffer(vertexBuffer2, vertices2, 2, positionLocation);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer2);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices2), indices2, GL_STATIC_DRAW);
glDrawElements(GL_LINES, 2, GL_UNSIGNED_BYTE, 0);
ASSERT_GL_NO_ERROR();
// Top-left pixels should be all red.
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kWindowSize / 4, kWindowSize / 4, GLColor::red);
// Triangle edge:
// Diagonal pixels from bottom-left to top-right are between red and black. Pixels above the
// diagonal are red and pixels below it are black.
{
const GLColor kMidRed = {128, 0, 0, 128};
constexpr int kErrorMargin = 16;
for (int i = 1; i + 1 < kWindowSize; ++i)
{
// Exclude the middle pixel where the triangle and line cross each other.
if (abs(kWindowSize / 2 - i) <= 1)
{
continue;
}
int j = kWindowSize - 1 - i;
EXPECT_PIXEL_COLOR_NEAR(i, j, kMidRed, kErrorMargin);
EXPECT_PIXEL_COLOR_EQ(i, j - 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(i, j + 1, GLColor::transparentBlack);
}
}
// Line edge:
{
const GLColor kDarkRed = {128, 0, 0, 128};
constexpr int kErrorMargin = 16;
constexpr int kLargeMargin = 80;
static_assert(kWindowSize == 8, "Verification code written for 8x8 window");
// Exclude the triangle region.
EXPECT_PIXEL_COLOR_NEAR(5, 4, GLColor::red, kErrorMargin);
EXPECT_PIXEL_COLOR_NEAR(6, 4, GLColor::red, kLargeMargin);
EXPECT_PIXEL_COLOR_NEAR(7, 5, kDarkRed, kLargeMargin);
}
}
// Test that resolve from multisample default framebuffer works.
TEST_P(MultisampleTestES3, ResolveToFBO)
{
@ -305,26 +413,41 @@ TEST_P(MultisampleTestES3, ResolveToFBO)
EXPECT_PIXEL_COLOR_NEAR(kWindowSize / 2, kWindowSize / 2, kResult, 1);
}
ANGLE_INSTANTIATE_TEST(MultisampleTest,
WithNoFixture(ES2_D3D11()),
WithNoFixture(ES3_D3D11()),
WithNoFixture(ES31_D3D11()),
WithNoFixture(ES2_OPENGL()),
WithNoFixture(ES3_OPENGL()),
WithNoFixture(ES31_OPENGL()),
WithNoFixture(ES2_OPENGLES()),
WithNoFixture(ES3_OPENGLES()),
WithNoFixture(ES31_OPENGLES()),
WithNoFixture(ES2_VULKAN()),
WithNoFixture(ES3_VULKAN()),
WithNoFixture(ES31_VULKAN()));
ANGLE_INSTANTIATE_TEST(MultisampleTestES3,
WithNoFixture(ES3_D3D11()),
WithNoFixture(ES31_D3D11()),
WithNoFixture(ES3_OPENGL()),
WithNoFixture(ES31_OPENGL()),
WithNoFixture(ES3_OPENGLES()),
WithNoFixture(ES31_OPENGLES()),
WithNoFixture(ES3_VULKAN()),
WithNoFixture(ES31_VULKAN()));
ANGLE_INSTANTIATE_TEST_COMBINE_1(MultisampleTest,
PrintToStringParamName,
testing::Values(false),
WithNoFixture(ES2_D3D11()),
WithNoFixture(ES3_D3D11()),
WithNoFixture(ES31_D3D11()),
WithNoFixture(ES2_METAL()),
WithNoFixture(ES2_OPENGL()),
WithNoFixture(ES3_OPENGL()),
WithNoFixture(ES31_OPENGL()),
WithNoFixture(ES2_OPENGLES()),
WithNoFixture(ES3_OPENGLES()),
WithNoFixture(ES31_OPENGLES()),
WithNoFixture(ES2_VULKAN()),
WithNoFixture(ES3_VULKAN()),
WithNoFixture(ES31_VULKAN()));
namespace store_and_resolve_feature_off
{
// Simulate missing msaa auto resolve feature in Metal.
ANGLE_INSTANTIATE_TEST_COMBINE_1(MultisampleTest,
PrintToStringParamName,
testing::Values(true),
WithNoFixture(ES2_METAL()));
} // namespace store_and_resolve_feature_off
ANGLE_INSTANTIATE_TEST_COMBINE_1(MultisampleTestES3,
PrintToStringParamName,
testing::Values(false),
WithNoFixture(ES3_D3D11()),
WithNoFixture(ES31_D3D11()),
WithNoFixture(ES3_OPENGL()),
WithNoFixture(ES31_OPENGL()),
WithNoFixture(ES3_OPENGLES()),
WithNoFixture(ES31_OPENGLES()),
WithNoFixture(ES3_VULKAN()),
WithNoFixture(ES31_VULKAN()));
} // anonymous namespace