diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal b/Sources/Plasma/FeatureLib/pfMetalPipeline/ShaderSrc/FixedPipelineShaders.metal index 7623c5c186..507fa655b5 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) ]]; @@ -100,7 +103,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); @@ -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; @@ -172,31 +177,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) @@ -212,6 +206,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)); @@ -231,16 +230,42 @@ 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, 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), function_constant(perVertexLighting) ]], + constant plMetalLights & lights [[ buffer(VertexShaderArgumentLights), function_constant(perVertexLighting) ]], + 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); + + const float3 Ndirection = normalize(float4(in.normal, 0.f) * uniforms.localToWorldMatrix).xyz; - out.vtxColor = half4(material.rgb, abs(uniforms.invVtxAlpha - MDiffuse.a)); + 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); + } + + if (perPixelLighting) { + // send the world pos on to the pixel shader for lighting + out.worldPos = position; + out.normal = Ndirection; + + out.vtxColor = inColor; + } else { + out.vtxColor = calcLitMaterialColor(lights, inColor, materialLighting, position, Ndirection); + } + const float4 vCamPosition = position * uniforms.worldToCameraMatrix; // Fog @@ -415,9 +440,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: @@ -445,7 +473,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)); @@ -612,6 +640,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; @@ -619,7 +648,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 2845921eb8..f3eb238ca9 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, @@ -62,14 +62,13 @@ 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 - VertexShaderArgumentShadowState = 9 -}; - -enum plMetalFragmentShaderArgumentIndex -{ + VertexShaderArgumentShadowState = 9, + /// Texture is a legacy argument for the simpler plate shader FragmentShaderArgumentTexture = 1, /// Fragment uniforms @@ -77,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 @@ -110,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 @@ -150,6 +155,7 @@ struct plMetalShaderLightSource half4 specular; simd::float3 direction; simd::float4 spotProps; // (falloff, theta, phi) + __fp16 range; __fp16 constAtten; __fp16 linAtten; __fp16 quadAtten; @@ -168,15 +174,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; @@ -186,7 +185,24 @@ struct VertexUniforms uint8_t emissiveSrc; half3 specularCol; uint8_t specularSrc; - bool invVtxAlpha; + + bool invertAlpha; + +#ifndef __METAL_VERSION__ + bool operator==(const plMaterialLightingDescriptor& rhs) const + { + return memcmp(this, &rhs, sizeof(plMaterialLightingDescriptor)) == 0; + } +#endif +}; + +struct VertexUniforms +{ + // transformation + matrix_float4x4 projectionMatrix; + matrix_float4x4 localToWorldMatrix; + matrix_float4x4 cameraToWorldMatrix; + matrix_float4x4 worldToCameraMatrix; uint8_t fogExponential; simd::float2 fogValues; @@ -197,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!"); @@ -208,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 951fd2590a..77cccab029 100644 --- a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp +++ b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp @@ -149,13 +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); - - plMetalLights* lights = &fDevice->fPipeline->fLights; - size_t lightSize = offsetof(plMetalLights, lampSources) + (sizeof(plMetalShaderLightSource) * lights->count); - - fDevice->CurrentRenderCommandEncoder()->setVertexBytes(lights, sizeof(plMetalLights), VertexShaderArgumentLights); + if ( !(fDevice->fPipeline->fState.fCurrentVertexUniforms.has_value() && fDevice->fPipeline->fState.fCurrentVertexUniforms == *fDevice->fPipeline->fCurrentRenderPassUniforms) ) + { + 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)); } @@ -1209,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(); } @@ -1304,16 +1307,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); @@ -1381,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(); @@ -1483,14 +1489,15 @@ 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; } - + + IBindLights(); render.RenderPrims(); } } @@ -1615,9 +1622,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); @@ -1667,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; @@ -1680,6 +1689,29 @@ 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, @@ -2118,18 +2150,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 +2181,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 +2225,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 +2247,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; @@ -2374,6 +2406,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 +2417,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 +2429,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(); @@ -2499,6 +2538,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()); @@ -3197,7 +3238,7 @@ bool plMetalPipeline::IPushShadowCastState(plShadowSlave* slave) return false; // Set texture to U_LUT - fCurrentRenderPassUniforms->specularSrc = 0.0; + fCurrentRenderPassMaterialLighting.specularSrc = 0.0; // if( !ref->fTexture ) //{ @@ -3886,17 +3927,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 /////////////////////////////////////////////////////////// @@ -4377,7 +4418,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 4d819490c7..99051c1296 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; @@ -262,8 +263,10 @@ class plMetalPipeline : public pl3DPipeline void PushCurrentLightSources(); void PopCurrentLightSources(); + void IBindLights(); plMetalLights fLights; std::vector fLightSourceStack; + bool fLightingPerPixel; static plMetalEnumerate enumerator; @@ -283,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 1225f4c452..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" @@ -110,6 +111,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)); + constants->setConstantValue(&fFragmentShaderDescription.fUsePerPixelLighting, 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); + hsAssert(!error, "Could not find vertex function"); + 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); + hsAssert(!error, "Could not find fragment function"); + return function->autorelease(); } plMetalMaterialPassPipelineState::~plMetalMaterialPassPipelineState() diff --git a/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.h b/Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipelineState.h index 0f5cf4114d..61ce9d7a51 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_FORCE_PER_PIXEL_LIGHTING +#define PLASMA_FORCE_PER_PIXEL_LIGHTING 0 +#endif + enum plMetalPipelineType { // Unknown is for abstract types, don't use it @@ -147,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; } @@ -169,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]);