| /* |
| * Copyright 2016 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "src/gpu/ganesh/GrProgramDesc.h" |
| |
| #include "include/private/SkChecksum.h" |
| #include "include/private/base/SkTo.h" |
| #include "src/gpu/KeyBuilder.h" |
| #include "src/gpu/ganesh/GrCaps.h" |
| #include "src/gpu/ganesh/GrFragmentProcessor.h" |
| #include "src/gpu/ganesh/GrGeometryProcessor.h" |
| #include "src/gpu/ganesh/GrPipeline.h" |
| #include "src/gpu/ganesh/GrProcessor.h" |
| #include "src/gpu/ganesh/GrProgramInfo.h" |
| #include "src/gpu/ganesh/GrRenderTarget.h" |
| #include "src/gpu/ganesh/GrShaderCaps.h" |
| #include "src/gpu/ganesh/GrTexture.h" |
| #include "src/gpu/ganesh/effects/GrTextureEffect.h" |
| #include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h" |
| |
| enum { |
| kSamplerOrImageTypeKeyBits = 4 |
| }; |
| |
| static inline uint16_t texture_type_key(GrTextureType type) { |
| int value = UINT16_MAX; |
| switch (type) { |
| case GrTextureType::k2D: |
| value = 0; |
| break; |
| case GrTextureType::kExternal: |
| value = 1; |
| break; |
| case GrTextureType::kRectangle: |
| value = 2; |
| break; |
| default: |
| SK_ABORT("Unexpected texture type"); |
| value = 3; |
| break; |
| } |
| SkASSERT((value & ((1 << kSamplerOrImageTypeKeyBits) - 1)) == value); |
| return SkToU16(value); |
| } |
| |
| static uint32_t sampler_key(GrTextureType textureType, const skgpu::Swizzle& swizzle, |
| const GrCaps& caps) { |
| int samplerTypeKey = texture_type_key(textureType); |
| |
| static_assert(2 == sizeof(swizzle.asKey())); |
| uint16_t swizzleKey = swizzle.asKey(); |
| return SkToU32(samplerTypeKey | swizzleKey << kSamplerOrImageTypeKeyBits); |
| } |
| |
| static void add_geomproc_sampler_keys(skgpu::KeyBuilder* b, |
| const GrGeometryProcessor& geomProc, |
| const GrCaps& caps) { |
| int numTextureSamplers = geomProc.numTextureSamplers(); |
| b->add32(numTextureSamplers, "ppNumSamplers"); |
| for (int i = 0; i < numTextureSamplers; ++i) { |
| const GrGeometryProcessor::TextureSampler& sampler = geomProc.textureSampler(i); |
| const GrBackendFormat& backendFormat = sampler.backendFormat(); |
| |
| uint32_t samplerKey = sampler_key(backendFormat.textureType(), sampler.swizzle(), caps); |
| b->add32(samplerKey); |
| |
| caps.addExtraSamplerKey(b, sampler.samplerState(), backendFormat); |
| } |
| } |
| |
| // Currently we allow 8 bits for the class id |
| static constexpr uint32_t kClassIDBits = 8; |
| |
| /** |
| * Functions which emit processor key info into the key builder. |
| * For every effect, we include the effect's class ID (different for every GrProcessor subclass), |
| * any information generated by the effect itself (addToKey), and some meta-information. |
| * Shader code may be dependent on properties of the effect not placed in the key by the effect |
| * (e.g. pixel format of textures used). |
| */ |
| static void gen_geomproc_key(const GrGeometryProcessor& geomProc, |
| const GrCaps& caps, |
| skgpu::KeyBuilder* b) { |
| b->appendComment(geomProc.name()); |
| b->addBits(kClassIDBits, geomProc.classID(), "geomProcClassID"); |
| |
| geomProc.addToKey(*caps.shaderCaps(), b); |
| geomProc.getAttributeKey(b); |
| |
| add_geomproc_sampler_keys(b, geomProc, caps); |
| } |
| |
| static void gen_xp_key(const GrXferProcessor& xp, |
| const GrCaps& caps, |
| const GrPipeline& pipeline, |
| skgpu::KeyBuilder* b) { |
| b->appendComment(xp.name()); |
| b->addBits(kClassIDBits, xp.classID(), "xpClassID"); |
| |
| const GrSurfaceOrigin* originIfDstTexture = nullptr; |
| GrSurfaceOrigin origin; |
| const GrSurfaceProxyView& dstView = pipeline.dstProxyView(); |
| if (dstView.proxy()) { |
| origin = dstView.origin(); |
| originIfDstTexture = &origin; |
| |
| uint32_t samplerKey = sampler_key(dstView.proxy()->backendFormat().textureType(), |
| dstView.swizzle(), caps); |
| b->add32(samplerKey); |
| } |
| |
| xp.addToKey(*caps.shaderCaps(), |
| b, |
| originIfDstTexture, |
| pipeline.dstSampleFlags() & GrDstSampleFlags::kAsInputAttachment); |
| } |
| |
| static void gen_fp_key(const GrFragmentProcessor& fp, |
| const GrCaps& caps, |
| skgpu::KeyBuilder* b) { |
| b->appendComment(fp.name()); |
| b->addBits(kClassIDBits, fp.classID(), "fpClassID"); |
| b->addBits(GrGeometryProcessor::kCoordTransformKeyBits, |
| GrGeometryProcessor::ComputeCoordTransformsKey(fp), "fpTransforms"); |
| |
| if (auto* te = fp.asTextureEffect()) { |
| const GrBackendFormat& backendFormat = te->view().proxy()->backendFormat(); |
| uint32_t samplerKey = sampler_key(backendFormat.textureType(), te->view().swizzle(), caps); |
| b->add32(samplerKey, "fpSamplerKey"); |
| caps.addExtraSamplerKey(b, te->samplerState(), backendFormat); |
| } |
| |
| fp.addToKey(*caps.shaderCaps(), b); |
| b->add32(fp.numChildProcessors(), "fpNumChildren"); |
| |
| for (int i = 0; i < fp.numChildProcessors(); ++i) { |
| if (auto child = fp.childProcessor(i)) { |
| gen_fp_key(*child, caps, b); |
| } else { |
| // Fold in a sentinel value as the "class ID" for any null children |
| b->appendComment("Null"); |
| b->addBits(kClassIDBits, GrProcessor::ClassID::kNull_ClassID, "fpClassID"); |
| } |
| } |
| } |
| |
| static void gen_key(skgpu::KeyBuilder* b, |
| const GrProgramInfo& programInfo, |
| const GrCaps& caps) { |
| gen_geomproc_key(programInfo.geomProc(), caps, b); |
| |
| const GrPipeline& pipeline = programInfo.pipeline(); |
| b->addBits(2, pipeline.numFragmentProcessors(), "numFPs"); |
| b->addBits(1, pipeline.numColorFragmentProcessors(), "numColorFPs"); |
| for (int i = 0; i < pipeline.numFragmentProcessors(); ++i) { |
| gen_fp_key(pipeline.getFragmentProcessor(i), caps, b); |
| } |
| |
| gen_xp_key(pipeline.getXferProcessor(), caps, pipeline, b); |
| |
| b->addBits(16, pipeline.writeSwizzle().asKey(), "writeSwizzle"); |
| b->addBool(pipeline.snapVerticesToPixelCenters(), "snapVertices"); |
| // The base descriptor only stores whether or not the primitiveType is kPoints. Backend- |
| // specific versions (e.g., Vulkan) require more detail |
| b->addBool((programInfo.primitiveType() == GrPrimitiveType::kPoints), "isPoints"); |
| |
| // Put a clean break between the "common" data written by this function, and any backend data |
| // appended later. The initial key length will just be this portion (rounded to 4 bytes). |
| b->flush(); |
| } |
| |
| void GrProgramDesc::Build(GrProgramDesc* desc, |
| const GrProgramInfo& programInfo, |
| const GrCaps& caps) { |
| desc->reset(); |
| skgpu::KeyBuilder b(desc->key()); |
| gen_key(&b, programInfo, caps); |
| desc->fInitialKeyLength = desc->keyLength(); |
| } |
| |
| SkString GrProgramDesc::Describe(const GrProgramInfo& programInfo, |
| const GrCaps& caps) { |
| GrProgramDesc desc; |
| skgpu::StringKeyBuilder b(desc.key()); |
| gen_key(&b, programInfo, caps); |
| b.flush(); |
| return b.description(); |
| } |