OverviewFilesCommitsIssuesDocsReleasesPeers

nvidia-rtx-godot commits / ad1e745

Expose CUSTOM0-3 vertex attributes to RT hit shaders
SceneShaderRaytracing already renamed CUSTOM0-3 to custom0_attrib..custom3_attrib (actions.renames) and defined CUSTOM0_USED, but nothing ever declared those identifiers in the RT hit template. Any shader touching CUSTOM0 therefore failed to compile: RT shader compile failed (hg1_v0_closest_hit): ERROR: 0:3212: 'custom0_attrib' : undeclared identifier which fails the whole hit group; build_tlas then skips every surface whose hit group is not ready, so the geometry is invisible while its collision -- being CPU-side -- still works. dc_level.gdshader:85 (`v_proj = CUSTOM0.x`, the per-face Quake projection index) hits exactly this. process_surface was already walking the CUSTOM arrays to size attribute_stride and throwing the offsets away. They are now recorded in RT_GeometryData as four packed words (byte offset in bits 0-23, ARRAY_CUSTOM_* format in bits 24-31), which fit in the existing _pad without changing the 128-byte std430 layout. Shader side: fetch_custom() decodes all eight ARRAY_CUSTOM_* formats and interpolates barycentrically, matching fetch_uv/fetch_color; it is reached via a new FETCH_CUSTOM flag so surfaces without custom arrays pay four compares rather than four buffer reads. VertexAttributes and HitData carry vec4 custom[4], and both the closest-hit and any-hit custom paths publish rt_custom0..3. Note on `flat`: a shader may declare its CUSTOM-derived varying flat (dc_level does, to stop per-face indices bleeding at shared vertices). There is no rasterizer interpolation in the RT path -- the hit shader runs once at the hit point -- and per-face data stored on a face's own vertices interpolates back to that same constant, so barycentric is correct here. If fringing ever appears on a mesh that genuinely shares vertices across faces, provoking-vertex (i0) is the alternative. Verified by compiling the real closest-hit stage offline: includes flattened the way Godot's preprocessor assembles them, engine defines applied, placeholders filled with dc_level-shaped code exercising custom0_attrib AND a sampler2DArray uniform. Both the default and custom hit-group variants generate SPIR-V. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ad1e745adad0609ac9cf445ac599ee930cada2f6
by plankatron <fleeky@gmail.com> · 2026-07-29T14:46:30.000Z

Changed files (8) +108 -3

servers/rendering/renderer_rd/forward_clustered/render_raytracing.cpp +5 -0
……@@ -726,9 +726,14 @@ void RenderRaytracing::_populate_surface_blas(
726726 }
727727 for (int ci = 0; ci < RSE::ARRAY_CUSTOM_COUNT; ci++) {
728728 const uint32_t fmt_shift[RSE::ARRAY_CUSTOM_COUNT] = { RSE::ARRAY_FORMAT_CUSTOM0_SHIFT, RSE::ARRAY_FORMAT_CUSTOM1_SHIFT, RSE::ARRAY_FORMAT_CUSTOM2_SHIFT, RSE::ARRAY_FORMAT_CUSTOM3_SHIFT };
729 geom.custom_packed[ci] = RT_OFFSET_NONE;
729730 if (surface_format & (1ULL << (RSE::ARRAY_CUSTOM0 + ci))) {
730731 uint32_t fmt = (surface_format >> fmt_shift[ci]) & RSE::ARRAY_FORMAT_CUSTOM_MASK;
731732 const uint32_t fmtsize[RSE::ARRAY_CUSTOM_MAX] = { 4, 4, 4, 8, 4, 8, 12, 16 };
733 // The offset was already being walked to size the stride; record it so
734 // hit shaders can actually read CUSTOM0-3 (shaders referencing them
735 // previously failed to compile on 'custom0_attrib' undeclared).
736 geom.custom_packed[ci] = (attrib_offset & 0x00FFFFFFu) | (fmt << 24);
732737 attrib_offset += fmtsize[fmt];
733738 }
734739 }
servers/rendering/renderer_rd/forward_clustered/render_raytracing.h +7 -1
……@@ -83,7 +83,13 @@ struct alignas(16) RT_GeometryData {
8383 // For deformed geometry: previous-frame position buffer used for motion vectors.
8484 uint32_t prev_vertex_buffer_address_lo;
8585 uint32_t prev_vertex_buffer_address_hi;
86 uint32_t _pad[5];
86 // CUSTOM0-3 attribute location, one packed word each:
87 // bits 0-23 byte offset inside attribute_stride
88 // bits 24-31 ARRAY_CUSTOM_* format
89 // RT_OFFSET_NONE (all bits set) when the surface lacks that custom array --
90 // unambiguous because format 0xFF is not a valid ARRAY_CUSTOM_* value.
91 uint32_t custom_packed[4];
92 uint32_t _pad[1];
8793};
8894static_assert(sizeof(RT_GeometryData) == 128, "RT_GeometryData must be 128 bytes for std430");
8995
servers/rendering/renderer_rd/shaders/raytracing/raytracing_closest_hit_common_inc.glsl +2 -0
……@@ -20,6 +20,7 @@ struct HitData {
2020 vec3 bitangent; // World space.
2121 vec2 uv; // Raw UV (no material scale/offset applied).
2222 vec4 color; // Vertex color (white if not present).
23 vec4 custom[4]; // CUSTOM0-3 (zero if not present); only filled for custom HGs.
2324 bool is_front_face;
2425 uint geometry_idx;
2526};
……@@ -40,6 +41,7 @@ HitData compute_hit_data() {
4041#endif
4142 h.uv = attrs.uv;
4243 h.color = attrs.color;
44 h.custom = attrs.custom;
4345
4446 mat3 model_rotation = mat3(gl_ObjectToWorldEXT);
4547 mat3 normal_matrix = mat3(
servers/rendering/renderer_rd/shaders/raytracing/raytracing_custom_fragment_inc.glsl +5 -0
……@@ -9,6 +9,7 @@
99// vec3 rt_tangent -- world-space tangent
1010// vec3 rt_bitangent -- world-space bitangent
1111// bool rt_front_face -- true if front-face hit
12// vec4 rt_custom0..3 -- CUSTOM0-3 vertex attributes (vec4(0) if absent)
1213//
1314// Required bindings/types:
1415// materials[], CustomMaterialUniforms, scene_data_block
……@@ -47,6 +48,10 @@ binormal = mat3(rt_world_to_object_decomp) * rt_bitangent;
4748uv_interp = rt_uv;
4849uv2_interp = rt_uv;
4950color_interp = rt_color;
51custom0_attrib = rt_custom0;
52custom1_attrib = rt_custom1;
53custom2_attrib = rt_custom2;
54custom3_attrib = rt_custom3;
5055view = -gl_WorldRayDirectionEXT;
5156rt_front_facing = rt_front_face;
5257rt_screen_uv = vec2(gl_LaunchIDEXT.xy) / vec2(gl_LaunchSizeEXT.xy);
servers/rendering/renderer_rd/shaders/raytracing/raytracing_custom_globals_inc.glsl +8 -0
……@@ -17,6 +17,14 @@ vec3 binormal = vec3(0.0, 1.0, 0.0);
1717vec2 uv_interp = vec2(0.0);
1818vec2 uv2_interp = vec2(0.0);
1919vec4 color_interp = vec4(1.0);
20// ShaderCompiler renames CUSTOM0-3 to these (see actions.renames in
21// SceneShaderRaytracing), so they must exist even when the surface has no such
22// array -- otherwise any shader touching CUSTOM0 fails to compile and takes its
23// whole hit group down.
24vec4 custom0_attrib = vec4(0.0);
25vec4 custom1_attrib = vec4(0.0);
26vec4 custom2_attrib = vec4(0.0);
27vec4 custom3_attrib = vec4(0.0);
2028vec3 view = vec3(0.0, 0.0, -1.0);
2129mat4 read_model_matrix = mat4(1.0);
2230mat4 read_view_matrix = mat4(1.0);
servers/rendering/renderer_rd/shaders/raytracing/raytracing_data_inc.glsl +4 -1
……@@ -48,7 +48,10 @@ struct GeometryData {
4848 uint prev_vertex_address_lo;
4949 uint prev_vertex_address_hi;
5050
51 uint _pad[5];
51 // CUSTOM0-3 location: bits 0-23 byte offset in attribute_stride, bits 24-31
52 // ARRAY_CUSTOM_* format. OFFSET_NONE when the surface has no such array.
53 uint custom_packed[4];
54 uint _pad[1];
5255};
5356
5457void get_aabb_compression_xforms(GeometryData geom, out mat4 aabb_xform, out mat4 inv_aabb_xform) {
servers/rendering/renderer_rd/shaders/raytracing/raytracing_hit_inc.glsl +69 -1
……@@ -12,6 +12,7 @@ struct VertexAttributes {
1212 vec3 tangent;
1313 float bitangent_sign;
1414 vec4 color;
15 vec4 custom[4]; // CUSTOM0-3; vec4(0) when the surface has no such array.
1516 bool has_uv;
1617 bool has_normal;
1718 bool has_tangent;
……@@ -130,6 +131,59 @@ vec4 fetch_color(in GeometryData geom, uint i0, uint i1, uint i2, vec3 bary) {
130131 return bary.x * col0 + bary.y * col1 + bary.z * col2;
131132}
132133
134// ============================================================================
135// CUSTOM0-3 FETCHING
136// ============================================================================
137// Decodes one vertex's ARRAY_CUSTOM_* value to vec4. Formats and their sizes
138// match the fmtsize table in RenderRaytracing::process_surface.
139vec4 decode_custom_vertex(in GeometryData geom, uint fmt, uint word_base) {
140 Uint32Buffer attr = Uint32Buffer(geom.attribute_address);
141 FloatBuffer attr_f = FloatBuffer(geom.attribute_address);
142
143 switch (fmt) {
144 case 0u: // RGBA8_UNORM
145 return unpackUnorm4x8(attr.v[word_base]);
146 case 1u: // RGBA8_SNORM
147 return unpackSnorm4x8(attr.v[word_base]);
148 case 2u: // RG_HALF
149 return vec4(unpackHalf2x16(attr.v[word_base]), 0.0, 0.0);
150 case 3u: // RGBA_HALF
151 return vec4(unpackHalf2x16(attr.v[word_base]), unpackHalf2x16(attr.v[word_base + 1u]));
152 case 4u: // R_FLOAT
153 return vec4(attr_f.v[word_base], 0.0, 0.0, 0.0);
154 case 5u: // RG_FLOAT
155 return vec4(attr_f.v[word_base], attr_f.v[word_base + 1u], 0.0, 0.0);
156 case 6u: // RGB_FLOAT
157 return vec4(attr_f.v[word_base], attr_f.v[word_base + 1u], attr_f.v[word_base + 2u], 0.0);
158 default: // 7u RGBA_FLOAT
159 return vec4(attr_f.v[word_base], attr_f.v[word_base + 1u],
160 attr_f.v[word_base + 2u], attr_f.v[word_base + 3u]);
161 }
162}
163
164// Barycentric, matching fetch_uv/fetch_color. A shader may declare its
165// CUSTOM-derived varying `flat` (per-face data), but there is no rasterizer
166// interpolation in the RT path -- the hit shader runs once at the hit point --
167// and per-face data stored on a face's own vertices interpolates back to that
168// same constant.
169vec4 fetch_custom(in GeometryData geom, uint custom_index, uint i0, uint i1, uint i2, vec3 bary) {
170 uint packed = geom.custom_packed[custom_index];
171 if (packed == OFFSET_NONE || geom.attribute_address == 0ul) {
172 return vec4(0.0);
173 }
174
175 uint byte_off = packed & 0x00FFFFFFu;
176 uint fmt = packed >> 24;
177 uint stride_words = geom.attribute_stride >> 2;
178 uint word_off = byte_off >> 2;
179
180 vec4 c0 = decode_custom_vertex(geom, fmt, i0 * stride_words + word_off);
181 vec4 c1 = decode_custom_vertex(geom, fmt, i1 * stride_words + word_off);
182 vec4 c2 = decode_custom_vertex(geom, fmt, i2 * stride_words + word_off);
183
184 return bary.x * c0 + bary.y * c1 + bary.z * c2;
185}
186
133187// ============================================================================
134188// TBN FETCHING
135189// ============================================================================
……@@ -231,7 +285,8 @@ TBNResult fetch_tbn(in GeometryData geom, uint i0, uint i1, uint i2, vec3 bary)
231285#define FETCH_UV (1u << 0)
232286#define FETCH_TBN (1u << 1)
233287#define FETCH_COLOR (1u << 2)
234#define FETCH_ALL (FETCH_UV | FETCH_TBN | FETCH_COLOR)
288#define FETCH_CUSTOM (1u << 3)
289#define FETCH_ALL (FETCH_UV | FETCH_TBN | FETCH_COLOR | FETCH_CUSTOM)
235290
236291#ifdef RT_HIT_ATTRIBS_DECLARED
237292VertexAttributes fetch_vertex_attributes(in GeometryData geom, vec2 hit_attribs, uint fetch_flags) {
……@@ -241,6 +296,10 @@ VertexAttributes fetch_vertex_attributes(in GeometryData geom, vec2 hit_attribs,
241296 attrs.tangent = vec3(1.0, 0.0, 0.0);
242297 attrs.bitangent_sign = 1.0;
243298 attrs.color = vec4(1.0);
299 attrs.custom[0] = vec4(0.0);
300 attrs.custom[1] = vec4(0.0);
301 attrs.custom[2] = vec4(0.0);
302 attrs.custom[3] = vec4(0.0);
244303 attrs.has_uv = false;
245304 attrs.has_normal = false;
246305 attrs.has_tangent = false;
……@@ -261,6 +320,15 @@ VertexAttributes fetch_vertex_attributes(in GeometryData geom, vec2 hit_attribs,
261320 attrs.has_color = true;
262321 }
263322
323 // fetch_custom early-outs per index, so a surface with no custom arrays pays
324 // four compares rather than four buffer reads.
325 if ((fetch_flags & FETCH_CUSTOM) != 0u) {
326 attrs.custom[0] = fetch_custom(geom, 0u, i0, i1, i2, bary);
327 attrs.custom[1] = fetch_custom(geom, 1u, i0, i1, i2, bary);
328 attrs.custom[2] = fetch_custom(geom, 2u, i0, i1, i2, bary);
329 attrs.custom[3] = fetch_custom(geom, 3u, i0, i1, i2, bary);
330 }
331
264332 if ((fetch_flags & FETCH_TBN) != 0u && geom.normal_byte_offset != OFFSET_NONE) {
265333 TBNResult tbn = fetch_tbn(geom, i0, i1, i2, bary);
266334 attrs.normal = tbn.normal;
servers/rendering/renderer_rd/shaders/raytracing/scene_raytracing_raygen.glsl +8 -0
……@@ -327,6 +327,10 @@ void main() {
327327 vec3 rt_hit_pos = h.hit_pos;
328328 vec2 rt_uv = h.uv;
329329 vec4 rt_color = h.color;
330 vec4 rt_custom0 = h.custom[0];
331 vec4 rt_custom1 = h.custom[1];
332 vec4 rt_custom2 = h.custom[2];
333 vec4 rt_custom3 = h.custom[3];
330334 vec3 rt_normal = h.geometry_normal;
331335 vec3 rt_tangent = h.tangent;
332336 vec3 rt_bitangent = h.bitangent;
……@@ -525,6 +529,10 @@ void main() {
525529
526530 vec3 rt_hit_pos = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT;
527531 vec4 rt_color = fetch_color(geom, i0, i1, i2, bary);
532 vec4 rt_custom0 = fetch_custom(geom, 0u, i0, i1, i2, bary);
533 vec4 rt_custom1 = fetch_custom(geom, 1u, i0, i1, i2, bary);
534 vec4 rt_custom2 = fetch_custom(geom, 2u, i0, i1, i2, bary);
535 vec4 rt_custom3 = fetch_custom(geom, 3u, i0, i1, i2, bary);
528536
529537#include "raytracing_custom_fragment_inc.glsl"
530538