Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Public Member Functions | Private Member Functions | List of all members
GrTextureEffect::Impl Class Reference

#include <GrTextureEffect.h>

Inheritance diagram for GrTextureEffect::Impl:
GrFragmentProcessor::ProgramImpl

Public Member Functions

void emitCode (EmitArgs &) override
 
void setSamplerHandle (GrGLSLShaderBuilder::SamplerHandle handle)
 
- Public Member Functions inherited from GrFragmentProcessor::ProgramImpl
 ProgramImpl ()=default
 
virtual ~ProgramImpl ()=default
 
void setData (const GrGLSLProgramDataManager &pdman, const GrFragmentProcessor &processor)
 
int numChildProcessors () const
 
ProgramImplchildProcessor (int index) const
 
void setFunctionName (SkString name)
 
const char * functionName () const
 
SkString invokeChild (int childIndex, EmitArgs &parentArgs, std::string_view skslCoords={})
 
SkString invokeChildWithMatrix (int childIndex, EmitArgs &parentArgs)
 
SkString invokeChild (int childIndex, const char *inputColor, EmitArgs &parentArgs, std::string_view skslCoords={})
 
SkString invokeChildWithMatrix (int childIndex, const char *inputColor, EmitArgs &parentArgs)
 
SkString invokeChild (int childIndex, const char *inputColor, const char *destColor, EmitArgs &parentArgs, std::string_view skslCoords={})
 
SkString invokeChildWithMatrix (int childIndex, const char *inputColor, const char *destColor, EmitArgs &parentArgs)
 

Private Member Functions

void onSetData (const GrGLSLProgramDataManager &, const GrFragmentProcessor &) override
 

Additional Inherited Members

- Public Types inherited from GrFragmentProcessor::ProgramImpl
using UniformHandle = GrGLSLUniformHandler::UniformHandle
 
using SamplerHandle = GrGLSLUniformHandler::SamplerHandle
 

Detailed Description

Definition at line 145 of file GrTextureEffect.h.

Member Function Documentation

◆ emitCode()

void GrTextureEffect::Impl::emitCode ( EmitArgs args)
overridevirtual

Implements GrFragmentProcessor::ProgramImpl.

Definition at line 372 of file GrTextureEffect.cpp.

372 {
373 using ShaderMode = GrTextureEffect::ShaderMode;
374
375 auto& te = args.fFp.cast<GrTextureEffect>();
376 auto* fb = args.fFragBuilder;
377
378 if (te.fShaderModes[0] == ShaderMode::kNone &&
379 te.fShaderModes[1] == ShaderMode::kNone) {
380 fb->codeAppendf("return ");
381 fb->appendTextureLookup(fSamplerHandle, args.fSampleCoord);
382 fb->codeAppendf(";");
383 } else {
384 // Here is the basic flow of the various ShaderModes are implemented in a series of
385 // steps. Not all the steps apply to all the modes. We try to emit only the steps
386 // that are necessary for the given x/y shader modes.
387 //
388 // 0) Start with interpolated coordinates (unnormalize if doing anything
389 // complicated).
390 // 1) Map the coordinates into the subset range [Repeat and MirrorRepeat], or pass
391 // through output of 0).
392 // 2) Clamp the coordinates to a 0.5 inset of the subset rect [Clamp, Repeat, and
393 // MirrorRepeat always or ClampToBorder only when filtering] or pass through
394 // output of 1). The clamp rect collapses to a line or point it if the subset
395 // rect is less than one pixel wide/tall.
396 // 3) Look up texture with output of 2) [All]
397 // 3) Use the difference between 1) and 2) to apply filtering at edge [Repeat or
398 // ClampToBorder]. In the Repeat case this requires extra texture lookups on the
399 // other side of the subset (up to 3 more reads). Or if ClampToBorder and not
400 // filtering do a hard less than/greater than test with the subset rect.
401
402 // Convert possible projective texture coordinates into non-homogeneous half2.
403 fb->codeAppendf("float2 inCoord = %s;", args.fSampleCoord);
404
405 const auto& m = te.fShaderModes;
406
407 const char* borderName = nullptr;
408 if (te.hasClampToBorderShaderMode()) {
409 fBorderUni = args.fUniformHandler->addUniform(
410 &te, kFragment_GrShaderFlag, SkSLType::kHalf4, "border", &borderName);
411 }
412 auto modeUsesSubset = [](ShaderMode m) {
413 switch (m) {
414 case ShaderMode::kNone: return false;
415 case ShaderMode::kClamp: return false;
416 case ShaderMode::kRepeat_Nearest_None: return true;
417 case ShaderMode::kRepeat_Linear_None: return true;
418 case ShaderMode::kRepeat_Nearest_Mipmap: return true;
419 case ShaderMode::kRepeat_Linear_Mipmap: return true;
420 case ShaderMode::kMirrorRepeat: return true;
421 case ShaderMode::kClampToBorder_Nearest: return true;
422 case ShaderMode::kClampToBorder_Filter: return true;
423 }
425 };
426
427 auto modeUsesClamp = [](ShaderMode m) {
428 switch (m) {
429 case ShaderMode::kNone: return false;
430 case ShaderMode::kClamp: return true;
431 case ShaderMode::kRepeat_Nearest_None: return true;
432 case ShaderMode::kRepeat_Linear_None: return true;
433 case ShaderMode::kRepeat_Nearest_Mipmap: return true;
434 case ShaderMode::kRepeat_Linear_Mipmap: return true;
435 case ShaderMode::kMirrorRepeat: return true;
436 case ShaderMode::kClampToBorder_Nearest: return false;
437 case ShaderMode::kClampToBorder_Filter: return true;
438 }
440 };
441
442 bool useSubset[2] = {modeUsesSubset(m[0]), modeUsesSubset(m[1])};
443 bool useClamp [2] = {modeUsesClamp (m[0]), modeUsesClamp (m[1])};
444
445 const char* subsetName = nullptr;
446 if (useSubset[0] || useSubset[1]) {
447 fSubsetUni = args.fUniformHandler->addUniform(
448 &te, kFragment_GrShaderFlag, SkSLType::kFloat4, "subset", &subsetName);
449 }
450
451 const char* clampName = nullptr;
452 if (useClamp[0] || useClamp[1]) {
453 fClampUni = args.fUniformHandler->addUniform(
454 &te, kFragment_GrShaderFlag, SkSLType::kFloat4, "clamp", &clampName);
455 }
456
457 bool unormCoordsRequiredForShaderMode = ShaderModeRequiresUnormCoord(m[0]) ||
458 ShaderModeRequiresUnormCoord(m[1]);
459 // We should not pre-normalize the input coords with GrMatrixEffect if we're going to
460 // operate on unnormalized coords and then normalize after the shader mode.
461 SkASSERT(!(unormCoordsRequiredForShaderMode && te.matrixEffectShouldNormalize()));
462 bool sampleCoordsMustBeNormalized =
463 te.fView.asTextureProxy()->textureType() != GrTextureType::kRectangle;
464
465 const char* idims = nullptr;
466 if (unormCoordsRequiredForShaderMode && sampleCoordsMustBeNormalized) {
467 // TODO: Detect support for textureSize() or polyfill textureSize() in SkSL and
468 // always use?
469 fIDimsUni = args.fUniformHandler->addUniform(&te, kFragment_GrShaderFlag,
470 SkSLType::kFloat2, "idims", &idims);
471 }
472
473 // Generates a string to read at a coordinate, normalizing coords if necessary.
474 auto read = [&](const char* coord) {
476 SkString normCoord;
477 if (idims) {
478 normCoord.printf("(%s) * %s", coord, idims);
479 } else {
480 normCoord = coord;
481 }
482 fb->appendTextureLookup(&result, fSamplerHandle, normCoord.c_str());
483 return result;
484 };
485
486 // Implements coord wrapping for kRepeat and kMirrorRepeat
487 auto subsetCoord = [&](ShaderMode mode,
488 const char* coordSwizzle,
489 const char* subsetStartSwizzle,
490 const char* subsetStopSwizzle,
491 const char* extraCoord,
492 const char* coordWeight) {
493 switch (mode) {
494 // These modes either don't use the subset rect or don't need to map the
495 // coords to be within the subset.
496 case ShaderMode::kNone:
497 case ShaderMode::kClampToBorder_Nearest:
498 case ShaderMode::kClampToBorder_Filter:
499 case ShaderMode::kClamp:
500 fb->codeAppendf("subsetCoord.%s = inCoord.%s;", coordSwizzle, coordSwizzle);
501 break;
502 case ShaderMode::kRepeat_Nearest_None:
503 case ShaderMode::kRepeat_Linear_None:
504 fb->codeAppendf(
505 "subsetCoord.%s = mod(inCoord.%s - %s.%s, %s.%s - %s.%s) + %s.%s;",
506 coordSwizzle, coordSwizzle, subsetName, subsetStartSwizzle, subsetName,
507 subsetStopSwizzle, subsetName, subsetStartSwizzle, subsetName,
508 subsetStartSwizzle);
509 break;
510 case ShaderMode::kRepeat_Nearest_Mipmap:
511 case ShaderMode::kRepeat_Linear_Mipmap:
512 // The approach here is to generate two sets of texture coords that
513 // are both "moving" at the same speed (if not direction) as
514 // inCoords. We accomplish that by using two out of phase mirror
515 // repeat coords. We will always sample using both coords but the
516 // read from the upward sloping one is selected using a weight
517 // that transitions from one set to the other near the reflection
518 // point. Like the coords, the weight is a saw-tooth function,
519 // phase-shifted, vertically translated, and then clamped to 0..1.
520 // TODO: Skip this and use textureGrad() when available.
521 SkASSERT(extraCoord);
522 SkASSERT(coordWeight);
523 fb->codeAppend("{");
524 fb->codeAppendf("float w = %s.%s - %s.%s;", subsetName, subsetStopSwizzle,
525 subsetName, subsetStartSwizzle);
526 fb->codeAppendf("float w2 = 2 * w;");
527 fb->codeAppendf("float d = inCoord.%s - %s.%s;", coordSwizzle, subsetName,
528 subsetStartSwizzle);
529 fb->codeAppend("float m = mod(d, w2);");
530 fb->codeAppend("float o = mix(m, w2 - m, step(w, m));");
531 fb->codeAppendf("subsetCoord.%s = o + %s.%s;", coordSwizzle, subsetName,
532 subsetStartSwizzle);
533 fb->codeAppendf("%s = w - o + %s.%s;", extraCoord, subsetName,
534 subsetStartSwizzle);
535 // coordWeight is used as the third param of mix() to blend between a
536 // sample taken using subsetCoord and a sample at extraCoord.
537 fb->codeAppend("float hw = w/2;");
538 fb->codeAppend("float n = mod(d - hw, w2);");
539 fb->codeAppendf("%s = saturate(half(mix(n, w2 - n, step(w, n)) - hw + 0.5));",
540 coordWeight);
541 fb->codeAppend("}");
542 break;
543 case ShaderMode::kMirrorRepeat:
544 fb->codeAppend("{");
545 fb->codeAppendf("float w = %s.%s - %s.%s;", subsetName, subsetStopSwizzle,
546 subsetName, subsetStartSwizzle);
547 fb->codeAppendf("float w2 = 2 * w;");
548 fb->codeAppendf("float m = mod(inCoord.%s - %s.%s, w2);", coordSwizzle,
549 subsetName, subsetStartSwizzle);
550 fb->codeAppendf("subsetCoord.%s = mix(m, w2 - m, step(w, m)) + %s.%s;",
551 coordSwizzle, subsetName, subsetStartSwizzle);
552 fb->codeAppend("}");
553 break;
554 }
555 };
556
557 auto clampCoord = [&](bool clamp,
558 const char* coordSwizzle,
559 const char* clampStartSwizzle,
560 const char* clampStopSwizzle) {
561 if (clamp) {
562 fb->codeAppendf("clampedCoord%s = clamp(subsetCoord%s, %s%s, %s%s);",
563 coordSwizzle, coordSwizzle,
564 clampName, clampStartSwizzle,
565 clampName, clampStopSwizzle);
566 } else {
567 fb->codeAppendf("clampedCoord%s = subsetCoord%s;", coordSwizzle, coordSwizzle);
568 }
569 };
570
571 // Insert vars for extra coords and blending weights for repeat + mip map.
572 const char* extraRepeatCoordX = nullptr;
573 const char* repeatCoordWeightX = nullptr;
574 const char* extraRepeatCoordY = nullptr;
575 const char* repeatCoordWeightY = nullptr;
576
577 bool mipmapRepeatX = m[0] == ShaderMode::kRepeat_Nearest_Mipmap ||
578 m[0] == ShaderMode::kRepeat_Linear_Mipmap;
579 bool mipmapRepeatY = m[1] == ShaderMode::kRepeat_Nearest_Mipmap ||
580 m[1] == ShaderMode::kRepeat_Linear_Mipmap;
581
582 if (mipmapRepeatX || mipmapRepeatY) {
583 fb->codeAppend("float2 extraRepeatCoord;");
584 }
585 if (mipmapRepeatX) {
586 fb->codeAppend("half repeatCoordWeightX;");
587 extraRepeatCoordX = "extraRepeatCoord.x";
588 repeatCoordWeightX = "repeatCoordWeightX";
589 }
590 if (mipmapRepeatY) {
591 fb->codeAppend("half repeatCoordWeightY;");
592 extraRepeatCoordY = "extraRepeatCoord.y";
593 repeatCoordWeightY = "repeatCoordWeightY";
594 }
595
596 // Apply subset rect and clamp rect to coords.
597 fb->codeAppend("float2 subsetCoord;");
598 subsetCoord(te.fShaderModes[0], "x", "x", "z", extraRepeatCoordX, repeatCoordWeightX);
599 subsetCoord(te.fShaderModes[1], "y", "y", "w", extraRepeatCoordY, repeatCoordWeightY);
600 fb->codeAppend("float2 clampedCoord;");
601 if (useClamp[0] == useClamp[1]) {
602 clampCoord(useClamp[0], "", ".xy", ".zw");
603 } else {
604 clampCoord(useClamp[0], ".x", ".x", ".z");
605 clampCoord(useClamp[1], ".y", ".y", ".w");
606 }
607 // Additional clamping for the extra coords for kRepeat with mip maps.
608 if (mipmapRepeatX && mipmapRepeatY) {
609 fb->codeAppendf("extraRepeatCoord = clamp(extraRepeatCoord, %s.xy, %s.zw);",
610 clampName, clampName);
611 } else if (mipmapRepeatX) {
612 fb->codeAppendf("extraRepeatCoord.x = clamp(extraRepeatCoord.x, %s.x, %s.z);",
613 clampName, clampName);
614 } else if (mipmapRepeatY) {
615 fb->codeAppendf("extraRepeatCoord.y = clamp(extraRepeatCoord.y, %s.y, %s.w);",
616 clampName, clampName);
617 }
618
619 // Do the 2 or 4 texture reads for kRepeatMipMap and then apply the weight(s)
620 // to blend between them. If neither direction is repeat or not using mip maps do a single
621 // read at clampedCoord.
622 if (mipmapRepeatX && mipmapRepeatY) {
623 fb->codeAppendf(
624 "half4 textureColor ="
625 " mix(mix(%s, %s, repeatCoordWeightX),"
626 " mix(%s, %s, repeatCoordWeightX),"
627 " repeatCoordWeightY);",
628 read("clampedCoord").c_str(),
629 read("float2(extraRepeatCoord.x, clampedCoord.y)").c_str(),
630 read("float2(clampedCoord.x, extraRepeatCoord.y)").c_str(),
631 read("float2(extraRepeatCoord.x, extraRepeatCoord.y)").c_str());
632
633 } else if (mipmapRepeatX) {
634 fb->codeAppendf("half4 textureColor = mix(%s, %s, repeatCoordWeightX);",
635 read("clampedCoord").c_str(),
636 read("float2(extraRepeatCoord.x, clampedCoord.y)").c_str());
637 } else if (mipmapRepeatY) {
638 fb->codeAppendf("half4 textureColor = mix(%s, %s, repeatCoordWeightY);",
639 read("clampedCoord").c_str(),
640 read("float2(clampedCoord.x, extraRepeatCoord.y)").c_str());
641 } else {
642 fb->codeAppendf("half4 textureColor = %s;", read("clampedCoord").c_str());
643 }
644
645 // Strings for extra texture reads used only in kRepeatLinear
646 SkString repeatLinearReadX;
647 SkString repeatLinearReadY;
648
649 // Calculate the amount the coord moved for clamping. This will be used
650 // to implement shader-based filtering for kClampToBorder and kRepeat.
651 bool repeatLinearFilterX = m[0] == ShaderMode::kRepeat_Linear_None ||
652 m[0] == ShaderMode::kRepeat_Linear_Mipmap;
653 bool repeatLinearFilterY = m[1] == ShaderMode::kRepeat_Linear_None ||
654 m[1] == ShaderMode::kRepeat_Linear_Mipmap;
655 if (repeatLinearFilterX || m[0] == ShaderMode::kClampToBorder_Filter) {
656 fb->codeAppend("half errX = half(subsetCoord.x - clampedCoord.x);");
657 if (repeatLinearFilterX) {
658 fb->codeAppendf("float repeatCoordX = errX > 0 ? %s.x : %s.z;",
659 clampName, clampName);
660 repeatLinearReadX = read("float2(repeatCoordX, clampedCoord.y)");
661 }
662 }
663 if (repeatLinearFilterY || m[1] == ShaderMode::kClampToBorder_Filter) {
664 fb->codeAppend("half errY = half(subsetCoord.y - clampedCoord.y);");
665 if (repeatLinearFilterY) {
666 fb->codeAppendf("float repeatCoordY = errY > 0 ? %s.y : %s.w;",
667 clampName, clampName);
668 repeatLinearReadY = read("float2(clampedCoord.x, repeatCoordY)");
669 }
670 }
671
672 // Add logic for kRepeat + linear filter. Do 1 or 3 more texture reads depending
673 // on whether both modes are kRepeat and whether we're near a single subset edge
674 // or a corner. Then blend the multiple reads using the err values calculated
675 // above.
676 const char* ifStr = "if";
677 if (repeatLinearFilterX && repeatLinearFilterY) {
678 auto repeatLinearReadXY = read("float2(repeatCoordX, repeatCoordY)");
679 fb->codeAppendf(
680 "if (errX != 0 && errY != 0) {"
681 " errX = abs(errX);"
682 " textureColor = mix(mix(textureColor, %s, errX),"
683 " mix(%s, %s, errX),"
684 " abs(errY));"
685 "}",
686 repeatLinearReadX.c_str(), repeatLinearReadY.c_str(),
687 repeatLinearReadXY.c_str());
688 ifStr = "else if";
689 }
690 if (repeatLinearFilterX) {
691 fb->codeAppendf(
692 "%s (errX != 0) {"
693 " textureColor = mix(textureColor, %s, abs(errX));"
694 "}",
695 ifStr, repeatLinearReadX.c_str());
696 }
697 if (repeatLinearFilterY) {
698 fb->codeAppendf(
699 "%s (errY != 0) {"
700 " textureColor = mix(textureColor, %s, abs(errY));"
701 "}",
702 ifStr, repeatLinearReadY.c_str());
703 }
704
705 // Do soft edge shader filtering against border color for kClampToBorderFilter using
706 // the err values calculated above.
707 if (m[0] == ShaderMode::kClampToBorder_Filter) {
708 fb->codeAppendf("textureColor = mix(textureColor, %s, min(abs(errX), 1));", borderName);
709 }
710 if (m[1] == ShaderMode::kClampToBorder_Filter) {
711 fb->codeAppendf("textureColor = mix(textureColor, %s, min(abs(errY), 1));", borderName);
712 }
713
714 // Do hard-edge shader transition to border color for kClampToBorderNearest at the
715 // subset boundaries. Snap the input coordinates to nearest neighbor (with an
716 // epsilon) before comparing to the subset rect to avoid GPU interpolation errors
717 if (m[0] == ShaderMode::kClampToBorder_Nearest) {
718 fb->codeAppendf(
719 "float snappedX = floor(inCoord.x + 0.001) + 0.5;"
720 "if (snappedX < %s.x || snappedX > %s.z) {"
721 " textureColor = %s;"
722 "}",
723 subsetName, subsetName, borderName);
724 }
725 if (m[1] == ShaderMode::kClampToBorder_Nearest) {
726 fb->codeAppendf(
727 "float snappedY = floor(inCoord.y + 0.001) + 0.5;"
728 "if (snappedY < %s.y || snappedY > %s.w) {"
729 " textureColor = %s;"
730 "}",
731 subsetName, subsetName, borderName);
732 }
733 fb->codeAppendf("return textureColor;");
734 }
735}
@ kFragment_GrShaderFlag
#define SkUNREACHABLE
Definition SkAssert.h:135
#define SkASSERT(cond)
Definition SkAssert.h:116
static unsigned clamp(SkFixed fx, int max)
static bool read(SkStream *stream, void *buffer, size_t amount)
void printf(const char format[],...) SK_PRINTF_LIKE(2
Definition SkString.cpp:534
const char * c_str() const
Definition SkString.h:133
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
GAsyncResult * result
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
Definition switches.h:228

◆ onSetData()

void GrTextureEffect::Impl::onSetData ( const GrGLSLProgramDataManager ,
const GrFragmentProcessor  
)
overrideprivatevirtual

A ProgramImpl instance can be reused with any GrFragmentProcessor that produces the same the same key; this function reads data from a GrFragmentProcessor and uploads any uniform variables required by the shaders created in emitCode(). The GrFragmentProcessor parameter is guaranteed to be of the same type that created this ProgramImpl and to have an identical key as the one that created this ProgramImpl.

Reimplemented from GrFragmentProcessor::ProgramImpl.

Definition at line 737 of file GrTextureEffect.cpp.

738 {
739 const auto& te = fp.cast<GrTextureEffect>();
740
741 const float w = te.texture()->width();
742 const float h = te.texture()->height();
743 const auto& s = te.fSubset;
744 const auto& c = te.fClamp;
745
746 auto type = te.texture()->textureType();
747
748 float idims[2] = {1.f/w, 1.f/h};
749
750 if (fIDimsUni.isValid()) {
751 pdm.set2fv(fIDimsUni, 1, idims);
753 }
754
755 auto pushRect = [&](float rect[4], UniformHandle uni) {
756 if (te.view().origin() == kBottomLeft_GrSurfaceOrigin) {
757 rect[1] = h - rect[1];
758 rect[3] = h - rect[3];
759 std::swap(rect[1], rect[3]);
760 }
761 if (!fIDimsUni.isValid() && type != GrTextureType::kRectangle) {
762 rect[0] *= idims[0];
763 rect[2] *= idims[0];
764 rect[1] *= idims[1];
765 rect[3] *= idims[1];
766 }
767 pdm.set4fv(uni, 1, rect);
768 };
769
770 if (fSubsetUni.isValid()) {
771 float subset[] = {s.fLeft, s.fTop, s.fRight, s.fBottom};
772 pushRect(subset, fSubsetUni);
773 }
774 if (fClampUni.isValid()) {
775 float subset[] = {c.fLeft, c.fTop, c.fRight, c.fBottom};
776 pushRect(subset, fClampUni);
777 }
778 if (fBorderUni.isValid()) {
779 pdm.set4fv(fBorderUni, 1, te.fBorder);
780 }
781}
@ kBottomLeft_GrSurfaceOrigin
Definition GrTypes.h:149
GrGLSLUniformHandler::UniformHandle UniformHandle
int width() const
Definition GrSurface.h:32
GrTexture * texture() const
struct MyStruct s
sk_sp< SkBlender > blender SkRect rect
Definition SkRecords.h:350
const uint32_t fp
SkScalar w
SkScalar h

◆ setSamplerHandle()

void GrTextureEffect::Impl::setSamplerHandle ( GrGLSLShaderBuilder::SamplerHandle  handle)
inline

Definition at line 149 of file GrTextureEffect.h.

149 {
150 fSamplerHandle = handle;
151 }

The documentation for this class was generated from the following files: