xref: /aosp_15_r20/external/skia/example/external_client/src/ganesh_gl.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/SkCanvas.h"
9 #include "include/core/SkRRect.h"
10 #include "include/core/SkRect.h"
11 #include "include/core/SkStream.h"
12 #include "include/core/SkSurface.h"
13 #include "include/gpu/ganesh/GrDirectContext.h"
14 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
15 #include "include/gpu/ganesh/gl/GrGLDirectContext.h"
16 #include "include/gpu/ganesh/gl/GrGLInterface.h"
17 #include "include/encode/SkWebpEncoder.h"
18 
19 #if defined(__linux__)
20 #include "include/gpu/ganesh/gl/glx/GrGLMakeGLXInterface.h"
21 
22 #include <X11/Xlib.h>
23 #include <GL/glx.h>
24 #include <GL/gl.h>
25 #endif
26 
27 #if defined(__APPLE__) && TARGET_OS_MAC == 1
28 #include "include/gpu/ganesh/gl/mac/GrGLMakeMacInterface.h"
29 
30 #include "gl_context_helper.h"
31 #endif
32 
33 #if defined(_MSC_VER)
34 #include <windows.h>
35 #include "include/gpu/ganesh/gl/win/GrGLMakeWinInterface.h"
36 #endif
37 
38 #include <cstdio>
39 
40 #if defined(__linux__)
41 
42 // Set up an X Display that can be rendered to GL. This will not be visible while
43 // the program runs. It is cribbed from how Skia's tooling sets itself up (e.g. viewer).
initialize_gl_linux()44 bool initialize_gl_linux() {
45     Display* display = XOpenDisplay(nullptr);
46     if (!display) {
47         printf("Could not open an X display\n");
48         return false;
49     }
50     static int constexpr kChooseFBConfigAtt[] = {
51         GLX_RENDER_TYPE, GLX_RGBA_BIT,
52         GLX_DOUBLEBUFFER, True,
53         GLX_STENCIL_SIZE, 8,
54         None
55     };
56     int n;
57     GLXFBConfig* fbConfig = glXChooseFBConfig(display, DefaultScreen(display), kChooseFBConfigAtt, &n);
58     XVisualInfo* visualInfo;
59     if (n > 0) {
60         visualInfo = glXGetVisualFromFBConfig(display, *fbConfig);
61     } else {
62         // For some reason glXChooseVisual takes a non-const pointer to the attributes.
63         int chooseVisualAtt[] = {
64             GLX_RGBA,
65             GLX_DOUBLEBUFFER,
66             GLX_STENCIL_SIZE, 8,
67             None
68         };
69         visualInfo = glXChooseVisual(display, DefaultScreen(display), chooseVisualAtt);
70     }
71     if (!visualInfo) {
72         printf("Could not get X visualInfo\n");
73         return false;
74     }
75     GLXContext glContext = glXCreateContext(display, visualInfo, nullptr, GL_TRUE);
76     if (!glContext) {
77         printf("Could not make GL X context\n");
78         return false;
79     }
80     Colormap colorMap = XCreateColormap(display,
81                                         RootWindow(display, visualInfo->screen),
82                                         visualInfo->visual,
83                                         AllocNone);
84     XSetWindowAttributes swa;
85     swa.colormap = colorMap;
86     swa.event_mask = 0;
87     Window window = XCreateWindow(display,
88                             RootWindow(display, visualInfo->screen),
89                             0, 0, // x, y
90                             1280, 960, // width, height
91                             0, // border width
92                             visualInfo->depth,
93                             InputOutput,
94                             visualInfo->visual,
95                             CWEventMask | CWColormap,
96                             &swa);
97 
98     if (!glXMakeCurrent(display, window, glContext)) {
99         printf("Could not set GL X context to be the created one\n");
100         return false;
101     }
102     return true;
103 }
104 #endif  // defined(__linux__)
105 
106 #if defined(_MSC_VER)
107 
108 // This was mostly taken from
109 // https://skia.googlesource.com/skia/+/f725a5ba8a29ec7c271805624d54755ce34968a1/tools/gpu/gl/win/CreatePlatformGLTestContext_win.cpp#57
initialize_gl_win()110 bool initialize_gl_win() {
111     HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(nullptr);
112     WNDCLASS wc;
113     wc.cbClsExtra = 0;
114     wc.cbWndExtra = 0;
115     wc.hbrBackground = nullptr;
116     wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
117     wc.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
118     wc.hInstance = hInstance;
119     wc.lpfnWndProc = (WNDPROC) DefWindowProc;
120     wc.lpszClassName = TEXT("external_client");
121     wc.lpszMenuName = nullptr;
122     wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
123 
124     ATOM cls = RegisterClass(&wc);
125     if (!cls) {
126         printf("Could not register window class.\n");
127         return false;
128     }
129 
130     HWND window;
131     if (!(window = CreateWindow(TEXT("external_client"),
132                                  TEXT("my-window"),
133                                  CS_OWNDC,
134                                  0, 0, 1, 1,
135                                  nullptr, nullptr,
136                                  hInstance, nullptr))) {
137         printf("Could not create window.\n");
138         return false;
139     }
140 
141     HDC deviceContext;
142     if (!(deviceContext = GetDC(window))) {
143         printf("Could not get device context.\n");
144         return false;
145     }
146 
147     // Taken from https://www.khronos.org/opengl/wiki/Creating_an_OpenGL_Context_(WGL)
148     PIXELFORMATDESCRIPTOR pfd = {
149         sizeof(PIXELFORMATDESCRIPTOR),
150         1,
151         PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,    // Flags
152         PFD_TYPE_RGBA,        // The kind of framebuffer. RGBA or palette.
153         32,                   // Colordepth of the framebuffer.
154         0, 0, 0, 0, 0, 0,
155         0,
156         0,
157         0,
158         0, 0, 0, 0,
159         24,                   // Number of bits for the depthbuffer
160         8,                    // Number of bits for the stencilbuffer
161         0,                    // Number of Aux buffers in the framebuffer.
162         PFD_MAIN_PLANE,
163         0,
164         0, 0, 0
165     };
166     int pixelFormat = ChoosePixelFormat(deviceContext, &pfd);
167     SetPixelFormat(deviceContext, pixelFormat, &pfd);
168 
169     HGLRC glrc;
170     if (!(glrc = wglCreateContext(deviceContext))) {
171         printf("Could not create rendering context.\n");
172         return false;
173     }
174 
175     if (!(wglMakeCurrent(deviceContext, glrc))) {
176         printf("Could not set the context.\n");
177         return false;
178     }
179     return true;
180 }
181 #endif  // defined(_MSC_VER)
182 
main(int argc,char ** argv)183 int main(int argc, char** argv) {
184     if (argc != 2) {
185         printf("Usage: %s <name.webp>\n", argv[0]);
186         return 1;
187     }
188 
189     SkFILEWStream output(argv[1]);
190     if (!output.isValid()) {
191         printf("Cannot open output file %s\n", argv[1]);
192         return 1;
193     }
194 
195     GrContextOptions opts;
196     opts.fSuppressPrints = true;
197 #if defined(__linux__)
198     if (!initialize_gl_linux()) {
199         return 1;
200     }
201     sk_sp<const GrGLInterface> iface = GrGLInterfaces::MakeGLX();
202 #elif defined(__APPLE__) && TARGET_OS_MAC == 1
203     if (!initialize_gl_mac()) {
204         return 1;
205     }
206     sk_sp<const GrGLInterface> iface = GrGLInterfaces::MakeMac();
207 #elif defined(_MSC_VER)
208     if (!initialize_gl_win()) {
209         return 1;
210     }
211     sk_sp<const GrGLInterface> iface = GrGLInterfaces::MakeWin();
212 #endif
213     if (!iface) {
214         printf("Could not make GL interface\n");
215         return 1;
216     }
217 
218     sk_sp<GrDirectContext> ctx = GrDirectContexts::MakeGL(iface, opts);
219     if (!ctx) {
220         printf("Could not make GrDirectContext\n");
221         return 1;
222     }
223     printf("Context made, now to make the surface\n");
224 
225     SkImageInfo imageInfo =
226             SkImageInfo::Make(200, 400, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
227 
228     sk_sp<SkSurface> surface =
229             SkSurfaces::RenderTarget(ctx.get(), skgpu::Budgeted::kYes, imageInfo);
230     if (!surface) {
231         printf("Could not make surface from GL DirectContext\n");
232         return 1;
233     }
234 
235     SkCanvas* canvas = surface->getCanvas();
236     canvas->clear(SK_ColorRED);
237     SkRRect rrect = SkRRect::MakeRectXY(SkRect::MakeLTRB(10, 20, 50, 70), 10, 10);
238 
239     SkPaint paint;
240     paint.setColor(SK_ColorBLUE);
241     paint.setAntiAlias(true);
242 
243     canvas->drawRRect(rrect, paint);
244 
245     ctx->flush();
246 
247     printf("Drew to surface, now doing readback\n");
248     sk_sp<SkImage> img = surface->makeImageSnapshot();
249     sk_sp<SkData> webp = SkWebpEncoder::Encode(ctx.get(), img.get(), {});
250     if (!webp) {
251         printf("Readback of pixels (or encoding) failed\n");
252         return 1;
253     }
254     output.write(webp->data(), webp->size());
255     output.fsync();
256 
257     return 0;
258 }
259