Skip to content
Merged
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
8 changes: 5 additions & 3 deletions doc/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,12 @@ Every parser takes its input as hostile: an LLSD capability response picks its o
`LLViewerObject` (`indra/newview/llviewerobject.h`) is the base for all world objects, deriving from `LLPrimitive`. Key subclasses:
- **`LLVOVolume`** (`llvovolume.h`) — Most common type: prims, meshes, sculpts. Contains volume geometry with multiple faces, materials, texture animation, media-on-surface, flex. A sculpt samples its map at its detail's mesh; where the samples of a map sampled more than two pixels apart have no area (an oblong map with a collapsed line every few, at a coarser detail), `LLVolume::sculpt` samples the map averaged down by two along that axis, until they have or none is left, before it builds the sphere
- **`LLVOAvatar`** (`llvoavatar.h`) — Avatar with skeletal system, mesh deformation, animation blending, appearance layering, impostor rendering. `LLVOAvatarSelf` is the player's own avatar
- **`LLVOGrass`**, **`LLVOTree`**, **`LLVOSurfacePatch`** (terrain), **`LLVOPartGroup`** (particles), **`LLVOWLSky`** (sky dome)
- **`LLVOGrass`**, **`LLVOTree`**, **`LLVOSurfacePatch`** (terrain), **`LLVOPartGroup`** (particles)

Water is not an object. A region's water height is `LLSurface`'s; `ALWaterSurface` (`alwatersurface.h`, owned by `LLPipeline`) lays the water out around the main camera at the start of each frame's display, before the mirrors, the shadow fit and the probes read it, from `ALWaterField` (`alwaterfield.h`, pure arithmetic): a square of region-sized cells over the loaded regions, each at its region's height and the empty ones at the agent region's, and rings of quads doubling in size out to the rim. A 32x32 cell map holds the heights, and the water is drawn instanced with no vertex buffer, placed by `environment/waterSurface.glsl` in parts (`ALWaterField::Draw`): the cells, the rings at three triangles a quad, ring 0's four sides fanned onto the square's cell corners, and, with waves, the cells beside the patches fanned onto the patches' corners. Every vertex two parts share is worked from the same exact multiples and lies on no edge it is not a corner of, so the field has no T-junction for a crack to open along. With `AlchemyRenderWaterWaves` on a GL 4.0 context, the main view draws the cells within the tessellation reach instead as 32 m tessellation patches (probe captures and mirrors, whose hero probe is a cube of faces, draw the field flat) (`gWaterWavesProgram`: `waterPatchV`, `waterTC`, `waterTE`; built, with the depth pyramid's program, only while the waves are wanted, and their failing leaves the water flat), their edges past the reach left whole so they meet the cells beside them corner to corner, displaced by heights integrated from the normal maps that shade the surface: `ALWaterHeightBake` (pocketfft, Frankot-Chellappa) bakes each map on the General queue and `ALWaterSurface` keeps the heights as RG16F textures. The waves' bound, A, widens the shadow fit and the field's slab; the plane stays the plane for every height consumer. The patches are drawn before the flat field and front to back along the view (`waterPatchOrder`), so the depth test turns away water a nearer crest hides before it is shaded, and the control stage drops a patch whose box is out of the frustum or, in the main view where the session computes (`AlchemyRenderWaterOcclusionCull`), wholly behind the scene's depth, through `ALDepthPyramid` built from the depth the water pass copied.

The sky is not an object either. `ALSky` (`alsky.h`, owned by `LLPipeline`) takes the frame's sky at the start of each frame's display, before the mirrors draw it: the sun and moon and their quads, and the sky's textures at the priority they are drawn at. It takes them again once the frame's environment update has moved the sky on, before the main view (`followEnvironment`), and with them, where reflection probes are off, the environment cube the shiny and probe-less reflections read, baked from `windlightDome` when the sky changes (`ALSkyCube`) and neutral grey until its first bake. It holds the star field, laid out on the General queue and drawn once it lands, its buffer and the meteors. Each view takes what it draws of the sky once, at its G-buffer's clear (`ALSky::beginView`, from `LLPipeline::beginSkyView`, which clears the stars' attachment only where the view draws them); ALSky then draws the stars after `renderGeomDeferred`'s pool loop, binds the sky's pass (`gDeferredSkyCompositeProgram`) and the dome for the haze on water; nothing of it is culled or drawn through a pool.

`LLViewerObjectList` (`llviewerobjectlist.h`) is the global registry tracking all objects by UUID.

### Rendering Pipeline
Expand Down Expand Up @@ -157,7 +159,7 @@ A free target goes only to an acquisition of exactly its description and shared
Post-processing settings are exposed in the Lightbox floater (`ALFloaterLightBox`); see `doc/LIGHTBOX.md` for how to add UI sections for new effects.

**Snapshot cutouts.** The snapshot floater's Cutout mode (and File > Take Snapshot to Disk while it is on) draws one avatar with what it wears, or one linkset, and fills the rest with a colour whose alpha may be 0. The request is `ALSnapshotCutout`, built only by `ALSnapshotCutout::fromSettings()`; the subject is armed in `ALRenderIsolation`, whose `Scope` covers the frames of one `rawSnapshot`:
- **Cull:** `LLPipeline::cullingToCutoutSubject()` is true for the main camera's pass only, so shadows, probes and impostor bakes keep the whole world and the subject stays lit where it stands. The choke points are the two sky pushes in `updateCull`, the render-by-group branch of `markNotCulled`, `markVisible` and a `stateSort(LLDrawable*)` backstop; what they turn away is still marked visible, for object-cache eviction, LOD and texture priority. A static linkset is made active on arming, since only a bridge of its own separates its geometry from its neighbours'. The water is drawn from its field rather than culled, so the water pool runs no pass there (`LLDrawPoolWater::getNumPostDeferredPasses`).
- **Cull:** `LLPipeline::cullingToCutoutSubject()` is true for the main camera's pass only, so shadows, probes and impostor bakes keep the whole world and the subject stays lit where it stands. The choke points are the render-by-group branch of `markNotCulled`, `markVisible` and a `stateSort(LLDrawable*)` backstop; what they turn away is still marked visible, for object-cache eviction, LOD and texture priority. A static linkset is made active on arming, since only a bridge of its own separates its geometry from its neighbours'. The water is drawn from its field rather than culled, so the water pool runs no pass there (`LLDrawPoolWater::getNumPostDeferredPasses`).
- **Coverage:** kept in `mRT->screen`'s alpha as an impostor bake keeps it — `seedCutoutCoverageFromDepth` once the opaque forward pools (fullbright, shiny) have drawn and before the first blended one, then the alpha pool's blend (`alphaWritesCoverage()`), which the haze pass leaves alone — and written into the finished frame's alpha by `copyCutoutCoverageToAlpha`, since nothing in the post chain carries usable alpha. `colorCorrect` divides coverage out before grading and multiplies it back after (`uCutoutPremultiplied`).
- **Post:** a cutout frame drops the lens and print effects (`suppressesLensAndPrintEffects()`), measures nothing (no metering, SSR copy, scopes or pixel probe) and runs nothing that spreads past a pixel (DoF, bloom, glow, AA, CAS); `render_ui` draws nothing over it. Edges come from rendering offscreen at a multiple of the size (the screen targets' size, with the resolution multiplier, held under the floater's largest snapshot) and averaging down while premultiplied in `ALCutoutImage::resolve`; `ALCutoutImage::layOverFill` then composites over the fill and keeps alpha only where the format can (`LLSnapshotModel::keepsAlpha`). The preview keeps the resolved cutout, so a new fill or format is laid down again without a render (`ALSnapshotCutout::drawsSameAs`).

Expand Down Expand Up @@ -191,7 +193,7 @@ Key shader groups: GBuffer write (`gDeferredDiffuseProgram`, `gDeferredPBROpaque

### Draw Pool & Spatial System

**Draw pools** (`indra/newview/lldrawpool.h` and `lldrawpool*.h`) group geometry by material type for batched rendering. Each pool corresponds to a render pass. Key pools: `POOL_SIMPLE`, `POOL_BUMP`, `POOL_MATERIALS` (legacy), `POOL_GLTF_PBR` (PBR opaque), `POOL_GLTF_PBR_ALPHA_MASK`, `POOL_TERRAIN`, `POOL_AVATAR`, `POOL_CONTROL_AV` (animesh), `POOL_ALPHA_PRE_WATER`/`POOL_ALPHA_POST_WATER`, `POOL_WATER` (no faces: it draws the water field), `POOL_WL_SKY`, `POOL_GLOW`. Many pools have `_RIGGED` pass variants for skinned geometry.
**Draw pools** (`indra/newview/lldrawpool.h` and `lldrawpool*.h`) group geometry by material type for batched rendering. Each pool corresponds to a render pass. Key pools: `POOL_SIMPLE`, `POOL_BUMP`, `POOL_MATERIALS` (legacy), `POOL_GLTF_PBR` (PBR opaque), `POOL_GLTF_PBR_ALPHA_MASK`, `POOL_TERRAIN`, `POOL_AVATAR`, `POOL_CONTROL_AV` (animesh), `POOL_ALPHA_PRE_WATER`/`POOL_ALPHA_POST_WATER`, `POOL_WATER` (no faces: it draws the water field), `POOL_GLOW`. Many pools have `_RIGGED` pass variants for skinned geometry.

**Spatial partitioning** uses an octree. `LLSpatialPartition` (`indra/newview/llspatialpartition.h`) is the base, with specialized partitions: `LLVolumePartition`, `LLTerrainPartition`, `LLTreePartition`, `LLGrassPartition`, `LLParticlePartition`. `LLSpatialGroup` is an octree node grouping drawable geometry. `LLDrawInfo` encapsulates a single draw call (vertex buffer, index range, textures, material, blend mode, skin info). `LLCullResult` stores per-frame visible geometry organized by render type. The cull (`LLViewerOctreeCull`) visits each node's children nearest the camera first (`LLOctreeTraveler::traverseNearFirst`, by the node's child map), and the render map keeps the order groups were found in, so opaque geometry reaches the GPU roughly front to back; culls whose results keep no order (the object cache's, selection) and the sun's shadow cameras, which stand at the origin, keep the children's own order. `LLPipeline::updateCull` tests each region's partitions' roots together first, against the camera's frustum without its far plane, which is no stricter than any partition's own cull of its root, and culls none of a region whose content lies outside it; the object cache's cull, which picks cached objects behind the camera too, runs either way. No node that can still split holds more than `OctreeMaxNodeCapacity` elements: an element too big for a node stays below its own size's node only while that node is full, and passes on to a child of its own once the node it stays in is full too, so that a group does not gather more faces than a rebuild draws (`MAX_FACE_COUNT`, warned of once where it does). Only a node within twice `OctreeMinimumNodeSize`, whose children would sit at its centre, takes whatever comes.

Expand Down
23 changes: 14 additions & 9 deletions indra/alrender/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
# change, the far plane, the water's field, wave heights and stencil bits,
# the depth pyramid, the light map's blur, the depth under the HUD,
# which placed reflection probes swallow automatic ones, snapshot cutouts,
# whether a spot light's shadow map is drawn again, the sky dome's mesh and
# what the sky's other layers are worked out from along the view ray; and
# the pool that lends the frame's short-lived render targets by description.
# Whatever reads settings or the scene hands these their inputs as values.
# whether a spot light's shadow map is drawn again, what the sky's layers
# are worked out from along the view ray and the environment cube baked
# from the dome; and the pool that lends the frame's
# short-lived render targets by description. Whatever reads settings or the
# scene hands these their inputs as values.

set(
alrender_SOURCES
Expand All @@ -21,7 +22,7 @@ set(
alrenderinputrequests.cpp
alrenderresourceplan.cpp
alshaderinputs.cpp
alskydome.cpp
alskycube.cpp
alskylayers.cpp
alspotshadowcache.cpp
alstarfield.cpp
Expand All @@ -46,7 +47,7 @@ set(
alrenderresourceinputs.h
alrenderresourceplan.h
alshaderinputs.h
alskydome.h
alskycube.h
alskylayers.h
alspotshadowcache.h
alstarfield.h
Expand Down Expand Up @@ -85,8 +86,8 @@ if(AL_BUILD_TESTS)
# that ask for it again, the transient target pool's policy, what the programs are built from, the far plane
# and the constants its shaders share, the water's field and the wave
# heights a normal map's slopes integrate to, snapshot cutouts, the spot
# shadows' cache, how a display pass ends its frame, the star field and the
# sky's layers.
# shadows' cache, how a display pass ends its frame, the star field, the
# sky's layers and the environment cube's directions.
al_add_test_binary(
alrender_tests
PROJECT alrender
Expand All @@ -98,6 +99,7 @@ if(AL_BUILD_TESTS)
tests/alrenderinputrequests_test.cpp
tests/alrenderresourceplan_test.cpp
tests/alshaderinputs_test.cpp
tests/alskycube_test.cpp
tests/alskylayers_test.cpp
tests/alspotshadowcache_test.cpp
tests/alstarfield_test.cpp
Expand All @@ -117,7 +119,8 @@ if(AL_BUILD_TESTS)
# the light map's blur by one dispatch against its two fragment passes,
# WindLight's dome along the view ray against the dome's mesh, the sky's
# other layers along it against the programs that drew them and the whole
# sky's composite against the sky they drew, the stars
# sky's composite against the sky they drew, the environment cube against
# the dome it is baked from and the cube the CPU built, the stars
# against the picture they left before their rework, FXAA on an RGB10_A2
# source, the reflection probes' taps in one pass over them against their
# list, the transient target pool's GL objects, shared depth and poison,
Expand All @@ -131,6 +134,7 @@ if(AL_BUILD_TESTS)
tests/allightmapblur_gl_test.cpp
tests/alondemandtarget_gl_test.cpp
tests/alskycomposite_gl_test.cpp
tests/alskycube_gl_test.cpp
tests/alskydome_gl_test.cpp
tests/alskylayers_gl_test.cpp
tests/alspotshadowcache_gl_test.cpp
Expand All @@ -142,6 +146,7 @@ if(AL_BUILD_TESTS)
BENCH_SOURCES
tests/alcompute_gl_bench.cpp
tests/alondemandtarget_bench.cpp
tests/alstarfield_gl_bench.cpp
tests/alwatertargets_bench.cpp
tests/reflectionprobe_gl_bench.cpp
LIBRARIES alrender
Expand Down
12 changes: 10 additions & 2 deletions indra/alrender/alrenderresourceplan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,10 @@ namespace
"shadow.spot0", "shadow.spot1", "smaa.edges", "smaa.blend", "ssr.scene", "main.light.blurred",
"glow0", "glow1", "glow2", "downres", "preview", "bake", "brdf.lut", "exposure.meter", "exposure.histogram",
"exposure", "exposure.last", "lens.flare.a", "lens.flare.b", "noise", "noise.true", "smaa.area", "smaa.search",
"smaa.sample", "tone.curve",
"smaa.sample", "tone.curve", "sky.cube",
};

static_assert(OWN_NAMES[(size_t)Id::ToneCurve - PACK_IDS] == "tone.curve", "a name for every id after the packs");
static_assert(OWN_NAMES[(size_t)Id::EnvironmentCube - PACK_IDS] == "sky.cube", "a name for every id after the packs");
}

// static
Expand Down Expand Up @@ -345,6 +345,14 @@ ALRenderResourcePlan ALRenderResourcePlan::make(const ALRenderResourceInputs& in
plan.at(Id::TrueNoise).mDesc = ALRenderTargetDesc{ .color = { GL_RGB16F } };
plan.at(Id::ToneCurve).mDesc = ALRenderTargetDesc{ .color = { GL_RGBA16 } };

// Where there are no reflection probes, the shiny and probe-less reflections read the sky from a small cube the
// sky bakes, with every level.
if (!in.mProbes)
{
plan.at(Id::EnvironmentCube).mDesc =
ALRenderTargetDesc{ .color = { GL_RGBA8 }, .mips = ALRenderTargetDesc::Mips::Manual };
}

// SMAA's area and search tables, and the debug image it can blend, for as long as SMAA is what antialiases, as its
// edges and blend weights are.
if (in.mFSAAType == 2)
Expand Down
1 change: 1 addition & 0 deletions indra/alrender/alrenderresourceplan.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class ALRenderResourcePlan
SMAASearch,
SMAASample,
ToneCurve,
EnvironmentCube, ///< the sky's cube for the shiny and probe-less reflections, without probes (ALSkyCube)

COUNT
};
Expand Down
Loading
Loading