зеркало из https://github.com/AvaloniaUI/angle.git
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:
Родитель
f9e01f1230
Коммит
9277ee7413
|
@ -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 ¶ms);
|
const BlitParams ¶ms);
|
||||||
|
|
||||||
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 ¶ms);
|
const BlitParams ¶ms);
|
||||||
|
// 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 ¶ms)
|
const IndexConversionParams ¶ms)
|
||||||
{
|
{
|
||||||
|
@ -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
|
||||||
|
|
Загрузка…
Ссылка в новой задаче