mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-13 09:39:33 +01:00
GPU/Metal, modelled on GPU/Vulkan since the shaders are the same Vulkan GLSL, translated to MSL. Drawing goes through the render manager's state setters, so it shares encoders with thin3d. thin3d fixes found along the way: - Binding the framebuffer that's already the target continues the pass, applying clears and ignoring DontCare, like VulkanRenderManager. - Separate depth or stencil copies, through a buffer, so the stencil survives a framebuffer resize (Need for Speed Shift). - Stencil export, so stencil uploads take one pass. Out of the 1061 dumps in ppsspp-framedumps, 1058 render identically to Vulkan (two of the others are bad dumps), as do the eight benchmark savestates. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
167 lines
5.0 KiB
C++
167 lines
5.0 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();
|
|
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();
|
|
id<MTLRenderPipelineState> GetPipeline(const VShaderID &vsid, const FShaderID &fsid, bool useHWTransform);
|
|
id<MTLDepthStencilState> GetDepthStencilState(const MetalDepthStencilKey &key);
|
|
bool ApplyState(id<MTLRenderPipelineState> pipeline, bool overrideStencilRef, uint8_t stencilRef);
|
|
void BindResources();
|
|
void ResetAfterSkippedDraw();
|
|
void DrawPrimitives(MTLPrimitiveType prim, 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;
|
|
|
|
struct Pipeline {
|
|
id<MTLRenderPipelineState> pso;
|
|
uint32_t fragmentArrayTextureMask;
|
|
};
|
|
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_{};
|
|
uint32_t fragmentArrayTextureMask_ = 0;
|
|
GEPrimitiveType lastPrim_ = GE_PRIM_INVALID;
|
|
__unsafe_unretained id<MTLRenderPipelineState> lastPipeline_ = nil;
|
|
|
|
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;
|
|
};
|