blob: 3994f38ea06ee382187e7f21cfcd5dcdc61acab9 [file] [log] [blame]
//
// 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.h:
// Defines the class interface for StateCache, RenderPipelineCache and various
// C struct versions of Metal sampler, depth stencil, render pass, render pipeline descriptors.
//
#ifndef LIBANGLE_RENDERER_METAL_MTL_STATE_CACHE_H_
#define LIBANGLE_RENDERER_METAL_MTL_STATE_CACHE_H_
#import <Metal/Metal.h>
#include <unordered_map>
#include "libANGLE/State.h"
#include "libANGLE/angletypes.h"
#include "libANGLE/renderer/metal/mtl_common.h"
#include "libANGLE/renderer/metal/mtl_resources.h"
static inline bool operator==(const MTLClearColor &lhs, const MTLClearColor &rhs);
namespace rx
{
class ContextMtl;
namespace mtl
{
struct StencilDesc
{
bool operator==(const StencilDesc &rhs) const;
// Set default values
void reset();
MTLStencilOperation stencilFailureOperation;
MTLStencilOperation depthFailureOperation;
MTLStencilOperation depthStencilPassOperation;
MTLCompareFunction stencilCompareFunction;
uint32_t readMask;
uint32_t writeMask;
};
struct DepthStencilDesc
{
DepthStencilDesc();
bool operator==(const DepthStencilDesc &rhs) const;
// Set default values.
// Default is depth/stencil test disabled. Depth/stencil write enabled.
void reset();
size_t hash() const;
void updateDepthTestEnabled(const gl::DepthStencilState &dsState);
void updateDepthWriteEnabled(const gl::DepthStencilState &dsState);
void updateDepthCompareFunc(const gl::DepthStencilState &dsState);
void updateStencilTestEnabled(const gl::DepthStencilState &dsState);
void updateStencilFrontOps(const gl::DepthStencilState &dsState);
void updateStencilBackOps(const gl::DepthStencilState &dsState);
void updateStencilFrontFuncs(const gl::DepthStencilState &dsState);
void updateStencilBackFuncs(const gl::DepthStencilState &dsState);
void updateStencilFrontWriteMask(const gl::DepthStencilState &dsState);
void updateStencilBackWriteMask(const gl::DepthStencilState &dsState);
StencilDesc backFaceStencil;
StencilDesc frontFaceStencil;
MTLCompareFunction depthCompareFunction;
bool depthWriteEnabled;
};
struct SamplerDesc
{
SamplerDesc();
explicit SamplerDesc(const gl::SamplerState &glState);
// Set default values. All filters are nearest, and addresModes are clamp to edge.
void reset();
bool operator==(const SamplerDesc &rhs) const;
size_t hash() const;
MTLSamplerAddressMode rAddressMode;
MTLSamplerAddressMode sAddressMode;
MTLSamplerAddressMode tAddressMode;
MTLSamplerMinMagFilter minFilter;
MTLSamplerMinMagFilter magFilter;
MTLSamplerMipFilter mipFilter;
uint32_t maxAnisotropy;
};
struct VertexAttributeDesc
{
inline bool operator==(const VertexAttributeDesc &rhs) const
{
return format == rhs.format && offset == rhs.offset && bufferIndex == rhs.bufferIndex;
}
inline bool operator!=(const VertexAttributeDesc &rhs) const { return !(*this == rhs); }
MTLVertexFormat format;
NSUInteger offset;
NSUInteger bufferIndex;
};
struct VertexBufferLayoutDesc
{
inline bool operator==(const VertexBufferLayoutDesc &rhs) const
{
return stepFunction == rhs.stepFunction && stepRate == rhs.stepRate && stride == rhs.stride;
}
inline bool operator!=(const VertexBufferLayoutDesc &rhs) const { return !(*this == rhs); }
MTLVertexStepFunction stepFunction;
NSUInteger stepRate;
NSUInteger stride;
};
struct VertexDesc
{
VertexAttributeDesc attributes[kMaxVertexAttribs];
VertexBufferLayoutDesc layouts[kMaxVertexAttribs];
uint8_t numAttribs;
uint8_t numBufferLayouts;
};
struct BlendDesc
{
bool operator==(const BlendDesc &rhs) const;
BlendDesc &operator=(const BlendDesc &src) = default;
// Set default values
void reset();
void reset(MTLColorWriteMask writeMask);
void updateWriteMask(const gl::BlendState &blendState);
void updateBlendFactors(const gl::BlendState &blendState);
void updateBlendOps(const gl::BlendState &blendState);
void updateBlendEnabled(const gl::BlendState &blendState);
MTLColorWriteMask writeMask;
MTLBlendOperation alphaBlendOperation;
MTLBlendOperation rgbBlendOperation;
MTLBlendFactor destinationAlphaBlendFactor;
MTLBlendFactor destinationRGBBlendFactor;
MTLBlendFactor sourceAlphaBlendFactor;
MTLBlendFactor sourceRGBBlendFactor;
bool blendingEnabled;
};
struct RenderPipelineColorAttachmentDesc : public BlendDesc
{
bool operator==(const RenderPipelineColorAttachmentDesc &rhs) const;
inline bool operator!=(const RenderPipelineColorAttachmentDesc &rhs) const
{
return !(*this == rhs);
}
// Set default values
void reset();
void reset(MTLPixelFormat format);
void reset(MTLPixelFormat format, MTLColorWriteMask writeMask);
void reset(MTLPixelFormat format, const BlendDesc &blendState);
void update(const BlendDesc &blendState);
MTLPixelFormat pixelFormat;
};
struct RenderPipelineOutputDesc
{
bool operator==(const RenderPipelineOutputDesc &rhs) const;
RenderPipelineColorAttachmentDesc colorAttachments[kMaxRenderTargets];
MTLPixelFormat depthAttachmentPixelFormat;
MTLPixelFormat stencilAttachmentPixelFormat;
uint8_t numColorAttachments;
};
// Some SDK levels don't declare MTLPrimitiveTopologyClass. Needs to do compile time check here:
#if !(TARGET_OS_OSX || TARGET_OS_MACCATALYST) && ANGLE_IOS_DEPLOY_TARGET < __IPHONE_12_0
# define ANGLE_MTL_PRIMITIVE_TOPOLOGY_CLASS_AVAILABLE 0
using PrimitiveTopologyClass = uint32_t;
constexpr PrimitiveTopologyClass kPrimitiveTopologyClassTriangle = 0;
constexpr PrimitiveTopologyClass kPrimitiveTopologyClassPoint = 0;
#else
# define ANGLE_MTL_PRIMITIVE_TOPOLOGY_CLASS_AVAILABLE 1
using PrimitiveTopologyClass = MTLPrimitiveTopologyClass;
constexpr PrimitiveTopologyClass kPrimitiveTopologyClassTriangle =
MTLPrimitiveTopologyClassTriangle;
constexpr PrimitiveTopologyClass kPrimitiveTopologyClassPoint = MTLPrimitiveTopologyClassPoint;
#endif
struct RenderPipelineDesc
{
RenderPipelineDesc();
bool operator==(const RenderPipelineDesc &rhs) const;
size_t hash() const;
VertexDesc vertexDescriptor;
RenderPipelineOutputDesc outputDescriptor;
PrimitiveTopologyClass inputPrimitiveTopology;
bool rasterizationEnabled;
};
struct RenderPassAttachmentDesc
{
RenderPassAttachmentDesc();
// Set default values
void reset();
bool equalIgnoreLoadStoreOptions(const RenderPassAttachmentDesc &other) const;
bool operator==(const RenderPassAttachmentDesc &other) const;
TextureRef texture;
uint32_t level;
uint32_t slice;
MTLLoadAction loadAction;
MTLStoreAction storeAction;
MTLStoreActionOptions storeActionOptions;
};
struct RenderPassColorAttachmentDesc : public RenderPassAttachmentDesc
{
inline bool operator==(const RenderPassColorAttachmentDesc &other) const
{
return RenderPassAttachmentDesc::operator==(other) && clearColor == other.clearColor;
}
inline bool operator!=(const RenderPassColorAttachmentDesc &other) const
{
return !(*this == other);
}
MTLClearColor clearColor = {0, 0, 0, 0};
};
struct RenderPassDepthAttachmentDesc : public RenderPassAttachmentDesc
{
inline bool operator==(const RenderPassDepthAttachmentDesc &other) const
{
return RenderPassAttachmentDesc::operator==(other) && clearDepth == other.clearDepth;
}
inline bool operator!=(const RenderPassDepthAttachmentDesc &other) const
{
return !(*this == other);
}
double clearDepth = 0;
};
struct RenderPassStencilAttachmentDesc : public RenderPassAttachmentDesc
{
inline bool operator==(const RenderPassStencilAttachmentDesc &other) const
{
return RenderPassAttachmentDesc::operator==(other) && clearStencil == other.clearStencil;
}
inline bool operator!=(const RenderPassStencilAttachmentDesc &other) const
{
return !(*this == other);
}
uint32_t clearStencil = 0;
};
struct RenderPassDesc
{
RenderPassColorAttachmentDesc colorAttachments[kMaxRenderTargets];
RenderPassDepthAttachmentDesc depthAttachment;
RenderPassStencilAttachmentDesc stencilAttachment;
// This will populate the RenderPipelineOutputDesc with default blend state and
// MTLColorWriteMaskAll
void populateRenderPipelineOutputDesc(RenderPipelineOutputDesc *outDesc) const;
// This will populate the RenderPipelineOutputDesc with default blend state and the specified
// MTLColorWriteMask
void populateRenderPipelineOutputDesc(MTLColorWriteMask colorWriteMask,
RenderPipelineOutputDesc *outDesc) const;
// This will populate the RenderPipelineOutputDesc with the specified blend state
void populateRenderPipelineOutputDesc(const BlendDesc &blendState,
RenderPipelineOutputDesc *outDesc) const;
bool equalIgnoreLoadStoreOptions(const RenderPassDesc &other) const;
bool operator==(const RenderPassDesc &other) const;
inline bool operator!=(const RenderPassDesc &other) const { return !(*this == other); }
uint32_t numColorAttachments = 0;
};
// convert to Metal object
AutoObjCObj<MTLRenderPassDescriptor> ToMetalObj(const RenderPassDesc &desc);
} // namespace mtl
} // namespace rx
namespace std
{
template <>
struct hash<rx::mtl::DepthStencilDesc>
{
size_t operator()(const rx::mtl::DepthStencilDesc &key) const { return key.hash(); }
};
template <>
struct hash<rx::mtl::SamplerDesc>
{
size_t operator()(const rx::mtl::SamplerDesc &key) const { return key.hash(); }
};
template <>
struct hash<rx::mtl::RenderPipelineDesc>
{
size_t operator()(const rx::mtl::RenderPipelineDesc &key) const { return key.hash(); }
};
} // namespace std
namespace rx
{
namespace mtl
{
// render pipeline state cache per shader program
class RenderPipelineCache final : angle::NonCopyable
{
public:
RenderPipelineCache();
~RenderPipelineCache();
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(); }
AutoObjCPtr<id<MTLRenderPipelineState>> getRenderPipelineState(ContextMtl *context,
const RenderPipelineDesc &desc);
void clear();
protected:
AutoObjCPtr<id<MTLFunction>> mVertexShader = nil;
AutoObjCPtr<id<MTLFunction>> mFragmentShader = nil;
private:
void clearPipelineStates();
void recreatePipelineStates(Context *context);
AutoObjCPtr<id<MTLRenderPipelineState>> insertRenderPipelineState(
Context *context,
const RenderPipelineDesc &desc,
bool insertDefaultAttribLayout);
AutoObjCPtr<id<MTLRenderPipelineState>> createRenderPipelineState(
Context *context,
const RenderPipelineDesc &desc,
bool insertDefaultAttribLayout);
bool hasDefaultAttribs(const RenderPipelineDesc &desc) const;
// One table with default attrib and one table without.
std::unordered_map<RenderPipelineDesc, AutoObjCPtr<id<MTLRenderPipelineState>>>
mRenderPipelineStates[2];
};
class StateCache final : angle::NonCopyable
{
public:
StateCache();
~StateCache();
// Null depth stencil state has depth/stecil read & write disabled.
inline AutoObjCPtr<id<MTLDepthStencilState>> getNullDepthStencilState(Context *context)
{
return getNullDepthStencilState(context->getMetalDevice());
}
AutoObjCPtr<id<MTLDepthStencilState>> getNullDepthStencilState(id<MTLDevice> device);
AutoObjCPtr<id<MTLDepthStencilState>> getDepthStencilState(id<MTLDevice> device,
const DepthStencilDesc &desc);
AutoObjCPtr<id<MTLSamplerState>> getSamplerState(id<MTLDevice> device, const SamplerDesc &desc);
// Null sampler state uses default SamplerDesc
AutoObjCPtr<id<MTLSamplerState>> getNullSamplerState(Context *context);
AutoObjCPtr<id<MTLSamplerState>> getNullSamplerState(id<MTLDevice> device);
void clear();
private:
AutoObjCPtr<id<MTLDepthStencilState>> mNullDepthStencilState = nil;
std::unordered_map<DepthStencilDesc, AutoObjCPtr<id<MTLDepthStencilState>>> mDepthStencilStates;
std::unordered_map<SamplerDesc, AutoObjCPtr<id<MTLSamplerState>>> mSamplerStates;
};
} // namespace mtl
} // namespace rx
static inline bool operator==(const rx::mtl::VertexDesc &lhs, const rx::mtl::VertexDesc &rhs)
{
if (lhs.numAttribs != rhs.numAttribs || lhs.numBufferLayouts != rhs.numBufferLayouts)
{
return false;
}
for (uint8_t i = 0; i < lhs.numAttribs; ++i)
{
if (lhs.attributes[i] != rhs.attributes[i])
{
return false;
}
}
for (uint8_t i = 0; i < lhs.numBufferLayouts; ++i)
{
if (lhs.layouts[i] != rhs.layouts[i])
{
return false;
}
}
return true;
}
static inline bool operator==(const MTLClearColor &lhs, const MTLClearColor &rhs)
{
return lhs.red == rhs.red && lhs.green == rhs.green && lhs.blue == rhs.blue &&
lhs.alpha == rhs.alpha;
}
#endif /* LIBANGLE_RENDERER_METAL_MTL_STATE_CACHE_H_ */