From 8a83ca4e9afc9e3c08b4e8c33a74392f9b3154d7 Mon Sep 17 00:00:00 2001 From: Brian Osman Date: Mon, 12 Feb 2018 14:32:17 -0500 Subject: 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 Reviewed-by: Ethan Nicholas Commit-Queue: Brian Osman --- include/gpu/GrContext.h | 1 + include/gpu/GrContextOptions.h | 7 ++++ src/gpu/GrContext.cpp | 1 + src/gpu/GrContextPriv.h | 1 + src/gpu/gl/builders/GrGLProgramBuilder.cpp | 1 + src/gpu/vk/GrVkPipelineStateBuilder.cpp | 3 ++ src/sksl/SkSLGLSLCodeGenerator.cpp | 8 +++++ src/sksl/SkSLSPIRVCodeGenerator.cpp | 12 +++++-- src/sksl/ir/SkSLProgram.h | 2 ++ tests/SkSLGLSLTest.cpp | 56 ++++++++++++++++++++++++++++++ 10 files changed, 90 insertions(+), 2 deletions(-) diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h index aac0d8dc5a..4d937c727e 100644 --- a/include/gpu/GrContext.h +++ b/include/gpu/GrContext.h @@ -370,6 +370,7 @@ private: std::unique_ptr 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 338c538963..84863f50a6 100644 --- a/include/gpu/GrContextOptions.h +++ b/include/gpu/GrContextOptions.h @@ -134,6 +134,13 @@ struct GrContextOptions { */ 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 8a5417ed17..09985e6f79 100644 --- a/src/gpu/GrContext.cpp +++ b/src/gpu/GrContext.cpp @@ -255,6 +255,7 @@ bool GrContext::init(const GrContextOptions& options) { 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 e0074252d8..93c1c17dc9 100644 --- a/src/gpu/GrContextPriv.h +++ b/src/gpu/GrContextPriv.h @@ -67,6 +67,7 @@ public: 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 7ae63a262c..ed6386b7f3 100644 --- a/src/gpu/gl/builders/GrGLProgramBuilder.cpp +++ b/src/gpu/gl/builders/GrGLProgramBuilder.cpp @@ -152,6 +152,7 @@ GrGLProgram* GrGLProgramBuilder::finalize() { 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 shadersToDelete; bool cached = nullptr != fCached.get(); diff --git a/src/gpu/vk/GrVkPipelineStateBuilder.cpp b/src/gpu/vk/GrVkPipelineStateBuilder.cpp index 9b9b070073..a263b776d1 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 @@ GrVkPipelineState* GrVkPipelineStateBuilder::finalize(const GrStencilSettings& s 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 d02948f388..f94cc4ca0b 100644 --- a/src/sksl/SkSLGLSLCodeGenerator.cpp +++ b/src/sksl/SkSLGLSLCodeGenerator.cpp @@ -505,12 +505,14 @@ void GLSLCodeGenerator::writeFunctionCall(const FunctionCall& c) { 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 @@ void GLSLCodeGenerator::writeFunctionCall(const FunctionCall& c) { break; case SpvDim2D: dim = "2D"; + isTextureFunctionWithBias = true; if (c.fArguments[1]->fType == *fContext.fFloat2_Type) { proj = false; } else { @@ -529,6 +532,7 @@ void GLSLCodeGenerator::writeFunctionCall(const FunctionCall& c) { break; case SpvDim3D: dim = "3D"; + isTextureFunctionWithBias = true; if (c.fArguments[1]->fType == *fContext.fFloat3_Type) { proj = false; } else { @@ -538,6 +542,7 @@ void GLSLCodeGenerator::writeFunctionCall(const FunctionCall& c) { break; case SpvDimCube: dim = "Cube"; + isTextureFunctionWithBias = true; proj = false; break; case SpvDimRect: @@ -573,6 +578,9 @@ void GLSLCodeGenerator::writeFunctionCall(const FunctionCall& c) { 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 cb8b388521..d01a82dae2 100644 --- a/src/sksl/SkSLSPIRVCodeGenerator.cpp +++ b/src/sksl/SkSLSPIRVCodeGenerator.cpp @@ -824,8 +824,16 @@ SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIn 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 3184547476..a63cd237ac 100644 --- a/src/sksl/ir/SkSLProgram.h +++ b/src/sksl/ir/SkSLProgram.h @@ -76,6 +76,8 @@ struct Program { 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 fArgs; }; diff --git a/tests/SkSLGLSLTest.cpp b/tests/SkSLGLSLTest.cpp index 62c5cd6955..e63719781d 100644 --- a/tests/SkSLGLSLTest.cpp +++ b/tests/SkSLGLSLTest.cpp @@ -983,6 +983,62 @@ DEF_TEST(SkSLTexture, r) { "}\n"); } +DEF_TEST(SkSLSharpen, r) { + SkSL::Program::Settings settings; + settings.fSharpenTextures = true; + sk_sp 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 {" -- cgit v1.2.3