Add "sharpen" option to SkSL, to LOD bias all textures
This adds a fixed bias (-0.5) to the computed LOD of all
mip-mapped texture fetches. (Technically, to all texture
fetches, but that only matters for mip-mapped ones).
Clients can opt-in with a new GrContextOption.
Bug: skia:7541
Bug: chromium:562162
Change-Id: Ie3cd0679c4ab66f62d2dc32e7e68e5c99355115e
Reviewed-on: https://skia-review.googlesource.com/106322
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index aac0d8d..4d937c7 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -370,6 +370,7 @@
std::unique_ptr<GrTextBlobCache> fTextBlobCache;
bool fDisableGpuYUVConversion;
+ bool fSharpenMipmappedTextures;
bool fDidTestPMConversions;
// true if the PM/UPM conversion succeeded; false otherwise
bool fPMUPMConversionsRoundTrip;
diff --git a/include/gpu/GrContextOptions.h b/include/gpu/GrContextOptions.h
index 338c538..84863f5 100644
--- a/include/gpu/GrContextOptions.h
+++ b/include/gpu/GrContextOptions.h
@@ -135,6 +135,13 @@
bool fAvoidStencilBuffers = false;
/**
+ * If true, texture fetches from mip-mapped textures will be biased to read larger MIP levels.
+ * This has the effect of sharpening those textures, at the cost of some aliasing, and possible
+ * performance impact.
+ */
+ bool fSharpenMipmappedTextures = false;
+
+ /**
* Enables driver workaround to use draws instead of glClear. This only applies to
* kOpenGL_GrBackend.
*/
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 8a5417e..09985e6 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -255,6 +255,7 @@
options));
fDisableGpuYUVConversion = options.fDisableGpuYUVConversion;
+ fSharpenMipmappedTextures = options.fSharpenMipmappedTextures;
fDidTestPMConversions = false;
GrPathRendererChain::Options prcOptions;
diff --git a/src/gpu/GrContextPriv.h b/src/gpu/GrContextPriv.h
index e007425..93c1c17 100644
--- a/src/gpu/GrContextPriv.h
+++ b/src/gpu/GrContextPriv.h
@@ -67,6 +67,7 @@
const SkSurfaceProps* = nullptr);
bool disableGpuYUVConversion() const { return fContext->fDisableGpuYUVConversion; }
+ bool sharpenMipmappedTextures() const { return fContext->fSharpenMipmappedTextures; }
/**
* Call to ensure all drawing to the context has been issued to the
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.cpp b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
index 7ae63a2..ed6386b 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
@@ -152,6 +152,7 @@
SkSL::Program::Settings settings;
settings.fCaps = this->gpu()->glCaps().shaderCaps();
settings.fFlipY = this->pipeline().proxy()->origin() != kTopLeft_GrSurfaceOrigin;
+ settings.fSharpenTextures = this->gpu()->getContext()->contextPriv().sharpenMipmappedTextures();
SkSL::Program::Inputs inputs;
SkTDArray<GrGLuint> shadersToDelete;
bool cached = nullptr != fCached.get();
diff --git a/src/gpu/vk/GrVkPipelineStateBuilder.cpp b/src/gpu/vk/GrVkPipelineStateBuilder.cpp
index 9b9b070..a263b77 100644
--- a/src/gpu/vk/GrVkPipelineStateBuilder.cpp
+++ b/src/gpu/vk/GrVkPipelineStateBuilder.cpp
@@ -7,6 +7,8 @@
#include "vk/GrVkPipelineStateBuilder.h"
+#include "GrContext.h"
+#include "GrContextPriv.h"
#include "GrShaderCaps.h"
#include "vk/GrVkDescriptorSetManager.h"
#include "vk/GrVkGpu.h"
@@ -141,6 +143,7 @@
SkSL::Program::Settings settings;
settings.fCaps = this->caps()->shaderCaps();
settings.fFlipY = this->pipeline().proxy()->origin() != kTopLeft_GrSurfaceOrigin;
+ settings.fSharpenTextures = this->gpu()->getContext()->contextPriv().sharpenMipmappedTextures();
SkAssertResult(this->createVkShaderModule(VK_SHADER_STAGE_VERTEX_BIT,
fVS,
&vertShaderModule,
diff --git a/src/sksl/SkSLGLSLCodeGenerator.cpp b/src/sksl/SkSLGLSLCodeGenerator.cpp
index d02948f..f94cc4c 100644
--- a/src/sksl/SkSLGLSLCodeGenerator.cpp
+++ b/src/sksl/SkSLGLSLCodeGenerator.cpp
@@ -505,12 +505,14 @@
fHeader.writeText(" : require\n");
fFoundDerivatives = true;
}
+ bool isTextureFunctionWithBias = false;
if (c.fFunction.fName == "texture" && c.fFunction.fBuiltin) {
const char* dim = "";
bool proj = false;
switch (c.fArguments[0]->fType.dimensions()) {
case SpvDim1D:
dim = "1D";
+ isTextureFunctionWithBias = true;
if (c.fArguments[1]->fType == *fContext.fFloat_Type) {
proj = false;
} else {
@@ -520,6 +522,7 @@
break;
case SpvDim2D:
dim = "2D";
+ isTextureFunctionWithBias = true;
if (c.fArguments[1]->fType == *fContext.fFloat2_Type) {
proj = false;
} else {
@@ -529,6 +532,7 @@
break;
case SpvDim3D:
dim = "3D";
+ isTextureFunctionWithBias = true;
if (c.fArguments[1]->fType == *fContext.fFloat3_Type) {
proj = false;
} else {
@@ -538,6 +542,7 @@
break;
case SpvDimCube:
dim = "Cube";
+ isTextureFunctionWithBias = true;
proj = false;
break;
case SpvDimRect:
@@ -573,6 +578,9 @@
separator = ", ";
this->writeExpression(*arg, kSequence_Precedence);
}
+ if (fProgram.fSettings.fSharpenTextures && isTextureFunctionWithBias) {
+ this->write(", -0.5");
+ }
this->write(")");
}
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.cpp b/src/sksl/SkSLSPIRVCodeGenerator.cpp
index cb8b388..d01a82d 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.cpp
+++ b/src/sksl/SkSLSPIRVCodeGenerator.cpp
@@ -824,8 +824,16 @@
out);
} else {
ASSERT(c.fArguments.size() == 2);
- this->writeInstruction(op, type, result, sampler, uv,
- out);
+ if (fProgram.fSettings.fSharpenTextures) {
+ FloatLiteral lodBias(fContext, -1, -0.5);
+ this->writeInstruction(op, type, result, sampler, uv,
+ SpvImageOperandsBiasMask,
+ this->writeFloatLiteral(lodBias),
+ out);
+ } else {
+ this->writeInstruction(op, type, result, sampler, uv,
+ out);
+ }
}
break;
}
diff --git a/src/sksl/ir/SkSLProgram.h b/src/sksl/ir/SkSLProgram.h
index 3184547..a63cd23 100644
--- a/src/sksl/ir/SkSLProgram.h
+++ b/src/sksl/ir/SkSLProgram.h
@@ -76,6 +76,8 @@
bool fReplaceSettings = true;
// if true, all halfs are forced to be floats
bool fForceHighPrecision = false;
+ // if true, add -0.5 bias to LOD of all texture lookups
+ bool fSharpenTextures = false;
std::unordered_map<String, Value> fArgs;
};
diff --git a/tests/SkSLGLSLTest.cpp b/tests/SkSLGLSLTest.cpp
index 62c5cd6..e637197 100644
--- a/tests/SkSLGLSLTest.cpp
+++ b/tests/SkSLGLSLTest.cpp
@@ -983,6 +983,62 @@
"}\n");
}
+DEF_TEST(SkSLSharpen, r) {
+ SkSL::Program::Settings settings;
+ settings.fSharpenTextures = true;
+ sk_sp<GrShaderCaps> caps = SkSL::ShaderCapsFactory::Default();
+ settings.fCaps = caps.get();
+ SkSL::Program::Inputs inputs;
+ test(r,
+ "uniform sampler1D one;"
+ "uniform sampler2D two;"
+ "void main() {"
+ "float4 a = texture(one, 0);"
+ "float4 b = texture(two, float2(0));"
+ "float4 c = texture(one, float2(0));"
+ "float4 d = texture(two, float3(0));"
+ "sk_FragColor = half4(a.x, b.x, c.x, d.x);"
+ "}",
+ settings,
+ "#version 400\n"
+ "out vec4 sk_FragColor;\n"
+ "uniform sampler1D one;\n"
+ "uniform sampler2D two;\n"
+ "void main() {\n"
+ " vec4 a = texture(one, 0.0, -0.5);\n"
+ " vec4 b = texture(two, vec2(0.0), -0.5);\n"
+ " vec4 c = textureProj(one, vec2(0.0), -0.5);\n"
+ " vec4 d = textureProj(two, vec3(0.0), -0.5);\n"
+ " sk_FragColor = vec4(a.x, b.x, c.x, d.x);\n"
+ "}\n",
+ &inputs);
+
+ caps = SkSL::ShaderCapsFactory::Version110();
+ settings.fCaps = caps.get();
+ test(r,
+ "uniform sampler1D one;"
+ "uniform sampler2D two;"
+ "void main() {"
+ "float4 a = texture(one, 0);"
+ "float4 b = texture(two, float2(0));"
+ "float4 c = texture(one, float2(0));"
+ "float4 d = texture(two, float3(0));"
+ "sk_FragColor = half4(a.x, b.x, c.x, d.x);"
+ "}",
+ settings,
+ "#version 110\n"
+ "uniform sampler1D one;\n"
+ "uniform sampler2D two;\n"
+ "void main() {\n"
+ " vec4 a = texture1D(one, 0.0, -0.5);\n"
+ " vec4 b = texture2D(two, vec2(0.0), -0.5);\n"
+ " vec4 c = texture1DProj(one, vec2(0.0), -0.5);\n"
+ " vec4 d = texture2DProj(two, vec3(0.0), -0.5);\n"
+ " gl_FragColor = vec4(a.x, b.x, c.x, d.x);\n"
+ "}\n",
+ &inputs);
+}
+
DEF_TEST(SkSLOffset, r) {
test(r,
"struct Test {"