About Social Code
aboutsummaryrefslogtreecommitdiff
path: root/src/kosmickrisp/vulkan/kk_cmd_buffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kosmickrisp/vulkan/kk_cmd_buffer.c')
-rw-r--r--src/kosmickrisp/vulkan/kk_cmd_buffer.c533
1 files changed, 533 insertions, 0 deletions
diff --git a/src/kosmickrisp/vulkan/kk_cmd_buffer.c b/src/kosmickrisp/vulkan/kk_cmd_buffer.c
new file mode 100644
index 00000000000..c4366012a8f
--- /dev/null
+++ b/src/kosmickrisp/vulkan/kk_cmd_buffer.c
@@ -0,0 +1,533 @@
+/*
+ * Copyright © 2022 Collabora Ltd. and Red Hat Inc.
+ * Copyright 2025 LunarG, Inc.
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: MIT
+ */
+
+#include "kk_cmd_buffer.h"
+
+#include "kk_buffer.h"
+#include "kk_cmd_pool.h"
+#include "kk_descriptor_set_layout.h"
+#include "kk_encoder.h"
+#include "kk_entrypoints.h"
+
+#include "kosmickrisp/bridge/mtl_bridge.h"
+
+#include "vk_alloc.h"
+#include "vk_pipeline_layout.h"
+
+static void
+kk_descriptor_state_fini(struct kk_cmd_buffer *cmd,
+ struct kk_descriptor_state *desc)
+{
+ struct kk_cmd_pool *pool = kk_cmd_buffer_pool(cmd);
+
+ for (unsigned i = 0; i < KK_MAX_SETS; i++) {
+ vk_free(&pool->vk.alloc, desc->push[i]);
+ desc->push[i] = NULL;
+ desc->sets[i] = NULL; /* We also need to set sets to NULL so state doesn't
+ propagate if we reset it */
+ desc->sets_not_resident = 0u;
+ }
+}
+
+void
+kk_cmd_release_resources(struct kk_device *dev, struct kk_cmd_buffer *cmd)
+{
+ kk_cmd_release_dynamic_ds_state(cmd);
+ kk_descriptor_state_fini(cmd, &cmd->state.gfx.descriptors);
+ kk_descriptor_state_fini(cmd, &cmd->state.cs.descriptors);
+
+ /* Release all BOs used as descriptor buffers for submissions */
+ util_dynarray_foreach(&cmd->large_bos, struct kk_bo *, bo) {
+ kk_destroy_bo(dev, *bo);
+ }
+ util_dynarray_clear(&cmd->large_bos);
+}
+
+static void
+kk_destroy_cmd_buffer(struct vk_command_buffer *vk_cmd_buffer)
+{
+ struct kk_cmd_buffer *cmd =
+ container_of(vk_cmd_buffer, struct kk_cmd_buffer, vk);
+ struct kk_cmd_pool *pool = kk_cmd_buffer_pool(cmd);
+
+ vk_command_buffer_finish(&cmd->vk);
+ struct kk_device *dev = kk_cmd_buffer_device(cmd);
+
+ kk_cmd_release_resources(dev, cmd);
+
+ vk_free(&pool->vk.alloc, cmd);
+}
+
+static VkResult
+kk_create_cmd_buffer(struct vk_command_pool *vk_pool,
+ VkCommandBufferLevel level,
+ struct vk_command_buffer **cmd_buffer_out)
+{
+ struct kk_cmd_pool *pool = container_of(vk_pool, struct kk_cmd_pool, vk);
+ struct kk_device *dev = kk_cmd_pool_device(pool);
+ struct kk_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, &kk_cmd_buffer_ops, level);
+ if (result != VK_SUCCESS) {
+ vk_free(&pool->vk.alloc, cmd);
+ return result;
+ }
+
+ util_dynarray_init(&cmd->large_bos, NULL);
+
+ 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
+kk_reset_cmd_buffer(struct vk_command_buffer *vk_cmd_buffer,
+ UNUSED VkCommandBufferResetFlags flags)
+{
+ struct kk_cmd_buffer *cmd =
+ container_of(vk_cmd_buffer, struct kk_cmd_buffer, vk);
+ struct kk_device *dev = kk_cmd_buffer_device(cmd);
+
+ vk_command_buffer_reset(&cmd->vk);
+ kk_cmd_release_resources(dev, cmd);
+}
+
+const struct vk_command_buffer_ops kk_cmd_buffer_ops = {
+ .create = kk_create_cmd_buffer,
+ .reset = kk_reset_cmd_buffer,
+ .destroy = kk_destroy_cmd_buffer,
+};
+
+VKAPI_ATTR VkResult VKAPI_CALL
+kk_BeginCommandBuffer(VkCommandBuffer commandBuffer,
+ const VkCommandBufferBeginInfo *pBeginInfo)
+{
+ VK_FROM_HANDLE(kk_cmd_buffer, cmd, commandBuffer);
+
+ kk_reset_cmd_buffer(&cmd->vk, 0u);
+ vk_command_buffer_begin(&cmd->vk, pBeginInfo);
+
+ return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL
+kk_EndCommandBuffer(VkCommandBuffer commandBuffer)
+{
+ VK_FROM_HANDLE(kk_cmd_buffer, cmd, commandBuffer);
+
+ return vk_command_buffer_end(&cmd->vk);
+}
+
+static bool
+kk_can_ignore_barrier(VkAccessFlags2 access, VkPipelineStageFlags2 stage)
+{
+ if (access == VK_ACCESS_2_NONE || stage == VK_PIPELINE_STAGE_2_NONE)
+ return true;
+
+ const VkAccessFlags2 ignore_access =
+ VK_ACCESS_2_HOST_READ_BIT | VK_ACCESS_2_HOST_WRITE_BIT;
+ const VkPipelineStageFlags2 ignore_stage = VK_PIPELINE_STAGE_2_HOST_BIT;
+ return (!(access ^ ignore_access)) || (!(stage ^ ignore_stage));
+}
+
+VKAPI_ATTR void VKAPI_CALL
+kk_CmdPipelineBarrier2(VkCommandBuffer commandBuffer,
+ const VkDependencyInfo *pDependencyInfo)
+{
+ VK_FROM_HANDLE(kk_cmd_buffer, cmd, commandBuffer);
+ enum kk_encoder_type last_used = cmd->encoder->main.last_used;
+ kk_encoder_signal_fence_and_end(cmd);
+
+ /* If we were inside a render pass, restart it loading attachments */
+ if (last_used == KK_ENC_RENDER) {
+ struct kk_graphics_state *state = &cmd->state.gfx;
+ assert(state->render_pass_descriptor);
+ kk_encoder_start_render(cmd, state->render_pass_descriptor,
+ state->render.view_mask);
+ kk_cmd_buffer_dirty_all_gfx(cmd);
+ }
+}
+
+static void
+kk_bind_descriptor_sets(struct kk_descriptor_state *desc,
+ const VkBindDescriptorSetsInfoKHR *info)
+{
+ VK_FROM_HANDLE(vk_pipeline_layout, pipeline_layout, info->layout);
+
+ /* From the Vulkan 1.3.275 spec:
+ *
+ * "When binding a descriptor set (see Descriptor Set Binding) to
+ * set number N...
+ *
+ * If, additionally, the previously bound descriptor set for set
+ * N was bound using a pipeline layout not compatible for set N,
+ * then all bindings in sets numbered greater than N are
+ * disturbed."
+ *
+ * This means that, if some earlier set gets bound in such a way that
+ * it changes set_dynamic_buffer_start[s], this binding is implicitly
+ * invalidated. Therefore, we can always look at the current value
+ * of set_dynamic_buffer_start[s] as the base of our dynamic buffer
+ * range and it's only our responsibility to adjust all
+ * set_dynamic_buffer_start[p] for p > s as needed.
+ */
+ uint8_t dyn_buffer_start =
+ desc->root.set_dynamic_buffer_start[info->firstSet];
+
+ uint32_t next_dyn_offset = 0;
+ for (uint32_t i = 0; i < info->descriptorSetCount; ++i) {
+ unsigned s = i + info->firstSet;
+ VK_FROM_HANDLE(kk_descriptor_set, set, info->pDescriptorSets[i]);
+
+ if (desc->sets[s] != set) {
+ if (set != NULL) {
+ desc->root.sets[s] = set->addr;
+ desc->set_sizes[s] = set->size;
+ } else {
+ desc->root.sets[s] = 0;
+ desc->set_sizes[s] = 0;
+ }
+ desc->sets[s] = set;
+
+ desc->sets_not_resident |= BITFIELD_BIT(s);
+
+ /* Binding descriptors invalidates push descriptors */
+ desc->push_dirty &= ~BITFIELD_BIT(s);
+ }
+
+ if (pipeline_layout->set_layouts[s] != NULL) {
+ const struct kk_descriptor_set_layout *set_layout =
+ vk_to_kk_descriptor_set_layout(pipeline_layout->set_layouts[s]);
+
+ if (set != NULL && set_layout->dynamic_buffer_count > 0) {
+ for (uint32_t j = 0; j < set_layout->dynamic_buffer_count; j++) {
+ struct kk_buffer_address addr = set->dynamic_buffers[j];
+ addr.base_addr += info->pDynamicOffsets[next_dyn_offset + j];
+ desc->root.dynamic_buffers[dyn_buffer_start + j] = addr;
+ }
+ next_dyn_offset += set->layout->dynamic_buffer_count;
+ }
+
+ dyn_buffer_start += set_layout->dynamic_buffer_count;
+ } else {
+ assert(set == NULL);
+ }
+ }
+ assert(dyn_buffer_start <= KK_MAX_DYNAMIC_BUFFERS);
+ assert(next_dyn_offset <= info->dynamicOffsetCount);
+
+ for (uint32_t s = info->firstSet + info->descriptorSetCount; s < KK_MAX_SETS;
+ s++)
+ desc->root.set_dynamic_buffer_start[s] = dyn_buffer_start;
+
+ desc->root_dirty = true;
+}
+
+VKAPI_ATTR void VKAPI_CALL
+kk_CmdBindDescriptorSets2KHR(
+ VkCommandBuffer commandBuffer,
+ const VkBindDescriptorSetsInfoKHR *pBindDescriptorSetsInfo)
+{
+ VK_FROM_HANDLE(kk_cmd_buffer, cmd, commandBuffer);
+
+ if (pBindDescriptorSetsInfo->stageFlags & VK_SHADER_STAGE_ALL_GRAPHICS) {
+ kk_bind_descriptor_sets(&cmd->state.gfx.descriptors,
+ pBindDescriptorSetsInfo);
+ }
+
+ if (pBindDescriptorSetsInfo->stageFlags & VK_SHADER_STAGE_COMPUTE_BIT) {
+ kk_bind_descriptor_sets(&cmd->state.cs.descriptors,
+ pBindDescriptorSetsInfo);
+ }
+}
+
+static struct kk_push_descriptor_set *
+kk_cmd_push_descriptors(struct kk_cmd_buffer *cmd,
+ struct kk_descriptor_state *desc,
+ struct kk_descriptor_set_layout *set_layout,
+ uint32_t set)
+{
+ struct kk_device *dev = kk_cmd_buffer_device(cmd);
+ assert(set < KK_MAX_SETS);
+ if (unlikely(desc->push[set] == NULL)) {
+ size_t size = sizeof(*desc->push[set]) +
+ (sizeof(mtl_resource *) * set_layout->descriptor_count);
+ desc->push[set] = vk_zalloc(&cmd->vk.pool->alloc, size, 8,
+ VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+ if (unlikely(desc->push[set] == NULL)) {
+ vk_command_buffer_set_error(&cmd->vk, VK_ERROR_OUT_OF_HOST_MEMORY);
+ return NULL;
+ }
+ desc->push[set]->layout = set_layout;
+ for (uint32_t i = 0u; i < set_layout->descriptor_count; ++i)
+ desc->push[set]->mtl_resources[i] = dev->null_descriptor->map;
+ }
+
+ /* Pushing descriptors replaces whatever sets are bound */
+ desc->sets[set] = NULL;
+ desc->push_dirty |= BITFIELD_BIT(set);
+ desc->sets_not_resident |= BITFIELD_BIT(set);
+
+ return desc->push[set];
+}
+
+static void
+kk_push_descriptor_set(struct kk_cmd_buffer *cmd,
+ struct kk_descriptor_state *desc,
+ const VkPushDescriptorSetInfoKHR *info)
+{
+ VK_FROM_HANDLE(vk_pipeline_layout, pipeline_layout, info->layout);
+
+ struct kk_descriptor_set_layout *set_layout =
+ vk_to_kk_descriptor_set_layout(pipeline_layout->set_layouts[info->set]);
+
+ struct kk_push_descriptor_set *push_set =
+ kk_cmd_push_descriptors(cmd, desc, set_layout, info->set);
+ if (unlikely(push_set == NULL))
+ return;
+
+ kk_push_descriptor_set_update(push_set, info->descriptorWriteCount,
+ info->pDescriptorWrites);
+}
+
+VKAPI_ATTR void VKAPI_CALL
+kk_CmdPushDescriptorSet2KHR(
+ VkCommandBuffer commandBuffer,
+ const VkPushDescriptorSetInfoKHR *pPushDescriptorSetInfo)
+{
+ VK_FROM_HANDLE(kk_cmd_buffer, cmd, commandBuffer);
+
+ if (pPushDescriptorSetInfo->stageFlags & VK_SHADER_STAGE_ALL_GRAPHICS) {
+ kk_push_descriptor_set(cmd, &cmd->state.gfx.descriptors,
+ pPushDescriptorSetInfo);
+ }
+
+ if (pPushDescriptorSetInfo->stageFlags & VK_SHADER_STAGE_COMPUTE_BIT) {
+ kk_push_descriptor_set(cmd, &cmd->state.cs.descriptors,
+ pPushDescriptorSetInfo);
+ }
+}
+
+static void
+kk_push_constants(UNUSED struct kk_cmd_buffer *cmd,
+ struct kk_descriptor_state *desc,
+ const VkPushConstantsInfoKHR *info)
+{
+ memcpy(desc->root.push + info->offset, info->pValues, info->size);
+ desc->root_dirty = true;
+}
+
+VKAPI_ATTR void VKAPI_CALL
+kk_CmdPushConstants2KHR(VkCommandBuffer commandBuffer,
+ const VkPushConstantsInfoKHR *pPushConstantsInfo)
+{
+ VK_FROM_HANDLE(kk_cmd_buffer, cmd, commandBuffer);
+
+ if (pPushConstantsInfo->stageFlags & VK_SHADER_STAGE_ALL_GRAPHICS)
+ kk_push_constants(cmd, &cmd->state.gfx.descriptors, pPushConstantsInfo);
+
+ if (pPushConstantsInfo->stageFlags & VK_SHADER_STAGE_COMPUTE_BIT)
+ kk_push_constants(cmd, &cmd->state.cs.descriptors, pPushConstantsInfo);
+}
+
+void
+kk_cmd_buffer_write_descriptor_buffer(struct kk_cmd_buffer *cmd,
+ struct kk_descriptor_state *desc,
+ size_t size, size_t offset)
+{
+ assert(size + offset <= sizeof(desc->root.sets));
+
+ struct kk_bo *root_buffer = desc->root.root_buffer;
+
+ memcpy(root_buffer->cpu, (uint8_t *)desc->root.sets + offset, size);
+}
+
+void
+kk_cmd_release_dynamic_ds_state(struct kk_cmd_buffer *cmd)
+{
+ if (cmd->state.gfx.is_depth_stencil_dynamic &&
+ cmd->state.gfx.depth_stencil_state)
+ mtl_release(cmd->state.gfx.depth_stencil_state);
+ cmd->state.gfx.depth_stencil_state = NULL;
+}
+
+struct kk_bo *
+kk_cmd_allocate_buffer(struct kk_cmd_buffer *cmd, size_t size_B,
+ size_t alignment_B)
+{
+ struct kk_bo *buffer = NULL;
+
+ VkResult result = kk_alloc_bo(kk_cmd_buffer_device(cmd), &cmd->vk.base,
+ size_B, alignment_B, &buffer);
+ if (result != VK_SUCCESS) {
+ vk_command_buffer_set_error(&cmd->vk, result);
+ return NULL;
+ }
+ util_dynarray_append(&cmd->large_bos, struct kk_bo *, buffer);
+
+ return buffer;
+}
+
+struct kk_pool
+kk_pool_upload(struct kk_cmd_buffer *cmd, void *data, size_t size_B,
+ size_t alignment_B)
+{
+ struct kk_bo *bo = kk_cmd_allocate_buffer(cmd, size_B, alignment_B);
+ if (!bo)
+ return (struct kk_pool){};
+
+ memcpy(bo->cpu, data, size_B);
+ struct kk_pool pool = {.handle = bo->map, .gpu = bo->gpu, .cpu = bo->cpu};
+
+ return pool;
+}
+
+uint64_t
+kk_upload_descriptor_root(struct kk_cmd_buffer *cmd,
+ VkPipelineBindPoint bind_point)
+{
+ struct kk_descriptor_state *desc = kk_get_descriptors_state(cmd, bind_point);
+ struct kk_root_descriptor_table *root = &desc->root;
+ struct kk_bo *bo = kk_cmd_allocate_buffer(cmd, sizeof(*root), 8u);
+ if (bo == NULL)
+ return 0u;
+
+ memcpy(bo->cpu, root, sizeof(*root));
+ root->root_buffer = bo;
+
+ return bo->gpu;
+}
+
+void
+kk_cmd_buffer_flush_push_descriptors(struct kk_cmd_buffer *cmd,
+ struct kk_descriptor_state *desc)
+{
+ u_foreach_bit(set_idx, desc->push_dirty) {
+ struct kk_push_descriptor_set *push_set = desc->push[set_idx];
+ struct kk_bo *bo = kk_cmd_allocate_buffer(cmd, sizeof(push_set->data),
+ KK_MIN_UBO_ALIGNMENT);
+ if (bo == NULL)
+ return;
+
+ memcpy(bo->cpu, push_set->data, sizeof(push_set->data));
+ push_set->mtl_descriptor_buffer = bo->map;
+ desc->root.sets[set_idx] = bo->gpu;
+ desc->set_sizes[set_idx] = sizeof(push_set->data);
+ }
+
+ desc->root_dirty = true;
+ desc->push_dirty = 0;
+}
+
+static void
+kk_make_graphics_descriptor_resources_resident(struct kk_cmd_buffer *cmd)
+{
+ struct kk_descriptor_state *desc = &cmd->state.gfx.descriptors;
+ mtl_render_encoder *encoder = kk_render_encoder(cmd);
+ /* Make resources resident as required by Metal */
+ u_foreach_bit(set_index, desc->sets_not_resident) {
+ mtl_resource *descriptor_buffer = NULL;
+
+ /* If we have no set, it means it was a push set */
+ if (desc->sets[set_index]) {
+ struct kk_descriptor_set *set = desc->sets[set_index];
+ descriptor_buffer = set->mtl_descriptor_buffer;
+ } else {
+ struct kk_push_descriptor_set *push_set = desc->push[set_index];
+ descriptor_buffer = push_set->mtl_descriptor_buffer;
+ }
+
+ /* We could have empty descriptor sets for some reason... */
+ if (descriptor_buffer) {
+ mtl_render_use_resource(encoder, descriptor_buffer,
+ MTL_RESOURCE_USAGE_READ);
+ }
+ }
+
+ desc->sets_not_resident = 0u;
+}
+
+static void
+kk_make_compute_descriptor_resources_resident(struct kk_cmd_buffer *cmd)
+{
+ struct kk_descriptor_state *desc = &cmd->state.cs.descriptors;
+ mtl_compute_encoder *encoder = kk_compute_encoder(cmd);
+ u_foreach_bit(set_index, desc->sets_not_resident) {
+ /* Make resources resident as required by Metal */
+ mtl_resource *descriptor_buffer = NULL;
+ if (desc->sets[set_index]) {
+ struct kk_descriptor_set *set = desc->sets[set_index];
+ descriptor_buffer = set->mtl_descriptor_buffer;
+ } else {
+ struct kk_push_descriptor_set *push_set = desc->push[set_index];
+ descriptor_buffer = push_set->mtl_descriptor_buffer;
+ }
+
+ /* We could have empty descriptor sets for some reason... */
+ if (descriptor_buffer) {
+ mtl_compute_use_resource(encoder, descriptor_buffer,
+ MTL_RESOURCE_USAGE_READ);
+ }
+ }
+
+ desc->sets_not_resident = 0u;
+}
+
+void
+kk_make_descriptor_resources_resident(struct kk_cmd_buffer *cmd,
+ VkPipelineBindPoint bind_point)
+{
+ if (bind_point == VK_PIPELINE_BIND_POINT_GRAPHICS)
+ kk_make_graphics_descriptor_resources_resident(cmd);
+ else if (bind_point == VK_PIPELINE_BIND_POINT_COMPUTE)
+ kk_make_compute_descriptor_resources_resident(cmd);
+}
+
+void
+kk_cmd_write(struct kk_cmd_buffer *cmd, mtl_buffer *buffer, uint64_t addr,
+ uint64_t value)
+{
+ util_dynarray_append(&cmd->encoder->imm_writes, uint64_t, addr);
+ util_dynarray_append(&cmd->encoder->imm_writes, uint64_t, value);
+ util_dynarray_append(&cmd->encoder->resident_buffers, mtl_buffer *, buffer);
+}
+
+VKAPI_ATTR void VKAPI_CALL
+kk_CmdPushDescriptorSetWithTemplate2KHR(
+ VkCommandBuffer commandBuffer, const VkPushDescriptorSetWithTemplateInfoKHR
+ *pPushDescriptorSetWithTemplateInfo)
+{
+ VK_FROM_HANDLE(kk_cmd_buffer, cmd, commandBuffer);
+ VK_FROM_HANDLE(vk_descriptor_update_template, template,
+ pPushDescriptorSetWithTemplateInfo->descriptorUpdateTemplate);
+ VK_FROM_HANDLE(vk_pipeline_layout, pipeline_layout,
+ pPushDescriptorSetWithTemplateInfo->layout);
+
+ struct kk_descriptor_state *desc =
+ kk_get_descriptors_state(cmd, template->bind_point);
+ struct kk_descriptor_set_layout *set_layout = vk_to_kk_descriptor_set_layout(
+ pipeline_layout->set_layouts[pPushDescriptorSetWithTemplateInfo->set]);
+ struct kk_push_descriptor_set *push_set = kk_cmd_push_descriptors(
+ cmd, desc, set_layout, pPushDescriptorSetWithTemplateInfo->set);
+ if (unlikely(push_set == NULL))
+ return;
+
+ kk_push_descriptor_set_update_template(
+ push_set, set_layout, template,
+ pPushDescriptorSetWithTemplateInfo->pData);
+}