xref: /aosp_15_r20/external/skia/example/external_client/src/graphite_native_metal.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2024 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 "include/core/SkBitmap.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkImage.h"
11 #include "include/core/SkRRect.h"
12 #include "include/core/SkRect.h"
13 #include "include/core/SkSize.h"
14 #include "include/core/SkStream.h"
15 #include "include/core/SkSurface.h"
16 #include "include/encode/SkJpegEncoder.h"
17 #include "include/gpu/graphite/Context.h"
18 #include "include/gpu/graphite/ContextOptions.h"
19 #include "include/gpu/graphite/GraphiteTypes.h"
20 #include "include/gpu/graphite/Recorder.h"
21 #include "include/gpu/graphite/Surface.h"
22 #include "include/gpu/graphite/mtl/MtlBackendContext.h"
23 #include "include/gpu/graphite/mtl/MtlGraphiteUtils.h"
24 
25 #include "graphite_metal_context_helper.h"
26 
27 #define WIDTH 200
28 #define HEIGHT 400
29 
main(int argc,char * argv[])30 int main(int argc, char *argv[]) {
31     if (argc != 2) {
32         printf("Usage: %s <name.jpeg>\n", argv[0]);
33         return 1;
34     }
35 
36     SkFILEWStream output(argv[1]);
37     if (!output.isValid()) {
38         printf("Cannot open output file %s\n", argv[1]);
39         return 1;
40     }
41 
42     skgpu::graphite::MtlBackendContext backendContext = GetMetalContext();
43     skgpu::graphite::ContextOptions options;
44 
45     std::unique_ptr<skgpu::graphite::Context> context =
46         skgpu::graphite::ContextFactory::MakeMetal(backendContext, options);
47     if (!context) {
48         printf("Could not make Graphite Native Metal context\n");
49         return 1;
50     }
51     printf("Context made, now to make the surface\n");
52 
53     SkImageInfo imageInfo =
54             SkImageInfo::Make(WIDTH, HEIGHT, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
55 
56     std::unique_ptr<skgpu::graphite::Recorder> recorder = context->makeRecorder();
57     if (!recorder) {
58         printf("Could not make recorder\n");
59         return 1;
60     }
61     sk_sp<SkSurface> surface =
62             SkSurfaces::RenderTarget(recorder.get(), imageInfo);
63     if (!surface) {
64         printf("Could not make surface from Metal Recorder\n");
65         return 1;
66     }
67 
68     SkCanvas* canvas = surface->getCanvas();
69     canvas->clear(SK_ColorCYAN);
70     SkRRect rrect = SkRRect::MakeRectXY(SkRect::MakeLTRB(10, 20, 50, 70), 10, 10);
71 
72     SkPaint paint;
73     paint.setColor(SK_ColorYELLOW);
74     paint.setAntiAlias(true);
75 
76     canvas->drawRRect(rrect, paint);
77 
78     printf("ready to snap the GPU calls\n");
79     // Now to send the draws to the GPU
80     std::unique_ptr<skgpu::graphite::Recording> recording = recorder->snap();
81     if (!recording) {
82         printf("Could not create a recording\n");
83         return 1;
84     }
85     skgpu::graphite::InsertRecordingInfo info;
86     info.fRecording = recording.get();
87     if (!context->insertRecording(info)) {
88         printf("Context::insertRecording failed\n");
89         return 1;
90     }
91 
92     sk_sp<SkImage> img;
93     auto callback = [](SkImage::ReadPixelsContext ctx,
94                        std::unique_ptr<const SkImage::AsyncReadResult> result) {
95         if (result->count() != 1) {
96             printf("Didn't load exactly one plane\n");
97             return;
98         }
99         auto ii = SkImageInfo::Make(WIDTH, HEIGHT, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
100         sk_sp<SkImage>* output = reinterpret_cast<sk_sp<SkImage>*>(ctx);
101 
102         SkPixmap pm(ii, result->data(0), result->rowBytes(0));
103         *output = SkImages::RasterFromPixmapCopy(pm);
104     };
105     printf("GPU operations modifying surface have been inserted in the command buffer,"
106            "scheduling pixel readback\n");
107     context->asyncRescaleAndReadPixels(surface.get(), imageInfo, SkIRect::MakeSize({WIDTH, HEIGHT}),
108                                        SkImage::RescaleGamma::kSrc,
109                                        SkImage::RescaleMode::kRepeatedCubic,
110                                        callback, &img);
111 
112     printf("Submitting work to GPU and waiting for it to be done\n");
113     // Note this doesn't work on all backend types, e.g. Dawn.
114     context->submit(skgpu::graphite::SyncToCpu::kYes);
115     if (context->hasUnfinishedGpuWork()) {
116         printf("Sync with GPU completion failed\n");
117         return 1;
118     }
119 
120     sk_sp<SkData> jpeg = SkJpegEncoder::Encode(nullptr, img.get(), {});
121     if (!jpeg) {
122         printf("Encoding failed\n");
123         return 1;
124     }
125     output.write(jpeg->data(), jpeg->size());
126     output.fsync();
127     printf("done\n");
128     return 0;
129 }
130