sksl: Add a "sk_Clockwise" built-in
This allows us to identify clockwise-winding triangles, in terms of Skia device space, in all backends and with all render target origins. Bug: skia: Change-Id: I220e1c459e0129d1cc4dee6458ef94277fbedd21 Reviewed-on: https://skia-review.googlesource.com/142662 Commit-Queue: Chris Dalton <csmartdalton@google.com> Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
This commit is contained in:
parent
f4f6bbfada
commit
49d14e98fe
191
gm/clockwise.cpp
Normal file
191
gm/clockwise.cpp
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright 2018 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "gm.h"
|
||||
|
||||
#include "GrClip.h"
|
||||
#include "GrContext.h"
|
||||
#include "GrGpuCommandBuffer.h"
|
||||
#include "GrMemoryPool.h"
|
||||
#include "GrOpFlushState.h"
|
||||
#include "GrRenderTargetContext.h"
|
||||
#include "GrRenderTargetContextPriv.h"
|
||||
#include "GrRenderTarget.h"
|
||||
#include "glsl/GrGLSLFragmentShaderBuilder.h"
|
||||
#include "glsl/GrGLSLGeometryProcessor.h"
|
||||
#include "glsl/GrGLSLVarying.h"
|
||||
#include "glsl/GrGLSLVertexGeoBuilder.h"
|
||||
|
||||
namespace skiagm {
|
||||
|
||||
static constexpr GrGeometryProcessor::Attribute gVertex{"vertex", kFloat2_GrVertexAttribType};
|
||||
|
||||
/**
|
||||
* This is a GPU-backend specific test. It ensures that SkSL properly identifies clockwise-winding
|
||||
* triangles (sk_Clockwise), in terms of to Skia device space, in all backends and with all render
|
||||
* target origins. We draw clockwise triangles green and counter-clockwise red.
|
||||
*/
|
||||
class ClockwiseGM : public GM {
|
||||
private:
|
||||
SkString onShortName() final { return SkString("clockwise"); }
|
||||
SkISize onISize() override { return SkISize::Make(300, 200); }
|
||||
void onDraw(SkCanvas*) override;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SkSL code.
|
||||
|
||||
class ClockwiseTestProcessor : public GrGeometryProcessor {
|
||||
public:
|
||||
ClockwiseTestProcessor(bool readSkFragCoord)
|
||||
: GrGeometryProcessor(kClockwiseTestProcessor_ClassID)
|
||||
, fReadSkFragCoord(readSkFragCoord) {
|
||||
this->setVertexAttributeCnt(1);
|
||||
}
|
||||
const char* name() const override { return "ClockwiseTestProcessor"; }
|
||||
void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {
|
||||
b->add32(fReadSkFragCoord);
|
||||
}
|
||||
GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
|
||||
|
||||
private:
|
||||
const Attribute& onVertexAttribute(int i) const override { return gVertex; }
|
||||
|
||||
const bool fReadSkFragCoord;
|
||||
|
||||
friend class GLSLClockwiseTestProcessor;
|
||||
};
|
||||
|
||||
class GLSLClockwiseTestProcessor : public GrGLSLGeometryProcessor {
|
||||
void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
|
||||
FPCoordTransformIter&& transformIter) override {}
|
||||
|
||||
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
|
||||
const ClockwiseTestProcessor& proc = args.fGP.cast<ClockwiseTestProcessor>();
|
||||
args.fVaryingHandler->emitAttributes(proc);
|
||||
gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex");
|
||||
args.fFragBuilder->codeAppendf(
|
||||
"%s = sk_Clockwise ? half4(0,1,0,1) : half4(1,0,0,1);", args.fOutputColor);
|
||||
if (!proc.fReadSkFragCoord) {
|
||||
args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputCoverage);
|
||||
} else {
|
||||
// Verify layout(origin_upper_left) on gl_FragCoord does not affect gl_FrontFacing.
|
||||
args.fFragBuilder->codeAppendf("%s = half4(min(sk_FragCoord.y, 1));",
|
||||
args.fOutputCoverage);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
GrGLSLPrimitiveProcessor* ClockwiseTestProcessor::createGLSLInstance(
|
||||
const GrShaderCaps&) const {
|
||||
return new GLSLClockwiseTestProcessor;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Draw Op.
|
||||
|
||||
class ClockwiseTestOp : public GrDrawOp {
|
||||
public:
|
||||
DEFINE_OP_CLASS_ID
|
||||
|
||||
static std::unique_ptr<GrDrawOp> Make(GrContext* context, bool readSkFragCoord, int y = 0) {
|
||||
GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
|
||||
return pool->allocate<ClockwiseTestOp>(readSkFragCoord, y);
|
||||
}
|
||||
|
||||
private:
|
||||
ClockwiseTestOp(bool readSkFragCoord, float y)
|
||||
: GrDrawOp(ClassID()), fReadSkFragCoord(readSkFragCoord), fY(y) {
|
||||
this->setBounds(SkRect::MakeIWH(300, 100), HasAABloat::kNo, IsZeroArea::kNo);
|
||||
}
|
||||
|
||||
const char* name() const override { return "ClockwiseTestOp"; }
|
||||
FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
|
||||
RequiresDstTexture finalize(const GrCaps&, const GrAppliedClip*) override {
|
||||
return RequiresDstTexture::kNo;
|
||||
}
|
||||
bool onCombineIfPossible(GrOp* other, const GrCaps& caps) override { return false; }
|
||||
void onPrepare(GrOpFlushState*) override {}
|
||||
void onExecute(GrOpFlushState* flushState) override {
|
||||
SkPoint vertices[4] = {
|
||||
{100, fY},
|
||||
{0, fY+100},
|
||||
{0, fY},
|
||||
{100, fY+100},
|
||||
};
|
||||
sk_sp<GrBuffer> vertexBuffer(flushState->resourceProvider()->createBuffer(
|
||||
sizeof(vertices), kVertex_GrBufferType, kStatic_GrAccessPattern,
|
||||
GrResourceProvider::kNone_Flag, vertices));
|
||||
GrPipeline pipeline(flushState->drawOpArgs().fProxy, GrPipeline::ScissorState::kDisabled,
|
||||
SkBlendMode::kPlus);
|
||||
GrMesh mesh(GrPrimitiveType::kTriangleStrip);
|
||||
mesh.setNonIndexedNonInstanced(4);
|
||||
mesh.setVertexData(vertexBuffer.get());
|
||||
flushState->rtCommandBuffer()->draw(ClockwiseTestProcessor(fReadSkFragCoord), pipeline,
|
||||
nullptr, nullptr, &mesh, 1, SkRect::MakeIWH(100, 100));
|
||||
}
|
||||
|
||||
const bool fReadSkFragCoord;
|
||||
const float fY;
|
||||
|
||||
friend class ::GrOpMemoryPool; // for ctor
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Test.
|
||||
|
||||
void ClockwiseGM::onDraw(SkCanvas* canvas) {
|
||||
GrContext* ctx = canvas->getGrContext();
|
||||
GrRenderTargetContext* rtc = canvas->internal_private_accessTopLayerRenderTargetContext();
|
||||
if (!ctx || !rtc) {
|
||||
DrawGpuOnlyMessage(canvas);
|
||||
return;
|
||||
}
|
||||
|
||||
rtc->clear(nullptr, GrColorPackRGBA(0,0,0,255),
|
||||
GrRenderTargetContext::CanClearFullscreen::kYes);
|
||||
|
||||
// Draw the test directly to the frame buffer.
|
||||
rtc->priv().testingOnly_addDrawOp(ClockwiseTestOp::Make(ctx, false, 0));
|
||||
rtc->priv().testingOnly_addDrawOp(ClockwiseTestOp::Make(ctx, true, 100));
|
||||
|
||||
// Draw the test to an off-screen, top-down render target.
|
||||
if (auto topLeftRTC = ctx->contextPriv().makeDeferredRenderTargetContext(
|
||||
SkBackingFit::kExact, 100, 200, rtc->accessRenderTarget()->config(),
|
||||
nullptr, 1, GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin, nullptr,
|
||||
SkBudgeted::kYes)) {
|
||||
topLeftRTC->clear(nullptr, 0, GrRenderTargetContext::CanClearFullscreen::kYes);
|
||||
topLeftRTC->priv().testingOnly_addDrawOp(ClockwiseTestOp::Make(ctx, false, 0));
|
||||
topLeftRTC->priv().testingOnly_addDrawOp(ClockwiseTestOp::Make(ctx, true, 100));
|
||||
rtc->drawTexture(GrNoClip(), sk_ref_sp(topLeftRTC->asTextureProxy()),
|
||||
GrSamplerState::Filter::kNearest, 0xffffffff, {0,0,100,200},
|
||||
{100,0,200,200}, GrAA::kNo,
|
||||
SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint, SkMatrix::I(),
|
||||
nullptr);
|
||||
}
|
||||
|
||||
// Draw the test to an off-screen, bottom-up render target.
|
||||
if (auto topLeftRTC = ctx->contextPriv().makeDeferredRenderTargetContext(
|
||||
SkBackingFit::kExact, 100, 200, rtc->accessRenderTarget()->config(),
|
||||
nullptr, 1, GrMipMapped::kNo, kBottomLeft_GrSurfaceOrigin, nullptr,
|
||||
SkBudgeted::kYes)) {
|
||||
topLeftRTC->clear(nullptr, 0, GrRenderTargetContext::CanClearFullscreen::kYes);
|
||||
topLeftRTC->priv().testingOnly_addDrawOp(ClockwiseTestOp::Make(ctx, false, 0));
|
||||
topLeftRTC->priv().testingOnly_addDrawOp(ClockwiseTestOp::Make(ctx, true, 100));
|
||||
rtc->drawTexture(GrNoClip(), sk_ref_sp(topLeftRTC->asTextureProxy()),
|
||||
GrSamplerState::Filter::kNearest, 0xffffffff, {0,0,100,200},
|
||||
{200,0,300,200}, GrAA::kNo,
|
||||
SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint, SkMatrix::I(),
|
||||
nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DEF_GM( return new ClockwiseGM(); )
|
||||
|
||||
}
|
@ -70,6 +70,7 @@ gm_sources = [
|
||||
"$_gm/clip_strokerect.cpp",
|
||||
"$_gm/clipdrawdraw.cpp",
|
||||
"$_gm/clippedbitmapshaders.cpp",
|
||||
"$_gm/clockwise.cpp",
|
||||
"$_gm/color4f.cpp",
|
||||
"$_gm/coloremoji.cpp",
|
||||
"$_gm/coloremoji_blendmodes.cpp",
|
||||
|
@ -71,6 +71,7 @@ public:
|
||||
kButtCapStrokedCircleGeometryProcessor_ClassID,
|
||||
kCircleGeometryProcessor_ClassID,
|
||||
kCircularRRectEffect_ClassID,
|
||||
kClockwiseTestProcessor_ClassID,
|
||||
kColorMatrixEffect_ClassID,
|
||||
kColorTableEffect_ClassID,
|
||||
kComposeOneFragmentProcessor_ClassID,
|
||||
|
@ -359,9 +359,9 @@ void GrGLGpu::onResetContext(uint32_t resetBits) {
|
||||
|
||||
// We don't use face culling.
|
||||
GL_CALL(Disable(GR_GL_CULL_FACE));
|
||||
// We do use separate stencil. Our algorithms don't care which face is front vs. back so
|
||||
// just set this to the default for self-consistency.
|
||||
GL_CALL(FrontFace(GR_GL_CCW));
|
||||
|
||||
// Setting the front face keeps gl_FrontFacing consistent in device space.
|
||||
fHWFrontFace = GR_GL_NONE;
|
||||
|
||||
fHWBufferState[kTexel_GrBufferType].invalidate();
|
||||
fHWBufferState[kDrawIndirect_GrBufferType].invalidate();
|
||||
@ -1704,6 +1704,7 @@ bool GrGLGpu::flushGLState(const GrPrimitiveProcessor& primProc,
|
||||
fHWProgram->setData(primProc, pipeline);
|
||||
|
||||
GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(pipeline.renderTarget());
|
||||
GrSurfaceOrigin origin = pipeline.proxy()->origin();
|
||||
GrStencilSettings stencil;
|
||||
if (pipeline.isStencilEnabled()) {
|
||||
// TODO: attach stencil and create settings during render target flush.
|
||||
@ -1715,16 +1716,16 @@ bool GrGLGpu::flushGLState(const GrPrimitiveProcessor& primProc,
|
||||
if (pipeline.isScissorEnabled()) {
|
||||
static constexpr SkIRect kBogusScissor{0, 0, 1, 1};
|
||||
GrScissorState state(fixedDynamicState ? fixedDynamicState->fScissorRect : kBogusScissor);
|
||||
this->flushScissor(state, glRT->getViewport(), pipeline.proxy()->origin());
|
||||
this->flushScissor(state, glRT->getViewport(), origin);
|
||||
} else {
|
||||
this->disableScissor();
|
||||
}
|
||||
this->flushWindowRectangles(pipeline.getWindowRectsState(), glRT, pipeline.proxy()->origin());
|
||||
this->flushWindowRectangles(pipeline.getWindowRectsState(), glRT, origin);
|
||||
this->flushHWAAState(glRT, pipeline.isHWAntialiasState(), !stencil.isDisabled());
|
||||
|
||||
// This must come after textures are flushed because a texture may need
|
||||
// to be msaa-resolved (which will modify bound FBO state).
|
||||
this->flushRenderTarget(glRT);
|
||||
this->flushRenderTarget(glRT, origin);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1856,9 +1857,9 @@ void GrGLGpu::clear(const GrFixedClip& clip, GrColor color,
|
||||
GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(target);
|
||||
|
||||
if (clip.scissorEnabled()) {
|
||||
this->flushRenderTarget(glRT, origin, clip.scissorRect());
|
||||
this->flushRenderTarget(glRT, origin, &clip.scissorRect());
|
||||
} else {
|
||||
this->flushRenderTarget(glRT);
|
||||
this->flushRenderTarget(glRT, origin);
|
||||
}
|
||||
this->flushScissor(clip.scissorState(), glRT->getViewport(), origin);
|
||||
this->flushWindowRectangles(clip.windowRectsState(), glRT, origin);
|
||||
@ -2121,14 +2122,17 @@ GrGpuTextureCommandBuffer* GrGLGpu::createCommandBuffer(GrTexture* texture,
|
||||
}
|
||||
|
||||
void GrGLGpu::flushRenderTarget(GrGLRenderTarget* target, GrSurfaceOrigin origin,
|
||||
const SkIRect& bounds) {
|
||||
const SkIRect* bounds) {
|
||||
this->flushRenderTargetNoColorWrites(target);
|
||||
this->didWriteToSurface(target, origin, &bounds);
|
||||
}
|
||||
|
||||
void GrGLGpu::flushRenderTarget(GrGLRenderTarget* target) {
|
||||
this->flushRenderTargetNoColorWrites(target);
|
||||
this->didWriteToSurface(target, kTopLeft_GrSurfaceOrigin, nullptr);
|
||||
// A triangle is front-facing if it winds clockwise in device space.
|
||||
GrGLenum frontFace = (kBottomLeft_GrSurfaceOrigin == origin) ? GR_GL_CW : GR_GL_CCW;
|
||||
if (frontFace != fHWFrontFace) {
|
||||
GL_CALL(FrontFace(frontFace));
|
||||
fHWFrontFace = frontFace;
|
||||
}
|
||||
|
||||
this->didWriteToSurface(target, origin, bounds);
|
||||
}
|
||||
|
||||
void GrGLGpu::flushRenderTargetNoColorWrites(GrGLRenderTarget* target) {
|
||||
@ -3360,7 +3364,7 @@ void GrGLGpu::clearStencilClipAsDraw(const GrFixedClip& clip, bool insideStencil
|
||||
}
|
||||
|
||||
GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(rt->asRenderTarget());
|
||||
this->flushRenderTarget(glRT);
|
||||
this->flushRenderTarget(glRT, origin);
|
||||
|
||||
this->flushProgram(fStencilClipClearProgram);
|
||||
|
||||
|
@ -341,9 +341,7 @@ private:
|
||||
|
||||
// The passed bounds contains the render target's color values that will subsequently be
|
||||
// written.
|
||||
void flushRenderTarget(GrGLRenderTarget*, GrSurfaceOrigin, const SkIRect& bounds);
|
||||
// This version has an implicit bounds of the entire render target.
|
||||
void flushRenderTarget(GrGLRenderTarget*);
|
||||
void flushRenderTarget(GrGLRenderTarget*, GrSurfaceOrigin, const SkIRect* bounds = nullptr);
|
||||
// This version can be used when the render target's colors will not be written.
|
||||
void flushRenderTargetNoColorWrites(GrGLRenderTarget*);
|
||||
|
||||
@ -560,6 +558,7 @@ private:
|
||||
GrStencilSettings fHWStencilSettings;
|
||||
TriState fHWStencilTestEnabled;
|
||||
|
||||
GrGLenum fHWFrontFace;
|
||||
|
||||
TriState fHWWriteToColor;
|
||||
GrGpuResource::UniqueID fHWBoundRenderTargetUniqueID;
|
||||
|
@ -88,11 +88,12 @@ void GrGLPathRendering::onStencilPath(const StencilPathArgs& args, const GrPath*
|
||||
gpu->flushColorWrite(false);
|
||||
|
||||
GrGLRenderTarget* rt = static_cast<GrGLRenderTarget*>(args.fProxy->priv().peekRenderTarget());
|
||||
GrSurfaceOrigin origin = args.fProxy->origin();
|
||||
SkISize size = SkISize::Make(rt->width(), rt->height());
|
||||
this->setProjectionMatrix(*args.fViewMatrix, size, args.fProxy->origin());
|
||||
gpu->flushScissor(*args.fScissor, rt->getViewport(), args.fProxy->origin());
|
||||
this->setProjectionMatrix(*args.fViewMatrix, size, origin);
|
||||
gpu->flushScissor(*args.fScissor, rt->getViewport(), origin);
|
||||
gpu->flushHWAAState(rt, args.fUseHWAA, true);
|
||||
gpu->flushRenderTarget(rt);
|
||||
gpu->flushRenderTarget(rt, origin);
|
||||
|
||||
const GrGLPath* glPath = static_cast<const GrGLPath*>(path);
|
||||
|
||||
|
@ -436,7 +436,9 @@ static void setup_raster_state(const GrPipeline& pipeline,
|
||||
rasterInfo->polygonMode = caps->wireframeMode() ? VK_POLYGON_MODE_LINE
|
||||
: VK_POLYGON_MODE_FILL;
|
||||
rasterInfo->cullMode = VK_CULL_MODE_NONE;
|
||||
rasterInfo->frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
||||
// A triangle is front-facing if it winds clockwise in device space.
|
||||
rasterInfo->frontFace = (kTopLeft_GrSurfaceOrigin == pipeline.proxy()->origin())
|
||||
? VK_FRONT_FACE_CLOCKWISE : VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
||||
rasterInfo->depthBiasEnable = VK_FALSE;
|
||||
rasterInfo->depthBiasConstantFactor = 0.0f;
|
||||
rasterInfo->depthBiasClamp = 0.0f;
|
||||
|
@ -204,19 +204,23 @@ GrVkPipelineState* GrVkPipelineStateBuilder::finalize(const GrStencilSettings& s
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
uint32_t get_blend_info_key(const GrPipeline& pipeline) {
|
||||
GrXferProcessor::BlendInfo blendInfo;
|
||||
pipeline.getXferProcessor().getBlendInfo(&blendInfo);
|
||||
|
||||
static const uint32_t kBlendWriteShift = 1;
|
||||
uint32_t get_pipeline_info_key(const GrPipeline& pipeline) {
|
||||
static const uint32_t kBlendCoeffShift = 5;
|
||||
GR_STATIC_ASSERT(kLast_GrBlendCoeff < (1 << kBlendCoeffShift));
|
||||
GR_STATIC_ASSERT(kFirstAdvancedGrBlendEquation - 1 < 4);
|
||||
|
||||
uint32_t key = blendInfo.fWriteColor;
|
||||
key |= (blendInfo.fSrcBlend << kBlendWriteShift);
|
||||
key |= (blendInfo.fDstBlend << (kBlendWriteShift + kBlendCoeffShift));
|
||||
key |= (blendInfo.fEquation << (kBlendWriteShift + 2 * kBlendCoeffShift));
|
||||
GrXferProcessor::BlendInfo blendInfo;
|
||||
pipeline.getXferProcessor().getBlendInfo(&blendInfo);
|
||||
|
||||
GrSurfaceOrigin origin = pipeline.proxy()->origin();
|
||||
SkASSERT(0 == origin || 1 == origin);
|
||||
|
||||
uint32_t key;
|
||||
key = blendInfo.fEquation;
|
||||
key = blendInfo.fDstBlend | (key << kBlendCoeffShift);
|
||||
key = blendInfo.fSrcBlend | (key << kBlendCoeffShift);
|
||||
key = (int)blendInfo.fWriteColor | (key << 1);
|
||||
key = origin | (key << 1);
|
||||
|
||||
return key;
|
||||
}
|
||||
@ -238,7 +242,7 @@ bool GrVkPipelineStateBuilder::Desc::Build(Desc* desc,
|
||||
|
||||
stencil.genKey(&b);
|
||||
|
||||
b.add32(get_blend_info_key(pipeline));
|
||||
b.add32(get_pipeline_info_key(pipeline));
|
||||
|
||||
b.add32((uint32_t)primitiveType);
|
||||
|
||||
|
@ -45,6 +45,8 @@ Differences from GLSL
|
||||
* use sk_InstanceID instead of gl_InstanceID
|
||||
* the fragment coordinate is sk_FragCoord, and is always relative to the upper
|
||||
left.
|
||||
* use sk_Clockwise instead of gl_FrontFacing. This is always relative to an
|
||||
upper left origin.
|
||||
* you do not need to include ".0" to make a number a float (meaning that
|
||||
"float2(x, y) * 4" is perfectly legal in SkSL, unlike GLSL where it would
|
||||
often have to be expressed "float2(x, y) * 4.0". There is no performance
|
||||
@ -149,4 +151,4 @@ Creating a new .fp file
|
||||
7. At this point you can reference the new fragment processor from within Skia.
|
||||
|
||||
Once you have done this initial setup, simply re-build Skia to pick up any
|
||||
changes to the .fp file.
|
||||
changes to the .fp file.
|
||||
|
@ -27,6 +27,7 @@
|
||||
#define SK_OUT_BUILTIN 10007
|
||||
#define SK_LASTFRAGCOLOR_BUILTIN 10008
|
||||
#define SK_FRAGCOORD_BUILTIN 15
|
||||
#define SK_CLOCKWISE_BUILTIN 17
|
||||
#define SK_VERTEXID_BUILTIN 42
|
||||
#define SK_INSTANCEID_BUILTIN 43
|
||||
#define SK_CLIPDISTANCE_BUILTIN 3
|
||||
|
@ -689,6 +689,9 @@ void GLSLCodeGenerator::writeVariableReference(const VariableReference& ref) {
|
||||
case SK_FRAGCOORD_BUILTIN:
|
||||
this->writeFragCoord();
|
||||
break;
|
||||
case SK_CLOCKWISE_BUILTIN:
|
||||
this->write("gl_FrontFacing");
|
||||
break;
|
||||
case SK_VERTEXID_BUILTIN:
|
||||
this->write("gl_VertexID");
|
||||
break;
|
||||
|
@ -2,7 +2,9 @@ STRINGIFY(
|
||||
|
||||
// defines built-in interfaces supported by SkiaSL fragment shaders
|
||||
|
||||
// See "enum SpvBuiltIn_" in ./spirv.h
|
||||
layout(builtin=15) in float4 sk_FragCoord;
|
||||
layout(builtin=17) in bool sk_Clockwise; // Similar to gl_FrontFacing, but defined in device space.
|
||||
layout(builtin=3) float sk_ClipDistance[1];
|
||||
|
||||
// 9999 is a temporary value that causes us to ignore these declarations beyond
|
||||
|
@ -1154,6 +1154,17 @@ DEF_TEST(SkSLFragCoord, r) {
|
||||
"}\n");
|
||||
}
|
||||
|
||||
DEF_TEST(SkSLClockwise, r) {
|
||||
test(r,
|
||||
"void main() { sk_FragColor = half4(sk_Clockwise ? +1 : -1); }",
|
||||
*SkSL::ShaderCapsFactory::Default(),
|
||||
"#version 400\n"
|
||||
"out vec4 sk_FragColor;\n"
|
||||
"void main() {\n"
|
||||
" sk_FragColor = vec4(float(gl_FrontFacing ? 1 : -1));\n"
|
||||
"}\n");
|
||||
}
|
||||
|
||||
DEF_TEST(SkSLVertexID, r) {
|
||||
test(r,
|
||||
"out int id; void main() { id = sk_VertexID; }",
|
||||
|
Loading…
Reference in New Issue
Block a user