Rendering Pipelines in ArmorPaint: Architecture and Implementation Guide
ArmorPaint utilizes a low-level GPU abstraction called gpu_pipeline_t to bundle shaders, vertex layouts, and render states, with all rendering pipelines centralized in paint/sources/pipes.c and compiled during startup via pipes_init().
ArmorPaint is an open-source 3D texture painting application built on the Krom engine. The rendering pipelines in ArmorPaint form the core GPU abstraction layer that handles layer composition, mask operations, brush rendering, and UI display. Every pipeline defines a complete GPU state including shader pairs, vertex attribute layouts, and blend modes.
Core Pipeline Architecture
The foundation of ArmorPaint's rendering system is the gpu_pipeline_t structure defined in the engine layer. A pipeline bundles:
- Vertex and fragment shaders loaded via
sys_get_shader - A vertex layout (
gpu_vertex_structure_t) defining input attributes such as position, texture coordinates, and color - Render state flags including blend modes, depth write settings, and color-write masks
- Constant locations for per-draw data like transformation matrices and opacity values
All pipelines are instantiated once during application startup inside paint/sources/pipes.c. The initialization function pipes_init() (lines 19‑38) creates each pipeline object, configures its vertex layout, and compiles the GPU program via gpu_pipeline_compile.
Layer Composition Pipelines
The primary rendering pipelines in ArmorPaint handle texture layer merging and pixel manipulation. These operate within the paint render path defined in paint/sources/render/render_path_paint.c.
pipes_merge and Channel Variants
The pipes_merge pipeline performs general RGBA layer merging using the layer_merge.vert and layer_merge.frag shader pair. This pipeline writes to all four color channels simultaneously.
For channel-specific operations, ArmorPaint provides pipes_merge_r, pipes_merge_g, and pipes_merge_b. These utilize the same shaders as pipes_merge but configure the color-write mask to target only the red, green, or blue channel respectively.
Texture Copy Pipelines
The copy family of pipelines handles texture blitting between render targets:
pipes_copy: Standard RGBA copy usinglayer_copy.vertandlayer_copy.fragwith a vertex layout containing pos, tex, and col attributespipes_copy_bgra: Variant for BGRA texture formats using dedicatedlayer_copy_bgrashaderspipes_copy8,pipes_copy64,pipes_copy128: Target-specific bit-depth variants that set thecolor_attachmentformat toGPU_TEXTURE_FORMAT_*appropriate for 8-bit, 64-bit, or 128-bit render targetspipes_copy_rgb: Copies RGB data while disabling alpha channel writes, useful for preview passes
Mask Operation Pipelines
ArmorPaint implements several specialized rendering pipelines for mask generation and manipulation:
pipes_invert_mask: Inverts mask textures usinglayer_invert.vertandlayer_invert.fragpipes_apply_mask: Applies a mask to a layer viamask_apply.vertandmask_apply.frag, binding two texture slots (pipes_tex0_maskandpipes_texa_mask)pipes_merge_mask: Merges two masks together usingmask_merge.vertandmask_merge.frag, providing constant locations for opacity and blending parameterspipes_colorid_to_mask: Generates selection masks from color-ID textures usingmask_colorid.vertandmask_colorid.frag, enabling "paint by ID" workflows
Cursor and Interaction Pipelines
Interactive painting requires real-time cursor rendering:
pipes_cursor: Renders the brush tip usingcursor.vertandcursor.frag. This pipeline configures alpha blending with depth testing disabledpipes_cursor_decal: Handles texture stamp decals under the cursor usingcursor_decal.vertandcursor_decal.frag, providing additional constant slots for decal parameters such as rotation and scale
Utility and UI Pipelines
Beyond painting operations, specialized rendering pipelines handle interface elements:
ui_view2d_pipe: Defined inpaint/sources/ui/ui_view2d.c, this pipeline renders 2D UI overlay elements usingui_view2d.vertandui_view2d.fragtab_timeline_tween_pipe: Created inpaint/sources/ui/tab_timeline.c, this handles animation scrubber rendering viatimeline_tween.vertandtimeline_tween.fragutil_uv_pipe_dilate: Located inpaint/sources/util/util_uv.c, performs UV dilation operations essential for texture painting algorithms
Pipeline Initialization and Usage
All pipelines are created inside pipes_init() in paint/sources/pipes.c. After compilation, the engine binds pipelines using gpu_set_pipeline() or the helper draw_set_pipeline() before issuing draw calls.
Typical usage follows this pattern:
/* Merging a painted layer */
gpu_set_pipeline(pipes_merge);
gpu_set_uniform(pipes_opac, opacity);
draw_call(...);
For UI elements:
draw_set_pipeline(ui_view2d_pipe);
draw_quad(...);
draw_set_pipeline(NULL); // Reset to default
Copy operations with specific formats:
gpu_set_pipeline(pipes_copy8);
gpu_set_uniform(pipes_opac, 1.0f);
draw_quad(...);
Cursor rendering with custom constants:
gpu_set_pipeline(pipes_cursor);
gpu_set_uniform(pipes_cursor_radius, brush_radius);
gpu_set_uniform(pipes_cursor_tint, brush_color);
draw_mesh(cursor_mesh);
gpu_set_pipeline(NULL);
Integration with the Render Path
The rendering pipelines are orchestrated within paint/sources/render/render_path_paint.c. This file manages the high-level composition flow: binding the appropriate pipeline for layer merging, switching to mask pipelines for selection operations, and finally rendering the cursor and decals on top.
The low-level GPU abstraction functions—including gpu_create_pipeline() and gpu_set_pipeline()—are implemented in base/sources/engine.c, providing the hardware abstraction that makes the pipeline system portable across graphics APIs.
Summary
- Rendering pipelines in ArmorPaint are encapsulated by the
gpu_pipeline_tstructure, bundling shaders, vertex layouts, and render states - All pipeline definitions reside in
paint/sources/pipes.c, initialized viapipes_init()during startup - The system provides specialized pipelines for RGBA merging, single-channel operations, mask manipulation, and UI rendering
- Copy pipelines support multiple bit-depths (8, 64, 128-bit) and color formats (RGBA, BGRA)
- Real-time brush interaction uses dedicated cursor pipelines with alpha blending and depth testing disabled
- The paint render path in
render_path_paint.ccoordinates pipeline binding to composite the final texture output
Frequently Asked Questions
What is the purpose of the gpu_pipeline_t structure in ArmorPaint?
The gpu_pipeline_t structure serves as ArmorPaint's low-level GPU abstraction, bundling compiled shader programs, vertex attribute layouts, and fixed-function render states like blend modes and depth writes. This design allows the application to pre-compile all GPU states at startup and switch between them efficiently during rendering by calling gpu_set_pipeline().
Where are the rendering pipelines initialized in the ArmorPaint codebase?
All rendering pipelines are initialized in paint/sources/pipes.c within the pipes_init() function (lines 19‑38). This function creates each pipeline object, configures the vertex structure using gpu_vertex_structure_t, loads shaders via sys_get_shader(), and compiles the final GPU program using gpu_pipeline_compile() before the main render loop begins.
How does ArmorPaint handle different color channel operations in pipelines?
ArmorPaint uses separate pipeline instances for channel-specific operations. While pipes_merge writes to all RGBA channels, pipes_merge_r, pipes_merge_g, and pipes_merge_b target individual channels by configuring the color-write mask during pipeline setup. Similarly, pipes_copy_rgb disables alpha writes for operations that should only affect color data.
What is the difference between pipes_copy and pipes_copy8/pipes_copy64/pipes_copy128?
pipes_copy is the standard 32-bit RGBA copy pipeline, while pipes_copy8, pipes_copy64, and pipes_copy128 are specialized variants that configure the render target format via the color_attachment parameter to match 8-bit, 64-bit, or 128-bit GPU texture formats. These variants ensure correct pixel data handling when copying to high-dynamic-range or reduced-precision render targets.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →