Skip to content

Commit

Permalink
Add textureSampleBaseClampToEdge
Browse files Browse the repository at this point in the history
  • Loading branch information
evahop committed Oct 4, 2023
1 parent 6668d06 commit 0e1dd25
Show file tree
Hide file tree
Showing 19 changed files with 540 additions and 187 deletions.
5 changes: 3 additions & 2 deletions src/back/dot/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,8 +460,9 @@ fn write_function_expressions(
edges.insert("array_index", expr);
}
match level {
crate::SampleLevel::Auto => {}
crate::SampleLevel::Zero => {}
crate::SampleLevel::Auto
| crate::SampleLevel::Zero
| crate::SampleLevel::Base => {}
crate::SampleLevel::Exact(expr) => {
edges.insert("level", expr);
}
Expand Down
98 changes: 80 additions & 18 deletions src/back/glsl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ pub const SUPPORTED_ES_VERSIONS: &[u16] = &[300, 310, 320];
/// of detail for bounds checking in `ImageLoad`
const CLAMPED_LOD_SUFFIX: &str = "_clamped_lod";

/// The suffix of the variable that will hold the calculated half-texel
/// for use with `textureSampleBaseClampToEdge`
const HALF_TEXEL_SUFFIX: &str = "_half_texel";

pub(crate) const MODF_FUNCTION: &str = "naga_modf";
pub(crate) const FREXP_FUNCTION: &str = "naga_frexp";

Expand Down Expand Up @@ -1855,6 +1859,17 @@ impl<'a, W: Write> Writer<'a, W> {
}
}

// If we are going to write a `textureSampleBaseClampToEdge` next,
// precompute the half-texel before clamping the coordinates.
if let crate::Expression::ImageSample {
level: crate::SampleLevel::Base,
image,
..
} = ctx.expressions[handle]
{
self.write_half_texel(ctx, handle, image, level)?
}

if let Some(name) = expr_name {
write!(self.out, "{level}")?;
self.write_named_expr(handle, name, handle, ctx)?;
Expand Down Expand Up @@ -2482,6 +2497,11 @@ impl<'a, W: Write> Writer<'a, W> {
)))
}
crate::SampleLevel::Auto => {}
crate::SampleLevel::Base => {
unreachable!(
"textureSampleBaseClampToEdge should not have passed validation"
)
}
}
}

Expand All @@ -2508,6 +2528,7 @@ impl<'a, W: Write> Writer<'a, W> {
}
}
crate::SampleLevel::Gradient { .. } => "textureGrad",
crate::SampleLevel::Base => "textureLod",
};
let offset_name = match offset {
Some(_) => "Offset",
Expand Down Expand Up @@ -2539,24 +2560,38 @@ impl<'a, W: Write> Writer<'a, W> {

let tex_1d_hack = dim == crate::ImageDimension::D1 && self.options.version.is_es();
let is_vec = tex_1d_hack || coord_dim != 1;
// Compose a new texture coordinates vector
if is_vec {
write!(self.out, "vec{}(", coord_dim + tex_1d_hack as u8)?;
}
self.write_expr(coordinate, ctx)?;
if tex_1d_hack {
write!(self.out, ", 0.0")?;
}
if let Some(expr) = array_index {
write!(self.out, ", ")?;
self.write_expr(expr, ctx)?;
}
if merge_depth_ref {
write!(self.out, ", ")?;
self.write_expr(depth_ref.unwrap(), ctx)?;
}
if is_vec {
write!(self.out, ")")?;

if level == crate::SampleLevel::Base {
// clamp the coordinates to [ half_texel, 1 - half_texel ]
write!(self.out, "clamp(")?;
self.write_expr(coordinate, ctx)?;
write!(
self.out,
", {}{}{}, vec2(1.0) - {0}{1}{2})",
back::BAKE_PREFIX,
expr.index(),
HALF_TEXEL_SUFFIX
)?
} else {
// Compose a new texture coordinates vector
if is_vec {
write!(self.out, "vec{}(", coord_dim + tex_1d_hack as u8)?;
}
self.write_expr(coordinate, ctx)?;
if tex_1d_hack {
write!(self.out, ", 0.0")?;
}
if let Some(expr) = array_index {
write!(self.out, ", ")?;
self.write_expr(expr, ctx)?;
}
if merge_depth_ref {
write!(self.out, ", ")?;
self.write_expr(depth_ref.unwrap(), ctx)?;
}
if is_vec {
write!(self.out, ")")?;
}
}

if let (Some(expr), false) = (depth_ref, merge_depth_ref) {
Expand Down Expand Up @@ -2609,6 +2644,9 @@ impl<'a, W: Write> Writer<'a, W> {
self.write_expr(y, ctx)?;
}
}
crate::SampleLevel::Base => {
write!(self.out, ", 0.0")?;
}
}

if let Some(constant) = offset {
Expand Down Expand Up @@ -3471,6 +3509,30 @@ impl<'a, W: Write> Writer<'a, W> {
Ok(())
}

/// Helper function to write the local holding the half-texel
/// for use with `textureSampleBaseClampToEdge`
fn write_half_texel(
&mut self,
ctx: &back::FunctionCtx,
expr: Handle<crate::Expression>,
image: Handle<crate::Expression>,
level: back::Level,
) -> Result<(), Error> {
write!(
self.out,
"{level}vec2 {}{}{} = vec2(0.5) / vec2(textureSize(",
back::BAKE_PREFIX,
expr.index(),
HALF_TEXEL_SUFFIX,
)?;

self.write_expr(image, ctx)?;

writeln!(self.out, ", 0));")?;

Ok(())
}

// Helper method used to retrieve how many elements a coordinate vector
// for the images operations need.
fn get_coordinate_vector_size(&self, dim: crate::ImageDimension, arrayed: bool) -> u8 {
Expand Down
81 changes: 72 additions & 9 deletions src/back/hlsl/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ const SPECIAL_BASE_VERTEX: &str = "base_vertex";
const SPECIAL_BASE_INSTANCE: &str = "base_instance";
const SPECIAL_OTHER: &str = "other";

/// The suffix of the variable that will hold the calculated half-texel
/// for use with `textureSampleBaseClampToEdge`
const HALF_TEXEL_SUFFIX: &str = "_half_texel";

pub(crate) const MODF_FUNCTION: &str = "naga_modf";
pub(crate) const FREXP_FUNCTION: &str = "naga_frexp";

Expand Down Expand Up @@ -1339,6 +1343,17 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
None
};

// If we are going to write a `textureSampleBaseClampToEdge` next,
// precompute the half-texel before clamping the coordinates.
if let crate::Expression::ImageSample {
level: crate::SampleLevel::Base,
image,
..
} = func_ctx.expressions[handle]
{
self.write_half_texel(module, handle, image, level, func_ctx)?
}

if let Some(name) = expr_name {
write!(self.out, "{level}")?;
self.write_named_expr(module, handle, name, handle, func_ctx)?;
Expand Down Expand Up @@ -2380,7 +2395,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
let level_str = match level {
Sl::Zero if gather.is_none() => "LevelZero",
Sl::Auto | Sl::Zero => "",
Sl::Exact(_) => "Level",
Sl::Exact(_) | Sl::Base => "Level",
Sl::Bias(_) => "Bias",
Sl::Gradient { .. } => "Grad",
};
Expand All @@ -2389,14 +2404,26 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
write!(self.out, ".{base_str}{cmp_str}{component_str}{level_str}(")?;
self.write_expr(module, sampler, func_ctx)?;
write!(self.out, ", ")?;
self.write_texture_coordinates(
"float",
coordinate,
array_index,
None,
module,
func_ctx,
)?;
if level == Sl::Base {
// clamp the coordinates to [ half_texel, 1 - half_texel ]
write!(self.out, "clamp(")?;
self.write_expr(module, coordinate, func_ctx)?;
write!(
self.out,
", _expr{}{}, (1.0).xx - _expr{0}{1})",
expr.index(),
HALF_TEXEL_SUFFIX
)?
} else {
self.write_texture_coordinates(
"float",
coordinate,
array_index,
None,
module,
func_ctx,
)?;
}

if let Some(depth_ref) = depth_ref {
write!(self.out, ", ")?;
Expand All @@ -2419,6 +2446,9 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
write!(self.out, ", ")?;
self.write_expr(module, y, func_ctx)?;
}
Sl::Base => {
write!(self.out, ", 0.0")?;
}
}

if let Some(offset) = offset {
Expand Down Expand Up @@ -3256,6 +3286,39 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
}
Ok(())
}

/// Helper function to write the locals holding the half-texel
/// for use with `textureSampleBaseClampToEdge`
fn write_half_texel(
&mut self,
module: &Module,
expr: Handle<crate::Expression>,
image: Handle<crate::Expression>,
level: back::Level,
func_ctx: &back::FunctionCtx,
) -> Result<(), Error> {
let prefix = format!("_expr{}", expr.index());

writeln!(self.out, "{level}float2 {prefix}_dim;")?;

// will not be used, but required for the method call
writeln!(self.out, "{level}float {prefix}_num;")?;

write!(self.out, "{level}")?;
self.write_expr(module, image, func_ctx)?;

writeln!(
self.out,
".GetDimensions(0u, {prefix}_dim.x, {prefix}_dim.y, {prefix}_num);"
)?;

writeln!(
self.out,
"{level}float2 {prefix}{HALF_TEXEL_SUFFIX} = (0.5).xx / {prefix}_dim;"
)?;

Ok(())
}
}

pub(super) struct MatrixType {
Expand Down
65 changes: 64 additions & 1 deletion src/back/msl/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ const RAY_QUERY_FUN_MAP_INTERSECTION: &str = "_map_intersection_type";
pub(crate) const MODF_FUNCTION: &str = "naga_modf";
pub(crate) const FREXP_FUNCTION: &str = "naga_frexp";

/// The suffix of the variable that will hold the calculated half-texel
/// for use with `textureSampleBaseClampToEdge`
const HALF_TEXEL_SUFFIX: &str = "_half_texel";

/// Write the Metal name for a Naga numeric type: scalar, vector, or matrix.
///
/// The `sizes` slice determines whether this function writes a
Expand Down Expand Up @@ -766,6 +770,9 @@ impl<W: Write> Writer<W> {
self.put_expression(y, context, true)?;
write!(self.out, ")")?;
}
crate::SampleLevel::Base => {
write!(self.out, ", {NAMESPACE}::level(0.0)")?;
}
}
Ok(())
}
Expand Down Expand Up @@ -1455,7 +1462,22 @@ impl<W: Write> Writer<W> {
write!(self.out, ".{main_op}{comparison_op}(")?;
self.put_expression(sampler, context, true)?;
write!(self.out, ", ")?;
self.put_expression(coordinate, context, true)?;

if level == crate::SampleLevel::Base {
// clamp the coordinates to [ half_texel, 1 - half_texel ]
write!(self.out, "{NAMESPACE}::clamp(")?;
self.put_expression(coordinate, context, true)?;
write!(
self.out,
", {}{}{}, {NAMESPACE}::float2(1.0) - {0}{1}{2})",
back::BAKE_PREFIX,
expr_handle.index(),
HALF_TEXEL_SUFFIX
)?
} else {
self.put_expression(coordinate, context, true)?;
}

if let Some(expr) = array_index {
write!(self.out, ", ")?;
self.put_expression(expr, context, true)?;
Expand Down Expand Up @@ -2628,6 +2650,17 @@ impl<W: Write> Writer<W> {
}
};

// If we are going to write a `textureSampleBaseClampToEdge` next,
// precompute the half-texel before clamping the coordinates.
if let crate::Expression::ImageSample {
level: crate::SampleLevel::Base,
image,
..
} = context.expression.function.expressions[handle]
{
self.write_half_texel(handle, image, level, &context.expression)?
}

if let Some(name) = expr_name {
write!(self.out, "{level}")?;
self.start_baking_expression(handle, &context.expression, &name)?;
Expand Down Expand Up @@ -4166,6 +4199,36 @@ impl<W: Write> Writer<W> {
}
Ok(())
}

/// Helper function to write the locals holding the half-texel
/// for use with `textureSampleBaseClampToEdge`
fn write_half_texel(
&mut self,
expr_handle: Handle<crate::Expression>,
image: Handle<crate::Expression>,
level: back::Level,
context: &ExpressionContext,
) -> Result<(), Error> {
let prefix = format!("{}{}", back::BAKE_PREFIX, expr_handle.index());

write!(
self.out,
"{level}{NAMESPACE}::float2 {prefix}_dim = {NAMESPACE}::float2("
)?;

self.put_expression(image, context, true)?;
write!(self.out, ".get_width(), ")?;

self.put_expression(image, context, true)?;
writeln!(self.out, ".get_height());")?;

writeln!(
self.out,
"{level}{NAMESPACE}::float2 {prefix}{HALF_TEXEL_SUFFIX} = {NAMESPACE}::float2(0.5) / {prefix}_dim;"
)?;

Ok(())
}
}

/// Initializing workgroup variables is more tricky for Metal because we have to deal
Expand Down
Loading

0 comments on commit 0e1dd25

Please sign in to comment.