wip(assembler): round L22 -- one 8 m layout chunk for every strate; real ceiling -> content -> bedrock
StrateLayoutChunkSizeMeters (world setting, default 8 m; owner decision) is resolved once in the director/adapter and standalone loader; every generator reads the resolved width (no CHUNK_SIZE / Core-tile inference). Every strate gets solid ceiling and bedrock bands from its BoundarySealThickness in the shared composition path (diggable by default); faces come from the shared boundary path. Owner world: FloatingIslands +8..-120 m (128 m, now with a bedrock floor), SurfaceWorld -120..-632 m (unchanged; full-detail and exact-query hashes equal L21); the 60 m gap is gone. The strict layout validator passes on the real world, with 0 endpoint dispatch disagreements. Spawn/benchmark camera derived from route.front. Ledges under the islands gone (reviewer capture). Evaluator 7, recipe schema 2, layout snapshot 2, FI geometry 4. Open: SW cap profiles exceed the ceiling band (owner call); transitions still Hard; Unreal setting/manager/adapter edits uncompiled here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CSFYgsdW8ejZSyiMf4qRSX
This commit is contained in:
1 parent
8753f6dbe4
commit
55c26a05a7
28 files changed
+1432
-579
No files matched your search
@@ -17,6 +17,7 @@
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
@@ -66,6 +67,7 @@ namespace
|
||||
std::filesystem::path ResolvedLayoutOutputPath;
|
||||
bool ResolvedLayoutOnly=false;
|
||||
bool AuditResolvedLayoutBoundaries=false;
|
||||
bool AuditSurfaceClearance=false;
|
||||
};
|
||||
|
||||
bool ParseInteger(std::string_view text,int& output)
|
||||
@@ -123,6 +125,7 @@ namespace
|
||||
" --resolved-layout-output=FILE (canonical plan-time layout snapshot)\n"
|
||||
" --resolved-layout-only (exit after plan and snapshot validation)\n"
|
||||
" --audit-resolved-layout-boundaries (query Core at each recorded endpoint)\n"
|
||||
" --audit-surface-clearance (sample biome terrain/caps against the resolved ceiling)\n"
|
||||
" --auto-surface-camera (place the 27-tile cube on this seed's surface)\n"
|
||||
" --mesh-coverage (report non-empty tiles and indexed triangles)\n"
|
||||
" --screen-space-gate (measure display error and historical negative controls)\n"
|
||||
@@ -142,6 +145,7 @@ namespace
|
||||
if(arg=="--world-assembler-prototype")options.WorldAssemblerPrototype=true;
|
||||
else if(arg=="--resolved-layout-only")options.ResolvedLayoutOnly=true;
|
||||
else if(arg=="--audit-resolved-layout-boundaries")options.AuditResolvedLayoutBoundaries=true;
|
||||
else if(arg=="--audit-surface-clearance")options.AuditSurfaceClearance=true;
|
||||
else if(arg=="--stream-benchmark")options.StreamBenchmark=true;
|
||||
else if(arg=="--stream-pipeline")options.StreamPipeline=true;
|
||||
else if(arg=="--stream-hash")options.StreamHash=true;
|
||||
@@ -313,7 +317,11 @@ int main(int argc,char** argv)
|
||||
|
||||
FVector cameraVoxels=ownerWorld->ViewerStartVoxels;
|
||||
if(options.HasCamera)
|
||||
cameraVoxels=FVector(options.CameraMeters.X*4.0f,options.CameraMeters.Y*4.0f,options.CameraMeters.Z*4.0f);
|
||||
{
|
||||
const float VoxelsPerMetre=1000000.0f/std::max<std::uint32_t>(1,ownerWorld->CoreVoxelSizeMicrons);
|
||||
cameraVoxels=FVector(options.CameraMeters.X*VoxelsPerMetre,
|
||||
options.CameraMeters.Y*VoxelsPerMetre,options.CameraMeters.Z*VoxelsPerMetre);
|
||||
}
|
||||
|
||||
const auto planStart=Clock::now();
|
||||
VoxelForgeCore::WorldAssemblyManifest worldAssembly;
|
||||
@@ -324,22 +332,19 @@ int main(int argc,char** argv)
|
||||
&&!StandaloneWorldAssemblyPrototype::AttachFloatingIslandWorldAssembly(
|
||||
*ownerWorld,ownerJsonRoot,worldAssembly,error))
|
||||
{std::fprintf(stderr,"Floating-island assembly setup failed: %s\n",error.c_str());return 2;}
|
||||
VoxelForgeCore::ResolvedLayoutSnapshotV1 resolvedLayout;
|
||||
const bool buildResolvedLayout=!options.ResolvedLayoutOutputPath.empty()
|
||||
||options.AuditResolvedLayoutBoundaries||options.ResolvedLayoutOnly;
|
||||
if(buildResolvedLayout)
|
||||
VoxelForgeCore::ResolvedLayoutSnapshotV2 resolvedLayout;
|
||||
{
|
||||
const std::string_view variant=options.FloatingIslandsRecipe?"floating-islands":"surface-world";
|
||||
if(!StandaloneWorldAssemblyPrototype::BuildResolvedLayoutSnapshot(
|
||||
*ownerWorld,worldAssembly,variant,resolvedLayout,error))
|
||||
{std::fprintf(stderr,"Resolved-layout snapshot failed: %s\n",error.c_str());return 2;}
|
||||
const std::string layoutJson=resolvedLayout.ToJson();
|
||||
const auto roundTrip=VoxelForgeCore::ResolvedLayoutSnapshotV2::FromJson(layoutJson,error);
|
||||
if(!roundTrip||roundTrip->ToJson()!=layoutJson||roundTrip->stableIdentity!=resolvedLayout.stableIdentity)
|
||||
{if(error.empty())error="resolved-layout canonical serialization did not round-trip";
|
||||
std::fprintf(stderr,"Resolved-layout round trip failed: %s\n",error.c_str());return 2;}
|
||||
if(!options.ResolvedLayoutOutputPath.empty())
|
||||
{
|
||||
const auto roundTrip=VoxelForgeCore::ResolvedLayoutSnapshotV1::FromJson(layoutJson,error);
|
||||
if(!roundTrip||roundTrip->ToJson()!=layoutJson||roundTrip->stableIdentity!=resolvedLayout.stableIdentity)
|
||||
{if(error.empty())error="resolved-layout canonical serialization did not round-trip";
|
||||
std::fprintf(stderr,"Resolved-layout round trip failed: %s\n",error.c_str());return 2;}
|
||||
if(!options.ResolvedLayoutOutputPath.parent_path().empty())
|
||||
std::filesystem::create_directories(options.ResolvedLayoutOutputPath.parent_path());
|
||||
std::ofstream output(options.ResolvedLayoutOutputPath,std::ios::binary);
|
||||
@@ -347,12 +352,16 @@ int main(int argc,char** argv)
|
||||
output.write(layoutJson.data(),static_cast<std::streamsize>(layoutJson.size()));
|
||||
if(!output){std::fprintf(stderr,"Resolved-layout snapshot write failed: %s\n",options.ResolvedLayoutOutputPath.c_str());return 2;}
|
||||
}
|
||||
const auto controls=VoxelForgeCore::AuditResolvedLayoutValidatorV1();
|
||||
std::vector<VoxelForgeCore::ResolvedLayoutIssueV1> compatibilityIssues;
|
||||
const auto controls=VoxelForgeCore::AuditResolvedLayoutValidatorV2();
|
||||
std::vector<VoxelForgeCore::ResolvedLayoutIssueV2> compatibilityIssues;
|
||||
std::string compatibilityError;
|
||||
const bool compatibilityAccepted=resolvedLayout.validate(
|
||||
VoxelForgeCore::ResolvedLayoutValidationModeV1::compatibility,
|
||||
VoxelForgeCore::ResolvedLayoutValidationModeV2::compatibility,
|
||||
compatibilityIssues,compatibilityError);
|
||||
std::vector<VoxelForgeCore::ResolvedLayoutIssueV2> strictIssues;
|
||||
std::string strictError;
|
||||
const bool strictAccepted=resolvedLayout.validate(
|
||||
VoxelForgeCore::ResolvedLayoutValidationModeV2::strict,strictIssues,strictError);
|
||||
std::printf("[ResolvedLayoutSnapshot] schema=%u stable_identity=%s strata=%zu bytes=%zu round_trip=PASS\n",
|
||||
resolvedLayout.schemaVersion,resolvedLayout.stableIdentity.c_str(),resolvedLayout.strates.size(),layoutJson.size());
|
||||
std::printf("[ResolvedLayoutCompatibility] accepted=%s issues=%zu error=%s\n",
|
||||
@@ -362,12 +371,124 @@ int main(int argc,char** argv)
|
||||
issue.code.c_str(),issue.detail.c_str());
|
||||
if(!compatibilityAccepted)
|
||||
{std::fprintf(stderr,"Resolved-layout compatibility validation failed: %s\n",compatibilityError.c_str());return 2;}
|
||||
std::printf("[ResolvedLayoutStrict] accepted=%s issues=%zu error=%s\n",
|
||||
strictAccepted?"true":"false",strictIssues.size(),strictError.c_str());
|
||||
for(const auto& issue:strictIssues)
|
||||
std::printf("[ResolvedLayoutStrictIssue] code=%s detail=%s\n",issue.code.c_str(),issue.detail.c_str());
|
||||
if(!strictAccepted)
|
||||
{std::fprintf(stderr,"Resolved-layout strict validation failed: %s\n",strictError.c_str());return 2;}
|
||||
std::printf("[ResolvedLayoutValidatorControls] rejected=%u/%u status=%s\n",
|
||||
controls.controlsRejectedAsExpected,controls.controlsRun,controls.passed?"PASS":"FAIL");
|
||||
if(!controls.passed)
|
||||
{for(const auto& failure:controls.failures)std::printf("[ResolvedLayoutValidatorControlFailure] %s %s\n",failure.code.c_str(),failure.detail.c_str());return 2;}
|
||||
if(!options.ResolvedLayoutOutputPath.empty())
|
||||
std::printf("[ResolvedLayoutSnapshotOutput] path=%s\n",options.ResolvedLayoutOutputPath.c_str());
|
||||
if(auto* root=std::get_if<VFJson::Value::Object>(&ownerJsonRoot.Data))
|
||||
{VFJson::Value layoutText;layoutText.Data=layoutJson;(*root)["resolved_layout_json"]=std::move(layoutText);}
|
||||
else
|
||||
{std::fprintf(stderr,"Owner snapshot root cannot carry its resolved layout.\n");return 2;}
|
||||
if(options.AuditSurfaceClearance)
|
||||
{
|
||||
if(options.FloatingIslandsRecipe)
|
||||
{std::fprintf(stderr,"--audit-surface-clearance requires --recipe=surface-world\n");return 2;}
|
||||
const auto surface=std::find_if(resolvedLayout.strates.begin(),resolvedLayout.strates.end(),
|
||||
[](const auto& Stratum){return Stratum.generatorKind=="SurfaceWorld";});
|
||||
const auto plan=surface==resolvedLayout.strates.end()?worldAssembly.plan.strates.end()
|
||||
:std::find_if(worldAssembly.plan.strates.begin(),worldAssembly.plan.strates.end(),
|
||||
[&](const auto& Stratum){return Stratum.strateIndex==surface->strateIndex;});
|
||||
if(surface==resolvedLayout.strates.end()||plan==worldAssembly.plan.strates.end()
|
||||
||!surface->ceiling.presentInCurrentField)
|
||||
{std::fprintf(stderr,"SurfaceWorld clearance audit could not resolve its plan/ceiling\n");return 2;}
|
||||
const VoxelForgeCore::Heightfield2D Field(worldAssembly.recipe,*plan);
|
||||
if(!Field.isValid())
|
||||
{std::fprintf(stderr,"SurfaceWorld clearance audit field is invalid\n");return 2;}
|
||||
constexpr std::int64_t HalfExtentMetres=2048,StepMetres=1;
|
||||
const double CoreMicrometres=std::max<std::uint32_t>(1,ownerWorld->CoreVoxelSizeMicrons);
|
||||
const double MetresPerSubvoxel=CoreMicrometres
|
||||
/(1000000.0*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
double CenterX=0.0,CenterY=0.0;
|
||||
if(!plan->route.empty())
|
||||
{CenterX=plan->route.front().positionSubvoxels[0]*MetresPerSubvoxel;
|
||||
CenterY=plan->route.front().positionSubvoxels[1]*MetresPerSubvoxel;}
|
||||
const std::int64_t CenterXQ=static_cast<std::int64_t>(std::llround(
|
||||
CenterX/MetresPerSubvoxel));
|
||||
const std::int64_t CenterYQ=static_cast<std::int64_t>(std::llround(
|
||||
CenterY/MetresPerSubvoxel));
|
||||
const std::int64_t StepQ=static_cast<std::int64_t>(std::llround(
|
||||
StepMetres/MetresPerSubvoxel));
|
||||
struct FBiomeClearance
|
||||
{
|
||||
std::uint64_t Samples=0;
|
||||
double MaximumGround=-std::numeric_limits<double>::infinity();
|
||||
double MaximumCap=-std::numeric_limits<double>::infinity();
|
||||
double MaximumSurface=-std::numeric_limits<double>::infinity();
|
||||
double MinimumClearance=std::numeric_limits<double>::infinity();
|
||||
double AtX=0.0,AtY=0.0,AtHeight=0.0;
|
||||
std::int64_t MaximumCapQ=std::numeric_limits<std::int64_t>::min();
|
||||
};
|
||||
std::map<std::uint32_t,FBiomeClearance> ByBiome;
|
||||
const double CeilingFace=static_cast<double>(surface->ceiling.bottomMicrometres)/1000000.0;
|
||||
for(std::int64_t Y=-HalfExtentMetres;Y<=HalfExtentMetres;Y+=StepMetres)
|
||||
for(std::int64_t X=-HalfExtentMetres;X<=HalfExtentMetres;X+=StepMetres)
|
||||
{
|
||||
const auto Sample=Field.point(CenterXQ+(X/StepMetres)*StepQ,
|
||||
CenterYQ+(Y/StepMetres)*StepQ,0);
|
||||
const double Ground=Sample.groundHeightSubvoxels*MetresPerSubvoxel;
|
||||
const double Cap=Sample.capHeightSubvoxels*MetresPerSubvoxel;
|
||||
const double SurfaceHeight=std::max(Ground,Cap);
|
||||
auto& Stats=ByBiome[Sample.biomeId];
|
||||
++Stats.Samples;
|
||||
Stats.MaximumGround=std::max(Stats.MaximumGround,Ground);
|
||||
Stats.MaximumCap=std::max(Stats.MaximumCap,Cap);
|
||||
Stats.MaximumCapQ=std::max(Stats.MaximumCapQ,Sample.capHeightSubvoxels);
|
||||
Stats.MaximumSurface=std::max(Stats.MaximumSurface,SurfaceHeight);
|
||||
const double Clearance=CeilingFace-SurfaceHeight;
|
||||
if(Clearance<Stats.MinimumClearance)
|
||||
{Stats.MinimumClearance=Clearance;Stats.AtX=CenterX+X;Stats.AtY=CenterY+Y;
|
||||
Stats.AtHeight=SurfaceHeight;}
|
||||
}
|
||||
double MaximumRouteZ=-std::numeric_limits<double>::infinity();
|
||||
for(const auto& Point:plan->route)
|
||||
MaximumRouteZ=std::max(MaximumRouteZ,Point.positionSubvoxels[2]*MetresPerSubvoxel);
|
||||
const auto CapBounds=Field.capHeightBounds();
|
||||
const auto RouteSample=plan->route.empty()?VoxelForgeCore::HeightfieldPointSample{}
|
||||
:Field.point(plan->route.front().positionSubvoxels[0],
|
||||
plan->route.front().positionSubvoxels[1],0);
|
||||
std::printf("[SurfaceClearancePlan] has_recipe_override=%s layout_chunk_q=%lld plan_cap_q=%lld cap_bounds_q=(%lld,%lld)\n",
|
||||
plan->hasRecipeOverride?"true":"false",
|
||||
static_cast<long long>(worldAssembly.recipe.layoutChunkWidthSubvoxels),
|
||||
static_cast<long long>(plan->capHeightSubvoxels),
|
||||
static_cast<long long>(CapBounds[0]),static_cast<long long>(CapBounds[1]));
|
||||
for(const auto& Profile:worldAssembly.recipe.biomes)
|
||||
std::printf("[SurfaceClearanceProfile] biome_id=%u has_cap=%s cap_q=%lld undulation_q=%lld detail_q=%lld ridge_q=%lld\n",
|
||||
Profile.stableId,Profile.hasCapProfile?"true":"false",
|
||||
static_cast<long long>(Profile.capHeightSubvoxels),
|
||||
static_cast<long long>(Profile.capUndulationSubvoxels),
|
||||
static_cast<long long>(Profile.capDetailAmplitudeSubvoxels),
|
||||
static_cast<long long>(Profile.capRidgeAmplitudeSubvoxels));
|
||||
std::printf("[SurfaceClearanceAudit] box_m=4096x4096 step_m=%lld samples=%lld ceiling_inner_face_m=%.3f unmodified_field=true\n",
|
||||
static_cast<long long>(StepMetres),
|
||||
static_cast<long long>((2*HalfExtentMetres/StepMetres+1)*(2*HalfExtentMetres/StepMetres+1)),
|
||||
CeilingFace);
|
||||
std::printf("[SurfaceClearanceCapRange] plan_min_m=%.3f plan_max_m=%.3f spawn_cap_m=%.3f spawn_ground_m=%.3f\n",
|
||||
CapBounds[0]*MetresPerSubvoxel,CapBounds[1]*MetresPerSubvoxel,
|
||||
RouteSample.capHeightSubvoxels*MetresPerSubvoxel,
|
||||
RouteSample.groundHeightSubvoxels*MetresPerSubvoxel);
|
||||
for(const auto& Feature:plan->features)
|
||||
std::printf("[SurfaceClearanceFeature] type=%u params_q=(%lld,%lld,%lld,%lld)\n",
|
||||
Feature.type,static_cast<long long>(Feature.parametersSubvoxels[0]),
|
||||
static_cast<long long>(Feature.parametersSubvoxels[1]),
|
||||
static_cast<long long>(Feature.parametersSubvoxels[2]),
|
||||
static_cast<long long>(Feature.parametersSubvoxels[3]));
|
||||
for(const auto& [BiomeId,Stats]:ByBiome)
|
||||
std::printf("[SurfaceClearanceBiome] biome_id=%u samples=%llu max_ground_m=%.3f max_cap_m=%.3f max_cap_q=%lld max_surface_m=%.3f min_clearance_m=%.3f at_m=(%.1f,%.1f,%.3f)\n",
|
||||
BiomeId,static_cast<unsigned long long>(Stats.Samples),Stats.MaximumGround,
|
||||
Stats.MaximumCap,static_cast<long long>(Stats.MaximumCapQ),Stats.MaximumSurface,Stats.MinimumClearance,
|
||||
Stats.AtX,Stats.AtY,Stats.AtHeight);
|
||||
std::printf("[SurfaceClearanceRoute] route_points=%zu max_route_z_m=%.3f clearance_m=%.3f status=%s\n",
|
||||
plan->route.size(),MaximumRouteZ,CeilingFace-MaximumRouteZ,
|
||||
MaximumRouteZ<CeilingFace?"PASS":"COLLISION");
|
||||
}
|
||||
if(options.ResolvedLayoutOnly)return 0;
|
||||
}
|
||||
if(options.AutoSurfaceCamera)
|
||||
@@ -391,25 +512,17 @@ int main(int argc,char** argv)
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto xSubvoxels=static_cast<std::int64_t>(std::llround(cameraVoxels.X*UnitsPerVoxel));
|
||||
const auto ySubvoxels=static_cast<std::int64_t>(std::llround(cameraVoxels.Y*UnitsPerVoxel));
|
||||
const auto currentZSubvoxels=static_cast<std::int64_t>(std::llround(cameraVoxels.Z*UnitsPerVoxel));
|
||||
std::int64_t selectedGround=0,bestDistance=std::numeric_limits<std::int64_t>::max();
|
||||
for(const auto& stratum:worldAssembly.plan.strates)
|
||||
{
|
||||
VoxelForgeCore::Heightfield2D field(worldAssembly.recipe,stratum);
|
||||
if(!field.isValid())continue;
|
||||
const auto column=field.point(xSubvoxels,ySubvoxels,0);
|
||||
const std::int64_t distance=std::abs(column.groundHeightSubvoxels-currentZSubvoxels);
|
||||
if(distance<bestDistance)
|
||||
{bestDistance=distance;selectedGround=column.groundHeightSubvoxels;}
|
||||
}
|
||||
if(bestDistance==std::numeric_limits<std::int64_t>::max())
|
||||
{std::fprintf(stderr,"[SeedSurfacePlacement] FAIL: no valid SurfaceWorld heightfield\n");return 2;}
|
||||
cameraVoxels.Z=static_cast<float>(static_cast<double>(selectedGround)/UnitsPerVoxel);
|
||||
std::printf("[SeedSurfacePlacement] recipe=surface-world seed=%d ground_voxel=%.6f camera_voxel=(%.6f,%.6f,%.6f) status=PASS\n",
|
||||
seed,static_cast<double>(selectedGround)/UnitsPerVoxel,
|
||||
cameraVoxels.X,cameraVoxels.Y,cameraVoxels.Z);
|
||||
if(worldAssembly.plan.strates.empty()||worldAssembly.plan.strates.front().route.empty())
|
||||
{std::fprintf(stderr,"[SeedSurfacePlacement] FAIL: route-derived spawn is missing\n");return 2;}
|
||||
const auto& spawn=worldAssembly.plan.strates.front().route.front();
|
||||
cameraVoxels={static_cast<float>(static_cast<double>(spawn.positionSubvoxels[0])/UnitsPerVoxel),
|
||||
static_cast<float>(static_cast<double>(spawn.positionSubvoxels[1])/UnitsPerVoxel),
|
||||
static_cast<float>(static_cast<double>(spawn.positionSubvoxels[2])/UnitsPerVoxel)};
|
||||
std::printf("[SeedSurfacePlacement] recipe=surface-world seed=%d source=route.front position_voxel=(%.6f,%.6f,%.6f) position_m=(%.3f,%.3f,%.3f) status=PASS\n",
|
||||
seed,cameraVoxels.X,cameraVoxels.Y,cameraVoxels.Z,
|
||||
cameraVoxels.X*ownerWorld->CoreVoxelSizeMicrons/1000000.0f,
|
||||
cameraVoxels.Y*ownerWorld->CoreVoxelSizeMicrons/1000000.0f,
|
||||
cameraVoxels.Z*ownerWorld->CoreVoxelSizeMicrons/1000000.0f);
|
||||
}
|
||||
}
|
||||
std::printf("[WorldAssemblerPlanTiming] total_ms=%.3f status=PASS\n",
|
||||
@@ -422,8 +535,9 @@ int main(int argc,char** argv)
|
||||
|
||||
FStandaloneCoreScheduler scheduler;
|
||||
const FVector cameraStepVoxels=options.HasCameraStep
|
||||
?FVector(options.CameraStepMeters.X*4.0f,options.CameraStepMeters.Y*4.0f,
|
||||
options.CameraStepMeters.Z*4.0f):FVector::ZeroVector;
|
||||
?FVector(options.CameraStepMeters.X*1000000.0f/std::max<std::uint32_t>(1,ownerWorld->CoreVoxelSizeMicrons),
|
||||
options.CameraStepMeters.Y*1000000.0f/std::max<std::uint32_t>(1,ownerWorld->CoreVoxelSizeMicrons),
|
||||
options.CameraStepMeters.Z*1000000.0f/std::max<std::uint32_t>(1,ownerWorld->CoreVoxelSizeMicrons)):FVector::ZeroVector;
|
||||
return StandaloneStreamBenchmark::RunPipelinedStreamingBenchmark(ownerWorld,snapshot,
|
||||
cameraVoxels,options.WorkerCount,options.MaxLevel,VoxelForgeCore::DetailVariant::coherent,
|
||||
scheduler,options.StreamHash,false,false,false,false,coldStart,
|
||||
@@ -434,5 +548,5 @@ int main(int argc,char** argv)
|
||||
options.RegionCrossing,options.AuditTileShapeReadiness,
|
||||
options.DiagnosticSlowShapeCompileMs,options.AuditBasePageEquivalence,
|
||||
options.AuditRouteParity,options.AuditResolvedLayoutBoundaries,
|
||||
buildResolvedLayout?&resolvedLayout:nullptr);
|
||||
&resolvedLayout);
|
||||
}
|
||||
Reference in new issue
Block a user