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:
parent
550de7f872
commit
d8d85b9b89
@ -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",
|
||||
|
@ -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
|
||||
|
@ -1,6 +0,0 @@
|
||||
/*#pragma settings FlipY*/
|
||||
|
||||
void main() {
|
||||
sk_FragColor.r = half(dFdx(1)),
|
||||
sk_FragColor.g = half(dFdy(1));
|
||||
}
|
@ -1,3 +1 @@
|
||||
/*#pragma settings Default FlipY*/
|
||||
|
||||
void main() { sk_FragColor.xy = half2(sk_FragCoord.xy); }
|
@ -1,3 +0,0 @@
|
||||
/*#pragma settings FragCoordsNew FlipY*/
|
||||
|
||||
void main() { sk_FragColor.xy = half2(sk_FragCoord.xy); }
|
@ -1,3 +0,0 @@
|
||||
/*#pragma settings FragCoordsOld FlipY*/
|
||||
|
||||
void main() { sk_FragColor.xy = half2(sk_FragCoord.xy); }
|
@ -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();
|
||||
|
@ -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-
|
||||
|
@ -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(¤tOffset, kFloat_GrSLType, 0);
|
||||
return get_ubo_offset(¤tOffset, kFloat2_GrSLType, 0);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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:
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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];
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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(¤tOffset, kFloat_GrSLType, 0, layout);
|
||||
return get_aligned_offset(¤tOffset, 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();
|
||||
}
|
||||
|
@ -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];
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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";
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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("};");
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
|
||||
out vec4 sk_FragColor;
|
||||
void main() {
|
||||
(sk_FragColor.x = dFdx(1.0) , sk_FragColor.y = -dFdy(1.0));
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
|
||||
out vec4 sk_FragColor;
|
||||
void main() {
|
||||
(sk_FragColor.x = dFdx(1.0) , sk_FragColor.y = dFdy(1.0));
|
||||
}
|
65
tests/sksl/shared/FragCoords.asm.frag
Normal file
65
tests/sksl/shared/FragCoords.asm.frag
Normal 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
|
7
tests/sksl/shared/FragCoords.glsl
Normal file
7
tests/sksl/shared/FragCoords.glsl
Normal 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;
|
||||
}
|
17
tests/sksl/shared/FragCoords.metal
Normal file
17
tests/sksl/shared/FragCoords.metal
Normal 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;
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user