Compare commits

...
Author SHA1 Message Date
Horizon agentandClaude Opus 5.5 27740e2e8f geometry.rs: GbuffersInputs only in its tests; lockfiles: the pipeline crate takes naga
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:46:06 +01:00
Horizon agentandClaude Opus 5.5 a7d211a1fc Translated programs' types move to horizon-render-pipeline
A translated program as the renderer builds pipelines from it (Program, ComputeProgram,
Interface, Uniform, Sampler, Image, Target, Error, DRAW_UNIFORMS) and the vertex inputs the
renderer offers it (GbuffersInputs, GeometryInputs, AlphaTest, WATER_MASK, WATER_PATCHES) move
unchanged from horizon-shaders' translator, which re-exports them. The renderer's library code
takes them from the engine crate. Layer plan M6.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 16:42:43 +01:00
17 changed files with 248 additions and 224 deletions

No files matched your search

Generated
+1
View File
@@ -1858,6 +1858,7 @@ name = "horizon-render-pipeline"
version = "0.1.0"
dependencies = [
"bytemuck",
"naga",
]
[[package]]
+2 -2
View File
@@ -7714,7 +7714,7 @@ impl Renderer {
inputs.declarations.push_str(
"layout(set = 5, binding = 0) readonly buffer HzIndices { uint hz_indices[]; };\n",
);
horizon_shaders::GeometryInputs {
horizon_render_pipeline::GeometryInputs {
inputs,
pull: pack_atlas::PULL.to_owned(),
index: "hz_indices[@]".to_owned(),
@@ -7737,7 +7737,7 @@ impl Renderer {
}
}
fn pack_terrain_inputs(&mut self) -> Option<horizon_shaders::GbuffersInputs> {
fn pack_terrain_inputs(&mut self) -> Option<horizon_render_pipeline::GbuffersInputs> {
// Flat draws whenever the device has them (`render` keeps them on with a pack).
let flat = self.flat.is_some() && self.flat_on;
// Flat draws read each sprite's place from a table: the atlas is stitched as the game's.
@@ -597,9 +597,9 @@ const FACE: &str = " uint hz_q = (uint(gl_VertexIndex) & ~3u) * 6u;\n \
/// quad lands whole in one part. The colour's alpha can't decide it, as with `separateAo` it
/// holds each vertex's own ambient occlusion.
pub fn terrain_parts(
terrain: &horizon_shaders::GbuffersInputs,
terrain: &horizon_render_pipeline::GbuffersInputs,
lava: &[u16],
) -> [horizon_shaders::GbuffersInputs; 2] {
) -> [horizon_render_pipeline::GbuffersInputs; 2] {
let opaque = crate::mesher::OPAQUE_LAYER;
let lava: String = lava
.iter()
@@ -619,7 +619,7 @@ pub fn terrain_parts(
impl PackAtlas {
/// The vertex inputs of a gbuffers program drawing terrain (`TerrainVertex`, terrain.wgsl's
/// `vs_terrain` or, for `flat` draws, `vs_terrain_flat`), in atlas coordinates.
pub fn terrain_inputs(&self, flat: bool) -> horizon_shaders::GbuffersInputs {
pub fn terrain_inputs(&self, flat: bool) -> horizon_render_pipeline::GbuffersInputs {
let mut declarations = String::from(
"layout(location = 0) in vec3 hz_v_pos;\nlayout(location = 1) in uvec2 hz_v_uv;\n\
layout(location = 2) in uint hz_v_layer;\nlayout(location = 3) in vec4 hz_v_color;\n\
@@ -753,7 +753,7 @@ impl PackAtlas {
("vaColor", "hz_Color"),
("vaNormal", "hz_Normal"),
];
horizon_shaders::GbuffersInputs {
horizon_render_pipeline::GbuffersInputs {
declarations,
body,
attributes: attributes
@@ -31,7 +31,7 @@ pub fn buffers() -> [Option<wgpu::VertexBufferLayout<'static>>; 1] {
/// position, the shared atlas's UV, colour, light (block, sky in 16ths) and the world-oriented
/// normal. The quad's corners, read from the same vertices at set 1, give the face normal where
/// the vertex has none (custom geometry) and `at_tangent` along the texture's u, as for terrain.
pub fn inputs() -> horizon_shaders::GbuffersInputs {
pub fn inputs() -> horizon_render_pipeline::GbuffersInputs {
let words = std::mem::size_of::<Vertex>() / 4;
let mut declarations = "layout(location = 0) in vec3 hz_v_pos;\nlayout(location = 1) in vec2 hz_v_uv;\n\
layout(location = 2) in uint hz_v_overlay;\nlayout(location = 3) in vec4 hz_v_color;\n\
@@ -112,7 +112,7 @@ pub fn inputs() -> horizon_shaders::GbuffersInputs {
// bright.
("horizonTier", "float((hz_v_overlay >> 16u) & 255u)"),
];
horizon_shaders::GbuffersInputs {
horizon_render_pipeline::GbuffersInputs {
declarations,
body,
attributes: attributes
@@ -121,7 +121,7 @@ pub fn inputs() -> horizon_shaders::GbuffersInputs {
.collect(),
after: String::new(),
// Cutout, as vanilla's entity types mostly are (OQ-23): the loader's test at 0.1.
alpha_test: Some(horizon_shaders::AlphaTest {
alpha_test: Some(horizon_render_pipeline::AlphaTest {
op: ">",
reference: 0.1,
}),
@@ -138,7 +138,7 @@ pub fn inputs() -> horizon_shaders::GbuffersInputs {
/// inputs of [`inputs`], each vertex read from set 1 (`hz_entity_vertices`), as `Vertex` lays
/// it out. The draw's corners walk its quads two triangles each, as `quad_indices` does: corner
/// `c` of a draw from vertex `first` (a whole quad) is drawn as vertex `first / 4 * 6 + c`.
pub fn geometry_inputs() -> horizon_shaders::GeometryInputs {
pub fn geometry_inputs() -> horizon_render_pipeline::GeometryInputs {
let words = std::mem::size_of::<Vertex>() / 4;
let pull = format!(
" uint hz_pw = hz_vertex_index * {words}u;\n \
@@ -150,7 +150,7 @@ pub fn geometry_inputs() -> horizon_shaders::GeometryInputs {
hz_v_light = uvec4(hz_pl & 255u, (hz_pl >> 8u) & 255u, (hz_pl >> 16u) & 255u, hz_pl >> 24u);\n \
hz_v_normal = unpackSnorm4x8(hz_entity_vertices[hz_pw + 8u]);\n"
);
horizon_shaders::GeometryInputs {
horizon_render_pipeline::GeometryInputs {
inputs: inputs(),
pull,
// Quad `c / 6`, corner 0, 1, 2, 0, 2, 3 of it.
@@ -163,7 +163,7 @@ pub fn geometry_inputs() -> horizon_shaders::GeometryInputs {
/// What `gbuffers_clouds` reads from the clouds' mesh (entity vertices, `vs_cloud`): the
/// position moved by the clouds' offset, the colour of the face's shade from their palette,
/// full light, the face's normal. Set 1 holds the clouds' uniform (`clouds::Uniform`).
pub fn cloud_inputs() -> horizon_shaders::GbuffersInputs {
pub fn cloud_inputs() -> horizon_render_pipeline::GbuffersInputs {
let declarations = "layout(location = 0) in vec3 hz_v_pos;\nlayout(location = 1) in vec2 hz_v_uv;\n\
layout(location = 2) in uint hz_v_overlay;\nlayout(location = 3) in vec4 hz_v_color;\n\
layout(location = 4) in uvec4 hz_v_light;\nlayout(location = 5) in vec4 hz_v_normal;\n\
@@ -183,7 +183,7 @@ pub fn cloud_inputs() -> horizon_shaders::GbuffersInputs {
("vaColor", "hz_Color"),
("vaNormal", "hz_Normal"),
];
horizon_shaders::GbuffersInputs {
horizon_render_pipeline::GbuffersInputs {
declarations,
body,
attributes: attributes
@@ -17,7 +17,7 @@ const UNIFORM: u64 = 2 * 64;
const SLOT: u64 = 32;
/// What `gbuffers_hand` reads: the entity inputs, and the hand's projection from set 1.
pub fn inputs() -> horizon_shaders::GbuffersInputs {
pub fn inputs() -> horizon_render_pipeline::GbuffersInputs {
let mut inputs = super::pack_entities::inputs();
// No last frame's hand to measure from: the hand's `at_velocity` reads zero (and its set 1
// has no previous vertices).
@@ -8,7 +8,7 @@
/// width in pixels. The strip's corners 0 and 1 are at the start, 2 and 3 at the end (six
/// vertices per edge: pack pipelines draw triangle lists); the
/// width is applied in screen space after the pack's transform.
pub fn inputs() -> horizon_shaders::GbuffersInputs {
pub fn inputs() -> horizon_render_pipeline::GbuffersInputs {
let declarations =
"layout(location = 0) in vec3 hz_l_start;\nlayout(location = 1) in vec3 hz_l_end;\n\
layout(location = 2) in vec4 hz_l_argb;\nlayout(location = 3) in float hz_l_width;\n\
@@ -41,7 +41,7 @@ pub fn inputs() -> horizon_shaders::GbuffersInputs {
if ((hz_l_c & 1) != 0) hz_l_o = -hz_l_o;\n \
gl_Position.xy += hz_l_o * gl_Position.w;\n }\n"
.to_owned();
horizon_shaders::GbuffersInputs {
horizon_render_pipeline::GbuffersInputs {
declarations,
body,
attributes: attributes
@@ -125,7 +125,7 @@ enum Source {
ShadowDepth(usize, bool),
/// `shadowcolor0/1` read outside the shadow programs (in them they are `Color`).
ShadowColor(usize),
/// The boats' water patches' depth (`horizon_shaders::WATER_MASK`), for translucent terrain.
/// The boats' water patches' depth (`horizon_render_pipeline::WATER_MASK`), for translucent terrain.
WaterMask,
}
@@ -883,7 +883,7 @@ fn fits(ty: &str, sample_type: wgpu::TextureSampleType) -> bool {
/// `hz_depth_linear` for a program: 1 for each of shadowtex0, shadowtex1, `shadow` and
/// `watershadow` whose depth filters (`linear`, bit n: shadowtex n). `shadow` is shadowtex1 in a
/// program that also declares `watershadow` (6.3).
fn depth_linear(interface: &horizon_shaders::Interface, linear: u8) -> [f32; 4] {
fn depth_linear(interface: &horizon_render_pipeline::Interface, linear: u8) -> [f32; 4] {
let watershadow = interface.samplers.iter().any(|s| s.name == "watershadow");
[0, 1, usize::from(watershadow), 0].map(|n| f32::from(linear >> n & 1))
}
@@ -921,7 +921,7 @@ fn source(name: &str, shadow_space: bool, shadow_pass: bool, watershadow: bool)
"lightmap" => Source::Lightmap,
"normals" => Source::Normals,
"specular" => Source::Specular,
horizon_shaders::WATER_MASK => Source::WaterMask,
horizon_render_pipeline::WATER_MASK => Source::WaterMask,
n => horizon_render_pipeline::buffer_index(n).map_or(Source::Dummy, Source::Color),
}
}
@@ -1503,7 +1503,7 @@ fn owned_buffers(
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
fn bindings(
features: wgpu::Features,
interface: &horizon_shaders::Interface,
interface: &horizon_render_pipeline::Interface,
name: &str,
group: Group,
buffers: &[u32],
@@ -2283,7 +2283,7 @@ fn build_compute(
device: &wgpu::Device,
name: String,
group: Group,
c: horizon_shaders::ComputeProgram,
c: horizon_render_pipeline::ComputeProgram,
formats: &[wgpu::TextureFormat; BUFFERS],
shadow: Option<&[wgpu::TextureFormat; SHADOW_COLORS]>,
notes: &mut Vec<String>,
@@ -58,7 +58,7 @@ pub(super) const ATTRIBUTES: [wgpu::VertexAttribute; 2] =
wgpu::vertex_attr_array![0 => Float32x3, 1 => Uint32];
/// What `gbuffers_skybasic` reads from these vertices.
pub fn inputs() -> horizon_shaders::GbuffersInputs {
pub fn inputs() -> horizon_render_pipeline::GbuffersInputs {
let declarations =
"layout(location = 0) in vec3 hz_v_pos;\nlayout(location = 1) in uint hz_v_kind;\n\
layout(set = 1, binding = 0) uniform HzSky { mat4 hz_sky_stars; vec4 hz_sky_color; \
@@ -90,7 +90,7 @@ pub fn inputs() -> horizon_shaders::GbuffersInputs {
hz_MultiTexCoord2 = hz_MultiTexCoord1;\n \
hz_Normal = vec3(0.0, 0.0, 1.0);\n"
);
horizon_shaders::GbuffersInputs {
horizon_render_pipeline::GbuffersInputs {
declarations,
body,
attributes: vec![
@@ -111,8 +111,8 @@ pub fn inputs() -> horizon_shaders::GbuffersInputs {
}
/// What `gbuffers_skytextured` reads from the sun and moon's vertices.
pub fn celestial_inputs() -> horizon_shaders::GbuffersInputs {
horizon_shaders::GbuffersInputs {
pub fn celestial_inputs() -> horizon_render_pipeline::GbuffersInputs {
horizon_render_pipeline::GbuffersInputs {
declarations:
"layout(location = 0) in vec3 hz_v_pos;\nlayout(location = 1) in vec2 hz_v_uv;\n\
layout(location = 2) in vec4 hz_v_color;\n"
@@ -2717,7 +2717,7 @@ pub(super) mod initial_history_tests {
/// `horizonWaterPatch<k>`'s `k`: patch `k / 2`, its corner and y range (even) or edges (odd).
fn water_patch_index(name: &str) -> Option<usize> {
let k: usize = name.strip_prefix("horizonWaterPatch")?.parse().ok()?;
(k < 2 * horizon_shaders::WATER_PATCHES).then_some(k)
(k < 2 * horizon_render_pipeline::WATER_PATCHES).then_some(k)
}
/// The boats' water patches among the frame's entity quads (`WATER_MASK_OVERLAY`), the first
@@ -2729,7 +2729,7 @@ pub(super) fn water_patches(entities: &[super::Vertex]) -> Vec<[[f32; 4]; 2]> {
let mut points: Vec<[f32; 3]> = Vec::new();
let flush = |points: &mut Vec<[f32; 3]>, out: &mut Vec<[[f32; 4]; 2]>| {
if let Some(b) = patch_box(points)
&& out.len() < horizon_shaders::WATER_PATCHES
&& out.len() < horizon_render_pipeline::WATER_PATCHES
{
out.push(b);
}
@@ -11,7 +11,7 @@ use super::pack_post::Weather;
/// What `gbuffers_weather` reads from a weather vertex: camera-relative position, the UV in
/// repeats of the sprite, colour (with vanilla's fade) and light. Weather has no normal; the
/// pack sees straight up.
pub fn inputs() -> horizon_shaders::GbuffersInputs {
pub fn inputs() -> horizon_render_pipeline::GbuffersInputs {
let declarations =
"layout(location = 0) in vec3 hz_v_pos;\nlayout(location = 1) in vec2 hz_v_uv;\n\
layout(location = 2) in uint hz_v_overlay;\nlayout(location = 3) in vec4 hz_v_color;\n\
@@ -31,7 +31,7 @@ pub fn inputs() -> horizon_shaders::GbuffersInputs {
("vaColor", "hz_Color"),
("vaNormal", "hz_Normal"),
];
horizon_shaders::GbuffersInputs {
horizon_render_pipeline::GbuffersInputs {
declarations,
body,
attributes: attributes
@@ -40,7 +40,7 @@ pub fn inputs() -> horizon_shaders::GbuffersInputs {
.collect(),
after: String::new(),
// Vanilla's weather drops texels below 0.1, as Horizon's own weather shader does.
alpha_test: Some(horizon_shaders::AlphaTest {
alpha_test: Some(horizon_render_pipeline::AlphaTest {
op: ">",
reference: 0.1,
}),
+1
View File
@@ -497,6 +497,7 @@ name = "horizon-render-pipeline"
version = "0.1.0"
dependencies = [
"bytemuck",
"naga",
]
[[package]]
@@ -9,6 +9,8 @@ description = "What an external shader pipeline gives the renderer: its buffers,
[dependencies]
# The frame uniform is uploaded as bytes (inputs.rs).
bytemuck = { version = "1.24", features = ["derive"] }
# A translated program's stages are naga modules (program.rs).
naga = "30.0.1"
[lints]
workspace = true
@@ -5,6 +5,8 @@
//! `shaders/` packs). No wgpu and no pack file here.
//!
//! - [`config`]: the colortex buffers and the shadow map: formats, clearing, mipmaps, settings.
//! - [`program`]: a translated program (naga modules and their interface) and the vertex
//! inputs the renderer offers it.
//! - [`textures`]: the custom textures the programs read; [`RENDER_STAGES`]: the stages they draw in.
//! - [`stages`], [`inputs`], [`Api`]: the WGSL stage interface Horizon's own packs are written
//! against (`horizon-shader-api` reads and checks those packs): the stages' functions, preludes,
@@ -13,6 +15,7 @@
mod api;
pub mod config;
pub mod inputs;
pub mod program;
mod render_stage;
pub mod stages;
pub mod textures;
@@ -23,5 +26,9 @@ pub use config::{
shadow_color,
};
pub use inputs::Frame;
pub use program::{
AlphaTest, ComputeProgram, GbuffersInputs, GeometryInputs, Interface, Program, WATER_MASK,
WATER_PATCHES,
};
pub use render_stage::{RENDER_STAGES, render_stage};
pub use stages::StageKind;
@@ -0,0 +1,199 @@
//! A translated program as the renderer builds its pipelines from it: its naga modules and the
//! interface they expect (uniforms, samplers, images, outputs), and what the renderer offers a
//! program's vertex stage (`GbuffersInputs`, `GeometryInputs`). `horizon-shaders` translates a
//! `shaders/` pack's GLSL into these.
/// What a program's stages need from the game.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Interface {
/// The uniform block at binding 0: its size and each value's name, GLSL type and offset.
/// Ours come first: `hz_target` (the target's width, height, 1/width, 1/height), and the
/// fixed-function fog the compatibility `gl_Fog` reads (OQ-28): `hz_fog_color` (vanilla's fog
/// colour) and `hz_fog` (density, start, end, 1 / (end - start)), then `hz_depth_linear`:
/// 1 where `shadowtex0`, `shadowtex1`, `shadow` and `watershadow` (x to w) read through a
/// plain sampler filter linearly, 0 where they take the nearest texel.
pub uniform_size: u32,
pub uniforms: Vec<Uniform>,
/// std140 initial contents; runtime values overwrite their members each frame.
pub initial_values: Vec<u8>,
/// Each sampler: texture at `binding`, sampler object at `binding + 1`.
pub samplers: Vec<Sampler>,
/// The fragment stage's output locations.
pub outputs: Vec<u32>,
/// A gbuffers program reads `DRAW_UNIFORMS`: the block at set 2, binding 0, set per draw.
pub draw: bool,
/// Storage images (`colorimg4`...), after the samplers.
pub images: Vec<Image>,
/// The SSBOs' bindings in set 4 (`bufferObject.<n>`).
pub buffers: Vec<u32>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Image {
pub name: String,
/// `image2D`, `uimage3D`...
pub ty: String,
/// The format qualifier (`rgba16f`).
pub format: String,
/// `readonly`, `writeonly`, empty for both, or `atomic` for an image an atomic function
/// uses.
pub access: String,
pub binding: u32,
/// A compute program's copy of image `before` as it was before the dispatch, which its
/// loads read (see `ordering`).
pub before: Option<String>,
}
/// A compute program (`deferred4_a.csh`): its module, what it reads and writes, and how many
/// work groups it runs: `workGroups`, or `workGroupsRender` times the target's size over the
/// local size (1, 1 when neither is set).
#[derive(Clone)]
pub struct ComputeProgram {
pub module: naga::Module,
pub interface: Interface,
/// Non-fatal translation diagnostics, located in the authored source.
pub notes: Vec<Error>,
pub work_groups: Option<[u32; 3]>,
pub work_groups_render: [f32; 2],
}
/// The uniforms a gbuffers program reads per draw (5.3's "D"), in a block of their own
/// (`HzDraw`, std140: four ints, a vec4; 32 bytes) the game sets for each draw.
pub const DRAW_UNIFORMS: [(&str, &str); 5] = [
("renderStage", "int"),
("entityId", "int"),
("blockEntityId", "int"),
("currentRenderedItemId", "int"),
("entityColor", "vec4"),
];
#[derive(Clone, Debug, PartialEq)]
pub struct Uniform {
pub name: String,
/// The type the pack declared (`float`, `ivec2`, `mat4`; `bool` is stored as an `int`).
pub ty: String,
pub offset: u32,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Sampler {
pub name: String,
/// The type the pack declared: `sampler2D`, `sampler2DShadow`, `usampler2D`, `sampler3D`...
pub ty: String,
pub binding: u32,
}
/// A translation diagnostic at a pack file and line.
#[derive(Clone, Debug, PartialEq)]
pub struct Error {
pub file: String,
pub line: u32,
pub message: String,
}
#[derive(Clone)]
pub struct Program {
pub vertex: naga::Module,
pub fragment: naga::Module,
pub interface: Interface,
/// Non-fatal translation diagnostics, located in the authored source.
pub notes: Vec<Error>,
}
/// Where the program draws, which decides the conventions of 5.5.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Target {
/// Pack buffers: rows stored as GL stores them.
Buffers,
/// The window (`final`).
Window,
}
/// What a gbuffers-style program's vertex stage reads. The game writes it: it knows its vertex
/// formats, the translator only places it.
#[derive(Clone, Debug, Default)]
pub struct GbuffersInputs {
/// GLSL declarations: the vertex inputs and any bindings of set 1.
pub declarations: String,
/// GLSL statements that set `hz_Vertex`, `hz_Color`, `hz_MultiTexCoord0`,
/// `hz_MultiTexCoord1` and `hz_Normal` (the compatibility inputs), run first in `main`.
pub body: String,
/// The value of each attribute a pack may declare (`mc_Entity`, `vaPosition`...), as a GLSL
/// expression of the declared type's size; an attribute not listed reads zero. Terrain
/// also supplies `mc_chunkFade`, an implicit float input rather than a declared attribute.
pub attributes: Vec<(String, String)>,
/// GLSL statements run last in `main`, with `gl_Position` in wgpu's clip space.
pub after: String,
/// The alpha test applied to output 0 (`gl_FragData[0]`, `gl_FragColor`), the loader's
/// (3.2 `alphaTest.<program>`): `None` keeps every fragment.
pub alpha_test: Option<AlphaTest>,
/// The program may read `currentRenderedItemId` without declaring it, as Iris 1.11.7
/// lets the hand's (observed with probe packs; terrain's may not).
pub item_id_declared: bool,
/// The depth test sees the far plane (window depth 1) unless the program writes
/// `gl_FragDepth`, while `gl_FragCoord.z` stays the geometry's own: the sky shows only where
/// nothing else drew, and reads its own depth as in Iris 1.11.7.
pub far_depth: bool,
/// `normals` and `specular` are bound as paged maps of the atlas `texture` reads
/// (`horizon-client-render`'s `paged_maps.rs`), not as images of its layout: the program's
/// texture calls on them read through `hz_pagedTexture`.
pub paged_maps: bool,
/// Translucent terrain: a fragment behind the boats' water patches (`horizonWaterMask`, the
/// patches' depth, 1 where there are none) is discarded, so no water shows inside a hull
/// (`RenderTypes.waterMask`), while the frame's depth stays the pack's own.
pub water_mask: bool,
/// The shadow pass's translucent terrain: a fragment inside a boat's water patch
/// (`horizonWaterPatch*`, camera-relative boxes) is discarded, so no water casts its light
/// into a hull. Not with a geometry stage.
pub water_patches: bool,
}
/// The sampler of the boats' water patches' depth (`GbuffersInputs::water_mask`).
pub const WATER_MASK: &str = "horizonWaterMask";
/// How many boats' water patches the shadow pass keeps water out of (`GbuffersInputs::water_patches`).
pub const WATER_PATCHES: usize = 16;
/// A fragment stays when its output 0's alpha compares to `reference` as `op` says (a GLSL
/// comparison operator, `true` for GL's `ALWAYS`, `false` for `NEVER`).
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct AlphaTest {
pub op: &'static str,
pub reference: f32,
}
impl AlphaTest {
/// `alphaTest.<program>`'s value: `off`, or a GL comparison and a reference.
pub fn parse(value: &str) -> Option<Option<Self>> {
let mut words = value.split_whitespace();
let func = words.next()?;
if func == "off" {
return Some(None);
}
let op = match func.trim_start_matches("GL_") {
"NEVER" => "false",
"LESS" => "<",
"EQUAL" => "==",
"LEQUAL" => "<=",
"GREATER" => ">",
"NOTEQUAL" => "!=",
"GEQUAL" => ">=",
"ALWAYS" => return Some(None),
_ => return None,
};
let reference = words.next()?.parse().ok()?;
Some(Some(Self { op, reference }))
}
}
/// How a geometry program's vertex invocations reach their triangle's vertices.
#[derive(Clone, Debug, Default)]
pub struct GeometryInputs {
/// The game's vertex inputs, as `GbuffersInputs`; their `layout(location = n) in`
/// declarations become plain globals the pull sets.
pub inputs: GbuffersInputs,
/// GLSL statements that set the vertex inputs from vertex `hz_vertex_index` (a `uint`).
pub pull: String,
/// The vertex of the `@`th corner of the draw (a `uint` expression, `hz_indices[@]`).
pub index: String,
}
@@ -13,21 +13,11 @@ use std::collections::BTreeSet;
use std::fmt::Write;
use super::scan::{QUALIFIERS, STORAGE, significant};
use super::{Error, GbuffersInputs, Program, Target};
use super::{Error, Program, Target};
use crate::Preprocessed;
use crate::macros::lex;
/// How a geometry program's vertex invocations reach their triangle's vertices.
#[derive(Clone, Debug, Default)]
pub struct GeometryInputs {
/// The game's vertex inputs, as `GbuffersInputs`; their `layout(location = n) in`
/// declarations become plain globals the pull sets.
pub inputs: GbuffersInputs,
/// GLSL statements that set the vertex inputs from vertex `hz_vertex_index` (a `uint`).
pub pull: String,
/// The vertex of the `@`th corner of the draw (a `uint` expression, `hz_indices[@]`).
pub index: String,
}
pub use horizon_render_pipeline::program::GeometryInputs;
/// Translates `vertex`, `geometry` and `fragment` (a shadow program): the program, and how many
/// triangles the geometry stage can emit (the instances to draw).
@@ -601,6 +591,7 @@ fn strip_storage(text: &mut [String], tokens: &[&str], range: std::ops::Range<us
#[cfg(test)]
mod tests {
use super::*;
use crate::GbuffersInputs;
use crate::pack::tests::zip;
use crate::{Macros, Pack, preprocess};
+4 -182
View File
@@ -44,188 +44,10 @@ pub use geometry::{GeometryInputs, translate_geometry};
use scan::Stage;
/// What a program's stages need from the game.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Interface {
/// The uniform block at binding 0: its size and each value's name, GLSL type and offset.
/// Ours come first: `hz_target` (the target's width, height, 1/width, 1/height), and the
/// fixed-function fog the compatibility `gl_Fog` reads (OQ-28): `hz_fog_color` (vanilla's fog
/// colour) and `hz_fog` (density, start, end, 1 / (end - start)), then `hz_depth_linear`:
/// 1 where `shadowtex0`, `shadowtex1`, `shadow` and `watershadow` (x to w) read through a
/// plain sampler filter linearly, 0 where they take the nearest texel.
pub uniform_size: u32,
pub uniforms: Vec<Uniform>,
/// std140 initial contents; runtime values overwrite their members each frame.
pub initial_values: Vec<u8>,
/// Each sampler: texture at `binding`, sampler object at `binding + 1`.
pub samplers: Vec<Sampler>,
/// The fragment stage's output locations.
pub outputs: Vec<u32>,
/// A gbuffers program reads `DRAW_UNIFORMS`: the block at set 2, binding 0, set per draw.
pub draw: bool,
/// Storage images (`colorimg4`...), after the samplers.
pub images: Vec<Image>,
/// The SSBOs' bindings in set 4 (`bufferObject.<n>`).
pub buffers: Vec<u32>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Image {
pub name: String,
/// `image2D`, `uimage3D`...
pub ty: String,
/// The format qualifier (`rgba16f`).
pub format: String,
/// `readonly`, `writeonly`, empty for both, or `atomic` for an image an atomic function
/// uses.
pub access: String,
pub binding: u32,
/// A compute program's copy of image `before` as it was before the dispatch, which its
/// loads read (see `ordering`).
pub before: Option<String>,
}
/// A compute program (`deferred4_a.csh`): its module, what it reads and writes, and how many
/// work groups it runs: `workGroups`, or `workGroupsRender` times the target's size over the
/// local size (1, 1 when neither is set).
#[derive(Clone)]
pub struct ComputeProgram {
pub module: naga::Module,
pub interface: Interface,
/// Non-fatal translation diagnostics, located in the authored source.
pub notes: Vec<Error>,
pub work_groups: Option<[u32; 3]>,
pub work_groups_render: [f32; 2],
}
/// The uniforms a gbuffers program reads per draw (5.3's "D"), in a block of their own
/// (`HzDraw`, std140: four ints, a vec4; 32 bytes) the game sets for each draw.
pub const DRAW_UNIFORMS: [(&str, &str); 5] = [
("renderStage", "int"),
("entityId", "int"),
("blockEntityId", "int"),
("currentRenderedItemId", "int"),
("entityColor", "vec4"),
];
#[derive(Clone, Debug, PartialEq)]
pub struct Uniform {
pub name: String,
/// The type the pack declared (`float`, `ivec2`, `mat4`; `bool` is stored as an `int`).
pub ty: String,
pub offset: u32,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Sampler {
pub name: String,
/// The type the pack declared: `sampler2D`, `sampler2DShadow`, `usampler2D`, `sampler3D`...
pub ty: String,
pub binding: u32,
}
/// A translation diagnostic at a pack file and line.
#[derive(Clone, Debug, PartialEq)]
pub struct Error {
pub file: String,
pub line: u32,
pub message: String,
}
#[derive(Clone)]
pub struct Program {
pub vertex: naga::Module,
pub fragment: naga::Module,
pub interface: Interface,
/// Non-fatal translation diagnostics, located in the authored source.
pub notes: Vec<Error>,
}
/// Where the program draws, which decides the conventions of 5.5.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Target {
/// Pack buffers: rows stored as GL stores them.
Buffers,
/// The window (`final`).
Window,
}
/// What a gbuffers-style program's vertex stage reads. The game writes it: it knows its vertex
/// formats, the translator only places it.
#[derive(Clone, Debug, Default)]
pub struct GbuffersInputs {
/// GLSL declarations: the vertex inputs and any bindings of set 1.
pub declarations: String,
/// GLSL statements that set `hz_Vertex`, `hz_Color`, `hz_MultiTexCoord0`,
/// `hz_MultiTexCoord1` and `hz_Normal` (the compatibility inputs), run first in `main`.
pub body: String,
/// The value of each attribute a pack may declare (`mc_Entity`, `vaPosition`...), as a GLSL
/// expression of the declared type's size; an attribute not listed reads zero. Terrain
/// also supplies `mc_chunkFade`, an implicit float input rather than a declared attribute.
pub attributes: Vec<(String, String)>,
/// GLSL statements run last in `main`, with `gl_Position` in wgpu's clip space.
pub after: String,
/// The alpha test applied to output 0 (`gl_FragData[0]`, `gl_FragColor`), the loader's
/// (3.2 `alphaTest.<program>`): `None` keeps every fragment.
pub alpha_test: Option<AlphaTest>,
/// The program may read `currentRenderedItemId` without declaring it, as Iris 1.11.7
/// lets the hand's (observed with probe packs; terrain's may not).
pub item_id_declared: bool,
/// The depth test sees the far plane (window depth 1) unless the program writes
/// `gl_FragDepth`, while `gl_FragCoord.z` stays the geometry's own: the sky shows only where
/// nothing else drew, and reads its own depth as in Iris 1.11.7.
pub far_depth: bool,
/// `normals` and `specular` are bound as paged maps of the atlas `texture` reads
/// (`horizon-client-render`'s `paged_maps.rs`), not as images of its layout: the program's
/// texture calls on them read through `hz_pagedTexture`.
pub paged_maps: bool,
/// Translucent terrain: a fragment behind the boats' water patches (`horizonWaterMask`, the
/// patches' depth, 1 where there are none) is discarded, so no water shows inside a hull
/// (`RenderTypes.waterMask`), while the frame's depth stays the pack's own.
pub water_mask: bool,
/// The shadow pass's translucent terrain: a fragment inside a boat's water patch
/// (`horizonWaterPatch*`, camera-relative boxes) is discarded, so no water casts its light
/// into a hull. Not with a geometry stage.
pub water_patches: bool,
}
/// The sampler of the boats' water patches' depth (`GbuffersInputs::water_mask`).
pub const WATER_MASK: &str = "horizonWaterMask";
/// How many boats' water patches the shadow pass keeps water out of (`GbuffersInputs::water_patches`).
pub const WATER_PATCHES: usize = 16;
/// A fragment stays when its output 0's alpha compares to `reference` as `op` says (a GLSL
/// comparison operator, `true` for GL's `ALWAYS`, `false` for `NEVER`).
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct AlphaTest {
pub op: &'static str,
pub reference: f32,
}
impl AlphaTest {
/// `alphaTest.<program>`'s value: `off`, or a GL comparison and a reference.
pub fn parse(value: &str) -> Option<Option<Self>> {
let mut words = value.split_whitespace();
let func = words.next()?;
if func == "off" {
return Some(None);
}
let op = match func.trim_start_matches("GL_") {
"NEVER" => "false",
"LESS" => "<",
"EQUAL" => "==",
"LEQUAL" => "<=",
"GREATER" => ">",
"NOTEQUAL" => "!=",
"GEQUAL" => ">=",
"ALWAYS" => return Some(None),
_ => return None,
};
let reference = words.next()?.parse().ok()?;
Some(Some(Self { op, reference }))
}
}
pub use horizon_render_pipeline::program::{
AlphaTest, ComputeProgram, DRAW_UNIFORMS, Error, GbuffersInputs, Image, Interface, Program,
Sampler, Target, Uniform, WATER_MASK, WATER_PATCHES,
};
/// Translates a composite-style program (`composite`, `deferred`, `final`...).
pub fn translate_composite(
+1
View File
@@ -2845,6 +2845,7 @@ name = "horizon-render-pipeline"
version = "0.1.0"
dependencies = [
"bytemuck",
"naga",
]
[[package]]