-
-
Save ad8e/dd150b775ae6aa4d5cf1a092e4713add to your computer and use it in GitHub Desktop.
/* Note: this Google copyright notice only applies to the original file, which has large sections copy-pasted here. my changes are under CC0 (public domain). | |
* Copyright 2015 Google Inc. | |
* | |
* Use of this source code is governed by a BSD-style license that can be | |
* found in the LICENSE file. | |
*/ | |
/* | |
The official instructions don't work well. These alternative instructions are intended to be the shortest path to get a minimal setup running. | |
The Linux steps were run through successfully on Sept 2023. | |
The Windows steps are known to be broken; the broken part is Step 7. The Include and Library directories should be tweaked. | |
This was made by copy-pasting and fixing two sources: https://github.com/google/skia/tree/master/experimental/GLFWTest and https://gist.github.com/zester/5163313 | |
Don't bother trying these two sources; neither of them works. | |
step 1: install glfw (on Linux, "sudo apt install libglfw3-dev" will get you an acceptable (and outdated) version. on Visual Studio, you can get glfw from vcpkg.) | |
step 2: follow https://skia.org/docs/user/download/ to download and build skia. | |
Move forward to either Windows step 6 or Linux step 6. | |
Windows step 6, Visual Studio 2017: | |
if you're in Windows, you will need to use bash (tested as of 2017; haven't tried recently). on my system, a copy of bash came with my installation of Git for Windows. | |
cmd.exe doesn't allow single quotes, which are necessary to give the VC path. the various skia_use_foo commands are necessary to stop VS from erroring out when the headers are missing | |
run these two commands, replacing "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC" with your own VC directory: | |
gn gen out/Static --args='is_official_build=true win_vc="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC" skia_use_libpng=false skia_use_zlib=false skia_use_libwebp=false skia_enable_pdf=false skia_use_libjpeg_turbo=false skia_use_expat=false' | |
ninja -C out/Static | |
Windows step 7. Warning, this is outdated. The massive "FOLDER1\x" was changed to not be necessary, but I haven't tested the correct steps now: | |
add this file to a new VS project | |
append "FOLDER1\skia\include\core;FOLDER1\skia\include\gpu;FOLDER1\skia\include\config;FOLDER1\skia\include\utils;FOLDER2\glfw\include'" to the VC include directories of your project, where FOLDERX represents the directories you put them in. | |
you must include all 4 skia folders because the files inside skia folders assume they see the other folders. | |
if you're unfamiliar with how the include directory works, it's in Project->Properties, VC++ Directories, Include Directories. | |
append "FOLDER1\skia\out\Static;FOLDER2\glfw\src\Debug;" to Library Directories, again replacing FOLDERX with the true location. add "opengl32.lib;skia.lib;glfw3.lib;" to Linker->Input->Additional Dependencies | |
Set build mode to x64. | |
Build! This will produce a debug mode binary. | |
If in the future you want a release mode binary, you will need to re-build glfw in release mode, and change the glfw library folder to FOLDER2\glfw\src\Release; | |
Linux step 6, Ubuntu 23.04. Sept 3, 2023: | |
Run: | |
sudo apt install clang libjpeg-dev libicu-dev libwebp-dev libfontconfig-dev | |
bin/gn gen out/Static --args='is_official_build=true cc="clang" cxx="clang++"' | |
ninja -C out/Static | |
Linux step 7: | |
download this file as "glfw_ship.cpp", and place it in the parent folder of the "skia" directory. (this just makes "-Iskia" in the right place) | |
g++ -g -std=c++1z glfw_ship.cpp -lskia -ldl -lpthread -ljpeg -lfreetype -lz -lpng -lglfw -lfontconfig -lwebp -lwebpmux -lwebpdemux -lGL -Iskia -Lskia/out/Static/ | |
./a.out | |
eventually, you will want color-correct spaces, and there are 5 places below (Ctrl+F "enable correct color spaces"), where you should replace/uncomment lines to enable this. | |
warning: color-correct spaces don't work in VMWare, because mesa doesn't support it. | |
*/ | |
#include "GLFW/glfw3.h" | |
#define SK_GANESH | |
#define SK_GL | |
#include "include/gpu/GrBackendSurface.h" | |
#include "include/gpu/GrDirectContext.h" | |
#include "include/gpu/gl/GrGLInterface.h" | |
#include "include/gpu/gl/GrGLAssembleInterface.h" | |
#include "include/gpu/ganesh/SkSurfaceGanesh.h" | |
#include "include/gpu/ganesh/gl/GrGLBackendSurface.h" | |
#include "include/core/SkCanvas.h" | |
#include "include/core/SkColorSpace.h" | |
#include "include/core/SkSurface.h" | |
#include <stdio.h> | |
#include <stdlib.h> | |
//uncomment the two lines below to enable correct color spaces | |
//#define GL_FRAMEBUFFER_SRGB 0x8DB9 | |
//#define GL_SRGB8_ALPHA8 0x8C43 | |
GrDirectContext* sContext = nullptr; | |
SkSurface* sSurface = nullptr; | |
void error_callback(int error, const char* description) { | |
fputs(description, stderr); | |
} | |
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { | |
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) | |
glfwSetWindowShouldClose(window, GL_TRUE); | |
} | |
void init_skia(int w, int h) { | |
auto interface = GrGLMakeNativeInterface(); | |
if (interface == nullptr) { | |
//backup plan. see https://gist.github.com/ad8e/dd150b775ae6aa4d5cf1a092e4713add?permalink_comment_id=4680136#gistcomment-4680136 | |
interface = GrGLMakeAssembledInterface( | |
nullptr, (GrGLGetProc) * [](void*, const char* p) -> void* { return (void*)glfwGetProcAddress(p); }); | |
} | |
sContext = GrDirectContext::MakeGL(interface).release(); | |
GrGLFramebufferInfo framebufferInfo; | |
framebufferInfo.fFBOID = 0; // assume default framebuffer | |
// We are always using OpenGL and we use RGBA8 internal format for both RGBA and BGRA configs in OpenGL. | |
//(replace line below with this one to enable correct color spaces) framebufferInfo.fFormat = GL_SRGB8_ALPHA8; | |
framebufferInfo.fFormat = GL_RGBA8; | |
SkColorType colorType = kRGBA_8888_SkColorType; | |
GrBackendRenderTarget backendRenderTarget = GrBackendRenderTargets::MakeGL(w, h, | |
0, // sample count | |
0, // stencil bits | |
framebufferInfo); | |
//(replace line below with this one to enable correct color spaces) sSurface = SkSurfaces::WrapBackendRenderTarget(sContext, backendRenderTarget, kBottomLeft_GrSurfaceOrigin, colorType, SkColorSpace::MakeSRGB(), nullptr).release(); | |
sSurface = SkSurfaces::WrapBackendRenderTarget(sContext, backendRenderTarget, kBottomLeft_GrSurfaceOrigin, colorType, nullptr, nullptr).release(); | |
if (sSurface == nullptr) abort(); | |
} | |
void cleanup_skia() { | |
delete sSurface; | |
delete sContext; | |
} | |
const int kWidth = 960; | |
const int kHeight = 640; | |
int main(void) { | |
GLFWwindow* window; | |
glfwSetErrorCallback(error_callback); | |
if (!glfwInit()) { | |
exit(EXIT_FAILURE); | |
} | |
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); | |
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); | |
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); | |
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); | |
//(uncomment to enable correct color spaces) glfwWindowHint(GLFW_SRGB_CAPABLE, GL_TRUE); | |
glfwWindowHint(GLFW_STENCIL_BITS, 0); | |
//glfwWindowHint(GLFW_ALPHA_BITS, 0); | |
glfwWindowHint(GLFW_DEPTH_BITS, 0); | |
window = glfwCreateWindow(kWidth, kHeight, "Simple example", NULL, NULL); | |
if (!window) { | |
glfwTerminate(); | |
exit(EXIT_FAILURE); | |
} | |
glfwMakeContextCurrent(window); | |
//(uncomment to enable correct color spaces) glEnable(GL_FRAMEBUFFER_SRGB); | |
init_skia(kWidth, kHeight); | |
glfwSwapInterval(1); | |
glfwSetKeyCallback(window, key_callback); | |
// Draw to the surface via its SkCanvas. | |
SkCanvas* canvas = sSurface->getCanvas(); // We don't manage this pointer's lifetime. | |
while (!glfwWindowShouldClose(window)) { | |
glfwWaitEvents(); | |
SkPaint paint; | |
paint.setColor(SK_ColorWHITE); | |
canvas->drawPaint(paint); | |
paint.setColor(SK_ColorBLUE); | |
canvas->drawRect({100, 200, 300, 500}, paint); | |
sContext->flush(); | |
glfwSwapBuffers(window); | |
} | |
cleanup_skia(); | |
glfwDestroyWindow(window); | |
glfwTerminate(); | |
exit(EXIT_SUCCESS); | |
} |
@prakhar144 Here are the Skia related files I used, I'm kinda giving up on that project (this was an rewrite attempt of the original in Python) so not sure if I'll keep this repo, but basically I had to:
#define SK_GL // For GrContext::MakeGL
#include "GLFW/glfw3.h"
And on this gist that says..
if (kRGBA_8888_GrPixelConfig == kSkia8888_GrPixelConfig) {
colorType = kRGBA_8888_SkColorType;
}
else {
colorType = kBGRA_8888_SkColorType;
}
It was undefined one of the two color at the top so I manually set to only
color_type = kRGBA_8888_SkColorType;
(I'm not sure but I think Windows uses BGRA by default and Linux uses RGBA, at least on skia-python
I had to change the pixel format of the FFmpeg piped video for it to render colors right)
Also to link Skia properly I moved its shared library build to /usr/lib/libskia.so
and added this file to the global link arguments on Meson.
Sorry I'm a but inexperienced with C++ don't think I can really help any further :p
Also, I just noticed the formatting is a bit wrong, thank you tabs vs spaces lol; and I made this gist's code into a class there as I understand more this way.
@prakhar144 Here are the Skia related files I used, I'm kinda giving up on that project (this was an rewrite attempt of the original in Python) so not sure if I'll keep this repo, but basically I had to:
#define SK_GL // For GrContext::MakeGL #include "GLFW/glfw3.h"And on this gist that says..
if (kRGBA_8888_GrPixelConfig == kSkia8888_GrPixelConfig) { colorType = kRGBA_8888_SkColorType; } else { colorType = kBGRA_8888_SkColorType; }It was undefined one of the two color at the top so I manually set to only
color_type = kRGBA_8888_SkColorType;
(I'm not sure but I think Windows uses BGRA by default and Linux uses RGBA, at least on
skia-python
I had to change the pixel format of the FFmpeg piped video for it to render colors right)Also to link Skia properly I moved its shared library build to
/usr/lib/libskia.so
and added this file to the global link arguments on Meson.Sorry I'm a but inexperienced with C++ don't think I can really help any further :p
Also, I just noticed the formatting is a bit wrong, thank you tabs vs spaces lol; and I made this gist's code into a class there as I understand more this way.
Yea that is pretty much it. Idk why its so hard to find working examples related to this library when its extensively used everywhere. I will myself create a complete tutorial once I am comfortable enough with basics of this library. Also, in the newer versions of skia they have changed the code and it looks like GrContext is no more inside include/gpu directory but in /include/private. Thanks
GrContext doesn't exist anymore and it's not in include/private neither. The name was changed to GrDirectContext. I forked it and updated here.
Thanks for tips in previous comments, it helped.
The code provided did not work under macOS and led to crashing inside Skia.
Upon further inspection, and by using ASAN -fsanitize-address
the cause becomes apparent:
==43003==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffee98f8060 at pc 0x00010845c5e8 bp 0x7ffee98f7a40 sp 0x7ffee98f71e0
READ of size 152 at 0x7ffee98f8060 thread T0
#0 0x10845c5e7 in wrap_memcpy (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x1c5e7)
#1 0x106a1598e in GrContextThreadSafeProxy::GrContextThreadSafeProxy(GrBackendApi, GrContextOptions const&) GrContextThreadSafeProxy.cpp:35
#2 0x106a15c82 in GrContextThreadSafeProxy::GrContextThreadSafeProxy(GrBackendApi, GrContextOptions const&) GrContextThreadSafeProxy.cpp:35
#3 0x106a173cc in GrContextThreadSafeProxyPriv::Make(GrBackendApi, GrContextOptions const&) GrContextThreadSafeProxy.cpp:167
#4 0x106a20695 in GrDirectContext::GrDirectContext(GrBackendApi, GrContextOptions const&) GrDirectContext.cpp:54
#5 0x106a20d12 in GrDirectContext::GrDirectContext(GrBackendApi, GrContextOptions const&) GrDirectContext.cpp:54
#6 0x106a2964b in GrDirectContext::MakeGL(sk_sp<GrGLInterface const>, GrContextOptions const&) GrDirectContext.cpp:905
#7 0x106308daa in init_skia(int, int) glfw_ship.cpp:85
#8 0x1063096f7 in main glfw_ship.cpp:146
#9 0x7fff7b697014 in start (libdyld.dylib:x86_64+0x1014)
Address 0x7ffee98f8060 is located in stack of thread T0 at offset 1
I got it to work like this:
- GrContextOptions options;
- //options.fRequireDecodeDisableForSRGB = false; //was removed?
- sContext = GrDirectContext::MakeGL(nullptr, options).release();
+ auto interface = GrGLMakeNativeInterface();
+ sContext = GrDirectContext::MakeGL(interface).release();
However there are still some issues, when the window is first shown, the blue rectangle inside is pretty small, and only after the mouse moves (does not even need to be over the window) does it go to the correct size.
On Linux everything works like expected. Any idea what it could be? Some kind of DPI issue maybe?
auto interface = GrGLMakeNativeInterface();
After this, I get a null for interface and GrDirectContext::MakeGL
is returning another null. Any hints?
Crazy to think I started this gist 8 years ago https://gist.github.com/zester/5163313 and I only ever got it to work once in glfw and once in sfml until google made changes to the build system and it never worked properly again.
That's amusing, and I'm in the same boat. My gist too is in a state of disrepair and doesn't work. I'm lucky I don't need skia anymore.
With cairo obsolete and pathfinder being unmaintained, skia is now the premier spline rendering middleware. The in-tree SDL2 version was still being maintained when I last checked 2 years ago, so it's probably the way forward for an initial bootstrap. I generally prefer glfw because it's lighter, but skia is already 8 MB, so SDL2's 1 MB may be acceptable. It's probably a better (easier) bootstrap.
I tried to incorporate all the suggested changes. I haven't tried to compile so it might not work!
- BGRA detection now removed, as suggested by Tremeschin. I don't know if it's correct to assume that it's always RGBA, but at least with an OpenGL-only backend, I can't imagine channel ordering becoming a problem.
- GrContextOptions replaced by GrGLMakeNativeInterface, as suggested by tritao. Maybe some people might need options, but maybe the bootstrap doesn't.
High DPI probably still not working.
auto interface = GrGLMakeNativeInterface();
After this, I get a null for interface and
GrDirectContext::MakeGL
is returning another null. Any hints?
same issue. Have you figured it out ? @RyanAfrish7
I got the following error:
GNU gdb (GDB) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./a.out...
(gdb) r
Starting program: /home/lyh/Documents/CS/Projects/skia_test/a.out
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
[New Thread 0x7fffeb9ff6c0 (LWP 579178)]
[New Thread 0x7fffeb0bd6c0 (LWP 579179)]
[New Thread 0x7fffea8bc6c0 (LWP 579180)]
[New Thread 0x7fffea0bb6c0 (LWP 579181)]
[New Thread 0x7fffe94ba6c0 (LWP 579182)]
[New Thread 0x7fffe8cb96c0 (LWP 579183)]
Thread 1 "a.out" received signal SIGSEGV, Segmentation fault.
0x00005555557c6cc0 in GrGLGetVersion(GrGLInterface const*) ()
(gdb)
Can anyone tell me how to fix?
Thank you! The code worked on windows!
I'm getting the error 'MakeGL': is not a member of 'GrDirectContext'
, even with #define SK_GL
enabled
Same as @everdrone here, stuck trying to create a GPU-backed surface. Raster works fine. Git pulled fresh with a LLVM clang compile.
Build configuration:
clang_win = "C:\LLVM"
extra_cflags = [
"/MT",
"-Wno-deprecated-declarations",
]
cc = "clang"
target_cpu = "x64"
is_debug = false
is_official_build = true
skia_use_freetype = true
skia_pdf_subset_harfbuzz = true
skia_enable_ganesh = true
skia_enable_gpu = true
skia_use_gl = true
skia_use_dawn = false
skia_use_angle = false
skia_use_egl = false
skia_use_direct3d = false
skia_use_vulkan = false
skia_use_metal = false
skia_use_dng_sdk = false
skia_use_ffmpeg = false
skia_use_sfntly = false
skia_use_system_expat = false
skia_use_system_libjpeg_turbo = false
skia_use_system_libpng = false
skia_use_system_libwebp = false
skia_use_system_zlib = false
skia_use_system_icu = false
skia_use_system_harfbuzz = false
skia_use_system_freetype2 = false
Tried to define SK_GL while compiling Skia in 'extra_flags'. Defined it as:
extra_cflags = [
"/MT",
"-Wno-deprecated-declarations",
"-DSK_GL",
]
Still doesn't work...
Tried defining it in preprocessor definitions in my project.
... Still doesn't work ...
I don't know what I am doing wrong. Please help!
@everdrone After A LOT of pulling hair, I managed to solve the issue by also defining SK_GANESH AND SK_GL.
#define SK_GANESH
#define SK_GL
#includes...
The code is basically defined in include/core/SkTypes.h
I added "#define SK_GANESH" as @Grimtonz1337 suggests. I haven't built this in a while, so it's unclear if this revision is sufficient to make it work.
I found a mention of SK_GANESH in Skia's docs at https://api.skia.org/classSkSurface.html#a5e5ba93640ce8e65a7cb401d5554e84c, but no clear guide anywhere. @Grimtonz1337's discovery is not easy to find.
@ad8e I am now stuck like other people here. GrGLMakeNativeInterface returns nullptr and with that, MakeGL also fails to initialize. Even after manually creating the context using wglCreateContext and getting 0 with GetLastError() after calling wglCreateContext. Skia simply fails to create anything related to the GPU. I really don't know what I am doing wrong at this point...
Unfortunately, I have not tried to build this in a long time and no longer use Skia. If Skia supplies an SDL2 version that is maintained, that is probably a better starting point for everyone.
@everdrone After A LOT of pulling hair, I managed to solve the issue by also defining SK_GANESH AND SK_GL.
#define SK_GANESH #define SK_GL #includes...
The code is basically defined in
include/core/SkTypes.h
I managed to solve the issue by using the chrome/m112
branch, apparently I was using the master
Alright. After a lot of trial and error, I will explain how I got it to work briefly (cause I think I lost my mind lol). If someone has questions, feel free to reply.
So, the commands I used to basically build skia were the following:
1. bin\gn gen out/Clang --args="is_debug=false is_official_build=true is_component_build=false target_cpu=\"x64\" clang_win=\"C:\LLVM\" cc=\"clang\" cxx=\"clang++\" extra_cflags_cc=[\"/MT\", \"-DSK_GANESH\", \"-DSK_GL\"] skia_compile_modules=true skia_enable_tools=true skia_enable_pdf=true skia_enable_sksl=true skia_enable_skshaper=true skia_enable_svg=true skia_enable_ganesh=true skia_enable_gpu=true skia_use_gl=true skia_use_angle=true skia_use_vulkan=false skia_use_dawn=false skia_use_metal=false skia_use_direct3d=false skia_use_expat=true skia_use_harfbuzz=true skia_use_icu=true skia_use_zlib=true skia_use_freetype=true skia_use_ffmpeg=false skia_use_sfml=false skia_use_sfntly=true skia_use_system_expat=false skia_use_system_harfbuzz=false skia_use_system_icu=false skia_use_system_libjpeg_turbo=false skia_use_system_libpng=false skia_use_system_libwebp=false skia_use_system_zlib=false skia_use_system_freetype2=false skia_enable_fontmgr_custom_directory=false text_tests_enabled=false skia_enable_skshaper_tests=false skia_compile_sksl_tests=false" --config=\"angle_gl_es2\"
2. python3 tools/git-sync-deps --config=\"angle_gl_es2\"
3. ninja -C out/Clang skia modules angle2
After it's done, you will have everything in skia/out/Clang. You will see 2 popular DLL files used in Chromium, basically everywhere and those are libEGL.dll and libGLESv2.dll. These are used by ANGLE to translate OpenGL ES API calls to hardware-supported APIs available for that platform (DirectX, Metal, etc.). This is used by Google to achieve same rendering across platforms on Chromium.
Next, you're gonna see .lib versions as well of these 2 files, you're gonna have to link these with your project, along with your skia.lib and copy those 2 DLL files into your .exe folder.
Finally, your project linked .libs should be:
Opengl32.lib
skia.lib
libEGL.dll.lib
libGLESv2.dll.lib
And your .exe folder should contain:
libEGL.dll
libGLESv2.dll
Your actual .exe
And your code should look something like this:
#define SK_GANESH
#define SK_GL
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GL/GL.h>
#include <skia/include/gpu/gl/GrGLInterface.h>
#include <skia/include/gpu/gl/GrGLAssembleInterface.h>
#include <skia/include/gpu/GrDirectContext.h>
#include <skia/include/config/SkUserConfig.h>
#include <skia/include/core/SkCanvas.h>
#include <skia/include/core/SkSurface.h>
#include <skia/include/core/SkColor.h>
#include <skia/include/core/SkColorSpace.h>
#include <skia/include/core/SkRect.h>
#include <skia/include/core/SkRRect.h>
#include <skia/src/gpu/ganesh/gl/GrGLDefines_impl.h>
#include <Windows.h>
#include <windowsx.h>
#include <iostream>
sk_sp<SkSurface> createGPUSurface(HWND hwnd)
{
constexpr const int kDefaultSampleCount = 4;
constexpr const int kDefaultStencilBits = 8;
constexpr const EGLint kEGLPlatformType = EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE;
constexpr const EGLint kEGLPlatformAttributes[] = { EGL_PLATFORM_ANGLE_TYPE_ANGLE, kEGLPlatformType, EGL_NONE };
PIXELFORMATDESCRIPTOR pfd = { 0 };
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cDepthBits = 24;
pfd.cStencilBits = kDefaultStencilBits;
pfd.iLayerType = PFD_MAIN_PLANE;
HDC hdc = GetDC(hwnd);
int pixelFormat = ChoosePixelFormat(hdc, &pfd);
SetPixelFormat(hdc, pixelFormat, &pfd);
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
EGLDisplay m_EGLDisplay = eglGetPlatformDisplayEXT ? eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, hdc, kEGLPlatformAttributes) : EGL_NO_DISPLAY;
ReleaseDC(hwnd, hdc);
if (m_EGLDisplay == EGL_NO_DISPLAY)
{
std::cerr << "Failed to initialize EGLDisplay.\n";
return nullptr;
}
EGLint majorVersion, minorVersion;
if (!eglInitialize(m_EGLDisplay, &majorVersion, &minorVersion))
{
std::cerr << "Failed to initialize EGLDisplay.\n";
return nullptr;
}
const EGLint configAttribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE
};
EGLint numConfigs;
EGLConfig m_EGLSurfaceConfig;
if (!eglChooseConfig(m_EGLDisplay, configAttribs, &m_EGLSurfaceConfig, 1, &numConfigs))
{
std::cerr << "Failed to initialize EGLConfig.\n";
return nullptr;
}
const EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE };
EGLContext m_EGLContext = eglCreateContext(m_EGLDisplay, m_EGLSurfaceConfig, nullptr, contextAttribs);
if (m_EGLContext == EGL_NO_CONTEXT)
{
std::cerr << "Failed to initialize EGLContext.\n";
return nullptr;
}
EGLSurface m_EGLSurface = eglCreateWindowSurface(m_EGLDisplay, m_EGLSurfaceConfig, hwnd, nullptr);
if (m_EGLSurface == EGL_NO_SURFACE)
{
std::cerr << "Failed to initialize EGLSurface.\n";
return nullptr;
}
if (!eglMakeCurrent(m_EGLDisplay, m_EGLSurface, m_EGLSurface, m_EGLContext))
{
std::cerr << "Failed to make EGLContext current.\n";
return nullptr;
}
sk_sp<const GrGLInterface> m_EGLInterface = GrGLMakeAssembledInterface(nullptr, [](void* ctx, const char name[]) -> GrGLFuncPtr { return eglGetProcAddress(name); });
if (!m_EGLInterface)
{
std::cerr << "Failed to initialize GrGLInterface.\n";
return nullptr;
}
GrContextOptions contextOptions;
sk_sp<GrDirectContext> m_DirectContext = GrDirectContext::MakeGL(m_EGLInterface, contextOptions);
if (!m_DirectContext)
{
std::cerr << "Failed to initialize GrDirectContext.\n";
return nullptr;
}
eglSwapInterval(m_EGLDisplay, 1);
GrGLint m_FrameBufferId;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_FrameBufferId);
GrGLFramebufferInfo m_FrameBufferInfo;
m_FrameBufferInfo.fFBOID = static_cast<GrGLuint>(m_FrameBufferId);
m_FrameBufferInfo.fFormat = GR_GL_RGBA8;
RECT size_rect;
GetWindowRect(hwnd, &size_rect);
const int width = size_rect.right - size_rect.left;
const int height = size_rect.bottom - size_rect.top;
m_EGLInterface->fFunctions.fViewport(0, 0, width, height);
GrBackendRenderTarget m_RenderTarget = GrBackendRenderTarget(width, height, kDefaultSampleCount, kDefaultStencilBits, m_FrameBufferInfo);
return SkSurface::MakeFromBackendRenderTarget(m_DirectContext.get(), m_RenderTarget, GrSurfaceOrigin::kBottomLeft_GrSurfaceOrigin, SkColorType::kRGBA_8888_SkColorType, SkColorSpace::MakeSRGB(), nullptr, nullptr, nullptr);
}
To actually draw something on the screen, your paint method should look something like this:
if (m_WindowSurface)
{
SkRect rect = SkRect::MakeLTRB(0, 0, m_Width, 100);
SkRRect circle = SkRRect::MakeRectXY(SkRect::MakeLTRB(200, 200, 400, 400), 100, 100);
SkPaint paint;
paint.setAntiAlias(true);
paint.setColor(SK_ColorRED);
m_WindowSurface->getCanvas()->clear(SK_ColorWHITE);
m_WindowSurface->getCanvas()->drawRect(rect, paint);
m_WindowSurface->getCanvas()->drawRRect(circle, paint);
m_WindowSurface->flushAndSubmit();
}
eglSwapBuffers(m_EGLDisplay, m_EGLSurface);
Project Solution setup:
C/C++ -> Preprocessor -> Preprocessor Definitions: NOMINMAX
C/C++ -> Code Generation -> Runtime Library set to: /MT
C/C++ -> General -> Additional Include Directories:
C:/skia
C:/skia/include
C:/skia/third_party/externals/angle2/include
Linker -> General -> Additional Library Directories:
C:/skia/out/Clang
Linker -> Input -> Additional Dependencies:
Opengl32.lib
skia.lib
libEGL.dll.lib
libGLESv2.dll.lib
I think that's pretty much everything you need to do. I think this should work for everyone, at least on Windows.
There's a 3rd DLL that ANGLE loads. By passing EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE (afaik it can only do D9 and D11), it loads D3DCompiler_47.dll from C:\Windows\System32, at least for me. So, it should be better to copy that DLL as well. That is all.
I tried the Linux build for a few hours and didn't get it working. libfontconfig-dev
is added to sudo apt install
. SkSurface::MakeFromBackendRenderTarget
no longer exists and I didn't find a replacement. GrBackendRenderTarget
's constructor also doesn't exist.
https://skia.org/docs/user/api/skcanvas_creation/#gpu has some instructions, but SkSurface::MakeRenderTarget
doesn't exist.
GrDirectContext::MakeGL
is undefined no matter where I define SK_GL
; I don't understand how this can happen, so maybe it's my fault. EDIT: it was the SK_GANESH
issue found a few comments above. Amusingly, even the sole incidental mention of SK_GANESH
in the docs is no longer there.
Skia used to have a SDL2 demo, but it's gone now. To start a new Skia project, you'll have to copy code from an existing project that has a recent copy of Skia working.
@ad8e AFAIK the function you are looking for now exists in the SkSurfaces
namespace as WrapBackendRenderTarget
and has the exact same arguments:
https://github.com/google/skia/blob/main/include/gpu/ganesh/SkSurfaceGanesh.h#L158
EDIT: The constructor for GrBackendRenderTarget
has also been replaced with GrBackendRenderTargets::MakeGL()
in https://github.com/google/skia/blob/main/include/gpu/ganesh/gl/GrGLBackendSurface.h#L49
This gist is working again, thanks to @mmcjimsey26. Couldn't have done it without him pointing the way.
The shown graphic works fine on my current 2x-scaled monitor, in Linux. I guess High DPI issues are for Macs (and maybe Windows, which I haven't tested).
Also, it may be worth noting that in some rare cases, GrGLMakeNativeInterface()
returns null, causing MakeGL()
to fail. Here is the code I use (that works most of the time) to get around this issue:
#include "include/gpu/gl/GrGLAssembleInterface.h"
auto interface = GrGLMakeAssembledInterface(nullptr, (GrGLGetProc)*[](void *, const char *p) -> void * {
return (void *)glfwGetProcAddress(p);
});
This idea came from neovide's renderer, and from my experience it seems to only work in cases where GrGLMakeNativeInterface()
fails, so it probably won't work here, but it may be helpful to other folks.
I have no devices to test GrGLMakeAssembledInterface
on, but you know what you're talking about, so I added it.
For anyone it doesn't work, you now have to GrDirectContext::MakeGL to GrDirectContexts::MakeGL.
This example uses SFML for Window, Input and GL Context, Skia for Canvas and 2D Graphics on a GPU Surface and Yoga Layout Engine for Flexbox layout.
https://github.com/SFML/SFML
https://github.com/SFML/SFML/archive/refs/tags/2.6.1.zip
https://github.com/google/skia
https://github.com/google/skia/archive/refs/tags/canvaskit/0.34.0.zip
https://github.com/facebook/yoga
https://github.com/facebook/yoga/archive/refs/tags/v3.0.4.zip
g++ -o test test.cpp -I/usr/include/skia -lskia -lsfml-graphics -lsfml-window -lsfml-system -lthorvg -lyogacore -lEGL -lGLESv2
Example Code
#define SK_GL
// Compile command:
// This project uses the Skia graphics library along with SFML, EGL, and other libraries.
// Compilation requires linking against these libraries as specified below.
// g++ -o test test.cpp -I/usr/include/skia -lskia -lsfml-graphics -lsfml-window -lsfml-system -lyogacore -lEGL -lGLESv2
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <SFML/Graphics.hpp>
#include <memory>
#include <yoga/Yoga.h>
#include <cmath> // Include for mathematical functions like sin and cos
#include <iostream>
#include <skia/include/gpu/GrDirectContext.h>
#include <skia/include/gpu/gl/GrGLInterface.h>
#include <skia/include/core/SkSurface.h>
#include <skia/include/core/SkCanvas.h>
#include <skia/include/core/SkPath.h>
#include <skia/include/core/SkPaint.h>
#include <skia/include/core/SkRect.h>
#include <skia/include/core/SkRRect.h>
// Function to create and return an SkRRect representing a rounded rectangle
SkRRect createRoundedRectangle(float x, float y, float width, float height,
float radiusTopLeft, float radiusTopRight,
float radiusBottomRight, float radiusBottomLeft) {
// Define the rectangle from specified position and size
SkRect rect = SkRect::MakeXYWH(x, y, width, height);
// Define radii for each corner of the rectangle
SkVector radii[4];
radii[SkRRect::kUpperLeft_Corner] = SkVector::Make(radiusTopLeft, radiusTopLeft);
radii[SkRRect::kUpperRight_Corner] = SkVector::Make(radiusTopRight, radiusTopRight);
radii[SkRRect::kLowerRight_Corner] = SkVector::Make(radiusBottomRight, radiusBottomRight);
radii[SkRRect::kLowerLeft_Corner] = SkVector::Make(radiusBottomLeft, radiusBottomLeft);
// Create and return the rounded rectangle with individual corner radii
SkRRect roundedRect;
roundedRect.setRectRadii(rect, radii);
return roundedRect;
}
int main() {
// Initialize an SFML window
sf::RenderWindow window(sf::VideoMode(800, 600), "Hydra v0.0.1");
// Setup Skia OpenGL context
sk_sp<const GrGLInterface> interface = GrGLMakeNativeInterface();
sk_sp<GrDirectContext> context = GrDirectContext::MakeGL(interface);
// Define the image properties and create a Skia GPU surface
SkImageInfo imageinfo = SkImageInfo::MakeN32Premul(800, 600);
sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(context.get(), SkBudgeted::kNo, imageinfo);
// Ensure the GPU surface was created successfully
if (!surface) {
std::cerr << "Failed to create Skia GPU surface\n";
return -1;
}
// Prepare the Skia drawing context
context->flush();
// Retrieve the canvas from the Skia surface
SkCanvas* canvas = surface->getCanvas();
// Activate SFML window context
window.setActive(true);
// Set up Yoga layout for UI elements
YGNodeRef container = YGNodeNew();
YGNodeStyleSetWidth(container, 800);
YGNodeStyleSetHeight(container, 100);
YGNodeStyleSetFlexDirection(container, YGFlexDirectionRow);
YGNodeStyleSetPadding(container, YGEdgeAll, 20);
// Create and configure child nodes for layout
YGNodeRef child = YGNodeNew();
YGNodeStyleSetFlexGrow(child, 1); // Allow the child to grow and fill the container
YGNodeStyleSetMargin(child, YGEdgeAll, 5);
YGNodeInsertChild(container, child, 0);
YGNodeRef child1 = YGNodeNew();
YGNodeStyleSetFlexGrow(child1, 1);
YGNodeStyleSetMargin(child1, YGEdgeAll, 5);
YGNodeInsertChild(container, child1, 1);
// Compute the layout for all children
YGNodeCalculateLayout(container, YGUndefined, YGUndefined, YGDirectionLTR);
// Main application loop
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
// Handle window resizing
if (event.type == sf::Event::Resized) {
sf::FloatRect visibleArea(0, 0, event.size.width, event.size.height);
window.setView(sf::View(visibleArea));
}
}
// Clear window with a specific color
window.clear(sf::Color(23, 23, 23));
// Start drawing operations
canvas->clear(SkColorSetARGB(255, 255, 255, 0)); // Clear with yellow color, full opacity
// Draw rounded and regular rectangles based on Yoga layout
SkRRect rect = createRoundedRectangle(YGNodeLayoutGetLeft(child), YGNodeLayoutGetTop(child), YGNodeLayoutGetWidth(child), YGNodeLayoutGetHeight(child), 0, 20, 0, 20);
SkPaint paint;
paint.setAntiAlias(true); // Enable anti-aliasing for smoother edges
paint.setColor(SK_ColorBLUE);
canvas->drawRRect(rect, paint);
SkRect rect1 = SkRect::MakeXYWH(YGNodeLayoutGetLeft(child1), YGNodeLayoutGetTop(child1), YGNodeLayoutGetWidth(child1), YGNodeLayoutGetHeight(child1));
SkPaint paint1;
paint1.setAntiAlias(true);
paint1.setColor(SK_ColorRED);
canvas->drawRect(rect1, paint1);
// Finish drawing and update the canvas
canvas->flush();
// Display the updated window
window.display();
}
// Cleanup
delete canvas;
return 0;
}
Screenshot
Shell Script to build skia
#!/bin/bash
# Set the PKG directory where everything will be installed
export PKG=$(pwd)/install
# Ensure the PKG directory exists
mkdir -pv $PKG
git clone 'https://chromium.googlesource.com/chromium/tools/depot_tools.git'
export PATH="${PWD}/depot_tools:${PATH}"
# Download the packages
wget https://github.com/google/skia/archive/refs/tags/canvaskit/0.34.0.tar.gz
tar -xvf 0.34.0.tar.gz
# Change directory to the glibc directory
cd skia-canvaskit-0.34.0
python3 tools/git-sync-deps
# Run configure with the adjusted paths
./bin/gn gen 'out/Shared' --args='
is_official_build=true
is_component_build=true
skia_enable_tools=false
target_os="linux"
target_cpu="x64"
skia_use_icu=false
skia_use_sfntly=false
skia_use_piex=true
skia_use_system_expat=false
skia_use_system_freetype2=false
skia_use_system_libjpeg_turbo=false
skia_use_system_libpng=false
skia_use_system_libwebp=false
skia_use_system_zlib=false
skia_enable_gpu=true'
ninja -C out/Shared
cd $PKG
mkdir -p usr/lib64
mkdir -p usr/include/skia
cd $PKG/usr/lib64
cp ../../../skia-canvaskit-0.34.0/out/Shared/*.a .
cp ../../../skia-canvaskit-0.34.0/out/Shared/*.so .
cd $PKG/usr/include/skia
cp -r ../../../../skia-canvaskit-0.34.0/include/* .
cd $PKG
#zip -r9 ../skia-0.34.0-1.pkg usr/lib64/
#zip -r9 ../skia-devel-0.34.0-1.pkg usr/include/
# Cleanup
#cd ..
#rm -rf 0.34.0.tar.gz install skia-canvaskit-0.34.0 depot_tools
this should work
#include "GLFW/glfw3.h"
#define SK_GANESH
#define SK_GL
#include "include/gpu/GrBackendSurface.h"
#include "include/gpu/GrDirectContext.h"
#include "include/gpu/gl/GrGLInterface.h"
#include "include/gpu/gl/GrGLAssembleInterface.h"
#include "include/gpu/ganesh/SkSurfaceGanesh.h"
#include "include/gpu/ganesh/gl/GrGLBackendSurface.h"
#include "include/gpu/ganesh/gl/GrGLDirectContext.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColorSpace.h"
#include "include/core/SkSurface.h"
#include "include/core/SkGraphics.h"
#include <stdio.h>
#include <stdlib.h>
//uncomment the two lines below to enable correct color spaces
//#define GL_FRAMEBUFFER_SRGB 0x8DB9
//#define GL_SRGB8_ALPHA8 0x8C43
GrDirectContext* sContext = nullptr;
SkSurface* sSurface = nullptr;
void error_callback(int error, const char* description) {
fputs(description, stderr);
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
}
void init_skia(int w, int h) {
auto interface = GrGLMakeNativeInterface();
if (interface == nullptr) {
//backup plan. see https://gist.github.com/ad8e/dd150b775ae6aa4d5cf1a092e4713add?permalink_comment_id=4680136#gistcomment-4680136
interface = GrGLMakeAssembledInterface(
nullptr, (GrGLGetProc) * [](void*, const char* p) -> void* { return (void*)glfwGetProcAddress(p); });
}
sContext = GrDirectContexts::MakeGL(interface).release();
GrGLFramebufferInfo framebufferInfo;
framebufferInfo.fFBOID = 0; // assume default framebuffer
// We are always using OpenGL and we use RGBA8 internal format for both RGBA and BGRA configs in OpenGL.
//(replace line below with this one to enable correct color spaces) framebufferInfo.fFormat = GL_SRGB8_ALPHA8;
framebufferInfo.fFormat = GL_RGBA8;
SkColorType colorType = kRGBA_8888_SkColorType;
GrBackendRenderTarget backendRenderTarget = GrBackendRenderTargets::MakeGL(w, h,
0, // sample count
0, // stencil bits
framebufferInfo);
//(replace line below with this one to enable correct color spaces) sSurface = SkSurfaces::WrapBackendRenderTarget(sContext, backendRenderTarget, kBottomLeft_GrSurfaceOrigin, colorType, SkColorSpace::MakeSRGB(), nullptr).release();
sSurface = SkSurfaces::WrapBackendRenderTarget(sContext, backendRenderTarget, kBottomLeft_GrSurfaceOrigin, colorType, nullptr, nullptr).release();
if (sSurface == nullptr) abort();
}
void cleanup_skia() {
delete sSurface;
delete sContext;
}
const int kWidth = 960;
const int kHeight = 640;
int main(void) {
GLFWwindow* window;
glfwSetErrorCallback(error_callback);
if (!glfwInit()) {
exit(EXIT_FAILURE);
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//(uncomment to enable correct color spaces) glfwWindowHint(GLFW_SRGB_CAPABLE, GL_TRUE);
glfwWindowHint(GLFW_STENCIL_BITS, 0);
//glfwWindowHint(GLFW_ALPHA_BITS, 0);
glfwWindowHint(GLFW_DEPTH_BITS, 0);
window = glfwCreateWindow(kWidth, kHeight, "Simple example", NULL, NULL);
if (!window) {
glfwTerminate();
exit(EXIT_FAILURE);
}
glfwMakeContextCurrent(window);
//(uncomment to enable correct color spaces) glEnable(GL_FRAMEBUFFER_SRGB);
init_skia(kWidth, kHeight);
glfwSwapInterval(1);
glfwSetKeyCallback(window, key_callback);
// Draw to the surface via its SkCanvas.
SkCanvas* canvas = sSurface->getCanvas(); // We don't manage this pointer's lifetime.
while (!glfwWindowShouldClose(window)) {
glfwWaitEvents();
SkPaint paint;
paint.setColor(SK_ColorWHITE);
canvas->drawPaint(paint);
paint.setColor(SK_ColorBLUE);
canvas->drawRect({100, 200, 300, 500}, paint);
sContext->flush();
glfwSwapBuffers(window);
}
cleanup_skia();
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
}
but using vcpkg with cmake doesnt compile
You haven't provided any information, such as the operating system you're using, the exact compiler errors, or any other relevant details. How is anyone supposed to help you figure out why it doesn't compile without that information?
Hi, Can you share the command you used to make this program work ? I created a makefile to make it work but unfortunately I lost it. It will also help people who land here in future.