Upgrade WebRender to e491e1ae637b2eed1e7195855d88357e5eb3ddf9 (#30323)

* Upgrade vendored version of WebRender

* Patch WebRender: upgrade version of gleam

* Restore hit testing implementation

* Fix WebRender warnings

* Adapt Servo to new WebRender

* Update results

* Add a workaround for #30313

This slightly expands text boundaries in order to take into account the
fact that layout isn't measuring glyph boundaries.
This commit is contained in:
Martin Robinson 2023-09-10 14:38:56 +02:00 committed by GitHub
parent c079acb3c3
commit a9d37cb85a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
563 changed files with 48524 additions and 51657 deletions

View file

@ -2,7 +2,7 @@
name = "glsl-to-cxx"
version = "0.1.0"
license = "MPL-2.0"
authors = ["The Mozilla Project Developers"]
authors = ["The Mozilla Project Developers", "Dimitri Sabadie"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View file

@ -1,3 +1,21 @@
A GLSL to C++ translator.
Translates GLSL to vectorized C++. Intended for use with WebRender software backend.
Architecture
------------
GLSL code is parsed by the glsl crate. In hir.rs we traverse the resulting AST
and build a higher level representation by doing type checking and name
resolution. The resulting hir tree is traversed by lib.rs to output C++ code.
The generated C++ code is 4x wider then the original glsl. i.e. a glsl 'float'
becomes a C++ 'Float' which is represented by a xmm register (a vector of 4 floats).
Likewise, a vec4 becomes a struct of 4 'Float's for a total of 4 xmm registers and
16 floating point values.
Vector branching is flattened to non-branching code that unconditionally runs both
sides of the branch and combines the results with a mask based on the condition.
The compiler also supports scalarization. Values that are known to be the same
across all vector lanes are translated to scalars instead of vectors. Branches on
scalars are translated as actual branches.

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use glsl;
extern crate glsl;
mod hir;
@ -61,8 +61,6 @@ pub fn translate(args: &mut dyn Iterator<Item = String>) -> String {
.to_string_lossy()
.to_string();
let frag_include = args.next();
let (vs_state, vs_hir, vs_is_frag) = parse_shader(vertex_file);
let (fs_state, fs_hir, fs_is_frag) = parse_shader(frag_file);
@ -79,7 +77,6 @@ pub fn translate(args: &mut dyn Iterator<Item = String>) -> String {
vs_hir,
vs_is_frag,
&uniform_indices,
None,
);
result += "\n";
result += &translate_shader(
@ -88,7 +85,6 @@ pub fn translate(args: &mut dyn Iterator<Item = String>) -> String {
fs_hir,
fs_is_frag,
&uniform_indices,
frag_include,
);
result
}
@ -120,7 +116,6 @@ fn translate_shader(
hir: hir::TranslationUnit,
is_frag: bool,
uniform_indices: &UniformIndices,
include_file: Option<String>,
) -> String {
//println!("{:#?}", state);
@ -185,8 +180,6 @@ fn translate_shader(
uses_discard: false,
used_fragcoord: Cell::new(0),
use_perspective: false,
has_draw_span_rgba8: false,
has_draw_span_r8: false,
used_globals: RefCell::new(Vec::new()),
texel_fetches: RefCell::new(Vec::new()),
};
@ -215,10 +208,6 @@ fn translate_shader(
show_translation_unit(&mut state, &hir);
if let Some(include_file) = include_file {
write_include_file(&mut state, include_file);
}
let pruned_inputs: Vec<_> = inputs
.iter()
.filter(|i| state.used_globals.borrow().contains(i))
@ -250,6 +239,7 @@ fn translate_shader(
write!(state, " return this;\n}}\n");
write!(state, "FragmentShaderImpl* get_fragment_shader() override {{\n");
write!(state, " return this;\n}}\n");
write!(state, "const char* get_name() const override {{ return \"{}\"; }}\n", name);
write!(state, "static ProgramImpl* loader() {{ return new {}_program; }}\n", name);
write!(state, "}};\n\n");
}
@ -302,8 +292,7 @@ fn write_program_samplers(state: &mut OutputState, uniform_indices: &UniformIndi
match tk {
hir::TypeKind::Sampler2D
| hir::TypeKind::Sampler2DRect
| hir::TypeKind::ISampler2D
| hir::TypeKind::Sampler2DArray => {
| hir::TypeKind::ISampler2D => {
write!(state, " ");
show_type_kind(state, &tk);
let suffix = if let hir::StorageClass::Sampler(format) = storage {
@ -326,8 +315,7 @@ fn write_program_samplers(state: &mut OutputState, uniform_indices: &UniformIndi
match tk {
hir::TypeKind::Sampler2D
| hir::TypeKind::Sampler2DRect
| hir::TypeKind::ISampler2D
| hir::TypeKind::Sampler2DArray => {
| hir::TypeKind::ISampler2D => {
write!(state, " case {}:\n", index);
write!(state, " {}_slot = value;\n", name);
write!(state, " return true;\n");
@ -355,9 +343,6 @@ fn write_bind_textures(state: &mut OutputState, uniforms: &UniformIndices) {
hir::TypeKind::ISampler2D => write!(state,
" {0} = lookup_isampler(&samplers.{0}_impl, samplers.{0}_slot);\n",
name),
hir::TypeKind::Sampler2DArray => write!(state,
" {0} = lookup_sampler_array(&samplers.{0}_impl, samplers.{0}_slot);\n",
name),
_ => {}
};
}
@ -408,9 +393,8 @@ fn write_set_uniform_4fv(
if float4_compatible(tk.clone()) {
write!(
state,
" self->{} = {}_scalar(value);\n",
name,
tk.glsl_primitive_type_name().unwrap(),
" self->{} = vec4_scalar::load_from_ptr(value);\n",
name
);
} else {
write!(state, " assert(0); // {}\n", name);
@ -551,6 +535,9 @@ fn write_load_attribs(state: &mut OutputState, attribs: &[hir::SymRef]) {
fn write_store_outputs(state: &mut OutputState, outputs: &[hir::SymRef]) {
let is_scalar = state.is_scalar.replace(true);
write!(state, "public:\nstruct InterpOutputs {{\n");
if state.hir.used_clip_dist != 0 {
state.write(" Float swgl_ClipDistance;\n");
}
for i in outputs {
let sym = state.hir.sym(*i);
match &sym.decl {
@ -576,6 +563,15 @@ fn write_store_outputs(state: &mut OutputState, outputs: &[hir::SymRef]) {
state,
" auto* dest = reinterpret_cast<InterpOutputs*>(dest_ptr);\n"
);
if state.hir.used_clip_dist != 0 {
for (i, comp) in "xyzw".chars().enumerate() {
if (state.hir.used_clip_dist & (1 << i)) != 0 {
write!(state, " dest->swgl_ClipDistance.{} = get_nth(gl_ClipDistance[{}], n);\n", comp, i);
} else {
write!(state, " dest->swgl_ClipDistance.{} = 0.0f;\n", comp);
}
}
}
for i in outputs {
let sym = state.hir.sym(*i);
match &sym.decl {
@ -626,7 +622,7 @@ fn write_read_inputs(state: &mut OutputState, inputs: &[hir::SymRef]) {
write!(state,
"static void read_interp_inputs(\
Self *self, const InterpInputs *init, const InterpInputs *step, float step_width) {{\n");
Self *self, const InterpInputs *init, const InterpInputs *step) {{\n");
for i in inputs {
let sym = state.hir.sym(*i);
match &sym.decl {
@ -640,7 +636,7 @@ fn write_read_inputs(state: &mut OutputState, inputs: &[hir::SymRef]) {
);
write!(
state,
" self->interp_step.{0} = step->{0} * step_width;\n",
" self->interp_step.{0} = step->{0} * 4.0f;\n",
name
);
}
@ -657,7 +653,7 @@ fn write_read_inputs(state: &mut OutputState, inputs: &[hir::SymRef]) {
if state.use_perspective {
write!(state,
"static void read_perspective_inputs(\
Self *self, const InterpInputs *init, const InterpInputs *step, float step_width) {{\n");
Self *self, const InterpInputs *init, const InterpInputs *step) {{\n");
if has_varying {
write!(state, " Float w = 1.0f / self->gl_FragCoord.w;\n");
}
@ -675,7 +671,7 @@ fn write_read_inputs(state: &mut OutputState, inputs: &[hir::SymRef]) {
write!(state, " self->{0} = self->interp_perspective.{0} * w;\n", name);
write!(
state,
" self->interp_step.{0} = step->{0} * step_width;\n",
" self->interp_step.{0} = step->{0} * 4.0f;\n",
name
);
}
@ -686,9 +682,12 @@ fn write_read_inputs(state: &mut OutputState, inputs: &[hir::SymRef]) {
write!(state, "}}\n");
}
write!(state, "ALWAYS_INLINE void step_interp_inputs() {{\n");
write!(state, "ALWAYS_INLINE void step_interp_inputs(int steps = 4) {{\n");
if (used_fragcoord & 1) != 0 {
write!(state, " step_fragcoord();\n");
write!(state, " step_fragcoord(steps);\n");
}
if !inputs.is_empty() {
write!(state, " float chunks = steps * 0.25f;\n");
}
for i in inputs {
let sym = state.hir.sym(*i);
@ -696,7 +695,7 @@ fn write_read_inputs(state: &mut OutputState, inputs: &[hir::SymRef]) {
hir::SymDecl::Global(_, _, _, run_class) => {
if *run_class != hir::RunClass::Scalar {
let name = sym.name.as_str();
write!(state, " {0} += interp_step.{0};\n", name);
write!(state, " {0} += interp_step.{0} * chunks;\n", name);
}
}
_ => panic!(),
@ -705,11 +704,14 @@ fn write_read_inputs(state: &mut OutputState, inputs: &[hir::SymRef]) {
write!(state, "}}\n");
if state.use_perspective {
write!(state, "ALWAYS_INLINE void step_perspective_inputs() {{\n");
write!(state, "ALWAYS_INLINE void step_perspective_inputs(int steps = 4) {{\n");
if (used_fragcoord & 1) != 0 {
write!(state, " step_fragcoord();\n");
write!(state, " step_fragcoord(steps);\n");
}
write!(state, " step_perspective(steps);\n");
if !inputs.is_empty() {
write!(state, " float chunks = steps * 0.25f;\n");
}
write!(state, " step_perspective();\n");
if has_varying {
write!(state, " Float w = 1.0f / gl_FragCoord.w;\n");
}
@ -719,7 +721,7 @@ fn write_read_inputs(state: &mut OutputState, inputs: &[hir::SymRef]) {
hir::SymDecl::Global(_, _, _, run_class) => {
if *run_class != hir::RunClass::Scalar {
let name = sym.name.as_str();
write!(state, " interp_perspective.{0} += interp_step.{0};\n", name);
write!(state, " interp_perspective.{0} += interp_step.{0} * chunks;\n", name);
write!(state, " {0} = w * interp_perspective.{0};\n", name);
}
}
@ -728,58 +730,6 @@ fn write_read_inputs(state: &mut OutputState, inputs: &[hir::SymRef]) {
}
write!(state, "}}\n");
}
if state.has_draw_span_rgba8 || state.has_draw_span_r8 {
write!(
state,
"ALWAYS_INLINE void step_interp_inputs(int chunks) {{\n"
);
if (used_fragcoord & 1) != 0 {
write!(state, " step_fragcoord(chunks);\n");
}
for i in inputs {
let sym = state.hir.sym(*i);
match &sym.decl {
hir::SymDecl::Global(_, _, _, run_class) => {
if *run_class != hir::RunClass::Scalar {
let name = sym.name.as_str();
write!(state, " {0} += interp_step.{0} * chunks;\n", name);
}
}
_ => panic!(),
}
}
write!(state, "}}\n");
}
}
fn write_include_file(state: &mut OutputState, include_file: String) {
let include_contents = std::fs::read_to_string(&include_file).unwrap();
let mut offset = 0;
while offset < include_contents.len() {
let s = &include_contents[offset ..];
if let Some(start_proto) = s.find("draw_span") {
let s = &s[start_proto ..];
if let Some(end_proto) = s.find(')') {
let proto = &s[.. end_proto];
if proto.contains("uint32_t") {
state.has_draw_span_rgba8 = true;
} else if proto.contains("uint8_t") {
state.has_draw_span_r8 = true;
}
offset += start_proto + end_proto;
continue;
}
}
break;
}
let include_name = std::path::Path::new(&include_file)
.file_name()
.unwrap()
.to_string_lossy();
write!(state, "\n#include \"{}\"\n\n", include_name);
}
pub struct OutputState {
@ -804,8 +754,6 @@ pub struct OutputState {
uses_discard: bool,
used_fragcoord: Cell<i32>,
use_perspective: bool,
has_draw_span_rgba8: bool,
has_draw_span_r8: bool,
used_globals: RefCell<Vec<hir::SymRef>>,
texel_fetches: RefCell<Vec<(hir::SymRef, hir::SymRef, hir::TexelFetchOffsets)>>,
}
@ -874,7 +822,7 @@ fn add_used_global(state: &OutputState, i: &hir::SymRef) {
pub fn show_sym(state: &OutputState, i: &hir::SymRef) {
let sym = state.hir.sym(*i);
match &sym.decl {
hir::SymDecl::NativeFunction(_, ref cxx_name) => {
hir::SymDecl::NativeFunction(_, ref cxx_name, _) => {
let mut name = sym.name.as_str();
if state.output_cxx {
name = cxx_name.unwrap_or(name);
@ -1567,8 +1515,10 @@ fn expr_run_class(state: &OutputState, expr: &hir::Expr) -> hir::RunClass {
});
match fun {
hir::FunIdentifier::Identifier(ref sym) => match &state.hir.sym(*sym).decl {
hir::SymDecl::NativeFunction(..) => {
if arg_mask != 0 {
hir::SymDecl::NativeFunction(_, _, ref ret_class) => {
if *ret_class != hir::RunClass::Unknown {
*ret_class
} else if arg_mask != 0 {
hir::RunClass::Vector
} else {
hir::RunClass::Scalar
@ -1896,33 +1846,21 @@ pub fn show_hir_expr_inner(state: &OutputState, expr: &hir::Expr, top_level: boo
&state.hir, &args[0], &args[1], &args[3],
) {
let base_sym = state.hir.sym(base);
if symbol_run_class(&base_sym.decl, state.vector_mask)
== hir::RunClass::Scalar
{
let sampler_sym = state.hir.sym(sampler);
add_used_global(state, &sampler);
if let hir::SymDecl::Global(..) = &base_sym.decl {
add_used_global(state, &base);
}
if y != 0 {
write!(
state,
"{}_{}_fetch[{}+{}*{}->stride]",
sampler_sym.name,
base_sym.name,
x,
y,
sampler_sym.name
);
} else {
write!(
state,
"{}_{}_fetch[{}]",
sampler_sym.name, base_sym.name, x
);
}
return;
let sampler_sym = state.hir.sym(sampler);
add_used_global(state, &sampler);
if let hir::SymDecl::Global(..) = &base_sym.decl {
add_used_global(state, &base);
}
write!(
state,
"texelFetchUnchecked({}, {}_{}_fetch, {}, {})",
sampler_sym.name,
sampler_sym.name,
base_sym.name,
x,
y,
);
return;
}
}
show_sym(state, name)
@ -2358,15 +2296,12 @@ pub fn show_declaration(state: &mut OutputState, d: &hir::Declaration) {
let base = list.head.name;
let base_sym = state.hir.sym(base);
if let hir::SymDecl::Local(..) = &base_sym.decl {
if symbol_run_class(&base_sym.decl, state.vector_mask) == hir::RunClass::Scalar
let mut texel_fetches = state.texel_fetches.borrow_mut();
while let Some(idx) = texel_fetches.iter().position(|&(_, b, _)| b == base)
{
let mut texel_fetches = state.texel_fetches.borrow_mut();
while let Some(idx) = texel_fetches.iter().position(|&(_, b, _)| b == base)
{
let (sampler, _, offsets) = texel_fetches.remove(idx);
let sampler_sym = state.hir.sym(sampler);
define_texel_fetch_ptr(state, &base_sym, &sampler_sym, &offsets);
}
let (sampler, _, offsets) = texel_fetches.remove(idx);
let sampler_sym = state.hir.sym(sampler);
define_texel_fetch_ptr(state, &base_sym, &sampler_sym, &offsets);
}
}
}
@ -2384,19 +2319,22 @@ pub fn show_declaration(state: &mut OutputState, d: &hir::Declaration) {
//state.write(";\n");
}
hir::Declaration::Global(ref qual, ref identifiers) => {
show_type_qualifier(state, &qual);
// We only want to output GLSL layout qualifiers if not C++
if !state.output_cxx {
show_type_qualifier(state, &qual);
if !identifiers.is_empty() {
let mut iter = identifiers.iter();
let first = iter.next().unwrap();
show_identifier(state, first);
if !identifiers.is_empty() {
let mut iter = identifiers.iter();
let first = iter.next().unwrap();
show_identifier(state, first);
for identifier in iter {
let _ = write!(state, ", {}", identifier);
for identifier in iter {
let _ = write!(state, ", {}", identifier);
}
}
}
state.write(";\n");
state.write(";\n");
}
}
hir::Declaration::StructDefinition(ref sym) => {
show_sym_decl(state, sym);
@ -2672,32 +2610,32 @@ fn define_texel_fetch_ptr(
offsets: &hir::TexelFetchOffsets,
) {
show_indent(state);
if let hir::SymDecl::Global(_, _, ty, _) = &sampler_sym.decl {
match ty.kind {
hir::TypeKind::Sampler2D
| hir::TypeKind::Sampler2DRect => {
write!(
state,
"vec4_scalar* {}_{}_fetch = ",
sampler_sym.name, base_sym.name
);
let ptr_type = if let hir::SymDecl::Global(_, _, ty, _) = &sampler_sym.decl {
if symbol_run_class(&base_sym.decl, state.vector_mask) == hir::RunClass::Scalar {
match ty.kind {
hir::TypeKind::Sampler2D
| hir::TypeKind::Sampler2DRect => "vec4_scalar*",
hir::TypeKind::ISampler2D => "ivec4_scalar*",
_ => panic!(),
}
hir::TypeKind::ISampler2D => {
write!(
state,
"ivec4_scalar* {}_{}_fetch = ",
sampler_sym.name, base_sym.name
);
}
_ => panic!(),
} else {
"I32"
}
} else {
panic!();
}
};
write!(
state,
"texelFetchPtr({}, {}, {}, {}, {}, {});\n",
sampler_sym.name, base_sym.name, offsets.min_x, offsets.max_x, offsets.min_y, offsets.max_y
"{} {}_{}_fetch = texelFetchPtr({}, {}, {}, {}, {}, {});\n",
ptr_type,
sampler_sym.name,
base_sym.name,
sampler_sym.name,
base_sym.name,
offsets.min_x,
offsets.max_x,
offsets.min_y,
offsets.max_y,
);
}
@ -2750,27 +2688,64 @@ pub fn show_function_definition(
}
if state.output_cxx {
match fd.prototype.name.as_str() {
"swgl_drawSpanRGBA8" |
"swgl_drawSpanR8" => {
// Partial spans are not drawn using span shaders, but rather drawn with a fragment shader
// where the span shader left off. We need to undo any changes to the interpolants made by
// the span shaders so that we can reset the interpolants to where the fragment shader
// expects them. We do this by saving them in an _Undo_ struct on entry to the span shader,
// and then restore them in the _Undo_ struct destructor.
let mut needs_undo = vec![];
for global in &fd.globals {
let sym = state.hir.sym(*global);
match &sym.decl {
hir::SymDecl::Global(hir::StorageClass::In, _, ty, hir::RunClass::Vector) => {
if needs_undo.is_empty() {
state.write("struct _Undo_ {\nSelf* self;\n");
}
show_type(state, ty);
write!(state, " {};\n", sym.name);
needs_undo.push(sym.name.clone());
}
_ => {}
}
}
if !needs_undo.is_empty() {
state.write("explicit _Undo_(Self* self) : self(self)");
for name in &needs_undo {
write!(state, ", {0}(self->{0})", name);
}
state.write(" {}\n");
state.write("~_Undo_() {\n");
for name in &needs_undo {
write!(state, "self->{0} = {0};\n", name);
}
state.write("}} _undo_(this);\n");
}
}
_ => {}
}
let mut texel_fetches = state.texel_fetches.borrow_mut();
texel_fetches.clear();
for ((sampler, base), offsets) in fd.texel_fetches.iter() {
add_used_global(state, sampler);
let sampler_sym = state.hir.sym(*sampler);
let base_sym = state.hir.sym(*base);
if symbol_run_class(&base_sym.decl, vector_mask) == hir::RunClass::Scalar {
add_used_global(state, sampler);
let sampler_sym = state.hir.sym(*sampler);
match &base_sym.decl {
hir::SymDecl::Global(..) => {
add_used_global(state, base);
define_texel_fetch_ptr(state, &base_sym, &sampler_sym, &offsets);
}
hir::SymDecl::Local(..) => {
if fd.prototype.has_parameter(*base) {
define_texel_fetch_ptr(state, &base_sym, &sampler_sym, &offsets);
} else {
texel_fetches.push((*sampler, *base, offsets.clone()));
}
}
_ => panic!(),
match &base_sym.decl {
hir::SymDecl::Global(..) => {
add_used_global(state, base);
define_texel_fetch_ptr(state, &base_sym, &sampler_sym, &offsets);
}
hir::SymDecl::Local(..) => {
if fd.prototype.has_parameter(*base) {
define_texel_fetch_ptr(state, &base_sym, &sampler_sym, &offsets);
} else {
texel_fetches.push((*sampler, *base, offsets.clone()));
}
}
_ => panic!(),
}
}
}
@ -3195,7 +3170,7 @@ pub fn show_iteration_statement(state: &mut OutputState, ist: &hir::IterationSta
show_statement(state, body);
state.write(" while (");
show_hir_expr(state, cond);
state.write(")\n");
state.write(");\n");
}
hir::IterationStatement::For(ref init, ref rest, ref body) => {
state.write("for (");
@ -3266,7 +3241,7 @@ pub fn show_jump_statement(state: &mut OutputState, j: &hir::JumpStatement) {
if state.output_cxx {
state.uses_discard = true;
if let Some(mask) = &state.mask {
state.write("isPixelDiscarded |= (");
state.write("swgl_IsPixelDiscarded |= (");
show_hir_expr(state, mask);
state.write(")");
if state.return_declared {
@ -3274,7 +3249,7 @@ pub fn show_jump_statement(state: &mut OutputState, j: &hir::JumpStatement) {
}
state.write(";\n");
} else {
state.write("isPixelDiscarded = true;\n");
state.write("swgl_IsPixelDiscarded = true;\n");
}
} else {
state.write("discard;\n");
@ -3579,9 +3554,11 @@ pub fn show_translation_unit(state: &mut OutputState, tu: &hir::TranslationUnit)
state.flush_buffer();
}
if state.output_cxx {
if let Some(name) = state.hir.lookup("main") {
show_cxx_function_definition(state, name, 0);
state.flush_buffer();
for name in &["main", "swgl_drawSpanRGBA8", "swgl_drawSpanR8"] {
if let Some(sym) = state.hir.lookup(name) {
show_cxx_function_definition(state, sym, 0);
state.flush_buffer();
}
}
}
}
@ -3591,37 +3568,33 @@ fn write_abi(state: &mut OutputState) {
ShaderKind::Fragment => {
state.write("static void run(Self *self) {\n");
if state.uses_discard {
state.write(" self->isPixelDiscarded = false;\n");
state.write(" self->swgl_IsPixelDiscarded = false;\n");
}
state.write(" self->main();\n");
state.write(" self->step_interp_inputs();\n");
state.write("}\n");
state.write("static void skip(Self* self, int chunks) {\n");
state.write(" self->step_interp_inputs();\n");
state.write(" while (--chunks > 0) self->step_interp_inputs();\n");
state.write("static void skip(Self* self, int steps) {\n");
state.write(" self->step_interp_inputs(steps);\n");
state.write("}\n");
if state.use_perspective {
state.write("static void run_perspective(Self *self) {\n");
if state.uses_discard {
state.write(" self->isPixelDiscarded = false;\n");
state.write(" self->swgl_IsPixelDiscarded = false;\n");
}
state.write(" self->main();\n");
state.write(" self->step_perspective_inputs();\n");
state.write("}\n");
state.write("static void skip_perspective(Self* self, int chunks) {\n");
state.write(" self->step_perspective_inputs();\n");
state.write(" while (--chunks > 0) self->step_perspective_inputs();\n");
state.write("static void skip_perspective(Self* self, int steps) {\n");
state.write(" self->step_perspective_inputs(steps);\n");
state.write("}\n");
}
if state.has_draw_span_rgba8 {
if state.hir.lookup("swgl_drawSpanRGBA8").is_some() {
state.write(
"static void draw_span_RGBA8(Self* self, uint32_t* buf, int len) { \
DISPATCH_DRAW_SPAN(self, buf, len); }\n");
"static int draw_span_RGBA8(Self* self) { DISPATCH_DRAW_SPAN(self, RGBA8); }\n");
}
if state.has_draw_span_r8 {
if state.hir.lookup("swgl_drawSpanR8").is_some() {
state.write(
"static void draw_span_R8(Self* self, uint8_t* buf, int len) { \
DISPATCH_DRAW_SPAN(self, buf, len); }\n");
"static int draw_span_R8(Self* self) { DISPATCH_DRAW_SPAN(self, R8); }\n");
}
write!(state, "public:\n{}_frag() {{\n", state.name);
@ -3643,10 +3616,10 @@ fn write_abi(state: &mut OutputState) {
state.write(" init_span_func = (InitSpanFunc)&read_interp_inputs;\n");
state.write(" run_func = (RunFunc)&run;\n");
state.write(" skip_func = (SkipFunc)&skip;\n");
if state.has_draw_span_rgba8 {
if state.hir.lookup("swgl_drawSpanRGBA8").is_some() {
state.write(" draw_span_RGBA8_func = (DrawSpanRGBA8Func)&draw_span_RGBA8;\n");
}
if state.has_draw_span_r8 {
if state.hir.lookup("swgl_drawSpanR8").is_some() {
state.write(" draw_span_R8_func = (DrawSpanR8Func)&draw_span_R8;\n");
}
if state.uses_discard {
@ -3670,6 +3643,9 @@ fn write_abi(state: &mut OutputState) {
state.write(" init_batch_func = (InitBatchFunc)&init_batch;\n");
state.write(" load_attribs_func = (LoadAttribsFunc)&load_attribs;\n");
state.write(" run_primitive_func = (RunPrimitiveFunc)&run;\n");
if state.hir.used_clip_dist != 0 {
state.write(" enable_clip_distance();\n");
}
}
}
state.write("}\n");