{% import "utils.j2" as u with context %}
/* Generated by gloam. DO NOT EDIT. */
{% if fs.spec_name == "glx" %}
#ifdef __linux
{% elif fs.spec_name == "wgl" %}
#ifdef _WIN32
#include <gloam/gl.h>
{% endif %}
#include <gloam/{{ stem }}.h>
#include <stdlib.h> /* calloc, free */
#include <stddef.h>
#include <stdio.h> /* sscanf */
#include <string.h> /* strlen, strncmp */
{% if loader -%}
#ifdef _WIN32
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <windows.h> /* LoadLibrary, GetProcAddress */
#else
# include <dlfcn.h> /* dlopen, dlsym, dlclose */
#endif
{%- endif %}
{% if fs.extensions | length > 0 -%}
#if defined(__x86_64__) || defined(__i386__) || defined(_M_IX86) || defined(_M_X64)
# define XXH_VECTOR XXH_SSE2
# include <immintrin.h>
#elif defined(__aarch64__) || defined(__arm__) || defined(_M_ARM) || defined(_M_ARM64)
# define XXH_VECTOR XXH_NEON
# include <arm_neon.h>
#endif
#ifndef GLOAM_EXTERNAL_XXHASH
# define XXH_INLINE_ALL
# define XXH_NO_STREAM
#endif
#include "xxhash.h"
{% endif %}
{% include "impl_util.j2" %}
{% include "hash_search.j2" %}
/* ---- Global context (zero-initialised at program startup) ---------------- */
#ifdef __cplusplus
{{ fs.context_name }} gloam_{{ fs.spec_name }}_context = {};
#else
{{ fs.context_name }} gloam_{{ fs.spec_name }}_context = { 0 };
#endif
/* ---- Function name table -------------------------------------------------
Indexed by command.index. Used for bootstrapping and by the PFN-range
loader to call getProcAddr(name) for each slot. */
static const char * const kFnNames_{{ fs.spec_name | spec_display }}[] = {
{%- for cmd in fs.commands %}
/* {{ cmd.index | rjust(4) }} */ "{{ cmd.name }}"{% if not loop.last %},{% endif %}
{%- endfor %}
};
{% if fs.is_vulkan -%}
/* ---- Command scope table -------------------------------------------------
Indexed in lockstep with kFnNames_{{ fs.spec_name | spec_display }}[]. Each entry is the GloamCommandScope
value for that slot — stored as uint8_t so the whole table is one byte per
command (compares well against the alternatives: a parallel pointer array
or a switch inside the loop). */
static const uint8_t kCommandScopes_{{ fs.spec_name | spec_display }}[] = {
{%- for cmd in fs.commands %}
/* {{ cmd.index | rjust(4) }} */ {{ cmd.scope }}, /* {{ cmd.name }} */
{%- endfor %}
};
{% endif %}
{% if fs.extensions | length > 0 -%}
/* ---- Extension hash table ------------------------------------------------
One XXH3-64 hash per extension, in extArray index order.
Pre-baked at generator time with the same algorithm used at load time. */
static const uint64_t kExtHashes_{{ fs.spec_name | spec_display }}[] = {
{%- for ext in fs.extensions %}
/* {{ ext.index | rjust(4) }} */ {{ ext.hash }}ULL{% if not loop.last %},{% else %} {% endif %} /* {{ ext.name }} */
{%- endfor %}
};
{% endif %}
{% if fs.feature_pfn_ranges | length > 0 -%}
/* ---- Feature PFN range table ---------------------------------------------
Each entry maps one feature (by featArray index) to a contiguous run of
pfnArray slots. The loader iterates this table and bulk-loads the run
when featArray[entry.extension] is set. */
static const GloamPfnRange_t kFeatPfnRanges_{{ fs.spec_name | spec_display }}[] = {
{%- for r in fs.feature_pfn_ranges %}
{ {{ r.extension | rjust(4) }}, {{ r.start | rjust(4) }}, {{ r.count | rjust(4) }} }, /* {{ fs.features[r.extension].full_name }} */
{%- endfor %}
};
{% endif %}
{% if not fs.is_vulkan -%}
/* ---- PFN range helper (GL / EGL / GLX / WGL) ----------------------------
Walks a contiguous run of pfnArray slots and calls the plain load callback
(name only) for each one. */
static void gloam_load_pfn_range_{{ fs.spec_name }}({{ u.ctx_arg(', ') }}GloamLoadFunc getProcAddr,
uint16_t start, uint16_t count) {
uint16_t i;
for (i = start; i < (uint16_t)(start + count); ++i)
context->pfnArray[i] = (void *)getProcAddr(kFnNames_{{ fs.spec_name | spec_display }}[i]);
}
{% else -%}
/* ---- Vulkan scope-aware PFN range helper ---------------------------------
Same as the GL variant but consults kCommandScopes_{{ fs.spec_name }}[] to pass the correct
GloamCommandScope to the load callback for each slot. The callback uses
the scope to pick the right Vulkan proc-addr function:
Global → vkGetInstanceProcAddr(NULL, name)
Instance → vkGetInstanceProcAddr(instance, name)
Device → vkGetDeviceProcAddr(device, name)
Unknown → dlsym (only vkGetInstanceProcAddr itself) */
static void gloam_load_pfn_range_{{ fs.spec_name }}({{ u.ctx_arg(', ') }}GloamVkUserptrLoadFunc load,
void *userptr, uint16_t start, uint16_t count) {
uint16_t i;
for (i = start; i < (uint16_t)(start + count); ++i)
context->pfnArray[i] = (void *)load(userptr, kFnNames_{{ fs.spec_name | spec_display }}[i],
(GloamCommandScope)kCommandScopes_{{ fs.spec_name | spec_display }}[i]);
}
{% endif %}
{% if alias and fs.alias_pairs | length > 0 -%}
/* ---- Alias resolution table ----------------------------------------------
Pairs are sorted by canonical index so same-canonical entries are
consecutive; the resolver processes them in a single forward pass. */
static const GloamAliasPair_t kAliases_{{ fs.spec_name | spec_display }}[] = {
{%- for p in fs.alias_pairs %}
{ {{ p.canonical | rjust(4) }}, {{ p.secondary | rjust(4) }} }, /* {{ fs.commands[p.canonical].name }} and {{ fs.commands[p.secondary].name }} */
{%- endfor %}
};
GLOAM_NO_INLINE static void gloam_resolve_aliases_{{ fs.spec_name }}({{ u.ctx_arg() }}) {
uint32_t i;
for (i = 0; i < GLOAM_ARRAYSIZE(kAliases_{{ fs.spec_name | spec_display }}); ++i) {
uint16_t ci = kAliases_{{ fs.spec_name | spec_display }}[i].first;
uint16_t si = kAliases_{{ fs.spec_name | spec_display }}[i].second;
if (!context->pfnArray[ci] && context->pfnArray[si])
context->pfnArray[ci] = context->pfnArray[si];
else if (context->pfnArray[ci] && !context->pfnArray[si])
context->pfnArray[si] = context->pfnArray[ci];
}
}
{% endif %}
/* ==========================================================================
Per-API sections
========================================================================== */
{% for api in fs.apis %}
/* --------------------------------------------------------------------------
API: {{ api }}
-------------------------------------------------------------------------- */
{%- set subset = fs.ext_subset_indices[api] %}
{% set extranges = fs.ext_pfn_ranges[api] %}
{%- if fs.extensions | length > 0 and subset | length > 0 %}
/* Extension index subset for {{ api }}: extArray indices this API supports. */
static const uint16_t kExtIdx_{{ api }}[] = {
{%- for idx in subset %}
{{ idx | rjust(4) }}, /* {{ fs.extensions[idx].name }} */
{%- endfor %}
};
/* Extension PFN range table for {{ api }}. */
static const GloamPfnRange_t kExtPfnRanges_{{ api }}[] = {
{%- for r in extranges %}
{ {{ r.extension | rjust(4) }}, {{ r.start | rjust(4) }}, {{ r.count | rjust(4) }} }, /* {{ fs.extensions[r.extension].name }} */
{%- endfor %}
{%- if extranges | length == 0 %}
{ 0, 0, 0 } /* sentinel — no extension commands for this API */
{%- endif %}
};
{% endif -%}
{#- ---- Extension enumeration and detection --------------------------------- -#}
{% if fs.extensions | length > 0 and subset | length > 0 %}
{%- if fs.spec_name in ["gl", "gles2", "glcore"] %}
/* Query driver-reported extension names, hash them, and fill *out_exts.
* Returns 1 on success, 0 on failure. Caller must free(*out_exts). */
static int gloam_{{ fs.spec_name }}_get_extensions_{{ api }}({{ u.ctx_arg(', ') }}
uint64_t **out_exts,
uint32_t *out_num_exts) {
uint32_t num_exts = 0;
uint64_t *exts = NULL;
#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0)
/* Modern path: glGetIntegerv(GL_NUM_EXTENSIONS) + glGetStringi. */
if (context->GetStringi != NULL && context->GetIntegerv != NULL) {
GLint n = 0;
uint32_t i;
context->GetIntegerv(0x821D /* GL_NUM_EXTENSIONS */, &n);
num_exts = (uint32_t)n;
if (num_exts > 0) {
exts = (uint64_t *)calloc(num_exts, sizeof(uint64_t));
if (!exts) return 0;
for (i = 0; i < num_exts; ++i) {
const char *name = (const char *)context->GetStringi(GL_EXTENSIONS, i);
if (name) exts[i] = gloam_hash_string(name, strlen(name));
}
}
} else
#endif
{
/* Legacy path: glGetString(GL_EXTENSIONS) — space-separated string.
* Two passes: first count tokens, then hash them. */
const char *ext_str;
const char *cur, *next;
uint32_t j;
if (!context->GetString) return 0;
ext_str = (const char *)context->GetString(GL_EXTENSIONS);
if (!ext_str) return 0;
for (j = 0; j < 2; ++j) {
num_exts = 0;
cur = ext_str;
next = cur + strcspn(cur, " ");
while (1) {
size_t len;
cur += strspn(cur, " ");
if (!cur[0]) break;
len = (size_t)(next - cur);
if (exts) exts[num_exts] = gloam_hash_string(cur, len);
++num_exts;
cur = next + strspn(next, " ");
next = cur + strcspn(cur, " ");
}
if (!exts) {
exts = (uint64_t *)calloc(num_exts, sizeof(uint64_t));
if (!exts) return 0;
}
}
}
gloam_sort_hashes(exts, num_exts);
*out_exts = exts;
*out_num_exts = num_exts;
return 1;
}
{% elif fs.spec_name == "gles1" %}
/* GLES 1.x: only glGetString(GL_EXTENSIONS), space-separated. */
static int gloam_{{ fs.spec_name }}_get_extensions_{{ api }}({{ u.ctx_arg(', ') }}
uint64_t **out_exts,
uint32_t *out_num_exts) {
const char *ext_str, *cur, *next;
uint64_t *exts = NULL;
uint32_t num_exts = 0, j;
if (!context->GetString) return 0;
ext_str = (const char *)context->GetString(GL_EXTENSIONS);
if (!ext_str) return 0;
for (j = 0; j < 2; ++j) {
num_exts = 0;
cur = ext_str;
next = cur + strcspn(cur, " ");
while (1) {
size_t len;
cur += strspn(cur, " ");
if (!cur[0]) break;
len = (size_t)(next - cur);
if (exts) exts[num_exts] = gloam_hash_string(cur, len);
++num_exts;
cur = next + strspn(next, " ");
next = cur + strcspn(cur, " ");
}
if (!exts) {
exts = (uint64_t *)calloc(num_exts, sizeof(uint64_t));
if (!exts) return 0;
}
}
gloam_sort_hashes(exts, num_exts);
*out_exts = exts;
*out_num_exts = num_exts;
return 1;
}
{% elif fs.spec_name == "egl" %}
/* EGL: concatenate client extensions (EGL_NO_DISPLAY) and display
* extensions, then hash the combined space-separated list. */
static int gloam_{{ fs.spec_name }}_get_extensions_{{ api }}({{ u.ctx_arg(', ') }}EGLDisplay display,
uint64_t **out_exts,
uint32_t *out_num_exts) {
const char *client_str, *display_str, *cur, *next;
char *concat = NULL;
uint64_t *exts = NULL;
uint32_t num_exts = 0, j;
size_t client_len, display_len;
if (!context->QueryString) return 0;
/* Client extensions live at EGL_NO_DISPLAY. */
client_str = (const char *)context->QueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
display_str = (display == EGL_NO_DISPLAY) ? "" :
(const char *)context->QueryString(display, EGL_EXTENSIONS);
if (!client_str) return 0;
if (!display_str) return 0;
client_len = strlen(client_str);
display_len = strlen(display_str);
/* Concatenate with a space separator. */
concat = (char *)malloc(client_len + display_len + 2);
if (!concat) return 0;
concat[0] = '\0';
strcat(concat, client_str);
if (display_len) {
if (client_len && concat[client_len - 1] != ' ')
strcat(concat, " ");
strcat(concat, display_str);
}
/* Two-pass: count then fill. */
for (j = 0; j < 2; ++j) {
num_exts = 0;
cur = concat;
next = cur + strcspn(cur, " ");
while (1) {
size_t len;
cur += strspn(cur, " ");
if (!cur[0]) break;
len = (size_t)(next - cur);
if (exts) exts[num_exts] = gloam_hash_string(cur, len);
++num_exts;
cur = next + strspn(next, " ");
next = cur + strcspn(cur, " ");
}
if (!exts) {
exts = (uint64_t *)calloc(num_exts, sizeof(uint64_t));
if (!exts) { free(concat); return 0; }
}
}
free(concat);
gloam_sort_hashes(exts, num_exts);
*out_exts = exts;
*out_num_exts = num_exts;
return 1;
}
{% elif fs.spec_name == "glx" %}
/* GLX: glXQueryExtensionsString(display, screen) — space-separated. */
static int gloam_{{ fs.spec_name }}_get_extensions_{{ api }}({{ u.ctx_arg(', ') }}Display *display, int screen,
uint64_t **out_exts,
uint32_t *out_num_exts) {
const char *ext_str, *cur, *next;
uint64_t *exts = NULL;
uint32_t num_exts = 0, j;
if (!context->QueryExtensionsString) return 0;
ext_str = (const char *)context->QueryExtensionsString(display, screen);
if (!ext_str) return 0;
for (j = 0; j < 2; ++j) {
num_exts = 0;
cur = ext_str;
next = cur + strcspn(cur, " ");
while (1) {
size_t len;
cur += strspn(cur, " ");
if (!cur[0]) break;
len = (size_t)(next - cur);
if (exts) exts[num_exts] = gloam_hash_string(cur, len);
++num_exts;
cur = next + strspn(next, " ");
next = cur + strcspn(cur, " ");
}
if (!exts) {
exts = (uint64_t *)calloc(num_exts, sizeof(uint64_t));
if (!exts) return 0;
}
}
gloam_sort_hashes(exts, num_exts);
*out_exts = exts;
*out_num_exts = num_exts;
return 1;
}
{% elif fs.spec_name == "wgl" %}
/* WGL: wglGetExtensionsStringARB / wglGetExtensionsStringEXT. */
static int gloam_{{ fs.spec_name }}_get_extensions_{{ api }}({{ u.ctx_arg(', ') }}HDC hdc,
uint64_t **out_exts,
uint32_t *out_num_exts) {
const char *ext_str = NULL, *cur, *next;
uint64_t *exts = NULL;
uint32_t num_exts = 0, j;
if (context->GetExtensionsStringARB)
ext_str = (const char *)context->GetExtensionsStringARB(hdc);
if (!ext_str && context->GetExtensionsStringEXT)
ext_str = (const char *)context->GetExtensionsStringEXT();
if (!ext_str) return 0;
for (j = 0; j < 2; ++j) {
num_exts = 0;
cur = ext_str;
next = cur + strcspn(cur, " ");
while (1) {
size_t len;
cur += strspn(cur, " ");
if (!cur[0]) break;
len = (size_t)(next - cur);
if (exts) exts[num_exts] = gloam_hash_string(cur, len);
++num_exts;
cur = next + strspn(next, " ");
next = cur + strcspn(cur, " ");
}
if (!exts) {
exts = (uint64_t *)calloc(num_exts, sizeof(uint64_t));
if (!exts) return 0;
}
}
gloam_sort_hashes(exts, num_exts);
*out_exts = exts;
*out_num_exts = num_exts;
return 1;
}
{% elif fs.spec_name == "vk" %}
/* Enumerate Vulkan extensions and return a heap-allocated, sorted array of
* XXH3-64 hashes. Merges instance extensions (always) and device extensions
* (when physical_device is non-null) into one flat array.
*
* The idempotency flags on the context (vk_found_instance_exts /
* vk_found_device_exts) are set here and tested by find_extensions so that
* repeated calls on the same context don't re-enumerate unnecessarily. */
static int gloam_{{ fs.spec_name }}_get_extensions_{{ api }}({{ u.ctx_arg(', ') }}
VkPhysicalDevice physical_device,
uint64_t **out_exts,
uint32_t *out_num_exts) {
uint32_t inst_count = 0, dev_count = 0, total, i;
VkExtensionProperties *props = NULL;
uint64_t *exts = NULL;
if (!context->EnumerateInstanceExtensionProperties) return 0;
context->EnumerateInstanceExtensionProperties(NULL, &inst_count, NULL);
if (physical_device != NULL && context->EnumerateDeviceExtensionProperties != NULL)
context->EnumerateDeviceExtensionProperties(physical_device, NULL, &dev_count, NULL);
total = inst_count + dev_count;
if (total == 0) { *out_exts = NULL; *out_num_exts = 0; return 1; }
/* Single allocation covers the larger of inst / dev for the props buffer,
and the exact total for the hash array. */
props = (VkExtensionProperties *)calloc(
(inst_count > dev_count ? inst_count : dev_count), sizeof(*props));
exts = (uint64_t *)calloc(total, sizeof(uint64_t));
if (!props || !exts) { free(props); free(exts); return 0; }
context->EnumerateInstanceExtensionProperties(NULL, &inst_count, props);
for (i = 0; i < inst_count; ++i)
exts[i] = gloam_hash_string(props[i].extensionName,
strlen(props[i].extensionName));
if (inst_count) context->vk_found_instance_exts = 1;
if (dev_count) {
context->EnumerateDeviceExtensionProperties(physical_device, NULL, &dev_count, props);
for (i = 0; i < dev_count; ++i)
exts[inst_count + i] = gloam_hash_string(props[i].extensionName,
strlen(props[i].extensionName));
context->vk_found_device_exts = 1;
}
free(props);
gloam_sort_hashes(exts, total);
*out_exts = exts;
*out_num_exts = total;
return 1;
}
{% endif %}
/* Search pre-baked kExtHashes_{{ fs.spec_name | spec_display }} against the sorted driver hash list and set
* extArray flags for every matching extension. */
{%- if fs.spec_name == "vk" %}
static int gloam_{{ fs.spec_name }}_find_extensions_{{ api }}({{ u.ctx_arg(', ') }}VkPhysicalDevice physical_device) {
uint64_t *exts = NULL;
uint32_t num_exts = 0, i;
/* Skip re-enumeration if we already have the right scope of extensions. */
if (physical_device != NULL && context->vk_found_device_exts) return 1;
if (physical_device == NULL && context->vk_found_instance_exts) return 1;
if (!gloam_{{ fs.spec_name }}_get_extensions_{{ api }}(context, physical_device, &exts, &num_exts))
return 0;
for (i = 0; i < GLOAM_ARRAYSIZE(kExtIdx_{{ api }}); ++i) {
const uint16_t extIdx = kExtIdx_{{ api }}[i];
context->extArray[extIdx] = gloam_hash_search(exts, num_exts, kExtHashes_{{ fs.spec_name | spec_display }}[extIdx]);
}
free(exts);
return 1;
}
{% else %}
static int gloam_{{ fs.spec_name }}_find_extensions_{{ api }}({{ u.ctx_arg() }}{{ u.extra_load_params(fs.spec_name) }}) {
uint64_t *exts = NULL;
uint32_t num_exts = 0, i;
if (!gloam_{{ fs.spec_name }}_get_extensions_{{ api }}(context{% if fs.spec_name not in ["gl", "gles1", "gles2", "glcore", "vk"] %}{{ u.extra_load_args(fs.spec_name) }}{% endif %}, &exts, &num_exts))
return 0;
for (i = 0; i < GLOAM_ARRAYSIZE(kExtIdx_{{ api }}); ++i) {
const uint16_t extIdx = kExtIdx_{{ api }}[i];
context->extArray[extIdx] = gloam_hash_search(exts, num_exts, kExtHashes_{{ fs.spec_name | spec_display }}[extIdx]);
}
free(exts);
return 1;
}
{% endif %}
{% endif %}
{#- ---- find_core_* (GL / GLES only) --------------------------------------- -#}
{%- if fs.spec_name in ["gl", "gles1", "gles2", "glcore"] -%}
/* Parse the GL_VERSION string and set featArray entries for this API. */
static int gloam_{{ fs.spec_name }}_find_core_{{ api }}({{ u.ctx_arg() }}) {
int i, major = 0, minor = 0;
unsigned short version_value;
static const char * const kPrefixes[] = {
"OpenGL ES-CM ", "OpenGL ES-CL ", "OpenGL ES ",
"OpenGL SC ", "OpenGL ", NULL
};
const char *version = (const char *)context->GetString(GL_VERSION);
if (!version) return 0;
for (i = 0; kPrefixes[i]; ++i) {
const size_t len = strlen(kPrefixes[i]);
if (strncmp(version, kPrefixes[i], len) == 0) { version += len; break; }
}
GLOAM_IMPL_UTIL_SSCANF(version, "%d.%d", &major, &minor);
version_value = (unsigned short)((major << 8) | minor);
{% for feat in fs.features | selectattr("api", "equalto", api) %}
context->{{ feat.short_name }} = (version_value >= {{ feat.packed | hex4 }});
{%- endfor %}
return (int)version_value;
}
{%- elif fs.spec_name == "glx" -%}
/* Query the GLX version via glXQueryVersion and set featArray entries for GLX.
* Also returns the packed version (major << 8 | minor) for loader use. */
static int gloam_{{ fs.spec_name }}_find_core_{{ api }}({{ u.ctx_arg(', ') }}Display **display, int *screen) {
int major = 0, minor = 0;
unsigned short version_value;
if(*display == NULL) {
*display = XOpenDisplay(0);
if (*display == NULL) {
return 0;
}
*screen = XScreenNumberOfScreen(XDefaultScreenOfDisplay(*display));
}
context->QueryVersion(*display, &major, &minor);
version_value = (major << 8U) | minor;
context->VERSION_1_0 = version_value >= 0x0100;
context->VERSION_1_1 = version_value >= 0x0101;
context->VERSION_1_2 = version_value >= 0x0102;
context->VERSION_1_3 = version_value >= 0x0103;
context->VERSION_1_4 = version_value >= 0x0104;
return version_value;
}
{%- elif fs.spec_name == "vk" -%}
/* Determine the Vulkan API version and set featArray bits accordingly.
*
* Called on every gloamLoadVulkanContext call so that version fields can be
* filled in incrementally:
* - Instance version: queried once via EnumerateInstanceVersion (VK 1.1+)
* or assumed 1.0. Cached in context->vk_instance_version.
* - Device version: queried via GetPhysicalDeviceProperties when
* physical_device is non-null. Cached in context->vk_device_version.
* Device version takes precedence over instance version when set.
*
* Returns the packed version (major << 8 | minor), or 0 on hard failure. */
static int gloam_vk_find_core({{ u.ctx_arg(', ') }}VkPhysicalDevice physical_device) {
/* The top 3 bits of apiVersion encode the variant — mask them off so
Vulkan SC (variant 1) and plain Vulkan (variant 0) compare the same. */
const uint32_t kVariantMask = 0xe0000000u;
int major = 1, minor = 0;
uint16_t version_value;
#ifdef VK_VERSION_1_1
/* EnumerateInstanceVersion is Global-scope and was bootstrapped in the
load function before we are called. */
if (!context->vk_instance_version && context->EnumerateInstanceVersion != NULL) {
VkResult r = context->EnumerateInstanceVersion(&context->vk_instance_version);
if (r != VK_SUCCESS)
context->vk_instance_version = 0;
else
context->vk_instance_version &= ~kVariantMask;
}
if (context->vk_instance_version) {
major = (int)VK_VERSION_MAJOR(context->vk_instance_version);
minor = (int)VK_VERSION_MINOR(context->vk_instance_version);
}
#endif
/* If a physical device is provided and we haven't cached its version yet,
query it. GetPhysicalDeviceProperties is Instance-scope and will have
been loaded from the feature ranges on the previous call. */
if (!context->vk_device_version && physical_device != NULL &&
context->GetPhysicalDeviceProperties != NULL) {
VkPhysicalDeviceProperties props;
context->GetPhysicalDeviceProperties(physical_device, &props);
context->vk_device_version = props.apiVersion & ~kVariantMask;
}
/* Device version is the authoritative cap; prefer it over instance version. */
if (context->vk_device_version) {
major = (int)VK_VERSION_MAJOR(context->vk_device_version);
minor = (int)VK_VERSION_MINOR(context->vk_device_version);
}
version_value = (uint16_t)((major << 8) | minor);
{% for feat in fs.features %}
context->{{ feat.short_name }} = (version_value >= {{ feat.packed | hex4 }});
{%- endfor %}
return (int)version_value;
}
{%- elif fs.spec_name == "wgl" -%}
/* Faux version detection on WGL */
static int gloam_{{ fs.spec_name }}_find_core_{{ api }}({{ u.ctx_arg() }}) {
int major = 1, minor = 0;
unsigned short version_value;
version_value = (unsigned short)((major << 8) | minor);
{% for feat in fs.features | selectattr("api", "equalto", api) %}
context->{{ feat.short_name }} = (version_value >= {{ feat.packed | hex4 }});
{%- endfor %}
return (int)version_value;
}
{%- elif fs.spec_name == "egl" -%}
/* Parse the EGL version string from eglQueryString(display, EGL_VERSION). */
static int gloam_{{ fs.spec_name }}_find_core_{{ api }}({{ u.ctx_arg(', ') }}EGLDisplay display) {
int major = 0, minor = 0;
unsigned short version_value;
const char *version;
if (!context->QueryString) return 0;
version = (const char *)context->QueryString(display, EGL_VERSION);
if (!version) return 0;
GLOAM_IMPL_UTIL_SSCANF(version, "%d.%d", &major, &minor);
version_value = (unsigned short)((major << 8) | minor);
{% for feat in fs.features | selectattr("api", "equalto", api) %}
context->{{ feat.short_name }} = (version_value >= {{ feat.packed | hex4 }});
{%- endfor %}
return (int)version_value;
}
{% endif -%}
{# ---- Load function ------------------------------------------------------- #}
{%- if fs.spec_name in ["gl", "gles1", "gles2", "glcore"] %}
int gloamLoad{{ api | api_display }}Context({{ u.ctx_arg(', ') }}GloamLoadFunc getProcAddr) {
int version;
uint32_t i;
memset(context, 0, sizeof(*context));
/* Bootstrap: glGetString must be loaded before find_core can run. */
{%- for cmd in fs.commands %}{% if cmd.name == "glGetString" %}
context->{{ cmd.short_name }} = ({{ cmd.pfn_type }})getProcAddr("glGetString");
if (!context->{{ cmd.short_name }}) return 0;
{% endif %}{%- endfor %}
version = gloam_{{ fs.spec_name }}_find_core_{{ api }}(context);
if (!version) return 0;
/* Load PFNs for each enabled feature via the range table. */
for (i = 0; i < GLOAM_ARRAYSIZE(kFeatPfnRanges_{{ fs.spec_name | spec_display }}); ++i) {
const GloamPfnRange_t *r = &kFeatPfnRanges_{{ fs.spec_name | spec_display }}[i];
if (context->featArray[r->extension])
gloam_load_pfn_range_{{ fs.spec_name }}(context, getProcAddr, r->start, r->count);
}
{% if fs.extensions | length > 0 and subset | length > 0 %}
if (!gloam_{{ fs.spec_name }}_find_extensions_{{ api }}(context)) return 0;
/* Load PFNs for each detected extension via the range table. */
for (i = 0; i < GLOAM_ARRAYSIZE(kExtPfnRanges_{{ api }}); ++i) {
const GloamPfnRange_t *r = &kExtPfnRanges_{{ api }}[i];
if (context->extArray[r->extension])
gloam_load_pfn_range_{{ fs.spec_name }}(context, getProcAddr, r->start, r->count);
}
{% endif -%}
{% if alias and fs.alias_pairs | length > 0 %}
gloam_resolve_aliases_{{ fs.spec_name }}(context);
{% endif %}
return version;
}
int gloamLoad{{ api | api_display }}(GloamLoadFunc getProcAddr) {
return gloamLoad{{ api | api_display }}Context(&gloam_{{ fs.spec_name }}_context, getProcAddr);
}
{% elif fs.spec_name == "egl" %}
int gloamLoad{{ api | api_display }}Context({{ u.ctx_arg(', ') }}EGLDisplay display, GloamLoadFunc getProcAddr) {
int version;
uint32_t i;
memset(context, 0, sizeof(*context));
/* Bootstrap: QueryString must be loaded before version detection. */
{%- for cmd in fs.commands %}{% if cmd.name == "eglGetString" or cmd.name == "eglQueryString" %}
context->{{ cmd.short_name }} = ({{ cmd.pfn_type }})getProcAddr("{{ cmd.name }}");
{% endif %}{%- endfor %}
version = gloam_{{ fs.spec_name }}_find_core_{{ api }}(context, display);
if (!version) return 0;
/* Load PFNs for each enabled feature via the range table. */
for (i = 0; i < GLOAM_ARRAYSIZE(kFeatPfnRanges_{{ fs.spec_name | spec_display }}); ++i) {
const GloamPfnRange_t *r = &kFeatPfnRanges_{{ fs.spec_name | spec_display }}[i];
if (context->featArray[r->extension])
gloam_load_pfn_range_{{ fs.spec_name }}(context, getProcAddr, r->start, r->count);
}
{% if fs.extensions | length > 0 and subset | length > 0 %}
if (!gloam_{{ fs.spec_name }}_find_extensions_{{ api }}(context, display)) return 0;
for (i = 0; i < GLOAM_ARRAYSIZE(kExtPfnRanges_{{ api }}); ++i) {
const GloamPfnRange_t *r = &kExtPfnRanges_{{ api }}[i];
if (context->extArray[r->extension])
gloam_load_pfn_range_{{ fs.spec_name }}(context, getProcAddr, r->start, r->count);
}
{% endif -%}
{% if alias and fs.alias_pairs | length > 0 %}
gloam_resolve_aliases_{{ fs.spec_name }}(context);
{% endif %}
return version;
}
int gloamLoad{{ api | api_display }}(EGLDisplay display, GloamLoadFunc getProcAddr) {
return gloamLoad{{ api | api_display }}Context(&gloam_{{ fs.spec_name }}_context, display, getProcAddr);
}
{% elif fs.spec_name == "glx" %}
int gloamLoad{{ api | api_display }}Context({{ u.ctx_arg(', ') }}Display *display, int screen, GloamLoadFunc getProcAddr) {
int version;
uint32_t i;
memset(context, 0, sizeof(*context));
{% for cmd in fs.commands %}{% if cmd.name == "glXQueryVersion" %}
context->{{ cmd.short_name }} = ({{ cmd.pfn_type }})getProcAddr("{{ cmd.name }}");
{% endif %}{%- endfor %}
version = gloam_{{ fs.spec_name }}_find_core_{{ api }}(context, &display, &screen);
if (!version) return 0;
/* Load all PFNs upfront. */
for (i = 0; i < GLOAM_ARRAYSIZE(kFnNames_{{ fs.spec_name | spec_display }}); ++i)
context->pfnArray[i] = (void *)getProcAddr(kFnNames_{{ fs.spec_name | spec_display }}[i]);
/* Mark features based on PFN availability. */
for (i = 0; i < GLOAM_ARRAYSIZE(kFeatPfnRanges_{{ fs.spec_name | spec_display }}); ++i) {
const GloamPfnRange_t *r = &kFeatPfnRanges_{{ fs.spec_name | spec_display }}[i];
uint16_t j; int ok = 1;
for (j = r->start; j < (uint16_t)(r->start + r->count); ++j)
ok &= (context->pfnArray[j] != NULL);
if (ok) context->featArray[r->extension] = 1;
}
{% if fs.extensions | length > 0 and subset | length > 0 %}
if (!gloam_{{ fs.spec_name }}_find_extensions_{{ api }}(context, display, screen)) return 0;
for (i = 0; i < GLOAM_ARRAYSIZE(kExtPfnRanges_{{ api }}); ++i) {
const GloamPfnRange_t *r = &kExtPfnRanges_{{ api }}[i];
if (context->extArray[r->extension])
gloam_load_pfn_range_{{ fs.spec_name }}(context, getProcAddr, r->start, r->count);
}
{% endif -%}
{% if alias and fs.alias_pairs | length > 0 %}
gloam_resolve_aliases_{{ fs.spec_name }}(context);
{% endif %}
return 1;
}
int gloamLoad{{ api | api_display }}(Display *display, int screen, GloamLoadFunc getProcAddr) {
return gloamLoad{{ api | api_display }}Context(&gloam_{{ fs.spec_name }}_context, display, screen, getProcAddr);
}
{% elif fs.spec_name == "wgl" -%}
int gloamLoad{{ api | api_display }}Context({{ u.ctx_arg(', ') }}HDC hdc, GloamLoadFunc getProcAddr) {
int version;
uint32_t i;
memset(context, 0, sizeof(*context));
/* WGL mandatory extensions must be loaded first for extension detection. */
{%- for cmd in fs.commands %}
{%- if cmd.name in ["wglGetExtensionsStringARB", "wglGetExtensionsStringEXT"] %}
context->{{ cmd.short_name }} = ({{ cmd.pfn_type }})getProcAddr("{{ cmd.name }}");
{%- endif %}
{%- endfor %}
version = gloam_{{ fs.spec_name }}_find_core_{{ api }}(context);
if (!version) return 0;
/* Load all PFNs upfront. */
for (i = 0; i < GLOAM_ARRAYSIZE(kFnNames_{{ fs.spec_name | spec_display }}); ++i)
context->pfnArray[i] = (void *)getProcAddr(kFnNames_{{ fs.spec_name | spec_display }}[i]);
/* Mark features based on PFN availability. */
for (i = 0; i < GLOAM_ARRAYSIZE(kFeatPfnRanges_{{ fs.spec_name | spec_display }}); ++i) {
const GloamPfnRange_t *r = &kFeatPfnRanges_{{ fs.spec_name | spec_display }}[i];
uint16_t j; int ok = 1;
for (j = r->start; j < (uint16_t)(r->start + r->count); ++j)
ok &= (context->pfnArray[j] != NULL);
if (ok) context->featArray[r->extension] = 1;
}
{% if fs.extensions | length > 0 and subset | length > 0 %}
if (!gloam_{{ fs.spec_name }}_find_extensions_{{ api }}(context, hdc)) return 0;
for (i = 0; i < GLOAM_ARRAYSIZE(kExtPfnRanges_{{ api }}); ++i) {
const GloamPfnRange_t *r = &kExtPfnRanges_{{ api }}[i];
if (context->extArray[r->extension])
gloam_load_pfn_range_{{ fs.spec_name }}(context, getProcAddr, r->start, r->count);
}
{% endif -%}
{% if alias and fs.alias_pairs | length > 0 %}
gloam_resolve_aliases_{{ fs.spec_name }}(context);
{% endif %}
return 1;
}
int gloamLoad{{ api | api_display }}(HDC hdc, GloamLoadFunc getProcAddr) {
return gloamLoad{{ api | api_display }}Context(&gloam_{{ fs.spec_name }}_context, hdc, getProcAddr);
}
{% elif fs.spec_name == "vk" %}
/* Thin adapter so callers using the plain (name, scope) callback form don't
* need to deal with the userptr convention. The callback pointer itself is
* passed as userptr. */
static GloamAPIProc gloam_vk_get_proc_from_userptr(void *userptr, const char *name,
GloamCommandScope scope) {
return ((GloamVkLoadFunc)userptr)(name, scope);
}
/* gloamLoadVulkanContextUserPtr — canonical Vulkan loader.
*
* May be called multiple times on the same context as the application
* progresses through Vulkan initialisation:
* 1. (NULL, NULL, NULL) — loads Global-scope functions; detects instance
* extensions so the caller can choose which to enable.
* 2. (instance, NULL, NULL) — loads Instance-scope functions; detects device
* extensions given the now-live instance.
* 3. (instance, physical_device, device) — loads Device-scope functions.
*
* Each call is additive: context state from previous calls is preserved and
* only new slots (or better-scoped replacements) are filled. */
int gloamLoadVulkanContextUserPtr({{ u.ctx_arg(', ') }}VkInstance instance,
VkPhysicalDevice physical_device, VkDevice device,
GloamVkUserptrLoadFunc load, void *userptr) {
int version;
uint32_t i;
GLOAM_UNUSED(instance);
GLOAM_UNUSED(device);
/* Bootstrap: EnumerateInstanceVersion is Global-scope — it can be loaded
before any instance exists — and must be available before find_core. */
#ifdef VK_VERSION_1_1
if (!context->EnumerateInstanceVersion)
context->EnumerateInstanceVersion = (PFN_vkEnumerateInstanceVersion)
load(userptr, "vkEnumerateInstanceVersion", GloamCommandScopeGlobal);
#endif
version = gloam_vk_find_core(context, physical_device);
if (!version) return 0;
/* Load PFNs for every enabled feature via the range table. */
for (i = 0; i < GLOAM_ARRAYSIZE(kFeatPfnRanges_{{ fs.spec_name | spec_display }}); ++i) {
const GloamPfnRange_t *r = &kFeatPfnRanges_{{ fs.spec_name | spec_display }}[i];
if (context->featArray[r->extension])
gloam_load_pfn_range_{{ fs.spec_name }}(context, load, userptr, r->start, r->count);
}
{% if fs.extensions | length > 0 and subset | length > 0 %}
if (!gloam_{{ fs.spec_name }}_find_extensions_{{ api }}(context, physical_device)) return 0;
for (i = 0; i < GLOAM_ARRAYSIZE(kExtPfnRanges_{{ api }}); ++i) {
const GloamPfnRange_t *r = &kExtPfnRanges_{{ api }}[i];
if (context->extArray[r->extension])
gloam_load_pfn_range_{{ fs.spec_name }}(context, load, userptr, r->start, r->count);
}
{% endif -%}
{% if alias and fs.alias_pairs | length > 0 %}
gloam_resolve_aliases_{{ fs.spec_name }}(context);
{% endif %}
return version;
}
int gloamLoadVulkanContext({{ u.ctx_arg(', ') }}VkInstance instance,
VkPhysicalDevice physical_device, VkDevice device,
GloamVkLoadFunc load) {
return gloamLoadVulkanContextUserPtr(context, instance, physical_device, device,
gloam_vk_get_proc_from_userptr, (void *)load);
}
int gloamLoadVulkanUserPtr(VkInstance instance, VkPhysicalDevice physical_device,
VkDevice device, GloamVkUserptrLoadFunc load, void *userptr) {
return gloamLoadVulkanContextUserPtr(&gloam_vk_context, instance, physical_device,
device, load, userptr);
}
int gloamLoadVulkan(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device,
GloamVkLoadFunc load) {
return gloamLoadVulkanContext(&gloam_vk_context, instance, physical_device, device, load);
}
{% endif %}
{% endfor %}{# end for api in fs.apis #}
{%- if loader -%}
/* ==========================================================================
Built-in loader (--loader)
========================================================================== */
{% include "loader.j2" -%}
{% endif -%}
{% if fs.spec_name == "glx" -%}
#endif /* __linux */
{% elif fs.spec_name == "wgl" -%}
#endif /* _WIN32 */
{% endif %}