зеркало из 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.
|
||||
// It is incremented every time the API changes.
|
||||
#define ANGLE_SH_VERSION 230
|
||||
#define ANGLE_SH_VERSION 231
|
||||
|
||||
enum ShShaderSpec
|
||||
{
|
||||
|
@ -790,6 +790,12 @@ extern const char kAtomicCountersBlockName[];
|
|||
extern const char kLineRasterEmulationPosition[];
|
||||
|
||||
} // namespace vk
|
||||
|
||||
namespace mtl
|
||||
{
|
||||
// Specialization constant to enable GL_SAMPLE_COVERAGE_VALUE emulation.
|
||||
extern const char kCoverageMaskEnabledConstName[];
|
||||
} // namespace mtl
|
||||
} // namespace sh
|
||||
|
||||
#endif // GLSLANG_SHADERLANG_H_
|
||||
|
|
|
@ -41,6 +41,10 @@ struct FeaturesMtl : FeatureSetBase
|
|||
"Some Apple platforms such as iOS allows separate depth & stencil buffers, "
|
||||
"whereas others such as macOS don't",
|
||||
&members};
|
||||
|
||||
Feature allowMultisampleStoreAndResolve = {
|
||||
"allow_msaa_store_and_resolve", FeatureCategory::MetalFeatures,
|
||||
"The renderer supports MSAA store and resolve in the same pass", &members};
|
||||
};
|
||||
|
||||
} // namespace angle
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
{
|
||||
"src/libANGLE/renderer/metal/shaders/blit.metal":
|
||||
"9281aba529ceb4ed5131b7f9c0515362",
|
||||
"1a12b22f56799bd38cf1c6b301b720ee",
|
||||
"src/libANGLE/renderer/metal/shaders/clear.metal":
|
||||
"1c231afc6100433a79fce49046aa5965",
|
||||
"src/libANGLE/renderer/metal/shaders/common.h":
|
||||
"569171e345ef36dd6a3b12aeebfae4a6",
|
||||
"7330bd3f7ab21214e4fe16bc526749bb",
|
||||
"src/libANGLE/renderer/metal/shaders/compiled/mtl_default_shaders.inc":
|
||||
"c42c37285c375de9a065b925ed54aebd",
|
||||
"445538e99fb3679c3c8436f565911c69",
|
||||
"src/libANGLE/renderer/metal/shaders/constants.h":
|
||||
"9bb6e63bf2b48a7a56978c787bde4850",
|
||||
"src/libANGLE/renderer/metal/shaders/gen_indices.metal":
|
||||
"002511e2b980a7fca7e80cbda6a82712",
|
||||
"src/libANGLE/renderer/metal/shaders/gen_mtl_internal_shaders.py":
|
||||
"8de75752bb966cdbe575defc04fa7a7a",
|
||||
"0e599fb113dbc3f714291383d85c39c2",
|
||||
"src/libANGLE/renderer/metal/shaders/master_source.metal":
|
||||
"fbe6f4bfb49a48ae87791a4cff5fab0a",
|
||||
"src/libANGLE/renderer/metal/shaders/mtl_default_shaders_src_autogen.inc":
|
||||
"8a94beb0d979f472d71686b6f95b624f"
|
||||
"ee7ff414da20e7b84f1187d2bea1e84d"
|
||||
}
|
|
@ -25,9 +25,31 @@
|
|||
namespace sh
|
||||
{
|
||||
|
||||
namespace mtl
|
||||
{
|
||||
/** extern */
|
||||
const char kCoverageMaskEnabledConstName[] = "ANGLECoverageMaskEnabled";
|
||||
} // namespace mtl
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
constexpr ImmutableString kCoverageMaskField = ImmutableString("coverageMask");
|
||||
constexpr ImmutableString kSampleMaskWriteFuncName = ImmutableString("ANGLEWriteSampleMask");
|
||||
|
||||
TIntermBinary *CreateDriverUniformRef(const TVariable *driverUniforms, const char *fieldName)
|
||||
{
|
||||
size_t fieldIndex =
|
||||
FindFieldIndex(driverUniforms->getType().getInterfaceBlock()->fields(), fieldName);
|
||||
|
||||
TIntermSymbol *angleUniformsRef = new TIntermSymbol(driverUniforms);
|
||||
TConstantUnion *uniformIndex = new TConstantUnion;
|
||||
uniformIndex->setIConst(static_cast<int>(fieldIndex));
|
||||
TIntermConstantUnion *indexRef =
|
||||
new TIntermConstantUnion(uniformIndex, *StaticType::GetBasic<EbtInt>());
|
||||
return new TIntermBinary(EOpIndexDirectInterfaceBlock, angleUniformsRef, indexRef);
|
||||
}
|
||||
|
||||
// Unlike Vulkan having auto viewport flipping extension, in Metal we have to flip gl_Position.y
|
||||
// manually.
|
||||
// This operation performs flipping the gl_Position.y using this expression:
|
||||
|
@ -89,6 +111,13 @@ bool TranslatorMetal::translate(TIntermBlock *root,
|
|||
return false;
|
||||
}
|
||||
}
|
||||
else if (getShaderType() == GL_FRAGMENT_SHADER)
|
||||
{
|
||||
if (!insertSampleMaskWritingLogic(root, driverUniforms))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Write translated shader.
|
||||
root->traverse(&outputGLSL);
|
||||
|
@ -124,4 +153,70 @@ bool TranslatorMetal::transformDepthBeforeCorrection(TIntermBlock *root,
|
|||
return RunAtTheEndOfShader(this, root, assignment, &getSymbolTable());
|
||||
}
|
||||
|
||||
void TranslatorMetal::createAdditionalGraphicsDriverUniformFields(std::vector<TField *> *fieldsOut)
|
||||
{
|
||||
// Add coverage mask to driver uniform. Metal doesn't have built-in GL_SAMPLE_COVERAGE_VALUE
|
||||
// equivalent functionality, needs to emulate it using fragment shader's [[sample_mask]] output
|
||||
// value.
|
||||
TField *coverageMaskField =
|
||||
new TField(new TType(EbtUInt), kCoverageMaskField, TSourceLoc(), SymbolType::AngleInternal);
|
||||
fieldsOut->push_back(coverageMaskField);
|
||||
}
|
||||
|
||||
// Add sample_mask writing to main, guarded by the specialization constant
|
||||
// kCoverageMaskEnabledConstName
|
||||
ANGLE_NO_DISCARD bool TranslatorMetal::insertSampleMaskWritingLogic(TIntermBlock *root,
|
||||
const TVariable *driverUniforms)
|
||||
{
|
||||
TInfoSinkBase &sink = getInfoSink().obj;
|
||||
TSymbolTable *symbolTable = &getSymbolTable();
|
||||
|
||||
// Insert coverageMaskEnabled specialization constant and sample_mask writing function.
|
||||
sink << "layout (constant_id=0) const bool " << mtl::kCoverageMaskEnabledConstName;
|
||||
sink << " = false;\n";
|
||||
sink << "void " << kSampleMaskWriteFuncName << "(uint mask)\n";
|
||||
sink << "{\n";
|
||||
sink << " if (" << mtl::kCoverageMaskEnabledConstName << ")\n";
|
||||
sink << " {\n";
|
||||
sink << " gl_SampleMask[0] = int(mask);\n";
|
||||
sink << " }\n";
|
||||
sink << "}\n";
|
||||
|
||||
// Create kCoverageMaskEnabledConstName and kSampleMaskWriteFuncName variable references.
|
||||
TType *boolType = new TType(EbtBool);
|
||||
boolType->setQualifier(EvqConst);
|
||||
TVariable *coverageMaskEnabledVar =
|
||||
new TVariable(symbolTable, ImmutableString(mtl::kCoverageMaskEnabledConstName), boolType,
|
||||
SymbolType::AngleInternal);
|
||||
|
||||
TFunction *sampleMaskWriteFunc =
|
||||
new TFunction(symbolTable, kSampleMaskWriteFuncName, SymbolType::AngleInternal,
|
||||
StaticType::GetBasic<EbtVoid>(), false);
|
||||
|
||||
TType *uintType = new TType(EbtUInt);
|
||||
TVariable *maskArg =
|
||||
new TVariable(symbolTable, ImmutableString("mask"), uintType, SymbolType::AngleInternal);
|
||||
sampleMaskWriteFunc->addParameter(maskArg);
|
||||
|
||||
// coverageMask
|
||||
TIntermBinary *coverageMask = CreateDriverUniformRef(driverUniforms, kCoverageMaskField.data());
|
||||
|
||||
// Insert this code to the end of main()
|
||||
// if (ANGLECoverageMaskEnabled)
|
||||
// {
|
||||
// ANGLEWriteSampleMask(ANGLEUniforms.coverageMask);
|
||||
// }
|
||||
TIntermSequence *args = new TIntermSequence;
|
||||
args->push_back(coverageMask);
|
||||
TIntermAggregate *callSampleMaskWriteFunc =
|
||||
TIntermAggregate::CreateFunctionCall(*sampleMaskWriteFunc, args);
|
||||
TIntermBlock *callBlock = new TIntermBlock;
|
||||
callBlock->appendStatement(callSampleMaskWriteFunc);
|
||||
|
||||
TIntermSymbol *coverageMaskEnabled = new TIntermSymbol(coverageMaskEnabledVar);
|
||||
TIntermIfElse *ifCall = new TIntermIfElse(coverageMaskEnabled, callBlock, nullptr);
|
||||
|
||||
return RunAtTheEndOfShader(this, root, ifCall, symbolTable);
|
||||
}
|
||||
|
||||
} // namespace sh
|
||||
|
|
|
@ -32,6 +32,11 @@ class TranslatorMetal : public TranslatorVulkan
|
|||
|
||||
ANGLE_NO_DISCARD bool transformDepthBeforeCorrection(TIntermBlock *root,
|
||||
const TVariable *driverUniforms) override;
|
||||
|
||||
void createAdditionalGraphicsDriverUniformFields(std::vector<TField *> *fieldsOut) override;
|
||||
|
||||
ANGLE_NO_DISCARD bool insertSampleMaskWritingLogic(TIntermBlock *root,
|
||||
const TVariable *driverUniforms);
|
||||
};
|
||||
|
||||
} // namespace sh
|
||||
|
|
|
@ -196,19 +196,6 @@ constexpr size_t kNumComputeDriverUniforms
|
|||
constexpr std::array<const char *, kNumComputeDriverUniforms> kComputeDriverUniformNames = {
|
||||
{kAcbBufferOffsets}};
|
||||
|
||||
size_t FindFieldIndex(const TFieldList &fieldList, const char *fieldName)
|
||||
{
|
||||
for (size_t fieldIndex = 0; fieldIndex < fieldList.size(); ++fieldIndex)
|
||||
{
|
||||
if (strcmp(fieldList[fieldIndex]->name().data(), fieldName) == 0)
|
||||
{
|
||||
return fieldIndex;
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
TIntermBinary *CreateDriverUniformRef(const TVariable *driverUniforms, const char *fieldName)
|
||||
{
|
||||
size_t fieldIndex =
|
||||
|
@ -389,7 +376,9 @@ ANGLE_NO_DISCARD bool AppendVertexShaderTransformFeedbackOutputToMain(TCompiler
|
|||
// variable.
|
||||
//
|
||||
// There are Graphics and Compute variations as they require different uniforms.
|
||||
const TVariable *AddGraphicsDriverUniformsToShader(TIntermBlock *root, TSymbolTable *symbolTable)
|
||||
const TVariable *AddGraphicsDriverUniformsToShader(TIntermBlock *root,
|
||||
TSymbolTable *symbolTable,
|
||||
const std::vector<TField *> &additionalFields)
|
||||
{
|
||||
// Init the depth range type.
|
||||
TFieldList *depthRangeParamsFields = new TFieldList();
|
||||
|
@ -445,6 +434,10 @@ const TVariable *AddGraphicsDriverUniformsToShader(TIntermBlock *root, TSymbolTa
|
|||
driverFieldList->push_back(driverUniformField);
|
||||
}
|
||||
|
||||
// Back-end specific fields
|
||||
driverFieldList->insert(driverFieldList->end(), additionalFields.begin(),
|
||||
additionalFields.end());
|
||||
|
||||
// Define a driver uniform block "ANGLEUniformBlock" with instance name "ANGLEUniforms".
|
||||
return DeclareInterfaceBlock(
|
||||
root, symbolTable, driverFieldList, EvqUniform, TMemoryQualifier::Create(), 0,
|
||||
|
@ -865,7 +858,10 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root,
|
|||
}
|
||||
else
|
||||
{
|
||||
driverUniforms = AddGraphicsDriverUniformsToShader(root, &getSymbolTable());
|
||||
std::vector<TField *> additionalFields;
|
||||
createAdditionalGraphicsDriverUniformFields(&additionalFields);
|
||||
driverUniforms =
|
||||
AddGraphicsDriverUniformsToShader(root, &getSymbolTable(), additionalFields);
|
||||
}
|
||||
|
||||
if (atomicCounterCount > 0)
|
||||
|
|
|
@ -48,6 +48,9 @@ class TranslatorVulkan : public TCompiler
|
|||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Back-end specific fields to be added to driver uniform. See TranslatorMetal.cpp.
|
||||
virtual void createAdditionalGraphicsDriverUniformFields(std::vector<TField *> *fieldsOut) {}
|
||||
};
|
||||
|
||||
} // namespace sh
|
||||
|
|
|
@ -962,4 +962,17 @@ bool IsValidImplicitConversion(sh::ImplicitTypeConversion conversion, TOperator
|
|||
return false;
|
||||
}
|
||||
|
||||
size_t FindFieldIndex(const TFieldList &fieldList, const char *fieldName)
|
||||
{
|
||||
for (size_t fieldIndex = 0; fieldIndex < fieldList.size(); ++fieldIndex)
|
||||
{
|
||||
if (strcmp(fieldList[fieldIndex]->name().data(), fieldName) == 0)
|
||||
{
|
||||
return fieldIndex;
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace sh
|
||||
|
|
|
@ -88,6 +88,8 @@ bool IsSpecWithFunctionBodyNewScope(ShShaderSpec shaderSpec, int shaderVersion);
|
|||
ImplicitTypeConversion GetConversion(TBasicType t1, TBasicType t2);
|
||||
|
||||
bool IsValidImplicitConversion(ImplicitTypeConversion conversion, TOperator op);
|
||||
|
||||
size_t FindFieldIndex(const TFieldList &fieldList, const char *fieldName);
|
||||
} // namespace sh
|
||||
|
||||
#endif // COMPILER_TRANSLATOR_UTIL_H_
|
||||
|
|
|
@ -55,6 +55,7 @@ _metal_backend_sources = [
|
|||
"mtl_utils.h",
|
||||
"mtl_utils.mm",
|
||||
"shaders/compiled/mtl_default_shaders.inc",
|
||||
"shaders/constants.h",
|
||||
"shaders/mtl_default_shaders_src_autogen.inc",
|
||||
]
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ class DisplayMtl;
|
|||
class FramebufferMtl;
|
||||
class VertexArrayMtl;
|
||||
class ProgramMtl;
|
||||
class WindowSurfaceMtl;
|
||||
|
||||
class ContextMtl : public ContextImpl, public mtl::Context
|
||||
{
|
||||
|
@ -266,6 +267,7 @@ class ContextMtl : public ContextImpl, public mtl::Context
|
|||
|
||||
// Call this to notify ContextMtl whenever FramebufferMtl's state changed
|
||||
void onDrawFrameBufferChange(const gl::Context *context, FramebufferMtl *framebuffer);
|
||||
void onBackbufferResized(const gl::Context *context, WindowSurfaceMtl *backbuffer);
|
||||
|
||||
const MTLClearColor &getClearColorValue() const;
|
||||
MTLColorWriteMask getColorMask() const;
|
||||
|
@ -296,13 +298,10 @@ class ContextMtl : public ContextImpl, public mtl::Context
|
|||
|
||||
// Check whether compatible render pass has been started.
|
||||
bool hasStartedRenderPass(const mtl::RenderPassDesc &desc);
|
||||
bool hasStartedRenderPass(FramebufferMtl *framebuffer);
|
||||
|
||||
// Get current render encoder. May be nullptr if no render pass has been started.
|
||||
mtl::RenderCommandEncoder *getRenderCommandEncoder();
|
||||
|
||||
mtl::RenderCommandEncoder *getCurrentFramebufferRenderCommandEncoder();
|
||||
|
||||
// Will end current command encoder if it is valid, then start new encoder.
|
||||
// Unless hasStartedRenderPass(desc) returns true.
|
||||
mtl::RenderCommandEncoder *getRenderCommandEncoder(const mtl::RenderPassDesc &desc);
|
||||
|
@ -448,6 +447,10 @@ class ContextMtl : public ContextImpl, public mtl::Context
|
|||
// Used to pre-rotate gl_FragCoord for Vulkan swapchain images on Android (a mat2, which is
|
||||
// padded to the size of two vec4's).
|
||||
float fragRotation[8];
|
||||
|
||||
uint32_t coverageMask;
|
||||
|
||||
float padding2[3];
|
||||
};
|
||||
|
||||
struct DefaultAttribute
|
||||
|
@ -466,12 +469,6 @@ class ContextMtl : public ContextImpl, public mtl::Context
|
|||
VertexArrayMtl *mVertexArray = nullptr;
|
||||
ProgramMtl *mProgram = nullptr;
|
||||
|
||||
// Special flag to indicate current draw framebuffer is default framebuffer.
|
||||
// We need this instead of calling mDrawFramebuffer->getState().isDefault() because
|
||||
// mDrawFramebuffer might point to a deleted object, ContextMtl only knows about this very late,
|
||||
// only during syncState() function call.
|
||||
bool mDrawFramebufferIsDefault = true;
|
||||
|
||||
using DirtyBits = angle::BitSet<DIRTY_BIT_MAX>;
|
||||
|
||||
gl::AttributesMask mDirtyDefaultAttribsMask;
|
||||
|
|
|
@ -654,19 +654,19 @@ angle::Result ContextMtl::syncState(const gl::Context *context,
|
|||
invalidateRenderPipeline();
|
||||
break;
|
||||
case gl::State::DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE_ENABLED:
|
||||
// NOTE(hqle): MSAA support
|
||||
invalidateRenderPipeline();
|
||||
break;
|
||||
case gl::State::DIRTY_BIT_SAMPLE_COVERAGE_ENABLED:
|
||||
// NOTE(hqle): MSAA support
|
||||
invalidateRenderPipeline();
|
||||
break;
|
||||
case gl::State::DIRTY_BIT_SAMPLE_COVERAGE:
|
||||
// NOTE(hqle): MSAA support
|
||||
invalidateDriverUniforms();
|
||||
break;
|
||||
case gl::State::DIRTY_BIT_SAMPLE_MASK_ENABLED:
|
||||
// NOTE(hqle): MSAA support
|
||||
// NOTE(hqle): 3.1 MSAA support
|
||||
break;
|
||||
case gl::State::DIRTY_BIT_SAMPLE_MASK:
|
||||
// NOTE(hqle): MSAA support
|
||||
// NOTE(hqle): 3.1 MSAA support
|
||||
break;
|
||||
case gl::State::DIRTY_BIT_DEPTH_TEST_ENABLED:
|
||||
mDepthStencilDesc.updateDepthTestEnabled(glState.getDepthStencilState());
|
||||
|
@ -801,7 +801,7 @@ angle::Result ContextMtl::syncState(const gl::Context *context,
|
|||
invalidateCurrentTextures();
|
||||
break;
|
||||
case gl::State::DIRTY_BIT_MULTISAMPLING:
|
||||
// NOTE(hqle): MSAA feature.
|
||||
// NOTE(hqle): MSAA on/off.
|
||||
break;
|
||||
case gl::State::DIRTY_BIT_SAMPLE_ALPHA_TO_ONE:
|
||||
// NOTE(hqle): this is part of EXT_multisample_compatibility.
|
||||
|
@ -896,7 +896,7 @@ ProgramImpl *ContextMtl::createProgram(const gl::ProgramState &state)
|
|||
// Framebuffer creation
|
||||
FramebufferImpl *ContextMtl::createFramebuffer(const gl::FramebufferState &state)
|
||||
{
|
||||
return new FramebufferMtl(state, false);
|
||||
return new FramebufferMtl(state, false, nullptr);
|
||||
}
|
||||
|
||||
// Texture creation
|
||||
|
@ -1170,16 +1170,10 @@ void ContextMtl::present(const gl::Context *context, id<CAMetalDrawable> present
|
|||
{
|
||||
ensureCommandBufferReady();
|
||||
|
||||
// Always discard default FBO's depth stencil buffers at the end of the frame:
|
||||
if (mDrawFramebufferIsDefault && hasStartedRenderPass(mDrawFramebuffer))
|
||||
FramebufferMtl *currentframebuffer = mtl::GetImpl(getState().getDrawFramebuffer());
|
||||
if (currentframebuffer)
|
||||
{
|
||||
constexpr GLenum dsAttachments[] = {GL_DEPTH, GL_STENCIL};
|
||||
(void)mDrawFramebuffer->invalidate(context, 2, dsAttachments);
|
||||
|
||||
endEncoding(false);
|
||||
|
||||
// Reset discard flag by notify framebuffer that a new render pass has started.
|
||||
mDrawFramebuffer->onStartedDrawingToFrameBuffer(context);
|
||||
currentframebuffer->onFrameEnd(context);
|
||||
}
|
||||
|
||||
endEncoding(false);
|
||||
|
@ -1202,11 +1196,6 @@ bool ContextMtl::hasStartedRenderPass(const mtl::RenderPassDesc &desc)
|
|||
mRenderEncoder.renderPassDesc().equalIgnoreLoadStoreOptions(desc);
|
||||
}
|
||||
|
||||
bool ContextMtl::hasStartedRenderPass(FramebufferMtl *framebuffer)
|
||||
{
|
||||
return framebuffer && hasStartedRenderPass(framebuffer->getRenderPassDesc(this));
|
||||
}
|
||||
|
||||
// Get current render encoder
|
||||
mtl::RenderCommandEncoder *ContextMtl::getRenderCommandEncoder()
|
||||
{
|
||||
|
@ -1218,16 +1207,6 @@ mtl::RenderCommandEncoder *ContextMtl::getRenderCommandEncoder()
|
|||
return &mRenderEncoder;
|
||||
}
|
||||
|
||||
mtl::RenderCommandEncoder *ContextMtl::getCurrentFramebufferRenderCommandEncoder()
|
||||
{
|
||||
if (!mDrawFramebuffer)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return getRenderCommandEncoder(mDrawFramebuffer->getRenderPassDesc(this));
|
||||
}
|
||||
|
||||
mtl::RenderCommandEncoder *ContextMtl::getRenderCommandEncoder(const mtl::RenderPassDesc &desc)
|
||||
{
|
||||
if (hasStartedRenderPass(desc))
|
||||
|
@ -1256,10 +1235,10 @@ mtl::RenderCommandEncoder *ContextMtl::getRenderCommandEncoder(
|
|||
|
||||
mtl::RenderPassDesc rpDesc;
|
||||
|
||||
rpDesc.colorAttachments[0].texture = textureTarget;
|
||||
rpDesc.colorAttachments[0].level = index.getLevelIndex();
|
||||
rpDesc.colorAttachments[0].slice = index.hasLayer() ? index.getLayerIndex() : 0;
|
||||
rpDesc.numColorAttachments = 1;
|
||||
rpDesc.colorAttachments[0].texture = textureTarget;
|
||||
rpDesc.colorAttachments[0].level = index.getLevelIndex();
|
||||
rpDesc.colorAttachments[0].sliceOrDepth = index.hasLayer() ? index.getLayerIndex() : 0;
|
||||
rpDesc.numColorAttachments = 1;
|
||||
|
||||
if (clearColor.valid())
|
||||
{
|
||||
|
@ -1415,8 +1394,7 @@ void ContextMtl::updateDrawFrameBufferBinding(const gl::Context *context)
|
|||
{
|
||||
const gl::State &glState = getState();
|
||||
|
||||
mDrawFramebuffer = mtl::GetImpl(glState.getDrawFramebuffer());
|
||||
mDrawFramebufferIsDefault = mDrawFramebuffer->getState().isDefault();
|
||||
mDrawFramebuffer = mtl::GetImpl(glState.getDrawFramebuffer());
|
||||
|
||||
mDrawFramebuffer->onStartedDrawingToFrameBuffer(context);
|
||||
|
||||
|
@ -1435,10 +1413,26 @@ void ContextMtl::onDrawFrameBufferChange(const gl::Context *context, Framebuffer
|
|||
updateFrontFace(glState);
|
||||
updateScissor(glState);
|
||||
|
||||
// End any render encoding using the old render pass.
|
||||
endEncoding(false);
|
||||
// Need to re-apply state to RenderCommandEncoder
|
||||
invalidateState(context);
|
||||
}
|
||||
|
||||
void ContextMtl::onBackbufferResized(const gl::Context *context, WindowSurfaceMtl *backbuffer)
|
||||
{
|
||||
const gl::State &glState = getState();
|
||||
FramebufferMtl *framebuffer = mtl::GetImpl(glState.getDrawFramebuffer());
|
||||
if (framebuffer->getAttachedBackbuffer() != backbuffer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
updateViewport(framebuffer, glState.getViewport(), glState.getNearPlane(),
|
||||
glState.getFarPlane());
|
||||
updateScissor(glState);
|
||||
}
|
||||
|
||||
void ContextMtl::updateProgramExecutable(const gl::Context *context)
|
||||
{
|
||||
// Need to rebind textures
|
||||
|
@ -1519,8 +1513,7 @@ angle::Result ContextMtl::setupDraw(const gl::Context *context,
|
|||
if (mDirtyBits.test(DIRTY_BIT_DRAW_FRAMEBUFFER))
|
||||
{
|
||||
// Start new render command encoder
|
||||
const mtl::RenderPassDesc &rpDesc = mDrawFramebuffer->getRenderPassDesc(this);
|
||||
ANGLE_MTL_TRY(this, getRenderCommandEncoder(rpDesc));
|
||||
ANGLE_MTL_TRY(this, mDrawFramebuffer->ensureRenderPassStarted(context));
|
||||
|
||||
// re-apply everything
|
||||
invalidateState(context);
|
||||
|
@ -1729,6 +1722,19 @@ angle::Result ContextMtl::handleDirtyDriverUniforms(const gl::Context *context)
|
|||
mDriverUniforms.fragRotation[6] = 0.0f;
|
||||
mDriverUniforms.fragRotation[7] = 0.0f;
|
||||
|
||||
// Sample coverage mask
|
||||
uint32_t sampleBitCount = mDrawFramebuffer->getSamples();
|
||||
uint32_t coverageSampleBitCount =
|
||||
static_cast<uint32_t>(std::round(mState.getSampleCoverageValue() * sampleBitCount));
|
||||
ASSERT(sampleBitCount < 32);
|
||||
uint32_t coverageMask = (1u << coverageSampleBitCount) - 1;
|
||||
uint32_t sampleMask = (1u << sampleBitCount) - 1;
|
||||
if (mState.getSampleCoverageInvert())
|
||||
{
|
||||
coverageMask = sampleMask & (~coverageMask);
|
||||
}
|
||||
mDriverUniforms.coverageMask = coverageMask;
|
||||
|
||||
ASSERT(mRenderEncoder.valid());
|
||||
mRenderEncoder.setFragmentData(mDriverUniforms, mtl::kDriverUniformsBindingIndex);
|
||||
mRenderEncoder.setVertexData(mDriverUniforms, mtl::kDriverUniformsBindingIndex);
|
||||
|
@ -1742,7 +1748,7 @@ angle::Result ContextMtl::handleDirtyDepthStencilState(const gl::Context *contex
|
|||
|
||||
// Need to handle the case when render pass doesn't have depth/stencil attachment.
|
||||
mtl::DepthStencilDesc dsDesc = mDepthStencilDesc;
|
||||
const mtl::RenderPassDesc &renderPassDesc = mDrawFramebuffer->getRenderPassDesc(this);
|
||||
const mtl::RenderPassDesc &renderPassDesc = mRenderEncoder.renderPassDesc();
|
||||
|
||||
if (!renderPassDesc.depthAttachment.texture)
|
||||
{
|
||||
|
@ -1796,12 +1802,14 @@ angle::Result ContextMtl::checkIfPipelineChanged(
|
|||
|
||||
if (rppChange)
|
||||
{
|
||||
const mtl::RenderPassDesc &renderPassDesc = mDrawFramebuffer->getRenderPassDesc(this);
|
||||
const mtl::RenderPassDesc &renderPassDesc = mRenderEncoder.renderPassDesc();
|
||||
// Obtain RenderPipelineDesc's output descriptor.
|
||||
renderPassDesc.populateRenderPipelineOutputDesc(mBlendDesc,
|
||||
&mRenderPipelineDesc.outputDescriptor);
|
||||
|
||||
mRenderPipelineDesc.inputPrimitiveTopology = topologyClass;
|
||||
mRenderPipelineDesc.alphaToCoverageEnabled = mState.isSampleAlphaToCoverageEnabled();
|
||||
mRenderPipelineDesc.emulateCoverageMask = mState.isSampleCoverageEnabled();
|
||||
|
||||
*changedPipelineDesc = mRenderPipelineDesc;
|
||||
}
|
||||
|
|
|
@ -295,8 +295,13 @@ egl::ConfigSet DisplayMtl::generateConfigs()
|
|||
|
||||
config.surfaceType = EGL_WINDOW_BIT;
|
||||
|
||||
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
|
||||
config.minSwapInterval = 0;
|
||||
config.maxSwapInterval = 1;
|
||||
#else
|
||||
config.minSwapInterval = 1;
|
||||
config.maxSwapInterval = 1;
|
||||
#endif
|
||||
|
||||
config.renderTargetFormat = GL_RGBA8;
|
||||
config.depthStencilFormat = GL_DEPTH24_STENCIL8;
|
||||
|
@ -308,40 +313,52 @@ egl::ConfigSet DisplayMtl::generateConfigs()
|
|||
|
||||
config.colorComponentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
|
||||
|
||||
// Buffer sizes
|
||||
config.redSize = 8;
|
||||
config.greenSize = 8;
|
||||
config.blueSize = 8;
|
||||
config.alphaSize = 8;
|
||||
config.bufferSize = config.redSize + config.greenSize + config.blueSize + config.alphaSize;
|
||||
constexpr int samplesSupported[] = {0, 4};
|
||||
|
||||
// With DS
|
||||
config.depthSize = 24;
|
||||
config.stencilSize = 8;
|
||||
configs.add(config);
|
||||
for (int samples : samplesSupported)
|
||||
{
|
||||
config.samples = samples;
|
||||
config.sampleBuffers = (samples == 0) ? 0 : 1;
|
||||
|
||||
// With D
|
||||
config.depthSize = 24;
|
||||
config.stencilSize = 0;
|
||||
configs.add(config);
|
||||
// Buffer sizes
|
||||
config.redSize = 8;
|
||||
config.greenSize = 8;
|
||||
config.blueSize = 8;
|
||||
config.alphaSize = 8;
|
||||
config.bufferSize = config.redSize + config.greenSize + config.blueSize + config.alphaSize;
|
||||
|
||||
// With S
|
||||
config.depthSize = 0;
|
||||
config.stencilSize = 8;
|
||||
configs.add(config);
|
||||
// With DS
|
||||
config.depthSize = 24;
|
||||
config.stencilSize = 8;
|
||||
|
||||
// No DS
|
||||
config.depthSize = 0;
|
||||
config.stencilSize = 0;
|
||||
configs.add(config);
|
||||
configs.add(config);
|
||||
|
||||
// With D
|
||||
config.depthSize = 24;
|
||||
config.stencilSize = 0;
|
||||
configs.add(config);
|
||||
|
||||
// With S
|
||||
config.depthSize = 0;
|
||||
config.stencilSize = 8;
|
||||
configs.add(config);
|
||||
|
||||
// No DS
|
||||
config.depthSize = 0;
|
||||
config.stencilSize = 0;
|
||||
configs.add(config);
|
||||
}
|
||||
|
||||
return configs;
|
||||
}
|
||||
|
||||
bool DisplayMtl::isValidNativeWindow(EGLNativeWindowType window) const
|
||||
{
|
||||
NSObject *layer = (__bridge NSObject *)(window);
|
||||
return [layer isKindOfClass:[CALayer class]];
|
||||
ANGLE_MTL_OBJC_SCOPE
|
||||
{
|
||||
NSObject *layer = (__bridge NSObject *)(window);
|
||||
return [layer isKindOfClass:[CALayer class]];
|
||||
}
|
||||
}
|
||||
|
||||
std::string DisplayMtl::getRendererDescription() const
|
||||
|
@ -611,7 +628,8 @@ void DisplayMtl::initializeFeatures()
|
|||
mFeatures.allowSeparatedDepthStencilBuffers.enabled = false;
|
||||
|
||||
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
|
||||
mFeatures.hasDepthTextureFiltering.enabled = true;
|
||||
mFeatures.hasDepthTextureFiltering.enabled = true;
|
||||
mFeatures.allowMultisampleStoreAndResolve.enabled = true;
|
||||
|
||||
// Texture swizzle is only supported if macos sdk 10.15 is present
|
||||
# if defined(__MAC_10_15)
|
||||
|
@ -630,6 +648,9 @@ void DisplayMtl::initializeFeatures()
|
|||
ANGLE_FEATURE_CONDITION((&mFeatures), hasNonUniformDispatch,
|
||||
[getMetalDevice() supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily4_v1]);
|
||||
|
||||
ANGLE_FEATURE_CONDITION((&mFeatures), allowMultisampleStoreAndResolve,
|
||||
[getMetalDevice() supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]);
|
||||
|
||||
# if !TARGET_OS_SIMULATOR
|
||||
mFeatures.allowSeparatedDepthStencilBuffers.enabled = true;
|
||||
# endif
|
||||
|
@ -637,6 +658,8 @@ void DisplayMtl::initializeFeatures()
|
|||
|
||||
angle::PlatformMethods *platform = ANGLEPlatformCurrent();
|
||||
platform->overrideFeaturesMtl(platform, &mFeatures);
|
||||
|
||||
ApplyFeatureOverrides(&mFeatures, getState());
|
||||
}
|
||||
|
||||
angle::Result DisplayMtl::initializeShaderLibrary()
|
||||
|
|
|
@ -18,13 +18,17 @@
|
|||
|
||||
namespace rx
|
||||
{
|
||||
namespace mtl
|
||||
{
|
||||
class RenderCommandEncoder;
|
||||
} // namespace mtl
|
||||
class ContextMtl;
|
||||
class SurfaceMtl;
|
||||
class WindowSurfaceMtl;
|
||||
|
||||
class FramebufferMtl : public FramebufferImpl
|
||||
{
|
||||
public:
|
||||
explicit FramebufferMtl(const gl::FramebufferState &state, bool flipY);
|
||||
FramebufferMtl(const gl::FramebufferState &state, bool flipY, WindowSurfaceMtl *backbuffer);
|
||||
~FramebufferMtl() override;
|
||||
void destroy(const gl::Context *context) override;
|
||||
|
||||
|
@ -84,19 +88,26 @@ class FramebufferMtl : public FramebufferImpl
|
|||
size_t index,
|
||||
GLfloat *xy) const override;
|
||||
|
||||
RenderTargetMtl *getColorReadRenderTarget() const;
|
||||
RenderTargetMtl *getColorReadRenderTarget(const gl::Context *context) const;
|
||||
RenderTargetMtl *getDepthRenderTarget() const { return mDepthRenderTarget; }
|
||||
RenderTargetMtl *getStencilRenderTarget() const { return mStencilRenderTarget; }
|
||||
|
||||
bool flipY() const { return mFlipY; }
|
||||
|
||||
gl::Rectangle getCompleteRenderArea() const;
|
||||
int getSamples() const;
|
||||
WindowSurfaceMtl *getAttachedBackbuffer() const { return mBackbuffer; }
|
||||
|
||||
const mtl::RenderPassDesc &getRenderPassDesc(ContextMtl *context);
|
||||
bool renderPassHasStarted(ContextMtl *contextMtl) const;
|
||||
mtl::RenderCommandEncoder *ensureRenderPassStarted(const gl::Context *context);
|
||||
|
||||
// Call this to notify FramebufferMtl whenever its render pass has started.
|
||||
void onStartedDrawingToFrameBuffer(const gl::Context *context);
|
||||
void onFrameEnd(const gl::Context *context);
|
||||
|
||||
// The actual area will be adjusted based on framebuffer flipping property.
|
||||
gl::Rectangle getReadPixelArea(const gl::Rectangle &glArea);
|
||||
gl::Rectangle getCorrectFlippedReadArea(const gl::Context *context,
|
||||
const gl::Rectangle &glArea) const;
|
||||
|
||||
// NOTE: this method doesn't do the flipping of area. Caller must do it if needed before
|
||||
// callling this. See getReadPixelsArea().
|
||||
|
@ -121,9 +132,17 @@ class FramebufferMtl : public FramebufferImpl
|
|||
gl::DrawBufferMask clearColorBuffers,
|
||||
const mtl::ClearRectParams &clearOpts);
|
||||
|
||||
angle::Result prepareRenderPass(const gl::Context *context,
|
||||
gl::DrawBufferMask drawColorBuffers,
|
||||
mtl::RenderPassDesc *descOut);
|
||||
// Initialize load store options for a render pass's first start (i.e. not render pass resuming
|
||||
// from interruptions such as those caused by a conversion compute pass)
|
||||
void setLoadStoreActionOnRenderPassFirstStart(mtl::RenderPassAttachmentDesc *attachmentOut);
|
||||
|
||||
// Fill RenderPassDesc with relevant attachment's info from GL front end.
|
||||
angle::Result prepareRenderPass(const gl::Context *context, mtl::RenderPassDesc *descOut);
|
||||
|
||||
// Check if a render pass specified by the given RenderPassDesc has started or not, if not this
|
||||
// method will start the render pass and return its render encoder.
|
||||
mtl::RenderCommandEncoder *ensureRenderPassStarted(const gl::Context *context,
|
||||
const mtl::RenderPassDesc &desc);
|
||||
|
||||
void overrideClearColor(const mtl::TextureRef &texture,
|
||||
MTLClearColor clearColor,
|
||||
|
@ -143,7 +162,13 @@ class FramebufferMtl : public FramebufferImpl
|
|||
RenderTargetMtl *mDepthRenderTarget = nullptr;
|
||||
RenderTargetMtl *mStencilRenderTarget = nullptr;
|
||||
mtl::RenderPassDesc mRenderPassDesc;
|
||||
const bool mFlipY = false;
|
||||
|
||||
// Flag indicating the render pass start is a clean start or a resume from interruption such
|
||||
// as by a compute pass.
|
||||
bool mRenderPassCleanStart = false;
|
||||
|
||||
WindowSurfaceMtl *mBackbuffer = nullptr;
|
||||
const bool mFlipY = false;
|
||||
};
|
||||
} // namespace rx
|
||||
|
||||
|
|
|
@ -23,8 +23,10 @@
|
|||
namespace rx
|
||||
{
|
||||
// FramebufferMtl implementation
|
||||
FramebufferMtl::FramebufferMtl(const gl::FramebufferState &state, bool flipY)
|
||||
: FramebufferImpl(state), mFlipY(flipY)
|
||||
FramebufferMtl::FramebufferMtl(const gl::FramebufferState &state,
|
||||
bool flipY,
|
||||
WindowSurfaceMtl *backbuffer)
|
||||
: FramebufferImpl(state), mBackbuffer(backbuffer), mFlipY(flipY)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
@ -164,7 +166,7 @@ angle::Result FramebufferMtl::readPixels(const gl::Context *context,
|
|||
// nothing to read
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
gl::Rectangle flippedArea = getReadPixelArea(clippedArea);
|
||||
gl::Rectangle flippedArea = getCorrectFlippedReadArea(context, clippedArea);
|
||||
|
||||
ContextMtl *contextMtl = mtl::GetImpl(context);
|
||||
|
||||
|
@ -190,7 +192,7 @@ angle::Result FramebufferMtl::readPixels(const gl::Context *context,
|
|||
params.reverseRowOrder = !params.reverseRowOrder;
|
||||
}
|
||||
|
||||
ANGLE_TRY(readPixelsImpl(context, flippedArea, params, getColorReadRenderTarget(),
|
||||
ANGLE_TRY(readPixelsImpl(context, flippedArea, params, getColorReadRenderTarget(context),
|
||||
static_cast<uint8_t *>(pixels) + outputSkipBytes));
|
||||
|
||||
return angle::Result::Continue;
|
||||
|
@ -273,7 +275,7 @@ angle::Result FramebufferMtl::syncState(const gl::Context *context,
|
|||
|
||||
auto oldRenderPassDesc = mRenderPassDesc;
|
||||
|
||||
ANGLE_TRY(prepareRenderPass(context, mState.getEnabledDrawBuffers(), &mRenderPassDesc));
|
||||
ANGLE_TRY(prepareRenderPass(context, &mRenderPassDesc));
|
||||
|
||||
if (!oldRenderPassDesc.equalIgnoreLoadStoreOptions(mRenderPassDesc))
|
||||
{
|
||||
|
@ -296,62 +298,166 @@ angle::Result FramebufferMtl::getSamplePosition(const gl::Context *context,
|
|||
return angle::Result::Stop;
|
||||
}
|
||||
|
||||
RenderTargetMtl *FramebufferMtl::getColorReadRenderTarget() const
|
||||
RenderTargetMtl *FramebufferMtl::getColorReadRenderTarget(const gl::Context *context) const
|
||||
{
|
||||
if (mState.getReadIndex() >= mColorRenderTargets.size())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mBackbuffer)
|
||||
{
|
||||
if (IsError(mBackbuffer->ensureCurrentDrawableObtained(context)))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return mColorRenderTargets[mState.getReadIndex()];
|
||||
}
|
||||
|
||||
int FramebufferMtl::getSamples() const
|
||||
{
|
||||
return mRenderPassDesc.sampleCount;
|
||||
}
|
||||
|
||||
gl::Rectangle FramebufferMtl::getCompleteRenderArea() const
|
||||
{
|
||||
return gl::Rectangle(0, 0, mState.getDimensions().width, mState.getDimensions().height);
|
||||
}
|
||||
|
||||
const mtl::RenderPassDesc &FramebufferMtl::getRenderPassDesc(ContextMtl *context)
|
||||
bool FramebufferMtl::renderPassHasStarted(ContextMtl *contextMtl) const
|
||||
{
|
||||
return mRenderPassDesc;
|
||||
return contextMtl->hasStartedRenderPass(mRenderPassDesc);
|
||||
}
|
||||
|
||||
mtl::RenderCommandEncoder *FramebufferMtl::ensureRenderPassStarted(const gl::Context *context)
|
||||
{
|
||||
return ensureRenderPassStarted(context, mRenderPassDesc);
|
||||
}
|
||||
|
||||
mtl::RenderCommandEncoder *FramebufferMtl::ensureRenderPassStarted(const gl::Context *context,
|
||||
const mtl::RenderPassDesc &desc)
|
||||
{
|
||||
ContextMtl *contextMtl = mtl::GetImpl(context);
|
||||
|
||||
if (renderPassHasStarted(contextMtl))
|
||||
{
|
||||
return contextMtl->getRenderCommandEncoder();
|
||||
}
|
||||
|
||||
if (mBackbuffer)
|
||||
{
|
||||
// Backbuffer might obtain new drawable, which means it might change the
|
||||
// the native texture used as the target of the render pass.
|
||||
// We need to call this before creating render encoder.
|
||||
if (IsError(mBackbuffer->ensureCurrentDrawableObtained(context)))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Only support ensureRenderPassStarted() with different load & store options.
|
||||
// The texture, level, slice must be the same.
|
||||
ASSERT(desc.equalIgnoreLoadStoreOptions(mRenderPassDesc));
|
||||
|
||||
mtl::RenderCommandEncoder *encoder = contextMtl->getRenderCommandEncoder(desc);
|
||||
|
||||
if (mRenderPassCleanStart)
|
||||
{
|
||||
// After a clean start we should reset the loadOp to MTLLoadActionLoad in case this render
|
||||
// pass could be interrupted by a conversion compute shader pass then being resumed later.
|
||||
mRenderPassCleanStart = false;
|
||||
for (mtl::RenderPassColorAttachmentDesc &colorAttachment : mRenderPassDesc.colorAttachments)
|
||||
{
|
||||
colorAttachment.loadAction = MTLLoadActionLoad;
|
||||
}
|
||||
mRenderPassDesc.depthAttachment.loadAction = MTLLoadActionLoad;
|
||||
mRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
|
||||
}
|
||||
|
||||
return encoder;
|
||||
}
|
||||
|
||||
void FramebufferMtl::setLoadStoreActionOnRenderPassFirstStart(
|
||||
mtl::RenderPassAttachmentDesc *attachmentOut)
|
||||
{
|
||||
ASSERT(mRenderPassCleanStart);
|
||||
|
||||
mtl::RenderPassAttachmentDesc &attachment = *attachmentOut;
|
||||
|
||||
if (attachment.storeAction == MTLStoreActionDontCare ||
|
||||
attachment.storeAction == MTLStoreActionMultisampleResolve)
|
||||
{
|
||||
// If we previously discarded attachment's content, then don't need to load it.
|
||||
attachment.loadAction = MTLLoadActionDontCare;
|
||||
}
|
||||
else
|
||||
{
|
||||
attachment.loadAction = MTLLoadActionLoad;
|
||||
}
|
||||
|
||||
if (attachment.hasImplicitMSTexture())
|
||||
{
|
||||
if (mBackbuffer)
|
||||
{
|
||||
// Default action for default framebuffer is resolve and keep MS texture's content.
|
||||
// We only discard MS texture's content at the end of the frame. See onFrameEnd().
|
||||
attachment.storeAction = MTLStoreActionStoreAndMultisampleResolve;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default action is resolve but don't keep MS texture's content.
|
||||
attachment.storeAction = MTLStoreActionMultisampleResolve;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
attachment.storeAction = MTLStoreActionStore; // Default action is store
|
||||
}
|
||||
}
|
||||
|
||||
void FramebufferMtl::onStartedDrawingToFrameBuffer(const gl::Context *context)
|
||||
{
|
||||
mRenderPassCleanStart = true;
|
||||
|
||||
// Compute loadOp based on previous storeOp and reset storeOp flags:
|
||||
for (mtl::RenderPassColorAttachmentDesc &colorAttachment : mRenderPassDesc.colorAttachments)
|
||||
{
|
||||
if (colorAttachment.storeAction == MTLStoreActionDontCare)
|
||||
{
|
||||
// If we previously discarded attachment's content, then don't need to load it.
|
||||
colorAttachment.loadAction = MTLLoadActionDontCare;
|
||||
}
|
||||
else
|
||||
{
|
||||
colorAttachment.loadAction = MTLLoadActionLoad;
|
||||
}
|
||||
colorAttachment.storeAction = MTLStoreActionStore; // Default action is store
|
||||
setLoadStoreActionOnRenderPassFirstStart(&colorAttachment);
|
||||
}
|
||||
// Depth load/store
|
||||
if (mRenderPassDesc.depthAttachment.storeAction == MTLStoreActionDontCare)
|
||||
{
|
||||
mRenderPassDesc.depthAttachment.loadAction = MTLLoadActionDontCare;
|
||||
}
|
||||
else
|
||||
{
|
||||
mRenderPassDesc.depthAttachment.loadAction = MTLLoadActionLoad;
|
||||
}
|
||||
mRenderPassDesc.depthAttachment.storeAction = MTLStoreActionStore;
|
||||
setLoadStoreActionOnRenderPassFirstStart(&mRenderPassDesc.depthAttachment);
|
||||
|
||||
// Stencil load/store
|
||||
if (mRenderPassDesc.stencilAttachment.storeAction == MTLStoreActionDontCare)
|
||||
setLoadStoreActionOnRenderPassFirstStart(&mRenderPassDesc.stencilAttachment);
|
||||
}
|
||||
|
||||
void FramebufferMtl::onFrameEnd(const gl::Context *context)
|
||||
{
|
||||
if (!mBackbuffer)
|
||||
{
|
||||
mRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionDontCare;
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
ContextMtl *contextMtl = mtl::GetImpl(context);
|
||||
// Always discard default FBO's depth stencil & multisample buffers at the end of the frame:
|
||||
if (this->renderPassHasStarted(contextMtl))
|
||||
{
|
||||
mRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
|
||||
mtl::RenderCommandEncoder *encoder = contextMtl->getRenderCommandEncoder();
|
||||
|
||||
constexpr GLenum dsAttachments[] = {GL_DEPTH, GL_STENCIL};
|
||||
(void)invalidateImpl(contextMtl, 2, dsAttachments);
|
||||
if (mBackbuffer->getSamples() > 1)
|
||||
{
|
||||
encoder->setColorStoreAction(MTLStoreActionMultisampleResolve, 0);
|
||||
}
|
||||
|
||||
contextMtl->endEncoding(false);
|
||||
|
||||
// Reset discard flag.
|
||||
onStartedDrawingToFrameBuffer(context);
|
||||
}
|
||||
mRenderPassDesc.stencilAttachment.storeAction = MTLStoreActionStore;
|
||||
}
|
||||
|
||||
angle::Result FramebufferMtl::updateColorRenderTarget(const gl::Context *context,
|
||||
|
@ -394,36 +500,53 @@ angle::Result FramebufferMtl::updateCachedRenderTarget(const gl::Context *contex
|
|||
}
|
||||
|
||||
angle::Result FramebufferMtl::prepareRenderPass(const gl::Context *context,
|
||||
gl::DrawBufferMask drawColorBuffers,
|
||||
mtl::RenderPassDesc *pDescOut)
|
||||
{
|
||||
auto &desc = *pDescOut;
|
||||
gl::DrawBufferMask enabledBuffer = mState.getEnabledDrawBuffers();
|
||||
|
||||
desc.numColorAttachments = static_cast<uint32_t>(drawColorBuffers.count());
|
||||
size_t attachmentIdx = 0;
|
||||
mtl::RenderPassDesc &desc = *pDescOut;
|
||||
|
||||
for (size_t colorIndexGL : drawColorBuffers)
|
||||
uint32_t maxColorAttachments = static_cast<uint32_t>(mState.getColorAttachments().size());
|
||||
desc.numColorAttachments = 0;
|
||||
desc.sampleCount = 1;
|
||||
for (uint32_t colorIndexGL = 0; colorIndexGL < maxColorAttachments; ++colorIndexGL)
|
||||
{
|
||||
if (colorIndexGL >= mtl::kMaxRenderTargets)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const RenderTargetMtl *colorRenderTarget = mColorRenderTargets[colorIndexGL];
|
||||
ASSERT(colorRenderTarget);
|
||||
ASSERT(colorIndexGL < mtl::kMaxRenderTargets);
|
||||
|
||||
mtl::RenderPassColorAttachmentDesc &colorAttachment =
|
||||
desc.colorAttachments[attachmentIdx++];
|
||||
colorRenderTarget->toRenderPassAttachmentDesc(&colorAttachment);
|
||||
mtl::RenderPassColorAttachmentDesc &colorAttachment = desc.colorAttachments[colorIndexGL];
|
||||
const RenderTargetMtl *colorRenderTarget = mColorRenderTargets[colorIndexGL];
|
||||
|
||||
if (colorRenderTarget && enabledBuffer.test(colorIndexGL))
|
||||
{
|
||||
colorRenderTarget->toRenderPassAttachmentDesc(&colorAttachment);
|
||||
|
||||
desc.numColorAttachments = std::max(desc.numColorAttachments, colorIndexGL + 1);
|
||||
desc.sampleCount = std::max(desc.sampleCount, colorRenderTarget->getRenderSamples());
|
||||
}
|
||||
else
|
||||
{
|
||||
colorAttachment.reset();
|
||||
}
|
||||
}
|
||||
|
||||
if (mDepthRenderTarget)
|
||||
{
|
||||
mDepthRenderTarget->toRenderPassAttachmentDesc(&desc.depthAttachment);
|
||||
desc.sampleCount = std::max(desc.sampleCount, mDepthRenderTarget->getRenderSamples());
|
||||
}
|
||||
else
|
||||
{
|
||||
desc.depthAttachment.reset();
|
||||
}
|
||||
|
||||
if (mStencilRenderTarget)
|
||||
{
|
||||
mStencilRenderTarget->toRenderPassAttachmentDesc(&desc.stencilAttachment);
|
||||
desc.sampleCount = std::max(desc.sampleCount, mStencilRenderTarget->getRenderSamples());
|
||||
}
|
||||
else
|
||||
{
|
||||
desc.stencilAttachment.reset();
|
||||
}
|
||||
|
||||
return angle::Result::Continue;
|
||||
|
@ -519,7 +642,7 @@ angle::Result FramebufferMtl::clearWithLoadOp(const gl::Context *context,
|
|||
}
|
||||
|
||||
// Start new render encoder with loadOp=Clear
|
||||
contextMtl->getRenderCommandEncoder(tempDesc);
|
||||
ensureRenderPassStarted(context, tempDesc);
|
||||
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
@ -532,7 +655,7 @@ angle::Result FramebufferMtl::clearWithDraw(const gl::Context *context,
|
|||
DisplayMtl *display = contextMtl->getDisplay();
|
||||
|
||||
// Start new render encoder if not already.
|
||||
mtl::RenderCommandEncoder *encoder = contextMtl->getRenderCommandEncoder(mRenderPassDesc);
|
||||
mtl::RenderCommandEncoder *encoder = ensureRenderPassStarted(context, mRenderPassDesc);
|
||||
|
||||
return display->getUtils().clearWithDraw(context, encoder, clearOpts);
|
||||
}
|
||||
|
@ -659,9 +782,10 @@ angle::Result FramebufferMtl::invalidateImpl(ContextMtl *contextMtl,
|
|||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
gl::Rectangle FramebufferMtl::getReadPixelArea(const gl::Rectangle &glArea)
|
||||
gl::Rectangle FramebufferMtl::getCorrectFlippedReadArea(const gl::Context *context,
|
||||
const gl::Rectangle &glArea) const
|
||||
{
|
||||
RenderTargetMtl *colorReadRT = getColorReadRenderTarget();
|
||||
RenderTargetMtl *colorReadRT = getColorReadRenderTarget(context);
|
||||
ASSERT(colorReadRT);
|
||||
gl::Rectangle flippedArea = glArea;
|
||||
if (mFlipY)
|
||||
|
@ -690,11 +814,20 @@ angle::Result FramebufferMtl::readPixelsImpl(const gl::Context *context,
|
|||
{
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
const mtl::TextureRef &texture = renderTarget->getTexture();
|
||||
|
||||
if (!texture)
|
||||
mtl::Texture *texture;
|
||||
if (mBackbuffer)
|
||||
{
|
||||
return angle::Result::Continue;
|
||||
// Backbuffer might have MSAA texture as render target, needs to obtain the
|
||||
// resolved texture to be able to read pixels.
|
||||
ANGLE_TRY(mBackbuffer->ensureColorTextureReadyForReadPixels(context));
|
||||
texture = mBackbuffer->getColorTexture().get();
|
||||
}
|
||||
else
|
||||
{
|
||||
texture = renderTarget->getTexture().get();
|
||||
// For non-default framebuffer, MSAA read pixels is disallowed.
|
||||
ANGLE_MTL_CHECK(contextMtl, texture->samples() == 1, GL_INVALID_OPERATION);
|
||||
}
|
||||
|
||||
const mtl::Format &readFormat = *renderTarget->getFormat();
|
||||
|
|
|
@ -27,7 +27,18 @@ namespace rx
|
|||
{
|
||||
class ContextMtl;
|
||||
|
||||
class ProgramMtl : public ProgramImpl
|
||||
// Represents a specialized shader variant. For example, a shader variant with fragment coverage
|
||||
// mask enabled and a shader variant without.
|
||||
struct ProgramShaderVariantMtl
|
||||
{
|
||||
void reset(ContextMtl *contextMtl);
|
||||
|
||||
mtl::AutoObjCPtr<id<MTLFunction>> metalShader;
|
||||
// NOTE(hqle): might need additional info such as uniform buffer encoder, fragment coverage mask
|
||||
// enabled or not, etc.
|
||||
};
|
||||
|
||||
class ProgramMtl : public ProgramImpl, public mtl::RenderPipelineCacheSpecializeShaderFactory
|
||||
{
|
||||
public:
|
||||
ProgramMtl(const gl::ProgramState &state);
|
||||
|
@ -100,6 +111,14 @@ class ProgramMtl : public ProgramImpl
|
|||
void getUniformiv(const gl::Context *context, GLint location, GLint *params) const override;
|
||||
void getUniformuiv(const gl::Context *context, GLint location, GLuint *params) const override;
|
||||
|
||||
// Override mtl::RenderPipelineCacheSpecializeShaderFactory
|
||||
angle::Result getSpecializedShader(mtl::Context *context,
|
||||
gl::ShaderType shaderType,
|
||||
const mtl::RenderPipelineDesc &renderPipelineDesc,
|
||||
id<MTLFunction> *shaderOut) override;
|
||||
bool hasSpecializedShader(gl::ShaderType shaderType,
|
||||
const mtl::RenderPipelineDesc &renderPipelineDesc) override;
|
||||
|
||||
// Calls this before drawing, changedPipelineDesc is passed when vertex attributes desc and/or
|
||||
// shader program changed.
|
||||
angle::Result setupDraw(const gl::Context *glContext,
|
||||
|
@ -132,10 +151,10 @@ class ProgramMtl : public ProgramImpl
|
|||
const gl::ProgramLinkedResources &resources,
|
||||
gl::InfoLog &infoLog);
|
||||
|
||||
angle::Result createMslShader(const gl::Context *glContext,
|
||||
gl::ShaderType shaderType,
|
||||
gl::InfoLog &infoLog,
|
||||
const std::string &translatedSource);
|
||||
angle::Result createMslShaderLib(const gl::Context *glContext,
|
||||
gl::ShaderType shaderType,
|
||||
gl::InfoLog &infoLog,
|
||||
const std::string &translatedSource);
|
||||
|
||||
// State for the default uniform blocks.
|
||||
struct DefaultUniformBlock final : private angle::NonCopyable
|
||||
|
@ -158,6 +177,13 @@ class ProgramMtl : public ProgramImpl
|
|||
gl::ShaderMap<std::string> mTranslatedMslShader;
|
||||
|
||||
gl::ShaderMap<mtl::TranslatedShaderInfo> mMslShaderTranslateInfo;
|
||||
gl::ShaderMap<mtl::AutoObjCPtr<id<MTLLibrary>>> mMslShaderLibrary;
|
||||
|
||||
// Shader variants:
|
||||
// - Vertex shader: One variant for now.
|
||||
std::array<ProgramShaderVariantMtl, 1> mVertexShaderVariants;
|
||||
// - Fragment shader: One with sample coverage mask enabled, one with it disabled.
|
||||
std::array<ProgramShaderVariantMtl, 2> mFragmentShaderVariants;
|
||||
|
||||
mtl::RenderPipelineCache mMetalRenderPipelineCache;
|
||||
};
|
||||
|
|
|
@ -30,6 +30,7 @@ namespace
|
|||
{
|
||||
|
||||
#define SHADER_ENTRY_NAME @"main0"
|
||||
constexpr char kSpirvCrossSpecConstSuffix[] = "_tmp";
|
||||
|
||||
void InitDefaultUniformBlock(const std::vector<sh::Uniform> &uniforms,
|
||||
gl::Shader *shader,
|
||||
|
@ -124,14 +125,59 @@ class Std140BlockLayoutEncoderFactory : public gl::CustomBlockLayoutEncoderFacto
|
|||
sh::BlockLayoutEncoder *makeEncoder() override { return new sh::Std140BlockEncoder(); }
|
||||
};
|
||||
|
||||
angle::Result CreateMslShader(mtl::Context *context,
|
||||
id<MTLLibrary> shaderLib,
|
||||
MTLFunctionConstantValues *funcConstants,
|
||||
mtl::AutoObjCPtr<id<MTLFunction>> *shaderOut)
|
||||
{
|
||||
NSError *nsErr = nil;
|
||||
|
||||
id<MTLFunction> mtlShader;
|
||||
|
||||
if (funcConstants)
|
||||
{
|
||||
mtlShader = [shaderLib newFunctionWithName:SHADER_ENTRY_NAME
|
||||
constantValues:funcConstants
|
||||
error:&nsErr];
|
||||
}
|
||||
else
|
||||
{
|
||||
mtlShader = [shaderLib newFunctionWithName:SHADER_ENTRY_NAME];
|
||||
}
|
||||
|
||||
[mtlShader ANGLE_MTL_AUTORELEASE];
|
||||
if (nsErr && !mtlShader)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << "Internal error compiling Metal shader:\n"
|
||||
<< nsErr.localizedDescription.UTF8String << "\n";
|
||||
|
||||
ERR() << ss.str();
|
||||
|
||||
ANGLE_MTL_CHECK(context, false, GL_INVALID_OPERATION);
|
||||
}
|
||||
|
||||
shaderOut->retainAssign(mtlShader);
|
||||
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// ProgramShaderVariantMtl implementation
|
||||
void ProgramShaderVariantMtl::reset(ContextMtl *contextMtl)
|
||||
{
|
||||
metalShader = nil;
|
||||
}
|
||||
|
||||
// ProgramMtl implementation
|
||||
ProgramMtl::DefaultUniformBlock::DefaultUniformBlock() {}
|
||||
|
||||
ProgramMtl::DefaultUniformBlock::~DefaultUniformBlock() = default;
|
||||
|
||||
ProgramMtl::ProgramMtl(const gl::ProgramState &state) : ProgramImpl(state) {}
|
||||
ProgramMtl::ProgramMtl(const gl::ProgramState &state)
|
||||
: ProgramImpl(state), mMetalRenderPipelineCache(this)
|
||||
{}
|
||||
|
||||
ProgramMtl::~ProgramMtl() {}
|
||||
|
||||
|
@ -149,8 +195,10 @@ void ProgramMtl::reset(ContextMtl *context)
|
|||
block.uniformLayout.clear();
|
||||
}
|
||||
|
||||
for (gl::ShaderType shaderType : gl::AllGLES2ShaderTypes())
|
||||
for (gl::ShaderType shaderType : gl::AllShaderTypes())
|
||||
{
|
||||
mMslShaderLibrary[shaderType] = nil;
|
||||
|
||||
for (mtl::SamplerBinding &binding :
|
||||
mMslShaderTranslateInfo[shaderType].actualSamplerBindings)
|
||||
{
|
||||
|
@ -158,6 +206,15 @@ void ProgramMtl::reset(ContextMtl *context)
|
|||
}
|
||||
}
|
||||
|
||||
for (ProgramShaderVariantMtl &var : mVertexShaderVariants)
|
||||
{
|
||||
var.reset(context);
|
||||
}
|
||||
for (ProgramShaderVariantMtl &var : mFragmentShaderVariants)
|
||||
{
|
||||
var.reset(context);
|
||||
}
|
||||
|
||||
mMetalRenderPipelineCache.clear();
|
||||
}
|
||||
|
||||
|
@ -228,7 +285,7 @@ angle::Result ProgramMtl::linkImpl(const gl::Context *glContext,
|
|||
{
|
||||
// Create actual Metal shader
|
||||
ANGLE_TRY(
|
||||
createMslShader(glContext, shaderType, infoLog, mTranslatedMslShader[shaderType]));
|
||||
createMslShaderLib(glContext, shaderType, infoLog, mTranslatedMslShader[shaderType]));
|
||||
}
|
||||
|
||||
return angle::Result::Continue;
|
||||
|
@ -325,10 +382,76 @@ angle::Result ProgramMtl::initDefaultUniformBlocks(const gl::Context *glContext)
|
|||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
angle::Result ProgramMtl::createMslShader(const gl::Context *glContext,
|
||||
gl::ShaderType shaderType,
|
||||
gl::InfoLog &infoLog,
|
||||
const std::string &translatedMsl)
|
||||
angle::Result ProgramMtl::getSpecializedShader(mtl::Context *context,
|
||||
gl::ShaderType shaderType,
|
||||
const mtl::RenderPipelineDesc &renderPipelineDesc,
|
||||
id<MTLFunction> *shaderOut)
|
||||
{
|
||||
static_assert(YES == 1, "YES should have value of 1");
|
||||
|
||||
mtl::AutoObjCPtr<id<MTLLibrary>> mtlShaderLib = mMslShaderLibrary[shaderType];
|
||||
|
||||
if (shaderType == gl::ShaderType::Vertex)
|
||||
{
|
||||
// NOTE(hqle): Only one vertex shader variant for now. In future, there should be a variant
|
||||
// with rasterization discard enabled.
|
||||
ProgramShaderVariantMtl &shaderVariant = mVertexShaderVariants[0];
|
||||
if (shaderVariant.metalShader)
|
||||
{
|
||||
// Already created.
|
||||
*shaderOut = shaderVariant.metalShader;
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
ANGLE_MTL_OBJC_SCOPE
|
||||
{
|
||||
ANGLE_TRY(CreateMslShader(context, mtlShaderLib, nil, &shaderVariant.metalShader));
|
||||
}
|
||||
|
||||
*shaderOut = shaderVariant.metalShader;
|
||||
}
|
||||
else if (shaderType == gl::ShaderType::Fragment)
|
||||
{
|
||||
// For fragment shader, we need to create 2 variants, one with sample coverage mask
|
||||
// disabled, one with the mask enabled.
|
||||
BOOL emulateCoverageMask = renderPipelineDesc.emulateCoverageMask;
|
||||
ProgramShaderVariantMtl &shaderVariant = mFragmentShaderVariants[emulateCoverageMask];
|
||||
if (shaderVariant.metalShader)
|
||||
{
|
||||
// Already created.
|
||||
*shaderOut = shaderVariant.metalShader;
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
ANGLE_MTL_OBJC_SCOPE
|
||||
{
|
||||
NSString *coverageMaskEnabledStr =
|
||||
[NSString stringWithFormat:@"%s%s", sh::mtl::kCoverageMaskEnabledConstName,
|
||||
kSpirvCrossSpecConstSuffix];
|
||||
|
||||
auto funcConstants = [[[MTLFunctionConstantValues alloc] init] ANGLE_MTL_AUTORELEASE];
|
||||
[funcConstants setConstantValue:&emulateCoverageMask
|
||||
type:MTLDataTypeBool
|
||||
withName:coverageMaskEnabledStr];
|
||||
|
||||
ANGLE_TRY(
|
||||
CreateMslShader(context, mtlShaderLib, funcConstants, &shaderVariant.metalShader));
|
||||
}
|
||||
|
||||
*shaderOut = shaderVariant.metalShader;
|
||||
} // gl::ShaderType::Fragment
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
bool ProgramMtl::hasSpecializedShader(gl::ShaderType shaderType,
|
||||
const mtl::RenderPipelineDesc &renderPipelineDesc)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
angle::Result ProgramMtl::createMslShaderLib(const gl::Context *glContext,
|
||||
gl::ShaderType shaderType,
|
||||
gl::InfoLog &infoLog,
|
||||
const std::string &translatedMsl)
|
||||
{
|
||||
ANGLE_MTL_OBJC_SCOPE
|
||||
{
|
||||
|
@ -338,9 +461,8 @@ angle::Result ProgramMtl::createMslShader(const gl::Context *glContext,
|
|||
|
||||
// Convert to actual binary shader
|
||||
mtl::AutoObjCPtr<NSError *> err = nil;
|
||||
mtl::AutoObjCPtr<id<MTLLibrary>> mtlShaderLib =
|
||||
mtl::CreateShaderLibrary(mtlDevice, translatedMsl, &err);
|
||||
if (err && !mtlShaderLib)
|
||||
mMslShaderLibrary[shaderType] = mtl::CreateShaderLibrary(mtlDevice, translatedMsl, &err);
|
||||
if (err && !mMslShaderLibrary[shaderType])
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << "Internal error compiling Metal shader:\n"
|
||||
|
@ -353,18 +475,6 @@ angle::Result ProgramMtl::createMslShader(const gl::Context *glContext,
|
|||
ANGLE_MTL_CHECK(contextMtl, false, GL_INVALID_OPERATION);
|
||||
}
|
||||
|
||||
auto mtlShader = [mtlShaderLib.get() newFunctionWithName:SHADER_ENTRY_NAME];
|
||||
[mtlShader ANGLE_MTL_AUTORELEASE];
|
||||
ASSERT(mtlShader);
|
||||
if (shaderType == gl::ShaderType::Vertex)
|
||||
{
|
||||
mMetalRenderPipelineCache.setVertexShader(contextMtl, mtlShader);
|
||||
}
|
||||
else if (shaderType == gl::ShaderType::Fragment)
|
||||
{
|
||||
mMetalRenderPipelineCache.setFragmentShader(contextMtl, mtlShader);
|
||||
}
|
||||
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,21 +32,33 @@ class RenderTargetMtl final : public FramebufferAttachmentRenderTarget
|
|||
// Used in std::vector initialization.
|
||||
RenderTargetMtl(RenderTargetMtl &&other);
|
||||
|
||||
void set(const mtl::TextureRef &texture, size_t level, size_t layer, const mtl::Format &format);
|
||||
void set(const mtl::TextureRef &texture);
|
||||
void set(const mtl::TextureRef &texture,
|
||||
uint32_t level,
|
||||
uint32_t layer,
|
||||
const mtl::Format &format);
|
||||
void set(const mtl::TextureRef &texture,
|
||||
const mtl::TextureRef &implicitMSTexture,
|
||||
uint32_t level,
|
||||
uint32_t layer,
|
||||
const mtl::Format &format);
|
||||
void setTexture(const mtl::TextureRef &texture);
|
||||
void setImplicitMSTexture(const mtl::TextureRef &implicitMSTexture);
|
||||
void reset();
|
||||
|
||||
const mtl::TextureRef &getTexture() const { return mTexture; }
|
||||
size_t getLevelIndex() const { return mLevelIndex; }
|
||||
size_t getLayerIndex() const { return mLayerIndex; }
|
||||
mtl::TextureRef getTexture() const { return mTexture; }
|
||||
mtl::TextureRef getImplicitMSTexture() const { return mImplicitMSTexture; }
|
||||
uint32_t getLevelIndex() const { return mLevelIndex; }
|
||||
uint32_t getLayerIndex() const { return mLayerIndex; }
|
||||
uint32_t getRenderSamples() const;
|
||||
const mtl::Format *getFormat() const { return mFormat; }
|
||||
|
||||
void toRenderPassAttachmentDesc(mtl::RenderPassAttachmentDesc *rpaDescOut) const;
|
||||
|
||||
private:
|
||||
mtl::TextureRef mTexture;
|
||||
size_t mLevelIndex = 0;
|
||||
size_t mLayerIndex = 0;
|
||||
mtl::TextureRef mImplicitMSTexture;
|
||||
uint32_t mLevelIndex = 0;
|
||||
uint32_t mLayerIndex = 0;
|
||||
const mtl::Format *mFormat = nullptr;
|
||||
};
|
||||
} // namespace rx
|
||||
|
|
|
@ -20,38 +20,61 @@ RenderTargetMtl::~RenderTargetMtl()
|
|||
|
||||
RenderTargetMtl::RenderTargetMtl(RenderTargetMtl &&other)
|
||||
: mTexture(std::move(other.mTexture)),
|
||||
mImplicitMSTexture(std::move(other.mImplicitMSTexture)),
|
||||
mLevelIndex(other.mLevelIndex),
|
||||
mLayerIndex(other.mLayerIndex)
|
||||
{}
|
||||
|
||||
void RenderTargetMtl::set(const mtl::TextureRef &texture,
|
||||
size_t level,
|
||||
size_t layer,
|
||||
uint32_t level,
|
||||
uint32_t layer,
|
||||
const mtl::Format &format)
|
||||
{
|
||||
mTexture = texture;
|
||||
mLevelIndex = level;
|
||||
mLayerIndex = layer;
|
||||
mFormat = &format;
|
||||
set(texture, nullptr, level, layer, format);
|
||||
}
|
||||
|
||||
void RenderTargetMtl::set(const mtl::TextureRef &texture)
|
||||
void RenderTargetMtl::set(const mtl::TextureRef &texture,
|
||||
const mtl::TextureRef &implicitMSTexture,
|
||||
uint32_t level,
|
||||
uint32_t layer,
|
||||
const mtl::Format &format)
|
||||
{
|
||||
mTexture = texture;
|
||||
mImplicitMSTexture = implicitMSTexture;
|
||||
mLevelIndex = level;
|
||||
mLayerIndex = layer;
|
||||
mFormat = &format;
|
||||
}
|
||||
|
||||
void RenderTargetMtl::setTexture(const mtl::TextureRef &texture)
|
||||
{
|
||||
mTexture = texture;
|
||||
}
|
||||
|
||||
void RenderTargetMtl::setImplicitMSTexture(const mtl::TextureRef &implicitMSTexture)
|
||||
{
|
||||
mImplicitMSTexture = implicitMSTexture;
|
||||
}
|
||||
|
||||
void RenderTargetMtl::reset()
|
||||
{
|
||||
mTexture.reset();
|
||||
mImplicitMSTexture.reset();
|
||||
mLevelIndex = 0;
|
||||
mLayerIndex = 0;
|
||||
mFormat = nullptr;
|
||||
}
|
||||
|
||||
uint32_t RenderTargetMtl::getRenderSamples() const
|
||||
{
|
||||
return mImplicitMSTexture ? mImplicitMSTexture->samples()
|
||||
: (mTexture ? mTexture->samples() : 1);
|
||||
}
|
||||
void RenderTargetMtl::toRenderPassAttachmentDesc(mtl::RenderPassAttachmentDesc *rpaDescOut) const
|
||||
{
|
||||
rpaDescOut->texture = mTexture;
|
||||
rpaDescOut->level = static_cast<uint32_t>(mLevelIndex);
|
||||
rpaDescOut->slice = static_cast<uint32_t>(mLayerIndex);
|
||||
rpaDescOut->texture = mTexture;
|
||||
rpaDescOut->implicitMSTexture = mImplicitMSTexture;
|
||||
rpaDescOut->level = mLevelIndex;
|
||||
rpaDescOut->sliceOrDepth = mLayerIndex;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,6 +64,10 @@ class SurfaceMtl : public SurfaceImpl
|
|||
EGLint isPostSubBufferSupported() const override;
|
||||
EGLint getSwapBehavior() const override;
|
||||
|
||||
const mtl::TextureRef &getColorTexture() { return mColorTexture; }
|
||||
const mtl::Format &getColorFormat() const { return mColorFormat; }
|
||||
int getSamples() const { return mSamples; }
|
||||
|
||||
angle::Result getAttachmentRenderTarget(const gl::Context *context,
|
||||
GLenum binding,
|
||||
const gl::ImageIndex &imageIndex,
|
||||
|
@ -71,20 +75,29 @@ class SurfaceMtl : public SurfaceImpl
|
|||
FramebufferAttachmentRenderTarget **rtOut) override;
|
||||
|
||||
protected:
|
||||
// Ensure companion (depth, stencil) textures' size is correct w.r.t color texture.
|
||||
angle::Result ensureDepthStencilSizeCorrect(const gl::Context *context,
|
||||
gl::Framebuffer::DirtyBits *fboDirtyBits);
|
||||
// Ensure companion (MS, depth, stencil) textures' size is correct w.r.t color texture.
|
||||
angle::Result ensureCompanionTexturesSizeCorrect(const gl::Context *context,
|
||||
const gl::Extents &size);
|
||||
angle::Result resolveColorTextureIfNeeded(const gl::Context *context);
|
||||
|
||||
// Normal textures
|
||||
mtl::TextureRef mColorTexture;
|
||||
mtl::TextureRef mDepthTexture;
|
||||
mtl::TextureRef mStencilTexture;
|
||||
|
||||
// Implicit multisample texture
|
||||
mtl::TextureRef mMSColorTexture;
|
||||
|
||||
bool mUsePackedDepthStencil = false;
|
||||
// Auto resolve MS texture at the end of render pass or requires a separate blitting pass?
|
||||
bool mAutoResolveMSColorTexture = false;
|
||||
|
||||
mtl::Format mColorFormat;
|
||||
mtl::Format mDepthFormat;
|
||||
mtl::Format mStencilFormat;
|
||||
|
||||
int mSamples = 0;
|
||||
|
||||
RenderTargetMtl mColorRenderTarget;
|
||||
RenderTargetMtl mDepthRenderTarget;
|
||||
RenderTargetMtl mStencilRenderTarget;
|
||||
|
@ -105,9 +118,9 @@ class WindowSurfaceMtl : public SurfaceMtl
|
|||
FramebufferImpl *createDefaultFramebuffer(const gl::Context *context,
|
||||
const gl::FramebufferState &state) override;
|
||||
|
||||
egl::Error makeCurrent(const gl::Context *context) override;
|
||||
egl::Error swap(const gl::Context *context) override;
|
||||
|
||||
void setSwapInterval(EGLint interval) override;
|
||||
EGLint getSwapBehavior() const override;
|
||||
|
||||
// width and height can change with client window resizing
|
||||
|
@ -120,17 +133,29 @@ class WindowSurfaceMtl : public SurfaceMtl
|
|||
GLsizei samples,
|
||||
FramebufferAttachmentRenderTarget **rtOut) override;
|
||||
|
||||
angle::Result ensureCurrentDrawableObtained(const gl::Context *context);
|
||||
|
||||
// Ensure the the texture returned from getColorTexture() is ready for glReadPixels(). This
|
||||
// implicitly calls ensureCurrentDrawableObtained().
|
||||
angle::Result ensureColorTextureReadyForReadPixels(const gl::Context *context);
|
||||
|
||||
private:
|
||||
angle::Result swapImpl(const gl::Context *context);
|
||||
angle::Result ensureRenderTargetsCreated(const gl::Context *context);
|
||||
angle::Result obtainNextDrawable(const gl::Context *context);
|
||||
angle::Result ensureCompanionTexturesSizeCorrect(const gl::Context *context);
|
||||
|
||||
CGSize calcExpectedDrawableSize() const;
|
||||
// Check if metal layer has been resized.
|
||||
void checkIfLayerResized();
|
||||
bool checkIfLayerResized(const gl::Context *context);
|
||||
|
||||
mtl::AutoObjCObj<CAMetalLayer> mMetalLayer = nil;
|
||||
CALayer *mLayer;
|
||||
mtl::AutoObjCPtr<id<CAMetalDrawable>> mCurrentDrawable = nil;
|
||||
|
||||
// Cache last known drawable size that is used by GL context. Can be used to detect resize
|
||||
// event. We don't use mMetalLayer.drawableSize directly since it might be changed internally by
|
||||
// metal runtime.
|
||||
CGSize mCurrentKnownDrawableSize;
|
||||
};
|
||||
|
||||
} // namespace rx
|
||||
|
|
|
@ -46,6 +46,30 @@ constexpr angle::FormatID kDefaultFrameBufferStencilFormatId = angle::FormatID::
|
|||
constexpr angle::FormatID kDefaultFrameBufferDepthStencilFormatId =
|
||||
angle::FormatID::D24_UNORM_S8_UINT;
|
||||
|
||||
angle::Result CreateTexture(const gl::Context *context,
|
||||
const mtl::Format &format,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
uint32_t samples,
|
||||
bool renderTargetOnly,
|
||||
mtl::TextureRef *textureOut)
|
||||
{
|
||||
ContextMtl *contextMtl = mtl::GetImpl(context);
|
||||
if (samples > 1)
|
||||
{
|
||||
ANGLE_TRY(mtl::Texture::Make2DMSTexture(contextMtl, format, width, height, samples,
|
||||
/** renderTargetOnly */ renderTargetOnly,
|
||||
/** allowFormatView */ false, textureOut));
|
||||
}
|
||||
else
|
||||
{
|
||||
ANGLE_TRY(mtl::Texture::Make2DTexture(contextMtl, format, width, height, 1,
|
||||
/** renderTargetOnly */ renderTargetOnly,
|
||||
/** allowFormatView */ false, textureOut));
|
||||
}
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
ANGLE_MTL_UNUSED
|
||||
bool IsFrameCaptureEnabled()
|
||||
{
|
||||
|
@ -194,6 +218,8 @@ SurfaceMtl::SurfaceMtl(DisplayMtl *display,
|
|||
mColorFormat.intendedFormatId = mColorFormat.actualFormatId = angle::FormatID::B8G8R8A8_UNORM;
|
||||
mColorFormat.metalFormat = MTLPixelFormatBGRA8Unorm;
|
||||
|
||||
mSamples = state.config->samples;
|
||||
|
||||
int depthBits = 0;
|
||||
int stencilBits = 0;
|
||||
if (state.config)
|
||||
|
@ -235,6 +261,8 @@ void SurfaceMtl::destroy(const egl::Display *display)
|
|||
mDepthTexture = nullptr;
|
||||
mStencilTexture = nullptr;
|
||||
|
||||
mMSColorTexture = nullptr;
|
||||
|
||||
mColorRenderTarget.reset();
|
||||
mDepthRenderTarget.reset();
|
||||
mStencilRenderTarget.reset();
|
||||
|
@ -248,18 +276,25 @@ egl::Error SurfaceMtl::initialize(const egl::Display *display)
|
|||
FramebufferImpl *SurfaceMtl::createDefaultFramebuffer(const gl::Context *context,
|
||||
const gl::FramebufferState &state)
|
||||
{
|
||||
auto fbo = new FramebufferMtl(state, /* flipY */ false);
|
||||
auto fbo = new FramebufferMtl(state, /* flipY */ false, /* backbuffer */ nullptr);
|
||||
|
||||
return fbo;
|
||||
}
|
||||
|
||||
egl::Error SurfaceMtl::makeCurrent(const gl::Context *context)
|
||||
{
|
||||
ContextMtl *contextMtl = mtl::GetImpl(context);
|
||||
StartFrameCapture(contextMtl);
|
||||
|
||||
return egl::NoError();
|
||||
}
|
||||
|
||||
egl::Error SurfaceMtl::unMakeCurrent(const gl::Context *context)
|
||||
{
|
||||
ContextMtl *contextMtl = mtl::GetImpl(context);
|
||||
contextMtl->flushCommandBufer();
|
||||
|
||||
StopFrameCapture();
|
||||
return egl::NoError();
|
||||
}
|
||||
|
||||
|
@ -376,21 +411,38 @@ angle::Result SurfaceMtl::getAttachmentRenderTarget(const gl::Context *context,
|
|||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
angle::Result SurfaceMtl::ensureDepthStencilSizeCorrect(const gl::Context *context,
|
||||
gl::Framebuffer::DirtyBits *fboDirtyBits)
|
||||
angle::Result SurfaceMtl::ensureCompanionTexturesSizeCorrect(const gl::Context *context,
|
||||
const gl::Extents &size)
|
||||
{
|
||||
ASSERT(mColorTexture && mColorTexture->get());
|
||||
|
||||
ContextMtl *contextMtl = mtl::GetImpl(context);
|
||||
auto size = mColorTexture->size();
|
||||
|
||||
ASSERT(mColorTexture);
|
||||
|
||||
if (mSamples > 1 && (!mMSColorTexture || mMSColorTexture->size() != size))
|
||||
{
|
||||
mAutoResolveMSColorTexture =
|
||||
contextMtl->getDisplay()->getFeatures().allowMultisampleStoreAndResolve.enabled;
|
||||
ANGLE_TRY(CreateTexture(context, mColorFormat, size.width, size.height, mSamples,
|
||||
/** renderTargetOnly */ mAutoResolveMSColorTexture,
|
||||
&mMSColorTexture));
|
||||
|
||||
if (mAutoResolveMSColorTexture)
|
||||
{
|
||||
// Use auto MSAA resolve at the end of render pass.
|
||||
mColorRenderTarget.setImplicitMSTexture(mMSColorTexture);
|
||||
}
|
||||
else
|
||||
{
|
||||
mColorRenderTarget.setTexture(mMSColorTexture);
|
||||
}
|
||||
}
|
||||
|
||||
if (mDepthFormat.valid() && (!mDepthTexture || mDepthTexture->size() != size))
|
||||
{
|
||||
ANGLE_TRY(mtl::Texture::Make2DTexture(contextMtl, mDepthFormat, size.width, size.height, 1,
|
||||
true, false, &mDepthTexture));
|
||||
ANGLE_TRY(CreateTexture(context, mDepthFormat, size.width, size.height, mSamples,
|
||||
/** renderTargetOnly */ true, &mDepthTexture));
|
||||
|
||||
mDepthRenderTarget.set(mDepthTexture, 0, 0, mDepthFormat);
|
||||
fboDirtyBits->set(gl::Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT);
|
||||
}
|
||||
|
||||
if (mStencilFormat.valid() && (!mStencilTexture || mStencilTexture->size() != size))
|
||||
|
@ -401,24 +453,43 @@ angle::Result SurfaceMtl::ensureDepthStencilSizeCorrect(const gl::Context *conte
|
|||
}
|
||||
else
|
||||
{
|
||||
ANGLE_TRY(mtl::Texture::Make2DTexture(contextMtl, mStencilFormat, size.width,
|
||||
size.height, 1, true, false, &mStencilTexture));
|
||||
ANGLE_TRY(CreateTexture(context, mStencilFormat, size.width, size.height, mSamples,
|
||||
/** renderTargetOnly */ true, &mStencilTexture));
|
||||
}
|
||||
|
||||
mStencilRenderTarget.set(mStencilTexture, 0, 0, mStencilFormat);
|
||||
fboDirtyBits->set(gl::Framebuffer::DIRTY_BIT_STENCIL_ATTACHMENT);
|
||||
}
|
||||
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
angle::Result SurfaceMtl::resolveColorTextureIfNeeded(const gl::Context *context)
|
||||
{
|
||||
ASSERT(mMSColorTexture);
|
||||
if (!mAutoResolveMSColorTexture)
|
||||
{
|
||||
// Manually resolve texture
|
||||
ContextMtl *contextMtl = mtl::GetImpl(context);
|
||||
|
||||
mtl::RenderCommandEncoder *encoder =
|
||||
contextMtl->getRenderCommandEncoder(mColorTexture, gl::ImageIndex::Make2D(0));
|
||||
ANGLE_TRY(
|
||||
contextMtl->getDisplay()->getUtils().blitWithDraw(context, encoder, mMSColorTexture));
|
||||
contextMtl->endEncoding(true);
|
||||
}
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
// WindowSurfaceMtl implementation.
|
||||
WindowSurfaceMtl::WindowSurfaceMtl(DisplayMtl *display,
|
||||
const egl::SurfaceState &state,
|
||||
EGLNativeWindowType window,
|
||||
const egl::AttributeMap &attribs)
|
||||
: SurfaceMtl(display, state, attribs), mLayer((__bridge CALayer *)(window))
|
||||
{}
|
||||
{
|
||||
// NOTE(hqle): Width and height attributes is ignored for now.
|
||||
mCurrentKnownDrawableSize = CGSizeMake(0, 0);
|
||||
}
|
||||
|
||||
WindowSurfaceMtl::~WindowSurfaceMtl() {}
|
||||
|
||||
|
@ -470,15 +541,15 @@ egl::Error WindowSurfaceMtl::initialize(const egl::Display *display)
|
|||
mMetalLayer.get().autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
|
||||
#endif
|
||||
|
||||
// ensure drawableSize is set to correct value:
|
||||
mMetalLayer.get().drawableSize = mCurrentKnownDrawableSize = calcExpectedDrawableSize();
|
||||
|
||||
if (mMetalLayer.get() != mLayer)
|
||||
{
|
||||
mMetalLayer.get().contentsScale = mLayer.contentsScale;
|
||||
|
||||
[mLayer addSublayer:mMetalLayer.get()];
|
||||
}
|
||||
|
||||
// ensure drawableSize is set to correct value:
|
||||
checkIfLayerResized();
|
||||
}
|
||||
|
||||
return egl::NoError();
|
||||
|
@ -487,18 +558,11 @@ egl::Error WindowSurfaceMtl::initialize(const egl::Display *display)
|
|||
FramebufferImpl *WindowSurfaceMtl::createDefaultFramebuffer(const gl::Context *context,
|
||||
const gl::FramebufferState &state)
|
||||
{
|
||||
auto fbo = new FramebufferMtl(state, /* flipY */ true);
|
||||
auto fbo = new FramebufferMtl(state, /* flipY */ true, /* backbuffer */ this);
|
||||
|
||||
return fbo;
|
||||
}
|
||||
|
||||
egl::Error WindowSurfaceMtl::makeCurrent(const gl::Context *context)
|
||||
{
|
||||
ANGLE_TO_EGL_TRY(obtainNextDrawable(context));
|
||||
|
||||
return egl::NoError();
|
||||
}
|
||||
|
||||
egl::Error WindowSurfaceMtl::swap(const gl::Context *context)
|
||||
{
|
||||
ANGLE_TO_EGL_TRY(swapImpl(context));
|
||||
|
@ -506,31 +570,22 @@ egl::Error WindowSurfaceMtl::swap(const gl::Context *context)
|
|||
return egl::NoError();
|
||||
}
|
||||
|
||||
void WindowSurfaceMtl::setSwapInterval(EGLint interval)
|
||||
{
|
||||
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
|
||||
mMetalLayer.get().displaySyncEnabled = interval != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// width and height can change with client window resizing
|
||||
EGLint WindowSurfaceMtl::getWidth() const
|
||||
{
|
||||
if (mColorTexture)
|
||||
{
|
||||
return static_cast<EGLint>(mColorTexture->width());
|
||||
}
|
||||
if (mMetalLayer)
|
||||
{
|
||||
return static_cast<EGLint>(mMetalLayer.get().drawableSize.width);
|
||||
}
|
||||
return SurfaceMtl::getWidth();
|
||||
return static_cast<EGLint>(mCurrentKnownDrawableSize.width);
|
||||
}
|
||||
|
||||
EGLint WindowSurfaceMtl::getHeight() const
|
||||
{
|
||||
if (mColorTexture)
|
||||
{
|
||||
return static_cast<EGLint>(mColorTexture->height());
|
||||
}
|
||||
if (mMetalLayer)
|
||||
{
|
||||
return static_cast<EGLint>(mMetalLayer.get().drawableSize.height);
|
||||
}
|
||||
return SurfaceMtl::getHeight();
|
||||
return static_cast<EGLint>(mCurrentKnownDrawableSize.height);
|
||||
}
|
||||
|
||||
EGLint WindowSurfaceMtl::getSwapBehavior() const
|
||||
|
@ -544,15 +599,15 @@ angle::Result WindowSurfaceMtl::getAttachmentRenderTarget(const gl::Context *con
|
|||
GLsizei samples,
|
||||
FramebufferAttachmentRenderTarget **rtOut)
|
||||
{
|
||||
// NOTE(hqle): Support MSAA.
|
||||
ANGLE_TRY(ensureRenderTargetsCreated(context));
|
||||
ANGLE_TRY(ensureCurrentDrawableObtained(context));
|
||||
ANGLE_TRY(ensureCompanionTexturesSizeCorrect(context));
|
||||
|
||||
return SurfaceMtl::getAttachmentRenderTarget(context, binding, imageIndex, samples, rtOut);
|
||||
}
|
||||
|
||||
angle::Result WindowSurfaceMtl::ensureRenderTargetsCreated(const gl::Context *context)
|
||||
angle::Result WindowSurfaceMtl::ensureCurrentDrawableObtained(const gl::Context *context)
|
||||
{
|
||||
if (!mColorTexture)
|
||||
if (!mCurrentDrawable)
|
||||
{
|
||||
ANGLE_TRY(obtainNextDrawable(context));
|
||||
}
|
||||
|
@ -560,39 +615,81 @@ angle::Result WindowSurfaceMtl::ensureRenderTargetsCreated(const gl::Context *co
|
|||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
void WindowSurfaceMtl::checkIfLayerResized()
|
||||
angle::Result WindowSurfaceMtl::ensureCompanionTexturesSizeCorrect(const gl::Context *context)
|
||||
{
|
||||
ASSERT(mMetalLayer);
|
||||
|
||||
gl::Extents size(static_cast<int>(mMetalLayer.get().drawableSize.width),
|
||||
static_cast<int>(mMetalLayer.get().drawableSize.height), 1);
|
||||
|
||||
ANGLE_TRY(SurfaceMtl::ensureCompanionTexturesSizeCorrect(context, size));
|
||||
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
angle::Result WindowSurfaceMtl::ensureColorTextureReadyForReadPixels(const gl::Context *context)
|
||||
{
|
||||
ANGLE_TRY(ensureCurrentDrawableObtained(context));
|
||||
|
||||
if (mMSColorTexture)
|
||||
{
|
||||
if (mMSColorTexture->isCPUReadMemNeedSync())
|
||||
{
|
||||
ANGLE_TRY(resolveColorTextureIfNeeded(context));
|
||||
mMSColorTexture->resetCPUReadMemNeedSync();
|
||||
}
|
||||
}
|
||||
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
CGSize WindowSurfaceMtl::calcExpectedDrawableSize() const
|
||||
{
|
||||
CGSize currentDrawableSize = mMetalLayer.get().drawableSize;
|
||||
CGSize currentLayerSize = mMetalLayer.get().bounds.size;
|
||||
CGFloat currentLayerContentsScale = mMetalLayer.get().contentsScale;
|
||||
CGSize expectedDrawableSize = CGSizeMake(currentLayerSize.width * currentLayerContentsScale,
|
||||
currentLayerSize.height * currentLayerContentsScale);
|
||||
if (currentDrawableSize.width != expectedDrawableSize.width ||
|
||||
currentDrawableSize.height != expectedDrawableSize.height)
|
||||
|
||||
return expectedDrawableSize;
|
||||
}
|
||||
|
||||
bool WindowSurfaceMtl::checkIfLayerResized(const gl::Context *context)
|
||||
{
|
||||
CGSize currentLayerDrawableSize = mMetalLayer.get().drawableSize;
|
||||
CGSize expectedDrawableSize = calcExpectedDrawableSize();
|
||||
|
||||
// NOTE(hqle): We need to compare the size against mCurrentKnownDrawableSize also.
|
||||
// That is because metal framework might internally change the drawableSize property of
|
||||
// metal layer, and it might become equal to expectedDrawableSize. If that happens, we cannot
|
||||
// know whether the layer has been resized or not.
|
||||
if (currentLayerDrawableSize.width != expectedDrawableSize.width ||
|
||||
currentLayerDrawableSize.height != expectedDrawableSize.height ||
|
||||
mCurrentKnownDrawableSize.width != expectedDrawableSize.width ||
|
||||
mCurrentKnownDrawableSize.height != expectedDrawableSize.height)
|
||||
{
|
||||
// Resize the internal drawable texture.
|
||||
mMetalLayer.get().drawableSize = expectedDrawableSize;
|
||||
mMetalLayer.get().drawableSize = mCurrentKnownDrawableSize = expectedDrawableSize;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
angle::Result WindowSurfaceMtl::obtainNextDrawable(const gl::Context *context)
|
||||
{
|
||||
checkIfLayerResized();
|
||||
|
||||
ANGLE_MTL_OBJC_SCOPE
|
||||
{
|
||||
ContextMtl *contextMtl = mtl::GetImpl(context);
|
||||
|
||||
StartFrameCapture(contextMtl);
|
||||
|
||||
ANGLE_MTL_TRY(contextMtl, mMetalLayer);
|
||||
|
||||
if (mColorTexture)
|
||||
// Check if layer was resized
|
||||
if (checkIfLayerResized(context))
|
||||
{
|
||||
mColorTexture->set(nil);
|
||||
contextMtl->onBackbufferResized(context, this);
|
||||
}
|
||||
|
||||
mCurrentDrawable = nil;
|
||||
mCurrentDrawable.retainAssign([mMetalLayer nextDrawable]);
|
||||
if (!mCurrentDrawable)
|
||||
{
|
||||
|
@ -607,7 +704,8 @@ angle::Result WindowSurfaceMtl::obtainNextDrawable(const gl::Context *context)
|
|||
if (!mColorTexture)
|
||||
{
|
||||
mColorTexture = mtl::Texture::MakeFromMetal(mCurrentDrawable.get().texture);
|
||||
mColorRenderTarget.set(mColorTexture, 0, 0, mColorFormat);
|
||||
ASSERT(!mColorRenderTarget.getTexture());
|
||||
mColorRenderTarget.set(mColorTexture, mMSColorTexture, 0, 0, mColorFormat);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -617,22 +715,8 @@ angle::Result WindowSurfaceMtl::obtainNextDrawable(const gl::Context *context)
|
|||
ANGLE_MTL_LOG("Current metal drawable size=%d,%d", mColorTexture->width(),
|
||||
mColorTexture->height());
|
||||
|
||||
gl::Framebuffer::DirtyBits fboDirtyBits;
|
||||
fboDirtyBits.set(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0);
|
||||
|
||||
// Now we have to resize depth stencil buffers if necessary.
|
||||
ANGLE_TRY(ensureDepthStencilSizeCorrect(context, &fboDirtyBits));
|
||||
|
||||
// Need to notify default framebuffer to invalidate its render targets.
|
||||
// Since a new drawable texture has been obtained, also, the depth stencil
|
||||
// buffers might have been resized.
|
||||
gl::Framebuffer *defaultFbo =
|
||||
context->getFramebuffer(gl::Framebuffer::kDefaultDrawFramebufferHandle);
|
||||
if (defaultFbo)
|
||||
{
|
||||
FramebufferMtl *framebufferMtl = mtl::GetImpl(defaultFbo);
|
||||
ANGLE_TRY(framebufferMtl->syncState(context, GL_FRAMEBUFFER, fboDirtyBits));
|
||||
}
|
||||
// Now we have to resize depth stencil buffers if required.
|
||||
ANGLE_TRY(ensureCompanionTexturesSizeCorrect(context));
|
||||
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
@ -640,15 +724,26 @@ angle::Result WindowSurfaceMtl::obtainNextDrawable(const gl::Context *context)
|
|||
|
||||
angle::Result WindowSurfaceMtl::swapImpl(const gl::Context *context)
|
||||
{
|
||||
ANGLE_TRY(ensureRenderTargetsCreated(context));
|
||||
if (mCurrentDrawable)
|
||||
{
|
||||
ASSERT(mColorTexture);
|
||||
|
||||
ContextMtl *contextMtl = mtl::GetImpl(context);
|
||||
ContextMtl *contextMtl = mtl::GetImpl(context);
|
||||
|
||||
contextMtl->present(context, mCurrentDrawable);
|
||||
if (mMSColorTexture)
|
||||
{
|
||||
ANGLE_TRY(resolveColorTextureIfNeeded(context));
|
||||
}
|
||||
|
||||
StopFrameCapture();
|
||||
contextMtl->present(context, mCurrentDrawable);
|
||||
|
||||
ANGLE_TRY(obtainNextDrawable(context));
|
||||
StopFrameCapture();
|
||||
StartFrameCapture(contextMtl);
|
||||
|
||||
// Invalidate current drawable
|
||||
mColorTexture->set(nil);
|
||||
mCurrentDrawable = nil;
|
||||
}
|
||||
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
|
|
@ -337,7 +337,7 @@ void TextureMtl::releaseTexture(bool releaseImages)
|
|||
|
||||
for (RenderTargetMtl &rt : mLayeredRenderTargets)
|
||||
{
|
||||
rt.set(nullptr);
|
||||
rt.reset();
|
||||
}
|
||||
|
||||
if (releaseImages)
|
||||
|
@ -1153,7 +1153,7 @@ angle::Result TextureMtl::copySubImageWithDraw(const gl::Context *context,
|
|||
DisplayMtl *displayMtl = contextMtl->getDisplay();
|
||||
FramebufferMtl *framebufferMtl = mtl::GetImpl(source);
|
||||
|
||||
RenderTargetMtl *colorReadRT = framebufferMtl->getColorReadRenderTarget();
|
||||
RenderTargetMtl *colorReadRT = framebufferMtl->getColorReadRenderTarget(context);
|
||||
|
||||
if (!colorReadRT || !colorReadRT->getTexture())
|
||||
{
|
||||
|
@ -1192,7 +1192,7 @@ angle::Result TextureMtl::copySubImageCPU(const gl::Context *context,
|
|||
|
||||
ContextMtl *contextMtl = mtl::GetImpl(context);
|
||||
FramebufferMtl *framebufferMtl = mtl::GetImpl(source);
|
||||
RenderTargetMtl *colorReadRT = framebufferMtl->getColorReadRenderTarget();
|
||||
RenderTargetMtl *colorReadRT = framebufferMtl->getColorReadRenderTarget(context);
|
||||
|
||||
if (!colorReadRT || !colorReadRT->getTexture())
|
||||
{
|
||||
|
@ -1216,10 +1216,10 @@ angle::Result TextureMtl::copySubImageCPU(const gl::Context *context,
|
|||
PackPixelsParams packParams(srcRowArea, dstFormat, dstRowPitch, false, nullptr, 0);
|
||||
|
||||
// Read pixels from framebuffer to memory:
|
||||
gl::Rectangle flippedSrcRowArea = framebufferMtl->getReadPixelArea(srcRowArea);
|
||||
gl::Rectangle flippedSrcRowArea =
|
||||
framebufferMtl->getCorrectFlippedReadArea(context, srcRowArea);
|
||||
ANGLE_TRY(framebufferMtl->readPixelsImpl(context, flippedSrcRowArea, packParams,
|
||||
framebufferMtl->getColorReadRenderTarget(),
|
||||
conversionRow.data()));
|
||||
colorReadRT, conversionRow.data()));
|
||||
|
||||
// Upload to texture
|
||||
ANGLE_TRY(UploadTextureContents(context, image, dstFormat, mtlDstRowArea, 0, 0,
|
||||
|
|
|
@ -788,6 +788,24 @@ void RenderCommandEncoder::finalizeLoadStoreAction(
|
|||
return;
|
||||
}
|
||||
|
||||
if (objCRenderPassAttachment.resolveTexture)
|
||||
{
|
||||
if (objCRenderPassAttachment.storeAction == MTLStoreActionStore)
|
||||
{
|
||||
// NOTE(hqle): Currently if the store action with implicit MS texture is
|
||||
// MTLStoreActionStore, it is automatically convert to store and resolve action. It
|
||||
// might introduce unnecessary overhead. Consider an improvement such as only store the
|
||||
// MS texture, and resolve only at the end of real render pass (not render pass the is
|
||||
// interrupted by compute pass) or before glBlitFramebuffer operation starts.
|
||||
objCRenderPassAttachment.storeAction = MTLStoreActionStoreAndMultisampleResolve;
|
||||
}
|
||||
else if (objCRenderPassAttachment.storeAction == MTLStoreActionDontCare)
|
||||
{
|
||||
// Ignore resolve texture if the store action is not a resolve action.
|
||||
objCRenderPassAttachment.resolveTexture = nil;
|
||||
}
|
||||
}
|
||||
|
||||
if (objCRenderPassAttachment.storeAction == MTLStoreActionUnknown)
|
||||
{
|
||||
// If storeAction hasn't been set for this attachment, we set to dontcare.
|
||||
|
|
|
@ -171,6 +171,21 @@ void GetAssignedSamplerBindings(const spirv_cross::CompilerMSL &compilerMsl,
|
|||
}
|
||||
}
|
||||
|
||||
std::string PostProcessTranslatedMsl(const std::string &translatedSource)
|
||||
{
|
||||
// Add function_constant attribute to gl_SampleMask.
|
||||
// Even though this varying is only used when ANGLECoverageMaskEnabled is true,
|
||||
// the spirv-cross doesn't assign function_constant attribute to it. Thus it won't be dead-code
|
||||
// removed when ANGLECoverageMaskEnabled=false.
|
||||
std::string sampleMaskReplaceStr = std::string("[[sample_mask, function_constant(") +
|
||||
sh::mtl::kCoverageMaskEnabledConstName + ")]]";
|
||||
|
||||
// This replaces "gl_SampleMask [[sample_mask]]"
|
||||
// with "gl_SampleMask [[sample_mask, function_constant(ANGLECoverageMaskEnabled)]]"
|
||||
std::regex sampleMaskDeclareRegex(R"(\[\s*\[\s*sample_mask\s*\]\s*\])");
|
||||
return std::regex_replace(translatedSource, sampleMaskDeclareRegex, sampleMaskReplaceStr);
|
||||
}
|
||||
|
||||
// Customized spirv-cross compiler
|
||||
class SpirvToMslCompiler : public spirv_cross::CompilerMSL
|
||||
{
|
||||
|
@ -210,7 +225,7 @@ class SpirvToMslCompiler : public spirv_cross::CompilerMSL
|
|||
spirv_cross::CompilerMSL::set_msl_options(compOpt);
|
||||
|
||||
// Actual compilation
|
||||
std::string translatedMsl = spirv_cross::CompilerMSL::compile();
|
||||
std::string translatedMsl = PostProcessTranslatedMsl(spirv_cross::CompilerMSL::compile());
|
||||
|
||||
// Retrieve automatic texture slot assignments
|
||||
GetAssignedSamplerBindings(*this, originalSamplerBindings,
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "libANGLE/angletypes.h"
|
||||
#include "libANGLE/renderer/metal/mtl_command_buffer.h"
|
||||
#include "libANGLE/renderer/metal/mtl_state_cache.h"
|
||||
#include "libANGLE/renderer/metal/shaders/constants.h"
|
||||
|
||||
namespace rx
|
||||
{
|
||||
|
@ -129,6 +130,7 @@ class ColorBlitUtils
|
|||
// Defer loading of render pipeline state cache.
|
||||
void ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx,
|
||||
int alphaPremultiplyType,
|
||||
int textureType,
|
||||
RenderPipelineCache *cacheOut);
|
||||
|
||||
void setupBlitWithDraw(const gl::Context *context,
|
||||
|
@ -139,9 +141,13 @@ class ColorBlitUtils
|
|||
RenderCommandEncoder *cmdEncoder,
|
||||
const BlitParams ¶ms);
|
||||
|
||||
RenderPipelineCache mBlitRenderPipelineCache;
|
||||
RenderPipelineCache mBlitPremultiplyAlphaRenderPipelineCache;
|
||||
RenderPipelineCache mBlitUnmultiplyAlphaRenderPipelineCache;
|
||||
// Blit with draw pipeline caches:
|
||||
// - array dimension: source texture type (2d, ms, array, 3d, etc)
|
||||
using ColorBlitRenderPipelineCacheArray =
|
||||
std::array<RenderPipelineCache, mtl_shader::kTextureTypeCount>;
|
||||
ColorBlitRenderPipelineCacheArray mBlitRenderPipelineCache;
|
||||
ColorBlitRenderPipelineCacheArray mBlitPremultiplyAlphaRenderPipelineCache;
|
||||
ColorBlitRenderPipelineCacheArray mBlitUnmultiplyAlphaRenderPipelineCache;
|
||||
};
|
||||
|
||||
// util class for generating index buffer
|
||||
|
@ -231,6 +237,11 @@ class RenderUtils : public Context, angle::NonCopyable
|
|||
angle::Result blitWithDraw(const gl::Context *context,
|
||||
RenderCommandEncoder *cmdEncoder,
|
||||
const BlitParams ¶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
|
||||
angle::Result convertIndexBufferGPU(ContextMtl *contextMtl,
|
||||
|
|
|
@ -29,6 +29,9 @@ namespace
|
|||
#define SOURCE_IDX_IS_U8_CONSTANT_NAME @"kSourceIndexIsU8"
|
||||
#define SOURCE_IDX_IS_U16_CONSTANT_NAME @"kSourceIndexIsU16"
|
||||
#define SOURCE_IDX_IS_U32_CONSTANT_NAME @"kSourceIndexIsU32"
|
||||
#define PREMULTIPLY_ALPHA_CONSTANT_NAME @"kPremultiplyAlpha"
|
||||
#define UNMULTIPLY_ALPHA_CONSTANT_NAME @"kUnmultiplyAlpha"
|
||||
#define SOURCE_TEXTURE_TYPE_CONSTANT_NAME @"kSourceTextureType"
|
||||
|
||||
// See libANGLE/renderer/metal/shaders/clear.metal
|
||||
struct ClearParamsUniform
|
||||
|
@ -44,10 +47,12 @@ struct BlitParamsUniform
|
|||
// 0: lower left, 1: lower right, 2: upper left
|
||||
float srcTexCoords[3][2];
|
||||
int srcLevel = 0;
|
||||
uint8_t srcLuminance = 0; // source texture is luminance texture
|
||||
int srcLayer = 0;
|
||||
uint8_t dstFlipX = 0;
|
||||
uint8_t dstFlipY = 0;
|
||||
uint8_t dstLuminance = 0; // dest texture is luminace
|
||||
uint8_t padding;
|
||||
uint8_t padding1;
|
||||
float padding2[3];
|
||||
};
|
||||
|
||||
struct IndexConversionUniform
|
||||
|
@ -131,6 +136,31 @@ void GetFirstLastIndicesFromClientElements(GLsizei count,
|
|||
memcpy(lastOut, indices + count - 1, sizeof(indices[0]));
|
||||
}
|
||||
|
||||
int GetShaderTextureType(const TextureRef &texture)
|
||||
{
|
||||
if (!texture)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
switch (texture->textureType())
|
||||
{
|
||||
case MTLTextureType2D:
|
||||
return mtl_shader::kTextureType2D;
|
||||
case MTLTextureType2DArray:
|
||||
return mtl_shader::kTextureType2DArray;
|
||||
case MTLTextureType2DMultisample:
|
||||
return mtl_shader::kTextureType2DMultisample;
|
||||
case MTLTextureTypeCube:
|
||||
return mtl_shader::kTextureTypeCube;
|
||||
case MTLTextureType3D:
|
||||
return mtl_shader::kTextureType3D;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ANGLE_INLINE
|
||||
void EnsureComputePipelineInitialized(DisplayMtl *display,
|
||||
NSString *functionName,
|
||||
|
@ -207,6 +237,15 @@ void EnsureSpecializedComputePipelineInitialized(
|
|||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ClearRenderPipelineCacheArray(T *pipelineCacheArray)
|
||||
{
|
||||
for (RenderPipelineCache &pipelineCache : *pipelineCacheArray)
|
||||
{
|
||||
pipelineCache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ClearPipelineStateArray(T *pipelineCacheArray)
|
||||
{
|
||||
|
@ -353,6 +392,20 @@ angle::Result RenderUtils::blitWithDraw(const gl::Context *context,
|
|||
return mColorBlitUtils.blitWithDraw(context, cmdEncoder, params);
|
||||
}
|
||||
|
||||
angle::Result RenderUtils::blitWithDraw(const gl::Context *context,
|
||||
RenderCommandEncoder *cmdEncoder,
|
||||
const TextureRef &srcTexture)
|
||||
{
|
||||
if (!srcTexture)
|
||||
{
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
BlitParams params;
|
||||
params.src = srcTexture;
|
||||
params.srcRect = gl::Rectangle(0, 0, srcTexture->width(), srcTexture->height());
|
||||
return blitWithDraw(context, cmdEncoder, params);
|
||||
}
|
||||
|
||||
angle::Result RenderUtils::convertIndexBufferGPU(ContextMtl *contextMtl,
|
||||
const IndexConversionParams ¶ms)
|
||||
{
|
||||
|
@ -570,13 +623,14 @@ ColorBlitUtils::ColorBlitUtils() = default;
|
|||
|
||||
void ColorBlitUtils::onDestroy()
|
||||
{
|
||||
mBlitRenderPipelineCache.clear();
|
||||
mBlitPremultiplyAlphaRenderPipelineCache.clear();
|
||||
mBlitUnmultiplyAlphaRenderPipelineCache.clear();
|
||||
ClearRenderPipelineCacheArray(&mBlitRenderPipelineCache);
|
||||
ClearRenderPipelineCacheArray(&mBlitPremultiplyAlphaRenderPipelineCache);
|
||||
ClearRenderPipelineCacheArray(&mBlitUnmultiplyAlphaRenderPipelineCache);
|
||||
}
|
||||
|
||||
void ColorBlitUtils::ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx,
|
||||
int alphaPremultiplyType,
|
||||
int textureType,
|
||||
RenderPipelineCache *cacheOut)
|
||||
{
|
||||
RenderPipelineCache &pipelineCache = *cacheOut;
|
||||
|
@ -588,24 +642,43 @@ void ColorBlitUtils::ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx,
|
|||
|
||||
ANGLE_MTL_OBJC_SCOPE
|
||||
{
|
||||
NSString *const fragmentShaderNames[] = {// Normal blit
|
||||
@"blitFS",
|
||||
// Blit premultiply-alpha
|
||||
@"blitPremultiplyAlphaFS",
|
||||
// Blit unmultiply alpha
|
||||
@"blitUnmultiplyAlphaFS"};
|
||||
|
||||
NSError *err = nil;
|
||||
id<MTLLibrary> shaderLib = ctx->getDisplay()->getDefaultShadersLib();
|
||||
id<MTLFunction> vertexShader =
|
||||
[[shaderLib newFunctionWithName:@"blitVS"] ANGLE_MTL_AUTORELEASE];
|
||||
id<MTLFunction> fragmentShader = [[shaderLib
|
||||
newFunctionWithName:fragmentShaderNames[alphaPremultiplyType]] ANGLE_MTL_AUTORELEASE];
|
||||
MTLFunctionConstantValues *funcConstants =
|
||||
[[[MTLFunctionConstantValues alloc] init] ANGLE_MTL_AUTORELEASE];
|
||||
|
||||
constexpr BOOL multiplyAlphaFlags[][2] = {// premultiply, unmultiply
|
||||
|
||||
// Normal blit
|
||||
{NO, NO},
|
||||
// Blit premultiply-alpha
|
||||
{YES, NO},
|
||||
// Blit unmultiply alpha
|
||||
{NO, YES}};
|
||||
|
||||
// Set alpha multiply flags
|
||||
[funcConstants setConstantValue:&multiplyAlphaFlags[alphaPremultiplyType][0]
|
||||
type:MTLDataTypeBool
|
||||
withName:PREMULTIPLY_ALPHA_CONSTANT_NAME];
|
||||
[funcConstants setConstantValue:&multiplyAlphaFlags[alphaPremultiplyType][1]
|
||||
type:MTLDataTypeBool
|
||||
withName:UNMULTIPLY_ALPHA_CONSTANT_NAME];
|
||||
|
||||
// Set texture type constant
|
||||
[funcConstants setConstantValue:&textureType
|
||||
type:MTLDataTypeInt
|
||||
withName:SOURCE_TEXTURE_TYPE_CONSTANT_NAME];
|
||||
|
||||
id<MTLFunction> fragmentShader =
|
||||
[[shaderLib newFunctionWithName:@"blitFS" constantValues:funcConstants
|
||||
error:&err] ANGLE_MTL_AUTORELEASE];
|
||||
|
||||
ASSERT(vertexShader);
|
||||
ASSERT(fragmentShader);
|
||||
|
||||
mBlitRenderPipelineCache.setVertexShader(ctx, vertexShader);
|
||||
mBlitRenderPipelineCache.setFragmentShader(ctx, fragmentShader);
|
||||
pipelineCache.setVertexShader(ctx, vertexShader);
|
||||
pipelineCache.setFragmentShader(ctx, fragmentShader);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -625,23 +698,25 @@ id<MTLRenderPipelineState> ColorBlitUtils::getBlitRenderPipelineState(
|
|||
|
||||
RenderPipelineCache *pipelineCache;
|
||||
int alphaPremultiplyType;
|
||||
int textureType = GetShaderTextureType(params.src);
|
||||
if (params.unpackPremultiplyAlpha == params.unpackUnmultiplyAlpha)
|
||||
{
|
||||
alphaPremultiplyType = 0;
|
||||
pipelineCache = &mBlitRenderPipelineCache;
|
||||
pipelineCache = &mBlitRenderPipelineCache[textureType];
|
||||
}
|
||||
else if (params.unpackPremultiplyAlpha)
|
||||
{
|
||||
alphaPremultiplyType = 1;
|
||||
pipelineCache = &mBlitPremultiplyAlphaRenderPipelineCache;
|
||||
pipelineCache = &mBlitPremultiplyAlphaRenderPipelineCache[textureType];
|
||||
}
|
||||
else
|
||||
{
|
||||
alphaPremultiplyType = 2;
|
||||
pipelineCache = &mBlitUnmultiplyAlphaRenderPipelineCache;
|
||||
pipelineCache = &mBlitUnmultiplyAlphaRenderPipelineCache[textureType];
|
||||
}
|
||||
|
||||
ensureRenderPipelineStateCacheInitialized(contextMtl, alphaPremultiplyType, pipelineCache);
|
||||
ensureRenderPipelineStateCacheInitialized(contextMtl, alphaPremultiplyType, textureType,
|
||||
pipelineCache);
|
||||
|
||||
return pipelineCache->getRenderPipelineState(contextMtl, pipelineDesc);
|
||||
}
|
||||
|
|
|
@ -111,6 +111,15 @@ class Texture final : public Resource,
|
|||
bool allowTextureView,
|
||||
TextureRef *refOut);
|
||||
|
||||
static angle::Result Make2DMSTexture(ContextMtl *context,
|
||||
const Format &format,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
uint32_t samples,
|
||||
bool renderTargetOnly,
|
||||
bool allowTextureView,
|
||||
TextureRef *refOut);
|
||||
|
||||
static TextureRef MakeFromMetal(id<MTLTexture> metalTexture);
|
||||
|
||||
// Allow CPU to read & write data directly to this texture?
|
||||
|
@ -148,6 +157,8 @@ class Texture final : public Resource,
|
|||
gl::Extents size(uint32_t level = 0) const;
|
||||
gl::Extents size(const gl::ImageIndex &index) const;
|
||||
|
||||
uint32_t samples() const;
|
||||
|
||||
// For render target
|
||||
MTLColorWriteMask getColorWritableMask() const { return *mColorWritableMask; }
|
||||
void setColorWritableMask(MTLColorWriteMask mask) { *mColorWritableMask = mask; }
|
||||
|
|
|
@ -147,6 +147,38 @@ angle::Result Texture::MakeCubeTexture(ContextMtl *context,
|
|||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
/** static */
|
||||
angle::Result Texture::Make2DMSTexture(ContextMtl *context,
|
||||
const Format &format,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
uint32_t samples,
|
||||
bool renderTargetOnly,
|
||||
bool allowTextureView,
|
||||
TextureRef *refOut)
|
||||
{
|
||||
ANGLE_MTL_OBJC_SCOPE
|
||||
{
|
||||
MTLTextureDescriptor *desc = [[MTLTextureDescriptor new] ANGLE_MTL_AUTORELEASE];
|
||||
desc.textureType = MTLTextureType2DMultisample;
|
||||
desc.pixelFormat = format.metalFormat;
|
||||
desc.width = width;
|
||||
desc.height = height;
|
||||
desc.mipmapLevelCount = 1;
|
||||
desc.sampleCount = samples;
|
||||
|
||||
SetTextureSwizzle(context, format, desc);
|
||||
refOut->reset(new Texture(context, desc, 1, renderTargetOnly, allowTextureView));
|
||||
} // ANGLE_MTL_OBJC_SCOPE
|
||||
|
||||
if (!refOut || !refOut->get())
|
||||
{
|
||||
ANGLE_MTL_CHECK(context, false, GL_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
return angle::Result::Continue;
|
||||
}
|
||||
|
||||
/** static */
|
||||
TextureRef Texture::MakeFromMetal(id<MTLTexture> metalTexture)
|
||||
{
|
||||
|
@ -183,7 +215,8 @@ Texture::Texture(ContextMtl *context,
|
|||
desc.usage |= MTLTextureUsageRenderTarget;
|
||||
}
|
||||
|
||||
if (!Format::FormatCPUReadable(desc.pixelFormat))
|
||||
if (!Format::FormatCPUReadable(desc.pixelFormat) ||
|
||||
desc.textureType == MTLTextureType2DMultisample)
|
||||
{
|
||||
desc.resourceOptions = MTLResourceStorageModePrivate;
|
||||
}
|
||||
|
@ -405,6 +438,11 @@ gl::Extents Texture::size(const gl::ImageIndex &index) const
|
|||
return size(index.getLevelIndex());
|
||||
}
|
||||
|
||||
uint32_t Texture::samples() const
|
||||
{
|
||||
return static_cast<uint32_t>(get().sampleCount);
|
||||
}
|
||||
|
||||
void Texture::set(id<MTLTexture> metalTexture)
|
||||
{
|
||||
ParentClass::set(metalTexture);
|
||||
|
|
|
@ -214,6 +214,7 @@ struct RenderPipelineOutputDesc
|
|||
|
||||
static_assert(kMaxRenderTargets <= 4, "kMaxRenderTargets must be <= 4");
|
||||
uint8_t numColorAttachments : 3;
|
||||
uint8_t sampleCount : 5;
|
||||
};
|
||||
|
||||
// Some SDK levels don't declare MTLPrimitiveTopologyClass. Needs to do compile time check here:
|
||||
|
@ -249,7 +250,13 @@ struct alignas(4) RenderPipelineDesc
|
|||
// Use uint8_t instead of PrimitiveTopologyClass to compact space.
|
||||
uint8_t inputPrimitiveTopology : 2;
|
||||
|
||||
bool rasterizationEnabled;
|
||||
bool rasterizationEnabled : 1;
|
||||
bool alphaToCoverageEnabled : 1;
|
||||
|
||||
// These flags are for emulation and do not correspond to any flags in
|
||||
// MTLRenderPipelineDescriptor descriptor. These flags should be used by
|
||||
// RenderPipelineCacheSpecializeShaderFactory.
|
||||
bool emulateCoverageMask : 1;
|
||||
};
|
||||
|
||||
struct RenderPassAttachmentDesc
|
||||
|
@ -261,9 +268,15 @@ struct RenderPassAttachmentDesc
|
|||
bool equalIgnoreLoadStoreOptions(const RenderPassAttachmentDesc &other) const;
|
||||
bool operator==(const RenderPassAttachmentDesc &other) const;
|
||||
|
||||
ANGLE_INLINE bool hasImplicitMSTexture() const { return implicitMSTexture.get(); }
|
||||
|
||||
TextureRef texture;
|
||||
// Implicit multisample texture that will be rendered into and discarded at the end of
|
||||
// a render pass. Its result will be resolved into normal texture above.
|
||||
TextureRef implicitMSTexture;
|
||||
uint32_t level;
|
||||
uint32_t slice;
|
||||
uint32_t sliceOrDepth;
|
||||
|
||||
MTLLoadAction loadAction;
|
||||
MTLStoreAction storeAction;
|
||||
MTLStoreActionOptions storeActionOptions;
|
||||
|
@ -333,6 +346,7 @@ struct RenderPassDesc
|
|||
inline bool operator!=(const RenderPassDesc &other) const { return !(*this == other); }
|
||||
|
||||
uint32_t numColorAttachments = 0;
|
||||
uint32_t sampleCount = 1;
|
||||
};
|
||||
|
||||
} // namespace mtl
|
||||
|
@ -365,18 +379,43 @@ namespace rx
|
|||
{
|
||||
namespace mtl
|
||||
{
|
||||
// render pipeline state cache per shader program
|
||||
|
||||
// Abstract factory to create specialized vertex & fragment shaders based on RenderPipelineDesc.
|
||||
class RenderPipelineCacheSpecializeShaderFactory
|
||||
{
|
||||
public:
|
||||
virtual ~RenderPipelineCacheSpecializeShaderFactory() = default;
|
||||
|
||||
// Get specialized shader for the render pipeline cache.
|
||||
virtual angle::Result getSpecializedShader(Context *context,
|
||||
gl::ShaderType shaderType,
|
||||
const RenderPipelineDesc &renderPipelineDesc,
|
||||
id<MTLFunction> *shaderOut) = 0;
|
||||
// Check whether specialized shaders is required for the specified RenderPipelineDesc.
|
||||
// If not, the render pipeline cache will use the supplied non-specialized shaders.
|
||||
virtual bool hasSpecializedShader(gl::ShaderType shaderType,
|
||||
const RenderPipelineDesc &renderPipelineDesc) = 0;
|
||||
};
|
||||
|
||||
// Render pipeline state cache per shader program.
|
||||
class RenderPipelineCache final : angle::NonCopyable
|
||||
{
|
||||
public:
|
||||
RenderPipelineCache();
|
||||
RenderPipelineCache(RenderPipelineCacheSpecializeShaderFactory *specializedShaderFactory);
|
||||
~RenderPipelineCache();
|
||||
|
||||
// Set non-specialized vertex/fragment shader to be used by render pipeline cache to create
|
||||
// render pipeline state. If the internal
|
||||
// RenderPipelineCacheSpecializeShaderFactory.hasSpecializedShader() returns false for a
|
||||
// particular RenderPipelineDesc, the render pipeline cache will use the non-specialized
|
||||
// shaders.
|
||||
void setVertexShader(Context *context, id<MTLFunction> shader);
|
||||
void setFragmentShader(Context *context, id<MTLFunction> shader);
|
||||
|
||||
id<MTLFunction> getVertexShader() { return mVertexShader.get(); }
|
||||
id<MTLFunction> getFragmentShader() { return mFragmentShader.get(); }
|
||||
// Get non-specialized shaders supplied via set*Shader().
|
||||
id<MTLFunction> getVertexShader() { return mVertexShader; }
|
||||
id<MTLFunction> getFragmentShader() { return mFragmentShader; }
|
||||
|
||||
AutoObjCPtr<id<MTLRenderPipelineState>> getRenderPipelineState(ContextMtl *context,
|
||||
const RenderPipelineDesc &desc);
|
||||
|
@ -384,8 +423,10 @@ class RenderPipelineCache final : angle::NonCopyable
|
|||
void clear();
|
||||
|
||||
protected:
|
||||
AutoObjCPtr<id<MTLFunction>> mVertexShader = nil;
|
||||
AutoObjCPtr<id<MTLFunction>> mFragmentShader = nil;
|
||||
// Non-specialized vertex shader
|
||||
AutoObjCPtr<id<MTLFunction>> mVertexShader;
|
||||
// Non-specialized fragment shader
|
||||
AutoObjCPtr<id<MTLFunction>> mFragmentShader;
|
||||
|
||||
private:
|
||||
void clearPipelineStates();
|
||||
|
@ -404,6 +445,8 @@ class RenderPipelineCache final : angle::NonCopyable
|
|||
// One table with default attrib and one table without.
|
||||
std::unordered_map<RenderPipelineDesc, AutoObjCPtr<id<MTLRenderPipelineState>>>
|
||||
mRenderPipelineStates[2];
|
||||
|
||||
RenderPipelineCacheSpecializeShaderFactory *mSpecializedShaderFactory;
|
||||
};
|
||||
|
||||
class StateCache final : angle::NonCopyable
|
||||
|
|
|
@ -156,11 +156,13 @@ MTLRenderPipelineDescriptor *ToObjC(id<MTLFunction> vertexShader,
|
|||
}
|
||||
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc.outputDescriptor, depthAttachmentPixelFormat);
|
||||
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc.outputDescriptor, stencilAttachmentPixelFormat);
|
||||
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc.outputDescriptor, sampleCount);
|
||||
|
||||
#if ANGLE_MTL_PRIMITIVE_TOPOLOGY_CLASS_AVAILABLE
|
||||
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, inputPrimitiveTopology);
|
||||
#endif
|
||||
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, rasterizationEnabled);
|
||||
ANGLE_OBJC_CP_PROPERTY(objCDesc, desc, alphaToCoverageEnabled);
|
||||
|
||||
return [objCDesc ANGLE_MTL_AUTORELEASE];
|
||||
}
|
||||
|
@ -174,9 +176,44 @@ id<MTLTexture> ToObjC(const TextureRef &texture)
|
|||
void BaseRenderPassAttachmentDescToObjC(const RenderPassAttachmentDesc &src,
|
||||
MTLRenderPassAttachmentDescriptor *dst)
|
||||
{
|
||||
ANGLE_OBJC_CP_PROPERTY(dst, src, texture);
|
||||
ANGLE_OBJC_CP_PROPERTY(dst, src, level);
|
||||
ANGLE_OBJC_CP_PROPERTY(dst, src, slice);
|
||||
const TextureRef &implicitMsTexture = src.implicitMSTexture;
|
||||
|
||||
if (implicitMsTexture)
|
||||
{
|
||||
dst.texture = ToObjC(implicitMsTexture);
|
||||
dst.level = 0;
|
||||
dst.slice = 0;
|
||||
dst.resolveTexture = ToObjC(src.texture);
|
||||
dst.resolveLevel = src.level;
|
||||
if (dst.resolveTexture.textureType == MTLTextureType3D)
|
||||
{
|
||||
dst.resolveDepthPlane = src.sliceOrDepth;
|
||||
dst.resolveSlice = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst.resolveSlice = src.sliceOrDepth;
|
||||
dst.resolveDepthPlane = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dst.texture = ToObjC(src.texture);
|
||||
dst.level = src.level;
|
||||
if (dst.texture.textureType == MTLTextureType3D)
|
||||
{
|
||||
dst.depthPlane = src.sliceOrDepth;
|
||||
dst.slice = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst.slice = src.sliceOrDepth;
|
||||
dst.depthPlane = 0;
|
||||
}
|
||||
dst.resolveTexture = nil;
|
||||
dst.resolveLevel = 0;
|
||||
dst.resolveSlice = 0;
|
||||
}
|
||||
|
||||
ANGLE_OBJC_CP_PROPERTY(dst, src, loadAction);
|
||||
ANGLE_OBJC_CP_PROPERTY(dst, src, storeAction);
|
||||
|
@ -585,7 +622,8 @@ bool RenderPipelineOutputDesc::operator==(const RenderPipelineOutputDesc &rhs) c
|
|||
RenderPipelineDesc::RenderPipelineDesc()
|
||||
{
|
||||
memset(this, 0, sizeof(*this));
|
||||
rasterizationEnabled = true;
|
||||
outputDescriptor.sampleCount = 1;
|
||||
rasterizationEnabled = true;
|
||||
}
|
||||
|
||||
RenderPipelineDesc::RenderPipelineDesc(const RenderPipelineDesc &src)
|
||||
|
@ -626,8 +664,9 @@ RenderPassAttachmentDesc::RenderPassAttachmentDesc()
|
|||
void RenderPassAttachmentDesc::reset()
|
||||
{
|
||||
texture.reset();
|
||||
implicitMSTexture.reset();
|
||||
level = 0;
|
||||
slice = 0;
|
||||
sliceOrDepth = 0;
|
||||
loadAction = MTLLoadActionLoad;
|
||||
storeAction = MTLStoreActionStore;
|
||||
storeActionOptions = MTLStoreActionOptionNone;
|
||||
|
@ -636,7 +675,8 @@ void RenderPassAttachmentDesc::reset()
|
|||
bool RenderPassAttachmentDesc::equalIgnoreLoadStoreOptions(
|
||||
const RenderPassAttachmentDesc &other) const
|
||||
{
|
||||
return texture == other.texture && level == other.level && slice == other.slice;
|
||||
return texture == other.texture && implicitMSTexture == other.implicitMSTexture &&
|
||||
level == other.level && sliceOrDepth == other.sliceOrDepth;
|
||||
}
|
||||
|
||||
bool RenderPassAttachmentDesc::operator==(const RenderPassAttachmentDesc &other) const
|
||||
|
@ -668,8 +708,9 @@ void RenderPassDesc::populateRenderPipelineOutputDesc(MTLColorWriteMask colorWri
|
|||
void RenderPassDesc::populateRenderPipelineOutputDesc(const BlendDesc &blendState,
|
||||
RenderPipelineOutputDesc *outDesc) const
|
||||
{
|
||||
auto &outputDescriptor = *outDesc;
|
||||
outputDescriptor.numColorAttachments = this->numColorAttachments;
|
||||
RenderPipelineOutputDesc &outputDescriptor = *outDesc;
|
||||
outputDescriptor.numColorAttachments = this->numColorAttachments;
|
||||
outputDescriptor.sampleCount = this->sampleCount;
|
||||
for (uint32_t i = 0; i < this->numColorAttachments; ++i)
|
||||
{
|
||||
auto &renderPassColorAttachment = this->colorAttachments[i];
|
||||
|
@ -694,6 +735,12 @@ void RenderPassDesc::populateRenderPipelineOutputDesc(const BlendDesc &blendStat
|
|||
}
|
||||
}
|
||||
|
||||
// Reset the unused output slots to ensure consistent hash value
|
||||
for (uint32_t i = this->numColorAttachments; i < kMaxRenderTargets; ++i)
|
||||
{
|
||||
outputDescriptor.colorAttachments[i].reset();
|
||||
}
|
||||
|
||||
auto depthTexture = this->depthAttachment.texture;
|
||||
outputDescriptor.depthAttachmentPixelFormat =
|
||||
depthTexture ? depthTexture->pixelFormat() : MTLPixelFormatInvalid;
|
||||
|
@ -770,7 +817,12 @@ void RenderPassDesc::convertToMetalDesc(MTLRenderPassDescriptor *objCDesc) const
|
|||
}
|
||||
|
||||
// RenderPipelineCache implementation
|
||||
RenderPipelineCache::RenderPipelineCache() {}
|
||||
RenderPipelineCache::RenderPipelineCache() : RenderPipelineCache(nullptr) {}
|
||||
|
||||
RenderPipelineCache::RenderPipelineCache(
|
||||
RenderPipelineCacheSpecializeShaderFactory *specializedShaderFactory)
|
||||
: mSpecializedShaderFactory(specializedShaderFactory)
|
||||
{}
|
||||
|
||||
RenderPipelineCache::~RenderPipelineCache() {}
|
||||
|
||||
|
@ -837,6 +889,10 @@ AutoObjCPtr<id<MTLRenderPipelineState>> RenderPipelineCache::insertRenderPipelin
|
|||
{
|
||||
AutoObjCPtr<id<MTLRenderPipelineState>> newState =
|
||||
createRenderPipelineState(context, desc, insertDefaultAttribLayout);
|
||||
if (!newState)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
int tableIdx = insertDefaultAttribLayout ? 1 : 0;
|
||||
auto re = mRenderPipelineStates[tableIdx].insert(std::make_pair(desc, newState));
|
||||
|
@ -850,16 +906,66 @@ AutoObjCPtr<id<MTLRenderPipelineState>> RenderPipelineCache::insertRenderPipelin
|
|||
|
||||
AutoObjCPtr<id<MTLRenderPipelineState>> RenderPipelineCache::createRenderPipelineState(
|
||||
Context *context,
|
||||
const RenderPipelineDesc &desc,
|
||||
const RenderPipelineDesc &originalDesc,
|
||||
bool insertDefaultAttribLayout)
|
||||
{
|
||||
ANGLE_MTL_OBJC_SCOPE
|
||||
{
|
||||
auto metalDevice = context->getMetalDevice();
|
||||
AutoObjCObj<MTLRenderPipelineDescriptor> objCDesc =
|
||||
ToObjC(mVertexShader, mFragmentShader, desc);
|
||||
// Disable coverage if the render pipeline's sample count is only 1.
|
||||
RenderPipelineDesc desc = originalDesc;
|
||||
if (desc.outputDescriptor.sampleCount == 1)
|
||||
{
|
||||
// Disable sample coverage if the output is not multisample
|
||||
desc.emulateCoverageMask = false;
|
||||
desc.alphaToCoverageEnabled = false;
|
||||
}
|
||||
|
||||
// special attribute slot for default attribute
|
||||
// Choose shader variant
|
||||
id<MTLFunction> vertShader = nil;
|
||||
id<MTLFunction> fragShader = nil;
|
||||
if (mSpecializedShaderFactory &&
|
||||
mSpecializedShaderFactory->hasSpecializedShader(gl::ShaderType::Vertex, desc))
|
||||
{
|
||||
if (IsError(mSpecializedShaderFactory->getSpecializedShader(
|
||||
context, gl::ShaderType::Vertex, desc, &vertShader)))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-specialized version
|
||||
vertShader = mVertexShader;
|
||||
}
|
||||
|
||||
if (mSpecializedShaderFactory &&
|
||||
mSpecializedShaderFactory->hasSpecializedShader(gl::ShaderType::Fragment, desc))
|
||||
{
|
||||
if (IsError(mSpecializedShaderFactory->getSpecializedShader(
|
||||
context, gl::ShaderType::Fragment, desc, &fragShader)))
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-specialized version
|
||||
fragShader = mFragmentShader;
|
||||
}
|
||||
|
||||
if (!vertShader)
|
||||
{
|
||||
// Render pipeline without vertex shader is invalid.
|
||||
context->handleError(GL_INVALID_OPERATION, __FILE__, ANGLE_FUNCTION, __LINE__);
|
||||
return nil;
|
||||
}
|
||||
|
||||
id<MTLDevice> metalDevice = context->getMetalDevice();
|
||||
|
||||
// Convert to Objective-C desc:
|
||||
AutoObjCObj<MTLRenderPipelineDescriptor> objCDesc = ToObjC(vertShader, fragShader, desc);
|
||||
|
||||
// Special attribute slot for default attribute
|
||||
if (insertDefaultAttribLayout)
|
||||
{
|
||||
MTLVertexBufferLayoutDescriptor *defaultAttribLayoutObjCDesc =
|
||||
|
@ -873,8 +979,9 @@ AutoObjCPtr<id<MTLRenderPipelineState>> RenderPipelineCache::createRenderPipelin
|
|||
atIndexedSubscript:kDefaultAttribsBindingIndex];
|
||||
}
|
||||
// Create pipeline state
|
||||
NSError *err = nil;
|
||||
auto newState = [metalDevice newRenderPipelineStateWithDescriptor:objCDesc error:&err];
|
||||
NSError *err = nil;
|
||||
id<MTLRenderPipelineState> newState =
|
||||
[metalDevice newRenderPipelineStateWithDescriptor:objCDesc error:&err];
|
||||
if (err)
|
||||
{
|
||||
context->handleError(err, __FILE__, ANGLE_FUNCTION, __LINE__);
|
||||
|
|
|
@ -7,15 +7,28 @@
|
|||
|
||||
#include "common.h"
|
||||
|
||||
using namespace rx::mtl_shader;
|
||||
|
||||
// function_constant(0-3) is already used by gen_indices.metal
|
||||
constant bool kPremultiplyAlpha [[function_constant(4)]];
|
||||
constant bool kUnmultiplyAlpha [[function_constant(5)]];
|
||||
constant int kSourceTextureType [[function_constant(6)]]; // Source texture type.
|
||||
constant bool kSourceTextureType2D = kSourceTextureType == kTextureType2D;
|
||||
constant bool kSourceTextureType2DArray = kSourceTextureType == kTextureType2DArray;
|
||||
constant bool kSourceTextureType2DMS = kSourceTextureType == kTextureType2DMultisample;
|
||||
constant bool kSourceTextureTypeCube = kSourceTextureType == kTextureTypeCube;
|
||||
constant bool kSourceTextureType3D = kSourceTextureType == kTextureType3D;
|
||||
|
||||
struct BlitParams
|
||||
{
|
||||
// 0: lower left, 1: lower right, 2: upper left
|
||||
float2 srcTexCoords[3];
|
||||
int srcLevel;
|
||||
bool srcLuminance; // source texture is luminance texture
|
||||
int srcLevel; // Source texture level.
|
||||
int srcLayer; // Source texture layer.
|
||||
|
||||
bool dstFlipViewportX;
|
||||
bool dstFlipViewportY;
|
||||
bool dstLuminance; // destination texture is luminance;
|
||||
bool dstLuminance; // destination texture is luminance. Unused by depth & stencil blitting.
|
||||
};
|
||||
|
||||
struct BlitVSOut
|
||||
|
@ -24,13 +37,16 @@ struct BlitVSOut
|
|||
float2 texCoords [[user(locn1)]];
|
||||
};
|
||||
|
||||
vertex BlitVSOut blitVS(unsigned int vid [[ vertex_id ]],
|
||||
constant BlitParams &options [[buffer(0)]])
|
||||
vertex BlitVSOut blitVS(unsigned int vid [[vertex_id]], constant BlitParams &options [[buffer(0)]])
|
||||
{
|
||||
BlitVSOut output;
|
||||
output.position = float4(gCorners[vid], 0.0, 1.0);
|
||||
output.texCoords = options.srcTexCoords[vid];
|
||||
|
||||
if (options.dstFlipViewportX)
|
||||
{
|
||||
output.position.x = -output.position.x;
|
||||
}
|
||||
if (!options.dstFlipViewportY)
|
||||
{
|
||||
// If viewport is not flipped, we have to flip Y in normalized device coordinates.
|
||||
|
@ -41,58 +57,124 @@ vertex BlitVSOut blitVS(unsigned int vid [[ vertex_id ]],
|
|||
return output;
|
||||
}
|
||||
|
||||
float4 blitSampleTexture(texture2d<float> srcTexture,
|
||||
float2 texCoords,
|
||||
constant BlitParams &options)
|
||||
static inline float3 cubeTexcoords(float2 texcoords, int face)
|
||||
{
|
||||
constexpr sampler textureSampler(mag_filter::linear,
|
||||
min_filter::linear);
|
||||
float4 output = srcTexture.sample(textureSampler, texCoords, level(options.srcLevel));
|
||||
|
||||
if (options.srcLuminance)
|
||||
texcoords = 2.0 * texcoords - 1.0;
|
||||
switch (face)
|
||||
{
|
||||
output.gb = float2(output.r, output.r);
|
||||
case 0:
|
||||
return float3(1.0, -texcoords.y, -texcoords.x);
|
||||
case 1:
|
||||
return float3(-1.0, -texcoords.y, texcoords.x);
|
||||
case 2:
|
||||
return float3(texcoords.x, 1.0, texcoords.y);
|
||||
case 3:
|
||||
return float3(texcoords.x, -1.0, -texcoords.y);
|
||||
case 4:
|
||||
return float3(texcoords.x, -texcoords.y, 1.0);
|
||||
case 5:
|
||||
return float3(-texcoords.x, -texcoords.y, -1.0);
|
||||
}
|
||||
return float3(texcoords, 0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline vec<T, 4> blitSampleTextureMS(texture2d_ms<T> srcTexture, float2 texCoords)
|
||||
{
|
||||
uint2 dimens(srcTexture.get_width(), srcTexture.get_height());
|
||||
uint2 coords = uint2(texCoords * float2(dimens));
|
||||
|
||||
uint samples = srcTexture.get_num_samples();
|
||||
|
||||
vec<T, 4> output(0);
|
||||
|
||||
for (uint sample = 0; sample < samples; ++sample)
|
||||
{
|
||||
output += srcTexture.read(coords, sample);
|
||||
}
|
||||
|
||||
output = output / samples;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline vec<T, 4> blitSampleTexture3D(texture3d<T> srcTexture,
|
||||
sampler textureSampler,
|
||||
float2 texCoords,
|
||||
constant BlitParams &options)
|
||||
{
|
||||
uint depth = srcTexture.get_depth(options.srcLevel);
|
||||
float zCoord = (float(options.srcLayer) + 0.5) / float(depth);
|
||||
|
||||
return srcTexture.sample(textureSampler, float3(texCoords, zCoord), level(options.srcLevel));
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
#define BLIT_COLOR_FS_PARAMS(TYPE) \
|
||||
BlitVSOut input [[stage_in]], \
|
||||
texture2d<TYPE> srcTexture2d [[texture(0), function_constant(kSourceTextureType2D)]], \
|
||||
texture2d_array<TYPE> srcTexture2dArray \
|
||||
[[texture(0), function_constant(kSourceTextureType2DArray)]], \
|
||||
texture2d_ms<TYPE> srcTexture2dMS [[texture(0), function_constant(kSourceTextureType2DMS)]], \
|
||||
texturecube<TYPE> srcTextureCube [[texture(0), function_constant(kSourceTextureTypeCube)]], \
|
||||
texture3d<TYPE> srcTexture3d [[texture(0), function_constant(kSourceTextureType3D)]], \
|
||||
sampler textureSampler [[sampler(0)]], \
|
||||
constant BlitParams &options [[buffer(0)]]
|
||||
// clang-format on
|
||||
|
||||
#define FORWARD_BLIT_COLOR_FS_PARAMS \
|
||||
input, srcTexture2d, srcTexture2dArray, srcTexture2dMS, srcTextureCube, srcTexture3d, \
|
||||
textureSampler, options
|
||||
|
||||
template <typename T>
|
||||
static inline vec<T, 4> blitReadTexture(BLIT_COLOR_FS_PARAMS(T))
|
||||
{
|
||||
vec<T, 4> output;
|
||||
|
||||
switch (kSourceTextureType)
|
||||
{
|
||||
case kTextureType2D:
|
||||
output = srcTexture2d.sample(textureSampler, input.texCoords, level(options.srcLevel));
|
||||
break;
|
||||
case kTextureType2DArray:
|
||||
output = srcTexture2dArray.sample(textureSampler, input.texCoords, options.srcLayer,
|
||||
level(options.srcLevel));
|
||||
break;
|
||||
case kTextureType2DMultisample:
|
||||
output = blitSampleTextureMS(srcTexture2dMS, input.texCoords);
|
||||
break;
|
||||
case kTextureTypeCube:
|
||||
output = srcTextureCube.sample(textureSampler,
|
||||
cubeTexcoords(input.texCoords, options.srcLayer),
|
||||
level(options.srcLevel));
|
||||
break;
|
||||
case kTextureType3D:
|
||||
output = blitSampleTexture3D(srcTexture3d, textureSampler, input.texCoords, options);
|
||||
break;
|
||||
}
|
||||
|
||||
if (kPremultiplyAlpha)
|
||||
{
|
||||
output.xyz *= output.a;
|
||||
}
|
||||
else if (kUnmultiplyAlpha)
|
||||
{
|
||||
if (output.a != 0.0)
|
||||
{
|
||||
output.xyz /= output.a;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.dstLuminance)
|
||||
{
|
||||
output.g = output.b = output.r;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
float4 blitOutput(float4 color, constant BlitParams &options)
|
||||
fragment float4 blitFS(BLIT_COLOR_FS_PARAMS(float))
|
||||
{
|
||||
float4 ret = color;
|
||||
|
||||
if (options.dstLuminance)
|
||||
{
|
||||
ret.r = ret.g = ret.b = color.r;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
fragment float4 blitFS(BlitVSOut input [[stage_in]],
|
||||
texture2d<float> srcTexture [[texture(0)]],
|
||||
constant BlitParams &options [[buffer(0)]])
|
||||
{
|
||||
return blitOutput(blitSampleTexture(srcTexture, input.texCoords, options), options);
|
||||
}
|
||||
|
||||
fragment float4 blitPremultiplyAlphaFS(BlitVSOut input [[stage_in]],
|
||||
texture2d<float> srcTexture [[texture(0)]],
|
||||
constant BlitParams &options [[buffer(0)]])
|
||||
{
|
||||
float4 output = blitSampleTexture(srcTexture, input.texCoords, options);
|
||||
output.xyz *= output.a;
|
||||
return blitOutput(output, options);
|
||||
}
|
||||
|
||||
fragment float4 blitUnmultiplyAlphaFS(BlitVSOut input [[stage_in]],
|
||||
texture2d<float> srcTexture [[texture(0)]],
|
||||
constant BlitParams &options [[buffer(0)]])
|
||||
{
|
||||
float4 output = blitSampleTexture(srcTexture, input.texCoords, options);
|
||||
if (output.a != 0.0)
|
||||
{
|
||||
output.xyz *= 1.0 / output.a;
|
||||
}
|
||||
return blitOutput(output, options);
|
||||
return blitReadTexture(FORWARD_BLIT_COLOR_FS_PARAMS);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
# include <metal_stdlib>
|
||||
#endif
|
||||
|
||||
#include "constants.h"
|
||||
|
||||
#define ANGLE_KERNEL_GUARD(IDX, MAX_COUNT) \
|
||||
if (IDX >= MAX_COUNT) \
|
||||
{ \
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// Copyright 2020 The ANGLE Project. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// constants.h: Declare some constant values to be used by metal defaultshaders.
|
||||
|
||||
#ifndef LIBANGLE_RENDERER_METAL_SHADERS_ENUM_H_
|
||||
#define LIBANGLE_RENDERER_METAL_SHADERS_ENUM_H_
|
||||
|
||||
namespace rx
|
||||
{
|
||||
namespace mtl_shader
|
||||
{
|
||||
|
||||
enum
|
||||
{
|
||||
kTextureType2D = 0,
|
||||
kTextureType2DMultisample = 1,
|
||||
kTextureType2DArray = 2,
|
||||
kTextureTypeCube = 3,
|
||||
kTextureType3D = 4,
|
||||
kTextureTypeCount = 5,
|
||||
};
|
||||
|
||||
} // namespace mtl_shader
|
||||
} // namespace rx
|
||||
|
||||
#endif
|
|
@ -26,7 +26,8 @@ def main():
|
|||
# auto_script parameters.
|
||||
if len(sys.argv) > 1:
|
||||
inputs = [
|
||||
'master_source.metal', 'blit.metal', 'clear.metal', 'gen_indices.metal', 'common.h'
|
||||
'master_source.metal', 'blit.metal', 'clear.metal', 'gen_indices.metal', 'common.h',
|
||||
'constants.h'
|
||||
]
|
||||
outputs = ['compiled/mtl_default_shaders.inc', 'mtl_default_shaders_src_autogen.inc']
|
||||
|
||||
|
|
|
@ -38,7 +38,34 @@ constexpr char default_metallib_src[] = R"(
|
|||
|
||||
|
||||
# 1 "./common.h" 1
|
||||
# 22 "./common.h"
|
||||
# 16 "./common.h"
|
||||
# 1 "./constants.h" 1
|
||||
# 11 "./constants.h"
|
||||
namespace rx
|
||||
{
|
||||
namespace mtl_shader
|
||||
{
|
||||
|
||||
enum
|
||||
{
|
||||
kTextureType2D = 0,
|
||||
kTextureType2DMultisample = 1,
|
||||
kTextureType2DArray = 2,
|
||||
kTextureTypeCube = 3,
|
||||
kTextureType3D = 4,
|
||||
kTextureTypeCount = 5,
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
# 17 "./common.h" 2
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
using namespace metal;
|
||||
|
||||
|
||||
|
@ -68,13 +95,27 @@ fragment float4 clearFS(constant ClearParams &clearParams [[buffer(0)]])
|
|||
}
|
||||
# 10 "master_source.metal" 2
|
||||
# 1 "./blit.metal" 1
|
||||
# 11 "./blit.metal"
|
||||
# 10 "./blit.metal"
|
||||
using namespace rx::mtl_shader;
|
||||
|
||||
|
||||
constant bool kPremultiplyAlpha [[function_constant(4)]];
|
||||
constant bool kUnmultiplyAlpha [[function_constant(5)]];
|
||||
constant int kSourceTextureType [[function_constant(6)]];
|
||||
constant bool kSourceTextureType2D = kSourceTextureType == kTextureType2D;
|
||||
constant bool kSourceTextureType2DArray = kSourceTextureType == kTextureType2DArray;
|
||||
constant bool kSourceTextureType2DMS = kSourceTextureType == kTextureType2DMultisample;
|
||||
constant bool kSourceTextureTypeCube = kSourceTextureType == kTextureTypeCube;
|
||||
constant bool kSourceTextureType3D = kSourceTextureType == kTextureType3D;
|
||||
|
||||
struct BlitParams
|
||||
{
|
||||
|
||||
float2 srcTexCoords[3];
|
||||
int srcLevel;
|
||||
bool srcLuminance;
|
||||
int srcLayer;
|
||||
|
||||
bool dstFlipViewportX;
|
||||
bool dstFlipViewportY;
|
||||
bool dstLuminance;
|
||||
};
|
||||
|
@ -85,13 +126,16 @@ struct BlitVSOut
|
|||
float2 texCoords [[user(locn1)]];
|
||||
};
|
||||
|
||||
vertex BlitVSOut blitVS(unsigned int vid [[ vertex_id ]],
|
||||
constant BlitParams &options [[buffer(0)]])
|
||||
vertex BlitVSOut blitVS(unsigned int vid [[vertex_id]], constant BlitParams &options [[buffer(0)]])
|
||||
{
|
||||
BlitVSOut output;
|
||||
output.position = float4(gCorners[vid], 0.0, 1.0);
|
||||
output.texCoords = options.srcTexCoords[vid];
|
||||
|
||||
if (options.dstFlipViewportX)
|
||||
{
|
||||
output.position.x = -output.position.x;
|
||||
}
|
||||
if (!options.dstFlipViewportY)
|
||||
{
|
||||
|
||||
|
@ -102,60 +146,109 @@ vertex BlitVSOut blitVS(unsigned int vid [[ vertex_id ]],
|
|||
return output;
|
||||
}
|
||||
|
||||
float4 blitSampleTexture(texture2d<float> srcTexture,
|
||||
float2 texCoords,
|
||||
constant BlitParams &options)
|
||||
static inline float3 cubeTexcoords(float2 texcoords, int face)
|
||||
{
|
||||
constexpr sampler textureSampler(mag_filter::linear,
|
||||
min_filter::linear);
|
||||
float4 output = srcTexture.sample(textureSampler, texCoords, level(options.srcLevel));
|
||||
|
||||
if (options.srcLuminance)
|
||||
texcoords = 2.0 * texcoords - 1.0;
|
||||
switch (face)
|
||||
{
|
||||
output.gb = float2(output.r, output.r);
|
||||
case 0:
|
||||
return float3(1.0, -texcoords.y, -texcoords.x);
|
||||
case 1:
|
||||
return float3(-1.0, -texcoords.y, texcoords.x);
|
||||
case 2:
|
||||
return float3(texcoords.x, 1.0, texcoords.y);
|
||||
case 3:
|
||||
return float3(texcoords.x, -1.0, -texcoords.y);
|
||||
case 4:
|
||||
return float3(texcoords.x, -texcoords.y, 1.0);
|
||||
case 5:
|
||||
return float3(-texcoords.x, -texcoords.y, -1.0);
|
||||
}
|
||||
return float3(texcoords, 0);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline vec<T, 4> blitSampleTextureMS(texture2d_ms<T> srcTexture, float2 texCoords)
|
||||
{
|
||||
uint2 dimens(srcTexture.get_width(), srcTexture.get_height());
|
||||
uint2 coords = uint2(texCoords * float2(dimens));
|
||||
|
||||
uint samples = srcTexture.get_num_samples();
|
||||
|
||||
vec<T, 4> output(0);
|
||||
|
||||
for (uint sample = 0; sample < samples; ++sample)
|
||||
{
|
||||
output += srcTexture.read(coords, sample);
|
||||
}
|
||||
|
||||
output = output / samples;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline vec<T, 4> blitSampleTexture3D(texture3d<T> srcTexture,
|
||||
sampler textureSampler,
|
||||
float2 texCoords,
|
||||
constant BlitParams &options)
|
||||
{
|
||||
uint depth = srcTexture.get_depth(options.srcLevel);
|
||||
float zCoord = (float(options.srcLayer) + 0.5) / float(depth);
|
||||
|
||||
return srcTexture.sample(textureSampler, float3(texCoords, zCoord), level(options.srcLevel));
|
||||
}
|
||||
# 130 "./blit.metal"
|
||||
template <typename T>
|
||||
static inline vec<T, 4> blitReadTexture(BlitVSOut input [[stage_in]], texture2d<T> srcTexture2d [[texture(0), function_constant(kSourceTextureType2D)]], texture2d_array<T> srcTexture2dArray [[texture(0), function_constant(kSourceTextureType2DArray)]], texture2d_ms<T> srcTexture2dMS [[texture(0), function_constant(kSourceTextureType2DMS)]], texturecube<T> srcTextureCube [[texture(0), function_constant(kSourceTextureTypeCube)]], texture3d<T> srcTexture3d [[texture(0), function_constant(kSourceTextureType3D)]], sampler textureSampler [[sampler(0)]], constant BlitParams &options [[buffer(0)]])
|
||||
{
|
||||
vec<T, 4> output;
|
||||
|
||||
switch (kSourceTextureType)
|
||||
{
|
||||
case kTextureType2D:
|
||||
output = srcTexture2d.sample(textureSampler, input.texCoords, level(options.srcLevel));
|
||||
break;
|
||||
case kTextureType2DArray:
|
||||
output = srcTexture2dArray.sample(textureSampler, input.texCoords, options.srcLayer,
|
||||
level(options.srcLevel));
|
||||
break;
|
||||
case kTextureType2DMultisample:
|
||||
output = blitSampleTextureMS(srcTexture2dMS, input.texCoords);
|
||||
break;
|
||||
case kTextureTypeCube:
|
||||
output = srcTextureCube.sample(textureSampler,
|
||||
cubeTexcoords(input.texCoords, options.srcLayer),
|
||||
level(options.srcLevel));
|
||||
break;
|
||||
case kTextureType3D:
|
||||
output = blitSampleTexture3D(srcTexture3d, textureSampler, input.texCoords, options);
|
||||
break;
|
||||
}
|
||||
|
||||
if (kPremultiplyAlpha)
|
||||
{
|
||||
output.xyz *= output.a;
|
||||
}
|
||||
else if (kUnmultiplyAlpha)
|
||||
{
|
||||
if (output.a != 0.0)
|
||||
{
|
||||
output.xyz /= output.a;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.dstLuminance)
|
||||
{
|
||||
output.g = output.b = output.r;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
float4 blitOutput(float4 color, constant BlitParams &options)
|
||||
fragment float4 blitFS(BlitVSOut input [[stage_in]], texture2d<float> srcTexture2d [[texture(0), function_constant(kSourceTextureType2D)]], texture2d_array<float> srcTexture2dArray [[texture(0), function_constant(kSourceTextureType2DArray)]], texture2d_ms<float> srcTexture2dMS [[texture(0), function_constant(kSourceTextureType2DMS)]], texturecube<float> srcTextureCube [[texture(0), function_constant(kSourceTextureTypeCube)]], texture3d<float> srcTexture3d [[texture(0), function_constant(kSourceTextureType3D)]], sampler textureSampler [[sampler(0)]], constant BlitParams &options [[buffer(0)]])
|
||||
{
|
||||
float4 ret = color;
|
||||
|
||||
if (options.dstLuminance)
|
||||
{
|
||||
ret.r = ret.g = ret.b = color.r;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
fragment float4 blitFS(BlitVSOut input [[stage_in]],
|
||||
texture2d<float> srcTexture [[texture(0)]],
|
||||
constant BlitParams &options [[buffer(0)]])
|
||||
{
|
||||
return blitOutput(blitSampleTexture(srcTexture, input.texCoords, options), options);
|
||||
}
|
||||
|
||||
fragment float4 blitPremultiplyAlphaFS(BlitVSOut input [[stage_in]],
|
||||
texture2d<float> srcTexture [[texture(0)]],
|
||||
constant BlitParams &options [[buffer(0)]])
|
||||
{
|
||||
float4 output = blitSampleTexture(srcTexture, input.texCoords, options);
|
||||
output.xyz *= output.a;
|
||||
return blitOutput(output, options);
|
||||
}
|
||||
|
||||
fragment float4 blitUnmultiplyAlphaFS(BlitVSOut input [[stage_in]],
|
||||
texture2d<float> srcTexture [[texture(0)]],
|
||||
constant BlitParams &options [[buffer(0)]])
|
||||
{
|
||||
float4 output = blitSampleTexture(srcTexture, input.texCoords, options);
|
||||
if (output.a != 0.0)
|
||||
{
|
||||
output.xyz *= 1.0 / output.a;
|
||||
}
|
||||
return blitOutput(output, options);
|
||||
return blitReadTexture(input, srcTexture2d, srcTexture2dArray, srcTexture2dMS, srcTextureCube, srcTexture3d, textureSampler, options);
|
||||
}
|
||||
# 11 "master_source.metal" 2
|
||||
# 1 "./gen_indices.metal" 1
|
||||
|
|
|
@ -16,15 +16,39 @@ using namespace angle;
|
|||
|
||||
namespace
|
||||
{
|
||||
class MultisampleTest : public ANGLETest
|
||||
|
||||
using MultisampleTestParams = std::tuple<angle::PlatformParameters, bool>;
|
||||
|
||||
std::string PrintToStringParamName(const ::testing::TestParamInfo<MultisampleTestParams> &info)
|
||||
{
|
||||
::std::stringstream ss;
|
||||
ss << std::get<0>(info.param);
|
||||
if (std::get<1>(info.param))
|
||||
{
|
||||
ss << "__NoStoreAndResolve";
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
class MultisampleTest : public ANGLETestWithParam<MultisampleTestParams>
|
||||
{
|
||||
protected:
|
||||
void testSetUp() override
|
||||
{
|
||||
const angle::PlatformParameters platform = ::testing::get<0>(GetParam());
|
||||
std::vector<const char *> disabledFeatures;
|
||||
if (::testing::get<1>(GetParam()))
|
||||
{
|
||||
disabledFeatures.push_back("allow_msaa_store_and_resolve");
|
||||
}
|
||||
disabledFeatures.push_back(nullptr);
|
||||
|
||||
// Get display.
|
||||
EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(), EGL_NONE};
|
||||
mDisplay = eglGetPlatformDisplayEXT(
|
||||
EGL_PLATFORM_ANGLE_ANGLE, reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
|
||||
EGLAttrib dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, platform.getRenderer(),
|
||||
EGL_FEATURE_OVERRIDES_DISABLED_ANGLE,
|
||||
reinterpret_cast<EGLAttrib>(disabledFeatures.data()), EGL_NONE};
|
||||
mDisplay = eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE,
|
||||
reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
|
||||
ASSERT_TRUE(mDisplay != EGL_NO_DISPLAY);
|
||||
|
||||
ASSERT_TRUE(eglInitialize(mDisplay, nullptr, nullptr) == EGL_TRUE);
|
||||
|
@ -57,9 +81,9 @@ class MultisampleTest : public ANGLETest
|
|||
|
||||
EGLint contextAttributes[] = {
|
||||
EGL_CONTEXT_MAJOR_VERSION_KHR,
|
||||
GetParam().majorVersion,
|
||||
platform.majorVersion,
|
||||
EGL_CONTEXT_MINOR_VERSION_KHR,
|
||||
GetParam().minorVersion,
|
||||
platform.minorVersion,
|
||||
EGL_NONE,
|
||||
};
|
||||
|
||||
|
@ -267,6 +291,90 @@ TEST_P(MultisampleTest, Triangle)
|
|||
}
|
||||
}
|
||||
|
||||
// Test polygon rendering on a multisampled surface. And rendering is interrupted by a compute pass
|
||||
// that converts the index buffer. Make sure the rendering's multisample result is preserved after
|
||||
// interruption.
|
||||
TEST_P(MultisampleTest, ContentPresevedAfterInterruption)
|
||||
{
|
||||
ANGLE_SKIP_TEST_IF(!mMultisampledConfigExists);
|
||||
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_rgb8_rgba8"));
|
||||
// http://anglebug.com/3470
|
||||
ANGLE_SKIP_TEST_IF(IsAndroid() && IsNVIDIAShield() && IsOpenGLES());
|
||||
|
||||
// http://anglebug.com/4609
|
||||
ANGLE_SKIP_TEST_IF(IsD3D11());
|
||||
|
||||
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
|
||||
glUseProgram(program);
|
||||
const GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
|
||||
|
||||
if (IsGLExtensionEnabled("GL_EXT_discard_framebuffer"))
|
||||
{
|
||||
GLenum attachments[] = {GL_COLOR_EXT, GL_DEPTH_EXT, GL_STENCIL_EXT};
|
||||
glDiscardFramebufferEXT(GL_FRAMEBUFFER, 3, attachments);
|
||||
}
|
||||
// Draw triangle
|
||||
GLBuffer vertexBuffer;
|
||||
const Vector3 vertices[3] = {{-1.0f, -1.0f, 0.0f}, {-1.0f, 1.0f, 0.0f}, {1.0f, -1.0f, 0.0f}};
|
||||
prepareVertexBuffer(vertexBuffer, vertices, 3, positionLocation);
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
|
||||
ASSERT_GL_NO_ERROR();
|
||||
|
||||
// Draw a line
|
||||
GLBuffer vertexBuffer2;
|
||||
GLBuffer indexBuffer2;
|
||||
const Vector3 vertices2[2] = {{-1.0f, -0.3f, 0.0f}, {1.0f, 0.3f, 0.0f}};
|
||||
const GLubyte indices2[] = {0, 1};
|
||||
prepareVertexBuffer(vertexBuffer2, vertices2, 2, positionLocation);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer2);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices2), indices2, GL_STATIC_DRAW);
|
||||
|
||||
glDrawElements(GL_LINES, 2, GL_UNSIGNED_BYTE, 0);
|
||||
|
||||
ASSERT_GL_NO_ERROR();
|
||||
|
||||
// Top-left pixels should be all red.
|
||||
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
|
||||
EXPECT_PIXEL_COLOR_EQ(kWindowSize / 4, kWindowSize / 4, GLColor::red);
|
||||
|
||||
// Triangle edge:
|
||||
// Diagonal pixels from bottom-left to top-right are between red and black. Pixels above the
|
||||
// diagonal are red and pixels below it are black.
|
||||
{
|
||||
const GLColor kMidRed = {128, 0, 0, 128};
|
||||
constexpr int kErrorMargin = 16;
|
||||
|
||||
for (int i = 1; i + 1 < kWindowSize; ++i)
|
||||
{
|
||||
// Exclude the middle pixel where the triangle and line cross each other.
|
||||
if (abs(kWindowSize / 2 - i) <= 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int j = kWindowSize - 1 - i;
|
||||
EXPECT_PIXEL_COLOR_NEAR(i, j, kMidRed, kErrorMargin);
|
||||
EXPECT_PIXEL_COLOR_EQ(i, j - 1, GLColor::red);
|
||||
EXPECT_PIXEL_COLOR_EQ(i, j + 1, GLColor::transparentBlack);
|
||||
}
|
||||
}
|
||||
|
||||
// Line edge:
|
||||
{
|
||||
const GLColor kDarkRed = {128, 0, 0, 128};
|
||||
constexpr int kErrorMargin = 16;
|
||||
constexpr int kLargeMargin = 80;
|
||||
|
||||
static_assert(kWindowSize == 8, "Verification code written for 8x8 window");
|
||||
// Exclude the triangle region.
|
||||
EXPECT_PIXEL_COLOR_NEAR(5, 4, GLColor::red, kErrorMargin);
|
||||
EXPECT_PIXEL_COLOR_NEAR(6, 4, GLColor::red, kLargeMargin);
|
||||
EXPECT_PIXEL_COLOR_NEAR(7, 5, kDarkRed, kLargeMargin);
|
||||
}
|
||||
}
|
||||
|
||||
// Test that resolve from multisample default framebuffer works.
|
||||
TEST_P(MultisampleTestES3, ResolveToFBO)
|
||||
{
|
||||
|
@ -305,26 +413,41 @@ TEST_P(MultisampleTestES3, ResolveToFBO)
|
|||
EXPECT_PIXEL_COLOR_NEAR(kWindowSize / 2, kWindowSize / 2, kResult, 1);
|
||||
}
|
||||
|
||||
ANGLE_INSTANTIATE_TEST(MultisampleTest,
|
||||
WithNoFixture(ES2_D3D11()),
|
||||
WithNoFixture(ES3_D3D11()),
|
||||
WithNoFixture(ES31_D3D11()),
|
||||
WithNoFixture(ES2_OPENGL()),
|
||||
WithNoFixture(ES3_OPENGL()),
|
||||
WithNoFixture(ES31_OPENGL()),
|
||||
WithNoFixture(ES2_OPENGLES()),
|
||||
WithNoFixture(ES3_OPENGLES()),
|
||||
WithNoFixture(ES31_OPENGLES()),
|
||||
WithNoFixture(ES2_VULKAN()),
|
||||
WithNoFixture(ES3_VULKAN()),
|
||||
WithNoFixture(ES31_VULKAN()));
|
||||
ANGLE_INSTANTIATE_TEST(MultisampleTestES3,
|
||||
WithNoFixture(ES3_D3D11()),
|
||||
WithNoFixture(ES31_D3D11()),
|
||||
WithNoFixture(ES3_OPENGL()),
|
||||
WithNoFixture(ES31_OPENGL()),
|
||||
WithNoFixture(ES3_OPENGLES()),
|
||||
WithNoFixture(ES31_OPENGLES()),
|
||||
WithNoFixture(ES3_VULKAN()),
|
||||
WithNoFixture(ES31_VULKAN()));
|
||||
ANGLE_INSTANTIATE_TEST_COMBINE_1(MultisampleTest,
|
||||
PrintToStringParamName,
|
||||
testing::Values(false),
|
||||
WithNoFixture(ES2_D3D11()),
|
||||
WithNoFixture(ES3_D3D11()),
|
||||
WithNoFixture(ES31_D3D11()),
|
||||
WithNoFixture(ES2_METAL()),
|
||||
WithNoFixture(ES2_OPENGL()),
|
||||
WithNoFixture(ES3_OPENGL()),
|
||||
WithNoFixture(ES31_OPENGL()),
|
||||
WithNoFixture(ES2_OPENGLES()),
|
||||
WithNoFixture(ES3_OPENGLES()),
|
||||
WithNoFixture(ES31_OPENGLES()),
|
||||
WithNoFixture(ES2_VULKAN()),
|
||||
WithNoFixture(ES3_VULKAN()),
|
||||
WithNoFixture(ES31_VULKAN()));
|
||||
|
||||
namespace store_and_resolve_feature_off
|
||||
{
|
||||
// Simulate missing msaa auto resolve feature in Metal.
|
||||
ANGLE_INSTANTIATE_TEST_COMBINE_1(MultisampleTest,
|
||||
PrintToStringParamName,
|
||||
testing::Values(true),
|
||||
WithNoFixture(ES2_METAL()));
|
||||
} // namespace store_and_resolve_feature_off
|
||||
|
||||
ANGLE_INSTANTIATE_TEST_COMBINE_1(MultisampleTestES3,
|
||||
PrintToStringParamName,
|
||||
testing::Values(false),
|
||||
WithNoFixture(ES3_D3D11()),
|
||||
WithNoFixture(ES31_D3D11()),
|
||||
WithNoFixture(ES3_OPENGL()),
|
||||
WithNoFixture(ES31_OPENGL()),
|
||||
WithNoFixture(ES3_OPENGLES()),
|
||||
WithNoFixture(ES31_OPENGLES()),
|
||||
WithNoFixture(ES3_VULKAN()),
|
||||
WithNoFixture(ES31_VULKAN()));
|
||||
} // anonymous namespace
|
||||
|
|
Загрузка…
Ссылка в новой задаче