blob: c196037667e7c0eee8d9e3ccd1bc1a0ccc90b41a [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_context_device.h"
#include "libANGLE/renderer/metal/mtl_resources.h"
static inline bool operator==(const MTLClearColor &lhs, const MTLClearColor &rhs);
namespace angle
{
struct FeaturesMtl;
}
namespace rx
{
class ContextMtl;
namespace mtl
{
struct alignas(1) StencilDesc
{
bool operator==(const StencilDesc &rhs) const;
// Set default values
void reset();
// Use uint8_t instead of MTLStencilOperation to compact space
uint8_t stencilFailureOperation : 3;
uint8_t depthFailureOperation : 3;
uint8_t depthStencilPassOperation : 3;
// Use uint8_t instead of MTLCompareFunction to compact space
uint8_t stencilCompareFunction : 3;
uint8_t readMask : 8;
uint8_t writeMask : 8;
};
struct alignas(4) DepthStencilDesc
{
DepthStencilDesc();
DepthStencilDesc(const DepthStencilDesc &src);
DepthStencilDesc(DepthStencilDesc &&src);
DepthStencilDesc &operator=(const DepthStencilDesc &src);
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;
// Use uint8_t instead of MTLCompareFunction to compact space
uint8_t depthCompareFunction : 3;
bool depthWriteEnabled : 1;
};
struct alignas(4) SamplerDesc
{
SamplerDesc();
SamplerDesc(const SamplerDesc &src);
SamplerDesc(SamplerDesc &&src);
explicit SamplerDesc(const gl::SamplerState &glState);
SamplerDesc &operator=(const SamplerDesc &src);
// 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;
// Use uint8_t instead of MTLSamplerAddressMode to compact space
uint8_t rAddressMode : 3;
uint8_t sAddressMode : 3;
uint8_t tAddressMode : 3;
// Use uint8_t instead of MTLSamplerMinMagFilter to compact space
uint8_t minFilter : 1;
uint8_t magFilter : 1;
uint8_t mipFilter : 2;
uint8_t maxAnisotropy : 5;
// Use uint8_t instead of MTLCompareFunction to compact space
uint8_t compareFunction : 3;
};
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); }
// Use uint8_t instead of MTLVertexFormat to compact space
uint8_t format : 6;
// Offset is only used for default attributes buffer. So 8 bits are enough.
uint8_t offset : 8;
uint8_t bufferIndex : 5;
};
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); }
uint32_t stepRate;
uint32_t stride;
// Use uint8_t instead of MTLVertexStepFunction to compact space
uint8_t stepFunction;
};
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 uint8_t angleMask);
// Use uint8_t instead of MTLColorWriteMask to compact space
uint8_t writeMask : 4;
// Use uint8_t instead of MTLBlendOperation to compact space
uint8_t alphaBlendOperation : 3;
uint8_t rgbBlendOperation : 3;
// Use uint8_t instead of MTLBlendFactor to compact space
uint8_t destinationAlphaBlendFactor : 5;
uint8_t destinationRGBBlendFactor : 5;
uint8_t sourceAlphaBlendFactor : 5;
uint8_t sourceRGBBlendFactor : 5;
bool blendingEnabled : 1;
};
using BlendDescArray = std::array<BlendDesc, kMaxRenderTargets>;
using WriteMaskArray = std::array<uint8_t, kMaxRenderTargets>;
struct alignas(2) 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 &blendDesc);
// Use uint16_t instead of MTLPixelFormat to compact space
uint16_t pixelFormat : 16;
};
struct RenderPipelineOutputDesc
{
bool operator==(const RenderPipelineOutputDesc &rhs) const;
void updateEnabledDrawBuffers(gl::DrawBufferMask enabledBuffers);
std::array<RenderPipelineColorAttachmentDesc, kMaxRenderTargets> colorAttachments;
// Use uint16_t instead of MTLPixelFormat to compact space
uint16_t depthAttachmentPixelFormat : 16;
uint16_t stencilAttachmentPixelFormat : 16;
uint8_t numColorAttachments;
uint8_t rasterSampleCount;
};
enum class RenderPipelineRasterization : uint32_t
{
// This flag is used for vertex shader not writing any stage output (e.g gl_Position).
// This will disable fragment shader stage. This is useful for transform feedback ouput vertex
// shader.
Disabled,
// Fragment shader is enabled.
Enabled,
// This flag is for rasterization discard emulation when vertex shader still writes to stage
// output. Disabled flag cannot be used in this case since Metal doesn't allow that. The
// emulation would insert a code snippet to move gl_Position out of clip space's visible area to
// simulate the discard.
EmulatedDiscard,
EnumCount,
};
template <typename T>
using RenderPipelineRasterStateMap = angle::PackedEnumMap<RenderPipelineRasterization, T>;
struct alignas(4) RenderPipelineDesc
{
RenderPipelineDesc();
RenderPipelineDesc(const RenderPipelineDesc &src);
RenderPipelineDesc(RenderPipelineDesc &&src);
RenderPipelineDesc &operator=(const RenderPipelineDesc &src);
bool operator==(const RenderPipelineDesc &rhs) const;
size_t hash() const;
bool rasterizationEnabled() const;
angle::ObjCPtr<MTLRenderPipelineDescriptor> createMetalDesc(
id<MTLFunction> vertexShader,
id<MTLFunction> fragmentShader) const;
VertexDesc vertexDescriptor;
RenderPipelineOutputDesc outputDescriptor;
// Use uint8_t instead of MTLPrimitiveTopologyClass to compact space.
uint8_t inputPrimitiveTopology : 2;
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.
RenderPipelineRasterization rasterizationType : 2;
};
struct alignas(4) ProvokingVertexComputePipelineDesc
{
ProvokingVertexComputePipelineDesc();
ProvokingVertexComputePipelineDesc(const ProvokingVertexComputePipelineDesc &src);
ProvokingVertexComputePipelineDesc(ProvokingVertexComputePipelineDesc &&src);
ProvokingVertexComputePipelineDesc &operator=(const ProvokingVertexComputePipelineDesc &src);
bool operator==(const ProvokingVertexComputePipelineDesc &rhs) const;
bool operator!=(const ProvokingVertexComputePipelineDesc &rhs) const;
size_t hash() const;
gl::PrimitiveMode primitiveMode;
uint8_t elementType;
bool primitiveRestartEnabled;
bool generateIndices;
};
struct RenderPassAttachmentDesc
{
RenderPassAttachmentDesc();
// Set default values
void reset();
bool equalIgnoreLoadStoreOptions(const RenderPassAttachmentDesc &other) const;
bool operator==(const RenderPassAttachmentDesc &other) const;
ANGLE_INLINE bool hasImplicitMSTexture() const { return implicitMSTexture.get(); }
const TextureRef &getImplicitMSTextureIfAvailOrTexture() const
{
return hasImplicitMSTexture() ? implicitMSTexture : texture;
}
TextureRef texture;
// Implicit multisample texture that will be rendered into and discarded at the end of
// a render pass. Its result will be resolved into normal texture above.
TextureRef implicitMSTexture;
MipmapNativeLevel level;
uint32_t sliceOrDepth;
// This attachment is blendable or not.
bool blendable;
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 = 1.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;
};
//
// This is C++ equivalent of Objective-C MTLRenderPassDescriptor.
// We could use MTLRenderPassDescriptor directly, however, using C++ struct has benefits of fast
// copy, stack allocation, inlined comparing function, etc.
//
struct RenderPassDesc
{
std::array<RenderPassColorAttachmentDesc, kMaxRenderTargets> colorAttachments;
RenderPassDepthAttachmentDesc depthAttachment;
RenderPassStencilAttachmentDesc stencilAttachment;
void convertToMetalDesc(MTLRenderPassDescriptor *objCDesc,
uint32_t deviceMaxRenderTargets) const;
// 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(const WriteMaskArray &writeMaskArray,
RenderPipelineOutputDesc *outDesc) const;
// This will populate the RenderPipelineOutputDesc with the specified blend state
void populateRenderPipelineOutputDesc(const BlendDescArray &blendDescArray,
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;
uint32_t rasterSampleCount = 1;
uint32_t defaultWidth = 0;
uint32_t defaultHeight = 0;
};
} // 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(); }
};
template <>
struct hash<rx::mtl::ProvokingVertexComputePipelineDesc>
{
size_t operator()(const rx::mtl::ProvokingVertexComputePipelineDesc &key) const
{
return key.hash();
}
};
} // namespace std
namespace rx
{
namespace mtl
{
class StateCache final : angle::NonCopyable
{
public:
StateCache(const angle::FeaturesMtl &features);
~StateCache();
// Null depth stencil state has depth/stecil read & write disabled.
angle::ObjCPtr<id<MTLDepthStencilState>> getNullDepthStencilState(
const mtl::ContextDevice &device);
angle::ObjCPtr<id<MTLDepthStencilState>> getDepthStencilState(const mtl::ContextDevice &device,
const DepthStencilDesc &desc);
angle::ObjCPtr<id<MTLSamplerState>> getSamplerState(const mtl::ContextDevice &device,
const SamplerDesc &desc);
// Null sampler state uses default SamplerDesc
angle::ObjCPtr<id<MTLSamplerState>> getNullSamplerState(ContextMtl *context);
angle::ObjCPtr<id<MTLSamplerState>> getNullSamplerState(const mtl::ContextDevice &device);
void clear();
private:
const angle::FeaturesMtl &mFeatures;
angle::ObjCPtr<id<MTLDepthStencilState>> mNullDepthStencilState = nil;
angle::HashMap<DepthStencilDesc, angle::ObjCPtr<id<MTLDepthStencilState>>> mDepthStencilStates;
angle::HashMap<SamplerDesc, angle::ObjCPtr<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_ */