72 lines
3.2 KiB
GLSL
72 lines
3.2 KiB
GLSL
/**
|
|
******************************************************************************
|
|
* Xenia : Xbox 360 Emulator Research Project *
|
|
******************************************************************************
|
|
* Copyright 2025. All rights reserved. *
|
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
******************************************************************************
|
|
*/
|
|
|
|
#version 460
|
|
#extension GL_GOOGLE_include_directive : require
|
|
|
|
// Tessellation control shader for adaptive triangle tessellation.
|
|
// Edge factors come from the vertex shader (read from the index buffer).
|
|
// Uses fractional_even spacing for smooth tessellation.
|
|
|
|
layout(vertices = 1) out;
|
|
|
|
#include "xenos_draw.glsli"
|
|
|
|
// Input from vertex shader - edge tessellation factor (already processed:
|
|
// 1.0 added and clamped to range in the vertex shader).
|
|
layout(location = 0) in float in_edge_factor[];
|
|
|
|
// Output to tessellation evaluation shader - patch index as float.
|
|
layout(location = 0) out float out_index[];
|
|
|
|
void main() {
|
|
// Factors for adaptive tessellation are taken from the index buffer.
|
|
//
|
|
// 1.0 added to the factors according to the images in
|
|
// https://www.slideshare.net/blackdevilvikas/next-generation-graphics-programming-on-xbox-360
|
|
// (fractional_even also requires a factor of at least 2.0), to the min/max it
|
|
// has already been added on the CPU.
|
|
|
|
// It appears that on the Xbox 360:
|
|
// - [0] is the factor for the v0->v1 edge.
|
|
// - [1] is the factor for the v1->v2 edge.
|
|
// - [2] is the factor for the v2->v0 edge.
|
|
// Where v0 is the U1V0W0 vertex, v1 is the U0V1W0 vertex, and v2 is the
|
|
// U0V0W1 vertex.
|
|
// The hint at the order was provided in the Code Listing 15 of:
|
|
// http://www.uraldev.ru/files/download/21/Real-Time_Tessellation_on_GPU.pdf
|
|
//
|
|
// In Vulkan/OpenGL:
|
|
// - gl_TessLevelOuter[0] is the factor for the U0 edge (v1->v2).
|
|
// - gl_TessLevelOuter[1] is the factor for the V0 edge (v2->v0).
|
|
// - gl_TessLevelOuter[2] is the factor for the W0 edge (v0->v1).
|
|
//
|
|
// In Xenia's domain shaders, the barycentric coordinates are handled as:
|
|
// 1) gl_TessCoord.xyz -> r0.zyx by Xenia.
|
|
// 2) r0.zyx -> r0.zyx by the guest (because r1.y is set to 0 by Xenia, which
|
|
// apparently means identity swizzle to games).
|
|
// 3) r0.z * v0 + r0.y * v1 + r0.x * v2 by the guest.
|
|
// With this order, there are no cracks in 4D5307E6 water.
|
|
gl_TessLevelOuter[0] = in_edge_factor[1]; // v1->v2 edge
|
|
gl_TessLevelOuter[1] = in_edge_factor[2]; // v2->v0 edge
|
|
gl_TessLevelOuter[2] = in_edge_factor[0]; // v0->v1 edge
|
|
|
|
// Inside factor is the minimum of edge factors.
|
|
gl_TessLevelInner[0] = min(min(gl_TessLevelOuter[0], gl_TessLevelOuter[1]),
|
|
gl_TessLevelOuter[2]);
|
|
|
|
// Output the patch index.
|
|
// Only the lower 24 bits of the vertex index are used (tested on an Adreno
|
|
// 200 phone). `((index & 0xFFFFFF) + offset) & 0xFFFFFF` is the same as
|
|
// `(index + offset) & 0xFFFFFF`.
|
|
out_index[gl_InvocationID] =
|
|
float(clamp((gl_PrimitiveID + xe_vertex_index_offset) & 0xFFFFFFu,
|
|
xe_vertex_index_min_max.x, xe_vertex_index_min_max.y));
|
|
}
|