mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-21 10:19:04 +02:00
Pipeline states are created on compile threads, along with the shader functions they need, which compile once on first use. Draws record a handle to the pipeline state, and the render thread waits for it if it gets there first, as with Vulkan, so the emu thread never waits for a compile. Translation to MSL stays synchronous, as recording needs the texture masks it produces. Each game's shader IDs and pipeline keys are saved to a <discID>.mtlshadercache, and recreated while the game boots. macOS's own Metal cache then makes them quick: Wipeout Pure's 36 pipelines took 25 ms, or 260 ms with an empty Metal cache, instead of stalls during play. Cold Wipeout Pure, 5 emulated seconds from a savestate: 3.0 s before, 1.6 s with the background compiles alone. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
176 lines
5.5 KiB
C++
176 lines
5.5 KiB
C++
// Copyright (c) 2012- PPSSPP Project.
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
// Official git repository and contact information can be found at
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
#pragma once
|
|
|
|
// Objective-C++ only.
|
|
|
|
#include <unordered_map>
|
|
|
|
#include "Common/Data/Collections/Hashmaps.h"
|
|
#include "Common/GPU/Metal/MetalRenderManager.h"
|
|
#include "GPU/GPUState.h"
|
|
#include "GPU/Common/DrawEngineCommon.h"
|
|
#include "GPU/Common/GPUStateUtils.h"
|
|
#include "GPU/Common/ShaderId.h"
|
|
|
|
class ShaderManagerMetal;
|
|
class TextureCacheMetal;
|
|
class FramebufferManagerMetal;
|
|
|
|
// What goes into a MTLRenderPipelineState, other than the shaders.
|
|
struct MetalBlendKey {
|
|
union {
|
|
uint32_t value;
|
|
struct {
|
|
uint32_t blendEnable : 1;
|
|
uint32_t srcColor : 5; // BlendFactor
|
|
uint32_t dstColor : 5;
|
|
uint32_t srcAlpha : 5;
|
|
uint32_t dstAlpha : 5;
|
|
uint32_t eqColor : 3; // BlendEq
|
|
uint32_t eqAlpha : 3;
|
|
uint32_t colorWriteMask : 4; // R=1, G=2, B=4, A=8
|
|
};
|
|
};
|
|
};
|
|
|
|
struct MetalDepthStencilKey {
|
|
union {
|
|
uint64_t value;
|
|
struct {
|
|
uint64_t depthTestEnable : 1;
|
|
uint64_t depthWriteEnable : 1;
|
|
uint64_t depthCompare : 3; // GEComparison
|
|
uint64_t stencilTestEnable : 1;
|
|
uint64_t stencilCompare : 3; // GEComparison
|
|
uint64_t stencilPass : 3; // GEStencilOp
|
|
uint64_t stencilFail : 3;
|
|
uint64_t stencilDepthFail : 3;
|
|
uint64_t writeMask : 8;
|
|
uint64_t compareMask : 8;
|
|
};
|
|
};
|
|
};
|
|
|
|
struct MetalPipelineKey {
|
|
VShaderID vsid;
|
|
FShaderID fsid;
|
|
uint32_t vtxFmtId; // The decoded format ID, or SOFTWARE_TRANSFORM_VTXFMT.
|
|
uint32_t blend; // MetalBlendKey
|
|
uint32_t targetFormat; // MetalRenderTargetFormat::Key()
|
|
uint32_t pad = 0;
|
|
};
|
|
|
|
struct MetalDynamicState {
|
|
MTLViewport viewport;
|
|
int scissor[4];
|
|
bool useBlendColor;
|
|
float blendColor[4];
|
|
uint8_t stencilRef;
|
|
MTLCullMode cull;
|
|
bool depthClamp;
|
|
};
|
|
|
|
class DrawEngineMetal : public DrawEngineCommon {
|
|
public:
|
|
DrawEngineMetal(Draw::DrawContext *draw);
|
|
~DrawEngineMetal();
|
|
|
|
void SetShaderManager(ShaderManagerMetal *shaderManager) { shaderManager_ = shaderManager; }
|
|
void SetTextureCache(TextureCacheMetal *textureCache) { textureCache_ = textureCache; }
|
|
void SetFramebufferManager(FramebufferManagerMetal *fbManager) { framebufferManager_ = fbManager; }
|
|
|
|
void DeviceLost() override;
|
|
void DeviceRestore(Draw::DrawContext *draw) override;
|
|
|
|
void BeginFrame() override;
|
|
void Flush() override;
|
|
void FinishDeferred() {
|
|
// The indices are read from PSP memory at flush time, and the game may change them (#10095).
|
|
Flush();
|
|
}
|
|
|
|
void ClearPipelines();
|
|
|
|
// The shader cache: the pipelines this game has used, and the formats of their targets.
|
|
bool SavePipelines(FILE *f);
|
|
static uint32_t PipelineRecordSize();
|
|
// Creates the pipelines, which compile in the background. waitForCompiles waits for all of them.
|
|
bool LoadPipelines(FILE *f, int count, bool waitForCompiles);
|
|
int GetNumPipelines() const { return (int)pipelines_.size(); }
|
|
|
|
void SetDepalTexture(id<MTLTexture> depal, bool smooth) {
|
|
if (boundDepal_ != depal) {
|
|
boundDepal_ = depal;
|
|
boundDepalSmoothed_ = smooth;
|
|
gstate_c.Dirty(DIRTY_FRAGMENTSHADER_STATE);
|
|
}
|
|
}
|
|
|
|
private:
|
|
void InitDeviceObjects();
|
|
void Invalidate(InvalidationCallbackFlags flags);
|
|
void ConvertStateToKeys(int prim);
|
|
void BindShaderBlendTex();
|
|
struct Pipeline {
|
|
MetalPipelineState *state; // May still be compiling.
|
|
MetalRenderTargetFormat format;
|
|
uint32_t fragmentArrayTextureMask;
|
|
uint32_t fragmentTextureMask;
|
|
};
|
|
|
|
Pipeline *GetPipeline(const VShaderID &vsid, const FShaderID &fsid, bool useHWTransform);
|
|
Pipeline *GetOrCreatePipeline(const MetalPipelineKey &key, const DecVtxFormat *decFmt, const MetalRenderTargetFormat &format);
|
|
id<MTLDepthStencilState> GetDepthStencilState(const MetalDepthStencilKey &key);
|
|
bool ApplyState(const Pipeline *pipeline, bool overrideStencilRef, uint8_t stencilRef);
|
|
void BindResources(const Pipeline *pipeline);
|
|
void ResetAfterSkippedDraw();
|
|
void DrawPrimitives(MTLPrimitiveType prim, __unsafe_unretained id<MTLBuffer> vbuf, uint32_t vbOffset, int count, const uint16_t *inds);
|
|
|
|
Draw::DrawContext *draw_;
|
|
MetalRenderManager *rm_ = nullptr;
|
|
|
|
ShaderManagerMetal *shaderManager_ = nullptr;
|
|
TextureCacheMetal *textureCache_ = nullptr;
|
|
FramebufferManagerMetal *framebufferManager_ = nullptr;
|
|
|
|
DenseHashMap<MetalPipelineKey, Pipeline *> pipelines_;
|
|
std::unordered_map<uint64_t, id<MTLDepthStencilState>> depthStencilCache_;
|
|
std::unordered_map<uint32_t, MTLVertexDescriptor *> vertexDescriptors_;
|
|
|
|
// The current state.
|
|
MetalBlendKey blendKey_{};
|
|
MetalDepthStencilKey depthStencilKey_{};
|
|
MetalDynamicState dynState_{};
|
|
GEPrimitiveType lastPrim_ = GE_PRIM_INVALID;
|
|
Pipeline *lastPipeline_ = nullptr;
|
|
|
|
id<MTLTexture> boundSecondary_;
|
|
id<MTLTexture> boundDepal_;
|
|
bool boundDepalSmoothed_ = false;
|
|
id<MTLTexture> nullTexture_;
|
|
id<MTLSamplerState> samplerNearest_;
|
|
id<MTLSamplerState> samplerLinear_;
|
|
|
|
FBOTexState fboTexBindState_ = FBO_TEX_NONE;
|
|
|
|
MetalPushAlloc baseUBO_;
|
|
MetalPushAlloc lightUBO_;
|
|
uint64_t uboFrame_ = (uint64_t)-1;
|
|
};
|