About Social Code
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--meson.build3
-rw-r--r--meson.options2
-rw-r--r--src/compiler/meson.build2
-rw-r--r--src/frygon/.rustfmt.toml1
-rw-r--r--src/frygon/compiler/fgcc.h33
-rw-r--r--src/frygon/compiler/fgcc/api.rs108
-rw-r--r--src/frygon/compiler/fgcc/lib.rs4
-rw-r--r--src/frygon/compiler/fgcc_bindings.h6
-rw-r--r--src/frygon/compiler/fgcc_nir.c12
-rw-r--r--src/frygon/compiler/fgcc_private.h24
-rw-r--r--src/frygon/compiler/meson.build107
-rw-r--r--src/frygon/headers/fg_device_info.h14
-rw-r--r--src/frygon/headers/meson.build6
-rw-r--r--src/frygon/meson.build18
-rw-r--r--src/frygon/vulkan/fgvk_buffer.c89
-rw-r--r--src/frygon/vulkan/fgvk_buffer.h19
-rw-r--r--src/frygon/vulkan/fgvk_cmd_buffer.c93
-rw-r--r--src/frygon/vulkan/fgvk_cmd_buffer.h42
-rw-r--r--src/frygon/vulkan/fgvk_cmd_clear.c16
-rw-r--r--src/frygon/vulkan/fgvk_cmd_copy.c11
-rw-r--r--src/frygon/vulkan/fgvk_cmd_draw.c37
-rw-r--r--src/frygon/vulkan/fgvk_cmd_pool.c5
-rw-r--r--src/frygon/vulkan/fgvk_cmd_pool.h25
-rw-r--r--src/frygon/vulkan/fgvk_debug.h12
-rw-r--r--src/frygon/vulkan/fgvk_device.c76
-rw-r--r--src/frygon/vulkan/fgvk_device.h19
-rw-r--r--src/frygon/vulkan/fgvk_device_memory.c81
-rw-r--r--src/frygon/vulkan/fgvk_device_memory.h21
-rw-r--r--src/frygon/vulkan/fgvk_format.c21
-rw-r--r--src/frygon/vulkan/fgvk_format.h8
-rw-r--r--src/frygon/vulkan/fgvk_image.c113
-rw-r--r--src/frygon/vulkan/fgvk_image.h18
-rw-r--r--src/frygon/vulkan/fgvk_image_view.c42
-rw-r--r--src/frygon/vulkan/fgvk_image_view.h19
-rw-r--r--src/frygon/vulkan/fgvk_instance.c128
-rw-r--r--src/frygon/vulkan/fgvk_instance.h25
-rw-r--r--src/frygon/vulkan/fgvk_physical_device.c573
-rw-r--r--src/frygon/vulkan/fgvk_physical_device.h32
-rw-r--r--src/frygon/vulkan/fgvk_private.h19
-rw-r--r--src/frygon/vulkan/fgvk_queue.c44
-rw-r--r--src/frygon/vulkan/fgvk_queue.h22
-rw-r--r--src/frygon/vulkan/fgvk_shader.c204
-rw-r--r--src/frygon/vulkan/fgvk_shader.h23
-rw-r--r--src/frygon/vulkan/meson.build106
-rw-r--r--src/meson.build3
-rw-r--r--src/vulkan/runtime/vk_sync.c3
-rw-r--r--src/vulkan/runtime/vk_sync_dummy.c9
47 files changed, 2294 insertions, 4 deletions
diff --git a/meson.build b/meson.build
index f241ba862cd..a0465112836 100644
--- a/meson.build
+++ b/meson.build
@@ -289,6 +289,7 @@ with_nouveau_vk = _vulkan_drivers.contains('nouveau')
with_asahi_vk = _vulkan_drivers.contains('asahi')
with_gfxstream_vk = _vulkan_drivers.contains('gfxstream')
with_kosmickrisp_vk = _vulkan_drivers.contains('kosmickrisp')
+with_frygon_vk = _vulkan_drivers.contains('frygon')
with_any_vk = _vulkan_drivers.length() != 0
with_llvm = with_llvm \
@@ -754,7 +755,7 @@ if with_gallium_rusticl
endif
with_virtgpu_kumquat = get_option('virtgpu_kumquat') and with_gfxstream_vk
-if with_gallium_rusticl or with_nouveau_vk or with_tools.contains('etnaviv') or with_virtgpu_kumquat
+if with_gallium_rusticl or with_nouveau_vk or with_tools.contains('etnaviv') or with_virtgpu_kumquat or with_frygon_vk
# rust.bindgen() does not pass `--rust-target` to bindgen until 1.7.0.
if meson.version().version_compare('< 1.7.0')
error('Mesa Rust support requires Meson 1.7.0 or newer')
diff --git a/meson.options b/meson.options
index 75731475c12..beedcb9836e 100644
--- a/meson.options
+++ b/meson.options
@@ -209,7 +209,7 @@ option(
choices : ['auto', 'amd', 'broadcom', 'freedreno', 'intel', 'intel_hasvk',
'panfrost', 'swrast', 'virtio', 'imagination',
'microsoft-experimental', 'nouveau', 'asahi', 'gfxstream',
- 'kosmickrisp', 'all'],
+ 'kosmickrisp', 'frygon', 'all'],
description : 'List of vulkan drivers to build. If this is set to auto ' +
'all drivers applicable to the target OS/architecture ' +
'will be built'
diff --git a/src/compiler/meson.build b/src/compiler/meson.build
index 92e72a44f9a..3404bc0c445 100644
--- a/src/compiler/meson.build
+++ b/src/compiler/meson.build
@@ -72,6 +72,6 @@ if with_gallium
endif
subdir('isaspec')
-if with_nouveau_vk
+if with_nouveau_vk or with_frygon_vk
subdir('rust')
endif
diff --git a/src/frygon/.rustfmt.toml b/src/frygon/.rustfmt.toml
new file mode 100644
index 00000000000..df99c69198f
--- /dev/null
+++ b/src/frygon/.rustfmt.toml
@@ -0,0 +1 @@
+max_width = 80
diff --git a/src/frygon/compiler/fgcc.h b/src/frygon/compiler/fgcc.h
new file mode 100644
index 00000000000..a611818d19e
--- /dev/null
+++ b/src/frygon/compiler/fgcc.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#ifndef FGCC_H
+#define FGCC_H 1
+
+#include "compiler/shader_enums.h"
+#include "nir_defines.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct fgcc_compiler;
+struct fg_device_info;
+
+struct fgcc_compiler *fgcc_compiler_create(const struct fg_device_info *dev);
+
+const struct nir_shader_compiler_options *
+fgcc_nir_options(const struct fgcc_compiler *fgcc);
+
+void fgcc_preprocess_nir(nir_shader *nir, const struct fgcc_compiler *fgcc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FGCC_H */
diff --git a/src/frygon/compiler/fgcc/api.rs b/src/frygon/compiler/fgcc/api.rs
new file mode 100644
index 00000000000..ff09c213305
--- /dev/null
+++ b/src/frygon/compiler/fgcc/api.rs
@@ -0,0 +1,108 @@
+// Copyright © 2025 Lucas Francisco Fryzek
+// SPDX-License-Identifier: MIT
+
+use compiler::bindings::*;
+use fgcc_bindings::*;
+
+fn nir_options(dev: &fg_device_info) -> nir_shader_compiler_options {
+ let mut op: nir_shader_compiler_options = Default::default();
+
+ op.lower_fdiv = true;
+ op.fuse_ffma16 = true;
+ op.fuse_ffma32 = true;
+ op.fuse_ffma64 = true;
+ op.lower_flrp16 = true;
+ op.lower_flrp32 = true;
+ op.lower_flrp64 = true;
+ op.lower_fsqrt = true; // TODO
+ op.lower_bitfield_extract = false;
+ op.lower_bitfield_extract8 = true;
+ op.lower_bitfield_extract16 = true;
+ op.lower_bitfield_insert = true;
+ op.lower_pack_half_2x16 = true;
+ op.lower_pack_unorm_2x16 = true;
+ op.lower_pack_snorm_2x16 = true;
+ op.lower_pack_unorm_4x8 = true;
+ op.lower_pack_snorm_4x8 = true;
+ op.lower_unpack_half_2x16 = true;
+ op.lower_unpack_unorm_2x16 = true;
+ op.lower_unpack_snorm_2x16 = true;
+ op.lower_unpack_unorm_4x8 = true;
+ op.lower_unpack_snorm_4x8 = true;
+ op.lower_insert_byte = true;
+ op.lower_insert_word = true;
+ op.lower_cs_local_index_to_id = true;
+ op.lower_device_index_to_zero = true;
+ op.lower_isign = true;
+ op.lower_uadd_sat = true; // TODO
+ op.lower_usub_sat = true; // TODO
+ op.lower_iadd_sat = true; // TODO
+ op.lower_doubles_options = nir_lower_drcp
+ | nir_lower_dsqrt
+ | nir_lower_drsq
+ | nir_lower_dtrunc
+ | nir_lower_dfloor
+ | nir_lower_dceil
+ | nir_lower_dfract
+ | nir_lower_dround_even
+ | nir_lower_dsat
+ | nir_lower_dminmax;
+ op.lower_int64_options = !(nir_lower_icmp64
+ | nir_lower_iadd64
+ | nir_lower_ineg64
+ | nir_lower_shift64
+ | nir_lower_imul_2x32_64
+ | nir_lower_vote_ieq64
+ | nir_lower_conv64)
+ | nir_lower_vote_ieq64
+ | nir_lower_shift64;
+ op.lower_ldexp = true;
+ op.lower_fmod = true;
+ op.lower_ffract = true;
+ op.lower_fpow = true;
+ op.lower_scmp = true;
+ op.lower_uadd_carry = true;
+ op.lower_usub_borrow = true;
+ /*
+ op.has_iadd3 = dev.sm >= 70;
+ op.has_imad32 = dev.sm >= 70;
+ op.has_sdot_4x8 = dev.sm >= 70;
+ op.has_udot_4x8 = dev.sm >= 70;
+ op.has_sudot_4x8 = dev.sm >= 70;
+ */
+ // We set .ftz on f32 by default so we can support fmulz whenever the client
+ // doesn't explicitly request denorms.
+ op.has_fmulz_no_denorms = true;
+ op.has_find_msb_rev = true;
+ op.has_pack_half_2x16_rtz = true;
+ //op.has_bfm = dev.sm >= 70;
+ op.discard_is_demote = true;
+
+ op.max_unroll_iterations = 32;
+ op.scalarize_ddx = true;
+
+ op
+}
+
+#[no_mangle]
+pub extern "C" fn fgcc_nir_options(
+ fgcc: *const fgcc_compiler,
+) -> *const nir_shader_compiler_options {
+ assert!(!fgcc.is_null());
+ let fgcc = unsafe { &*fgcc };
+ &fgcc.nir_options
+}
+
+#[no_mangle]
+pub extern "C" fn fgcc_compiler_create(
+ dev: *const fg_device_info,
+) -> *mut fgcc_compiler {
+ assert!(!dev.is_null());
+ let dev = unsafe { &*dev };
+
+ let nak = Box::new(fgcc_compiler {
+ nir_options: nir_options(dev),
+ });
+
+ Box::into_raw(nak)
+}
diff --git a/src/frygon/compiler/fgcc/lib.rs b/src/frygon/compiler/fgcc/lib.rs
new file mode 100644
index 00000000000..413edd9af39
--- /dev/null
+++ b/src/frygon/compiler/fgcc/lib.rs
@@ -0,0 +1,4 @@
+// Copyright © 2025 Lucas Francisco Fryzek
+// SPDX-License-Identifier: MIT
+
+mod api;
diff --git a/src/frygon/compiler/fgcc_bindings.h b/src/frygon/compiler/fgcc_bindings.h
new file mode 100644
index 00000000000..07b65794584
--- /dev/null
+++ b/src/frygon/compiler/fgcc_bindings.h
@@ -0,0 +1,6 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+
+#include "fgcc_private.h"
diff --git a/src/frygon/compiler/fgcc_nir.c b/src/frygon/compiler/fgcc_nir.c
new file mode 100644
index 00000000000..11a56bfa54a
--- /dev/null
+++ b/src/frygon/compiler/fgcc_nir.c
@@ -0,0 +1,12 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+
+#include "fgcc_private.h"
+#include "nir_builder.h"
+
+void
+fgcc_preprocess_nir(nir_shader *nir, const struct fgcc_compiler *fgcc)
+{
+}
diff --git a/src/frygon/compiler/fgcc_private.h b/src/frygon/compiler/fgcc_private.h
new file mode 100644
index 00000000000..82bdaa12343
--- /dev/null
+++ b/src/frygon/compiler/fgcc_private.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#ifndef FGCC_PRIAVTE_H
+#define FGCC_PRIVATE_H 1
+
+#include "fgcc.h"
+#include "nir.h"
+#include "fg_device_info.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct fgcc_compiler {
+ struct nir_shader_compiler_options nir_options;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/frygon/compiler/meson.build b/src/frygon/compiler/meson.build
new file mode 100644
index 00000000000..378d7c41e8f
--- /dev/null
+++ b/src/frygon/compiler/meson.build
@@ -0,0 +1,107 @@
+# Copyright © 2025 Lucas Francisco Fryzek
+# SPDX-License-Identifier: MIT
+
+fgcc_rust_args = [
+ rust_2024_lint_args,
+ '-Aclippy::identity_op',
+ '-Aclippy::len_zero',
+ '-Aclippy::manual_range_contains',
+ # normally this is a good one, but we use it where the "better" code is worse
+ '-Aclippy::needless_range_loop',
+ '-Aclippy::redundant_field_names',
+ '-Aclippy::upper_case_acronyms',
+ '-Aclippy::vec_box',
+ '-Aclippy::write_with_newline',
+ # warns about public function might dereference a raw pointer, but nothing is
+ # actually public here
+ '-Aclippy::not_unsafe_ptr_arg_deref',
+ '-Anon_snake_case',
+]
+
+libfgcc_c_files = files(
+ 'fgcc.h',
+ 'fgcc_nir.c',
+)
+
+libfgcc_deps = [
+ idep_mesautil,
+ idep_nir_headers,
+ idep_fg_headers,
+]
+
+_fgcc_bindings_rs = rust.bindgen(
+ input : ['fgcc_bindings.h'],
+ output : 'fgcc_bindings.rs',
+ c_args : [
+ pre_args,
+ ],
+ args : [
+ bindgen_output_args,
+ compiler_rs_bindgen_blocklist,
+ '--raw-line', 'use compiler::bindings::*;',
+ #'--allowlist-type', 'drm.*',
+ '--allowlist-type', 'fgcc_.*',
+ #'--allowlist-type', 'nouveau_ws_.*',
+ #'--allowlist-var', 'DRM_.*',
+ '--allowlist-var', 'FGCC_.*',
+ #'--allowlist-var', 'NVIDIA_VENDOR_ID',
+ #'--allowlist-function', 'drm.*',
+ '--allowlist-function', 'fgcc_.*',
+ #'--allowlist-function', 'nouveau_ws_.*',
+ # provided through compiler::bindings::*
+ '--blocklist-type', 'glsl_.*',
+ '--no-prepend-enum-name',
+ '--with-derive-default',
+ ],
+ dependencies : [
+ dep_libdrm,
+ libfgcc_deps,
+ ],
+)
+
+_libfgcc_bindings_rs = static_library(
+ 'fgcc_bindings',
+ _fgcc_bindings_rs,
+ gnu_symbol_visibility : 'hidden',
+ dependencies : [
+ idep_compiler_rs,
+ ],
+ rust_abi : 'rust',
+)
+
+_libfgcc_rs = static_library(
+ 'fgcc_rs',
+ files('fgcc/lib.rs'),
+ gnu_symbol_visibility : 'hidden',
+ rust_abi : 'c',
+ rust_args : [
+ fgcc_rust_args,
+ # Otherwise, rustc trips up on -pthread
+ '-Clink-arg=-Wno-unused-command-line-argument',
+ ],
+ dependencies : [
+ #dep_paste,
+ #dep_rustc_hash,
+ #idep_bitview_rs,
+ idep_compiler_rs,
+ ],
+ link_with : [
+ _libfgcc_bindings_rs,
+ #_libnak_ir_proc_rs,
+ ],
+)
+
+_libfgcc = static_library(
+ 'fgcc',
+ [libfgcc_c_files],
+ include_directories: [inc_include, inc_src, inc_gallium],
+ dependencies : libfgcc_deps,
+ link_with : [_libfgcc_rs],
+ c_args : [no_override_init_args],
+ gnu_symbol_visibility : 'hidden',
+)
+
+idep_fgcc = declare_dependency(
+ include_directories : include_directories('.'),
+ link_with : _libfgcc,
+)
diff --git a/src/frygon/headers/fg_device_info.h b/src/frygon/headers/fg_device_info.h
new file mode 100644
index 00000000000..20a243f6c84
--- /dev/null
+++ b/src/frygon/headers/fg_device_info.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#ifndef FG_DEVICE_INFO_H
+#define FG_DEVICE_INFO_H 1
+
+struct fg_device_info {
+ uint16_t device_id;
+
+ char device_name[64];
+};
+
+#endif
diff --git a/src/frygon/headers/meson.build b/src/frygon/headers/meson.build
new file mode 100644
index 00000000000..664363bed08
--- /dev/null
+++ b/src/frygon/headers/meson.build
@@ -0,0 +1,6 @@
+# Copyright © 2025 Lucas Francisco Fryzek
+# SPDX-License-Identifier: MIT
+
+idep_fg_headers = declare_dependency(
+ include_directories : include_directories('.'),
+)
diff --git a/src/frygon/meson.build b/src/frygon/meson.build
new file mode 100644
index 00000000000..fdfbf9e94d4
--- /dev/null
+++ b/src/frygon/meson.build
@@ -0,0 +1,18 @@
+# Copyright © 2025 Lucas Francisco Fryzek
+# SPDX-License-Identifier: MIT
+
+#subdir('drm')
+subdir('headers')
+#subdir('winsys')
+#subdir('rust')
+#subdir('nil')
+subdir('compiler')
+
+# Probably not needed until we have real HW
+#if with_tools.contains('drm-shim')
+# subdir('drm-shim')
+#endif
+
+# We are always building vk
+#subdir('mme')
+subdir('vulkan')
diff --git a/src/frygon/vulkan/fgvk_buffer.c b/src/frygon/vulkan/fgvk_buffer.c
new file mode 100644
index 00000000000..f5b8f70758b
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_buffer.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#include "fgvk_buffer.h"
+
+#include "fgvk_device.h"
+#include "fgvk_device_memory.h"
+
+VKAPI_ATTR VkResult VKAPI_CALL
+fgvk_CreateBuffer(VkDevice device,
+ const VkBufferCreateInfo *pCreateInfo,
+ const VkAllocationCallbacks *pAllocator,
+ VkBuffer *pBuffer)
+{
+ VK_FROM_HANDLE(fgvk_device, dev, device);
+ struct fgvk_buffer *buffer;
+
+ buffer = vk_buffer_create(&dev->vk, pCreateInfo, pAllocator,
+ sizeof(*buffer));
+
+ if (!buffer)
+ return vk_error(dev, VK_ERROR_OUT_OF_HOST_MEMORY);
+
+ *pBuffer = fgvk_buffer_to_handle(buffer);
+
+ return VK_SUCCESS;
+}
+
+VKAPI_ATTR void VKAPI_CALL
+fgvk_DestroyBuffer(VkDevice device,
+ VkBuffer _buffer,
+ const VkAllocationCallbacks *pAllocator)
+{
+ VK_FROM_HANDLE(fgvk_device, dev, device);
+ VK_FROM_HANDLE(fgvk_buffer, buffer, _buffer);
+
+ if (!buffer)
+ return;
+
+ vk_buffer_destroy(&dev->vk, pAllocator, &buffer->vk);
+}
+
+VKAPI_ATTR void VKAPI_CALL
+fgvk_GetDeviceBufferMemoryRequirements(
+ VkDevice device,
+ const VkDeviceBufferMemoryRequirements *pInfo,
+ VkMemoryRequirements2 *pMemoryRequirements)
+{
+ pMemoryRequirements->memoryRequirements = (VkMemoryRequirements) {
+ .size = align64(pInfo->pCreateInfo->size, 8),
+ .alignment = 8,
+ .memoryTypeBits = 1,
+ };
+}
+
+static VkResult
+fgvk_bind_buffer_memory(struct fgvk_device *dev,
+ const VkBindBufferMemoryInfo *info)
+{
+ VK_FROM_HANDLE(fgvk_device_memory, mem, info->memory);
+ VK_FROM_HANDLE(fgvk_buffer, buffer, info->buffer);
+ VkResult result = VK_SUCCESS;
+
+ return result;
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL
+fgvk_BindBufferMemory2(VkDevice device,
+ uint32_t bindInfoCount,
+ const VkBindBufferMemoryInfo *pBindInfos)
+{
+ VK_FROM_HANDLE(fgvk_device, dev, device);
+ VkResult first_error_or_success = VK_SUCCESS;
+
+ for (uint32_t i = 0; i < bindInfoCount; ++i) {
+ VkResult result = fgvk_bind_buffer_memory(dev, &pBindInfos[i]);
+
+ const VkBindMemoryStatusKHR *status =
+ vk_find_struct_const(pBindInfos[i].pNext, BIND_MEMORY_STATUS_KHR);
+ if (status != NULL && status->pResult != NULL)
+ *status->pResult = result;
+
+ if (first_error_or_success == VK_SUCCESS)
+ first_error_or_success = result;
+ }
+
+ return first_error_or_success;
+}
diff --git a/src/frygon/vulkan/fgvk_buffer.h b/src/frygon/vulkan/fgvk_buffer.h
new file mode 100644
index 00000000000..db287b587ea
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_buffer.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#ifndef FGVK_BUFFER_H
+#define FGVK_BUFFER_H 1
+
+#include "fgvk_private.h"
+
+#include "vk_buffer.h"
+
+struct fgvk_buffer {
+ struct vk_buffer vk;
+};
+
+VK_DEFINE_NONDISP_HANDLE_CASTS(fgvk_buffer, vk.base, VkBuffer,
+ VK_OBJECT_TYPE_BUFFER)
+
+#endif
diff --git a/src/frygon/vulkan/fgvk_cmd_buffer.c b/src/frygon/vulkan/fgvk_cmd_buffer.c
new file mode 100644
index 00000000000..181a8f8e819
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_cmd_buffer.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#include "fgvk_cmd_buffer.h"
+
+#include "fgvk_cmd_pool.h"
+
+static VkResult
+fgvk_create_cmd_buffer(struct vk_command_pool *vk_pool,
+ VkCommandBufferLevel level,
+ struct vk_command_buffer **cmd_buffer_out)
+{
+ struct fgvk_cmd_pool *pool = container_of(vk_pool, struct fgvk_cmd_pool, vk);
+ struct fgvk_device *dev = fgvk_cmd_pool_device(pool);
+ struct fgvk_cmd_buffer *cmd;
+ VkResult result;
+
+ cmd = vk_zalloc(&pool->vk.alloc, sizeof(*cmd), 8,
+ VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+
+ if (cmd == NULL)
+ return vk_error(dev, VK_ERROR_OUT_OF_HOST_MEMORY);
+
+ result = vk_command_buffer_init(&pool->vk, &cmd->vk,
+ &fgvk_cmd_buffer_ops, level);
+ if (result != VK_SUCCESS) {
+ vk_free(&pool->vk.alloc, cmd);
+ return result;
+ }
+
+ cmd->vk.dynamic_graphics_state.vi = &cmd->state.gfx._dynamic_vi;
+ cmd->vk.dynamic_graphics_state.ms.sample_locations = &cmd->state.gfx._dynamic_sl;
+
+ *cmd_buffer_out = &cmd->vk;
+
+ return VK_SUCCESS;
+}
+
+static void
+fgvk_reset_cmd_buffer(struct vk_command_buffer *vk_cmd_buffer,
+ UNUSED VkCommandBufferResetFlags flags)
+{
+ struct fgvk_cmd_buffer *cmd =
+ container_of(vk_cmd_buffer, struct fgvk_cmd_buffer, vk);
+
+ vk_command_buffer_reset(&cmd->vk);
+}
+
+static void
+fgvk_destroy_cmd_buffer(struct vk_command_buffer *vk_cmd_buffer)
+{
+ struct fgvk_cmd_buffer *cmd =
+ container_of(vk_cmd_buffer, struct fgvk_cmd_buffer, vk);
+ struct fgvk_cmd_pool *pool = fgvk_cmd_buffer_pool(cmd);
+
+ vk_command_buffer_finish(&cmd->vk);
+ vk_free(&pool->vk.alloc, cmd);
+}
+
+const struct vk_command_buffer_ops fgvk_cmd_buffer_ops = {
+ .create = fgvk_create_cmd_buffer,
+ .reset = fgvk_reset_cmd_buffer,
+ .destroy = fgvk_destroy_cmd_buffer,
+};
+
+void
+fgvk_cmd_bind_shaders(struct vk_command_buffer *vk_cmd,
+ uint32_t stage_count,
+ const mesa_shader_stage *stages,
+ struct vk_shader ** const shaders)
+{
+ //UNREACHABLE("TODO");
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL
+fgvk_BeginCommandBuffer(VkCommandBuffer commandBuffer,
+ const VkCommandBufferBeginInfo *pBeginInfo)
+{
+ return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL
+fgvk_EndCommandBuffer(VkCommandBuffer commandBuffer)
+{
+ return VK_SUCCESS;
+}
+
+VKAPI_ATTR void VKAPI_CALL
+fgvk_CmdPipelineBarrier2(VkCommandBuffer commandBuffer,
+ const VkDependencyInfo *pDependencyInfo)
+{
+}
diff --git a/src/frygon/vulkan/fgvk_cmd_buffer.h b/src/frygon/vulkan/fgvk_cmd_buffer.h
new file mode 100644
index 00000000000..151ed631166
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_cmd_buffer.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#ifndef FGVK_CMD_BUFFER_H
+#define FGVK_CMD_BUFFER_H 1
+
+#include "fgvk_private.h"
+
+#include "vk_command_buffer.h"
+#include "vk_shader.h"
+
+struct fgvk_graphics_state {
+ struct vk_vertex_input_state _dynamic_vi;
+ struct vk_sample_locations_state _dynamic_sl;
+};
+
+struct fgvk_cmd_buffer {
+ struct vk_command_buffer vk;
+
+ struct fgvk_cmd_state {
+ struct fgvk_graphics_state gfx;
+ } state;
+};
+
+VK_DEFINE_HANDLE_CASTS(fgvk_cmd_buffer, vk.base, VkCommandBuffer,
+ VK_OBJECT_TYPE_COMMAND_BUFFER)
+
+static inline struct fgvk_cmd_pool *
+fgvk_cmd_buffer_pool(struct fgvk_cmd_buffer *cmd)
+{
+ return (struct fgvk_cmd_pool *)cmd->vk.pool;
+}
+
+extern const struct vk_command_buffer_ops fgvk_cmd_buffer_ops;
+
+void fgvk_cmd_bind_shaders(struct vk_command_buffer *vk_cmd,
+ uint32_t stage_count,
+ const mesa_shader_stage *stages,
+ struct vk_shader ** const shaders);
+
+#endif
diff --git a/src/frygon/vulkan/fgvk_cmd_clear.c b/src/frygon/vulkan/fgvk_cmd_clear.c
new file mode 100644
index 00000000000..67702989769
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_cmd_clear.c
@@ -0,0 +1,16 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#include "fgvk_cmd_buffer.h"
+
+VKAPI_ATTR void VKAPI_CALL
+fgvk_CmdClearColorImage(VkCommandBuffer commandBuffer,
+ VkImage _image,
+ VkImageLayout imageLayout,
+ const VkClearColorValue *pColor,
+ uint32_t rangeCount,
+ const VkImageSubresourceRange *pRanges)
+{
+ // TODO actually get hardware to clear the image
+}
diff --git a/src/frygon/vulkan/fgvk_cmd_copy.c b/src/frygon/vulkan/fgvk_cmd_copy.c
new file mode 100644
index 00000000000..5fe668264e8
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_cmd_copy.c
@@ -0,0 +1,11 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#include <fgvk_cmd_buffer.h>
+
+VKAPI_ATTR void VKAPI_CALL
+fgvk_CmdCopyImage2(VkCommandBuffer commandBuffer,
+ const VkCopyImageInfo2 *pCopyImageInfo)
+{
+}
diff --git a/src/frygon/vulkan/fgvk_cmd_draw.c b/src/frygon/vulkan/fgvk_cmd_draw.c
new file mode 100644
index 00000000000..15f6b29c05c
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_cmd_draw.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#include "fgvk_cmd_buffer.h"
+
+VKAPI_ATTR void VKAPI_CALL
+fgvk_CmdBeginRendering(VkCommandBuffer commandBuffer,
+ const VkRenderingInfo *pRenderingInfo)
+{
+}
+
+VKAPI_ATTR void VKAPI_CALL
+fgvk_CmdEndRendering2KHR(VkCommandBuffer commandBuffer,
+ const VkRenderingEndInfoKHR *pRenderingEndInfo)
+{
+}
+
+VKAPI_ATTR void VKAPI_CALL
+fgvk_CmdBindVertexBuffers2(VkCommandBuffer commandBuffer,
+ uint32_t firstBinding,
+ uint32_t bindingCount,
+ const VkBuffer *pBuffers,
+ const VkDeviceSize *pOffsets,
+ const VkDeviceSize *pSizes,
+ const VkDeviceSize *pStrides)
+{
+}
+
+VKAPI_ATTR void VKAPI_CALL
+fgvk_CmdDraw(VkCommandBuffer commandBuffer,
+ uint32_t vertexCount,
+ uint32_t instanceCount,
+ uint32_t firstVertex,
+ uint32_t firstInstance)
+{
+}
diff --git a/src/frygon/vulkan/fgvk_cmd_pool.c b/src/frygon/vulkan/fgvk_cmd_pool.c
new file mode 100644
index 00000000000..37ffebc1de4
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_cmd_pool.c
@@ -0,0 +1,5 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#include "fgvk_cmd_pool.h"
diff --git a/src/frygon/vulkan/fgvk_cmd_pool.h b/src/frygon/vulkan/fgvk_cmd_pool.h
new file mode 100644
index 00000000000..52e3c431be7
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_cmd_pool.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#ifndef FGVK_CMD_POOL_H
+#define FGVK_CMD_POOL_H 1
+
+#include "fgvk_private.h"
+
+#include "vk_command_pool.h"
+
+struct fgvk_cmd_pool {
+ struct vk_command_pool vk;
+};
+
+VK_DEFINE_NONDISP_HANDLE_CASTS(fgvk_cmd_pool, vk.base, VkCommandPool,
+ VK_OBJECT_TYPE_COMMAND_POOL)
+
+static inline struct fgvk_device *
+fgvk_cmd_pool_device(struct fgvk_cmd_pool *pool)
+{
+ return (struct fgvk_device *)pool->vk.base.device;
+}
+
+#endif
diff --git a/src/frygon/vulkan/fgvk_debug.h b/src/frygon/vulkan/fgvk_debug.h
new file mode 100644
index 00000000000..15b434a7828
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_debug.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#ifndef FGVK_DEBUG_H
+#define FGVK_DEBUG_H 1
+
+enum fgvk_debug {
+ FGVK_DEBUG_ALL = 1ull << 0,
+};
+
+#endif
diff --git a/src/frygon/vulkan/fgvk_device.c b/src/frygon/vulkan/fgvk_device.c
new file mode 100644
index 00000000000..6f36e725070
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_device.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#include "fgvk_device.h"
+
+#include "fgvk_physical_device.h"
+#include "fgvk_cmd_buffer.h"
+#include "fgvk_shader.h"
+#include "fgvk_queue.h"
+
+VKAPI_ATTR VkResult VKAPI_CALL
+fgvk_CreateDevice(VkPhysicalDevice physicalDevice,
+ const VkDeviceCreateInfo *pCreateInfo,
+ const VkAllocationCallbacks *pAllocator,
+ VkDevice *pDevice)
+{
+ VK_FROM_HANDLE(fgvk_physical_device, pdev, physicalDevice);
+ VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY;
+ struct fgvk_device *dev;
+
+ dev = vk_zalloc2(&pdev->vk.instance->alloc, pAllocator,
+ sizeof(*dev), 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
+
+ if (!dev)
+ return vk_error(pdev, VK_ERROR_OUT_OF_HOST_MEMORY);
+
+ struct vk_device_dispatch_table dispatch_table;
+ vk_device_dispatch_table_from_entrypoints(&dispatch_table,
+ &fgvk_device_entrypoints, true);
+ //vk_device_dispatch_table_from_entrypoints(&dispatch_table,
+ // &wsi_device_entrypoints, false);
+
+ result = vk_device_init(&dev->vk, &pdev->vk, &dispatch_table,
+ pCreateInfo, pAllocator);
+
+ if (result != VK_SUCCESS)
+ goto fail_alloc;
+
+ dev->vk.shader_ops = &fgvk_device_shader_ops;
+ dev->vk.command_buffer_ops = &fgvk_cmd_buffer_ops;
+
+ for (unsigned i = 0; i < pCreateInfo->queueCreateInfoCount; i++) {
+ for (unsigned q = 0; q < pCreateInfo->pQueueCreateInfos[i].queueCount; q++) {
+ result = fgvk_queue_create(dev, &pCreateInfo->pQueueCreateInfos[i], q);
+ if (result != VK_SUCCESS)
+ goto fail_queues;
+ }
+ }
+
+ *pDevice = fgvk_device_to_handle(dev);
+
+ return result;
+
+fail_queues:
+ vk_foreach_queue_safe(iter, &dev->vk) {
+ struct fgvk_queue *queue = container_of(iter, struct fgvk_queue, vk);
+ fgvk_queue_destroy(dev, queue);
+ }
+fail_alloc:
+ vk_free(&dev->vk.alloc, dev);
+ return result;
+}
+
+VKAPI_ATTR void VKAPI_CALL
+fgvk_DestroyDevice(VkDevice _device, const VkAllocationCallbacks *pAllocator)
+{
+ VK_FROM_HANDLE(fgvk_device, dev, _device);
+
+ if (!dev)
+ return;
+
+ // TODO we can't finish cause sync doesn't work
+ //vk_device_finish(&dev->vk);
+ vk_free(&dev->vk.alloc, dev);
+}
diff --git a/src/frygon/vulkan/fgvk_device.h b/src/frygon/vulkan/fgvk_device.h
new file mode 100644
index 00000000000..51e6873612f
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_device.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#ifndef FGVK_DEVICE_H
+#define FGVK_DEVICE_H 1
+
+#include "fgvk_private.h"
+
+#include "vk_device.h"
+
+struct fgvk_device {
+ struct vk_device vk;
+ int bleh;
+};
+
+VK_DEFINE_HANDLE_CASTS(fgvk_device, vk.base, VkDevice, VK_OBJECT_TYPE_DEVICE)
+
+#endif
diff --git a/src/frygon/vulkan/fgvk_device_memory.c b/src/frygon/vulkan/fgvk_device_memory.c
new file mode 100644
index 00000000000..d4b7b418a2c
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_device_memory.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#include "fgvk_device_memory.h"
+
+#include "fgvk_device.h"
+
+VKAPI_ATTR VkResult VKAPI_CALL
+fgvk_AllocateMemory(VkDevice device,
+ const VkMemoryAllocateInfo *pAllocateInfo,
+ const VkAllocationCallbacks *pAllocator,
+ VkDeviceMemory *pMem)
+{
+ VK_FROM_HANDLE(fgvk_device, dev, device);
+ VkResult result = VK_SUCCESS;
+ struct fgvk_device_memory *mem;
+
+ mem = vk_device_memory_create(&dev->vk, pAllocateInfo,
+ pAllocator, sizeof(*mem));
+ if (!mem)
+ return vk_error(dev, VK_ERROR_OUT_OF_HOST_MEMORY);
+
+ mem->data = malloc(pAllocateInfo->allocationSize);
+
+ if (!mem->data)
+ goto fail_alloc;
+
+ *pMem = fgvk_device_memory_to_handle(mem);
+
+ return VK_SUCCESS;
+
+fail_alloc:
+ vk_device_memory_destroy(&dev->vk, pAllocator, &mem->vk);
+ return result;
+}
+
+VKAPI_ATTR void VKAPI_CALL
+fgvk_FreeMemory(VkDevice device,
+ VkDeviceMemory _mem,
+ const VkAllocationCallbacks *pAllocator)
+{
+ VK_FROM_HANDLE(fgvk_device, dev, device);
+ VK_FROM_HANDLE(fgvk_device_memory, mem, _mem);
+
+ vk_device_memory_destroy(&dev->vk, pAllocator, &mem->vk);
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL
+fgvk_MapMemory2KHR(VkDevice device,
+ const VkMemoryMapInfoKHR *pMemoryMapInfo,
+ void **ppData)
+{
+ VK_FROM_HANDLE(fgvk_device_memory, mem, pMemoryMapInfo->memory);
+
+ *ppData = mem->data;
+ return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL
+fgvk_UnmapMemory2KHR(VkDevice device,
+ const VkMemoryUnmapInfoKHR *pMemoryUnmapInfo)
+{
+ return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL
+fgvk_FlushMappedMemoryRanges(VkDevice device,
+ uint32_t memoryRangeCount,
+ const VkMappedMemoryRange *pMemoryRanges)
+{
+ return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL
+fgvk_InvalidateMappedMemoryRanges(VkDevice device,
+ uint32_t memoryRangeCount,
+ const VkMappedMemoryRange *pMemoryRanges)
+{
+ return VK_SUCCESS;
+}
diff --git a/src/frygon/vulkan/fgvk_device_memory.h b/src/frygon/vulkan/fgvk_device_memory.h
new file mode 100644
index 00000000000..c07cdfc9339
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_device_memory.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#ifndef FGVK_DEVICE_MEMORY_H
+#define FGVK_DEVICE_MEMORY_H 1
+
+#include "fgvk_private.h"
+
+#include "vk_device_memory.h"
+
+struct fgvk_device_memory {
+ struct vk_device_memory vk;
+
+ void *data;
+};
+
+VK_DEFINE_NONDISP_HANDLE_CASTS(fgvk_device_memory, vk.base, VkDeviceMemory,
+ VK_OBJECT_TYPE_DEVICE_MEMORY)
+
+#endif
diff --git a/src/frygon/vulkan/fgvk_format.c b/src/frygon/vulkan/fgvk_format.c
new file mode 100644
index 00000000000..08a5a15e963
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_format.c
@@ -0,0 +1,21 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#include "fgvk_format.h"
+
+#include "fgvk_physical_device.h"
+
+VKAPI_ATTR void VKAPI_CALL
+fgvk_GetPhysicalDeviceFormatProperties2(VkPhysicalDevice physicalDevice,
+ VkFormat format,
+ VkFormatProperties2 *pFormatProperties)
+{
+ VK_FROM_HANDLE(fgvk_physical_device, pdevice, physicalDevice);
+
+ pFormatProperties->formatProperties = (VkFormatProperties) {
+ .linearTilingFeatures = 0,
+ .optimalTilingFeatures = 0,
+ .bufferFeatures = 0,
+ };
+}
diff --git a/src/frygon/vulkan/fgvk_format.h b/src/frygon/vulkan/fgvk_format.h
new file mode 100644
index 00000000000..ff667cd9f58
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_format.h
@@ -0,0 +1,8 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#ifndef FGVK_FORMAT_H
+#define FGVK_FORMAT_H 1
+
+#endif
diff --git a/src/frygon/vulkan/fgvk_image.c b/src/frygon/vulkan/fgvk_image.c
new file mode 100644
index 00000000000..39129a31d48
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_image.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#include "fgvk_image.h"
+
+#include "fgvk_device.h"
+
+VKAPI_ATTR VkResult VKAPI_CALL
+fgvk_CreateImage(VkDevice _device,
+ const VkImageCreateInfo *pCreateInfo,
+ const VkAllocationCallbacks *pAllocator,
+ VkImage *pImage)
+{
+ VK_FROM_HANDLE(fgvk_device, dev, _device);
+ struct fgvk_image *image;
+
+ image = vk_zalloc2(&dev->vk.alloc, pAllocator, sizeof(*image), 8,
+ VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+ if (!image)
+ return vk_error(dev, VK_ERROR_OUT_OF_HOST_MEMORY);
+
+ vk_image_init(&dev->vk, &image->vk, pCreateInfo);
+
+ *pImage = fgvk_image_to_handle(image);
+
+ return VK_SUCCESS;
+}
+
+VKAPI_ATTR void VKAPI_CALL
+fgvk_DestroyImage(VkDevice device,
+ VkImage _image,
+ const VkAllocationCallbacks *pAllocator)
+{
+ VK_FROM_HANDLE(fgvk_device, dev, device);
+ VK_FROM_HANDLE(fgvk_image, image, _image);
+
+ if (!image)
+ return;
+
+ vk_free2(&dev->vk.alloc, pAllocator, image);
+}
+
+VKAPI_ATTR void VKAPI_CALL
+fgvk_GetImageMemoryRequirements2(VkDevice device,
+ const VkImageMemoryRequirementsInfo2 *pInfo,
+ VkMemoryRequirements2 *pMemoryRequirements)
+{
+ VK_FROM_HANDLE(fgvk_device, dev, device);
+ VK_FROM_HANDLE(fgvk_image, image, pInfo->image);
+
+ pMemoryRequirements->memoryRequirements.memoryTypeBits = 1;
+ pMemoryRequirements->memoryRequirements.alignment = 8;
+ pMemoryRequirements->memoryRequirements.size = 4*1024*1024;
+}
+
+static VkResult
+fgvk_bind_image_memory(struct fgvk_device *dev,
+ const VkBindImageMemoryInfo *info)
+{
+ return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL
+fgvk_BindImageMemory2(VkDevice device,
+ uint32_t bindInfoCount,
+ const VkBindImageMemoryInfo *pBindInfos)
+{
+ VK_FROM_HANDLE(fgvk_device, dev, device);
+ VkResult first_error_or_success = VK_SUCCESS;
+
+ for (uint32_t i = 0; i < bindInfoCount; ++i) {
+ VkResult result = fgvk_bind_image_memory(dev, &pBindInfos[i]);
+
+ const VkBindMemoryStatusKHR *status =
+ vk_find_struct_const(pBindInfos[i].pNext, BIND_MEMORY_STATUS_KHR);
+ if (status != NULL && status->pResult != NULL)
+ *status->pResult = VK_SUCCESS;
+
+ if (first_error_or_success == VK_SUCCESS)
+ first_error_or_success = result;
+ }
+
+ return first_error_or_success;
+}
+
+static void
+fgvk_get_image_subresource_layout(struct fgvk_device *dev,
+ struct fgvk_image *image,
+ const VkImageSubresource2KHR *pSubresource,
+ VkSubresourceLayout2KHR *pLayout)
+{
+ const VkImageSubresource *isr = &pSubresource->imageSubresource;
+ pLayout->subresourceLayout = (VkSubresourceLayout) {
+ .offset = 0,
+ .size = image->vk.extent.width * image->vk.extent.height * 4,
+ .rowPitch = image->vk.extent.width * 4,
+ .arrayPitch = image->vk.extent.width * image->vk.extent.height * 4,
+ .depthPitch = 0,
+ };
+}
+
+VKAPI_ATTR void VKAPI_CALL
+fgvk_GetImageSubresourceLayout2KHR(VkDevice device,
+ VkImage _image,
+ const VkImageSubresource2KHR *pSubresource,
+ VkSubresourceLayout2KHR *pLayout)
+{
+ VK_FROM_HANDLE(fgvk_device, dev, device);
+ VK_FROM_HANDLE(fgvk_image, image, _image);
+
+ fgvk_get_image_subresource_layout(dev, image, pSubresource, pLayout);
+}
diff --git a/src/frygon/vulkan/fgvk_image.h b/src/frygon/vulkan/fgvk_image.h
new file mode 100644
index 00000000000..d2592de32c6
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_image.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#ifndef FGVK_IMAGE_H
+#define FGVK_IMAGE_H 1
+
+#include "fgvk_private.h"
+
+#include "vk_image.h"
+
+struct fgvk_image {
+ struct vk_image vk;
+};
+
+VK_DEFINE_NONDISP_HANDLE_CASTS(fgvk_image, vk.base, VkImage, VK_OBJECT_TYPE_IMAGE)
+
+#endif
diff --git a/src/frygon/vulkan/fgvk_image_view.c b/src/frygon/vulkan/fgvk_image_view.c
new file mode 100644
index 00000000000..2a029265c33
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_image_view.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#include "fgvk_image_view.h"
+
+#include "fgvk_device.h"
+
+VKAPI_ATTR VkResult VKAPI_CALL
+fgvk_CreateImageView(VkDevice _device,
+ const VkImageViewCreateInfo *pCreateInfo,
+ const VkAllocationCallbacks *pAllocator,
+ VkImageView *pView)
+{
+ VK_FROM_HANDLE(fgvk_device, dev, _device);
+ struct fgvk_image_view *view;
+
+ view = vk_alloc2(&dev->vk.alloc, pAllocator, sizeof(*view), 8,
+ VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+ if (!view)
+ return vk_error(dev, VK_ERROR_OUT_OF_HOST_MEMORY);
+
+ vk_image_view_init(&dev->vk, &view->vk, pCreateInfo);
+
+ *pView = fgvk_image_view_to_handle(view);
+
+ return VK_SUCCESS;
+}
+
+VKAPI_ATTR void VKAPI_CALL
+fgvk_DestroyImageView(VkDevice _device,
+ VkImageView imageView,
+ const VkAllocationCallbacks *pAllocator)
+{
+ VK_FROM_HANDLE(fgvk_device, dev, _device);
+ VK_FROM_HANDLE(fgvk_image_view, view, imageView);
+
+ if (!view)
+ return;
+
+ vk_free2(&dev->vk.alloc, pAllocator, view);
+}
diff --git a/src/frygon/vulkan/fgvk_image_view.h b/src/frygon/vulkan/fgvk_image_view.h
new file mode 100644
index 00000000000..dcb2acd90e5
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_image_view.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#ifndef FGVK_IMAGE_VIEW_H
+#define FGVK_IMAGE_VIEW_H 1
+
+#include "fgvk_private.h"
+
+#include "vk_image.h"
+
+struct fgvk_image_view {
+ struct vk_image_view vk;
+};
+
+VK_DEFINE_NONDISP_HANDLE_CASTS(fgvk_image_view, vk.base, VkImageView,
+ VK_OBJECT_TYPE_IMAGE_VIEW)
+
+#endif
diff --git a/src/frygon/vulkan/fgvk_instance.c b/src/frygon/vulkan/fgvk_instance.c
new file mode 100644
index 00000000000..c4d23e0c974
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_instance.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+
+#include "fgvk_instance.h"
+
+#include "fgvk_entrypoints.h"
+
+#include "vulkan/wsi/wsi_common.h"
+
+#include "util/build_id.h"
+
+VKAPI_ATTR VkResult VKAPI_CALL
+fgvk_EnumerateInstanceVersion(uint32_t *pApiVersion)
+{
+ uint32_t version_override = vk_get_version_override();
+ *pApiVersion = version_override ? version_override :
+ VK_MAKE_VERSION(1, 0, VK_HEADER_VERSION);
+
+ return VK_SUCCESS;
+}
+
+static const struct vk_instance_extension_table instance_extensions = {
+};
+
+VKAPI_ATTR VkResult VKAPI_CALL
+fgvk_EnumerateInstanceExtensionProperties(const char *pLayerName,
+ uint32_t *pPropertyCount,
+ VkExtensionProperties *pProperties)
+{
+ if (pLayerName)
+ return vk_error(NULL, VK_ERROR_LAYER_NOT_PRESENT);
+
+ return vk_enumerate_instance_extension_properties(
+ &instance_extensions, pPropertyCount, pProperties);
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL
+fgvk_CreateInstance(const VkInstanceCreateInfo *pCreateInfo,
+ const VkAllocationCallbacks *pAllocator,
+ VkInstance *pInstance)
+{
+ struct fgvk_instance *instance;
+ VkResult result;
+
+ if (pAllocator == NULL)
+ pAllocator = vk_default_allocator();
+
+ instance = vk_alloc(pAllocator, sizeof(*instance), 8,
+ VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
+ if (!instance)
+ return vk_error(NULL, VK_ERROR_OUT_OF_HOST_MEMORY);
+
+ struct vk_instance_dispatch_table dispatch_table;
+ vk_instance_dispatch_table_from_entrypoints(&dispatch_table,
+ &fgvk_instance_entrypoints,
+ true);
+ vk_instance_dispatch_table_from_entrypoints(&dispatch_table,
+ &wsi_instance_entrypoints,
+ false);
+
+ result = vk_instance_init(&instance->vk, &instance_extensions,
+ &dispatch_table, pCreateInfo, pAllocator);
+ if (result != VK_SUCCESS)
+ goto fail_alloc;
+
+ //fgvk_init_debug_flags(instance);
+
+ instance->vk.physical_devices.enumerate = fgvk_physical_device_enumerate;
+ instance->vk.physical_devices.destroy = fgvk_physical_device_destroy;
+
+ const struct build_id_note *note =
+ build_id_find_nhdr_for_addr(fgvk_CreateInstance);
+ if (!note) {
+ result = vk_errorf(NULL, VK_ERROR_INITIALIZATION_FAILED,
+ "Failed to find build-id");
+ goto fail_init;
+ }
+
+ unsigned build_id_len = build_id_length(note);
+ if (build_id_len < SHA1_DIGEST_LENGTH) {
+ result = vk_errorf(NULL, VK_ERROR_INITIALIZATION_FAILED,
+ "build-id too short. It needs to be a SHA");
+ goto fail_init;
+ }
+
+ STATIC_ASSERT(sizeof(instance->driver_build_sha) == SHA1_DIGEST_LENGTH);
+ memcpy(instance->driver_build_sha, build_id_data(note), SHA1_DIGEST_LENGTH);
+
+ *pInstance = fgvk_instance_to_handle(instance);
+ return VK_SUCCESS;
+
+fail_init:
+ vk_instance_finish(&instance->vk);
+fail_alloc:
+ vk_free(pAllocator, instance);
+
+ return result;
+}
+
+VKAPI_ATTR void VKAPI_CALL
+fgvk_DestroyInstance(VkInstance _instance,
+ const VkAllocationCallbacks *pAllocator)
+{
+ VK_FROM_HANDLE(fgvk_instance, instance, _instance);
+
+ if (!instance)
+ return;
+
+ vk_instance_finish(&instance->vk);
+ vk_free(&instance->vk.alloc, instance);
+}
+
+VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL
+fgvk_GetInstanceProcAddr(VkInstance _instance, const char *pName)
+{
+ VK_FROM_HANDLE(fgvk_instance, instance, _instance);
+ return vk_instance_get_proc_addr(&instance->vk,
+ &fgvk_instance_entrypoints,
+ pName);
+}
+
+PUBLIC VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL
+vk_icdGetInstanceProcAddr(VkInstance instance, const char *pName)
+{
+ return fgvk_GetInstanceProcAddr(instance, pName);
+}
diff --git a/src/frygon/vulkan/fgvk_instance.h b/src/frygon/vulkan/fgvk_instance.h
new file mode 100644
index 00000000000..e2c1c753a38
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_instance.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#ifndef FGVK_INSTANCE_H
+#define FGVK_INSTANCE_H 1
+
+#include "fgvk_private.h"
+
+#include "fgvk_debug.h"
+#include "vk_instance.h"
+#include "util/xmlconfig.h"
+
+struct fgvk_instance {
+ struct vk_instance vk;
+
+ enum fgvk_debug debug_flags;
+
+ uint8_t driver_build_sha[20];
+ uint32_t force_vk_vendor;
+};
+
+VK_DEFINE_HANDLE_CASTS(fgvk_instance, vk.base, VkInstance, VK_OBJECT_TYPE_INSTANCE)
+
+#endif
diff --git a/src/frygon/vulkan/fgvk_physical_device.c b/src/frygon/vulkan/fgvk_physical_device.c
new file mode 100644
index 00000000000..44b9b63135e
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_physical_device.c
@@ -0,0 +1,573 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#include "fgvk_physical_device.h"
+
+#include "fgcc.h"
+#include "vk_sync_dummy.h"
+
+static void
+fgvk_get_device_info(struct fg_device_info *info) {
+ info->device_id = 0x0001;
+ snprintf(info->device_name, sizeof(info->device_name), "Frygon GFX1");
+}
+
+static void
+fgvk_get_device_properties(const struct fgvk_instance *instance,
+ struct vk_properties *properties)
+{
+ *properties = (struct vk_properties) {
+ .apiVersion = VK_MAKE_VERSION(1, 0, VK_HEADER_VERSION),
+ .driverVersion = vk_get_driver_version(),
+ .vendorID = 0xF5C5,
+ .deviceID = 0x0001,
+ .deviceType = VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU,
+
+ /* Vulkan 1.0 limits */
+ .maxImageDimension1D = 16384,
+ .maxImageDimension2D = 16384,
+ .maxImageDimension3D = 16384,
+ .maxImageDimensionCube = 0x8000,
+ .maxImageArrayLayers = 2048,
+ .maxTexelBufferElements = 128 * 1024 * 1024,
+ .maxUniformBufferRange = 65536,
+ .maxStorageBufferRange = UINT32_MAX,
+ .maxPushConstantsSize = 4096,
+ .maxMemoryAllocationCount = 4096,
+ .maxSamplerAllocationCount = 4000,
+ .bufferImageGranularity = 0x10000, // TODO
+ .sparseAddressSpaceSize = 0, // TODO
+ .maxBoundDescriptorSets = 8, // TODO
+ .maxPerStageDescriptorSamplers = 8, // TODO
+ .maxPerStageDescriptorUniformBuffers = 8, // TODO
+ .maxPerStageDescriptorStorageBuffers = 8, // TODO
+ .maxPerStageDescriptorSampledImages = 8, // TODO
+ .maxPerStageDescriptorStorageImages = 8, // TODO
+ .maxPerStageDescriptorInputAttachments = 8, // TODO
+ .maxPerStageResources = UINT32_MAX,
+ .maxDescriptorSetSamplers = 8, // TODO
+ .maxDescriptorSetUniformBuffers = 8, // TODO
+ .maxDescriptorSetUniformBuffersDynamic = 0, // TODO
+ .maxDescriptorSetStorageBuffers = 8, // TODO
+ .maxDescriptorSetStorageBuffersDynamic = 0, // TODO
+ .maxDescriptorSetSampledImages = 8, // TODO
+ .maxDescriptorSetStorageImages = 8, // TODO
+ .maxDescriptorSetInputAttachments = 8, // TODO
+ .maxVertexInputAttributes = 32,
+ .maxVertexInputBindings = 32,
+ .maxVertexInputAttributeOffset = 2047,
+ .maxVertexInputBindingStride = 2048,
+ .maxVertexOutputComponents = 128,
+ .maxTessellationGenerationLevel = 64,
+ .maxTessellationPatchSize = 32,
+ .maxTessellationControlPerVertexInputComponents = 128,
+ .maxTessellationControlPerVertexOutputComponents = 128,
+ .maxTessellationControlPerPatchOutputComponents = 120,
+ .maxTessellationControlTotalOutputComponents = 4216,
+ .maxTessellationEvaluationInputComponents = 128,
+ .maxTessellationEvaluationOutputComponents = 128,
+ .maxGeometryShaderInvocations = 32,
+ .maxGeometryInputComponents = 128,
+ .maxGeometryOutputComponents = 128,
+ .maxGeometryOutputVertices = 1024,
+ .maxGeometryTotalOutputComponents = 1024,
+ .maxFragmentInputComponents = 128,
+ .maxFragmentOutputAttachments = 8, // TODO
+ .maxFragmentDualSrcAttachments = 1,
+ .maxFragmentCombinedOutputResources = 16,
+ /* Nvidia limits this to 48kB for consistency reasons, we could lift the
+ * limit if we wanted to.
+ */
+ .maxComputeSharedMemorySize = 64 * 1024, // TODO
+ .maxComputeWorkGroupCount = {0x7fffffff, 65535, 65535},
+ .maxComputeWorkGroupInvocations = 1024,
+ .maxComputeWorkGroupSize = {1024, 1024, 64},
+ .subPixelPrecisionBits = 8,
+ .subTexelPrecisionBits = 8,
+ .mipmapPrecisionBits = 8,
+ .maxDrawIndexedIndexValue = UINT32_MAX,
+ .maxDrawIndirectCount = UINT32_MAX,
+ .maxSamplerLodBias = 15,
+ .maxSamplerAnisotropy = 16,
+ .maxViewports = 8,
+ .maxViewportDimensions = { 32768, 32768 },
+ .viewportBoundsRange = { -65536, 65536 },
+ .viewportSubPixelBits = 8,
+ .minMemoryMapAlignment = 4096, // TODO
+ .minTexelBufferOffsetAlignment = 8,
+ .minUniformBufferOffsetAlignment = 8,
+ .minStorageBufferOffsetAlignment = 8,
+ .minTexelOffset = -8,
+ .maxTexelOffset = 7,
+ .minTexelGatherOffset = -32,
+ .maxTexelGatherOffset = 31,
+ .minInterpolationOffset = -0.5,
+ .maxInterpolationOffset = 0.4375,
+ .subPixelInterpolationOffsetBits = 4,
+ .maxFramebufferHeight = 16384,
+ .maxFramebufferWidth = 16384,
+ .maxFramebufferLayers = 2048,
+ .framebufferColorSampleCounts = 1,
+ .framebufferDepthSampleCounts = 1,
+ .framebufferNoAttachmentsSampleCounts = 1,
+ .framebufferStencilSampleCounts = 1,
+ .maxColorAttachments = 8,
+ .sampledImageColorSampleCounts = 1,
+ .sampledImageIntegerSampleCounts = 1,
+ .sampledImageDepthSampleCounts = 1,
+ .sampledImageStencilSampleCounts = 1,
+ .storageImageSampleCounts = 1,
+ .maxSampleMaskWords = 1,
+ .timestampComputeAndGraphics = true,
+ /* FIXME: Is timestamp period actually 1? */
+ .timestampPeriod = 1.0f,
+ .maxClipDistances = 8,
+ .maxCullDistances = 8,
+ .maxCombinedClipAndCullDistances = 8,
+ .discreteQueuePriorities = 2,
+ .pointSizeRange = { 1.0, 2047.94 },
+ .lineWidthRange = { 1, 64 },
+ .pointSizeGranularity = 0.0625,
+ .lineWidthGranularity = 0.0625,
+ .strictLines = true,
+ .standardSampleLocations = true,
+ .optimalBufferCopyOffsetAlignment = 1,
+ .optimalBufferCopyRowPitchAlignment = 1,
+ /* Default to 64 if we don't know the atom size */
+ .nonCoherentAtomSize = 64,
+
+ /* Vulkan 1.0 sparse properties */
+ .sparseResidencyNonResidentStrict = true,
+ .sparseResidencyAlignedMipSize = VK_FALSE,
+ .sparseResidencyStandard2DBlockShape = true,
+ .sparseResidencyStandard2DMultisampleBlockShape = true,
+ .sparseResidencyStandard3DBlockShape = true,
+
+#if 0
+ /* Vulkan 1.1 properties */
+ .subgroupSize = 32,
+ .subgroupSupportedStages = VK_SHADER_STAGE_VERTEX_BIT |
+ VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT |
+ VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT |
+ VK_SHADER_STAGE_GEOMETRY_BIT |
+ VK_SHADER_STAGE_FRAGMENT_BIT |
+ VK_SHADER_STAGE_COMPUTE_BIT,
+ .subgroupSupportedOperations = VK_SUBGROUP_FEATURE_ARITHMETIC_BIT |
+ VK_SUBGROUP_FEATURE_BALLOT_BIT |
+ VK_SUBGROUP_FEATURE_BASIC_BIT |
+ VK_SUBGROUP_FEATURE_CLUSTERED_BIT |
+ VK_SUBGROUP_FEATURE_QUAD_BIT |
+ VK_SUBGROUP_FEATURE_ROTATE_BIT_KHR |
+ VK_SUBGROUP_FEATURE_ROTATE_CLUSTERED_BIT_KHR |
+ VK_SUBGROUP_FEATURE_SHUFFLE_BIT |
+ VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT |
+ VK_SUBGROUP_FEATURE_VOTE_BIT,
+ .subgroupQuadOperationsInAllStages = false,
+ .pointClippingBehavior = VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY,
+ .maxMultiviewViewCount = NVK_MAX_MULTIVIEW_VIEW_COUNT,
+ .maxMultiviewInstanceIndex = UINT32_MAX,
+ .maxPerSetDescriptors = UINT32_MAX,
+ .maxMemoryAllocationSize = (1u << 31),
+
+ /* Vulkan 1.2 properties */
+ .supportedDepthResolveModes = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT |
+ VK_RESOLVE_MODE_AVERAGE_BIT |
+ VK_RESOLVE_MODE_MIN_BIT |
+ VK_RESOLVE_MODE_MAX_BIT,
+ .supportedStencilResolveModes = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT |
+ VK_RESOLVE_MODE_MIN_BIT |
+ VK_RESOLVE_MODE_MAX_BIT,
+ .independentResolveNone = true,
+ .independentResolve = true,
+ .driverID = VK_DRIVER_ID_MESA_NVK,
+ .conformanceVersion =
+ nvk_is_conformant(info) ? (VkConformanceVersion) { 1, 4, 3, 0 }
+ : (VkConformanceVersion) { 0, 0, 0, 0 },
+ .denormBehaviorIndependence = VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL,
+ .roundingModeIndependence = VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL,
+ .shaderSignedZeroInfNanPreserveFloat16 = true,
+ .shaderSignedZeroInfNanPreserveFloat32 = true,
+ .shaderSignedZeroInfNanPreserveFloat64 = true,
+ .shaderDenormPreserveFloat16 = true,
+ .shaderDenormPreserveFloat32 = true,
+ .shaderDenormPreserveFloat64 = true,
+ .shaderDenormFlushToZeroFloat16 = false,
+ .shaderDenormFlushToZeroFloat32 = true,
+ .shaderDenormFlushToZeroFloat64 = false,
+ .shaderRoundingModeRTEFloat16 = true,
+ .shaderRoundingModeRTEFloat32 = true,
+ .shaderRoundingModeRTEFloat64 = true,
+ .shaderRoundingModeRTZFloat16 = false,
+ .shaderRoundingModeRTZFloat32 = true,
+ .shaderRoundingModeRTZFloat64 = true,
+ .maxUpdateAfterBindDescriptorsInAllPools = UINT32_MAX,
+ .shaderUniformBufferArrayNonUniformIndexingNative = true,
+ .shaderSampledImageArrayNonUniformIndexingNative = info->cls_eng3d >= TURING_A,
+ .shaderStorageBufferArrayNonUniformIndexingNative = true,
+ .shaderStorageImageArrayNonUniformIndexingNative = info->cls_eng3d >= TURING_A,
+ .shaderInputAttachmentArrayNonUniformIndexingNative = false,
+ .robustBufferAccessUpdateAfterBind = true,
+ .quadDivergentImplicitLod = info->cls_eng3d >= TURING_A,
+ .maxPerStageDescriptorUpdateAfterBindSamplers = NVK_MAX_DESCRIPTORS,
+ .maxPerStageDescriptorUpdateAfterBindUniformBuffers = NVK_MAX_DESCRIPTORS,
+ .maxPerStageDescriptorUpdateAfterBindStorageBuffers = NVK_MAX_DESCRIPTORS,
+ .maxPerStageDescriptorUpdateAfterBindSampledImages = NVK_MAX_DESCRIPTORS,
+ .maxPerStageDescriptorUpdateAfterBindStorageImages = NVK_MAX_DESCRIPTORS,
+ .maxPerStageDescriptorUpdateAfterBindInputAttachments = NVK_MAX_DESCRIPTORS,
+ .maxPerStageUpdateAfterBindResources = UINT32_MAX,
+ .maxDescriptorSetUpdateAfterBindSamplers = NVK_MAX_DESCRIPTORS,
+ .maxDescriptorSetUpdateAfterBindUniformBuffers = NVK_MAX_DESCRIPTORS,
+ .maxDescriptorSetUpdateAfterBindUniformBuffersDynamic = NVK_MAX_DYNAMIC_BUFFERS / 2,
+ .maxDescriptorSetUpdateAfterBindStorageBuffers = NVK_MAX_DESCRIPTORS,
+ .maxDescriptorSetUpdateAfterBindStorageBuffersDynamic = NVK_MAX_DYNAMIC_BUFFERS / 2,
+ .maxDescriptorSetUpdateAfterBindSampledImages = NVK_MAX_DESCRIPTORS,
+ .maxDescriptorSetUpdateAfterBindStorageImages = NVK_MAX_DESCRIPTORS,
+ .maxDescriptorSetUpdateAfterBindInputAttachments = NVK_MAX_DESCRIPTORS,
+ .filterMinmaxSingleComponentFormats = info->cls_eng3d >= MAXWELL_B,
+ .filterMinmaxImageComponentMapping = info->cls_eng3d >= MAXWELL_B,
+ .maxTimelineSemaphoreValueDifference = UINT64_MAX,
+ .framebufferIntegerColorSampleCounts = sample_counts,
+
+ /* Vulkan 1.3 properties */
+ .minSubgroupSize = 32,
+ .maxSubgroupSize = 32,
+ .maxComputeWorkgroupSubgroups = 1024 / 32,
+ .requiredSubgroupSizeStages = 0,
+ .maxInlineUniformBlockSize = NVK_MAX_INLINE_UNIFORM_BLOCK_SIZE,
+ .maxPerStageDescriptorInlineUniformBlocks = 32,
+ .maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks = 32,
+ .maxDescriptorSetInlineUniformBlocks = 6 * 32,
+ .maxDescriptorSetUpdateAfterBindInlineUniformBlocks = 6 * 32,
+ .maxInlineUniformTotalSize = 1 << 16,
+ .integerDotProduct4x8BitPackedUnsignedAccelerated
+ = info->cls_eng3d >= VOLTA_A,
+ .integerDotProduct4x8BitPackedSignedAccelerated
+ = info->cls_eng3d >= VOLTA_A,
+ .integerDotProduct4x8BitPackedMixedSignednessAccelerated
+ = info->cls_eng3d >= VOLTA_A,
+ .storageTexelBufferOffsetAlignmentBytes = NVK_MIN_TEXEL_BUFFER_ALIGNMENT,
+ .storageTexelBufferOffsetSingleTexelAlignment = true,
+ .uniformTexelBufferOffsetAlignmentBytes = NVK_MIN_TEXEL_BUFFER_ALIGNMENT,
+ .uniformTexelBufferOffsetSingleTexelAlignment = true,
+ .maxBufferSize = NVK_MAX_BUFFER_SIZE,
+
+ /* Vulkan 1.4 properties */
+ .lineSubPixelPrecisionBits = 8,
+ .maxVertexAttribDivisor = UINT32_MAX,
+ .supportsNonZeroFirstInstance = true,
+ .maxPushDescriptors = NVK_MAX_PUSH_DESCRIPTORS,
+ .dynamicRenderingLocalReadDepthStencilAttachments = true,
+ .dynamicRenderingLocalReadMultisampledAttachments = true,
+ .earlyFragmentMultisampleCoverageAfterSampleCounting = true,
+ .earlyFragmentSampleMaskTestBeforeSampleCounting = true,
+ .depthStencilSwizzleOneSupport = true,
+ .polygonModePointSize = true,
+ .nonStrictSinglePixelWideLinesUseParallelogram = false,
+ .nonStrictWideLinesUseParallelogram = false,
+ .blockTexelViewCompatibleMultipleLayers = true,
+ .maxCombinedImageSamplerDescriptorCount = NVK_MAX_IMAGE_PLANES,
+ .fragmentShadingRateClampCombinerInputs = false, /* TODO */
+ .defaultRobustnessStorageBuffers =
+ VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_DISABLED_EXT,
+ .defaultRobustnessUniformBuffers =
+ VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_DISABLED_EXT,
+ .defaultRobustnessVertexInputs =
+ VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS_2_EXT,
+ .defaultRobustnessImages =
+ VK_PIPELINE_ROBUSTNESS_IMAGE_BEHAVIOR_ROBUST_IMAGE_ACCESS_2_EXT,
+
+ /* VK_KHR_cooperative_matrix */
+ .cooperativeMatrixSupportedStages = VK_SHADER_STAGE_COMPUTE_BIT,
+
+ /* VK_KHR_discard_rectangles */
+ .maxDiscardRectangles = NVK_MAX_DISCARD_RECTANGLES,
+
+ /* VK_KHR_compute_shader_derivatives */
+ .meshAndTaskShaderDerivatives = false,
+
+ /* VK_KHR_pipeline_binary
+ *
+ * InternalCache properties are set by
+ * nvk_physical_device_init_pipeline_cache()
+ */
+ .pipelineBinaryCompressedData = false,
+
+ /* VK_EXT_conservative_rasterization */
+ .primitiveOverestimationSize = info->cls_eng3d >= VOLTA_A ? 1.0f / 512.0f : 0.0,
+ .maxExtraPrimitiveOverestimationSize = 0.75,
+ .extraPrimitiveOverestimationSizeGranularity = 0.25,
+ .primitiveUnderestimation = info->cls_eng3d >= VOLTA_A,
+ .conservativePointAndLineRasterization = true,
+ .degenerateLinesRasterized = info->cls_eng3d >= VOLTA_A,
+ .degenerateTrianglesRasterized = info->cls_eng3d >= PASCAL_A,
+ .fullyCoveredFragmentShaderInputVariable = false,
+ .conservativeRasterizationPostDepthCoverage = info->cls_eng3d >= MAXWELL_B,
+
+ /* VK_EXT_custom_border_color */
+ .maxCustomBorderColorSamplers = 4000,
+
+ /* VK_EXT_descriptor_buffer */
+ .combinedImageSamplerDescriptorSingleArray = true,
+ .bufferlessPushDescriptors = true,
+ .allowSamplerImageViewPostSubmitCreation = false,
+ .descriptorBufferOffsetAlignment = nvk_min_cbuf_alignment(info),
+ .maxDescriptorBufferBindings = 32,
+ .maxResourceDescriptorBufferBindings = 32,
+ .maxSamplerDescriptorBufferBindings = 32,
+ .maxEmbeddedImmutableSamplerBindings = 32,
+ .maxEmbeddedImmutableSamplers = 4000,
+ .bufferCaptureReplayDescriptorDataSize = sizeof(uint64_t),
+ .imageCaptureReplayDescriptorDataSize = 0,
+ .imageViewCaptureReplayDescriptorDataSize =
+ sizeof(struct nvk_image_view_capture),
+ .samplerCaptureReplayDescriptorDataSize =
+ sizeof(struct nvk_sampler_capture),
+ .accelerationStructureCaptureReplayDescriptorDataSize = 0, // todo
+ .samplerDescriptorSize = sizeof(struct nvk_sampled_image_descriptor),
+ .combinedImageSamplerDescriptorSize = sizeof(struct nvk_sampled_image_descriptor),
+ .sampledImageDescriptorSize = sizeof(struct nvk_sampled_image_descriptor),
+ .storageImageDescriptorSize = sizeof(struct nvk_storage_image_descriptor),
+ .uniformTexelBufferDescriptorSize = sizeof(struct nvk_edb_buffer_view_descriptor),
+ .robustUniformTexelBufferDescriptorSize = sizeof(struct nvk_edb_buffer_view_descriptor),
+ .storageTexelBufferDescriptorSize = sizeof(struct nvk_edb_buffer_view_descriptor),
+ .robustStorageTexelBufferDescriptorSize = sizeof(struct nvk_edb_buffer_view_descriptor),
+ .uniformBufferDescriptorSize = sizeof(union nvk_buffer_descriptor),
+ .robustUniformBufferDescriptorSize = sizeof(union nvk_buffer_descriptor),
+ .storageBufferDescriptorSize = sizeof(union nvk_buffer_descriptor),
+ .robustStorageBufferDescriptorSize = sizeof(union nvk_buffer_descriptor),
+ .inputAttachmentDescriptorSize = sizeof(struct nvk_sampled_image_descriptor),
+ .accelerationStructureDescriptorSize = 0,
+ .maxSamplerDescriptorBufferRange = UINT32_MAX,
+ .maxResourceDescriptorBufferRange = UINT32_MAX,
+ .samplerDescriptorBufferAddressSpaceSize = UINT32_MAX,
+ .resourceDescriptorBufferAddressSpaceSize = UINT32_MAX,
+ .descriptorBufferAddressSpaceSize = UINT32_MAX,
+
+ /* VK_EXT_device_generated_commands */
+ .maxIndirectPipelineCount = UINT32_MAX,
+ .maxIndirectShaderObjectCount = UINT32_MAX,
+ .maxIndirectSequenceCount = 1 << 20,
+ .maxIndirectCommandsTokenCount = 16,
+ .maxIndirectCommandsTokenOffset = 2047,
+ .maxIndirectCommandsIndirectStride = 1 << 12,
+ .supportedIndirectCommandsInputModes =
+ VK_INDIRECT_COMMANDS_INPUT_MODE_VULKAN_INDEX_BUFFER_EXT |
+ VK_INDIRECT_COMMANDS_INPUT_MODE_DXGI_INDEX_BUFFER_EXT,
+ .supportedIndirectCommandsShaderStages =
+ NVK_SHADER_STAGE_GRAPHICS_BITS | VK_SHADER_STAGE_COMPUTE_BIT,
+ .supportedIndirectCommandsShaderStagesPipelineBinding =
+ NVK_SHADER_STAGE_GRAPHICS_BITS | VK_SHADER_STAGE_COMPUTE_BIT,
+ .supportedIndirectCommandsShaderStagesShaderBinding =
+ NVK_SHADER_STAGE_GRAPHICS_BITS | VK_SHADER_STAGE_COMPUTE_BIT,
+ .deviceGeneratedCommandsTransformFeedback = true,
+ .deviceGeneratedCommandsMultiDrawIndirectCount = info->cls_eng3d >= TURING_A,
+
+ /* VK_EXT_extended_dynamic_state3 */
+ .dynamicPrimitiveTopologyUnrestricted = true,
+
+ /* VK_EXT_graphics_pipeline_library */
+ .graphicsPipelineLibraryFastLinking = true,
+ .graphicsPipelineLibraryIndependentInterpolationDecoration = true,
+
+ /* VK_KHR_maintenance7 */
+ .robustFragmentShadingRateAttachmentAccess = false,
+ .separateDepthStencilAttachmentAccess = false,
+ .maxDescriptorSetTotalUniformBuffersDynamic = NVK_MAX_DYNAMIC_BUFFERS / 2,
+ .maxDescriptorSetTotalStorageBuffersDynamic = NVK_MAX_DYNAMIC_BUFFERS / 2,
+ .maxDescriptorSetTotalBuffersDynamic = NVK_MAX_DYNAMIC_BUFFERS,
+ .maxDescriptorSetUpdateAfterBindTotalUniformBuffersDynamic = NVK_MAX_DYNAMIC_BUFFERS / 2,
+ .maxDescriptorSetUpdateAfterBindTotalStorageBuffersDynamic = NVK_MAX_DYNAMIC_BUFFERS / 2,
+ .maxDescriptorSetUpdateAfterBindTotalBuffersDynamic = NVK_MAX_DYNAMIC_BUFFERS,
+
+ /* VK_KHR_maintenance9 */
+ .image2DViewOf3DSparse = false,
+ .defaultVertexAttributeValue =
+ VK_DEFAULT_VERTEX_ATTRIBUTE_VALUE_ZERO_ZERO_ZERO_ZERO_KHR,
+
+ /* VK_KHR_maintenance10 */
+ .rgba4OpaqueBlackSwizzled = true,
+ .resolveSrgbFormatAppliesTransferFunction = true,
+ .resolveSrgbFormatSupportsTransferFunctionControl = true,
+
+ /* VK_EXT_legacy_vertex_attributes */
+ .nativeUnalignedPerformance = true,
+
+ /* VK_EXT_map_memory_placed */
+ .minPlacedMemoryMapAlignment = os_page_size,
+
+ /* VK_EXT_multi_draw */
+ .maxMultiDrawCount = UINT32_MAX,
+
+ /* VK_EXT_nested_command_buffer */
+ .maxCommandBufferNestingLevel = UINT32_MAX,
+
+ /* VK_EXT_pci_bus_info */
+ .pciDomain = info->pci.domain,
+ .pciBus = info->pci.bus,
+ .pciDevice = info->pci.dev,
+ .pciFunction = info->pci.func,
+
+ /* VK_EXT_physical_device_drm gets populated later */
+
+ /* VK_EXT_provoking_vertex */
+ .provokingVertexModePerPipeline = true,
+ .transformFeedbackPreservesTriangleFanProvokingVertex = true,
+
+ /* VK_EXT_robustness2 */
+ .robustStorageBufferAccessSizeAlignment = NVK_SSBO_BOUNDS_CHECK_ALIGNMENT,
+ .robustUniformBufferAccessSizeAlignment = nvk_min_cbuf_alignment(info),
+
+ /* VK_EXT_sample_locations
+ *
+ * There's a weird HW issue with per-sample interpolation for 1x. It
+ * always interpolates at (0.5, 0.5) so we just disable custom sample
+ * locations for 1x.
+ */
+ .sampleLocationSampleCounts = sample_counts & ~VK_SAMPLE_COUNT_1_BIT,
+ .maxSampleLocationGridSize = (VkExtent2D){ 1, 1 },
+ .sampleLocationCoordinateRange[0] = 0.0f,
+ .sampleLocationCoordinateRange[1] = 0.9375f,
+ .sampleLocationSubPixelBits = 4,
+ .variableSampleLocations = true,
+
+ /* VK_EXT_shader_object */
+ .shaderBinaryVersion = 0,
+
+ /* VK_EXT_transform_feedback */
+ .maxTransformFeedbackStreams = 4,
+ .maxTransformFeedbackBuffers = 4,
+ .maxTransformFeedbackBufferSize = UINT32_MAX,
+ .maxTransformFeedbackStreamDataSize = 2048,
+ .maxTransformFeedbackBufferDataSize = 512,
+ .maxTransformFeedbackBufferDataStride = 2048,
+ .transformFeedbackQueries = true,
+ .transformFeedbackStreamsLinesTriangles = false,
+ .transformFeedbackRasterizationStreamSelect = true,
+ .transformFeedbackDraw = true,
+
+ /* VK_KHR_fragment_shader_barycentric */
+ .triStripVertexOrderIndependentOfProvokingVertex = false,
+
+ /* VK_KHR_fragment_shading_rate */
+ .minFragmentShadingRateAttachmentTexelSize = { 16, 16 },
+ .maxFragmentShadingRateAttachmentTexelSize = { 16, 16 },
+ .maxFragmentShadingRateAttachmentTexelSizeAspectRatio = 1,
+ .primitiveFragmentShadingRateWithMultipleViewports = info->cls_eng3d >= AMPERE_B,
+ .layeredShadingRateAttachments = true,
+ .fragmentShadingRateNonTrivialCombinerOps = true,
+ .maxFragmentSize = { 4, 4 },
+ .maxFragmentSizeAspectRatio = 2,
+ .maxFragmentShadingRateCoverageSamples = 16,
+ .maxFragmentShadingRateRasterizationSamples = 16,
+ .fragmentShadingRateWithShaderDepthStencilWrites = true,
+ .fragmentShadingRateWithSampleMask = true,
+ .fragmentShadingRateWithShaderSampleMask = true,
+ .fragmentShadingRateWithConservativeRasterization = true,
+ //.fragmentShadingRateWithFragmentShaderInterlock = true,
+ .fragmentShadingRateWithCustomSampleLocations = true,
+ .fragmentShadingRateStrictMultiplyCombiner = true,
+
+ /* VK_MESA_image_alignment_control */
+ .supportedImageAlignmentMask = (4 * 1024) | (16 * 1024) | (64 * 1024),
+
+ /* VK_NV_shader_sm_builtins */
+ .shaderSMCount = (uint32_t)info->tpc_count * info->mp_per_tpc,
+ .shaderWarpsPerSM = info->max_warps_per_mp,
+#endif
+ };
+
+ snprintf(properties->deviceName, sizeof(properties->deviceName), "Frygon GFX1");
+}
+
+VkResult
+fgvk_physical_device_enumerate(struct vk_instance *_instance)
+{
+ VkResult result = VK_SUCCESS;
+ struct fgvk_instance *instance = (struct fgvk_instance *)_instance;
+
+ struct fgvk_physical_device *device =
+ vk_zalloc(&instance->vk.alloc, sizeof(*device), 8,
+ VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
+ if (!device)
+ return vk_error(instance, VK_ERROR_OUT_OF_HOST_MEMORY);
+
+ struct vk_physical_device_dispatch_table dispatch_table;
+ vk_physical_device_dispatch_table_from_entrypoints
+ (&dispatch_table, &fgvk_physical_device_entrypoints, true);
+ //vk_physical_device_dispatch_table_from_entrypoints(
+ // &dispatch_table, &wsi_physical_device_entrypoints, false);
+
+ struct vk_properties properties;
+ fgvk_get_device_properties(instance, &properties);
+
+ result = vk_physical_device_init(&device->vk, &instance->vk, NULL, NULL,
+ &properties, &dispatch_table);
+ if (result != VK_SUCCESS)
+ goto fail;
+
+ fgvk_get_device_info(&device->info);
+
+ device->fgcc = fgcc_compiler_create(&device->info);
+ device->sync_types[0] = &vk_sync_dummy_type;
+ device->sync_types[1] = NULL;
+ device->vk.supported_sync_types = device->sync_types;
+
+ list_addtail(&device->vk.link, &instance->vk.physical_devices.list);
+
+ return VK_SUCCESS;
+
+fail:
+ vk_physical_device_finish(&device->vk);
+ vk_free(&instance->vk.alloc, device);
+ return result;
+}
+
+void
+fgvk_physical_device_destroy(struct vk_physical_device *pdevice)
+{
+ struct fgvk_physical_device *pdev =
+ container_of(pdevice, struct fgvk_physical_device, vk);
+
+ vk_physical_device_finish(&pdev->vk);
+ vk_free(&pdev->vk.instance->alloc, pdev);
+}
+
+VKAPI_ATTR void VKAPI_CALL
+fgvk_GetPhysicalDeviceMemoryProperties2(
+ VkPhysicalDevice physicalDevice,
+ VkPhysicalDeviceMemoryProperties2 *pMemoryProperties)
+{
+ //VK_FROM_HANDLE(fgvk_physical_device, pdev, physicalDevice);
+
+ pMemoryProperties->memoryProperties.memoryHeapCount = 1;
+ pMemoryProperties->memoryProperties.memoryHeaps[0] = (VkMemoryHeap) {
+ .size = 256*1024*1024,
+ .flags = VK_MEMORY_HEAP_DEVICE_LOCAL_BIT,
+ };
+
+ pMemoryProperties->memoryProperties.memoryTypeCount = 1;
+ pMemoryProperties->memoryProperties.memoryTypes[0] = (VkMemoryType) {
+ .propertyFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
+ VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+ .heapIndex = 0,
+ };
+}
+
+VKAPI_ATTR void VKAPI_CALL
+fgvk_GetPhysicalDeviceQueueFamilyProperties2(
+ VkPhysicalDevice physicalDevice,
+ uint32_t *pQueueFamilyPropertyCount,
+ VkQueueFamilyProperties2 *pQueueFamilyProperties)
+{
+ VK_OUTARRAY_MAKE_TYPED(VkQueueFamilyProperties2, out, pQueueFamilyProperties,
+ pQueueFamilyPropertyCount);
+
+ vk_outarray_append_typed(VkQueueFamilyProperties2, &out, p) {
+ p->queueFamilyProperties.queueFlags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT;
+ p->queueFamilyProperties.queueCount = 1;
+ p->queueFamilyProperties.timestampValidBits = 64;
+ p->queueFamilyProperties.minImageTransferGranularity = (VkExtent3D){1, 1, 1};
+ }
+}
diff --git a/src/frygon/vulkan/fgvk_physical_device.h b/src/frygon/vulkan/fgvk_physical_device.h
new file mode 100644
index 00000000000..53ede1f495a
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_physical_device.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#ifndef FGVK_PHYSICAL_DEVICE_H
+#define FGVK_PHYSICAL_DEVICE_H 1
+
+#include "fgvk_private.h"
+#include "fgvk_instance.h"
+
+#include "fg_device_info.h"
+
+#include "vk_physical_device.h"
+#include "vk_sync.h"
+
+struct fgcc_compiler;
+
+struct fgvk_physical_device {
+ struct vk_physical_device vk;
+ struct fg_device_info info;
+
+ struct fgcc_compiler *fgcc;
+
+ const struct vk_sync_type *sync_types[2];
+};
+
+VK_DEFINE_HANDLE_CASTS(fgvk_physical_device,
+ vk.base,
+ VkPhysicalDevice,
+ VK_OBJECT_TYPE_PHYSICAL_DEVICE)
+
+#endif
diff --git a/src/frygon/vulkan/fgvk_private.h b/src/frygon/vulkan/fgvk_private.h
new file mode 100644
index 00000000000..4eafe7a616e
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_private.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#ifndef FGVK_PRIVATE_H
+#define FGVK_PRIVATE_H 1
+
+#include <assert.h>
+
+#include "fgvk_entrypoints.h"
+
+#include "vk_alloc.h"
+#include "vk_log.h"
+#include "vk_util.h"
+
+VkResult fgvk_physical_device_enumerate(struct vk_instance *instance);
+void fgvk_physical_device_destroy(struct vk_physical_device *pdevice);
+
+#endif
diff --git a/src/frygon/vulkan/fgvk_queue.c b/src/frygon/vulkan/fgvk_queue.c
new file mode 100644
index 00000000000..739de7f5bb4
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_queue.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#include "fgvk_queue.h"
+
+static VkResult
+fgvk_queue_submit(struct vk_queue *vk_queue,
+ struct vk_queue_submit *submit)
+{
+ return VK_SUCCESS;
+}
+
+VkResult
+fgvk_queue_create(struct fgvk_device *dev,
+ const VkDeviceQueueCreateInfo *pCreateInfo,
+ uint32_t index_in_family)
+{
+ VkResult result;
+ struct fgvk_queue *queue = vk_zalloc(&dev->vk.alloc, sizeof(struct fgvk_queue),
+ 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
+ if (!queue)
+ return VK_ERROR_OUT_OF_HOST_MEMORY;
+
+ result = vk_queue_init(&queue->vk, &dev->vk, pCreateInfo, index_in_family);
+ if (result != VK_SUCCESS)
+ goto fail_alloc;
+
+ queue->vk.driver_submit = fgvk_queue_submit;
+
+ return VK_SUCCESS;
+
+fail_alloc:
+ vk_free(&dev->vk.alloc, queue);
+
+ return result;
+}
+
+void
+fgvk_queue_destroy(struct fgvk_device *dev, struct fgvk_queue *queue)
+{
+ vk_queue_finish(&queue->vk);
+ vk_free(&dev->vk.alloc, queue);
+}
diff --git a/src/frygon/vulkan/fgvk_queue.h b/src/frygon/vulkan/fgvk_queue.h
new file mode 100644
index 00000000000..1ba1f0054fc
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_queue.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#ifndef FGVK_QUEUE_H
+#define FGVK_QUEUE_H 1
+
+#include "fgvk_device.h"
+
+#include "vk_queue.h"
+
+struct fgvk_queue {
+ struct vk_queue vk;
+};
+
+VkResult fgvk_queue_create(struct fgvk_device *dev,
+ const VkDeviceQueueCreateInfo *pCreateInfo,
+ uint32_t index_in_family);
+
+void fgvk_queue_destroy(struct fgvk_device *dev, struct fgvk_queue *queue);
+
+#endif
diff --git a/src/frygon/vulkan/fgvk_shader.c b/src/frygon/vulkan/fgvk_shader.c
new file mode 100644
index 00000000000..7cc871e0cbe
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_shader.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#include "fgvk_shader.h"
+
+#include "fgvk_physical_device.h"
+#include "fgvk_device.h"
+#include "fgvk_cmd_buffer.h"
+
+static const nir_shader_compiler_options *
+fgvk_get_nir_options(struct vk_physical_device *vk_pdev,
+ mesa_shader_stage stage,
+ UNUSED const struct vk_pipeline_robustness_state *rs)
+{
+ const struct fgvk_physical_device *pdev =
+ container_of(vk_pdev, struct fgvk_physical_device, vk);
+ return fgcc_nir_options(pdev->fgcc);
+}
+
+static struct spirv_to_nir_options
+fgvk_get_spirv_options(struct vk_physical_device *vk_pdev,
+ UNUSED mesa_shader_stage stage,
+ const struct vk_pipeline_robustness_state *rs)
+{
+ const struct fgvk_physical_device *pdev =
+ container_of(vk_pdev, struct fgvk_physical_device, vk);
+
+ return (struct spirv_to_nir_options) {};
+}
+
+static void
+fgvk_preprocess_nir(struct vk_physical_device *vk_pdev,
+ nir_shader *nir,
+ UNUSED const struct vk_pipeline_robustness_state *rs)
+{
+ const struct fgvk_physical_device *pdev =
+ container_of(vk_pdev, struct fgvk_physical_device, vk);
+
+ fgcc_preprocess_nir(nir, pdev->fgcc);
+}
+
+static void
+fgvk_hash_state(struct vk_physical_device *device,
+ const struct vk_graphics_pipeline_state *state,
+ const struct vk_features *enabled_features,
+ VkShaderStageFlags stages,
+ blake3_hash blake3_out)
+{
+ // TODO need to figure out what state should get hashed into shaders
+ struct mesa_blake3 blake3_ctx;
+ _mesa_blake3_init(&blake3_ctx);
+ _mesa_blake3_final(&blake3_ctx, blake3_out);
+}
+
+static const struct vk_shader_ops fgvk_shader_ops;
+
+static void
+fgvk_shader_destroy(struct vk_device *vk_dev,
+ struct vk_shader *vk_shader,
+ const VkAllocationCallbacks* pAllocator)
+{
+ struct fgvk_device *dev = container_of(vk_dev, struct fgvk_device, vk);
+ struct fgvk_shader *shader = container_of(vk_shader, struct fgvk_shader, vk);
+
+ vk_shader_free(&dev->vk, pAllocator, &shader->vk);
+}
+
+static VkResult
+fgvk_compile_shader(struct fgvk_device *dev,
+ struct vk_shader_compile_info *info,
+ const struct vk_graphics_pipeline_state *state,
+ const VkAllocationCallbacks* pAllocator,
+ struct vk_shader **shader_out)
+{
+ struct fgvk_shader *shader;
+ VkResult result = VK_SUCCESS;
+
+ /* We consume the NIR, regardless of success or failure */
+ nir_shader *nir = info->nir;
+
+ shader = vk_shader_zalloc(&dev->vk, &fgvk_shader_ops, info->stage,
+ pAllocator, sizeof(*shader));
+
+ if (shader == NULL) {
+ ralloc_free(nir);
+ return vk_error(dev, VK_ERROR_OUT_OF_HOST_MEMORY);
+ }
+
+ // TODO some shader compliation should happen here
+
+ ralloc_free(nir);
+ if (result != VK_SUCCESS) {
+ fgvk_shader_destroy(&dev->vk, &shader->vk, pAllocator);
+ return result;
+ }
+
+ *shader_out = &shader->vk;
+
+ return VK_SUCCESS;
+}
+
+static VkResult
+fgvk_compile_shaders(struct vk_device *vk_dev,
+ uint32_t shader_count,
+ struct vk_shader_compile_info *infos,
+ const struct vk_graphics_pipeline_state *state,
+ const struct vk_features *enabled_features,
+ const VkAllocationCallbacks* pAllocator,
+ struct vk_shader **shaders_out)
+{
+ struct fgvk_device *dev = container_of(vk_dev, struct fgvk_device, vk);
+
+ for (uint32_t i = 0; i < shader_count; i++) {
+ VkResult result = fgvk_compile_shader(dev, &infos[i], state,
+ pAllocator, &shaders_out[i]);
+ if (result != VK_SUCCESS) {
+ /* Clean up all the shaders before this point */
+ for (uint32_t j = 0; j < i; j++)
+ fgvk_shader_destroy(&dev->vk, shaders_out[j], pAllocator);
+
+ /* Clean up all the NIR after this point */
+ for (uint32_t j = i + 1; j < shader_count; j++)
+ ralloc_free(infos[j].nir);
+
+ /* Memset the output array */
+ memset(shaders_out, 0, shader_count * sizeof(*shaders_out));
+
+ return result;
+ }
+ }
+
+ return VK_SUCCESS;
+}
+
+static bool
+fgvk_shader_serialize(struct vk_device *vk_dev,
+ const struct vk_shader *vk_shader,
+ struct blob *blob)
+{
+ UNREACHABLE("TODO");
+}
+
+static VkResult
+fgvk_deserialize_shader(struct vk_device *vk_dev,
+ struct blob_reader *blob,
+ uint32_t binary_version,
+ const VkAllocationCallbacks* pAllocator,
+ struct vk_shader **shader_out)
+{
+ UNREACHABLE("TODO");
+}
+
+static VkResult
+fgvk_shader_get_executable_properties(
+ UNUSED struct vk_device *device,
+ const struct vk_shader *vk_shader,
+ uint32_t *executable_count,
+ VkPipelineExecutablePropertiesKHR *properties)
+{
+ UNREACHABLE("TODO");
+}
+
+static VkResult
+fgvk_shader_get_executable_statistics(
+ UNUSED struct vk_device *device,
+ const struct vk_shader *vk_shader,
+ uint32_t executable_index,
+ uint32_t *statistic_count,
+ VkPipelineExecutableStatisticKHR *statistics)
+{
+ UNREACHABLE("TODO");
+}
+
+static VkResult
+fgvk_shader_get_executable_internal_representations(
+ UNUSED struct vk_device *device,
+ const struct vk_shader *vk_shader,
+ uint32_t executable_index,
+ uint32_t *internal_representation_count,
+ VkPipelineExecutableInternalRepresentationKHR *internal_representations)
+{
+ UNREACHABLE("TODO");
+}
+
+static const struct vk_shader_ops fgvk_shader_ops = {
+ .destroy = fgvk_shader_destroy,
+ .serialize = fgvk_shader_serialize,
+ .get_executable_properties = fgvk_shader_get_executable_properties,
+ .get_executable_statistics = fgvk_shader_get_executable_statistics,
+ .get_executable_internal_representations =
+ fgvk_shader_get_executable_internal_representations,
+};
+
+const struct vk_device_shader_ops fgvk_device_shader_ops = {
+ .get_nir_options = fgvk_get_nir_options,
+ .get_spirv_options = fgvk_get_spirv_options,
+ .preprocess_nir = fgvk_preprocess_nir,
+ .hash_state = fgvk_hash_state,
+ .compile = fgvk_compile_shaders,
+ .deserialize = fgvk_deserialize_shader,
+ .cmd_set_dynamic_graphics_state = vk_cmd_set_dynamic_graphics_state,
+ .cmd_bind_shaders = fgvk_cmd_bind_shaders,
+};
diff --git a/src/frygon/vulkan/fgvk_shader.h b/src/frygon/vulkan/fgvk_shader.h
new file mode 100644
index 00000000000..5c57250827d
--- /dev/null
+++ b/src/frygon/vulkan/fgvk_shader.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright © 2025 Lucas Francisco Fryzek
+ * SPDX-License-Identifier: MIT
+ */
+#ifndef FGVK_SHADER_H
+#define FGVK_SHADER_H 1
+
+#include "fgvk_private.h"
+
+#include "vk_shader.h"
+
+#include "fgcc.h"
+
+struct fgvk_shader {
+ struct vk_shader vk;
+};
+
+VK_DEFINE_NONDISP_HANDLE_CASTS(fgvk_shader, vk.base, VkShaderEXT,
+ VK_OBJECT_TYPE_SHADER_EXT);
+
+extern const struct vk_device_shader_ops fgvk_device_shader_ops;
+
+#endif
diff --git a/src/frygon/vulkan/meson.build b/src/frygon/vulkan/meson.build
new file mode 100644
index 00000000000..44564edd2fc
--- /dev/null
+++ b/src/frygon/vulkan/meson.build
@@ -0,0 +1,106 @@
+# Copyright © 2025 Lucas Francisco Fryzek
+# SPDX-License-Identifier: MIT
+fgvk_files = files(
+ 'fgvk_instance.c',
+ 'fgvk_physical_device.c',
+ 'fgvk_device.c',
+ 'fgvk_device_memory.c',
+ 'fgvk_buffer.c',
+ 'fgvk_format.c',
+ 'fgvk_image.c',
+ 'fgvk_image_view.c',
+ 'fgvk_cmd_buffer.c',
+ 'fgvk_cmd_clear.c',
+ 'fgvk_cmd_draw.c',
+ 'fgvk_cmd_copy.c',
+ 'fgvk_cmd_pool.c',
+ 'fgvk_shader.c',
+ 'fgvk_queue.c',
+)
+
+fgvk_entrypoints = custom_target(
+ 'fgvk_entrypoints',
+ input : [vk_entrypoints_gen, vk_api_xml],
+ output : ['fgvk_entrypoints.h', 'fgvk_entrypoints.c'],
+ command : [
+ prog_python, '@INPUT0@', '--xml', '@INPUT1@', '--proto', '--weak',
+ '--out-h', '@OUTPUT0@', '--out-c', '@OUTPUT1@', '--prefix', 'fgvk',
+ '--beta', with_vulkan_beta.to_string(),
+ ],
+ depend_files : vk_entrypoints_gen_depend_files,
+)
+
+fgvk_deps = [
+ idep_fgcc,
+ idep_nir,
+ idep_fg_headers,
+ idep_vulkan_runtime,
+ idep_vulkan_util,
+ idep_vulkan_wsi,
+ idep_vulkan_wsi_headers,
+]
+
+fgvk_flags = []
+
+libfgvk = static_library(
+ 'fgvk',
+ [
+ fgvk_entrypoints,
+ fgvk_files
+ ],
+ include_directories : [
+ inc_include,
+ inc_src,
+ ],
+ dependencies: fgvk_deps,
+ c_args : [no_override_init_args, fgvk_flags],
+ gnu_symbol_visibility : 'hidden',
+)
+
+libvulkan_frygon = shared_library(
+ 'vulkan_frygon',
+ link_whole : [libfgvk],
+ link_args: [ld_args_build_id, ld_args_bsymbolic, ld_args_gc_sections],
+ gnu_symbol_visibility : 'hidden',
+ install : true,
+)
+
+icd_file_name = libname_prefix + 'vulkan_frygon.' + libname_suffix
+
+frygon_icd = custom_target(
+ 'frygon_icd',
+ input : [vk_icd_gen, vk_api_xml],
+ output : 'frygon_icd.@0@.json'.format(host_machine.cpu()),
+ command : [
+ prog_python, '@INPUT0@',
+ '--api-version', '1.0', '--xml', '@INPUT1@',
+ '--sizeof-pointer', sizeof_pointer,
+ '--lib-path', vulkan_icd_lib_path / icd_file_name,
+ '--out', '@OUTPUT@',
+ ],
+ build_by_default : true,
+ install_dir : with_vulkan_icd_dir,
+ install_tag : 'runtime',
+ install : true,
+)
+
+_dev_icdname = 'frygon_devenv_icd.@0@.json'.format(host_machine.cpu())
+custom_target(
+ 'frygon_devenv_icd',
+ input : [vk_icd_gen, vk_api_xml],
+ output : _dev_icdname,
+ command : [
+ prog_python, '@INPUT0@',
+ '--api-version', '1.4', '--xml', '@INPUT1@',
+ '--sizeof-pointer', sizeof_pointer,
+ '--lib-path', meson.current_build_dir() / icd_file_name,
+ '--out', '@OUTPUT@',
+ ],
+ build_by_default : true,
+)
+
+devenv.append('VK_DRIVER_FILES', meson.current_build_dir() / _dev_icdname)
+# Deprecated: replaced by VK_DRIVER_FILES above
+devenv.append('VK_ICD_FILENAMES', meson.current_build_dir() / _dev_icdname)
+
+
diff --git a/src/meson.build b/src/meson.build
index f27dae33631..b2486b4e2fb 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -101,6 +101,9 @@ endif
if with_any_nouveau
subdir('nouveau')
endif
+if with_frygon_vk
+ subdir('frygon')
+endif
if with_gfxstream_vk
dep_virtgpu_kumquat_ffi = null_dep
if with_virtgpu_kumquat
diff --git a/src/vulkan/runtime/vk_sync.c b/src/vulkan/runtime/vk_sync.c
index e5eb1346f4d..9263cc6bdcd 100644
--- a/src/vulkan/runtime/vk_sync.c
+++ b/src/vulkan/runtime/vk_sync.c
@@ -666,7 +666,8 @@ vk_sync_signal_unwrap(struct vk_device *device,
signal->signal_value = ++binary->next_point;
}
- assert(!vk_sync_type_is_dummy(signal->sync->type));
+ // TODO uncomment when we have sync working in frygon
+ //assert(!vk_sync_type_is_dummy(signal->sync->type));
return VK_SUCCESS;
}
diff --git a/src/vulkan/runtime/vk_sync_dummy.c b/src/vulkan/runtime/vk_sync_dummy.c
index 1cab72f491b..f27413d637c 100644
--- a/src/vulkan/runtime/vk_sync_dummy.c
+++ b/src/vulkan/runtime/vk_sync_dummy.c
@@ -46,14 +46,23 @@ vk_sync_dummy_wait_many(struct vk_device *device,
return VK_SUCCESS;
}
+static VkResult
+vk_sync_dummy_reset(struct vk_device *device,
+ struct vk_sync *sync)
+{
+ return VK_SUCCESS;
+}
+
const struct vk_sync_type vk_sync_dummy_type = {
.size = sizeof(struct vk_sync),
.features = VK_SYNC_FEATURE_BINARY |
VK_SYNC_FEATURE_GPU_WAIT |
VK_SYNC_FEATURE_CPU_WAIT |
+ VK_SYNC_FEATURE_CPU_RESET |
VK_SYNC_FEATURE_WAIT_ANY |
VK_SYNC_FEATURE_WAIT_PENDING,
.init = vk_sync_dummy_init,
.finish = vk_sync_dummy_finish,
+ .reset = vk_sync_dummy_reset,
.wait_many = vk_sync_dummy_wait_many,
};