--- /dev/null
+build/
+*.user
--- /dev/null
+[submodule "vkh"]
+ path = vkh
+ url = https://github.com/jpbruyere/vkhelpers.git
--- /dev/null
+CMAKE_MINIMUM_REQUIRED(VERSION 3.9)
+
+PROJECT(vkvg VERSION 0.1.0 DESCRIPTION "Vulkan Vector Graphic")
+
+SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
+SET(CMAKE_CXX_FLAGS "-W -Wall")
+SET(CMAKE_EXE_LINKER_FLAGS "-lm")
+
+IF(NOT CMAKE_BUILD_TYPE)
+ SET(CMAKE_BUILD_TYPE Release)
+ENDIF()
+MESSAGE(STATUS "${CMAKE_BUILD_TYPE} build.")
+
+FIND_PACKAGE(Vulkan REQUIRED)
+FIND_PACKAGE(GLFW3 REQUIRED)
+FIND_PACKAGE(Freetype REQUIRED)
+FIND_PACKAGE(FontConfig REQUIRED)
+
+INCLUDE(FindPkgConfig)
+INCLUDE(GNUInstallDirs)
+
+PKG_CHECK_MODULES(PC_HARFBUZZ harfbuzz>=0.9.0)
+
+FIND_PATH(HARFBUZZ_INCLUDE_DIRS NAMES hb.h
+ HINTS ${PC_HARFBUZZ_INCLUDE_DIRS} ${PC_HARFBUZZ_INCLUDEDIR}
+)
+
+FIND_LIBRARY(HARFBUZZ_LIBRARIES NAMES harfbuzz
+ HINTS ${PC_HARFBUZZ_LIBRARY_DIRS} ${PC_HARFBUZZ_LIBDIR}
+)
+
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(HarfBuzz DEFAULT_MSG HARFBUZZ_INCLUDE_DIRS HARFBUZZ_LIBRARIES)
+
+
+# Find glslc shader compiler.
+# On Android, the NDK includes the binary, so no external dependency.
+if(ANDROID)
+ file(GLOB glslc-folders ${ANDROID_NDK}/shader-tools/*)
+else()
+ file(GLOB glslc-folders ${VULKAN_SDK}/bin)
+endif()
+FIND_PROGRAM(GLSLC glslc HINTS ${glslc-folders})
+if(NOT GLSLC)
+ message(FATAL_ERROR "glslc compiler not found!")
+endif()
+
+SET(SHADER_DIR "shaders")
+SET(SHADER_FILES ${SHADER_DIR}/*.frag ${SHADER_DIR}/*.vert ${SHADER_DIR}/*.geom ${SHADER_DIR}/*.comp)
+FILE(GLOB_RECURSE SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${SHADER_FILES})
+FOREACH(SHADER ${SHADERS})
+ SET(shader-input ${CMAKE_CURRENT_SOURCE_DIR}/${SHADER})
+ SET(shader-output ${CMAKE_CURRENT_BINARY_DIR}/${SHADER}.spv)
+ ADD_CUSTOM_COMMAND (
+ OUTPUT ${shader-output}
+ COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/${SHADER_DIR}"
+ COMMAND ${GLSLC} ${shader-input} -o ${shader-output}
+ COMMENT "Compiling ${shader-input}"
+ DEPENDS ${SHADER}
+ VERBATIM
+ )
+ SET(SHADER_OUTPUTS ${SHADER_OUTPUTS} ${shader-output})
+ENDFOREACH()
+ADD_CUSTOM_TARGET(CompileShaders ALL DEPENDS ${SHADER_OUTPUTS})
+
+#add_definitions( -DDEBUG_VK_PERF=true )
+
+FILE(GLOB VKVG_SRC src/*.c vkh/*.c)
+
+ADD_LIBRARY(${PROJECT_NAME} SHARED ${VKVG_SRC} ${SHADERS})
+
+SET_TARGET_PROPERTIES(vkvg PROPERTIES
+ VERSION ${PROJECT_VERSION}
+ SOVERSION 1
+ PUBLIC_HEADER include/vkvg.h
+)
+
+TARGET_INCLUDE_DIRECTORIES(vkvg PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}/include
+ ${CMAKE_CURRENT_SOURCE_DIR}/vkh
+ ${CMAKE_CURRENT_SOURCE_DIR}/src
+ ${FREETYPE_INCLUDE_DIRS}
+ ${HARFBUZZ_INCLUDE_DIRS}
+ ${FONTCONFIG_INCLUDE_DIR}
+)
+
+TARGET_LINK_LIBRARIES(${PROJECT_NAME}
+ ${Vulkan_LIBRARY}
+ ${GLFW3_LIBRARY}
+ ${FREETYPE_LIBRARY}
+ ${HARFBUZZ_LIBRARIES}
+ ${FONTCONFIG_LIBRARIES}
+)
+
+CONFIGURE_FILE(vkvg.pc.in vkvg.pc @ONLY)
+INSTALL(FILES ${CMAKE_BINARY_DIR}/vkvg.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig)
+INSTALL(TARGETS vkvg
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
+
--- /dev/null
+# - Find FontConfig library
+# Find the FontConfig includes and library
+# This module defines
+# FONTCONFIG_INCLUDE_DIR, where to find fontconfig.h
+# FONTCONFIG_LIBRARIES, libraries to link against to use the FontConfig API.
+# FONTCONFIG_FOUND, If false, do not try to use FontConfig.
+
+#=============================================================================
+# Copyright 2012 Kitware, Inc.
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+# (To distribute this file outside of VTK, substitute the full
+# License text for the above reference.)
+
+find_path(FONTCONFIG_INCLUDE_DIR fontconfig/fontconfig.h)
+
+find_library(FONTCONFIG_LIBRARY NAMES fontconfig)
+
+# handle the QUIETLY and REQUIRED arguments and set FONTCONFIG_FOUND to TRUE if
+# all listed variables are TRUE
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(FontConfig DEFAULT_MSG
+ FONTCONFIG_LIBRARY FONTCONFIG_INCLUDE_DIR)
+
+if(FONTCONFIG_FOUND)
+ set( FONTCONFIG_LIBRARIES ${FONTCONFIG_LIBRARY} )
+endif()
+
+mark_as_advanced(FONTCONFIG_INCLUDE_DIR FONTCONFIG_LIBRARY FONTCONFIG_LIBRARIES)
--- /dev/null
+# Locate the glfw3 library
+#
+# This module defines the following variables:
+#
+# GLFW3_LIBRARY the name of the library;
+# GLFW3_INCLUDE_DIR where to find glfw include files.
+# GLFW3_FOUND true if both the GLFW3_LIBRARY and GLFW3_INCLUDE_DIR have been found.
+#
+# To help locate the library and include file, you can define a
+# variable called GLFW3_ROOT which points to the root of the glfw library
+# installation.
+#
+# default search dirs
+#
+# Cmake file from: https://github.com/daw42/glslcookbook
+
+set( _glfw3_HEADER_SEARCH_DIRS
+"/usr/include"
+"/usr/local/include"
+"${CMAKE_SOURCE_DIR}/includes"
+"C:/Program Files (x86)/glfw/include" )
+set( _glfw3_LIB_SEARCH_DIRS
+"/usr/lib"
+"/usr/local/lib"
+"${CMAKE_SOURCE_DIR}/lib"
+"C:/Program Files (x86)/glfw/lib-msvc110" )
+
+# Check environment for root search directory
+set( _glfw3_ENV_ROOT $ENV{GLFW3_ROOT} )
+if( NOT GLFW3_ROOT AND _glfw3_ENV_ROOT )
+ set(GLFW3_ROOT ${_glfw3_ENV_ROOT} )
+endif()
+
+# Put user specified location at beginning of search
+if( GLFW3_ROOT )
+ list( INSERT _glfw3_HEADER_SEARCH_DIRS 0 "${GLFW3_ROOT}/include" )
+ list( INSERT _glfw3_LIB_SEARCH_DIRS 0 "${GLFW3_ROOT}/lib" )
+endif()
+
+# Search for the header
+FIND_PATH(GLFW3_INCLUDE_DIR "GLFW/glfw3.h"
+PATHS ${_glfw3_HEADER_SEARCH_DIRS} )
+
+# Search for the library
+FIND_LIBRARY(GLFW3_LIBRARY NAMES glfw3 glfw
+PATHS ${_glfw3_LIB_SEARCH_DIRS} )
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(GLFW3 DEFAULT_MSG
+GLFW3_LIBRARY GLFW3_INCLUDE_DIR)
--- /dev/null
+#ifndef VKVG_H
+#define VKVG_H
+
+#include <vulkan/vulkan.h>
+#include "vectors.h"
+#include "math.h"
+
+#define VKVG_SAMPLES VK_SAMPLE_COUNT_8_BIT
+
+typedef enum VkvgDirection {
+ VKVG_HORIZONTAL = 0,
+ VKVG_VERTICAL = 1
+}VkvgDirection;
+
+typedef struct _vkvg_context_t* VkvgContext;
+typedef struct _vkvg_surface_t* VkvgSurface;
+typedef struct _vkvg_device_t* VkvgDevice;
+typedef struct _vkvg_pattern_t* VkvgPattern;
+
+VkvgDevice vkvg_device_create (VkDevice vkdev, VkQueue queue, uint32_t qFam,
+ VkPhysicalDeviceMemoryProperties memprops);
+void vkvg_device_destroy (VkvgDevice dev);
+
+VkvgSurface vkvg_surface_create (VkvgDevice dev, int32_t width, uint32_t height);
+void vkvg_surface_destroy (VkvgSurface surf);
+VkImage vkvg_surface_get_vk_image (VkvgSurface surf);
+VkImage vkvg_surface_get_vkh_image (VkvgSurface surf);
+
+//mimic from cairo, to facilitate usage of vkvg as cairo vulkan backend
+typedef enum _vkvg_operator {
+ VKVG_OPERATOR_CLEAR,
+
+ VKVG_OPERATOR_SOURCE,
+ VKVG_OPERATOR_OVER,
+ VKVG_OPERATOR_IN,
+ VKVG_OPERATOR_OUT,
+ VKVG_OPERATOR_ATOP,
+
+ VKVG_OPERATOR_DEST,
+ VKVG_OPERATOR_DEST_OVER,
+ VKVG_OPERATOR_DEST_IN,
+ VKVG_OPERATOR_DEST_OUT,
+ VKVG_OPERATOR_DEST_ATOP,
+
+ VKVG_OPERATOR_XOR,
+ VKVG_OPERATOR_ADD,
+ VKVG_OPERATOR_SATURATE,
+
+ VKVG_OPERATOR_MULTIPLY,
+ VKVG_OPERATOR_SCREEN,
+ VKVG_OPERATOR_OVERLAY,
+ VKVG_OPERATOR_DARKEN,
+ VKVG_OPERATOR_LIGHTEN,
+ VKVG_OPERATOR_COLOR_DODGE,
+ VKVG_OPERATOR_COLOR_BURN,
+ VKVG_OPERATOR_HARD_LIGHT,
+ VKVG_OPERATOR_SOFT_LIGHT,
+ VKVG_OPERATOR_DIFFERENCE,
+ VKVG_OPERATOR_EXCLUSION,
+ VKVG_OPERATOR_HSL_HUE,
+ VKVG_OPERATOR_HSL_SATURATION,
+ VKVG_OPERATOR_HSL_COLOR,
+ VKVG_OPERATOR_HSL_LUMINOSITY
+} vkvg_operator_t;
+
+/*Context*/
+VkvgContext vkvg_create (VkvgSurface surf);
+void vkvg_destroy (VkvgContext ctx);
+
+void vkvg_flush (VkvgContext ctx);
+
+void vkvg_close_path (VkvgContext ctx);
+void vkvg_line_to (VkvgContext ctx, float x, float y);
+void vkvg_move_to (VkvgContext ctx, float x, float y);
+void vkvg_arc (VkvgContext ctx, float xc, float yc, float radius, float a1, float a2);
+void vkvg_stroke (VkvgContext ctx);
+void vkvg_stroke_preserve (VkvgContext ctx);
+void vkvg_fill (VkvgContext ctx);
+void vkvg_fill_preserve (VkvgContext ctx);
+void vkvg_paint (VkvgContext ctx);
+void vkvg_reset_clip (VkvgContext ctx);
+void vkvg_clip (VkvgContext ctx);
+void vkvg_clip_preserve (VkvgContext ctx);
+void vkvg_set_rgba (VkvgContext ctx, float r, float g, float b, float a);
+void vkvg_set_linewidth (VkvgContext ctx, float width);
+void vkvg_set_source_surface(VkvgContext ctx, VkvgSurface surf, float x, float y);
+
+void vkvg_select_font_face (VkvgContext ctx, const char* name);
+void vkvg_set_font_size (VkvgContext ctx, uint32_t size);
+void vkvg_show_text (VkvgContext ctx, const char* text);
+
+void vkvg_save (VkvgContext ctx);
+void vkvg_restore (VkvgContext ctx);
+
+#endif
--- /dev/null
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (binding = 1) uniform sampler2DMS samplerPosition;
+layout (binding = 2) uniform sampler2DMS samplerNormal;
+layout (binding = 3) uniform sampler2DMS samplerAlbedo;
+
+layout (location = 0) in vec2 inUV;
+
+layout (location = 0) out vec4 outFragcolor;
+
+struct Light {
+ vec4 position;
+ vec3 color;
+ float radius;
+};
+
+layout (binding = 4) uniform UBO
+{
+ Light lights[6];
+ vec4 viewPos;
+ ivec2 windowSize;
+} ubo;
+
+layout (constant_id = 0) const int NUM_SAMPLES = 8;
+
+#define NUM_LIGHTS 6
+
+// Manual resolve for MSAA samples
+vec4 resolve(sampler2DMS tex, ivec2 uv)
+{
+ vec4 result = vec4(0.0);
+ for (int i = 0; i < NUM_SAMPLES; i++)
+ {
+ vec4 val = texelFetch(tex, uv, i);
+ result += val;
+ }
+ // Average resolved samples
+ return result / float(NUM_SAMPLES);
+}
+
+vec3 calculateLighting(vec3 pos, vec3 normal, vec4 albedo)
+{
+ vec3 result = vec3(0.0);
+
+ for(int i = 0; i < NUM_LIGHTS; ++i)
+ {
+ // Vector to light
+ vec3 L = ubo.lights[i].position.xyz - pos;
+ // Distance from light to fragment position
+ float dist = length(L);
+
+ // Viewer to fragment
+ vec3 V = ubo.viewPos.xyz - pos;
+ V = normalize(V);
+
+ // Light to fragment
+ L = normalize(L);
+
+ // Attenuation
+ float atten = ubo.lights[i].radius / (pow(dist, 2.0) + 1.0);
+
+ // Diffuse part
+ vec3 N = normalize(normal);
+ float NdotL = max(0.0, dot(N, L));
+ vec3 diff = ubo.lights[i].color * albedo.rgb * NdotL * atten;
+
+ // Specular part
+ vec3 R = reflect(-L, N);
+ float NdotR = max(0.0, dot(R, V));
+ vec3 spec = ubo.lights[i].color * albedo.a * pow(NdotR, 8.0) * atten;
+
+ result += diff + spec;
+ }
+ return result;
+}
+
+void main()
+{
+ ivec2 attDim = textureSize(samplerPosition);
+ ivec2 UV = ivec2(inUV * attDim);
+
+ #define ambient 0.15
+
+ // Ambient part
+ vec4 alb = resolve(samplerAlbedo, UV);
+ vec3 fragColor = vec3(0.0);
+
+ // Calualte lighting for every MSAA sample
+ for (int i = 0; i < NUM_SAMPLES; i++)
+ {
+ vec3 pos = texelFetch(samplerPosition, UV, i).rgb;
+ vec3 normal = texelFetch(samplerNormal, UV, i).rgb;
+ vec4 albedo = texelFetch(samplerAlbedo, UV, i);
+ fragColor += calculateLighting(pos, normal, albedo);
+ }
+
+ fragColor = (alb.rgb * ambient) + fragColor / float(NUM_SAMPLES);
+
+ outFragcolor = vec4(fragColor, 1.0);
+}
\ No newline at end of file
--- /dev/null
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (binding = 0) uniform UBO
+{
+ mat4 projection;
+ mat4 model;
+} ubo;
+
+layout (location = 0) out vec2 outUV;
+
+out gl_PerVertex
+{
+ vec4 gl_Position;
+};
+
+void main()
+{
+ outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
+ gl_Position = vec4(outUV * 2.0f - 1.0f, 0.0f, 1.0f);
+}
--- /dev/null
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (binding = 0) uniform sampler2D tex;
+
+layout (location = 0) in vec4 inColor;
+layout (location = 1) in vec3 inUV;
+
+layout (location = 0) out vec4 outFragColor;
+
+void main()
+{
+ vec4 c = texture(tex, inUV.xy);
+ outFragColor = c;
+}
--- /dev/null
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+#define WIDTH 3200
+#define HEIGHT 2400
+#define WORKGROUP_SIZE 32
+layout (local_size_x = WORKGROUP_SIZE, local_size_y = WORKGROUP_SIZE, local_size_z = 1 ) in;
+
+layout ( binding = 0, rgba8 ) uniform writeonly image2D buf;
+
+void main() {
+
+ /*
+ In order to fit the work into workgroups, some unnecessary threads are launched.
+ We terminate those threads here.
+ */
+ if(gl_GlobalInvocationID.x >= WIDTH || gl_GlobalInvocationID.y >= HEIGHT)
+ return;
+
+ float x = float(gl_GlobalInvocationID.x) / float(WIDTH);
+ float y = float(gl_GlobalInvocationID.y) / float(HEIGHT);
+
+ /*
+ What follows is code for rendering the mandelbrot set.
+ */
+ vec2 uv = vec2(x,y);
+ float n = 0.0;
+ vec2 c = vec2(-.445, 0.0) + (uv - 0.5)*(2.0+ 1.7*0.2 ),
+ z = vec2(0.0);
+ const int M =128;
+ for (int i = 0; i<M; i++)
+ {
+ z = vec2(z.x*z.x - z.y*z.y, 2.*z.x*z.y) + c;
+ if (dot(z, z) > 2) break;
+ n++;
+ }
+
+ // we use a simple cosine palette to determine color:
+ // http://iquilezles.org/www/articles/palettes/palettes.htm
+ float t = float(n) / float(M);
+ vec3 d = vec3(0.3, 0.3 ,0.5);
+ vec3 e = vec3(-0.2, -0.3 ,-0.5);
+ vec3 f = vec3(2.1, 2.0, 3.0);
+ vec3 g = vec3(0.0, 0.1, 0.0);
+ vec4 color = vec4( d + e*cos( 6.28318*(f*t+g) ) ,1.0);
+
+
+ imageStore(buf, ivec2(gl_GlobalInvocationID.xy), color);
+}
--- /dev/null
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+#define WIDTH 3200
+#define HEIGHT 2400
+#define WORKGROUP_SIZE 32
+layout (local_size_x = WORKGROUP_SIZE, local_size_y = WORKGROUP_SIZE, local_size_z = 1 ) in;
+
+layout ( binding = 0, rgba8 ) uniform writeonly image2D buf;
+
+// License: CC0 (http://creativecommons.org/publicdomain/zero/1.0/)
+
+// A standard gaussian function, used for weighting samples
+float gaussian(float x, float sigma) {
+ const float pi = 3.141592653589793;
+ return exp(-(x * x) / (2.0 * sigma * sigma)) / (sqrt(2.0 * pi) * sigma);
+}
+
+// This approximates the error function, needed for the gaussian integral
+vec2 erf(vec2 x) {
+ vec2 s = sign(x), a = abs(x);
+ x = 1.0 + (0.278393 + (0.230389 + 0.078108 * (a * a)) * a) * a;
+ x *= x;
+ return s - s / (x * x);
+}
+
+// Return the blurred mask along the x dimension
+float roundedBoxShadowX(float x, float y, float sigma, float corner, vec2 halfSize) {
+ float delta = min(halfSize.y - corner - abs(y), 0.0);
+ float curved = halfSize.x - corner + sqrt(max(0.0, corner * corner - delta * delta));
+ vec2 integral = 0.5 + 0.5 * erf((x + vec2(-curved, curved)) * (sqrt(0.5) / sigma));
+ return integral.y - integral.x;
+}
+
+// Return the mask for the shadow of a box from lower to upper
+float roundedBoxShadow(vec2 lower, vec2 upper, vec2 point, float sigma, float corner) {
+ // Center everything to make the math easier
+ vec2 center = (lower + upper) * 0.5;
+ vec2 halfSize = (upper - lower) * 0.5;
+ point -= center;
+
+ // The signal is only non-zero in a limited range, so don't waste samples
+ float low = point.y - halfSize.y;
+ float high = point.y + halfSize.y;
+ float start = clamp(-3.0 * sigma, low, high);
+ float end = clamp(3.0 * sigma, low, high);
+
+ // Accumulate samples (we can get away with surprisingly few samples)
+ float step = (end - start) / 4.0;
+ float y = start + step * 0.5;
+ float value = 0.0;
+ for (int i = 0; i < 4; i++) {
+ value += roundedBoxShadowX(point.x, point.y - y, sigma, corner, halfSize) * gaussian(y, sigma) * step;
+ y += step;
+ }
+
+ return value;
+}
+
+//---------------------------------------------------------
+// draw rectangle frame with rounded edges
+//---------------------------------------------------------
+float roundedFrame (vec2 uv, vec2 pos, vec2 size, float radius, float thickness)
+{
+ float d = length(max(abs(uv - pos),size) - size) - radius;
+ return smoothstep(0.95, 0.05, abs(d / thickness));
+ //return 1.0/abs(d / thickness);
+}
+
+void main() {
+
+ if(gl_GlobalInvocationID.x >= WIDTH || gl_GlobalInvocationID.y >= HEIGHT)
+ return;
+
+ vec2 pos = {float(gl_GlobalInvocationID.x) / float(WIDTH),float(gl_GlobalInvocationID.y) / float(HEIGHT)};
+
+ //vec4 color = vec4( 1.0,0.0,0.0,1.0);
+ //float s = roundedBoxShadow (vec2(0.1,0.1), vec2(0.5,0.5), vec2(x,y), 0.02, 0.1);
+ float s = roundedFrame (pos, vec2(0.6,0.6), vec2(0.1,0.1), 0.1,0.002);
+ vec4 color = vec4(s,s,s,s);
+
+ imageStore(buf, ivec2(gl_GlobalInvocationID.xy), color);
+}
--- /dev/null
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (set=0, binding = 0) uniform sampler2DArray fontMap;
+layout (set=1, binding = 0) uniform sampler2D source;
+
+layout (location = 0) in vec4 inColor; //source rgba
+layout (location = 1) in vec3 inFontUV; //if it is a text drawing, inFontUV.z hold fontMap layer
+layout (location = 2) in vec4 inSrcRect; //source bounds
+
+layout (location = 0) out vec4 outFragColor;
+
+layout (constant_id = 0) const int NUM_SAMPLES = 8;
+
+void main()
+{
+ vec4 c = inColor;
+ if (inSrcRect.z > 0.0){
+ vec2 srcUV = (gl_FragCoord.xy - inSrcRect.xy) / inSrcRect.zw;
+ c = texture(source, srcUV);
+ }
+ if (inFontUV.z >= 0.0)
+ c *= texture(fontMap, inFontUV).r;
+
+ outFragColor = c;
+}
--- /dev/null
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (location = 0) in vec2 inPos;
+layout (location = 1) in vec3 inUV;
+
+layout (location = 0) out vec4 outColor;
+layout (location = 1) out vec3 outUV;
+layout (location = 2) out vec4 outSrcRect;
+
+out gl_PerVertex
+{
+ vec4 gl_Position;
+};
+
+layout(push_constant) uniform PushConsts {
+ vec4 source;
+ vec2 scale;
+ vec2 translate;
+ int srcType;
+} pushConsts;
+
+void main()
+{
+ outUV = inUV;
+ if (pushConsts.srcType == 0){
+ outSrcRect.z = -1;
+ outColor = pushConsts.source;
+ }else
+ outSrcRect = pushConsts.source;
+
+ gl_Position = vec4(inPos.xy*pushConsts.scale+pushConsts.translate,0.0, 1.0);
+}
--- /dev/null
+#include "vectors.h"
+
+vec2 vec2_line_norm(vec2 a, vec2 b)
+{
+ vec2 d = {b.x - a.x, b.y - a.y};
+ float md = sqrt (d.x*d.x + d.y*d.y);
+ d.x/=md;
+ d.y/=md;
+ return d;
+}
+double vec2d_length(vec2d v){
+ return sqrt (v.x*v.x + v.y*v.y);
+}
+float vec2_length(vec2 v){
+ return sqrt (v.x*v.x + v.y*v.y);
+}
+vec2 vec2_norm(vec2 a)
+{
+ float m = sqrt (a.x*a.x + a.y*a.y);
+ vec2 d = {a.x/m, a.y/m};
+ return d;
+}
+vec2d vec2d_norm(vec2d a)
+{
+ double m = sqrt (a.x*a.x + a.y*a.y);
+ vec2d d = {a.x/m, a.y/m};
+ return d;
+}
+vec2d vec2d_mult(vec2d a, double m){
+ vec2d r = {a.x*m,a.y*m};
+ return r;
+}
+vec2 vec2_mult(vec2 a, float m){
+ vec2 r = {a.x*m,a.y*m};
+ return r;
+}
+
+vec2d vec2d_line_norm(vec2d a, vec2d b)
+{
+ vec2d d = {b.x - a.x, b.y - a.y};
+ double md = sqrt (d.x*d.x + d.y*d.y);
+ d.x/=md;
+ d.y/=md;
+ return d;
+}
+vec2d vec2d_perp (vec2d a){
+ vec2d vp = {a.y, -a.x};
+ return vp;
+}
+vec2 vec2_perp (vec2 a){
+ vec2 vp = {a.y, -a.x};
+ return vp;
+}
+vec2 vec2d_to_vec2(vec2d vd){
+ vec2 v = {vd.x,vd.y};
+ return v;
+}
+vec2 vec2_add (vec2 a, vec2 b){
+ vec2 r = {a.x + b.x, a.y + b.y};
+ return r;
+}
+vec2d vec2d_add (vec2d a, vec2d b){
+ vec2d r = {a.x + b.x, a.y + b.y};
+ return r;
+}
+vec2 vec2_sub (vec2 a, vec2 b){
+ vec2 r = {a.x - b.x, a.y - b.y};
+ return r;
+}
+vec2d vec2d_sub (vec2d a, vec2d b){
+ vec2d r = {a.x - b.x, a.y - b.y};
+ return r;
+}
+bool vec2_equ (vec2 a, vec2 b){
+ if ((a.x == b.x) && (a.y == b.y))
+ return true;
+ return false;
+}
--- /dev/null
+#ifndef VKVG_VECTORS_H
+#define VKVG_VECTORS_H
+
+#include <math.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+typedef struct {
+ float x;
+ float y;
+}vec2;
+typedef struct {
+ double x;
+ double y;
+}vec2d;
+
+typedef struct {
+ float x;
+ float y;
+ float z;
+}vec3;
+
+typedef struct {
+ union {
+ float x;
+ float r;
+ };
+ union {
+ float y;
+ float g;
+ };
+ union {
+ float z;
+ float width;
+ float b;
+ };
+ union {
+ float w;
+ float height;
+ float a;
+ };
+}vec4;
+
+typedef struct {
+ uint16_t x;
+ uint16_t y;
+ uint16_t z;
+ uint16_t w;
+}vec4i16;
+
+typedef struct {
+ int16_t x;
+ int16_t y;
+}vec2i16;
+
+float vec2_length (vec2 v);
+vec2 vec2_norm (vec2 a);
+vec2 vec2_perp (vec2 a);
+vec2 vec2_add (vec2 a, vec2 b);
+vec2 vec2_sub (vec2 a, vec2 b);
+vec2 vec2_mult (vec2 a, float m);
+bool vec2_equ (vec2 a, vec2 b);
+vec2 vec2_line_norm (vec2 a, vec2 b);
+
+double vec2d_length(vec2d v);
+vec2d vec2d_norm (vec2d a);
+vec2d vec2d_perp (vec2d a);
+vec2d vec2d_add (vec2d a, vec2d b);
+vec2d vec2d_sub (vec2d a, vec2d b);
+vec2d vec2d_mult (vec2d a, double m);
+vec2d vec2d_line_norm(vec2d a, vec2d b);
+
+vec2 vec2d_to_vec2(vec2d vd);
+#endif
--- /dev/null
+#include "vkvg_buff.h"
+#include "vkhelpers.h"
+
+void _set_size_and_map(VkhDevice pDev, VkBufferUsageFlags usage, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceSize size, vkvg_buff *buff){
+ VkMemoryRequirements memReq;
+ vkGetBufferMemoryRequirements(pDev->vkDev, buff->buffer, &memReq);
+ VkMemoryAllocateInfo memAllocInfo = { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+ .allocationSize = memReq.size };
+ assert(memory_type_from_properties(&pDev->phyMemProps, memReq.memoryTypeBits,memoryPropertyFlags, &memAllocInfo.memoryTypeIndex));
+ VK_CHECK_RESULT(vkAllocateMemory(pDev->vkDev, &memAllocInfo, NULL, &buff->memory));
+
+ buff->alignment = memReq.alignment;
+ buff->size = memAllocInfo.allocationSize;
+ buff->usageFlags = usage;
+ buff->memoryPropertyFlags = memoryPropertyFlags;
+
+ VK_CHECK_RESULT(vkBindBufferMemory(buff->pDev->vkDev, buff->buffer, buff->memory, 0));
+ VK_CHECK_RESULT(vkMapMemory(buff->pDev->vkDev, buff->memory, 0, VK_WHOLE_SIZE, 0, &buff->mapped));
+}
+
+void vkvg_buffer_create(VkhDevice pDev, VkBufferUsageFlags usage, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceSize size, vkvg_buff *buff){
+ buff->pDev = pDev;
+ VkBufferCreateInfo bufCreateInfo = {
+ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+ .usage = usage, .size = size, .sharingMode = VK_SHARING_MODE_EXCLUSIVE};
+ VK_CHECK_RESULT(vkCreateBuffer(pDev->vkDev, &bufCreateInfo, NULL, &buff->buffer));
+
+ _set_size_and_map(pDev,usage,memoryPropertyFlags,size,buff);
+}
+
+void vkvg_buffer_destroy(vkvg_buff *buff){
+ vkUnmapMemory (buff->pDev->vkDev, buff->memory);
+ vkDestroyBuffer (buff->pDev->vkDev, buff->buffer, NULL);
+ vkFreeMemory (buff->pDev->vkDev, buff->memory, NULL);
+}
+
+
+void vkvg_buffer_increase_size(vkvg_buff *buff, uint32_t sizeAdded){
+ size_t oldBSize = buff->size;
+ void* pSave = (void*)malloc (oldBSize);
+ memcpy (pSave, buff->mapped, oldBSize);
+
+ vkvg_buffer_destroy(buff);
+ vkvg_buffer_create(buff->pDev, buff->usageFlags, buff->memoryPropertyFlags, oldBSize + sizeAdded, buff);
+ memcpy (buff->mapped, pSave, oldBSize);
+ free(pSave);
+}
--- /dev/null
+#ifndef VKVG_BUFF_H
+#define VKVG_BUFF_H
+
+#include <vulkan/vulkan.h>
+#include "vkh_device.h"
+
+typedef struct vkvg_buff_t {
+ VkhDevice pDev;
+ VkBuffer buffer;
+ VkDeviceMemory memory;
+ VkDescriptorBufferInfo descriptor;
+ VkDeviceSize size;
+ VkDeviceSize alignment;
+
+ VkBufferUsageFlags usageFlags;
+ VkMemoryPropertyFlags memoryPropertyFlags;
+
+ void* mapped;
+}vkvg_buff;
+
+void vkvg_buffer_create (VkhDevice pDev, VkBufferUsageFlags usage,
+ VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceSize size, vkvg_buff* buff);
+void vkvg_buffer_destroy (vkvg_buff* buff);
+void vkvg_buffer_increase_size (vkvg_buff *buff, uint32_t sizeAdded);
+#endif
--- /dev/null
+#include "vkvg_device_internal.h"
+#include "vkvg_context_internal.h"
+#include "vkvg_surface_internal.h"
+
+#ifdef DEBUG
+static vec2 debugLinePoints[1000];
+static uint32_t dlpCount = 0;
+#endif
+
+VkvgContext vkvg_create(VkvgSurface surf)
+{
+ VkvgContext ctx = (vkvg_context*)calloc(1, sizeof(vkvg_context));
+
+ ctx->sizePoints = VKVG_PTS_SIZE;
+ ctx->sizeVertices = VKVG_VBO_SIZE;
+ ctx->sizeIndices = VKVG_IBO_SIZE;
+ ctx->sizePathes = VKVG_PATHES_SIZE;
+ ctx->curPos.x = ctx->curPos.y = 0;
+ ctx->lineWidth = 1;
+ ctx->pSurf = surf;
+
+ push_constants pc = {
+ {},
+ {2.0f/(float)ctx->pSurf->width,2.0f/(float)ctx->pSurf->height},
+ {-1.f,-1.f},
+ VKVG_SRC_SOLID
+ };
+ ctx->pushConsts = pc;
+
+ ctx->pPrev = surf->dev->lastCtx;
+ if (ctx->pPrev != NULL)
+ ctx->pPrev->pNext = ctx;
+ surf->dev->lastCtx = ctx;
+
+ ctx->selectedFont.fontFile = (char*)calloc(FONT_FILE_NAME_MAX_SIZE,sizeof(char));
+
+ ctx->flushFence = vkh_fence_create(ctx->pSurf->dev->vkDev);
+
+ ctx->points = (vec2*) malloc (VKVG_VBO_SIZE*sizeof(vec2));
+ ctx->pathes = (uint32_t*) malloc (VKVG_PATHES_SIZE*sizeof(uint32_t));
+
+ _create_vertices_buff (ctx);
+ _create_cmd_buff (ctx);
+ _init_descriptor_sets (ctx);
+ _update_font_descriptor_set (ctx);
+ _init_cmd_buff (ctx);
+ _clear_path (ctx);
+
+ return ctx;
+}
+void vkvg_flush (VkvgContext ctx){
+ _flush_cmd_buff(ctx);
+ _init_cmd_buff(ctx);
+
+#ifdef DEBUG
+
+ vec4 red = {0,0,1,1};
+ vec4 green = {0,1,0,1};
+ vec4 white = {1,1,1,1};
+
+ int j = 0;
+ while (j < dlpCount) {
+ add_line(ctx, debugLinePoints[j], debugLinePoints[j+1],green);
+ j+=2;
+ add_line(ctx, debugLinePoints[j], debugLinePoints[j+1],red);
+ j+=2;
+ add_line(ctx, debugLinePoints[j], debugLinePoints[j+1],white);
+ j+=2;
+ }
+ dlpCount = 0;
+ vkCmdBindPipeline(ctx->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->pSurf->dev->pipelineLineList);
+ vkCmdDrawIndexed(ctx->cmd, ctx->indCount-ctx->curIndStart, 1, ctx->curIndStart, 0, 1);
+ _flush_cmd_buff(ctx);
+#endif
+
+}
+
+void _free_ctx_save (vkvg_context_save_t* sav){
+ free(sav->pathes);
+ free(sav->points);
+ free(sav->selectedFont.fontFile);
+ vkh_image_destroy (sav->stencilMS);
+ free (sav);
+}
+
+void vkvg_destroy (VkvgContext ctx)
+{
+ _flush_cmd_buff(ctx);
+
+ VkDevice dev = ctx->pSurf->dev->vkDev;
+ vkDestroyFence (dev, ctx->flushFence,NULL);
+ vkFreeCommandBuffers(dev, ctx->pSurf->dev->cmdPool, 1, &ctx->cmd);
+
+ VkDescriptorSet dss[] = {ctx->dsFont,ctx->dsSrc};
+ vkFreeDescriptorSets(dev, ctx->pSurf->dev->descriptorPool,2,dss);
+
+ vkvg_buffer_destroy (&ctx->indices);
+ vkvg_buffer_destroy (&ctx->vertices);
+
+ //vkh_image_destroy (ctx->source);
+
+ free(ctx->selectedFont.fontFile);
+ free(ctx->pathes);
+ free(ctx->points);
+
+ //free saved context stack elmt
+ vkvg_context_save_t* next = ctx->pSavedCtxs;
+ while (next != NULL) {
+ vkvg_context_save_t* cur = next;
+ next = cur->pNext;
+ _free_ctx_save (cur);
+ }
+
+ if (ctx->pSurf->dev->lastCtx == ctx){
+ ctx->pSurf->dev->lastCtx = ctx->pPrev;
+ if (ctx->pPrev != NULL)
+ ctx->pPrev->pNext = NULL;
+ }else if (ctx->pPrev == NULL){
+ //first elmt, and it's not last one so pnext is not null
+ ctx->pNext->pPrev = NULL;
+ }else{
+ ctx->pPrev->pNext = ctx->pNext;
+ ctx->pNext->pPrev = ctx->pPrev;
+ }
+
+ free(ctx);
+}
+
+
+void vkvg_close_path (VkvgContext ctx){
+ if (ctx->pathPtr % 2 == 0)//current path is empty
+ return;
+ //check if at least 3 points are present
+ if (ctx->pointCount - ctx->pathes [ctx->pathPtr-1] > 2){
+ //set end idx of path to the same as start idx
+ ctx->pathes[ctx->pathPtr] = ctx->pathes [ctx->pathPtr-1];
+ //start new path
+ _check_pathes_array(ctx);
+ ctx->pathPtr++;
+ }
+}
+
+void vkvg_line_to (VkvgContext ctx, float x, float y)
+{
+ if (ctx->pathPtr % 2 == 0){//current path is empty
+ //set start to current idx in point array
+ ctx->pathes[ctx->pathPtr] = ctx->pointCount;
+ _check_pathes_array(ctx);
+ ctx->pathPtr++;
+ _add_curpos(ctx);
+ }
+ _add_point(ctx, x, y);
+}
+
+void vkvg_arc (VkvgContext ctx, float xc, float yc, float radius, float a1, float a2){
+ float aDiff = a2 - a1;
+ float aa1, aa2;
+ float step = M_PI/radius;
+
+ aa1 = _normalizeAngle(a1);
+ aa2 = aa1 + aDiff;
+
+ //if (aa1 > aa2)
+ // aa2 += M_PI * 2.0;
+ float a = aa1;
+ vec2 v = {cos(a)*radius + xc, sin(a)*radius + yc};
+
+ if (ctx->pathPtr % 2 == 0){//current path is empty
+ //set start to current idx in point array
+ ctx->pathes[ctx->pathPtr] = ctx->pointCount;
+ _check_pathes_array(ctx);
+ ctx->pathPtr++;
+ if (!vec2_equ(v,ctx->curPos))
+ _add_curpos(ctx);
+ }
+
+ if (aDiff < 0){
+ while(a > aa2){
+ v.x = cos(a)*radius + xc;
+ v.y = sin(a)*radius + yc;
+ _add_point(ctx,v.x,v.y);
+ a-=step;
+ }
+ }else{
+ while(a < aa2){
+ v.x = cos(a)*radius + xc;
+ v.y = sin(a)*radius + yc;
+ _add_point(ctx,v.x,v.y);
+ a+=step;
+ }
+ }
+ a = aa2;
+ v.x = cos(a)*radius + xc;
+ v.y = sin(a)*radius + yc;
+ if (!vec2_equ (v,ctx->curPos))
+ _add_point(ctx,v.x,v.y);
+}
+
+void vkvg_move_to (VkvgContext ctx, float x, float y)
+{
+ _finish_path(ctx);
+
+ ctx->curPos.x = x;
+ ctx->curPos.y = y;
+}
+
+void vkvg_clip_preserve (VkvgContext ctx){
+ vkCmdBindPipeline(ctx->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->pSurf->dev->pipelineClipping);
+ vkvg_fill_preserve(ctx);
+ vkvg_flush(ctx);
+ //should test current operator to bind correct pipeline
+ ctx->stencilRef++;
+ vkCmdBindPipeline(ctx->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->pSurf->dev->pipeline);
+ vkCmdSetStencilReference(ctx->cmd,VK_STENCIL_FRONT_AND_BACK, ctx->stencilRef);
+}
+void vkvg_reset_clip (VkvgContext ctx){
+ /*
+ VkClearValue clearValue = {0};
+ VkClearAttachment clrAtt = {VK_IMAGE_ASPECT_STENCIL_BIT, 2 ,clearValue};
+ VkClearRect clr = {{{0,0},{ctx->pSurf->width,ctx->pSurf->height}},0,1};
+ ctx->stencilRef=0;
+ vkCmdSetStencilReference(ctx->cmd,VK_STENCIL_FRONT_AND_BACK, ctx->stencilRef);
+ vkCmdClearAttachments (ctx->cmd ,1,&clrAtt,1,&clr);
+ */
+ _flush_cmd_buff(ctx);
+ _clear_stencil(ctx->pSurf);
+ ctx->stencilRef=0;
+ _init_cmd_buff(ctx);
+}
+void vkvg_clip (VkvgContext ctx){
+ vkvg_clip_preserve(ctx);
+ _clear_path(ctx);
+}
+void vkvg_fill_preserve (VkvgContext ctx){
+ if (ctx->pathPtr == 0) //nothing to fill
+ return;
+ if (ctx->pathPtr % 2 != 0) //current path is no close
+ vkvg_close_path(ctx);
+ if (ctx->pointCount * 4 > ctx->sizeIndices - ctx->indCount)
+ vkvg_flush(ctx);
+
+ uint32_t lastPathPointIdx, i = 0, ptrPath = 0;;
+ Vertex v = {};
+ v.uv.z = -1;
+
+ while (ptrPath < ctx->pathPtr){
+ if (!_path_is_closed(ctx,ptrPath)){
+ ptrPath+=2;
+ continue;
+ }
+ lastPathPointIdx = _get_last_point_of_closed_path (ctx, ptrPath);
+ uint32_t pathPointCount = lastPathPointIdx - ctx->pathes[ptrPath] + 1;
+ uint32_t firstVertIdx = ctx->vertCount;
+
+ ear_clip_point ecps[pathPointCount];
+ uint32_t ecps_count = pathPointCount;
+
+ while (i < lastPathPointIdx){
+ v.pos = ctx->points[i];
+ ear_clip_point ecp = {
+ v.pos,
+ i+firstVertIdx,
+ &ecps[i+1]
+ };
+ ecps[i] = ecp;
+ _add_vertex(ctx, v);
+ i++;
+ }
+ v.pos = ctx->points[i];
+ ear_clip_point ecp = {
+ v.pos,
+ i+firstVertIdx,
+ ecps
+ };
+ ecps[i] = ecp;
+ _add_vertex(ctx, v);
+
+ ear_clip_point* ecp_current = ecps;
+
+ while (ecps_count > 3) {
+ ear_clip_point* v0 = ecp_current->next,
+ *v1 = ecp_current, *v2 = ecp_current->next->next;
+ if (ecp_zcross (v0, v2, v1)<0){
+ ecp_current = ecp_current->next;
+ continue;
+ }
+ ear_clip_point* vP = v2->next;
+ bool isEar = true;
+ while (vP!=v1){
+ if (ptInTriangle(vP->pos,v0->pos,v2->pos,v1->pos)){
+ isEar = false;
+ break;
+ }
+ vP = vP->next;
+ }
+ if (isEar){
+ _add_triangle_indices(ctx, v0->idx,v1->idx,v2->idx);
+ v1->next = v2;
+ ecps_count --;
+ }else
+ ecp_current = ecp_current->next;
+ }
+ if (ecps_count == 3){
+ _add_triangle_indices(ctx, ecp_current->next->idx,ecp_current->idx,ecp_current->next->next->idx);
+ }
+
+ ptrPath+=2;
+ }
+ _record_draw_cmd(ctx);
+}
+void vkvg_fill (VkvgContext ctx){
+ vkvg_fill_preserve(ctx);
+ _clear_path(ctx);
+}
+void vkvg_stroke_preserve (VkvgContext ctx)
+{
+ _finish_path(ctx);
+
+ if (ctx->pathPtr == 0)//nothing to stroke
+ return;
+ if (ctx->pointCount * 4 > ctx->sizeIndices - ctx->indCount)
+ vkvg_flush(ctx);
+
+ Vertex v = {};
+ v.uv.z = -1;
+
+ float hw = ctx->lineWidth / 2.0;
+ int i = 0, ptrPath = 0;
+
+ uint32_t lastPathPointIdx, iL, iR;
+
+
+
+ while (ptrPath < ctx->pathPtr){
+ uint32_t firstIdx = ctx->vertCount;
+
+ if (_path_is_closed(ctx,ptrPath)){
+ lastPathPointIdx = _get_last_point_of_closed_path(ctx,ptrPath);
+ iL = lastPathPointIdx;
+ }else{
+ lastPathPointIdx = ctx->pathes[ptrPath+1];
+ vec2 bisec = vec2_line_norm(ctx->points[i], ctx->points[i+1]);
+ bisec = vec2_perp(bisec);
+ bisec = vec2_mult(bisec,hw);
+
+ v.pos = vec2_add(ctx->points[i], bisec);
+ _add_vertex(ctx, v);
+ v.pos = vec2_sub(ctx->points[i], bisec);
+ _add_vertex(ctx, v);
+ _add_tri_indices_for_rect(ctx, firstIdx);
+
+ iL = i++;
+ }
+
+ while (i < lastPathPointIdx){
+ iR = i+1;
+ _build_vb_step(ctx,v,hw,iL,i,iR);
+ iL = i++;
+ }
+
+ if (!_path_is_closed(ctx,ptrPath)){
+ vec2 bisec = vec2_line_norm(ctx->points[i-1], ctx->points[i]);
+ bisec = vec2_perp(bisec);
+ bisec = vec2_mult(bisec,hw);
+
+ v.pos = vec2_add(ctx->points[i], bisec);
+ _add_vertex(ctx, v);
+ v.pos = vec2_sub(ctx->points[i], bisec);
+ _add_vertex(ctx, v);
+
+ i++;
+ }else{
+ iR = ctx->pathes[ptrPath];
+ _build_vb_step(ctx,v,hw,iL,i,iR);
+
+ uint32_t* inds = (uint32_t*)(ctx->indices.mapped + ((ctx->indCount-6) * sizeof(uint32_t)));
+ uint32_t ii = firstIdx;
+ inds[1] = ii;
+ inds[4] = ii;
+ inds[5] = ii+1;
+ i++;
+ }
+
+ ptrPath+=2;
+ }
+ _record_draw_cmd(ctx);
+}
+void vkvg_stroke (VkvgContext ctx)
+{
+ vkvg_stroke_preserve(ctx);
+ _clear_path(ctx);
+}
+void _vkvg_fill_rectangle (VkvgContext ctx, float x, float y, float width, float height){
+ Vertex v[4] =
+ {
+ {{x,y}, {0,0,-1}},
+ {{x,y+height}, {0,0,-1}},
+ {{x+width,y}, {0,0,-1}},
+ {{x+width,y+height},{0,0,-1}}
+ };
+ uint32_t firstIdx = ctx->vertCount;
+ Vertex* pVert = (Vertex*)(ctx->vertices.mapped + ctx->vertCount * sizeof(Vertex));
+ memcpy (pVert,v,4*sizeof(Vertex));
+ ctx->vertCount+=4;
+ _add_tri_indices_for_rect(ctx, firstIdx);
+}
+void vkvg_paint (VkvgContext ctx){
+ _vkvg_fill_rectangle (ctx, 0, 0, ctx->pSurf->width, ctx->pSurf->height);
+}
+
+void vkvg_set_rgba (VkvgContext ctx, float r, float g, float b, float a)
+{
+ vec4 c = {r,g,b,a};
+ ctx->pushConsts.source = c;
+ ctx->pushConsts.srcType = VKVG_SRC_SOLID;
+ vkCmdPushConstants(ctx->cmd, ctx->pSurf->dev->pipelineLayout,
+ VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(push_constants),&ctx->pushConsts);
+ ctx->curRGBA.x = r;
+ ctx->curRGBA.y = g;
+ ctx->curRGBA.z = b;
+ ctx->curRGBA.w = a;
+ /*
+ _flush_cmd_buff(ctx);
+
+ vkh_cmd_begin (ctx->cmd,VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
+
+ VkClearColorValue clr = {r,g,b,a};
+ VkImageSubresourceRange range = {VK_IMAGE_ASPECT_COLOR_BIT,0,1,0,1};
+
+ set_image_layout (ctx->cmd, ctx->source->image, VK_IMAGE_ASPECT_COLOR_BIT,
+ VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
+ vkCmdClearColorImage (ctx->cmd, ctx->source->image,VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,&clr,1,&range);
+ set_image_layout (ctx->cmd, ctx->source->image, VK_IMAGE_ASPECT_COLOR_BIT,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
+ vkh_cmd_end (ctx->cmd);
+
+ _submit_wait_and_reset_cmd (ctx);
+ _init_cmd_buff (ctx);
+ */
+}
+void vkvg_set_source_surface(VkvgContext ctx, VkvgSurface surf, float x, float y){
+ _flush_cmd_buff(ctx);
+
+ ctx->source = surf->img;
+
+ vkh_image_create_sampler(ctx->source,VK_FILTER_NEAREST, VK_FILTER_NEAREST,
+ VK_SAMPLER_MIPMAP_MODE_NEAREST,VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER);
+
+ if (ctx->source->layout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL){
+ vkh_cmd_begin (ctx->cmd,VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
+
+ vkh_image_set_layout (ctx->cmd, ctx->source, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
+ vkh_cmd_end (ctx->cmd);
+
+ _submit_wait_and_reset_cmd (ctx);
+ }
+
+ _update_source_descriptor_set (ctx);
+ _init_cmd_buff (ctx);
+
+ vec4 srcRect = {x,y,surf->width,surf->height};
+ ctx->pushConsts.source = srcRect;
+ ctx->pushConsts.srcType = VKVG_SRC_PATTERN;
+ vkCmdPushConstants(ctx->cmd, ctx->pSurf->dev->pipelineLayout,
+ VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(push_constants),&ctx->pushConsts);
+}
+
+void vkvg_set_linewidth (VkvgContext ctx, float width){
+ ctx->lineWidth = width;
+}
+
+
+void vkvg_select_font_face (VkvgContext ctx, const char* name){
+
+ _select_font_face (ctx, name);
+}
+void vkvg_set_font_size (VkvgContext ctx, uint32_t size){
+ _set_font_size (ctx,size);
+}
+
+void vkvg_set_text_direction (vkvg_context* ctx, VkvgDirection direction){
+
+}
+
+void vkvg_show_text (VkvgContext ctx, const char* text){
+ _show_text(ctx,text);
+ _record_draw_cmd(ctx);
+}
+
+void vkvg_save (VkvgContext ctx){
+ _flush_cmd_buff(ctx);
+
+ VkvgDevice dev = ctx->pSurf->dev;
+ vkvg_context_save_t* sav = (vkvg_context_save_t*)calloc(1,sizeof(vkvg_context_save_t));
+
+ sav->stencilMS = vkh_image_ms_create(dev,VK_FORMAT_S8_UINT,VKVG_SAMPLES,ctx->pSurf->width,ctx->pSurf->height,
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT|VK_IMAGE_USAGE_TRANSFER_DST_BIT);
+
+ vkh_cmd_begin (ctx->cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
+
+ vkh_image_set_layout (ctx->cmd, ctx->pSurf->stencilMS, VK_IMAGE_ASPECT_STENCIL_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
+ vkh_image_set_layout (ctx->cmd, sav->stencilMS, VK_IMAGE_ASPECT_STENCIL_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
+
+ VkImageCopy cregion = { .srcSubresource = {VK_IMAGE_ASPECT_STENCIL_BIT, 0, 0, 1},
+ .dstSubresource = {VK_IMAGE_ASPECT_STENCIL_BIT, 0, 0, 1},
+ .extent = {ctx->pSurf->width,ctx->pSurf->height,1}};
+ vkCmdCopyImage(ctx->cmd,
+ ctx->pSurf->stencilMS->image,VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ sav->stencilMS->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ 1, &cregion);
+
+ vkh_image_set_layout (ctx->cmd, ctx->pSurf->stencilMS, VK_IMAGE_ASPECT_STENCIL_BIT, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
+
+ VK_CHECK_RESULT(vkEndCommandBuffer(ctx->cmd));
+ _submit_ctx_cmd(ctx);
+
+ sav->stencilRef = ctx->stencilRef;
+ sav->sizePoints = ctx->sizePoints;
+ sav->pointCount = ctx->pointCount;
+
+ sav->points = (vec2*)malloc (sav->pointCount * sizeof(vec2));
+ memcpy (sav->points, ctx->points, sav->pointCount * sizeof(vec2));
+
+ sav->pathPtr = ctx->pathPtr;
+ sav->sizePathes = ctx->sizePathes;
+
+ sav->pathes = (uint32_t*)malloc (sav->pathPtr * sizeof(uint32_t));
+ memcpy (sav->pathes, ctx->pathes, sav->pathPtr * sizeof(uint32_t));
+
+ sav->curPos = ctx->curPos;
+ sav->curRGBA = ctx->curRGBA;
+ sav->lineWidth = ctx->lineWidth;
+
+ sav->selectedFont = ctx->selectedFont;
+ sav->selectedFont.fontFile = (char*)calloc(FONT_FILE_NAME_MAX_SIZE,sizeof(char));
+ strcpy (sav->selectedFont.fontFile, ctx->selectedFont.fontFile);
+
+ sav->currentFont = ctx->currentFont;
+ sav->textDirection= ctx->textDirection;
+ sav->pushConsts = ctx->pushConsts;
+ sav->source = ctx->source;
+
+ sav->pNext = ctx->pSavedCtxs;
+ ctx->pSavedCtxs = sav;
+
+ _wait_and_reset_ctx_cmd (ctx);
+ _init_cmd_buff (ctx);
+}
+void vkvg_restore (VkvgContext ctx){
+ if (ctx->pSavedCtxs == NULL)
+ return;
+ _flush_cmd_buff(ctx);
+
+ vkvg_context_save_t* sav = ctx->pSavedCtxs;
+ ctx->pSavedCtxs = sav->pNext;
+
+ vkh_cmd_begin (ctx->cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
+
+ vkh_image_set_layout (ctx->cmd, ctx->pSurf->stencilMS, VK_IMAGE_ASPECT_STENCIL_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
+ vkh_image_set_layout (ctx->cmd, sav->stencilMS, VK_IMAGE_ASPECT_STENCIL_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
+
+ VkImageCopy cregion = { .srcSubresource = {VK_IMAGE_ASPECT_STENCIL_BIT, 0, 0, 1},
+ .dstSubresource = {VK_IMAGE_ASPECT_STENCIL_BIT, 0, 0, 1},
+ .extent = {ctx->pSurf->width,ctx->pSurf->height,1}};
+ vkCmdCopyImage(ctx->cmd,
+ sav->stencilMS->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ ctx->pSurf->stencilMS->image,VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ 1, &cregion);
+ vkh_image_set_layout (ctx->cmd, ctx->pSurf->stencilMS, VK_IMAGE_ASPECT_STENCIL_BIT, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
+
+ VK_CHECK_RESULT(vkEndCommandBuffer(ctx->cmd));
+ _submit_ctx_cmd(ctx);
+
+ ctx->stencilRef = sav->stencilRef;
+ ctx->sizePoints = sav->sizePoints;
+ ctx->pointCount = sav->pointCount;
+
+ ctx->points = (vec2*)realloc ( ctx->points, ctx->sizePoints * sizeof(vec2));
+ memset (ctx->points, 0, ctx->sizePoints * sizeof(vec2));
+ memcpy (ctx->points, sav->points, ctx->pointCount * sizeof(vec2));
+
+ ctx->pathPtr = sav->pathPtr;
+ ctx->sizePathes = sav->sizePathes;
+
+ ctx->pathes = (uint32_t*)realloc (ctx->pathes, ctx->sizePathes * sizeof(uint32_t));
+ memset (ctx->pathes, 0, ctx->sizePathes * sizeof(uint32_t));
+ memcpy (ctx->pathes, sav->pathes, ctx->pathPtr * sizeof(uint32_t));
+
+ ctx->curPos = sav->curPos;
+ ctx->curRGBA = sav->curRGBA;
+ ctx->lineWidth = sav->lineWidth;
+
+ ctx->selectedFont.charSize = sav->selectedFont.charSize;
+ strcpy (ctx->selectedFont.fontFile, sav->selectedFont.fontFile);
+
+ ctx->currentFont = sav->currentFont;
+ ctx->textDirection= sav->textDirection;
+ ctx->pushConsts = sav->pushConsts;
+ ctx->source = sav->source;
+
+ _wait_and_reset_ctx_cmd (ctx);
+ _init_cmd_buff (ctx);
+
+ _free_ctx_save(sav);
+}
--- /dev/null
+#include "vkvg_surface_internal.h"
+#include "vkvg_context_internal.h"
+#include "vkvg_device_internal.h"
+
+void _check_pathes_array (VkvgContext ctx){
+ if (ctx->sizePathes - ctx->pathPtr > VKVG_ARRAY_THRESHOLD)
+ return;
+ ctx->sizePathes += VKVG_PATHES_SIZE;
+ ctx->pathes = (uint32_t*) realloc (ctx->pathes, ctx->sizePathes*sizeof(uint32_t));
+}
+void _add_point(VkvgContext ctx, float x, float y){
+ ctx->curPos.x = x;
+ ctx->curPos.y = y;
+ ctx->points[ctx->pointCount] = ctx->curPos;
+ ctx->pointCount++;
+}
+void _add_point_v2(VkvgContext ctx, vec2 v){
+ ctx->curPos = v;
+ ctx->points[ctx->pointCount] = ctx->curPos;
+ ctx->pointCount++;
+}
+void _add_curpos (VkvgContext ctx){
+ ctx->points[ctx->pointCount] = ctx->curPos;
+ ctx->pointCount++;
+}
+float _normalizeAngle(float a)
+{
+ float res = ROUND_DOWN(fmod(a,2.0f*M_PI),100);
+ if (res < 0.0f)
+ return res + 2.0f*M_PI;
+ else
+ return res;
+}
+void _create_vertices_buff (VkvgContext ctx){
+ vkvg_buffer_create ((VkhDevice*)ctx->pSurf->dev,
+ VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+ ctx->sizeVertices * sizeof(Vertex), &ctx->vertices);
+ vkvg_buffer_create ((VkhDevice*)ctx->pSurf->dev,
+ VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+ ctx->sizeIndices * sizeof(uint32_t), &ctx->indices);
+}
+void _add_vertex(VkvgContext ctx, Vertex v){
+ Vertex* pVert = (Vertex*)(ctx->vertices.mapped + ctx->vertCount * sizeof(Vertex));
+ *pVert = v;
+ ctx->vertCount++;
+}
+void _set_vertex(VkvgContext ctx, uint32_t idx, Vertex v){
+ Vertex* pVert = (Vertex*)(ctx->vertices.mapped + idx * sizeof(Vertex));
+ *pVert = v;
+}
+void _add_tri_indices_for_rect (VkvgContext ctx, uint32_t i){
+ uint32_t* inds = (uint32_t*)(ctx->indices.mapped + (ctx->indCount * sizeof(uint32_t)));
+ inds[0] = i;
+ inds[1] = i+2;
+ inds[2] = i+1;
+ inds[3] = i+1;
+ inds[4] = i+2;
+ inds[5] = i+3;
+ ctx->indCount+=6;
+}
+void _add_triangle_indices(VkvgContext ctx, uint32_t i0, uint32_t i1, uint32_t i2){
+ uint32_t* inds = (uint32_t*)(ctx->indices.mapped + (ctx->indCount * sizeof(uint32_t)));
+ inds[0] = i0;
+ inds[1] = i1;
+ inds[2] = i2;
+ ctx->indCount+=3;
+}
+void _create_cmd_buff (VkvgContext ctx){
+ ctx->cmd = vkh_cmd_buff_create(ctx->pSurf->dev->vkDev, ctx->pSurf->dev->cmdPool,VK_COMMAND_BUFFER_LEVEL_PRIMARY);
+}
+void _record_draw_cmd (VkvgContext ctx){
+ if (ctx->indCount == ctx->curIndStart)
+ return;
+ vkCmdDrawIndexed(ctx->cmd, ctx->indCount - ctx->curIndStart, 1, ctx->curIndStart, 0, 1);
+ ctx->curIndStart = ctx->indCount;
+}
+
+void _submit_ctx_cmd (VkvgContext ctx){
+ VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
+ VkSubmitInfo submit_info = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
+ .commandBufferCount = 1,
+ .signalSemaphoreCount = 0,
+ .pSignalSemaphores = NULL,
+ .waitSemaphoreCount = 0,
+ .pWaitSemaphores = NULL,
+ .pWaitDstStageMask = &dstStageMask,
+ .pCommandBuffers = &ctx->cmd};
+ VK_CHECK_RESULT(vkQueueSubmit(ctx->pSurf->dev->queue, 1, &submit_info, ctx->flushFence));
+}
+void _wait_and_reset_ctx_cmd (VkvgContext ctx){
+ vkWaitForFences(ctx->pSurf->dev->vkDev,1,&ctx->flushFence,VK_TRUE,UINT64_MAX);
+ vkResetFences(ctx->pSurf->dev->vkDev,1,&ctx->flushFence);
+ vkResetCommandBuffer(ctx->cmd,0);
+}
+
+void _submit_wait_and_reset_cmd (VkvgContext ctx){
+ _submit_ctx_cmd(ctx);
+ _wait_and_reset_ctx_cmd(ctx);
+}
+void _explicit_ms_resolve (VkvgContext ctx){
+ vkh_image_set_layout (ctx->cmd, ctx->pSurf->imgMS, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
+ vkh_image_set_layout (ctx->cmd, ctx->pSurf->img, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
+
+ VkImageResolve re = {
+ .extent = {ctx->pSurf->width, ctx->pSurf->height,1},
+ .srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT,0,0,1},
+ .dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT,0,0,1}
+ };
+
+ vkCmdResolveImage(ctx->cmd,
+ ctx->pSurf->imgMS->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ ctx->pSurf->img->image ,VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ 1,&re);
+ vkh_image_set_layout (ctx->cmd, ctx->pSurf->imgMS, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL ,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
+}
+
+void _flush_cmd_buff (VkvgContext ctx){
+ if (ctx->indCount == 0){
+ vkResetCommandBuffer(ctx->cmd,0);
+ return;
+ }
+ _record_draw_cmd (ctx);
+ vkCmdEndRenderPass (ctx->cmd);
+ //_explicit_ms_resolve (ctx);
+ vkh_cmd_end (ctx->cmd);
+
+ _submit_wait_and_reset_cmd(ctx);
+}
+void _init_cmd_buff (VkvgContext ctx){
+ ctx->vertCount = ctx->indCount = ctx->curIndStart = 0;
+ //VkClearValue clearValues[2];
+ //clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } };
+ //clearValues[1].depthStencil = { 1.0f, 0 };
+ VkClearValue clearValues[4] = {
+ { 0.0f, 0.0f, 0.0f, 1.0f },
+ { 0.0f, 0.0f, 0.0f, 1.0f },
+ { 1.0f, 0 },
+ { 1.0f, 0 }
+ };
+ VkRenderPassBeginInfo renderPassBeginInfo = { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
+ .renderPass = ctx->pSurf->dev->renderPass,
+ .framebuffer = ctx->pSurf->fb,
+ .renderArea.extent = {ctx->pSurf->width,ctx->pSurf->height},
+ };
+ //.clearValueCount = 4,
+ //.pClearValues = clearValues};
+ vkh_cmd_begin (ctx->cmd,VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
+ vkCmdBeginRenderPass (ctx->cmd, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
+ VkViewport viewport = {0,0,ctx->pSurf->width,ctx->pSurf->height,0,1};
+ vkCmdSetViewport(ctx->cmd, 0, 1, &viewport);
+ VkRect2D scissor = {{0,0},{ctx->pSurf->width,ctx->pSurf->height}};
+ vkCmdSetScissor(ctx->cmd, 0, 1, &scissor);
+
+ /*push_constants pc = {
+ {(float)ctx->pSurf->width,(float)ctx->pSurf->height},
+ {2.0f/(float)ctx->pSurf->width,2.0f/(float)ctx->pSurf->height},
+ {-1.f,-1.f},
+ };*/
+
+ vkCmdPushConstants(ctx->cmd, ctx->pSurf->dev->pipelineLayout,
+ VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(push_constants),&ctx->pushConsts);
+ //vkCmdPushConstants(ctx->cmd, ctx->pSurf->dev->pipelineLayout,
+ // VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(push_constants),&pc);
+
+ VkDescriptorSet dss[] = {ctx->dsFont,ctx->dsSrc};
+ vkCmdBindDescriptorSets(ctx->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->pSurf->dev->pipelineLayout,
+ 0, 2, dss, 0, NULL);
+ VkDeviceSize offsets[1] = { 0 };
+ vkCmdBindVertexBuffers(ctx->cmd, 0, 1, &ctx->vertices.buffer, offsets);
+ vkCmdBindIndexBuffer(ctx->cmd, ctx->indices.buffer, 0, VK_INDEX_TYPE_UINT32);
+ vkCmdBindPipeline(ctx->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, ctx->pSurf->dev->pipeline);
+ vkCmdSetStencilReference(ctx->cmd,VK_STENCIL_FRONT_AND_BACK, ctx->stencilRef);
+}
+
+void _finish_path (VkvgContext ctx){
+ if (ctx->pathPtr % 2 == 0)//current path is empty
+ return;
+ //set end index of current path to last point in points array
+ ctx->pathes[ctx->pathPtr] = ctx->pointCount - 1;
+ _check_pathes_array(ctx);
+ ctx->pathPtr++;
+}
+void _clear_path (VkvgContext ctx){
+ ctx->pathPtr = 0;
+ ctx->pointCount = 0;
+}
+bool _path_is_closed (VkvgContext ctx, uint32_t ptrPath){
+ return (ctx->pathes[ptrPath] == ctx->pathes[ptrPath+1]);
+}
+uint32_t _get_last_point_of_closed_path(VkvgContext ctx, uint32_t ptrPath){
+ if (ptrPath+2 < ctx->pathPtr) //this is not the last path
+ return ctx->pathes[ptrPath+2]-1; //last p is p prior to first idx of next path
+ return ctx->pointCount-1; //last point of path is last point of point array
+}
+void _update_source_descriptor_set (VkvgContext ctx){
+ VkvgDevice dev = ctx->pSurf->dev;
+ VkDescriptorImageInfo descSrcTex = { .imageView = ctx->source->view,
+ .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ .sampler = ctx->source->sampler };
+
+ VkWriteDescriptorSet writeDescriptorSet = {
+ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+ .dstSet = ctx->dsSrc,
+ .dstBinding = 0,
+ .descriptorCount = 1,
+ .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ .pImageInfo = &descSrcTex
+ };
+ vkUpdateDescriptorSets(dev->vkDev, 1, &writeDescriptorSet, 0, NULL);
+}
+void _update_font_descriptor_set (VkvgContext ctx){
+ VkvgDevice dev = ctx->pSurf->dev;
+ _font_cache_t* cache = dev->fontCache;
+ VkDescriptorImageInfo descFontTex = { .imageView = cache->cacheTex->view,
+ .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ .sampler = cache->cacheTex->sampler };
+
+ VkWriteDescriptorSet writeDescriptorSet = {
+ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+ .dstSet = ctx->dsFont,
+ .dstBinding = 0,
+ .descriptorCount = 1,
+ .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ .pImageInfo = &descFontTex
+ };
+ vkUpdateDescriptorSets(dev->vkDev, 1, &writeDescriptorSet, 0, NULL);
+}
+
+void _init_descriptor_sets (VkvgContext ctx){
+ VkvgDevice dev = ctx->pSurf->dev;
+ VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
+ .descriptorPool = dev->descriptorPool,
+ .descriptorSetCount = 1,
+ .pSetLayouts = &dev->dslFont };
+ VK_CHECK_RESULT(vkAllocateDescriptorSets(dev->vkDev, &descriptorSetAllocateInfo, &ctx->dsFont));
+ descriptorSetAllocateInfo.pSetLayouts = &dev->dslSrc;
+ VK_CHECK_RESULT(vkAllocateDescriptorSets(dev->vkDev, &descriptorSetAllocateInfo, &ctx->dsSrc));
+}
+void add_line(vkvg_context* ctx, vec2 p1, vec2 p2, vec4 col){
+ Vertex v = {{p1.x,p1.y},{0,0,-1}};
+ _add_vertex(ctx, v);
+ v.pos = p2;
+ _add_vertex(ctx, v);
+ uint32_t* inds = (uint32_t*)(ctx->indices.mapped + (ctx->indCount * sizeof(uint32_t)));
+ inds[0] = ctx->vertCount - 2;
+ inds[1] = ctx->vertCount - 1;
+ ctx->indCount+=2;
+}
+
+void _build_vb_step (vkvg_context* ctx, Vertex v, double hw, uint32_t iL, uint32_t i, uint32_t iR){
+ double alpha = 0;
+ vec2 v0n = vec2_line_norm(ctx->points[iL], ctx->points[i]);
+ vec2 v1n = vec2_line_norm(ctx->points[i], ctx->points[iR]);
+
+ vec2 bisec = vec2_add(v0n,v1n);
+ bisec = vec2_norm(bisec);
+ alpha = acos(v0n.x*v1n.x+v0n.y*v1n.y)/2.0;
+
+ float lh = (float)hw / cos(alpha);
+ bisec = vec2_perp(bisec);
+ bisec = vec2_mult(bisec,lh);
+
+#ifdef DEBUG
+
+ debugLinePoints[dlpCount] = ctx->points[i];
+ debugLinePoints[dlpCount+1] = _v2add(ctx->points[i], _vec2dToVec2(_v2Multd(v0n,10)));
+ dlpCount+=2;
+ debugLinePoints[dlpCount] = ctx->points[i];
+ debugLinePoints[dlpCount+1] = _v2add(ctx->points[i], _vec2dToVec2(_v2Multd(v1n,10)));
+ dlpCount+=2;
+ debugLinePoints[dlpCount] = ctx->points[i];
+ debugLinePoints[dlpCount+1] = ctx->points[iR];
+ dlpCount+=2;
+#endif
+ uint32_t firstIdx = ctx->vertCount;
+ v.pos = vec2_add(ctx->points[i], bisec);
+ _add_vertex(ctx, v);
+ v.pos = vec2_sub(ctx->points[i], bisec);
+ _add_vertex(ctx, v);
+ _add_tri_indices_for_rect(ctx, firstIdx);
+}
+
+bool ptInTriangle(vec2 p, vec2 p0, vec2 p1, vec2 p2) {
+ float dX = p.x-p2.x;
+ float dY = p.y-p2.y;
+ float dX21 = p2.x-p1.x;
+ float dY12 = p1.y-p2.y;
+ float D = dY12*(p0.x-p2.x) + dX21*(p0.y-p2.y);
+ float s = dY12*dX + dX21*dY;
+ float t = (p2.y-p0.y)*dX + (p0.x-p2.x)*dY;
+ if (D<0)
+ return (s<=0) && (t<=0) && (s+t>=D);
+ return (s>=0) && (t>=0) && (s+t<=D);
+}
--- /dev/null
+#ifndef VKVG_CONTEXT_INTERNAL_H
+#define VKVG_CONTEXT_INTERNAL_H
+
+#include "vkvg_internal.h"
+#include "vkvg.h"
+#include "vkvg_buff.h"
+#include "vkh_image.h"
+#include "vkvg_fonts.h"
+
+#define VKVG_PTS_SIZE 4096
+#define VKVG_VBO_SIZE VKVG_PTS_SIZE * 2
+#define VKVG_IBO_SIZE VKVG_VBO_SIZE * 2
+#define VKVG_PATHES_SIZE 128
+#define VKVG_ARRAY_THRESHOLD 4
+
+#define ROUND_DOWN(v,p) (floorf(v * p) / p)
+
+typedef struct{
+ vec2 pos;
+ vec3 uv;
+}Vertex;
+
+typedef struct _ear_clip_point{
+ vec2 pos;
+ uint32_t idx;
+ struct _ear_clip_point* next;
+}ear_clip_point;
+
+#define VKVG_SRC_SOLID 0
+#define VKVG_SRC_PATTERN 1
+
+typedef struct {
+ vec4 source;
+ vec2 scale;
+ vec2 translate;
+ int srcType;
+}push_constants;
+
+typedef struct _vkvg_context_save_t{
+ struct _vkvg_context_save_t* pNext;
+
+ VkhImage source;
+ VkhImage stencilMS;
+ uint32_t stencilRef;
+ vec2* points; //points array
+ size_t sizePoints; //reserved size
+ uint32_t pointCount; //effective points count
+
+ uint32_t pathPtr;
+ uint32_t* pathes;
+ size_t sizePathes;
+
+ vec2 curPos;
+ vec4 curRGBA;
+ float lineWidth;
+
+ _vkvg_font_t selectedFont; //hold current face and size before cache addition
+ _vkvg_font_t* currentFont; //font ready for lookup
+ VkvgDirection textDirection;
+ push_constants pushConsts;
+
+}vkvg_context_save_t;
+
+typedef struct _vkvg_context_t {
+ VkvgContext pPrev; //double linked list of contexts
+ VkvgContext pNext;
+
+ VkvgSurface pSurf;
+ VkCommandBuffer cmd;
+ VkFence flushFence;
+ uint32_t stencilRef;
+ VkhImage source;
+ VkDescriptorSet dsFont;
+ VkDescriptorSet dsSrc;
+
+ //vk buffers, holds data until flush
+ vkvg_buff indices;
+ size_t sizeIndices;
+ uint32_t indCount;
+
+ uint32_t curIndStart;
+
+ vkvg_buff vertices;
+ size_t sizeVertices;
+ uint32_t vertCount;
+
+ //pathes, exists until stroke of fill
+ vec2* points; //points array
+ size_t sizePoints; //reserved size
+ uint32_t pointCount; //effective points count
+
+ uint32_t pathPtr;
+ uint32_t* pathes;
+ size_t sizePathes;
+
+ vec2 curPos;
+ vec4 curRGBA;
+ float lineWidth;
+
+ _vkvg_font_t selectedFont; //hold current face and size before cache addition
+ _vkvg_font_t* currentFont; //font ready for lookup
+ VkvgDirection textDirection;
+
+ push_constants pushConsts;
+
+ vkvg_context_save_t* pSavedCtxs;//last ctx saved ptr
+}vkvg_context;
+
+void _check_pathes_array (VkvgContext ctx);
+float _normalizeAngle (float a);
+
+void _add_point (VkvgContext ctx, float x, float y);
+void _add_point_v2 (VkvgContext ctx, vec2 v);
+void _add_curpos (VkvgContext ctx);
+void _vkvg_fill_rectangle (VkvgContext ctx, float x, float y, float width, float height);
+
+void _create_vertices_buff (VkvgContext ctx);
+void _add_vertex (VkvgContext ctx, Vertex v);
+void _set_vertex (VkvgContext ctx, uint32_t idx, Vertex v);
+void _add_tri_indices_for_rect (VkvgContext ctx, uint32_t i);
+void _add_triangle_indices (VkvgContext ctx, uint32_t i0, uint32_t i1,uint32_t i2);
+
+void _create_cmd_buff (VkvgContext ctx);
+void _init_cmd_buff (VkvgContext ctx);
+void _flush_cmd_buff (VkvgContext ctx);
+void _record_draw_cmd (VkvgContext ctx);
+void _submit_wait_and_reset_cmd(VkvgContext ctx);
+void _submit_ctx_cmd (VkvgContext ctx);
+void _wait_and_reset_ctx_cmd(VkvgContext ctx);
+
+void _finish_path (VkvgContext ctx);
+void _clear_path (VkvgContext ctx);
+bool _path_is_closed (VkvgContext ctx, uint32_t ptrPath);
+uint32_t _get_last_point_of_closed_path (VkvgContext ctx, uint32_t ptrPath);
+
+void _init_descriptor_sets (VkvgContext ctx);
+void _update_source_descriptor_set (VkvgContext ctx);
+void _update_font_descriptor_set (VkvgContext ctx);
+
+static inline float vec2_zcross (vec2 v1, vec2 v2){
+ return v1.x*v2.y-v1.y*v2.x;
+}
+static inline float ecp_zcross (ear_clip_point* p0, ear_clip_point* p1, ear_clip_point* p2){
+ return vec2_zcross (vec2_sub (p1->pos, p0->pos), vec2_sub (p2->pos, p0->pos));
+}
+#endif
--- /dev/null
+#include "vkvg_device_internal.h"
+#include "vkvg_context_internal.h"
+
+void _create_pipeline_cache (VkvgDevice dev);
+void _setupRenderPass (VkvgDevice dev);
+void _setupPipelines (VkvgDevice dev);
+void _createDescriptorSetLayout (VkvgDevice dev);
+void _createDescriptorSet (VkvgDevice dev);
+
+VkvgDevice vkvg_device_create(VkDevice vkdev, VkQueue queue, uint32_t qFam, VkPhysicalDeviceMemoryProperties memprops)
+{
+ VkvgDevice dev = (vkvg_device*)malloc(sizeof(vkvg_device));
+
+ dev->hdpi = 96;
+ dev->vdpi = 96;
+
+ dev->vkDev = vkdev;
+ dev->phyMemProps = memprops;
+
+ dev->queue = queue;
+ dev->cmdPool = vkh_cmd_pool_create (dev->vkDev, qFam, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT);
+
+ _create_pipeline_cache(dev);
+
+ _init_fonts_cache(dev);
+
+ _setupRenderPass (dev);
+
+ _createDescriptorSetLayout (dev);
+ _createDescriptorSet (dev);
+
+ _setupPipelines (dev);
+ return dev;
+}
+
+void vkvg_device_destroy(VkvgDevice dev)
+{
+ vkDestroyDescriptorSetLayout (dev->vkDev, dev->dslFont,NULL);
+ vkDestroyDescriptorSetLayout (dev->vkDev, dev->dslSrc, NULL);
+ vkDestroyDescriptorPool (dev->vkDev, dev->descriptorPool,NULL);
+
+ vkDestroyPipeline (dev->vkDev, dev->pipeline, NULL);
+ vkDestroyPipeline (dev->vkDev, dev->pipelineClipping, NULL);
+ vkDestroyPipeline (dev->vkDev, dev->pipeline_OP_SUB, NULL);
+ vkDestroyPipeline (dev->vkDev, dev->pipelineWired, NULL);
+ vkDestroyPipeline (dev->vkDev, dev->pipelineLineList, NULL);
+
+ vkDestroyPipelineLayout (dev->vkDev, dev->pipelineLayout, NULL);
+ vkDestroyPipelineCache (dev->vkDev, dev->pipelineCache, NULL);
+ vkDestroyRenderPass (dev->vkDev, dev->renderPass, NULL);
+ vkDestroyCommandPool (dev->vkDev, dev->cmdPool, NULL);
+ _destroy_font_cache(dev);
+ free(dev);
+}
+void _flush_all_contexes (VkvgDevice dev){
+ VkvgContext next = dev->lastCtx;
+ while (next != NULL){
+ _flush_cmd_buff(next);
+ next = next->pPrev;
+ }
+}
+void _init_all_contexes (VkvgDevice dev){
+ VkvgContext next = dev->lastCtx;
+ while (next != NULL){
+ _init_cmd_buff (next);
+ next = next->pPrev;
+ }
+}
+
+void _create_pipeline_cache(VkvgDevice dev){
+ VkPipelineCacheCreateInfo pipelineCacheCreateInfo = {.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO};
+ VK_CHECK_RESULT(vkCreatePipelineCache(dev->vkDev, &pipelineCacheCreateInfo, NULL, &dev->pipelineCache));
+}
+void _setupRenderPass(VkvgDevice dev)
+{
+ VkAttachmentDescription attColor = {
+ .format = FB_COLOR_FORMAT,
+ .samples = VKVG_SAMPLES,
+ .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
+ .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
+ .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
+ .stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE,
+ .finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
+ VkAttachmentDescription attColorResolve = {
+ .format = FB_COLOR_FORMAT,
+ .samples = VK_SAMPLE_COUNT_1_BIT,
+ .loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+ .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
+ .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+ .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
+ .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+ .finalLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL };
+ VkAttachmentDescription attDS = {
+ .format = VK_FORMAT_S8_UINT,
+ .samples = VKVG_SAMPLES,
+ .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
+ .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
+ .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
+ .stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE,
+ .finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
+/* VkAttachmentDescription attDSResolve = {
+ .format = VK_FORMAT_S8_UINT,
+ .samples = VK_SAMPLE_COUNT_1_BIT,
+ .loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+ .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
+ .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
+ .stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE,
+ .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+ .finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };*/
+ VkAttachmentDescription attachments[] = {attColor,attColorResolve,attDS};
+ VkAttachmentReference colorRef = {
+ .attachment = 0,
+ .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
+ VkAttachmentReference colorResolveRef = {
+ .attachment = 1,
+ .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
+ VkAttachmentReference dsRef = {
+ .attachment = 2,
+ .layout = VK_IMAGE_LAYOUT_GENERAL };
+ /*VkAttachmentReference dsResolveRef = {
+ .attachment = 3,
+ .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };*/
+ VkSubpassDescription subpassDescription = { .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
+ .colorAttachmentCount = 1,
+ .pColorAttachments = &colorRef,
+ .pResolveAttachments = &colorResolveRef,
+ .pDepthStencilAttachment = &dsRef};
+ VkSubpassDependency dep0 = {
+ .srcSubpass = VK_SUBPASS_EXTERNAL,
+ .dstSubpass = 0,
+ .srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
+ .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+ .srcAccessMask = VK_ACCESS_MEMORY_READ_BIT,
+ .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT };
+ VkSubpassDependency dep1 = {
+ .srcSubpass = 0,
+ .dstSubpass = VK_SUBPASS_EXTERNAL,
+ .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+ .dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
+ .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
+ .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT };
+
+ VkSubpassDependency dependencies[] = {dep0,dep1};
+ VkRenderPassCreateInfo renderPassInfo = { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
+ .attachmentCount = 3,
+ .pAttachments = attachments,
+ .subpassCount = 1,
+ .pSubpasses = &subpassDescription,
+ .dependencyCount = 2,
+ .pDependencies = dependencies };
+
+ VK_CHECK_RESULT(vkCreateRenderPass(dev->vkDev, &renderPassInfo, NULL, &dev->renderPass));
+}
+
+void _setupPipelines(VkvgDevice dev)
+{
+ VkGraphicsPipelineCreateInfo pipelineCreateInfo = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
+ .renderPass = dev->renderPass };
+
+ VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
+ .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST };
+
+ VkPipelineRasterizationStateCreateInfo rasterizationState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
+ .polygonMode = VK_POLYGON_MODE_FILL,
+ .cullMode = VK_CULL_MODE_NONE,
+ .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
+ .depthClampEnable = VK_FALSE,
+ .rasterizerDiscardEnable = VK_FALSE,
+ .depthBiasEnable = VK_FALSE,
+ .lineWidth = 1.0f };
+
+ VkPipelineColorBlendAttachmentState blendAttachmentState =
+ { .colorWriteMask = 0x0, .blendEnable = VK_TRUE,
+ .srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
+ .dstColorBlendFactor= VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
+ .colorBlendOp = VK_BLEND_OP_ADD,
+ .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
+ .dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
+ .alphaBlendOp = VK_BLEND_OP_ADD,
+ };
+
+ VkPipelineColorBlendStateCreateInfo colorBlendState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
+ .attachmentCount = 1,
+ .pAttachments = &blendAttachmentState };
+
+ /*failOp,passOp,depthFailOp,compareOp, compareMask, writeMask, reference;*/
+ VkStencilOpState clipingOpState = {VK_STENCIL_OP_KEEP,VK_STENCIL_OP_INCREMENT_AND_CLAMP,VK_STENCIL_OP_KEEP,VK_COMPARE_OP_EQUAL,0x0,0xf,0};
+ VkStencilOpState stencilOpState = {VK_STENCIL_OP_KEEP,VK_STENCIL_OP_REPLACE,VK_STENCIL_OP_KEEP,VK_COMPARE_OP_EQUAL,0xf,0x0,0};
+
+ VkPipelineDepthStencilStateCreateInfo dsStateCreateInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
+ .depthTestEnable = VK_FALSE,
+ .depthWriteEnable = VK_FALSE,
+ .depthCompareOp = VK_COMPARE_OP_ALWAYS,
+ .stencilTestEnable = VK_TRUE,
+ .front = clipingOpState,
+ .back = clipingOpState };
+
+ VkDynamicState dynamicStateEnables[] = {
+ VK_DYNAMIC_STATE_VIEWPORT,
+ VK_DYNAMIC_STATE_SCISSOR,
+ VK_DYNAMIC_STATE_STENCIL_REFERENCE,
+ };
+ VkPipelineDynamicStateCreateInfo dynamicState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
+ .dynamicStateCount = 3,
+ .pDynamicStates = dynamicStateEnables };
+
+ VkPipelineViewportStateCreateInfo viewportState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
+ .viewportCount = 1, .scissorCount = 1 };
+
+ VkPipelineMultisampleStateCreateInfo multisampleState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
+ .rasterizationSamples = VKVG_SAMPLES };
+ if (VKVG_SAMPLES != VK_SAMPLE_COUNT_1_BIT){
+ multisampleState.sampleShadingEnable = VK_TRUE;
+ multisampleState.minSampleShading = 0.25f;
+ multisampleState.alphaToCoverageEnable = VK_FALSE;
+ multisampleState.alphaToOneEnable = VK_FALSE;
+ }
+ VkVertexInputBindingDescription vertexInputBinding = { .binding = 0,
+ .stride = sizeof(Vertex),
+ .inputRate = VK_VERTEX_INPUT_RATE_VERTEX };
+
+ VkVertexInputAttributeDescription vertexInputAttributs[2] = {
+ {0, 0, VK_FORMAT_R32G32_SFLOAT, 0},
+ {1, 0, VK_FORMAT_R32G32B32_SFLOAT, sizeof(vec2)}
+ };
+
+ VkPipelineVertexInputStateCreateInfo vertexInputState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
+ .vertexBindingDescriptionCount = 1,
+ .pVertexBindingDescriptions = &vertexInputBinding,
+ .vertexAttributeDescriptionCount = 2,
+ .pVertexAttributeDescriptions = vertexInputAttributs };
+
+ VkPipelineShaderStageCreateInfo vertStage = { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+ .stage = VK_SHADER_STAGE_VERTEX_BIT,
+ .module = vkh_load_module(dev->vkDev, "shaders/triangle.vert.spv"),
+ .pName = "main",
+ };
+ VkPipelineShaderStageCreateInfo fragStage = { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+ .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
+ .module = vkh_load_module(dev->vkDev, "shaders/triangle.frag.spv"),
+ .pName = "main",
+ };
+
+ // Use specialization constants to pass number of samples to the shader (used for MSAA resolve)
+ VkSpecializationMapEntry specializationEntry = {
+ .constantID = 0,
+ .offset = 0,
+ .size = sizeof(uint32_t)};
+ uint32_t specializationData = VKVG_SAMPLES;
+ VkSpecializationInfo specializationInfo = {
+ .mapEntryCount = 1,
+ .pMapEntries = &specializationEntry,
+ .dataSize = sizeof(specializationData),
+ .pData = &specializationData};
+
+ VkPipelineShaderStageCreateInfo shaderStages[] = {vertStage,fragStage};
+
+ pipelineCreateInfo.stageCount = 2;
+ pipelineCreateInfo.pStages = shaderStages;
+ pipelineCreateInfo.pVertexInputState = &vertexInputState;
+ pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState;
+ pipelineCreateInfo.pViewportState = &viewportState;
+ pipelineCreateInfo.pRasterizationState = &rasterizationState;
+ pipelineCreateInfo.pMultisampleState = &multisampleState;
+ pipelineCreateInfo.pColorBlendState = &colorBlendState;
+ pipelineCreateInfo.pDepthStencilState = &dsStateCreateInfo;
+ pipelineCreateInfo.pDynamicState = &dynamicState;
+ pipelineCreateInfo.layout = dev->pipelineLayout;
+
+ VK_CHECK_RESULT(vkCreateGraphicsPipelines(dev->vkDev, dev->pipelineCache, 1, &pipelineCreateInfo, NULL, &dev->pipelineClipping));
+
+ //dsStateCreateInfo.back.writeMask = dsStateCreateInfo.front.writeMask = 0;
+ dsStateCreateInfo.back = dsStateCreateInfo.front = stencilOpState;
+ blendAttachmentState.colorWriteMask=0xf;
+ VK_CHECK_RESULT(vkCreateGraphicsPipelines(dev->vkDev, dev->pipelineCache, 1, &pipelineCreateInfo, NULL, &dev->pipeline));
+
+ blendAttachmentState.alphaBlendOp = blendAttachmentState.colorBlendOp = VK_BLEND_OP_SUBTRACT;
+ VK_CHECK_RESULT(vkCreateGraphicsPipelines(dev->vkDev, dev->pipelineCache, 1, &pipelineCreateInfo, NULL, &dev->pipeline_OP_SUB));
+
+ inputAssemblyState.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+ rasterizationState.polygonMode = VK_POLYGON_MODE_LINE;
+ VK_CHECK_RESULT(vkCreateGraphicsPipelines(dev->vkDev, dev->pipelineCache, 1, &pipelineCreateInfo, NULL, &dev->pipelineWired));
+
+ rasterizationState.polygonMode = VK_POLYGON_MODE_FILL;
+ inputAssemblyState.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
+ VK_CHECK_RESULT(vkCreateGraphicsPipelines(dev->vkDev, dev->pipelineCache, 1, &pipelineCreateInfo, NULL, &dev->pipelineLineList));
+
+ vkDestroyShaderModule(dev->vkDev, shaderStages[0].module, NULL);
+ vkDestroyShaderModule(dev->vkDev, shaderStages[1].module, NULL);
+}
+
+void _createDescriptorSetLayout (VkvgDevice dev) {
+
+ VkDescriptorSetLayoutBinding dsLayoutBinding =
+ {0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1,VK_SHADER_STAGE_FRAGMENT_BIT};
+ VkDescriptorSetLayoutCreateInfo dsLayoutCreateInfo = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
+ .bindingCount = 1,
+ .pBindings = &dsLayoutBinding };
+ VK_CHECK_RESULT(vkCreateDescriptorSetLayout(dev->vkDev, &dsLayoutCreateInfo, NULL, &dev->dslFont));
+ VK_CHECK_RESULT(vkCreateDescriptorSetLayout(dev->vkDev, &dsLayoutCreateInfo, NULL, &dev->dslSrc));
+
+ VkPushConstantRange pushConstantRange[] = {
+ {VK_SHADER_STAGE_VERTEX_BIT,0,sizeof(push_constants)},
+ //{VK_SHADER_STAGE_FRAGMENT_BIT,0,sizeof(push_constants)}
+ };
+ VkDescriptorSetLayout dsls[] = {dev->dslFont,dev->dslSrc};
+
+ VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
+ .pushConstantRangeCount = 1,
+ .pPushConstantRanges = &pushConstantRange,
+ .setLayoutCount = 2,
+ .pSetLayouts = dsls };
+ VK_CHECK_RESULT(vkCreatePipelineLayout(dev->vkDev, &pipelineLayoutCreateInfo, NULL, &dev->pipelineLayout));
+}
+
+void _createDescriptorSet (VkvgDevice dev) {
+ VkDescriptorPoolSize descriptorPoolSize = {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 4 };
+
+ VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
+ .maxSets = 4,
+ .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
+ .poolSizeCount = 1,
+ .pPoolSizes = &descriptorPoolSize };
+ VK_CHECK_RESULT(vkCreateDescriptorPool(dev->vkDev, &descriptorPoolCreateInfo, NULL, &dev->descriptorPool));
+}
--- /dev/null
+#ifndef VKVG_DEVICE_INTERNAL_H
+#define VKVG_DEVICE_INTERNAL_H
+
+#include "vkvg_internal.h"
+#include "vkvg.h"
+#include "vkvg_fonts.h"
+
+typedef struct _vkvg_device_t{
+ VkDevice vkDev;
+ VkPhysicalDeviceMemoryProperties phyMemProps;
+ VkRenderPass renderPass;
+
+ VkQueue queue;
+ VkCommandPool cmdPool;
+
+ VkPipeline pipeline;
+ VkPipeline pipelineClipping;
+ VkPipeline pipeline_OP_SUB;
+ VkPipeline pipelineWired;
+ VkPipeline pipelineLineList;
+
+ VkPipelineCache pipelineCache;
+ VkPipelineLayout pipelineLayout;
+ VkDescriptorPool descriptorPool;
+ VkDescriptorSetLayout dslFont;
+ VkDescriptorSetLayout dslSrc;
+
+ int hdpi,
+ vdpi;
+
+ _font_cache_t* fontCache;
+ VkvgContext lastCtx; //double linked list last elmt
+}vkvg_device;
+
+void _flush_all_contexes (VkvgDevice dev);
+void _init_all_contexes (VkvgDevice dev);
+#endif
--- /dev/null
+#include "vkvg_fonts.h"
+#include "vkvg_context_internal.h"
+#include "vkvg_surface_internal.h"
+#include "vkvg_device_internal.h"
+
+#include "vkh_buffer.h"
+
+int defaultFontCharSize = 12<<6;
+
+void _init_fonts_cache (VkvgDevice dev){
+ _font_cache_t* cache = (_font_cache_t*)malloc(sizeof(_font_cache_t));
+ memset (cache, 0, sizeof(_font_cache_t));
+
+ cache->config = FcInitLoadConfigAndFonts();
+
+ assert(!FT_Init_FreeType(&cache->library));
+
+ cache->cacheTexLength = FONT_CACHE_INIT_LAYERS;
+ cache->cacheTex = vkh_tex2d_array_create (dev, VK_FORMAT_R8_UNORM, FONT_PAGE_SIZE, FONT_PAGE_SIZE,
+ cache->cacheTexLength ,VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
+ VK_IMAGE_USAGE_SAMPLED_BIT|VK_IMAGE_USAGE_TRANSFER_DST_BIT|VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
+ vkh_image_create_descriptor (cache->cacheTex, VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_ASPECT_COLOR_BIT,
+ VK_FILTER_NEAREST, VK_FILTER_NEAREST, VK_SAMPLER_MIPMAP_MODE_NEAREST, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER);
+
+ cache->uploadFence = vkh_fence_create_signaled(dev->vkDev);
+
+ uint32_t buffLength = FONT_PAGE_SIZE*FONT_PAGE_SIZE*sizeof(uint8_t);
+ cache->buff = vkh_buffer_create(dev,VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT|VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+ buffLength);
+ vkh_buffer_map(cache->buff);
+
+ cache->cmd = vkh_cmd_buff_create(dev->vkDev,dev->cmdPool,VK_COMMAND_BUFFER_LEVEL_PRIMARY);
+
+ cache->hostBuff = (uint8_t*)malloc(FONT_PAGE_SIZE*FONT_PAGE_SIZE*sizeof(uint8_t));
+ cache->pensY = (int*)calloc(cache->cacheTexLength, sizeof(int));
+
+ dev->fontCache = cache;
+}
+void _increase_font_tex_array (VkvgDevice dev){
+ _font_cache_t* cache = dev->fontCache;
+
+ vkWaitForFences (dev->vkDev, 1, &cache->uploadFence, VK_TRUE, UINT64_MAX);
+ vkResetCommandBuffer(cache->cmd, NULL);
+ vkResetFences (dev->vkDev, 1, &cache->uploadFence);
+
+ uint8_t newSize = cache->cacheTexLength + FONT_CACHE_INIT_LAYERS;
+ VkhImage newImg = vkh_tex2d_array_create (dev, VK_FORMAT_R8_UNORM, FONT_PAGE_SIZE, FONT_PAGE_SIZE,
+ newSize ,VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
+ VK_IMAGE_USAGE_SAMPLED_BIT|VK_IMAGE_USAGE_TRANSFER_DST_BIT|VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
+ vkh_image_create_descriptor (newImg, VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_ASPECT_COLOR_BIT,
+ VK_FILTER_NEAREST, VK_FILTER_NEAREST, VK_SAMPLER_MIPMAP_MODE_NEAREST,VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER);
+
+ VkImageSubresourceRange subresNew = {VK_IMAGE_ASPECT_COLOR_BIT,0,1,0,newSize};
+ VkImageSubresourceRange subres = {VK_IMAGE_ASPECT_COLOR_BIT,0,1,0,cache->cacheTexLength};
+
+ vkh_cmd_begin (cache->cmd,VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
+
+ vkh_image_set_layout_subres(cache->cmd, newImg, subresNew, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+ vkh_image_set_layout_subres(cache->cmd, cache->cacheTex, subres, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+
+ VkImageCopy cregion = { .srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, cache->cacheTexLength},
+ .dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, cache->cacheTexLength},
+ .extent = {FONT_PAGE_SIZE,FONT_PAGE_SIZE,1}};
+
+ vkCmdCopyImage (cache->cmd, cache->cacheTex->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ newImg->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &cregion);
+
+ vkh_image_set_layout_subres(cache->cmd, newImg, subresNew, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
+
+ VK_CHECK_RESULT(vkEndCommandBuffer(cache->cmd));
+
+ vkh_cmd_submit (dev->queue, &cache->cmd, cache->uploadFence);
+ vkWaitForFences (dev->vkDev, 1, &cache->uploadFence, VK_TRUE, UINT64_MAX);
+
+ _flush_all_contexes (dev);
+
+ cache->pensY = (int*)realloc(cache->pensY, newSize * sizeof(int));
+ memset (cache->pensY + cache->cacheTexLength * sizeof(int),0,FONT_CACHE_INIT_LAYERS*sizeof(int));
+
+ vkh_image_destroy (cache->cacheTex);
+ cache->cacheTexLength = newSize;
+ cache->cacheTex = newImg;
+
+ VkvgContext next = dev->lastCtx;
+ while (next != NULL){
+ _update_source_descriptor_set (next);
+ next = next->pPrev;
+ }
+
+ _init_all_contexes (dev);
+}
+void _init_next_line_in_tex_cache (VkvgDevice dev, _vkvg_font_t* f){
+ _font_cache_t* cache = dev->fontCache;
+ int i;
+ for (i = 0; i < cache->cacheTexLength; ++i) {
+ if (cache->pensY[i] + f->curLine.height >= FONT_PAGE_SIZE)
+ continue;
+ f->curLine.pageIdx = i;
+ f->curLine.penX = 0;
+ f->curLine.penY = cache->pensY[i];
+ cache->pensY[i] += f->curLine.height;
+ return;
+ }
+ _flush_chars_to_tex (dev, f);
+ _increase_font_tex_array (dev);
+ _init_next_line_in_tex_cache(dev, f);
+}
+void _destroy_font_cache (VkvgDevice dev){
+ _font_cache_t* cache = (_font_cache_t*)dev->fontCache;
+
+ //FcFini();
+
+ free (cache->hostBuff);
+
+ for (int i = 0; i < cache->fontsCount; ++i) {
+ _vkvg_font_t f = cache->fonts[i];
+
+ for (int g = 0; g < f.face->num_glyphs; ++g) {
+ if (f.charLookup[g]!=NULL)
+ free(f.charLookup[g]);
+ }
+
+ FT_Done_Face (f.face);
+ hb_font_destroy (f.hb_font);
+
+ free(f.charLookup);
+ free(f.fontFile);
+ }
+
+ free(cache->fonts);
+ free(cache->pensY);
+
+ vkh_buffer_unmap (cache->buff);
+ vkh_buffer_destroy (cache->buff);
+ vkh_image_destroy (cache->cacheTex);
+ //vkFreeCommandBuffers(dev->vkDev,dev->cmdPool, 1, &cache->cmd);
+ vkDestroyFence (dev->vkDev,cache->uploadFence,NULL);
+
+ free (dev->fontCache);
+
+}
+
+void _dump_glyphs (FT_Face face){
+ FT_GlyphSlot slot;
+ char gname[256];
+
+ for (int i = 0; i < face->num_glyphs; ++i) {
+ assert(!FT_Load_Glyph(face,i,FT_LOAD_RENDER));
+ slot = face->glyph;
+
+ FT_Get_Glyph_Name(face,i,gname,256);
+
+
+ printf("glyph: %s (%d,%d;%d), max advance:%d\n", gname,
+ slot->bitmap.width, slot->bitmap.rows, slot->bitmap.pitch,
+ face->size->metrics.max_advance/64);
+ }
+}
+
+void _flush_chars_to_tex (VkvgDevice dev, _vkvg_font_t* f) {
+ _font_cache_t* cache = dev->fontCache;
+ if (cache->stagingX == 0)
+ return;
+
+ vkWaitForFences (dev->vkDev,1,&cache->uploadFence,VK_TRUE,UINT64_MAX);
+ vkResetCommandBuffer(cache->cmd,NULL);
+ vkResetFences (dev->vkDev,1,&cache->uploadFence);
+
+ memcpy(cache->buff->mapped, cache->hostBuff, f->curLine.height * FONT_PAGE_SIZE);
+
+ vkh_cmd_begin (cache->cmd,VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
+
+ VkImageSubresourceRange subres = {VK_IMAGE_ASPECT_COLOR_BIT,0,1,f->curLine.pageIdx,1};
+ vkh_image_set_layout_subres(cache->cmd, cache->cacheTex, subres, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
+
+ VkBufferImageCopy bufferCopyRegion = { .imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT,0,f->curLine.pageIdx,1},
+ .bufferRowLength = FONT_PAGE_SIZE,
+ .bufferImageHeight = f->curLine.height,
+ .imageOffset = {f->curLine.penX,f->curLine.penY,0},
+ .imageExtent = {FONT_PAGE_SIZE-f->curLine.penX,f->curLine.height,1}};
+
+ vkCmdCopyBufferToImage(cache->cmd, cache->buff->buffer, cache->cacheTex->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferCopyRegion);
+
+ vkh_image_set_layout_subres(cache->cmd, cache->cacheTex, subres, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
+
+ VK_CHECK_RESULT(vkEndCommandBuffer(cache->cmd));
+
+ vkh_cmd_submit(dev->queue,&cache->cmd,cache->uploadFence);
+
+ f->curLine.penX += cache->stagingX;
+ cache->stagingX = 0;
+ memset(cache->hostBuff, 0, FONT_PAGE_SIZE * FONT_PAGE_SIZE);
+}
+
+_char_ref* _prepare_char (VkvgDevice dev, _vkvg_font_t* f, FT_UInt gindex){
+ assert(!FT_Load_Glyph(f->face, gindex, FT_LOAD_RENDER));
+
+ FT_GlyphSlot slot = f->face->glyph;
+ FT_Bitmap bmp = slot->bitmap;
+ uint8_t* data = dev->fontCache->hostBuff;
+
+ if (dev->fontCache->stagingX + f->curLine.penX + bmp.width > FONT_PAGE_SIZE){
+ _flush_chars_to_tex (dev, f);
+ _init_next_line_in_tex_cache (dev, f);
+ }
+
+ int penX = dev->fontCache->stagingX;
+ for(int y=0; y<bmp.rows; y++) {
+ for(int x=0; x<bmp.width; x++)
+ data[ penX + x + y * FONT_PAGE_SIZE ] =
+ bmp.buffer[x + y * bmp.width];
+ }
+
+ _char_ref* cr = (_char_ref*)malloc(sizeof(_char_ref));
+ vec4 uvBounds = {
+ (float)(penX + f->curLine.penX) / (float)FONT_PAGE_SIZE,
+ (float)f->curLine.penY / (float)FONT_PAGE_SIZE,
+ bmp.width,
+ bmp.rows};
+ cr->bounds = uvBounds;
+ cr->pageIdx = f->curLine.pageIdx;
+ cr->bmpDiff.x = slot->bitmap_left;
+ cr->bmpDiff.y = slot->bitmap_top;
+
+ f->charLookup[gindex] = cr;
+ dev->fontCache->stagingX += bmp.width;
+ return cr;
+}
+
+void _set_font_size (VkvgContext ctx, uint32_t size){
+ ctx->selectedFont.charSize = size << 6;
+ ctx->currentFont = NULL;
+}
+void _select_font_face (VkvgContext ctx, const char* name){
+ _font_cache_t* cache = (_font_cache_t*)ctx->pSurf->dev->fontCache;
+
+ char* fontFile;
+
+ //make pattern from font name
+ FcPattern* pat = FcNameParse((const FcChar8*)name);
+ FcConfigSubstitute(cache->config, pat, FcMatchPattern);
+ FcDefaultSubstitute(pat);
+ // find the font
+ FcResult result;
+ FcPattern* font = FcFontMatch(cache->config, pat, &result);
+ if (font)
+ {
+ if (FcPatternGetString(font, FC_FILE, 0, (FcChar8 **)&fontFile) == FcResultMatch){
+ memset (ctx->selectedFont.fontFile, 0, FONT_FILE_NAME_MAX_SIZE);
+ strcpy(ctx->selectedFont.fontFile, fontFile);
+ }
+ }
+ FcPatternDestroy(pat);
+ FcPatternDestroy(font);
+
+ ctx->currentFont = NULL;
+}
+
+_vkvg_font_t* _tryFindVkvgFont (VkvgContext ctx){
+ _font_cache_t* cache = (_font_cache_t*)ctx->pSurf->dev->fontCache;
+ for (int i = 0; i < cache->fontsCount; ++i) {
+ if (strcmp (cache->fonts[i].fontFile, ctx->selectedFont.fontFile)==0 && cache->fonts[i].charSize == ctx->selectedFont.charSize)
+ return &cache->fonts[i];
+ }
+ return NULL;
+}
+
+void _show_text (VkvgContext ctx, const char* text){
+ VkvgDevice dev = ctx->pSurf->dev;
+
+ if (ctx->currentFont == NULL){
+ ctx->currentFont = _tryFindVkvgFont (ctx);
+ if (ctx->currentFont == NULL){
+ _font_cache_t* cache = (_font_cache_t*)dev->fontCache;
+ //create new font in cache
+ cache->fontsCount++;
+ if (cache->fontsCount == 1)
+ cache->fonts = (_vkvg_font_t*) malloc (cache->fontsCount * sizeof(_vkvg_font_t));
+ else
+ cache->fonts = (_vkvg_font_t*) realloc (cache->fonts, cache->fontsCount * sizeof(_vkvg_font_t));
+
+ _vkvg_font_t nf = ctx->selectedFont;
+ if (nf.charSize == 0)
+ nf.charSize = defaultFontCharSize;
+
+ nf.fontFile = (char*)calloc(strlen(ctx->selectedFont.fontFile),sizeof(char));
+ strcpy (nf.fontFile, ctx->selectedFont.fontFile);
+
+ assert(!FT_New_Face(cache->library, nf.fontFile, 0, &nf.face));
+ assert(!FT_Set_Char_Size(nf.face, 0, nf.charSize, dev->hdpi, dev->vdpi ));
+ nf.hb_font = hb_ft_font_create(nf.face, NULL);
+ nf.charLookup = (_char_ref*)calloc(nf.face->num_glyphs,sizeof(_char_ref));
+
+ //nf.curLine.height = (nf.face->bbox.xMax - nf.face->bbox.xMin) >> 6;
+ if (FT_IS_SCALABLE(nf.face))
+ nf.curLine.height = nf.face->size->metrics.height >> 6;
+ else
+ nf.curLine.height = nf.face->height >> 6;
+
+ _init_next_line_in_tex_cache (dev, &nf);
+ cache->fonts[cache->fontsCount-1] = nf;
+ ctx->currentFont = &cache->fonts[cache->fontsCount-1];
+ }
+ }
+
+ hb_buffer_t *buf = hb_buffer_create();
+
+ const char *lng = "fr";
+ hb_script_t script = HB_SCRIPT_LATIN;
+ script = hb_script_from_string(text,strlen(text));
+ hb_direction_t dir = hb_script_get_horizontal_direction(script);
+ //dir = HB_DIRECTION_TTB;
+ hb_buffer_set_direction (buf, dir);
+ hb_buffer_set_script (buf, script);
+ hb_buffer_set_language (buf, hb_language_from_string(lng,strlen(lng)));
+ hb_buffer_add_utf8 (buf, text, strlen(text), 0, strlen(text));
+
+ _vkvg_font_t* f = ctx->currentFont;
+ hb_shape (f->hb_font, buf, NULL, 0);
+
+ unsigned int glyph_count;
+ hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos (buf, &glyph_count);
+ hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions (buf, &glyph_count);
+
+ unsigned int string_width_in_pixels = 0;
+ for (int i=0; i < glyph_count; ++i)
+ string_width_in_pixels += glyph_pos[i].x_advance >> 6;
+
+
+ Vertex v = {};
+ vec2 pen = ctx->curPos;
+
+ for (int i=0; i < glyph_count; ++i) {
+ _char_ref* cr = f->charLookup[glyph_info[i].codepoint];
+
+ if (cr==NULL)
+ cr = _prepare_char(dev,f,glyph_info[i].codepoint);
+
+ //continue;
+ if (cr!=NULL){
+ float uvWidth = cr->bounds.width / (float)FONT_PAGE_SIZE;
+ float uvHeight = cr->bounds.height / (float)FONT_PAGE_SIZE;
+ vec2 p0 = {pen.x + cr->bmpDiff.x + (glyph_pos[i].x_offset >> 6),
+ pen.y - cr->bmpDiff.y + (glyph_pos[i].y_offset >> 6)};
+ v.pos = p0;
+
+ uint32_t firstIdx = ctx->vertCount;
+
+ v.uv.x = cr->bounds.x;
+ v.uv.y = cr->bounds.y;
+ v.uv.z = cr->pageIdx;
+ _add_vertex(ctx,v);
+
+ v.pos.y += cr->bounds.height;
+ v.uv.y += uvHeight;
+ _add_vertex(ctx,v);
+
+ v.pos.x += cr->bounds.width;
+ v.pos.y = p0.y;
+ v.uv.x += uvWidth;
+ v.uv.y = cr->bounds.y;
+ _add_vertex(ctx,v);
+
+ v.pos.y += cr->bounds.height;
+ v.uv.y += uvHeight;
+ _add_vertex(ctx,v);
+
+ _add_tri_indices_for_rect (ctx, firstIdx);
+ }
+
+ pen.x += (glyph_pos[i].x_advance >> 6);
+ pen.y -= (glyph_pos[i].y_advance >> 6);
+ }
+ _flush_chars_to_tex(dev,f);
+ //_show_texture(ctx); return;
+}
+
+
+void _show_texture (vkvg_context* ctx){
+ Vertex vs[] = {
+ {{0,0}, {0,0,1}},
+ {{0,FONT_PAGE_SIZE}, {0,1,1}},
+ {{FONT_PAGE_SIZE,0}, {1,0,1}},
+ {{FONT_PAGE_SIZE,FONT_PAGE_SIZE}, {1,1,1}}
+ };
+
+ _add_vertex(ctx,vs[0]);
+ _add_vertex(ctx,vs[1]);
+ _add_vertex(ctx,vs[2]);
+ _add_vertex(ctx,vs[3]);
+
+ _add_tri_indices_for_rect (ctx, 0);
+}
+/*void testfonts(){
+ FT_Library library;
+ FT_Face face;
+ FT_GlyphSlot slot;
+
+ assert(!FT_Init_FreeType(&library));
+ assert(!FT_New_Face(library, "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", 0, &face));
+ assert(!FT_Set_Char_Size(face, 0, ptSize, device_hdpi, device_vdpi ));
+
+ //_build_face_tex(face);
+
+ hb_font_t *hb_font = hb_ft_font_create(face, NULL);
+ hb_buffer_t *buf = hb_buffer_create();
+
+ const char *text = "Ленивый рыжий кот";
+ const char *lng = "en";
+ //"كسول الزنجبيل القط","懶惰的姜貓",
+
+
+ hb_buffer_set_direction (buf, HB_DIRECTION_LTR);
+ hb_buffer_set_script (buf, HB_SCRIPT_LATIN);
+ hb_buffer_set_language (buf, hb_language_from_string(lng,strlen(lng)));
+ hb_buffer_add_utf8 (buf, text, strlen(text), 0, strlen(text));
+
+ hb_unicode_funcs_t * unifc = hb_unicode_funcs_get_default();
+ hb_script_t sc = hb_buffer_get_script(buf);
+
+ sc = hb_unicode_script(unifc,0x0260);
+
+ FT_CharMap* cm = face->charmap;
+
+ //hb_script_to_iso15924_tag()
+
+
+ FT_Done_Face ( face );
+ FT_Done_FreeType( library );
+}*/
+
+
+//void main(){
+// testfonts();
+//}
+
--- /dev/null
+#ifndef VKVG_FONTS_H
+#define VKVG_FONTS_H
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#include <hb.h>
+#include <hb-ft.h>
+
+#include <fontconfig/fontconfig.h>
+
+#define FONT_PAGE_SIZE 2048
+#define FONT_CACHE_INIT_LAYERS 2
+#define FONT_FILE_NAME_MAX_SIZE 256
+
+#include "vkvg_internal.h"
+#include "vkvg.h"
+#include "vkh_image.h"
+
+
+///texture coords of one char
+typedef struct {
+ vec4 bounds;
+ vec2i16 bmpDiff;
+ uint8_t pageIdx;
+}_char_ref;
+//chars texture atlas reference
+typedef struct {
+ uint8_t pageIdx;
+ int penX;
+ int penY;
+ int height;
+}_tex_ref_t;
+
+typedef struct {
+ char* fontFile;
+ FT_F26Dot6 charSize;
+ hb_font_t* hb_font;
+ FT_Face face;
+ _char_ref** charLookup;
+
+ _tex_ref_t curLine; //tex coord where to add new char bmp's
+}_vkvg_font_t;
+
+typedef struct {
+ FT_Library library;
+ FcConfig* config;
+
+ int stagingX; //x pen in host buffer
+ uint8_t* hostBuff; //host mem where bitmaps are first loaded
+
+ VkCommandBuffer cmd; //upload cmd buff
+ VkhBuffer buff; //stagin buffer
+ VkhImage cacheTex; //tex 2d array
+ uint8_t cacheTexLength; //tex array length
+ int* pensY; //y pen pos in each texture of array
+ VkFence uploadFence;
+
+ _vkvg_font_t* fonts;
+ uint8_t fontsCount;
+}_font_cache_t;
+
+void _init_fonts_cache (VkvgDevice dev);
+void _destroy_font_cache (VkvgDevice dev);
+void _select_font_face (VkvgContext ctx, const char* name);
+void _set_font_size (VkvgContext ctx, uint32_t size);
+void _show_text (VkvgContext ctx, const char* text);
+#endif
--- /dev/null
+#ifndef VKVG_INTERNAL_H
+#define VKVG_INTERNAL_H
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#endif
--- /dev/null
+#include "vkvg_surface_internal.h"
+#include "vkvg_context_internal.h"
+#include "vkvg_device_internal.h"
+#include "vkvg_pattern.h"
+
+VkvgPattern _init_pattern (VkvgDevice dev){
+ VkvgPattern pat = (vkvg_pattern*)calloc(1,sizeof(vkvg_pattern));
+ pat->dev = dev;
+ VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
+ .descriptorPool = dev->descriptorPool,
+ .descriptorSetCount = 1,
+ .pSetLayouts = &dev->dslFont };
+ VK_CHECK_RESULT(vkAllocateDescriptorSets(dev->vkDev, &descriptorSetAllocateInfo, &pat->descriptorSet));
+ return pat;
+}
+
+void _update_descSet (VkvgPattern pat){
+ _font_cache_t* cache = pat->dev->fontCache;
+ VkDescriptorImageInfo descFontTex = {cache->cacheTex->sampler, cache->cacheTex->view,VK_IMAGE_LAYOUT_GENERAL};
+ VkDescriptorImageInfo descSrcTex = {pat->img->sampler, pat->img->view, VK_IMAGE_LAYOUT_GENERAL};
+
+ VkWriteDescriptorSet writeDescriptorSet[] = {
+ {
+ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+ .dstSet = pat->descriptorSet,
+ .dstBinding = 0,
+ .descriptorCount = 1,
+ .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ .pImageInfo = &descFontTex
+ },{
+ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+ .dstSet = pat->descriptorSet,
+ .dstBinding = 1,
+ .descriptorCount = 1,
+ .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ .pImageInfo = &descSrcTex
+ }};
+ vkUpdateDescriptorSets(pat->dev->vkDev, 2, &writeDescriptorSet, 0, NULL);
+}
+
+VkvgPattern vkvg_pattern_create(VkvgDevice dev){
+ VkvgPattern pat = _init_pattern (dev);
+ return pat;
+}
+
+VkvgPattern vkvg_pattern_create_for_surface (VkvgSurface surf){
+ VkvgPattern pat = _init_pattern (surf->dev);
+ pat->img = surf->img;
+ return pat;
+}
+VkvgPattern vkvg_pattern_create_linear (float x0, float y0, float x1, float y1){
+
+}
+VkvgPattern vkvg_pattern_create_radial (float cx0, float cy0, float radius0,
+ float cx1, float cy1, float radius1){
+
+}
+void vkvg_pattern_set_extend (VkvgPattern pat, vkvg_extend_t extend){
+ pat->extend = extend;
+}
+
+void vkvg_set_source (VkvgContext ctx, VkvgPattern pat){
+ _update_descSet (pat);
+
+ vkCmdBindDescriptorSets(ctx->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pat->dev->pipelineLayout,
+ 0, 1, &pat->descriptorSet, 0, NULL);
+}
+
+void vkvg_pattern_destroy(VkvgPattern pat)
+{
+ vkFreeDescriptorSets(pat->dev, pat->dev->descriptorPool, 1, &pat->descriptorSet);
+
+ free(pat);
+}
+
--- /dev/null
+#ifndef VKVG_PATTERN_H
+#define VKVG_PATTERN_H
+
+#include "vkvg_internal.h"
+#include "vkvg.h"
+#include "vkhelpers.h"
+
+typedef enum _vkvg_extend {
+ VKVG_EXTEND_NONE,
+ VKVG_EXTEND_REPEAT,
+ VKVG_EXTEND_REFLECT,
+ VKVG_EXTEND_PAD
+} vkvg_extend_t;
+
+typedef struct _vkvg_pattern_t {
+ VkvgDevice dev;
+ VkDescriptorSet descriptorSet;
+ vkvg_extend_t extend;
+ VkhImage img;
+}vkvg_pattern;
+#endif
--- /dev/null
+#include "vkvg_surface_internal.h"
+#include "vkvg_device_internal.h"
+
+void _clear_stencil (VkvgSurface surf)
+{
+ vkh_cmd_begin (surf->cmd,VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
+
+ VkClearDepthStencilValue clr = {1.0f,0};
+ VkImageSubresourceRange range = {VK_IMAGE_ASPECT_STENCIL_BIT,0,1,0,1};
+
+ vkh_image_set_layout (surf->cmd, surf->stencilMS, VK_IMAGE_ASPECT_STENCIL_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
+
+ vkCmdClearDepthStencilImage (surf->cmd, surf->stencilMS->image,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,&clr,1,&range);
+
+ vkh_image_set_layout (surf->cmd, surf->stencilMS, VK_IMAGE_ASPECT_STENCIL_BIT, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
+ vkh_cmd_end (surf->cmd);
+
+ //vkh_cmd_submit_with_semaphores (surf->dev->queue,&surf->cmd,VK_NULL_HANDLE,surf->semaphore,VK_NULL_HANDLE);
+ VkFence fence = vkh_fence_create(surf->dev->vkDev);
+ vkh_cmd_submit (surf->dev->queue,&surf->cmd,fence);
+ vkWaitForFences(surf->dev->vkDev,1,&fence,VK_TRUE,UINT64_MAX);
+ vkDestroyFence(surf->dev->vkDev,fence,NULL);
+}
+
+VkvgSurface vkvg_surface_create(VkvgDevice dev, int32_t width, uint32_t height){
+ VkvgSurface surf = (vkvg_surface*)calloc(1,sizeof(vkvg_surface));
+
+ surf->dev = dev;
+ surf->width = width;
+ surf->height = height;
+
+ surf->img = vkh_image_create(dev,FB_COLOR_FORMAT,width,height,VK_IMAGE_TILING_LINEAR,VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
+ VK_IMAGE_USAGE_SAMPLED_BIT|VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT|VK_IMAGE_USAGE_TRANSFER_SRC_BIT|VK_IMAGE_USAGE_TRANSFER_DST_BIT);
+ surf->imgMS = vkh_image_ms_create(dev,FB_COLOR_FORMAT,VKVG_SAMPLES,width,height,VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT|VK_IMAGE_USAGE_TRANSFER_DST_BIT|VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
+ surf->stencilMS = vkh_image_ms_create(dev,VK_FORMAT_S8_UINT,VKVG_SAMPLES,width,height,VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
+ VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT|VK_IMAGE_USAGE_TRANSFER_DST_BIT|VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
+
+ vkh_image_create_descriptor(surf->img, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_ASPECT_COLOR_BIT, VK_FILTER_NEAREST, VK_FILTER_NEAREST, VK_SAMPLER_MIPMAP_MODE_NEAREST,VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER);
+ vkh_image_create_descriptor(surf->imgMS, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_ASPECT_COLOR_BIT, VK_FILTER_NEAREST, VK_FILTER_NEAREST, VK_SAMPLER_MIPMAP_MODE_NEAREST,VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER);
+ vkh_image_create_descriptor(surf->stencilMS, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_ASPECT_STENCIL_BIT, VK_FILTER_NEAREST, VK_FILTER_NEAREST, VK_SAMPLER_MIPMAP_MODE_NEAREST,VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER);
+
+ VkImageView attachments[] = {
+ surf->imgMS->view, surf->img->view,
+ surf->stencilMS->view,
+ };
+ VkFramebufferCreateInfo frameBufferCreateInfo = { .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
+ .renderPass = dev->renderPass,
+ .attachmentCount = 3,
+ .pAttachments = attachments,
+ .width = width,
+ .height = height,
+ .layers = 1 };
+ VK_CHECK_RESULT(vkCreateFramebuffer(surf->dev->vkDev, &frameBufferCreateInfo, NULL, &surf->fb));
+
+ surf->semaphore = vkh_semaphore_create(dev->vkDev);
+ surf->cmd = vkh_cmd_buff_create(surf->dev->vkDev, surf->dev->cmdPool,VK_COMMAND_BUFFER_LEVEL_PRIMARY);
+
+ _clear_stencil(surf);
+
+ return surf;
+}
+
+void vkvg_surface_destroy(VkvgSurface surf)
+{
+ vkDestroySemaphore(surf->dev->vkDev,surf->semaphore,NULL);
+ vkFreeCommandBuffers(surf->dev->vkDev,surf->dev->cmdPool,1,&surf->cmd);
+ vkDestroyFramebuffer(surf->dev->vkDev, surf->fb, NULL);
+ vkh_image_destroy(surf->img);
+ vkh_image_destroy(surf->imgMS);
+ vkh_image_destroy(surf->stencilMS);
+ free(surf);
+}
+
+VkImage vkvg_surface_get_vk_image(VkvgSurface surf)
+{
+ return surf->img->image;
+}
+VkImage vkvg_surface_get_vkh_image(VkvgSurface surf)
+{
+ return surf->img;
+}
--- /dev/null
+#ifndef SURFACE_INTERNAL_H
+#define SURFACE_INTERNAL_H
+
+#include "vkvg_internal.h"
+#include "vkvg.h"
+#include "vkh_image.h"
+
+typedef struct _vkvg_surface_t {
+ VkvgDevice dev;
+ uint32_t width;
+ uint32_t height;
+ VkFramebuffer fb;
+ VkhImage img;
+ VkhImage imgMS;
+ VkhImage stencilMS;
+ VkSemaphore semaphore;
+ VkCommandBuffer cmd;
+}vkvg_surface;
+
+void _clear_stencil (VkvgSurface surf);
+#endif
--- /dev/null
+Subproject commit 5a27d2a48747db16eb098718dbe1396d0d291e19
--- /dev/null
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=@CMAKE_INSTALL_PREFIX@
+libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
+includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
+
+Name: @PROJECT_NAME@
+Description: @PROJECT_DESCRIPTION@
+Version: @PROJECT_VERSION@
+
+Requires:
+Libs: -L${libdir} -lvkvg
+Cflags: -I${includedir}