From 6ae48c13389490dd5a8200cbcb7bf9aeaae38ef6 Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Tue, 26 Nov 2024 22:44:45 -0800 Subject: [PATCH 1/6] Implementing light range support in Metal --- .../pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal | 5 +++++ .../FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h | 1 + .../Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp | 7 +++++++ 3 files changed, 13 insertions(+) diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal b/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal index 7623c5c186..bbd4ad2db1 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal @@ -212,6 +212,11 @@ vertex ColorInOut pipelineVertexShader(Vertex in [[stage_in]], // Omni Light in all directions const float3 v2l = lightSource->position.xyz - position.xyz; const float distance = length(v2l); + + if (distance > lightSource->range) { + continue; + } + direction.xyz = normalize(v2l); direction.w = 1.f / (lightSource->constAtten + lightSource->linAtten * distance + lightSource->quadAtten * pow(distance, 2.f)); diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h b/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h index 2845921eb8..1c07832eae 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h @@ -150,6 +150,7 @@ struct plMetalShaderLightSource half4 specular; simd::float3 direction; simd::float4 spotProps; // (falloff, theta, phi) + __fp16 range; __fp16 constAtten; __fp16 linAtten; __fp16 quadAtten; diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp index 951fd2590a..ba8989b160 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp @@ -2374,6 +2374,8 @@ void plMetalPipeline::IEnableLight(size_t i, plLightInfo* light) plDirectionalLightInfo* dirLight = nullptr; plOmniLightInfo* omniLight = nullptr; plSpotLightInfo* spotLight = nullptr; + + const float maxRange = 32767.f; if ((dirLight = plDirectionalLightInfo::ConvertNoRef(light)) != nullptr) { hsVector3 lightDir = dirLight->GetWorldDirection(); @@ -2383,6 +2385,9 @@ void plMetalPipeline::IEnableLight(size_t i, plLightInfo* light) fLights.lampSources[i].constAtten = 1.0f; fLights.lampSources[i].linAtten = 0.0f; fLights.lampSources[i].quadAtten = 0.0f; + + fLights.lampSources[i].range = maxRange; + } else if ((omniLight = plOmniLightInfo::ConvertNoRef(light)) != nullptr) { hsPoint3 pos = omniLight->GetWorldPosition(); fLights.lampSources[i].position = {pos.fX, pos.fY, pos.fZ, 1.0}; @@ -2392,6 +2397,8 @@ void plMetalPipeline::IEnableLight(size_t i, plLightInfo* light) fLights.lampSources[i].constAtten = omniLight->GetConstantAttenuation(); fLights.lampSources[i].linAtten = omniLight->GetLinearAttenuation(); fLights.lampSources[i].quadAtten = omniLight->GetQuadraticAttenuation(); + + fLights.lampSources[i].range = omniLight->GetRadius(); if (!omniLight->GetProjection() && (spotLight = plSpotLightInfo::ConvertNoRef(omniLight)) != nullptr) { hsVector3 lightDir = spotLight->GetWorldDirection(); From 775744aac34ed7509fd2c5c2dbb8407b270faa9e Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Sun, 7 Jan 2024 15:13:57 -0800 Subject: [PATCH 2/6] Initial Metal lighting calc cleanup --- .../ShaderSrc/FixedPipelineShaders.metal | 68 +++++---- .../pfMetalPipeline/ShaderSrc/ShaderTypes.h | 21 ++- .../pfMetalPipeline/plMetalPipeline.cpp | 143 +++++++++--------- .../pfMetalPipeline/plMetalPipeline.h | 1 + 4 files changed, 126 insertions(+), 107 deletions(-) diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal b/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal index bbd4ad2db1..57f5e1e4b2 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal @@ -100,7 +100,7 @@ constant const uint32_t miscFlags8 [[ function_constant(FunctionConstantLayerFla constant const uint8_t sourceTypes[MAX_BLEND_PASSES] = { sourceType1, sourceType2, sourceType3, sourceType4, sourceType5, sourceType6, sourceType7, sourceType8}; constant const uint32_t blendModes[MAX_BLEND_PASSES] = { blendModes1, blendModes2, blendModes3, blendModes4, blendModes5, blendModes6, blendModes7, blendModes8}; constant const uint32_t miscFlags[MAX_BLEND_PASSES] = { miscFlags1, miscFlags2, miscFlags3, miscFlags4, miscFlags5, miscFlags6, miscFlags7, miscFlags8}; - constant const uint8_t passCount = (sourceType1 > 0) + (sourceType2 > 0) + (sourceType3 > 0) + (sourceType4 > 0) + (sourceType5 > 0) + (sourceType6 > 0) + (sourceType7 > 0) + (sourceType8 > 0); +constant const uint8_t passCount = (sourceType1 > 0) + (sourceType2 > 0) + (sourceType3 > 0) + (sourceType4 > 0) + (sourceType5 > 0) + (sourceType6 > 0) + (sourceType7 > 0) + (sourceType8 > 0); constant const bool has2DTexture1 = (sourceType1 == PassTypeTexture && hasLayer1); constant const bool has2DTexture2 = (sourceType2 == PassTypeTexture && hasLayer2); @@ -172,31 +172,20 @@ typedef struct float4 position [[position, invariant]]; float3 texCoord1; } ShadowCasterInOut; - -vertex ColorInOut pipelineVertexShader(Vertex in [[stage_in]], - constant VertexUniforms & uniforms [[ buffer( VertexShaderArgumentFixedFunctionUniforms) ]], - constant plMetalLights & lights [[ buffer(VertexShaderArgumentLights) ]], - constant float4x4 & blendMatrix1 [[ buffer(VertexShaderArgumentBlendMatrix1), function_constant(temp_hasOnlyWeight1) ]]) + +half4 calcLitMaterialColor(constant plMetalLights & lights, + const half4 materialColor, + constant plMaterialLightingDescriptor & materialLighting, + const float4 position, + const float3 normal) { - ColorInOut out; - // we should have been able to swizzle, but it didn't work in Xcode beta? Try again later. - const half4 inColor = half4(in.color.b, in.color.g, in.color.r, in.color.a) / half4(255.f); - - const half3 MAmbient = mix(inColor.rgb, uniforms.ambientCol, uniforms.ambientSrc); - const half4 MDiffuse = mix(inColor, uniforms.diffuseCol, uniforms.diffuseSrc); - const half3 MEmissive = mix(inColor.rgb, uniforms.emissiveCol, uniforms.emissiveSrc); - half3 LAmbient = half3(0.h, 0.h, 0.h); half3 LDiffuse = half3(0.h, 0.h, 0.h); - - const float3 Ndirection = normalize(float4(in.normal, 0.f) * uniforms.localToWorldMatrix).xyz; - - float4 position = float4(in.position, 1.f) * uniforms.localToWorldMatrix; - if (temp_hasOnlyWeight1) { - const float4 position2 = float4(in.position, 1.f) * blendMatrix1; - position = (in.weight1 * position) + ((1.f - in.weight1) * position2); - } - + + const half3 MAmbient = mix(materialColor.rgb, materialLighting.ambientCol, materialLighting.ambientSrc); + const half4 MDiffuse = mix(materialColor, materialLighting.diffuseCol, materialLighting.diffuseSrc); + const half3 MEmissive = mix(materialColor.rgb, materialLighting.emissiveCol, materialLighting.emissiveSrc); + for (size_t i = 0; i < lights.count; i++) { constant const plMetalShaderLightSource *lightSource = &lights.lampSources[i]; if (lightSource->scale == 0.0h) @@ -236,16 +225,35 @@ vertex ColorInOut pipelineVertexShader(Vertex in [[stage_in]], } LAmbient.rgb = LAmbient.rgb + half3(direction.w * (lightSource->ambient.rgb * lightSource->scale)); - const float3 dotResult = dot(Ndirection, direction.xyz); + const float3 dotResult = dot(normal, direction.xyz); LDiffuse.rgb = LDiffuse.rgb + MDiffuse.rgb * (lightSource->diffuse.rgb * lightSource->scale) * half3(max(0.f, dotResult) * direction.w); } - const half3 ambient = (MAmbient.rgb) * clamp(uniforms.globalAmb.rgb + LAmbient.rgb, 0.h, 1.h); + const half3 ambient = (MAmbient.rgb) * clamp(materialLighting.globalAmb.rgb + LAmbient.rgb, 0.h, 1.h); const half3 diffuse = clamp(LDiffuse.rgb, 0.h, 1.h); - const half4 material = half4(clamp(ambient + diffuse + MEmissive.rgb, 0.h, 1.h), - abs(uniforms.invVtxAlpha - MDiffuse.a)); + return clamp(half4(ambient + diffuse + MEmissive.rgb, MDiffuse.a), 0.h, 1.h); +} + +vertex ColorInOut pipelineVertexShader(Vertex in [[stage_in]], + constant VertexUniforms & uniforms [[ buffer( VertexShaderArgumentFixedFunctionUniforms) ]], + constant plMaterialLightingDescriptor & materialLighting [[ buffer( VertexShaderArgumentMaterialLighting) ]], + constant plMetalLights & lights [[ buffer(VertexShaderArgumentLights) ]], + constant float4x4 & blendMatrix1 [[ buffer(VertexShaderArgumentBlendMatrix1), function_constant(temp_hasOnlyWeight1) ]]) +{ + ColorInOut out; + const half4 inColor = half4(in.color.b, in.color.g, in.color.r, in.color.a) / half4(255.f); - out.vtxColor = half4(material.rgb, abs(uniforms.invVtxAlpha - MDiffuse.a)); + const float3 Ndirection = normalize(float4(in.normal, 0.f) * uniforms.localToWorldMatrix).xyz; + + float4 position = float4(in.position, 1.f) * uniforms.localToWorldMatrix; + if (temp_hasOnlyWeight1) { + const float4 position2 = float4(in.position, 1.f) * blendMatrix1; + position = (in.weight1 * position) + ((1.f - in.weight1) * position2); + } + + out.vtxColor = calcLitMaterialColor(lights, inColor, materialLighting, position, Ndirection); + out.vtxColor.a = abs(uniforms.invVtxAlpha - out.vtxColor.a); + const float4 vCamPosition = position * uniforms.worldToCameraMatrix; // Fog @@ -617,6 +625,7 @@ fragment half4 shadowFragmentShader(ShadowCasterInOut in [[stage_in]]) vertex ColorInOut shadowCastVertexShader(Vertex in [[ stage_in ]], constant VertexUniforms & uniforms [[ buffer( VertexShaderArgumentFixedFunctionUniforms) ]], + constant plMaterialLightingDescriptor & materialLighting [[ buffer( VertexShaderArgumentMaterialLighting) ]], constant plShadowState & shadowState [[ buffer(VertexShaderArgumentShadowState) ]]) { ColorInOut out; @@ -624,7 +633,8 @@ vertex ColorInOut shadowCastVertexShader(Vertex in float4 position = (float4(in.position, 1.f) * uniforms.localToWorldMatrix); const float3 Ndirection = normalize(float4(in.normal, 0.f) * uniforms.localToWorldMatrix).xyz; // Shadow casting uses the diffuse material color to control opacity - const half4 MDiffuse = uniforms.diffuseCol; + // FIXME: Should this be something more specific + const half4 MDiffuse = materialLighting.diffuseCol; //w is attenation float4 direction; diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h b/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h index 1c07832eae..b5a5eede97 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h @@ -62,6 +62,8 @@ enum plMetalVertexShaderArgument VertexShaderArgumentMaterialShaderUniforms = 3, /// Light Table VertexShaderArgumentLights = 4, + /// Material properties for vertex lighting + VertexShaderArgumentMaterialLighting = 5, /// Blend matrix for GPU side animation blending VertexShaderArgumentBlendMatrix1 = 6, /// Describes the state of a shadow caster for shadow cast shader @@ -169,15 +171,8 @@ struct UVOutDescriptor static_assert(std::is_trivial_v, "UVOutDescriptor must be a trivial type!"); #endif -struct VertexUniforms +struct plMaterialLightingDescriptor { - // transformation - matrix_float4x4 projectionMatrix; - matrix_float4x4 localToWorldMatrix; - matrix_float4x4 cameraToWorldMatrix; - matrix_float4x4 worldToCameraMatrix; - - // lighting half4 globalAmb; half3 ambientCol; uint8_t ambientSrc; @@ -187,6 +182,16 @@ struct VertexUniforms uint8_t emissiveSrc; half3 specularCol; uint8_t specularSrc; +}; + +struct VertexUniforms +{ + // transformation + matrix_float4x4 projectionMatrix; + matrix_float4x4 localToWorldMatrix; + matrix_float4x4 cameraToWorldMatrix; + matrix_float4x4 worldToCameraMatrix; + bool invVtxAlpha; uint8_t fogExponential; diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp index ba8989b160..a40cc30f53 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp @@ -151,6 +151,7 @@ bool plRenderTriListFunc::RenderPrims() const 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); plMetalLights* lights = &fDevice->fPipeline->fLights; size_t lightSize = offsetof(plMetalLights, lampSources) + (sizeof(plMetalShaderLightSource) * lights->count); @@ -1304,16 +1305,18 @@ void plMetalPipeline::IRenderProjection(const plRenderPrimFunc& render, plLightI IScaleLight(0, true); - fCurrentRenderPassUniforms->ambientSrc = 1; - fCurrentRenderPassUniforms->diffuseSrc = 1; - fCurrentRenderPassUniforms->emissiveSrc = 1; - fCurrentRenderPassUniforms->specularSrc = 1; - fCurrentRenderPassUniforms->globalAmb = {1.f, 1.f, 1.f}; - fCurrentRenderPassUniforms->ambientCol = {0.f, 0.f, 0.f}; - fCurrentRenderPassUniforms->emissiveCol = {0.f, 0.f, 0.f}; - fCurrentRenderPassUniforms->specularCol = {0.f, 0.f, 0.f}; + fCurrentRenderPassMaterialLighting.ambientSrc = 1; + fCurrentRenderPassMaterialLighting.diffuseSrc = 1; + fCurrentRenderPassMaterialLighting.emissiveSrc = 1; + fCurrentRenderPassMaterialLighting.specularSrc = 1; + fCurrentRenderPassMaterialLighting.globalAmb = {1.f, 1.f, 1.f}; + fCurrentRenderPassMaterialLighting.ambientCol = {0.f, 0.f, 0.f}; + fCurrentRenderPassMaterialLighting.emissiveCol = {0.f, 0.f, 0.f}; + fCurrentRenderPassMaterialLighting.specularCol = {0.f, 0.f, 0.f}; + fCurrentRenderPassMaterialLighting.diffuseCol = {1.f, 1.f, 1.f, 1.f}; + + // FIXME: NEEDED? fCurrentRenderPassUniforms->fogColor = {0.f, 0.f, 0.f}; - fCurrentRenderPassUniforms->diffuseCol = {1.f, 1.f, 1.f, 1.f}; matrix_float4x4 tXfm; hsMatrix2SIMD(proj->GetTransform(), &tXfm); @@ -1483,12 +1486,12 @@ void plMetalPipeline::IRenderAuxSpan(const plSpan& span, const plAuxSpan* aux) for (int32_t pass = 0; pass < mRef->GetNumPasses(); pass++) { IHandleMaterialPass(material, pass, &span, vRef); if (aux->fFlags & plAuxSpan::kOverrideLiteModel) { - fCurrentRenderPassUniforms->ambientCol = {1.0f, 1.0f, 1.0f}; + fCurrentRenderPassMaterialLighting.ambientCol = {1.0f, 1.0f, 1.0f}; - fCurrentRenderPassUniforms->diffuseSrc = 1.0; - fCurrentRenderPassUniforms->ambientSrc = 1.0; - fCurrentRenderPassUniforms->emissiveSrc = 0.0; - fCurrentRenderPassUniforms->specularSrc = 1.0; + fCurrentRenderPassMaterialLighting.diffuseSrc = 1.0; + fCurrentRenderPassMaterialLighting.ambientSrc = 1.0; + fCurrentRenderPassMaterialLighting.emissiveSrc = 0.0; + fCurrentRenderPassMaterialLighting.specularSrc = 1.0; } render.RenderPrims(); @@ -2118,18 +2121,18 @@ void plMetalPipeline::ICalcLighting(plMetalMaterialShaderRef* mRef, const plLaye // plProfile_Inc(MatLightState); if (IsDebugFlagSet(plPipeDbg::kFlagAllBright)) { - fCurrentRenderPassUniforms->globalAmb = {1.f, 1.f, 1.f, 1.f}; + fCurrentRenderPassMaterialLighting.globalAmb = {1.f, 1.f, 1.f, 1.f}; - fCurrentRenderPassUniforms->ambientCol = {1.f, 1.f, 1.f}; - fCurrentRenderPassUniforms->diffuseCol = {1.f, 1.f, 1.f, 1.f}; - fCurrentRenderPassUniforms->emissiveCol = {1.f, 1.f, 1.f}; - fCurrentRenderPassUniforms->emissiveCol = {1.f, 1.f, 1.f}; - fCurrentRenderPassUniforms->specularCol = {1.f, 1.f, 1.f}; + fCurrentRenderPassMaterialLighting.ambientCol = {1.f, 1.f, 1.f}; + fCurrentRenderPassMaterialLighting.diffuseCol = {1.f, 1.f, 1.f}; + fCurrentRenderPassMaterialLighting.emissiveCol = {1.f, 1.f, 1.f}; + fCurrentRenderPassMaterialLighting.emissiveCol = {1.f, 1.f, 1.f}; + fCurrentRenderPassMaterialLighting.specularCol = {1.f, 1.f, 1.f}; - fCurrentRenderPassUniforms->ambientSrc = 1; - fCurrentRenderPassUniforms->diffuseSrc = 1; - fCurrentRenderPassUniforms->emissiveSrc = 1; - fCurrentRenderPassUniforms->specularSrc = 1; + fCurrentRenderPassMaterialLighting.ambientSrc = 1; + fCurrentRenderPassMaterialLighting.diffuseSrc = 1; + fCurrentRenderPassMaterialLighting.emissiveSrc = 1; + fCurrentRenderPassMaterialLighting.specularSrc = 1; return; } @@ -2149,42 +2152,42 @@ void plMetalPipeline::ICalcLighting(plMetalMaterialShaderRef* mRef, const plLaye case plSpan::kLiteMaterial: // Material shading { if (state.fShadeFlags & hsGMatState::kShadeWhite) { - fCurrentRenderPassUniforms->globalAmb = {1.f, 1.f, 1.f, 1.f}; - fCurrentRenderPassUniforms->ambientCol = {1.f, 1.f, 1.f}; + fCurrentRenderPassMaterialLighting.globalAmb = {1.f, 1.f, 1.f, 1.f}; + fCurrentRenderPassMaterialLighting.ambientCol = {1.f, 1.f, 1.f}; } else if (IsDebugFlagSet(plPipeDbg::kFlagNoPreShade)) { - fCurrentRenderPassUniforms->globalAmb = {0.f, 0.f, 0.f, 1.f}; - fCurrentRenderPassUniforms->ambientCol = {0.f, 0.f, 0.f}; + fCurrentRenderPassMaterialLighting.globalAmb = {0.f, 0.f, 0.f, 1.f}; + fCurrentRenderPassMaterialLighting.ambientCol = {0.f, 0.f, 0.f}; } else { hsColorRGBA amb = currLayer->GetPreshadeColor(); - fCurrentRenderPassUniforms->globalAmb = {static_cast(amb.r), static_cast(amb.g), static_cast(amb.b), 1.f}; - fCurrentRenderPassUniforms->ambientCol = {static_cast(amb.r), static_cast(amb.g), static_cast(amb.b)}; + fCurrentRenderPassMaterialLighting.globalAmb = {static_cast(amb.r), static_cast(amb.g), static_cast(amb.b), 1.f}; + fCurrentRenderPassMaterialLighting.ambientCol = {static_cast(amb.r), static_cast(amb.g), static_cast(amb.b)}; } hsColorRGBA dif = currLayer->GetRuntimeColor(); - fCurrentRenderPassUniforms->diffuseCol = {static_cast(dif.r), static_cast(dif.g), static_cast(dif.b), static_cast(currLayer->GetOpacity())}; + fCurrentRenderPassMaterialLighting.diffuseCol = {static_cast(dif.r), static_cast(dif.g), static_cast(dif.b), static_cast(currLayer->GetOpacity())}; hsColorRGBA em = currLayer->GetAmbientColor(); - fCurrentRenderPassUniforms->emissiveCol = {static_cast(em.r), static_cast(em.g), static_cast(em.b)}; + fCurrentRenderPassMaterialLighting.emissiveCol = {static_cast(em.r), static_cast(em.g), static_cast(em.b)}; // Set specular properties if (state.fShadeFlags & hsGMatState::kShadeSpecular) { hsColorRGBA spec = currLayer->GetSpecularColor(); - fCurrentRenderPassUniforms->specularCol = {static_cast(spec.r), static_cast(spec.g), static_cast(spec.b)}; + fCurrentRenderPassMaterialLighting.specularCol = {static_cast(spec.r), static_cast(spec.g), static_cast(spec.b)}; #if 0 mat.Power = currLayer->GetSpecularPower(); #endif } else { - fCurrentRenderPassUniforms->specularCol = {0.f, 0.f, 0.f}; + fCurrentRenderPassMaterialLighting.specularCol = {0.f, 0.f, 0.f}; } - fCurrentRenderPassUniforms->diffuseSrc = 1.f; - fCurrentRenderPassUniforms->emissiveSrc = 1.f; - fCurrentRenderPassUniforms->specularSrc = 1.f; + fCurrentRenderPassMaterialLighting.diffuseSrc = 1.f; + fCurrentRenderPassMaterialLighting.emissiveSrc = 1.f; + fCurrentRenderPassMaterialLighting.specularSrc = 1.f; if (state.fShadeFlags & hsGMatState::kShadeNoShade) { - fCurrentRenderPassUniforms->ambientSrc = 1.f; + fCurrentRenderPassMaterialLighting.ambientSrc = 1.f; } else { - fCurrentRenderPassUniforms->ambientSrc = 0.f; + fCurrentRenderPassMaterialLighting.ambientSrc = 0.f; } fCurrLightingMethod = plSpan::kLiteMaterial; @@ -2193,20 +2196,20 @@ void plMetalPipeline::ICalcLighting(plMetalMaterialShaderRef* mRef, const plLaye case plSpan::kLiteVtxPreshaded: // Vtx preshaded { - fCurrentRenderPassUniforms->globalAmb = {0.f, 0.f, 0.f}; - fCurrentRenderPassUniforms->ambientCol = {0.f, 0.f, 0.f}; - fCurrentRenderPassUniforms->diffuseCol = {0.f, 0.f, 0.f, 0.f}; - fCurrentRenderPassUniforms->emissiveCol = {0.f, 0.f, 0.f}; - fCurrentRenderPassUniforms->specularCol = {0.f, 0.f, 0.f}; + fCurrentRenderPassMaterialLighting.globalAmb = {0.f, 0.f, 0.f}; + fCurrentRenderPassMaterialLighting.ambientCol = {0.f, 0.f, 0.f}; + fCurrentRenderPassMaterialLighting.diffuseCol = {0.f, 0.f, 0.f, 0.f}; + fCurrentRenderPassMaterialLighting.emissiveCol = {0.f, 0.f, 0.f}; + fCurrentRenderPassMaterialLighting.specularCol = {0.f, 0.f, 0.f}; - fCurrentRenderPassUniforms->diffuseSrc = 0.f; - fCurrentRenderPassUniforms->ambientSrc = 1.f; - fCurrentRenderPassUniforms->specularSrc = 1.f; + fCurrentRenderPassMaterialLighting.diffuseSrc = 0.f; + fCurrentRenderPassMaterialLighting.ambientSrc = 1.f; + fCurrentRenderPassMaterialLighting.specularSrc = 1.f; if (state.fShadeFlags & hsGMatState::kShadeEmissive) { - fCurrentRenderPassUniforms->emissiveSrc = 0.f; + fCurrentRenderPassMaterialLighting.emissiveSrc = 0.f; } else { - fCurrentRenderPassUniforms->emissiveSrc = 1.f; + fCurrentRenderPassMaterialLighting.emissiveSrc = 1.f; } fCurrLightingMethod = plSpan::kLiteVtxPreshaded; @@ -2215,30 +2218,30 @@ void plMetalPipeline::ICalcLighting(plMetalMaterialShaderRef* mRef, const plLaye case plSpan::kLiteVtxNonPreshaded: // Vtx non-preshaded { - fCurrentRenderPassUniforms->ambientCol = {0.f, 0.f, 0.f}; - fCurrentRenderPassUniforms->diffuseCol = {0.f, 0.f, 0.f, 0.f}; + fCurrentRenderPassMaterialLighting.ambientCol = {0.f, 0.f, 0.f}; + fCurrentRenderPassMaterialLighting.diffuseCol = {0.f, 0.f, 0.f, 0.f}; hsColorRGBA em = currLayer->GetAmbientColor(); - fCurrentRenderPassUniforms->emissiveCol = {static_cast(em.r), static_cast(em.g), static_cast(em.b)}; + fCurrentRenderPassMaterialLighting.emissiveCol = {static_cast(em.r), static_cast(em.g), static_cast(em.b)}; // Set specular properties if (state.fShadeFlags & hsGMatState::kShadeSpecular) { hsColorRGBA spec = currLayer->GetSpecularColor(); - fCurrentRenderPassUniforms->specularCol = {static_cast(spec.r), static_cast(spec.g), static_cast(spec.b)}; + fCurrentRenderPassMaterialLighting.specularCol = {static_cast(spec.r), static_cast(spec.g), static_cast(spec.b)}; #if 0 mat.Power = currLayer->GetSpecularPower(); #endif } else { - fCurrentRenderPassUniforms->specularCol = {0.f, 0.f, 0.f}; + fCurrentRenderPassMaterialLighting.specularCol = {0.f, 0.f, 0.f}; } hsColorRGBA amb = currLayer->GetPreshadeColor(); - fCurrentRenderPassUniforms->globalAmb = {static_cast(amb.r), static_cast(amb.g), static_cast(amb.b), static_cast(amb.a)}; + fCurrentRenderPassMaterialLighting.globalAmb = {static_cast(amb.r), static_cast(amb.g), static_cast(amb.b), static_cast(amb.a)}; - fCurrentRenderPassUniforms->ambientSrc = 0; - fCurrentRenderPassUniforms->diffuseSrc = 0; - fCurrentRenderPassUniforms->emissiveSrc = 1; - fCurrentRenderPassUniforms->specularSrc = 1; + fCurrentRenderPassMaterialLighting.ambientSrc = 0; + fCurrentRenderPassMaterialLighting.diffuseSrc = 0; + fCurrentRenderPassMaterialLighting.emissiveSrc = 1; + fCurrentRenderPassMaterialLighting.specularSrc = 1; fCurrLightingMethod = plSpan::kLiteVtxNonPreshaded; break; @@ -3204,7 +3207,7 @@ bool plMetalPipeline::IPushShadowCastState(plShadowSlave* slave) return false; // Set texture to U_LUT - fCurrentRenderPassUniforms->specularSrc = 0.0; + fCurrentRenderPassMaterialLighting.specularSrc = 0.0; // if( !ref->fTexture ) //{ @@ -3893,17 +3896,17 @@ void plMetalPipeline::ISetShadowLightState(hsGMaterial* mat) fCurrLightingMethod = plSpan::kLiteShadow; if (mat && mat->GetNumLayers() && mat->GetLayer(0)) - fCurrentRenderPassUniforms->diffuseCol.r = fCurrentRenderPassUniforms->diffuseCol.g = fCurrentRenderPassUniforms->diffuseCol.b = mat->GetLayer(0)->GetOpacity(); + fCurrentRenderPassMaterialLighting.diffuseCol.r = fCurrentRenderPassMaterialLighting.diffuseCol.g = fCurrentRenderPassMaterialLighting.diffuseCol.b = mat->GetLayer(0)->GetOpacity(); else - fCurrentRenderPassUniforms->diffuseCol.r = fCurrentRenderPassUniforms->diffuseCol.g = fCurrentRenderPassUniforms->diffuseCol.b = 1.f; - fCurrentRenderPassUniforms->diffuseCol.a = 1.f; - - fCurrentRenderPassUniforms->diffuseSrc = 1.0f; - fCurrentRenderPassUniforms->emissiveSrc = 1.0f; - fCurrentRenderPassUniforms->emissiveCol = 0.0f; - fCurrentRenderPassUniforms->specularSrc = 0.0f; - fCurrentRenderPassUniforms->ambientSrc = 0.0f; - fCurrentRenderPassUniforms->globalAmb = 0.0f; + fCurrentRenderPassMaterialLighting.diffuseCol.r = fCurrentRenderPassMaterialLighting.diffuseCol.g = fCurrentRenderPassMaterialLighting.diffuseCol.b = 1.f; + fCurrentRenderPassMaterialLighting.diffuseCol.a = 1.f; + + fCurrentRenderPassMaterialLighting.diffuseSrc = 1.0f; + fCurrentRenderPassMaterialLighting.emissiveSrc = 1.0f; + fCurrentRenderPassMaterialLighting.emissiveCol = 0.0f; + fCurrentRenderPassMaterialLighting.specularSrc = 0.0f; + fCurrentRenderPassMaterialLighting.ambientSrc = 0.0f; + fCurrentRenderPassMaterialLighting.globalAmb = 0.0f; } // IDisableLightsForShadow /////////////////////////////////////////////////////////// diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.h b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.h index 4d819490c7..185f912a74 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.h +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.h @@ -168,6 +168,7 @@ class plMetalPipeline : public pl3DPipeline private: VertexUniforms* fCurrentRenderPassUniforms; + plMaterialLightingDescriptor fCurrentRenderPassMaterialLighting; bool fIsFullscreen; From f4ad83da6dca1523e9e8fae82ad322873744f924 Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Sun, 7 Jan 2024 16:17:18 -0800 Subject: [PATCH 3/6] Per pixel lighting option in Metal --- .../ShaderSrc/FixedPipelineShaders.metal | 67 ++++++++++++------- .../pfMetalPipeline/ShaderSrc/ShaderTypes.h | 21 +++--- .../pfMetalPipeline/plMetalPipeline.cpp | 16 ++++- .../pfMetalPipeline/plMetalPipelineState.cpp | 26 +++---- .../pfMetalPipeline/plMetalPipelineState.h | 4 ++ 5 files changed, 85 insertions(+), 49 deletions(-) diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal b/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal index 57f5e1e4b2..99146ed4ee 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal @@ -69,6 +69,9 @@ enum plUVWSrcModifiers: uint32_t using namespace metal; +constant const bool perPixelLighting [[ function_constant(FunctionConstantPerPixelLighting) ]]; +constant const bool perVertexLighting = !perPixelLighting; + constant const uint8_t sourceType1 [[ function_constant(FunctionConstantSources + 0) ]]; constant const uint8_t sourceType2 [[ function_constant(FunctionConstantSources + 1) ]]; constant const uint8_t sourceType3 [[ function_constant(FunctionConstantSources + 2) ]]; @@ -153,16 +156,18 @@ struct FragmentShaderArguments typedef struct { - float4 position [[position]]; - float3 texCoord1 [[function_constant(hasLayer1)]]; - float3 texCoord2 [[function_constant(hasLayer2)]]; - float3 texCoord3 [[function_constant(hasLayer3)]]; - float3 texCoord4 [[function_constant(hasLayer4)]]; - float3 texCoord5 [[function_constant(hasLayer5)]]; - float3 texCoord6 [[function_constant(hasLayer6)]]; - float3 texCoord7 [[function_constant(hasLayer7)]]; - float3 texCoord8 [[function_constant(hasLayer8)]]; - half4 vtxColor [[ centroid_perspective ]]; + float4 position [[ position ]]; + float4 worldPos [[ function_constant(perPixelLighting) ]]; + float3 normal [[ function_constant(perPixelLighting) ]]; + float3 texCoord1 [[ function_constant(hasLayer1) ]]; + float3 texCoord2 [[ function_constant(hasLayer2) ]]; + float3 texCoord3 [[ function_constant(hasLayer3) ]]; + float3 texCoord4 [[ function_constant(hasLayer4) ]]; + float3 texCoord5 [[ function_constant(hasLayer5) ]]; + float3 texCoord6 [[ function_constant(hasLayer6) ]]; + float3 texCoord7 [[ function_constant(hasLayer7) ]]; + float3 texCoord8 [[ function_constant(hasLayer8) ]]; + half4 vtxColor [[ centroid_perspective ]]; half4 fogColor; } ColorInOut; @@ -174,10 +179,10 @@ typedef struct } ShadowCasterInOut; half4 calcLitMaterialColor(constant plMetalLights & lights, - const half4 materialColor, - constant plMaterialLightingDescriptor & materialLighting, - const float4 position, - const float3 normal) + const half4 materialColor, + constant plMaterialLightingDescriptor & materialLighting, + const float4 position, + const float3 normal) { half3 LAmbient = half3(0.h, 0.h, 0.h); half3 LDiffuse = half3(0.h, 0.h, 0.h); @@ -231,13 +236,13 @@ half4 calcLitMaterialColor(constant plMetalLights & lights, const half3 ambient = (MAmbient.rgb) * clamp(materialLighting.globalAmb.rgb + LAmbient.rgb, 0.h, 1.h); const half3 diffuse = clamp(LDiffuse.rgb, 0.h, 1.h); - return clamp(half4(ambient + diffuse + MEmissive.rgb, MDiffuse.a), 0.h, 1.h); + return clamp(half4(ambient + diffuse + MEmissive.rgb, abs(materialLighting.invertAlpha - MDiffuse.a)), 0.h, 1.h); } vertex ColorInOut pipelineVertexShader(Vertex in [[stage_in]], - constant VertexUniforms & uniforms [[ buffer( VertexShaderArgumentFixedFunctionUniforms) ]], - constant plMaterialLightingDescriptor & materialLighting [[ buffer( VertexShaderArgumentMaterialLighting) ]], - constant plMetalLights & lights [[ buffer(VertexShaderArgumentLights) ]], + constant VertexUniforms & uniforms [[ buffer(VertexShaderArgumentFixedFunctionUniforms) ]], + constant plMaterialLightingDescriptor & materialLighting [[ buffer(VertexShaderArgumentMaterialLighting), function_constant(perVertexLighting) ]], + constant plMetalLights & lights [[ buffer(VertexShaderArgumentLights), function_constant(perVertexLighting) ]], constant float4x4 & blendMatrix1 [[ buffer(VertexShaderArgumentBlendMatrix1), function_constant(temp_hasOnlyWeight1) ]]) { ColorInOut out; @@ -250,9 +255,20 @@ vertex ColorInOut pipelineVertexShader(Vertex in [[stage_in]], const float4 position2 = float4(in.position, 1.f) * blendMatrix1; position = (in.weight1 * position) + ((1.f - in.weight1) * position2); } - - out.vtxColor = calcLitMaterialColor(lights, inColor, materialLighting, position, Ndirection); - out.vtxColor.a = abs(uniforms.invVtxAlpha - out.vtxColor.a); + + if (perPixelLighting) + { + // send the world pos on to the pixel shader for lighting + out.worldPos = position; + out.normal = Ndirection; + } + + if (perPixelLighting) + { + out.vtxColor = inColor; + } else { + out.vtxColor = calcLitMaterialColor(lights, inColor, materialLighting, position, Ndirection); + } const float4 vCamPosition = position * uniforms.worldToCameraMatrix; @@ -428,9 +444,12 @@ half4 FragmentShaderArguments::sampleLayer(const size_t index, const half4 verte } fragment half4 pipelineFragmentShader(ColorInOut in [[stage_in]], - const FragmentShaderArguments fragmentShaderArgs) + const FragmentShaderArguments fragmentShaderArgs, + constant plMetalLights & lights [[ buffer(FragmentShaderArgumentLights), function_constant(perPixelLighting) ]], + constant plMaterialLightingDescriptor & materialLighting [[ buffer(FragmentShaderArgumentMaterialLighting), function_constant(perPixelLighting) ]]) { - half4 currentColor = in.vtxColor; + const half4 lightingContributionColor = perPixelLighting ? calcLitMaterialColor(lights, in.vtxColor, materialLighting, in.worldPos, in.normal) : in.vtxColor; + half4 currentColor = lightingContributionColor; /* SPECIAL PLASMA RULE: @@ -458,7 +477,7 @@ fragment half4 pipelineFragmentShader(ColorInOut in [[stage_in]], } } - currentColor = half4(in.vtxColor.rgb, 1.0h) * currentColor; + currentColor = lightingContributionColor * currentColor; } currentColor.rgb = mix(in.fogColor.rgb, currentColor.rgb, (float)clamp(in.fogColor.a, 0.0h, 1.0h)); diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h b/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h index b5a5eede97..b679217fbc 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h @@ -54,7 +54,7 @@ typedef __attribute__((__ext_vector_type__(3))) half half3; typedef __attribute__((__ext_vector_type__(4))) half half4; #endif -enum plMetalVertexShaderArgument +enum plMetalShaderArgument { /// Material State VertexShaderArgumentFixedFunctionUniforms = 2, @@ -67,11 +67,8 @@ enum plMetalVertexShaderArgument /// Blend matrix for GPU side animation blending VertexShaderArgumentBlendMatrix1 = 6, /// Describes the state of a shadow caster for shadow cast shader - VertexShaderArgumentShadowState = 9 -}; - -enum plMetalFragmentShaderArgumentIndex -{ + VertexShaderArgumentShadowState = 9, + /// Texture is a legacy argument for the simpler plate shader FragmentShaderArgumentTexture = 1, /// Fragment uniforms @@ -79,7 +76,11 @@ enum plMetalFragmentShaderArgumentIndex /// Legacy argument buffer FragmentShaderArgumentUniforms = 5, /// Layer index of alpha for shadow fragment shader - FragmentShaderArgumentShadowCastAlphaSrc = 8 + FragmentShaderArgumentShadowCastAlphaSrc = 8, + /// Light Table + FragmentShaderArgumentLights = 10, + /// Material properties for vertex lighting + FragmentShaderArgumentMaterialLighting = 11 }; enum plMetalVertexAttribute @@ -112,6 +113,8 @@ enum plMetalFunctionConstant FunctionConstantLayerFlags = 18, /// Numbrer of weights in the FVF vertex layout. FunctionConstantNumWeights = 26, + /// Per pixel lighting enable flag + FunctionConstantPerPixelLighting = 27, }; enum plMetalLayerPassType: uint8_t @@ -182,6 +185,8 @@ struct plMaterialLightingDescriptor uint8_t emissiveSrc; half3 specularCol; uint8_t specularSrc; + + bool invertAlpha; }; struct VertexUniforms @@ -192,8 +197,6 @@ struct VertexUniforms matrix_float4x4 cameraToWorldMatrix; matrix_float4x4 worldToCameraMatrix; - bool invVtxAlpha; - uint8_t fogExponential; simd::float2 fogValues; half3 fogColor; diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp index a40cc30f53..216f7ff797 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp @@ -151,12 +151,22 @@ bool plRenderTriListFunc::RenderPrims() const 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); - fDevice->CurrentRenderCommandEncoder()->setVertexBytes(lights, sizeof(plMetalLights), VertexShaderArgumentLights); + if (PLASMA_PER_PIXEL_LIGHTING) + { + fDevice->CurrentRenderCommandEncoder()->setFragmentBytes(lights, sizeof(plMetalLights), FragmentShaderArgumentLights); + } else { + fDevice->CurrentRenderCommandEncoder()->setVertexBytes(lights, sizeof(plMetalLights), VertexShaderArgumentLights); + } fDevice->CurrentRenderCommandEncoder()->drawIndexedPrimitives(MTL::PrimitiveTypeTriangle, fNumTris * 3, MTL::IndexTypeUInt16, fDevice->fCurrentIndexBuffer, (sizeof(uint16_t) * fIStart)); } @@ -1618,9 +1628,9 @@ bool plMetalPipeline::IHandleMaterialPass(hsGMaterial* material, uint32_t pass, } if (s.fBlendFlags & hsGMatState::kBlendInvertVtxAlpha) - fCurrentRenderPassUniforms->invVtxAlpha = true; + fCurrentRenderPassMaterialLighting.invertAlpha = true; else - fCurrentRenderPassUniforms->invVtxAlpha = false; + fCurrentRenderPassMaterialLighting.invertAlpha = false; std::vector& spanLights = currSpan->GetLightList(false); diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.cpp b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.cpp index 1225f4c452..9801ebe804 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.cpp +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.cpp @@ -110,6 +110,8 @@ 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); } size_t plMetalMaterialPassPipelineState::GetHash() const @@ -267,23 +269,21 @@ void plMetalRenderSpanPipelineState::ConfigureBlendMode(const uint32_t blendMode MTL::Function* plMetalMaterialPassPipelineState::GetVertexFunction(MTL::Library* library) { NS::Error* error = nullptr; - MTL::FunctionConstantValues* constants = MTL::FunctionConstantValues::alloc()->init()->autorelease(); - GetFunctionConstants(constants); - MTL::Function* function = library->newFunction( - NS::String::string("pipelineVertexShader", NS::ASCIIStringEncoding), - MakeFunctionConstants(), - &error) - ->autorelease(); - return function; + MTL::Function* function = library->newFunction(NS::String::string("pipelineVertexShader", NS::ASCIIStringEncoding), + MakeFunctionConstants(), + &error); + assert(!error); + return function->autorelease(); } MTL::Function* plMetalMaterialPassPipelineState::GetFragmentFunction(MTL::Library* library) { - return library->newFunction( - NS::String::string("pipelineFragmentShader", NS::ASCIIStringEncoding), - MakeFunctionConstants(), - (NS::Error**)nullptr) - ->autorelease(); + NS::Error* error = nullptr; + MTL::Function* function = library->newFunction(NS::String::string("pipelineFragmentShader", NS::ASCIIStringEncoding), + MakeFunctionConstants(), + &error); + assert(!error); + return function->autorelease(); } plMetalMaterialPassPipelineState::~plMetalMaterialPassPipelineState() diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.h b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.h index 0f5cf4114d..d2279c1281 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.h +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.h @@ -50,6 +50,10 @@ 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 +#endif + enum plMetalPipelineType { // Unknown is for abstract types, don't use it From 9dc283c7849273de781f5ab3f9f7748a078184e2 Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Sat, 10 Aug 2024 20:41:19 -0700 Subject: [PATCH 4/6] Aligning per pixel lighting shader with code style --- .../pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal b/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal index 99146ed4ee..507fa655b5 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal @@ -256,15 +256,11 @@ vertex ColorInOut pipelineVertexShader(Vertex in [[stage_in]], position = (in.weight1 * position) + ((1.f - in.weight1) * position2); } - if (perPixelLighting) - { + if (perPixelLighting) { // send the world pos on to the pixel shader for lighting out.worldPos = position; out.normal = Ndirection; - } - - if (perPixelLighting) - { + out.vtxColor = inColor; } else { out.vtxColor = calcLitMaterialColor(lights, inColor, materialLighting, position, Ndirection); From 8ed728aac8f126e71793620aaa8744ea2d5f46c4 Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Sun, 17 Nov 2024 22:53:35 -0800 Subject: [PATCH 5/6] Allowing per pixel lighting to be turned on dynamically Also cleaning up some redundant state binding --- .../pfMetalPipeline/ShaderSrc/ShaderTypes.h | 22 +++++++ .../pfMetalPipeline/plMetalPipeline.cpp | 60 ++++++++++++++----- .../pfMetalPipeline/plMetalPipeline.h | 14 +++-- .../pfMetalPipeline/plMetalPipelineState.cpp | 3 +- .../pfMetalPipeline/plMetalPipelineState.h | 9 ++- 5 files changed, 83 insertions(+), 25 deletions(-) diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h b/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h index b679217fbc..f3eb238ca9 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/ShaderTypes.h @@ -187,6 +187,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 @@ -206,6 +213,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!"); @@ -217,6 +231,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 216f7ff797..b2ceace280 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, @@ -2519,6 +2542,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()); @@ -4397,7 +4422,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]); From b13fba8b8fcc0f463a15ded77cac2d8d88838bef Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Sun, 17 Nov 2024 22:57:03 -0800 Subject: [PATCH 6/6] Code style fixes --- .../FeatureLib/pfMetalPipeline/plMetalPipeline.cpp | 12 ++++-------- .../pfMetalPipeline/plMetalPipelineState.cpp | 5 +++-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp index b2ceace280..77cccab029 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp @@ -1694,23 +1694,19 @@ 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) ) - { + if ( !(fState.fBoundLights.has_value() && fState.fBoundLights == fLights) ) { fState.fBoundLights = fLights; - if (fLightingPerPixel) - { + 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) ) - { + if ( !(fState.fBoundMaterialProperties.has_value() && fState.fBoundMaterialProperties == fCurrentRenderPassMaterialLighting) ) { fState.fBoundMaterialProperties = fCurrentRenderPassMaterialLighting; fDevice.CurrentRenderCommandEncoder()->setVertexBytes(&fDevice.fPipeline->fCurrentRenderPassMaterialLighting, sizeof(plMaterialLightingDescriptor), VertexShaderArgumentMaterialLighting); - if (fLightingPerPixel) - { + if (fLightingPerPixel) { fDevice.CurrentRenderCommandEncoder()->setFragmentBytes(&fDevice.fPipeline->fCurrentRenderPassMaterialLighting, sizeof(plMaterialLightingDescriptor), FragmentShaderArgumentMaterialLighting); } } diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.cpp b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.cpp index db58b1cf77..037a50ff66 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.cpp +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.cpp @@ -42,6 +42,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plMetalPipelineState.h" +#include "HeadSpin.h" #include "plDrawable/plGBufferGroup.h" #include "plGImage/plCubicEnvironmap.h" #include "plGImage/plMipmap.h" @@ -271,7 +272,7 @@ MTL::Function* plMetalMaterialPassPipelineState::GetVertexFunction(MTL::Library* MTL::Function* function = library->newFunction(NS::String::string("pipelineVertexShader", NS::ASCIIStringEncoding), MakeFunctionConstants(), &error); - assert(!error); + hsAssert(!error, "Could not find vertex function"); return function->autorelease(); } @@ -281,7 +282,7 @@ MTL::Function* plMetalMaterialPassPipelineState::GetFragmentFunction(MTL::Librar MTL::Function* function = library->newFunction(NS::String::string("pipelineFragmentShader", NS::ASCIIStringEncoding), MakeFunctionConstants(), &error); - assert(!error); + hsAssert(!error, "Could not find fragment function"); return function->autorelease(); }