| … | … | @@ -97,9 +97,13 @@ public: |
| 97 | 97 | // gpu_custom_data above. |
| 98 | 98 | // |
| 99 | 99 | // Returns an array of EIGHT pointers -- plain 0-3, then packed 4-7 -- each |
| 100 | // to dim^3 RGBA float32. nullptr means the body samples no volumes, and | |
| 100 | // to dim^3 RGBA HALF floats (the .vol files' native format, and what the | |
| 101 | // texture is created as). Deliberately not float32: promoting them changes | |
| 102 | // the precision of the hardware trilinear filtering, and a 25-unit domain | |
| 103 | // warp amplifies that into a visible density difference from anything else | |
| 104 | // sampling the same volumes. nullptr means the body samples no volumes, and | |
| 101 | 105 | // then neither shader declares the bindings. |
| 102 | virtual const float* const* gpu_noise_volumes() const { return nullptr; } | |
| 106 | virtual const void* const* gpu_noise_volumes() const { return nullptr; } | |
| 103 | 107 | // Side length, unlike gpu_custom_data_count which is an element count. |
| 104 | 108 | virtual int gpu_noise_dim() const { return 0; } |
| 105 | 109 | virtual uint32_t gpu_noise_version() const { return 0; } |
| … | … | @@ -15,7 +15,6 @@ |
| 15 | 15 | #include "dc_generator.h" |
| 16 | 16 | #include "geiss_sdf_glsl.h" |
| 17 | 17 | |
| 18 | #include <cmath> | |
| 19 | 18 | #include <cstdint> |
| 20 | 19 | #include <cstdio> |
| 21 | 20 | #include <string> |
| … | … | @@ -26,7 +25,7 @@ public: |
| 26 | 25 | bool has_gpu_shader() const override { return volumes_ok_; } |
| 27 | 26 | const char* gpu_shader_source() const override { return GEISS_SDF_GLSL; } |
| 28 | 27 | |
| 29 | const float* const* gpu_noise_volumes() const override { | |
| 28 | const void* const* gpu_noise_volumes() const override { | |
| 30 | 29 | return volumes_ok_ ? volume_ptrs_ : nullptr; |
| 31 | 30 | } |
| 32 | 31 | int gpu_noise_dim() const override { return dim_; } |
| … | … | @@ -76,8 +75,8 @@ public: |
| 76 | 75 | private: |
| 77 | 76 | // 5-int header (w, h, d, bytes-per-texel, body-bytes), then RGBA half |
| 78 | 77 | // floats, mip 0 first. Only mip 0 is read: the density samples |
| 79 | // textureLod(..., 0) everywhere. | |
| 80 | static bool _load_one(const std::string& path, std::vector<float>& out) { | |
| 78 | // textureLod(..., 0) everywhere. Kept as HALF -- see gpu_noise_volumes(). | |
| 79 | static bool _load_one(const std::string& path, std::vector<uint16_t>& out) { | |
| 81 | 80 | FILE* f = fopen(path.c_str(), "rb"); |
| 82 | 81 | if (!f) return false; |
| 83 | 82 | int32_t hdr[5]; |
| … | … | @@ -85,40 +84,14 @@ private: |
| 85 | 84 | const int w = hdr[0], h = hdr[1], d = hdr[2]; |
| 86 | 85 | if (w <= 0 || h <= 0 || d <= 0 || w != h || h != d) { fclose(f); return false; } |
| 87 | 86 | const size_t n = size_t(w) * h * d * 4; |
| 88 | std::vector<uint16_t> halfs(n); | |
| 89 | const bool read_ok = fread(halfs.data(), sizeof(uint16_t), n, f) == n; | |
| 90 | fclose(f); | |
| 91 | if (!read_ok) return false; | |
| 92 | 87 | out.resize(n); |
| 93 | for (size_t i = 0; i < n; i++) out[i] = _half_to_float(halfs[i]); | |
| 94 | return true; | |
| 95 | } | |
| 96 | ||
| 97 | static float _half_to_float(uint16_t h) { | |
| 98 | const uint32_t sign = uint32_t(h & 0x8000) << 16; | |
| 99 | uint32_t exp = (h >> 10) & 0x1F; | |
| 100 | uint32_t man = h & 0x3FF; | |
| 101 | uint32_t bits; | |
| 102 | if (exp == 0) { | |
| 103 | if (man == 0) { bits = sign; } | |
| 104 | else { // subnormal: normalise it | |
| 105 | exp = 1; | |
| 106 | while ((man & 0x400) == 0) { man <<= 1; exp--; } | |
| 107 | man &= 0x3FF; | |
| 108 | bits = sign | ((exp + 112) << 23) | (man << 13); | |
| 109 | } | |
| 110 | } else if (exp == 31) { // inf / nan | |
| 111 | bits = sign | 0x7F800000u | (man << 13); | |
| 112 | } else { | |
| 113 | bits = sign | ((exp + 112) << 23) | (man << 13); | |
| 114 | } | |
| 115 | float out; | |
| 116 | __builtin_memcpy(&out, &bits, 4); | |
| 117 | return out; | |
| 88 | const bool read_ok = fread(out.data(), sizeof(uint16_t), n, f) == n; | |
| 89 | fclose(f); | |
| 90 | return read_ok; | |
| 118 | 91 | } |
| 119 | 92 | |
| 120 | std::vector<float> storage_[8]; | |
| 121 | const float* volume_ptrs_[8] = {}; | |
| 93 | std::vector<uint16_t> storage_[8]; | |
| 94 | const void* volume_ptrs_[8] = {}; | |
| 122 | 95 | int dim_ = 16; |
| 123 | 96 | uint32_t version_ = 0; |
| 124 | 97 | bool volumes_ok_ = false; |
| … | … | @@ -387,14 +387,14 @@ void DCCompute::_create_noise_placeholders() { |
| 387 | 387 | } |
| 388 | 388 | if (noise_tex_[0].is_valid()) return; |
| 389 | 389 | Ref<RDTextureFormat> fmt; fmt.instantiate(); |
| 390 | fmt->set_format(RenderingDevice::DATA_FORMAT_R32G32B32A32_SFLOAT); | |
| 390 | fmt->set_format(RenderingDevice::DATA_FORMAT_R16G16B16A16_SFLOAT); | |
| 391 | 391 | fmt->set_width(1); fmt->set_height(1); fmt->set_depth(1); |
| 392 | 392 | fmt->set_array_layers(1); fmt->set_mipmaps(1); |
| 393 | 393 | fmt->set_texture_type(RenderingDevice::TEXTURE_TYPE_3D); |
| 394 | 394 | fmt->set_usage_bits(RenderingDevice::TEXTURE_USAGE_SAMPLING_BIT |
| 395 | 395 | | RenderingDevice::TEXTURE_USAGE_CAN_UPDATE_BIT); |
| 396 | 396 | Ref<RDTextureView> view; view.instantiate(); |
| 397 | PackedByteArray zero; zero.resize(16); memset(zero.ptrw(), 0, 16); | |
| 397 | PackedByteArray zero; zero.resize(8); memset(zero.ptrw(), 0, 8); // 1 RGBA half texel | |
| 398 | 398 | TypedArray<PackedByteArray> data; data.push_back(zero); |
| 399 | 399 | for (int i = 0; i < 8; i++) noise_tex_[i] = rd_->texture_create(fmt, view, data); |
| 400 | 400 | noise_dim_ = 1; |
| … | … | @@ -407,20 +407,20 @@ void DCCompute::_free_noise() { |
| 407 | 407 | noise_dim_ = 0; |
| 408 | 408 | } |
| 409 | 409 | |
| 410 | void DCCompute::set_noise_volumes(const float* const* volumes, int dim) { | |
| 410 | void DCCompute::set_noise_volumes(const void* const* volumes, int dim) { | |
| 411 | 411 | if (rd_ == nullptr) return; |
| 412 | 412 | _free_noise(); |
| 413 | 413 | if (volumes == nullptr || dim <= 0) { _create_noise_placeholders(); return; } |
| 414 | 414 | |
| 415 | 415 | Ref<RDTextureFormat> fmt; fmt.instantiate(); |
| 416 | fmt->set_format(RenderingDevice::DATA_FORMAT_R32G32B32A32_SFLOAT); | |
| 416 | fmt->set_format(RenderingDevice::DATA_FORMAT_R16G16B16A16_SFLOAT); | |
| 417 | 417 | fmt->set_width(dim); fmt->set_height(dim); fmt->set_depth(dim); |
| 418 | 418 | fmt->set_array_layers(1); fmt->set_mipmaps(1); |
| 419 | 419 | fmt->set_texture_type(RenderingDevice::TEXTURE_TYPE_3D); |
| 420 | 420 | fmt->set_usage_bits(RenderingDevice::TEXTURE_USAGE_SAMPLING_BIT |
| 421 | 421 | | RenderingDevice::TEXTURE_USAGE_CAN_UPDATE_BIT); |
| 422 | 422 | Ref<RDTextureView> view; view.instantiate(); |
| 423 | const int64_t n_bytes = int64_t(dim) * dim * dim * 4 * 4; | |
| 423 | const int64_t n_bytes = int64_t(dim) * dim * dim * 4 * 2; // RGBA half | |
| 424 | 424 | for (int i = 0; i < 8; i++) { |
| 425 | 425 | PackedByteArray b; b.resize(n_bytes); |
| 426 | 426 | memcpy(b.ptrw(), volumes[i], size_t(n_bytes)); |
| … | … | @@ -105,9 +105,9 @@ public: |
| 105 | 105 | RID noise_sampler_linear() const { return noise_sampler_linear_; } |
| 106 | 106 | RID noise_sampler_nearest() const { return noise_sampler_nearest_; } |
| 107 | 107 | RID noise_tex(int i) const { return noise_tex_[i]; } |
| 108 | // data: 8 volumes of dim^3 RGBA float32, plain 0-3 then packed 4-7. | |
| 108 | // data: 8 volumes of dim^3 RGBA HALF, plain 0-3 then packed 4-7. | |
| 109 | 109 | // Pass nullptr to drop back to the placeholders. |
| 110 | void set_noise_volumes(const float* const* volumes, int dim); | |
| 110 | void set_noise_volumes(const void* const* volumes, int dim); | |
| 111 | 111 | // Upload a baked custom data block (metres, n*n row-major, row = Z). |
| 112 | 112 | // Pass n = 0 to release it back to the one-float placeholder. |
| 113 | 113 | void set_custom_data(const float* data, int n); |
| … | … | @@ -42,7 +42,7 @@ struct GpuFillRequest { |
| 42 | 42 | const float* custom_data = nullptr; // optional: baked custom data (binding 13) |
| 43 | 43 | int custom_data_n = 0; |
| 44 | 44 | uint32_t custom_data_version = 0; // content version; see DCGenerator |
| 45 | const float* const* noise_volumes = nullptr; // optional: 8 sampler3D volumes | |
| 45 | const void* const* noise_volumes = nullptr; // optional: 8 sampler3D volumes (RGBA half) | |
| 46 | 46 | int noise_dim = 0; // side length (16 for the GPU Gems volumes) |
| 47 | 47 | uint32_t noise_version = 0; |
| 48 | 48 | const std::vector<TerrainEdit>* edits = nullptr; // terrain edits for GPU evaluation |
| … | … | @@ -108,7 +108,7 @@ public: |
| 108 | 108 | uint32_t mesh_quality_flags = 0, |
| 109 | 109 | const float* custom_data = nullptr, int custom_data_n = 0, |
| 110 | 110 | uint32_t custom_data_version = 0, |
| 111 | const float* const* noise_volumes = nullptr, int noise_dim = 0, | |
| 111 | const void* const* noise_volumes = nullptr, int noise_dim = 0, | |
| 112 | 112 | uint32_t noise_version = 0) { |
| 113 | 113 | if (!running_.load() || !gpu_available_.load()) { |
| 114 | 114 | return false; |
| … | … | @@ -166,7 +166,7 @@ private: |
| 166 | 166 | DCGpuMeshPipeline mesh_pipeline_; |
| 167 | 167 | bool mesh_pipeline_available_ = false; |
| 168 | 168 | const char* current_custom_glsl_ = nullptr; // track to avoid recompiling same shader |
| 169 | const float* const* current_noise_volumes_ = nullptr; | |
| 169 | const void* const* current_noise_volumes_ = nullptr; | |
| 170 | 170 | uint32_t current_noise_version_ = 0; |
| 171 | 171 | const float* current_custom_data_ = nullptr; // same trick: re-upload only on change |
| 172 | 172 | uint32_t current_custom_data_version_ = 0; // ...and on content version, for growth |
| … | … | @@ -3199,7 +3199,7 @@ PackedFloat32Array DCTerrain::debug_gpu_density(const Vector3 &origin, float vox |
| 3199 | 3199 | const float* hb = generator_ptr_->gpu_custom_data(); |
| 3200 | 3200 | int hbn = generator_ptr_->gpu_custom_data_count(); |
| 3201 | 3201 | uint32_t hbv = generator_ptr_->gpu_custom_data_version(); |
| 3202 | const float* const* nv = generator_ptr_->gpu_noise_volumes(); | |
| 3202 | const void* const* nv = generator_ptr_->gpu_noise_volumes(); | |
| 3203 | 3203 | int nvd = generator_ptr_->gpu_noise_dim(); |
| 3204 | 3204 | uint32_t nvv = generator_ptr_->gpu_noise_version(); |
| 3205 | 3205 |
| … | … | @@ -239,7 +239,7 @@ void DCWorkerCPU::_process_cell(const CellRequest &request, int64_t picked_up_us |
| 239 | 239 | const float* custom_data = nullptr; |
| 240 | 240 | int custom_data_n = 0; |
| 241 | 241 | uint32_t custom_data_version = 0; |
| 242 | const float* const* noise_volumes = nullptr; | |
| 242 | const void* const* noise_volumes = nullptr; | |
| 243 | 243 | int noise_dim = 0; |
| 244 | 244 | uint32_t noise_version = 0; |
| 245 | 245 | if (request.generator && request.generator->has_gpu_shader()) { |