1 /*
2 * Copyright 2025 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/precompile/SerializationUtils.h"
9
10 #include "include/core/SkFourByteTag.h"
11 #include "include/core/SkStream.h"
12 #include "src/base/SkAutoMalloc.h"
13 #include "src/gpu/graphite/Caps.h"
14 #include "src/gpu/graphite/GraphicsPipelineDesc.h"
15 #include "src/gpu/graphite/PaintParamsKey.h"
16 #include "src/gpu/graphite/RenderPassDesc.h"
17 #include "src/gpu/graphite/Renderer.h"
18 #include "src/gpu/graphite/ShaderCodeDictionary.h"
19 #include "src/gpu/graphite/TextureInfoPriv.h"
20
21 namespace skgpu::graphite {
22
23 // This is the main control to version the serialized Pipelines (c.f. stream_is_blob)
24 static const int kCurrent_Version = 1;
25
26 namespace {
27
28 static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'p', 'e' };
29
stream_is_pipeline(SkStream * stream)30 [[nodiscard]] bool stream_is_pipeline(SkStream* stream) {
31 char magic[8];
32 static_assert(sizeof(kMagic) == sizeof(magic), "");
33
34 if (stream->read(magic, sizeof(kMagic)) != sizeof(kMagic)) {
35 return false;
36 }
37
38 if (0 != memcmp(magic, kMagic, sizeof(kMagic))) {
39 return false;
40 }
41
42 uint32_t version;
43 if (!stream->readU32(&version)) {
44 return false;
45 }
46
47 if (version != kCurrent_Version) {
48 return false;
49 }
50
51 return true;
52 }
53
serialize_graphics_pipeline_desc(ShaderCodeDictionary * shaderCodeDictionary,SkWStream * stream,const GraphicsPipelineDesc & pipelineDesc)54 [[nodiscard]] bool serialize_graphics_pipeline_desc(ShaderCodeDictionary* shaderCodeDictionary,
55 SkWStream* stream,
56 const GraphicsPipelineDesc& pipelineDesc) {
57 PaintParamsKey key = shaderCodeDictionary->lookup(pipelineDesc.paintParamsID());
58
59 if (!stream->write32(static_cast<uint32_t>(pipelineDesc.renderStepID()))) {
60 return false;
61 }
62
63 if (!key.isValid()) {
64 if (!stream->write32(0)) {
65 return false;
66 }
67 // Not all GraphicsPipeline have a valid PaintParamsKey
68 return true;
69 }
70
71 const SkSpan<const uint32_t> keySpan = key.data();
72
73 if (!key.isSerializable(shaderCodeDictionary)) {
74 return false;
75 }
76
77 if (!stream->write32(SkToU32(keySpan.size()))) {
78 return false;
79 }
80 if (!stream->write(keySpan.data(), 4 * keySpan.size())) {
81 return false;
82 }
83 return true;
84 }
85
deserialize_graphics_pipeline_desc(ShaderCodeDictionary * shaderCodeDictionary,SkStream * stream,GraphicsPipelineDesc * pipelineDesc)86 [[nodiscard]] bool deserialize_graphics_pipeline_desc(ShaderCodeDictionary* shaderCodeDictionary,
87 SkStream* stream,
88 GraphicsPipelineDesc* pipelineDesc) {
89 uint32_t tmp;
90 if (!stream->readU32(&tmp)) {
91 return false;
92 }
93
94 if (tmp >= RenderStep::kNumRenderSteps) {
95 return false;
96 }
97 RenderStep::RenderStepID renderStepID = static_cast<RenderStep::RenderStepID>(tmp);
98
99 if (!stream->readU32(&tmp)) {
100 return false;
101 }
102
103 UniquePaintParamsID paintParamsID = UniquePaintParamsID::Invalid();
104 if (tmp) {
105 SkAutoMalloc storage(4 * tmp);
106 if (stream->read(storage.get(), 4 * tmp) != 4 * tmp) {
107 return false;
108 }
109
110 PaintParamsKey ppk = PaintParamsKey(SkSpan<uint32_t>((uint32_t*) storage.get(), tmp));
111
112 if (!ppk.isSerializable(shaderCodeDictionary)) {
113 return false;
114 }
115
116 paintParamsID = shaderCodeDictionary->findOrCreate(ppk);
117 }
118
119 *pipelineDesc = GraphicsPipelineDesc(renderStepID, paintParamsID);
120 return true;
121 }
122
serialize_attachment_desc(const Caps * caps,SkWStream * stream,const AttachmentDesc & attachmentDesc)123 [[nodiscard]] bool serialize_attachment_desc(const Caps* caps,
124 SkWStream* stream,
125 const AttachmentDesc& attachmentDesc) {
126 if (!caps->serializeTextureInfo(attachmentDesc.fTextureInfo, stream)) {
127 return false;
128 }
129
130 if (attachmentDesc.fTextureInfo.isValid()) {
131 if (!stream->write32(SkSetFourByteTag(static_cast<uint8_t>(attachmentDesc.fStoreOp),
132 static_cast<uint8_t>(attachmentDesc.fLoadOp),
133 0, 0))) {
134 return false;
135 }
136 }
137
138 return true;
139 }
140
deserialize_attachment_desc(const Caps * caps,SkStream * stream,AttachmentDesc * attachmentDesc)141 [[nodiscard]] bool deserialize_attachment_desc(const Caps* caps,
142 SkStream* stream,
143 AttachmentDesc* attachmentDesc) {
144 if (!caps->deserializeTextureInfo(stream, &attachmentDesc->fTextureInfo)) {
145 return false;
146 }
147
148 if (attachmentDesc->fTextureInfo.isValid()) {
149 uint32_t tag;
150 if (!stream->readU32(&tag)) {
151 return false;
152 }
153
154 attachmentDesc->fStoreOp = static_cast<StoreOp>(0xFF & (tag >> 24));
155 attachmentDesc->fLoadOp = static_cast<LoadOp> (0xFF & (tag >> 16));
156 }
157
158 return true;
159 }
160
serialize_render_pass_desc(const Caps * caps,SkWStream * stream,const RenderPassDesc & renderPassDesc)161 [[nodiscard]] bool serialize_render_pass_desc(const Caps* caps,
162 SkWStream* stream,
163 const RenderPassDesc& renderPassDesc) {
164 if (!serialize_attachment_desc(caps, stream, renderPassDesc.fColorAttachment)) {
165 return false;
166 }
167
168 for (int i = 0; i < 4; ++i) {
169 if (!stream->writeScalar(renderPassDesc.fClearColor[i])) {
170 return false;
171 }
172 }
173
174 if (!serialize_attachment_desc(caps, stream, renderPassDesc.fColorResolveAttachment)) {
175 return false;
176 }
177 if (!serialize_attachment_desc(caps, stream, renderPassDesc.fDepthStencilAttachment)) {
178 return false;
179 }
180
181 if (!stream->writeScalar(renderPassDesc.fClearDepth)) {
182 return false;
183 }
184 if (!stream->write32(renderPassDesc.fClearStencil)) {
185 return false;
186 }
187
188 if (!stream->write32(SkSetFourByteTag(renderPassDesc.fWriteSwizzle[0],
189 renderPassDesc.fWriteSwizzle[1],
190 renderPassDesc.fWriteSwizzle[2],
191 renderPassDesc.fWriteSwizzle[3]))) {
192 return false;
193 }
194
195 if (!stream->write32(renderPassDesc.fSampleCount)) {
196 return false;
197 }
198
199 if (!stream->write8(static_cast<uint8_t>(renderPassDesc.fDstReadStrategyIfRequired))) {
200 return false;
201 }
202
203 return true;
204 }
205
deserialize_render_pass_desc(const Caps * caps,SkStream * stream,RenderPassDesc * renderPassDesc)206 [[nodiscard]] bool deserialize_render_pass_desc(const Caps* caps,
207 SkStream* stream,
208 RenderPassDesc* renderPassDesc) {
209 if (!deserialize_attachment_desc(caps, stream, &renderPassDesc->fColorAttachment)) {
210 return false;
211 }
212
213 for (int i = 0; i < 4; ++i) {
214 if (!stream->readScalar(&renderPassDesc->fClearColor[i])) {
215 return false;
216 }
217 }
218
219 if (!deserialize_attachment_desc(caps, stream, &renderPassDesc->fColorResolveAttachment)) {
220 return false;
221 }
222 if (!deserialize_attachment_desc(caps, stream, &renderPassDesc->fDepthStencilAttachment)) {
223 return false;
224 }
225
226 if (!stream->readScalar(&renderPassDesc->fClearDepth)) {
227 return false;
228 }
229 if (!stream->readU32(&renderPassDesc->fClearStencil)) {
230 return false;
231 }
232
233 uint32_t tag;
234 if (!stream->readU32(&tag)) {
235 return false;
236 }
237
238 char tmpSwizzle[4] = {
239 (char) (0xFF & (tag >> 24)),
240 (char) (0xFF & (tag >> 16)),
241 (char) (0xFF & (tag >> 8)),
242 (char) (0xFF & (tag)),
243 };
244
245 renderPassDesc->fWriteSwizzle = Swizzle(tmpSwizzle);
246
247 if (!stream->readU32(&renderPassDesc->fSampleCount)) {
248 return false;
249 }
250
251 uint8_t tmp8;
252 if (!stream->readU8(&tmp8)) {
253 return false;
254 }
255
256 renderPassDesc->fDstReadStrategyIfRequired = static_cast<DstReadStrategy>(tmp8);
257
258 return true;
259 }
260
261 #define SK_BLOB_END_TAG SkSetFourByteTag('e', 'n', 'd', ' ')
262
SerializePipelineDesc(const Caps * caps,ShaderCodeDictionary * shaderCodeDictionary,SkWStream * stream,const GraphicsPipelineDesc & pipelineDesc,const RenderPassDesc & renderPassDesc)263 bool SerializePipelineDesc(const Caps* caps,
264 ShaderCodeDictionary* shaderCodeDictionary,
265 SkWStream* stream,
266 const GraphicsPipelineDesc& pipelineDesc,
267 const RenderPassDesc& renderPassDesc) {
268
269 stream->write(kMagic, sizeof(kMagic));
270 stream->write32(kCurrent_Version);
271
272 if (!serialize_graphics_pipeline_desc(shaderCodeDictionary, stream, pipelineDesc)) {
273 return false;
274 }
275
276 if (!serialize_render_pass_desc(caps, stream, renderPassDesc)) {
277 return false;
278 }
279
280 stream->write32(SK_BLOB_END_TAG);
281 return true;
282 }
283
DeserializePipelineDesc(const Caps * caps,ShaderCodeDictionary * shaderCodeDictionary,SkStream * stream,GraphicsPipelineDesc * pipelineDesc,RenderPassDesc * renderPassDesc)284 bool DeserializePipelineDesc(const Caps* caps,
285 ShaderCodeDictionary* shaderCodeDictionary,
286 SkStream* stream,
287 GraphicsPipelineDesc* pipelineDesc,
288 RenderPassDesc* renderPassDesc) {
289 SkASSERT(stream);
290
291 if (!stream_is_pipeline(stream)) {
292 return false;
293 }
294
295 if (!deserialize_graphics_pipeline_desc(shaderCodeDictionary, stream, pipelineDesc)) {
296 return false;
297 }
298
299 if (!deserialize_render_pass_desc(caps, stream, renderPassDesc)) {
300 return false;
301 }
302
303 uint32_t tag;
304 if (!stream->readU32(&tag)) {
305 return false;
306 }
307
308 if (tag != SK_BLOB_END_TAG) {
309 return false;
310 }
311
312 return true;
313 }
314
315 } // anonymous namespace
316
PipelineDescToData(const Caps * caps,ShaderCodeDictionary * shaderCodeDictionary,const GraphicsPipelineDesc & pipelineDesc,const RenderPassDesc & renderPassDesc)317 sk_sp<SkData> PipelineDescToData(const Caps* caps,
318 ShaderCodeDictionary* shaderCodeDictionary,
319 const GraphicsPipelineDesc& pipelineDesc,
320 const RenderPassDesc& renderPassDesc) {
321 SkDynamicMemoryWStream stream;
322
323 if (!SerializePipelineDesc(caps,
324 shaderCodeDictionary,
325 &stream,
326 pipelineDesc, renderPassDesc)) {
327 return nullptr;
328 }
329
330 return stream.detachAsData();
331 }
332
DataToPipelineDesc(const Caps * caps,ShaderCodeDictionary * shaderCodeDictionary,const SkData * data,GraphicsPipelineDesc * pipelineDesc,RenderPassDesc * renderPassDesc)333 bool DataToPipelineDesc(const Caps* caps,
334 ShaderCodeDictionary* shaderCodeDictionary,
335 const SkData* data,
336 GraphicsPipelineDesc* pipelineDesc,
337 RenderPassDesc* renderPassDesc) {
338 if (!data) {
339 return false;
340 }
341 SkMemoryStream stream(data->data(), data->size());
342
343 if (!DeserializePipelineDesc(caps, shaderCodeDictionary, &stream,
344 pipelineDesc,
345 renderPassDesc)) {
346 return false;
347 }
348
349 return true;
350 }
351
352 #if defined(GPU_TEST_UTILS)
DumpPipelineDesc(const char * label,ShaderCodeDictionary * shaderCodeDictionary,const GraphicsPipelineDesc & pipelineDesc,const RenderPassDesc & renderPassDesc)353 void DumpPipelineDesc(const char* label,
354 ShaderCodeDictionary* shaderCodeDictionary,
355 const GraphicsPipelineDesc& pipelineDesc,
356 const RenderPassDesc& renderPassDesc) {
357 SkString pipelineStr = pipelineDesc.toString(shaderCodeDictionary);
358 SkString renderPassStr = renderPassDesc.toPipelineLabel();
359 SkDebugf("%s: %s - %s\n", label, pipelineStr.c_str(), renderPassStr.c_str());
360 }
361
ComparePipelineDescs(const GraphicsPipelineDesc & a1,const RenderPassDesc & b1,const GraphicsPipelineDesc & a2,const RenderPassDesc & b2)362 bool ComparePipelineDescs(const GraphicsPipelineDesc& a1, const RenderPassDesc& b1,
363 const GraphicsPipelineDesc& a2, const RenderPassDesc& b2) {
364 return (a1 == a2) && (b1 == b2);
365 }
366 #endif
367
368 } // namespace skgpu::graphite
369