]> O.S.I.I.S - jp/vkvg.git/commitdiff
first commit
authorJean-Philippe Bruyère <jp_bruyere@hotmail.com>
Wed, 27 Dec 2017 16:35:30 +0000 (17:35 +0100)
committerJean-Philippe Bruyère <jp_bruyere@hotmail.com>
Thu, 28 Dec 2017 02:29:08 +0000 (03:29 +0100)
32 files changed:
.gitignore [new file with mode: 0644]
.gitmodules [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0644]
README.md [new file with mode: 0644]
cmake/FindFontConfig.cmake [new file with mode: 0644]
cmake/FindGLFW3.cmake [new file with mode: 0644]
include/vkvg.h [new file with mode: 0644]
shaders/deferred.frag [new file with mode: 0644]
shaders/deferred.vert [new file with mode: 0644]
shaders/paint.frag [new file with mode: 0644]
shaders/shader.comp [new file with mode: 0644]
shaders/shader2.comp [new file with mode: 0644]
shaders/triangle.frag [new file with mode: 0644]
shaders/triangle.vert [new file with mode: 0644]
src/vectors.c [new file with mode: 0644]
src/vectors.h [new file with mode: 0644]
src/vkvg_buff.c [new file with mode: 0644]
src/vkvg_buff.h [new file with mode: 0644]
src/vkvg_context.c [new file with mode: 0644]
src/vkvg_context_internal.c [new file with mode: 0644]
src/vkvg_context_internal.h [new file with mode: 0644]
src/vkvg_device.c [new file with mode: 0644]
src/vkvg_device_internal.h [new file with mode: 0644]
src/vkvg_fonts.c [new file with mode: 0644]
src/vkvg_fonts.h [new file with mode: 0644]
src/vkvg_internal.h [new file with mode: 0644]
src/vkvg_pattern.c [new file with mode: 0644]
src/vkvg_pattern.h [new file with mode: 0644]
src/vkvg_surface.c [new file with mode: 0644]
src/vkvg_surface_internal.h [new file with mode: 0644]
vkh [new submodule]
vkvg.pc.in [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..f0bb5fa
--- /dev/null
@@ -0,0 +1,2 @@
+build/
+*.user
diff --git a/.gitmodules b/.gitmodules
new file mode 100644 (file)
index 0000000..146d4ce
--- /dev/null
@@ -0,0 +1,3 @@
+[submodule "vkh"]
+       path = vkh
+       url = https://github.com/jpbruyere/vkhelpers.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e244ceb
--- /dev/null
@@ -0,0 +1,100 @@
+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})
+
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..4945d74
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+# vkvg
diff --git a/cmake/FindFontConfig.cmake b/cmake/FindFontConfig.cmake
new file mode 100644 (file)
index 0000000..540aef0
--- /dev/null
@@ -0,0 +1,35 @@
+# - 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)
diff --git a/cmake/FindGLFW3.cmake b/cmake/FindGLFW3.cmake
new file mode 100644 (file)
index 0000000..13bbb84
--- /dev/null
@@ -0,0 +1,49 @@
+# 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)
diff --git a/include/vkvg.h b/include/vkvg.h
new file mode 100644 (file)
index 0000000..07aa7eb
--- /dev/null
@@ -0,0 +1,95 @@
+#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
diff --git a/shaders/deferred.frag b/shaders/deferred.frag
new file mode 100644 (file)
index 0000000..895d086
--- /dev/null
@@ -0,0 +1,103 @@
+#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
diff --git a/shaders/deferred.vert b/shaders/deferred.vert
new file mode 100644 (file)
index 0000000..6227952
--- /dev/null
@@ -0,0 +1,23 @@
+#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);
+}
diff --git a/shaders/paint.frag b/shaders/paint.frag
new file mode 100644 (file)
index 0000000..a46e3da
--- /dev/null
@@ -0,0 +1,17 @@
+#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;
+}
diff --git a/shaders/shader.comp b/shaders/shader.comp
new file mode 100644 (file)
index 0000000..c6624a1
--- /dev/null
@@ -0,0 +1,49 @@
+#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);
+}
diff --git a/shaders/shader2.comp b/shaders/shader2.comp
new file mode 100644 (file)
index 0000000..0bee94b
--- /dev/null
@@ -0,0 +1,83 @@
+#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);
+}
diff --git a/shaders/triangle.frag b/shaders/triangle.frag
new file mode 100644 (file)
index 0000000..e494451
--- /dev/null
@@ -0,0 +1,28 @@
+#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;
+}
diff --git a/shaders/triangle.vert b/shaders/triangle.vert
new file mode 100644 (file)
index 0000000..c36ca95
--- /dev/null
@@ -0,0 +1,35 @@
+#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);
+}
diff --git a/src/vectors.c b/src/vectors.c
new file mode 100644 (file)
index 0000000..cdd877b
--- /dev/null
@@ -0,0 +1,78 @@
+#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;
+}
diff --git a/src/vectors.h b/src/vectors.h
new file mode 100644 (file)
index 0000000..d9ac190
--- /dev/null
@@ -0,0 +1,74 @@
+#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
diff --git a/src/vkvg_buff.c b/src/vkvg_buff.c
new file mode 100644 (file)
index 0000000..1517d5b
--- /dev/null
@@ -0,0 +1,47 @@
+#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);
+}
diff --git a/src/vkvg_buff.h b/src/vkvg_buff.h
new file mode 100644 (file)
index 0000000..0d2e3bc
--- /dev/null
@@ -0,0 +1,25 @@
+#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
diff --git a/src/vkvg_context.c b/src/vkvg_context.c
new file mode 100644 (file)
index 0000000..79333b8
--- /dev/null
@@ -0,0 +1,615 @@
+#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);
+}
diff --git a/src/vkvg_context_internal.c b/src/vkvg_context_internal.c
new file mode 100644 (file)
index 0000000..0104fb7
--- /dev/null
@@ -0,0 +1,299 @@
+#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);
+}
diff --git a/src/vkvg_context_internal.h b/src/vkvg_context_internal.h
new file mode 100644 (file)
index 0000000..0e32b9a
--- /dev/null
@@ -0,0 +1,146 @@
+#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
diff --git a/src/vkvg_device.c b/src/vkvg_device.c
new file mode 100644 (file)
index 0000000..5b732a1
--- /dev/null
@@ -0,0 +1,327 @@
+#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));
+}
diff --git a/src/vkvg_device_internal.h b/src/vkvg_device_internal.h
new file mode 100644 (file)
index 0000000..3b04f49
--- /dev/null
@@ -0,0 +1,37 @@
+#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
diff --git a/src/vkvg_fonts.c b/src/vkvg_fonts.c
new file mode 100644 (file)
index 0000000..5765452
--- /dev/null
@@ -0,0 +1,442 @@
+#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();
+//}
+
diff --git a/src/vkvg_fonts.h b/src/vkvg_fonts.h
new file mode 100644 (file)
index 0000000..e5ba755
--- /dev/null
@@ -0,0 +1,68 @@
+#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
diff --git a/src/vkvg_internal.h b/src/vkvg_internal.h
new file mode 100644 (file)
index 0000000..174b621
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef VKVG_INTERNAL_H
+#define VKVG_INTERNAL_H
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#endif
diff --git a/src/vkvg_pattern.c b/src/vkvg_pattern.c
new file mode 100644 (file)
index 0000000..2fb4a4f
--- /dev/null
@@ -0,0 +1,75 @@
+#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);
+}
+
diff --git a/src/vkvg_pattern.h b/src/vkvg_pattern.h
new file mode 100644 (file)
index 0000000..9b58fc1
--- /dev/null
@@ -0,0 +1,21 @@
+#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
diff --git a/src/vkvg_surface.c b/src/vkvg_surface.c
new file mode 100644 (file)
index 0000000..17d7ea7
--- /dev/null
@@ -0,0 +1,85 @@
+#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;
+}
diff --git a/src/vkvg_surface_internal.h b/src/vkvg_surface_internal.h
new file mode 100644 (file)
index 0000000..b7c7f92
--- /dev/null
@@ -0,0 +1,21 @@
+#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
diff --git a/vkh b/vkh
new file mode 160000 (submodule)
index 0000000..5a27d2a
--- /dev/null
+++ b/vkh
@@ -0,0 +1 @@
+Subproject commit 5a27d2a48747db16eb098718dbe1396d0d291e19
diff --git a/vkvg.pc.in b/vkvg.pc.in
new file mode 100644 (file)
index 0000000..ccd6766
--- /dev/null
@@ -0,0 +1,12 @@
+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}