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

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

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

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

@ -1,18 +1,20 @@
{ {
"src/libANGLE/renderer/metal/shaders/blit.metal": "src/libANGLE/renderer/metal/shaders/blit.metal":
"9281aba529ceb4ed5131b7f9c0515362", "1a12b22f56799bd38cf1c6b301b720ee",
"src/libANGLE/renderer/metal/shaders/clear.metal": "src/libANGLE/renderer/metal/shaders/clear.metal":
"1c231afc6100433a79fce49046aa5965", "1c231afc6100433a79fce49046aa5965",
"src/libANGLE/renderer/metal/shaders/common.h": "src/libANGLE/renderer/metal/shaders/common.h":
"569171e345ef36dd6a3b12aeebfae4a6", "7330bd3f7ab21214e4fe16bc526749bb",
"src/libANGLE/renderer/metal/shaders/compiled/mtl_default_shaders.inc": "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": "src/libANGLE/renderer/metal/shaders/gen_indices.metal":
"002511e2b980a7fca7e80cbda6a82712", "002511e2b980a7fca7e80cbda6a82712",
"src/libANGLE/renderer/metal/shaders/gen_mtl_internal_shaders.py": "src/libANGLE/renderer/metal/shaders/gen_mtl_internal_shaders.py":
"8de75752bb966cdbe575defc04fa7a7a", "0e599fb113dbc3f714291383d85c39c2",
"src/libANGLE/renderer/metal/shaders/master_source.metal": "src/libANGLE/renderer/metal/shaders/master_source.metal":
"fbe6f4bfb49a48ae87791a4cff5fab0a", "fbe6f4bfb49a48ae87791a4cff5fab0a",
"src/libANGLE/renderer/metal/shaders/mtl_default_shaders_src_autogen.inc": "src/libANGLE/renderer/metal/shaders/mtl_default_shaders_src_autogen.inc":
"8a94beb0d979f472d71686b6f95b624f" "ee7ff414da20e7b84f1187d2bea1e84d"
} }

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

@ -25,9 +25,31 @@
namespace sh namespace sh
{ {
namespace mtl
{
/** extern */
const char kCoverageMaskEnabledConstName[] = "ANGLECoverageMaskEnabled";
} // namespace mtl
namespace 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 // Unlike Vulkan having auto viewport flipping extension, in Metal we have to flip gl_Position.y
// manually. // manually.
// This operation performs flipping the gl_Position.y using this expression: // This operation performs flipping the gl_Position.y using this expression:
@ -89,6 +111,13 @@ bool TranslatorMetal::translate(TIntermBlock *root,
return false; return false;
} }
} }
else if (getShaderType() == GL_FRAGMENT_SHADER)
{
if (!insertSampleMaskWritingLogic(root, driverUniforms))
{
return false;
}
}
// Write translated shader. // Write translated shader.
root->traverse(&outputGLSL); root->traverse(&outputGLSL);
@ -124,4 +153,70 @@ bool TranslatorMetal::transformDepthBeforeCorrection(TIntermBlock *root,
return RunAtTheEndOfShader(this, root, assignment, &getSymbolTable()); 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 } // namespace sh

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

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

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

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

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

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

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

@ -962,4 +962,17 @@ bool IsValidImplicitConversion(sh::ImplicitTypeConversion conversion, TOperator
return false; 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 } // namespace sh

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

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

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

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

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

@ -27,6 +27,7 @@ class DisplayMtl;
class FramebufferMtl; class FramebufferMtl;
class VertexArrayMtl; class VertexArrayMtl;
class ProgramMtl; class ProgramMtl;
class WindowSurfaceMtl;
class ContextMtl : public ContextImpl, public mtl::Context 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 // Call this to notify ContextMtl whenever FramebufferMtl's state changed
void onDrawFrameBufferChange(const gl::Context *context, FramebufferMtl *framebuffer); void onDrawFrameBufferChange(const gl::Context *context, FramebufferMtl *framebuffer);
void onBackbufferResized(const gl::Context *context, WindowSurfaceMtl *backbuffer);
const MTLClearColor &getClearColorValue() const; const MTLClearColor &getClearColorValue() const;
MTLColorWriteMask getColorMask() const; MTLColorWriteMask getColorMask() const;
@ -296,13 +298,10 @@ class ContextMtl : public ContextImpl, public mtl::Context
// Check whether compatible render pass has been started. // Check whether compatible render pass has been started.
bool hasStartedRenderPass(const mtl::RenderPassDesc &desc); bool hasStartedRenderPass(const mtl::RenderPassDesc &desc);
bool hasStartedRenderPass(FramebufferMtl *framebuffer);
// Get current render encoder. May be nullptr if no render pass has been started. // Get current render encoder. May be nullptr if no render pass has been started.
mtl::RenderCommandEncoder *getRenderCommandEncoder(); mtl::RenderCommandEncoder *getRenderCommandEncoder();
mtl::RenderCommandEncoder *getCurrentFramebufferRenderCommandEncoder();
// Will end current command encoder if it is valid, then start new encoder. // Will end current command encoder if it is valid, then start new encoder.
// Unless hasStartedRenderPass(desc) returns true. // Unless hasStartedRenderPass(desc) returns true.
mtl::RenderCommandEncoder *getRenderCommandEncoder(const mtl::RenderPassDesc &desc); 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 // Used to pre-rotate gl_FragCoord for Vulkan swapchain images on Android (a mat2, which is
// padded to the size of two vec4's). // padded to the size of two vec4's).
float fragRotation[8]; float fragRotation[8];
uint32_t coverageMask;
float padding2[3];
}; };
struct DefaultAttribute struct DefaultAttribute
@ -466,12 +469,6 @@ class ContextMtl : public ContextImpl, public mtl::Context
VertexArrayMtl *mVertexArray = nullptr; VertexArrayMtl *mVertexArray = nullptr;
ProgramMtl *mProgram = 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>; using DirtyBits = angle::BitSet<DIRTY_BIT_MAX>;
gl::AttributesMask mDirtyDefaultAttribsMask; gl::AttributesMask mDirtyDefaultAttribsMask;

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

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

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

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

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

@ -18,13 +18,17 @@
namespace rx namespace rx
{ {
namespace mtl
{
class RenderCommandEncoder;
} // namespace mtl
class ContextMtl; class ContextMtl;
class SurfaceMtl; class WindowSurfaceMtl;
class FramebufferMtl : public FramebufferImpl class FramebufferMtl : public FramebufferImpl
{ {
public: public:
explicit FramebufferMtl(const gl::FramebufferState &state, bool flipY); FramebufferMtl(const gl::FramebufferState &state, bool flipY, WindowSurfaceMtl *backbuffer);
~FramebufferMtl() override; ~FramebufferMtl() override;
void destroy(const gl::Context *context) override; void destroy(const gl::Context *context) override;
@ -84,19 +88,26 @@ class FramebufferMtl : public FramebufferImpl
size_t index, size_t index,
GLfloat *xy) const override; 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; } bool flipY() const { return mFlipY; }
gl::Rectangle getCompleteRenderArea() const; 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. // Call this to notify FramebufferMtl whenever its render pass has started.
void onStartedDrawingToFrameBuffer(const gl::Context *context); void onStartedDrawingToFrameBuffer(const gl::Context *context);
void onFrameEnd(const gl::Context *context);
// The actual area will be adjusted based on framebuffer flipping property. // 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 // NOTE: this method doesn't do the flipping of area. Caller must do it if needed before
// callling this. See getReadPixelsArea(). // callling this. See getReadPixelsArea().
@ -121,9 +132,17 @@ class FramebufferMtl : public FramebufferImpl
gl::DrawBufferMask clearColorBuffers, gl::DrawBufferMask clearColorBuffers,
const mtl::ClearRectParams &clearOpts); const mtl::ClearRectParams &clearOpts);
angle::Result prepareRenderPass(const gl::Context *context, // Initialize load store options for a render pass's first start (i.e. not render pass resuming
gl::DrawBufferMask drawColorBuffers, // from interruptions such as those caused by a conversion compute pass)
mtl::RenderPassDesc *descOut); 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, void overrideClearColor(const mtl::TextureRef &texture,
MTLClearColor clearColor, MTLClearColor clearColor,
@ -143,7 +162,13 @@ class FramebufferMtl : public FramebufferImpl
RenderTargetMtl *mDepthRenderTarget = nullptr; RenderTargetMtl *mDepthRenderTarget = nullptr;
RenderTargetMtl *mStencilRenderTarget = nullptr; RenderTargetMtl *mStencilRenderTarget = nullptr;
mtl::RenderPassDesc mRenderPassDesc; 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 } // namespace rx

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

@ -23,8 +23,10 @@
namespace rx namespace rx
{ {
// FramebufferMtl implementation // FramebufferMtl implementation
FramebufferMtl::FramebufferMtl(const gl::FramebufferState &state, bool flipY) FramebufferMtl::FramebufferMtl(const gl::FramebufferState &state,
: FramebufferImpl(state), mFlipY(flipY) bool flipY,
WindowSurfaceMtl *backbuffer)
: FramebufferImpl(state), mBackbuffer(backbuffer), mFlipY(flipY)
{ {
reset(); reset();
} }
@ -164,7 +166,7 @@ angle::Result FramebufferMtl::readPixels(const gl::Context *context,
// nothing to read // nothing to read
return angle::Result::Continue; return angle::Result::Continue;
} }
gl::Rectangle flippedArea = getReadPixelArea(clippedArea); gl::Rectangle flippedArea = getCorrectFlippedReadArea(context, clippedArea);
ContextMtl *contextMtl = mtl::GetImpl(context); ContextMtl *contextMtl = mtl::GetImpl(context);
@ -190,7 +192,7 @@ angle::Result FramebufferMtl::readPixels(const gl::Context *context,
params.reverseRowOrder = !params.reverseRowOrder; 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)); static_cast<uint8_t *>(pixels) + outputSkipBytes));
return angle::Result::Continue; return angle::Result::Continue;
@ -273,7 +275,7 @@ angle::Result FramebufferMtl::syncState(const gl::Context *context,
auto oldRenderPassDesc = mRenderPassDesc; auto oldRenderPassDesc = mRenderPassDesc;
ANGLE_TRY(prepareRenderPass(context, mState.getEnabledDrawBuffers(), &mRenderPassDesc)); ANGLE_TRY(prepareRenderPass(context, &mRenderPassDesc));
if (!oldRenderPassDesc.equalIgnoreLoadStoreOptions(mRenderPassDesc)) if (!oldRenderPassDesc.equalIgnoreLoadStoreOptions(mRenderPassDesc))
{ {
@ -296,62 +298,166 @@ angle::Result FramebufferMtl::getSamplePosition(const gl::Context *context,
return angle::Result::Stop; return angle::Result::Stop;
} }
RenderTargetMtl *FramebufferMtl::getColorReadRenderTarget() const RenderTargetMtl *FramebufferMtl::getColorReadRenderTarget(const gl::Context *context) const
{ {
if (mState.getReadIndex() >= mColorRenderTargets.size()) if (mState.getReadIndex() >= mColorRenderTargets.size())
{ {
return nullptr; return nullptr;
} }
if (mBackbuffer)
{
if (IsError(mBackbuffer->ensureCurrentDrawableObtained(context)))
{
return nullptr;
}
}
return mColorRenderTargets[mState.getReadIndex()]; return mColorRenderTargets[mState.getReadIndex()];
} }
int FramebufferMtl::getSamples() const
{
return mRenderPassDesc.sampleCount;
}
gl::Rectangle FramebufferMtl::getCompleteRenderArea() const gl::Rectangle FramebufferMtl::getCompleteRenderArea() const
{ {
return gl::Rectangle(0, 0, mState.getDimensions().width, mState.getDimensions().height); 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) void FramebufferMtl::onStartedDrawingToFrameBuffer(const gl::Context *context)
{ {
mRenderPassCleanStart = true;
// Compute loadOp based on previous storeOp and reset storeOp flags: // Compute loadOp based on previous storeOp and reset storeOp flags:
for (mtl::RenderPassColorAttachmentDesc &colorAttachment : mRenderPassDesc.colorAttachments) for (mtl::RenderPassColorAttachmentDesc &colorAttachment : mRenderPassDesc.colorAttachments)
{ {
if (colorAttachment.storeAction == MTLStoreActionDontCare) setLoadStoreActionOnRenderPassFirstStart(&colorAttachment);
{
// 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
} }
// Depth load/store // Depth load/store
if (mRenderPassDesc.depthAttachment.storeAction == MTLStoreActionDontCare) setLoadStoreActionOnRenderPassFirstStart(&mRenderPassDesc.depthAttachment);
{
mRenderPassDesc.depthAttachment.loadAction = MTLLoadActionDontCare;
}
else
{
mRenderPassDesc.depthAttachment.loadAction = MTLLoadActionLoad;
}
mRenderPassDesc.depthAttachment.storeAction = MTLStoreActionStore;
// Stencil load/store // 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, 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, angle::Result FramebufferMtl::prepareRenderPass(const gl::Context *context,
gl::DrawBufferMask drawColorBuffers,
mtl::RenderPassDesc *pDescOut) mtl::RenderPassDesc *pDescOut)
{ {
auto &desc = *pDescOut; gl::DrawBufferMask enabledBuffer = mState.getEnabledDrawBuffers();
desc.numColorAttachments = static_cast<uint32_t>(drawColorBuffers.count()); mtl::RenderPassDesc &desc = *pDescOut;
size_t attachmentIdx = 0;
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) ASSERT(colorIndexGL < mtl::kMaxRenderTargets);
{
continue;
}
const RenderTargetMtl *colorRenderTarget = mColorRenderTargets[colorIndexGL];
ASSERT(colorRenderTarget);
mtl::RenderPassColorAttachmentDesc &colorAttachment = mtl::RenderPassColorAttachmentDesc &colorAttachment = desc.colorAttachments[colorIndexGL];
desc.colorAttachments[attachmentIdx++]; const RenderTargetMtl *colorRenderTarget = mColorRenderTargets[colorIndexGL];
colorRenderTarget->toRenderPassAttachmentDesc(&colorAttachment);
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) if (mDepthRenderTarget)
{ {
mDepthRenderTarget->toRenderPassAttachmentDesc(&desc.depthAttachment); mDepthRenderTarget->toRenderPassAttachmentDesc(&desc.depthAttachment);
desc.sampleCount = std::max(desc.sampleCount, mDepthRenderTarget->getRenderSamples());
}
else
{
desc.depthAttachment.reset();
} }
if (mStencilRenderTarget) if (mStencilRenderTarget)
{ {
mStencilRenderTarget->toRenderPassAttachmentDesc(&desc.stencilAttachment); mStencilRenderTarget->toRenderPassAttachmentDesc(&desc.stencilAttachment);
desc.sampleCount = std::max(desc.sampleCount, mStencilRenderTarget->getRenderSamples());
}
else
{
desc.stencilAttachment.reset();
} }
return angle::Result::Continue; return angle::Result::Continue;
@ -519,7 +642,7 @@ angle::Result FramebufferMtl::clearWithLoadOp(const gl::Context *context,
} }
// Start new render encoder with loadOp=Clear // Start new render encoder with loadOp=Clear
contextMtl->getRenderCommandEncoder(tempDesc); ensureRenderPassStarted(context, tempDesc);
return angle::Result::Continue; return angle::Result::Continue;
} }
@ -532,7 +655,7 @@ angle::Result FramebufferMtl::clearWithDraw(const gl::Context *context,
DisplayMtl *display = contextMtl->getDisplay(); DisplayMtl *display = contextMtl->getDisplay();
// Start new render encoder if not already. // 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); return display->getUtils().clearWithDraw(context, encoder, clearOpts);
} }
@ -659,9 +782,10 @@ angle::Result FramebufferMtl::invalidateImpl(ContextMtl *contextMtl,
return angle::Result::Continue; 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); ASSERT(colorReadRT);
gl::Rectangle flippedArea = glArea; gl::Rectangle flippedArea = glArea;
if (mFlipY) if (mFlipY)
@ -690,11 +814,20 @@ angle::Result FramebufferMtl::readPixelsImpl(const gl::Context *context,
{ {
return angle::Result::Continue; 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(); const mtl::Format &readFormat = *renderTarget->getFormat();

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

@ -27,7 +27,18 @@ namespace rx
{ {
class ContextMtl; 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: public:
ProgramMtl(const gl::ProgramState &state); 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 getUniformiv(const gl::Context *context, GLint location, GLint *params) const override;
void getUniformuiv(const gl::Context *context, GLint location, GLuint *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 // Calls this before drawing, changedPipelineDesc is passed when vertex attributes desc and/or
// shader program changed. // shader program changed.
angle::Result setupDraw(const gl::Context *glContext, angle::Result setupDraw(const gl::Context *glContext,
@ -132,10 +151,10 @@ class ProgramMtl : public ProgramImpl
const gl::ProgramLinkedResources &resources, const gl::ProgramLinkedResources &resources,
gl::InfoLog &infoLog); gl::InfoLog &infoLog);
angle::Result createMslShader(const gl::Context *glContext, angle::Result createMslShaderLib(const gl::Context *glContext,
gl::ShaderType shaderType, gl::ShaderType shaderType,
gl::InfoLog &infoLog, gl::InfoLog &infoLog,
const std::string &translatedSource); const std::string &translatedSource);
// State for the default uniform blocks. // State for the default uniform blocks.
struct DefaultUniformBlock final : private angle::NonCopyable struct DefaultUniformBlock final : private angle::NonCopyable
@ -158,6 +177,13 @@ class ProgramMtl : public ProgramImpl
gl::ShaderMap<std::string> mTranslatedMslShader; gl::ShaderMap<std::string> mTranslatedMslShader;
gl::ShaderMap<mtl::TranslatedShaderInfo> mMslShaderTranslateInfo; 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; mtl::RenderPipelineCache mMetalRenderPipelineCache;
}; };

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

@ -30,6 +30,7 @@ namespace
{ {
#define SHADER_ENTRY_NAME @"main0" #define SHADER_ENTRY_NAME @"main0"
constexpr char kSpirvCrossSpecConstSuffix[] = "_tmp";
void InitDefaultUniformBlock(const std::vector<sh::Uniform> &uniforms, void InitDefaultUniformBlock(const std::vector<sh::Uniform> &uniforms,
gl::Shader *shader, gl::Shader *shader,
@ -124,14 +125,59 @@ class Std140BlockLayoutEncoderFactory : public gl::CustomBlockLayoutEncoderFacto
sh::BlockLayoutEncoder *makeEncoder() override { return new sh::Std140BlockEncoder(); } 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 } // namespace
// ProgramShaderVariantMtl implementation
void ProgramShaderVariantMtl::reset(ContextMtl *contextMtl)
{
metalShader = nil;
}
// ProgramMtl implementation // ProgramMtl implementation
ProgramMtl::DefaultUniformBlock::DefaultUniformBlock() {} ProgramMtl::DefaultUniformBlock::DefaultUniformBlock() {}
ProgramMtl::DefaultUniformBlock::~DefaultUniformBlock() = default; ProgramMtl::DefaultUniformBlock::~DefaultUniformBlock() = default;
ProgramMtl::ProgramMtl(const gl::ProgramState &state) : ProgramImpl(state) {} ProgramMtl::ProgramMtl(const gl::ProgramState &state)
: ProgramImpl(state), mMetalRenderPipelineCache(this)
{}
ProgramMtl::~ProgramMtl() {} ProgramMtl::~ProgramMtl() {}
@ -149,8 +195,10 @@ void ProgramMtl::reset(ContextMtl *context)
block.uniformLayout.clear(); block.uniformLayout.clear();
} }
for (gl::ShaderType shaderType : gl::AllGLES2ShaderTypes()) for (gl::ShaderType shaderType : gl::AllShaderTypes())
{ {
mMslShaderLibrary[shaderType] = nil;
for (mtl::SamplerBinding &binding : for (mtl::SamplerBinding &binding :
mMslShaderTranslateInfo[shaderType].actualSamplerBindings) 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(); mMetalRenderPipelineCache.clear();
} }
@ -228,7 +285,7 @@ angle::Result ProgramMtl::linkImpl(const gl::Context *glContext,
{ {
// Create actual Metal shader // Create actual Metal shader
ANGLE_TRY( ANGLE_TRY(
createMslShader(glContext, shaderType, infoLog, mTranslatedMslShader[shaderType])); createMslShaderLib(glContext, shaderType, infoLog, mTranslatedMslShader[shaderType]));
} }
return angle::Result::Continue; return angle::Result::Continue;
@ -325,10 +382,76 @@ angle::Result ProgramMtl::initDefaultUniformBlocks(const gl::Context *glContext)
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result ProgramMtl::createMslShader(const gl::Context *glContext, angle::Result ProgramMtl::getSpecializedShader(mtl::Context *context,
gl::ShaderType shaderType, gl::ShaderType shaderType,
gl::InfoLog &infoLog, const mtl::RenderPipelineDesc &renderPipelineDesc,
const std::string &translatedMsl) 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 ANGLE_MTL_OBJC_SCOPE
{ {
@ -338,9 +461,8 @@ angle::Result ProgramMtl::createMslShader(const gl::Context *glContext,
// Convert to actual binary shader // Convert to actual binary shader
mtl::AutoObjCPtr<NSError *> err = nil; mtl::AutoObjCPtr<NSError *> err = nil;
mtl::AutoObjCPtr<id<MTLLibrary>> mtlShaderLib = mMslShaderLibrary[shaderType] = mtl::CreateShaderLibrary(mtlDevice, translatedMsl, &err);
mtl::CreateShaderLibrary(mtlDevice, translatedMsl, &err); if (err && !mMslShaderLibrary[shaderType])
if (err && !mtlShaderLib)
{ {
std::ostringstream ss; std::ostringstream ss;
ss << "Internal error compiling Metal shader:\n" 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); 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; return angle::Result::Continue;
} }
} }

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

@ -32,21 +32,33 @@ class RenderTargetMtl final : public FramebufferAttachmentRenderTarget
// Used in std::vector initialization. // Used in std::vector initialization.
RenderTargetMtl(RenderTargetMtl &&other); 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(); void reset();
const mtl::TextureRef &getTexture() const { return mTexture; } mtl::TextureRef getTexture() const { return mTexture; }
size_t getLevelIndex() const { return mLevelIndex; } mtl::TextureRef getImplicitMSTexture() const { return mImplicitMSTexture; }
size_t getLayerIndex() const { return mLayerIndex; } uint32_t getLevelIndex() const { return mLevelIndex; }
uint32_t getLayerIndex() const { return mLayerIndex; }
uint32_t getRenderSamples() const;
const mtl::Format *getFormat() const { return mFormat; } const mtl::Format *getFormat() const { return mFormat; }
void toRenderPassAttachmentDesc(mtl::RenderPassAttachmentDesc *rpaDescOut) const; void toRenderPassAttachmentDesc(mtl::RenderPassAttachmentDesc *rpaDescOut) const;
private: private:
mtl::TextureRef mTexture; mtl::TextureRef mTexture;
size_t mLevelIndex = 0; mtl::TextureRef mImplicitMSTexture;
size_t mLayerIndex = 0; uint32_t mLevelIndex = 0;
uint32_t mLayerIndex = 0;
const mtl::Format *mFormat = nullptr; const mtl::Format *mFormat = nullptr;
}; };
} // namespace rx } // namespace rx

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

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

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

@ -64,6 +64,10 @@ class SurfaceMtl : public SurfaceImpl
EGLint isPostSubBufferSupported() const override; EGLint isPostSubBufferSupported() const override;
EGLint getSwapBehavior() 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, angle::Result getAttachmentRenderTarget(const gl::Context *context,
GLenum binding, GLenum binding,
const gl::ImageIndex &imageIndex, const gl::ImageIndex &imageIndex,
@ -71,20 +75,29 @@ class SurfaceMtl : public SurfaceImpl
FramebufferAttachmentRenderTarget **rtOut) override; FramebufferAttachmentRenderTarget **rtOut) override;
protected: protected:
// Ensure companion (depth, stencil) textures' size is correct w.r.t color texture. // Ensure companion (MS, depth, stencil) textures' size is correct w.r.t color texture.
angle::Result ensureDepthStencilSizeCorrect(const gl::Context *context, angle::Result ensureCompanionTexturesSizeCorrect(const gl::Context *context,
gl::Framebuffer::DirtyBits *fboDirtyBits); const gl::Extents &size);
angle::Result resolveColorTextureIfNeeded(const gl::Context *context);
// Normal textures
mtl::TextureRef mColorTexture; mtl::TextureRef mColorTexture;
mtl::TextureRef mDepthTexture; mtl::TextureRef mDepthTexture;
mtl::TextureRef mStencilTexture; mtl::TextureRef mStencilTexture;
// Implicit multisample texture
mtl::TextureRef mMSColorTexture;
bool mUsePackedDepthStencil = false; 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 mColorFormat;
mtl::Format mDepthFormat; mtl::Format mDepthFormat;
mtl::Format mStencilFormat; mtl::Format mStencilFormat;
int mSamples = 0;
RenderTargetMtl mColorRenderTarget; RenderTargetMtl mColorRenderTarget;
RenderTargetMtl mDepthRenderTarget; RenderTargetMtl mDepthRenderTarget;
RenderTargetMtl mStencilRenderTarget; RenderTargetMtl mStencilRenderTarget;
@ -105,9 +118,9 @@ class WindowSurfaceMtl : public SurfaceMtl
FramebufferImpl *createDefaultFramebuffer(const gl::Context *context, FramebufferImpl *createDefaultFramebuffer(const gl::Context *context,
const gl::FramebufferState &state) override; const gl::FramebufferState &state) override;
egl::Error makeCurrent(const gl::Context *context) override;
egl::Error swap(const gl::Context *context) override; egl::Error swap(const gl::Context *context) override;
void setSwapInterval(EGLint interval) override;
EGLint getSwapBehavior() const override; EGLint getSwapBehavior() const override;
// width and height can change with client window resizing // width and height can change with client window resizing
@ -120,17 +133,29 @@ class WindowSurfaceMtl : public SurfaceMtl
GLsizei samples, GLsizei samples,
FramebufferAttachmentRenderTarget **rtOut) override; 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: private:
angle::Result swapImpl(const gl::Context *context); angle::Result swapImpl(const gl::Context *context);
angle::Result ensureRenderTargetsCreated(const gl::Context *context);
angle::Result obtainNextDrawable(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. // Check if metal layer has been resized.
void checkIfLayerResized(); bool checkIfLayerResized(const gl::Context *context);
mtl::AutoObjCObj<CAMetalLayer> mMetalLayer = nil; mtl::AutoObjCObj<CAMetalLayer> mMetalLayer = nil;
CALayer *mLayer; CALayer *mLayer;
mtl::AutoObjCPtr<id<CAMetalDrawable>> mCurrentDrawable = nil; 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 } // namespace rx

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

@ -46,6 +46,30 @@ constexpr angle::FormatID kDefaultFrameBufferStencilFormatId = angle::FormatID::
constexpr angle::FormatID kDefaultFrameBufferDepthStencilFormatId = constexpr angle::FormatID kDefaultFrameBufferDepthStencilFormatId =
angle::FormatID::D24_UNORM_S8_UINT; 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 ANGLE_MTL_UNUSED
bool IsFrameCaptureEnabled() bool IsFrameCaptureEnabled()
{ {
@ -194,6 +218,8 @@ SurfaceMtl::SurfaceMtl(DisplayMtl *display,
mColorFormat.intendedFormatId = mColorFormat.actualFormatId = angle::FormatID::B8G8R8A8_UNORM; mColorFormat.intendedFormatId = mColorFormat.actualFormatId = angle::FormatID::B8G8R8A8_UNORM;
mColorFormat.metalFormat = MTLPixelFormatBGRA8Unorm; mColorFormat.metalFormat = MTLPixelFormatBGRA8Unorm;
mSamples = state.config->samples;
int depthBits = 0; int depthBits = 0;
int stencilBits = 0; int stencilBits = 0;
if (state.config) if (state.config)
@ -235,6 +261,8 @@ void SurfaceMtl::destroy(const egl::Display *display)
mDepthTexture = nullptr; mDepthTexture = nullptr;
mStencilTexture = nullptr; mStencilTexture = nullptr;
mMSColorTexture = nullptr;
mColorRenderTarget.reset(); mColorRenderTarget.reset();
mDepthRenderTarget.reset(); mDepthRenderTarget.reset();
mStencilRenderTarget.reset(); mStencilRenderTarget.reset();
@ -248,18 +276,25 @@ egl::Error SurfaceMtl::initialize(const egl::Display *display)
FramebufferImpl *SurfaceMtl::createDefaultFramebuffer(const gl::Context *context, FramebufferImpl *SurfaceMtl::createDefaultFramebuffer(const gl::Context *context,
const gl::FramebufferState &state) const gl::FramebufferState &state)
{ {
auto fbo = new FramebufferMtl(state, /* flipY */ false); auto fbo = new FramebufferMtl(state, /* flipY */ false, /* backbuffer */ nullptr);
return fbo; return fbo;
} }
egl::Error SurfaceMtl::makeCurrent(const gl::Context *context) egl::Error SurfaceMtl::makeCurrent(const gl::Context *context)
{ {
ContextMtl *contextMtl = mtl::GetImpl(context);
StartFrameCapture(contextMtl);
return egl::NoError(); return egl::NoError();
} }
egl::Error SurfaceMtl::unMakeCurrent(const gl::Context *context) egl::Error SurfaceMtl::unMakeCurrent(const gl::Context *context)
{ {
ContextMtl *contextMtl = mtl::GetImpl(context);
contextMtl->flushCommandBufer();
StopFrameCapture();
return egl::NoError(); return egl::NoError();
} }
@ -376,21 +411,38 @@ angle::Result SurfaceMtl::getAttachmentRenderTarget(const gl::Context *context,
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result SurfaceMtl::ensureDepthStencilSizeCorrect(const gl::Context *context, angle::Result SurfaceMtl::ensureCompanionTexturesSizeCorrect(const gl::Context *context,
gl::Framebuffer::DirtyBits *fboDirtyBits) const gl::Extents &size)
{ {
ASSERT(mColorTexture && mColorTexture->get());
ContextMtl *contextMtl = mtl::GetImpl(context); 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)) if (mDepthFormat.valid() && (!mDepthTexture || mDepthTexture->size() != size))
{ {
ANGLE_TRY(mtl::Texture::Make2DTexture(contextMtl, mDepthFormat, size.width, size.height, 1, ANGLE_TRY(CreateTexture(context, mDepthFormat, size.width, size.height, mSamples,
true, false, &mDepthTexture)); /** renderTargetOnly */ true, &mDepthTexture));
mDepthRenderTarget.set(mDepthTexture, 0, 0, mDepthFormat); mDepthRenderTarget.set(mDepthTexture, 0, 0, mDepthFormat);
fboDirtyBits->set(gl::Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT);
} }
if (mStencilFormat.valid() && (!mStencilTexture || mStencilTexture->size() != size)) if (mStencilFormat.valid() && (!mStencilTexture || mStencilTexture->size() != size))
@ -401,24 +453,43 @@ angle::Result SurfaceMtl::ensureDepthStencilSizeCorrect(const gl::Context *conte
} }
else else
{ {
ANGLE_TRY(mtl::Texture::Make2DTexture(contextMtl, mStencilFormat, size.width, ANGLE_TRY(CreateTexture(context, mStencilFormat, size.width, size.height, mSamples,
size.height, 1, true, false, &mStencilTexture)); /** renderTargetOnly */ true, &mStencilTexture));
} }
mStencilRenderTarget.set(mStencilTexture, 0, 0, mStencilFormat); mStencilRenderTarget.set(mStencilTexture, 0, 0, mStencilFormat);
fboDirtyBits->set(gl::Framebuffer::DIRTY_BIT_STENCIL_ATTACHMENT);
} }
return angle::Result::Continue; 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 implementation.
WindowSurfaceMtl::WindowSurfaceMtl(DisplayMtl *display, WindowSurfaceMtl::WindowSurfaceMtl(DisplayMtl *display,
const egl::SurfaceState &state, const egl::SurfaceState &state,
EGLNativeWindowType window, EGLNativeWindowType window,
const egl::AttributeMap &attribs) const egl::AttributeMap &attribs)
: SurfaceMtl(display, state, attribs), mLayer((__bridge CALayer *)(window)) : SurfaceMtl(display, state, attribs), mLayer((__bridge CALayer *)(window))
{} {
// NOTE(hqle): Width and height attributes is ignored for now.
mCurrentKnownDrawableSize = CGSizeMake(0, 0);
}
WindowSurfaceMtl::~WindowSurfaceMtl() {} WindowSurfaceMtl::~WindowSurfaceMtl() {}
@ -470,15 +541,15 @@ egl::Error WindowSurfaceMtl::initialize(const egl::Display *display)
mMetalLayer.get().autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable; mMetalLayer.get().autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
#endif #endif
// ensure drawableSize is set to correct value:
mMetalLayer.get().drawableSize = mCurrentKnownDrawableSize = calcExpectedDrawableSize();
if (mMetalLayer.get() != mLayer) if (mMetalLayer.get() != mLayer)
{ {
mMetalLayer.get().contentsScale = mLayer.contentsScale; mMetalLayer.get().contentsScale = mLayer.contentsScale;
[mLayer addSublayer:mMetalLayer.get()]; [mLayer addSublayer:mMetalLayer.get()];
} }
// ensure drawableSize is set to correct value:
checkIfLayerResized();
} }
return egl::NoError(); return egl::NoError();
@ -487,18 +558,11 @@ egl::Error WindowSurfaceMtl::initialize(const egl::Display *display)
FramebufferImpl *WindowSurfaceMtl::createDefaultFramebuffer(const gl::Context *context, FramebufferImpl *WindowSurfaceMtl::createDefaultFramebuffer(const gl::Context *context,
const gl::FramebufferState &state) const gl::FramebufferState &state)
{ {
auto fbo = new FramebufferMtl(state, /* flipY */ true); auto fbo = new FramebufferMtl(state, /* flipY */ true, /* backbuffer */ this);
return fbo; 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) egl::Error WindowSurfaceMtl::swap(const gl::Context *context)
{ {
ANGLE_TO_EGL_TRY(swapImpl(context)); ANGLE_TO_EGL_TRY(swapImpl(context));
@ -506,31 +570,22 @@ egl::Error WindowSurfaceMtl::swap(const gl::Context *context)
return egl::NoError(); 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 // width and height can change with client window resizing
EGLint WindowSurfaceMtl::getWidth() const EGLint WindowSurfaceMtl::getWidth() const
{ {
if (mColorTexture) return static_cast<EGLint>(mCurrentKnownDrawableSize.width);
{
return static_cast<EGLint>(mColorTexture->width());
}
if (mMetalLayer)
{
return static_cast<EGLint>(mMetalLayer.get().drawableSize.width);
}
return SurfaceMtl::getWidth();
} }
EGLint WindowSurfaceMtl::getHeight() const EGLint WindowSurfaceMtl::getHeight() const
{ {
if (mColorTexture) return static_cast<EGLint>(mCurrentKnownDrawableSize.height);
{
return static_cast<EGLint>(mColorTexture->height());
}
if (mMetalLayer)
{
return static_cast<EGLint>(mMetalLayer.get().drawableSize.height);
}
return SurfaceMtl::getHeight();
} }
EGLint WindowSurfaceMtl::getSwapBehavior() const EGLint WindowSurfaceMtl::getSwapBehavior() const
@ -544,15 +599,15 @@ angle::Result WindowSurfaceMtl::getAttachmentRenderTarget(const gl::Context *con
GLsizei samples, GLsizei samples,
FramebufferAttachmentRenderTarget **rtOut) FramebufferAttachmentRenderTarget **rtOut)
{ {
// NOTE(hqle): Support MSAA. ANGLE_TRY(ensureCurrentDrawableObtained(context));
ANGLE_TRY(ensureRenderTargetsCreated(context)); ANGLE_TRY(ensureCompanionTexturesSizeCorrect(context));
return SurfaceMtl::getAttachmentRenderTarget(context, binding, imageIndex, samples, rtOut); 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)); ANGLE_TRY(obtainNextDrawable(context));
} }
@ -560,39 +615,81 @@ angle::Result WindowSurfaceMtl::ensureRenderTargetsCreated(const gl::Context *co
return angle::Result::Continue; 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; CGSize currentLayerSize = mMetalLayer.get().bounds.size;
CGFloat currentLayerContentsScale = mMetalLayer.get().contentsScale; CGFloat currentLayerContentsScale = mMetalLayer.get().contentsScale;
CGSize expectedDrawableSize = CGSizeMake(currentLayerSize.width * currentLayerContentsScale, CGSize expectedDrawableSize = CGSizeMake(currentLayerSize.width * currentLayerContentsScale,
currentLayerSize.height * 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. // 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) angle::Result WindowSurfaceMtl::obtainNextDrawable(const gl::Context *context)
{ {
checkIfLayerResized();
ANGLE_MTL_OBJC_SCOPE ANGLE_MTL_OBJC_SCOPE
{ {
ContextMtl *contextMtl = mtl::GetImpl(context); ContextMtl *contextMtl = mtl::GetImpl(context);
StartFrameCapture(contextMtl);
ANGLE_MTL_TRY(contextMtl, mMetalLayer); 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]); mCurrentDrawable.retainAssign([mMetalLayer nextDrawable]);
if (!mCurrentDrawable) if (!mCurrentDrawable)
{ {
@ -607,7 +704,8 @@ angle::Result WindowSurfaceMtl::obtainNextDrawable(const gl::Context *context)
if (!mColorTexture) if (!mColorTexture)
{ {
mColorTexture = mtl::Texture::MakeFromMetal(mCurrentDrawable.get().texture); mColorTexture = mtl::Texture::MakeFromMetal(mCurrentDrawable.get().texture);
mColorRenderTarget.set(mColorTexture, 0, 0, mColorFormat); ASSERT(!mColorRenderTarget.getTexture());
mColorRenderTarget.set(mColorTexture, mMSColorTexture, 0, 0, mColorFormat);
} }
else else
{ {
@ -617,22 +715,8 @@ angle::Result WindowSurfaceMtl::obtainNextDrawable(const gl::Context *context)
ANGLE_MTL_LOG("Current metal drawable size=%d,%d", mColorTexture->width(), ANGLE_MTL_LOG("Current metal drawable size=%d,%d", mColorTexture->width(),
mColorTexture->height()); mColorTexture->height());
gl::Framebuffer::DirtyBits fboDirtyBits; // Now we have to resize depth stencil buffers if required.
fboDirtyBits.set(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0); ANGLE_TRY(ensureCompanionTexturesSizeCorrect(context));
// 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));
}
return angle::Result::Continue; 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::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; return angle::Result::Continue;
} }

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

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

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

@ -788,6 +788,24 @@ void RenderCommandEncoder::finalizeLoadStoreAction(
return; 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 (objCRenderPassAttachment.storeAction == MTLStoreActionUnknown)
{ {
// If storeAction hasn't been set for this attachment, we set to dontcare. // 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 // Customized spirv-cross compiler
class SpirvToMslCompiler : public spirv_cross::CompilerMSL class SpirvToMslCompiler : public spirv_cross::CompilerMSL
{ {
@ -210,7 +225,7 @@ class SpirvToMslCompiler : public spirv_cross::CompilerMSL
spirv_cross::CompilerMSL::set_msl_options(compOpt); spirv_cross::CompilerMSL::set_msl_options(compOpt);
// Actual compilation // Actual compilation
std::string translatedMsl = spirv_cross::CompilerMSL::compile(); std::string translatedMsl = PostProcessTranslatedMsl(spirv_cross::CompilerMSL::compile());
// Retrieve automatic texture slot assignments // Retrieve automatic texture slot assignments
GetAssignedSamplerBindings(*this, originalSamplerBindings, GetAssignedSamplerBindings(*this, originalSamplerBindings,

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

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

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

@ -29,6 +29,9 @@ namespace
#define SOURCE_IDX_IS_U8_CONSTANT_NAME @"kSourceIndexIsU8" #define SOURCE_IDX_IS_U8_CONSTANT_NAME @"kSourceIndexIsU8"
#define SOURCE_IDX_IS_U16_CONSTANT_NAME @"kSourceIndexIsU16" #define SOURCE_IDX_IS_U16_CONSTANT_NAME @"kSourceIndexIsU16"
#define SOURCE_IDX_IS_U32_CONSTANT_NAME @"kSourceIndexIsU32" #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 // See libANGLE/renderer/metal/shaders/clear.metal
struct ClearParamsUniform struct ClearParamsUniform
@ -44,10 +47,12 @@ struct BlitParamsUniform
// 0: lower left, 1: lower right, 2: upper left // 0: lower left, 1: lower right, 2: upper left
float srcTexCoords[3][2]; float srcTexCoords[3][2];
int srcLevel = 0; 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 dstFlipY = 0;
uint8_t dstLuminance = 0; // dest texture is luminace uint8_t dstLuminance = 0; // dest texture is luminace
uint8_t padding; uint8_t padding1;
float padding2[3];
}; };
struct IndexConversionUniform struct IndexConversionUniform
@ -131,6 +136,31 @@ void GetFirstLastIndicesFromClientElements(GLsizei count,
memcpy(lastOut, indices + count - 1, sizeof(indices[0])); 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 ANGLE_INLINE
void EnsureComputePipelineInitialized(DisplayMtl *display, void EnsureComputePipelineInitialized(DisplayMtl *display,
NSString *functionName, NSString *functionName,
@ -207,6 +237,15 @@ void EnsureSpecializedComputePipelineInitialized(
} }
} }
template <typename T>
void ClearRenderPipelineCacheArray(T *pipelineCacheArray)
{
for (RenderPipelineCache &pipelineCache : *pipelineCacheArray)
{
pipelineCache.clear();
}
}
template <typename T> template <typename T>
void ClearPipelineStateArray(T *pipelineCacheArray) void ClearPipelineStateArray(T *pipelineCacheArray)
{ {
@ -353,6 +392,20 @@ angle::Result RenderUtils::blitWithDraw(const gl::Context *context,
return mColorBlitUtils.blitWithDraw(context, cmdEncoder, params); 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, angle::Result RenderUtils::convertIndexBufferGPU(ContextMtl *contextMtl,
const IndexConversionParams &params) const IndexConversionParams &params)
{ {
@ -570,13 +623,14 @@ ColorBlitUtils::ColorBlitUtils() = default;
void ColorBlitUtils::onDestroy() void ColorBlitUtils::onDestroy()
{ {
mBlitRenderPipelineCache.clear(); ClearRenderPipelineCacheArray(&mBlitRenderPipelineCache);
mBlitPremultiplyAlphaRenderPipelineCache.clear(); ClearRenderPipelineCacheArray(&mBlitPremultiplyAlphaRenderPipelineCache);
mBlitUnmultiplyAlphaRenderPipelineCache.clear(); ClearRenderPipelineCacheArray(&mBlitUnmultiplyAlphaRenderPipelineCache);
} }
void ColorBlitUtils::ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx, void ColorBlitUtils::ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx,
int alphaPremultiplyType, int alphaPremultiplyType,
int textureType,
RenderPipelineCache *cacheOut) RenderPipelineCache *cacheOut)
{ {
RenderPipelineCache &pipelineCache = *cacheOut; RenderPipelineCache &pipelineCache = *cacheOut;
@ -588,24 +642,43 @@ void ColorBlitUtils::ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx,
ANGLE_MTL_OBJC_SCOPE ANGLE_MTL_OBJC_SCOPE
{ {
NSString *const fragmentShaderNames[] = {// Normal blit NSError *err = nil;
@"blitFS",
// Blit premultiply-alpha
@"blitPremultiplyAlphaFS",
// Blit unmultiply alpha
@"blitUnmultiplyAlphaFS"};
id<MTLLibrary> shaderLib = ctx->getDisplay()->getDefaultShadersLib(); id<MTLLibrary> shaderLib = ctx->getDisplay()->getDefaultShadersLib();
id<MTLFunction> vertexShader = id<MTLFunction> vertexShader =
[[shaderLib newFunctionWithName:@"blitVS"] ANGLE_MTL_AUTORELEASE]; [[shaderLib newFunctionWithName:@"blitVS"] ANGLE_MTL_AUTORELEASE];
id<MTLFunction> fragmentShader = [[shaderLib MTLFunctionConstantValues *funcConstants =
newFunctionWithName:fragmentShaderNames[alphaPremultiplyType]] ANGLE_MTL_AUTORELEASE]; [[[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(vertexShader);
ASSERT(fragmentShader); ASSERT(fragmentShader);
pipelineCache.setVertexShader(ctx, vertexShader);
mBlitRenderPipelineCache.setVertexShader(ctx, vertexShader); pipelineCache.setFragmentShader(ctx, fragmentShader);
mBlitRenderPipelineCache.setFragmentShader(ctx, fragmentShader);
} }
} }
@ -625,23 +698,25 @@ id<MTLRenderPipelineState> ColorBlitUtils::getBlitRenderPipelineState(
RenderPipelineCache *pipelineCache; RenderPipelineCache *pipelineCache;
int alphaPremultiplyType; int alphaPremultiplyType;
int textureType = GetShaderTextureType(params.src);
if (params.unpackPremultiplyAlpha == params.unpackUnmultiplyAlpha) if (params.unpackPremultiplyAlpha == params.unpackUnmultiplyAlpha)
{ {
alphaPremultiplyType = 0; alphaPremultiplyType = 0;
pipelineCache = &mBlitRenderPipelineCache; pipelineCache = &mBlitRenderPipelineCache[textureType];
} }
else if (params.unpackPremultiplyAlpha) else if (params.unpackPremultiplyAlpha)
{ {
alphaPremultiplyType = 1; alphaPremultiplyType = 1;
pipelineCache = &mBlitPremultiplyAlphaRenderPipelineCache; pipelineCache = &mBlitPremultiplyAlphaRenderPipelineCache[textureType];
} }
else else
{ {
alphaPremultiplyType = 2; alphaPremultiplyType = 2;
pipelineCache = &mBlitUnmultiplyAlphaRenderPipelineCache; pipelineCache = &mBlitUnmultiplyAlphaRenderPipelineCache[textureType];
} }
ensureRenderPipelineStateCacheInitialized(contextMtl, alphaPremultiplyType, pipelineCache); ensureRenderPipelineStateCacheInitialized(contextMtl, alphaPremultiplyType, textureType,
pipelineCache);
return pipelineCache->getRenderPipelineState(contextMtl, pipelineDesc); return pipelineCache->getRenderPipelineState(contextMtl, pipelineDesc);
} }

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

@ -111,6 +111,15 @@ class Texture final : public Resource,
bool allowTextureView, bool allowTextureView,
TextureRef *refOut); 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); static TextureRef MakeFromMetal(id<MTLTexture> metalTexture);
// Allow CPU to read & write data directly to this texture? // 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(uint32_t level = 0) const;
gl::Extents size(const gl::ImageIndex &index) const; gl::Extents size(const gl::ImageIndex &index) const;
uint32_t samples() const;
// For render target // For render target
MTLColorWriteMask getColorWritableMask() const { return *mColorWritableMask; } MTLColorWriteMask getColorWritableMask() const { return *mColorWritableMask; }
void setColorWritableMask(MTLColorWriteMask mask) { *mColorWritableMask = mask; } void setColorWritableMask(MTLColorWriteMask mask) { *mColorWritableMask = mask; }

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

@ -147,6 +147,38 @@ angle::Result Texture::MakeCubeTexture(ContextMtl *context,
return angle::Result::Continue; 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 */ /** static */
TextureRef Texture::MakeFromMetal(id<MTLTexture> metalTexture) TextureRef Texture::MakeFromMetal(id<MTLTexture> metalTexture)
{ {
@ -183,7 +215,8 @@ Texture::Texture(ContextMtl *context,
desc.usage |= MTLTextureUsageRenderTarget; desc.usage |= MTLTextureUsageRenderTarget;
} }
if (!Format::FormatCPUReadable(desc.pixelFormat)) if (!Format::FormatCPUReadable(desc.pixelFormat) ||
desc.textureType == MTLTextureType2DMultisample)
{ {
desc.resourceOptions = MTLResourceStorageModePrivate; desc.resourceOptions = MTLResourceStorageModePrivate;
} }
@ -405,6 +438,11 @@ gl::Extents Texture::size(const gl::ImageIndex &index) const
return size(index.getLevelIndex()); return size(index.getLevelIndex());
} }
uint32_t Texture::samples() const
{
return static_cast<uint32_t>(get().sampleCount);
}
void Texture::set(id<MTLTexture> metalTexture) void Texture::set(id<MTLTexture> metalTexture)
{ {
ParentClass::set(metalTexture); ParentClass::set(metalTexture);

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

@ -214,6 +214,7 @@ struct RenderPipelineOutputDesc
static_assert(kMaxRenderTargets <= 4, "kMaxRenderTargets must be <= 4"); static_assert(kMaxRenderTargets <= 4, "kMaxRenderTargets must be <= 4");
uint8_t numColorAttachments : 3; uint8_t numColorAttachments : 3;
uint8_t sampleCount : 5;
}; };
// Some SDK levels don't declare MTLPrimitiveTopologyClass. Needs to do compile time check here: // 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. // Use uint8_t instead of PrimitiveTopologyClass to compact space.
uint8_t inputPrimitiveTopology : 2; 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 struct RenderPassAttachmentDesc
@ -261,9 +268,15 @@ struct RenderPassAttachmentDesc
bool equalIgnoreLoadStoreOptions(const RenderPassAttachmentDesc &other) const; bool equalIgnoreLoadStoreOptions(const RenderPassAttachmentDesc &other) const;
bool operator==(const RenderPassAttachmentDesc &other) const; bool operator==(const RenderPassAttachmentDesc &other) const;
ANGLE_INLINE bool hasImplicitMSTexture() const { return implicitMSTexture.get(); }
TextureRef texture; 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 level;
uint32_t slice; uint32_t sliceOrDepth;
MTLLoadAction loadAction; MTLLoadAction loadAction;
MTLStoreAction storeAction; MTLStoreAction storeAction;
MTLStoreActionOptions storeActionOptions; MTLStoreActionOptions storeActionOptions;
@ -333,6 +346,7 @@ struct RenderPassDesc
inline bool operator!=(const RenderPassDesc &other) const { return !(*this == other); } inline bool operator!=(const RenderPassDesc &other) const { return !(*this == other); }
uint32_t numColorAttachments = 0; uint32_t numColorAttachments = 0;
uint32_t sampleCount = 1;
}; };
} // namespace mtl } // namespace mtl
@ -365,18 +379,43 @@ namespace rx
{ {
namespace mtl 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 class RenderPipelineCache final : angle::NonCopyable
{ {
public: public:
RenderPipelineCache(); RenderPipelineCache();
RenderPipelineCache(RenderPipelineCacheSpecializeShaderFactory *specializedShaderFactory);
~RenderPipelineCache(); ~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 setVertexShader(Context *context, id<MTLFunction> shader);
void setFragmentShader(Context *context, id<MTLFunction> shader); void setFragmentShader(Context *context, id<MTLFunction> shader);
id<MTLFunction> getVertexShader() { return mVertexShader.get(); } // Get non-specialized shaders supplied via set*Shader().
id<MTLFunction> getFragmentShader() { return mFragmentShader.get(); } id<MTLFunction> getVertexShader() { return mVertexShader; }
id<MTLFunction> getFragmentShader() { return mFragmentShader; }
AutoObjCPtr<id<MTLRenderPipelineState>> getRenderPipelineState(ContextMtl *context, AutoObjCPtr<id<MTLRenderPipelineState>> getRenderPipelineState(ContextMtl *context,
const RenderPipelineDesc &desc); const RenderPipelineDesc &desc);
@ -384,8 +423,10 @@ class RenderPipelineCache final : angle::NonCopyable
void clear(); void clear();
protected: protected:
AutoObjCPtr<id<MTLFunction>> mVertexShader = nil; // Non-specialized vertex shader
AutoObjCPtr<id<MTLFunction>> mFragmentShader = nil; AutoObjCPtr<id<MTLFunction>> mVertexShader;
// Non-specialized fragment shader
AutoObjCPtr<id<MTLFunction>> mFragmentShader;
private: private:
void clearPipelineStates(); void clearPipelineStates();
@ -404,6 +445,8 @@ class RenderPipelineCache final : angle::NonCopyable
// One table with default attrib and one table without. // One table with default attrib and one table without.
std::unordered_map<RenderPipelineDesc, AutoObjCPtr<id<MTLRenderPipelineState>>> std::unordered_map<RenderPipelineDesc, AutoObjCPtr<id<MTLRenderPipelineState>>>
mRenderPipelineStates[2]; mRenderPipelineStates[2];
RenderPipelineCacheSpecializeShaderFactory *mSpecializedShaderFactory;
}; };
class StateCache final : angle::NonCopyable 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, depthAttachmentPixelFormat);
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc.outputDescriptor, stencilAttachmentPixelFormat); ANGLE_OBJC_CP_PROPERTY(objCDesc, desc.outputDescriptor, stencilAttachmentPixelFormat);
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc.outputDescriptor, sampleCount);
#if ANGLE_MTL_PRIMITIVE_TOPOLOGY_CLASS_AVAILABLE #if ANGLE_MTL_PRIMITIVE_TOPOLOGY_CLASS_AVAILABLE
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, inputPrimitiveTopology); ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, inputPrimitiveTopology);
#endif #endif
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, rasterizationEnabled); ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, rasterizationEnabled);
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, alphaToCoverageEnabled);
return [objCDesc ANGLE_MTL_AUTORELEASE]; return [objCDesc ANGLE_MTL_AUTORELEASE];
} }
@ -174,9 +176,44 @@ id<MTLTexture> ToObjC(const TextureRef &texture)
void BaseRenderPassAttachmentDescToObjC(const RenderPassAttachmentDesc &src, void BaseRenderPassAttachmentDescToObjC(const RenderPassAttachmentDesc &src,
MTLRenderPassAttachmentDescriptor *dst) MTLRenderPassAttachmentDescriptor *dst)
{ {
ANGLE_OBJC_CP_PROPERTY(dst, src, texture); const TextureRef &implicitMsTexture = src.implicitMSTexture;
ANGLE_OBJC_CP_PROPERTY(dst, src, level);
ANGLE_OBJC_CP_PROPERTY(dst, src, slice); 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, loadAction);
ANGLE_OBJC_CP_PROPERTY(dst, src, storeAction); ANGLE_OBJC_CP_PROPERTY(dst, src, storeAction);
@ -585,7 +622,8 @@ bool RenderPipelineOutputDesc::operator==(const RenderPipelineOutputDesc &rhs) c
RenderPipelineDesc::RenderPipelineDesc() RenderPipelineDesc::RenderPipelineDesc()
{ {
memset(this, 0, sizeof(*this)); memset(this, 0, sizeof(*this));
rasterizationEnabled = true; outputDescriptor.sampleCount = 1;
rasterizationEnabled = true;
} }
RenderPipelineDesc::RenderPipelineDesc(const RenderPipelineDesc &src) RenderPipelineDesc::RenderPipelineDesc(const RenderPipelineDesc &src)
@ -626,8 +664,9 @@ RenderPassAttachmentDesc::RenderPassAttachmentDesc()
void RenderPassAttachmentDesc::reset() void RenderPassAttachmentDesc::reset()
{ {
texture.reset(); texture.reset();
implicitMSTexture.reset();
level = 0; level = 0;
slice = 0; sliceOrDepth = 0;
loadAction = MTLLoadActionLoad; loadAction = MTLLoadActionLoad;
storeAction = MTLStoreActionStore; storeAction = MTLStoreActionStore;
storeActionOptions = MTLStoreActionOptionNone; storeActionOptions = MTLStoreActionOptionNone;
@ -636,7 +675,8 @@ void RenderPassAttachmentDesc::reset()
bool RenderPassAttachmentDesc::equalIgnoreLoadStoreOptions( bool RenderPassAttachmentDesc::equalIgnoreLoadStoreOptions(
const RenderPassAttachmentDesc &other) const 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 bool RenderPassAttachmentDesc::operator==(const RenderPassAttachmentDesc &other) const
@ -668,8 +708,9 @@ void RenderPassDesc::populateRenderPipelineOutputDesc(MTLColorWriteMask colorWri
void RenderPassDesc::populateRenderPipelineOutputDesc(const BlendDesc &blendState, void RenderPassDesc::populateRenderPipelineOutputDesc(const BlendDesc &blendState,
RenderPipelineOutputDesc *outDesc) const RenderPipelineOutputDesc *outDesc) const
{ {
auto &outputDescriptor = *outDesc; RenderPipelineOutputDesc &outputDescriptor = *outDesc;
outputDescriptor.numColorAttachments = this->numColorAttachments; outputDescriptor.numColorAttachments = this->numColorAttachments;
outputDescriptor.sampleCount = this->sampleCount;
for (uint32_t i = 0; i < this->numColorAttachments; ++i) for (uint32_t i = 0; i < this->numColorAttachments; ++i)
{ {
auto &renderPassColorAttachment = this->colorAttachments[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; auto depthTexture = this->depthAttachment.texture;
outputDescriptor.depthAttachmentPixelFormat = outputDescriptor.depthAttachmentPixelFormat =
depthTexture ? depthTexture->pixelFormat() : MTLPixelFormatInvalid; depthTexture ? depthTexture->pixelFormat() : MTLPixelFormatInvalid;
@ -770,7 +817,12 @@ void RenderPassDesc::convertToMetalDesc(MTLRenderPassDescriptor *objCDesc) const
} }
// RenderPipelineCache implementation // RenderPipelineCache implementation
RenderPipelineCache::RenderPipelineCache() {} RenderPipelineCache::RenderPipelineCache() : RenderPipelineCache(nullptr) {}
RenderPipelineCache::RenderPipelineCache(
RenderPipelineCacheSpecializeShaderFactory *specializedShaderFactory)
: mSpecializedShaderFactory(specializedShaderFactory)
{}
RenderPipelineCache::~RenderPipelineCache() {} RenderPipelineCache::~RenderPipelineCache() {}
@ -837,6 +889,10 @@ AutoObjCPtr<id<MTLRenderPipelineState>> RenderPipelineCache::insertRenderPipelin
{ {
AutoObjCPtr<id<MTLRenderPipelineState>> newState = AutoObjCPtr<id<MTLRenderPipelineState>> newState =
createRenderPipelineState(context, desc, insertDefaultAttribLayout); createRenderPipelineState(context, desc, insertDefaultAttribLayout);
if (!newState)
{
return nil;
}
int tableIdx = insertDefaultAttribLayout ? 1 : 0; int tableIdx = insertDefaultAttribLayout ? 1 : 0;
auto re = mRenderPipelineStates[tableIdx].insert(std::make_pair(desc, newState)); auto re = mRenderPipelineStates[tableIdx].insert(std::make_pair(desc, newState));
@ -850,16 +906,66 @@ AutoObjCPtr<id<MTLRenderPipelineState>> RenderPipelineCache::insertRenderPipelin
AutoObjCPtr<id<MTLRenderPipelineState>> RenderPipelineCache::createRenderPipelineState( AutoObjCPtr<id<MTLRenderPipelineState>> RenderPipelineCache::createRenderPipelineState(
Context *context, Context *context,
const RenderPipelineDesc &desc, const RenderPipelineDesc &originalDesc,
bool insertDefaultAttribLayout) bool insertDefaultAttribLayout)
{ {
ANGLE_MTL_OBJC_SCOPE ANGLE_MTL_OBJC_SCOPE
{ {
auto metalDevice = context->getMetalDevice(); // Disable coverage if the render pipeline's sample count is only 1.
AutoObjCObj<MTLRenderPipelineDescriptor> objCDesc = RenderPipelineDesc desc = originalDesc;
ToObjC(mVertexShader, mFragmentShader, desc); 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) if (insertDefaultAttribLayout)
{ {
MTLVertexBufferLayoutDescriptor *defaultAttribLayoutObjCDesc = MTLVertexBufferLayoutDescriptor *defaultAttribLayoutObjCDesc =
@ -873,8 +979,9 @@ AutoObjCPtr<id<MTLRenderPipelineState>> RenderPipelineCache::createRenderPipelin
atIndexedSubscript:kDefaultAttribsBindingIndex]; atIndexedSubscript:kDefaultAttribsBindingIndex];
} }
// Create pipeline state // Create pipeline state
NSError *err = nil; NSError *err = nil;
auto newState = [metalDevice newRenderPipelineStateWithDescriptor:objCDesc error:&err]; id<MTLRenderPipelineState> newState =
[metalDevice newRenderPipelineStateWithDescriptor:objCDesc error:&err];
if (err) if (err)
{ {
context->handleError(err, __FILE__, ANGLE_FUNCTION, __LINE__); context->handleError(err, __FILE__, ANGLE_FUNCTION, __LINE__);

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

@ -7,15 +7,28 @@
#include "common.h" #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 struct BlitParams
{ {
// 0: lower left, 1: lower right, 2: upper left // 0: lower left, 1: lower right, 2: upper left
float2 srcTexCoords[3]; float2 srcTexCoords[3];
int srcLevel; int srcLevel; // Source texture level.
bool srcLuminance; // source texture is luminance texture int srcLayer; // Source texture layer.
bool dstFlipViewportX;
bool dstFlipViewportY; bool dstFlipViewportY;
bool dstLuminance; // destination texture is luminance; bool dstLuminance; // destination texture is luminance. Unused by depth & stencil blitting.
}; };
struct BlitVSOut struct BlitVSOut
@ -24,13 +37,16 @@ struct BlitVSOut
float2 texCoords [[user(locn1)]]; float2 texCoords [[user(locn1)]];
}; };
vertex BlitVSOut blitVS(unsigned int vid [[ vertex_id ]], vertex BlitVSOut blitVS(unsigned int vid [[vertex_id]], constant BlitParams &options [[buffer(0)]])
constant BlitParams &options [[buffer(0)]])
{ {
BlitVSOut output; BlitVSOut output;
output.position = float4(gCorners[vid], 0.0, 1.0); output.position = float4(gCorners[vid], 0.0, 1.0);
output.texCoords = options.srcTexCoords[vid]; output.texCoords = options.srcTexCoords[vid];
if (options.dstFlipViewportX)
{
output.position.x = -output.position.x;
}
if (!options.dstFlipViewportY) if (!options.dstFlipViewportY)
{ {
// If viewport is not flipped, we have to flip Y in normalized device coordinates. // 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; return output;
} }
float4 blitSampleTexture(texture2d<float> srcTexture, static inline float3 cubeTexcoords(float2 texcoords, int face)
float2 texCoords,
constant BlitParams &options)
{ {
constexpr sampler textureSampler(mag_filter::linear, texcoords = 2.0 * texcoords - 1.0;
min_filter::linear); switch (face)
float4 output = srcTexture.sample(textureSampler, texCoords, level(options.srcLevel));
if (options.srcLuminance)
{ {
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; return output;
} }
float4 blitOutput(float4 color, constant BlitParams &options) fragment float4 blitFS(BLIT_COLOR_FS_PARAMS(float))
{ {
float4 ret = color; return blitReadTexture(FORWARD_BLIT_COLOR_FS_PARAMS);
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);
} }

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

@ -13,6 +13,8 @@
# include <metal_stdlib> # include <metal_stdlib>
#endif #endif
#include "constants.h"
#define ANGLE_KERNEL_GUARD(IDX, MAX_COUNT) \ #define ANGLE_KERNEL_GUARD(IDX, MAX_COUNT) \
if (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. # auto_script parameters.
if len(sys.argv) > 1: if len(sys.argv) > 1:
inputs = [ 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'] 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 # 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; using namespace metal;
@ -68,13 +95,27 @@ fragment float4 clearFS(constant ClearParams &clearParams [[buffer(0)]])
} }
# 10 "master_source.metal" 2 # 10 "master_source.metal" 2
# 1 "./blit.metal" 1 # 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 struct BlitParams
{ {
float2 srcTexCoords[3]; float2 srcTexCoords[3];
int srcLevel; int srcLevel;
bool srcLuminance; int srcLayer;
bool dstFlipViewportX;
bool dstFlipViewportY; bool dstFlipViewportY;
bool dstLuminance; bool dstLuminance;
}; };
@ -85,13 +126,16 @@ struct BlitVSOut
float2 texCoords [[user(locn1)]]; float2 texCoords [[user(locn1)]];
}; };
vertex BlitVSOut blitVS(unsigned int vid [[ vertex_id ]], vertex BlitVSOut blitVS(unsigned int vid [[vertex_id]], constant BlitParams &options [[buffer(0)]])
constant BlitParams &options [[buffer(0)]])
{ {
BlitVSOut output; BlitVSOut output;
output.position = float4(gCorners[vid], 0.0, 1.0); output.position = float4(gCorners[vid], 0.0, 1.0);
output.texCoords = options.srcTexCoords[vid]; output.texCoords = options.srcTexCoords[vid];
if (options.dstFlipViewportX)
{
output.position.x = -output.position.x;
}
if (!options.dstFlipViewportY) if (!options.dstFlipViewportY)
{ {
@ -102,60 +146,109 @@ vertex BlitVSOut blitVS(unsigned int vid [[ vertex_id ]],
return output; return output;
} }
float4 blitSampleTexture(texture2d<float> srcTexture, static inline float3 cubeTexcoords(float2 texcoords, int face)
float2 texCoords,
constant BlitParams &options)
{ {
constexpr sampler textureSampler(mag_filter::linear, texcoords = 2.0 * texcoords - 1.0;
min_filter::linear); switch (face)
float4 output = srcTexture.sample(textureSampler, texCoords, level(options.srcLevel));
if (options.srcLuminance)
{ {
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; 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; return blitReadTexture(input, srcTexture2d, srcTexture2dArray, srcTexture2dMS, srcTextureCube, srcTexture3d, textureSampler, options);
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);
} }
# 11 "master_source.metal" 2 # 11 "master_source.metal" 2
# 1 "./gen_indices.metal" 1 # 1 "./gen_indices.metal" 1

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

@ -16,15 +16,39 @@ using namespace angle;
namespace 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: protected:
void testSetUp() override 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. // Get display.
EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(), EGL_NONE}; EGLAttrib dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, platform.getRenderer(),
mDisplay = eglGetPlatformDisplayEXT( EGL_FEATURE_OVERRIDES_DISABLED_ANGLE,
EGL_PLATFORM_ANGLE_ANGLE, reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs); 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(mDisplay != EGL_NO_DISPLAY);
ASSERT_TRUE(eglInitialize(mDisplay, nullptr, nullptr) == EGL_TRUE); ASSERT_TRUE(eglInitialize(mDisplay, nullptr, nullptr) == EGL_TRUE);
@ -57,9 +81,9 @@ class MultisampleTest : public ANGLETest
EGLint contextAttributes[] = { EGLint contextAttributes[] = {
EGL_CONTEXT_MAJOR_VERSION_KHR, EGL_CONTEXT_MAJOR_VERSION_KHR,
GetParam().majorVersion, platform.majorVersion,
EGL_CONTEXT_MINOR_VERSION_KHR, EGL_CONTEXT_MINOR_VERSION_KHR,
GetParam().minorVersion, platform.minorVersion,
EGL_NONE, 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 that resolve from multisample default framebuffer works.
TEST_P(MultisampleTestES3, ResolveToFBO) TEST_P(MultisampleTestES3, ResolveToFBO)
{ {
@ -305,26 +413,41 @@ TEST_P(MultisampleTestES3, ResolveToFBO)
EXPECT_PIXEL_COLOR_NEAR(kWindowSize / 2, kWindowSize / 2, kResult, 1); EXPECT_PIXEL_COLOR_NEAR(kWindowSize / 2, kWindowSize / 2, kResult, 1);
} }
ANGLE_INSTANTIATE_TEST(MultisampleTest, ANGLE_INSTANTIATE_TEST_COMBINE_1(MultisampleTest,
WithNoFixture(ES2_D3D11()), PrintToStringParamName,
WithNoFixture(ES3_D3D11()), testing::Values(false),
WithNoFixture(ES31_D3D11()), WithNoFixture(ES2_D3D11()),
WithNoFixture(ES2_OPENGL()), WithNoFixture(ES3_D3D11()),
WithNoFixture(ES3_OPENGL()), WithNoFixture(ES31_D3D11()),
WithNoFixture(ES31_OPENGL()), WithNoFixture(ES2_METAL()),
WithNoFixture(ES2_OPENGLES()), WithNoFixture(ES2_OPENGL()),
WithNoFixture(ES3_OPENGLES()), WithNoFixture(ES3_OPENGL()),
WithNoFixture(ES31_OPENGLES()), WithNoFixture(ES31_OPENGL()),
WithNoFixture(ES2_VULKAN()), WithNoFixture(ES2_OPENGLES()),
WithNoFixture(ES3_VULKAN()), WithNoFixture(ES3_OPENGLES()),
WithNoFixture(ES31_VULKAN())); WithNoFixture(ES31_OPENGLES()),
ANGLE_INSTANTIATE_TEST(MultisampleTestES3, WithNoFixture(ES2_VULKAN()),
WithNoFixture(ES3_D3D11()), WithNoFixture(ES3_VULKAN()),
WithNoFixture(ES31_D3D11()), WithNoFixture(ES31_VULKAN()));
WithNoFixture(ES3_OPENGL()),
WithNoFixture(ES31_OPENGL()), namespace store_and_resolve_feature_off
WithNoFixture(ES3_OPENGLES()), {
WithNoFixture(ES31_OPENGLES()), // Simulate missing msaa auto resolve feature in Metal.
WithNoFixture(ES3_VULKAN()), ANGLE_INSTANTIATE_TEST_COMBINE_1(MultisampleTest,
WithNoFixture(ES31_VULKAN())); 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 } // anonymous namespace