RecordingDevice: lend cached geometry instead of copying it per draw

A geometry-cache hit moved every vertex vector out of the cache into the draw
(deep copy of positions/normals/uv/diffuse/indices/bones each frame). The cache
now lends its vectors to the frame's first draw of that geometry and takes them
back in Render3DBeginFrame; later draws of the same key+revision in the frame
(terrain splat layers, shadow-map pass) are marked shared_geometry and carry no
vectors -- the native host looks the GPU geometry up by key+revision, and draw
captures expand them (Render3DExpandSharedGeometry). Index reads reuse a scratch
buffer. Desktop live game: RenderGame rnd 1.7 -> 1.2 ms, no visual change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-09-30 11:39:01 +09:00
co-authored by Claude Opus 5.5
parent 44a020f01e
commit 6bfcac7d68
5 changed files with 128 additions and 29 deletions
+3 -1
View File
@@ -362,7 +362,9 @@ int run_live_client(
renderer.touch_controller.update_screen_size(int(ui_w), int(ui_h));
renderer.touch_controller.append_ui_commands(cap_ui);
}
native_draw_capture::write(capture_out, Render3DDraws(), cap_textures, ui_w, ui_h, cap_ui);
std::vector<Render3DDraw> cap_draws = Render3DDraws();
Render3DExpandSharedGeometry(cap_draws);
native_draw_capture::write(capture_out, cap_draws, cap_textures, ui_w, ui_h, cap_ui);
}
if (!screenshot_out.empty() && frames > 0) {
+1 -1
View File
@@ -66,7 +66,7 @@ FixedFunctionState make_fixed_function_state(const Render3DDraw& draw) {
const std::uint32_t fog_mode = draw.fog_enable
? (draw.fog_table_mode ? draw.fog_table_mode : (draw.pretransformed ? 4u : draw.fog_vertex_mode)) : 0;
state.flags = {1u, !draw.texture0.empty() ? 1u : 0u, !draw.texture1.empty() ? 1u : 0u, fog_mode};
state.lighting_flags = {draw.lighting, draw.color_vertex, draw.diffuse.empty() ? 0u : 1u,
state.lighting_flags = {draw.lighting, draw.color_vertex, (draw.shared_geometry ? draw.shared_diffuse : !draw.diffuse.empty()) ? 1u : 0u,
draw.normalize_normals};
state.material_sources = {draw.diffuse_material_source, draw.ambient_material_source,
draw.emissive_material_source, draw.local_viewer};
+14 -5
View File
@@ -8,6 +8,7 @@
#include <cstring>
#include <fstream>
#include <stdexcept>
#include <tuple>
#include <unordered_set>
namespace mt_host {
@@ -191,14 +192,22 @@ void VulkanWindow::prepare_draws(const std::vector<Render3DDraw>& draws, const T
back_buffer_list(in_scene).push_back(clear);
continue;
}
if (draw.positions.empty() || draw.indices.empty()) continue;
const auto signature = draw.geometry_key ? draw.geometry_revision : geometry_hash(draw);
const GeometryId id{draw.geometry_key, signature};
auto [it, inserted] = geometries_.try_emplace(id);
decltype(geometries_)::iterator it;
bool inserted = false;
if (draw.shared_geometry) {
// Repeats an earlier draw of this frame, which uploaded (or kept) the geometry.
it = geometries_.find(GeometryId{draw.geometry_key, draw.geometry_revision});
if (it == geometries_.end()) continue;
} else {
if (draw.positions.empty() || draw.indices.empty()) continue;
const auto signature = draw.geometry_key ? draw.geometry_revision : geometry_hash(draw);
std::tie(it, inserted) = geometries_.try_emplace(GeometryId{draw.geometry_key, signature});
}
auto& geometry = it->second;
if (inserted) {
upload_geometry(draw, geometry);
} else if (geometry.vertex_count != draw.positions.size() / 3 || geometry.index_count != draw.indices.size()) {
} else if (!draw.shared_geometry && (geometry.vertex_count != draw.positions.size() / 3 ||
geometry.index_count != draw.indices.size())) {
throw std::runtime_error("geometry key/revision reused with a different size");
}
geometry.last_used_frame = frame_number_;