From 9f88aea92ff8aaa4304cb4a5057a88957807e739 Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Sun, 7 Jan 2024 16:17:18 -0800 Subject: [PATCH] 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 8aa5d2d29f..f7a7524764 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); @@ -226,13 +231,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; @@ -245,9 +250,20 @@ vertex ColorInOut pipelineVertexShader(Vertex in [[stage_in]], const float4 position2 = blendMatrix1 * float4(in.position, 1.f); 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; @@ -423,9 +439,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: @@ -453,7 +472,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 1651db7a7d..ea292d6b47 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 @@ -181,6 +184,8 @@ struct plMaterialLightingDescriptor uint8_t emissiveSrc; half3 specularCol; uint8_t specularSrc; + + bool invertAlpha; }; struct VertexUniforms @@ -191,8 +196,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 3a8a026a71..ff6b929b04 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