/* * Copyright 2021 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "src/gpu/graphite/ContextUtils.h" #include #include "include/private/SkSLString.h" #include "src/core/SkBlenderBase.h" #include "src/gpu/graphite/Caps.h" #include "src/gpu/graphite/GraphicsPipelineDesc.h" #include "src/gpu/graphite/KeyContext.h" #include "src/gpu/graphite/PaintParams.h" #include "src/gpu/graphite/PipelineData.h" #include "src/gpu/graphite/RecorderPriv.h" #include "src/gpu/graphite/Renderer.h" #include "src/gpu/graphite/ResourceProvider.h" #include "src/gpu/graphite/ShaderCodeDictionary.h" #include "src/gpu/graphite/UniformManager.h" #include "src/gpu/graphite/UniquePaintParamsID.h" namespace skgpu::graphite { std::tuple ExtractPaintData(Recorder* recorder, PipelineDataGatherer* gatherer, PaintParamsKeyBuilder* builder, const Layout layout, const SkM44& local2Dev, const PaintParams& p, const SkColorInfo& targetColorInfo) { SkDEBUGCODE(builder->checkReset()); gatherer->resetWithNewLayout(layout); KeyContext keyContext(recorder, local2Dev, targetColorInfo); p.toKey(keyContext, builder, gatherer); auto dict = recorder->priv().shaderCodeDictionary(); UniformDataCache* uniformDataCache = recorder->priv().uniformDataCache(); TextureDataCache* textureDataCache = recorder->priv().textureDataCache(); auto entry = dict->findOrCreate(builder); const UniformDataBlock* uniforms = gatherer->hasUniforms() ? uniformDataCache->insert(gatherer->finishUniformDataBlock()) : nullptr; const TextureDataBlock* textures = gatherer->hasTextures() ? textureDataCache->insert(gatherer->textureDataBlock()) : nullptr; return { entry->uniqueID(), uniforms, textures }; } std::tuple ExtractRenderStepData( UniformDataCache* uniformDataCache, TextureDataCache* textureDataCache, PipelineDataGatherer* gatherer, const Layout layout, const RenderStep* step, const DrawParams& params) { gatherer->resetWithNewLayout(layout); step->writeUniformsAndTextures(params, gatherer); const UniformDataBlock* uniforms = gatherer->hasUniforms() ? uniformDataCache->insert(gatherer->finishUniformDataBlock()) : nullptr; const TextureDataBlock* textures = gatherer->hasTextures() ? textureDataCache->insert(gatherer->textureDataBlock()) : nullptr; return { uniforms, textures }; } namespace { std::string get_uniform_header(int bufferID, const char* name) { std::string result; SkSL::String::appendf(&result, "layout (binding=%d) uniform %sUniforms {\n", bufferID, name); return result; } std::string get_uniforms(Layout layout, SkSpan uniforms, int* offset, int manglingSuffix) { std::string result; UniformOffsetCalculator offsetter(layout, *offset); for (const Uniform& u : uniforms) { SkSL::String::appendf(&result, " layout(offset=%zu) %s %s", offsetter.advanceOffset(u.type(), u.count()), SkSLTypeString(u.type()), u.name()); if (manglingSuffix >= 0) { result.append("_"); result.append(std::to_string(manglingSuffix)); } if (u.count()) { result.append("["); result.append(std::to_string(u.count())); result.append("]"); } result.append(";\n"); } *offset = offsetter.size(); return result; } } // anonymous namespace std::string EmitPaintParamsUniforms(int bufferID, const char* name, const Layout layout, const std::vector& readers) { int offset = 0; std::string result = get_uniform_header(bufferID, name); for (int i = 0; i < (int) readers.size(); ++i) { SkSpan uniforms = readers[i].entry()->fUniforms; if (!uniforms.empty()) { SkSL::String::appendf(&result, "// %s uniforms\n", readers[i].entry()->fName); result += get_uniforms(layout, uniforms, &offset, i); } } result.append("};\n\n"); return result; } std::string EmitRenderStepUniforms(int bufferID, const char* name, const Layout layout, SkSpan uniforms) { int offset = 0; std::string result = get_uniform_header(bufferID, name); result += get_uniforms(layout, uniforms, &offset, -1); result.append("};\n\n"); return result; } std::string EmitPaintParamsStorageBuffer( int bufferID, const char* bufferTypePrefix, const char* bufferNamePrefix, const std::vector& readers) { std::string result; SkSL::String::appendf(&result, "struct %sUniformData {\n", bufferTypePrefix); for (int i = 0; i < (int)readers.size(); ++i) { SkSpan uniforms = readers[i].entry()->fUniforms; if (uniforms.empty()) { continue; } SkSL::String::appendf(&result, "// %s uniforms\n", readers[i].entry()->fName); int manglingSuffix = i; for (const Uniform& u : uniforms) { SkSL::String::appendf( &result, " %s %s_%d", SkSLTypeString(u.type()), u.name(), manglingSuffix); if (u.count()) { SkSL::String::appendf(&result, "[%u]", u.count()); } result.append(";\n"); } } result.append("};\n\n"); SkSL::String::appendf(&result, "layout (binding=%d) buffer %sUniforms {\n" " %sUniformData %sUniformData[];\n" "};\n", bufferID, bufferTypePrefix, bufferTypePrefix, bufferNamePrefix); return result; } std::string EmitStorageBufferAccess(const char* bufferNamePrefix, const char* ssboIndex, const char* uniformName) { return SkSL::String::printf("%sUniformData[%s].%s", bufferNamePrefix, ssboIndex, uniformName); } std::string EmitTexturesAndSamplers(const ResourceBindingRequirements& bindingReqs, const std::vector& readers, int* binding) { std::string result; for (int i = 0; i < (int) readers.size(); ++i) { SkSpan samplers = readers[i].entry()->fTexturesAndSamplers; if (!samplers.empty()) { SkSL::String::appendf(&result, "// %s samplers\n", readers[i].entry()->fName); for (const TextureAndSampler& t : samplers) { result += EmitSamplerLayout(bindingReqs, binding); SkSL::String::appendf(&result, " uniform sampler2D %s_%d;\n", t.name(), i); } } } return result; } std::string EmitSamplerLayout(const ResourceBindingRequirements& bindingReqs, int* binding) { std::string result; // If fDistinctIndexRanges is false, then texture and sampler indices may clash with other // resource indices. Graphite assumes that they will be placed in descriptor set (Vulkan) and // bind group (Dawn) index 1. if (bindingReqs.fSeparateTextureAndSamplerBinding) { int samplerIndex = (*binding)++; int textureIndex = (*binding)++; SkSL::String::appendf(&result, "layout(wgsl, %ssampler=%d, texture=%d)", bindingReqs.fDistinctIndexRanges ? "" : "set=1, ", samplerIndex, textureIndex); } else { SkSL::String::appendf(&result, "layout(%sbinding=%d)", bindingReqs.fDistinctIndexRanges ? "" : "set=1, ", *binding); (*binding)++; } return result; } namespace { std::string emit_attributes(SkSpan vertexAttrs, SkSpan instanceAttrs) { std::string result; int attr = 0; auto add_attrs = [&](SkSpan attrs) { for (auto a : attrs) { SkSL::String::appendf(&result, " layout(location=%d) in ", attr++); result.append(SkSLTypeString(a.gpuType())); SkSL::String::appendf(&result, " %s;\n", a.name()); } }; if (!vertexAttrs.empty()) { result.append("// vertex attrs\n"); add_attrs(vertexAttrs); } if (!instanceAttrs.empty()) { result.append("// instance attrs\n"); add_attrs(instanceAttrs); } return result; } } // anonymous namespace std::string EmitVaryings(const RenderStep* step, const char* direction, bool emitShadingSsboIndexVarying, bool emitLocalCoordsVarying) { std::string result; int location = 0; if (emitShadingSsboIndexVarying) { SkSL::String::appendf(&result, " layout(location=%d) %s int shadingSsboIndexVar;\n", location++, direction); } if (emitLocalCoordsVarying) { SkSL::String::appendf(&result, " layout(location=%d) %s ", location++, direction); result.append(SkSLTypeString(SkSLType::kFloat2)); SkSL::String::appendf(&result, " localCoordsVar;\n"); } for (auto v : step->varyings()) { SkSL::String::appendf(&result, " layout(location=%d) %s ", location++, direction); result.append(SkSLTypeString(v.fType)); SkSL::String::appendf(&result, " %s;\n", v.fName); } return result; } std::string GetSkSLVS(const ResourceBindingRequirements& bindingReqs, const RenderStep* step, bool defineShadingSsboIndexVarying, bool defineLocalCoordsVarying) { // TODO: To more completely support end-to-end rendering, this will need to be updated so that // the RenderStep shader snippet can produce a device coord, a local coord, and depth. // If the paint combination doesn't need the local coord it can be ignored, otherwise we need // a varying for it. The fragment function's output will need to be updated to have a color and // the depth, or when there's no combination, just the depth. Lastly, we also should add the // static/intrinsic uniform binding point so that we can handle normalizing the device position // produced by the RenderStep automatically. // Fixed program header std::string sksl = "layout (binding=0) uniform intrinsicUniforms {\n" " layout(offset=0) float4 rtAdjust;\n" "};\n" "\n"; if (step->numVertexAttributes() > 0 || step->numInstanceAttributes() > 0) { sksl += emit_attributes(step->vertexAttributes(), step->instanceAttributes()); } // Uniforms needed by RenderStep // The uniforms are mangled by having their index in 'fEntries' as a suffix (i.e., "_%d") // TODO: replace hard-coded bufferID with the backend's renderstep uniform-buffer index. if (step->numUniforms() > 0) { sksl += EmitRenderStepUniforms( 1, "Step", bindingReqs.fUniformBufferLayout, step->uniforms()); } // Varyings needed by RenderStep sksl += EmitVaryings(step, "out", defineShadingSsboIndexVarying, defineLocalCoordsVarying); // Vertex shader function declaration sksl += "void main() {"; // Create stepLocalCoords which render steps can write to. sksl += "float2 stepLocalCoords = float2(0);"; // Vertex shader body sksl += step->vertexSkSL(); sksl += "sk_Position = float4(devPosition.xy * rtAdjust.xy + devPosition.ww * rtAdjust.zw," "devPosition.zw);"; if (defineShadingSsboIndexVarying) { // Assign SSBO index value to the SSBO index varying SkSL::String::appendf(&sksl, "shadingSsboIndexVar = %s;", step->ssboIndex()); } if (defineLocalCoordsVarying) { // Assign Render Step's stepLocalCoords to the localCoordsVar varying. sksl += "localCoordsVar = stepLocalCoords;"; } sksl += "}"; return sksl; } FragSkSLInfo GetSkSLFS(const ResourceBindingRequirements& bindingReqs, const ShaderCodeDictionary* dict, const RuntimeEffectDictionary* rteDict, const RenderStep* step, UniquePaintParamsID paintID, bool useStorageBuffers) { if (!paintID.isValid()) { // TODO: we should return the error shader code here return {}; } FragSkSLInfo result; const char* shadingSsboIndexVar = useStorageBuffers ? "shadingSsboIndexVar" : nullptr; ShaderInfo shaderInfo(rteDict, shadingSsboIndexVar); dict->getShaderInfo(paintID, &shaderInfo); result.fBlendInfo = shaderInfo.blendInfo(); result.fRequiresLocalCoords = shaderInfo.needsLocalCoords(); // Extra RenderStep uniforms are always backed by a UBO. Uniforms for the PaintParams are either // UBO or SSBO backed based on `useStorageBuffers`. result.fSkSL = shaderInfo.toSkSL(bindingReqs, step, useStorageBuffers, /*defineLocalCoordsVarying=*/result.fRequiresLocalCoords, /*numTexturesAndSamplersUsed=*/&result.fNumTexturesAndSamplers); return result; } } // namespace skgpu::graphite