Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 58 additions & 39 deletions indra/newview/llworldmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -608,98 +608,117 @@ void LLWorldMap::updateRegions(S32 x0, S32 y0, S32 x1, S32 y1)
constexpr S32 MAX_TOTAL_BLOCKS = MAX_REQUEST_REGIONS / REGIONS_PER_BLOCK; // 64 / 16 = 4 blocks total
constexpr S32 MAX_BLOCKS_PER_SIDE = MAX_TOTAL_BLOCKS; // Can have up to 4 blocks in one dimension (e.g., 4x1, 1x4, 2x2)

// Convert region coordinates to block coordinates
// We use fixed sized blocks for ease of storage and lookup,
// but the requests can be of variable size of up to
// MAX_REQUEST_REGIONS.
S32 block_x0 = x0 / MAP_BLOCK_SIZE;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't remove comments. This logic is complicated to parse without having at least some idea of what to expect.

S32 block_x1 = x1 / MAP_BLOCK_SIZE;
S32 block_y0 = y0 / MAP_BLOCK_SIZE;
S32 block_y1 = y1 / MAP_BLOCK_SIZE;

// Clamp to valid range
block_x0 = llmax(block_x0, 0);
block_x1 = llmin(block_x1, MAP_BLOCK_RES - 1);
block_y0 = llmax(block_y0, 0);
block_y1 = llmin(block_y1, MAP_BLOCK_RES - 1);

// Process blocks, grouping unloaded blocks into larger requests up to MAX_TOTAL_BLOCKS
for (S32 block_y = block_y0; block_y <= block_y1; )
S32 block_x0 = llclamp(x0 / MAP_BLOCK_SIZE, 0, MAP_BLOCK_RES - 1);
S32 block_x1 = llclamp(x1 / MAP_BLOCK_SIZE, 0, MAP_BLOCK_RES - 1);
S32 block_y0 = llclamp(y0 / MAP_BLOCK_SIZE, 0, MAP_BLOCK_RES - 1);
S32 block_y1 = llclamp(y1 / MAP_BLOCK_SIZE, 0, MAP_BLOCK_RES - 1);

const S32 tracked_region_x = llclamp((S32)(mTrackingLocation.mdV[VX] / REGION_WIDTH_UNITS), 0, MAP_MAX_SIZE - 1);
const S32 tracked_region_y = llclamp((S32)(mTrackingLocation.mdV[VY] / REGION_WIDTH_UNITS), 0, MAP_MAX_SIZE - 1);
const S32 tracked_block_x = tracked_region_x / MAP_BLOCK_SIZE;
const S32 tracked_block_y = tracked_region_y / MAP_BLOCK_SIZE;
const bool tracked_block_visible = isTracking() &&
tracked_block_x >= block_x0 && tracked_block_x <= block_x1 &&
tracked_block_y >= block_y0 && tracked_block_y <= block_y1;

if (tracked_block_visible)
{
const S32 tracked_offset = tracked_block_x | (tracked_block_y * MAP_BLOCK_RES);
if (!mMapBlockLoaded[tracked_offset])
{
const S32 min_x = tracked_block_x * MAP_BLOCK_SIZE;
const S32 min_y = tracked_block_y * MAP_BLOCK_SIZE;
const S32 max_x = min_x + MAP_BLOCK_SIZE - 1;
const S32 max_y = min_y + MAP_BLOCK_SIZE - 1;

LL_DEBUGS("WorldMap") << "Loading tracked block first (" << tracked_block_x << "," << tracked_block_y
<< ") [" << REGIONS_PER_BLOCK << " regions]" << LL_ENDL;

LLWorldMapMessage::getInstance()->sendMapBlockRequest(min_x, min_y, max_x, max_y);
mMapBlockLoaded[tracked_offset] = true;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Does this cover all 3 types of tracking, or just search search? There is no reason to prioritize just the search. Ex: If user triggers a trackLandmark, user wants that loaded first.
  2. LLFloaterWorldMap::trackLocation already requests the tile, I'm not sure if there is a reason to request it again here. But it does not set mMapBlockLoaded for some reason, might be a bug there. Nor are other types of tracking doing the same

After checking the code I'm not sure if this ticket actually needs fixing or if #5558 fixed it and it just needs to go through QA first to make sure issue is gone.

}
}

for (S32 block_y = block_y0; block_y <= block_y1; ++block_y)
{
for (S32 block_x = block_x0; block_x <= block_x1; )
{
S32 offset = block_x | (block_y * MAP_BLOCK_RES);
if (tracked_block_visible && block_x == tracked_block_x && block_y == tracked_block_y)
{
++block_x;
continue;
}

const S32 offset = block_x | (block_y * MAP_BLOCK_RES);
if (!mMapBlockLoaded[offset])
{
// Find the maximum contiguous unloaded rectangle starting at this block
S32 request_width = 1;
S32 request_height = 1;

// Expand width (check horizontal contiguous unloaded blocks)
while (request_width < MAX_BLOCKS_PER_SIDE &&
(block_x + request_width) <= block_x1)
{
// request_height is 1, can add blocks one by one.
S32 check_offset = (block_x + request_width) | (block_y * MAP_BLOCK_RES);
if (mMapBlockLoaded[check_offset])
const S32 check_block_x = block_x + request_width;
const S32 check_offset = check_block_x | (block_y * MAP_BLOCK_RES);
if (mMapBlockLoaded[check_offset] ||
(tracked_block_visible && check_block_x == tracked_block_x && block_y == tracked_block_y))
Copy link
Copy Markdown
Contributor

@akleshchev akleshchev May 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the point of checking this? Wouldn't mMapBlockLoaded[offset] already be true?

{
break;
}
++request_width;
}

// Expand height (check vertical contiguous unloaded blocks)
while (request_height < MAX_BLOCKS_PER_SIDE &&
(block_y + request_height) <= block_y1 &&
(request_width * (request_height + 1) <= MAX_TOTAL_BLOCKS)) // Don't exceed 64 total regions
(request_width * (request_height + 1) <= MAX_TOTAL_BLOCKS))
{
bool can_expand = true;
// Width can be >1, loop over blocks in the line
for (S32 x = 0; x < request_width; ++x)
{
S32 check_offset = (block_x + x) | ((block_y + request_height) * MAP_BLOCK_RES);
if (mMapBlockLoaded[check_offset])
const S32 check_block_x = block_x + x;
const S32 check_block_y = block_y + request_height;
const S32 check_offset = check_block_x | (check_block_y * MAP_BLOCK_RES);
if (mMapBlockLoaded[check_offset] ||
(tracked_block_visible && check_block_x == tracked_block_x && check_block_y == tracked_block_y))
{
can_expand = false;
break;
}
}
if (!can_expand) break;
if (!can_expand)
{
break;
}
++request_height;
}

// Send request for the contiguous rectangle
S32 min_x = block_x * MAP_BLOCK_SIZE;
S32 min_y = block_y * MAP_BLOCK_SIZE;
S32 max_x = (block_x + request_width) * MAP_BLOCK_SIZE - 1;
S32 max_y = (block_y + request_height) * MAP_BLOCK_SIZE - 1;
const S32 min_x = block_x * MAP_BLOCK_SIZE;
const S32 min_y = block_y * MAP_BLOCK_SIZE;
const S32 max_x = (block_x + request_width) * MAP_BLOCK_SIZE - 1;
const S32 max_y = (block_y + request_height) * MAP_BLOCK_SIZE - 1;

LL_DEBUGS("WorldMap") << "Loading Block rectangle (" << block_x << "," << block_y
<< ") to (" << (block_x + request_width - 1) << "," << (block_y + request_height - 1)
<< ") [" << (request_width * request_height * MAP_BLOCK_SIZE * MAP_BLOCK_SIZE) << " regions]" << LL_ENDL;

LLWorldMapMessage::getInstance()->sendMapBlockRequest(min_x, min_y, max_x, max_y);

// Mark all blocks in the requested rectangle as loaded
for (S32 y = 0; y < request_height; ++y)
{
for (S32 x = 0; x < request_width; ++x)
{
S32 mark_offset = (block_x + x) | ((block_y + y) * MAP_BLOCK_RES);
const S32 mark_offset = (block_x + x) | ((block_y + y) * MAP_BLOCK_RES);
mMapBlockLoaded[mark_offset] = true;
}
}

// Skip over the width of blocks we just requested
block_x += request_width;
}
else
{
// This block is already loaded, move to next
++block_x;
}
}
++block_y;
}
}

Expand Down
20 changes: 20 additions & 0 deletions indra/newview/tests/llworldmap_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ namespace tut
worldmap_test()
{
mWorld = LLWorldMap::getInstance();
gMapBlockRequests.clear();
mWorld->cancelTracking();
}
~worldmap_test()
{
Expand Down Expand Up @@ -588,5 +590,23 @@ namespace tut
ensure("updateRegions 4x4 blocks: request[2] max_y", gMapBlockRequests[2].max_y == 11);
ensure("updateRegions 4x4 blocks: request[3] min_y", gMapBlockRequests[3].min_y == 12);
ensure("updateRegions 4x4 blocks: request[3] max_y", gMapBlockRequests[3].max_y == 15);

// Test 30 : tracked block should be requested first so the searched region can render before surrounding tiles.
mWorld->reset();
gMapBlockRequests.clear();
LLVector3d tracked_pos(9.0 * REGION_WIDTH_METERS, 9.0 * REGION_WIDTH_METERS, 0.0);
mWorld->setTracking(tracked_pos);
mWorld->updateRegions(0, 0, 15, 15);
ensure("updateRegions tracked block: expected 5 requests", gMapBlockRequests.size() == 5);
ensure("updateRegions tracked block: first request min_x", gMapBlockRequests[0].min_x == 8);
Comment on lines +597 to +601
ensure("updateRegions tracked block: first request min_y", gMapBlockRequests[0].min_y == 8);
ensure("updateRegions tracked block: first request max_x", gMapBlockRequests[0].max_x == 11);
ensure("updateRegions tracked block: first request max_y", gMapBlockRequests[0].max_y == 11);
for (size_t i = 0; i < gMapBlockRequests.size(); ++i)
{
const MapBlockRequest& req = gMapBlockRequests[i];
S32 regions = (req.max_x - req.min_x + 1) * (req.max_y - req.min_y + 1);
ensure("updateRegions tracked block: each request must not exceed 64 regions", regions <= 64);
}
}
}
Loading