Skip to content

Commit

Permalink
build based on bba86ef
Browse files Browse the repository at this point in the history
  • Loading branch information
Documenter.jl committed Dec 14, 2023
1 parent d11d8d5 commit 6a18cc5
Show file tree
Hide file tree
Showing 50 changed files with 18,037 additions and 3 deletions.
2 changes: 1 addition & 1 deletion stable
2 changes: 1 addition & 1 deletion v0.6
1 change: 1 addition & 0 deletions v0.6.13/.documenter-siteinfo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"documenter":{"julia_version":"1.9.3","generation_timestamp":"2023-12-14T14:55:11","documenter_version":"1.1.0"}}
2 changes: 2 additions & 0 deletions v0.6.13/about/extension_mechanism/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width, initial-scale=1.0"/><title>Extension mechanism · Vulkan.jl</title><meta name="title" content="Extension mechanism · Vulkan.jl"/><meta property="og:title" content="Extension mechanism · Vulkan.jl"/><meta property="twitter:title" content="Extension mechanism · Vulkan.jl"/><meta name="description" content="Documentation for Vulkan.jl."/><meta property="og:description" content="Documentation for Vulkan.jl."/><meta property="twitter:description" content="Documentation for Vulkan.jl."/><script data-outdated-warner src="../../assets/warner.js"></script><link href="https://cdnjs.cloudflare.com/ajax/libs/lato-font/3.0.0/css/lato-font.min.css" rel="stylesheet" type="text/css"/><link href="https://cdnjs.cloudflare.com/ajax/libs/juliamono/0.050/juliamono.min.css" rel="stylesheet" type="text/css"/><link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/fontawesome.min.css" rel="stylesheet" type="text/css"/><link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/solid.min.css" rel="stylesheet" type="text/css"/><link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/brands.min.css" rel="stylesheet" type="text/css"/><link href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.8/katex.min.css" rel="stylesheet" type="text/css"/><script>documenterBaseURL="../.."</script><script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js" data-main="../../assets/documenter.js"></script><script src="../../search_index.js"></script><script src="../../siteinfo.js"></script><script src="../../../versions.js"></script><link class="docs-theme-link" rel="stylesheet" type="text/css" href="../../assets/themes/documenter-dark.css" data-theme-name="documenter-dark" data-theme-primary-dark/><link class="docs-theme-link" rel="stylesheet" type="text/css" href="../../assets/themes/documenter-light.css" data-theme-name="documenter-light" data-theme-primary/><script src="../../assets/themeswap.js"></script></head><body><div id="documenter"><nav class="docs-sidebar"><div class="docs-package-name"><span class="docs-autofit"><a href="../../">Vulkan.jl</a></span></div><button class="docs-search-query input is-rounded is-small is-clickable my-2 mx-auto py-1 px-2" id="documenter-search-query">Search docs (Ctrl + /)</button><ul class="docs-menu"><li><a class="tocitem" href="../../">Home</a></li><li><a class="tocitem" href="../../intro/">Introduction</a></li><li><a class="tocitem" href="../../glossary/">Glossary</a></li><li><span class="tocitem">Tutorial</span><ul><li><a class="tocitem" href="../../tutorial/getting_started/">Getting started</a></li><li><a class="tocitem" href="../../tutorial/error_handling/">Error handling</a></li><li><a class="tocitem" href="../../tutorial/resource_management/">Resource management</a></li><li><a class="tocitem" href="../../tutorial/indepth/">In-depth tutorial</a></li><li><a class="tocitem" href="../../tutorial/minimal_working_compute/">Running compute shaders</a></li></ul></li><li><span class="tocitem">How to</span><ul><li><a class="tocitem" href="../../howto/preferences/">Specify package options</a></li><li><a class="tocitem" href="../../howto/debugging/">Debug an application</a></li><li><a class="tocitem" href="../../howto/handles/">Manipulate handles</a></li><li><a class="tocitem" href="../../howto/shaders/">Compile a SPIR-V shader from Julia</a></li></ul></li><li><span class="tocitem">Reference</span><ul><li><a class="tocitem" href="../../reference/wrapper_types/">Wrapper types</a></li><li><a class="tocitem" href="../../reference/wrapper_functions/">Wrapper functions</a></li><li><a class="tocitem" href="../../reference/dispatch/">API function dispatch</a></li><li><a class="tocitem" href="../../reference/options/">Package options</a></li></ul></li><li><span class="tocitem">Explanations</span><ul><li><a class="tocitem" href="../motivations/">Motivations</a></li><li class="is-active"><a class="tocitem" href>Extension mechanism</a></li></ul></li><li><a class="tocitem" href="../../api/">API</a></li><li><a class="tocitem" href="../../utility/">Utility</a></li><li><a class="tocitem" href="../../troubleshooting/">Troubleshooting</a></li><li><span class="tocitem">Developer documentation</span><ul><li><a class="tocitem" href="../../dev/overview/">Overview</a></li><li><a class="tocitem" href="../../dev/spec/">Vulkan specification</a></li><li><a class="tocitem" href="../../dev/gen/">Generator</a></li><li><a class="tocitem" href="../../dev/next_chains/">Next chains</a></li></ul></li></ul><div class="docs-version-selector field has-addons"><div class="control"><span class="docs-label button is-static is-size-7">Version</span></div><div class="docs-selector control is-expanded"><div class="select is-fullwidth is-size-7"><select id="documenter-version-selector"></select></div></div></div></nav><div class="docs-main"><header class="docs-navbar"><a class="docs-sidebar-button docs-navbar-link fa-solid fa-bars is-hidden-desktop" id="documenter-sidebar-button" href="#"></a><nav class="breadcrumb"><ul class="is-hidden-mobile"><li><a class="is-disabled">Explanations</a></li><li class="is-active"><a href>Extension mechanism</a></li></ul><ul class="is-hidden-tablet"><li class="is-active"><a href>Extension mechanism</a></li></ul></nav><div class="docs-right"><a class="docs-navbar-link" href="https://github.com/JuliaGPU/Vulkan.jl/blob/main/docs/src/about/extension_mechanism.md#L" title="Edit source on GitHub"><span class="docs-icon fa-solid"></span></a><a class="docs-settings-button docs-navbar-link fa-solid fa-gear" id="documenter-settings-button" href="#" title="Settings"></a><a class="docs-article-toggle-button fa-solid fa-chevron-up" id="documenter-article-toggle-button" href="javascript:;" title="Collapse all docstrings"></a></div></header><article class="content" id="documenter-page"><h1 id="Optional-functionality"><a class="docs-heading-anchor" href="#Optional-functionality">Optional functionality</a><a id="Optional-functionality-1"></a><a class="docs-heading-anchor-permalink" href="#Optional-functionality" title="Permalink"></a></h1><p>Vulkan uses a particular functionality mechanism based on <a href="https://www.khronos.org/registry/vulkan/specs/1.2/html/vkspec.html#features">features</a>, <a href="https://www.khronos.org/registry/vulkan/specs/1.2/html/vkspec.html#extendingvulkan">extensions</a> and properties.</p><p>Properties are per-device, and are not specified by the user; instead, they are returned by the Vulkan corresponding driver. Features may be very similar to properties semantically: they may specify whether some functionality is available or not on the device, such as atomic operations. However, features are usually more complex than that: the presence or absence of specific features will cause the driver to behave differently. Therefore, the difference with properties is that enabling a feature may dynamically change the logic of the driver, while properties are static and can only tell whether some functionality is supported or not.</p><p>SPIR-V uses a similar mechanism, with capabilities (analogous to features) and extensions. However, one should note that SPIR-V is a format for GPU programs, and not an API in itself; there is no SPIR-V driver of any kind. Therefore, any configuration for SPIR-V will be specified through its execution environment, e.g. OpenCL or Vulkan. As a result, certain Vulkan features and extensions are directly related to SPIR-V capabilities and extensions.</p><p>As a client API for SPIR-V, Vulkan <a href="https://www.khronos.org/registry/vulkan/specs/1.2/html/vkspec.html#spirvenv">establishes</a> what SPIR-V capabilities and extensions are enabled given the level of functionality requested from or provided by the driver. Notably, no SPIR-V capability or extension can be enabled without a corresponding requirement for a Vulkan core version or the presence of a Vulkan feature or extension.</p><p>Optional SPIR-V functionality is therefore fully implicit, based on the Vulkan API configuration. To help automate this mapping (and alleviate or even remove the burden forced on the developer), <code>SPIRV_CAPABILITIES</code> and <code>SPIRV_EXTENSIONS</code> are exported which contain information about capability and extension requirements, respectively.</p></article><nav class="docs-footer"><a class="docs-footer-prevpage" href="../motivations/">« Motivations</a><a class="docs-footer-nextpage" href="../../api/">API »</a><div class="flexbox-break"></div><p class="footer-message">Powered by <a href="https://github.com/JuliaDocs/Documenter.jl">Documenter.jl</a> and the <a href="https://julialang.org/">Julia Programming Language</a>.</p></nav></div><div class="modal" id="documenter-settings"><div class="modal-background"></div><div class="modal-card"><header class="modal-card-head"><p class="modal-card-title">Settings</p><button class="delete"></button></header><section class="modal-card-body"><p><label class="label">Theme</label><div class="select"><select id="documenter-themepicker"><option value="documenter-light">documenter-light</option><option value="documenter-dark">documenter-dark</option><option value="auto">Automatic (OS)</option></select></div></p><hr/><p>This document was generated with <a href="https://github.com/JuliaDocs/Documenter.jl">Documenter.jl</a> version 1.1.0 on <span class="colophon-date" title="Thursday 14 December 2023 14:55">Thursday 14 December 2023</span>. Using Julia version 1.9.3.</p></section><footer class="modal-card-foot"></footer></div></div></div></body></html>
94 changes: 94 additions & 0 deletions v0.6.13/about/motivations.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#=
# Motivations
## Automating low-level patterns
Vulkan is a low-level API that exhibits many patterns than any C library exposes. For example, some functions return error codes as a result, or mutate pointer memory as a way of returning values. Arrays are requested in the form of a pointer and a length. Pointers are used in many places; and because their dependency to their pointed data escapes the Julia compiler and the garbage collection mechanism, it is not trivial to keep pointers valid, i.e.: to have them point to valid *unreclaimed* memory. These pitfalls lead to crashes. Furthermore, the Vulkan C API makes heavy use of structures with pointer fields and structure pointers, requiring from the Julia runtime a clear knowledge of variable preservation.
Usually, the patterns mentioned above are not problematic for small libraries, because the C structures involved are relatively simple. Vulkan being a large API, however, patterns start to feel heavy: they require lots of boilerplate code and any mistake is likely to result in a crash. That is why we developped a procedural approach to automate these patterns.
Vulkan.jl uses a generator to programmatically generate higher-level wrappers for low-level API functions. This is a critical part of this library, which helped us to minimize the amount of human errors in the wrapping process, while allowing a certain flexilibity. The related project is contained in the `generator` folder. Because its unique purpose is to generate wrappers, it is not included in the package, reducing the number of dependencies.
## Structures and variable preservation
Since the original Vulkan API is written in C, there are a lot of pointers to deal with and handling them is not always an easy task. With a little practice, one can figure out how to wrap function calls with `cconvert` and `unsafe_convert` provided by Julia. Those functions provide automatic conversions and `ccall` GC-roots `cconvert`ed variables to ensure that pointers will point to valid memory, by explicitly telling the compiler not to garbage-collect nor optimize away the original variable.
However, the situation gets a lot more complicated when you deal with pointers as type fields. We will look at a naive example that show how difficult it can get for a Julia developer unfamiliar with calling C code. If we wanted to create a `VkInstance`, we might be tempted to do:
=#

using Vulkan.VkCore

function create_instance(app_name, engine_name)
app_info = VkApplicationInfo(
VK_STRUCTURE_TYPE_APPLICATION_INFO, # sType
C_NULL, # pNext
pointer(app_name), # application name
1, # application version
pointer(engine_name), # engine name
0, # engine version
VK_VERSION_1_2, # requested API version
)
create_info = InstanceCreateInfo(
VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, # sType
C_NULL, # pNext
0, # flags
Base.unsafe_convert(Ptr{VkApplicationInfo}, (Ref(app_info))), # application info
0, # layer count
C_NULL, # layers (none requested)
0, # extension count
C_NULL, # extensions (none requested)
)
p_instance = Ref{VkInstance}()

GC.@preserve app_info begin
vkCreateInstance(
Ref(create_info),
C_NULL, # custom allocator (we choose the default one provided by Vulkan)
p_instance,
)
end

p_instance[]
end

## instance = create_instance("AppName", "NoEngine") # very likely to segfault

#=
which will probably result in a segmentation fault. Why?
Two causes may lead to such a result:
1. `app_name` and `engine_name` may never be allocated if the compiler decides not to, so there is no guarantee that `pointer(app_name)` and `pointer(engine_name)` will point to anything valid. Additionally, even if those variables were allocated with valid pointer addresses at some point, they can be garbage collected **at any time**, including before the call to `vkCreateInstance`.
2. `app_info` is not what should be preserved. It cannot be converted to a pointer, but a `Ref` to it can. Therefore it is the reference that needs to be `GC.@preserve`d, not `app_info`. So, `Ref(app_info)` must be assigned to a variable, and replace `app_info` in the call to `GC.@preserve`.
Basically, it all comes down to having to preserve everything you take a pointer of. And, if you need to create an intermediary object when converting a variable to a pointer, you need to preserve it too. For example, take of an array of `String`s, that need to be converted as a `Ptr{Cstring}`. You first need to create an array of `Cstring`s, then convert that array to a pointer. The `String`s and the `Cstring` array need to be preserved.
This is exactly what `cconvert` and `unsafe_convert` are for. `cconvert` converts a variable to a type that can be converted to the desired (possibly pointer) type using `unsafe_convert`. In addition of chaining both conversions, `ccall` also preserves the `cconvert`ed variable, so that the unsafe conversion becomes safe.
Because we cannot use `ccall` in this case, we need to `cconvert` any argument that will be transformed to a pointer, and store the result as long as the desired struct may be used. Then, `unsafe_convert` can be called on this result, to get the desired (pointer) type necessary to construct the API struct.
There are several possibilities for preserving what we may call "pointer dependencies". One of them is to reference them inside a global variable, such as a `Dict`, and deleting them once we no longer need it. This has the severe disadvantage of requiring to explicitly manage every dependency, along with large performance issues. Another possibility, which we have taken in this wrapper, is to create a new structure that will store both the API structure and the required dependencies. That way, we can safely rely on the GC for preserving what we need just when we need it.
Therefore, every API structure is wrapped inside another one (without the "Vk" prefix), as follows:
=#

abstract type VulkanStruct{has_deps} end

struct InstanceCreateInfo <: VulkanStruct{true}
vks::VkInstanceCreateInfo # API struct
deps::Vector{Any} # contains all required dependencies
end

#=
and every structure exposes a convenient constructor that works perfectly with `String`s and mutable `AbstractArray`s. No manual `Ref`s/`cconvert`/`unsafe_convert` needed.
We hope that the additional `Vector{Any}` will not introduce too much overhead. In the future, this might be changed to a `NTuple{N, Any}` or a `StaticArrays.SVector{N, Any}`. We could also have stored dependencies as additional fields, but this does not scale well with nested structs. It would either require putting an additional field for each dependency (be it direct, or indirect dependencies coming from a pointer to another struct), possibly defining other structures that hold dependencies to avoid having a large number of fields, inducing additional compilation time.
!!! tip
`cconvert`/`unsafe_convert` were extended on wrapper types so that, when using an API function directly, [`ccall`](https://docs.julialang.org/en/v1/base/c/#ccall) will convert a struct to its API-compatible version.
=#
Loading

0 comments on commit 6a18cc5

Please sign in to comment.