Source code

Revision control

Copy as Markdown

Other Tools

//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// mtl_state_cache.mm:
// Implements StateCache, RenderPipelineCache and various
// C struct versions of Metal sampler, depth stencil, render pass, render pipeline descriptors.
//
#include "libANGLE/renderer/metal/mtl_state_cache.h"
#include <sstream>
#include "common/debug.h"
#include "common/span.h"
#include "common/unsafe_buffers.h"
#include "libANGLE/renderer/metal/ContextMtl.h"
#include "libANGLE/renderer/metal/mtl_resources.h"
#include "libANGLE/renderer/metal/mtl_utils.h"
#include "platform/autogen/FeaturesMtl_autogen.h"
namespace rx
{
namespace mtl
{
namespace
{
inline angle::ObjCPtr<MTLStencilDescriptor> ToObjC(const StencilDesc &desc)
{
auto objCDesc = angle::adoptObjCPtr([[MTLStencilDescriptor alloc] init]);
objCDesc.get().stencilFailureOperation = desc.getStencilFailureOperation();
objCDesc.get().depthFailureOperation = desc.getDepthFailureOperation();
objCDesc.get().depthStencilPassOperation = desc.getDepthStencilPassOperation();
objCDesc.get().stencilCompareFunction = desc.getStencilCompareFunction();
objCDesc.get().readMask = desc.readMask;
objCDesc.get().writeMask = desc.writeMask;
return objCDesc;
}
inline angle::ObjCPtr<MTLDepthStencilDescriptor> ToObjC(const DepthStencilDesc &desc)
{
auto objCDesc = angle::adoptObjCPtr([[MTLDepthStencilDescriptor alloc] init]);
objCDesc.get().backFaceStencil = ToObjC(desc.backFaceStencil);
objCDesc.get().frontFaceStencil = ToObjC(desc.frontFaceStencil);
objCDesc.get().depthCompareFunction = desc.getDepthCompareFunction();
objCDesc.get().depthWriteEnabled = desc.isDepthWriteEnabled();
return objCDesc;
}
inline angle::ObjCPtr<MTLSamplerDescriptor> ToObjC(const SamplerDesc &desc)
{
auto objCDesc = angle::adoptObjCPtr([[MTLSamplerDescriptor alloc] init]);
objCDesc.get().rAddressMode = desc.getRAddressMode();
objCDesc.get().sAddressMode = desc.getSAddressMode();
objCDesc.get().tAddressMode = desc.getTAddressMode();
objCDesc.get().minFilter = desc.getMinFilter();
objCDesc.get().magFilter = desc.getMagFilter();
objCDesc.get().mipFilter = desc.getMipFilter();
objCDesc.get().maxAnisotropy = desc.getMaxAnisotropy();
objCDesc.get().compareFunction = desc.getCompareFunction();
return objCDesc;
}
inline angle::ObjCPtr<MTLVertexAttributeDescriptor> ToObjC(const VertexAttributeDesc &desc)
{
auto objCDesc = angle::adoptObjCPtr([[MTLVertexAttributeDescriptor alloc] init]);
objCDesc.get().format = desc.getFormat();
objCDesc.get().offset = desc.getOffset();
objCDesc.get().bufferIndex = desc.getBufferIndex();
ASSERT(desc.getBufferIndex() >= kVboBindingIndexStart);
return objCDesc;
}
inline angle::ObjCPtr<MTLVertexBufferLayoutDescriptor> ToObjC(const VertexBufferLayoutDesc &desc)
{
auto objCDesc = angle::adoptObjCPtr([[MTLVertexBufferLayoutDescriptor alloc] init]);
objCDesc.get().stepFunction = desc.getStepFunction();
objCDesc.get().stepRate = desc.stepRate;
objCDesc.get().stride = desc.stride;
return objCDesc;
}
inline angle::ObjCPtr<MTLVertexDescriptor> ToObjC(const VertexDesc &desc)
{
auto objCDesc = angle::adoptObjCPtr([[MTLVertexDescriptor alloc] init]);
[objCDesc reset];
for (uint8_t i = 0; i < desc.numAttribs; ++i)
{
[objCDesc.get().attributes setObject:ToObjC(ANGLE_UNSAFE_TODO(desc.attributes[i]))
atIndexedSubscript:i];
}
for (uint8_t i = 0; i < desc.numBufferLayouts; ++i)
{
// Ignore if stepFunction is kVertexStepFunctionInvalid.
// If we don't set this slot, it will apparently be disabled by metal runtime.
if (ANGLE_UNSAFE_TODO(desc.layouts[i].getStepFunction()) != kVertexStepFunctionInvalid)
{
[objCDesc.get().layouts setObject:ToObjC(ANGLE_UNSAFE_TODO(desc.layouts[i]))
atIndexedSubscript:i];
}
}
return objCDesc;
}
inline angle::ObjCPtr<MTLRenderPipelineColorAttachmentDescriptor> ToObjC(
const RenderPipelineColorAttachmentDesc &desc)
{
auto objCDesc = angle::adoptObjCPtr([[MTLRenderPipelineColorAttachmentDescriptor alloc] init]);
objCDesc.get().pixelFormat = desc.getPixelFormat();
objCDesc.get().writeMask = desc.getWriteMask();
objCDesc.get().sourceRGBBlendFactor = desc.getSourceRgbBlendFactor();
objCDesc.get().sourceAlphaBlendFactor = desc.getSourceAlphaBlendFactor();
objCDesc.get().destinationRGBBlendFactor = desc.getDestinationRgbBlendFactor();
objCDesc.get().destinationAlphaBlendFactor = desc.getDestinationAlphaBlendFactor();
objCDesc.get().rgbBlendOperation = desc.getRgbBlendOperation();
objCDesc.get().alphaBlendOperation = desc.getAlphaBlendOperation();
objCDesc.get().blendingEnabled = desc.isBlendingEnabled();
return objCDesc;
}
id<MTLTexture> ToObjC(const TextureRef &texture)
{
auto textureRef = texture;
return textureRef ? textureRef->get() : nil;
}
void BaseRenderPassAttachmentDescToObjC(const RenderPassAttachmentDesc &src,
MTLRenderPassAttachmentDescriptor *dst)
{
const TextureRef &implicitMsTexture = src.implicitMSTexture;
if (implicitMsTexture)
{
dst.texture = ToObjC(implicitMsTexture);
dst.level = 0;
dst.slice = 0;
dst.depthPlane = 0;
dst.resolveTexture = ToObjC(src.texture);
dst.resolveLevel = src.level.get();
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.get();
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;
dst.resolveDepthPlane = 0;
}
dst.loadAction = src.loadAction;
dst.storeAction = src.storeAction;
dst.storeActionOptions = src.storeActionOptions;
}
void ToObjC(const RenderPassColorAttachmentDesc &desc,
MTLRenderPassColorAttachmentDescriptor *objCDesc)
{
BaseRenderPassAttachmentDescToObjC(desc, objCDesc);
objCDesc.clearColor = desc.clearColor;
}
void ToObjC(const RenderPassDepthAttachmentDesc &desc,
MTLRenderPassDepthAttachmentDescriptor *objCDesc)
{
BaseRenderPassAttachmentDescToObjC(desc, objCDesc);
objCDesc.clearDepth = desc.clearDepth;
}
void ToObjC(const RenderPassStencilAttachmentDesc &desc,
MTLRenderPassStencilAttachmentDescriptor *objCDesc)
{
BaseRenderPassAttachmentDescToObjC(desc, objCDesc);
objCDesc.clearStencil = desc.clearStencil;
}
} // namespace
void DepthStencilDesc::updateDepthTestEnabled(const gl::DepthStencilState &dsState)
{
if (!dsState.depthTest)
{
setDepthWriteDisabled();
}
else
{
updateDepthCompareFunc(dsState);
updateDepthWriteEnabled(dsState);
}
}
void DepthStencilDesc::updateDepthWriteEnabled(const gl::DepthStencilState &dsState)
{
mDepthWriteEnabled = dsState.depthTest && dsState.depthMask;
}
void DepthStencilDesc::updateDepthCompareFunc(const gl::DepthStencilState &dsState)
{
if (!dsState.depthTest)
{
return;
}
mDepthCompareFunction = static_cast<uint32_t>(GetCompareFunc(dsState.depthFunc));
}
void DepthStencilDesc::updateStencilTestEnabled(const gl::DepthStencilState &dsState)
{
if (!dsState.stencilTest)
{
frontFaceStencil.setStencilCompareFunction(MTLCompareFunctionAlways);
frontFaceStencil.setDepthFailureOperation(MTLStencilOperationKeep);
frontFaceStencil.setDepthStencilPassOperation(MTLStencilOperationKeep);
frontFaceStencil.writeMask = 0;
backFaceStencil.setStencilCompareFunction(MTLCompareFunctionAlways);
backFaceStencil.setDepthFailureOperation(MTLStencilOperationKeep);
backFaceStencil.setDepthStencilPassOperation(MTLStencilOperationKeep);
backFaceStencil.writeMask = 0;
}
else
{
updateStencilFrontFuncs(dsState);
updateStencilFrontOps(dsState);
updateStencilFrontWriteMask(dsState);
updateStencilBackFuncs(dsState);
updateStencilBackOps(dsState);
updateStencilBackWriteMask(dsState);
}
}
void DepthStencilDesc::updateStencilFrontOps(const gl::DepthStencilState &dsState)
{
if (!dsState.stencilTest)
{
return;
}
frontFaceStencil.setStencilFailureOperation(GetStencilOp(dsState.stencilFail));
frontFaceStencil.setDepthFailureOperation(GetStencilOp(dsState.stencilPassDepthFail));
frontFaceStencil.setDepthStencilPassOperation(GetStencilOp(dsState.stencilPassDepthPass));
}
void DepthStencilDesc::updateStencilBackOps(const gl::DepthStencilState &dsState)
{
if (!dsState.stencilTest)
{
return;
}
backFaceStencil.setStencilFailureOperation(GetStencilOp(dsState.stencilBackFail));
backFaceStencil.setDepthFailureOperation(GetStencilOp(dsState.stencilBackPassDepthFail));
backFaceStencil.setDepthStencilPassOperation(GetStencilOp(dsState.stencilBackPassDepthPass));
}
void DepthStencilDesc::updateStencilFrontFuncs(const gl::DepthStencilState &dsState)
{
if (!dsState.stencilTest)
{
return;
}
frontFaceStencil.setStencilCompareFunction(GetCompareFunc(dsState.stencilFunc));
frontFaceStencil.readMask = dsState.stencilMask & mtl::kStencilMaskAll;
}
void DepthStencilDesc::updateStencilBackFuncs(const gl::DepthStencilState &dsState)
{
if (!dsState.stencilTest)
{
return;
}
backFaceStencil.setStencilCompareFunction(GetCompareFunc(dsState.stencilBackFunc));
backFaceStencil.readMask = dsState.stencilBackMask & mtl::kStencilMaskAll;
}
void DepthStencilDesc::updateStencilFrontWriteMask(const gl::DepthStencilState &dsState)
{
if (!dsState.stencilTest)
{
return;
}
frontFaceStencil.writeMask = dsState.stencilWritemask & mtl::kStencilMaskAll;
}
void DepthStencilDesc::updateStencilBackWriteMask(const gl::DepthStencilState &dsState)
{
if (!dsState.stencilTest)
{
return;
}
backFaceStencil.writeMask = dsState.stencilBackWritemask & mtl::kStencilMaskAll;
}
SamplerDesc::SamplerDesc(const gl::SamplerState &glState)
: mRAddressMode(static_cast<uint32_t>(GetSamplerAddressMode(glState.getWrapR()))),
mSAddressMode(static_cast<uint32_t>(GetSamplerAddressMode(glState.getWrapS()))),
mTAddressMode(static_cast<uint32_t>(GetSamplerAddressMode(glState.getWrapT()))),
mMinFilter(static_cast<uint32_t>(GetFilter(glState.getMinFilter()))),
mMagFilter(static_cast<uint32_t>(GetFilter(glState.getMagFilter()))),
mMipFilter(static_cast<uint32_t>(GetMipmapFilter(glState.getMinFilter()))),
mMaxAnisotropy(static_cast<uint32_t>(glState.getMaxAnisotropy())),
mCompareFunction(static_cast<uint32_t>(GetCompareFunc(glState.getCompareFunc())))
{
}
void BlendDesc::updateWriteMask(uint8_t angleMask)
{
ASSERT(angleMask == (angleMask & 0xF));
// ANGLE's packed color mask is abgr (matches Vulkan & D3D11), while Metal expects rgba.
#if defined(__aarch64__)
// ARM64 can reverse bits in a single instruction
mWriteMask = __builtin_bitreverse8(angleMask) >> 4;
#else
/* On other architectures, Clang generates a polyfill that uses more
instructions than the following expression optimized for a 4-bit value.
(abgr * 0x41) & 0x14A:
.......abgr +
.abgr...... &
00101001010 =
..b.r..a.g.
(b.r..a.g.) * 0x111:
b.r..a.g. +
b.r..a.g..... +
b.r..a.g......... =
b.r.bargbarg.a.g.
^^^^
*/
mWriteMask = ((((angleMask * 0x41) & 0x14A) * 0x111) >> 7) & 0xF;
#endif
}
void RenderPipelineColorAttachmentDesc::reset(MTLPixelFormat format, MTLColorWriteMask writeMask)
{
// Reset the entire BlendDesc to defaults (blendingEnabled=false, default factors/operations),
// then set the specific values.
BlendDesc::operator=({});
mWriteMask = static_cast<uint32_t>(writeMask);
mPixelFormat = static_cast<uint32_t>(format);
}
void RenderPipelineColorAttachmentDesc::reset(MTLPixelFormat format, const BlendDesc &blendDesc)
{
BlendDesc::operator=(blendDesc);
mPixelFormat = static_cast<uint32_t>(format);
}
bool RenderPipelineOutputDesc::operator==(const RenderPipelineOutputDesc &rhs) const
{
if (mNumColorAttachments != rhs.mNumColorAttachments)
{
return false;
}
for (uint8_t i = 0; i < mNumColorAttachments; ++i)
{
if (colorAttachments[i] != rhs.colorAttachments[i])
{
return false;
}
}
return mDepthAttachmentPixelFormat == rhs.mDepthAttachmentPixelFormat &&
mStencilAttachmentPixelFormat == rhs.mStencilAttachmentPixelFormat &&
mRasterSampleCount == rhs.mRasterSampleCount;
}
size_t RenderPipelineOutputDesc::hash() const
{
size_t hash = 0;
angle::HashCombine(hash, mNumColorAttachments);
for (uint8_t i = 0; i < mNumColorAttachments; ++i)
{
angle::HashCombine(hash, colorAttachments[i]);
}
angle::HashCombine(hash, mDepthAttachmentPixelFormat);
angle::HashCombine(hash, mStencilAttachmentPixelFormat);
angle::HashCombine(hash, mRasterSampleCount);
return hash;
}
void RenderPipelineOutputDesc::updateEnabledDrawBuffers(gl::DrawBufferMask enabledBuffers)
{
for (uint32_t colorIndex = 0; colorIndex < mNumColorAttachments; ++colorIndex)
{
if (!enabledBuffers.test(colorIndex))
{
colorAttachments[colorIndex].setWriteMask(MTLColorWriteMaskNone);
}
}
}
bool RenderPipelineDesc::rasterizationEnabled() const
{
return mRasterizationType != static_cast<uint32_t>(RenderPipelineRasterization::Disabled);
}
angle::ObjCPtr<MTLRenderPipelineDescriptor> RenderPipelineDesc::createMetalDesc(
id<MTLFunction> vertexShader,
id<MTLFunction> fragmentShader) const
{
auto objCDesc = angle::adoptObjCPtr([[MTLRenderPipelineDescriptor alloc] init]);
[objCDesc reset];
objCDesc.get().vertexDescriptor = ToObjC(vertexDescriptor);
for (uint8_t i = 0; i < outputDescriptor.getNumColorAttachments(); ++i)
{
[objCDesc.get().colorAttachments setObject:ToObjC(outputDescriptor.colorAttachments[i])
atIndexedSubscript:i];
}
objCDesc.get().depthAttachmentPixelFormat = outputDescriptor.getDepthAttachmentPixelFormat();
objCDesc.get().stencilAttachmentPixelFormat =
outputDescriptor.getStencilAttachmentPixelFormat();
objCDesc.get().rasterSampleCount = outputDescriptor.getRasterSampleCount();
objCDesc.get().inputPrimitiveTopology = getInputPrimitiveTopology();
objCDesc.get().alphaToCoverageEnabled = getAlphaToCoverageEnabled();
// rasterizationEnabled will be true for both EmulatedDiscard & Enabled.
objCDesc.get().rasterizationEnabled = rasterizationEnabled();
objCDesc.get().vertexFunction = vertexShader;
objCDesc.get().fragmentFunction = objCDesc.get().rasterizationEnabled ? fragmentShader : nil;
return objCDesc;
}
bool RenderPassAttachmentDesc::equalIgnoreLoadStoreOptions(
const RenderPassAttachmentDesc &other) const
{
return texture == other.texture && implicitMSTexture == other.implicitMSTexture &&
level == other.level && sliceOrDepth == other.sliceOrDepth &&
blendable == other.blendable;
}
bool RenderPassAttachmentDesc::operator==(const RenderPassAttachmentDesc &other) const
{
if (!equalIgnoreLoadStoreOptions(other))
{
return false;
}
return loadAction == other.loadAction && storeAction == other.storeAction &&
storeActionOptions == other.storeActionOptions;
}
void RenderPassDesc::populateRenderPipelineOutputDesc(RenderPipelineOutputDesc *outDesc) const
{
WriteMaskArray writeMaskArray;
writeMaskArray.fill(MTLColorWriteMaskAll);
populateRenderPipelineOutputDesc(writeMaskArray, outDesc);
}
void RenderPassDesc::populateRenderPipelineOutputDesc(const WriteMaskArray &writeMaskArray,
RenderPipelineOutputDesc *outDesc) const
{
// Default blend state with replaced color write masks.
BlendDescArray blendDescArray;
for (size_t i = 0; i < blendDescArray.size(); i++)
{
blendDescArray[i].reset(writeMaskArray[i]);
}
populateRenderPipelineOutputDesc(blendDescArray, outDesc);
}
void RenderPassDesc::populateRenderPipelineOutputDesc(const BlendDescArray &blendDescArray,
RenderPipelineOutputDesc *outDesc) const
{
outDesc->setNumColorAttachments(numColorAttachments);
outDesc->setRasterSampleCount(rasterSampleCount);
for (uint32_t i = 0; i < this->numColorAttachments; ++i)
{
auto &renderPassColorAttachment = colorAttachments[i];
auto texture = renderPassColorAttachment.texture;
auto &outColorAttachment = outDesc->colorAttachments[i];
if (texture)
{
if (renderPassColorAttachment.blendable &&
blendDescArray[i].getWriteMask() != MTLColorWriteMaskNone)
{
// Copy parameters from blend state
outColorAttachment.reset(texture->pixelFormat(), blendDescArray[i]);
}
else
{
// Disable blending if the attachment's render target doesn't support blending
// or if all its color channels are masked out. The latter is needed because:
//
// * When blending is enabled and *Source1* blend factors are used, Metal
// requires a fragment shader to bind both primary and secondary outputs
//
// * ANGLE frontend validation allows draw calls on draw buffers without
// bound fragment outputs if all their color channels are masked out
//
// * When all color channels are masked out, blending has no effect anyway
//
// Besides disabling blending, use default values for factors and
// operations to reduce the number of unique pipeline states.
outColorAttachment.reset(texture->pixelFormat(), blendDescArray[i].getWriteMask());
}
// Combine the masks. This is useful when the texture is not supposed to have alpha
// channel such as GL_RGB8, however, Metal doesn't natively support 24 bit RGB, so
// we need to use RGBA texture, and then disable alpha write to this texture
outColorAttachment.setWriteMask(outColorAttachment.getWriteMask() &
texture->getColorWritableMask());
}
else
{
outColorAttachment.setBlendingDisabled();
outColorAttachment.setPixelFormat(MTLPixelFormatInvalid);
}
}
// Reset the unused output slots to ensure consistent hash value
for (uint32_t i = this->numColorAttachments; i < outDesc->colorAttachments.size(); ++i)
{
outDesc->colorAttachments[i] = {};
}
auto depthTexture = depthAttachment.texture;
outDesc->setDepthAttachmentPixelFormat(depthTexture ? depthTexture->pixelFormat()
: MTLPixelFormatInvalid);
auto stencilTexture = stencilAttachment.texture;
outDesc->setStencilAttachmentPixelFormat(stencilTexture ? stencilTexture->pixelFormat()
: MTLPixelFormatInvalid);
}
bool RenderPassDesc::equalIgnoreLoadStoreOptions(const RenderPassDesc &other) const
{
if (numColorAttachments != other.numColorAttachments)
{
return false;
}
for (uint32_t i = 0; i < numColorAttachments; ++i)
{
auto &renderPassColorAttachment = colorAttachments[i];
auto &otherRPAttachment = other.colorAttachments[i];
if (!renderPassColorAttachment.equalIgnoreLoadStoreOptions(otherRPAttachment))
{
return false;
}
}
if (defaultWidth != other.defaultWidth || defaultHeight != other.defaultHeight)
{
return false;
}
if (rasterSampleCount != other.rasterSampleCount)
{
return false;
}
return depthAttachment.equalIgnoreLoadStoreOptions(other.depthAttachment) &&
stencilAttachment.equalIgnoreLoadStoreOptions(other.stencilAttachment);
}
bool RenderPassDesc::operator==(const RenderPassDesc &other) const
{
if (numColorAttachments != other.numColorAttachments)
{
return false;
}
for (uint32_t i = 0; i < numColorAttachments; ++i)
{
if (colorAttachments[i] != other.colorAttachments[i])
{
return false;
}
}
return depthAttachment == other.depthAttachment &&
stencilAttachment == other.stencilAttachment && defaultWidth == other.defaultWidth &&
defaultHeight == other.defaultHeight && rasterSampleCount == other.rasterSampleCount;
}
// Convert to Metal object
void RenderPassDesc::convertToMetalDesc(MTLRenderPassDescriptor *objCDesc,
uint32_t deviceMaxRenderTargets) const
{
ASSERT(deviceMaxRenderTargets <= kMaxRenderTargets);
for (uint32_t i = 0; i < numColorAttachments; ++i)
{
ToObjC(colorAttachments[i], objCDesc.colorAttachments[i]);
}
for (uint32_t i = numColorAttachments; i < deviceMaxRenderTargets; ++i)
{
// Inactive render target
objCDesc.colorAttachments[i].texture = nil;
objCDesc.colorAttachments[i].level = 0;
objCDesc.colorAttachments[i].slice = 0;
objCDesc.colorAttachments[i].depthPlane = 0;
objCDesc.colorAttachments[i].loadAction = MTLLoadActionDontCare;
objCDesc.colorAttachments[i].storeAction = MTLStoreActionDontCare;
}
ToObjC(depthAttachment, objCDesc.depthAttachment);
ToObjC(stencilAttachment, objCDesc.stencilAttachment);
if ((defaultWidth | defaultHeight) != 0)
{
objCDesc.renderTargetWidth = defaultWidth;
objCDesc.renderTargetHeight = defaultHeight;
objCDesc.defaultRasterSampleCount = 1;
}
else
{
objCDesc.renderTargetWidth = 0;
objCDesc.renderTargetHeight = 0;
// No need to reset defaultRasterSampleCount as it is only applied when no attachments are
// provided.
}
}
// StateCache implementation
StateCache::StateCache(const angle::FeaturesMtl &features) : mFeatures(features) {}
StateCache::~StateCache() {}
angle::ObjCPtr<id<MTLDepthStencilState>> StateCache::getNullDepthStencilState(
const mtl::ContextDevice &device)
{
if (!mNullDepthStencilState)
{
DepthStencilDesc desc(MTLCompareFunctionAlways, false);
ASSERT(desc.frontFaceStencil.getStencilCompareFunction() == MTLCompareFunctionAlways);
mNullDepthStencilState = getDepthStencilState(device, desc);
}
return mNullDepthStencilState;
}
angle::ObjCPtr<id<MTLDepthStencilState>> StateCache::getDepthStencilState(
const mtl::ContextDevice &device,
const DepthStencilDesc &desc)
{
auto ite = mDepthStencilStates.find(desc);
if (ite == mDepthStencilStates.end())
{
auto re = mDepthStencilStates.insert(
std::make_pair(desc, device.newDepthStencilStateWithDescriptor(ToObjC(desc))));
if (!re.second)
{
return nil;
}
ite = re.first;
}
return ite->second;
}
angle::ObjCPtr<id<MTLSamplerState>> StateCache::getSamplerState(const mtl::ContextDevice &device,
const SamplerDesc &desc)
{
auto ite = mSamplerStates.find(desc);
if (ite == mSamplerStates.end())
{
auto objCDesc = ToObjC(desc);
if (!mFeatures.allowRuntimeSamplerCompareMode.enabled)
{
// Runtime sampler compare mode is not supported, fallback to never.
objCDesc.get().compareFunction = MTLCompareFunctionNever;
}
auto re = mSamplerStates.insert(
std::make_pair(desc, device.newSamplerStateWithDescriptor(objCDesc)));
if (!re.second)
return nil;
ite = re.first;
}
return ite->second;
}
angle::ObjCPtr<id<MTLSamplerState>> StateCache::getNullSamplerState(ContextMtl *context)
{
return getNullSamplerState(context->getMetalDevice());
}
angle::ObjCPtr<id<MTLSamplerState>> StateCache::getNullSamplerState(
const mtl::ContextDevice &device)
{
SamplerDesc desc;
return getSamplerState(device, desc);
}
void StateCache::clear()
{
mNullDepthStencilState = nil;
mDepthStencilStates.clear();
mSamplerStates.clear();
}
} // namespace mtl
} // namespace rx