OverviewFilesCommitsIssuesDocsReleasesPeers

nvidia-rtx-godot commits / ec334ea

Add per-type bindless texture tables to the RT descriptor set
The RT bindless table was texture2D[] only. Every custom-shader texture uniform was #defined onto it regardless of its declared sampler type, so any material using a sampler2DArray/sampler3D/samplerCube generated texture(sampler2DArray(<texture2D>, SAMPLER_NEAREST_REPEAT), ...) which glslang rejects: 'sampler2DArray' : sampler-constructor first argument must be a *texture* type matching the dimensionality and sampled type of the constructor That fails the ENTIRE closest-hit shader for the hit group. build_tlas then skips every surface whose hit group is not ready (is_hg_ready_in_bundle), so the level silently renders nothing under path tracing. In hyper-tps both dc_level.gdshader (64-layer WAD atlas) and liquid_warp.gdshader trip it. Each texture type now gets its own table. Vulkan allows only one variable-count binding per set and it must have the LARGEST binding number (VUID-VkDescriptorSetLayoutBindingFlagsCreateInfo-pBindingFlags-03004), so the typed tables are fixed-capacity at bindings 0-3 and the 2D table -- the only one that needs to scale -- stays unbounded and moves to binding 4. Verified with spirv-dis that an explicitly sized array keeps its declared size even when never referenced, so the typed layouts exist without needing a keep-alive reference in the default hit shader; an UNSIZED unused array would instead be emitted as a 1-element fixed array and the reflection would register the binding as bounded length=1. Control/fix pair on the exact failing construct: the old form reproduces the error above verbatim, the new form compiles, and the runtime array lands on binding 4. Bounded bindings get no PARTIALLY_BOUND flag, so those tables are always written in full, padded with each kind's default texture. Also print the glslang error instead of discarding it. _build_pipeline_worker captured the diagnostic into `error` and passed only the source to _dump_failed_shader, so a failed hit group produced a dumped .glsl and no reason -- recovering it meant compiling the dump by hand. `error` is now also cleared before each compile so a stale message cannot be attributed to a later stage. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ec334ea8d278a2a87229302a5b62efedd3d84947
by plankatron <fleeky@gmail.com> · 2026-07-29T13:51:33.000Z

Changed files (7) +280 -71

servers/rendering/renderer_rd/bindless_block.cpp +41 -35
……@@ -32,31 +32,47 @@
3232
3333#include "servers/rendering/renderer_rd/storage_rd/texture_storage.h"
3434
35void BindlessBlock::initialize(RenderingDevice *p_rd) {
35void BindlessBlock::initialize(RenderingDevice *p_rd, TextureKind p_kind) {
3636 ERR_FAIL_NULL(p_rd);
3737
3838 rd = p_rd;
39 kind = p_kind;
40
41 RendererRD::TextureStorage::DefaultRDTexture default_id = RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_WHITE;
42 switch (kind) {
43 case TEXTURE_2D_ARRAY:
44 default_id = RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE;
45 break;
46 case TEXTURE_3D:
47 default_id = RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE;
48 break;
49 case TEXTURE_CUBE:
50 default_id = RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_CUBEMAP_WHITE;
51 break;
52 case TEXTURE_CUBE_ARRAY:
53 default_id = RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_CUBEMAP_ARRAY_WHITE;
54 break;
55 default:
56 break;
57 }
3958
40 default_texture = RendererRD::TextureStorage::get_singleton()->texture_rd_get_default(
41 RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_WHITE);
59 default_texture = RendererRD::TextureStorage::get_singleton()->texture_rd_get_default(default_id);
4260 textures.push_back(default_texture);
4361 texture_to_index[default_texture] = 0;
62 needs_refinalize = true;
4463}
4564
46void BindlessBlock::begin_frame() {
65void BindlessBlock::begin_frame(bool p_set_valid) {
4766 if (!is_initialized()) {
4867 return;
4968 }
5069
51 bool set_valid = uniform_set.is_valid() && rd->uniform_set_is_valid(uniform_set);
52 if (set_valid) {
70 if (p_set_valid) {
5371 return;
5472 }
5573
5674 // Uniform set was externally invalidated (a texture was destroyed/recreated).
5775 // Scrub dead RIDs: replace with default and add slots to the freelist.
58 uniform_set = RID();
59
6076 texture_to_index.clear();
6177 texture_to_index[default_texture] = 0;
6278
……@@ -90,14 +106,13 @@ uint32_t BindlessBlock::add_texture(RID p_texture) {
90106 return it->value;
91107 }
92108
93 if (textures.size() >= MAX_BINDLESS_TEXTURES && free_indices_count == 0) {
109 const uint32_t capacity = is_unbounded(kind) ? MAX_BINDLESS_TEXTURES : TYPED_TABLE_CAPACITY;
110 if (textures.size() >= capacity && free_indices_count == 0) {
94111 ERR_PRINT_ONCE("BindlessBlock: Maximum texture count exceeded. Using default texture.");
95112 return 0;
96113 }
97114
98 if (is_finalized()) {
99 needs_refinalize = true;
100 }
115 needs_refinalize = true;
101116
102117 uint32_t index;
103118 if (free_indices_count > 0) {
……@@ -113,46 +128,37 @@ uint32_t BindlessBlock::add_texture(RID p_texture) {
113128 return index;
114129}
115130
116void BindlessBlock::finalize(RID p_shader, uint32_t p_set_index) {
131void BindlessBlock::collect_uniform(Vector<RD::Uniform> &r_uniforms) const {
117132 ERR_FAIL_COND_MSG(!is_initialized(), "BindlessBlock not initialized.");
118133 ERR_FAIL_COND_MSG(textures.is_empty(), "BindlessBlock has no textures.");
119134
120 if (is_finalized() && !needs_refinalize && rd->uniform_set_is_valid(uniform_set)) {
121 return;
122 }
123
124 if (uniform_set.is_valid() && rd->uniform_set_is_valid(uniform_set)) {
125 rd->free_rid(uniform_set);
126 }
127 uniform_set = RID();
128
129 Vector<RD::Uniform> uniforms;
130
131135 RD::Uniform u;
132136 u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
133 u.binding = 0;
134 u.variable_count = true;
137 u.binding = get_binding(kind);
138 u.variable_count = is_unbounded(kind);
139
135140 for (uint32_t i = 0; i < textures.size(); i++) {
136141 u.append_id(textures[i]);
137142 }
138 uniforms.push_back(u);
139143
140 uniform_set = rd->uniform_set_create(uniforms, p_shader, p_set_index);
141 ERR_FAIL_COND_MSG(!uniform_set.is_valid(), "Failed to create bindless uniform set.");
142 rd->set_resource_name(uniform_set, "Bindless Texture Set");
144 // A bounded binding gets no PARTIALLY_BOUND flag, so every descriptor in the
145 // declared array must be written; pad the tail with this kind's default.
146 if (!is_unbounded(kind)) {
147 for (uint32_t i = textures.size(); i < TYPED_TABLE_CAPACITY; i++) {
148 u.append_id(default_texture);
149 }
150 }
143151
144 needs_refinalize = false;
152 r_uniforms.push_back(u);
145153}
146154
147155void BindlessBlock::clear() {
148 if (uniform_set.is_valid() && rd && rd->uniform_set_is_valid(uniform_set)) {
149 rd->free_rid(uniform_set);
150 }
151 uniform_set = RID();
156 // The uniform set is shared across kinds and owned by the caller.
152157 textures.clear();
153158 texture_to_index.clear();
154159 free_indices.clear();
155160 free_indices_count = 0;
161 needs_refinalize = true;
156162 rd = nullptr;
157163}
158164
servers/rendering/renderer_rd/bindless_block.h +43 -7
……@@ -38,29 +38,65 @@ class BindlessBlock {
3838public:
3939 constexpr static uint32_t MAX_BINDLESS_TEXTURES = 128000;
4040
41 // GLSL cannot construct a sampler of one dimensionality from a texture of
42 // another, so each texture type needs its OWN bindless table -- a single
43 // texture2D[] table makes every sampler2DArray/sampler3D/samplerCube uniform
44 // generate sampler2DArray(texture2D, ...), which glslang rejects, failing the
45 // whole hit group and rendering nothing.
46 //
47 // Vulkan permits only one variable-count (unbounded) binding per descriptor
48 // set, and it must be the binding with the LARGEST binding number
49 // (VUID-VkDescriptorSetLayoutBindingFlagsCreateInfo-pBindingFlags-03004; also
50 // enforced in rendering_device_driver_vulkan.cpp). So the rarely-used typed
51 // tables are fixed-capacity at the low bindings and the 2D table -- the one
52 // that actually needs to scale -- stays unbounded at the highest binding.
53 enum TextureKind {
54 TEXTURE_2D_ARRAY,
55 TEXTURE_3D,
56 TEXTURE_CUBE,
57 TEXTURE_CUBE_ARRAY,
58 TEXTURE_2D, // Unbounded; MUST remain the highest binding.
59 TEXTURE_KIND_MAX
60 };
61
62 // Fixed capacity of the typed (bounded) tables. Bounded bindings do not get
63 // PARTIALLY_BOUND, so these are always written in full, padded with the
64 // kind's default texture.
65 constexpr static uint32_t TYPED_TABLE_CAPACITY = 256;
66
67 static bool is_unbounded(TextureKind p_kind) { return p_kind == TEXTURE_2D; }
68 static uint32_t get_binding(TextureKind p_kind) { return (uint32_t)p_kind; }
69
4170 uint32_t add_texture(RID p_texture);
4271
43 void initialize(RenderingDevice *p_rd);
72 void initialize(RenderingDevice *p_rd, TextureKind p_kind = TEXTURE_2D);
73
74 // p_set_valid: whether the shared uniform set this table belongs to is still
75 // live. When it is not, dead texture RIDs are scrubbed and a rebuild is
76 // requested. The set itself is owned by the caller, since all kinds share one.
77 void begin_frame(bool p_set_valid);
4478
45 void begin_frame();
79 // Appends this table's binding to p_uniforms. Every table in the set must be
80 // collected before the set is created, so creation is the caller's job.
81 void collect_uniform(Vector<RD::Uniform> &r_uniforms) const;
4682
47 void finalize(RID p_shader, uint32_t p_set_index);
83 bool needs_rebuild() const { return needs_refinalize; }
84 void mark_rebuilt() { needs_refinalize = false; }
4885
49 RID get_uniform_set() const { return uniform_set; }
5086 uint32_t get_texture_count() const { return textures.size(); }
87 TextureKind get_kind() const { return kind; }
5188 bool is_initialized() const { return rd != nullptr; }
52 bool is_finalized() const { return uniform_set.is_valid(); }
5389
5490 void clear();
5591 ~BindlessBlock();
5692
5793private:
5894 RenderingDevice *rd = nullptr;
95 TextureKind kind = TEXTURE_2D;
5996 LocalVector<RID> textures;
6097 HashMap<RID, uint32_t> texture_to_index;
6198 LocalVector<uint32_t> free_indices;
6299 uint32_t free_indices_count = 0; // logical top to avoid resize of free_indices array
63100 RID default_texture;
64 RID uniform_set;
65 bool needs_refinalize = false;
101 bool needs_refinalize = true;
66102};
servers/rendering/renderer_rd/forward_clustered/render_raytracing.cpp +95 -19
……@@ -47,9 +47,30 @@ using namespace RendererSceneRenderImplementation;
4747// Lifecycle
4848// ---------------------------------------------------------------------------
4949
50BindlessBlock::TextureKind RenderRaytracing::_texture_kind_for_type(ShaderLanguage::DataType p_type) {
51 switch (p_type) {
52 case ShaderLanguage::TYPE_SAMPLER2DARRAY:
53 case ShaderLanguage::TYPE_ISAMPLER2DARRAY:
54 case ShaderLanguage::TYPE_USAMPLER2DARRAY:
55 return BindlessBlock::TEXTURE_2D_ARRAY;
56 case ShaderLanguage::TYPE_SAMPLER3D:
57 case ShaderLanguage::TYPE_ISAMPLER3D:
58 case ShaderLanguage::TYPE_USAMPLER3D:
59 return BindlessBlock::TEXTURE_3D;
60 case ShaderLanguage::TYPE_SAMPLERCUBE:
61 return BindlessBlock::TEXTURE_CUBE;
62 case ShaderLanguage::TYPE_SAMPLERCUBEARRAY:
63 return BindlessBlock::TEXTURE_CUBE_ARRAY;
64 default:
65 return BindlessBlock::TEXTURE_2D;
66 }
67}
68
5069void RenderRaytracing::initialize(RenderForwardClustered *p_owner) {
5170 owner = p_owner;
52 bindless_block = memnew(BindlessBlock);
71 for (uint32_t k = 0; k < BindlessBlock::TEXTURE_KIND_MAX; k++) {
72 bindless_blocks[k] = memnew(BindlessBlock);
73 }
5374}
5475
5576RenderRaytracing::~RenderRaytracing() {
……@@ -65,9 +86,16 @@ RenderRaytracing::~RenderRaytracing() {
6586 mat_ubo_pool_buffer = RID();
6687 }
6788
68 if (bindless_block) {
69 memdelete(bindless_block);
70 bindless_block = nullptr;
89 if (bindless_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(bindless_uniform_set)) {
90 RD::get_singleton()->free_rid(bindless_uniform_set);
91 }
92 bindless_uniform_set = RID();
93
94 for (uint32_t k = 0; k < BindlessBlock::TEXTURE_KIND_MAX; k++) {
95 if (bindless_blocks[k]) {
96 memdelete(bindless_blocks[k]);
97 bindless_blocks[k] = nullptr;
98 }
7199 }
72100 if (shader) {
73101 memdelete(shader);
……@@ -391,10 +419,21 @@ void RenderRaytracing::prepare_frame() {
391419 cache_hits = 0;
392420 cache_misses = 0;
393421
394 if (!bindless_block->is_initialized()) {
395 bindless_block->initialize(RD::get_singleton());
422 // The whole set is rebuilt as a unit, so its liveness is evaluated once and
423 // handed to every table.
424 const bool bindless_set_valid = bindless_uniform_set.is_valid() &&
425 RD::get_singleton()->uniform_set_is_valid(bindless_uniform_set);
426 if (!bindless_set_valid) {
427 bindless_uniform_set = RID();
428 }
429
430 for (uint32_t k = 0; k < BindlessBlock::TEXTURE_KIND_MAX; k++) {
431 BindlessBlock *block = bindless_blocks[k];
432 if (!block->is_initialized()) {
433 block->initialize(RD::get_singleton(), (BindlessBlock::TextureKind)k);
434 }
435 block->begin_frame(bindless_set_valid);
396436 }
397 bindless_block->begin_frame();
398437}
399438
400439// ---------------------------------------------------------------------------
……@@ -1190,12 +1229,12 @@ RTMaterialData *RenderRaytracing::process_material(RID p_material_rid, uint16_t
11901229 // Albedo is a color texture - needs sRGB->linear conversion
11911230 RID albedo_rd = get_material_texture("texture_albedo", true);
11921231 if (albedo_rd.is_valid()) {
1193 mat.albedo_texture_idx = bindless_block->add_texture(albedo_rd);
1232 mat.albedo_texture_idx = _bindless(BindlessBlock::TEXTURE_2D)->add_texture(albedo_rd);
11941233 }
11951234
11961235 RID normal_rd = get_material_texture("texture_normal");
11971236 if (normal_rd.is_valid()) {
1198 mat.normal_texture_idx = bindless_block->add_texture(normal_rd);
1237 mat.normal_texture_idx = _bindless(BindlessBlock::TEXTURE_2D)->add_texture(normal_rd);
11991238 mat.flags |= RT_MAT_FLAG_HAS_NORMAL_MAP;
12001239
12011240 Variant normal_scale_var = material_storage->material_get_param(p_material_rid, "normal_scale");
……@@ -1206,18 +1245,18 @@ RTMaterialData *RenderRaytracing::process_material(RID p_material_rid, uint16_t
12061245
12071246 RID orm_rd = get_material_texture("texture_orm");
12081247 if (orm_rd.is_valid()) {
1209 mat.orm_texture_idx = bindless_block->add_texture(orm_rd);
1248 mat.orm_texture_idx = _bindless(BindlessBlock::TEXTURE_2D)->add_texture(orm_rd);
12101249 } else {
12111250 RID roughness_rd = get_material_texture("texture_roughness");
12121251 if (roughness_rd.is_valid()) {
1213 mat.orm_texture_idx = bindless_block->add_texture(roughness_rd);
1252 mat.orm_texture_idx = _bindless(BindlessBlock::TEXTURE_2D)->add_texture(roughness_rd);
12141253 }
12151254 }
12161255
12171256 // Emission is a color texture - needs sRGB->linear conversion
12181257 RID emission_rd = get_material_texture("texture_emission", true);
12191258 if (emission_rd.is_valid()) {
1220 mat.emission_texture_idx = bindless_block->add_texture(emission_rd);
1259 mat.emission_texture_idx = _bindless(BindlessBlock::TEXTURE_2D)->add_texture(emission_rd);
12211260 mat.flags |= RT_MAT_FLAG_HAS_EMISSION_TEX;
12221261 // Set sensible defaults for emission when texture is present
12231262 mat.emission_color[0] = 1.0f;
……@@ -1282,12 +1321,18 @@ RTMaterialData *RenderRaytracing::process_material(RID p_material_rid, uint16_t
12821321 const SceneShaderRaytracing::TextureUniformInfo &tui = cse->texture_uniforms[ti];
12831322 uint32_t bindless_idx = 0;
12841323
1324 // The index is looked up in the table matching the uniform's sampler
1325 // type; the generated GLSL indexes that same table (see
1326 // SceneShaderRaytracing tex_defines), so the two must agree.
1327 const BindlessBlock::TextureKind kind = _texture_kind_for_type(tui.type);
1328 BindlessBlock *block = _bindless(kind);
1329
12851330 if (tui.is_global) {
12861331 RID tex_rid = material_storage->global_shader_uniform_get_texture(tui.name);
12871332 if (tex_rid.is_valid()) {
12881333 RID rd_tex = ts->texture_get_rd_texture(tex_rid, tui.use_color);
12891334 if (rd_tex.is_valid()) {
1290 bindless_idx = bindless_block->add_texture(rd_tex);
1335 bindless_idx = block->add_texture(rd_tex);
12911336 }
12921337 }
12931338 } else {
……@@ -1297,13 +1342,16 @@ RTMaterialData *RenderRaytracing::process_material(RID p_material_rid, uint16_t
12971342 if (tex_rid.is_valid()) {
12981343 RID rd_tex = ts->texture_get_rd_texture(tex_rid, tui.use_color);
12991344 if (rd_tex.is_valid()) {
1300 bindless_idx = bindless_block->add_texture(rd_tex);
1345 bindless_idx = block->add_texture(rd_tex);
13011346 }
13021347 }
13031348 }
13041349 }
13051350
1306 if (bindless_idx == 0 && tui.hint != ShaderLanguage::ShaderNode::Uniform::HINT_NONE) {
1351 // The hint defaults below are all 2D textures. For any other kind,
1352 // index 0 already IS that table's default of the correct type.
1353 if (bindless_idx == 0 && kind == BindlessBlock::TEXTURE_2D &&
1354 tui.hint != ShaderLanguage::ShaderNode::Uniform::HINT_NONE) {
13071355 using Hint = ShaderLanguage::ShaderNode::Uniform::Hint;
13081356 RID default_tex;
13091357 switch (tui.hint) {
……@@ -1324,7 +1372,7 @@ RTMaterialData *RenderRaytracing::process_material(RID p_material_rid, uint16_t
13241372 break;
13251373 }
13261374 if (default_tex.is_valid()) {
1327 bindless_idx = bindless_block->add_texture(default_tex);
1375 bindless_idx = block->add_texture(default_tex);
13281376 }
13291377 }
13301378
……@@ -2263,9 +2311,37 @@ RID RenderRaytracing::update_uniform_set(RTViewportState *p_state, const RenderD
22632311 RD::get_singleton()->set_resource_name(p_state->uniform_set, "RT Uniform Set");
22642312
22652313 // === SET 1: Bindless textures ===
2266 if (bindless_block && bindless_block->is_initialized()) {
2267 bindless_block->finalize(shader_rd, 1);
2268 bindless_uniform_set = bindless_block->get_uniform_set();
2314 // All texture kinds share set 1, so the set is rebuilt as a unit whenever
2315 // any table changed.
2316 bool bindless_ready = true;
2317 bool bindless_dirty = !bindless_uniform_set.is_valid();
2318 for (uint32_t k = 0; k < BindlessBlock::TEXTURE_KIND_MAX; k++) {
2319 if (!bindless_blocks[k] || !bindless_blocks[k]->is_initialized()) {
2320 bindless_ready = false;
2321 break;
2322 }
2323 bindless_dirty = bindless_dirty || bindless_blocks[k]->needs_rebuild();
2324 }
2325
2326 if (bindless_ready && bindless_dirty) {
2327 Vector<RD::Uniform> bindless_uniforms;
2328 for (uint32_t k = 0; k < BindlessBlock::TEXTURE_KIND_MAX; k++) {
2329 bindless_blocks[k]->collect_uniform(bindless_uniforms);
2330 }
2331
2332 if (bindless_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(bindless_uniform_set)) {
2333 RD::get_singleton()->free_rid(bindless_uniform_set);
2334 }
2335 bindless_uniform_set = RD::get_singleton()->uniform_set_create(bindless_uniforms, shader_rd, 1);
2336
2337 if (bindless_uniform_set.is_valid()) {
2338 RD::get_singleton()->set_resource_name(bindless_uniform_set, "Bindless Texture Set");
2339 for (uint32_t k = 0; k < BindlessBlock::TEXTURE_KIND_MAX; k++) {
2340 bindless_blocks[k]->mark_rebuilt();
2341 }
2342 } else {
2343 ERR_PRINT_ONCE("RT: failed to create the bindless texture uniform set.");
2344 }
22692345 }
22702346 }
22712347
servers/rendering/renderer_rd/forward_clustered/render_raytracing.h +11 -1
……@@ -37,6 +37,7 @@
3737#include "core/templates/vector.h"
3838#include "servers/rendering/renderer_rd/bindless_block.h"
3939#include "servers/rendering/rendering_device.h"
40#include "servers/rendering/shader_language.h"
4041
4142#define RB_TEX_RAYTRACING SNAME("raytracing")
4243#define RB_TEX_RT_DEPTH SNAME("rt_depth")
……@@ -271,10 +272,19 @@ class RenderRaytracing {
271272 RenderForwardClustered *owner = nullptr;
272273
273274 SceneShaderRaytracing *shader = nullptr;
274 BindlessBlock *bindless_block = nullptr;
275
276 // One bindless table per texture type, all sharing descriptor set 1.
277 BindlessBlock *bindless_blocks[BindlessBlock::TEXTURE_KIND_MAX] = {};
275278
276279 RID bindless_uniform_set;
277280
281 // Routes a shader uniform's sampler type to the table that can serve it.
282 static BindlessBlock::TextureKind _texture_kind_for_type(ShaderLanguage::DataType p_type);
283
284 _FORCE_INLINE_ BindlessBlock *_bindless(BindlessBlock::TextureKind p_kind) const {
285 return bindless_blocks[p_kind];
286 }
287
278288 // Caching (chunked sparse caches indexed by RID low bits / 256).
279289 Vector<RTCacheEntry *> surface_chunks;
280290 Vector<RTMaterialCacheEntry *> material_chunks;
servers/rendering/renderer_rd/forward_clustered/scene_shader_raytracing.cpp +40 -5
……@@ -42,7 +42,13 @@
4242
4343using namespace RendererSceneRenderImplementation;
4444
45static void _dump_failed_shader(const String &p_source, const String &p_label) {
45static void _dump_failed_shader(const String &p_source, const String &p_label, const String &p_error) {
46 // The compiler error is the only thing that identifies WHY the hit group failed;
47 // without it a failed hit group is indistinguishable from one that was never
48 // registered, and the level silently renders nothing.
49 if (!p_error.is_empty()) {
50 ERR_PRINT("RT shader compile failed (" + p_label + "): " + p_error);
51 }
4652 String tmp_dir = OS::get_singleton()->get_temp_path();
4753 String path = tmp_dir.path_join("rt_shader_" + p_label + ".glsl");
4854 Ref<FileAccess> f = FileAccess::open(path, FileAccess::WRITE);
……@@ -413,6 +419,26 @@ struct SceneShaderRaytracing::PipelineBuildTask {
413419 WorkerThreadPool::TaskID worker_id = WorkerThreadPool::INVALID_TASK_ID;
414420};
415421
422const char *SceneShaderRaytracing::_bindless_table_name(ShaderLanguage::DataType p_type) {
423 // Names must match the declarations in scene_raytracing_raygen.glsl.
424 switch (p_type) {
425 case ShaderLanguage::TYPE_SAMPLER2DARRAY:
426 case ShaderLanguage::TYPE_ISAMPLER2DARRAY:
427 case ShaderLanguage::TYPE_USAMPLER2DARRAY:
428 return "bindless_textures_2d_array";
429 case ShaderLanguage::TYPE_SAMPLER3D:
430 case ShaderLanguage::TYPE_ISAMPLER3D:
431 case ShaderLanguage::TYPE_USAMPLER3D:
432 return "bindless_textures_3d";
433 case ShaderLanguage::TYPE_SAMPLERCUBE:
434 return "bindless_textures_cube";
435 case ShaderLanguage::TYPE_SAMPLERCUBEARRAY:
436 return "bindless_textures_cube_array";
437 default:
438 return "bindless_textures";
439 }
440}
441
416442void SceneShaderRaytracing::_strip_texture_globals(String &r_globals, const String &p_tex_name) {
417443 const String decl_marker = " m_" + p_tex_name + ";";
418444 int pos = r_globals.find(decl_marker);
……@@ -469,6 +495,7 @@ void SceneShaderRaytracing::_finalize_uniforms_with_textures(
469495
470496 TextureUniformInfo tui;
471497 tui.name = tex.name;
498 tui.type = tex.type;
472499 tui.hint = tex.hint;
473500 tui.use_color = tex.use_color;
474501 tui.is_global = tex.global;
……@@ -957,7 +984,12 @@ SceneShaderRaytracing::PipelineBuildTask *SceneShaderRaytracing::_make_pipeline_
957984 String tex_defines;
958985 for (int ti = 0; ti < entry.texture_uniforms.size(); ti++) {
959986 const TextureUniformInfo &tui = entry.texture_uniforms[ti];
960 tex_defines += "#define m_" + tui.name + " bindless_textures[nonuniformEXT(material.m_" + tui.name + ")]\n";
987 // Must name the table whose element type matches the uniform's sampler
988 // type. Pointing everything at bindless_textures (texture2D) makes a
989 // sampler2DArray uniform generate sampler2DArray(texture2D, ...), which
990 // glslang rejects -- taking the whole hit group down with it.
991 tex_defines += "#define m_" + tui.name + " " + _bindless_table_name(tui.type) +
992 "[nonuniformEXT(material.m_" + tui.name + ")]\n";
961993 }
962994 String uniform_members = entry.uniform_members.is_empty() ? String("float _rt_pad;") : entry.uniform_members;
963995
……@@ -1090,11 +1122,12 @@ void SceneShaderRaytracing::_build_pipeline_worker(PipelineBuildTask *p_task) {
10901122 Vector<uint8_t> ch_spirv;
10911123 {
10921124 MutexLock lock(spirv_compile_mutex);
1125 error = String();
10931126 ch_spirv = RD::get_singleton()->shader_compile_spirv_from_source(
10941127 RD::SHADER_STAGE_CLOSEST_HIT, si.ch_src, RD::SHADER_LANGUAGE_GLSL, &error);
10951128 }
10961129 if (ch_spirv.is_empty()) {
1097 _dump_failed_shader(si.ch_src, vformat("hg%d_v%x_closest_hit", i, p_task->rt_flags));
1130 _dump_failed_shader(si.ch_src, vformat("hg%d_v%x_closest_hit", i, p_task->rt_flags), error);
10981131 p_task->new_per_hg_shaders[i] = RID();
10991132 p_task->new_ready_mask[i] = false;
11001133 continue;
……@@ -1110,11 +1143,12 @@ void SceneShaderRaytracing::_build_pipeline_worker(PipelineBuildTask *p_task) {
11101143 Vector<uint8_t> ah_spirv;
11111144 {
11121145 MutexLock lock(spirv_compile_mutex);
1146 error = String();
11131147 ah_spirv = RD::get_singleton()->shader_compile_spirv_from_source(
11141148 RD::SHADER_STAGE_ANY_HIT, si.ah_src, RD::SHADER_LANGUAGE_GLSL, &error);
11151149 }
11161150 if (ah_spirv.is_empty()) {
1117 _dump_failed_shader(si.ah_src, vformat("hg%d_v%x_any_hit", i, p_task->rt_flags));
1151 _dump_failed_shader(si.ah_src, vformat("hg%d_v%x_any_hit", i, p_task->rt_flags), error);
11181152 p_task->new_per_hg_shaders[i] = RID();
11191153 p_task->new_ready_mask[i] = false;
11201154 continue;
……@@ -1131,11 +1165,12 @@ void SceneShaderRaytracing::_build_pipeline_worker(PipelineBuildTask *p_task) {
11311165 Vector<uint8_t> is_spirv;
11321166 {
11331167 MutexLock lock(spirv_compile_mutex);
1168 error = String();
11341169 is_spirv = RD::get_singleton()->shader_compile_spirv_from_source(
11351170 RD::SHADER_STAGE_INTERSECTION, si.is_src, RD::SHADER_LANGUAGE_GLSL, &error);
11361171 }
11371172 if (is_spirv.is_empty()) {
1138 _dump_failed_shader(si.is_src, vformat("hg%d_v%x_intersection", i, p_task->rt_flags));
1173 _dump_failed_shader(si.is_src, vformat("hg%d_v%x_intersection", i, p_task->rt_flags), error);
11391174 p_task->new_per_hg_shaders[i] = RID();
11401175 p_task->new_ready_mask[i] = false;
11411176 continue;
servers/rendering/renderer_rd/forward_clustered/scene_shader_raytracing.h +5 -1
……@@ -343,6 +343,9 @@ public:
343343
344344 struct TextureUniformInfo {
345345 StringName name;
346 // Selects which bindless table the index refers to. A sampler2DArray
347 // cannot be constructed from a texture2D, so the table must match.
348 ShaderLanguage::DataType type = ShaderLanguage::TYPE_SAMPLER2D;
346349 ShaderLanguage::ShaderNode::Uniform::Hint hint = ShaderLanguage::ShaderNode::Uniform::HINT_NONE;
347350 bool use_color = false;
348351 bool is_global = false;
……@@ -360,7 +363,7 @@ public:
360363 uint32_t uniform_total_size = 0;
361364 Vector<uint32_t> uniform_offsets;
362365 HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> uniforms;
363 Vector<TextureUniformInfo> texture_uniforms; // Sampler2D packed as bindless indices after UBO
366 Vector<TextureUniformInfo> texture_uniforms; // Samplers packed as bindless indices after UBO
364367 bool uses_alpha_clip = false; // Writes ALPHA_SCISSOR_THRESHOLD; needs per-HG any-hit
365368 bool is_procedural = false; // Uses intersection shader instead of triangle geometry
366369 };
……@@ -448,6 +451,7 @@ private:
448451 // Source-hash slot management.
449452 uint32_t _register_slot(uint32_t p_shader_id, RID p_material, bool p_is_procedural);
450453 bool _preprocess_shader(RID p_material, bool p_is_procedural, CustomShaderEntry &r_entry);
454 static const char *_bindless_table_name(ShaderLanguage::DataType p_type);
451455 void _strip_texture_globals(String &r_globals, const String &p_tex_name);
452456 void _finalize_uniforms_with_textures(CustomShaderEntry &r_entry, const ShaderCompiler::GeneratedCode &p_gen_code, const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, bool p_strip_intersection_globals);
453457
servers/rendering/renderer_rd/shaders/raytracing/scene_raytracing_raygen.glsl +45 -3
……@@ -259,7 +259,21 @@ void main() {
259259layout(set = 0, binding = 1) uniform accelerationStructureEXT tlas;
260260layout(location = 0) rayPayloadInEXT PathPayload payload;
261261
262layout(set = 1, binding = 0) uniform texture2D bindless_textures[];
262// Bindless texture tables. One per texture type: GLSL cannot build a
263// sampler2DArray/sampler3D/samplerCube from a texture2D, so a single 2D table
264// makes every array/volume/cube uniform generate an invalid sampler constructor
265// and fails the whole hit group.
266//
267// Only ONE binding per set may be variable-count, and it must be the one with
268// the LARGEST binding number, so the typed tables are fixed-capacity here and
269// the 2D table -- the only one that needs to scale -- stays unbounded and last.
270// Keep these sizes in sync with BindlessBlock::TYPED_TABLE_CAPACITY, and the
271// binding numbers with BindlessBlock::TextureKind.
272layout(set = 1, binding = 0) uniform texture2DArray bindless_textures_2d_array[256];
273layout(set = 1, binding = 1) uniform texture3D bindless_textures_3d[256];
274layout(set = 1, binding = 2) uniform textureCube bindless_textures_cube[256];
275layout(set = 1, binding = 3) uniform textureCubeArray bindless_textures_cube_array[256];
276layout(set = 1, binding = 4) uniform texture2D bindless_textures[];
263277
264278#include "raytracing_samplers_inc.glsl"
265279
……@@ -449,7 +463,21 @@ layout(set = 0, binding = 5, std430) readonly buffer MaterialBuffer {
449463};
450464// clang-format on
451465
452layout(set = 1, binding = 0) uniform texture2D bindless_textures[];
466// Bindless texture tables. One per texture type: GLSL cannot build a
467// sampler2DArray/sampler3D/samplerCube from a texture2D, so a single 2D table
468// makes every array/volume/cube uniform generate an invalid sampler constructor
469// and fails the whole hit group.
470//
471// Only ONE binding per set may be variable-count, and it must be the one with
472// the LARGEST binding number, so the typed tables are fixed-capacity here and
473// the 2D table -- the only one that needs to scale -- stays unbounded and last.
474// Keep these sizes in sync with BindlessBlock::TYPED_TABLE_CAPACITY, and the
475// binding numbers with BindlessBlock::TextureKind.
476layout(set = 1, binding = 0) uniform texture2DArray bindless_textures_2d_array[256];
477layout(set = 1, binding = 1) uniform texture3D bindless_textures_3d[256];
478layout(set = 1, binding = 2) uniform textureCube bindless_textures_cube[256];
479layout(set = 1, binding = 3) uniform textureCubeArray bindless_textures_cube_array[256];
480layout(set = 1, binding = 4) uniform texture2D bindless_textures[];
453481
454482#include "raytracing_samplers_inc.glsl"
455483
……@@ -568,7 +596,21 @@ layout(set = 0, binding = 5, std430) readonly buffer MaterialBuffer {
568596};
569597// clang-format on
570598
571layout(set = 1, binding = 0) uniform texture2D bindless_textures[];
599// Bindless texture tables. One per texture type: GLSL cannot build a
600// sampler2DArray/sampler3D/samplerCube from a texture2D, so a single 2D table
601// makes every array/volume/cube uniform generate an invalid sampler constructor
602// and fails the whole hit group.
603//
604// Only ONE binding per set may be variable-count, and it must be the one with
605// the LARGEST binding number, so the typed tables are fixed-capacity here and
606// the 2D table -- the only one that needs to scale -- stays unbounded and last.
607// Keep these sizes in sync with BindlessBlock::TYPED_TABLE_CAPACITY, and the
608// binding numbers with BindlessBlock::TextureKind.
609layout(set = 1, binding = 0) uniform texture2DArray bindless_textures_2d_array[256];
610layout(set = 1, binding = 1) uniform texture3D bindless_textures_3d[256];
611layout(set = 1, binding = 2) uniform textureCube bindless_textures_cube[256];
612layout(set = 1, binding = 3) uniform textureCubeArray bindless_textures_cube_array[256];
613layout(set = 1, binding = 4) uniform texture2D bindless_textures[];
572614
573615#include "raytracing_samplers_inc.glsl"
574616