diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h b/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h index ea292d6b47..8459fafaef 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h @@ -186,6 +186,13 @@ struct plMaterialLightingDescriptor uint8_t specularSrc; bool invertAlpha; + +#ifndef __METAL_VERSION__ + bool operator==(const plMaterialLightingDescriptor& rhs) const + { + return memcmp(this, &rhs, sizeof(plMaterialLightingDescriptor)) == 0; + } +#endif }; struct VertexUniforms @@ -205,6 +212,13 @@ struct VertexUniforms float3 sampleLocation(size_t index, thread float3 *texCoords, const float4 normal, const float4 camPosition) constant; half4 calcFog(float4 camPosition) constant; #endif + +#ifndef __METAL_VERSION__ + bool operator==(const VertexUniforms& rhs) const + { + return memcmp(this, &rhs, sizeof(VertexUniforms)) == 0; + } +#endif }; #ifndef __METAL_VERSION__ static_assert(std::is_trivial_v, "VertexUniforms must be a trivial type!"); @@ -216,6 +230,14 @@ struct plMetalLights { uint8_t count; plMetalShaderLightSource lampSources[kMetalMaxLightCount]; + +#ifndef __METAL_VERSION__ + bool operator==(const plMetalLights& rhs) const + { + size_t lightSize = offsetof(plMetalLights, lampSources) + (sizeof(plMetalShaderLightSource) * count); + return rhs.count == count && memcmp(&rhs, this, lightSize ) == 0; + } +#endif }; #ifndef __METAL_VERSION__ static_assert(std::is_trivial_v, "plMetalLights must be a trivial type!"); diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp index 54ee108846..52294dbd90 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp @@ -149,24 +149,15 @@ bool plRenderTriListFunc::RenderPrims() const plProfile_IncCount(DrawTriangles, fNumTris); plProfile_Inc(DrawPrimStatic); + // FIXME: Why is fCurrentRenderPassUniforms stored as a reference? + // FIXME: Replace memory comparison with dirty bool size_t uniformsSize = offsetof(VertexUniforms, uvTransforms) + sizeof(UVOutDescriptor) * fDevice->fPipeline->fCurrNumLayers; - fDevice->CurrentRenderCommandEncoder()->setVertexBytes(fDevice->fPipeline->fCurrentRenderPassUniforms, sizeof(VertexUniforms), VertexShaderArgumentFixedFunctionUniforms); - - fDevice->CurrentRenderCommandEncoder()->setVertexBytes(&fDevice->fPipeline->fCurrentRenderPassMaterialLighting, sizeof(plMaterialLightingDescriptor), VertexShaderArgumentMaterialLighting); - if (PLASMA_PER_PIXEL_LIGHTING) - { - fDevice->CurrentRenderCommandEncoder()->setFragmentBytes(&fDevice->fPipeline->fCurrentRenderPassMaterialLighting, sizeof(plMaterialLightingDescriptor), FragmentShaderArgumentMaterialLighting); - } - - plMetalLights* lights = &fDevice->fPipeline->fLights; - size_t lightSize = offsetof(plMetalLights, lampSources) + (sizeof(plMetalShaderLightSource) * lights->count); - - if (PLASMA_PER_PIXEL_LIGHTING) + if ( !(fDevice->fPipeline->fState.fCurrentVertexUniforms.has_value() && fDevice->fPipeline->fState.fCurrentVertexUniforms == *fDevice->fPipeline->fCurrentRenderPassUniforms) ) { - fDevice->CurrentRenderCommandEncoder()->setFragmentBytes(lights, sizeof(plMetalLights), FragmentShaderArgumentLights); - } else { - fDevice->CurrentRenderCommandEncoder()->setVertexBytes(lights, sizeof(plMetalLights), VertexShaderArgumentLights); + fDevice->fPipeline->fState.fCurrentVertexUniforms = *fDevice->fPipeline->fCurrentRenderPassUniforms; + fDevice->CurrentRenderCommandEncoder()->setVertexBytes(fDevice->fPipeline->fCurrentRenderPassUniforms, sizeof(VertexUniforms), VertexShaderArgumentFixedFunctionUniforms); } + fDevice->CurrentRenderCommandEncoder()->drawIndexedPrimitives(MTL::PrimitiveTypeTriangle, fNumTris * 3, MTL::IndexTypeUInt16, fDevice->fCurrentIndexBuffer, (sizeof(uint16_t) * fIStart)); } @@ -1220,6 +1211,7 @@ void plMetalPipeline::IRenderBufferSpan(const plIcicle& span, hsGDeviceRef* vb, uint32_t pass; for (pass = 0; pass < mRef->GetNumPasses(); pass++) { if (IHandleMaterialPass(material, pass, &span, vRef)) { + IBindLights(); render.RenderPrims(); } @@ -1394,6 +1386,7 @@ void plMetalPipeline::IRenderProjectionEach(const plRenderPrimFunc& render, hsGM IHandleMaterialPass(material, iPass, &span, vRef, false); IScaleLight(0, true); + IBindLights(); // Do the render with projection. render.RenderPrims(); @@ -1503,7 +1496,8 @@ void plMetalPipeline::IRenderAuxSpan(const plSpan& span, const plAuxSpan* aux) fCurrentRenderPassMaterialLighting.emissiveSrc = 0.0; fCurrentRenderPassMaterialLighting.specularSrc = 1.0; } - + + IBindLights(); render.RenderPrims(); } } @@ -1680,6 +1674,8 @@ bool plMetalPipeline::IHandleMaterialPass(hsGMaterial* material, uint32_t pass, preEncodeTransform, postEncodeTransform); } + + fLightingPerPixel = fragmentShaderDescription.fUsePerPixelLighting = PLASMA_FORCE_PER_PIXEL_LIGHTING; plMetalDevice::plMetalLinkedPipeline* linkedPipeline = plMetalMaterialPassPipelineState(&fDevice, vRef, fragmentShaderDescription).GetRenderPipelineState(); const MTL::RenderPipelineState* pipelineState = linkedPipeline->pipelineState; @@ -1693,6 +1689,33 @@ bool plMetalPipeline::IHandleMaterialPass(hsGMaterial* material, uint32_t pass, return true; } +void plMetalPipeline::IBindLights() +{ + size_t lightSize = offsetof(plMetalLights, lampSources) + (sizeof(plMetalShaderLightSource) * fLights.count); + + // FIXME: These states should support dirtying instead of expense memcmps + if ( !(fState.fBoundLights.has_value() && fState.fBoundLights == fLights) ) + { + fState.fBoundLights = fLights; + if (fLightingPerPixel) + { + fDevice.CurrentRenderCommandEncoder()->setFragmentBytes(&fLights, sizeof(plMetalLights), FragmentShaderArgumentLights); + } else { + fDevice.CurrentRenderCommandEncoder()->setVertexBytes(&fLights, sizeof(plMetalLights), VertexShaderArgumentLights); + } + } + + if ( !(fState.fBoundMaterialProperties.has_value() && fState.fBoundMaterialProperties == fCurrentRenderPassMaterialLighting) ) + { + fState.fBoundMaterialProperties = fCurrentRenderPassMaterialLighting; + fDevice.CurrentRenderCommandEncoder()->setVertexBytes(&fDevice.fPipeline->fCurrentRenderPassMaterialLighting, sizeof(plMaterialLightingDescriptor), VertexShaderArgumentMaterialLighting); + if (fLightingPerPixel) + { + fDevice.CurrentRenderCommandEncoder()->setFragmentBytes(&fDevice.fPipeline->fCurrentRenderPassMaterialLighting, sizeof(plMaterialLightingDescriptor), FragmentShaderArgumentMaterialLighting); + } + } +} + // ISetPipeConsts ////////////////////////////////////////////////////////////////// // A shader can request that the pipeline fill in certain constants that are indeterminate // until the pipeline is about to render the object the shader is applied to. For example, @@ -2512,6 +2535,8 @@ void plMetalPipeline::IDrawPlate(plPlate* plate) // FIXME: Hacking the old texture drawing into the plate path mRef->prepareTextures(fDevice.CurrentRenderCommandEncoder(), 0); + // FIXME: Plates don't participate properly in caching + fState.fCurrentVertexUniforms.reset(); fDevice.CurrentRenderCommandEncoder()->setVertexBytes(&uniforms, sizeof(VertexUniforms), VertexShaderArgumentFixedFunctionUniforms); pm->EncodeDraw(fDevice.CurrentRenderCommandEncoder()); @@ -4390,7 +4415,10 @@ void plMetalPipeline::plMetalPipelineCurrentState::Reset() fCurrentPipelineState = nullptr; fCurrentDepthStencilState = nullptr; fCurrentVertexBuffer = nullptr; + fBoundLights.reset(); + fBoundMaterialProperties.reset(); fCurrentCullMode.reset(); + fCurrentVertexUniforms.reset(); for (auto& layer : layerStates) { layer.clampFlag = hsGMatState::hsGMatClampFlags(-1); diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.h b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.h index 185f912a74..99051c1296 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.h +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.h @@ -263,8 +263,10 @@ class plMetalPipeline : public pl3DPipeline void PushCurrentLightSources(); void PopCurrentLightSources(); + void IBindLights(); plMetalLights fLights; std::vector fLightSourceStack; + bool fLightingPerPixel; static plMetalEnumerate enumerator; @@ -284,12 +286,16 @@ class plMetalPipeline : public pl3DPipeline hsGMatState::hsGMatClampFlags clampFlag; } layerStates[8]; - std::optional fCurrentCullMode; - const MTL::RenderPipelineState* fCurrentPipelineState; - MTL::Buffer* fCurrentVertexBuffer; - MTL::DepthStencilState* fCurrentDepthStencilState; + std::optional fCurrentCullMode; + const MTL::RenderPipelineState* fCurrentPipelineState; + MTL::Buffer* fCurrentVertexBuffer; + MTL::DepthStencilState* fCurrentDepthStencilState; + std::optional fBoundLights; + std::optional fBoundMaterialProperties; + std::optional fCurrentVertexUniforms; void Reset(); + } fState; }; diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.cpp b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.cpp index 9801ebe804..db58b1cf77 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.cpp +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.cpp @@ -110,8 +110,7 @@ void plMetalMaterialPassPipelineState::GetFunctionConstants(MTL::FunctionConstan constants->setConstantValues(&fFragmentShaderDescription.fPassTypes, MTL::DataTypeUChar, NS::Range(FunctionConstantSources, 8)); constants->setConstantValues(&fFragmentShaderDescription.fBlendModes, MTL::DataTypeUInt, NS::Range(FunctionConstantBlendModes, 8)); constants->setConstantValues(&fFragmentShaderDescription.fMiscFlags, MTL::DataTypeUInt, NS::Range(FunctionConstantLayerFlags, 8)); - bool perPixelLighting = PLASMA_PER_PIXEL_LIGHTING; - constants->setConstantValue(&perPixelLighting, MTL::DataTypeBool, FunctionConstantPerPixelLighting); + constants->setConstantValue(&fFragmentShaderDescription.fUsePerPixelLighting, MTL::DataTypeBool, FunctionConstantPerPixelLighting); } size_t plMetalMaterialPassPipelineState::GetHash() const diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.h b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.h index d2279c1281..61ce9d7a51 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.h +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.h @@ -50,8 +50,8 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plMetalDevice.h" #include "plSurface/plShaderTable.h" -#ifndef PLASMA_PER_PIXEL_LIGHTING -#define PLASMA_PER_PIXEL_LIGHTING 0 +#ifndef PLASMA_FORCE_PER_PIXEL_LIGHTING +#define PLASMA_FORCE_PER_PIXEL_LIGHTING 0 #endif enum plMetalPipelineType @@ -151,12 +151,13 @@ struct plMetalFragmentShaderDescription uint32_t fBlendModes[8]; uint32_t fMiscFlags[8]; uint8_t fNumLayers; + bool fUsePerPixelLighting; size_t hash; bool operator==(const plMetalFragmentShaderDescription& p) const { - bool match = fNumLayers == p.fNumLayers && memcmp(fPassTypes, p.fPassTypes, sizeof(fPassTypes)) == 0 && memcmp(fBlendModes, p.fBlendModes, sizeof(fBlendModes)) == 0 && memcmp(fMiscFlags, p.fMiscFlags, sizeof(fMiscFlags)) == 0; + bool match = fNumLayers == p.fNumLayers && memcmp(fPassTypes, p.fPassTypes, sizeof(fPassTypes)) == 0 && memcmp(fBlendModes, p.fBlendModes, sizeof(fBlendModes)) == 0 && memcmp(fMiscFlags, p.fMiscFlags, sizeof(fMiscFlags)) == 0 && fUsePerPixelLighting == p.fUsePerPixelLighting; return match; } @@ -173,6 +174,8 @@ struct plMetalFragmentShaderDescription std::size_t value = std::hash()(fNumLayers); value ^= std::hash()(fNumLayers); + + value ^= std::hash()(fUsePerPixelLighting); for (int i = 0; i < 8; i++) { value ^= std::hash()(fBlendModes[i]);