Cache some more Metal state.

Caches MTLSamplerStates and MTLDepthStencilStates.

Bug: skia:8243
Change-Id: Id362507caedb3453b53d17f77dfbcee42ec52578
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/209811
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Jim Van Verth <jvanverth@google.com>
This commit is contained in:
Jim Van Verth 2019-04-26 12:23:51 -04:00 committed by Skia Commit-Bot
parent d425dee662
commit 75c5326bae
10 changed files with 279 additions and 84 deletions

View File

@ -706,6 +706,8 @@ skia_metal_sources = [
"$_src/gpu/mtl/GrMtlCppUtil.h",
"$_src/gpu/mtl/GrMtlCopyPipelineState.h",
"$_src/gpu/mtl/GrMtlCopyPipelineState.mm",
"$_src/gpu/mtl/GrMtlDepthStencil.h",
"$_src/gpu/mtl/GrMtlDepthStencil.mm",
"$_src/gpu/mtl/GrMtlGpu.h",
"$_src/gpu/mtl/GrMtlGpu.mm",
"$_src/gpu/mtl/GrMtlGpuCommandBuffer.h",

View File

@ -0,0 +1,63 @@
/*
* Copyright 2019 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrMtlDepthStencil_DEFINED
#define GrMtlDepthStencil_DEFINED
#import <metal/metal.h>
#include "include/gpu/GrTypes.h"
#include "src/core/SkOpts.h"
#include <atomic>
class GrMtlGpu;
class GrStencilSettings;
// A wrapper for a MTLDepthStencilState object with caching support.
class GrMtlDepthStencil {
public:
static GrMtlDepthStencil* Create(const GrMtlGpu*, const GrStencilSettings&, GrSurfaceOrigin);
id<MTLDepthStencilState> mtlDepthStencil() const { return fMtlDepthStencilState; }
struct Key {
struct Face {
uint32_t fReadMask;
uint32_t fWriteMask;
uint32_t fOps;
};
Face fFront;
Face fBack;
bool operator==(const Key& that) const {
return this->fFront.fReadMask == that.fFront.fReadMask &&
this->fFront.fWriteMask == that.fFront.fWriteMask &&
this->fFront.fOps == that.fFront.fOps &&
this->fBack.fReadMask == that.fBack.fReadMask &&
this->fBack.fWriteMask == that.fBack.fWriteMask &&
this->fBack.fOps == that.fBack.fOps;
}
};
// Helpers for hashing GrMtlSampler
static Key GenerateKey(const GrStencilSettings&, GrSurfaceOrigin);
static const Key& GetKey(const GrMtlDepthStencil& depthStencil) { return depthStencil.fKey; }
static uint32_t Hash(const Key& key) {
return SkOpts::hash(reinterpret_cast<const uint32_t*>(&key), sizeof(Key));
}
private:
GrMtlDepthStencil(id<MTLDepthStencilState> mtlDepthStencilState, Key key)
: fMtlDepthStencilState(mtlDepthStencilState)
, fKey(key) {}
id<MTLDepthStencilState> fMtlDepthStencilState;
Key fKey;
};
#endif

View File

@ -0,0 +1,122 @@
/*
* Copyright 2018 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/gpu/GrStencilSettings.h"
#include "src/gpu/mtl/GrMtlDepthStencil.h"
#include "src/gpu/mtl/GrMtlGpu.h"
MTLStencilOperation skia_stencil_op_to_mtl(GrStencilOp op) {
switch (op) {
case GrStencilOp::kKeep:
return MTLStencilOperationKeep;
case GrStencilOp::kZero:
return MTLStencilOperationZero;
case GrStencilOp::kReplace:
return MTLStencilOperationReplace;
case GrStencilOp::kInvert:
return MTLStencilOperationInvert;
case GrStencilOp::kIncWrap:
return MTLStencilOperationIncrementWrap;
case GrStencilOp::kDecWrap:
return MTLStencilOperationDecrementWrap;
case GrStencilOp::kIncClamp:
return MTLStencilOperationIncrementClamp;
case GrStencilOp::kDecClamp:
return MTLStencilOperationDecrementClamp;
}
}
MTLStencilDescriptor* skia_stencil_to_mtl(GrStencilSettings::Face face) {
MTLStencilDescriptor* result = [[MTLStencilDescriptor alloc] init];
switch (face.fTest) {
case GrStencilTest::kAlways:
result.stencilCompareFunction = MTLCompareFunctionAlways;
break;
case GrStencilTest::kNever:
result.stencilCompareFunction = MTLCompareFunctionNever;
break;
case GrStencilTest::kGreater:
result.stencilCompareFunction = MTLCompareFunctionGreater;
break;
case GrStencilTest::kGEqual:
result.stencilCompareFunction = MTLCompareFunctionGreaterEqual;
break;
case GrStencilTest::kLess:
result.stencilCompareFunction = MTLCompareFunctionLess;
break;
case GrStencilTest::kLEqual:
result.stencilCompareFunction = MTLCompareFunctionLessEqual;
break;
case GrStencilTest::kEqual:
result.stencilCompareFunction = MTLCompareFunctionEqual;
break;
case GrStencilTest::kNotEqual:
result.stencilCompareFunction = MTLCompareFunctionNotEqual;
break;
}
result.readMask = face.fTestMask;
result.writeMask = face.fWriteMask;
result.depthStencilPassOperation = skia_stencil_op_to_mtl(face.fPassOp);
result.stencilFailureOperation = skia_stencil_op_to_mtl(face.fFailOp);
return result;
}
GrMtlDepthStencil* GrMtlDepthStencil::Create(const GrMtlGpu* gpu,
const GrStencilSettings& stencil,
GrSurfaceOrigin origin) {
MTLDepthStencilDescriptor* desc = [[MTLDepthStencilDescriptor alloc] init];
if (!stencil.isDisabled()) {
if (stencil.isTwoSided()) {
desc.frontFaceStencil = skia_stencil_to_mtl(stencil.front(origin));
desc.backFaceStencil = skia_stencil_to_mtl(stencil.back(origin));
}
else {
desc.frontFaceStencil = skia_stencil_to_mtl(stencil.frontAndBack());
desc.backFaceStencil = desc.frontFaceStencil;
}
}
return new GrMtlDepthStencil([gpu->device() newDepthStencilStateWithDescriptor: desc],
GenerateKey(stencil, origin));
}
void skia_stencil_to_key(GrStencilSettings::Face face, GrMtlDepthStencil::Key::Face* faceKey) {
const int kPassOpShift = 3;
const int kFailOpShift = 6;
faceKey->fReadMask = face.fTestMask;
faceKey->fWriteMask = face.fWriteMask;
SkASSERT(static_cast<int>(face.fTest) <= 7);
faceKey->fOps = static_cast<uint32_t>(face.fTest);
SkASSERT(static_cast<int>(face.fPassOp) <= 7);
faceKey->fOps |= (static_cast<uint32_t>(face.fPassOp) << kPassOpShift);
SkASSERT(static_cast<int>(face.fFailOp) <= 7);
faceKey->fOps |= (static_cast<uint32_t>(face.fFailOp) << kFailOpShift);
}
GrMtlDepthStencil::Key GrMtlDepthStencil::GenerateKey(const GrStencilSettings& stencil,
GrSurfaceOrigin origin) {
Key depthStencilKey;
if (stencil.isDisabled()) {
memset(&depthStencilKey, 0, sizeof(Key));
} else {
if (stencil.isTwoSided()) {
skia_stencil_to_key(stencil.front(origin), &depthStencilKey.fFront);
skia_stencil_to_key(stencil.back(origin), &depthStencilKey.fBack);
}
else {
skia_stencil_to_key(stencil.frontAndBack(), &depthStencilKey.fFront);
memcpy(&depthStencilKey.fBack, &depthStencilKey.fFront, sizeof(Key::Face));
}
}
return depthStencilKey;
}

View File

@ -227,7 +227,8 @@ private:
GrMtlCommandBuffer* fCmdBuffer;
std::unique_ptr<SkSL::Compiler> fCompiler;
GrMtlCopyManager fCopyManager;
GrMtlCopyManager fCopyManager;
GrMtlResourceProvider fResourceProvider;
GrMtlBufferManager fBufferManager;

View File

@ -106,7 +106,7 @@ private:
void setDepthStencilState(id<MTLRenderCommandEncoder> renderCmdEncoder);
struct SamplerBindings {
id<MTLSamplerState> fSampler;
GrMtlSampler* fSampler;
id<MTLTexture> fTexture;
SamplerBindings(const GrSamplerState& state, GrTexture* texture, GrMtlGpu*);

View File

@ -18,17 +18,14 @@
#include "src/gpu/glsl/GrGLSLXferProcessor.h"
#include "src/gpu/mtl/GrMtlBuffer.h"
#include "src/gpu/mtl/GrMtlGpu.h"
#include "src/gpu/mtl/GrMtlSampler.h"
#include "src/gpu/mtl/GrMtlTexture.h"
GrMtlPipelineState::SamplerBindings::SamplerBindings(const GrSamplerState& state,
GrTexture* texture,
GrMtlGpu* gpu)
: fTexture(static_cast<GrMtlTexture*>(texture)->mtlTexture()) {
// TODO: use resource provider to get sampler.
std::unique_ptr<GrMtlSampler> sampler(
GrMtlSampler::Create(gpu, state, texture->texturePriv().maxMipMapLevel()));
fSampler = sampler->mtlSamplerState();
uint32_t maxMipMapLevel = texture->texturePriv().maxMipMapLevel();
fSampler = gpu->resourceProvider().findOrCreateCompatibleSampler(state, maxMipMapLevel);
}
GrMtlPipelineState::GrMtlPipelineState(
@ -126,7 +123,7 @@ void GrMtlPipelineState::bind(id<MTLRenderCommandEncoder> renderCmdEncoder) {
for (int index = 0; index < fNumSamplers; ++index) {
[renderCmdEncoder setFragmentTexture: fSamplerBindings[index].fTexture
atIndex: index];
[renderCmdEncoder setFragmentSamplerState: fSamplerBindings[index].fSampler
[renderCmdEncoder setFragmentSamplerState: fSamplerBindings[index].fSampler->mtlSampler()
atIndex: index];
}
}
@ -188,85 +185,20 @@ void GrMtlPipelineState::setBlendConstants(id<MTLRenderCommandEncoder> renderCmd
}
}
MTLStencilOperation skia_stencil_op_to_mtl(GrStencilOp op) {
switch (op) {
case GrStencilOp::kKeep:
return MTLStencilOperationKeep;
case GrStencilOp::kZero:
return MTLStencilOperationZero;
case GrStencilOp::kReplace:
return MTLStencilOperationReplace;
case GrStencilOp::kInvert:
return MTLStencilOperationInvert;
case GrStencilOp::kIncWrap:
return MTLStencilOperationIncrementWrap;
case GrStencilOp::kDecWrap:
return MTLStencilOperationDecrementWrap;
case GrStencilOp::kIncClamp:
return MTLStencilOperationIncrementClamp;
case GrStencilOp::kDecClamp:
return MTLStencilOperationDecrementClamp;
}
}
MTLStencilDescriptor* skia_stencil_to_mtl(GrStencilSettings::Face face) {
MTLStencilDescriptor* result = [[MTLStencilDescriptor alloc] init];
switch (face.fTest) {
case GrStencilTest::kAlways:
result.stencilCompareFunction = MTLCompareFunctionAlways;
break;
case GrStencilTest::kNever:
result.stencilCompareFunction = MTLCompareFunctionNever;
break;
case GrStencilTest::kGreater:
result.stencilCompareFunction = MTLCompareFunctionGreater;
break;
case GrStencilTest::kGEqual:
result.stencilCompareFunction = MTLCompareFunctionGreaterEqual;
break;
case GrStencilTest::kLess:
result.stencilCompareFunction = MTLCompareFunctionLess;
break;
case GrStencilTest::kLEqual:
result.stencilCompareFunction = MTLCompareFunctionLessEqual;
break;
case GrStencilTest::kEqual:
result.stencilCompareFunction = MTLCompareFunctionEqual;
break;
case GrStencilTest::kNotEqual:
result.stencilCompareFunction = MTLCompareFunctionNotEqual;
break;
}
result.readMask = face.fTestMask;
result.writeMask = face.fWriteMask;
result.depthStencilPassOperation = skia_stencil_op_to_mtl(face.fPassOp);
result.stencilFailureOperation = skia_stencil_op_to_mtl(face.fFailOp);
return result;
}
void GrMtlPipelineState::setDepthStencilState(id<MTLRenderCommandEncoder> renderCmdEncoder) {
if (fStencil.isDisabled()) {
MTLDepthStencilDescriptor* desc = [[MTLDepthStencilDescriptor alloc] init];
id<MTLDepthStencilState> state = [fGpu->device() newDepthStencilStateWithDescriptor:desc];
[renderCmdEncoder setDepthStencilState:state];
}
else {
MTLDepthStencilDescriptor* desc = [[MTLDepthStencilDescriptor alloc] init];
GrSurfaceOrigin origin = fRenderTargetState.fRenderTargetOrigin;
const GrSurfaceOrigin& origin = fRenderTargetState.fRenderTargetOrigin;
GrMtlDepthStencil* state =
fGpu->resourceProvider().findOrCreateCompatibleDepthStencilState(fStencil, origin);
if (!fStencil.isDisabled()) {
if (fStencil.isTwoSided()) {
desc.frontFaceStencil = skia_stencil_to_mtl(fStencil.front(origin));
desc.backFaceStencil = skia_stencil_to_mtl(fStencil.back(origin));
[renderCmdEncoder setStencilFrontReferenceValue:fStencil.front(origin).fRef
backReferenceValue:fStencil.back(origin).fRef];
}
else {
desc.frontFaceStencil = skia_stencil_to_mtl(fStencil.frontAndBack());
desc.backFaceStencil = desc.frontFaceStencil;
[renderCmdEncoder setStencilReferenceValue:fStencil.frontAndBack().fRef];
}
id<MTLDepthStencilState> state = [fGpu->device() newDepthStencilStateWithDescriptor:desc];
[renderCmdEncoder setDepthStencilState:state];
}
[renderCmdEncoder setDepthStencilState:state->mtlDepthStencil()];
}
void GrMtlPipelineState::SetDynamicScissorRectState(id<MTLRenderCommandEncoder> renderCmdEncoder,

View File

@ -11,7 +11,9 @@
#include "include/private/SkTArray.h"
#include "src/core/SkLRUCache.h"
#include "src/gpu/mtl/GrMtlCopyPipelineState.h"
#include "src/gpu/mtl/GrMtlDepthStencil.h"
#include "src/gpu/mtl/GrMtlPipelineStateBuilder.h"
#include "src/gpu/mtl/GrMtlSampler.h"
#import <metal/metal.h>
@ -33,6 +35,13 @@ public:
const GrTextureProxy* const primProcProxies[],
GrPrimitiveType);
// Finds or creates a compatible MTLDepthStencilState based on the GrStencilSettings.
GrMtlDepthStencil* findOrCreateCompatibleDepthStencilState(const GrStencilSettings&,
GrSurfaceOrigin);
// Finds or creates a compatible MTLSamplerState based on the GrSamplerState.
GrMtlSampler* findOrCreateCompatibleSampler(const GrSamplerState&, uint32_t maxMipLevel);
private:
#ifdef SK_DEBUG
#define GR_PIPELINE_STATE_CACHE_STATS
@ -80,6 +89,9 @@ private:
// Cache of GrMtlPipelineStates
std::unique_ptr<PipelineStateCache> fPipelineStateCache;
SkTDynamicHash<GrMtlSampler, GrMtlSampler::Key> fSamplers;
SkTDynamicHash<GrMtlDepthStencil, GrMtlDepthStencil::Key> fDepthStencilStates;
};
#endif

View File

@ -14,7 +14,6 @@
#include "src/sksl/SkSLCompiler.h"
GrMtlResourceProvider::GrMtlResourceProvider(GrMtlGpu* gpu)
: fGpu(gpu) {
fPipelineStateCache.reset(new PipelineStateCache(gpu));
@ -47,6 +46,33 @@ GrMtlPipelineState* GrMtlResourceProvider::findOrCreateCompatiblePipelineState(
////////////////////////////////////////////////////////////////////////////////////////////////
GrMtlDepthStencil* GrMtlResourceProvider::findOrCreateCompatibleDepthStencilState(
const GrStencilSettings& stencil, GrSurfaceOrigin origin) {
GrMtlDepthStencil* depthStencilState;
GrMtlDepthStencil::Key key = GrMtlDepthStencil::GenerateKey(stencil, origin);
depthStencilState = fDepthStencilStates.find(key);
if (!depthStencilState) {
depthStencilState = GrMtlDepthStencil::Create(fGpu, stencil, origin);
fDepthStencilStates.add(depthStencilState);
}
SkASSERT(depthStencilState);
return depthStencilState;
}
GrMtlSampler* GrMtlResourceProvider::findOrCreateCompatibleSampler(const GrSamplerState& params,
uint32_t maxMipLevel) {
GrMtlSampler* sampler;
sampler = fSamplers.find(GrMtlSampler::GenerateKey(params, maxMipLevel));
if (!sampler) {
sampler = GrMtlSampler::Create(fGpu, params, maxMipLevel);
fSamplers.add(sampler);
}
SkASSERT(sampler);
return sampler;
}
////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef GR_PIPELINE_STATE_CACHE_STATS
// Display pipeline state cache usage
static const bool c_DisplayMtlPipelineCache{false};

View File

@ -10,21 +10,36 @@
#import <metal/metal.h>
#include "src/core/SkOpts.h"
#include <atomic>
class GrSamplerState;
class GrMtlGpu;
// This class only acts as a wrapper for a MTLSamplerState object for now, but will be more useful
// once we start caching sampler states.
// A wrapper for a MTLSamplerState object with caching support.
class GrMtlSampler {
public:
static GrMtlSampler* Create(const GrMtlGpu* gpu, const GrSamplerState&, uint32_t maxMipLevel);
id<MTLSamplerState> mtlSamplerState() const { return fMtlSamplerState; }
id<MTLSamplerState> mtlSampler() const { return fMtlSamplerState; }
typedef uint32_t Key;
// Helpers for hashing GrMtlSampler
static Key GenerateKey(const GrSamplerState&, uint32_t maxMipLevel);
static const Key& GetKey(const GrMtlSampler& sampler) { return sampler.fKey; }
static uint32_t Hash(const Key& key) {
return SkOpts::hash(reinterpret_cast<const uint32_t*>(&key), sizeof(Key));
}
private:
GrMtlSampler(id<MTLSamplerState> mtlSamplerState) : fMtlSamplerState(mtlSamplerState) {}
GrMtlSampler(id<MTLSamplerState> mtlSamplerState, Key key)
: fMtlSamplerState(mtlSamplerState)
, fKey(key) {}
id<MTLSamplerState> fMtlSamplerState;
Key fKey;
};
#endif

View File

@ -61,5 +61,27 @@ GrMtlSampler* GrMtlSampler::Create(const GrMtlGpu* gpu, const GrSamplerState& sa
samplerDesc.normalizedCoordinates = true;
samplerDesc.compareFunction = MTLCompareFunctionNever;
return new GrMtlSampler([gpu->device() newSamplerStateWithDescriptor: samplerDesc]);
return new GrMtlSampler([gpu->device() newSamplerStateWithDescriptor: samplerDesc],
GenerateKey(samplerState, maxMipLevel));
}
GrMtlSampler::Key GrMtlSampler::GenerateKey(const GrSamplerState& samplerState,
uint32_t maxMipLevel) {
const int kTileModeXShift = 2;
const int kTileModeYShift = 4;
const int kMipLevelShift = 6;
SkASSERT(static_cast<int>(samplerState.filter()) <= 3);
Key samplerKey = static_cast<uint16_t>(samplerState.filter());
SkASSERT(static_cast<int>(samplerState.wrapModeX()) <= 3);
samplerKey |= (static_cast<uint16_t>(samplerState.wrapModeX()) << kTileModeXShift);
SkASSERT(static_cast<int>(samplerState.wrapModeY()) <= 3);
samplerKey |= (static_cast<uint16_t>(samplerState.wrapModeY()) << kTileModeYShift);
bool useMipMaps = GrSamplerState::Filter::kMipMap == samplerState.filter() && maxMipLevel > 0;
samplerKey |= (!useMipMaps ? 0 : (uint16_t) maxMipLevel) << kMipLevelShift;
return samplerKey;
}