From: Jean-Philippe Bruyère Date: Wed, 27 Dec 2017 16:35:30 +0000 (+0100) Subject: first commit X-Git-Tag: v0.1-alpha~166 X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=4e4b9e487f695f8ca7751f182c3066c86d5706e3;p=jp%2Fvkvg.git first commit --- 4e4b9e487f695f8ca7751f182c3066c86d5706e3 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f0bb5fa --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/ +*.user diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..146d4ce --- /dev/null +++ b/.gitmodules @@ -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 index 0000000..e244ceb --- /dev/null +++ b/CMakeLists.txt @@ -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 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 index 0000000..540aef0 --- /dev/null +++ b/cmake/FindFontConfig.cmake @@ -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 index 0000000..13bbb84 --- /dev/null +++ b/cmake/FindGLFW3.cmake @@ -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 index 0000000..07aa7eb --- /dev/null +++ b/include/vkvg.h @@ -0,0 +1,95 @@ +#ifndef VKVG_H +#define VKVG_H + +#include +#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 index 0000000..895d086 --- /dev/null +++ b/shaders/deferred.frag @@ -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 index 0000000..6227952 --- /dev/null +++ b/shaders/deferred.vert @@ -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 index 0000000..a46e3da --- /dev/null +++ b/shaders/paint.frag @@ -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 index 0000000..c6624a1 --- /dev/null +++ b/shaders/shader.comp @@ -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 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 index 0000000..0bee94b --- /dev/null +++ b/shaders/shader2.comp @@ -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 index 0000000..e494451 --- /dev/null +++ b/shaders/triangle.frag @@ -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 index 0000000..c36ca95 --- /dev/null +++ b/shaders/triangle.vert @@ -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 index 0000000..cdd877b --- /dev/null +++ b/src/vectors.c @@ -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 index 0000000..d9ac190 --- /dev/null +++ b/src/vectors.h @@ -0,0 +1,74 @@ +#ifndef VKVG_VECTORS_H +#define VKVG_VECTORS_H + +#include +#include +#include + +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 index 0000000..1517d5b --- /dev/null +++ b/src/vkvg_buff.c @@ -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 index 0000000..0d2e3bc --- /dev/null +++ b/src/vkvg_buff.h @@ -0,0 +1,25 @@ +#ifndef VKVG_BUFF_H +#define VKVG_BUFF_H + +#include +#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 index 0000000..79333b8 --- /dev/null +++ b/src/vkvg_context.c @@ -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 index 0000000..0104fb7 --- /dev/null +++ b/src/vkvg_context_internal.c @@ -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 index 0000000..0e32b9a --- /dev/null +++ b/src/vkvg_context_internal.h @@ -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 index 0000000..5b732a1 --- /dev/null +++ b/src/vkvg_device.c @@ -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 index 0000000..3b04f49 --- /dev/null +++ b/src/vkvg_device_internal.h @@ -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 index 0000000..5765452 --- /dev/null +++ b/src/vkvg_fonts.c @@ -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; ycurLine.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 index 0000000..e5ba755 --- /dev/null +++ b/src/vkvg_fonts.h @@ -0,0 +1,68 @@ +#ifndef VKVG_FONTS_H +#define VKVG_FONTS_H + +#include +#include FT_FREETYPE_H + +#include +#include + +#include + +#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 index 0000000..174b621 --- /dev/null +++ b/src/vkvg_internal.h @@ -0,0 +1,10 @@ +#ifndef VKVG_INTERNAL_H +#define VKVG_INTERNAL_H + +#include +#include +#include +#include +#include + +#endif diff --git a/src/vkvg_pattern.c b/src/vkvg_pattern.c new file mode 100644 index 0000000..2fb4a4f --- /dev/null +++ b/src/vkvg_pattern.c @@ -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 index 0000000..9b58fc1 --- /dev/null +++ b/src/vkvg_pattern.h @@ -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 index 0000000..17d7ea7 --- /dev/null +++ b/src/vkvg_surface.c @@ -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 index 0000000..b7c7f92 --- /dev/null +++ b/src/vkvg_surface_internal.h @@ -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 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 index 0000000..ccd6766 --- /dev/null +++ b/vkvg.pc.in @@ -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}