| … | … | @@ -6,6 +6,8 @@ |
| 6 | 6 | #include <godot_cpp/classes/rd_shader_source.hpp> |
| 7 | 7 | #include <godot_cpp/classes/rd_shader_spirv.hpp> |
| 8 | 8 | #include <godot_cpp/classes/rd_uniform.hpp> |
| 9 | #include <godot_cpp/classes/rd_texture_format.hpp> |
| 10 | #include <godot_cpp/classes/rd_texture_view.hpp> |
| 9 | 11 | #include <godot_cpp/variant/utility_functions.hpp> |
| 10 | 12 | |
| 11 | 13 | #include <cstring> |
| … | … | @@ -370,9 +372,62 @@ void DCCompute::set_custom_data(const float* data, int n) { |
| 370 | 372 | fprintf(stderr, "DCCompute: custom data uploaded %d floats (%zu bytes), range %.0f..%.0f\n", |
| 371 | 373 | n, bytes, (double)lo, (double)hi); |
| 372 | 374 | } |
| 375 | _upload_atlas(data, n); |
| 373 | 376 | uniform_set_dirty_ = true; |
| 374 | 377 | } |
| 375 | 378 | |
| 379 | |
| 380 | // The SAME floats, as a 2-D texture. Linear index i -> (i % W, i / W). |
| 381 | // |
| 382 | // A LINEAR MAPPING, NOT A TILE GRID, and deliberately so for this step: it is a |
| 383 | // pure TRANSPORT swap, so custom_data[i] and the atlas hold identical values at |
| 384 | // identical indices and the two can be proved equivalent by the existing |
| 385 | // gate. Laying the mip pyramid out as 2-D tile rectangles is a LAYOUT change |
| 386 | // and belongs in its own step, where it can fail on its own. |
| 387 | void DCCompute::_upload_atlas(const float* data, int n) { |
| 388 | if (!rd_) return; |
| 389 | if (atlas_tex_.is_valid()) { rd_->free_rid(atlas_tex_); atlas_tex_ = RID(); } |
| 390 | if (atlas_staging_.is_valid()) { rd_->free_rid(atlas_staging_); atlas_staging_ = RID(); } |
| 391 | |
| 392 | const int W = 4096; |
| 393 | int count = (data == nullptr || n <= 0) ? 1 : n; |
| 394 | int H = (count + W - 1) / W; |
| 395 | atlas_w_ = W; atlas_h_ = H; |
| 396 | |
| 397 | Ref<RDTextureFormat> fmt; fmt.instantiate(); |
| 398 | fmt->set_format(RenderingDevice::DATA_FORMAT_R32_SFLOAT); |
| 399 | fmt->set_width((uint32_t)W); fmt->set_height((uint32_t)H); fmt->set_depth(1); |
| 400 | fmt->set_array_layers(1); fmt->set_mipmaps(1); |
| 401 | fmt->set_texture_type(RenderingDevice::TEXTURE_TYPE_2D); |
| 402 | // ⚠ CAN_UPDATE_BIT is required by texture_update and its ABSENCE IS NOT A |
| 403 | // CREATION ERROR — texture_create succeeds and the update fails much later. |
| 404 | fmt->set_usage_bits(RenderingDevice::TEXTURE_USAGE_STORAGE_BIT |
| 405 | | RenderingDevice::TEXTURE_USAGE_CAN_UPDATE_BIT |
| 406 | | RenderingDevice::TEXTURE_USAGE_CAN_COPY_TO_BIT |
| 407 | | RenderingDevice::TEXTURE_USAGE_CAN_COPY_FROM_BIT); |
| 408 | |
| 409 | Ref<RDTextureView> view; view.instantiate(); |
| 410 | atlas_tex_ = rd_->texture_create(fmt, view, TypedArray<PackedByteArray>()); |
| 411 | if (!atlas_tex_.is_valid()) { |
| 412 | fprintf(stderr, "DCCompute: atlas texture_create FAILED (%dx%d)\n", W, H); |
| 413 | return; |
| 414 | } |
| 415 | |
| 416 | // texture_update is WHOLE-LAYER, so the padded full-size image is written |
| 417 | // once here. Per-tile sub-region blits are the point of the texture and |
| 418 | // come with the streaming window; this step only proves the transport. |
| 419 | PackedByteArray pb; |
| 420 | pb.resize((int64_t)W * H * 4); |
| 421 | memset(pb.ptrw(), 0, (size_t)W * H * 4); |
| 422 | if (data != nullptr && n > 0) memcpy(pb.ptrw(), data, (size_t)n * sizeof(float)); |
| 423 | if (rd_->texture_update(atlas_tex_, 0, pb) != 0) { |
| 424 | fprintf(stderr, "DCCompute: atlas texture_update FAILED\n"); |
| 425 | return; |
| 426 | } |
| 427 | fprintf(stderr, "DCCompute: atlas %dx%d holds %d floats (%.1f MB)\n", |
| 428 | W, H, count, (double)W * H * 4 / 1048576.0); |
| 429 | } |
| 430 | |
| 376 | 431 | void DCCompute::_rebuild_uniform_set() { |
| 377 | 432 | // Skip rebuild if buffers haven't changed (optimization 5) |
| 378 | 433 | if (!uniform_set_dirty_ && uniform_set_.is_valid()) return; |
| … | … | @@ -473,6 +528,16 @@ void DCCompute::_rebuild_uniform_set() { |
| 473 | 528 | u13->set_binding(13); |
| 474 | 529 | u13->add_id(custom_data_buf_); |
| 475 | 530 | |
| 531 | // binding 14: the SAME data as a texture. A descriptor must point at a live |
| 532 | // resource even when the shader never reads it (see the note on binding 13's |
| 533 | // buffer), so this is created unconditionally. |
| 534 | if (!atlas_tex_.is_valid()) _upload_atlas(nullptr, 0); |
| 535 | Ref<RDUniform> u14; |
| 536 | u14.instantiate(); |
| 537 | u14->set_uniform_type(RenderingDevice::UNIFORM_TYPE_IMAGE); |
| 538 | u14->set_binding(14); |
| 539 | u14->add_id(atlas_tex_); |
| 540 | |
| 476 | 541 | // binding 12: convex-brush planes SSBO |
| 477 | 542 | Ref<RDUniform> u12; |
| 478 | 543 | u12.instantiate(); |
| … | … | @@ -495,6 +560,7 @@ void DCCompute::_rebuild_uniform_set() { |
| 495 | 560 | uniforms.push_back(u11); |
| 496 | 561 | uniforms.push_back(u12); |
| 497 | 562 | uniforms.push_back(u13); |
| 563 | uniforms.push_back(u14); |
| 498 | 564 | |
| 499 | 565 | uniform_set_ = rd_->uniform_set_create(uniforms, shader_, 0); |
| 500 | 566 | if (!uniform_set_.is_valid()) { |