@@ -87,6 +87,18 @@ public class RenderManager {
8787
8888 private static final Logger logger = Logger .getLogger (RenderManager .class .getName ());
8989
90+ /**
91+ * Number of vec4 fragment uniform vectors consumed per light in g_LightData
92+ * (lightColor, lightData1, lightData2/spotDir).
93+ */
94+ private static final int VEC4_UNIFORMS_PER_LIGHT = 3 ;
95+ /**
96+ * Fraction of the total fragment uniform budget reserved for shader
97+ * parameters other than g_LightData (material params, transforms, etc.).
98+ * A value of 4 means one quarter is reserved.
99+ */
100+ private static final int RESERVED_UNIFORM_FRACTION = 4 ;
101+
90102 private final Renderer renderer ;
91103 private final UniformBindingManager uniformBindingManager = new UniformBindingManager ();
92104 private final ArrayList <ViewPort > preViewPorts = new ArrayList <>();
@@ -109,7 +121,7 @@ public class RenderManager {
109121 private LightFilter lightFilter = new DefaultLightFilter ();
110122 private TechniqueDef .LightMode preferredLightMode = TechniqueDef .LightMode .MultiPass ;
111123 private int singlePassLightBatchSize = 1 ;
112- private int maxSinglePassLightBatchSize = 32 ;
124+ private int maxSinglePassLightBatchSize ;
113125 private final MatParamOverride boundDrawBufferId = new MatParamOverride (VarType .Int , "BoundDrawBuffer" , 0 );
114126 private Predicate <Geometry > renderFilter ;
115127
@@ -125,6 +137,7 @@ public RenderManager(Renderer renderer) {
125137 this .forcedOverrides .add (boundDrawBufferId );
126138 // register default pipeline context
127139 contexts .put (PipelineContext .class , new DefaultPipelineContext ());
140+ setMaxSinglePassLightBatchSize (16 );
128141 }
129142
130143 /**
@@ -1098,7 +1111,10 @@ public void setSinglePassLightBatchSize(int singlePassLightBatchSize) {
10981111 * Returns the maximum number of lights allowed in a single pass batch.
10991112 *
11001113 * <p>The batch size will never be auto-scaled beyond this value.
1101- * The default is 16.
1114+ * The default is 16, further clamped at construction time by the hardware's
1115+ * {@link Limits#FragmentUniformVectors} capacity (each light consumes 3 vec4
1116+ * uniforms; a quarter of the available budget is reserved for other shader
1117+ * parameters).
11021118 *
11031119 * @return the maximum single pass light batch size.
11041120 */
@@ -1109,6 +1125,13 @@ public int getMaxSinglePassLightBatchSize() {
11091125 /**
11101126 * Sets the maximum number of lights allowed in a single pass batch.
11111127 *
1128+ * <p>The requested value is clamped to a hardware-safe upper bound derived
1129+ * from the renderer's {@link Limits#FragmentUniformVectors} capacity.
1130+ * Each light consumes 3 vec4 uniforms in {@code g_LightData}; one quarter
1131+ * of the available uniform budget is reserved for other shader parameters.
1132+ * If the hardware limit cannot be queried (e.g., renderer not yet
1133+ * initialised), the requested value is used as-is.
1134+ *
11121135 * <p>If the current {@link #getSinglePassLightBatchSize() batch size} exceeds the
11131136 * new maximum, it is clamped down to the new maximum. Otherwise the current
11141137 * batch size is left unchanged and will continue to auto-scale up to the new limit.
@@ -1117,6 +1140,16 @@ public int getMaxSinglePassLightBatchSize() {
11171140 */
11181141 public void setMaxSinglePassLightBatchSize (int maxSinglePassLightBatchSize ) {
11191142 this .maxSinglePassLightBatchSize = Math .max (maxSinglePassLightBatchSize , 1 );
1143+ // Clamp to a hardware-safe value.
1144+ // Each light uses 3 vec4 uniforms in g_LightData; reserve 1/4 of the
1145+ // fragment uniform budget for other shader parameters (material params,
1146+ // transforms, etc.).
1147+ Integer fragUniformVecs = renderer .getLimits ().get (Limits .FragmentUniformVectors );
1148+ if (fragUniformVecs != null && fragUniformVecs > 0 ) {
1149+ int reservedUniforms = Math .max (fragUniformVecs / RESERVED_UNIFORM_FRACTION , 1 );
1150+ int maxByHardware = Math .max ((fragUniformVecs - reservedUniforms ) / VEC4_UNIFORMS_PER_LIGHT , 1 );
1151+ this .maxSinglePassLightBatchSize = Math .min (this .maxSinglePassLightBatchSize , maxByHardware );
1152+ }
11201153 if (singlePassLightBatchSize > this .maxSinglePassLightBatchSize ) {
11211154 singlePassLightBatchSize = this .maxSinglePassLightBatchSize ;
11221155 }
0 commit comments