1 /*
2 * Copyright 2021 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "src/gpu/graphite/ContextUtils.h"
9
10 #include <string>
11 #include "include/private/SkSLString.h"
12 #include "src/core/SkBlenderBase.h"
13 #include "src/gpu/graphite/Caps.h"
14 #include "src/gpu/graphite/GraphicsPipelineDesc.h"
15 #include "src/gpu/graphite/KeyContext.h"
16 #include "src/gpu/graphite/PaintParams.h"
17 #include "src/gpu/graphite/PipelineData.h"
18 #include "src/gpu/graphite/RecorderPriv.h"
19 #include "src/gpu/graphite/Renderer.h"
20 #include "src/gpu/graphite/ResourceProvider.h"
21 #include "src/gpu/graphite/ShaderCodeDictionary.h"
22 #include "src/gpu/graphite/UniformManager.h"
23 #include "src/gpu/graphite/UniquePaintParamsID.h"
24
25 namespace skgpu::graphite {
26
27 std::tuple<UniquePaintParamsID, const UniformDataBlock*, const TextureDataBlock*>
ExtractPaintData(Recorder * recorder,PipelineDataGatherer * gatherer,PaintParamsKeyBuilder * builder,const Layout layout,const SkM44 & local2Dev,const PaintParams & p,const SkColorInfo & targetColorInfo)28 ExtractPaintData(Recorder* recorder,
29 PipelineDataGatherer* gatherer,
30 PaintParamsKeyBuilder* builder,
31 const Layout layout,
32 const SkM44& local2Dev,
33 const PaintParams& p,
34 const SkColorInfo& targetColorInfo) {
35 SkDEBUGCODE(builder->checkReset());
36
37 gatherer->resetWithNewLayout(layout);
38
39 KeyContext keyContext(recorder, local2Dev, targetColorInfo);
40 p.toKey(keyContext, builder, gatherer);
41
42 auto dict = recorder->priv().shaderCodeDictionary();
43 UniformDataCache* uniformDataCache = recorder->priv().uniformDataCache();
44 TextureDataCache* textureDataCache = recorder->priv().textureDataCache();
45
46 auto entry = dict->findOrCreate(builder);
47
48 const UniformDataBlock* uniforms =
49 gatherer->hasUniforms() ? uniformDataCache->insert(gatherer->finishUniformDataBlock())
50 : nullptr;
51 const TextureDataBlock* textures =
52 gatherer->hasTextures() ? textureDataCache->insert(gatherer->textureDataBlock())
53 : nullptr;
54
55 return { entry->uniqueID(), uniforms, textures };
56 }
57
ExtractRenderStepData(UniformDataCache * uniformDataCache,TextureDataCache * textureDataCache,PipelineDataGatherer * gatherer,const Layout layout,const RenderStep * step,const DrawParams & params)58 std::tuple<const UniformDataBlock*, const TextureDataBlock*> ExtractRenderStepData(
59 UniformDataCache* uniformDataCache,
60 TextureDataCache* textureDataCache,
61 PipelineDataGatherer* gatherer,
62 const Layout layout,
63 const RenderStep* step,
64 const DrawParams& params) {
65 gatherer->resetWithNewLayout(layout);
66 step->writeUniformsAndTextures(params, gatherer);
67
68 const UniformDataBlock* uniforms =
69 gatherer->hasUniforms() ? uniformDataCache->insert(gatherer->finishUniformDataBlock())
70 : nullptr;
71 const TextureDataBlock* textures =
72 gatherer->hasTextures() ? textureDataCache->insert(gatherer->textureDataBlock())
73 : nullptr;
74
75 return { uniforms, textures };
76 }
77
78 namespace {
get_uniform_header(int bufferID,const char * name)79 std::string get_uniform_header(int bufferID, const char* name) {
80 std::string result;
81
82 SkSL::String::appendf(&result, "layout (binding=%d) uniform %sUniforms {\n", bufferID, name);
83
84 return result;
85 }
86
get_uniforms(Layout layout,SkSpan<const Uniform> uniforms,int * offset,int manglingSuffix)87 std::string get_uniforms(Layout layout,
88 SkSpan<const Uniform> uniforms,
89 int* offset,
90 int manglingSuffix) {
91 std::string result;
92 UniformOffsetCalculator offsetter(layout, *offset);
93
94 for (const Uniform& u : uniforms) {
95 SkSL::String::appendf(&result,
96 " layout(offset=%zu) %s %s",
97 offsetter.advanceOffset(u.type(), u.count()),
98 SkSLTypeString(u.type()),
99 u.name());
100 if (manglingSuffix >= 0) {
101 result.append("_");
102 result.append(std::to_string(manglingSuffix));
103 }
104 if (u.count()) {
105 result.append("[");
106 result.append(std::to_string(u.count()));
107 result.append("]");
108 }
109 result.append(";\n");
110 }
111
112 *offset = offsetter.size();
113 return result;
114 }
115 } // anonymous namespace
116
EmitPaintParamsUniforms(int bufferID,const char * name,const Layout layout,const std::vector<PaintParamsKey::BlockReader> & readers)117 std::string EmitPaintParamsUniforms(int bufferID,
118 const char* name,
119 const Layout layout,
120 const std::vector<PaintParamsKey::BlockReader>& readers) {
121 int offset = 0;
122
123 std::string result = get_uniform_header(bufferID, name);
124 for (int i = 0; i < (int) readers.size(); ++i) {
125 SkSpan<const Uniform> uniforms = readers[i].entry()->fUniforms;
126
127 if (!uniforms.empty()) {
128 SkSL::String::appendf(&result, "// %s uniforms\n", readers[i].entry()->fName);
129 result += get_uniforms(layout, uniforms, &offset, i);
130 }
131 }
132 result.append("};\n\n");
133
134 return result;
135 }
136
EmitRenderStepUniforms(int bufferID,const char * name,const Layout layout,SkSpan<const Uniform> uniforms)137 std::string EmitRenderStepUniforms(int bufferID,
138 const char* name,
139 const Layout layout,
140 SkSpan<const Uniform> uniforms) {
141 int offset = 0;
142
143 std::string result = get_uniform_header(bufferID, name);
144 result += get_uniforms(layout, uniforms, &offset, -1);
145 result.append("};\n\n");
146
147 return result;
148 }
149
EmitPaintParamsStorageBuffer(int bufferID,const char * bufferTypePrefix,const char * bufferNamePrefix,const std::vector<PaintParamsKey::BlockReader> & readers)150 std::string EmitPaintParamsStorageBuffer(
151 int bufferID,
152 const char* bufferTypePrefix,
153 const char* bufferNamePrefix,
154 const std::vector<PaintParamsKey::BlockReader>& readers) {
155
156 std::string result;
157 SkSL::String::appendf(&result, "struct %sUniformData {\n", bufferTypePrefix);
158 for (int i = 0; i < (int)readers.size(); ++i) {
159 SkSpan<const Uniform> uniforms = readers[i].entry()->fUniforms;
160 if (uniforms.empty()) {
161 continue;
162 }
163 SkSL::String::appendf(&result, "// %s uniforms\n", readers[i].entry()->fName);
164 int manglingSuffix = i;
165 for (const Uniform& u : uniforms) {
166 SkSL::String::appendf(
167 &result, " %s %s_%d", SkSLTypeString(u.type()), u.name(), manglingSuffix);
168 if (u.count()) {
169 SkSL::String::appendf(&result, "[%u]", u.count());
170 }
171 result.append(";\n");
172 }
173 }
174 result.append("};\n\n");
175
176 SkSL::String::appendf(&result,
177 "layout (binding=%d) buffer %sUniforms {\n"
178 " %sUniformData %sUniformData[];\n"
179 "};\n",
180 bufferID,
181 bufferTypePrefix,
182 bufferTypePrefix,
183 bufferNamePrefix);
184 return result;
185 }
186
EmitStorageBufferAccess(const char * bufferNamePrefix,const char * ssboIndex,const char * uniformName)187 std::string EmitStorageBufferAccess(const char* bufferNamePrefix,
188 const char* ssboIndex,
189 const char* uniformName) {
190 return SkSL::String::printf("%sUniformData[%s].%s", bufferNamePrefix, ssboIndex, uniformName);
191 }
192
EmitTexturesAndSamplers(const ResourceBindingRequirements & bindingReqs,const std::vector<PaintParamsKey::BlockReader> & readers,int * binding)193 std::string EmitTexturesAndSamplers(const ResourceBindingRequirements& bindingReqs,
194 const std::vector<PaintParamsKey::BlockReader>& readers,
195 int* binding) {
196 std::string result;
197 for (int i = 0; i < (int) readers.size(); ++i) {
198 SkSpan<const TextureAndSampler> samplers = readers[i].entry()->fTexturesAndSamplers;
199
200 if (!samplers.empty()) {
201 SkSL::String::appendf(&result, "// %s samplers\n", readers[i].entry()->fName);
202
203 for (const TextureAndSampler& t : samplers) {
204 result += EmitSamplerLayout(bindingReqs, binding);
205 SkSL::String::appendf(&result, " uniform sampler2D %s_%d;\n", t.name(), i);
206 }
207 }
208 }
209
210 return result;
211 }
212
EmitSamplerLayout(const ResourceBindingRequirements & bindingReqs,int * binding)213 std::string EmitSamplerLayout(const ResourceBindingRequirements& bindingReqs, int* binding) {
214 std::string result;
215
216 // If fDistinctIndexRanges is false, then texture and sampler indices may clash with other
217 // resource indices. Graphite assumes that they will be placed in descriptor set (Vulkan) and
218 // bind group (Dawn) index 1.
219 if (bindingReqs.fSeparateTextureAndSamplerBinding) {
220 int samplerIndex = (*binding)++;
221 int textureIndex = (*binding)++;
222 SkSL::String::appendf(&result,
223 "layout(wgsl, %ssampler=%d, texture=%d)",
224 bindingReqs.fDistinctIndexRanges ? "" : "set=1, ",
225 samplerIndex,
226 textureIndex);
227 } else {
228 SkSL::String::appendf(&result,
229 "layout(%sbinding=%d)",
230 bindingReqs.fDistinctIndexRanges ? "" : "set=1, ",
231 *binding);
232 (*binding)++;
233 }
234 return result;
235 }
236
237 namespace {
emit_attributes(SkSpan<const Attribute> vertexAttrs,SkSpan<const Attribute> instanceAttrs)238 std::string emit_attributes(SkSpan<const Attribute> vertexAttrs,
239 SkSpan<const Attribute> instanceAttrs) {
240 std::string result;
241
242 int attr = 0;
243 auto add_attrs = [&](SkSpan<const Attribute> attrs) {
244 for (auto a : attrs) {
245 SkSL::String::appendf(&result, " layout(location=%d) in ", attr++);
246 result.append(SkSLTypeString(a.gpuType()));
247 SkSL::String::appendf(&result, " %s;\n", a.name());
248 }
249 };
250
251 if (!vertexAttrs.empty()) {
252 result.append("// vertex attrs\n");
253 add_attrs(vertexAttrs);
254 }
255 if (!instanceAttrs.empty()) {
256 result.append("// instance attrs\n");
257 add_attrs(instanceAttrs);
258 }
259
260 return result;
261 }
262 } // anonymous namespace
263
EmitVaryings(const RenderStep * step,const char * direction,bool emitShadingSsboIndexVarying,bool emitLocalCoordsVarying)264 std::string EmitVaryings(const RenderStep* step,
265 const char* direction,
266 bool emitShadingSsboIndexVarying,
267 bool emitLocalCoordsVarying) {
268 std::string result;
269 int location = 0;
270
271 if (emitShadingSsboIndexVarying) {
272 SkSL::String::appendf(&result,
273 " layout(location=%d) %s int shadingSsboIndexVar;\n",
274 location++,
275 direction);
276 }
277
278 if (emitLocalCoordsVarying) {
279 SkSL::String::appendf(&result, " layout(location=%d) %s ", location++, direction);
280 result.append(SkSLTypeString(SkSLType::kFloat2));
281 SkSL::String::appendf(&result, " localCoordsVar;\n");
282 }
283
284 for (auto v : step->varyings()) {
285 SkSL::String::appendf(&result, " layout(location=%d) %s ", location++, direction);
286 result.append(SkSLTypeString(v.fType));
287 SkSL::String::appendf(&result, " %s;\n", v.fName);
288 }
289
290 return result;
291 }
292
GetSkSLVS(const ResourceBindingRequirements & bindingReqs,const RenderStep * step,bool defineShadingSsboIndexVarying,bool defineLocalCoordsVarying)293 std::string GetSkSLVS(const ResourceBindingRequirements& bindingReqs,
294 const RenderStep* step,
295 bool defineShadingSsboIndexVarying,
296 bool defineLocalCoordsVarying) {
297 // TODO: To more completely support end-to-end rendering, this will need to be updated so that
298 // the RenderStep shader snippet can produce a device coord, a local coord, and depth.
299 // If the paint combination doesn't need the local coord it can be ignored, otherwise we need
300 // a varying for it. The fragment function's output will need to be updated to have a color and
301 // the depth, or when there's no combination, just the depth. Lastly, we also should add the
302 // static/intrinsic uniform binding point so that we can handle normalizing the device position
303 // produced by the RenderStep automatically.
304
305 // Fixed program header
306 std::string sksl =
307 "layout (binding=0) uniform intrinsicUniforms {\n"
308 " layout(offset=0) float4 rtAdjust;\n"
309 "};\n"
310 "\n";
311
312 if (step->numVertexAttributes() > 0 || step->numInstanceAttributes() > 0) {
313 sksl += emit_attributes(step->vertexAttributes(), step->instanceAttributes());
314 }
315
316 // Uniforms needed by RenderStep
317 // The uniforms are mangled by having their index in 'fEntries' as a suffix (i.e., "_%d")
318 // TODO: replace hard-coded bufferID with the backend's renderstep uniform-buffer index.
319 if (step->numUniforms() > 0) {
320 sksl += EmitRenderStepUniforms(
321 1, "Step", bindingReqs.fUniformBufferLayout, step->uniforms());
322 }
323
324 // Varyings needed by RenderStep
325 sksl += EmitVaryings(step, "out", defineShadingSsboIndexVarying, defineLocalCoordsVarying);
326
327 // Vertex shader function declaration
328 sksl += "void main() {";
329 // Create stepLocalCoords which render steps can write to.
330 sksl += "float2 stepLocalCoords = float2(0);";
331 // Vertex shader body
332 sksl += step->vertexSkSL();
333 sksl += "sk_Position = float4(devPosition.xy * rtAdjust.xy + devPosition.ww * rtAdjust.zw,"
334 "devPosition.zw);";
335
336 if (defineShadingSsboIndexVarying) {
337 // Assign SSBO index value to the SSBO index varying
338 SkSL::String::appendf(&sksl, "shadingSsboIndexVar = %s;", step->ssboIndex());
339 }
340
341 if (defineLocalCoordsVarying) {
342 // Assign Render Step's stepLocalCoords to the localCoordsVar varying.
343 sksl += "localCoordsVar = stepLocalCoords;";
344 }
345 sksl += "}";
346
347 return sksl;
348 }
349
GetSkSLFS(const ResourceBindingRequirements & bindingReqs,const ShaderCodeDictionary * dict,const RuntimeEffectDictionary * rteDict,const RenderStep * step,UniquePaintParamsID paintID,bool useStorageBuffers)350 FragSkSLInfo GetSkSLFS(const ResourceBindingRequirements& bindingReqs,
351 const ShaderCodeDictionary* dict,
352 const RuntimeEffectDictionary* rteDict,
353 const RenderStep* step,
354 UniquePaintParamsID paintID,
355 bool useStorageBuffers) {
356 if (!paintID.isValid()) {
357 // TODO: we should return the error shader code here
358 return {};
359 }
360
361 FragSkSLInfo result;
362
363 const char* shadingSsboIndexVar = useStorageBuffers ? "shadingSsboIndexVar" : nullptr;
364 ShaderInfo shaderInfo(rteDict, shadingSsboIndexVar);
365
366 dict->getShaderInfo(paintID, &shaderInfo);
367 result.fBlendInfo = shaderInfo.blendInfo();
368 result.fRequiresLocalCoords = shaderInfo.needsLocalCoords();
369
370 // Extra RenderStep uniforms are always backed by a UBO. Uniforms for the PaintParams are either
371 // UBO or SSBO backed based on `useStorageBuffers`.
372 result.fSkSL =
373 shaderInfo.toSkSL(bindingReqs,
374 step,
375 useStorageBuffers,
376 /*defineLocalCoordsVarying=*/result.fRequiresLocalCoords,
377 /*numTexturesAndSamplersUsed=*/&result.fNumTexturesAndSamplers);
378
379 return result;
380 }
381
382 } // namespace skgpu::graphite
383