summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/video/voodoo_gpu_ps.hlsl
blob: 46541d9017f5aa575f6b8b4594611f9f8df06fb8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
// license:BSD-3-Clause
// copyright-holders: Ted Green

Texture2D dither4x4Texture : register (t0);
Texture2D dither2x2Texture : register (t1);
SamplerState ditherState : register (s0);

Texture2D renderTexture : register (t2);
SamplerState renderState : register (s2);

Texture2D texTexture0 : register (t3);
SamplerState texState0 : register (s3);

Texture2D texTexture1 : register (t4);
SamplerState texState1 : register (s4);

cbuffer cbFrameBufferCtrl : register(b0)
{
	float xSize;
	float ySize;
	int fbzFlipY;
	int lfbFlipY;
	int enablePerspective0;
	int enablePerspective1;
	float pad00, pad01;
};

struct Combine_Struct {
	int c_zero_cother;
	int c_sub_clocal;
	int c_mselect;
	int c_reverse_blend;
	int c_add_aclocal;
	int c_invert_output;
	int a_zero_aother;
	int a_sub_alocal;
	int a_mselect;
	int a_reverse_blend;
	int a_add_aclocal;
	int a_invert_output;
};


cbuffer cbColorCtrl : register(b1)
{
	float4 color0;
	float4 color1;
	Combine_Struct colCtrl;
	int rgbselect;
	int aselect;
	int c_localselect;
	int a_localselect;
	int c_localselect_override;
	int enable_param_clamp;
	int enable_texture;
	// Depth Ctrl
	int depth_enable;
	int depthfloat_select;
	int wfloat_select;
	int zbias_enable;
	int depth_src_select;
	float zBias;  // From zaColor
	float zSrc;  // From zaColor
	// Alpha Testing
	int alpha_test_enable;
	int alpha_op;
	float alpha_ref;
	// Select pixel mode
	int pixel_mode;
	// Dithering
	int dither_sel;
	float pad10;
};

#define NUM_TEX 2
struct Tex_Ctrl_Struct
{
	float2 texSize;
	int enable;
	int tloddither;
	float detailScale;
	float detailBias;
	float detailMax;
	int send_config;
	//float pad;
};

cbuffer cbTexInterface : register(b2)
{
	Combine_Struct texCombine[NUM_TEX];
	Tex_Ctrl_Struct texCtrl[NUM_TEX];
	//float pad20, pad21;
};

cbuffer cbFogTable : register(b3)
{
	float4 fog_table[64]; // 8.0 fog alpha, 6.0 fog delta alpha, 1.0 fog delta alpha invert (2nd lsb of 6.2 fog delta alpha), pad
};

cbuffer cbFogCtrl : register(b4)
{
	float3 fogColor;
	int fog_enable;
	int fogadd;
	int fogmult;
	int fogza;
	int fogconstant;
	int fogdither;
	int fogzones;
	float pad30, pad31;
};

//--------------------------------------------------------------------------------------
// Function declarations
//--------------------------------------------------------------------------------------
float4 SelectOther(float4 iterColor, float4 texColor);
float4 SelectLocal(float4 iterColor, float4 texColor, float4 iterPos);
float4 Dither(float4 Pos, float4 color);
float4 ColorCombine(float4 c_other, float4 c_local, Combine_Struct ctrl, float msel4, float3 msel5);
float CalcDepth(float oow);
void AlphaTest(float srcAlpha, float refAlpha, int alphaOp);
float4 FogUnit(float4 srcColor, float fogDepth, float4 inputPos);

//--------------------------------------------------------------------------------------
// Stage 0 Pixel Shader
//--------------------------------------------------------------------------------------
struct PS_INPUT
{
	float4 Pos : SV_POSITION;
	noperspective float Oow : FOG;
	float4 Col : COLOR;
	float4 TexPos0 : TEXCOORD0;
	float4 TexPos1 : TEXCOORD1;
};

struct PS_OUTPUT
{
	float4 Col : SV_TARGET;
	float4 ColBeforeFog : SV_TARGET1;
	float Depth : SV_DEPTH;
};

PS_OUTPUT PS(PS_INPUT input)
{
	PS_OUTPUT output;
	// Reduce the input to 8 bit color
	float4 inColor = floor(input.Col * 255.0f) / 255.0f;
	
	float4 texColor = 0.0f;
	float lod = 0;
	// Tex1
	if (texCtrl[1].enable) {
		if (1 || texCtrl[1].send_config == 0) {
			// Not sure if this should be clamped
			lod = texTexture1.CalculateLevelOfDetailUnclamped(texState1, input.TexPos1.xy);
			if (texCtrl[1].tloddither)
				lod += dither2x2Texture.Sample(ditherState, input.Pos.xy / 4.0f).x / 16.0f;
			texColor.bgra = texTexture1.SampleLevel(texState1, input.TexPos1.xy, lod);
			float msel4, msel5;
			msel4 = (texCtrl[1].detailBias - lod) * texCtrl[1].detailScale;  // detail_factor (voodoo 1 doc)
			msel4 = clamp(msel4, 0.0f, texCtrl[1].detailMax);
			msel4 /= 255.0f;
			msel5 = frac(lod); // lod fraction
			texColor = ColorCombine(0.0f, texColor, texCombine[1], msel4, msel5);
			// Floor texture color to 8 bits
			texColor = floor(texColor * 255.0f) / 255.0f;
		} else {
			texColor.r = ((texCtrl[1].send_config >> 16)& 0xff) / 255.0f;
			texColor.g = ((texCtrl[1].send_config >> 8) & 0xff) / 255.0f;
			texColor.b = (texCtrl[1].send_config & 0xff) / 255.0f;
			texColor.a = 1.0f;
		}
	}
	// Tex0
	if (texCtrl[0].enable) {
		if (texCtrl[0].send_config == 0) {
			float4 texColor0;
			// Not sure if this should be clamped
			lod = texTexture0.CalculateLevelOfDetailUnclamped(texState0, input.TexPos0.xy);
			if (texCtrl[0].tloddither)
				lod += dither2x2Texture.Sample(ditherState, input.Pos.xy / 4.0f).x / 16.0f;
			texColor0.bgra = texTexture0.SampleLevel(texState0, input.TexPos0.xy, lod);
			float msel4, msel5;
			msel4 = (texCtrl[0].detailBias - lod) * texCtrl[0].detailScale;  // detail_factor (voodoo 1 doc)
			msel4 = clamp(msel4, 0.0f, texCtrl[0].detailMax);
			msel4 /= 255.0f;
			msel5 = frac(lod); // lod fraction
			texColor = ColorCombine(texColor, texColor0, texCombine[0], msel4, msel5);
			// Floor texture color to 8 bits
			texColor = floor(texColor * 255.0f) / 255.0f;
		} else {
			texColor.r = ((texCtrl[0].send_config >> 16)& 0xff) / 255.0f;
			texColor.g = ((texCtrl[0].send_config >> 8) & 0xff) / 255.0f;
			texColor.b = (texCtrl[0].send_config & 0xff) / 255.0f;
			texColor.a = 1.0f;
		}
	}

	float4 srcColor;
	if (pixel_mode == 0) {
		// Select c_other
		float4 c_other = SelectOther(input.Col, texColor);

		// Select c_local
		float4 c_local = SelectLocal(input.Col, texColor, input.Pos);

		// Color combining
		srcColor = ColorCombine(c_other, c_local, colCtrl, texColor.a, texColor.rgb);
	}
	else if (pixel_mode == 1) {
		// Pixel pipeline 3d LFB write
		srcColor = input.Col;
	}
	else {
		// LFB direct write
		output.Col = input.Col;
		output.Depth = input.Oow;
		return output;
	}

	// Alpha Testing
	if (alpha_test_enable) {
		AlphaTest(srcColor.a, alpha_ref, alpha_op);
	}

	// Depth Calculation
	float depth = 1.0f;
	float fogDepth;
	if (pixel_mode) {
		// Use w directly with no biasing
		fogDepth = input.Oow;
		depth = fogDepth;
	} else {
		fogDepth = CalcDepth(input.Oow);
		if (depth_enable) {
			if (depth_src_select) {
				depth = zSrc;
			} else {
				if (wfloat_select) {
					if (depthfloat_select == 0)
						depth = fogDepth;
					else
						depth = CalcDepth(input.Pos.z / 256.0f);  // 12.20 -> 4.28  TODO Check
				} else {
					depth = input.Pos.z / 32.0f;  // TODO Check
					if (zbias_enable)
						depth += zBias;
					saturate(depth);
				}
			}
		}
	}

	// Some blending modes use color before fog
	output.ColBeforeFog = srcColor;

	// Fog
	srcColor = FogUnit(srcColor, fogDepth, input.Pos);

	output.Col = srcColor;
	output.Depth = depth;
	return output;
}
//--------------------------------------------------------------------------------------
// COMP Pixel Shader
//--------------------------------------------------------------------------------------
struct COMP_PS_INPUT
{
	float4 Pos : SV_POSITION;
	noperspective float Oow : FOG;
	float2 TexPos : TEXCOORD;
};

struct COMP_PS_OUTPUT
{
	float4 Col : SV_TARGET;
};

COMP_PS_OUTPUT COMP_PS(COMP_PS_INPUT input)
{
	COMP_PS_OUTPUT output;

	float4 srcColor = renderTexture.Sample(renderState, input.TexPos.xy);

	// Dithering
	srcColor = Dither(input.Pos, srcColor * 255.0f);

	// Reduce bits for RGB565 format compression
	srcColor.rb = floor(srcColor.rb / 8.0f) * 8.0f;
	srcColor.g = floor(srcColor.g / 4.0f) * 4.0f;
	srcColor /= 255.0;

	output.Col = srcColor;

	return output;
}

//--------------------------------------------------------------------------------------
// FAST Fill Pixel Shader
//--------------------------------------------------------------------------------------
PS_OUTPUT FASTFILL_PS(PS_INPUT input)
{
	PS_OUTPUT output;

	output.Col = input.Col;
	output.Depth = input.Oow;

	return output;
}
//--------------------------------------------------------------------------------------
// Functions
//--------------------------------------------------------------------------------------
float4 SelectOther(float4 iterColor, float4 texColor)
{
	float4 c_other;
	// Select c_other rgb
	switch (rgbselect) {
	case 0:
		c_other.rgb = iterColor.rgb;  // Is saturation needed???
		break;
	case 1:
		c_other.rgb = texColor.rgb;
		break;
	case 2:
		c_other.rgb = color1.rgb;
		break;
	case 3:
	default:
		c_other.rgb = 0.0f; // Linear Frame Buffer voodoo > 3
		break;
	}
	// Select c_other a
	switch (aselect) {
	case 0:
		c_other.a = iterColor.a; // Is saturation needed???
		break;
	case 1:
		c_other.a = texColor.a;
		break;
	case 2:
		c_other.a = color1.a;
		break;
	case 3:
	default:
		c_other.a = 0.0f; // Linear Frame Buffer voodoo > 3
		break;
	}
	return c_other;
}

float4 SelectLocal(float4 iterColor, float4 texColor, float4 iterPos)
{
	float4 c_local;
	// Select c_local rgb

	// TODO: Voodoo 1 doc says bit 7 of texture alpha, other docs say bit 0
	int select = c_localselect_override ? (texColor.a >= 128.0f / 255.0f ? 1 : 0) : c_localselect;
	if (select == 0)
		c_local.rgb = iterColor.rgb; // Need to check saturation???
	else
		c_local.rgb = color0.rgb;

	// Select c_local alpha
	switch (a_localselect) {
	case 0:
		c_local.a = iterColor.a; // Is saturation needed???
		break;
	case 1:
		c_local.a = color0.a;
		break;
	case 2:
		c_local.a = saturate(iterPos.z); // interated Z[27:20], clamped  TODO
		break;
	case 3:
	default:
		c_local.a = saturate(iterPos.w); // iterated W[29:32], clamped TODO
		break;
	}
	return c_local;
}

float4 Dither(float4 Pos, float4 color)
{
	float4 outColor;
	float dither;
	if (dither_sel == 0) {
		return color;
	} else if (dither_sel == 1) {
		// 4x4 dithering
		dither = dither4x4Texture.Sample(ditherState, Pos.xy / 4.0f).x * 255.0f;
	} else {
		// 2x2 dithering
		dither = dither2x2Texture.Sample(ditherState, Pos.xy / 4.0f).x * 255.0f;
	}

	outColor.rb = floor((color.rb * 2.0f - floor(color.rb / 16.0f) + floor(color.rb / 128.0f) + dither) / 2.0f);
	outColor.g = floor((color.g * 4.0f - floor(color.g / 16.0f) + floor(color.g / 64.0f) + dither) / 4.0f);
	outColor.a = color.a;

	return outColor;
}

float4 ColorCombine(float4 c_other, float4 c_local, Combine_Struct ctrl, float msel4, float3 msel5)
{
	float4 dst = c_other;

	if (ctrl.c_zero_cother)
		dst.rgb = 0.0f;

	if (ctrl.a_zero_aother)
		dst.a = 0.0f;

	if (ctrl.c_sub_clocal)
		dst.rgb -= c_local.rgb;

	if (ctrl.a_sub_alocal)
		dst.a -= c_local.a;

	float4 blendFactor;
	switch (ctrl.c_mselect) {
	case 0:
		blendFactor.rgb = 0.0f;
		break;
	case 1:
		blendFactor.rgb = c_local.rgb;
		break;
	case 2:
		blendFactor.rgb = c_other.a;
		break;
	case 3:
		blendFactor.rgb = c_local.a;
		break;
	case 4:
		blendFactor.rgb = msel4;
		break;
	case 5:
	default:
		blendFactor.rgb = msel5;
		break;
	}

	switch (ctrl.a_mselect) {
	case 0:
		blendFactor.a = 0.0f;
		break;
	case 1:
		blendFactor.a = c_local.a;
		break;
	case 2:
		blendFactor.a = c_other.a;
		break;
	case 3:
		blendFactor.a = c_local.a;
		break;
	case 4:
	default:
		blendFactor.a = msel4;
		break;
	}

	if (ctrl.c_reverse_blend==0)
		blendFactor.rgb = 1.0f - blendFactor.rgb;

	if (ctrl.a_reverse_blend==0)
		blendFactor.a = 1.0f - blendFactor.a;

	// Perform the blend
	dst *= blendFactor;

	// truncate  TODO: check this
	dst = floor(dst * 255.0f) / 255.0f;

	switch (ctrl.c_add_aclocal) {
	case 0:
		break;
	case 1:
		dst.rgb += c_local.rgb;
		break;
	case 2:
	default:
		dst.rgb += c_local.a;
		break;
	}

	switch (ctrl.a_add_aclocal) {
	case 0:
		break;
	case 1:
	case 2:
	default:
		dst.a += c_local.a;
		break;
	}

	dst = saturate(dst);

	if (ctrl.c_invert_output)
		dst.rgb = 1.0f - dst.rgb;

	if (ctrl.a_invert_output)
		dst.a = 1.0f - dst.a;

	return dst;
}

float CalcDepth(float oow)
{
	float depth;
	// Top 16 of 16.32 set
	if (oow >= 1.0f) {
		depth = -1.0f;
	}
	// Middle 16 of 16.32 not set
	else if (oow < 1.0f / 65536.0f) {
		depth = 1.0f;
	} else {
		uint temp = oow * 65536.0f * 65536.0f;
		uint exp = firstbithigh(temp) ^ 31; // 31 - firstbithigh
		temp >>= 20 - (exp + 1);
		temp &= 0xfff;
		// Invert
		temp ^= 0xfff;
		temp |= (exp << 12);
		// Add one
		temp++;
		depth = float(temp);
		// Normalize to 16 bits (0xffff = 1.0)
		depth /= 65535.0f;
	}
	// Add bias
	if (zbias_enable)
		depth += zBias;

	// Need to clamp after bias adjust
	saturate(depth);

	return depth;
}

void AlphaTest(float srcAlpha, float refAlpha, int alphaOp)
{
	switch (alphaOp) {
	case 0: // never
		discard;
		break;
	case 1: // less than
		if (srcAlpha >= refAlpha)
			discard;
		break;
	case 2: // equal
		if (srcAlpha != refAlpha)
			discard;
		break;
	case 3: // less than or equal
		if (srcAlpha > refAlpha)
			discard;
		break;
	case 4: // greater than
		if (srcAlpha <= refAlpha)
			discard;
		break;
	case 5: // not equal
		if (srcAlpha == refAlpha)
			discard;
		break;
	case 6: // greater than or equal
		if (srcAlpha < refAlpha)
			discard;
		break;
	case 7: // always
	default:
		break;
	}
}

float4 FogUnit(float4 srcColor, float fogDepth, float4 inputPos)
{
	if (fog_enable == 0) {
		return srcColor;
	} else {
		float4 foggedColor = srcColor;
		
		if (fogmult)
			foggedColor.rgb = 0.0f;
		
		if (fogconstant) {
			foggedColor.rgb += fogColor.rgb;
		} else {
			float3 fogMult = -foggedColor.rgb;  // TODO: Check 2's complement
			if (fogadd == 0)
				fogMult += fogColor;
			// Fog Alpha
			float fogAlpha;
			switch (fogza) {
			case 0:
				fogDepth *= 65535.0f; // Un-normalize to 16 bits
				float fogExp = floor(fogDepth / 1024.0f); // 4 bits exp, 2 bit mant [11:10]
				float fogMant = floor(fogDepth / 4.0f) - fogExp * 256.0f; // 8 bit mant [9:2]
				fogAlpha = fog_table[fogExp][0];
				float fogDelta = fog_table[fogExp][1]; // 6.0
				float fogSel = fog_table[fogExp][2]; // 1 bit from 2nd lsb of fog delta alpha
				fogDelta *= fogMant;
				// Reduce 14 to 10 bits
				fogDelta = floor(fogDelta / 16.0f);
				if (fogzones == 1 && fogSel == 1.0f)
					fogDelta = -fogDelta;
				// Convert to 7.4
				fogDelta /= 16.0f;
				// Add dither
				if (fogdither)
					fogDelta += dither4x4Texture.Sample(ditherState, inputPos.xy / 4.0f).x * 255.0f / 16.0f;
				fogAlpha += fogDelta;
				fogAlpha = floor(fogAlpha);
				fogAlpha /= 255.0f; // Normalize
				break;
			case 1:
				fogAlpha = srcColor.a;
				break;
			case 2:
				float iterZClamp = inputPos.z;
				fogAlpha = iterZClamp; // iterated Z (27:20), clamped   TODO: fix
				break;
			case 3:
			default:
				float iterWClamp = inputPos.w;
				fogAlpha = iterWClamp; // iterated W (39:32), clamped  TODO: fix
				break;
			}
			foggedColor.rgb += fogMult * fogAlpha;
		}
		saturate(foggedColor);
		return foggedColor;
	}
}