| /* | |
| * Copyright 2023 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #ifndef SkRasterPipelineOpContexts_DEFINED | |
| #define SkRasterPipelineOpContexts_DEFINED | |
| #include <algorithm> | |
| #include <cstddef> | |
| #include <cstdint> | |
| namespace SkSL { class TraceHook; } | |
| // state shared by stack_checkpoint and stack_rewind | |
| struct SkRasterPipelineStage; | |
| enum class SkPerlinNoiseShaderType; | |
| namespace SkRasterPipelineContexts { | |
| // The largest number of pixels we handle at a time. We have a separate value for the largest number | |
| // of pixels we handle in the highp pipeline. Many of the context structs in this file are only used | |
| // by stages that have no lowp implementation. They can therefore use the (smaller) highp value to | |
| // save memory in the arena. | |
| inline static constexpr int kMaxStride = 16; | |
| inline static constexpr int kMaxStride_highp = 16; | |
| // How much space to allocate for each MemoryCtx scratch buffer, as part of tail-pixel handling. | |
| inline static constexpr size_t kMaxScratchPerPatch = | |
| std::max(kMaxStride_highp * 16, // 16 == largest highp bpp (RGBA_F32) | |
| kMaxStride * 4); // 4 == largest lowp bpp (RGBA_8888) | |
| // These structs hold the context data for many of the Raster Pipeline ops. | |
| struct MemoryCtx { | |
| void* pixels; | |
| int stride; | |
| }; | |
| // Raster Pipeline typically processes N (4, 8, 16) pixels at a time, in SIMT fashion. If the | |
| // number of pixels in a row isn't evenly divisible by N, there will be leftover pixels; this is | |
| // called the "tail". To avoid reading or writing past the end of any source or destination buffers | |
| // when we reach the tail: | |
| // | |
| // 1) Source buffers have their tail contents copied to a scratch buffer that is at least N wide. | |
| // In practice, each scratch buffer uses kMaxScratchPerPatch bytes. | |
| // 2) Each MemoryCtx in the pipeline is patched, such that access to them (at the current scanline | |
| // and x-offset) will land in the scratch buffer. | |
| // 3) Pipeline is run as normal (with all memory access happening safely in the scratch buffers). | |
| // 4) Destination buffers have their tail contents copied back from the scratch buffer. | |
| // 5) Each MemoryCtx is "un-patched". | |
| // | |
| // To do all of this, the pipeline creates a MemoryCtxPatch for each unique MemoryCtx referenced by | |
| // the pipeline. | |
| struct MemoryCtxInfo { | |
| MemoryCtx* context; | |
| int bytesPerPixel; | |
| bool load; | |
| bool store; | |
| }; | |
| struct MemoryCtxPatch { | |
| std::byte scratch[kMaxScratchPerPatch]; | |
| MemoryCtxInfo info; | |
| void* backup; // Remembers context->pixels so we can restore it | |
| }; | |
| struct GatherCtx { | |
| const void* pixels; | |
| int stride; | |
| float width; | |
| float height; | |
| float weights[16]; // for bicubic and bicubic_clamp_8888 | |
| // Controls whether pixel i-1 or i is selected when floating point sample position is exactly i. | |
| bool roundDownAtInteger = false; | |
| }; | |
| // State shared by save_xy, accumulate, and bilinear_* / bicubic_*. | |
| struct SamplerCtx { | |
| float x[kMaxStride_highp]; | |
| float y[kMaxStride_highp]; | |
| float fx[kMaxStride_highp]; | |
| float fy[kMaxStride_highp]; | |
| float scalex[kMaxStride_highp]; | |
| float scaley[kMaxStride_highp]; | |
| // for bicubic_[np][13][xy] | |
| float weights[16]; | |
| float wx[4][kMaxStride_highp]; | |
| float wy[4][kMaxStride_highp]; | |
| }; | |
| struct TileCtx { | |
| float scale; | |
| float invScale; // cache of 1/scale | |
| // When in the reflection portion of mirror tiling we need to snap the opposite direction | |
| // at integer sample points than when in the forward direction. This controls which way we bias | |
| // in the reflection. It should be 1 if GatherCtx::roundDownAtInteger is true | |
| // and otherwise -1. | |
| int mirrorBiasDir = -1; | |
| }; | |
| struct DecalTileCtx { | |
| uint32_t mask[kMaxStride]; | |
| float limit_x; | |
| float limit_y; | |
| // These control which edge of the interval is included (i.e. closed interval at 0 or at limit). | |
| // They should be set to limit_x and limit_y if GatherCtx::roundDownAtInteger | |
| // is true and otherwise zero. | |
| float inclusiveEdge_x = 0; | |
| float inclusiveEdge_y = 0; | |
| }; | |
| struct PerlinNoiseCtx { | |
| SkPerlinNoiseShaderType noiseType; | |
| float baseFrequencyX, baseFrequencyY; | |
| float stitchDataInX, stitchDataInY; | |
| bool stitching; | |
| int numOctaves; | |
| const uint8_t* latticeSelector; // [256 values] | |
| const uint16_t* noiseData; // [4 channels][256 elements][vector of 2] | |
| }; | |
| // State used by mipmap_linear_* | |
| struct MipmapCtx { | |
| // Original coords, saved before the base level logic | |
| float x[kMaxStride_highp]; | |
| float y[kMaxStride_highp]; | |
| // Base level color | |
| float r[kMaxStride_highp]; | |
| float g[kMaxStride_highp]; | |
| float b[kMaxStride_highp]; | |
| float a[kMaxStride_highp]; | |
| // Scale factors to transform base level coords to lower level coords | |
| float scaleX; | |
| float scaleY; | |
| float lowerWeight; | |
| }; | |
| struct CoordClampCtx { | |
| float min_x, min_y; | |
| float max_x, max_y; | |
| }; | |
| struct CallbackCtx { | |
| void (*fn)(CallbackCtx* self, int active_pixels /*<= kMaxStride_highp*/); | |
| // When called, fn() will have our active pixels available in rgba. | |
| // When fn() returns, the pipeline will read back those active pixels from read_from. | |
| float rgba[4 * kMaxStride_highp]; | |
| float* read_from = rgba; | |
| }; | |
| struct RewindCtx { | |
| float r[kMaxStride_highp]; | |
| float g[kMaxStride_highp]; | |
| float b[kMaxStride_highp]; | |
| float a[kMaxStride_highp]; | |
| float dr[kMaxStride_highp]; | |
| float dg[kMaxStride_highp]; | |
| float db[kMaxStride_highp]; | |
| float da[kMaxStride_highp]; | |
| std::byte* base; | |
| SkRasterPipelineStage* stage; | |
| }; | |
| constexpr size_t kRGBAChannels = 4; | |
| struct GradientCtx { | |
| size_t stopCount; | |
| float* factors[kRGBAChannels]; | |
| float* biases[kRGBAChannels]; | |
| float* ts; | |
| }; | |
| struct EvenlySpaced2StopGradientCtx { | |
| float factor[kRGBAChannels]; | |
| float bias[kRGBAChannels]; | |
| }; | |
| struct Conical2PtCtx { | |
| uint32_t fMask[kMaxStride_highp]; | |
| float fP0, | |
| fP1; | |
| }; | |
| struct UniformColorCtx { | |
| float r,g,b,a; | |
| uint16_t rgba[4]; // [0,255] in a 16-bit lane. | |
| }; | |
| struct EmbossCtx { | |
| MemoryCtx mul, add; | |
| }; | |
| struct TablesCtx { | |
| const uint8_t *r, *g, *b, *a; | |
| }; | |
| using SkRPOffset = uint32_t; | |
| struct InitLaneMasksCtx { | |
| uint8_t* tail; | |
| }; | |
| struct ConstantCtx { | |
| int32_t value; | |
| SkRPOffset dst; | |
| }; | |
| struct UniformCtx { | |
| int32_t* dst; | |
| const int32_t* src; | |
| }; | |
| struct BinaryOpCtx { | |
| SkRPOffset dst; | |
| SkRPOffset src; | |
| }; | |
| struct TernaryOpCtx { | |
| SkRPOffset dst; | |
| SkRPOffset delta; | |
| }; | |
| struct MatrixMultiplyCtx { | |
| SkRPOffset dst; | |
| uint8_t leftColumns, leftRows, rightColumns, rightRows; | |
| }; | |
| struct SwizzleCtx { | |
| // If we are processing more than 16 pixels at a time, an 8-bit offset won't be sufficient and | |
| // `offsets` will need to use uint16_t (or dial down the premultiplication). | |
| static_assert(kMaxStride_highp <= 16); | |
| SkRPOffset dst; | |
| uint8_t offsets[4]; // values must be byte offsets (4 * highp-stride * component-index) | |
| }; | |
| struct ShuffleCtx { | |
| int32_t* ptr; | |
| int count; | |
| uint16_t offsets[16]; // values must be byte offsets (4 * highp-stride * component-index) | |
| }; | |
| struct SwizzleCopyCtx { | |
| int32_t* dst; | |
| const int32_t* src; // src values must _not_ overlap dst values | |
| uint16_t offsets[4]; // values must be byte offsets (4 * highp-stride * component-index) | |
| }; | |
| struct CopyIndirectCtx { | |
| int32_t* dst; | |
| const int32_t* src; | |
| const uint32_t *indirectOffset; // this applies to `src` or `dst` based on the op | |
| uint32_t indirectLimit; // the indirect offset is clamped to this upper bound | |
| uint32_t slots; // the number of slots to copy | |
| }; | |
| struct SwizzleCopyIndirectCtx : public CopyIndirectCtx { | |
| uint16_t offsets[4]; // values must be byte offsets (4 * highp-stride * component-index) | |
| }; | |
| struct BranchCtx { | |
| int offset; // contains the label ID during compilation, and the program offset when compiled | |
| }; | |
| struct BranchIfAllLanesActiveCtx : public BranchCtx { | |
| uint8_t* tail = nullptr; // lanes past the tail are _never_ active, so we need to exclude them | |
| }; | |
| struct BranchIfEqualCtx : public BranchCtx { | |
| int value; | |
| const int* ptr; | |
| }; | |
| struct CaseOpCtx { | |
| int expectedValue; | |
| SkRPOffset offset; // points to a pair of adjacent I32s: {I32 actualValue, I32 defaultMask} | |
| }; | |
| struct TraceFuncCtx { | |
| const int* traceMask; | |
| SkSL::TraceHook* traceHook; | |
| int funcIdx; | |
| }; | |
| struct TraceScopeCtx { | |
| const int* traceMask; | |
| SkSL::TraceHook* traceHook; | |
| int delta; | |
| }; | |
| struct TraceLineCtx { | |
| const int* traceMask; | |
| SkSL::TraceHook* traceHook; | |
| int lineNumber; | |
| }; | |
| struct TraceVarCtx { | |
| const int* traceMask; | |
| SkSL::TraceHook* traceHook; | |
| int slotIdx, numSlots; | |
| const int* data; | |
| const uint32_t *indirectOffset; // can be null; if set, an offset applied to `data` | |
| uint32_t indirectLimit; // the indirect offset is clamped to this upper bound | |
| }; | |
| } // namespace SkRasterPipelineContexts | |
| #endif // SkRasterPipelineOpContexts_DEFINED |