Graphics objects (gobj)
This cluster is the device-independent representation of everything that gets drawn: geometry (Geom, GeomPrimitive), the vertex-data layer (GeomVertexData / GeomVertexFormat / GeomVertexArrayData), textures (Texture, TextureStage, SamplerState), shaders (Shader, ShaderBuffer), materials, lenses, and the GPU-resource bookkeeping (PreparedGraphicsObjects and the per-GSG *Context handles). Everything here lives first in system RAM as renderer-agnostic data; it is converted to a backend-specific layout by GeomMunger and uploaded to the GPU lazily through the abstract GraphicsStateGuardianBase (gsgbase) interface on first render. The two directories are deliberately separate Panda packages: gobj (libp3gobj) holds the concrete data classes; gsgbase (libp3gsgbase) holds only the abstract GSG/output interfaces, kept in their own package “so we can avoid circular build dependency problems” (graphicsStateGuardianBase.h). Almost every class in gobj is built around the copy-on-write + pipeline-cycler concurrency pattern so a render thread can read geometry lock-free while an app thread mutates it.
gobj
What it is. panda/src/gobj is the largest leaf package in Panda’s graphics stack. It defines the in-memory data model for renderable content: a Geom bundles one GeomVertexData (the vertex table) with one or more GeomPrimitives (index lists describing how vertices form triangles/lines/points). Separately it defines textures, shaders, materials, lenses, the format/registry machinery, the munger that bridges to a specific renderer, and the LRU/paging systems that keep both vertex data and GPU resources within a memory budget. It has no knowledge of OpenGL/DirectX/Vulkan — those backends (panda/src/glstuff, dxgsg9, etc.) consume these classes through the gsgbase double-dispatch interface.
The geometry core: Geom, GeomPrimitive, GeomVertexData
Geom(geom.h/geom.cxx, ~61 KB of impl) —class Geom : public CopyOnWriteObject, public GeomEnums. A container associating oneCOWPT(GeomVertexData)with apvector<COWPT(GeomPrimitive)>. All primitives in a Geom share the same vertex table and the same render state. Key methods:set_vertex_data/modify_vertex_data,add_primitive,draw()(the public entry into rendering, takes aGraphicsStateGuardianBase *),prepare_now(), and a family of in-place transforms (decompose_in_place,unify_in_place,make_points_in_place,doubleside_in_place). It maintains a per-format munge cache (Geom::CacheEntry, keyed by(GeomVertexData*, GeomMunger*)) and a per-GSG context map (Contexts _contexts).GeomPrimitive(geomPrimitive.h/.cxx, ~75 KB) —class GeomPrimitive : public CopyOnWriteObject, public GeomEnums. Abstract base for an ordered list of vertex indices. The index list itself is stored as aGeomVertexArrayData(so index buffers reuse the same paging/upload machinery as vertex buffers). Concrete subclasses (all: public GeomPrimitive):GeomTriangles,GeomTristrips,GeomTrifans,GeomPatches,GeomLines,GeomLinestrips,GeomPoints, plus adjacency variants.make_copy(),get_primitive_type(), anddraw()are pure virtual; each subclass dispatches to the matchingGraphicsStateGuardianBase::draw_triangles/draw_tristrips/.... Notable: indices can be non-indexed (afirst_vertex/num_verticesrange) or indexed (uint8/uint16/uint32, auto-elevated viaconsider_elevate_index_type); composite types (strips/fans) use a_endsarray;_mins/_maxscache the index range per sub-primitive.GeomVertexData(geomVertexData.h/.cxx, ~90 KB) —class GeomVertexData : public CopyOnWriteObject, public GeomEnums. The vertex table: a list ofGeomVertexArrayDataarrays interpreted through aGeomVertexFormat. Holds optional animation tables (TransformTable,TransformBlendTable,SliderTable) and a cached_animated_verticesresult. Key methods:convert_to(new_format),animate_vertices(force, thread)(runs CPU skinning/morphing),transform_vertices(mat),scale_color/set_color,set_num_rows. App code should not poke arrays directly — it goes through reader/writer cursors (below).
The vertex-data subsystem (format, arrays, cursors, animation, paging)
GeomVertexFormat(geomVertexFormat.h/.cxx) —final : public TypedWritableReferenceCount, public GeomEnums. Describes the physical layout: a list ofGeomVertexArrayFormats, each a list ofGeomVertexColumns (name + numeric type + components). Formats must be registered before use (GeomVertexFormat::register_format); registration deduplicates through a globalRegistryguarded byLightReMutex _lock, so two logically equal formats return the same pointer and can be compared by identity. Provides standard prebuilt formats (get_v3n3t2,get_v3c4, etc.) and the all-importantget_post_animated_format()/get_union_format()used by the munger.GeomVertexArrayFormat(geomVertexArrayFormat.h) andGeomVertexColumn(geomVertexColumn.h, ~100 KB of.cxx— it contains every numeric-type read/write packer) define one array’s columns.GeomVertexColumn : public GeomEnums.GeomVertexArrayData(geomVertexArrayData.h/.cxx) —class GeomVertexArrayData : public CopyOnWriteObject, public SimpleLruPage, public GeomEnums. One contiguous byte buffer (“stream”/vertex buffer in DX/GL terms). The raw bytes live in aVertexDataBuffer, which can be paged out (see below). InheritingSimpleLruPageis what enrolls each array in the global vertex-memory LRU. Access goes throughGeomVertexArrayDataHandle.Cursors:
GeomVertexReader/GeomVertexWriter(both: public GeomEnums) andGeomVertexRewriter : public GeomVertexWriter, public GeomVertexReader. These are the high-level, type-safe way to read/write columns byInternalName. The.Ifiles are enormous (geomVertexWriter.I~37 KB) because they inline a specialized path per numeric type.Animation:
GeomVertexAnimationSpecrecords whether a format animates on CPU (AT_panda) or GPU (AT_hardware). The transform pipeline isVertexTransform(abstract;get_matrix/accumulate_matrix) →TransformBlend(a weighted set of transforms for one vertex) →TransformBlendTable→ referenced fromGeomVertexData.VertexSlider/SliderTabledrive morph targets.UserVertexTransform/UserVertexSliderare app-settable leaves.Paging (vertex data to disk):
VertexDataBuffer(vertexDataBuffer.h) holds the bytes;VertexDataPage : public SimpleAllocator, public SimpleLruPage(vertexDataPage.h) packs many buffers onto a page that can transitionRC_resident → RC_compressed → RC_diskunder memory pressure;VertexDataBookis the allocator-of-pages;VertexDataSaveFileis the on-disk backing store. Background threads (configvertex-data-page-threads) do the compress/spill work.
The texture subsystem
Texture(texture.h, 50 KB header;texture.cxx, 339 KB — the single biggest file in the cluster) —class Texture : public TypedWritableReferenceCount, public Namable. Holds a CPU RAM image plus all the metadata (TextureType1d/2d/3d/array/cube/buffer,ComponentType,Format— dozens of formats incl. sRGB, integer, packed-float, depth). It is pipelined (CData/PipelineCycler) and tracks a per-GSGTextureContext. Gotcha (community-confirmed): Panda frees the system-RAM image once the texture is uploaded to the GPU; if the GPU copy is later lost (e.g. cube map mipmaps), reload can fail — setTexture.keep_ram_image = Trueor rely onkeep-texture-ram. See GitHub issue #1091 “Loading/unloading cubemap texture with manual mipmaps needs TexturePool.release()”, https://github.com/panda3d/panda3d/issues/1091 (“Panda unloads the texture from RAM once it’s been uploaded to the GPU… Texture.keep_ram_image = True works and solves the issue”).TextureStage(textureStage.h) — names a slot in the fixed-function multitexture pipeline and its combine mode; pooled viaTextureStagePool.SamplerState(samplerState.h) —class SamplerState : public MemoryBase. A value object (not ref-counted) describing wrap modes (WM_clamp/repeat/mirror/...), min/magFilterType, anisotropy, LOD bias, border color. Decoupled fromTexturesince rdb’s 2014 rework so the same image can be sampled differently in different places; the GSG prepares aSamplerContextfor it.TexturePool(texturePool.h, ~45 KB impl) — process-global registry/cache keyed by filename, with a filter chain (TexturePoolFilter,PythonTexturePoolFilter).VideoTexture : public Texture, public AnimInterfaceis the base for movie textures.TexturePeekerreads pixels back from a RAM image;TextureCollectionis a list type.TextureReloadRequestis the async background reload task (threads:texture-reload-num-threads).
Shaders and materials
Shader(shader.h;shader.cxx~120 KB) —class Shader : public TypedWritableReferenceCount. Holds source/metadata forSL_Cg / SL_GLSL / SL_HLSL / SL_SPIR_V, split byShaderType(vertex/fragment/geometry/tess/compute).Shader::load/make/make_computeare the factories. The bigShaderMatInputenum (SMO_model_to_view,SMO_attr_colorscale, …) enumerates the auto-bound shader inputs Panda can compute from render state.prepare()returns aPT(AsyncFuture)(compilation can be async).ShaderBuffer : public TypedWritableReferenceCount, public Namable, public GeomEnums(shaderBuffer.h) is GPU storage for compute (SSBO).Material(material.h) —class Material : public TypedWritableReferenceCount, public Namable. Lighting properties; supports both classic (ambient/diffuse/specular) and metalness (base color + metallic + roughness) PBR workflows.MaterialPooldeduplicates.Lens(lens.h;lens.cxx~64 KB) —class Lens : public TypedWritableReferenceCount. Camera/projection abstraction withextrude/project. Subclasses:PerspectiveLens,OrthographicLens,MatrixLens. Lives in gobj (not display) because aSpotlightalso uses aLens.ParamTextureSampler/ParamTextureImage(paramTexture.h, both: public ParamValueBase) wrap a texture+sampler (or image) as a shader-input parameter.
Munging, caching, and GPU preparation
GeomMunger(geomMunger.h/.cxx) —class GeomMunger : public TypedReferenceCount, public GeomEnums. The pivotal bridge: converts aGeomVertexDatafrom its authoring format into one the backend can consume, and applies render-state-driven vertex changes (e.g. baking aColorScaleAttribinto vertex colors). Each backend subclasses it (CLP(GeomMunger),DXGeomMunger). Registered mungers are deduplicated like formats (same operation ⇒ same pointer). The GSG hands one out viaGraphicsStateGuardianBase::get_geom_munger(state, thread). Performance note (trusted forum): “‘munge’ is the time required to massage the GVD into a format suitable for the backend, and is what fills up the geom-vertex-cache” — https://discourse.panda3d.org/t/8211. Matching your authoring format to the GSG’s preferred format avoids munge cost.GeomCacheManager/GeomCacheEntry(geomCacheManager.h,geomCacheEntry.h) — a global LRU bounding the total munge/decompose cache (geom-cache-size,geom-cache-min-frames).AnimateVerticesRequestruns CPU vertex animation off-thread.PreparedGraphicsObjects(preparedGraphicsObjects.h;.cxx~54 KB) —class PreparedGraphicsObjects : public ReferenceCount. The per-GSG (or shared-context) registry of GPU resources. For each resource kind (texture, sampler, geom, shader, vertex buffer, index buffer, shader buffer) it offersenqueue_*/dequeue_*/is_*_prepared/release_*/prepare_*_now. It owns the*Contextobjects and enforces the graphics-memory budget throughBufferResidencyTracker+AdaptiveLru. Protected by aReMutex.Context objects (per-GSG opaque handles):
SavedContext : public TypedObject←BufferContext : public SavedContext, private LinkedListNode←TextureContext : public BufferContext, public AdaptiveLruPage; alsoVertexBufferContext,IndexBufferContext,GeomContext,ShaderContext,SamplerContext,BufferContextChain. Backends subclass these to stash the real API handle (e.g. a GL texture id).Memory machinery:
SimpleAllocator(block sub-allocation),SimpleLru+SimpleLruPage(basic LRU; vertex arrays/pages are pages),AdaptiveLru+AdaptiveLruPage(frequency-weighted LRU used for GPU residency; textures are pages). Configgraphics-memory-limit,adaptive-lru-weight.
gsgbase
What it is. panda/src/gsgbase is a tiny package (two real classes) that exists purely to break a build-dependency cycle. The renderable data in gobj needs to call into a GSG to draw, but a concrete GSG (in display/backends) depends on gobj. The fix is to put the abstract GSG interface here, in a package that gobj can depend on, so Geom/GeomPrimitive can invoke gsg->draw_triangles(...) without a circular link. As the header states: “It lives in a separate class in its own package so we can avoid circular build dependency problems” (graphicsStateGuardianBase.h).
Key classes.
GraphicsStateGuardianBase(graphicsStateGuardianBase.h/.cxx) —class GraphicsStateGuardianBase : public TypedWritableReferenceCount. A pure-virtual “device driver” interface enumerating everything a renderer must implement: capability queries (get_max_texture_dimension,get_supported_geom_rendering,prefers_triangle_strips,get_supports_compressed_texture_format,get_supports_hlsl), resource preparation (prepare_texture/prepare_geom/prepare_shader/prepare_vertex_buffer/prepare_index_buffer/prepare_sampler/prepare_shader_bufferand theirrelease_*), state binding (set_state_and_transform,get_geom_munger,bind_light), and the double-dispatch draw protocol:begin_draw_primitives→ one ofdraw_triangles / draw_triangles_adj / draw_tristrips / draw_tristrips_adj / draw_trifans / draw_patches / draw_lines / draw_lines_adj / draw_linestrips / draw_linestrips_adj / draw_points→end_draw_primitives. EachGeomPrimitivesubclass picks the matching method, so the primitive type is resolved without RTTI or switch statements. It also keeps the global GSG registry (add_gsg/get_gsg/get_default_gsg) used so aTexture/Geomcan be released from all GSGs.GraphicsOutputBase(graphicsOutputBase.h/.cxx) —class GraphicsOutputBase : public TypedWritableReferenceCount. A two-method abstract base (set_sort,get_texture) forGraphicsOutput(windows/buffers), again split out so lower packages can refer to an output without depending ondisplay.
How it plugs in. Concrete subclass GraphicsStateGuardian (panda/src/display) fleshes most of these out; backend GSGs (GLGraphicsStateGuardian, DXGraphicsStateGuardian9, Vulkan) subclass that. gobj (and pgraph, via cull) only ever sees the base type — Geom::draw(GraphicsStateGuardianBase *gsg, ...). The forward-declaration block at the top of graphicsStateGuardianBase.h (every Geom*, Texture, attrib, light type) documents exactly which engine objects participate in the GSG dispatch.
Where to start. To add a capability flag or a new draw entry point, edit graphicsStateGuardianBase.h (add the pure-virtual), then implement it in panda/src/display/graphicsStateGuardian.cxx and each backend. config_gsgbase.cxx only does init_type() registration — there are no gsgbase config vars. Community pointer on profiling: time attributed to begin_draw_primitives in PStats is the GSG draw-setup cost (Discord, https://discord.com/channels/524691714909274162/533306680406966273/1126767468858331156).
Where to start (this cluster)
A new contributor should orient through these files, in order:
panda/src/gobj/geom.h— the top-level container; fromGeom::draw()you can trace the whole render path.panda/src/gobj/geomEnums.h— small, and decodes the vocabulary (UsageHint,GeomRendering,NumericType,Contents) used everywhere else.panda/src/gobj/geomVertexFormat.h+geomVertexData.h— how vertices are described and stored; read alongsidegeomVertexReader.h/geomVertexWriter.hfor the cursor API.panda/src/gobj/geomPrimitive.h— index/primitive model and thedraw_*dispatch tie-in.panda/src/gobj/geomMunger.h— the format-conversion bridge; the single most important class for understanding the gobj↔backend boundary.panda/src/gsgbase/graphicsStateGuardianBase.h— the abstract driver interface every backend implements; the forward-declare block is a map of who talks to the GSG.panda/src/gobj/preparedGraphicsObjects.h— GPU-resource lifecycle and the memory budget.panda/src/gobj/texture.handshader.h— the other two big resource types; large, so start from the published factory methods (Texture::read,Shader::load/make).