Reland "Don't key progams/pipelines on origin.""

Reland works around Adreno issue with this formulation of sk_Clockwise:
 (sk_RTFlip.y < 0.0 ? !gl_FrontFacing : gl_FrontFacing)

and instead adds this to the top of the function:
 bool sk_Clockwise = gl_FrontFacing;
 if (sk_RTFlip.y < 0.0) {
      sk_Clockwise = !sk_Clockwise;
 }

Original description:

SkSL language features that are origin sensitive now use a uniform
to conditionally flip their result rather than generating different
code.

Previously we would insert a "rt height" uniform if sk_FragCoord needed
to be flipped. sk_FragCoord,y was implemented as "realFragCoord.y" or
"rtHeight - realFragCoord.y" depending on SkSL::ProgramSettings::fFlipY.

Now we instead use a two component vector rtFlip and sk_FragCoord.y is
always "rtFlip.x + rtFlip.y*realFragCoord.y". We configure rtFlip as
either (0, 1) or (rtHeight, -1). sk_Clockwise and dFdy simiarly use
rtFlip.y to emit code that always works with either origin.

Bug: skia:12037
Change-Id: I3a2ad6f5667eb4dcd823b939abd5698f89b58929
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/425178
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
This commit is contained in:
Brian Salomon 2021-07-07 09:41:17 -04:00 committed by Skia Commit-Bot
parent 550de7f872
commit d8d85b9b89
66 changed files with 658 additions and 557 deletions

View File

@ -362,9 +362,7 @@ sksl_shared_tests = [
"/sksl/shared/Enum.sksl",
"/sksl/shared/ForLoopControlFlow.sksl",
"/sksl/shared/ForLoopMultipleInit.sksl",
"/sksl/shared/FragCoordsFlipY.sksl",
"/sksl/shared/FragCoordsNew.sksl",
"/sksl/shared/FragCoordsOld.sksl",
"/sksl/shared/FragCoords.sksl",
"/sksl/shared/FunctionArgTypeMatch.sksl",
"/sksl/shared/FunctionReturnTypeMatch.sksl",
"/sksl/shared/FunctionPrototype.sksl",
@ -556,7 +554,6 @@ sksl_settings_tests = [
"/sksl/inliner/ExponentialGrowth.sksl",
"/sksl/inliner/InlinerCanBeDisabled.sksl",
"/sksl/shared/Derivatives.sksl",
"/sksl/shared/DerivativesFlipY.sksl",
"/sksl/workarounds/AbsInt.sksl",
"/sksl/workarounds/BlendGuardedDivide.sksl",
"/sksl/workarounds/BlendModesAllZeroVec.sksl",

View File

@ -15,6 +15,7 @@
namespace SkSL {
class IRGenerator;
class SPIRVCodeGenerator;
class Variable;
enum class VariableStorage : int8_t;
@ -165,6 +166,7 @@ private:
friend class DSLFunction;
friend class DSLWriter;
friend class ::SkSL::IRGenerator;
friend class ::SkSL::SPIRVCodeGenerator;
};
} // namespace dsl

View File

@ -1,6 +0,0 @@
/*#pragma settings FlipY*/
void main() {
sk_FragColor.r = half(dFdx(1)),
sk_FragColor.g = half(dFdy(1));
}

View File

@ -1,3 +1 @@
/*#pragma settings Default FlipY*/
void main() { sk_FragColor.xy = half2(sk_FragCoord.xy); }

View File

@ -1,3 +0,0 @@
/*#pragma settings FragCoordsNew FlipY*/
void main() { sk_FragColor.xy = half2(sk_FragCoord.xy); }

View File

@ -1,3 +0,0 @@
/*#pragma settings FragCoordsOld FlipY*/
void main() { sk_FragColor.xy = half2(sk_FragCoord.xy); }

View File

@ -13,14 +13,14 @@
namespace GrPersistentCacheUtils {
static constexpr int kCurrentVersion = 6;
static constexpr int kCurrentVersion = 7;
int GetCurrentVersion() {
// The persistent cache stores a copy of the SkSL::Program::Inputs struct. If you alter the
// Program::Inputs struct in any way, you must increment kCurrentVersion to invalidate the
// outdated persistent cache files. The KnownSkSLProgramInputs struct must also be updated to
// match the new contents of Program::Inputs.
struct KnownSkSLProgramInputs { bool height, flipY, usesYDeriv; };
struct KnownSkSLProgramInputs { bool useRTFlipUniform; };
static_assert(sizeof(SkSL::Program::Inputs) == sizeof(KnownSkSLProgramInputs));
return kCurrentVersion;
@ -46,7 +46,6 @@ sk_sp<SkData> PackCachedShaders(SkFourByteTag shaderType,
if (meta) {
writer.writeBool(SkToBool(meta->fSettings));
if (meta->fSettings) {
writer.writeBool(meta->fSettings->fFlipY);
writer.writeBool(meta->fSettings->fFragColorIsInOut);
writer.writeBool(meta->fSettings->fForceHighPrecision);
writer.writeBool(meta->fSettings->fUsePushConstants);
@ -97,7 +96,6 @@ bool UnpackCachedShaders(SkReadBuffer* reader,
SkASSERT(meta->fSettings != nullptr);
if (reader->readBool()) {
meta->fSettings->fFlipY = reader->readBool();
meta->fSettings->fFragColorIsInOut = reader->readBool();
meta->fSettings->fForceHighPrecision = reader->readBool();
meta->fSettings->fUsePushConstants = reader->readBool();

View File

@ -154,9 +154,6 @@ static void gen_key(GrProcessorKeyBuilder* b,
gen_xp_key(pipeline.getXferProcessor(), caps, pipeline, b);
b->addBits(16, pipeline.writeSwizzle().asKey(), "writeSwizzle");
// If we knew the shader won't depend on origin, we could skip this (and use the same program
// for both origins). Instrumenting all fragment processors would be difficult and error prone.
b->addBits(2, GrGLSLFragmentShaderBuilder::KeyForSurfaceOrigin(programInfo.origin()), "origin");
b->addBits(1, static_cast<uint32_t>(programInfo.requestedFeatures()), "requestedFeatures");
b->addBool(pipeline.snapVerticesToPixelCenters(), "snapVertices");
// The base descriptor only stores whether or not the primitiveType is kPoints. Backend-

View File

@ -318,7 +318,7 @@ void GrSPIRVUniformHandler::appendUniformDecls(GrShaderFlags visibility, SkStrin
}
}
uint32_t GrSPIRVUniformHandler::getRTHeightOffset() const {
uint32_t GrSPIRVUniformHandler::getRTFlipOffset() const {
uint32_t currentOffset = fCurrentUBOOffset;
return get_ubo_offset(&currentOffset, kFloat_GrSLType, 0);
return get_ubo_offset(&currentOffset, kFloat2_GrSLType, 0);
}

View File

@ -33,7 +33,7 @@ public:
kUniformDescriptorSet = 0,
kSamplerTextureDescriptorSet = 1,
};
uint32_t getRTHeightOffset() const;
uint32_t getRTFlipOffset() const;
int numUniforms() const override {
return fUniforms.count();
@ -69,7 +69,7 @@ private:
SkTArray<SkString> fSamplerReferences;
uint32_t fCurrentUBOOffset = 0;
uint32_t fRTHeightOffset = 0;
uint32_t fRTFlipOffset = 0;
friend class GrD3DPipelineStateBuilder;
friend class GrDawnProgramBuilder;

View File

@ -65,13 +65,7 @@ void GrD3DPipelineState::setAndBindConstants(GrD3DGpu* gpu,
}
void GrD3DPipelineState::setRenderTargetState(const GrRenderTarget* rt, GrSurfaceOrigin origin) {
// Load the RT height uniform if it is needed to y-flip gl_FragCoord.
if (fBuiltinUniformHandles.fRTHeightUni.isValid() &&
fRenderTargetState.fRenderTargetSize.fHeight != rt->height()) {
fDataManager.set1f(fBuiltinUniformHandles.fRTHeightUni, SkIntToScalar(rt->height()));
}
// set RT adjustment
// Set RT adjustment and RT flip
SkISize dimensions = rt->dimensions();
SkASSERT(fBuiltinUniformHandles.fRTAdjustmentUni.isValid());
if (fRenderTargetState.fRenderTargetOrigin != origin ||
@ -85,6 +79,11 @@ void GrD3DPipelineState::setRenderTargetState(const GrRenderTarget* rt, GrSurfac
bool flip = (origin == kTopLeft_GrSurfaceOrigin);
std::array<float, 4> v = SkSL::Compiler::GetRTAdjustVector(dimensions, flip);
fDataManager.set4fv(fBuiltinUniformHandles.fRTAdjustmentUni, 1, v.data());
if (fBuiltinUniformHandles.fRTFlipUni.isValid()) {
// Note above that framebuffer space has origin top left. So we need !flip here.
std::array<float, 2> d = SkSL::Compiler::GetRTFlipVector(rt->height(), !flip);
fDataManager.set2fv(fBuiltinUniformHandles.fRTFlipUni, 1, d.data());
}
}
}

View File

@ -125,8 +125,8 @@ bool GrD3DPipelineStateBuilder::loadHLSLFromCache(SkReadBuffer* reader, gr_cp<ID
}
auto compile = [&](SkSL::ProgramKind kind, GrShaderType shaderType) {
if (inputs[shaderType].fRTHeight) {
this->addRTHeightUniform(SKSL_RTHEIGHT_NAME);
if (inputs[shaderType].fUseFlipRTUniform) {
this->addRTFlipUniform(SKSL_RTFLIP_NAME);
}
shaders[shaderType] = GrCompileHLSLShader(fGpu, hlsl[shaderType], kind);
return shaders[shaderType].get();
@ -172,8 +172,8 @@ gr_cp<ID3DBlob> GrD3DPipelineStateBuilder::compileD3DProgram(
}
}
if (program->fInputs.fRTHeight) {
this->addRTHeightUniform(SKSL_RTHEIGHT_NAME);
if (program->fInputs.fUseFlipRTUniform) {
this->addRTFlipUniform(SKSL_RTFLIP_NAME);
}
return GrCompileHLSLShader(fGpu, *outHLSL, kind);
@ -578,12 +578,11 @@ std::unique_ptr<GrD3DPipelineState> GrD3DPipelineStateBuilder::finalize() {
this->finalizeShaders();
SkSL::Program::Settings settings;
settings.fFlipY = this->origin() != kTopLeft_GrSurfaceOrigin;
settings.fSharpenTextures =
this->gpu()->getContext()->priv().options().fSharpenMipmappedTextures;
settings.fRTHeightOffset = fUniformHandler.getRTHeightOffset();
settings.fRTHeightBinding = 0;
settings.fRTHeightSet = 0;
settings.fRTFlipOffset = fUniformHandler.getRTFlipOffset();
settings.fRTFlipBinding = 0;
settings.fRTFlipSet = 0;
sk_sp<SkData> cached;
SkReadBuffer reader;

View File

@ -706,8 +706,10 @@ bool GrDawnGpu::onRegenerateMipMapLevels(GrTexture* tex) {
" sk_Position = float4(positions[sk_VertexID], 0.0, 1.0);\n"
" texCoord = texCoords[sk_VertexID];\n"
"}\n";
SkSL::String vsSPIRV =
this->SkSLToSPIRV(vs, SkSL::ProgramKind::kVertex, false, 0, nullptr);
SkSL::String vsSPIRV = this->SkSLToSPIRV(vs,
SkSL::ProgramKind::kVertex,
/*rtFlipOffset*/ 0,
nullptr);
const char* fs =
"layout(set = 0, binding = 0) uniform sampler samp;\n"
@ -716,8 +718,10 @@ bool GrDawnGpu::onRegenerateMipMapLevels(GrTexture* tex) {
"void main() {\n"
" sk_FragColor = sample(makeSampler2D(tex, samp), texCoord);\n"
"}\n";
SkSL::String fsSPIRV =
this->SkSLToSPIRV(fs, SkSL::ProgramKind::kFragment, false, 0, nullptr);
SkSL::String fsSPIRV = this->SkSLToSPIRV(fs,
SkSL::ProgramKind::kFragment,
/*rtFlipOffset=*/ 0,
nullptr);
wgpu::VertexState vertexState;
vertexState.module = this->createShaderModule(vsSPIRV);
@ -923,14 +927,15 @@ void GrDawnGpu::moveStagingBuffersToBusyAndMapAsync() {
fSubmittedStagingBuffers.clear();
}
SkSL::String GrDawnGpu::SkSLToSPIRV(const char* shaderString, SkSL::ProgramKind kind, bool flipY,
uint32_t rtHeightOffset, SkSL::Program::Inputs* inputs) {
SkSL::String GrDawnGpu::SkSLToSPIRV(const char* shaderString,
SkSL::ProgramKind kind,
uint32_t rtFlipOffset,
SkSL::Program::Inputs* inputs) {
auto errorHandler = this->getContext()->priv().getShaderErrorHandler();
SkSL::Program::Settings settings;
settings.fFlipY = flipY;
settings.fRTHeightOffset = rtHeightOffset;
settings.fRTHeightBinding = 0;
settings.fRTHeightSet = 0;
settings.fRTFlipOffset = rtFlipOffset;
settings.fRTFlipBinding = 0;
settings.fRTFlipSet = 0;
std::unique_ptr<SkSL::Program> program = this->shaderCompiler()->convertProgram(
kind,
shaderString,

View File

@ -100,8 +100,10 @@ public:
void appendCommandBuffer(wgpu::CommandBuffer commandBuffer);
void waitOnAllBusyStagingBuffers();
SkSL::String SkSLToSPIRV(const char* shaderString, SkSL::ProgramKind, bool flipY,
uint32_t rtHeightOffset, SkSL::Program::Inputs*);
SkSL::String SkSLToSPIRV(const char* shaderString,
SkSL::ProgramKind,
uint32_t rtFlipOffset,
SkSL::Program::Inputs*);
wgpu::ShaderModule createShaderModule(const SkSL::String& spirvSource);
private:

View File

@ -439,10 +439,12 @@ wgpu::ShaderModule GrDawnProgramBuilder::createShaderModule(const GrGLSLShaderBu
printf("converting program:\n%s\n", sksl.c_str());
#endif
SkSL::String spirvSource = fGpu->SkSLToSPIRV(source.c_str(), kind, flipY,
fUniformHandler.getRTHeightOffset(), inputs);
if (inputs->fRTHeight) {
this->addRTHeightUniform(SKSL_RTHEIGHT_NAME);
SkSL::String spirvSource = fGpu->SkSLToSPIRV(source.c_str(),
kind,
fUniformHandler.getRTFlipOffset(),
inputs);
if (inputs->fUseFlipRTUniform) {
this->addRTFlipUniform(SKSL_RTFLIP_NAME);
}
return fGpu->createShaderModule(spirvSource);
@ -457,13 +459,7 @@ SkSL::Compiler* GrDawnProgramBuilder::shaderCompiler() const {
}
void GrDawnProgram::setRenderTargetState(const GrRenderTarget* rt, GrSurfaceOrigin origin) {
// Load the RT height uniform if it is needed to y-flip gl_FragCoord.
if (fBuiltinUniformHandles.fRTHeightUni.isValid() &&
fRenderTargetState.fRenderTargetSize.fHeight != rt->height()) {
fDataManager.set1f(fBuiltinUniformHandles.fRTHeightUni, SkIntToScalar(rt->height()));
}
// set RT adjustment
// Set RT adjustment and RT flip
SkISize dimensions = rt->dimensions();
SkASSERT(fBuiltinUniformHandles.fRTAdjustmentUni.isValid());
if (fRenderTargetState.fRenderTargetOrigin != origin ||
@ -477,6 +473,11 @@ void GrDawnProgram::setRenderTargetState(const GrRenderTarget* rt, GrSurfaceOrig
bool flip = (origin == kTopLeft_GrSurfaceOrigin);
std::array<float, 4> v = SkSL::Compiler::GetRTAdjustVector(dimensions, flip);
fDataManager.set4fv(fBuiltinUniformHandles.fRTAdjustmentUni, 1, v.data());
if (fBuiltinUniformHandles.fRTFlipUni.isValid()) {
// Note above that framebuffer space has origin top left. So we need !flip here.
std::array<float, 2> d = SkSL::Compiler::GetRTFlipVector(rt->height(), !flip);
fDataManager.set2fv(fBuiltinUniformHandles.fRTFlipUni, 1, d.data());
}
}
}

View File

@ -3147,7 +3147,7 @@ bool GrGLGpu::createCopyProgram(GrTexture* srcTex) {
GrGLuint vshader = GrGLCompileAndAttachShader(*fGLContext, fCopyPrograms[progIdx].fProgram,
GR_GL_VERTEX_SHADER, glsl, fProgramCache->stats(),
errorHandler);
SkASSERT(program->fInputs.isEmpty());
SkASSERT(program->fInputs == SkSL::Program::Inputs());
sksl.assign(fshaderTxt.c_str(), fshaderTxt.size());
program = GrSkSLtoGLSL(this, SkSL::ProgramKind::kFragment, sksl, settings, &glsl,
@ -3155,7 +3155,7 @@ bool GrGLGpu::createCopyProgram(GrTexture* srcTex) {
GrGLuint fshader = GrGLCompileAndAttachShader(*fGLContext, fCopyPrograms[progIdx].fProgram,
GR_GL_FRAGMENT_SHADER, glsl,
fProgramCache->stats(), errorHandler);
SkASSERT(program->fInputs.isEmpty());
SkASSERT(program->fInputs == SkSL::Program::Inputs());
GL_CALL(LinkProgram(fCopyPrograms[progIdx].fProgram));
@ -3301,7 +3301,7 @@ bool GrGLGpu::createMipmapProgram(int progIdx) {
GrGLuint vshader = GrGLCompileAndAttachShader(*fGLContext, fMipmapPrograms[progIdx].fProgram,
GR_GL_VERTEX_SHADER, glsl,
fProgramCache->stats(), errorHandler);
SkASSERT(program->fInputs.isEmpty());
SkASSERT(program->fInputs == SkSL::Program::Inputs());
sksl.assign(fshaderTxt.c_str(), fshaderTxt.size());
program = GrSkSLtoGLSL(this, SkSL::ProgramKind::kFragment, sksl, settings, &glsl,
@ -3309,7 +3309,7 @@ bool GrGLGpu::createMipmapProgram(int progIdx) {
GrGLuint fshader = GrGLCompileAndAttachShader(*fGLContext, fMipmapPrograms[progIdx].fProgram,
GR_GL_FRAGMENT_SHADER, glsl,
fProgramCache->stats(), errorHandler);
SkASSERT(program->fInputs.isEmpty());
SkASSERT(program->fInputs == SkSL::Program::Inputs());
GL_CALL(LinkProgram(fMipmapPrograms[progIdx].fProgram));

View File

@ -154,13 +154,7 @@ void GrGLProgram::bindTextures(const GrGeometryProcessor& geomProc,
void GrGLProgram::setRenderTargetState(const GrRenderTarget* rt,
GrSurfaceOrigin origin,
const GrGeometryProcessor& geomProc) {
// Load the RT height uniform if it is needed
if (fBuiltinUniformHandles.fRTHeightUni.isValid() &&
fRenderTargetState.fRenderTargetSize.fHeight != rt->height()) {
fProgramDataManager.set1f(fBuiltinUniformHandles.fRTHeightUni, SkIntToScalar(rt->height()));
}
// set RT adjustment
// Set RT adjustment and RT flip
SkISize dimensions = rt->dimensions();
if (fRenderTargetState.fRenderTargetOrigin != origin ||
fRenderTargetState.fRenderTargetSize != dimensions) {
@ -174,5 +168,9 @@ void GrGLProgram::setRenderTargetState(const GrRenderTarget* rt,
bool flip = (origin == kBottomLeft_GrSurfaceOrigin);
std::array<float, 4> v = SkSL::Compiler::GetRTAdjustVector(dimensions, flip);
fProgramDataManager.set4fv(fBuiltinUniformHandles.fRTAdjustmentUni, 1, v.data());
if (fBuiltinUniformHandles.fRTFlipUni.isValid()) {
std::array<float, 2> d = SkSL::Compiler::GetRTFlipVector(dimensions.height(), flip);
fProgramDataManager.set2fv(fBuiltinUniformHandles.fRTFlipUni, 1, d.data());
}
}
}

View File

@ -147,8 +147,8 @@ void GrGLProgramBuilder::computeCountsAndStrides(GrGLuint programID,
}
void GrGLProgramBuilder::addInputVars(const SkSL::Program::Inputs& inputs) {
if (inputs.fRTHeight) {
this->addRTHeightUniform(SKSL_RTHEIGHT_NAME);
if (inputs.fUseFlipRTUniform) {
this->addRTFlipUniform(SKSL_RTFLIP_NAME);
}
}
@ -232,7 +232,6 @@ sk_sp<GrGLProgram> GrGLProgramBuilder::finalize(const GrGLPrecompiledProgram* pr
auto errorHandler = this->gpu()->getContext()->priv().getShaderErrorHandler();
const GrGeometryProcessor& geomProc = this->geometryProcessor();
SkSL::Program::Settings settings;
settings.fFlipY = this->origin() != kTopLeft_GrSurfaceOrigin;
settings.fSharpenTextures =
this->gpu()->getContext()->priv().options().fSharpenMipmappedTextures;
settings.fFragColorIsInOut = this->fragColorIsInOut();

View File

@ -13,14 +13,6 @@
#include "src/gpu/glsl/GrGLSLUniformHandler.h"
#include "src/gpu/glsl/GrGLSLVarying.h"
uint8_t GrGLSLFragmentShaderBuilder::KeyForSurfaceOrigin(GrSurfaceOrigin origin) {
SkASSERT(kTopLeft_GrSurfaceOrigin == origin || kBottomLeft_GrSurfaceOrigin == origin);
return origin + 1;
static_assert(0 == kTopLeft_GrSurfaceOrigin);
static_assert(1 == kBottomLeft_GrSurfaceOrigin);
}
GrGLSLFragmentShaderBuilder::GrGLSLFragmentShaderBuilder(GrGLSLProgramBuilder* program)
: GrGLSLShaderBuilder(program) {
fSubstageIndices.push_back(0);

View File

@ -92,10 +92,6 @@ public:
*/
class GrGLSLFragmentShaderBuilder : public GrGLSLFPFragmentBuilder, public GrGLSLXPFragmentBuilder {
public:
/** Returns a nonzero key for a surface's origin. This should only be called if a processor will
use the fragment position and/or sample locations. */
static uint8_t KeyForSurfaceOrigin(GrSurfaceOrigin);
GrGLSLFragmentShaderBuilder(GrGLSLProgramBuilder* program);
// Shared FP/XP interface.

View File

@ -382,12 +382,17 @@ void GrGLSLProgramBuilder::appendUniformDecls(GrShaderFlags visibility, SkString
this->uniformHandler()->appendUniformDecls(visibility, out);
}
void GrGLSLProgramBuilder::addRTHeightUniform(const char* name) {
SkASSERT(!fUniformHandles.fRTHeightUni.isValid());
void GrGLSLProgramBuilder::addRTFlipUniform(const char* name) {
SkASSERT(!fUniformHandles.fRTFlipUni.isValid());
GrGLSLUniformHandler* uniformHandler = this->uniformHandler();
fUniformHandles.fRTHeightUni =
uniformHandler->internalAddUniformArray(nullptr, kFragment_GrShaderFlag, kHalf_GrSLType,
name, false, 0, nullptr);
fUniformHandles.fRTFlipUni =
uniformHandler->internalAddUniformArray(nullptr,
kFragment_GrShaderFlag,
kHalf2_GrSLType,
name,
false,
0,
nullptr);
}
void GrGLSLProgramBuilder::finalizeShaders() {

View File

@ -71,9 +71,9 @@ public:
return this->uniformHandler()->inputSamplerSwizzle(handle);
}
// Used to add a uniform for the RenderTarget height (used for u_skRTHeight and frag position)
// Used to add a uniform for render target flip (used for dFdy, sk_Clockwise, and sk_FragCoord)
// without mangling the name of the uniform inside of a stage.
void addRTHeightUniform(const char* name);
void addRTFlipUniform(const char* name);
// Generates a name for a variable. The generated string will be name prefixed by the prefix
// char (unless the prefix is '\0'). It also will mangle the name to be stage-specific unless

View File

@ -23,9 +23,8 @@ class GrSurfaceProxy;
// Handles for program uniforms (other than per-effect uniforms)
struct GrGLSLBuiltinUniformHandles {
GrGLSLProgramDataManager::UniformHandle fRTAdjustmentUni;
// Render target height, used to implement u_skRTHeight and to calculate sk_FragCoord when
// origin_upper_left is not supported.
GrGLSLProgramDataManager::UniformHandle fRTHeightUni;
// Render target flip uniform (used for dFdy, sk_Clockwise, and sk_FragCoord)
GrGLSLProgramDataManager::UniformHandle fRTFlipUni;
// Destination texture origin and scale, used when dest-texture readback is enabled.
GrGLSLProgramDataManager::UniformHandle fDstTextureCoordsUni;
};

View File

@ -128,13 +128,7 @@ void GrMtlPipelineState::bindTextures(GrMtlRenderCommandEncoder* renderCmdEncode
}
void GrMtlPipelineState::setRenderTargetState(const GrRenderTarget* rt, GrSurfaceOrigin origin) {
// Load the RT height uniform if it is needed to y-flip gl_FragCoord.
if (fBuiltinUniformHandles.fRTHeightUni.isValid() &&
fRenderTargetState.fRenderTargetSize.fHeight != rt->height()) {
fDataManager.set1f(fBuiltinUniformHandles.fRTHeightUni, SkIntToScalar(rt->height()));
}
// set RT adjustment
// Set RT adjustment and RT flip
SkISize dimensions = rt->dimensions();
SkASSERT(fBuiltinUniformHandles.fRTAdjustmentUni.isValid());
if (fRenderTargetState.fRenderTargetOrigin != origin ||
@ -148,6 +142,11 @@ void GrMtlPipelineState::setRenderTargetState(const GrRenderTarget* rt, GrSurfac
bool flip = (origin == kTopLeft_GrSurfaceOrigin);
std::array<float, 4> v = SkSL::Compiler::GetRTAdjustVector(dimensions, flip);
fDataManager.set4fv(fBuiltinUniformHandles.fRTAdjustmentUni, 1, v.data());
if (fBuiltinUniformHandles.fRTFlipUni.isValid()) {
// Note above that framebuffer space has origin top left. So we need !flip here.
std::array<float, 2> d = SkSL::Compiler::GetRTFlipVector(rt->height(), !flip);
fDataManager.set2fv(fBuiltinUniformHandles.fRTFlipUni, 1, d.data());
}
}
}

View File

@ -27,7 +27,7 @@ struct GrMtlPrecompiledLibraries {
// TODO: wrap these in sk_cfp<> or unique_ptr<> when we remove ARC
id<MTLLibrary> fVertexLibrary;
id<MTLLibrary> fFragmentLibrary;
bool fRTHeight = false;
bool fRTFlip = false;
};
class GrMtlPipelineStateBuilder : public GrGLSLProgramBuilder {

View File

@ -91,8 +91,8 @@ id<MTLLibrary> GrMtlPipelineStateBuilder::compileMtlShaderLibrary(
const SkSL::String& shader, SkSL::Program::Inputs inputs,
GrContextOptions::ShaderErrorHandler* errorHandler) {
id<MTLLibrary> shaderLibrary = GrCompileMtlShaderLibrary(fGpu, shader, errorHandler);
if (shaderLibrary != nil && inputs.fRTHeight) {
this->addRTHeightUniform(SKSL_RTHEIGHT_NAME);
if (shaderLibrary != nil && inputs.fUseFlipRTUniform) {
this->addRTFlipUniform(SKSL_RTFLIP_NAME);
}
return shaderLibrary;
}
@ -524,8 +524,8 @@ GrMtlPipelineState* GrMtlPipelineStateBuilder::finalize(
[precompiledLibs->fFragmentLibrary newFunctionWithName: @"fragmentMain"];
SkASSERT(pipelineDescriptor.vertexFunction);
SkASSERT(pipelineDescriptor.fragmentFunction);
if (precompiledLibs->fRTHeight) {
this->addRTHeightUniform(SKSL_RTHEIGHT_NAME);
if (precompiledLibs->fRTFlip) {
this->addRTFlipUniform(SKSL_RTFLIP_NAME);
}
} else {
id<MTLLibrary> shaderLibraries[kGrShaderTypeCount];
@ -538,7 +538,6 @@ GrMtlPipelineState* GrMtlPipelineStateBuilder::finalize(
this->finalizeShaders();
SkSL::Program::Settings settings;
settings.fFlipY = this->origin() != kTopLeft_GrSurfaceOrigin;
settings.fSharpenTextures = fGpu->getContext()->priv().options().fSharpenMipmappedTextures;
SkASSERT(!this->fragColorIsInOut());
@ -832,7 +831,7 @@ bool GrMtlPipelineStateBuilder::PrecompileShaders(GrMtlGpu* gpu, const SkData& c
completionHandler: completionHandler];
}
precompiledLibs->fRTHeight = inputs[kFragment_GrShaderType].fRTHeight;
precompiledLibs->fRTFlip = inputs[kFragment_GrShaderType].fUseFlipRTUniform;
return true;
}

View File

@ -68,7 +68,7 @@ bool GrVkMSAALoadManager::createMSAALoadProgram(GrVkGpu* gpu) {
this->destroyResources(gpu);
return false;
}
SkASSERT(inputs.isEmpty());
SkASSERT(inputs == SkSL::Program::Inputs());
if (!GrCompileVkShaderModule(gpu, fragShaderText, VK_SHADER_STAGE_FRAGMENT_BIT,
&fFragShaderModule, &fShaderStageInfo[1], settings, &spirv,
@ -76,7 +76,7 @@ bool GrVkMSAALoadManager::createMSAALoadProgram(GrVkGpu* gpu) {
this->destroyResources(gpu);
return false;
}
SkASSERT(inputs.isEmpty());
SkASSERT(inputs == SkSL::Program::Inputs());
VkDescriptorSetLayout dsLayout[GrVkUniformHandler::kDescSetCount];

View File

@ -246,15 +246,7 @@ bool GrVkPipelineState::setAndBindInputAttachment(GrVkGpu* gpu,
void GrVkPipelineState::setRenderTargetState(SkISize colorAttachmentDimensions,
GrSurfaceOrigin origin) {
// Load the RT height uniform if it is needed to y-flip gl_FragCoord.
if (fBuiltinUniformHandles.fRTHeightUni.isValid() &&
fRenderTargetState.fRenderTargetSize.fHeight != colorAttachmentDimensions.height()) {
fDataManager.set1f(fBuiltinUniformHandles.fRTHeightUni,
SkIntToScalar(colorAttachmentDimensions.height()));
}
// set RT adjustment
// Set RT adjustment and RT flip
SkASSERT(fBuiltinUniformHandles.fRTAdjustmentUni.isValid());
if (fRenderTargetState.fRenderTargetOrigin != origin ||
fRenderTargetState.fRenderTargetSize != colorAttachmentDimensions) {
@ -268,6 +260,11 @@ void GrVkPipelineState::setRenderTargetState(SkISize colorAttachmentDimensions,
bool flip = (origin == kBottomLeft_GrSurfaceOrigin);
std::array<float, 4> v = SkSL::Compiler::GetRTAdjustVector(colorAttachmentDimensions, flip);
fDataManager.set4fv(fBuiltinUniformHandles.fRTAdjustmentUni, 1, v.data());
if (fBuiltinUniformHandles.fRTFlipUni.isValid()) {
std::array<float, 2> d =
SkSL::Compiler::GetRTFlipVector(colorAttachmentDimensions.height(), flip);
fDataManager.set2fv(fBuiltinUniformHandles.fRTFlipUni, 1, d.data());
}
}
}

View File

@ -82,8 +82,8 @@ bool GrVkPipelineStateBuilder::createVkShaderModule(VkShaderStageFlagBits stage,
stageInfo, settings, outSPIRV, outInputs)) {
return false;
}
if (outInputs->fRTHeight) {
this->addRTHeightUniform(SKSL_RTHEIGHT_NAME);
if (outInputs->fUseFlipRTUniform) {
this->addRTFlipUniform(SKSL_RTFLIP_NAME);
}
return true;
}
@ -97,8 +97,8 @@ bool GrVkPipelineStateBuilder::installVkShaderModule(VkShaderStageFlagBits stage
if (!GrInstallVkShaderModule(fGpu, spirv, stage, shaderModule, stageInfo)) {
return false;
}
if (inputs.fRTHeight) {
this->addRTHeightUniform(SKSL_RTHEIGHT_NAME);
if (inputs.fUseFlipRTUniform) {
this->addRTFlipUniform(SKSL_RTFLIP_NAME);
}
return true;
}
@ -204,12 +204,11 @@ GrVkPipelineState* GrVkPipelineStateBuilder::finalize(const GrProgramDesc& desc,
bool usePushConstants = fUniformHandler.usePushConstants();
VkPipelineShaderStageCreateInfo shaderStageInfo[3];
SkSL::Program::Settings settings;
settings.fRTHeightBinding = this->gpu()->vkCaps().getFragmentUniformBinding();
settings.fRTHeightSet = this->gpu()->vkCaps().getFragmentUniformSet();
settings.fFlipY = fUniformHandler.getFlipY();
settings.fRTFlipBinding = this->gpu()->vkCaps().getFragmentUniformBinding();
settings.fRTFlipSet = this->gpu()->vkCaps().getFragmentUniformSet();
settings.fSharpenTextures =
this->gpu()->getContext()->priv().options().fSharpenMipmappedTextures;
settings.fRTHeightOffset = fUniformHandler.getRTHeightOffset();
settings.fRTFlipOffset = fUniformHandler.getRTFlipOffset();
settings.fUsePushConstants = usePushConstants;
if (fFS.fForceHighPrecision) {
settings.fForceHighPrecision = true;

View File

@ -382,17 +382,18 @@ void GrVkUniformHandler::appendUniformDecls(GrShaderFlags visibility, SkString*
}
}
uint32_t GrVkUniformHandler::getRTHeightOffset() const {
uint32_t GrVkUniformHandler::getRTFlipOffset() const {
Layout layout = fUsePushConstants ? kStd430Layout : kStd140Layout;
uint32_t currentOffset = fCurrentOffsets[layout];
return get_aligned_offset(&currentOffset, kFloat_GrSLType, 0, layout);
return get_aligned_offset(&currentOffset, kFloat2_GrSLType, 0, layout);
}
void GrVkUniformHandler::determineIfUsePushConstants() const {
// If flipY is enabled we may be adding the RTHeight uniform during compilation.
// We may insert a uniform for flipping origin-sensitive language features (e.g. sk_FragCoord).
// We won't know that for sure until then but we need to make this determination now,
// so assume we will need it.
uint32_t pad = fFlipY ? sizeof(float) : 0;
fUsePushConstants = fCurrentOffsets[kStd430Layout] > 0 &&
fCurrentOffsets[kStd430Layout] + pad <= fProgramBuilder->caps()->maxPushConstantsSize();
static constexpr uint32_t kPad = 2*sizeof(float);
fUsePushConstants =
fCurrentOffsets[kStd430Layout] > 0 &&
fCurrentOffsets[kStd430Layout] + kPad <= fProgramBuilder->caps()->maxPushConstantsSize();
}

View File

@ -73,9 +73,9 @@ public:
}
/**
* Returns the offset that the RTHeight synthetic uniform should use if it needs to be created.
* Returns the offset that the RTFlip synthetic uniform should use if it needs to be created.
*/
uint32_t getRTHeightOffset() const;
uint32_t getRTFlipOffset() const;
int numUniforms() const override {
return fUniforms.count();
@ -88,8 +88,6 @@ public:
return fUniforms.item(idx);
}
bool getFlipY() const { return fFlipY; }
bool usePushConstants() const { return fUsePushConstants; }
uint32_t currentOffset() const {
return fUsePushConstants ? fCurrentOffsets[kStd430Layout] : fCurrentOffsets[kStd140Layout];
@ -100,7 +98,6 @@ private:
: INHERITED(program)
, fUniforms(kUniformsPerBlock)
, fSamplers(kUniformsPerBlock)
, fFlipY(program->origin() != kTopLeft_GrSurfaceOrigin)
, fUsePushConstants(false)
, fCurrentOffsets{0, 0} {
}
@ -161,7 +158,6 @@ private:
SkTArray<GrSwizzle> fSamplerSwizzles;
UniformInfo fInputUniform;
GrSwizzle fInputSwizzle;
bool fFlipY;
mutable bool fUsePushConstants;
uint32_t fCurrentOffsets[kLayoutCount];

View File

@ -895,6 +895,10 @@ bool Compiler::optimize(Program& program) {
bool Compiler::toSPIRV(Program& program, OutputStream& out) {
TRACE_EVENT0("skia.shaders", "SkSL::Compiler::toSPIRV");
AutoSource as(this, program.fSource.get());
ProgramSettings settings;
settings.fDSLUseMemoryPool = false;
dsl::Start(this, program.fConfig->fKind, settings);
dsl::DSLWriter::IRGenerator().fSymbolTable = program.fSymbols;
#ifdef SK_ENABLE_SPIRV_VALIDATION
StringStream buffer;
SPIRVCodeGenerator cg(fContext.get(), &program, this, &buffer);
@ -933,6 +937,7 @@ bool Compiler::toSPIRV(Program& program, OutputStream& out) {
SPIRVCodeGenerator cg(fContext.get(), &program, this, &out);
bool result = cg.generateCode();
#endif
dsl::End();
return result;
}

View File

@ -94,6 +94,17 @@ public:
return result;
}
/**
* Uniform values by the compiler to implement origin-neutral dFdy, sk_Clockwise, and
* sk_FragCoord.
*/
static std::array<float, 2> GetRTFlipVector(int rtHeight, bool flipY) {
std::array<float, 2> result;
result[0] = flipY ? rtHeight : 0.f;
result[1] = flipY ? -1.f : 1.f;
return result;
}
struct OptimizationContext {
// nodes we have already reported errors for and should not error on again
std::unordered_set<const IRNode*> fSilences;

View File

@ -1268,14 +1268,14 @@ std::unique_ptr<Expression> IRGenerator::convertIdentifier(int offset, skstd::st
const Variable* var = &result->as<Variable>();
const Modifiers& modifiers = var->modifiers();
switch (modifiers.fLayout.fBuiltin) {
#ifndef SKSL_STANDALONE
case SK_FRAGCOORD_BUILTIN:
fInputs.fFlipY = true;
if (this->settings().fFlipY &&
!this->caps().fragCoordConventionsExtensionString()) {
fInputs.fRTHeight = true;
if (caps().canUseFragCoord()) {
fInputs.fUseFlipRTUniform = true;
}
#endif
break;
case SK_CLOCKWISE_BUILTIN:
fInputs.fUseFlipRTUniform = true;
break;
}
if (this->programKind() == ProgramKind::kFragmentProcessor &&
(modifiers.fFlags & Modifiers::kIn_Flag) &&
@ -1411,7 +1411,7 @@ std::unique_ptr<Expression> IRGenerator::call(int offset,
ExpressionArray arguments) {
if (function.isBuiltin()) {
if (function.intrinsicKind() == k_dFdy_IntrinsicKind) {
fInputs.fUsesYDerivative = true;
fInputs.fUseFlipRTUniform = true;
}
if (function.definition()) {
fReferencedIntrinsics.insert(&function);
@ -1841,7 +1841,7 @@ void IRGenerator::start(const ParsedModule& base,
}
fIsBuiltinCode = isBuiltinCode;
fInputs.reset();
fInputs = {};
fInvocations = -1;
fRTAdjust = nullptr;
fRTAdjustInterfaceBlock = nullptr;

View File

@ -140,14 +140,6 @@ static bool detect_shader_settings(const SkSL::String& text,
static auto s_emulateAbsIntCaps = Factory::EmulateAbsIntFunction();
*caps = s_emulateAbsIntCaps.get();
}
if (settingsText.consumeSuffix(" FragCoordsOld")) {
static auto s_fragCoordsOld = Factory::FragCoordsOld();
*caps = s_fragCoordsOld.get();
}
if (settingsText.consumeSuffix(" FragCoordsNew")) {
static auto s_fragCoordsNew = Factory::FragCoordsNew();
*caps = s_fragCoordsNew.get();
}
if (settingsText.consumeSuffix(" GeometryShaderExtensionString")) {
static auto s_geometryExtCaps = Factory::GeometryShaderExtensionString();
*caps = s_geometryExtCaps.get();
@ -217,9 +209,6 @@ static bool detect_shader_settings(const SkSL::String& text,
static auto s_version450CoreCaps = Factory::Version450Core();
*caps = s_version450CoreCaps.get();
}
if (settingsText.consumeSuffix(" FlipY")) {
settings->fFlipY = true;
}
if (settingsText.consumeSuffix(" ForceHighPrecision")) {
settings->fForceHighPrecision = true;
}
@ -319,6 +308,13 @@ ResultCode processCommand(std::vector<SkSL::String>& args) {
}
}
// This tells the compiler where the rt-flip uniform will live should it be required. For
// testing purposes we don't care where that is, but the compiler will report an error if we
// leave them at their default invalid values.
settings.fRTFlipOffset = 32;
settings.fRTFlipSet = 0;
settings.fRTFlipBinding = 0;
const SkSL::String& outputPath = args[2];
auto emitCompileError = [&](SkSL::FileOutputStream& out, const char* errorText) {
// Overwrite the compiler output, if any, with an error message.

View File

@ -21,9 +21,6 @@ class ExternalFunction;
* Holds the compiler settings for a program.
*/
struct ProgramSettings {
// if false, sk_FragCoord is exactly the same as gl_FragCoord. If true, the y coordinate
// must be flipped.
bool fFlipY = false;
// If true the destination fragment color is read sk_FragColor. It must be declared inout.
bool fFragColorIsInOut = false;
// if true, Setting objects (e.g. sk_Caps.fbFetchSupport) should be replaced with their
@ -33,13 +30,13 @@ struct ProgramSettings {
bool fForceHighPrecision = false;
// if true, add -0.5 bias to LOD of all texture lookups
bool fSharpenTextures = false;
// if the program needs to create an RTHeight uniform, this is its offset in the uniform
// if the program needs to create an RTFlip uniform, this is its offset in the uniform
// buffer
int fRTHeightOffset = -1;
// if the program needs to create an RTHeight uniform and is creating spriv, this is the
int fRTFlipOffset = -1;
// if the program needs to create an RTFlip uniform and is creating spriv, this is the
// binding and set number of the uniform buffer.
int fRTHeightBinding = -1;
int fRTHeightSet = -1;
int fRTFlipBinding = -1;
int fRTFlipSet = -1;
// If layout(set=S, binding=B) is not specified for a uniform, these values will be used.
// At present, zero is always used by our backends.
int fDefaultUniformSet = 0;
@ -75,6 +72,8 @@ struct ProgramSettings {
bool fDSLMangling = true;
// If true, the DSL should automatically mark variables declared upon creation.
bool fDSLMarkVarsDeclared = false;
// If true, the DSL should install a memory pool when possible.
bool fDSLUseMemoryPool = true;
// External functions available for use in runtime effects. These values are registered in the
// symbol table of the Program, but ownership is *not* transferred. It is up to the caller to
// keep them alive.

View File

@ -361,20 +361,6 @@ public:
return result;
}
static ShaderCapsPointer FragCoordsNew() {
ShaderCapsPointer result = MakeShaderCaps();
result->fVersionDeclString = "#version 400";
result->fFragCoordConventionsExtensionString = "GL_ARB_fragment_coord_conventions";
return result;
}
static ShaderCapsPointer FragCoordsOld() {
ShaderCapsPointer result = MakeShaderCaps();
result->fVersionDeclString = "#version 110";
result->fGLSLGeneration = GrGLSLGeneration::k110_GrGLSLGeneration;
result->fFragCoordConventionsExtensionString = "GL_ARB_fragment_coord_conventions";
return result;
}
static ShaderCapsPointer GeometryShaderExtensionString() {
ShaderCapsPointer result = MakeShaderCaps();
result->fVersionDeclString = "#version 310es";

View File

@ -483,11 +483,9 @@ void GLSLCodeGenerator::writeFunctionCall(const FunctionCall& c) {
}
break;
case k_dFdy_IntrinsicKind:
if (fProgram.fConfig->fSettings.fFlipY) {
// Flipping Y also negates the Y derivatives.
this->write("-dFdy");
nameWritten = true;
}
// Flipping Y also negates the Y derivatives.
this->write(SKSL_RTFLIP_NAME ".y * dFdy");
nameWritten = true;
[[fallthrough]];
case k_dFdx_IntrinsicKind:
case k_fwidth_IntrinsicKind:
@ -743,29 +741,16 @@ void GLSLCodeGenerator::writeFragCoord() {
return;
}
// We only declare "gl_FragCoord" when we're in the case where we want to use layout qualifiers
// to reverse y. Otherwise it isn't necessary and whether the "in" qualifier appears in the
// declaration varies in earlier GLSL specs. So it is simpler to omit it.
if (!fProgram.fConfig->fSettings.fFlipY) {
this->write("gl_FragCoord");
} else if (const char* extension = this->caps().fragCoordConventionsExtensionString()) {
if (!fSetupFragPositionGlobal) {
if (this->caps().generation() < k150_GrGLSLGeneration) {
this->writeExtension(extension);
}
fGlobals.writeText("layout(origin_upper_left) in vec4 gl_FragCoord;\n");
fSetupFragPositionGlobal = true;
}
this->write("gl_FragCoord");
} else {
if (!fSetupFragPositionLocal) {
fFunctionHeader += usesPrecisionModifiers() ? "highp " : "";
fFunctionHeader += " vec4 sk_FragCoord = vec4(gl_FragCoord.x, " SKSL_RTHEIGHT_NAME
" - gl_FragCoord.y, gl_FragCoord.z, gl_FragCoord.w);\n";
fSetupFragPositionLocal = true;
}
this->write("sk_FragCoord");
if (!fSetupFragPosition) {
fFunctionHeader += usesPrecisionModifiers() ? "highp " : "";
fFunctionHeader += " vec4 sk_FragCoord = vec4("
"gl_FragCoord.x, "
SKSL_RTFLIP_NAME ".x + " SKSL_RTFLIP_NAME ".y * gl_FragCoord.y, "
"gl_FragCoord.z, "
"gl_FragCoord.w);\n";
fSetupFragPosition = true;
}
this->write("sk_FragCoord");
}
void GLSLCodeGenerator::writeVariableReference(const VariableReference& ref) {
@ -781,7 +766,15 @@ void GLSLCodeGenerator::writeVariableReference(const VariableReference& ref) {
this->writeFragCoord();
break;
case SK_CLOCKWISE_BUILTIN:
this->write(fProgram.fConfig->fSettings.fFlipY ? "(!gl_FrontFacing)" : "gl_FrontFacing");
if (!fSetupClockwise) {
fFunctionHeader +=
" bool sk_Clockwise = gl_FrontFacing;\n"
" if (" SKSL_RTFLIP_NAME ".y < 0.0) {\n"
" sk_Clockwise = !sk_Clockwise;\n"
" }\n";
fSetupClockwise = true;
}
this->write("sk_Clockwise");
break;
case SK_VERTEXID_BUILTIN:
this->write("gl_VertexID");
@ -1035,7 +1028,7 @@ void GLSLCodeGenerator::writeFunctionDeclaration(const FunctionDeclaration& f) {
}
void GLSLCodeGenerator::writeFunction(const FunctionDefinition& f) {
fSetupFragPositionLocal = false;
fSetupFragPosition = false;
fSetupFragCoordWorkaround = false;
this->writeFunctionDeclaration(f.declaration());
@ -1475,11 +1468,11 @@ void GLSLCodeGenerator::writeProgramElement(const ProgramElement& e) {
}
void GLSLCodeGenerator::writeInputVars() {
if (fProgram.fInputs.fRTHeight) {
if (fProgram.fInputs.fUseFlipRTUniform) {
const char* precision = usesPrecisionModifiers() ? "highp " : "";
fGlobals.writeText("uniform ");
fGlobals.writeText(precision);
fGlobals.writeText("float " SKSL_RTHEIGHT_NAME ";\n");
fGlobals.writeText("vec2 " SKSL_RTFLIP_NAME ";\n");
}
}

View File

@ -193,8 +193,8 @@ protected:
bool fFoundExternalSamplerDecl = false;
bool fFoundRectSamplerDecl = false;
bool fFoundGSInvocations = false;
bool fSetupFragPositionGlobal = false;
bool fSetupFragPositionLocal = false;
bool fSetupClockwise = false;
bool fSetupFragPosition = false;
bool fSetupFragCoordWorkaround = false;
// if non-empty, replace all texture / texture2D / textureProj / etc. calls with this name
String fTextureFunctionOverride;

View File

@ -618,11 +618,7 @@ bool MetalCodeGenerator::writeIntrinsicCall(const FunctionCall& c, IntrinsicKind
return true;
}
case k_dFdy_IntrinsicKind: {
// Flipping Y also negates the Y derivatives.
if (fProgram.fConfig->fSettings.fFlipY) {
this->write("-");
}
this->write("dfdy");
this->write(fRTFlipName + ".y*dfdy");
this->writeArgumentList(c.arguments());
return true;
}
@ -1107,13 +1103,12 @@ void MetalCodeGenerator::writeCastConstructor(const AnyConstructor& c,
}
void MetalCodeGenerator::writeFragCoord() {
if (fRTHeightName.length()) {
this->write("float4(_fragCoord.x, ");
this->write(fRTHeightName.c_str());
this->write(" - _fragCoord.y, 0.0, _fragCoord.w)");
} else {
this->write("float4(_fragCoord.x, _fragCoord.y, 0.0, _fragCoord.w)");
}
SkASSERT(fRTFlipName.length());
this->write("float4(_fragCoord.x, ");
this->write(fRTFlipName.c_str());
this->write(".x + ");
this->write(fRTFlipName.c_str());
this->write(".y * _fragCoord.y, 0.0, _fragCoord.w)");
}
void MetalCodeGenerator::writeVariableReference(const VariableReference& ref) {
@ -1141,7 +1136,7 @@ void MetalCodeGenerator::writeVariableReference(const VariableReference& ref) {
case SK_CLOCKWISE_BUILTIN:
// We'd set the front facing winding in the MTLRenderCommandEncoder to be counter
// clockwise to match Skia convention.
this->write(fProgram.fConfig->fSettings.fFlipY ? "_frontFacing" : "(!_frontFacing)");
this->write("(" + fRTFlipName + ".y < 0 ? _frontFacing : !_frontFacing)");
break;
default:
const Variable& var = *ref.variable();
@ -1596,7 +1591,9 @@ int MetalCodeGenerator::getUniformSet(const Modifiers& m) {
}
bool MetalCodeGenerator::writeFunctionDeclaration(const FunctionDeclaration& f) {
fRTHeightName = fProgram.fInputs.fRTHeight ? "_globals._anonInterface0->u_skRTHeight" : "";
fRTFlipName = fProgram.fInputs.fUseFlipRTUniform
? "_globals._anonInterface0->" SKSL_RTFLIP_NAME
: "";
const char* separator = "";
if (f.isMain()) {
switch (fProgram.fConfig->fKind) {
@ -1657,9 +1654,9 @@ bool MetalCodeGenerator::writeFunctionDeclaration(const FunctionDeclaration& f)
}
}
if (fProgram.fConfig->fKind == ProgramKind::kFragment) {
if (fProgram.fInputs.fRTHeight && fInterfaceBlockNameMap.empty()) {
if (fProgram.fInputs.fUseFlipRTUniform && fInterfaceBlockNameMap.empty()) {
this->write(", constant sksl_synthetic_uniforms& _anonInterface0 [[buffer(1)]]");
fRTHeightName = "_anonInterface0.u_skRTHeight";
fRTFlipName = "_anonInterface0." SKSL_RTFLIP_NAME;
}
this->write(", bool _frontFacing [[front_facing]]");
this->write(", float4 _fragCoord [[position]]");
@ -1784,8 +1781,8 @@ void MetalCodeGenerator::writeInterfaceBlock(const InterfaceBlock& intf) {
}
fIndentation++;
this->writeFields(structType->fields(), structType->fOffset, &intf);
if (fProgram.fInputs.fRTHeight) {
this->writeLine("float u_skRTHeight;");
if (fProgram.fInputs.fUseFlipRTUniform) {
this->writeLine("float2 " SKSL_RTFLIP_NAME ";");
}
fIndentation--;
this->write("}");
@ -2165,9 +2162,9 @@ void MetalCodeGenerator::writeInterfaceBlocks() {
wroteInterfaceBlock = true;
}
}
if (!wroteInterfaceBlock && fProgram.fInputs.fRTHeight) {
if (!wroteInterfaceBlock && fProgram.fInputs.fUseFlipRTUniform) {
this->writeLine("struct sksl_synthetic_uniforms {");
this->writeLine(" float u_skRTHeight;");
this->writeLine(" float2 " SKSL_RTFLIP_NAME ";");
this->writeLine("};");
}
}

View File

@ -286,7 +286,7 @@ protected:
bool fSetupFragPositionLocal = false;
std::unordered_set<String> fHelpers;
int fUniformBuffer = -1;
String fRTHeightName;
String fRTFlipName;
const FunctionDeclaration* fCurrentFunction = nullptr;
int fSwizzleHelperCount = 0;
bool fIgnoreVariableReferenceModifiers = false;

View File

@ -9,11 +9,14 @@
#include "src/sksl/GLSL.std.450.h"
#include "include/sksl/DSLCore.h"
#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/SkSLOperators.h"
#include "src/sksl/dsl/priv/DSLWriter.h"
#include "src/sksl/ir/SkSLBlock.h"
#include "src/sksl/ir/SkSLExpressionStatement.h"
#include "src/sksl/ir/SkSLExtension.h"
#include "src/sksl/ir/SkSLField.h"
#include "src/sksl/ir/SkSLIndexExpression.h"
#include "src/sksl/ir/SkSLVariableReference.h"
@ -23,6 +26,9 @@
#define kLast_Capability SpvCapabilityMultiViewport
constexpr int DEVICE_FRAGCOORDS_BUILTIN = -1000;
constexpr int DEVICE_CLOCKWISE_BUILTIN = -1001;
namespace SkSL {
static const int32_t SKSL_MAGIC = 0x0; // FIXME: we should probably register a magic number
@ -844,14 +850,35 @@ SpvId SPIRVCodeGenerator::writeIntrinsicCall(const FunctionCall& c, OutputStream
}
}
SpvId SPIRVCodeGenerator::vectorize(const Expression& arg, int vectorSize, OutputStream& out) {
SkASSERT(vectorSize >= 1 && vectorSize <= 4);
const Type& argType = arg.type();
SpvId raw = this->writeExpression(arg, out);
if (argType.isScalar()) {
if (vectorSize == 1) {
return raw;
}
SpvId vector = this->nextId(&argType);
this->writeOpCode(SpvOpCompositeConstruct, 3 + vectorSize, out);
this->writeWord(this->getType(argType.toCompound(fContext, vectorSize, 1)), out);
this->writeWord(vector, out);
for (int i = 0; i < vectorSize; i++) {
this->writeWord(raw, out);
}
return vector;
} else {
SkASSERT(vectorSize == argType.columns());
return raw;
}
}
std::vector<SpvId> SPIRVCodeGenerator::vectorize(const ExpressionArray& args, OutputStream& out) {
int vectorSize = 0;
int vectorSize = 1;
for (const auto& a : args) {
if (a->type().isVector()) {
if (vectorSize) {
if (vectorSize > 1) {
SkASSERT(a->type().columns() == vectorSize);
}
else {
} else {
vectorSize = a->type().columns();
}
}
@ -859,20 +886,7 @@ std::vector<SpvId> SPIRVCodeGenerator::vectorize(const ExpressionArray& args, Ou
std::vector<SpvId> result;
result.reserve(args.size());
for (const auto& arg : args) {
const Type& argType = arg->type();
SpvId raw = this->writeExpression(*arg, out);
if (vectorSize && argType.isScalar()) {
SpvId vector = this->nextId(&arg->type());
this->writeOpCode(SpvOpCompositeConstruct, 3 + vectorSize, out);
this->writeWord(this->getType(argType.toCompound(fContext, vectorSize, 1)), out);
this->writeWord(vector, out);
for (int i = 0; i < vectorSize; i++) {
this->writeWord(raw, out);
}
result.push_back(vector);
} else {
result.push_back(raw);
}
result.push_back(this->vectorize(*arg, vectorSize, out));
}
return result;
}
@ -1045,13 +1059,15 @@ SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIn
this->writeWord(this->getType(callType), out);
this->writeWord(result, out);
this->writeWord(fn, out);
if (fProgram.fConfig->fSettings.fFlipY) {
// Flipping Y also negates the Y derivatives.
SpvId flipped = this->nextId(&callType);
this->writeInstruction(SpvOpFNegate, this->getType(callType), flipped, result,
out);
result = flipped;
}
this->addRTFlipUniform(c.fOffset);
using namespace dsl;
DSLExpression rtFlip(DSLWriter::IRGenerator().convertIdentifier(/*offset=*/-1,
SKSL_RTFLIP_NAME));
SpvId rtFlipY = this->vectorize(*rtFlip.y().release(), callType.columns(), out);
SpvId flipped = this->nextId(&callType);
this->writeInstruction(SpvOpFMul, this->getType(callType), flipped, result, rtFlipY,
out);
result = flipped;
break;
}
case kClamp_SpecialIntrinsic: {
@ -2032,137 +2048,98 @@ std::unique_ptr<SPIRVCodeGenerator::LValue> SPIRVCodeGenerator::getLValue(const
}
SpvId SPIRVCodeGenerator::writeVariableReference(const VariableReference& ref, OutputStream& out) {
SpvId result = this->getLValue(ref, out)->load(out);
if (ref.variable()->modifiers().fLayout.fBuiltin == DEVICE_FRAGCOORDS_BUILTIN) {
// Down below, we rewrite raw references to sk_FragCoord with expressions that reference
// DEVICE_FRAGCOORDS_BUILTIN. This is a fake variable that means we need to directly access
// the fragcoord; do so now.
dsl::DSLVar fragCoord("sk_FragCoord");
return this->getLValue(*dsl::DSLExpression(fragCoord).release(), out)->load(out);
}
if (ref.variable()->modifiers().fLayout.fBuiltin == DEVICE_CLOCKWISE_BUILTIN) {
// Down below, we rewrite raw references to sk_Clockwise with expressions that reference
// DEVICE_CLOCKWISE_BUILTIN. This is a fake variable that means we need to directly
// access front facing; do so now.
dsl::DSLVar clockwise("sk_Clockwise");
return this->getLValue(*dsl::DSLExpression(clockwise).release(), out)->load(out);
}
// Handle the "flipY" setting when reading sk_FragCoord.
// Handle inserting use of uniform to flip y when referencing sk_FragCoord.
const Variable* variable = ref.variable();
if (variable->modifiers().fLayout.fBuiltin == SK_FRAGCOORD_BUILTIN &&
fProgram.fConfig->fSettings.fFlipY) {
// The x component never changes, so just grab it
SpvId xId = this->nextId(Precision::kDefault);
this->writeInstruction(SpvOpCompositeExtract, this->getType(*fContext.fTypes.fFloat), xId,
result, 0, out);
// Calculate the y component which may need to be flipped
SpvId rawYId = this->nextId(nullptr);
this->writeInstruction(SpvOpCompositeExtract, this->getType(*fContext.fTypes.fFloat),
rawYId, result, 1, out);
SpvId flippedYId = 0;
if (fProgram.fConfig->fSettings.fFlipY) {
// need to remap to a top-left coordinate system
if (fRTHeightStructId == (SpvId)-1) {
// height variable hasn't been written yet
SkASSERT(fRTHeightFieldIndex == (SpvId)-1);
std::vector<Type::Field> fields;
if (fProgram.fConfig->fSettings.fRTHeightOffset < 0) {
fErrors.error(ref.fOffset, "RTHeightOffset is negative");
}
fields.emplace_back(
Modifiers(Layout(/*flags=*/0, /*location=*/-1,
fProgram.fConfig->fSettings.fRTHeightOffset,
/*binding=*/-1, /*index=*/-1, /*set=*/-1, /*builtin=*/-1,
/*inputAttachmentIndex=*/-1,
Layout::kUnspecified_Primitive, /*maxVertices=*/1,
/*invocations=*/-1, /*when=*/"", Layout::CType::kDefault),
/*flags=*/0),
SKSL_RTHEIGHT_NAME, fContext.fTypes.fFloat.get());
String name("sksl_synthetic_uniforms");
std::unique_ptr<Type> intfStruct = Type::MakeStructType(/*offset=*/-1, name,
fields);
int binding = fProgram.fConfig->fSettings.fRTHeightBinding;
if (binding == -1) {
fErrors.error(ref.fOffset, "layout(binding=...) is required in SPIR-V");
}
int set = fProgram.fConfig->fSettings.fRTHeightSet;
if (set == -1) {
fErrors.error(ref.fOffset, "layout(set=...) is required in SPIR-V");
}
bool usePushConstants = fProgram.fConfig->fSettings.fUsePushConstants;
int flags = usePushConstants ? Layout::Flag::kPushConstant_Flag : 0;
Modifiers modifiers(
Layout(flags, /*location=*/-1, /*offset=*/-1, binding, /*index=*/-1,
set, /*builtin=*/-1, /*inputAttachmentIndex=*/-1,
Layout::kUnspecified_Primitive,
/*maxVertices=*/-1, /*invocations=*/-1, /*when=*/"",
Layout::CType::kDefault),
Modifiers::kUniform_Flag);
const Variable* intfVar = fSynthetics.takeOwnershipOfSymbol(
std::make_unique<Variable>(/*offset=*/-1,
fProgram.fModifiers->add(modifiers),
name,
intfStruct.get(),
/*builtin=*/false,
Variable::Storage::kGlobal));
InterfaceBlock intf(/*offset=*/-1,
intfVar,
name,
/*instanceName=*/"",
/*arraySize=*/0,
std::make_shared<SymbolTable>(&fErrors, /*builtin=*/false));
fRTHeightStructId = this->writeInterfaceBlock(intf, false);
fRTHeightFieldIndex = 0;
fRTHeightStorageClass = usePushConstants ? SpvStorageClassPushConstant
: SpvStorageClassUniform;
if (variable->modifiers().fLayout.fBuiltin == SK_FRAGCOORD_BUILTIN) {
this->addRTFlipUniform(ref.fOffset);
// Use sk_RTAdjust to compute the flipped coordinate
using namespace dsl;
const char* DEVICE_COORDS_NAME = "__device_FragCoords";
SymbolTable& symbols = *dsl::DSLWriter::SymbolTable();
// Use a uniform to flip the Y coordinate. The new expression will be written in
// terms of __device_FragCoords, which is a fake variable that means "access the
// underlying fragcoords directly without flipping it".
DSLExpression rtFlip(DSLWriter::IRGenerator().convertIdentifier(/*offset=*/-1,
SKSL_RTFLIP_NAME));
if (!symbols[DEVICE_COORDS_NAME]) {
Modifiers modifiers;
modifiers.fLayout.fBuiltin = DEVICE_FRAGCOORDS_BUILTIN;
if (fProgram.fPool) {
fProgram.fPool->attachToThread();
}
symbols.add(std::make_unique<Variable>(/*offset=*/-1,
fContext.fModifiersPool->add(modifiers),
DEVICE_COORDS_NAME,
fContext.fTypes.fFloat4.get(),
true,
Variable::Storage::kGlobal));
if (fProgram.fPool) {
fProgram.fPool->detachFromThread();
}
SkASSERT(fRTHeightFieldIndex != (SpvId)-1);
IntLiteral fieldIndex(/*offset=*/-1, fRTHeightFieldIndex, fContext.fTypes.fInt.get());
SpvId fieldIndexId = this->writeIntLiteral(fieldIndex);
SpvId heightPtr = this->nextId(nullptr);
this->writeOpCode(SpvOpAccessChain, 5, out);
this->writeWord(this->getPointerType(*fContext.fTypes.fFloat, fRTHeightStorageClass),
out);
this->writeWord(heightPtr, out);
this->writeWord(fRTHeightStructId, out);
this->writeWord(fieldIndexId, out);
SpvId heightRead = this->nextId(nullptr);
this->writeInstruction(SpvOpLoad, this->getType(*fContext.fTypes.fFloat), heightRead,
heightPtr, out);
flippedYId = this->nextId(nullptr);
this->writeInstruction(SpvOpFSub, this->getType(*fContext.fTypes.fFloat), flippedYId,
heightRead, rawYId, out);
}
// The z component will always be zero so we just get an id to the 0 literal
FloatLiteral zero(/*offset=*/-1, /*value=*/0.0, fContext.fTypes.fFloat.get());
SpvId zeroId = writeFloatLiteral(zero);
// Calculate the w component
SpvId rawWId = this->nextId(nullptr);
this->writeInstruction(SpvOpCompositeExtract, this->getType(*fContext.fTypes.fFloat),
rawWId, result, 3, out);
// Fill in the new fragcoord with the components from above
SpvId adjusted = this->nextId(nullptr);
this->writeOpCode(SpvOpCompositeConstruct, 7, out);
this->writeWord(this->getType(*fContext.fTypes.fFloat4), out);
this->writeWord(adjusted, out);
this->writeWord(xId, out);
if (fProgram.fConfig->fSettings.fFlipY) {
this->writeWord(flippedYId, out);
} else {
this->writeWord(rawYId, out);
}
this->writeWord(zeroId, out);
this->writeWord(rawWId, out);
return adjusted;
DSLVar deviceCoord(DEVICE_COORDS_NAME);
std::unique_ptr<Expression> rtFlipSkSLExpr = rtFlip.release();
DSLExpression x = DSLExpression(rtFlipSkSLExpr->clone()).x();
DSLExpression y = DSLExpression(std::move(rtFlipSkSLExpr)).y();
return this->writeExpression(*dsl::Float4(deviceCoord.x(),
std::move(x) + std::move(y) * deviceCoord.y(),
deviceCoord.z(),
deviceCoord.w()).release(),
out);
}
// Handle the "flipY" setting when reading sk_Clockwise.
if (variable->modifiers().fLayout.fBuiltin == SK_CLOCKWISE_BUILTIN &&
!fProgram.fConfig->fSettings.fFlipY) {
// FrontFacing in Vulkan is defined in terms of a top-down render target. In skia, we use
// the default convention of "counter-clockwise face is front".
SpvId inverse = this->nextId(nullptr);
this->writeInstruction(SpvOpLogicalNot, this->getType(*fContext.fTypes.fBool), inverse,
result, out);
return inverse;
// Handle flipping sk_Clockwise.
if (variable->modifiers().fLayout.fBuiltin == SK_CLOCKWISE_BUILTIN) {
this->addRTFlipUniform(ref.fOffset);
using namespace dsl;
const char* DEVICE_CLOCKWISE_NAME = "__device_Clockwise";
SymbolTable& symbols = *dsl::DSLWriter::SymbolTable();
// Use a uniform to flip the Y coordinate. The new expression will be written in
// terms of __device_Clockwise, which is a fake variable that means "access the
// underlying FrontFacing directly".
DSLExpression rtFlip(DSLWriter::IRGenerator().convertIdentifier(/*offset=*/-1,
SKSL_RTFLIP_NAME));
if (!symbols[DEVICE_CLOCKWISE_NAME]) {
Modifiers modifiers;
modifiers.fLayout.fBuiltin = DEVICE_CLOCKWISE_BUILTIN;
if (fProgram.fPool) {
fProgram.fPool->attachToThread();
}
symbols.add(std::make_unique<Variable>(/*offset=*/-1,
fContext.fModifiersPool->add(modifiers),
DEVICE_CLOCKWISE_NAME,
fContext.fTypes.fBool.get(),
true,
Variable::Storage::kGlobal));
if (fProgram.fPool) {
fProgram.fPool->detachFromThread();
}
}
DSLVar deviceClockwise(DEVICE_CLOCKWISE_NAME);
// FrontFacing in Vulkan is defined in terms of a top-down render target. In skia,
// we use the default convention of "counter-clockwise face is front".
return this->writeExpression(*dsl::Bool(Select(rtFlip.y() > 0,
!deviceClockwise,
deviceClockwise)).release(),
out);
}
return result;
return this->getLValue(ref, out)->load(out);
}
SpvId SPIRVCodeGenerator::writeIndexExpression(const IndexExpression& expr, OutputStream& out) {
@ -3048,31 +3025,68 @@ static void update_sk_in_count(const Modifiers& m, int* outSkInCount) {
}
}
SpvId SPIRVCodeGenerator::writeInterfaceBlock(const InterfaceBlock& intf, bool appendRTHeight) {
SpvId SPIRVCodeGenerator::writeInterfaceBlock(const InterfaceBlock& intf, bool appendRTFlip) {
MemoryLayout memoryLayout = this->memoryLayoutForVariable(intf.variable());
SpvId result = this->nextId(nullptr);
std::unique_ptr<Type> rtHeightStructType;
const Type* type = &intf.variable().type();
if (!MemoryLayout::LayoutIsSupported(*type)) {
fErrors.error(type->fOffset, "type '" + type->name() + "' is not permitted here");
const Variable& intfVar = intf.variable();
const Type& type = intfVar.type();
if (!MemoryLayout::LayoutIsSupported(type)) {
fErrors.error(type.fOffset, "type '" + type.name() + "' is not permitted here");
return this->nextId(nullptr);
}
SpvStorageClass_ storageClass = get_storage_class(intf.variable(), SpvStorageClassFunction);
if (fProgram.fInputs.fRTHeight && appendRTHeight) {
SkASSERT(fRTHeightStructId == (SpvId) -1);
SkASSERT(fRTHeightFieldIndex == (SpvId) -1);
std::vector<Type::Field> fields = type->fields();
fRTHeightStructId = result;
fRTHeightFieldIndex = fields.size();
fRTHeightStorageClass = storageClass;
fields.emplace_back(Modifiers(), skstd::string_view(SKSL_RTHEIGHT_NAME),
fContext.fTypes.fFloat.get());
rtHeightStructType = Type::MakeStructType(type->fOffset, String(type->name()),
std::move(fields));
type = rtHeightStructType.get();
if (fProgram.fInputs.fUseFlipRTUniform && appendRTFlip) {
// We can only have one interface block (because we use push_constant and that is limited
// to one per program), so we need to append rtflip to this one rather than synthesize an
// entirely new block when the variable is referenced. And we can't modify the existing
// block, so we instead create a modified copy of it and write that.
std::vector<Type::Field> fields = type.fields();
fields.emplace_back(Modifiers(Layout(/*flags=*/0,
/*location=*/-1,
fProgram.fConfig->fSettings.fRTFlipOffset,
/*binding=*/-1,
/*index=*/-1,
/*set=*/-1,
/*builtin=*/-1,
/*inputAttachmentIndex=*/-1,
Layout::kUnspecified_Primitive,
/*maxVertices=*/1,
/*invocations=*/-1,
/*when=*/"",
Layout::CType::kDefault),
/*flags=*/0),
SKSL_RTFLIP_NAME,
fContext.fTypes.fFloat2.get());
if (fProgram.fPool) {
fProgram.fPool->attachToThread();
}
const Type* rtFlipStructType = fProgram.fSymbols->takeOwnershipOfSymbol(
Type::MakeStructType(type.fOffset, String(type.name()), std::move(fields)));
const Variable* modifiedVar = fProgram.fSymbols->takeOwnershipOfSymbol(
std::make_unique<Variable>(intfVar.fOffset,
&intfVar.modifiers(),
intfVar.name(),
rtFlipStructType,
intfVar.isBuiltin(),
intfVar.storage()));
InterfaceBlock modifiedCopy(intf.fOffset,
modifiedVar,
intf.typeName(),
intf.instanceName(),
intf.arraySize(),
intf.typeOwner());
SpvId result = this->writeInterfaceBlock(modifiedCopy, false);
fProgram.fSymbols->add(std::make_unique<Field>(
/*offset=*/-1, modifiedVar, rtFlipStructType->fields().size() - 1));
if (fProgram.fPool) {
fProgram.fPool->detachFromThread();
}
fVariableMap[&intfVar] = result;
fWroteRTFlip = true;
return result;
}
SpvId typeId;
const Modifiers& intfModifiers = intf.variable().modifiers();
const Modifiers& intfModifiers = intfVar.modifiers();
if (intfModifiers.fLayout.fBuiltin == SK_IN_BUILTIN) {
for (const ProgramElement* e : fProgram.elements()) {
if (e->is<ModifiersDeclaration>()) {
@ -3080,11 +3094,10 @@ SpvId SPIRVCodeGenerator::writeInterfaceBlock(const InterfaceBlock& intf, bool a
update_sk_in_count(m, &fSkInCount);
}
}
typeId = this->getType(
*Type::MakeArrayType("sk_in", intf.variable().type().componentType(), fSkInCount),
memoryLayout);
typeId = this->getType(*Type::MakeArrayType("sk_in", type.componentType(), fSkInCount),
memoryLayout);
} else {
typeId = this->getType(*type, memoryLayout);
typeId = this->getType(type, memoryLayout);
}
if (intfModifiers.fLayout.fBuiltin == -1) {
this->writeInstruction(SpvOpDecorate, typeId, SpvDecorationBlock, fDecorationBuffer);
@ -3097,7 +3110,7 @@ SpvId SPIRVCodeGenerator::writeInterfaceBlock(const InterfaceBlock& intf, bool a
layout.fSet = 0;
}
this->writeLayout(layout, result);
fVariableMap[&intf.variable()] = result;
fVariableMap[&intfVar] = result;
return result;
}
@ -3556,6 +3569,91 @@ void SPIRVCodeGenerator::writeUniformBuffer(std::shared_ptr<SymbolTable> topLeve
fUniformBufferId = this->writeInterfaceBlock(*fUniformBuffer.fInterfaceBlock);
}
void SPIRVCodeGenerator::addRTFlipUniform(int offset) {
if (fWroteRTFlip) {
return;
}
// Flip variable hasn't been written yet. This means we don't have an existing
// interface block, so we're free to just synthesize one.
fWroteRTFlip = true;
std::vector<Type::Field> fields;
if (fProgram.fConfig->fSettings.fRTFlipOffset < 0) {
fErrors.error(offset, "RTFlipOffset is negative");
}
fields.emplace_back(Modifiers(Layout(/*flags=*/0,
/*location=*/-1,
fProgram.fConfig->fSettings.fRTFlipOffset,
/*binding=*/-1,
/*index=*/-1,
/*set=*/-1,
/*builtin=*/-1,
/*inputAttachmentIndex=*/-1,
Layout::kUnspecified_Primitive,
/*maxVertices=*/1,
/*invocations=*/-1,
/*when=*/"",
Layout::CType::kDefault),
/*flags=*/0),
SKSL_RTFLIP_NAME,
fContext.fTypes.fFloat2.get());
String name("sksl_synthetic_uniforms");
const Type* intfStruct =
fSynthetics.takeOwnershipOfSymbol(Type::MakeStructType(/*offset=*/-1, name, fields));
int binding = fProgram.fConfig->fSettings.fRTFlipBinding;
if (binding == -1) {
fErrors.error(offset, "layout(binding=...) is required in SPIR-V");
}
int set = fProgram.fConfig->fSettings.fRTFlipSet;
if (set == -1) {
fErrors.error(offset, "layout(set=...) is required in SPIR-V");
}
bool usePushConstants = fProgram.fConfig->fSettings.fUsePushConstants;
int flags = usePushConstants ? Layout::Flag::kPushConstant_Flag : 0;
if (fProgram.fPool) {
fProgram.fPool->attachToThread();
}
Modifiers modifiers(Layout(flags,
/*location=*/-1,
/*offset=*/-1,
binding,
/*index=*/-1,
set,
/*builtin=*/-1,
/*inputAttachmentIndex=*/-1,
Layout::kUnspecified_Primitive,
/*maxVertices=*/-1,
/*invocations=*/-1,
/*when=*/"",
Layout::CType::kDefault),
Modifiers::kUniform_Flag);
const Modifiers* modsPtr = fProgram.fModifiers->add(modifiers);
if (fProgram.fPool) {
fProgram.fPool->detachFromThread();
}
const Variable* intfVar = fSynthetics.takeOwnershipOfSymbol(
std::make_unique<Variable>(/*offset=*/-1,
modsPtr,
name,
intfStruct,
/*builtin=*/false,
Variable::Storage::kGlobal));
if (fProgram.fPool) {
fProgram.fPool->attachToThread();
}
fProgram.fSymbols->add(std::make_unique<Field>(/*offset=*/-1, intfVar, /*field=*/0));
if (fProgram.fPool) {
fProgram.fPool->detachFromThread();
}
InterfaceBlock intf(/*offset=*/-1,
intfVar,
name,
/*instanceName=*/"",
/*arraySize=*/0,
std::make_shared<SymbolTable>(&fErrors, /*builtin=*/false));
this->writeInterfaceBlock(intf, false);
}
void SPIRVCodeGenerator::writeInstructions(const Program& program, OutputStream& out) {
fGLSLExtendedInstructions = this->nextId(nullptr);
StringStream body;

View File

@ -208,7 +208,7 @@ private:
void writeProgramElement(const ProgramElement& pe, OutputStream& out);
SpvId writeInterfaceBlock(const InterfaceBlock& intf, bool appendRTHeight = true);
SpvId writeInterfaceBlock(const InterfaceBlock& intf, bool appendRTFlip = true);
SpvId writeFunctionStart(const FunctionDeclaration& f, OutputStream& out);
@ -237,6 +237,14 @@ private:
SpvId signedInst, SpvId unsignedInst,
const std::vector<SpvId>& args, OutputStream& out);
/**
* Promotes an expression to a vector. If the expression is already a vector with vectorSize
* columns, returns it unmodified. If the expression is a scalar, either promotes it to a
* vector (if vectorSize > 1) or returns it unmodified (if vectorSize == 1). Asserts if the
* expression is already a vector and it does not have vectorSize columns.
*/
SpvId vectorize(const Expression& expr, int vectorSize, OutputStream& out);
/**
* Given a list of potentially mixed scalars and vectors, promotes the scalars to match the
* size of the vectors and returns the ids of the written expressions. e.g. given (float, vec2),
@ -455,6 +463,8 @@ private:
void writeUniformBuffer(std::shared_ptr<SymbolTable> topLevelSymbolTable);
void addRTFlipUniform(int offset);
const Context& fContext;
const MemoryLayout fDefaultLayout;
@ -485,9 +495,7 @@ private:
SpvId fCurrentBlock;
std::stack<SpvId> fBreakTarget;
std::stack<SpvId> fContinueTarget;
SpvId fRTHeightStructId = (SpvId) -1;
SpvId fRTHeightFieldIndex = (SpvId) -1;
SpvStorageClass_ fRTHeightStorageClass;
bool fWroteRTFlip = false;
// holds variables synthesized during output, for lifetime purposes
SymbolTable fSynthetics;
int fSkInCount = 1;

View File

@ -42,7 +42,7 @@ DSLWriter::DSLWriter(SkSL::Compiler* compiler, SkSL::ProgramKind kind,
fOldConfig = fCompiler->fContext->fConfig;
if (!isModule) {
if (compiler->context().fCaps.useNodePools()) {
if (compiler->context().fCaps.useNodePools() && settings.fDSLUseMemoryPool) {
fPool = Pool::Create();
fPool->attachToThread();
}
@ -231,7 +231,7 @@ const SkSL::Variable* DSLWriter::Var(DSLVar& var) {
var.fStorage);
SkSL::Variable* varPtr = skslvar.get();
// We can't call VarDeclaration::Convert directly here, because the IRGenerator has special
// treatment for sk_FragColor and sk_RTHeight that we want to preserve in DSL.
// treatment for sk_FragColor that we want to preserve in DSL.
var.fDeclaration = DSLWriter::IRGenerator().convertVarDeclaration(
std::move(skslvar),
var.fInitialValue.releaseIfValid());

View File

@ -27,8 +27,8 @@
#include "src/gpu/vk/GrVkCaps.h"
#endif
// name of the render target height uniform
#define SKSL_RTHEIGHT_NAME "u_skRTHeight"
// name of the uniform used to handle features that are sensitive to whether Y is flipped.
#define SKSL_RTFLIP_NAME "u_skRTFlip"
namespace SkSL {
@ -67,25 +67,11 @@ struct Program {
using Settings = ProgramSettings;
struct Inputs {
// if true, this program requires the render target height uniform to be defined
bool fRTHeight;
// if true, this program must be recompiled if the flipY setting changes. If false, the
// program will compile to the same code regardless of the flipY setting.
bool fFlipY;
// If true, this program includes a call to `dFdy`.
bool fUsesYDerivative;
void reset() {
fRTHeight = false;
fFlipY = false;
fUsesYDerivative = false;
}
bool isEmpty() {
return !fRTHeight && !fFlipY && !fUsesYDerivative;
bool fUseFlipRTUniform = false;
bool operator==(const Inputs& that) const {
return fUseFlipRTUniform == that.fUseFlipRTUniform;
}
bool operator!=(const Inputs& that) const { return !(*this == that); }
};
Program(std::unique_ptr<String> source,

View File

@ -7,6 +7,7 @@ OpName %sk_FragColor "sk_FragColor"
OpName %sk_Clockwise "sk_Clockwise"
OpName %_UniformBuffer "_UniformBuffer"
OpMemberName %_UniformBuffer 0 "a"
OpMemberName %_UniformBuffer 1 "u_skRTFlip"
OpName %main "main"
OpDecorate %sk_FragColor RelaxedPrecision
OpDecorate %sk_FragColor Location 0
@ -14,10 +15,12 @@ OpDecorate %sk_FragColor Index 0
OpDecorate %sk_Clockwise BuiltIn FrontFacing
OpMemberDecorate %_UniformBuffer 0 Offset 0
OpMemberDecorate %_UniformBuffer 0 RelaxedPrecision
OpMemberDecorate %_UniformBuffer 1 Offset 32
OpDecorate %_UniformBuffer Block
OpDecorate %10 Binding 0
OpDecorate %10 DescriptorSet 0
OpDecorate %21 RelaxedPrecision
OpDecorate %11 Binding 0
OpDecorate %11 DescriptorSet 0
OpDecorate %23 RelaxedPrecision
OpDecorate %29 RelaxedPrecision
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
@ -25,21 +28,28 @@ OpDecorate %21 RelaxedPrecision
%bool = OpTypeBool
%_ptr_Input_bool = OpTypePointer Input %bool
%sk_Clockwise = OpVariable %_ptr_Input_bool Input
%_UniformBuffer = OpTypeStruct %float
%v2float = OpTypeVector %float 2
%_UniformBuffer = OpTypeStruct %float %v2float
%_ptr_Uniform__UniformBuffer = OpTypePointer Uniform %_UniformBuffer
%10 = OpVariable %_ptr_Uniform__UniformBuffer Uniform
%11 = OpVariable %_ptr_Uniform__UniformBuffer Uniform
%void = OpTypeVoid
%14 = OpTypeFunction %void
%16 = OpTypeFunction %void
%_ptr_Uniform_float = OpTypePointer Uniform %float
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float
%_ptr_Output_float = OpTypePointer Output %float
%main = OpFunction %void None %14
%15 = OpLabel
%17 = OpAccessChain %_ptr_Uniform_float %10 %int_0
%21 = OpLoad %float %17
%16 = OpDPdy %float %21
%22 = OpAccessChain %_ptr_Output_float %sk_FragColor %int_0
OpStore %22 %16
%main = OpFunction %void None %16
%17 = OpLabel
%19 = OpAccessChain %_ptr_Uniform_float %11 %int_0
%23 = OpLoad %float %19
%18 = OpDPdy %float %23
%25 = OpAccessChain %_ptr_Uniform_v2float %11 %int_1
%27 = OpLoad %v2float %25
%28 = OpCompositeExtract %float %27 1
%29 = OpFMul %float %18 %28
%30 = OpAccessChain %_ptr_Output_float %sk_FragColor %int_0
OpStore %30 %29
OpReturn
OpFunctionEnd

View File

@ -1,6 +1,7 @@
uniform vec2 u_skRTFlip;
out vec4 sk_FragColor;
uniform float a;
void main() {
sk_FragColor.x = dFdy(a);
sk_FragColor.x = u_skRTFlip.y * dFdy(a);
}

View File

@ -9,9 +9,12 @@ struct Inputs {
struct Outputs {
float4 sk_FragColor [[color(0)]];
};
fragment Outputs fragmentMain(Inputs _in [[stage_in]], constant Uniforms& _uniforms [[buffer(0)]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
struct sksl_synthetic_uniforms {
float2 u_skRTFlip;
};
fragment Outputs fragmentMain(Inputs _in [[stage_in]], constant Uniforms& _uniforms [[buffer(0)]], constant sksl_synthetic_uniforms& _anonInterface0 [[buffer(1)]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
Outputs _out;
(void)_out;
_out.sk_FragColor.x = dfdy(_uniforms.a);
_out.sk_FragColor.x = _anonInterface0.u_skRTFlip.y*dfdy(_uniforms.a);
return _out;
}

View File

@ -6,13 +6,20 @@ OpExecutionMode %main OriginUpperLeft
OpName %sk_FragColor "sk_FragColor"
OpName %sk_Clockwise "sk_Clockwise"
OpName %main "main"
OpName %sksl_synthetic_uniforms "sksl_synthetic_uniforms"
OpMemberName %sksl_synthetic_uniforms 0 "u_skRTFlip"
OpDecorate %sk_FragColor RelaxedPrecision
OpDecorate %sk_FragColor Location 0
OpDecorate %sk_FragColor Index 0
OpDecorate %sk_Clockwise BuiltIn FrontFacing
OpDecorate %13 RelaxedPrecision
OpDecorate %19 RelaxedPrecision
OpDecorate %20 RelaxedPrecision
OpMemberDecorate %sksl_synthetic_uniforms 0 Offset 32
OpDecorate %sksl_synthetic_uniforms Block
OpDecorate %13 Binding 0
OpDecorate %13 DescriptorSet 0
OpDecorate %31 RelaxedPrecision
OpDecorate %32 RelaxedPrecision
OpDecorate %37 RelaxedPrecision
OpDecorate %38 RelaxedPrecision
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
@ -22,16 +29,40 @@ OpDecorate %20 RelaxedPrecision
%sk_Clockwise = OpVariable %_ptr_Input_bool Input
%void = OpTypeVoid
%11 = OpTypeFunction %void
%v2float = OpTypeVector %float 2
%sksl_synthetic_uniforms = OpTypeStruct %v2float
%_ptr_Uniform_sksl_synthetic_uniforms = OpTypePointer Uniform %sksl_synthetic_uniforms
%13 = OpVariable %_ptr_Uniform_sksl_synthetic_uniforms Uniform
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float
%float_0 = OpConstant %float 0
%_ptr_Function_bool = OpTypePointer Function %bool
%int_1 = OpConstant %int 1
%int_n1 = OpConstant %int -1
%main = OpFunction %void None %11
%12 = OpLabel
%13 = OpLoad %bool %sk_Clockwise
%14 = OpLogicalNot %bool %13
%15 = OpSelect %int %14 %int_1 %int_n1
%19 = OpConvertSToF %float %15
%20 = OpCompositeConstruct %v4float %19 %19 %19 %19
OpStore %sk_FragColor %20
%25 = OpVariable %_ptr_Function_bool Function
%19 = OpAccessChain %_ptr_Uniform_v2float %13 %int_0
%21 = OpLoad %v2float %19
%22 = OpCompositeExtract %float %21 1
%24 = OpFOrdGreaterThan %bool %22 %float_0
OpSelectionMerge %29 None
OpBranchConditional %24 %27 %28
%27 = OpLabel
%31 = OpLoad %bool %sk_Clockwise
%30 = OpLogicalNot %bool %31
OpStore %25 %30
OpBranch %29
%28 = OpLabel
%32 = OpLoad %bool %sk_Clockwise
OpStore %25 %32
OpBranch %29
%29 = OpLabel
%33 = OpLoad %bool %25
%34 = OpSelect %int %33 %int_1 %int_n1
%37 = OpConvertSToF %float %34
%38 = OpCompositeConstruct %v4float %37 %37 %37 %37
OpStore %sk_FragColor %38
OpReturn
OpFunctionEnd

View File

@ -1,5 +1,10 @@
uniform vec2 u_skRTFlip;
out vec4 sk_FragColor;
void main() {
sk_FragColor = vec4(float(gl_FrontFacing ? 1 : -1));
bool sk_Clockwise = gl_FrontFacing;
if (u_skRTFlip.y < 0.0) {
sk_Clockwise = !sk_Clockwise;
}
sk_FragColor = vec4(float(sk_Clockwise ? 1 : -1));
}

View File

@ -6,9 +6,12 @@ struct Inputs {
struct Outputs {
float4 sk_FragColor [[color(0)]];
};
fragment Outputs fragmentMain(Inputs _in [[stage_in]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
struct sksl_synthetic_uniforms {
float2 u_skRTFlip;
};
fragment Outputs fragmentMain(Inputs _in [[stage_in]], constant sksl_synthetic_uniforms& _anonInterface0 [[buffer(1)]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
Outputs _out;
(void)_out;
_out.sk_FragColor = float4(float((!_frontFacing) ? 1 : -1));
_out.sk_FragColor = float4(float((_anonInterface0.u_skRTFlip.y < 0 ? _frontFacing : !_frontFacing) ? 1 : -1));
return _out;
}

View File

@ -1,5 +0,0 @@
out vec4 sk_FragColor;
void main() {
(sk_FragColor.x = dFdx(1.0) , sk_FragColor.y = -dFdy(1.0));
}

View File

@ -1,5 +0,0 @@
out vec4 sk_FragColor;
void main() {
(sk_FragColor.x = dFdx(1.0) , sk_FragColor.y = dFdy(1.0));
}

View File

@ -0,0 +1,65 @@
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %sk_FragColor %sk_FragCoord %sk_Clockwise
OpExecutionMode %main OriginUpperLeft
OpName %sk_FragColor "sk_FragColor"
OpName %sk_FragCoord "sk_FragCoord"
OpName %sk_Clockwise "sk_Clockwise"
OpName %main "main"
OpName %sksl_synthetic_uniforms "sksl_synthetic_uniforms"
OpMemberName %sksl_synthetic_uniforms 0 "u_skRTFlip"
OpDecorate %sk_FragColor RelaxedPrecision
OpDecorate %sk_FragColor Location 0
OpDecorate %sk_FragColor Index 0
OpDecorate %sk_FragCoord BuiltIn FragCoord
OpDecorate %sk_Clockwise BuiltIn FrontFacing
OpMemberDecorate %sksl_synthetic_uniforms 0 Offset 32
OpDecorate %sksl_synthetic_uniforms Block
OpDecorate %15 Binding 0
OpDecorate %15 DescriptorSet 0
OpDecorate %40 RelaxedPrecision
OpDecorate %41 RelaxedPrecision
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%sk_FragColor = OpVariable %_ptr_Output_v4float Output
%_ptr_Input_v4float = OpTypePointer Input %v4float
%sk_FragCoord = OpVariable %_ptr_Input_v4float Input
%bool = OpTypeBool
%_ptr_Input_bool = OpTypePointer Input %bool
%sk_Clockwise = OpVariable %_ptr_Input_bool Input
%void = OpTypeVoid
%13 = OpTypeFunction %void
%v2float = OpTypeVector %float 2
%sksl_synthetic_uniforms = OpTypeStruct %v2float
%_ptr_Uniform_sksl_synthetic_uniforms = OpTypePointer Uniform %sksl_synthetic_uniforms
%15 = OpVariable %_ptr_Uniform_sksl_synthetic_uniforms Uniform
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float
%main = OpFunction %void None %13
%14 = OpLabel
%19 = OpLoad %v4float %sk_FragCoord
%20 = OpCompositeExtract %float %19 0
%23 = OpAccessChain %_ptr_Uniform_v2float %15 %int_0
%25 = OpLoad %v2float %23
%26 = OpCompositeExtract %float %25 0
%27 = OpAccessChain %_ptr_Uniform_v2float %15 %int_0
%28 = OpLoad %v2float %27
%29 = OpCompositeExtract %float %28 1
%30 = OpLoad %v4float %sk_FragCoord
%31 = OpCompositeExtract %float %30 1
%32 = OpFMul %float %29 %31
%33 = OpFAdd %float %26 %32
%34 = OpLoad %v4float %sk_FragCoord
%35 = OpCompositeExtract %float %34 2
%36 = OpLoad %v4float %sk_FragCoord
%37 = OpCompositeExtract %float %36 3
%38 = OpCompositeConstruct %v4float %20 %33 %35 %37
%39 = OpVectorShuffle %v2float %38 %38 0 1
%40 = OpLoad %v4float %sk_FragColor
%41 = OpVectorShuffle %v4float %40 %39 4 5 2 3
OpStore %sk_FragColor %41
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,7 @@
uniform vec2 u_skRTFlip;
out vec4 sk_FragColor;
void main() {
vec4 sk_FragCoord = vec4(gl_FragCoord.x, u_skRTFlip.x + u_skRTFlip.y * gl_FragCoord.y, gl_FragCoord.z, gl_FragCoord.w);
sk_FragColor.xy = sk_FragCoord.xy;
}

View File

@ -0,0 +1,17 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct Inputs {
};
struct Outputs {
float4 sk_FragColor [[color(0)]];
};
struct sksl_synthetic_uniforms {
float2 u_skRTFlip;
};
fragment Outputs fragmentMain(Inputs _in [[stage_in]], constant sksl_synthetic_uniforms& _anonInterface0 [[buffer(1)]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
Outputs _out;
(void)_out;
_out.sk_FragColor.xy = float4(_fragCoord.x, _anonInterface0.u_skRTFlip.x + _anonInterface0.u_skRTFlip.y * _fragCoord.y, 0.0, _fragCoord.w).xy;
return _out;
}

View File

@ -1,6 +0,0 @@
### Compilation failed:
error: 3: RTHeightOffset is negative
error: 3: layout(binding=...) is required in SPIR-V
error: 3: layout(set=...) is required in SPIR-V
3 errors

View File

@ -1,6 +0,0 @@
#version 400
out vec4 sk_FragColor;
void main() {
vec4 sk_FragCoord = vec4(gl_FragCoord.x, u_skRTHeight - gl_FragCoord.y, gl_FragCoord.z, gl_FragCoord.w);
sk_FragColor.xy = sk_FragCoord.xy;
}

View File

@ -1,14 +0,0 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct Inputs {
};
struct Outputs {
float4 sk_FragColor [[color(0)]];
};
fragment Outputs fragmentMain(Inputs _in [[stage_in]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
Outputs _out;
(void)_out;
_out.sk_FragColor.xy = float4(_fragCoord.x, _fragCoord.y, 0.0, _fragCoord.w).xy;
return _out;
}

View File

@ -1,6 +0,0 @@
### Compilation failed:
error: 3: RTHeightOffset is negative
error: 3: layout(binding=...) is required in SPIR-V
error: 3: layout(set=...) is required in SPIR-V
3 errors

View File

@ -1,6 +0,0 @@
#version 400
layout(origin_upper_left) in vec4 gl_FragCoord;
out vec4 sk_FragColor;
void main() {
sk_FragColor.xy = gl_FragCoord.xy;
}

View File

@ -1,14 +0,0 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct Inputs {
};
struct Outputs {
float4 sk_FragColor [[color(0)]];
};
fragment Outputs fragmentMain(Inputs _in [[stage_in]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
Outputs _out;
(void)_out;
_out.sk_FragColor.xy = float4(_fragCoord.x, _fragCoord.y, 0.0, _fragCoord.w).xy;
return _out;
}

View File

@ -1,6 +0,0 @@
### Compilation failed:
error: 3: RTHeightOffset is negative
error: 3: layout(binding=...) is required in SPIR-V
error: 3: layout(set=...) is required in SPIR-V
3 errors

View File

@ -1,6 +0,0 @@
#version 110
#extension GL_ARB_fragment_coord_conventions : require
layout(origin_upper_left) in vec4 gl_FragCoord;
void main() {
gl_FragColor.xy = gl_FragCoord.xy;
}

View File

@ -1,14 +0,0 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct Inputs {
};
struct Outputs {
float4 sk_FragColor [[color(0)]];
};
fragment Outputs fragmentMain(Inputs _in [[stage_in]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
Outputs _out;
(void)_out;
_out.sk_FragColor.xy = float4(_fragCoord.x, _fragCoord.y, 0.0, _fragCoord.w).xy;
return _out;
}

View File

@ -1,5 +1,7 @@
uniform vec2 u_skRTFlip;
out vec4 sk_FragColor;
void main() {
sk_FragColor.xy = gl_FragCoord.xy;
vec4 sk_FragCoord = vec4(gl_FragCoord.x, u_skRTFlip.x + u_skRTFlip.y * gl_FragCoord.y, gl_FragCoord.z, gl_FragCoord.w);
sk_FragColor.xy = sk_FragCoord.xy;
}