diff --git a/src/core/device/resource.cpp b/src/core/device/resource.cpp index 889d275..f716978 100644 --- a/src/core/device/resource.cpp +++ b/src/core/device/resource.cpp @@ -359,6 +359,44 @@ GPUMeshBuffers ResourceManager::uploadMesh(std::span indices, std::spa return newSurface; } +AllocatedBuffer ResourceManager::upload_buffer(const void *data, size_t size, VkBufferUsageFlags usage, + VmaMemoryUsage memoryUsage) +{ + if (data == nullptr || size == 0) + { + return {}; + } + + AllocatedBuffer dst = create_buffer(size, usage | VK_BUFFER_USAGE_TRANSFER_DST_BIT, memoryUsage); + + AllocatedBuffer staging = create_buffer(size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VMA_MEMORY_USAGE_CPU_ONLY); + + memcpy(staging.info.pMappedData, data, size); + vmaFlushAllocation(_deviceManager->allocator(), staging.allocation, 0, size); + + PendingBufferUpload pending{}; + pending.staging = staging; + pending.copies.push_back(BufferCopyRegion{ + .destination = dst.buffer, + .dstOffset = 0, + .size = size, + .stagingOffset = 0, + }); + + { + std::lock_guard lk(_pendingMutex); + _pendingBufferUploads.push_back(std::move(pending)); + } + + if (!_deferUploads) + { + process_queued_uploads_immediate(); + } + + return dst; +} + bool ResourceManager::has_pending_uploads() const { std::lock_guard lk(_pendingMutex); diff --git a/src/core/device/resource.h b/src/core/device/resource.h index 61b6d77..9174926 100644 --- a/src/core/device/resource.h +++ b/src/core/device/resource.h @@ -94,6 +94,12 @@ public: GPUMeshBuffers uploadMesh(std::span indices, std::span vertices); + // Upload raw bytes into a GPU buffer. The destination buffer is created with the provided 'usage' + // flags plus VK_BUFFER_USAGE_TRANSFER_DST_BIT. Staging is handled internally and freed via the + // per-frame deletion queue when deferred uploads are enabled. + AllocatedBuffer upload_buffer(const void *data, size_t size, VkBufferUsageFlags usage, + VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_GPU_ONLY); + void immediate_submit(std::function &&function) const; bool has_pending_uploads() const; diff --git a/src/scene/planet/cubesphere.cpp b/src/scene/planet/cubesphere.cpp index b678263..55209aa 100644 --- a/src/scene/planet/cubesphere.cpp +++ b/src/scene/planet/cubesphere.cpp @@ -83,38 +83,114 @@ namespace planet return glm::max(10.0, 0.02 * edge_m); } - void build_cubesphere_patch_mesh(CubeSpherePatchMesh &out, - const WorldVec3 ¢er_world, - double radius_m, - CubeFace face, - uint32_t level, - uint32_t x, - uint32_t y, - uint32_t resolution, - const glm::vec4 &vertex_color, - bool generate_tangents) + void build_cubesphere_patch_indices(std::vector &out_indices, uint32_t resolution) { - out.vertices.clear(); - out.indices.clear(); - out.patch_center_world = center_world; + out_indices.clear(); if (resolution < 2) { return; } + const size_t grid_index_count = + static_cast(resolution - 1u) * static_cast(resolution - 1u) * 6u; + const size_t skirt_index_count = static_cast(4u) * static_cast(resolution - 1u) * 6u; + out_indices.reserve(grid_index_count + skirt_index_count); + + // Base grid indices + for (uint32_t j = 0; j + 1 < resolution; ++j) + { + for (uint32_t i = 0; i + 1 < resolution; ++i) + { + const uint32_t i0 = j * resolution + i; + const uint32_t i1 = i0 + 1; + const uint32_t i2 = i0 + resolution; + const uint32_t i3 = i2 + 1; + + // CCW winding when viewed from outside the sphere. + out_indices.push_back(i0); + out_indices.push_back(i1); + out_indices.push_back(i2); + + out_indices.push_back(i2); + out_indices.push_back(i1); + out_indices.push_back(i3); + } + } + + auto add_skirt_quads = [&](uint32_t base0, uint32_t base1, uint32_t skirt0, uint32_t skirt1) + { + out_indices.push_back(base0); + out_indices.push_back(base1); + out_indices.push_back(skirt0); + + out_indices.push_back(skirt0); + out_indices.push_back(base1); + out_indices.push_back(skirt1); + }; + + const uint32_t base_vertex_count = resolution * resolution; + const uint32_t top_skirt_start = base_vertex_count + 0u * resolution; + const uint32_t right_skirt_start = base_vertex_count + 1u * resolution; + const uint32_t bottom_skirt_start = base_vertex_count + 2u * resolution; + const uint32_t left_skirt_start = base_vertex_count + 3u * resolution; + + // Skirt indices: 4 edges, (N-1) segments each. + for (uint32_t i = 0; i + 1 < resolution; ++i) + { + // Top edge + add_skirt_quads(0u * resolution + i, + 0u * resolution + (i + 1u), + top_skirt_start + i, + top_skirt_start + (i + 1u)); + // Bottom edge + add_skirt_quads((resolution - 1u) * resolution + i, + (resolution - 1u) * resolution + (i + 1u), + bottom_skirt_start + i, + bottom_skirt_start + (i + 1u)); + } + for (uint32_t j = 0; j + 1 < resolution; ++j) + { + // Left edge + add_skirt_quads(j * resolution + 0u, + (j + 1u) * resolution + 0u, + left_skirt_start + j, + left_skirt_start + (j + 1u)); + // Right edge + add_skirt_quads(j * resolution + (resolution - 1u), + (j + 1u) * resolution + (resolution - 1u), + right_skirt_start + j, + right_skirt_start + (j + 1u)); + } + } + + glm::dvec3 build_cubesphere_patch_vertices(std::vector &out_vertices, + double radius_m, + CubeFace face, + uint32_t level, + uint32_t x, + uint32_t y, + uint32_t resolution, + const glm::vec4 &vertex_color) + { + out_vertices.clear(); + + if (resolution < 2) + { + return glm::dvec3(0.0, 0.0, 1.0); + } + + const glm::dvec3 patch_center_dir = cubesphere_patch_center_direction(face, level, x, y); + const double skirt_depth_m = cubesphere_skirt_depth_m(radius_m, level); const double skirt_radius_m = glm::max(0.0, radius_m - skirt_depth_m); double u0 = 0.0, u1 = 0.0, v0 = 0.0, v1 = 0.0; cubesphere_tile_uv_bounds(level, x, y, u0, u1, v0, v1); - const glm::dvec3 patch_center_dir = cubesphere_patch_center_direction(face, level, x, y); - out.patch_center_world = center_world + patch_center_dir * radius_m; - const uint32_t base_vertex_count = resolution * resolution; const uint32_t skirt_vertex_count = 4u * resolution; - out.vertices.resize(static_cast(base_vertex_count) + static_cast(skirt_vertex_count)); + out_vertices.resize(static_cast(base_vertex_count) + static_cast(skirt_vertex_count)); const double inv = 1.0 / static_cast(resolution - 1u); const double du = (u1 - u0) * inv; @@ -145,26 +221,26 @@ namespace planet vert.tangent = glm::vec4(1.0f, 0.0f, 0.0f, 1.0f); const uint32_t idx = j * resolution + i; - out.vertices[idx] = vert; + out_vertices[idx] = vert; } } auto add_skirt_vertex = [&](uint32_t base_index, uint32_t skirt_index) { - const glm::vec3 n = out.vertices[base_index].normal; + const glm::vec3 n = out_vertices[base_index].normal; const glm::dvec3 unit_dir(static_cast(n.x), static_cast(n.y), static_cast(n.z)); const glm::dvec3 delta_d = unit_dir * skirt_radius_m - patch_center_dir * radius_m; - Vertex vert = out.vertices[base_index]; + Vertex vert = out_vertices[base_index]; vert.position = glm::vec3(static_cast(delta_d.x), static_cast(delta_d.y), static_cast(delta_d.z)); vert.normal = glm::vec3(static_cast(unit_dir.x), static_cast(unit_dir.y), static_cast(unit_dir.z)); - out.vertices[skirt_index] = vert; + out_vertices[skirt_index] = vert; }; const uint32_t top_skirt_start = base_vertex_count + 0u * resolution; @@ -193,70 +269,34 @@ namespace planet add_skirt_vertex(j * resolution + 0u, left_skirt_start + j); } - const size_t grid_index_count = - static_cast(resolution - 1u) * static_cast(resolution - 1u) * 6u; - const size_t skirt_index_count = static_cast(4u) * static_cast(resolution - 1u) * 6u; - out.indices.reserve(grid_index_count + skirt_index_count); + return patch_center_dir; + } - // Base grid indices - for (uint32_t j = 0; j + 1 < resolution; ++j) + void build_cubesphere_patch_mesh(CubeSpherePatchMesh &out, + const WorldVec3 ¢er_world, + double radius_m, + CubeFace face, + uint32_t level, + uint32_t x, + uint32_t y, + uint32_t resolution, + const glm::vec4 &vertex_color, + bool generate_tangents) + { + out.vertices.clear(); + out.indices.clear(); + out.patch_center_world = center_world; + + if (resolution < 2) { - for (uint32_t i = 0; i + 1 < resolution; ++i) - { - const uint32_t i0 = j * resolution + i; - const uint32_t i1 = i0 + 1; - const uint32_t i2 = i0 + resolution; - const uint32_t i3 = i2 + 1; - - // CCW winding when viewed from outside the sphere. - out.indices.push_back(i0); - out.indices.push_back(i1); - out.indices.push_back(i2); - - out.indices.push_back(i2); - out.indices.push_back(i1); - out.indices.push_back(i3); - } + return; } - auto add_skirt_quads = [&](uint32_t base0, uint32_t base1, uint32_t skirt0, uint32_t skirt1) - { - out.indices.push_back(base0); - out.indices.push_back(base1); - out.indices.push_back(skirt0); + const glm::dvec3 patch_center_dir = + build_cubesphere_patch_vertices(out.vertices, radius_m, face, level, x, y, resolution, vertex_color); + build_cubesphere_patch_indices(out.indices, resolution); - out.indices.push_back(skirt0); - out.indices.push_back(base1); - out.indices.push_back(skirt1); - }; - - // Skirt indices: 4 edges, (N-1) segments each. - for (uint32_t i = 0; i + 1 < resolution; ++i) - { - // Top edge - add_skirt_quads(0u * resolution + i, - 0u * resolution + (i + 1u), - top_skirt_start + i, - top_skirt_start + (i + 1u)); - // Bottom edge - add_skirt_quads((resolution - 1u) * resolution + i, - (resolution - 1u) * resolution + (i + 1u), - bottom_skirt_start + i, - bottom_skirt_start + (i + 1u)); - } - for (uint32_t j = 0; j + 1 < resolution; ++j) - { - // Left edge - add_skirt_quads(j * resolution + 0u, - (j + 1u) * resolution + 0u, - left_skirt_start + j, - left_skirt_start + (j + 1u)); - // Right edge - add_skirt_quads(j * resolution + (resolution - 1u), - (j + 1u) * resolution + (resolution - 1u), - right_skirt_start + j, - right_skirt_start + (j + 1u)); - } + out.patch_center_world = center_world + patch_center_dir * radius_m; if (generate_tangents) { diff --git a/src/scene/planet/cubesphere.h b/src/scene/planet/cubesphere.h index 2c5b890..5e26dbd 100644 --- a/src/scene/planet/cubesphere.h +++ b/src/scene/planet/cubesphere.h @@ -55,6 +55,21 @@ namespace planet WorldVec3 patch_center_world{0.0, 0.0, 0.0}; }; + // Build the shared index list for a patch grid with skirts. Indices are identical for all + // patches as long as 'resolution' is constant. + void build_cubesphere_patch_indices(std::vector &out_indices, uint32_t resolution); + + // Build patch vertices (including skirts). Vertex positions are relative to the patch center on + // the sphere surface (computed from face/level/x/y). Returns the patch center direction. + glm::dvec3 build_cubesphere_patch_vertices(std::vector &out_vertices, + double radius_m, + CubeFace face, + uint32_t level, + uint32_t x, + uint32_t y, + uint32_t resolution, + const glm::vec4 &vertex_color); + // Build a cube-sphere patch mesh with skirts. Vertex positions are relative to patch_center_world. void build_cubesphere_patch_mesh(CubeSpherePatchMesh &out, const WorldVec3 ¢er_world, diff --git a/src/scene/planet/planet_system.cpp b/src/scene/planet/planet_system.cpp index 50aee44..c4d5e41 100644 --- a/src/scene/planet/planet_system.cpp +++ b/src/scene/planet/planet_system.cpp @@ -1,6 +1,7 @@ #include "planet_system.h" #include +#include #include #include #include @@ -16,12 +17,42 @@ #include #include +#include "device.h" + namespace { constexpr double kEarthRadiusM = 6378137.0; // WGS84 equatorial radius constexpr double kMoonRadiusM = 1737400.0; // mean radius constexpr double kMoonDistanceM = 384400000.0; // mean Earth-Moon distance + struct PatchBoundsData + { + glm::vec3 origin{0.0f}; + glm::vec3 extents{0.5f}; + float sphere_radius{0.5f}; + }; + + PatchBoundsData compute_patch_bounds(const std::vector &vertices) + { + PatchBoundsData b{}; + if (vertices.empty()) + { + return b; + } + + glm::vec3 minpos = vertices[0].position; + glm::vec3 maxpos = vertices[0].position; + for (const auto &v : vertices) + { + minpos = glm::min(minpos, v.position); + maxpos = glm::max(maxpos, v.position); + } + b.origin = (maxpos + minpos) * 0.5f; + b.extents = (maxpos - minpos) * 0.5f; + b.sphere_radius = glm::length(b.extents); + return b; + } + GLTFMetallic_Roughness::MaterialConstants make_planet_constants() { GLTFMetallic_Roughness::MaterialConstants c{}; @@ -114,52 +145,193 @@ void PlanetSystem::ensure_bodies_created() _bodies.push_back(std::move(moon)); } -std::shared_ptr PlanetSystem::get_or_create_earth_patch_mesh(const PlanetBody &earth, - const planet::PatchKey &key) +PlanetSystem::EarthPatch *PlanetSystem::find_earth_patch(const planet::PatchKey &key) { - auto it = _earth_patch_cache.find(key); - if (it != _earth_patch_cache.end()) + auto it = _earth_patch_lookup.find(key); + if (it == _earth_patch_lookup.end()) { - it->second.last_used_frame = _context ? _context->frameIndex : 0; - _earth_patch_lru.splice(_earth_patch_lru.begin(), _earth_patch_lru, it->second.lru_it); - return it->second.mesh; + return nullptr; + } + const uint32_t idx = it->second; + if (idx >= _earth_patches.size()) + { + return nullptr; + } + return &_earth_patches[idx]; +} + +void PlanetSystem::ensure_earth_patch_index_buffer() +{ + if (_earth_patch_index_buffer.buffer != VK_NULL_HANDLE && _earth_patch_index_resolution == _earth_patch_resolution) + { + return; } - if (!_context || !_context->assets || !earth.material) + if (!_context) { - return {}; + return; } - planet::CubeSpherePatchMesh mesh{}; - planet::build_cubesphere_patch_mesh(mesh, - earth.center_world, - earth.radius_m, - key.face, - key.level, - key.x, - key.y, - _earth_patch_resolution, - debug_color_for_level(key.level), - /*generate_tangents=*/false); + ResourceManager *rm = _context->getResources(); + if (!rm) + { + return; + } - const uint32_t face_i = static_cast(key.face); - const std::string name = - "Planet_EarthPatch_f" + std::to_string(face_i) + - "_L" + std::to_string(key.level) + - "_X" + std::to_string(key.x) + - "_Y" + std::to_string(key.y); + // Resolution changed (or first init): clear existing patch cache and shared index buffer. + if (_earth_patch_index_buffer.buffer != VK_NULL_HANDLE) + { + FrameResources *frame = _context->currentFrame; - std::shared_ptr out = - _context->assets->createMesh(name, mesh.vertices, mesh.indices, earth.material, /*build_bvh=*/false); + // Destroy per-patch vertex buffers. + for (const auto &kv : _earth_patch_lookup) + { + const uint32_t idx = kv.second; + if (idx >= _earth_patches.size()) + { + continue; + } + EarthPatch &p = _earth_patches[idx]; + if (p.vertex_buffer.buffer == VK_NULL_HANDLE) + { + continue; + } - EarthPatchCacheEntry entry{}; - entry.mesh = out; - entry.patch_center_dir = planet::cubesphere_patch_center_direction(key.face, key.level, key.x, key.y); - entry.last_used_frame = _context ? _context->frameIndex : 0; - _earth_patch_lru.push_front(key); - entry.lru_it = _earth_patch_lru.begin(); - _earth_patch_cache.emplace(key, std::move(entry)); - return out; + const AllocatedBuffer vb = p.vertex_buffer; + if (frame) + { + frame->_deletionQueue.push_function([rm, vb]() { rm->destroy_buffer(vb); }); + } + else + { + rm->destroy_buffer(vb); + } + } + + _earth_patch_lookup.clear(); + _earth_patch_lru.clear(); + _earth_patch_free.clear(); + _earth_patches.clear(); + + const AllocatedBuffer ib = _earth_patch_index_buffer; + if (frame) + { + frame->_deletionQueue.push_function([rm, ib]() { rm->destroy_buffer(ib); }); + } + else + { + rm->destroy_buffer(ib); + } + _earth_patch_index_buffer = {}; + _earth_patch_index_count = 0; + _earth_patch_index_resolution = 0; + } + + std::vector indices; + planet::build_cubesphere_patch_indices(indices, _earth_patch_resolution); + _earth_patch_index_count = static_cast(indices.size()); + _earth_patch_index_buffer = + rm->upload_buffer(indices.data(), + indices.size() * sizeof(uint32_t), + VK_BUFFER_USAGE_INDEX_BUFFER_BIT | + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | + VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR); + _earth_patch_index_resolution = _earth_patch_resolution; +} + +PlanetSystem::EarthPatch *PlanetSystem::get_or_create_earth_patch(const PlanetBody &earth, + const planet::PatchKey &key, + uint32_t frame_index) +{ + if (EarthPatch *p = find_earth_patch(key)) + { + p->last_used_frame = frame_index; + _earth_patch_lru.splice(_earth_patch_lru.begin(), _earth_patch_lru, p->lru_it); + return p; + } + + if (!_context) + { + return nullptr; + } + + ResourceManager *rm = _context->getResources(); + DeviceManager *device = _context->getDevice(); + if (!rm || !device) + { + return nullptr; + } + + if (_earth_patch_index_buffer.buffer == VK_NULL_HANDLE || _earth_patch_index_count == 0) + { + return nullptr; + } + + std::vector vertices; + const glm::dvec3 patch_center_dir = + planet::build_cubesphere_patch_vertices(vertices, + earth.radius_m, + key.face, + key.level, + key.x, + key.y, + _earth_patch_resolution, + debug_color_for_level(key.level)); + + if (vertices.empty()) + { + return nullptr; + } + + const PatchBoundsData bounds = compute_patch_bounds(vertices); + + AllocatedBuffer vb = + rm->upload_buffer(vertices.data(), + vertices.size() * sizeof(Vertex), + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | + VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR); + if (vb.buffer == VK_NULL_HANDLE) + { + return nullptr; + } + + VkBufferDeviceAddressInfo addrInfo{.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO}; + addrInfo.buffer = vb.buffer; + VkDeviceAddress addr = vkGetBufferDeviceAddress(device->device(), &addrInfo); + + uint32_t idx = 0; + if (!_earth_patch_free.empty()) + { + idx = _earth_patch_free.back(); + _earth_patch_free.pop_back(); + } + else + { + idx = static_cast(_earth_patches.size()); + _earth_patches.emplace_back(); + } + + if (idx >= _earth_patches.size()) + { + return nullptr; + } + + EarthPatch &p = _earth_patches[idx]; + p.key = key; + p.state = EarthPatchState::Ready; + p.vertex_buffer = vb; + p.vertex_buffer_address = addr; + p.bounds_origin = bounds.origin; + p.bounds_extents = bounds.extents; + p.bounds_sphere_radius = bounds.sphere_radius; + p.patch_center_dir = patch_center_dir; + p.last_used_frame = frame_index; + _earth_patch_lru.push_front(idx); + p.lru_it = _earth_patch_lru.begin(); + + _earth_patch_lookup.emplace(key, idx); + return &p; } void PlanetSystem::trim_earth_patch_cache() @@ -169,46 +341,72 @@ void PlanetSystem::trim_earth_patch_cache() return; } - if (_earth_patch_cache.size() <= static_cast(_earth_patch_cache_max)) + if (_earth_patch_lookup.size() <= static_cast(_earth_patch_cache_max)) { return; } - if (!_context || !_context->assets) + if (!_context) + { + return; + } + + ResourceManager *rm = _context->getResources(); + if (!rm) { return; } - AssetManager *assets = _context->assets; FrameResources *frame = _context->currentFrame; + const uint32_t now = _earth_patch_frame_stamp; - while (_earth_patch_cache.size() > static_cast(_earth_patch_cache_max) && !_earth_patch_lru.empty()) + size_t guard = 0; + const size_t guard_limit = _earth_patch_lru.size(); + + while (_earth_patch_lookup.size() > static_cast(_earth_patch_cache_max) && !_earth_patch_lru.empty()) { - const planet::PatchKey key = _earth_patch_lru.back(); - _earth_patch_lru.pop_back(); - - auto it = _earth_patch_cache.find(key); - if (it == _earth_patch_cache.end()) + if (guard++ >= guard_limit) { + // No evictable patches (all used this frame). Avoid thrashing. + break; + } + + const uint32_t idx = _earth_patch_lru.back(); + if (idx >= _earth_patches.size()) + { + _earth_patch_lru.pop_back(); continue; } - std::shared_ptr mesh = std::move(it->second.mesh); - _earth_patch_cache.erase(it); - - if (!mesh) + EarthPatch &p = _earth_patches[idx]; + if (p.last_used_frame == now) { + // Keep all patches referenced this frame. + _earth_patch_lru.splice(_earth_patch_lru.begin(), _earth_patch_lru, p.lru_it); continue; } - if (frame) + // Made progress: we found an evictable patch. + guard = 0; + + _earth_patch_lru.erase(p.lru_it); + _earth_patch_lookup.erase(p.key); + + if (p.vertex_buffer.buffer != VK_NULL_HANDLE) { - assets->removeMeshDeferred(mesh->name, frame->_deletionQueue); - } - else - { - assets->removeMesh(mesh->name); + const AllocatedBuffer vb = p.vertex_buffer; + if (frame) + { + frame->_deletionQueue.push_function([rm, vb]() { rm->destroy_buffer(vb); }); + } + else + { + rm->destroy_buffer(vb); + } } + + p = EarthPatch{}; + _earth_patch_free.push_back(idx); } } @@ -246,24 +444,24 @@ void PlanetSystem::update_and_emit(const SceneManager &scene, DrawContext &draw_ logical_extent); const Clock::time_point t_q1 = Clock::now(); + ensure_earth_patch_index_buffer(); + uint32_t created_patches = 0; double ms_patch_create = 0.0; const uint32_t max_create = _earth_patch_create_budget_per_frame; const double max_create_ms = (_earth_patch_create_budget_ms > 0.0f) ? static_cast(_earth_patch_create_budget_ms) : 0.0; - const uint32_t frame_index = _context->frameIndex; + const uint32_t frame_index = ++_earth_patch_frame_stamp; const Clock::time_point t_emit0 = Clock::now(); for (const planet::PatchKey &k : _earth_quadtree.visible_leaves()) { - EarthPatchCacheEntry *entry = nullptr; + EarthPatch *patch = find_earth_patch(k); { - auto it = _earth_patch_cache.find(k); - if (it != _earth_patch_cache.end()) + if (patch) { - it->second.last_used_frame = frame_index; - _earth_patch_lru.splice(_earth_patch_lru.begin(), _earth_patch_lru, it->second.lru_it); - entry = &it->second; + patch->last_used_frame = frame_index; + _earth_patch_lru.splice(_earth_patch_lru.begin(), _earth_patch_lru, patch->lru_it); } else { @@ -272,53 +470,55 @@ void PlanetSystem::update_and_emit(const SceneManager &scene, DrawContext &draw_ if (!hit_count_budget && !hit_time_budget) { const Clock::time_point t_c0 = Clock::now(); - (void)get_or_create_earth_patch_mesh(*earth, k); + patch = get_or_create_earth_patch(*earth, k, frame_index); const Clock::time_point t_c1 = Clock::now(); - created_patches++; + if (patch) + { + created_patches++; + } ms_patch_create += std::chrono::duration(t_c1 - t_c0).count(); } - - auto it2 = _earth_patch_cache.find(k); - if (it2 != _earth_patch_cache.end()) - { - entry = &it2->second; - } } } - if (!entry || !entry->mesh || entry->mesh->surfaces.empty()) + if (!patch || + patch->state != EarthPatchState::Ready || + patch->vertex_buffer.buffer == VK_NULL_HANDLE || + patch->vertex_buffer_address == 0 || + _earth_patch_index_buffer.buffer == VK_NULL_HANDLE || + _earth_patch_index_count == 0) { continue; } - const std::shared_ptr &mesh = entry->mesh; - const WorldVec3 patch_center_world = - earth->center_world + entry->patch_center_dir * earth->radius_m; + earth->center_world + patch->patch_center_dir * earth->radius_m; const glm::vec3 patch_center_local = world_to_local(patch_center_world, origin_world); const glm::mat4 transform = glm::translate(glm::mat4(1.0f), patch_center_local); - uint32_t surface_index = 0; - for (const GeoSurface &surf : mesh->surfaces) - { - RenderObject obj{}; - obj.indexCount = surf.count; - obj.firstIndex = surf.startIndex; - obj.indexBuffer = mesh->meshBuffers.indexBuffer.buffer; - obj.vertexBuffer = mesh->meshBuffers.vertexBuffer.buffer; - obj.vertexBufferAddress = mesh->meshBuffers.vertexBufferAddress; - obj.material = surf.material ? &surf.material->data : nullptr; - obj.bounds = surf.bounds; - obj.transform = transform; - // Planet terrain patches are not meaningful RT occluders; skip BLAS/TLAS builds. - obj.sourceMesh = nullptr; - obj.surfaceIndex = surface_index++; - obj.objectID = draw_context.nextID++; - obj.ownerType = RenderObject::OwnerType::MeshInstance; - obj.ownerName = earth->name; + Bounds b{}; + b.origin = patch->bounds_origin; + b.extents = patch->bounds_extents; + b.sphereRadius = patch->bounds_sphere_radius; + b.type = BoundsType::Box; - draw_context.OpaqueSurfaces.push_back(obj); - } + RenderObject obj{}; + obj.indexCount = _earth_patch_index_count; + obj.firstIndex = 0; + obj.indexBuffer = _earth_patch_index_buffer.buffer; + obj.vertexBuffer = patch->vertex_buffer.buffer; + obj.vertexBufferAddress = patch->vertex_buffer_address; + obj.material = earth->material ? &earth->material->data : nullptr; + obj.bounds = b; + obj.transform = transform; + // Planet terrain patches are not meaningful RT occluders; skip BLAS/TLAS builds. + obj.sourceMesh = nullptr; + obj.surfaceIndex = 0; + obj.objectID = draw_context.nextID++; + obj.ownerType = RenderObject::OwnerType::MeshInstance; + obj.ownerName = earth->name; + + draw_context.OpaqueSurfaces.push_back(obj); } const Clock::time_point t_emit1 = Clock::now(); @@ -333,7 +533,7 @@ void PlanetSystem::update_and_emit(const SceneManager &scene, DrawContext &draw_ _earth_debug_stats.quadtree = _earth_quadtree.stats(); _earth_debug_stats.visible_patches = visible_patches; _earth_debug_stats.created_patches = created_patches; - _earth_debug_stats.patch_cache_size = static_cast(_earth_patch_cache.size()); + _earth_debug_stats.patch_cache_size = static_cast(_earth_patch_lookup.size()); _earth_debug_stats.estimated_triangles = estimated_tris; _earth_debug_stats.ms_quadtree = static_cast(std::chrono::duration(t_q1 - t_q0).count()); _earth_debug_stats.ms_patch_create = static_cast(ms_patch_create); diff --git a/src/scene/planet/planet_system.h b/src/scene/planet/planet_system.h index 476645b..9db3a53 100644 --- a/src/scene/planet/planet_system.h +++ b/src/scene/planet/planet_system.h @@ -74,16 +74,35 @@ public: void set_earth_patch_cache_max(uint32_t max_patches) { _earth_patch_cache_max = max_patches; } private: - struct EarthPatchCacheEntry + enum class EarthPatchState : uint8_t { - std::shared_ptr mesh; + Allocating = 0, + Ready = 1, + }; + + struct EarthPatch + { + planet::PatchKey key{}; + EarthPatchState state = EarthPatchState::Allocating; + + AllocatedBuffer vertex_buffer{}; + VkDeviceAddress vertex_buffer_address = 0; + + glm::vec3 bounds_origin{0.0f}; + glm::vec3 bounds_extents{0.5f}; + float bounds_sphere_radius = 0.5f; + WorldVec3 patch_center_dir{0.0, 0.0, 1.0}; uint32_t last_used_frame = 0; - std::list::iterator lru_it; + std::list::iterator lru_it{}; }; void ensure_bodies_created(); - std::shared_ptr get_or_create_earth_patch_mesh(const PlanetBody &earth, const planet::PatchKey &key); + EarthPatch *find_earth_patch(const planet::PatchKey &key); + EarthPatch *get_or_create_earth_patch(const PlanetBody &earth, + const planet::PatchKey &key, + uint32_t frame_index); + void ensure_earth_patch_index_buffer(); void trim_earth_patch_cache(); EngineContext *_context = nullptr; @@ -94,8 +113,14 @@ private: planet::PlanetQuadtree _earth_quadtree{}; planet::PlanetQuadtree::Settings _earth_quadtree_settings{}; EarthDebugStats _earth_debug_stats{}; - std::unordered_map _earth_patch_cache; - std::list _earth_patch_lru; + std::unordered_map _earth_patch_lookup; + std::vector _earth_patches; + std::vector _earth_patch_free; + std::list _earth_patch_lru; + AllocatedBuffer _earth_patch_index_buffer{}; + uint32_t _earth_patch_index_count = 0; + uint32_t _earth_patch_index_resolution = 0; + uint32_t _earth_patch_frame_stamp = 0; uint32_t _earth_patch_resolution = 33; uint32_t _earth_patch_create_budget_per_frame = 16; float _earth_patch_create_budget_ms = 2.0f;