mirror of
https://github.com/elwrcl/intel-hard-graphics.git
synced 2026-11-01 11:18:03 +03:00
init
This commit is contained in:
12511 files changed
+6156641
No files matched your search
@@ -0,0 +1,7 @@
|
||||
BasedOnStyle: InheritParentConfig
|
||||
DisableFormat: false
|
||||
|
||||
AlignConsecutiveBitFields: true
|
||||
ColumnLimit: 80
|
||||
BreakStringLiterals: false
|
||||
SpaceBeforeParens: ControlStatementsExceptControlMacros
|
||||
@@ -0,0 +1,63 @@
|
||||
# Copyright 2025 LunarG, Inc.
|
||||
# Copyright 2025 Google LLC
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
mtl_bridge_files = files(
|
||||
'vk_to_mtl_map.h',
|
||||
'vk_to_mtl_map.c',
|
||||
'mtl_format.h',
|
||||
)
|
||||
|
||||
if host_machine.system() == 'darwin'
|
||||
mtl_bridge_files += files(
|
||||
'mtl_bridge.m',
|
||||
'mtl_buffer.m',
|
||||
'mtl_command_buffer.m',
|
||||
'mtl_command_queue.m',
|
||||
'mtl_compute_state.m',
|
||||
'mtl_device.m',
|
||||
'mtl_encoder.m',
|
||||
'mtl_heap.m',
|
||||
'mtl_library.m',
|
||||
'mtl_render_state.m',
|
||||
'mtl_residency_set.m',
|
||||
'mtl_sampler.m',
|
||||
'mtl_sync.m',
|
||||
'mtl_texture.m',
|
||||
)
|
||||
else
|
||||
mtl_bridge_files += files(
|
||||
'stubs/mtl_bridge.c',
|
||||
'stubs/mtl_buffer.c',
|
||||
'stubs/mtl_command_buffer.c',
|
||||
'stubs/mtl_command_queue.c',
|
||||
'stubs/mtl_compute_state.c',
|
||||
'stubs/mtl_device.c',
|
||||
'stubs/mtl_encoder.c',
|
||||
'stubs/mtl_heap.c',
|
||||
'stubs/mtl_library.c',
|
||||
'stubs/mtl_render_state.c',
|
||||
'stubs/mtl_residency_set.c',
|
||||
'stubs/mtl_sampler.c',
|
||||
'stubs/mtl_sync.c',
|
||||
'stubs/mtl_texture.c',
|
||||
)
|
||||
endif
|
||||
|
||||
mtl_bridge_dependencies = [
|
||||
idep_vulkan_lite_runtime_headers,
|
||||
idep_vulkan_util
|
||||
]
|
||||
|
||||
libmtl_bridge = static_library(
|
||||
'mtl_bridge',
|
||||
[mtl_bridge_files],
|
||||
include_directories : [include_directories('../vulkan/'), inc_include, inc_src],
|
||||
dependencies : mtl_bridge_dependencies,
|
||||
gnu_symbol_visibility: 'hidden',
|
||||
build_by_default: false,
|
||||
)
|
||||
|
||||
idep_mtl_bridge = declare_dependency(
|
||||
link_with : libmtl_bridge,
|
||||
)
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef KK_BRIDGE_H
|
||||
#define KK_BRIDGE_H 1
|
||||
|
||||
/* C wrappers for Metal. May not be complete. If you find something you need
|
||||
* feel free to add them where they belong. As a rule of thumb, member functions
|
||||
* go in the objects' .h/.m/.c Naming convention for wrappers is:
|
||||
* object_type* mtl_new_object_type(params...);
|
||||
* void mtl_member_function(object_type* ptr, params...);
|
||||
* void mtl_object_set_member(object_type* ptr, member_type value);
|
||||
* member_type mtl_object_get_member(object_type* ptr);
|
||||
*
|
||||
* Functions that have new in the name require to release the returned object
|
||||
* via mtl_release(object);
|
||||
* */
|
||||
|
||||
#include "mtl_types.h"
|
||||
|
||||
#include "mtl_buffer.h"
|
||||
#include "mtl_command_buffer.h"
|
||||
#include "mtl_command_queue.h"
|
||||
#include "mtl_compute_state.h"
|
||||
#include "mtl_device.h"
|
||||
#include "mtl_encoder.h"
|
||||
#include "mtl_format.h"
|
||||
#include "mtl_heap.h"
|
||||
#include "mtl_library.h"
|
||||
#include "mtl_render_state.h"
|
||||
#include "mtl_residency_set.h"
|
||||
#include "mtl_sampler.h"
|
||||
#include "mtl_sync.h"
|
||||
#include "mtl_texture.h"
|
||||
|
||||
mtl_texture *mtl_drawable_get_texture(void *drawable_ptr);
|
||||
|
||||
void *mtl_retain(void *handle);
|
||||
void mtl_release(void *handle);
|
||||
|
||||
#endif /* KK_BRIDGE_H */
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_bridge.h"
|
||||
|
||||
// kk_image_layout.h should also includes "vulkan/vulkan.h", but just to be safe
|
||||
#include "vulkan/vulkan.h"
|
||||
#include "kk_image_layout.h"
|
||||
|
||||
#include "util/macros.h"
|
||||
|
||||
#include <Metal/MTLCommandBuffer.h>
|
||||
#include <Metal/MTLCommandQueue.h>
|
||||
#include <Metal/MTLDevice.h>
|
||||
#include <Metal/MTLHeap.h>
|
||||
#include <Metal/MTLEvent.h>
|
||||
|
||||
#include <QuartzCore/CAMetalLayer.h>
|
||||
|
||||
static_assert(sizeof(MTLResourceID) == sizeof(uint64_t), "Must match, otherwise descriptors are broken");
|
||||
|
||||
mtl_texture *
|
||||
mtl_drawable_get_texture(void *drawable_ptr)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<CAMetalDrawable> drawable = (id<CAMetalDrawable>)drawable_ptr;
|
||||
return drawable.texture;
|
||||
}
|
||||
}
|
||||
|
||||
void *
|
||||
mtl_retain(void *handle)
|
||||
{
|
||||
@autoreleasepool {
|
||||
NSObject *obj = (NSObject *)handle;
|
||||
return [obj retain];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_release(void *handle)
|
||||
{
|
||||
@autoreleasepool {
|
||||
NSObject *obj = (NSObject *)handle;
|
||||
[obj release];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef MTL_BUFFER_H
|
||||
#define MTL_BUFFER_H 1
|
||||
|
||||
#include "mtl_types.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
struct kk_image_layout;
|
||||
|
||||
/* Utils */
|
||||
uint64_t mtl_buffer_get_length(mtl_buffer *buffer);
|
||||
uint64_t mtl_buffer_get_gpu_address(mtl_buffer *buffer);
|
||||
/* Gets CPU address */
|
||||
void *mtl_get_contents(mtl_buffer *buffer);
|
||||
|
||||
/* Allocation from buffer */
|
||||
mtl_texture *mtl_new_texture_with_descriptor_linear(
|
||||
mtl_buffer *buffer, const struct kk_image_layout *layout, uint64_t offset);
|
||||
|
||||
#endif /* MTL_BUFFER_H */
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_buffer.h"
|
||||
|
||||
/* TODO_KOSMICKRISP Remove */
|
||||
#include "kk_image_layout.h"
|
||||
|
||||
#include <Metal/MTLBuffer.h>
|
||||
#include <Metal/MTLTexture.h>
|
||||
|
||||
uint64_t
|
||||
mtl_buffer_get_length(mtl_buffer *buffer)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLBuffer> buf = (id<MTLBuffer>)buffer;
|
||||
return buf.length;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t
|
||||
mtl_buffer_get_gpu_address(mtl_buffer *buffer)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLBuffer> buf = (id<MTLBuffer>)buffer;
|
||||
return [buf gpuAddress];
|
||||
}
|
||||
}
|
||||
|
||||
void *
|
||||
mtl_get_contents(mtl_buffer *buffer)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLBuffer> buf = (id<MTLBuffer>)buffer;
|
||||
return [buf contents];
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO_KOSMICKRISP This is a duplicate, but both should be removed once we move kk_image_layout to the bridge. */
|
||||
static MTLTextureDescriptor *
|
||||
mtl_new_texture_descriptor(const struct kk_image_layout *layout)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLTextureDescriptor *descriptor = [MTLTextureDescriptor new];
|
||||
descriptor.textureType = (MTLTextureType)layout->type;
|
||||
descriptor.pixelFormat = layout->format.mtl;
|
||||
descriptor.width = layout->width_px;
|
||||
descriptor.height = layout->height_px;
|
||||
descriptor.depth = layout->depth_px;
|
||||
descriptor.mipmapLevelCount = layout->levels;
|
||||
descriptor.sampleCount = layout->sample_count_sa;
|
||||
descriptor.arrayLength = layout->layers;
|
||||
descriptor.allowGPUOptimizedContents = layout->optimized_layout;
|
||||
descriptor.usage = (MTLTextureUsage)layout->usage;
|
||||
/* We don't set the swizzle because Metal complains when the usage has store or render target with swizzle... */
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
mtl_texture *
|
||||
mtl_new_texture_with_descriptor_linear(mtl_buffer *buffer,
|
||||
const struct kk_image_layout *layout,
|
||||
uint64_t offset)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLBuffer> buf = (id<MTLBuffer>)buffer;
|
||||
MTLTextureDescriptor *descriptor = [mtl_new_texture_descriptor(layout) autorelease];
|
||||
descriptor.resourceOptions = buf.resourceOptions;
|
||||
id<MTLTexture> texture = [buf newTextureWithDescriptor:descriptor offset:offset bytesPerRow:layout->linear_stride_B];
|
||||
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef MTL_COMMAND_BUFFER_H
|
||||
#define MTL_COMMAND_BUFFER_H 1
|
||||
|
||||
#include "mtl_types.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void mtl_encode_signal_event(mtl_command_buffer *cmd_buf_handle,
|
||||
mtl_event *event_handle, uint64_t value);
|
||||
|
||||
void mtl_encode_wait_for_event(mtl_command_buffer *cmd_buf_handle,
|
||||
mtl_event *event_handle, uint64_t value);
|
||||
|
||||
void mtl_add_completed_handler(mtl_command_buffer *cmd,
|
||||
void (*callback)(void *data), void *data);
|
||||
|
||||
void mtl_command_buffer_commit(mtl_command_buffer *cmd_buf);
|
||||
|
||||
void mtl_present_drawable(mtl_command_buffer *cmd_buf, void *drawable);
|
||||
|
||||
#endif /* MTL_COMMAND_BUFFER_H */
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_command_buffer.h"
|
||||
|
||||
#include <Metal/MTLCommandBuffer.h>
|
||||
#include <QuartzCore/CAMetalLayer.h>
|
||||
|
||||
void
|
||||
mtl_encode_signal_event(mtl_command_buffer *cmd_buf_handle,
|
||||
mtl_event *event_handle, uint64_t value)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLCommandBuffer> cmd_buf = (id<MTLCommandBuffer>)cmd_buf_handle;
|
||||
id<MTLEvent> event = (id<MTLEvent>)event_handle;
|
||||
[cmd_buf encodeSignalEvent:event value:value];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_encode_wait_for_event(mtl_command_buffer *cmd_buf_handle,
|
||||
mtl_event *event_handle, uint64_t value)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLCommandBuffer> cmd_buf = (id<MTLCommandBuffer>)cmd_buf_handle;
|
||||
id<MTLEvent> event = (id<MTLEvent>)event_handle;
|
||||
[cmd_buf encodeWaitForEvent:event value:value];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_add_completed_handler(mtl_command_buffer *cmd, void (*callback)(void *data),
|
||||
void *data)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLCommandBuffer> mtl_cmd = (id<MTLCommandBuffer>)cmd;
|
||||
[mtl_cmd addCompletedHandler:^(id<MTLCommandBuffer> _Nonnull cmd_buf) {
|
||||
@autoreleasepool {
|
||||
if (callback)
|
||||
callback(data);
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_command_buffer_commit(mtl_command_buffer *cmd_buffer)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLCommandBuffer> cmd_buf = (id<MTLCommandBuffer>)cmd_buffer;
|
||||
[cmd_buf commit];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_present_drawable(mtl_command_buffer *cmd_buf, void *drawable_ptr)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLCommandBuffer> cmd = (id<MTLCommandBuffer>)cmd_buf;
|
||||
id<CAMetalDrawable> drawable = [(id<CAMetalDrawable>)drawable_ptr autorelease];
|
||||
[cmd presentDrawable:drawable];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef MTL_COMMAND_QUEUE_H
|
||||
#define MTL_COMMAND_QUEUE_H 1
|
||||
|
||||
#include "mtl_types.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
mtl_command_queue *mtl_new_command_queue(mtl_device *device,
|
||||
uint32_t cmd_buffer_count);
|
||||
|
||||
mtl_command_buffer *mtl_new_command_buffer(mtl_command_queue *cmd_queue);
|
||||
|
||||
void mtl_command_queue_add_residency_set(mtl_command_queue *cmd_queue,
|
||||
mtl_residency_set *set);
|
||||
|
||||
void mtl_command_queue_remove_residency_set(mtl_command_queue *cmd_queue,
|
||||
mtl_residency_set *set);
|
||||
|
||||
#endif /* MTL_COMMAND_QUEUE_H */
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_command_queue.h"
|
||||
|
||||
#include <Metal/MTLDevice.h>
|
||||
#include <Metal/MTLCommandQueue.h>
|
||||
|
||||
mtl_command_queue *
|
||||
mtl_new_command_queue(mtl_device *device, uint32_t cmd_buffer_count)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> dev = (id<MTLDevice>)device;
|
||||
return [dev newCommandQueueWithMaxCommandBufferCount:cmd_buffer_count];
|
||||
}
|
||||
}
|
||||
|
||||
mtl_command_buffer *
|
||||
mtl_new_command_buffer(mtl_command_queue *cmd_queue)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLCommandQueue> queue = (id<MTLCommandQueue>)cmd_queue;
|
||||
return [[queue commandBuffer] retain];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_command_queue_add_residency_set(mtl_command_queue *cmd_queue,
|
||||
mtl_residency_set *set)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLCommandQueue> queue = (id<MTLCommandQueue>)cmd_queue;
|
||||
id<MTLResidencySet> s = (id<MTLResidencySet>)set;
|
||||
return [queue addResidencySet:s];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_command_queue_remove_residency_set(mtl_command_queue *cmd_queue,
|
||||
mtl_residency_set *set)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLCommandQueue> queue = (id<MTLCommandQueue>)cmd_queue;
|
||||
id<MTLResidencySet> s = (id<MTLResidencySet>)set;
|
||||
return [queue removeResidencySet:s];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_types.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
mtl_compute_pipeline_state *
|
||||
mtl_new_compute_pipeline_state(mtl_device *device, mtl_function *function,
|
||||
uint64_t max_total_threads_per_threadgroup);
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_compute_state.h"
|
||||
|
||||
#include <Metal/MTLComputePipeline.h>
|
||||
|
||||
mtl_compute_pipeline_state *
|
||||
mtl_new_compute_pipeline_state(mtl_device *device, mtl_function *function,
|
||||
uint64_t max_total_threads_per_threadgroup)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> dev = (id<MTLDevice>)device;
|
||||
id<MTLComputePipelineState> pipeline = NULL;
|
||||
|
||||
MTLComputePipelineDescriptor *comp_desc = [[[MTLComputePipelineDescriptor alloc] init] autorelease];
|
||||
NSError *error;
|
||||
comp_desc.computeFunction = (id<MTLFunction>)function;
|
||||
comp_desc.maxTotalThreadsPerThreadgroup = max_total_threads_per_threadgroup;
|
||||
pipeline = [dev newComputePipelineStateWithDescriptor:comp_desc options:0 reflection:nil error:&error];
|
||||
|
||||
/* TODO_KOSMICKRISP Error checking */
|
||||
|
||||
return pipeline;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef MTL_DEVICE_H
|
||||
#define MTL_DEVICE_H 1
|
||||
|
||||
#include "mtl_format.h"
|
||||
#include "mtl_types.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* TODO_KOSMICKRISP Remove */
|
||||
struct kk_image_layout;
|
||||
|
||||
/* Device creation */
|
||||
mtl_device *mtl_device_create(void);
|
||||
|
||||
/* Device operations */
|
||||
void mtl_start_gpu_capture(mtl_device *mtl_dev_handle, const char *directory);
|
||||
void mtl_stop_gpu_capture(void);
|
||||
|
||||
/* Device feature query */
|
||||
void mtl_device_get_name(mtl_device *dev, char buffer[256]);
|
||||
void mtl_device_get_architecture_name(mtl_device *dev, char buffer[256]);
|
||||
uint64_t mtl_device_get_peer_group_id(mtl_device *dev);
|
||||
uint32_t mtl_device_get_peer_index(mtl_device *dev);
|
||||
uint64_t mtl_device_get_registry_id(mtl_device *dev);
|
||||
bool mtl_device_supports_sample_count(mtl_device *dev, uint32_t sample_count);
|
||||
struct mtl_size mtl_device_max_threads_per_threadgroup(mtl_device *dev);
|
||||
uint32_t mtl_device_max_threadgroup_memory_length(mtl_device *dev);
|
||||
uint64_t mtl_device_max_buffer_length(mtl_device *dev);
|
||||
uint64_t mtl_device_recommended_max_working_set_size(mtl_device *dev);
|
||||
uint64_t mtl_device_current_allocated_size(mtl_device *dev);
|
||||
|
||||
/* Timestamp query */
|
||||
uint64_t mtl_device_get_gpu_timestamp(mtl_device *dev);
|
||||
|
||||
/* Resource queries */
|
||||
void mtl_heap_buffer_size_and_align_with_length(mtl_device *device,
|
||||
uint64_t *size_B,
|
||||
uint64_t *align_B);
|
||||
uint64_t mtl_minimum_linear_texture_alignment_for_pixel_format(
|
||||
mtl_device *device, enum mtl_pixel_format format);
|
||||
void mtl_heap_texture_size_and_align_with_descriptor(
|
||||
mtl_device *device, struct kk_image_layout *layout, uint64_t *size_B,
|
||||
uint64_t *align_B);
|
||||
|
||||
uint32_t mtl_sparse_tile_size_in_bytes(mtl_device *device);
|
||||
struct mtl_size mtl_sparse_tile_size(mtl_device *device,
|
||||
struct kk_image_layout *layout);
|
||||
struct mtl_size mtl_sparse_tile_count(mtl_device *device,
|
||||
struct kk_image_layout *layout,
|
||||
struct mtl_size tile_size);
|
||||
|
||||
/* Resource creation */
|
||||
mtl_buffer *mtl_new_buffer_with_bytes_no_copy(mtl_device *device, void *ptr,
|
||||
uint64_t size_B);
|
||||
|
||||
#endif /* MTL_DEVICE_H */
|
||||
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_device.h"
|
||||
|
||||
/* TODO_KOSMICKRISP Remove */
|
||||
#include "kk_image_layout.h"
|
||||
#include "kk_private.h"
|
||||
|
||||
#include <Metal/MTLDevice.h>
|
||||
#include <Metal/MTLCaptureManager.h>
|
||||
|
||||
/* Device creation */
|
||||
mtl_device *
|
||||
mtl_device_create()
|
||||
{
|
||||
mtl_device *device = 0u;
|
||||
|
||||
@autoreleasepool {
|
||||
NSArray<id<MTLDevice>> *devs = [MTLCopyAllDevices() autorelease];
|
||||
uint32_t device_count = [devs count];
|
||||
|
||||
for (uint32_t i = 0u; i < device_count; ++i) {
|
||||
if (@available(macOS 10.15, *)) {
|
||||
if (!device && [devs[i] supportsFamily:MTLGPUFamilyMetal3]) {
|
||||
device = (mtl_device *)[devs[i] retain];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
/* Device operations */
|
||||
void
|
||||
mtl_start_gpu_capture(mtl_device *mtl_dev_handle, const char *directory)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> mtl_dev = (id<MTLDevice>)mtl_dev_handle;
|
||||
MTLCaptureManager *captureMgr = [MTLCaptureManager sharedCaptureManager];
|
||||
|
||||
MTLCaptureDescriptor *captureDesc = [[MTLCaptureDescriptor new] autorelease];
|
||||
captureDesc.captureObject = mtl_dev;
|
||||
captureDesc.destination = MTLCaptureDestinationDeveloperTools;
|
||||
|
||||
if (directory && [captureMgr supportsDestination: MTLCaptureDestinationGPUTraceDocument]) {
|
||||
NSString *dir = [NSString stringWithUTF8String:directory];
|
||||
NSString *pname = [[NSProcessInfo processInfo] processName];
|
||||
NSString *capture_path = [NSString stringWithFormat:@"%@/%@.gputrace", dir, pname];
|
||||
captureDesc.destination = MTLCaptureDestinationGPUTraceDocument;
|
||||
captureDesc.outputURL = [NSURL fileURLWithPath: capture_path];
|
||||
}
|
||||
|
||||
NSError *err = nil;
|
||||
if (![captureMgr startCaptureWithDescriptor:captureDesc error:&err]) {
|
||||
fprintf(stderr, "Failed to automatically start GPU capture session (Error code %li) using startCaptureWithDescriptor: %s\n",
|
||||
(long)err.code, err.localizedDescription.UTF8String);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_stop_gpu_capture()
|
||||
{
|
||||
@autoreleasepool {
|
||||
[[MTLCaptureManager sharedCaptureManager] stopCapture];
|
||||
}
|
||||
}
|
||||
|
||||
/* Device feature query */
|
||||
void
|
||||
mtl_device_get_name(mtl_device *dev, char buffer[256])
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> device = (id<MTLDevice>)dev;
|
||||
[device.name getCString:buffer maxLength:(sizeof(char) * 256) encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_device_get_architecture_name(mtl_device *dev, char buffer[256])
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> device = (id<MTLDevice>)dev;
|
||||
[device.architecture.name getCString:buffer maxLength:(sizeof(char) * 256) encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t
|
||||
mtl_device_get_peer_group_id(mtl_device *dev)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> device = (id<MTLDevice>)dev;
|
||||
return device.peerGroupID;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
mtl_device_get_peer_index(mtl_device *dev)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> device = (id<MTLDevice>)dev;
|
||||
return device.peerIndex;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t
|
||||
mtl_device_get_registry_id(mtl_device *dev)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> device = (id<MTLDevice>)dev;
|
||||
return device.registryID;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
mtl_device_supports_sample_count(mtl_device *dev, uint32_t sample_count)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> device = (id<MTLDevice>)dev;
|
||||
return [device supportsTextureSampleCount:sample_count];
|
||||
}
|
||||
}
|
||||
|
||||
struct mtl_size
|
||||
mtl_device_max_threads_per_threadgroup(mtl_device *dev)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> device = (id<MTLDevice>)dev;
|
||||
return (struct mtl_size){.x = device.maxThreadsPerThreadgroup.width,
|
||||
.y = device.maxThreadsPerThreadgroup.height,
|
||||
.z = device.maxThreadsPerThreadgroup.depth};
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
mtl_device_max_threadgroup_memory_length(mtl_device *dev)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> device = (id<MTLDevice>)dev;
|
||||
return device.maxThreadgroupMemoryLength;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t
|
||||
mtl_device_max_buffer_length(mtl_device *dev)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> device = (id<MTLDevice>)dev;
|
||||
return device.maxBufferLength;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t
|
||||
mtl_device_recommended_max_working_set_size(mtl_device *dev)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> device = (id<MTLDevice>)dev;
|
||||
return device.recommendedMaxWorkingSetSize;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t
|
||||
mtl_device_current_allocated_size(mtl_device *dev)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> device = (id<MTLDevice>)dev;
|
||||
return device.currentAllocatedSize;
|
||||
}
|
||||
}
|
||||
|
||||
/* Timestamp query */
|
||||
uint64_t
|
||||
mtl_device_get_gpu_timestamp(mtl_device *dev)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> device = (id<MTLDevice>)dev;
|
||||
MTLTimestamp cpu_ts, gpu_ts;
|
||||
|
||||
[device sampleTimestamps:&cpu_ts gpuTimestamp:&gpu_ts];
|
||||
|
||||
return (uint64_t)gpu_ts;
|
||||
}
|
||||
}
|
||||
|
||||
/* Resource queries */
|
||||
/* TODO_KOSMICKRISP Return a struct */
|
||||
void
|
||||
mtl_heap_buffer_size_and_align_with_length(mtl_device *device, uint64_t *size_B,
|
||||
uint64_t *align_B)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> dev = (id<MTLDevice>)device;
|
||||
MTLSizeAndAlign size_align = [dev heapBufferSizeAndAlignWithLength:*size_B options:KK_MTL_RESOURCE_OPTIONS];
|
||||
*size_B = size_align.size;
|
||||
*align_B = size_align.align;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO_KOSMICKRISP Remove */
|
||||
static MTLTextureDescriptor *
|
||||
mtl_new_texture_descriptor(const struct kk_image_layout *layout)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLTextureDescriptor *descriptor = [MTLTextureDescriptor new];
|
||||
descriptor.textureType = (MTLTextureType)layout->type;
|
||||
descriptor.pixelFormat = layout->format.mtl;
|
||||
descriptor.width = layout->width_px;
|
||||
descriptor.height = layout->height_px;
|
||||
descriptor.depth = layout->depth_px;
|
||||
descriptor.mipmapLevelCount = layout->levels;
|
||||
descriptor.sampleCount = layout->sample_count_sa;
|
||||
descriptor.arrayLength = layout->layers;
|
||||
descriptor.allowGPUOptimizedContents = layout->optimized_layout;
|
||||
descriptor.usage = (MTLTextureUsage)layout->usage;
|
||||
/* We don't set the swizzle because Metal complains when the usage has store or render target with swizzle... */
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t
|
||||
mtl_minimum_linear_texture_alignment_for_pixel_format(
|
||||
mtl_device *device, enum mtl_pixel_format format)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> dev = (id<MTLDevice>)device;
|
||||
return [dev minimumLinearTextureAlignmentForPixelFormat:(MTLPixelFormat)format];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_heap_texture_size_and_align_with_descriptor(mtl_device *device,
|
||||
struct kk_image_layout *layout,
|
||||
uint64_t *size_B,
|
||||
uint64_t *align_B)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> dev = (id<MTLDevice>)device;
|
||||
MTLTextureDescriptor *descriptor = [mtl_new_texture_descriptor(layout) autorelease];
|
||||
descriptor.resourceOptions = KK_MTL_RESOURCE_OPTIONS;
|
||||
|
||||
MTLSizeAndAlign size_align = [dev heapTextureSizeAndAlignWithDescriptor:descriptor];
|
||||
if (size_B)
|
||||
*size_B = size_align.size;
|
||||
if (align_B)
|
||||
*align_B = size_align.align;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
mtl_sparse_tile_size_in_bytes(mtl_device *device) {
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> dev = (id<MTLDevice>)device;
|
||||
return [dev sparseTileSizeInBytes];
|
||||
}
|
||||
}
|
||||
|
||||
struct mtl_size
|
||||
mtl_sparse_tile_size(mtl_device *device, struct kk_image_layout *layout) {
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> dev = (id<MTLDevice>)device;
|
||||
MTLSize tile_size = [dev sparseTileSizeWithTextureType:(MTLTextureType)layout->type
|
||||
pixelFormat:layout->format.mtl
|
||||
sampleCount:layout->sample_count_sa];
|
||||
return (struct mtl_size){tile_size.width, tile_size.height, tile_size.depth};
|
||||
}
|
||||
}
|
||||
|
||||
struct mtl_size
|
||||
mtl_sparse_tile_count(mtl_device *device, struct kk_image_layout *layout,
|
||||
struct mtl_size tile_size) {
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> dev = (id<MTLDevice>)device;
|
||||
MTLRegion pixel_region = MTLRegionMake3D(
|
||||
0, 0, 0, layout->width_px, layout->height_px, layout->depth_px);
|
||||
MTLRegion tile_region;
|
||||
[dev convertSparsePixelRegions:&pixel_region
|
||||
toTileRegions:&tile_region
|
||||
withTileSize:MTLSizeMake(tile_size.x, tile_size.y, tile_size.z)
|
||||
alignmentMode:MTLSparseTextureRegionAlignmentModeOutward
|
||||
numRegions:1];
|
||||
|
||||
return (struct mtl_size){tile_region.size.width, tile_region.size.height,
|
||||
tile_region.size.depth};
|
||||
}
|
||||
}
|
||||
|
||||
/* Resource creation */
|
||||
mtl_buffer *
|
||||
mtl_new_buffer_with_bytes_no_copy(mtl_device *device, void* ptr,
|
||||
uint64_t size_B)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> dev = (id<MTLDevice>)device;
|
||||
return [dev newBufferWithBytesNoCopy:ptr length:size_B options:KK_MTL_RESOURCE_OPTIONS deallocator:nil];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef MTL_ENCODER_H
|
||||
#define MTL_ENCODER_H 1
|
||||
|
||||
#include "mtl_types.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Common encoder utils */
|
||||
void mtl_end_encoding(void *encoder);
|
||||
|
||||
/* MTLBlitEncoder */
|
||||
mtl_blit_encoder *mtl_new_blit_command_encoder(mtl_command_buffer *cmd_buffer);
|
||||
|
||||
void mtl_blit_update_fence(mtl_blit_encoder *encoder, mtl_fence *fence);
|
||||
void mtl_blit_wait_for_fence(mtl_blit_encoder *encoder, mtl_fence *fence);
|
||||
|
||||
void mtl_copy_from_buffer_to_buffer(mtl_blit_encoder *blit_enc_handle,
|
||||
mtl_buffer *src_buf, size_t src_offset,
|
||||
mtl_buffer *dst_buf, size_t dst_offset,
|
||||
size_t size);
|
||||
|
||||
void mtl_copy_from_buffer_to_texture(mtl_blit_encoder *blit_enc_handle,
|
||||
struct mtl_buffer_image_copy *data);
|
||||
|
||||
void mtl_copy_from_texture_to_buffer(mtl_blit_encoder *blit_enc_handle,
|
||||
struct mtl_buffer_image_copy *data);
|
||||
|
||||
void mtl_copy_from_texture_to_texture(
|
||||
mtl_blit_encoder *blit_enc_handle, mtl_texture *src_tex_handle,
|
||||
size_t src_slice, size_t src_level, struct mtl_origin src_origin,
|
||||
struct mtl_size src_size, mtl_texture *dst_tex_handle, size_t dst_slice,
|
||||
size_t dst_level, struct mtl_origin dst_origin);
|
||||
|
||||
/* MTLComputeEncoder */
|
||||
mtl_compute_encoder *
|
||||
mtl_new_compute_command_encoder(mtl_command_buffer *cmd_buffer);
|
||||
|
||||
void mtl_compute_update_fence(mtl_compute_encoder *encoder, mtl_fence *fence);
|
||||
void mtl_compute_wait_for_fence(mtl_compute_encoder *encoder, mtl_fence *fence);
|
||||
|
||||
void mtl_compute_set_pipeline_state(mtl_compute_encoder *encoder,
|
||||
mtl_compute_pipeline_state *state_handle);
|
||||
|
||||
void mtl_compute_set_buffer(mtl_compute_encoder *encoder, mtl_buffer *buffer,
|
||||
size_t offset, size_t index);
|
||||
|
||||
void mtl_compute_use_heaps(mtl_compute_encoder *encoder, mtl_heap **heaps,
|
||||
uint32_t count);
|
||||
|
||||
void mtl_dispatch_threads(mtl_compute_encoder *encoder,
|
||||
struct mtl_size grid_size,
|
||||
struct mtl_size local_size);
|
||||
|
||||
void mtl_dispatch_threadgroups_with_indirect_buffer(
|
||||
mtl_compute_encoder *encoder, mtl_buffer *buffer, uint32_t offset,
|
||||
struct mtl_size local_size);
|
||||
|
||||
void mtl_memory_barrier_with_scope(mtl_compute_encoder *encoder,
|
||||
enum mtl_barrier_scope scope);
|
||||
|
||||
/* MTLRenderEncoder */
|
||||
mtl_render_encoder *mtl_new_render_command_encoder_with_descriptor(
|
||||
mtl_command_buffer *command_buffer, mtl_render_pass_descriptor *descriptor);
|
||||
|
||||
void mtl_render_update_fence(mtl_render_encoder *encoder, mtl_fence *fence);
|
||||
void mtl_render_wait_for_fence(mtl_render_encoder *encoder, mtl_fence *fence);
|
||||
|
||||
void mtl_set_viewports(mtl_render_encoder *encoder,
|
||||
struct mtl_viewport *viewports, uint32_t count);
|
||||
|
||||
void mtl_set_scissor_rects(mtl_render_encoder *encoder,
|
||||
struct mtl_scissor_rect *scissor_rects,
|
||||
uint32_t count);
|
||||
|
||||
void mtl_render_set_pipeline_state(mtl_render_encoder *encoder,
|
||||
mtl_render_pipeline_state *pipeline);
|
||||
|
||||
void mtl_set_depth_stencil_state(mtl_render_encoder *encoder,
|
||||
mtl_depth_stencil_state *state);
|
||||
|
||||
void mtl_set_stencil_references(mtl_render_encoder *encoder, uint32_t front,
|
||||
uint32_t back);
|
||||
|
||||
void mtl_set_front_face_winding(mtl_render_encoder *encoder,
|
||||
enum mtl_winding winding);
|
||||
|
||||
void mtl_set_cull_mode(mtl_render_encoder *encoder, enum mtl_cull_mode mode);
|
||||
|
||||
void mtl_set_visibility_result_mode(mtl_render_encoder *encoder,
|
||||
enum mtl_visibility_result_mode mode,
|
||||
size_t offset);
|
||||
|
||||
void mtl_set_depth_bias(mtl_render_encoder *encoder, float depth_bias,
|
||||
float slope_scale, float clamp);
|
||||
|
||||
void mtl_set_depth_clip_mode(mtl_render_encoder *encoder,
|
||||
enum mtl_depth_clip_mode mode);
|
||||
|
||||
void mtl_set_vertex_amplification_count(mtl_render_encoder *encoder,
|
||||
uint32_t *layer_ids, uint32_t id_count);
|
||||
|
||||
void mtl_set_vertex_buffer(mtl_render_encoder *encoder, mtl_buffer *buffer,
|
||||
uint32_t offset, uint32_t index);
|
||||
|
||||
void mtl_set_vertex_bytes(mtl_render_encoder *encoder, const void *bytes,
|
||||
uint32_t length, uint32_t index);
|
||||
|
||||
void mtl_set_fragment_buffer(mtl_render_encoder *encoder, mtl_buffer *buffer,
|
||||
uint32_t offset, uint32_t index);
|
||||
|
||||
void mtl_set_fragment_bytes(mtl_render_encoder *encoder, const void *bytes,
|
||||
uint32_t length, uint32_t index);
|
||||
|
||||
void mtl_draw_primitives(mtl_render_encoder *encoder,
|
||||
enum mtl_primitive_type primitve_type,
|
||||
uint32_t vertexStart, uint32_t vertexCount,
|
||||
uint32_t instanceCount, uint32_t baseInstance);
|
||||
|
||||
void mtl_draw_indexed_primitives(
|
||||
mtl_render_encoder *encoder, enum mtl_primitive_type primitve_type,
|
||||
uint32_t index_count, enum mtl_index_type index_type,
|
||||
mtl_buffer *index_buffer, uint32_t index_buffer_offset,
|
||||
uint32_t instance_count, int32_t base_vertex, uint32_t base_instance);
|
||||
|
||||
void mtl_draw_primitives_indirect(mtl_render_encoder *encoder,
|
||||
enum mtl_primitive_type primitve_type,
|
||||
mtl_buffer *indirect_buffer,
|
||||
uint64_t indirect_buffer_offset);
|
||||
|
||||
void mtl_draw_indexed_primitives_indirect(mtl_render_encoder *encoder,
|
||||
enum mtl_primitive_type primitve_type,
|
||||
enum mtl_index_type index_type,
|
||||
mtl_buffer *index_buffer,
|
||||
uint32_t index_buffer_offset,
|
||||
mtl_buffer *indirect_buffer,
|
||||
uint64_t indirect_buffer_offset);
|
||||
|
||||
void mtl_render_use_resource(mtl_compute_encoder *encoder,
|
||||
mtl_resource *res_handle, uint32_t usage);
|
||||
|
||||
void mtl_render_use_resources(mtl_render_encoder *encoder,
|
||||
mtl_resource **resource_handles, uint32_t count,
|
||||
enum mtl_resource_usage usage);
|
||||
|
||||
void mtl_render_use_heaps(mtl_render_encoder *encoder, mtl_heap **heaps,
|
||||
uint32_t count);
|
||||
|
||||
#endif /* MTL_ENCODER_H */
|
||||
@@ -0,0 +1,541 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_encoder.h"
|
||||
|
||||
#include <Metal/MTLBlitCommandEncoder.h>
|
||||
#include <Metal/MTLComputeCommandEncoder.h>
|
||||
#include <Metal/MTLRenderCommandEncoder.h>
|
||||
|
||||
/* Common encoder utils */
|
||||
void
|
||||
mtl_end_encoding(void *encoder)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLCommandEncoder> enc = (id<MTLComputeCommandEncoder>)encoder;
|
||||
[enc endEncoding];
|
||||
}
|
||||
}
|
||||
|
||||
/* MTLBlitEncoder */
|
||||
mtl_blit_encoder *
|
||||
mtl_new_blit_command_encoder(mtl_command_buffer *cmd_buffer)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLCommandBuffer> cmd_buf = (id<MTLCommandBuffer>)cmd_buffer;
|
||||
return [[cmd_buf blitCommandEncoder] retain];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_blit_update_fence(mtl_blit_encoder *encoder,
|
||||
mtl_fence *fence)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLBlitCommandEncoder> enc = (id<MTLBlitCommandEncoder>)encoder;
|
||||
id<MTLFence> f = (id<MTLFence>)fence;
|
||||
[enc updateFence:f];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_blit_wait_for_fence(mtl_blit_encoder *encoder,
|
||||
mtl_fence *fence)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLBlitCommandEncoder> enc = (id<MTLBlitCommandEncoder>)encoder;
|
||||
id<MTLFence> f = (id<MTLFence>)fence;
|
||||
[enc waitForFence:f];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_copy_from_buffer_to_buffer(mtl_blit_encoder *blit_enc_handle,
|
||||
mtl_buffer *src_buf, size_t src_offset,
|
||||
mtl_buffer *dst_buf, size_t dst_offset,
|
||||
size_t size)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLBlitCommandEncoder> blit = (id<MTLBlitCommandEncoder>)blit_enc_handle;
|
||||
id<MTLBuffer> mtl_src_buffer = (id<MTLBuffer>)src_buf;
|
||||
id<MTLBuffer> mtl_dst_buffer = (id<MTLBuffer>)dst_buf;
|
||||
[blit copyFromBuffer:mtl_src_buffer sourceOffset:src_offset toBuffer:mtl_dst_buffer destinationOffset:dst_offset size:size];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_copy_from_buffer_to_texture(mtl_blit_encoder *blit_enc_handle,
|
||||
struct mtl_buffer_image_copy *data)
|
||||
{
|
||||
@autoreleasepool {
|
||||
const MTLSize size = MTLSizeMake(data->image_size.x, data->image_size.y, data->image_size.z);
|
||||
const MTLOrigin origin = MTLOriginMake(data->image_origin.x, data->image_origin.y, data->image_origin.z);
|
||||
id<MTLBlitCommandEncoder> blit = (id<MTLBlitCommandEncoder>)blit_enc_handle;
|
||||
id<MTLBuffer> buffer = (id<MTLBuffer>)data->buffer;
|
||||
id<MTLTexture> image = (id<MTLTexture>)data->image;
|
||||
[blit copyFromBuffer:buffer
|
||||
sourceOffset:data->buffer_offset_B
|
||||
sourceBytesPerRow:data->buffer_stride_B
|
||||
sourceBytesPerImage:data->buffer_2d_image_size_B
|
||||
sourceSize:size
|
||||
toTexture:image
|
||||
destinationSlice:data->image_slice
|
||||
destinationLevel:data->image_level
|
||||
destinationOrigin:origin
|
||||
options:(MTLBlitOption)data->options];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_copy_from_texture_to_buffer(mtl_blit_encoder *blit_enc_handle,
|
||||
struct mtl_buffer_image_copy *data)
|
||||
{
|
||||
@autoreleasepool {
|
||||
const MTLSize size = MTLSizeMake(data->image_size.x, data->image_size.y, data->image_size.z);
|
||||
const MTLOrigin origin = MTLOriginMake(data->image_origin.x, data->image_origin.y, data->image_origin.z);
|
||||
id<MTLBlitCommandEncoder> blit = (id<MTLBlitCommandEncoder>)blit_enc_handle;
|
||||
id<MTLBuffer> buffer = (id<MTLBuffer>)data->buffer;
|
||||
id<MTLTexture> image = (id<MTLTexture>)data->image;
|
||||
[blit copyFromTexture:image
|
||||
sourceSlice:data->image_slice
|
||||
sourceLevel:data->image_level
|
||||
sourceOrigin:origin
|
||||
sourceSize:size
|
||||
toBuffer:buffer
|
||||
destinationOffset:data->buffer_offset_B
|
||||
destinationBytesPerRow:data->buffer_stride_B
|
||||
destinationBytesPerImage:data->buffer_2d_image_size_B
|
||||
options:(MTLBlitOption)data->options];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_copy_from_texture_to_texture(mtl_blit_encoder *blit_enc_handle,
|
||||
mtl_texture *src_tex_handle, size_t src_slice,
|
||||
size_t src_level, struct mtl_origin src_origin,
|
||||
struct mtl_size src_size,
|
||||
mtl_texture *dst_tex_handle, size_t dst_slice,
|
||||
size_t dst_level, struct mtl_origin dst_origin)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLOrigin mtl_src_origin = MTLOriginMake(src_origin.x, src_origin.y, src_origin.z);
|
||||
MTLSize mtl_src_size = MTLSizeMake(src_size.x, src_size.y, src_size.z);
|
||||
MTLOrigin mtl_dst_origin = MTLOriginMake(dst_origin.x, dst_origin.y, dst_origin.z);
|
||||
id<MTLTexture> mtl_dst_tex = (id<MTLTexture>)dst_tex_handle;
|
||||
id<MTLBlitCommandEncoder> blit = (id<MTLBlitCommandEncoder>)blit_enc_handle;
|
||||
id<MTLTexture> mtl_src_tex = (id<MTLTexture>)src_tex_handle;
|
||||
[blit copyFromTexture:mtl_src_tex
|
||||
sourceSlice:src_slice
|
||||
sourceLevel:src_level
|
||||
sourceOrigin:mtl_src_origin
|
||||
sourceSize:mtl_src_size
|
||||
toTexture:mtl_dst_tex
|
||||
destinationSlice:dst_slice
|
||||
destinationLevel:dst_level
|
||||
destinationOrigin:mtl_dst_origin];
|
||||
}
|
||||
}
|
||||
|
||||
/* MTLComputeEncoder */
|
||||
mtl_compute_encoder *
|
||||
mtl_new_compute_command_encoder(mtl_command_buffer *cmd_buffer)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLCommandBuffer> cmd_buf = (id<MTLCommandBuffer>)cmd_buffer;
|
||||
return [[cmd_buf computeCommandEncoder] retain];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_compute_update_fence(mtl_compute_encoder *encoder, mtl_fence *fence)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLComputeCommandEncoder> enc = (id<MTLComputeCommandEncoder>)encoder;
|
||||
id<MTLFence> f = (id<MTLFence>)fence;
|
||||
[enc updateFence:f];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_compute_wait_for_fence(mtl_compute_encoder *encoder, mtl_fence *fence)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLComputeCommandEncoder> enc = (id<MTLComputeCommandEncoder>)encoder;
|
||||
id<MTLFence> f = (id<MTLFence>)fence;
|
||||
[enc waitForFence:f];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_compute_set_pipeline_state(mtl_compute_encoder *encoder,
|
||||
mtl_compute_pipeline_state *state_handle)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLComputeCommandEncoder> enc = (id<MTLComputeCommandEncoder>)encoder;
|
||||
id<MTLComputePipelineState> state = (id<MTLComputePipelineState>)state_handle;
|
||||
[enc setComputePipelineState:state];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_compute_set_buffer(mtl_compute_encoder *encoder,
|
||||
mtl_buffer *buffer, size_t offset, size_t index)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLComputeCommandEncoder> enc = (id<MTLComputeCommandEncoder>)encoder;
|
||||
id<MTLBuffer> buf = (id<MTLBuffer>)buffer;
|
||||
[enc setBuffer:buf offset:offset atIndex:index];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_compute_use_heaps(mtl_compute_encoder *encoder, mtl_heap **heaps,
|
||||
uint32_t count)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLComputeCommandEncoder> enc = (id<MTLComputeCommandEncoder>)encoder;
|
||||
id<MTLHeap> *handles = (id<MTLHeap>*)heaps;
|
||||
[enc useHeaps:handles count:count];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_dispatch_threads(mtl_compute_encoder *encoder,
|
||||
struct mtl_size grid_size, struct mtl_size local_size)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLComputeCommandEncoder> enc = (id<MTLComputeCommandEncoder>)encoder;
|
||||
MTLSize thread_count = MTLSizeMake(grid_size.x, grid_size.y, grid_size.z);
|
||||
MTLSize threads_per_threadgroup = MTLSizeMake(local_size.x, local_size.y,
|
||||
local_size.z);
|
||||
|
||||
// TODO_KOSMICKRISP can we rely on nonuniform threadgroup size support?
|
||||
[enc dispatchThreads:thread_count threadsPerThreadgroup:threads_per_threadgroup];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_dispatch_threadgroups_with_indirect_buffer(mtl_compute_encoder *encoder,
|
||||
mtl_buffer *buffer,
|
||||
uint32_t offset,
|
||||
struct mtl_size local_size)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLComputeCommandEncoder> enc = (id<MTLComputeCommandEncoder>)encoder;
|
||||
id<MTLBuffer> buf = (id<MTLBuffer>)buffer;
|
||||
MTLSize threads_per_threadgroup = MTLSizeMake(local_size.x,
|
||||
local_size.y,
|
||||
local_size.z);
|
||||
|
||||
[enc dispatchThreadgroupsWithIndirectBuffer:buf indirectBufferOffset:offset threadsPerThreadgroup:threads_per_threadgroup];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_memory_barrier_with_scope(mtl_compute_encoder *encoder,
|
||||
enum mtl_barrier_scope scope)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLComputeCommandEncoder> enc = (id<MTLComputeCommandEncoder>)encoder;
|
||||
[enc memoryBarrierWithScope:(MTLBarrierScope)scope];
|
||||
}
|
||||
}
|
||||
|
||||
/* MTLRenderEncoder */
|
||||
|
||||
/* Encoder commands */
|
||||
mtl_render_encoder *
|
||||
mtl_new_render_command_encoder_with_descriptor(
|
||||
mtl_command_buffer *command_buffer, mtl_render_pass_descriptor *descriptor)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLCommandBuffer> cmd = (id<MTLCommandBuffer>)command_buffer;
|
||||
MTLRenderPassDescriptor *desc = (MTLRenderPassDescriptor *)descriptor;
|
||||
return [[cmd renderCommandEncoderWithDescriptor:desc] retain];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_update_fence(mtl_render_encoder *encoder, mtl_fence *fence)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLRenderCommandEncoder> enc = (id<MTLRenderCommandEncoder>)encoder;
|
||||
id<MTLFence> f = (id<MTLFence>)fence;
|
||||
[enc updateFence:f afterStages:MTLRenderStageFragment];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_wait_for_fence(mtl_render_encoder *encoder, mtl_fence *fence)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLRenderCommandEncoder> enc = (id<MTLRenderCommandEncoder>)encoder;
|
||||
id<MTLFence> f = (id<MTLFence>)fence;
|
||||
[enc waitForFence:f beforeStages:MTLRenderStageVertex];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_viewports(mtl_render_encoder *encoder, struct mtl_viewport *viewports,
|
||||
uint32_t count)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLRenderCommandEncoder> enc = (id<MTLRenderCommandEncoder>)encoder;
|
||||
MTLViewport *vps = (MTLViewport *)viewports;
|
||||
[enc setViewports:vps count:count];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_scissor_rects(mtl_render_encoder *encoder,
|
||||
struct mtl_scissor_rect *scissor_rects, uint32_t count)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLRenderCommandEncoder> enc = (id<MTLRenderCommandEncoder>)encoder;
|
||||
MTLScissorRect *rects = (MTLScissorRect *)scissor_rects;
|
||||
[enc setScissorRects:rects count:count];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_set_pipeline_state(mtl_render_encoder *encoder,
|
||||
mtl_render_pipeline_state *pipeline)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLRenderCommandEncoder> enc = (id<MTLRenderCommandEncoder>)encoder;
|
||||
id<MTLRenderPipelineState> pipe = (id<MTLRenderPipelineState>)pipeline;
|
||||
[enc setRenderPipelineState:pipe];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_depth_stencil_state(mtl_render_encoder *encoder,
|
||||
mtl_depth_stencil_state *state)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLRenderCommandEncoder> enc = (id<MTLRenderCommandEncoder>)encoder;
|
||||
id<MTLDepthStencilState> s = (id<MTLDepthStencilState>)state;
|
||||
[enc setDepthStencilState:s];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_stencil_references(mtl_render_encoder *encoder, uint32_t front,
|
||||
uint32_t back)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLRenderCommandEncoder> enc = (id<MTLRenderCommandEncoder>)encoder;
|
||||
[enc setStencilFrontReferenceValue:front backReferenceValue:back];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_front_face_winding(mtl_render_encoder *encoder,
|
||||
enum mtl_winding winding)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLRenderCommandEncoder> enc = (id<MTLRenderCommandEncoder>)encoder;
|
||||
[enc setFrontFacingWinding:(MTLWinding)winding];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_cull_mode(mtl_render_encoder *encoder, enum mtl_cull_mode mode)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLRenderCommandEncoder> enc = (id<MTLRenderCommandEncoder>)encoder;
|
||||
[enc setCullMode:(MTLCullMode)mode];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_visibility_result_mode(mtl_render_encoder *encoder,
|
||||
enum mtl_visibility_result_mode mode,
|
||||
size_t offset)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLRenderCommandEncoder> enc = (id<MTLRenderCommandEncoder>)encoder;
|
||||
[enc setVisibilityResultMode:(MTLVisibilityResultMode)mode offset:offset];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_depth_bias(mtl_render_encoder *encoder, float depth_bias,
|
||||
float slope_scale, float clamp)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLRenderCommandEncoder> enc = (id<MTLRenderCommandEncoder>)encoder;
|
||||
[enc setDepthBias:depth_bias slopeScale:slope_scale clamp:clamp];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_depth_clip_mode(mtl_render_encoder *encoder,
|
||||
enum mtl_depth_clip_mode mode)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLRenderCommandEncoder> enc = (id<MTLRenderCommandEncoder>)encoder;
|
||||
[enc setDepthClipMode:(MTLDepthClipMode)mode];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_vertex_amplification_count(mtl_render_encoder *encoder,
|
||||
uint32_t *layer_ids, uint32_t id_count)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLRenderCommandEncoder> enc = (id<MTLRenderCommandEncoder>)encoder;
|
||||
MTLVertexAmplificationViewMapping mappings[32];
|
||||
for (uint32_t i = 0u; i < id_count; ++i) {
|
||||
mappings[i].renderTargetArrayIndexOffset = layer_ids[i];
|
||||
mappings[i].viewportArrayIndexOffset = 0u;
|
||||
}
|
||||
[enc setVertexAmplificationCount:id_count viewMappings:mappings];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_vertex_buffer(mtl_render_encoder *encoder, mtl_buffer *buffer,
|
||||
uint32_t offset, uint32_t index)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLRenderCommandEncoder> enc = (id<MTLRenderCommandEncoder>)encoder;
|
||||
id<MTLBuffer> buf = (id<MTLBuffer>)buffer;
|
||||
[enc setVertexBuffer:buf offset:offset atIndex:index];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_vertex_bytes(mtl_render_encoder *encoder, const void *bytes,
|
||||
uint32_t length, uint32_t index)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLRenderCommandEncoder> enc = (id<MTLRenderCommandEncoder>)encoder;
|
||||
[enc setVertexBytes:bytes length:length atIndex:index];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_fragment_buffer(mtl_render_encoder *encoder, mtl_buffer *buffer,
|
||||
uint32_t offset, uint32_t index)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLRenderCommandEncoder> enc = (id<MTLRenderCommandEncoder>)encoder;
|
||||
id<MTLBuffer> buf = (id<MTLBuffer>)buffer;
|
||||
[enc setFragmentBuffer:buf offset:offset atIndex:index];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_fragment_bytes(mtl_render_encoder *encoder, const void *bytes,
|
||||
uint32_t length, uint32_t index)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLRenderCommandEncoder> enc = (id<MTLRenderCommandEncoder>)encoder;
|
||||
[enc setFragmentBytes:bytes length:length atIndex:index];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_draw_primitives(mtl_render_encoder *encoder,
|
||||
enum mtl_primitive_type primitve_type, uint32_t vertexStart,
|
||||
uint32_t vertexCount, uint32_t instanceCount,
|
||||
uint32_t baseInstance)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLRenderCommandEncoder> enc = (id<MTLRenderCommandEncoder>)encoder;
|
||||
MTLPrimitiveType type = (MTLPrimitiveType)primitve_type;
|
||||
[enc drawPrimitives:type vertexStart:vertexStart vertexCount:vertexCount instanceCount:instanceCount baseInstance:baseInstance];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_draw_indexed_primitives(
|
||||
mtl_render_encoder *encoder, enum mtl_primitive_type primitve_type,
|
||||
uint32_t index_count, enum mtl_index_type index_type,
|
||||
mtl_buffer *index_buffer, uint32_t index_buffer_offset,
|
||||
uint32_t instance_count, int32_t base_vertex, uint32_t base_instance)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLRenderCommandEncoder> enc = (id<MTLRenderCommandEncoder>)encoder;
|
||||
id<MTLBuffer> buf = (id<MTLBuffer>)index_buffer;
|
||||
MTLIndexType ndx_type = (MTLIndexType)index_type;
|
||||
MTLPrimitiveType primitive = (MTLPrimitiveType)primitve_type;
|
||||
[enc drawIndexedPrimitives:primitive indexCount:index_count indexType:ndx_type indexBuffer:buf indexBufferOffset:index_buffer_offset instanceCount:instance_count baseVertex:base_vertex baseInstance:base_instance];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_draw_primitives_indirect(mtl_render_encoder *encoder,
|
||||
enum mtl_primitive_type primitve_type,
|
||||
mtl_buffer *indirect_buffer,
|
||||
uint64_t indirect_buffer_offset)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLRenderCommandEncoder> enc = (id<MTLRenderCommandEncoder>)encoder;
|
||||
id<MTLBuffer> buf = (id<MTLBuffer>)indirect_buffer;
|
||||
MTLPrimitiveType type = (MTLPrimitiveType)primitve_type;
|
||||
[enc drawPrimitives:type indirectBuffer:buf indirectBufferOffset:indirect_buffer_offset];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_draw_indexed_primitives_indirect(mtl_render_encoder *encoder,
|
||||
enum mtl_primitive_type primitve_type,
|
||||
enum mtl_index_type index_type,
|
||||
mtl_buffer *index_buffer,
|
||||
uint32_t index_buffer_offset,
|
||||
mtl_buffer *indirect_buffer,
|
||||
uint64_t indirect_buffer_offset)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLRenderCommandEncoder> enc = (id<MTLRenderCommandEncoder>)encoder;
|
||||
id<MTLBuffer> buf = (id<MTLBuffer>)indirect_buffer;
|
||||
id<MTLBuffer> ndx_buf = (id<MTLBuffer>)index_buffer;
|
||||
MTLPrimitiveType type = (MTLPrimitiveType)primitve_type;
|
||||
MTLIndexType ndx_type = (MTLIndexType)index_type;
|
||||
[enc drawIndexedPrimitives:type indexType:ndx_type indexBuffer:ndx_buf indexBufferOffset:index_buffer_offset indirectBuffer:buf indirectBufferOffset:indirect_buffer_offset];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_use_resource(mtl_compute_encoder *encoder, mtl_resource *res_handle,
|
||||
uint32_t usage)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLRenderCommandEncoder> enc = (id<MTLRenderCommandEncoder>)encoder;
|
||||
id<MTLResource> res = (id<MTLResource>)res_handle;
|
||||
[enc useResource:res usage:(MTLResourceUsage)usage stages:MTLRenderStageVertex|MTLRenderStageFragment];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_use_resources(mtl_render_encoder *encoder,
|
||||
mtl_resource **resource_handles, uint32_t count,
|
||||
enum mtl_resource_usage usage)
|
||||
{
|
||||
@autoreleasepool {
|
||||
// id<MTLRenderCommandEncoder> enc = (id<MTLRenderCommandEncoder>)encoder;
|
||||
id<MTLResource> *handles = (id<MTLResource>*)resource_handles;
|
||||
for (uint32_t i = 0u; i < count; ++i) {
|
||||
if (handles[i] != NULL)
|
||||
mtl_render_use_resource(encoder, handles[i], usage);
|
||||
}
|
||||
/* TODO_KOSMICKRISP No null values in the array or Metal complains */
|
||||
// [enc useResources:handles count:count usage:(MTLResourceUsage)usage];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_use_heaps(mtl_render_encoder *encoder, mtl_heap **heaps,
|
||||
uint32_t count)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLRenderCommandEncoder> enc = (id<MTLRenderCommandEncoder>)encoder;
|
||||
id<MTLHeap> *handles = (id<MTLHeap>*)heaps;
|
||||
[enc useHeaps:handles count:count stages:MTLRenderStageVertex|MTLRenderStageFragment];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef MTL_FORMAT_H
|
||||
#define MTL_FORMAT_H 1
|
||||
|
||||
/* TODO_KOSMICKRISP Haven't modified all
|
||||
* Slightly modified naming so they match to enum pipe_format for convenience
|
||||
*/
|
||||
enum mtl_pixel_format {
|
||||
MTL_PIXEL_FORMAT_INVALID = 0,
|
||||
|
||||
/* Normal 8 bit formats */
|
||||
MTL_PIXEL_FORMAT_A8_UNORM = 1,
|
||||
|
||||
MTL_PIXEL_FORMAT_R8_UNORM = 10,
|
||||
MTL_PIXEL_FORMAT_R8_SRGB = 11,
|
||||
MTL_PIXEL_FORMAT_R8_SNORM = 12,
|
||||
MTL_PIXEL_FORMAT_R8_UINT = 13,
|
||||
MTL_PIXEL_FORMAT_R8_SINT = 14,
|
||||
|
||||
/* Normal 16 bit formats */
|
||||
MTL_PIXEL_FORMAT_R16_UNORM = 20,
|
||||
MTL_PIXEL_FORMAT_R16_SNORM = 22,
|
||||
MTL_PIXEL_FORMAT_R16_UINT = 23,
|
||||
MTL_PIXEL_FORMAT_R16_SINT = 24,
|
||||
MTL_PIXEL_FORMAT_R16_FLOAT = 25,
|
||||
|
||||
MTL_PIXEL_FORMAT_R8G8_UNORM = 30,
|
||||
MTL_PIXEL_FORMAT_R8G8_SRGB = 31,
|
||||
MTL_PIXEL_FORMAT_R8G8_SNORM = 32,
|
||||
MTL_PIXEL_FORMAT_R8G8_UINT = 33,
|
||||
MTL_PIXEL_FORMAT_R8G8_SINT = 34,
|
||||
|
||||
/* Packed 16 bit formats */
|
||||
MTL_PIXEL_FORMAT_B5G6R5_UNORM = 40,
|
||||
MTL_PIXEL_FORMAT_A1B5G5R5_UNORM = 41,
|
||||
MTL_PIXEL_FORMAT_A4B4G4R4_UNORM = 42,
|
||||
MTL_PIXEL_FORMAT_B5G5R5A1_UNORM = 43,
|
||||
|
||||
/* Normal 32 bit formats */
|
||||
MTL_PIXEL_FORMAT_R32_UINT = 53,
|
||||
MTL_PIXEL_FORMAT_R32_SINT = 54,
|
||||
MTL_PIXEL_FORMAT_R32_FLOAT = 55,
|
||||
|
||||
MTL_PIXEL_FORMAT_R16G16_UNORM = 60,
|
||||
MTL_PIXEL_FORMAT_R16G16_SNORM = 62,
|
||||
MTL_PIXEL_FORMAT_R16G16_UINT = 63,
|
||||
MTL_PIXEL_FORMAT_R16G16_SINT = 64,
|
||||
MTL_PIXEL_FORMAT_R16G16_FLOAT = 65,
|
||||
|
||||
MTL_PIXEL_FORMAT_R8G8B8A8_UNORM = 70,
|
||||
MTL_PIXEL_FORMAT_R8G8B8A8_SRGB = 71,
|
||||
MTL_PIXEL_FORMAT_R8G8B8A8_SNORM = 72,
|
||||
MTL_PIXEL_FORMAT_R8G8B8A8_UINT = 73,
|
||||
MTL_PIXEL_FORMAT_R8G8B8A8_SINT = 74,
|
||||
|
||||
MTL_PIXEL_FORMAT_B8G8R8A8_UNORM = 80,
|
||||
MTL_PIXEL_FORMAT_B8G8R8A8_SRGB = 81,
|
||||
|
||||
/* Packed 32 bit formats */
|
||||
MTL_PIXEL_FORMAT_R10G10B10A2_UNORM = 90,
|
||||
MTL_PIXEL_FORMAT_R10G10B10A2_UINT = 91,
|
||||
|
||||
MTL_PIXEL_FORMAT_R11G11B10_FLOAT = 92,
|
||||
MTL_PIXEL_FORMAT_R9G9B9E5_FLOAT = 93,
|
||||
|
||||
MTL_PIXEL_FORMAT_B10G10R10A2_UNORM = 94,
|
||||
|
||||
MTL_PIXEL_FORMAT_BGR10_XR = 554,
|
||||
MTL_PIXEL_FORMAT_BGR10_XR_SRGB = 555,
|
||||
|
||||
/* Normal 64 bit formats */
|
||||
MTL_PIXEL_FORMAT_R32G32_UINT = 103,
|
||||
MTL_PIXEL_FORMAT_R32G32_SINT = 104,
|
||||
MTL_PIXEL_FORMAT_R32G32_FLOAT = 105,
|
||||
|
||||
MTL_PIXEL_FORMAT_R16G16B16A16_UNORM = 110,
|
||||
MTL_PIXEL_FORMAT_R16G16B16A16_SNORM = 112,
|
||||
MTL_PIXEL_FORMAT_R16G16B16A16_UINT = 113,
|
||||
MTL_PIXEL_FORMAT_R16G16B16A16_SINT = 114,
|
||||
MTL_PIXEL_FORMAT_R16G16B16A16_FLOAT = 115,
|
||||
|
||||
MTL_PIXEL_FORMAT_BGRA10_XR = 552,
|
||||
MTL_PIXEL_FORMAT_BGRA10_XR_SRGB = 553,
|
||||
|
||||
/* Normal 128 bit formats */
|
||||
MTL_PIXEL_FORMAT_R32G32B32A32_UINT = 123,
|
||||
MTL_PIXEL_FORMAT_R32G32B32A32_SINT = 124,
|
||||
MTL_PIXEL_FORMAT_R32G32B32A32_FLOAT = 125,
|
||||
|
||||
/* Compressed formats. */
|
||||
|
||||
/* S3TC/DXT */
|
||||
MTL_PIXEL_FORMAT_DXT1_RGBA = 130,
|
||||
MTL_PIXEL_FORMAT_DXT1_SRGBA = 131,
|
||||
MTL_PIXEL_FORMAT_DXT3_RGBA = 132,
|
||||
MTL_PIXEL_FORMAT_DXT3_SRGBA = 133,
|
||||
MTL_PIXEL_FORMAT_DXT5_RGBA = 134,
|
||||
MTL_PIXEL_FORMAT_DXT5_SRGBA = 135,
|
||||
|
||||
/* RGTC */
|
||||
MTL_PIXEL_FORMAT_RGTC1_UNORM = 140,
|
||||
MTL_PIXEL_FORMAT_RGTC1_SNORM = 141,
|
||||
MTL_PIXEL_FORMAT_RGTC2_UNORM = 142,
|
||||
MTL_PIXEL_FORMAT_RGTC2_SNORM = 143,
|
||||
|
||||
/* BPTC */
|
||||
MTL_PIXEL_FORMAT_BPTC_RGB_FLOAT = 150,
|
||||
MTL_PIXEL_FORMAT_BPTC_RGB_UFLOAT = 151,
|
||||
MTL_PIXEL_FORMAT_BPTC_RGBA_UNORM = 152,
|
||||
MTL_PIXEL_FORMAT_BPTC_SRGBA = 153,
|
||||
|
||||
/* PVRTC */
|
||||
MTL_PIXEL_FORMAT_PVRTC_RGB_2BPP = 160,
|
||||
MTL_PIXEL_FORMAT_PVRTC_RGB_2BPP_SRGB = 161,
|
||||
MTL_PIXEL_FORMAT_PVRTC_RGB_4BPP = 162,
|
||||
MTL_PIXEL_FORMAT_PVRTC_RGB_4BPP_SRGB = 163,
|
||||
MTL_PIXEL_FORMAT_PVRTC_RGBA_2BPP = 164,
|
||||
MTL_PIXEL_FORMAT_PVRTC_RGBA_2BPP_SRGB = 165,
|
||||
MTL_PIXEL_FORMAT_PVRTC_RGBA_4BPP = 166,
|
||||
MTL_PIXEL_FORMAT_PVRTC_RGBA_4BPP_SRGB = 167,
|
||||
|
||||
/* ETC2 */
|
||||
MTL_PIXEL_FORMAT_ETC2_R11_UNORM = 170,
|
||||
MTL_PIXEL_FORMAT_ETC2_R11_SNORM = 172,
|
||||
MTL_PIXEL_FORMAT_ETC2_RG11_UNORM = 174,
|
||||
MTL_PIXEL_FORMAT_ETC2_RG11_SNORM = 176,
|
||||
MTL_PIXEL_FORMAT_ETC2_RGBA8 = 178,
|
||||
MTL_PIXEL_FORMAT_ETC2_SRGBA8 = 179,
|
||||
|
||||
MTL_PIXEL_FORMAT_ETC2_RGB8 = 180,
|
||||
MTL_PIXEL_FORMAT_ETC2_SRGB8 = 181,
|
||||
MTL_PIXEL_FORMAT_ETC2_RGB8A1 = 182,
|
||||
MTL_PIXEL_FORMAT_ETC2_SRGB8A1 = 183,
|
||||
|
||||
/* ASTC */
|
||||
MTL_PIXEL_FORMAT_ASTC_4x4_SRGB = 186,
|
||||
MTL_PIXEL_FORMAT_ASTC_5x4_SRGB = 187,
|
||||
MTL_PIXEL_FORMAT_ASTC_5x5_SRGB = 188,
|
||||
MTL_PIXEL_FORMAT_ASTC_6x5_SRGB = 189,
|
||||
MTL_PIXEL_FORMAT_ASTC_6x6_SRGB = 190,
|
||||
MTL_PIXEL_FORMAT_ASTC_8x5_SRGB = 192,
|
||||
MTL_PIXEL_FORMAT_ASTC_8x6_SRGB = 193,
|
||||
MTL_PIXEL_FORMAT_ASTC_8x8_SRGB = 194,
|
||||
MTL_PIXEL_FORMAT_ASTC_10x5_SRGB = 195,
|
||||
MTL_PIXEL_FORMAT_ASTC_10x6_SRGB = 196,
|
||||
MTL_PIXEL_FORMAT_ASTC_10x8_SRGB = 197,
|
||||
MTL_PIXEL_FORMAT_ASTC_10x10_SRGB = 198,
|
||||
MTL_PIXEL_FORMAT_ASTC_12x10_SRGB = 199,
|
||||
MTL_PIXEL_FORMAT_ASTC_12x12_SRGB = 200,
|
||||
|
||||
MTL_PIXEL_FORMAT_ASTC_4x4 = 204,
|
||||
MTL_PIXEL_FORMAT_ASTC_5x4 = 205,
|
||||
MTL_PIXEL_FORMAT_ASTC_5x5 = 206,
|
||||
MTL_PIXEL_FORMAT_ASTC_6x5 = 207,
|
||||
MTL_PIXEL_FORMAT_ASTC_6x6 = 208,
|
||||
MTL_PIXEL_FORMAT_ASTC_8x5 = 210,
|
||||
MTL_PIXEL_FORMAT_ASTC_8x6 = 211,
|
||||
MTL_PIXEL_FORMAT_ASTC_8x8 = 212,
|
||||
MTL_PIXEL_FORMAT_ASTC_10x5 = 213,
|
||||
MTL_PIXEL_FORMAT_ASTC_10x6 = 214,
|
||||
MTL_PIXEL_FORMAT_ASTC_10x8 = 215,
|
||||
MTL_PIXEL_FORMAT_ASTC_10x10 = 216,
|
||||
MTL_PIXEL_FORMAT_ASTC_12x10 = 217,
|
||||
MTL_PIXEL_FORMAT_ASTC_12x12 = 218,
|
||||
|
||||
/* ASTC HDR (High Dynamic Range) */
|
||||
MTL_PIXEL_FORMAT_ASTC_4x4_FLOAT = 222,
|
||||
MTL_PIXEL_FORMAT_ASTC_5x4_FLOAT = 223,
|
||||
MTL_PIXEL_FORMAT_ASTC_5x5_FLOAT = 224,
|
||||
MTL_PIXEL_FORMAT_ASTC_6x5_FLOAT = 225,
|
||||
MTL_PIXEL_FORMAT_ASTC_6x6_FLOAT = 226,
|
||||
MTL_PIXEL_FORMAT_ASTC_8x5_FLOAT = 228,
|
||||
MTL_PIXEL_FORMAT_ASTC_8x6_FLOAT = 229,
|
||||
MTL_PIXEL_FORMAT_ASTC_8x8_FLOAT = 230,
|
||||
MTL_PIXEL_FORMAT_ASTC_10x5_FLOAT = 231,
|
||||
MTL_PIXEL_FORMAT_ASTC_10x6_FLOAT = 232,
|
||||
MTL_PIXEL_FORMAT_ASTC_10x8_FLOAT = 233,
|
||||
MTL_PIXEL_FORMAT_ASTC_10x10_FLOAT = 234,
|
||||
MTL_PIXEL_FORMAT_ASTC_12x10_FLOAT = 235,
|
||||
MTL_PIXEL_FORMAT_ASTC_12x12_FLOAT = 236,
|
||||
|
||||
/* YUV */
|
||||
MTL_PIXEL_FORMAT_GBGR422 = 240,
|
||||
MTL_PIXEL_FORMAT_BGRG422 = 241,
|
||||
|
||||
/* DEPTH */
|
||||
MTL_PIXEL_FORMAT_Z16_UNORM = 250,
|
||||
MTL_PIXEL_FORMAT_Z32_FLOAT = 252,
|
||||
|
||||
/* STENCIL */
|
||||
MTL_PIXEL_FORMAT_S8_UINT = 253,
|
||||
|
||||
/* DEPTH STENCIL */
|
||||
MTL_PIXEL_FORMAT_Z24_UNORM_S8_UINT = 255,
|
||||
MTL_PIXEL_FORMAT_Z32_FLOAT_S8X24_UINT = 260,
|
||||
|
||||
MTL_PIXEL_FORMAT_X32_S8X24_UINT = 261,
|
||||
MTL_PIXEL_FORMAT_X24_S8_UINT = 262,
|
||||
};
|
||||
|
||||
#endif /* MTL_FORMAT_H */
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef MTL_HEAP_H
|
||||
#define MTL_HEAP_H 1
|
||||
|
||||
#include "mtl_types.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
/* TODO_KOSMICKRISP We should move this struct to the bridge side. */
|
||||
struct kk_image_layout;
|
||||
|
||||
/* Creation */
|
||||
mtl_heap *mtl_new_heap(mtl_device *device, uint64_t size,
|
||||
enum mtl_resource_options resource_options);
|
||||
|
||||
/* Utils */
|
||||
uint64_t mtl_heap_get_size(mtl_heap *heap);
|
||||
|
||||
/* Allocation from heap */
|
||||
mtl_buffer *mtl_new_buffer_with_length(mtl_heap *heap, uint64_t size_B,
|
||||
uint64_t offset_B);
|
||||
mtl_texture *mtl_new_texture_with_descriptor(
|
||||
mtl_heap *heap, const struct kk_image_layout *layout, uint64_t offset);
|
||||
|
||||
#endif /* MTL_HEAP_H */
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_heap.h"
|
||||
|
||||
/* TODO_KOSMICKRISP Remove */
|
||||
#include "kk_private.h"
|
||||
#include "kk_image_layout.h"
|
||||
|
||||
#include <Metal/MTLHeap.h>
|
||||
|
||||
/* Creation */
|
||||
mtl_heap *
|
||||
mtl_new_heap(mtl_device *device, uint64_t size,
|
||||
enum mtl_resource_options resource_options)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> dev = (id<MTLDevice>)device;
|
||||
MTLHeapDescriptor *descriptor = [[MTLHeapDescriptor new] autorelease];
|
||||
descriptor.type = MTLHeapTypePlacement;
|
||||
descriptor.resourceOptions = (MTLResourceOptions)resource_options;
|
||||
descriptor.size = size;
|
||||
descriptor.sparsePageSize = MTLSparsePageSize16;
|
||||
return [dev newHeapWithDescriptor:descriptor];
|
||||
}
|
||||
}
|
||||
|
||||
/* Utils */
|
||||
uint64_t
|
||||
mtl_heap_get_size(mtl_heap *heap)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLHeap> hp = (id<MTLHeap>)heap;
|
||||
return hp.size;
|
||||
}
|
||||
}
|
||||
|
||||
static MTLTextureDescriptor *
|
||||
mtl_new_texture_descriptor(const struct kk_image_layout *layout)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLTextureDescriptor *descriptor = [MTLTextureDescriptor new];
|
||||
descriptor.textureType = (MTLTextureType)layout->type;
|
||||
descriptor.pixelFormat = layout->format.mtl;
|
||||
descriptor.width = layout->width_px;
|
||||
descriptor.height = layout->height_px;
|
||||
descriptor.depth = layout->depth_px;
|
||||
descriptor.mipmapLevelCount = layout->levels;
|
||||
descriptor.sampleCount = layout->sample_count_sa;
|
||||
descriptor.arrayLength = layout->layers;
|
||||
descriptor.allowGPUOptimizedContents = layout->optimized_layout;
|
||||
descriptor.usage = (MTLTextureUsage)layout->usage;
|
||||
/* We don't set the swizzle because Metal complains when the usage has store or render target with swizzle... */
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocation from heap */
|
||||
mtl_buffer *
|
||||
mtl_new_buffer_with_length(mtl_heap *heap, uint64_t size_B, uint64_t offset_B)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLHeap> hp = (id<MTLHeap>)heap;
|
||||
return (mtl_buffer *)[hp newBufferWithLength:size_B options:KK_MTL_RESOURCE_OPTIONS offset:offset_B];
|
||||
}
|
||||
}
|
||||
|
||||
mtl_texture *
|
||||
mtl_new_texture_with_descriptor(mtl_heap *heap,
|
||||
const struct kk_image_layout *layout,
|
||||
uint64_t offset)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLHeap> hp = (id<MTLHeap>)heap;
|
||||
MTLTextureDescriptor *descriptor = [mtl_new_texture_descriptor(layout) autorelease];
|
||||
descriptor.resourceOptions = hp.resourceOptions;
|
||||
return [hp newTextureWithDescriptor:descriptor offset:offset];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef MTL_LIBRARY_H
|
||||
#define MTL_LIBRARY_H 1
|
||||
|
||||
#include "mtl_types.h"
|
||||
|
||||
mtl_library *mtl_new_library(mtl_device *device, const char *src);
|
||||
mtl_function *mtl_new_function_with_name(mtl_library *lib,
|
||||
const char *entry_point);
|
||||
|
||||
#endif /* MTL_LIBRARY_H */
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_library.h"
|
||||
|
||||
#include <Metal/MTLDevice.h>
|
||||
|
||||
mtl_library *
|
||||
mtl_new_library(mtl_device *device, const char *src)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> dev = (id<MTLDevice>)device;
|
||||
id<MTLLibrary> lib = NULL;
|
||||
NSString *nsstr = [NSString stringWithCString:src encoding:NSASCIIStringEncoding];
|
||||
NSError *error;
|
||||
MTLCompileOptions *comp_opts = [MTLCompileOptions new];
|
||||
comp_opts.languageVersion = MTLLanguageVersion3_2;
|
||||
comp_opts.mathMode = MTLMathModeSafe;
|
||||
comp_opts.mathFloatingPointFunctions = MTLMathFloatingPointFunctionsPrecise;
|
||||
lib = [dev newLibraryWithSource:nsstr options:comp_opts error:&error];
|
||||
|
||||
if (error != nil) {
|
||||
fprintf(stderr, "Failed to create MTLLibrary: %s\n", [error.localizedDescription UTF8String]);
|
||||
}
|
||||
|
||||
[comp_opts release];
|
||||
return lib;
|
||||
}
|
||||
}
|
||||
|
||||
mtl_function *
|
||||
mtl_new_function_with_name(mtl_library *lib, const char *entry_point)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLLibrary> mtl_lib = (id<MTLLibrary>)lib;
|
||||
NSString *ns_entry_point = [NSString stringWithCString:entry_point encoding:NSASCIIStringEncoding];
|
||||
return [mtl_lib newFunctionWithName:ns_entry_point];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef MTL_RENDER_STATE_H
|
||||
#define MTL_RENDER_STATE_H 1
|
||||
|
||||
#include "mtl_types.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Bridge enums */
|
||||
enum mtl_pixel_format;
|
||||
|
||||
/* TODO_KOSMICKRISP Remove */
|
||||
enum VkCompareOp;
|
||||
enum VkStencilOp;
|
||||
|
||||
/* Render pass descriptor */
|
||||
mtl_render_pass_descriptor *mtl_new_render_pass_descriptor(void);
|
||||
|
||||
mtl_render_pass_attachment_descriptor *
|
||||
mtl_render_pass_descriptor_get_color_attachment(
|
||||
mtl_render_pass_descriptor *descriptor, uint32_t index);
|
||||
|
||||
mtl_render_pass_attachment_descriptor *
|
||||
mtl_render_pass_descriptor_get_depth_attachment(
|
||||
mtl_render_pass_descriptor *descriptor);
|
||||
|
||||
mtl_render_pass_attachment_descriptor *
|
||||
mtl_render_pass_descriptor_get_stencil_attachment(
|
||||
mtl_render_pass_descriptor *descriptor);
|
||||
|
||||
void mtl_render_pass_attachment_descriptor_set_texture(
|
||||
mtl_render_pass_attachment_descriptor *descriptor, mtl_texture *texture);
|
||||
|
||||
void mtl_render_pass_attachment_descriptor_set_level(
|
||||
mtl_render_pass_attachment_descriptor *descriptor, uint32_t level);
|
||||
|
||||
void mtl_render_pass_attachment_descriptor_set_slice(
|
||||
mtl_render_pass_attachment_descriptor *descriptor, uint32_t slice);
|
||||
|
||||
void mtl_render_pass_attachment_descriptor_set_load_action(
|
||||
mtl_render_pass_attachment_descriptor *descriptor,
|
||||
enum mtl_load_action action);
|
||||
|
||||
void mtl_render_pass_attachment_descriptor_set_store_action(
|
||||
mtl_render_pass_attachment_descriptor *descriptor,
|
||||
enum mtl_store_action action);
|
||||
|
||||
void mtl_render_pass_attachment_descriptor_set_clear_color(
|
||||
mtl_render_pass_attachment_descriptor *descriptor,
|
||||
struct mtl_clear_color clear_color);
|
||||
|
||||
void mtl_render_pass_attachment_descriptor_set_clear_depth(
|
||||
mtl_render_pass_attachment_descriptor *descriptor, double depth);
|
||||
|
||||
void mtl_render_pass_attachment_descriptor_set_clear_stencil(
|
||||
mtl_render_pass_attachment_descriptor *descriptor, uint32_t stencil);
|
||||
|
||||
void mtl_render_pass_descriptor_set_render_target_array_length(
|
||||
mtl_render_pass_descriptor *descriptor, uint32_t length);
|
||||
|
||||
void mtl_render_pass_descriptor_set_render_target_width(
|
||||
mtl_render_pass_descriptor *descriptor, uint32_t width);
|
||||
|
||||
void mtl_render_pass_descriptor_set_render_target_height(
|
||||
mtl_render_pass_descriptor *descriptor, uint32_t height);
|
||||
|
||||
void mtl_render_pass_descriptor_set_default_raster_sample_count(
|
||||
mtl_render_pass_descriptor *descriptor, uint32_t sample_count);
|
||||
|
||||
void mtl_render_pass_descriptor_set_sample_positions(
|
||||
mtl_render_pass_descriptor *descriptor,
|
||||
const struct mtl_sample_position *positions, uint32_t count);
|
||||
|
||||
void mtl_render_pass_descriptor_set_visibility_buffer(
|
||||
mtl_render_pass_descriptor *descriptor, mtl_buffer *visibility_buffer);
|
||||
|
||||
/* Render pipeline descriptor */
|
||||
mtl_render_pipeline_descriptor *mtl_new_render_pipeline_descriptor(void);
|
||||
|
||||
void mtl_render_pipeline_descriptor_set_vertex_shader(
|
||||
mtl_render_pass_descriptor *descriptor, mtl_function *shader);
|
||||
|
||||
void mtl_render_pipeline_descriptor_set_fragment_shader(
|
||||
mtl_render_pass_descriptor *descriptor, mtl_function *shader);
|
||||
|
||||
void mtl_render_pipeline_descriptor_set_input_primitive_topology(
|
||||
mtl_render_pass_descriptor *descriptor,
|
||||
enum mtl_primitive_topology_class topology_class);
|
||||
|
||||
void mtl_render_pipeline_descriptor_set_color_attachment_format(
|
||||
mtl_render_pass_descriptor *descriptor, uint8_t index,
|
||||
enum mtl_pixel_format format);
|
||||
|
||||
void mtl_render_pipeline_descriptor_set_depth_attachment_format(
|
||||
mtl_render_pass_descriptor *descriptor, enum mtl_pixel_format format);
|
||||
|
||||
void mtl_render_pipeline_descriptor_set_stencil_attachment_format(
|
||||
mtl_render_pass_descriptor *descriptor, enum mtl_pixel_format format);
|
||||
|
||||
void mtl_render_pipeline_descriptor_set_raster_sample_count(
|
||||
mtl_render_pass_descriptor *descriptor, uint32_t sample_count);
|
||||
|
||||
void mtl_render_pipeline_descriptor_set_alpha_to_coverage(
|
||||
mtl_render_pass_descriptor *descriptor, bool enabled);
|
||||
|
||||
void mtl_render_pipeline_descriptor_set_alpha_to_one(
|
||||
mtl_render_pass_descriptor *descriptor, bool enabled);
|
||||
|
||||
void mtl_render_pipeline_descriptor_set_rasterization_enabled(
|
||||
mtl_render_pass_descriptor *descriptor, bool enabled);
|
||||
|
||||
void mtl_render_pipeline_descriptor_set_max_vertex_amplification_count(
|
||||
mtl_render_pass_descriptor *descriptor, uint32_t count);
|
||||
|
||||
/* Render pipeline */
|
||||
mtl_render_pipeline_state *
|
||||
mtl_new_render_pipeline(mtl_device *device,
|
||||
mtl_render_pass_descriptor *descriptor);
|
||||
|
||||
/* Stencil descriptor */
|
||||
mtl_stencil_descriptor *mtl_new_stencil_descriptor(void);
|
||||
|
||||
void mtl_stencil_descriptor_set_stencil_failure_operation(
|
||||
mtl_stencil_descriptor *descriptor, enum VkStencilOp op);
|
||||
|
||||
void mtl_stencil_descriptor_set_depth_failure_operation(
|
||||
mtl_stencil_descriptor *descriptor, enum VkStencilOp op);
|
||||
|
||||
void mtl_stencil_descriptor_set_depth_stencil_pass_operation(
|
||||
mtl_stencil_descriptor *descriptor, enum VkStencilOp op);
|
||||
|
||||
void mtl_stencil_descriptor_set_stencil_compare_function(
|
||||
mtl_stencil_descriptor *descriptor, enum VkCompareOp op);
|
||||
|
||||
void mtl_stencil_descriptor_set_read_mask(mtl_stencil_descriptor *descriptor,
|
||||
uint32_t mask);
|
||||
|
||||
void mtl_stencil_descriptor_set_write_mask(mtl_stencil_descriptor *descriptor,
|
||||
uint32_t mask);
|
||||
|
||||
/* Depth stencil descriptor */
|
||||
mtl_depth_stencil_descriptor *mtl_new_depth_stencil_descriptor(void);
|
||||
|
||||
void mtl_depth_stencil_descriptor_set_depth_compare_function(
|
||||
mtl_depth_stencil_descriptor *descriptor, enum VkCompareOp op);
|
||||
|
||||
void mtl_depth_stencil_descriptor_set_depth_write_enabled(
|
||||
mtl_depth_stencil_descriptor *descriptor, bool enable_write);
|
||||
|
||||
void mtl_depth_stencil_descriptor_set_back_face_stencil(
|
||||
mtl_depth_stencil_descriptor *descriptor,
|
||||
mtl_stencil_descriptor *stencil_descriptor);
|
||||
|
||||
void mtl_depth_stencil_descriptor_set_front_face_stencil(
|
||||
mtl_depth_stencil_descriptor *descriptor,
|
||||
mtl_stencil_descriptor *stencil_descriptor);
|
||||
|
||||
/* Depth stencil state */
|
||||
mtl_depth_stencil_state *
|
||||
mtl_new_depth_stencil_state(mtl_device *device,
|
||||
mtl_depth_stencil_descriptor *descriptor);
|
||||
|
||||
#endif /* MTL_RENDER_STATE_H */
|
||||
@@ -0,0 +1,495 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_render_state.h"
|
||||
|
||||
#include "mtl_format.h"
|
||||
|
||||
/* TODO_KOSMICKRISP Remove */
|
||||
#include "vk_to_mtl_map.h"
|
||||
|
||||
/* TODO_KOSMICKRISP Remove */
|
||||
#include "vulkan/vulkan.h"
|
||||
|
||||
#include <Metal/MTLRenderPass.h>
|
||||
#include <Metal/MTLRenderPipeline.h>
|
||||
#include <Metal/MTLDepthStencil.h>
|
||||
|
||||
/* Render pass descriptor */
|
||||
mtl_render_pass_descriptor *
|
||||
mtl_new_render_pass_descriptor(void)
|
||||
{
|
||||
@autoreleasepool {
|
||||
return [[MTLRenderPassDescriptor renderPassDescriptor] retain];
|
||||
}
|
||||
}
|
||||
|
||||
mtl_render_pass_attachment_descriptor *
|
||||
mtl_render_pass_descriptor_get_color_attachment(
|
||||
mtl_render_pass_descriptor *descriptor, uint32_t index)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPassDescriptor *desc = (MTLRenderPassDescriptor *)descriptor;
|
||||
return desc.colorAttachments[index];
|
||||
}
|
||||
}
|
||||
|
||||
mtl_render_pass_attachment_descriptor *
|
||||
mtl_render_pass_descriptor_get_depth_attachment(
|
||||
mtl_render_pass_descriptor *descriptor)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPassDescriptor *desc = (MTLRenderPassDescriptor *)descriptor;
|
||||
return desc.depthAttachment;
|
||||
}
|
||||
}
|
||||
|
||||
mtl_render_pass_attachment_descriptor *
|
||||
mtl_render_pass_descriptor_get_stencil_attachment(
|
||||
mtl_render_pass_descriptor *descriptor)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPassDescriptor *desc = (MTLRenderPassDescriptor *)descriptor;
|
||||
return desc.stencilAttachment;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_attachment_descriptor_set_texture(
|
||||
mtl_render_pass_attachment_descriptor *descriptor, mtl_texture *texture)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPassAttachmentDescriptor *desc = (MTLRenderPassAttachmentDescriptor *)descriptor;
|
||||
desc.texture = (id<MTLTexture>)texture;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_attachment_descriptor_set_level(
|
||||
mtl_render_pass_attachment_descriptor *descriptor, uint32_t level)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPassAttachmentDescriptor *desc = (MTLRenderPassAttachmentDescriptor *)descriptor;
|
||||
desc.level = level;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_attachment_descriptor_set_slice(
|
||||
mtl_render_pass_attachment_descriptor *descriptor, uint32_t slice)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPassAttachmentDescriptor *desc = (MTLRenderPassAttachmentDescriptor *)descriptor;
|
||||
desc.slice = slice;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_attachment_descriptor_set_load_action(
|
||||
mtl_render_pass_attachment_descriptor *descriptor,
|
||||
enum mtl_load_action action)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPassAttachmentDescriptor *desc = (MTLRenderPassAttachmentDescriptor *)descriptor;
|
||||
desc.loadAction = (MTLLoadAction)action;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_attachment_descriptor_set_store_action(
|
||||
mtl_render_pass_attachment_descriptor *descriptor,
|
||||
enum mtl_store_action action)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPassAttachmentDescriptor *desc = (MTLRenderPassAttachmentDescriptor *)descriptor;
|
||||
desc.storeAction = (MTLStoreAction)action;
|
||||
desc.storeActionOptions = MTLStoreActionOptionNone; /* TODO_KOSMICKRISP Maybe expose this? */
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_attachment_descriptor_set_clear_color(
|
||||
mtl_render_pass_attachment_descriptor *descriptor,
|
||||
struct mtl_clear_color clear_color)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPassColorAttachmentDescriptor *desc = (MTLRenderPassColorAttachmentDescriptor *)descriptor;
|
||||
desc.clearColor = MTLClearColorMake(clear_color.red, clear_color.green, clear_color.blue, clear_color.alpha);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_attachment_descriptor_set_clear_depth(
|
||||
mtl_render_pass_attachment_descriptor *descriptor, double depth)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPassDepthAttachmentDescriptor *desc = (MTLRenderPassDepthAttachmentDescriptor *)descriptor;
|
||||
desc.clearDepth = depth;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_attachment_descriptor_set_clear_stencil(mtl_render_pass_attachment_descriptor *descriptor,
|
||||
uint32_t stencil)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPassStencilAttachmentDescriptor *desc = (MTLRenderPassStencilAttachmentDescriptor *)descriptor;
|
||||
desc.clearStencil = stencil;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_descriptor_set_render_target_array_length(mtl_render_pass_descriptor *descriptor,
|
||||
uint32_t length)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPassDescriptor *desc = (MTLRenderPassDescriptor *)descriptor;
|
||||
desc.renderTargetArrayLength = length;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_descriptor_set_render_target_width(mtl_render_pass_descriptor *descriptor,
|
||||
uint32_t width)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPassDescriptor *desc = (MTLRenderPassDescriptor *)descriptor;
|
||||
desc.renderTargetWidth = width;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_descriptor_set_render_target_height(mtl_render_pass_descriptor *descriptor,
|
||||
uint32_t height)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPassDescriptor *desc = (MTLRenderPassDescriptor *)descriptor;
|
||||
desc.renderTargetHeight = height;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_descriptor_set_default_raster_sample_count(mtl_render_pass_descriptor *descriptor,
|
||||
uint32_t sample_count)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPassDescriptor *desc = (MTLRenderPassDescriptor *)descriptor;
|
||||
desc.defaultRasterSampleCount = sample_count;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_descriptor_set_sample_positions(
|
||||
mtl_render_pass_descriptor *descriptor,
|
||||
const struct mtl_sample_position *positions,
|
||||
uint32_t count)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPassDescriptor *desc = (MTLRenderPassDescriptor *)descriptor;
|
||||
if (count > 0) {
|
||||
MTLSamplePosition pos[count];
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
pos[i] = MTLSamplePositionMake(positions[i].x, positions[i].y);
|
||||
}
|
||||
[desc setSamplePositions:pos count:count];
|
||||
} else {
|
||||
[desc setSamplePositions:NULL count:count];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_descriptor_set_visibility_buffer(mtl_render_pass_descriptor *descriptor,
|
||||
mtl_buffer *visibility_buffer)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPassDescriptor *desc = (MTLRenderPassDescriptor *)descriptor;
|
||||
id<MTLBuffer> buffer = (id<MTLBuffer>)visibility_buffer;
|
||||
desc.visibilityResultBuffer = buffer;
|
||||
}
|
||||
}
|
||||
|
||||
/* Render pipeline descriptor */
|
||||
mtl_render_pipeline_descriptor *
|
||||
mtl_new_render_pipeline_descriptor()
|
||||
{
|
||||
@autoreleasepool {
|
||||
return [[MTLRenderPipelineDescriptor alloc] init];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pipeline_descriptor_set_vertex_shader(mtl_render_pass_descriptor *descriptor,
|
||||
mtl_function *shader)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPipelineDescriptor *desc = (MTLRenderPipelineDescriptor *)descriptor;
|
||||
desc.vertexFunction = (id<MTLFunction>)shader;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pipeline_descriptor_set_fragment_shader(mtl_render_pass_descriptor *descriptor,
|
||||
mtl_function *shader)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPipelineDescriptor *desc = (MTLRenderPipelineDescriptor *)descriptor;
|
||||
desc.fragmentFunction = (id<MTLFunction>)shader;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pipeline_descriptor_set_input_primitive_topology(mtl_render_pass_descriptor *descriptor,
|
||||
enum mtl_primitive_topology_class class)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPipelineDescriptor *desc = (MTLRenderPipelineDescriptor *)descriptor;
|
||||
desc.inputPrimitiveTopology = (MTLPrimitiveTopologyClass)class;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pipeline_descriptor_set_color_attachment_format(mtl_render_pass_descriptor *descriptor,
|
||||
uint8_t index,
|
||||
enum mtl_pixel_format format)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPipelineDescriptor *desc = (MTLRenderPipelineDescriptor *)descriptor;
|
||||
desc.colorAttachments[index].pixelFormat = (MTLPixelFormat)format;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pipeline_descriptor_set_depth_attachment_format(mtl_render_pass_descriptor *descriptor,
|
||||
enum mtl_pixel_format format)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPipelineDescriptor *desc = (MTLRenderPipelineDescriptor *)descriptor;
|
||||
desc.depthAttachmentPixelFormat = (MTLPixelFormat)format;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pipeline_descriptor_set_stencil_attachment_format(mtl_render_pass_descriptor *descriptor,
|
||||
enum mtl_pixel_format format)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPipelineDescriptor *desc = (MTLRenderPipelineDescriptor *)descriptor;
|
||||
desc.stencilAttachmentPixelFormat = (MTLPixelFormat)format;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pipeline_descriptor_set_raster_sample_count(mtl_render_pass_descriptor *descriptor,
|
||||
uint32_t sample_count)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPipelineDescriptor *desc = (MTLRenderPipelineDescriptor *)descriptor;
|
||||
desc.rasterSampleCount = sample_count;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pipeline_descriptor_set_alpha_to_coverage(mtl_render_pass_descriptor *descriptor,
|
||||
bool enabled)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPipelineDescriptor *desc = (MTLRenderPipelineDescriptor *)descriptor;
|
||||
desc.alphaToCoverageEnabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pipeline_descriptor_set_alpha_to_one(mtl_render_pass_descriptor *descriptor,
|
||||
bool enabled)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPipelineDescriptor *desc = (MTLRenderPipelineDescriptor *)descriptor;
|
||||
desc.alphaToOneEnabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pipeline_descriptor_set_rasterization_enabled(mtl_render_pass_descriptor *descriptor,
|
||||
bool enabled)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPipelineDescriptor *desc = (MTLRenderPipelineDescriptor *)descriptor;
|
||||
desc.rasterizationEnabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pipeline_descriptor_set_max_vertex_amplification_count( mtl_render_pass_descriptor *descriptor,
|
||||
uint32_t count)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPipelineDescriptor *desc = (MTLRenderPipelineDescriptor *)descriptor;
|
||||
desc.maxVertexAmplificationCount = count;
|
||||
}
|
||||
}
|
||||
|
||||
/* Render pipeline */
|
||||
mtl_render_pipeline_state *
|
||||
mtl_new_render_pipeline(mtl_device *device, mtl_render_pass_descriptor *descriptor)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLRenderPipelineDescriptor *desc = (MTLRenderPipelineDescriptor *)descriptor;
|
||||
id<MTLDevice> dev = (id<MTLDevice>)device;
|
||||
NSError *error = nil;
|
||||
mtl_render_pipeline_state *pipeline = [dev newRenderPipelineStateWithDescriptor:desc error:&error];
|
||||
if (error != nil) {
|
||||
fprintf(stderr, "Failed to create MTLLibrary: %s\n", [error.localizedDescription UTF8String]);
|
||||
}
|
||||
|
||||
return pipeline;
|
||||
}
|
||||
}
|
||||
|
||||
/* Stencil descriptor */
|
||||
mtl_stencil_descriptor *
|
||||
mtl_new_stencil_descriptor()
|
||||
{
|
||||
@autoreleasepool {
|
||||
return [[MTLStencilDescriptor new] init];
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO_KOSMICKRISP Move this to map */
|
||||
static MTLStencilOperation
|
||||
map_vk_stencil_op_to_mtl_stencil_operation(VkStencilOp op)
|
||||
{
|
||||
switch (op) {
|
||||
case VK_STENCIL_OP_KEEP:
|
||||
return MTLStencilOperationKeep;
|
||||
case VK_STENCIL_OP_ZERO:
|
||||
return MTLStencilOperationZero;
|
||||
case VK_STENCIL_OP_REPLACE:
|
||||
return MTLStencilOperationReplace;
|
||||
case VK_STENCIL_OP_INCREMENT_AND_CLAMP:
|
||||
return MTLStencilOperationIncrementClamp;
|
||||
case VK_STENCIL_OP_DECREMENT_AND_CLAMP:
|
||||
return MTLStencilOperationDecrementClamp;
|
||||
case VK_STENCIL_OP_INVERT:
|
||||
return MTLStencilOperationInvert;
|
||||
case VK_STENCIL_OP_INCREMENT_AND_WRAP:
|
||||
return MTLStencilOperationIncrementWrap;
|
||||
case VK_STENCIL_OP_DECREMENT_AND_WRAP:
|
||||
return MTLStencilOperationDecrementWrap;
|
||||
default:
|
||||
assert(false && "Unsupported VkStencilOp");
|
||||
return MTLStencilOperationZero;
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
mtl_stencil_descriptor_set_stencil_failure_operation(mtl_stencil_descriptor *descriptor, VkStencilOp op)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLStencilDescriptor *desc = (MTLStencilDescriptor *)descriptor;
|
||||
desc.stencilFailureOperation = map_vk_stencil_op_to_mtl_stencil_operation(op);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_stencil_descriptor_set_depth_failure_operation(mtl_stencil_descriptor *descriptor, VkStencilOp op)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLStencilDescriptor *desc = (MTLStencilDescriptor *)descriptor;
|
||||
desc.depthFailureOperation = map_vk_stencil_op_to_mtl_stencil_operation(op);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_stencil_descriptor_set_depth_stencil_pass_operation(mtl_stencil_descriptor *descriptor, VkStencilOp op)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLStencilDescriptor *desc = (MTLStencilDescriptor *)descriptor;
|
||||
desc.depthStencilPassOperation = map_vk_stencil_op_to_mtl_stencil_operation(op);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_stencil_descriptor_set_stencil_compare_function(mtl_stencil_descriptor *descriptor, VkCompareOp op)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLStencilDescriptor *desc = (MTLStencilDescriptor *)descriptor;
|
||||
desc.stencilCompareFunction = (MTLCompareFunction)vk_compare_op_to_mtl_compare_function(op);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_stencil_descriptor_set_read_mask(mtl_stencil_descriptor *descriptor, uint32_t mask)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLStencilDescriptor *desc = (MTLStencilDescriptor *)descriptor;
|
||||
desc.readMask = mask;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_stencil_descriptor_set_write_mask(mtl_stencil_descriptor *descriptor, uint32_t mask)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLStencilDescriptor *desc = (MTLStencilDescriptor *)descriptor;
|
||||
desc.writeMask = mask;
|
||||
}
|
||||
}
|
||||
|
||||
/* Depth stencil descriptor */
|
||||
mtl_depth_stencil_descriptor *
|
||||
mtl_new_depth_stencil_descriptor()
|
||||
{
|
||||
@autoreleasepool {
|
||||
return [[MTLDepthStencilDescriptor new] init];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_depth_stencil_descriptor_set_depth_compare_function(mtl_depth_stencil_descriptor *descriptor, VkCompareOp op)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLDepthStencilDescriptor *desc = (MTLDepthStencilDescriptor *)descriptor;
|
||||
desc.depthCompareFunction = (MTLCompareFunction)vk_compare_op_to_mtl_compare_function(op);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_depth_stencil_descriptor_set_depth_write_enabled(mtl_depth_stencil_descriptor *descriptor, bool enable_write)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLDepthStencilDescriptor *desc = (MTLDepthStencilDescriptor *)descriptor;
|
||||
desc.depthWriteEnabled = enable_write;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_depth_stencil_descriptor_set_back_face_stencil(mtl_depth_stencil_descriptor *descriptor, mtl_stencil_descriptor *stencil_descriptor)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLDepthStencilDescriptor *desc = (MTLDepthStencilDescriptor *)descriptor;
|
||||
desc.backFaceStencil = (MTLStencilDescriptor *)stencil_descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_depth_stencil_descriptor_set_front_face_stencil(mtl_depth_stencil_descriptor *descriptor, mtl_stencil_descriptor *stencil_descriptor)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLDepthStencilDescriptor *desc = (MTLDepthStencilDescriptor *)descriptor;
|
||||
desc.frontFaceStencil = (MTLStencilDescriptor *)stencil_descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
mtl_depth_stencil_state *
|
||||
mtl_new_depth_stencil_state(mtl_device *device, mtl_depth_stencil_descriptor *descriptor)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> dev = (id<MTLDevice>)device;
|
||||
MTLDepthStencilDescriptor *desc = (MTLDepthStencilDescriptor *)descriptor;
|
||||
return [dev newDepthStencilStateWithDescriptor:desc];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef MTL_RESIDENCY_SET_H
|
||||
#define MTL_RESIDENCY_SET_H 1
|
||||
|
||||
#include "mtl_types.h"
|
||||
|
||||
mtl_residency_set *mtl_new_residency_set(mtl_device *device);
|
||||
void mtl_residency_set_add_allocation(mtl_residency_set *residency_set,
|
||||
mtl_allocation *allocation);
|
||||
void mtl_residency_set_remove_allocation(mtl_residency_set *residency_set,
|
||||
mtl_allocation *allocation);
|
||||
void mtl_residency_set_commit(mtl_residency_set *residency_set);
|
||||
void mtl_residency_set_request_residency(mtl_residency_set *residency_set);
|
||||
void mtl_residency_set_end_residency(mtl_residency_set *residency_set);
|
||||
|
||||
#endif /* MTL_RESIDENCY_SET_H */
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_residency_set.h"
|
||||
|
||||
#include <Metal/MTLDevice.h>
|
||||
#include <Metal/MTLResidencySet.h>
|
||||
|
||||
mtl_residency_set *
|
||||
mtl_new_residency_set(mtl_device *device)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> dev = (id<MTLDevice>)device;
|
||||
MTLResidencySetDescriptor *setDescriptor = [[[MTLResidencySetDescriptor alloc] init] autorelease];
|
||||
setDescriptor.initialCapacity = 100;
|
||||
NSError *error;
|
||||
id<MTLResidencySet> set = [dev newResidencySetWithDescriptor:setDescriptor
|
||||
error:&error];
|
||||
|
||||
if (error != nil) {
|
||||
fprintf(stderr, "Failed to create MTLResidencySet: %s\n", [error.localizedDescription UTF8String]);
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_residency_set_add_allocation(mtl_residency_set *residency_set,
|
||||
mtl_allocation *allocation)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLResidencySet> set = (id<MTLResidencySet>)residency_set;
|
||||
id<MTLAllocation> alloc = (id<MTLAllocation>)allocation;
|
||||
[set addAllocation:alloc];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_residency_set_remove_allocation(mtl_residency_set *residency_set,
|
||||
mtl_allocation *allocation)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLResidencySet> set = (id<MTLResidencySet>)residency_set;
|
||||
id<MTLAllocation> alloc = (id<MTLAllocation>)allocation;
|
||||
[set removeAllocation:alloc];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_residency_set_commit(mtl_residency_set *residency_set)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLResidencySet> set = (id<MTLResidencySet>)residency_set;
|
||||
[set commit];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_residency_set_request_residency(mtl_residency_set *residency_set)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLResidencySet> set = (id<MTLResidencySet>)residency_set;
|
||||
[set requestResidency];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_residency_set_end_residency(mtl_residency_set *residency_set)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLResidencySet> set = (id<MTLResidencySet>)residency_set;
|
||||
[set endResidency];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef MTL_SAMPLER_H
|
||||
#define MTL_SAMPLER_H 1
|
||||
|
||||
#include "mtl_types.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Sampler descriptor */
|
||||
mtl_sampler_descriptor *mtl_new_sampler_descriptor(void);
|
||||
|
||||
/* Sampler descriptor utils */
|
||||
void mtl_sampler_descriptor_set_normalized_coordinates(
|
||||
mtl_sampler_descriptor *descriptor, bool normalized_coordinates);
|
||||
void mtl_sampler_descriptor_set_address_mode(
|
||||
mtl_sampler_descriptor *descriptor,
|
||||
enum mtl_sampler_address_mode address_mode_u,
|
||||
enum mtl_sampler_address_mode address_mode_v,
|
||||
enum mtl_sampler_address_mode address_mode_w);
|
||||
void
|
||||
mtl_sampler_descriptor_set_border_color(mtl_sampler_descriptor *descriptor,
|
||||
enum mtl_sampler_border_color color);
|
||||
void
|
||||
mtl_sampler_descriptor_set_filters(mtl_sampler_descriptor *descriptor,
|
||||
enum mtl_sampler_min_mag_filter min_filter,
|
||||
enum mtl_sampler_min_mag_filter mag_filter,
|
||||
enum mtl_sampler_mip_filter mip_filter);
|
||||
void mtl_sampler_descriptor_set_lod_clamp(mtl_sampler_descriptor *descriptor,
|
||||
float min, float max);
|
||||
void
|
||||
mtl_sampler_descriptor_set_max_anisotropy(mtl_sampler_descriptor *descriptor,
|
||||
uint64_t max);
|
||||
void
|
||||
mtl_sampler_descriptor_set_compare_function(mtl_sampler_descriptor *descriptor,
|
||||
enum mtl_compare_function function);
|
||||
|
||||
/* Sampler */
|
||||
mtl_sampler *mtl_new_sampler(mtl_device *device,
|
||||
mtl_sampler_descriptor *descriptor);
|
||||
|
||||
/* Sampler utils */
|
||||
uint64_t mtl_sampler_get_gpu_resource_id(mtl_sampler *sampler);
|
||||
|
||||
#endif /* MTL_SAMPLER_H */
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_sampler.h"
|
||||
|
||||
#include <Metal/MTLSampler.h>
|
||||
|
||||
mtl_sampler_descriptor *
|
||||
mtl_new_sampler_descriptor()
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLSamplerDescriptor *descriptor = [MTLSamplerDescriptor new];
|
||||
/* Set common variables we don't expose */
|
||||
descriptor.lodAverage = false;
|
||||
descriptor.supportArgumentBuffers = true;
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_sampler_descriptor_set_normalized_coordinates(mtl_sampler_descriptor *descriptor, bool normalized_coordinates)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLSamplerDescriptor *desc = (MTLSamplerDescriptor *)descriptor;
|
||||
desc.normalizedCoordinates = normalized_coordinates;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_sampler_descriptor_set_address_mode(mtl_sampler_descriptor *descriptor,
|
||||
enum mtl_sampler_address_mode address_mode_u,
|
||||
enum mtl_sampler_address_mode address_mode_v,
|
||||
enum mtl_sampler_address_mode address_mode_w)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLSamplerDescriptor *desc = (MTLSamplerDescriptor *)descriptor;
|
||||
desc.sAddressMode = (MTLSamplerAddressMode)address_mode_u;
|
||||
desc.tAddressMode = (MTLSamplerAddressMode)address_mode_v;
|
||||
desc.rAddressMode = (MTLSamplerAddressMode)address_mode_w;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_sampler_descriptor_set_border_color(mtl_sampler_descriptor *descriptor, enum mtl_sampler_border_color color)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLSamplerDescriptor *desc = (MTLSamplerDescriptor *)descriptor;
|
||||
desc.borderColor = (MTLSamplerBorderColor)color;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_sampler_descriptor_set_filters(mtl_sampler_descriptor *descriptor,
|
||||
enum mtl_sampler_min_mag_filter min_filter,
|
||||
enum mtl_sampler_min_mag_filter mag_filter,
|
||||
enum mtl_sampler_mip_filter mip_filter)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLSamplerDescriptor *desc = (MTLSamplerDescriptor *)descriptor;
|
||||
desc.minFilter = (MTLSamplerMinMagFilter)min_filter;
|
||||
desc.magFilter = (MTLSamplerMinMagFilter)mag_filter;
|
||||
desc.mipFilter = (MTLSamplerMipFilter)mip_filter;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_sampler_descriptor_set_lod_clamp(mtl_sampler_descriptor *descriptor,
|
||||
float min,
|
||||
float max)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLSamplerDescriptor *desc = (MTLSamplerDescriptor *)descriptor;
|
||||
desc.lodMinClamp = min;
|
||||
desc.lodMaxClamp = max;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_sampler_descriptor_set_max_anisotropy(mtl_sampler_descriptor *descriptor,
|
||||
uint64_t max)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLSamplerDescriptor *desc = (MTLSamplerDescriptor *)descriptor;
|
||||
desc.maxAnisotropy = max ? max : 1u; /* Metal requires a non-zero value */
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_sampler_descriptor_set_compare_function(mtl_sampler_descriptor *descriptor,
|
||||
enum mtl_compare_function function)
|
||||
{
|
||||
@autoreleasepool {
|
||||
MTLSamplerDescriptor *desc = (MTLSamplerDescriptor *)descriptor;
|
||||
desc.compareFunction = (MTLCompareFunction)function;
|
||||
}
|
||||
}
|
||||
|
||||
mtl_sampler *
|
||||
mtl_new_sampler(mtl_device *device, mtl_sampler_descriptor *descriptor)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> dev = (id<MTLDevice>)device;
|
||||
MTLSamplerDescriptor *desc = (MTLSamplerDescriptor *)descriptor;
|
||||
return [dev newSamplerStateWithDescriptor:desc];
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t
|
||||
mtl_sampler_get_gpu_resource_id(mtl_sampler *sampler)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLSamplerState> samp = (id<MTLSamplerState>)sampler;
|
||||
return [samp gpuResourceID]._impl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef MTL_SYNC_H
|
||||
#define MTL_SYNC_H 1
|
||||
|
||||
#include "mtl_types.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
/* MTLFence */
|
||||
mtl_fence *mtl_new_fence(mtl_device *device);
|
||||
|
||||
/* MTLEvent */
|
||||
mtl_event *mtl_new_event(mtl_device *device);
|
||||
|
||||
/* MTLSharedEvent */
|
||||
mtl_shared_event *mtl_new_shared_event(mtl_device *device);
|
||||
int mtl_shared_event_wait_until_signaled_value(mtl_shared_event *event_handle,
|
||||
uint64_t value,
|
||||
uint64_t timeout_ms);
|
||||
uint64_t mtl_shared_event_get_signaled_value(mtl_shared_event *event_handle);
|
||||
void mtl_shared_event_set_signaled_value(mtl_shared_event *event_handle,
|
||||
uint64_t value);
|
||||
|
||||
#endif /* MTL_SYNC_H */
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_sync.h"
|
||||
|
||||
#include <Metal/MTLEvent.h>
|
||||
|
||||
/* MTLFence */
|
||||
mtl_fence *
|
||||
mtl_new_fence(mtl_device *device)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> dev = (id<MTLDevice>)device;
|
||||
return (mtl_fence *)[dev newFence];
|
||||
}
|
||||
}
|
||||
|
||||
/* MTLEvent */
|
||||
mtl_event *
|
||||
mtl_new_event(mtl_device *device)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> dev = (id<MTLDevice>)device;
|
||||
return [dev newEvent];
|
||||
}
|
||||
}
|
||||
|
||||
/* MTLSharedEvent */
|
||||
mtl_shared_event *
|
||||
mtl_new_shared_event(mtl_device *device)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLDevice> dev = (id<MTLDevice>)device;
|
||||
return [dev newSharedEvent];
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
mtl_shared_event_wait_until_signaled_value(mtl_shared_event *event_handle, uint64_t value, uint64_t timeout_ms)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLSharedEvent> event = (id<MTLSharedEvent>)event_handle;
|
||||
return (int)[event waitUntilSignaledValue:value timeoutMS:timeout_ms];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_shared_event_set_signaled_value(mtl_shared_event *event_handle, uint64_t value)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLSharedEvent> event = (id<MTLSharedEvent>)event_handle;
|
||||
event.signaledValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t
|
||||
mtl_shared_event_get_signaled_value(mtl_shared_event *event_handle)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLSharedEvent> event = (id<MTLSharedEvent>)event_handle;
|
||||
return event.signaledValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef MTL_TEXTURE_H
|
||||
#define MTL_TEXTURE_H 1
|
||||
|
||||
#include "mtl_types.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
/* TODO_KOSMICKRISP Move this to bridge. */
|
||||
struct kk_view_layout;
|
||||
|
||||
/* Utils*/
|
||||
uint64_t mtl_texture_get_gpu_resource_id(mtl_texture *texture);
|
||||
|
||||
/* Texture view creation */
|
||||
mtl_texture *mtl_new_texture_view_with(mtl_texture *texture,
|
||||
const struct kk_view_layout *layout);
|
||||
mtl_texture *
|
||||
mtl_new_texture_view_with_no_swizzle(mtl_texture *texture,
|
||||
const struct kk_view_layout *layout);
|
||||
|
||||
void mtl_texture_get_bytes(mtl_texture *texture, void *host_ptr,
|
||||
struct mtl_texture_memory_copy *data);
|
||||
|
||||
void mtl_texture_replace_region(mtl_texture *texture, const void *host_ptr,
|
||||
struct mtl_texture_memory_copy *data);
|
||||
|
||||
#endif /* MTL_TEXTURE_H */
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_texture.h"
|
||||
|
||||
/* TODO_LUNARG Remove */
|
||||
#include "kk_image_layout.h"
|
||||
|
||||
/* TODO_LUNARG Remove */
|
||||
#include "vulkan/vulkan.h"
|
||||
|
||||
#include <Metal/MTLTexture.h>
|
||||
|
||||
uint64_t
|
||||
mtl_texture_get_gpu_resource_id(mtl_texture *texture)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLTexture> tex = (id<MTLTexture>)texture;
|
||||
return (uint64_t)[tex gpuResourceID]._impl;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO_KOSMICKRISP This should be part of the mapping */
|
||||
static uint32_t
|
||||
mtl_texture_view_type(uint32_t type, uint8_t sample_count)
|
||||
{
|
||||
switch (type) {
|
||||
case VK_IMAGE_VIEW_TYPE_1D:
|
||||
return MTLTextureType1D;
|
||||
case VK_IMAGE_VIEW_TYPE_1D_ARRAY:
|
||||
return MTLTextureType1DArray;
|
||||
case VK_IMAGE_VIEW_TYPE_2D:
|
||||
return sample_count > 1u ? MTLTextureType2DMultisample : MTLTextureType2D;;
|
||||
case VK_IMAGE_VIEW_TYPE_CUBE:
|
||||
return MTLTextureTypeCube;
|
||||
case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY:
|
||||
return MTLTextureTypeCubeArray;
|
||||
case VK_IMAGE_VIEW_TYPE_2D_ARRAY:
|
||||
return sample_count > 1u ? MTLTextureType2DMultisampleArray : MTLTextureType2DArray;
|
||||
case VK_IMAGE_VIEW_TYPE_3D:
|
||||
return MTLTextureType3D;
|
||||
default:
|
||||
assert(false && "Unsupported VkViewType");
|
||||
return MTLTextureType1D;
|
||||
}
|
||||
}
|
||||
|
||||
static MTLTextureSwizzle
|
||||
mtl_texture_swizzle(enum pipe_swizzle swizzle)
|
||||
{
|
||||
const MTLTextureSwizzle map[] =
|
||||
{
|
||||
[PIPE_SWIZZLE_X] = MTLTextureSwizzleRed,
|
||||
[PIPE_SWIZZLE_Y] = MTLTextureSwizzleGreen,
|
||||
[PIPE_SWIZZLE_Z] = MTLTextureSwizzleBlue,
|
||||
[PIPE_SWIZZLE_W] = MTLTextureSwizzleAlpha,
|
||||
[PIPE_SWIZZLE_0] = MTLTextureSwizzleZero,
|
||||
[PIPE_SWIZZLE_1] = MTLTextureSwizzleOne,
|
||||
};
|
||||
|
||||
return map[swizzle];
|
||||
}
|
||||
|
||||
mtl_texture *
|
||||
mtl_new_texture_view_with(mtl_texture *texture, const struct kk_view_layout *layout)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLTexture> tex = (id<MTLTexture>)texture;
|
||||
MTLTextureType type = mtl_texture_view_type(layout->view_type, layout->sample_count_sa);
|
||||
NSRange levels = NSMakeRange(layout->base_level, layout->num_levels);
|
||||
NSRange slices = NSMakeRange(layout->base_array_layer, layout->array_len);
|
||||
MTLTextureSwizzleChannels swizzle = MTLTextureSwizzleChannelsMake(mtl_texture_swizzle(layout->swizzle.red),
|
||||
mtl_texture_swizzle(layout->swizzle.green),
|
||||
mtl_texture_swizzle(layout->swizzle.blue),
|
||||
mtl_texture_swizzle(layout->swizzle.alpha));
|
||||
return [tex newTextureViewWithPixelFormat:layout->format.mtl textureType:type levels:levels slices:slices swizzle:swizzle];
|
||||
}
|
||||
}
|
||||
|
||||
mtl_texture *
|
||||
mtl_new_texture_view_with_no_swizzle(mtl_texture *texture, const struct kk_view_layout *layout)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLTexture> tex = (id<MTLTexture>)texture;
|
||||
MTLTextureType type = mtl_texture_view_type(layout->view_type, layout->sample_count_sa);
|
||||
NSRange levels = NSMakeRange(layout->base_level, layout->num_levels);
|
||||
NSRange slices = NSMakeRange(layout->base_array_layer, layout->array_len);
|
||||
return [tex newTextureViewWithPixelFormat:layout->format.mtl textureType:type levels:levels slices:slices];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_texture_get_bytes(mtl_texture *texture, void *host_ptr,
|
||||
struct mtl_texture_memory_copy *data)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLTexture> tex = (id<MTLTexture>)texture;
|
||||
MTLRegion region = MTLRegionMake3D(data->image_origin.x, data->image_origin.y, data->image_origin.z,
|
||||
data->image_size.x, data->image_size.y, data->image_size.z);
|
||||
return [tex getBytes:host_ptr
|
||||
bytesPerRow:data->buffer_stride_B
|
||||
bytesPerImage:data->buffer_2d_image_size_B
|
||||
fromRegion:region
|
||||
mipmapLevel:data->image_level
|
||||
slice:data->image_slice];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mtl_texture_replace_region(mtl_texture *texture, const void *host_ptr,
|
||||
struct mtl_texture_memory_copy *data)
|
||||
{
|
||||
@autoreleasepool {
|
||||
id<MTLTexture> tex = (id<MTLTexture>)texture;
|
||||
MTLRegion region = MTLRegionMake3D(data->image_origin.x, data->image_origin.y, data->image_origin.z,
|
||||
data->image_size.x, data->image_size.y, data->image_size.z);
|
||||
return [tex replaceRegion:region
|
||||
mipmapLevel:data->image_level
|
||||
slice:data->image_slice
|
||||
withBytes:host_ptr
|
||||
bytesPerRow:data->buffer_stride_B
|
||||
bytesPerImage:data->buffer_2d_image_size_B];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Structures and enums found in this file are a 1-1 mapping of Metal's
|
||||
* equivalents
|
||||
*/
|
||||
|
||||
#ifndef KK_MTL_TYPES_H
|
||||
#define KK_MTL_TYPES_H 1
|
||||
|
||||
#include <stddef.h> /* For size_t definition */
|
||||
|
||||
/** HANDLES */
|
||||
typedef void mtl_device;
|
||||
typedef void mtl_heap;
|
||||
typedef void mtl_buffer;
|
||||
typedef void mtl_texture;
|
||||
typedef void mtl_command_queue;
|
||||
typedef void mtl_command_buffer;
|
||||
typedef void mtl_command_encoder;
|
||||
typedef void mtl_blit_encoder;
|
||||
typedef void mtl_compute_encoder;
|
||||
typedef void mtl_render_encoder;
|
||||
typedef void mtl_event;
|
||||
typedef void mtl_shared_event;
|
||||
typedef void mtl_sampler_descriptor;
|
||||
typedef void mtl_sampler;
|
||||
typedef void mtl_compute_pipeline_state;
|
||||
typedef void mtl_library;
|
||||
typedef void mtl_render_pipeline_state;
|
||||
typedef void mtl_function;
|
||||
typedef void mtl_resource;
|
||||
typedef void mtl_render_pass_descriptor;
|
||||
typedef void mtl_render_pipeline_descriptor;
|
||||
typedef void mtl_fence;
|
||||
typedef void mtl_stencil_descriptor;
|
||||
typedef void mtl_depth_stencil_descriptor;
|
||||
typedef void mtl_depth_stencil_state;
|
||||
typedef void mtl_render_pass_attachment_descriptor;
|
||||
typedef void mtl_residency_set;
|
||||
typedef void mtl_allocation;
|
||||
|
||||
/** ENUMS */
|
||||
enum mtl_cpu_cache_mode {
|
||||
MTL_CPU_CACHE_MODE_DEFAULT_CACHE = 0,
|
||||
MTL_CPU_CACHE_MODE_WRITE_COMBINED = 1,
|
||||
};
|
||||
|
||||
enum mtl_storage_mode {
|
||||
MTL_STORAGE_MODE_SHARED = 0,
|
||||
MTL_STORAGE_MODE_MANAGED = 1,
|
||||
MTL_STORAGE_MODE_PRIVATE = 2,
|
||||
MTL_STORAGE_MODE_MEMORYLESS = 3,
|
||||
};
|
||||
|
||||
enum mtl_hazard_tracking_mode {
|
||||
MTL_HAZARD_TRACKING_MODE_DFEAULT = 0,
|
||||
MTL_HAZARD_TRACKING_MODE_UNTRACKED = 1,
|
||||
MTL_HAZARD_TRACKING_MODE_TRACKED = 2,
|
||||
};
|
||||
|
||||
#define MTL_RESOURCE_CPU_CACHE_MODE_SHIFT 0
|
||||
#define MTL_RESOURCE_STORAGE_MODE_SHIFT 4
|
||||
#define MTL_RESOURCE_HAZARD_TRACKING_MODE_SHIFT 8
|
||||
enum mtl_resource_options {
|
||||
MTL_RESOURCE_CPU_CACHE_MODE_DEFAULT_CACHE =
|
||||
MTL_CPU_CACHE_MODE_DEFAULT_CACHE << MTL_RESOURCE_CPU_CACHE_MODE_SHIFT,
|
||||
MTL_RESOURCE_CPU_CACHE_MODE_WRITE_COMBINED =
|
||||
MTL_CPU_CACHE_MODE_WRITE_COMBINED << MTL_RESOURCE_CPU_CACHE_MODE_SHIFT,
|
||||
MTL_RESOURCE_STORAGE_MODE_SHARED = MTL_STORAGE_MODE_SHARED
|
||||
<< MTL_RESOURCE_STORAGE_MODE_SHIFT,
|
||||
MTL_RESOURCE_STORAGE_MODE_PRIVATE = MTL_STORAGE_MODE_PRIVATE
|
||||
<< MTL_RESOURCE_STORAGE_MODE_SHIFT,
|
||||
MTL_RESOURCE_TRACKING_MODE_DEFAULT =
|
||||
MTL_HAZARD_TRACKING_MODE_DFEAULT
|
||||
<< MTL_RESOURCE_HAZARD_TRACKING_MODE_SHIFT,
|
||||
MTL_RESOURCE_TRACKING_MODE_UNTRACKED =
|
||||
MTL_HAZARD_TRACKING_MODE_UNTRACKED
|
||||
<< MTL_RESOURCE_HAZARD_TRACKING_MODE_SHIFT,
|
||||
MTL_RESOURCE_TRACKING_MODE_TRACKED =
|
||||
MTL_HAZARD_TRACKING_MODE_TRACKED
|
||||
<< MTL_RESOURCE_HAZARD_TRACKING_MODE_SHIFT,
|
||||
};
|
||||
|
||||
enum mtl_blit_options {
|
||||
MTL_BLIT_OPTION_NONE = 0,
|
||||
MTL_BLIT_OPTION_DEPTH_FROM_DEPTH_STENCIL = 1 << 0,
|
||||
MTL_BLIT_OPTION_STENCIL_FROM_DEPTH_STENCIL = 1 << 1,
|
||||
};
|
||||
|
||||
enum mtl_resource_usage {
|
||||
MTL_RESOURCE_USAGE_READ = 1 << 0,
|
||||
MTL_RESOURCE_USAGE_WRITE = 1 << 1,
|
||||
};
|
||||
|
||||
enum mtl_primitive_type {
|
||||
MTL_PRIMITIVE_TYPE_POINT = 0,
|
||||
MTL_PRIMITIVE_TYPE_LINE = 1,
|
||||
MTL_PRIMITIVE_TYPE_LINE_STRIP = 2,
|
||||
MTL_PRIMITIVE_TYPE_TRIANGLE = 3,
|
||||
MTL_PRIMITIVE_TYPE_TRIANGLE_STRIP = 4,
|
||||
};
|
||||
|
||||
enum mtl_primitive_topology_class {
|
||||
MTL_PRIMITIVE_TOPOLOGY_CLASS_UNSPECIFIED = 0,
|
||||
MTL_PRIMITIVE_TOPOLOGY_CLASS_POINT = 1,
|
||||
MTL_PRIMITIVE_TOPOLOGY_CLASS_LINE = 2,
|
||||
MTL_PRIMITIVE_TOPOLOGY_CLASS_TRIANGLE = 3,
|
||||
};
|
||||
|
||||
enum mtl_texture_type {
|
||||
MTL_TEXTURE_TYPE_1D = 0u,
|
||||
MTL_TEXTURE_TYPE_1D_ARRAY = 1u,
|
||||
MTL_TEXTURE_TYPE_2D = 2u,
|
||||
MTL_TEXTURE_TYPE_2D_ARRAY = 3u,
|
||||
MTL_TEXTURE_TYPE_2D_MULTISAMPLE = 4u,
|
||||
MTL_TEXTURE_TYPE_CUBE = 5u,
|
||||
MTL_TEXTURE_TYPE_CUBE_ARRAY = 6u,
|
||||
MTL_TEXTURE_TYPE_3D = 7u,
|
||||
MTL_TEXTURE_TYPE_2D_ARRAY_MULTISAMPLE = 8u,
|
||||
MTL_TEXTURE_TYPE_TEXTURE_BUFFER = 9u,
|
||||
};
|
||||
|
||||
enum mtl_texture_usage {
|
||||
MTL_TEXTURE_USAGE_UNKNOWN = 0x0000,
|
||||
MTL_TEXTURE_USAGE_SHADER_READ = 0x0001,
|
||||
MTL_TEXTURE_USAGE_SHADER_WRITE = 0X0002,
|
||||
MTL_TEXTURE_USAGE_RENDER_TARGET = 0X0004,
|
||||
MTL_TEXTURE_USAGE_PIXEL_FORMAT_VIEW = 0X0010,
|
||||
MTL_TEXTURE_USAGE_SHADER_ATOMIC = 0X0020,
|
||||
};
|
||||
|
||||
enum mtl_load_action {
|
||||
MTL_LOAD_ACTION_DONT_CARE = 0u,
|
||||
MTL_LOAD_ACTION_LOAD = 1u,
|
||||
MTL_LOAD_ACTION_CLEAR = 2u,
|
||||
};
|
||||
|
||||
enum mtl_store_action {
|
||||
MTL_STORE_ACTION_DONT_CARE = 0u,
|
||||
MTL_STORE_ACTION_STORE = 1u,
|
||||
MTL_STORE_ACTION_MULTISAMPLE_RESOLVE = 2u,
|
||||
MTL_STORE_ACTION_STORE_AND_MULTISAMPLE_RESOLVE = 3u,
|
||||
MTL_STORE_ACTION_UNKNOWN = 4u,
|
||||
MTL_STORE_ACTION_CUSTOM_SAMPLE_DEPTH_STORE = 5u,
|
||||
};
|
||||
|
||||
enum mtl_texture_swizzle {
|
||||
MTL_TEXTURE_SWIZZLE_ZERO = 0,
|
||||
MTL_TEXTURE_SWIZZLE_ONE = 1,
|
||||
MTL_TEXTURE_SWIZZLE_RED = 2,
|
||||
MTL_TEXTURE_SWIZZLE_GREEN = 3,
|
||||
MTL_TEXTURE_SWIZZLE_BLUE = 4,
|
||||
MTL_TEXTURE_SWIZZLE_ALPHA = 5,
|
||||
};
|
||||
|
||||
enum mtl_index_type {
|
||||
MTL_INDEX_TYPE_UINT16 = 0,
|
||||
MTL_INDEX_TYPE_UINT32 = 1,
|
||||
};
|
||||
|
||||
enum mtl_sampler_address_mode {
|
||||
MTL_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE = 0,
|
||||
MTL_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE = 1,
|
||||
MTL_SAMPLER_ADDRESS_MODE_REPEAT = 2,
|
||||
MTL_SAMPLER_ADDRESS_MODE_MIRROR_REPEAT = 3,
|
||||
MTL_SAMPLER_ADDRESS_MODE_CLAMP_TO_ZERO = 4,
|
||||
MTL_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER_COLOR = 5,
|
||||
};
|
||||
|
||||
enum mtl_sampler_border_color {
|
||||
MTL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK = 0,
|
||||
MTL_SAMPLER_BORDER_COLOR_OPAQUE_BLACK = 1,
|
||||
MTL_SAMPLER_BORDER_COLOR_OPAQUE_WHITE = 2,
|
||||
};
|
||||
|
||||
enum mtl_sampler_min_mag_filter {
|
||||
MTL_SAMPLER_MIN_MAG_FILTER_NEAREST = 0,
|
||||
MTL_SAMPLER_MIN_MAG_FILTER_LINEAR = 1,
|
||||
};
|
||||
|
||||
enum mtl_sampler_mip_filter {
|
||||
MTL_SAMPLER_MIP_FILTER_NOT_MIP_MAPPED = 0,
|
||||
MTL_SAMPLER_MIP_FILTER_NEAREST = 1,
|
||||
MTL_SAMPLER_MIP_FILTER_LINEAR = 2,
|
||||
};
|
||||
|
||||
enum mtl_compare_function {
|
||||
MTL_COMPARE_FUNCTION_NEVER = 0,
|
||||
MTL_COMPARE_FUNCTION_LESS = 1,
|
||||
MTL_COMPARE_FUNCTION_EQUAL = 2,
|
||||
MTL_COMPARE_FUNCTION_LESS_EQUAL = 3,
|
||||
MTL_COMPARE_FUNCTION_GREATER = 4,
|
||||
MTL_COMPARE_FUNCTION_NOT_EQUAL = 5,
|
||||
MTL_COMPARE_FUNCTION_GREATER_EQUAL = 6,
|
||||
MTL_COMPARE_FUNCTION_ALWAYS = 7,
|
||||
};
|
||||
|
||||
enum mtl_winding {
|
||||
MTL_WINDING_CLOCKWISE = 0,
|
||||
MTL_WINDING_COUNTER_CLOCKWISE = 1,
|
||||
};
|
||||
|
||||
enum mtl_cull_mode {
|
||||
MTL_CULL_MODE_NONE = 0,
|
||||
MTL_CULL_MODE_FRONT = 1,
|
||||
MTL_CULL_MODE_BACK = 2,
|
||||
};
|
||||
|
||||
enum mtl_visibility_result_mode {
|
||||
MTL_VISIBILITY_RESULT_MODE_DISABLED = 0,
|
||||
MTL_VISIBILITY_RESULT_MODE_BOOLEAN = 1,
|
||||
MTL_VISIBILITY_RESULT_MODE_COUNTING = 2,
|
||||
};
|
||||
|
||||
enum mtl_depth_clip_mode {
|
||||
MTL_DEPTH_CLIP_MODE_CLIP = 0,
|
||||
MTL_DEPTH_CLIP_MODE_CLAMP = 1,
|
||||
};
|
||||
|
||||
enum mtl_barrier_scope {
|
||||
MTL_BARRIER_SCOPE_BUFFERS = 1 << 0,
|
||||
MTL_BARRIER_SCOPE_TEXTURES = 1 << 1,
|
||||
MTL_BARRIER_SCOPE_RENDER_TARGETS = 1 << 2,
|
||||
};
|
||||
|
||||
/** STRUCTURES */
|
||||
struct mtl_range {
|
||||
size_t offset;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
struct mtl_origin {
|
||||
size_t x, y, z;
|
||||
};
|
||||
|
||||
struct mtl_size {
|
||||
size_t x, y, z;
|
||||
};
|
||||
|
||||
struct mtl_sample_position {
|
||||
float x, y;
|
||||
};
|
||||
|
||||
struct mtl_viewport {
|
||||
double originX, originY, width, height, znear, zfar;
|
||||
};
|
||||
|
||||
struct mtl_clear_color {
|
||||
union {
|
||||
struct {
|
||||
double red, green, blue, alpha;
|
||||
};
|
||||
double channel[4];
|
||||
};
|
||||
};
|
||||
|
||||
struct mtl_scissor_rect {
|
||||
size_t x, y, width, height;
|
||||
};
|
||||
|
||||
struct mtl_texture_swizzle_channels {
|
||||
enum mtl_texture_swizzle red;
|
||||
enum mtl_texture_swizzle green;
|
||||
enum mtl_texture_swizzle blue;
|
||||
enum mtl_texture_swizzle alpha;
|
||||
};
|
||||
|
||||
struct mtl_buffer_image_copy {
|
||||
struct mtl_size image_size;
|
||||
struct mtl_origin image_origin;
|
||||
mtl_buffer *buffer;
|
||||
mtl_texture *image;
|
||||
size_t buffer_offset_B;
|
||||
size_t buffer_stride_B;
|
||||
size_t buffer_2d_image_size_B;
|
||||
size_t image_slice;
|
||||
size_t image_level;
|
||||
enum mtl_blit_options options;
|
||||
};
|
||||
|
||||
struct mtl_texture_memory_copy {
|
||||
struct mtl_size image_size;
|
||||
struct mtl_origin image_origin;
|
||||
size_t buffer_stride_B;
|
||||
size_t buffer_2d_image_size_B;
|
||||
size_t image_slice;
|
||||
size_t image_level;
|
||||
};
|
||||
|
||||
#endif /* KK_MTL_TYPES_H */
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_bridge.h"
|
||||
|
||||
mtl_texture *
|
||||
mtl_drawable_get_texture(void *drawable_ptr)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *
|
||||
mtl_retain(void *handle)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
mtl_release(void *handle)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_buffer.h"
|
||||
|
||||
uint64_t
|
||||
mtl_buffer_get_length(mtl_buffer *buffer)
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
mtl_buffer_get_gpu_address(mtl_buffer *buffer)
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
|
||||
void *
|
||||
mtl_get_contents(mtl_buffer *buffer)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mtl_texture *
|
||||
mtl_new_texture_with_descriptor_linear(mtl_buffer *buffer,
|
||||
const struct kk_image_layout *layout,
|
||||
uint64_t offset)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_command_buffer.h"
|
||||
|
||||
void
|
||||
mtl_encode_signal_event(mtl_command_buffer *cmd_buf_handle,
|
||||
mtl_event *event_handle, uint64_t value)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_encode_wait_for_event(mtl_command_buffer *cmd_buf_handle,
|
||||
mtl_event *event_handle, uint64_t value)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_add_completed_handler(mtl_command_buffer *cmd, void (*callback)(void *data),
|
||||
void *data)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_command_buffer_commit(mtl_command_buffer *cmd_buf)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_present_drawable(mtl_command_buffer *cmd_buf, void *drawable)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_command_queue.h"
|
||||
|
||||
mtl_command_queue *
|
||||
mtl_new_command_queue(mtl_device *device, uint32_t cmd_buffer_count)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mtl_command_buffer *
|
||||
mtl_new_command_buffer(mtl_command_queue *cmd_queue)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
mtl_command_queue_add_residency_set(mtl_command_queue *cmd_queue,
|
||||
mtl_residency_set *set)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_command_queue_remove_residency_set(mtl_command_queue *cmd_queue,
|
||||
mtl_residency_set *set)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_compute_state.h"
|
||||
|
||||
mtl_compute_pipeline_state *
|
||||
mtl_new_compute_pipeline_state(mtl_device *device, mtl_function *function,
|
||||
uint64_t max_total_threads_per_threadgroup)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_device.h"
|
||||
|
||||
/* Device creation */
|
||||
mtl_device *
|
||||
mtl_device_create(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Device operations */
|
||||
void
|
||||
mtl_start_gpu_capture(mtl_device *mtl_dev_handle, const char *directory)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_stop_gpu_capture(void)
|
||||
{
|
||||
}
|
||||
|
||||
/* Device feature query */
|
||||
void
|
||||
mtl_device_get_name(mtl_device *dev, char buffer[256])
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_device_get_architecture_name(mtl_device *dev, char buffer[256])
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t
|
||||
mtl_device_get_peer_group_id(mtl_device *dev)
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
mtl_device_get_peer_index(mtl_device *dev)
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
mtl_device_get_registry_id(mtl_device *dev)
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
|
||||
bool
|
||||
mtl_device_supports_sample_count(mtl_device *dev, uint32_t sample_count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
struct mtl_size
|
||||
mtl_device_max_threads_per_threadgroup(mtl_device *dev)
|
||||
{
|
||||
return (struct mtl_size){};
|
||||
}
|
||||
|
||||
uint32_t
|
||||
mtl_device_max_threadgroup_memory_length(mtl_device *dev)
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
mtl_device_max_buffer_length(mtl_device *dev)
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
mtl_device_recommended_max_working_set_size(mtl_device *dev)
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
mtl_device_current_allocated_size(mtl_device *dev)
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
|
||||
/* Timestamp query */
|
||||
uint64_t
|
||||
mtl_device_get_gpu_timestamp(mtl_device *dev)
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
|
||||
/* Resource queries */
|
||||
void
|
||||
mtl_heap_buffer_size_and_align_with_length(mtl_device *device, uint64_t *size_B,
|
||||
uint64_t *align_B)
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t
|
||||
mtl_minimum_linear_texture_alignment_for_pixel_format(
|
||||
mtl_device *device, enum mtl_pixel_format format)
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
|
||||
void
|
||||
mtl_heap_texture_size_and_align_with_descriptor(mtl_device *device,
|
||||
struct kk_image_layout *layout,
|
||||
uint64_t *size_B,
|
||||
uint64_t *align_B)
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t
|
||||
mtl_sparse_tile_size_in_bytes(mtl_device *device)
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
|
||||
struct mtl_size
|
||||
mtl_sparse_tile_size(mtl_device *device, struct kk_image_layout *layout)
|
||||
{
|
||||
return (struct mtl_size){};
|
||||
}
|
||||
|
||||
struct mtl_size
|
||||
mtl_sparse_tile_count(mtl_device *device, struct kk_image_layout *layout,
|
||||
struct mtl_size tile_size)
|
||||
{
|
||||
return (struct mtl_size){};
|
||||
}
|
||||
|
||||
/* Resource creation */
|
||||
mtl_buffer *
|
||||
mtl_new_buffer_with_bytes_no_copy(mtl_device *device, void *ptr,
|
||||
uint64_t size_B)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_encoder.h"
|
||||
|
||||
/* Common encoder utils */
|
||||
void
|
||||
mtl_end_encoding(void *encoder)
|
||||
{
|
||||
}
|
||||
|
||||
/* MTLBlitEncoder */
|
||||
mtl_blit_encoder *
|
||||
mtl_new_blit_command_encoder(mtl_command_buffer *cmd_buffer)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
mtl_blit_update_fence(mtl_blit_encoder *encoder, mtl_fence *fence)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_blit_wait_for_fence(mtl_blit_encoder *encoder, mtl_fence *fence)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_copy_from_buffer_to_buffer(mtl_blit_encoder *blit_enc_handle,
|
||||
mtl_buffer *src_buf, size_t src_offset,
|
||||
mtl_buffer *dst_buf, size_t dst_offset,
|
||||
size_t size)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_copy_from_buffer_to_texture(mtl_blit_encoder *blit_enc_handle,
|
||||
struct mtl_buffer_image_copy *data)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_copy_from_texture_to_buffer(mtl_blit_encoder *blit_enc_handle,
|
||||
struct mtl_buffer_image_copy *data)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_copy_from_texture_to_texture(mtl_blit_encoder *blit_enc_handle,
|
||||
mtl_texture *src_tex_handle, size_t src_slice,
|
||||
size_t src_level, struct mtl_origin src_origin,
|
||||
struct mtl_size src_size,
|
||||
mtl_texture *dst_tex_handle, size_t dst_slice,
|
||||
size_t dst_level, struct mtl_origin dst_origin)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_memory_barrier_with_scope(mtl_compute_encoder *encoder,
|
||||
enum mtl_barrier_scope scope)
|
||||
{
|
||||
}
|
||||
|
||||
/* MTLComputeEncoder */
|
||||
mtl_compute_encoder *
|
||||
mtl_new_compute_command_encoder(mtl_command_buffer *cmd_buffer)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
mtl_compute_update_fence(mtl_compute_encoder *encoder, mtl_fence *fence)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_compute_wait_for_fence(mtl_compute_encoder *encoder, mtl_fence *fence)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_compute_set_pipeline_state(mtl_compute_encoder *encoder,
|
||||
mtl_compute_pipeline_state *state_handle)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_compute_set_buffer(mtl_compute_encoder *encoder, mtl_buffer *buffer,
|
||||
size_t offset, size_t index)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_compute_use_heaps(mtl_compute_encoder *encoder, mtl_heap **heaps,
|
||||
uint32_t count)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_dispatch_threads(mtl_compute_encoder *encoder, struct mtl_size grid_size,
|
||||
struct mtl_size local_size)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_dispatch_threadgroups_with_indirect_buffer(mtl_compute_encoder *encoder,
|
||||
mtl_buffer *buffer,
|
||||
uint32_t offset,
|
||||
struct mtl_size local_size)
|
||||
{
|
||||
}
|
||||
|
||||
/* MTLRenderEncoder */
|
||||
mtl_render_encoder *
|
||||
mtl_new_render_command_encoder_with_descriptor(
|
||||
mtl_command_buffer *command_buffer, mtl_render_pass_descriptor *descriptor)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_update_fence(mtl_render_encoder *encoder, mtl_fence *fence)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_wait_for_fence(mtl_render_encoder *encoder, mtl_fence *fence)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_viewports(mtl_render_encoder *encoder, struct mtl_viewport *viewports,
|
||||
uint32_t count)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_scissor_rects(mtl_render_encoder *encoder,
|
||||
struct mtl_scissor_rect *scissor_rects, uint32_t count)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_set_pipeline_state(mtl_render_encoder *encoder,
|
||||
mtl_render_pipeline_state *pipeline)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_depth_stencil_state(mtl_render_encoder *encoder,
|
||||
mtl_depth_stencil_state *state)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_stencil_references(mtl_render_encoder *encoder, uint32_t front,
|
||||
uint32_t back)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_front_face_winding(mtl_render_encoder *encoder,
|
||||
enum mtl_winding winding)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_cull_mode(mtl_render_encoder *encoder, enum mtl_cull_mode mode)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_visibility_result_mode(mtl_render_encoder *encoder,
|
||||
enum mtl_visibility_result_mode mode,
|
||||
size_t offset)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_depth_bias(mtl_render_encoder *encoder, float depth_bias,
|
||||
float slope_scale, float clamp)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_depth_clip_mode(mtl_render_encoder *encoder,
|
||||
enum mtl_depth_clip_mode mode)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_vertex_amplification_count(mtl_render_encoder *encoder,
|
||||
uint32_t *layer_ids, uint32_t id_count)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_vertex_buffer(mtl_render_encoder *encoder, mtl_buffer *buffer,
|
||||
uint32_t offset, uint32_t index)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_vertex_bytes(mtl_render_encoder *encoder, const void *bytes,
|
||||
uint32_t length, uint32_t index)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_fragment_buffer(mtl_render_encoder *encoder, mtl_buffer *buffer,
|
||||
uint32_t offset, uint32_t index)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_set_fragment_bytes(mtl_render_encoder *encoder, const void *bytes,
|
||||
uint32_t length, uint32_t index)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_draw_primitives(mtl_render_encoder *encoder,
|
||||
enum mtl_primitive_type primitve_type, uint32_t vertexStart,
|
||||
uint32_t vertexCount, uint32_t instanceCount,
|
||||
uint32_t baseInstance)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_draw_indexed_primitives(
|
||||
mtl_render_encoder *encoder, enum mtl_primitive_type primitve_type,
|
||||
uint32_t index_count, enum mtl_index_type index_type,
|
||||
mtl_buffer *index_buffer, uint32_t index_buffer_offset,
|
||||
uint32_t instance_count, int32_t base_vertex, uint32_t base_instance)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_draw_primitives_indirect(mtl_render_encoder *encoder,
|
||||
enum mtl_primitive_type primitve_type,
|
||||
mtl_buffer *indirect_buffer,
|
||||
uint64_t indirect_buffer_offset)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_draw_indexed_primitives_indirect(mtl_render_encoder *encoder,
|
||||
enum mtl_primitive_type primitve_type,
|
||||
enum mtl_index_type index_type,
|
||||
mtl_buffer *index_buffer,
|
||||
uint32_t index_buffer_offset,
|
||||
mtl_buffer *indirect_buffer,
|
||||
uint64_t indirect_buffer_offset)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_use_resource(mtl_compute_encoder *encoder, mtl_resource *res_handle,
|
||||
uint32_t usage)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_use_resources(mtl_render_encoder *encoder,
|
||||
mtl_resource **resource_handles, uint32_t count,
|
||||
enum mtl_resource_usage usage)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_use_heaps(mtl_render_encoder *encoder, mtl_heap **heaps,
|
||||
uint32_t count)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_heap.h"
|
||||
|
||||
/* Creation */
|
||||
mtl_heap *
|
||||
mtl_new_heap(mtl_device *device, uint64_t size,
|
||||
enum mtl_resource_options resource_options)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Utils */
|
||||
uint64_t
|
||||
mtl_heap_get_size(mtl_heap *heap)
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
|
||||
/* Allocation from heap */
|
||||
mtl_buffer *
|
||||
mtl_new_buffer_with_length(mtl_heap *heap, uint64_t size_B, uint64_t offset_B)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mtl_texture *
|
||||
mtl_new_texture_with_descriptor(mtl_heap *heap,
|
||||
const struct kk_image_layout *layout,
|
||||
uint64_t offset)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_library.h"
|
||||
|
||||
mtl_library *
|
||||
mtl_new_library(mtl_device *device, const char *src)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mtl_function *
|
||||
mtl_new_function_with_name(mtl_library *lib, const char *entry_point)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_render_state.h"
|
||||
|
||||
#include "mtl_format.h"
|
||||
|
||||
/* TODO_KOSMICKRISP Remove */
|
||||
#include "vulkan/vulkan.h"
|
||||
|
||||
/* Render pass descriptor */
|
||||
mtl_render_pass_descriptor *
|
||||
mtl_new_render_pass_descriptor(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mtl_render_pass_attachment_descriptor *
|
||||
mtl_render_pass_descriptor_get_color_attachment(
|
||||
mtl_render_pass_descriptor *descriptor, uint32_t index)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mtl_render_pass_attachment_descriptor *
|
||||
mtl_render_pass_descriptor_get_depth_attachment(
|
||||
mtl_render_pass_descriptor *descriptor)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mtl_render_pass_attachment_descriptor *
|
||||
mtl_render_pass_descriptor_get_stencil_attachment(
|
||||
mtl_render_pass_descriptor *descriptor)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_attachment_descriptor_set_texture(
|
||||
mtl_render_pass_attachment_descriptor *descriptor, mtl_texture *texture)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_attachment_descriptor_set_level(
|
||||
mtl_render_pass_attachment_descriptor *descriptor, uint32_t level)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_attachment_descriptor_set_slice(
|
||||
mtl_render_pass_attachment_descriptor *descriptor, uint32_t slice)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_attachment_descriptor_set_load_action(
|
||||
mtl_render_pass_attachment_descriptor *descriptor,
|
||||
enum mtl_load_action action)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_attachment_descriptor_set_store_action(
|
||||
mtl_render_pass_attachment_descriptor *descriptor,
|
||||
enum mtl_store_action action)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_attachment_descriptor_set_clear_color(
|
||||
mtl_render_pass_attachment_descriptor *descriptor,
|
||||
struct mtl_clear_color clear_color)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_attachment_descriptor_set_clear_depth(
|
||||
mtl_render_pass_attachment_descriptor *descriptor, double depth)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_attachment_descriptor_set_clear_stencil(
|
||||
mtl_render_pass_attachment_descriptor *descriptor, uint32_t stencil)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_descriptor_set_render_target_array_length(
|
||||
mtl_render_pass_descriptor *descriptor, uint32_t length)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_descriptor_set_render_target_width(
|
||||
mtl_render_pass_descriptor *descriptor, uint32_t width)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_descriptor_set_render_target_height(
|
||||
mtl_render_pass_descriptor *descriptor, uint32_t height)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_descriptor_set_default_raster_sample_count(
|
||||
mtl_render_pass_descriptor *descriptor, uint32_t sample_count)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_descriptor_set_sample_positions(
|
||||
mtl_render_pass_descriptor *descriptor,
|
||||
const struct mtl_sample_position *positions, uint32_t count)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pass_descriptor_set_visibility_buffer(
|
||||
mtl_render_pass_descriptor *descriptor, mtl_buffer *visibility_buffer)
|
||||
{
|
||||
}
|
||||
|
||||
/* Render pipeline descriptor */
|
||||
mtl_render_pipeline_descriptor *
|
||||
mtl_new_render_pipeline_descriptor(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pipeline_descriptor_set_vertex_shader(
|
||||
mtl_render_pass_descriptor *descriptor, mtl_function *shader)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pipeline_descriptor_set_fragment_shader(
|
||||
mtl_render_pass_descriptor *descriptor, mtl_function *shader)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pipeline_descriptor_set_input_primitive_topology(
|
||||
mtl_render_pass_descriptor *descriptor,
|
||||
enum mtl_primitive_topology_class topology_class)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pipeline_descriptor_set_color_attachment_format(
|
||||
mtl_render_pass_descriptor *descriptor, uint8_t index,
|
||||
enum mtl_pixel_format format)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pipeline_descriptor_set_depth_attachment_format(
|
||||
mtl_render_pass_descriptor *descriptor, enum mtl_pixel_format format)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pipeline_descriptor_set_stencil_attachment_format(
|
||||
mtl_render_pass_descriptor *descriptor, enum mtl_pixel_format format)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pipeline_descriptor_set_raster_sample_count(
|
||||
mtl_render_pass_descriptor *descriptor, uint32_t sample_count)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pipeline_descriptor_set_alpha_to_coverage(
|
||||
mtl_render_pass_descriptor *descriptor, bool enabled)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pipeline_descriptor_set_alpha_to_one(
|
||||
mtl_render_pass_descriptor *descriptor, bool enabled)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pipeline_descriptor_set_rasterization_enabled(
|
||||
mtl_render_pass_descriptor *descriptor, bool enabled)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_render_pipeline_descriptor_set_max_vertex_amplification_count(
|
||||
mtl_render_pass_descriptor *descriptor, uint32_t count)
|
||||
{
|
||||
}
|
||||
|
||||
/* Render pipeline */
|
||||
mtl_render_pipeline_state *
|
||||
mtl_new_render_pipeline(mtl_device *device,
|
||||
mtl_render_pass_descriptor *descriptor)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Stencil descriptor */
|
||||
mtl_stencil_descriptor *
|
||||
mtl_new_stencil_descriptor(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
mtl_stencil_descriptor_set_stencil_failure_operation(
|
||||
mtl_stencil_descriptor *descriptor, enum VkStencilOp op)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_stencil_descriptor_set_depth_failure_operation(
|
||||
mtl_stencil_descriptor *descriptor, enum VkStencilOp op)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_stencil_descriptor_set_depth_stencil_pass_operation(
|
||||
mtl_stencil_descriptor *descriptor, enum VkStencilOp op)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_stencil_descriptor_set_stencil_compare_function(
|
||||
mtl_stencil_descriptor *descriptor, enum VkCompareOp op)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_stencil_descriptor_set_read_mask(mtl_stencil_descriptor *descriptor,
|
||||
uint32_t mask)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_stencil_descriptor_set_write_mask(mtl_stencil_descriptor *descriptor,
|
||||
uint32_t mask)
|
||||
{
|
||||
}
|
||||
|
||||
/* Depth stencil descriptor */
|
||||
mtl_depth_stencil_descriptor *
|
||||
mtl_new_depth_stencil_descriptor(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
mtl_depth_stencil_descriptor_set_depth_compare_function(
|
||||
mtl_depth_stencil_descriptor *descriptor, enum VkCompareOp op)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_depth_stencil_descriptor_set_depth_write_enabled(
|
||||
mtl_depth_stencil_descriptor *descriptor, bool enable_write)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_depth_stencil_descriptor_set_back_face_stencil(
|
||||
mtl_depth_stencil_descriptor *descriptor,
|
||||
mtl_stencil_descriptor *stencil_descriptor)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_depth_stencil_descriptor_set_front_face_stencil(
|
||||
mtl_depth_stencil_descriptor *descriptor,
|
||||
mtl_stencil_descriptor *stencil_descriptor)
|
||||
{
|
||||
}
|
||||
|
||||
/* Depth stencil state */
|
||||
mtl_depth_stencil_state *
|
||||
mtl_new_depth_stencil_state(mtl_device *device,
|
||||
mtl_depth_stencil_descriptor *descriptor)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_residency_set.h"
|
||||
|
||||
mtl_residency_set *
|
||||
mtl_new_residency_set(mtl_device *device)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
mtl_residency_set_add_allocation(mtl_residency_set *residency_set,
|
||||
mtl_allocation *allocation)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_residency_set_remove_allocation(mtl_residency_set *residency_set,
|
||||
mtl_allocation *allocation)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_residency_set_commit(mtl_residency_set *residency_set)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_residency_set_request_residency(mtl_residency_set *residency_set)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_residency_set_end_residency(mtl_residency_set *residency_set)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_sampler.h"
|
||||
|
||||
/* Sampler descriptor */
|
||||
mtl_sampler_descriptor *
|
||||
mtl_new_sampler_descriptor(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Sampler descriptor utils */
|
||||
void
|
||||
mtl_sampler_descriptor_set_normalized_coordinates(
|
||||
mtl_sampler_descriptor *descriptor, bool normalized_coordinates)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_sampler_descriptor_set_address_mode(
|
||||
mtl_sampler_descriptor *descriptor,
|
||||
enum mtl_sampler_address_mode address_mode_u,
|
||||
enum mtl_sampler_address_mode address_mode_v,
|
||||
enum mtl_sampler_address_mode address_mode_w)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_sampler_descriptor_set_border_color(mtl_sampler_descriptor *descriptor,
|
||||
enum mtl_sampler_border_color color)
|
||||
{
|
||||
}
|
||||
void
|
||||
mtl_sampler_descriptor_set_filters(mtl_sampler_descriptor *descriptor,
|
||||
enum mtl_sampler_min_mag_filter min_filter,
|
||||
enum mtl_sampler_min_mag_filter mag_filter,
|
||||
enum mtl_sampler_mip_filter mip_filter)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_sampler_descriptor_set_lod_clamp(mtl_sampler_descriptor *descriptor,
|
||||
float min, float max)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_sampler_descriptor_set_max_anisotropy(mtl_sampler_descriptor *descriptor,
|
||||
uint64_t max)
|
||||
{
|
||||
}
|
||||
void
|
||||
mtl_sampler_descriptor_set_compare_function(mtl_sampler_descriptor *descriptor,
|
||||
enum mtl_compare_function function)
|
||||
{
|
||||
}
|
||||
|
||||
/* Sampler */
|
||||
mtl_sampler *
|
||||
mtl_new_sampler(mtl_device *device, mtl_sampler_descriptor *descriptor)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Sampler utils */
|
||||
uint64_t
|
||||
mtl_sampler_get_gpu_resource_id(mtl_sampler *sampler)
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_sync.h"
|
||||
|
||||
/* MTLFence */
|
||||
mtl_fence *
|
||||
mtl_new_fence(mtl_device *device)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* MTLEvent */
|
||||
mtl_event *
|
||||
mtl_new_event(mtl_device *device)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* MTLSharedEvent */
|
||||
mtl_shared_event *
|
||||
mtl_new_shared_event(mtl_device *device)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
mtl_shared_event_wait_until_signaled_value(mtl_shared_event *event_handle,
|
||||
uint64_t value, uint64_t timeout_ms)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
mtl_shared_event_get_signaled_value(mtl_shared_event *event_handle)
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
|
||||
void
|
||||
mtl_shared_event_set_signaled_value(mtl_shared_event *event_handle,
|
||||
uint64_t value)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "mtl_texture.h"
|
||||
|
||||
/* Utils*/
|
||||
uint64_t
|
||||
mtl_texture_get_gpu_resource_id(mtl_texture *texture)
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
|
||||
/* Texture view creation */
|
||||
mtl_texture *
|
||||
mtl_new_texture_view_with(mtl_texture *texture,
|
||||
const struct kk_view_layout *layout)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mtl_texture *
|
||||
mtl_new_texture_view_with_no_swizzle(mtl_texture *texture,
|
||||
const struct kk_view_layout *layout)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
mtl_texture_get_bytes(mtl_texture *texture, void *host_ptr,
|
||||
struct mtl_texture_memory_copy *data)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mtl_texture_replace_region(mtl_texture *texture, const void *host_ptr,
|
||||
struct mtl_texture_memory_copy *data)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "vk_to_mtl_map.h"
|
||||
|
||||
#include "kosmickrisp/bridge/mtl_types.h"
|
||||
|
||||
#include "util/format/u_format.h"
|
||||
|
||||
#include "vulkan/vulkan.h"
|
||||
#include "vk_meta.h"
|
||||
|
||||
struct mtl_origin
|
||||
vk_offset_3d_to_mtl_origin(const struct VkOffset3D *offset)
|
||||
{
|
||||
struct mtl_origin ret = {
|
||||
.x = offset->x,
|
||||
.y = offset->y,
|
||||
.z = offset->z,
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct mtl_size
|
||||
vk_extent_3d_to_mtl_size(const struct VkExtent3D *extent)
|
||||
{
|
||||
struct mtl_size ret = {
|
||||
.x = extent->width,
|
||||
.y = extent->height,
|
||||
.z = extent->depth,
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum mtl_primitive_type
|
||||
vk_primitive_topology_to_mtl_primitive_type(enum VkPrimitiveTopology topology)
|
||||
{
|
||||
switch (topology) {
|
||||
case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
|
||||
return MTL_PRIMITIVE_TYPE_POINT;
|
||||
case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
|
||||
return MTL_PRIMITIVE_TYPE_LINE;
|
||||
case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
|
||||
return MTL_PRIMITIVE_TYPE_LINE_STRIP;
|
||||
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wswitch"
|
||||
case VK_PRIMITIVE_TOPOLOGY_META_RECT_LIST_MESA:
|
||||
#pragma GCC diagnostic pop
|
||||
/* Triangle fans are emulated meaning we'll translate the index buffer to
|
||||
* triangle list or generate a index buffer if there's none */
|
||||
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
|
||||
return MTL_PRIMITIVE_TYPE_TRIANGLE;
|
||||
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
|
||||
return MTL_PRIMITIVE_TYPE_TRIANGLE_STRIP;
|
||||
default:
|
||||
assert(0 && "Primitive topology not supported!");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
enum mtl_primitive_topology_class
|
||||
vk_primitive_topology_to_mtl_primitive_topology_class(
|
||||
enum VkPrimitiveTopology topology)
|
||||
{
|
||||
switch (topology) {
|
||||
case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
|
||||
return MTL_PRIMITIVE_TOPOLOGY_CLASS_POINT;
|
||||
case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
|
||||
case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
|
||||
return MTL_PRIMITIVE_TOPOLOGY_CLASS_LINE;
|
||||
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
|
||||
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wswitch"
|
||||
case VK_PRIMITIVE_TOPOLOGY_META_RECT_LIST_MESA:
|
||||
#pragma GCC diagnostic pop
|
||||
return MTL_PRIMITIVE_TOPOLOGY_CLASS_TRIANGLE;
|
||||
default:
|
||||
return MTL_PRIMITIVE_TOPOLOGY_CLASS_UNSPECIFIED;
|
||||
}
|
||||
}
|
||||
|
||||
enum mtl_load_action
|
||||
vk_attachment_load_op_to_mtl_load_action(enum VkAttachmentLoadOp op)
|
||||
{
|
||||
switch (op) {
|
||||
case VK_ATTACHMENT_LOAD_OP_LOAD:
|
||||
return MTL_LOAD_ACTION_LOAD;
|
||||
case VK_ATTACHMENT_LOAD_OP_CLEAR:
|
||||
return MTL_LOAD_ACTION_CLEAR;
|
||||
case VK_ATTACHMENT_LOAD_OP_DONT_CARE:
|
||||
return MTL_LOAD_ACTION_DONT_CARE;
|
||||
default:
|
||||
assert(false && "Unsupported VkAttachmentLoadOp");
|
||||
return MTL_LOAD_ACTION_DONT_CARE;
|
||||
};
|
||||
}
|
||||
|
||||
enum mtl_store_action
|
||||
vk_attachment_store_op_to_mtl_store_action(enum VkAttachmentStoreOp op)
|
||||
{
|
||||
switch (op) {
|
||||
case VK_ATTACHMENT_STORE_OP_STORE:
|
||||
return MTL_STORE_ACTION_STORE;
|
||||
case VK_ATTACHMENT_STORE_OP_DONT_CARE:
|
||||
return MTL_STORE_ACTION_DONT_CARE;
|
||||
case VK_ATTACHMENT_STORE_OP_NONE:
|
||||
return MTL_STORE_ACTION_UNKNOWN;
|
||||
default:
|
||||
assert(false && "Unsupported VkAttachmentStoreOp");
|
||||
return MTL_STORE_ACTION_UNKNOWN;
|
||||
};
|
||||
}
|
||||
|
||||
enum mtl_sampler_address_mode
|
||||
vk_sampler_address_mode_to_mtl_sampler_address_mode(
|
||||
enum VkSamplerAddressMode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case VK_SAMPLER_ADDRESS_MODE_REPEAT:
|
||||
return MTL_SAMPLER_ADDRESS_MODE_REPEAT;
|
||||
case VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT:
|
||||
return MTL_SAMPLER_ADDRESS_MODE_MIRROR_REPEAT;
|
||||
case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE:
|
||||
return MTL_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER:
|
||||
return MTL_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER_COLOR;
|
||||
case VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE:
|
||||
return MTL_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
|
||||
default:
|
||||
UNREACHABLE("Unsupported address mode");
|
||||
}
|
||||
}
|
||||
|
||||
enum mtl_sampler_border_color
|
||||
vk_border_color_to_mtl_sampler_border_color(enum VkBorderColor color)
|
||||
{
|
||||
switch (color) {
|
||||
case VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK:
|
||||
case VK_BORDER_COLOR_INT_TRANSPARENT_BLACK:
|
||||
return MTL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK;
|
||||
case VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK:
|
||||
case VK_BORDER_COLOR_INT_OPAQUE_BLACK:
|
||||
return MTL_SAMPLER_BORDER_COLOR_OPAQUE_BLACK;
|
||||
case VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE:
|
||||
case VK_BORDER_COLOR_INT_OPAQUE_WHITE:
|
||||
return MTL_SAMPLER_BORDER_COLOR_OPAQUE_WHITE;
|
||||
case VK_BORDER_COLOR_FLOAT_CUSTOM_EXT:
|
||||
case VK_BORDER_COLOR_INT_CUSTOM_EXT:
|
||||
return MTL_SAMPLER_BORDER_COLOR_OPAQUE_WHITE;
|
||||
default:
|
||||
UNREACHABLE("Unsupported address mode");
|
||||
}
|
||||
}
|
||||
|
||||
enum mtl_sampler_min_mag_filter
|
||||
vk_filter_to_mtl_sampler_min_mag_filter(enum VkFilter filter)
|
||||
{
|
||||
switch (filter) {
|
||||
case VK_FILTER_NEAREST:
|
||||
return MTL_SAMPLER_MIN_MAG_FILTER_NEAREST;
|
||||
case VK_FILTER_LINEAR:
|
||||
return MTL_SAMPLER_MIN_MAG_FILTER_LINEAR;
|
||||
default:
|
||||
UNREACHABLE("Unsupported address mode");
|
||||
}
|
||||
}
|
||||
|
||||
enum mtl_sampler_mip_filter
|
||||
vk_sampler_mipmap_mode_to_mtl_sampler_mip_filter(enum VkSamplerMipmapMode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case VK_SAMPLER_MIPMAP_MODE_NEAREST:
|
||||
return MTL_SAMPLER_MIP_FILTER_NEAREST;
|
||||
case VK_SAMPLER_MIPMAP_MODE_LINEAR:
|
||||
return MTL_SAMPLER_MIP_FILTER_LINEAR;
|
||||
default:
|
||||
UNREACHABLE("Unsupported address mode");
|
||||
}
|
||||
}
|
||||
|
||||
enum mtl_compare_function
|
||||
vk_compare_op_to_mtl_compare_function(enum VkCompareOp op)
|
||||
{
|
||||
switch (op) {
|
||||
case VK_COMPARE_OP_NEVER:
|
||||
return MTL_COMPARE_FUNCTION_NEVER;
|
||||
case VK_COMPARE_OP_LESS:
|
||||
return MTL_COMPARE_FUNCTION_LESS;
|
||||
case VK_COMPARE_OP_EQUAL:
|
||||
return MTL_COMPARE_FUNCTION_EQUAL;
|
||||
case VK_COMPARE_OP_LESS_OR_EQUAL:
|
||||
return MTL_COMPARE_FUNCTION_LESS_EQUAL;
|
||||
case VK_COMPARE_OP_GREATER:
|
||||
return MTL_COMPARE_FUNCTION_GREATER;
|
||||
case VK_COMPARE_OP_NOT_EQUAL:
|
||||
return MTL_COMPARE_FUNCTION_NOT_EQUAL;
|
||||
case VK_COMPARE_OP_GREATER_OR_EQUAL:
|
||||
return MTL_COMPARE_FUNCTION_GREATER_EQUAL;
|
||||
case VK_COMPARE_OP_ALWAYS:
|
||||
return MTL_COMPARE_FUNCTION_ALWAYS;
|
||||
default:
|
||||
UNREACHABLE("Unsupported address mode");
|
||||
}
|
||||
}
|
||||
|
||||
enum mtl_winding
|
||||
vk_front_face_to_mtl_winding(enum VkFrontFace face)
|
||||
{
|
||||
switch (face) {
|
||||
case VK_FRONT_FACE_CLOCKWISE:
|
||||
return MTL_WINDING_CLOCKWISE;
|
||||
case VK_FRONT_FACE_COUNTER_CLOCKWISE:
|
||||
return MTL_WINDING_COUNTER_CLOCKWISE;
|
||||
default:
|
||||
assert(false && "Unsupported VkFrontFace");
|
||||
return MTL_WINDING_CLOCKWISE;
|
||||
}
|
||||
}
|
||||
|
||||
enum mtl_cull_mode
|
||||
vk_front_face_to_mtl_cull_mode(enum VkCullModeFlagBits mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case VK_CULL_MODE_NONE:
|
||||
case VK_CULL_MODE_FRONT_AND_BACK: // Emulated with scissor
|
||||
return MTL_CULL_MODE_NONE;
|
||||
case VK_CULL_MODE_FRONT_BIT:
|
||||
return MTL_CULL_MODE_FRONT;
|
||||
case VK_CULL_MODE_BACK_BIT:
|
||||
return MTL_CULL_MODE_BACK;
|
||||
default:
|
||||
UNREACHABLE("Unsupported VkCullModeFlags");
|
||||
}
|
||||
}
|
||||
|
||||
enum mtl_index_type
|
||||
index_size_in_bytes_to_mtl_index_type(unsigned bytes)
|
||||
{
|
||||
switch (bytes) {
|
||||
case 2u:
|
||||
return MTL_INDEX_TYPE_UINT16;
|
||||
case 4u:
|
||||
return MTL_INDEX_TYPE_UINT32;
|
||||
default:
|
||||
UNREACHABLE("Unsupported byte size for index");
|
||||
}
|
||||
}
|
||||
|
||||
unsigned
|
||||
mtl_index_type_to_size_B(enum mtl_index_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case MTL_INDEX_TYPE_UINT16:
|
||||
return 2u;
|
||||
case MTL_INDEX_TYPE_UINT32:
|
||||
return 4u;
|
||||
default:
|
||||
UNREACHABLE("Unhandled index type");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef KK_MTL_TO_VK_MAP_H
|
||||
#define KK_MTL_TO_VK_MAP_H 1
|
||||
|
||||
struct mtl_origin;
|
||||
struct mtl_size;
|
||||
enum mtl_primitive_type;
|
||||
enum mtl_primitive_topology_class;
|
||||
enum mtl_load_action;
|
||||
enum mtl_store_action;
|
||||
enum mtl_sampler_address_mode;
|
||||
enum mtl_sampler_border_color;
|
||||
enum mtl_sampler_min_mag_filter;
|
||||
enum mtl_sampler_mip_filter;
|
||||
enum mtl_compare_function;
|
||||
enum mtl_winding;
|
||||
enum mtl_cull_mode;
|
||||
enum mtl_index_type;
|
||||
|
||||
struct VkOffset3D;
|
||||
struct VkExtent3D;
|
||||
union VkClearColorValue;
|
||||
enum VkPrimitiveTopology;
|
||||
enum VkAttachmentLoadOp;
|
||||
enum VkAttachmentStoreOp;
|
||||
enum VkSamplerAddressMode;
|
||||
enum VkBorderColor;
|
||||
enum VkFilter;
|
||||
enum VkSamplerMipmapMode;
|
||||
enum VkCompareOp;
|
||||
enum VkFrontFace;
|
||||
enum VkCullModeFlagBits;
|
||||
|
||||
/* STRUCTS */
|
||||
struct mtl_origin vk_offset_3d_to_mtl_origin(const struct VkOffset3D *offset);
|
||||
|
||||
struct mtl_size vk_extent_3d_to_mtl_size(const struct VkExtent3D *extent);
|
||||
|
||||
/* ENUMS */
|
||||
enum mtl_primitive_type
|
||||
vk_primitive_topology_to_mtl_primitive_type(enum VkPrimitiveTopology topology);
|
||||
|
||||
enum mtl_primitive_topology_class
|
||||
vk_primitive_topology_to_mtl_primitive_topology_class(
|
||||
enum VkPrimitiveTopology topology);
|
||||
|
||||
enum mtl_load_action
|
||||
vk_attachment_load_op_to_mtl_load_action(enum VkAttachmentLoadOp op);
|
||||
|
||||
enum mtl_store_action
|
||||
vk_attachment_store_op_to_mtl_store_action(enum VkAttachmentStoreOp op);
|
||||
|
||||
enum mtl_sampler_address_mode
|
||||
vk_sampler_address_mode_to_mtl_sampler_address_mode(
|
||||
enum VkSamplerAddressMode mode);
|
||||
|
||||
enum mtl_sampler_border_color
|
||||
vk_border_color_to_mtl_sampler_border_color(enum VkBorderColor color);
|
||||
|
||||
enum mtl_sampler_min_mag_filter
|
||||
vk_filter_to_mtl_sampler_min_mag_filter(enum VkFilter filter);
|
||||
|
||||
enum mtl_sampler_mip_filter
|
||||
vk_sampler_mipmap_mode_to_mtl_sampler_mip_filter(enum VkSamplerMipmapMode mode);
|
||||
|
||||
enum mtl_compare_function
|
||||
vk_compare_op_to_mtl_compare_function(enum VkCompareOp op);
|
||||
|
||||
enum mtl_winding vk_front_face_to_mtl_winding(enum VkFrontFace face);
|
||||
|
||||
enum mtl_cull_mode vk_front_face_to_mtl_cull_mode(enum VkCullModeFlagBits mode);
|
||||
|
||||
enum mtl_index_type index_size_in_bytes_to_mtl_index_type(unsigned bytes);
|
||||
|
||||
unsigned mtl_index_type_to_size_B(enum mtl_index_type type);
|
||||
|
||||
#endif /* KK_MTL_TO_VK_MAP_H */
|
||||
@@ -0,0 +1,336 @@
|
||||
/*
|
||||
* Copyright 2026 LunarG, Inc.
|
||||
* Copyright 2026 Google LLC
|
||||
* Copyright 2023 Alyssa Rosenzweig
|
||||
* Copyright 2020 Intel Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "kosmickrisp/compiler/nir_to_msl.h"
|
||||
|
||||
#include "compiler/glsl_types.h"
|
||||
#include "compiler/spirv/nir_spirv.h"
|
||||
#include "nir.h"
|
||||
#include "nir_builder.h"
|
||||
#include "nir_builder_opcodes.h"
|
||||
#include "nir_intrinsics.h"
|
||||
#include "nir_precompiled.h"
|
||||
#include "shader_enums.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "util/macros.h"
|
||||
#include <sys/mman.h>
|
||||
|
||||
static const struct spirv_to_nir_options spirv_options = {
|
||||
.environment = NIR_SPIRV_OPENCL,
|
||||
.shared_addr_format = nir_address_format_62bit_generic,
|
||||
.global_addr_format = nir_address_format_62bit_generic,
|
||||
.temp_addr_format = nir_address_format_62bit_generic,
|
||||
.constant_addr_format = nir_address_format_64bit_global,
|
||||
.create_library = true,
|
||||
.printf = false, /* TODO_KOSMICKRISP Enable */
|
||||
};
|
||||
|
||||
/* Standard optimization loop */
|
||||
static void
|
||||
optimize(nir_shader *nir)
|
||||
{
|
||||
bool progress;
|
||||
do {
|
||||
progress = false;
|
||||
|
||||
NIR_PASS(progress, nir, nir_split_var_copies);
|
||||
NIR_PASS(progress, nir, nir_split_struct_vars, nir_var_function_temp);
|
||||
NIR_PASS(progress, nir, nir_lower_var_copies);
|
||||
NIR_PASS(progress, nir, nir_lower_vars_to_ssa);
|
||||
|
||||
NIR_PASS(progress, nir, nir_opt_copy_prop);
|
||||
NIR_PASS(progress, nir, nir_opt_remove_phis);
|
||||
NIR_PASS(progress, nir, nir_lower_all_phis_to_scalar);
|
||||
NIR_PASS(progress, nir, nir_opt_dce);
|
||||
NIR_PASS(progress, nir, nir_opt_dead_cf);
|
||||
NIR_PASS(progress, nir, nir_opt_cse);
|
||||
|
||||
nir_opt_peephole_select_options peephole_select_options = {
|
||||
.limit = 64,
|
||||
.expensive_alu_ok = true,
|
||||
};
|
||||
NIR_PASS(progress, nir, nir_opt_peephole_select,
|
||||
&peephole_select_options);
|
||||
NIR_PASS(progress, nir, nir_opt_phi_precision);
|
||||
NIR_PASS(progress, nir, nir_opt_algebraic);
|
||||
NIR_PASS(progress, nir, nir_opt_constant_folding);
|
||||
|
||||
NIR_PASS(progress, nir, nir_opt_deref);
|
||||
NIR_PASS(progress, nir, nir_opt_copy_prop_vars);
|
||||
NIR_PASS(progress, nir, nir_opt_undef);
|
||||
NIR_PASS(progress, nir, nir_lower_undef_to_zero);
|
||||
|
||||
NIR_PASS(progress, nir, nir_opt_shrink_vectors, true);
|
||||
NIR_PASS(progress, nir, nir_opt_loop_unroll);
|
||||
|
||||
} while (progress);
|
||||
}
|
||||
|
||||
static nir_shader *
|
||||
compile(void *memctx, const uint32_t *spirv, size_t spirv_size)
|
||||
{
|
||||
const nir_shader_compiler_options *nir_options = &kk_nir_options;
|
||||
|
||||
assert(spirv_size % 4 == 0);
|
||||
nir_shader *nir =
|
||||
spirv_to_nir(spirv, spirv_size / 4, NULL, MESA_SHADER_KERNEL, "library",
|
||||
&spirv_options, nir_options);
|
||||
nir_validate_shader(nir, "after spirv_to_nir");
|
||||
ralloc_steal(memctx, nir);
|
||||
|
||||
nir_fixup_is_exported(nir);
|
||||
|
||||
NIR_PASS(_, nir, nir_lower_system_values);
|
||||
NIR_PASS(_, nir, nir_lower_calls_to_builtins);
|
||||
|
||||
nir_lower_compute_system_values_options cs = {.global_id_is_32bit = true};
|
||||
NIR_PASS(_, nir, nir_lower_compute_system_values, &cs);
|
||||
|
||||
/* We have to lower away local constant initializers right before we
|
||||
* inline functions. That way they get properly initialized at the top
|
||||
* of the function and not at the top of its caller.
|
||||
*/
|
||||
NIR_PASS(_, nir, nir_lower_variable_initializers, nir_var_function_temp);
|
||||
NIR_PASS(_, nir, nir_lower_returns);
|
||||
NIR_PASS(_, nir, nir_inline_functions);
|
||||
nir_remove_non_exported(nir);
|
||||
NIR_PASS(_, nir, nir_opt_copy_prop);
|
||||
NIR_PASS(_, nir, nir_opt_deref);
|
||||
|
||||
/* We can't deal with constant data, get rid of it */
|
||||
nir_lower_constant_to_temp(nir);
|
||||
|
||||
/* We can go ahead and lower the rest of the constant initializers. We do
|
||||
* this here so that nir_remove_dead_variables and split_per_member_structs
|
||||
* below see the corresponding stores.
|
||||
*/
|
||||
NIR_PASS(_, nir, nir_lower_variable_initializers, ~0);
|
||||
|
||||
/* LLVM loves take advantage of the fact that vec3s in OpenCL are 16B
|
||||
* aligned and so it can just read/write them as vec4s. This results in a
|
||||
* LOT of vec4->vec3 casts on loads and stores. One solution to this
|
||||
* problem is to get rid of all vec3 variables.
|
||||
*/
|
||||
NIR_PASS(_, nir, nir_lower_vec3_to_vec4,
|
||||
nir_var_shader_temp | nir_var_function_temp | nir_var_mem_shared |
|
||||
nir_var_mem_global | nir_var_mem_constant);
|
||||
|
||||
/* We assign explicit types early so that the optimizer can take advantage
|
||||
* of that information and hopefully get rid of some of our memcpys.
|
||||
*/
|
||||
NIR_PASS(_, nir, nir_lower_vars_to_explicit_types,
|
||||
nir_var_uniform | nir_var_shader_temp | nir_var_function_temp |
|
||||
nir_var_mem_shared | nir_var_mem_global,
|
||||
glsl_get_cl_type_size_align);
|
||||
|
||||
optimize(nir);
|
||||
|
||||
NIR_PASS(_, nir, nir_remove_dead_variables, nir_var_all, NULL);
|
||||
|
||||
/* Lower again, this time after dead-variables to get more compact variable
|
||||
* layouts.
|
||||
*/
|
||||
NIR_PASS(_, nir, nir_lower_vars_to_explicit_types,
|
||||
nir_var_shader_temp | nir_var_function_temp | nir_var_mem_shared |
|
||||
nir_var_mem_global | nir_var_mem_constant,
|
||||
glsl_get_cl_type_size_align);
|
||||
assert(nir->constant_data_size == 0);
|
||||
|
||||
NIR_PASS(_, nir, nir_lower_memcpy);
|
||||
|
||||
NIR_PASS(_, nir, nir_lower_explicit_io, nir_var_mem_constant,
|
||||
nir_address_format_64bit_global);
|
||||
|
||||
NIR_PASS(_, nir, nir_lower_explicit_io, nir_var_uniform,
|
||||
nir_address_format_32bit_offset_as_64bit);
|
||||
|
||||
/* Note: we cannot lower explicit I/O here, because we need derefs in tact
|
||||
* for function calls into the library to work.
|
||||
*/
|
||||
|
||||
NIR_PASS(_, nir, nir_lower_convert_alu_types, NULL);
|
||||
NIR_PASS(_, nir, nir_opt_if, 0);
|
||||
NIR_PASS(_, nir, nir_opt_idiv_const, 16);
|
||||
|
||||
msl_lower_textures(nir);
|
||||
|
||||
optimize(nir);
|
||||
|
||||
return nir;
|
||||
}
|
||||
|
||||
static void
|
||||
print_shader(FILE *fp, const char *name, const char *suffix, uint32_t variant,
|
||||
const char *msl, nir_shader *nir)
|
||||
{
|
||||
uint32_t msl_length = strlen(msl) + 1;
|
||||
uint32_t workgroup_count_size = sizeof(nir->info.workgroup_size);
|
||||
size_t sz_B = workgroup_count_size + msl_length;
|
||||
size_t sz_el = DIV_ROUND_UP(sz_B, 4);
|
||||
uint32_t *mem = calloc(sz_el, 4);
|
||||
|
||||
memcpy(mem, nir->info.workgroup_size, workgroup_count_size);
|
||||
memcpy((uint8_t *)mem + workgroup_count_size, msl, msl_length);
|
||||
|
||||
nir_precomp_print_blob(fp, name, suffix, variant, mem, sz_B, true);
|
||||
free(mem);
|
||||
}
|
||||
|
||||
static nir_def *
|
||||
load_kernel_input(nir_builder *b, unsigned num_components, unsigned bit_size,
|
||||
unsigned offset_B)
|
||||
{
|
||||
nir_def *root = nir_load_buffer_ptr_kk(b, 1, 64, .binding = 0);
|
||||
|
||||
/* We've bound the address of the root descriptor, index in. */
|
||||
nir_def *addr = nir_iadd(b, root, nir_imm_int64(b, offset_B));
|
||||
return nir_build_load_global_constant(b, num_components, bit_size, addr,
|
||||
.align_mul = bit_size,
|
||||
.access = ACCESS_CAN_SPECULATE);
|
||||
}
|
||||
|
||||
static int
|
||||
type_size_vec4(const struct glsl_type *type, bool bindless)
|
||||
{
|
||||
return glsl_count_attribute_slots(type, false);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 4) {
|
||||
fprintf(stderr, "Usage: %s [input spir-v] [output header] [output C]\n",
|
||||
argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *infile = argv[1];
|
||||
const char *outh_file = argv[2];
|
||||
const char *outc_file = argv[3];
|
||||
|
||||
void *mem_ctx = ralloc_context(NULL);
|
||||
|
||||
int fd = open(infile, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Failed to open %s\n", infile);
|
||||
ralloc_free(mem_ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
off_t spirv_len = lseek(fd, 0, SEEK_END);
|
||||
const void *spirv_map = mmap(NULL, spirv_len, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
close(fd);
|
||||
if (spirv_map == MAP_FAILED) {
|
||||
fprintf(stderr, "Failed to mmap the file: errno=%d, %s\n", errno,
|
||||
strerror(errno));
|
||||
ralloc_free(mem_ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
FILE *fp_h = fopen(outh_file, "w");
|
||||
FILE *fp_c = fopen(outc_file, "w");
|
||||
glsl_type_singleton_init_or_ref();
|
||||
|
||||
nir_precomp_print_header(fp_c, fp_h, "KosmicKrisp Contributors",
|
||||
"libkk_shaders.h");
|
||||
|
||||
nir_shader *nir = compile(mem_ctx, spirv_map, spirv_len);
|
||||
|
||||
/* load_preamble works at 16-bit granularity */
|
||||
struct nir_precomp_opts opt = {.arg_align_B = 2};
|
||||
|
||||
const char *target = "AppleSilicon";
|
||||
|
||||
nir_foreach_entrypoint(libfunc, nir) {
|
||||
libfunc->pass_flags = 0;
|
||||
unsigned nr_vars = nir_precomp_nr_variants(libfunc);
|
||||
|
||||
nir_precomp_print_layout_struct(fp_h, &opt, libfunc);
|
||||
|
||||
for (unsigned v = 0; v < nr_vars; ++v) {
|
||||
nir_shader *s = nir_precompiled_build_variant(
|
||||
libfunc, MESA_SHADER_COMPUTE, v, &kk_nir_options, &opt,
|
||||
load_kernel_input);
|
||||
|
||||
nir_link_shader_functions(s, nir);
|
||||
NIR_PASS(_, s, nir_inline_functions);
|
||||
nir_remove_non_entrypoints(s);
|
||||
NIR_PASS(_, s, nir_opt_deref);
|
||||
NIR_PASS(_, s, nir_lower_vars_to_ssa);
|
||||
NIR_PASS(_, s, nir_remove_dead_derefs);
|
||||
NIR_PASS(_, s, nir_remove_dead_variables,
|
||||
nir_var_function_temp | nir_var_shader_temp, NULL);
|
||||
NIR_PASS(_, s, nir_lower_vars_to_explicit_types,
|
||||
nir_var_shader_temp | nir_var_function_temp,
|
||||
glsl_get_cl_type_size_align);
|
||||
|
||||
NIR_PASS(_, s, nir_lower_vars_to_explicit_types, nir_var_mem_shared,
|
||||
glsl_get_cl_type_size_align);
|
||||
|
||||
/* Workgroup size cannot be lowered when the shader is in library form,
|
||||
* so we need to lower compute system values again now that it is a
|
||||
* variant with a distinct entry-point. */
|
||||
NIR_PASS(_, s, nir_lower_compute_system_values, NULL);
|
||||
|
||||
NIR_PASS(_, s, nir_lower_explicit_io, nir_var_mem_shared,
|
||||
nir_address_format_62bit_generic);
|
||||
|
||||
msl_preprocess_nir(s);
|
||||
msl_preprocess_nir_workarounds(nir, 0);
|
||||
msl_optimize_nir(nir);
|
||||
|
||||
NIR_PASS(_, s, nir_opt_deref);
|
||||
NIR_PASS(_, s, nir_lower_vars_to_ssa);
|
||||
|
||||
NIR_PASS(_, s, nir_lower_explicit_io,
|
||||
nir_var_shader_temp | nir_var_function_temp |
|
||||
nir_var_mem_shared | nir_var_mem_global,
|
||||
nir_address_format_62bit_generic);
|
||||
|
||||
/* nir_lower_explicit_io can create phis we need to get rid of */
|
||||
NIR_PASS(_, s, nir_convert_from_ssa, true, true);
|
||||
NIR_PASS(_, s, nir_trivialize_registers);
|
||||
|
||||
/* nir_lower_explicit_io will create unpack_64 we need to lower.
|
||||
* Perform in a loop to make sure any optimizations that result in
|
||||
* instructions we don't implement are fully lowered */
|
||||
bool progress = false;
|
||||
do {
|
||||
progress = false;
|
||||
NIR_PASS(progress, s, nir_opt_algebraic);
|
||||
} while (progress);
|
||||
|
||||
nir_shader_gather_info(s, nir_shader_get_entrypoint(s));
|
||||
struct nir_to_msl_options options = {};
|
||||
const char *msl = nir_to_msl(s, &options);
|
||||
print_shader(fp_c, libfunc->name, target, v, msl, s);
|
||||
ralloc_free((void *)msl);
|
||||
|
||||
ralloc_free(s);
|
||||
}
|
||||
}
|
||||
|
||||
nir_precomp_print_program_enum(fp_h, nir, "libkk");
|
||||
nir_precomp_print_dispatch_macros(fp_h, &opt, nir);
|
||||
|
||||
/* For each target, generate a table mapping programs to binaries */
|
||||
|
||||
nir_precomp_print_extern_binary_map(fp_h, "libkk", target);
|
||||
nir_precomp_print_binary_map(fp_c, nir, "libkk", target, NULL);
|
||||
|
||||
glsl_type_singleton_decref();
|
||||
fclose(fp_c);
|
||||
fclose(fp_h);
|
||||
ralloc_free(mem_ctx);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2026 LunarG, Inc.
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef KK_PRECOMPILED_SHADER_H
|
||||
#define KK_PRECOMPILED_SHADER_H 1
|
||||
|
||||
#include "kosmickrisp/bridge/mtl_types.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct kk_precompiled_info {
|
||||
uint16_t workgroup_size[3];
|
||||
};
|
||||
|
||||
struct kk_precompiled_shader {
|
||||
struct kk_precompiled_info info;
|
||||
mtl_compute_pipeline_state *pipeline;
|
||||
};
|
||||
|
||||
#endif /* KK_PRECOMPILED_SHADER_H_ */
|
||||
@@ -0,0 +1,13 @@
|
||||
# Copyright 2026 LunarG, Inc.
|
||||
# Copyright 2026 Google LLC
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
prog_kk_clc = executable(
|
||||
'kk_clc',
|
||||
['kk_clc.c'],
|
||||
link_with : [libmsl_compiler],
|
||||
include_directories : [inc_include, inc_src],
|
||||
c_args : [pre_args, no_override_init_args],
|
||||
link_args : [ld_args_build_id],
|
||||
dependencies : [idep_vtn, idep_nir, idep_mesautil],
|
||||
)
|
||||
@@ -0,0 +1,36 @@
|
||||
# Copyright 2025 LunarG, Inc.
|
||||
# Copyright 2025 Google LLC
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
libmsl_compiler_files = files(
|
||||
'nir_to_msl.c',
|
||||
'msl_type_inference.c',
|
||||
'msl_iomap.c',
|
||||
'msl_nir_gather_common.c',
|
||||
'msl_nir_lower_common.c',
|
||||
'msl_nir_lower_subgroups.c',
|
||||
)
|
||||
|
||||
msl_nir_algebraic_c = custom_target(
|
||||
input : 'msl_nir_algebraic.py',
|
||||
output : 'msl_nir_algebraic.c',
|
||||
command : [
|
||||
prog_python, '@INPUT@', '-p', dir_compiler_nir,
|
||||
],
|
||||
capture : true,
|
||||
depend_files : nir_algebraic_depends,
|
||||
)
|
||||
|
||||
|
||||
libmsl_compiler = static_library(
|
||||
'msl_compiler',
|
||||
[libmsl_compiler_files, msl_nir_algebraic_c],
|
||||
dependencies : [idep_nir, idep_mesautil],
|
||||
gnu_symbol_visibility: 'hidden',
|
||||
build_by_default: false,
|
||||
)
|
||||
|
||||
idep_msl_to_nir = declare_dependency(
|
||||
link_with : libmsl_compiler,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,535 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/* This file primarily concerns itself with mapping from the NIR (and Vulkan)
|
||||
* model of I/O to the Metal one. */
|
||||
|
||||
#include "msl_private.h"
|
||||
|
||||
#include "nir_builder.h"
|
||||
|
||||
/* Mapping from alu type to Metal scalar type */
|
||||
static const char *
|
||||
alu_type_to_string(nir_alu_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case nir_type_uint8:
|
||||
return "uchar";
|
||||
case nir_type_uint16:
|
||||
return "ushort";
|
||||
case nir_type_uint32:
|
||||
return "uint";
|
||||
case nir_type_uint64:
|
||||
return "ulong";
|
||||
case nir_type_int8:
|
||||
return "char";
|
||||
case nir_type_int16:
|
||||
return "short";
|
||||
case nir_type_int32:
|
||||
return "int";
|
||||
case nir_type_int64:
|
||||
return "long";
|
||||
case nir_type_float16:
|
||||
return "half";
|
||||
case nir_type_float32:
|
||||
return "float";
|
||||
case nir_type_bool8:
|
||||
return "bool";
|
||||
default:
|
||||
UNREACHABLE("Unsupported nir_alu_type");
|
||||
}
|
||||
};
|
||||
|
||||
/* Type suffix for a vector of a given size. */
|
||||
static const char *vector_suffixes[] = {
|
||||
[1] = "",
|
||||
[2] = "2",
|
||||
[3] = "3",
|
||||
[4] = "4",
|
||||
};
|
||||
|
||||
/* The type names of the generated output structs */
|
||||
static const char *VERTEX_OUTPUT_TYPE = "VertexOut";
|
||||
static const char *FRAGMENT_OUTPUT_TYPE = "FragmentOut";
|
||||
|
||||
/* Mapping from NIR's varying slots to the generated struct member name */
|
||||
static const struct {
|
||||
const char *name;
|
||||
bool user;
|
||||
bool scalarized;
|
||||
} VARYING_SLOT_INFO[NUM_TOTAL_VARYING_SLOTS] = {
|
||||
[VARYING_SLOT_POS] = {"position"},
|
||||
[VARYING_SLOT_PSIZ] = {"point_size"},
|
||||
[VARYING_SLOT_PRIMITIVE_ID] = {"primitive_id"},
|
||||
[VARYING_SLOT_LAYER] = {"render_target_array_index"},
|
||||
[VARYING_SLOT_VIEWPORT] = {"viewport_array_index"},
|
||||
[VARYING_SLOT_CLIP_DIST0] = {"clip_0", .user = true, .scalarized = true},
|
||||
[VARYING_SLOT_CLIP_DIST1] = {"clip_1", .user = true, .scalarized = true},
|
||||
[VARYING_SLOT_CULL_DIST0] = {"cull_0", .user = true, .scalarized = true},
|
||||
[VARYING_SLOT_CULL_DIST1] = {"cull_1", .user = true, .scalarized = true},
|
||||
[VARYING_SLOT_CULL_PRIMITIVE] = {"cull_primitive_0", .user = true,
|
||||
.scalarized = true},
|
||||
/* Using cull primitive slots to emulate cull distances in fragment shader,
|
||||
* which may extend to one varying extra (which is otherwise unused) */
|
||||
[VARYING_SLOT_CULL_PRIMITIVE + 1] = {"cull_primitive_1", .user = true,
|
||||
.scalarized = true},
|
||||
[VARYING_SLOT_VAR0] = {"vary_00", .user = true},
|
||||
[VARYING_SLOT_VAR1] = {"vary_01", .user = true},
|
||||
[VARYING_SLOT_VAR2] = {"vary_02", .user = true},
|
||||
[VARYING_SLOT_VAR3] = {"vary_03", .user = true},
|
||||
[VARYING_SLOT_VAR4] = {"vary_04", .user = true},
|
||||
[VARYING_SLOT_VAR5] = {"vary_05", .user = true},
|
||||
[VARYING_SLOT_VAR6] = {"vary_06", .user = true},
|
||||
[VARYING_SLOT_VAR7] = {"vary_07", .user = true},
|
||||
[VARYING_SLOT_VAR8] = {"vary_08", .user = true},
|
||||
[VARYING_SLOT_VAR9] = {"vary_09", .user = true},
|
||||
[VARYING_SLOT_VAR10] = {"vary_10", .user = true},
|
||||
[VARYING_SLOT_VAR11] = {"vary_11", .user = true},
|
||||
[VARYING_SLOT_VAR12] = {"vary_12", .user = true},
|
||||
[VARYING_SLOT_VAR13] = {"vary_13", .user = true},
|
||||
[VARYING_SLOT_VAR14] = {"vary_14", .user = true},
|
||||
[VARYING_SLOT_VAR15] = {"vary_15", .user = true},
|
||||
[VARYING_SLOT_VAR16] = {"vary_16", .user = true},
|
||||
[VARYING_SLOT_VAR17] = {"vary_17", .user = true},
|
||||
[VARYING_SLOT_VAR18] = {"vary_18", .user = true},
|
||||
[VARYING_SLOT_VAR19] = {"vary_19", .user = true},
|
||||
[VARYING_SLOT_VAR20] = {"vary_20", .user = true},
|
||||
[VARYING_SLOT_VAR21] = {"vary_21", .user = true},
|
||||
[VARYING_SLOT_VAR22] = {"vary_22", .user = true},
|
||||
[VARYING_SLOT_VAR23] = {"vary_23", .user = true},
|
||||
[VARYING_SLOT_VAR24] = {"vary_24", .user = true},
|
||||
[VARYING_SLOT_VAR25] = {"vary_25", .user = true},
|
||||
[VARYING_SLOT_VAR26] = {"vary_26", .user = true},
|
||||
[VARYING_SLOT_VAR27] = {"vary_27", .user = true},
|
||||
[VARYING_SLOT_VAR28] = {"vary_28", .user = true},
|
||||
[VARYING_SLOT_VAR29] = {"vary_29", .user = true},
|
||||
[VARYING_SLOT_VAR30] = {"vary_30", .user = true},
|
||||
[VARYING_SLOT_VAR31] = {"vary_31", .user = true},
|
||||
};
|
||||
|
||||
static void
|
||||
varying_slot_name(struct nir_to_msl_ctx *ctx, unsigned location,
|
||||
unsigned component)
|
||||
{
|
||||
if (VARYING_SLOT_INFO[location].scalarized) {
|
||||
P(ctx, "%s_%c", VARYING_SLOT_INFO[location].name, "xyzw"[component]);
|
||||
} else {
|
||||
P(ctx, "%s", VARYING_SLOT_INFO[location].name);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
varying_slot_semantic(struct nir_to_msl_ctx *ctx, unsigned location,
|
||||
unsigned component)
|
||||
{
|
||||
if (VARYING_SLOT_INFO[location].user) {
|
||||
P(ctx, "[[user(");
|
||||
varying_slot_name(ctx, location, component);
|
||||
P(ctx, ")]]");
|
||||
} else {
|
||||
P(ctx, "[[");
|
||||
varying_slot_name(ctx, location, component);
|
||||
P(ctx, "]]");
|
||||
}
|
||||
}
|
||||
|
||||
/* Mapping from NIR fragment output slot to MSL struct member name */
|
||||
static const char *FS_OUTPUT_NAME[] = {
|
||||
[FRAG_RESULT_DEPTH] = "depth_out",
|
||||
[FRAG_RESULT_STENCIL] = "stencil_out",
|
||||
[FRAG_RESULT_SAMPLE_MASK] = "sample_mask_out",
|
||||
[FRAG_RESULT_DATA0] = "color_0",
|
||||
[FRAG_RESULT_DATA1] = "color_1",
|
||||
[FRAG_RESULT_DATA2] = "color_2",
|
||||
[FRAG_RESULT_DATA3] = "color_3",
|
||||
[FRAG_RESULT_DATA4] = "color_4",
|
||||
[FRAG_RESULT_DATA5] = "color_5",
|
||||
[FRAG_RESULT_DATA6] = "color_6",
|
||||
[FRAG_RESULT_DATA7] = "color_7",
|
||||
};
|
||||
|
||||
/* Mapping from NIR fragment output slot to MSL struct member attribute */
|
||||
static const char *FS_OUTPUT_SEMANTIC[] = {
|
||||
[FRAG_RESULT_DEPTH] = "", // special case, depends on depth layout
|
||||
[FRAG_RESULT_STENCIL] = "stencil", [FRAG_RESULT_SAMPLE_MASK] = "sample_mask",
|
||||
[FRAG_RESULT_DATA0] = "color(0)", [FRAG_RESULT_DATA1] = "color(1)",
|
||||
[FRAG_RESULT_DATA2] = "color(2)", [FRAG_RESULT_DATA3] = "color(3)",
|
||||
[FRAG_RESULT_DATA4] = "color(4)", [FRAG_RESULT_DATA5] = "color(5)",
|
||||
[FRAG_RESULT_DATA6] = "color(6)", [FRAG_RESULT_DATA7] = "color(7)",
|
||||
};
|
||||
|
||||
const char *depth_layout_arg[8] = {
|
||||
[FRAG_DEPTH_LAYOUT_ANY] = "any",
|
||||
[FRAG_DEPTH_LAYOUT_GREATER] = "greater",
|
||||
[FRAG_DEPTH_LAYOUT_LESS] = "less",
|
||||
[FRAG_DEPTH_LAYOUT_UNCHANGED] = "any",
|
||||
};
|
||||
|
||||
/* Generate the struct definition for the vertex shader return value */
|
||||
static void
|
||||
vs_output_block(nir_shader *shader, struct nir_to_msl_ctx *ctx)
|
||||
{
|
||||
P(ctx, "struct %s {\n", VERTEX_OUTPUT_TYPE);
|
||||
ctx->indentlevel++;
|
||||
u_foreach_bit64(location, shader->info.outputs_written) {
|
||||
struct io_slot_info info = ctx->outputs_info[location];
|
||||
bool scalarized = VARYING_SLOT_INFO[location].scalarized;
|
||||
const char *type = alu_type_to_string(info.type);
|
||||
const char *vector_suffix =
|
||||
scalarized ? "" : vector_suffixes[info.num_components];
|
||||
unsigned components = scalarized ? info.num_components : 1;
|
||||
for (int c = 0; c < components; c++) {
|
||||
P_IND(ctx, "%s%s ", type, vector_suffix);
|
||||
varying_slot_name(ctx, location, c);
|
||||
P(ctx, " ");
|
||||
varying_slot_semantic(ctx, location, c);
|
||||
P(ctx, ";\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (shader->info.clip_distance_array_size)
|
||||
P_IND(ctx, "float gl_ClipDistance [[clip_distance]] [%d];\n",
|
||||
shader->info.clip_distance_array_size);
|
||||
ctx->indentlevel--;
|
||||
P(ctx, "};\n");
|
||||
}
|
||||
|
||||
static void
|
||||
fs_input_interpolant(struct nir_to_msl_ctx *ctx, uint64_t location)
|
||||
{
|
||||
struct io_slot_info info = ctx->inputs_info[location];
|
||||
bool scalarized = VARYING_SLOT_INFO[location].scalarized;
|
||||
const char *type = alu_type_to_string(info.type);
|
||||
const char *vector_suffix =
|
||||
scalarized ? "" : vector_suffixes[info.num_components];
|
||||
unsigned components = scalarized ? info.num_components : 1;
|
||||
const char *interp = "";
|
||||
switch (info.interpolation) {
|
||||
case INTERP_MODE_SMOOTH:
|
||||
interp = "interpolation::perspective";
|
||||
break;
|
||||
case INTERP_MODE_NOPERSPECTIVE:
|
||||
interp = "interpolation::no_perspective";
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE("Only perspective and no-perspective are allowed");
|
||||
}
|
||||
for (int c = 0; c < components; c++) {
|
||||
P_IND(ctx, "interpolant<%s%s, %s> ", type, vector_suffix, interp);
|
||||
varying_slot_name(ctx, location, c);
|
||||
P(ctx, " ");
|
||||
varying_slot_semantic(ctx, location, c);
|
||||
P(ctx, ";\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fs_input_no_interpolant(struct nir_to_msl_ctx *ctx, uint64_t location)
|
||||
{
|
||||
struct io_slot_info info = ctx->inputs_info[location];
|
||||
bool scalarized = VARYING_SLOT_INFO[location].scalarized;
|
||||
const char *type = alu_type_to_string(info.type);
|
||||
const char *vector_suffix =
|
||||
scalarized ? "" : vector_suffixes[info.num_components];
|
||||
unsigned components = scalarized ? info.num_components : 1;
|
||||
const char *interp = "";
|
||||
switch (info.interpolation) {
|
||||
case INTERP_MODE_NOPERSPECTIVE:
|
||||
if (info.centroid)
|
||||
interp = "[[centroid_no_perspective]]";
|
||||
else if (info.sample)
|
||||
interp = "[[sample_no_perspective]]";
|
||||
else
|
||||
interp = "[[center_no_perspective]]";
|
||||
break;
|
||||
case INTERP_MODE_FLAT:
|
||||
interp = "[[flat]]";
|
||||
break;
|
||||
default:
|
||||
if (info.centroid)
|
||||
interp = "[[centroid_perspective]]";
|
||||
else if (info.sample)
|
||||
interp = "[[sample_perspective]]";
|
||||
break;
|
||||
}
|
||||
for (int c = 0; c < components; c++) {
|
||||
P_IND(ctx, "%s%s ", type, vector_suffix);
|
||||
varying_slot_name(ctx, location, c);
|
||||
P(ctx, " ");
|
||||
varying_slot_semantic(ctx, location, c);
|
||||
P(ctx, " %s;\n", interp);
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate the struct definition for the fragment shader input argument */
|
||||
static void
|
||||
fs_input_block(nir_shader *shader, struct nir_to_msl_ctx *ctx)
|
||||
{
|
||||
P(ctx, "struct FragmentIn {\n");
|
||||
ctx->indentlevel++;
|
||||
u_foreach_bit64(location, shader->info.inputs_read) {
|
||||
struct io_slot_info info = ctx->inputs_info[location];
|
||||
if (info.uses_interpolant)
|
||||
fs_input_interpolant(ctx, location);
|
||||
else
|
||||
fs_input_no_interpolant(ctx, location);
|
||||
}
|
||||
|
||||
/* Enable reading from framebuffer */
|
||||
u_foreach_bit64(location, shader->info.outputs_read) {
|
||||
struct io_slot_info info = ctx->outputs_info[location];
|
||||
const char *type = alu_type_to_string(info.type);
|
||||
const char *vector_suffix = vector_suffixes[info.num_components];
|
||||
P_IND(ctx, "%s%s ", type, vector_suffix);
|
||||
P(ctx, "%s [[%s, raster_order_group(0)]];\n", FS_OUTPUT_NAME[location],
|
||||
FS_OUTPUT_SEMANTIC[location]);
|
||||
}
|
||||
|
||||
ctx->indentlevel--;
|
||||
P(ctx, "};\n");
|
||||
}
|
||||
|
||||
/* Generate the struct definition for the fragment shader return value */
|
||||
static void
|
||||
fs_output_block(nir_shader *shader, struct nir_to_msl_ctx *ctx)
|
||||
{
|
||||
P_IND(ctx, "struct %s {\n", FRAGMENT_OUTPUT_TYPE);
|
||||
ctx->indentlevel++;
|
||||
u_foreach_bit64(location, shader->info.outputs_written) {
|
||||
struct io_slot_info info = ctx->outputs_info[location];
|
||||
const char *type = alu_type_to_string(info.type);
|
||||
const char *vector_suffix = vector_suffixes[info.num_components];
|
||||
P_IND(ctx, "%s%s ", type, vector_suffix);
|
||||
if (location == FRAG_RESULT_DEPTH) {
|
||||
enum gl_frag_depth_layout depth_layout = shader->info.fs.depth_layout;
|
||||
assert(depth_layout_arg[depth_layout]);
|
||||
P(ctx, "%s [[depth(%s)]];\n", FS_OUTPUT_NAME[location],
|
||||
depth_layout_arg[depth_layout]);
|
||||
} else {
|
||||
// TODO: scalarized fs outputs
|
||||
P(ctx, "%s [[%s]];\n", FS_OUTPUT_NAME[location],
|
||||
FS_OUTPUT_SEMANTIC[location]);
|
||||
}
|
||||
}
|
||||
ctx->indentlevel--;
|
||||
P_IND(ctx, "};\n")
|
||||
}
|
||||
|
||||
struct gather_ctx {
|
||||
struct io_slot_info *input;
|
||||
struct io_slot_info *output;
|
||||
};
|
||||
|
||||
static bool
|
||||
msl_nir_gather_io_info(nir_builder *b, nir_intrinsic_instr *intrin, void *data)
|
||||
{
|
||||
struct gather_ctx *ctx = (struct gather_ctx *)data;
|
||||
switch (intrin->intrinsic) {
|
||||
case nir_intrinsic_load_interpolated_input: {
|
||||
unsigned component = nir_intrinsic_component(intrin);
|
||||
struct nir_io_semantics io = nir_intrinsic_io_semantics(intrin);
|
||||
assert(io.num_slots == 1u && "We don't support arrays");
|
||||
|
||||
unsigned location = nir_src_as_uint(intrin->src[1u]) + io.location;
|
||||
ctx->input[location].type = nir_intrinsic_dest_type(intrin);
|
||||
ctx->input[location].num_components =
|
||||
MAX2(ctx->input[location].num_components,
|
||||
intrin->num_components + component);
|
||||
assert(ctx->input[location].num_components <= 4u &&
|
||||
"Cannot have more than a vec4");
|
||||
|
||||
nir_intrinsic_instr *interp_intrin =
|
||||
nir_src_as_intrinsic(intrin->src[0u]);
|
||||
ctx->input[location].interpolation =
|
||||
nir_intrinsic_interp_mode(interp_intrin);
|
||||
ctx->input[location].centroid =
|
||||
interp_intrin->intrinsic == nir_intrinsic_load_barycentric_centroid;
|
||||
ctx->input[location].sample =
|
||||
interp_intrin->intrinsic == nir_intrinsic_load_barycentric_sample;
|
||||
ctx->input[location].uses_interpolant |=
|
||||
interp_intrin->intrinsic == nir_intrinsic_load_barycentric_at_sample ||
|
||||
interp_intrin->intrinsic == nir_intrinsic_load_barycentric_at_offset;
|
||||
break;
|
||||
}
|
||||
case nir_intrinsic_load_input: {
|
||||
unsigned component = nir_intrinsic_component(intrin);
|
||||
struct nir_io_semantics io = nir_intrinsic_io_semantics(intrin);
|
||||
assert(io.num_slots == 1u && "We don't support arrays");
|
||||
|
||||
unsigned location = nir_src_as_uint(intrin->src[0u]) + io.location;
|
||||
ctx->input[location].type = nir_intrinsic_dest_type(intrin);
|
||||
ctx->input[location].interpolation = INTERP_MODE_FLAT;
|
||||
ctx->input[location].num_components =
|
||||
MAX2(ctx->input[location].num_components,
|
||||
intrin->num_components + component);
|
||||
assert(ctx->input[location].num_components <= 4u &&
|
||||
"Cannot have more than a vec4");
|
||||
break;
|
||||
}
|
||||
case nir_intrinsic_load_output: {
|
||||
unsigned component = nir_intrinsic_component(intrin);
|
||||
struct nir_io_semantics io = nir_intrinsic_io_semantics(intrin);
|
||||
assert(io.num_slots == 1u && "We don't support arrays");
|
||||
|
||||
unsigned location = nir_src_as_uint(intrin->src[0u]) + io.location;
|
||||
ctx->output[location].type = nir_intrinsic_dest_type(intrin);
|
||||
ctx->output[location].num_components =
|
||||
MAX2(ctx->output[location].num_components,
|
||||
intrin->num_components + component);
|
||||
assert(ctx->output[location].num_components <= 4u &&
|
||||
"Cannot have more than a vec4");
|
||||
break;
|
||||
}
|
||||
case nir_intrinsic_store_output: {
|
||||
unsigned component = nir_intrinsic_component(intrin);
|
||||
unsigned write_mask = nir_intrinsic_write_mask(intrin);
|
||||
struct nir_io_semantics io = nir_intrinsic_io_semantics(intrin);
|
||||
assert(io.num_slots == 1u && "We don't support arrays");
|
||||
|
||||
/* Due to nir_lower_blend that doesn't generate intrinsics with the same
|
||||
* num_components as destination, we need to compute current store's
|
||||
* num_components using offset and mask. */
|
||||
unsigned num_components = component + 1u;
|
||||
unsigned mask_left_most_index = 0u;
|
||||
for (unsigned i = 0u; i < intrin->num_components; ++i) {
|
||||
if ((write_mask >> i) & 1u)
|
||||
mask_left_most_index = i;
|
||||
}
|
||||
num_components += mask_left_most_index;
|
||||
unsigned location = nir_src_as_uint(intrin->src[1u]) + io.location;
|
||||
ctx->output[location].type = nir_intrinsic_src_type(intrin);
|
||||
ctx->output[location].num_components =
|
||||
MAX3(ctx->output[location].num_components, num_components,
|
||||
intrin->num_components);
|
||||
assert(ctx->output[location].num_components <= 4u &&
|
||||
"Cannot have more than a vec4");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
msl_gather_io_info(struct nir_to_msl_ctx *ctx,
|
||||
struct io_slot_info *info_array_input,
|
||||
struct io_slot_info *info_array_output)
|
||||
{
|
||||
struct gather_ctx gather_ctx = {
|
||||
.input = info_array_input,
|
||||
.output = info_array_output,
|
||||
};
|
||||
nir_shader_intrinsics_pass(ctx->shader, msl_nir_gather_io_info,
|
||||
nir_metadata_all, &gather_ctx);
|
||||
}
|
||||
|
||||
/* Generate all the struct definitions needed for shader I/O */
|
||||
void
|
||||
msl_emit_io_blocks(struct nir_to_msl_ctx *ctx, nir_shader *shader)
|
||||
{
|
||||
switch (ctx->shader->info.stage) {
|
||||
case MESA_SHADER_VERTEX:
|
||||
vs_output_block(shader, ctx);
|
||||
break;
|
||||
case MESA_SHADER_FRAGMENT:
|
||||
fs_input_block(shader, ctx);
|
||||
fs_output_block(shader, ctx);
|
||||
break;
|
||||
case MESA_SHADER_COMPUTE:
|
||||
case MESA_SHADER_TESS_CTRL:
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
// TODO_KOSMICKRISP This should not exist. We need to create input structs in
|
||||
// nir that will later be translated
|
||||
P(ctx, "struct Buffer {\n");
|
||||
ctx->indentlevel++;
|
||||
P_IND(ctx, "uint64_t contents[1];\n"); // TODO_KOSMICKRISP This should not be
|
||||
// a cpu pointer
|
||||
ctx->indentlevel--;
|
||||
P(ctx, "};\n")
|
||||
|
||||
P(ctx, "struct SamplerTable {\n");
|
||||
ctx->indentlevel++;
|
||||
P_IND(ctx, "sampler handles[1024];\n");
|
||||
ctx->indentlevel--;
|
||||
P(ctx, "};\n")
|
||||
}
|
||||
|
||||
void
|
||||
msl_emit_output_var(struct nir_to_msl_ctx *ctx, nir_shader *shader)
|
||||
{
|
||||
switch (shader->info.stage) {
|
||||
case MESA_SHADER_VERTEX:
|
||||
P_IND(ctx, "%s out = {};\n", VERTEX_OUTPUT_TYPE);
|
||||
break;
|
||||
case MESA_SHADER_FRAGMENT:
|
||||
P_IND(ctx, "%s out = {};\n", FRAGMENT_OUTPUT_TYPE);
|
||||
|
||||
/* Load inputs to output */
|
||||
u_foreach_bit64(location, shader->info.outputs_read) {
|
||||
P_IND(ctx, "out.%s = in.%s;\n", FS_OUTPUT_NAME[location],
|
||||
FS_OUTPUT_NAME[location]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
msl_output_name(struct nir_to_msl_ctx *ctx, unsigned location,
|
||||
unsigned component)
|
||||
{
|
||||
P(ctx, "out.")
|
||||
switch (ctx->shader->info.stage) {
|
||||
case MESA_SHADER_VERTEX:
|
||||
varying_slot_name(ctx, location, component);
|
||||
break;
|
||||
case MESA_SHADER_FRAGMENT:
|
||||
P(ctx, "%s", FS_OUTPUT_NAME[location]);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE("Invalid shader stage");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
msl_input_name(struct nir_to_msl_ctx *ctx, unsigned location,
|
||||
unsigned component)
|
||||
{
|
||||
P(ctx, "in.");
|
||||
switch (ctx->shader->info.stage) {
|
||||
case MESA_SHADER_FRAGMENT:
|
||||
varying_slot_name(ctx, location, component);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE("Invalid shader stage");
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
msl_input_num_components(struct nir_to_msl_ctx *ctx, uint32_t location)
|
||||
{
|
||||
if (ctx->shader->info.stage == MESA_SHADER_FRAGMENT &&
|
||||
VARYING_SLOT_INFO[location].scalarized)
|
||||
return 1;
|
||||
else
|
||||
return ctx->inputs_info[location].num_components;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
msl_output_num_components(struct nir_to_msl_ctx *ctx, uint32_t location)
|
||||
{
|
||||
if (ctx->shader->info.stage == MESA_SHADER_VERTEX &&
|
||||
VARYING_SLOT_INFO[location].scalarized)
|
||||
return 1;
|
||||
else
|
||||
return ctx->outputs_info[location].num_components;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
# Copyright 2025 LunarG, Inc.
|
||||
# Copyright 2025 Google LLC
|
||||
# Copyright 2022 Alyssa Rosenzweig
|
||||
# Copyright 2021 Collabora, Ltd.
|
||||
# Copyright 2016 Intel Corporation
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import math
|
||||
|
||||
a = 'a'
|
||||
b = 'b'
|
||||
|
||||
lower = [
|
||||
# Based on the VIR lowering
|
||||
(('f2f16_rtz', 'a@32'),
|
||||
('bcsel', ('flt', ('fabs', a), ('fabs', ('f2f32', ('f2f16_rtne', a)))),
|
||||
('isub', ('f2f16_rtne', a), 1), ('f2f16_rtne', a))),
|
||||
|
||||
# Handle imod behavior with negative values using lowering
|
||||
(('imod', a, b),
|
||||
('bcsel', ('ior', ('ieq', ('irem', a, b), 0), ('ieq', ('isign', a), ('isign', b))),
|
||||
('irem', a, b), ('iadd', ('irem', a, b), b))),
|
||||
]
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-p', '--import-path', required=True)
|
||||
args = parser.parse_args()
|
||||
sys.path.insert(0, args.import_path)
|
||||
run()
|
||||
|
||||
def run():
|
||||
import nir_algebraic # pylint: disable=import-error
|
||||
|
||||
print('#include "msl_private.h"')
|
||||
|
||||
print(nir_algebraic.AlgebraicPass("msl_nir_lower_algebraic_late", lower).render())
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2026 LunarG, Inc.
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include "msl_private.h"
|
||||
#include "nir_to_msl.h"
|
||||
|
||||
#include "nir.h"
|
||||
#include "nir_builder.h"
|
||||
|
||||
static bool
|
||||
uses_per_draw_data(UNUSED nir_builder *b, nir_intrinsic_instr *intr,
|
||||
UNUSED void *data)
|
||||
{
|
||||
return intr->intrinsic == nir_intrinsic_load_per_draw_ptr_kk;
|
||||
}
|
||||
|
||||
bool
|
||||
msl_gather_uses_per_draw_data(nir_shader *nir)
|
||||
{
|
||||
return nir_shader_intrinsics_pass(nir, uses_per_draw_data, nir_metadata_all,
|
||||
NULL);
|
||||
}
|
||||
@@ -0,0 +1,598 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include "msl_private.h"
|
||||
#include "nir_to_msl.h"
|
||||
|
||||
#include "nir.h"
|
||||
#include "nir_builder.h"
|
||||
|
||||
#include "util/format/u_format.h"
|
||||
|
||||
bool
|
||||
msl_nir_vs_remove_point_size_write(nir_builder *b, nir_intrinsic_instr *intrin,
|
||||
void *data)
|
||||
{
|
||||
if (intrin->intrinsic != nir_intrinsic_store_output)
|
||||
return false;
|
||||
|
||||
nir_io_semantics io = nir_intrinsic_io_semantics(intrin);
|
||||
if (io.location == VARYING_SLOT_PSIZ) {
|
||||
return nir_remove_sysval_output(intrin, MESA_SHADER_FRAGMENT);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
msl_nir_fs_remove_depth_write(nir_builder *b, nir_intrinsic_instr *intrin,
|
||||
void *data)
|
||||
{
|
||||
if (intrin->intrinsic != nir_intrinsic_store_output)
|
||||
return false;
|
||||
|
||||
nir_io_semantics io = nir_intrinsic_io_semantics(intrin);
|
||||
if (io.location == FRAG_RESULT_DEPTH) {
|
||||
return nir_remove_sysval_output(intrin, MESA_SHADER_FRAGMENT);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
fs_force_output_type(nir_builder *b, nir_intrinsic_instr *intrin, void *data)
|
||||
{
|
||||
if (intrin->intrinsic != nir_intrinsic_store_output)
|
||||
return false;
|
||||
|
||||
nir_io_semantics io = nir_intrinsic_io_semantics(intrin);
|
||||
if (io.location < FRAG_RESULT_DATA0 || FRAG_RESULT_DATA7 < io.location)
|
||||
return false;
|
||||
|
||||
enum pipe_format *render_target_formats = (enum pipe_format *)data;
|
||||
enum pipe_format format =
|
||||
render_target_formats[io.location - FRAG_RESULT_DATA0];
|
||||
nir_alu_type type = nir_intrinsic_src_type(intrin);
|
||||
if (util_format_is_float(format) || util_format_is_unorm(format)) {
|
||||
if (type & nir_type_uint) {
|
||||
b->cursor = nir_before_instr(&intrin->instr);
|
||||
nir_def *value =
|
||||
nir_u2fN(b, intrin->src[0].ssa, nir_src_bit_size(intrin->src[0]));
|
||||
nir_src_rewrite(&intrin->src[0], value);
|
||||
type ^= (nir_type_float | nir_type_uint);
|
||||
nir_intrinsic_set_src_type(intrin, type);
|
||||
return true;
|
||||
} else if (type & nir_type_int) {
|
||||
b->cursor = nir_before_instr(&intrin->instr);
|
||||
nir_def *value =
|
||||
nir_u2fN(b, intrin->src[0].ssa, nir_src_bit_size(intrin->src[0]));
|
||||
nir_src_rewrite(&intrin->src[0], value);
|
||||
type ^= (nir_type_float | nir_type_int);
|
||||
nir_intrinsic_set_src_type(intrin, type);
|
||||
return true;
|
||||
}
|
||||
} else if (util_format_is_pure_sint(format)) {
|
||||
if (type & nir_type_uint) {
|
||||
type ^= (nir_type_uint | nir_type_int);
|
||||
nir_intrinsic_set_src_type(intrin, type);
|
||||
return true;
|
||||
} else if (type & nir_type_float) {
|
||||
b->cursor = nir_before_instr(&intrin->instr);
|
||||
nir_def *value =
|
||||
nir_f2uN(b, intrin->src[0].ssa, nir_src_bit_size(intrin->src[0]));
|
||||
nir_src_rewrite(&intrin->src[0], value);
|
||||
type ^= (nir_type_float | nir_type_int);
|
||||
nir_intrinsic_set_src_type(intrin, type);
|
||||
return true;
|
||||
}
|
||||
} else if (util_format_is_pure_uint(format)) {
|
||||
if (type & nir_type_int) {
|
||||
type ^= (nir_type_int | nir_type_uint);
|
||||
nir_intrinsic_set_src_type(intrin, type);
|
||||
return true;
|
||||
} else if (type & nir_type_float) {
|
||||
b->cursor = nir_before_instr(&intrin->instr);
|
||||
nir_def *value =
|
||||
nir_f2uN(b, intrin->src[0].ssa, nir_src_bit_size(intrin->src[0]));
|
||||
nir_src_rewrite(&intrin->src[0], value);
|
||||
type ^= (nir_type_float | nir_type_uint);
|
||||
nir_intrinsic_set_src_type(intrin, type);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
msl_nir_fs_force_output_signedness(
|
||||
nir_shader *nir, enum pipe_format render_target_formats[MAX_DRAW_BUFFERS])
|
||||
{
|
||||
return nir_shader_intrinsics_pass(nir, fs_force_output_type,
|
||||
nir_metadata_control_flow,
|
||||
render_target_formats);
|
||||
}
|
||||
|
||||
bool
|
||||
msl_lower_textures(nir_shader *nir)
|
||||
{
|
||||
bool progress = false;
|
||||
nir_lower_tex_options lower_tex_options = {
|
||||
.lower_txp = ~0u,
|
||||
.lower_sampler_lod_bias = true,
|
||||
|
||||
/* We don't use 1D textures because they are really limited in Metal */
|
||||
.lower_1d = true,
|
||||
|
||||
/* Metal does not support tg4 with individual offsets for each sample */
|
||||
.lower_tg4_offsets = true,
|
||||
|
||||
/* Metal does not natively support offsets for texture.read operations */
|
||||
.lower_txf_offset = true,
|
||||
.lower_txd_cube_map = true,
|
||||
};
|
||||
|
||||
NIR_PASS(progress, nir, nir_lower_tex, &lower_tex_options);
|
||||
return progress;
|
||||
}
|
||||
|
||||
static bool
|
||||
replace_sample_id_for_sample_mask(nir_builder *b, nir_intrinsic_instr *intrin,
|
||||
void *data)
|
||||
{
|
||||
if (intrin->intrinsic != nir_intrinsic_load_sample_mask_in)
|
||||
return false;
|
||||
|
||||
nir_def_replace(nir_instr_def(&intrin->instr), (nir_def *)data);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
msl_replace_load_sample_mask_in_for_static_sample_mask(
|
||||
nir_builder *b, nir_intrinsic_instr *intr, void *data)
|
||||
{
|
||||
if (intr->intrinsic != nir_intrinsic_load_sample_mask_in)
|
||||
return false;
|
||||
|
||||
b->cursor = nir_after_instr(&intr->instr);
|
||||
nir_def *static_sample_mask = (nir_def *)data;
|
||||
nir_def *sample_mask = nir_iand(b, &intr->def, static_sample_mask);
|
||||
nir_def_rewrite_uses_after(&intr->def, sample_mask);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
msl_lower_static_sample_mask(nir_shader *nir, uint32_t sample_mask)
|
||||
{
|
||||
/* Only support vertex for now */
|
||||
assert(nir->info.stage == MESA_SHADER_FRAGMENT);
|
||||
|
||||
/* Embed sample mask */
|
||||
nir_function_impl *entrypoint = nir_shader_get_entrypoint(nir);
|
||||
nir_builder b = nir_builder_at(nir_before_impl(entrypoint));
|
||||
|
||||
struct nir_io_semantics io_semantics = {
|
||||
.location = FRAG_RESULT_SAMPLE_MASK,
|
||||
.num_slots = 1u,
|
||||
};
|
||||
nir_def *static_sample_mask = nir_imm_int(&b, sample_mask);
|
||||
nir_store_output(&b, nir_load_sample_mask_in(&b), nir_imm_int(&b, 0u),
|
||||
.base = 0u, .range = 1u, .write_mask = 0x1, .component = 0u,
|
||||
.src_type = nir_type_uint32, .io_semantics = io_semantics);
|
||||
BITSET_SET(nir->info.system_values_read, SYSTEM_VALUE_SAMPLE_MASK_IN);
|
||||
|
||||
nir_shader_intrinsics_pass(
|
||||
nir, msl_replace_load_sample_mask_in_for_static_sample_mask,
|
||||
nir_metadata_control_flow, static_sample_mask);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
msl_ensure_depth_write(nir_shader *nir)
|
||||
{
|
||||
assert(nir->info.stage == MESA_SHADER_FRAGMENT);
|
||||
|
||||
bool has_depth_write =
|
||||
nir->info.outputs_written & BITFIELD64_BIT(FRAG_RESULT_DEPTH);
|
||||
if (!has_depth_write) {
|
||||
nir_variable *depth_var = nir_create_variable_with_location(
|
||||
nir, nir_var_shader_out, FRAG_RESULT_DEPTH, glsl_float_type());
|
||||
|
||||
/* Write to depth at the very beginning */
|
||||
nir_function_impl *entrypoint = nir_shader_get_entrypoint(nir);
|
||||
nir_builder b = nir_builder_at(nir_before_impl(entrypoint));
|
||||
|
||||
nir_deref_instr *depth_deref = nir_build_deref_var(&b, depth_var);
|
||||
nir_def *position = nir_build_frag_coord(&b, 3);
|
||||
nir_store_deref(&b, depth_deref, nir_channel(&b, position, 2u),
|
||||
0xFFFFFFFF);
|
||||
|
||||
nir->info.outputs_written |= BITFIELD64_BIT(FRAG_RESULT_DEPTH);
|
||||
nir->info.fs.depth_layout = FRAG_DEPTH_LAYOUT_ANY;
|
||||
return nir_progress(true, entrypoint, nir_metadata_control_flow);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
msl_ensure_vertex_position_output(nir_shader *nir)
|
||||
{
|
||||
assert(nir->info.stage == MESA_SHADER_VERTEX);
|
||||
|
||||
bool has_position_write =
|
||||
nir->info.outputs_written & BITFIELD64_BIT(VARYING_SLOT_POS);
|
||||
if (!has_position_write) {
|
||||
/* Write to depth at the very beginning */
|
||||
nir_function_impl *entrypoint = nir_shader_get_entrypoint(nir);
|
||||
nir_builder b = nir_builder_at(nir_before_impl(entrypoint));
|
||||
|
||||
struct nir_io_semantics io_semantics = {
|
||||
.location = VARYING_SLOT_POS,
|
||||
.num_slots = 4u,
|
||||
};
|
||||
nir_def *zero = nir_imm_float(&b, 0.0f);
|
||||
nir_store_output(
|
||||
&b, nir_vec4(&b, zero, zero, zero, zero), nir_imm_int(&b, 0u),
|
||||
.base = 0u, .range = 4u, .write_mask = 0xf, .component = 0u,
|
||||
.src_type = nir_type_float32, .io_semantics = io_semantics);
|
||||
|
||||
nir->info.outputs_written |= BITFIELD64_BIT(VARYING_SLOT_POS);
|
||||
return nir_progress(true, entrypoint, nir_metadata_control_flow);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
msl_ensure_vertex_point_size_output(nir_shader *nir)
|
||||
{
|
||||
assert(nir->info.stage == MESA_SHADER_VERTEX);
|
||||
|
||||
bool has_point_size_write =
|
||||
nir->info.outputs_written & BITFIELD64_BIT(VARYING_SLOT_PSIZ);
|
||||
if (!has_point_size_write) {
|
||||
nir_function_impl *entrypoint = nir_shader_get_entrypoint(nir);
|
||||
nir_builder b = nir_builder_at(nir_before_impl(entrypoint));
|
||||
|
||||
struct nir_io_semantics io_semantics = {
|
||||
.location = VARYING_SLOT_PSIZ,
|
||||
.num_slots = 1u,
|
||||
};
|
||||
nir_store_output(&b, nir_imm_float(&b, 1.0f), nir_imm_int(&b, 0u),
|
||||
.base = 0u, .range = 1u, .write_mask = 0x1,
|
||||
.component = 0u, .src_type = nir_type_float32,
|
||||
.io_semantics = io_semantics);
|
||||
nir->info.outputs_written |= BITFIELD64_BIT(VARYING_SLOT_PSIZ);
|
||||
return nir_progress(true, entrypoint, nir_metadata_control_flow);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
msl_fs_io_types(nir_builder *b, nir_intrinsic_instr *intr, void *data)
|
||||
{
|
||||
if (intr->intrinsic == nir_intrinsic_load_input) {
|
||||
struct nir_io_semantics io = nir_intrinsic_io_semantics(intr);
|
||||
if (io.location == VARYING_SLOT_VIEWPORT) {
|
||||
nir_intrinsic_set_dest_type(intr, nir_type_uint32);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (intr->intrinsic == nir_intrinsic_store_output) {
|
||||
struct nir_io_semantics io = nir_intrinsic_io_semantics(intr);
|
||||
if (io.location == FRAG_RESULT_SAMPLE_MASK) {
|
||||
nir_intrinsic_set_src_type(intr, nir_type_uint32);
|
||||
return true;
|
||||
} else if (io.location == FRAG_RESULT_STENCIL) {
|
||||
nir_alu_type type = nir_intrinsic_src_type(intr);
|
||||
nir_intrinsic_set_src_type(
|
||||
intr, nir_type_uint | nir_alu_type_get_type_size(type));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (intr->intrinsic == nir_intrinsic_load_output) {
|
||||
struct nir_io_semantics io = nir_intrinsic_io_semantics(intr);
|
||||
if (io.location == FRAG_RESULT_STENCIL) {
|
||||
nir_alu_type type = nir_intrinsic_dest_type(intr);
|
||||
nir_intrinsic_set_dest_type(
|
||||
intr, nir_type_uint | nir_alu_type_get_type_size(type));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
msl_nir_fs_io_types(nir_shader *nir)
|
||||
{
|
||||
assert(nir->info.stage == MESA_SHADER_FRAGMENT);
|
||||
return nir_shader_intrinsics_pass(nir, msl_fs_io_types, nir_metadata_all,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static bool
|
||||
msl_vs_io_types(nir_builder *b, nir_intrinsic_instr *intr, void *data)
|
||||
{
|
||||
if (intr->intrinsic == nir_intrinsic_store_output) {
|
||||
struct nir_io_semantics io = nir_intrinsic_io_semantics(intr);
|
||||
if (io.location == VARYING_SLOT_LAYER ||
|
||||
io.location == VARYING_SLOT_VIEWPORT) {
|
||||
nir_intrinsic_set_src_type(intr, nir_type_uint32);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
msl_nir_vs_io_types(nir_shader *nir)
|
||||
{
|
||||
assert(nir->info.stage == MESA_SHADER_VERTEX);
|
||||
return nir_shader_intrinsics_pass(nir, msl_vs_io_types, nir_metadata_all,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static bool
|
||||
fake_guard_for_discards(nir_builder *b, nir_intrinsic_instr *intrin, void *data)
|
||||
{
|
||||
if (intrin->intrinsic != nir_intrinsic_demote)
|
||||
return false;
|
||||
|
||||
b->cursor = nir_before_instr(&intrin->instr);
|
||||
nir_def *helper = nir_is_helper_invocation(b, 1);
|
||||
nir_demote_if(b, nir_inot(b, helper));
|
||||
nir_instr_remove(&intrin->instr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
msl_nir_fake_guard_for_discards(struct nir_shader *nir)
|
||||
{
|
||||
assert(nir->info.stage == MESA_SHADER_FRAGMENT);
|
||||
|
||||
/* No side effects, no lowering needed */
|
||||
if (!nir->info.writes_memory)
|
||||
return false;
|
||||
|
||||
return nir_shader_intrinsics_pass(nir, fake_guard_for_discards,
|
||||
nir_metadata_control_flow, NULL);
|
||||
}
|
||||
|
||||
/* Returns true if gl_SampleID is required. */
|
||||
static bool
|
||||
gather_fs_input_interpolant_usage(nir_builder *b, nir_intrinsic_instr *intr,
|
||||
void *data)
|
||||
{
|
||||
if (intr->intrinsic != nir_intrinsic_load_interpolated_input)
|
||||
return false;
|
||||
|
||||
bool *uses_interpolant = (bool *)data;
|
||||
struct nir_io_semantics io = nir_intrinsic_io_semantics(intr);
|
||||
nir_intrinsic_instr *interpolation = nir_src_as_intrinsic(intr->src[0]);
|
||||
bool at_sample =
|
||||
interpolation->intrinsic == nir_intrinsic_load_barycentric_at_sample;
|
||||
uses_interpolant[io.location] |=
|
||||
at_sample ||
|
||||
interpolation->intrinsic == nir_intrinsic_load_barycentric_at_offset;
|
||||
return at_sample;
|
||||
}
|
||||
|
||||
static bool
|
||||
lower_sample_shading(nir_builder *b, nir_intrinsic_instr *intr, void *data)
|
||||
{
|
||||
if (intr->intrinsic == nir_intrinsic_load_frag_coord) {
|
||||
b->cursor = nir_after_instr(&intr->instr);
|
||||
nir_def *offset =
|
||||
nir_fadd(b, nir_load_sample_pos_from_id(b, 32u, nir_load_sample_id(b)),
|
||||
nir_imm_vec2(b, -0.5f, -0.5f));
|
||||
nir_def *sample_position =
|
||||
nir_fadd(b, &intr->def, nir_pad_vector_imm_int(b, offset, 0u, 4u));
|
||||
nir_def_rewrite_uses_after(&intr->def, sample_position);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (intr->intrinsic == nir_intrinsic_load_sample_mask_in) {
|
||||
b->cursor = nir_after_instr(&intr->instr);
|
||||
nir_def *sample_id = nir_load_sample_id(b);
|
||||
nir_def *sample_bit = nir_ishl(b, nir_imm_int(b, 1), sample_id);
|
||||
nir_def *sample_mask_bit = nir_iand(b, &intr->def, sample_bit);
|
||||
nir_def_rewrite_uses_after(&intr->def, sample_mask_bit);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (intr->intrinsic != nir_intrinsic_load_interpolated_input)
|
||||
return false;
|
||||
|
||||
bool *uses_interpolant = (bool *)data;
|
||||
struct nir_io_semantics io = nir_intrinsic_io_semantics(intr);
|
||||
nir_intrinsic_instr *interpolation = nir_src_as_intrinsic(intr->src[0]);
|
||||
if (!uses_interpolant[io.location] ||
|
||||
interpolation->intrinsic != nir_intrinsic_load_barycentric_sample)
|
||||
return false;
|
||||
|
||||
b->cursor = nir_before_instr(&intr->instr);
|
||||
nir_def *def = nir_load_barycentric_at_sample(
|
||||
b, intr->def.bit_size, nir_load_sample_id(b),
|
||||
.interp_mode = nir_intrinsic_interp_mode(interpolation));
|
||||
nir_def_rewrite_uses_after(&interpolation->def, def);
|
||||
nir_instr_remove(&interpolation->instr);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
msl_nir_lower_sample_shading(nir_shader *nir)
|
||||
{
|
||||
assert(nir->info.stage == MESA_SHADER_FRAGMENT);
|
||||
|
||||
bool uses_interpolant[NUM_TOTAL_VARYING_SLOTS] = {};
|
||||
|
||||
if (nir_shader_intrinsics_pass(nir, gather_fs_input_interpolant_usage,
|
||||
nir_metadata_all, uses_interpolant))
|
||||
BITSET_SET(nir->info.system_values_read, SYSTEM_VALUE_SAMPLE_ID);
|
||||
|
||||
return nir_shader_intrinsics_pass(
|
||||
nir, lower_sample_shading, nir_metadata_control_flow, uses_interpolant);
|
||||
}
|
||||
|
||||
static bool
|
||||
lower_clip_cull_distance_write(nir_builder *b, nir_intrinsic_instr *intr,
|
||||
UNUSED void *data)
|
||||
{
|
||||
if (intr->intrinsic != nir_intrinsic_store_output)
|
||||
return false;
|
||||
|
||||
nir_io_semantics sem = nir_intrinsic_io_semantics(intr);
|
||||
if (sem.location != VARYING_SLOT_CLIP_DIST0 &&
|
||||
sem.location != VARYING_SLOT_CLIP_DIST1 &&
|
||||
sem.location != VARYING_SLOT_CULL_DIST0 &&
|
||||
sem.location != VARYING_SLOT_CULL_DIST1)
|
||||
return false;
|
||||
|
||||
assert(nir_src_num_components(intr->src[0]) == 1 && "must be scalarized");
|
||||
|
||||
signed location = sem.location + nir_src_as_uint(intr->src[1]);
|
||||
|
||||
if (sem.location == VARYING_SLOT_CLIP_DIST0 ||
|
||||
sem.location == VARYING_SLOT_CLIP_DIST1) {
|
||||
/* Clip distance, add write to MSL clip_distance output */
|
||||
unsigned component = (location - VARYING_SLOT_CLIP_DIST0) * 4 +
|
||||
nir_intrinsic_component(intr);
|
||||
|
||||
b->cursor = nir_after_instr(&intr->instr);
|
||||
nir_store_clip_distance_kk(b, intr->src[0].ssa, .base = component);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sem.location == VARYING_SLOT_CULL_DIST0 ||
|
||||
sem.location == VARYING_SLOT_CULL_DIST1) {
|
||||
/* Cull distance, add write to cull primitive output */
|
||||
unsigned component = (location - VARYING_SLOT_CULL_DIST0) * 4 +
|
||||
nir_intrinsic_component(intr);
|
||||
|
||||
b->cursor = nir_before_instr(&intr->instr);
|
||||
nir_def *offs = nir_imm_int(b, component / 4);
|
||||
nir_def *v = nir_b2f32(b, nir_fge_imm(b, intr->src[0].ssa, 0.0));
|
||||
|
||||
nir_store_output(b, v, offs, .component = component % 4,
|
||||
.src_type = nir_type_float32,
|
||||
.io_semantics.location = VARYING_SLOT_CULL_PRIMITIVE,
|
||||
.io_semantics.num_slots = 2);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
msl_nir_lower_clip_cull_distance_vs(nir_shader *s)
|
||||
{
|
||||
if (s->info.clip_distance_array_size == 0 &&
|
||||
s->info.cull_distance_array_size == 0)
|
||||
return false;
|
||||
|
||||
nir_shader_intrinsics_pass(s, lower_clip_cull_distance_write,
|
||||
nir_metadata_control_flow, NULL);
|
||||
|
||||
if (s->info.cull_distance_array_size > 0)
|
||||
s->info.outputs_written |=
|
||||
BITFIELD64_RANGE(VARYING_SLOT_CULL_PRIMITIVE,
|
||||
DIV_ROUND_UP(s->info.cull_distance_array_size, 4));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
msl_nir_lower_cull_distance_fs(nir_shader *s, unsigned nr_distances)
|
||||
{
|
||||
assert(s->info.stage == MESA_SHADER_FRAGMENT);
|
||||
|
||||
if (nr_distances == 0)
|
||||
return false;
|
||||
|
||||
nir_builder b_ =
|
||||
nir_builder_at(nir_before_impl(nir_shader_get_entrypoint(s)));
|
||||
nir_builder *b = &b_;
|
||||
|
||||
/* Test each half-space */
|
||||
nir_def *culled = nir_imm_false(b);
|
||||
|
||||
for (unsigned i = 0; i < nr_distances; ++i) {
|
||||
/* Load the cull primitive input for this cull distance */
|
||||
nir_def *baryc = nir_load_barycentric_pixel(
|
||||
b, 32, .interp_mode = INTERP_MODE_NOPERSPECTIVE);
|
||||
nir_def *cull = nir_load_interpolated_input(
|
||||
b, 1, 32, baryc, nir_imm_int(b, 0), .component = i & 3,
|
||||
.io_semantics.location = VARYING_SLOT_CULL_PRIMITIVE + (i / 4),
|
||||
.io_semantics.num_slots = nr_distances / 4);
|
||||
|
||||
/* When the cull distance is negative in the vertex shader, the resulting
|
||||
* cull primitive output is zero, otherwise it is one. Thus, the
|
||||
* interpolated value will be zero only if all of its vertices had
|
||||
* negative cull distances, indicating the primitive should be called.
|
||||
* Note that, since the value is interpolated at the pixel center, we
|
||||
* don't have to worry about corner values. */
|
||||
culled = nir_ior(b, culled, nir_ball(b, nir_feq_imm(b, cull, 0)));
|
||||
}
|
||||
|
||||
/* Emulate primitive culling by discarding fragments */
|
||||
nir_demote_if(b, culled);
|
||||
|
||||
s->info.inputs_read |= BITFIELD64_RANGE(VARYING_SLOT_CULL_PRIMITIVE,
|
||||
DIV_ROUND_UP(nr_distances, 4));
|
||||
|
||||
s->info.fs.uses_discard = true;
|
||||
return nir_progress(true, b->impl, nir_metadata_control_flow);
|
||||
}
|
||||
|
||||
/* Scalarize stores to CLIP_DIST* varyings */
|
||||
static bool
|
||||
scalarize_clip_cull_distance_filter(const nir_intrinsic_instr *intrin,
|
||||
UNUSED const void *_data)
|
||||
{
|
||||
if (intrin->intrinsic != nir_intrinsic_store_output)
|
||||
return false;
|
||||
nir_io_semantics semantics = nir_intrinsic_io_semantics(intrin);
|
||||
return semantics.location == VARYING_SLOT_CLIP_DIST0 ||
|
||||
semantics.location == VARYING_SLOT_CLIP_DIST1 ||
|
||||
semantics.location == VARYING_SLOT_CULL_DIST0 ||
|
||||
semantics.location == VARYING_SLOT_CULL_DIST1;
|
||||
}
|
||||
|
||||
void
|
||||
msl_nir_lower_clip_cull_distance(nir_shader *nir, unsigned num_cull_distances)
|
||||
{
|
||||
NIR_PASS(_, nir, nir_lower_io_to_scalar, nir_var_shader_out,
|
||||
scalarize_clip_cull_distance_filter, NULL);
|
||||
NIR_PASS(_, nir, nir_separate_merged_clip_cull_io);
|
||||
if (nir->info.stage == MESA_SHADER_FRAGMENT)
|
||||
NIR_PASS(_, nir, msl_nir_lower_cull_distance_fs, num_cull_distances);
|
||||
else
|
||||
NIR_PASS(_, nir, msl_nir_lower_clip_cull_distance_vs);
|
||||
}
|
||||
|
||||
static bool
|
||||
lower_instance_id(nir_builder *b, nir_intrinsic_instr *intr, UNUSED void *data)
|
||||
{
|
||||
if (intr->intrinsic != nir_intrinsic_load_instance_id)
|
||||
return false;
|
||||
|
||||
b->cursor = nir_after_instr(&intr->instr);
|
||||
nir_def *base_instance = nir_load_base_instance(b);
|
||||
nir_def *instance_id = nir_isub(b, &intr->def, base_instance);
|
||||
nir_def_rewrite_uses_after(&intr->def, instance_id);
|
||||
BITSET_SET(b->shader->info.system_values_read, SYSTEM_VALUE_BASE_INSTANCE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
msl_nir_lower_instance_id(nir_shader *nir)
|
||||
{
|
||||
return nir_shader_intrinsics_pass(nir, lower_instance_id,
|
||||
nir_metadata_control_flow, NULL);
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2023 Valve Corporation
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "msl_private.h"
|
||||
#include "nir.h"
|
||||
#include "nir_builder.h"
|
||||
|
||||
static bool
|
||||
needs_bool_widening(nir_intrinsic_instr *intrin)
|
||||
{
|
||||
switch (intrin->intrinsic) {
|
||||
case nir_intrinsic_read_invocation:
|
||||
case nir_intrinsic_read_first_invocation:
|
||||
case nir_intrinsic_reduce:
|
||||
case nir_intrinsic_quad_broadcast:
|
||||
case nir_intrinsic_quad_swap_horizontal:
|
||||
case nir_intrinsic_quad_swap_vertical:
|
||||
case nir_intrinsic_quad_swap_diagonal:
|
||||
case nir_intrinsic_rotate:
|
||||
case nir_intrinsic_shuffle:
|
||||
case nir_intrinsic_shuffle_down:
|
||||
case nir_intrinsic_shuffle_up:
|
||||
case nir_intrinsic_shuffle_xor:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
lower_bool_ops(nir_builder *b, nir_intrinsic_instr *intrin, void *_unused)
|
||||
{
|
||||
if (!needs_bool_widening(intrin))
|
||||
return false;
|
||||
|
||||
if (intrin->def.bit_size != 1)
|
||||
return false;
|
||||
|
||||
b->cursor = nir_before_instr(&intrin->instr);
|
||||
nir_def *widen = nir_b2i32(b, intrin->src[0].ssa);
|
||||
nir_src_rewrite(&intrin->src[0], widen);
|
||||
intrin->def.bit_size = 32;
|
||||
b->cursor = nir_after_instr(&intrin->instr);
|
||||
nir_def *narrow = nir_b2b1(b, &intrin->def);
|
||||
nir_def_rewrite_uses_after(&intrin->def, narrow);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
msl_nir_lower_subgroups(nir_shader *nir)
|
||||
{
|
||||
const nir_lower_subgroups_options subgroups_options = {
|
||||
.subgroup_size = 32,
|
||||
.ballot_bit_size = 32,
|
||||
.ballot_components = 1,
|
||||
.lower_subgroup_masks = true,
|
||||
.lower_vote_ieq = true,
|
||||
.lower_vote_feq = true,
|
||||
.lower_vote_bool_eq = true,
|
||||
.lower_inverse_ballot = true,
|
||||
/* Metal requires relative shuffle operations to have uniform delta */
|
||||
.lower_relative_shuffle = true,
|
||||
/* Metal reduce operations do not support certain types or cluster size */
|
||||
.lower_reduce = true,
|
||||
/* Metal rotate operations do not support cluster size */
|
||||
.lower_rotate_clustered_to_shuffle = true,
|
||||
};
|
||||
NIR_PASS(_, nir, nir_lower_subgroups, &subgroups_options);
|
||||
NIR_PASS(_, nir, nir_shader_intrinsics_pass, lower_bool_ops,
|
||||
nir_metadata_control_flow, NULL);
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/string_buffer.h"
|
||||
#include "nir.h"
|
||||
|
||||
struct io_slot_info {
|
||||
nir_alu_type type;
|
||||
uint32_t interpolation;
|
||||
unsigned num_components;
|
||||
bool centroid;
|
||||
bool sample;
|
||||
bool uses_interpolant;
|
||||
};
|
||||
|
||||
struct nir_to_msl_ctx {
|
||||
FILE *output;
|
||||
struct hash_table *types;
|
||||
nir_shader *shader;
|
||||
struct _mesa_string_buffer *text;
|
||||
uint64_t disabled_workarounds;
|
||||
bool uses_per_draw_data;
|
||||
unsigned short indentlevel;
|
||||
struct io_slot_info inputs_info[NUM_TOTAL_VARYING_SLOTS];
|
||||
struct io_slot_info outputs_info[NUM_TOTAL_VARYING_SLOTS];
|
||||
};
|
||||
|
||||
#define P_IND(ctx, ...) \
|
||||
do { \
|
||||
for (unsigned i = 0; i < (ctx)->indentlevel; i++) \
|
||||
_mesa_string_buffer_append((ctx)->text, " "); \
|
||||
_mesa_string_buffer_printf((ctx)->text, __VA_ARGS__); \
|
||||
} while (0);
|
||||
|
||||
#define P(ctx, ...) _mesa_string_buffer_printf((ctx)->text, __VA_ARGS__);
|
||||
|
||||
#define P_INDENT(ctx) \
|
||||
do { \
|
||||
for (unsigned i = 0; i < (ctx)->indentlevel; i++) \
|
||||
_mesa_string_buffer_append((ctx)->text, " "); \
|
||||
} while (0)
|
||||
|
||||
/* Perform type inference. The returned value is a
|
||||
* map from nir_def* to base type.*/
|
||||
|
||||
struct hash_table *msl_infer_types(nir_shader *shader);
|
||||
|
||||
const char *msl_type_for_def(struct hash_table *types, nir_def *def);
|
||||
|
||||
const char *msl_uint_type(uint8_t bit_size, uint8_t num_components);
|
||||
|
||||
const char *msl_type_for_src(struct hash_table *types, nir_src *src);
|
||||
|
||||
const char *msl_bitcast_for_src(struct hash_table *types, nir_src *src);
|
||||
|
||||
void msl_src_as_const(struct nir_to_msl_ctx *ctx, nir_src *src);
|
||||
|
||||
void msl_emit_io_blocks(struct nir_to_msl_ctx *ctx, nir_shader *shader);
|
||||
|
||||
void msl_emit_output_var(struct nir_to_msl_ctx *ctx, nir_shader *shader);
|
||||
|
||||
void msl_gather_io_info(struct nir_to_msl_ctx *ctx,
|
||||
struct io_slot_info *info_array_input,
|
||||
struct io_slot_info *info_array_output);
|
||||
|
||||
void msl_input_name(struct nir_to_msl_ctx *ctx, unsigned location,
|
||||
unsigned component);
|
||||
|
||||
void msl_output_name(struct nir_to_msl_ctx *ctx, unsigned location,
|
||||
unsigned component);
|
||||
|
||||
uint32_t msl_input_num_components(struct nir_to_msl_ctx *ctx,
|
||||
uint32_t location);
|
||||
|
||||
uint32_t msl_output_num_components(struct nir_to_msl_ctx *ctx,
|
||||
uint32_t location);
|
||||
|
||||
bool msl_src_is_float(struct nir_to_msl_ctx *ctx, nir_src *src);
|
||||
bool msl_def_is_sampler(struct nir_to_msl_ctx *ctx, nir_def *def);
|
||||
|
||||
void msl_nir_lower_subgroups(nir_shader *nir);
|
||||
|
||||
bool msl_nir_lower_algebraic_late(nir_shader *shader);
|
||||
@@ -0,0 +1,885 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "vulkan/vulkan_core.h"
|
||||
#include "msl_private.h"
|
||||
|
||||
typedef enum ti_type {
|
||||
/* We haven't been able to assign a type yet */
|
||||
TYPE_NONE = 0,
|
||||
/* All we know is that this is used in I/O, we
|
||||
* can treat it as an opaque value (i.e. uint) */
|
||||
TYPE_GENERIC_DATA,
|
||||
/* A generic int used in ALU operations but also can a bool for bitwise ops */
|
||||
TYPE_GENERIC_INT_OR_BOOL,
|
||||
/* A generic int used in ALU operations that can be int or uint */
|
||||
TYPE_GENERIC_INT,
|
||||
/* These are actual concrete types. */
|
||||
TYPE_INT,
|
||||
TYPE_UINT,
|
||||
TYPE_BOOL,
|
||||
TYPE_FLOAT,
|
||||
TYPE_SAMPLER,
|
||||
} ti_type;
|
||||
|
||||
static ti_type
|
||||
unify_types(ti_type t1, ti_type t2)
|
||||
{
|
||||
ti_type generic = MIN2(t1, t2);
|
||||
ti_type specific = MAX2(t1, t2);
|
||||
if (t1 == t2)
|
||||
return TYPE_NONE;
|
||||
// NONE or GENERIC_DATA can be upgraded into any concrete type
|
||||
if (generic == TYPE_GENERIC_DATA || generic == TYPE_NONE)
|
||||
return specific;
|
||||
if ((generic == TYPE_GENERIC_INT_OR_BOOL) &&
|
||||
((specific == TYPE_INT) || (specific == TYPE_UINT) ||
|
||||
(specific == TYPE_BOOL)))
|
||||
return specific;
|
||||
if ((generic == TYPE_GENERIC_INT) &&
|
||||
((specific == TYPE_INT) || (specific == TYPE_UINT)))
|
||||
return specific;
|
||||
return TYPE_NONE;
|
||||
}
|
||||
|
||||
static ti_type
|
||||
ti_type_from_nir(nir_alu_type nir_type)
|
||||
{
|
||||
switch (nir_alu_type_get_base_type(nir_type)) {
|
||||
case nir_type_int:
|
||||
return TYPE_INT;
|
||||
case nir_type_uint:
|
||||
return TYPE_UINT;
|
||||
case nir_type_float:
|
||||
return TYPE_FLOAT;
|
||||
case nir_type_bool:
|
||||
return TYPE_BOOL;
|
||||
default:
|
||||
assert(0);
|
||||
return TYPE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static ti_type
|
||||
ti_type_from_pipe_format(enum pipe_format format)
|
||||
{
|
||||
switch (format) {
|
||||
case PIPE_FORMAT_R16_FLOAT:
|
||||
case PIPE_FORMAT_R32_FLOAT:
|
||||
return TYPE_FLOAT;
|
||||
case PIPE_FORMAT_R8_UINT:
|
||||
case PIPE_FORMAT_R16_UINT:
|
||||
case PIPE_FORMAT_R32_UINT:
|
||||
case PIPE_FORMAT_R64_UINT:
|
||||
return TYPE_UINT;
|
||||
case PIPE_FORMAT_R8_SINT:
|
||||
case PIPE_FORMAT_R16_SINT:
|
||||
case PIPE_FORMAT_R32_SINT:
|
||||
case PIPE_FORMAT_R64_SINT:
|
||||
return TYPE_INT;
|
||||
default:
|
||||
assert(0);
|
||||
return 0u;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
set_type(struct hash_table *types, void *key, ti_type type)
|
||||
{
|
||||
// convert nir_type
|
||||
_mesa_hash_table_insert(types, key, (void *)type);
|
||||
}
|
||||
|
||||
static ti_type
|
||||
get_type(struct hash_table *types, void *key)
|
||||
{
|
||||
struct hash_entry *entry = _mesa_hash_table_search(types, key);
|
||||
if (!entry)
|
||||
return TYPE_NONE;
|
||||
return (ti_type)(intptr_t)(entry->data);
|
||||
}
|
||||
|
||||
static bool
|
||||
update_instr_type(struct hash_table *types, nir_instr *instr, ti_type type)
|
||||
{
|
||||
if (instr->type == nir_instr_type_alu) {
|
||||
nir_alu_instr *alu = nir_instr_as_alu(instr);
|
||||
switch (alu->op) {
|
||||
case nir_op_iadd:
|
||||
case nir_op_isub:
|
||||
case nir_op_ishl:
|
||||
case nir_op_iand:
|
||||
case nir_op_ior:
|
||||
case nir_op_ixor:
|
||||
set_type(types, &alu->def, type);
|
||||
set_type(types, &alu->src[0].src, type);
|
||||
set_type(types, &alu->src[1].src, type);
|
||||
return true;
|
||||
case nir_op_inot:
|
||||
set_type(types, &alu->def, type);
|
||||
set_type(types, &alu->src[0].src, type);
|
||||
return true;
|
||||
case nir_op_ieq:
|
||||
case nir_op_ine:
|
||||
set_type(types, &alu->src[0].src, type);
|
||||
set_type(types, &alu->src[1].src, type);
|
||||
return true;
|
||||
case nir_op_bcsel:
|
||||
set_type(types, &alu->def, type);
|
||||
set_type(types, &alu->src[1].src, type);
|
||||
set_type(types, &alu->src[2].src, type);
|
||||
return true;
|
||||
case nir_op_mov:
|
||||
case nir_op_vec2:
|
||||
case nir_op_vec3:
|
||||
case nir_op_vec4:
|
||||
set_type(types, &alu->def, type);
|
||||
for (int i = 0; i < nir_op_infos[alu->op].num_inputs; i++)
|
||||
set_type(types, &alu->src[i].src, type);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} else if (instr->type == nir_instr_type_intrinsic) {
|
||||
nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr);
|
||||
nir_intrinsic_info info = nir_intrinsic_infos[intr->intrinsic];
|
||||
switch (intr->intrinsic) {
|
||||
case nir_intrinsic_load_reg:
|
||||
set_type(types, &intr->def, type);
|
||||
set_type(types, &intr->src[0], type);
|
||||
return true;
|
||||
case nir_intrinsic_store_reg:
|
||||
set_type(types, &intr->src[0], type);
|
||||
set_type(types, &intr->src[1], type);
|
||||
return true;
|
||||
case nir_intrinsic_decl_reg:
|
||||
set_type(types, &intr->def, type);
|
||||
return true;
|
||||
case nir_intrinsic_load_global:
|
||||
case nir_intrinsic_load_global_constant:
|
||||
case nir_intrinsic_load_global_constant_bounded:
|
||||
case nir_intrinsic_load_global_constant_offset:
|
||||
case nir_intrinsic_load_push_constant:
|
||||
set_type(types, &intr->def, type);
|
||||
return true;
|
||||
/* Scratch and shared are always UINT */
|
||||
case nir_intrinsic_load_scratch:
|
||||
case nir_intrinsic_store_scratch:
|
||||
case nir_intrinsic_load_shared:
|
||||
case nir_intrinsic_store_shared:
|
||||
case nir_intrinsic_load_frag_coord:
|
||||
return false;
|
||||
case nir_intrinsic_store_global:
|
||||
set_type(types, &intr->src[0], type);
|
||||
return true;
|
||||
case nir_intrinsic_read_first_invocation:
|
||||
case nir_intrinsic_read_invocation:
|
||||
case nir_intrinsic_quad_vote_all:
|
||||
case nir_intrinsic_quad_vote_any:
|
||||
case nir_intrinsic_quad_broadcast:
|
||||
case nir_intrinsic_quad_swap_horizontal:
|
||||
case nir_intrinsic_quad_swap_vertical:
|
||||
case nir_intrinsic_quad_swap_diagonal:
|
||||
case nir_intrinsic_rotate:
|
||||
case nir_intrinsic_shuffle:
|
||||
case nir_intrinsic_shuffle_down:
|
||||
case nir_intrinsic_shuffle_up:
|
||||
case nir_intrinsic_shuffle_xor:
|
||||
set_type(types, &intr->src[0], type);
|
||||
set_type(types, &intr->def, type);
|
||||
return true;
|
||||
default:
|
||||
if (info.has_dest && info.num_srcs == 0) {
|
||||
set_type(types, &intr->def, type);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
infer_types_from_alu(struct hash_table *types, nir_alu_instr *alu)
|
||||
{
|
||||
// for most types, we infer the type from the nir_op_info,
|
||||
// but some ALU instructions are the same for int and uint. Those
|
||||
// have their sources and defs get marked by TYPE_GENERIC_INT.
|
||||
switch (alu->op) {
|
||||
case nir_op_iadd:
|
||||
case nir_op_isub:
|
||||
case nir_op_ishl:
|
||||
// (N, N) -> N
|
||||
set_type(types, &alu->def, TYPE_GENERIC_INT);
|
||||
set_type(types, &alu->src[0].src, TYPE_GENERIC_INT);
|
||||
set_type(types, &alu->src[1].src, TYPE_GENERIC_INT);
|
||||
break;
|
||||
case nir_op_iand:
|
||||
case nir_op_ior:
|
||||
case nir_op_ixor:
|
||||
set_type(types, &alu->def, TYPE_GENERIC_INT_OR_BOOL);
|
||||
set_type(types, &alu->src[0].src, TYPE_GENERIC_INT_OR_BOOL);
|
||||
set_type(types, &alu->src[1].src, TYPE_GENERIC_INT_OR_BOOL);
|
||||
break;
|
||||
case nir_op_inot:
|
||||
// N -> N
|
||||
set_type(types, &alu->def, TYPE_GENERIC_INT_OR_BOOL);
|
||||
set_type(types, &alu->src[0].src, TYPE_GENERIC_INT_OR_BOOL);
|
||||
break;
|
||||
case nir_op_ieq:
|
||||
case nir_op_ine:
|
||||
// (N, N) -> bool
|
||||
set_type(types, &alu->def, TYPE_BOOL);
|
||||
set_type(types, &alu->src[0].src, TYPE_GENERIC_INT_OR_BOOL);
|
||||
set_type(types, &alu->src[1].src, TYPE_GENERIC_INT_OR_BOOL);
|
||||
break;
|
||||
case nir_op_bcsel:
|
||||
// (bool, T, T) -> T
|
||||
set_type(types, &alu->def, TYPE_GENERIC_DATA);
|
||||
set_type(types, &alu->src[0].src, TYPE_BOOL);
|
||||
set_type(types, &alu->src[1].src, TYPE_GENERIC_DATA);
|
||||
set_type(types, &alu->src[2].src, TYPE_GENERIC_DATA);
|
||||
break;
|
||||
// These don't provide any type information, we rely on type propagation
|
||||
// to fill in the type data
|
||||
case nir_op_mov:
|
||||
case nir_op_vec2:
|
||||
case nir_op_vec3:
|
||||
case nir_op_vec4:
|
||||
break;
|
||||
/* We don't have 32-bit width boolean, those are uints. */
|
||||
case nir_op_b2b32:
|
||||
set_type(types, &alu->def, TYPE_UINT);
|
||||
set_type(types, &alu->src[0].src, TYPE_UINT);
|
||||
break;
|
||||
|
||||
default: {
|
||||
// set type for def
|
||||
const nir_op_info *info = &nir_op_infos[alu->op];
|
||||
set_type(types, &alu->def, ti_type_from_nir(info->output_type));
|
||||
for (int i = 0; i < info->num_inputs; i++) {
|
||||
// set type for src
|
||||
set_type(types, &alu->src[i].src,
|
||||
ti_type_from_nir(info->input_types[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
infer_types_from_intrinsic(struct hash_table *types, nir_intrinsic_instr *instr)
|
||||
{
|
||||
switch (instr->intrinsic) {
|
||||
case nir_intrinsic_load_input:
|
||||
case nir_intrinsic_load_interpolated_input:
|
||||
case nir_intrinsic_load_output: {
|
||||
ti_type ty = ti_type_from_nir(nir_intrinsic_dest_type(instr));
|
||||
set_type(types, &instr->def, ty);
|
||||
break;
|
||||
}
|
||||
case nir_intrinsic_load_sample_pos_from_id:
|
||||
set_type(types, &instr->def, TYPE_FLOAT);
|
||||
set_type(types, &instr->src[0], TYPE_UINT);
|
||||
break;
|
||||
case nir_intrinsic_load_global_constant:
|
||||
set_type(types, &instr->def, TYPE_GENERIC_DATA);
|
||||
set_type(types, &instr->src[0], TYPE_UINT);
|
||||
break;
|
||||
case nir_intrinsic_load_global_constant_bounded:
|
||||
set_type(types, &instr->def, TYPE_GENERIC_DATA);
|
||||
set_type(types, &instr->src[0], TYPE_UINT);
|
||||
set_type(types, &instr->src[1], TYPE_UINT);
|
||||
set_type(types, &instr->src[2], TYPE_UINT);
|
||||
break;
|
||||
case nir_intrinsic_load_global_constant_offset:
|
||||
set_type(types, &instr->def, TYPE_GENERIC_DATA);
|
||||
set_type(types, &instr->src[0], TYPE_UINT);
|
||||
set_type(types, &instr->src[1], TYPE_UINT);
|
||||
break;
|
||||
case nir_intrinsic_load_global:
|
||||
case nir_intrinsic_load_push_constant:
|
||||
set_type(types, &instr->def, TYPE_GENERIC_DATA);
|
||||
set_type(types, &instr->src[0], TYPE_UINT);
|
||||
break;
|
||||
|
||||
case nir_intrinsic_global_atomic:
|
||||
case nir_intrinsic_global_atomic_swap:
|
||||
case nir_intrinsic_shared_atomic:
|
||||
case nir_intrinsic_shared_atomic_swap: {
|
||||
ti_type type =
|
||||
ti_type_from_nir(nir_atomic_op_type(nir_intrinsic_atomic_op(instr)));
|
||||
set_type(types, &instr->def, type);
|
||||
set_type(types, &instr->src[0], TYPE_UINT);
|
||||
set_type(types, &instr->src[1], type);
|
||||
set_type(types, &instr->src[2], type);
|
||||
break;
|
||||
}
|
||||
case nir_intrinsic_store_global:
|
||||
set_type(types, &instr->src[0], TYPE_GENERIC_DATA);
|
||||
set_type(types, &instr->src[1], TYPE_UINT);
|
||||
break;
|
||||
case nir_intrinsic_store_output: {
|
||||
ti_type ty = ti_type_from_nir(nir_intrinsic_src_type(instr));
|
||||
set_type(types, &instr->src[0], ty);
|
||||
break;
|
||||
}
|
||||
case nir_intrinsic_decl_reg:
|
||||
if (nir_intrinsic_bit_size(instr) == 1)
|
||||
set_type(types, &instr->def, TYPE_BOOL);
|
||||
else
|
||||
set_type(types, &instr->def, TYPE_NONE);
|
||||
break;
|
||||
case nir_intrinsic_store_reg:
|
||||
set_type(types, &instr->src[0], TYPE_NONE);
|
||||
set_type(types, &instr->src[1], TYPE_NONE);
|
||||
break;
|
||||
case nir_intrinsic_load_reg:
|
||||
set_type(types, &instr->src[0], TYPE_NONE);
|
||||
set_type(types, &instr->def, TYPE_NONE);
|
||||
break;
|
||||
case nir_intrinsic_load_scratch:
|
||||
case nir_intrinsic_load_shared:
|
||||
set_type(types, &instr->def, TYPE_UINT);
|
||||
set_type(types, &instr->src[0], TYPE_UINT);
|
||||
break;
|
||||
case nir_intrinsic_store_scratch:
|
||||
case nir_intrinsic_store_shared:
|
||||
set_type(types, &instr->src[0], TYPE_UINT);
|
||||
set_type(types, &instr->src[1], TYPE_UINT);
|
||||
break;
|
||||
case nir_intrinsic_load_first_vertex:
|
||||
case nir_intrinsic_load_base_instance:
|
||||
case nir_intrinsic_load_vertex_id:
|
||||
case nir_intrinsic_load_instance_id:
|
||||
case nir_intrinsic_load_primitive_id:
|
||||
case nir_intrinsic_load_layer_id:
|
||||
case nir_intrinsic_load_workgroup_id:
|
||||
case nir_intrinsic_load_subgroup_id:
|
||||
case nir_intrinsic_load_local_invocation_id:
|
||||
case nir_intrinsic_load_global_invocation_id:
|
||||
case nir_intrinsic_load_local_invocation_index:
|
||||
case nir_intrinsic_load_num_workgroups:
|
||||
case nir_intrinsic_load_num_subgroups:
|
||||
case nir_intrinsic_load_subgroup_size:
|
||||
case nir_intrinsic_load_sample_id:
|
||||
case nir_intrinsic_load_sample_mask:
|
||||
case nir_intrinsic_load_subgroup_invocation:
|
||||
case nir_intrinsic_load_amplification_id_kk:
|
||||
set_type(types, &instr->def, TYPE_UINT);
|
||||
break;
|
||||
case nir_intrinsic_load_vulkan_descriptor:
|
||||
case nir_intrinsic_load_barycentric_at_sample:
|
||||
set_type(types, &instr->src[0], TYPE_UINT);
|
||||
set_type(types, &instr->def, TYPE_UINT);
|
||||
break;
|
||||
case nir_intrinsic_load_buffer_ptr_kk:
|
||||
set_type(types, &instr->def, TYPE_UINT);
|
||||
break;
|
||||
// The defs of these instructions don't participate in type inference
|
||||
// but their sources are pointers (i.e. uints).
|
||||
case nir_intrinsic_load_texture_handle_kk:
|
||||
case nir_intrinsic_load_depth_texture_kk:
|
||||
set_type(types, &instr->src[0], TYPE_UINT);
|
||||
break;
|
||||
case nir_intrinsic_load_sampler_handle_kk:
|
||||
set_type(types, &instr->def, TYPE_SAMPLER);
|
||||
break;
|
||||
case nir_intrinsic_store_clip_distance_kk:
|
||||
set_type(types, &instr->src[0], TYPE_FLOAT);
|
||||
break;
|
||||
case nir_intrinsic_ddx:
|
||||
case nir_intrinsic_ddy:
|
||||
case nir_intrinsic_ddx_coarse:
|
||||
case nir_intrinsic_ddy_coarse:
|
||||
case nir_intrinsic_ddx_fine:
|
||||
case nir_intrinsic_ddy_fine:
|
||||
case nir_intrinsic_load_barycentric_at_offset:
|
||||
set_type(types, &instr->src[0], TYPE_FLOAT);
|
||||
set_type(types, &instr->def, TYPE_FLOAT);
|
||||
break;
|
||||
case nir_intrinsic_load_point_coord:
|
||||
case nir_intrinsic_load_sample_pos:
|
||||
case nir_intrinsic_load_frag_coord:
|
||||
set_type(types, &instr->def, TYPE_FLOAT);
|
||||
break;
|
||||
case nir_intrinsic_load_front_face:
|
||||
case nir_intrinsic_elect:
|
||||
case nir_intrinsic_load_helper_invocation:
|
||||
case nir_intrinsic_is_helper_invocation:
|
||||
set_type(types, &instr->def, TYPE_BOOL);
|
||||
break;
|
||||
case nir_intrinsic_load_constant_agx:
|
||||
set_type(types, &instr->src[0], TYPE_UINT);
|
||||
set_type(types, &instr->src[1], TYPE_UINT);
|
||||
set_type(types, &instr->def,
|
||||
ti_type_from_pipe_format(nir_intrinsic_format(instr)));
|
||||
break;
|
||||
case nir_intrinsic_bindless_image_load:
|
||||
set_type(types, &instr->def,
|
||||
ti_type_from_nir(nir_intrinsic_dest_type(instr)));
|
||||
set_type(types, &instr->src[1], TYPE_UINT); // coords
|
||||
set_type(types, &instr->src[3], TYPE_UINT); // level
|
||||
break;
|
||||
case nir_intrinsic_bindless_image_store:
|
||||
set_type(types, &instr->src[1], TYPE_UINT); // coords
|
||||
set_type(types, &instr->src[3],
|
||||
ti_type_from_nir(nir_intrinsic_src_type(instr)));
|
||||
set_type(types, &instr->src[4], TYPE_UINT); // level
|
||||
break;
|
||||
case nir_intrinsic_demote_if:
|
||||
case nir_intrinsic_terminate_if:
|
||||
set_type(types, &instr->src[0], TYPE_BOOL);
|
||||
break;
|
||||
case nir_intrinsic_bindless_image_atomic:
|
||||
case nir_intrinsic_bindless_image_atomic_swap: {
|
||||
set_type(types, &instr->src[1], TYPE_UINT); // coords
|
||||
set_type(types, &instr->src[2], TYPE_UINT); // level
|
||||
ti_type type =
|
||||
ti_type_from_nir(nir_atomic_op_type(nir_intrinsic_atomic_op(instr)));
|
||||
set_type(types, &instr->src[3], type);
|
||||
if (instr->intrinsic == nir_intrinsic_bindless_image_atomic_swap)
|
||||
set_type(types, &instr->src[4], type);
|
||||
set_type(types, &instr->def, type);
|
||||
break;
|
||||
}
|
||||
case nir_intrinsic_ballot:
|
||||
set_type(types, &instr->src[0], TYPE_BOOL);
|
||||
set_type(types, &instr->def, TYPE_UINT);
|
||||
break;
|
||||
case nir_intrinsic_vote_all:
|
||||
case nir_intrinsic_vote_any:
|
||||
set_type(types, &instr->src[0], TYPE_BOOL);
|
||||
set_type(types, &instr->def, TYPE_BOOL);
|
||||
break;
|
||||
case nir_intrinsic_read_first_invocation:
|
||||
case nir_intrinsic_quad_swap_horizontal:
|
||||
case nir_intrinsic_quad_swap_vertical:
|
||||
case nir_intrinsic_quad_swap_diagonal:
|
||||
set_type(types, &instr->src[0], TYPE_GENERIC_DATA);
|
||||
set_type(types, &instr->def, TYPE_GENERIC_DATA);
|
||||
break;
|
||||
case nir_intrinsic_read_invocation:
|
||||
case nir_intrinsic_quad_broadcast:
|
||||
case nir_intrinsic_rotate:
|
||||
case nir_intrinsic_shuffle:
|
||||
case nir_intrinsic_shuffle_down:
|
||||
case nir_intrinsic_shuffle_up:
|
||||
case nir_intrinsic_shuffle_xor:
|
||||
set_type(types, &instr->src[0], TYPE_GENERIC_DATA);
|
||||
set_type(types, &instr->def, TYPE_GENERIC_DATA);
|
||||
set_type(types, &instr->src[1], TYPE_UINT);
|
||||
break;
|
||||
case nir_intrinsic_reduce:
|
||||
switch (nir_intrinsic_reduction_op(instr)) {
|
||||
case nir_op_iand:
|
||||
case nir_op_ior:
|
||||
case nir_op_ixor:
|
||||
case nir_op_iadd:
|
||||
case nir_op_imul:
|
||||
set_type(types, &instr->src[0], TYPE_GENERIC_INT);
|
||||
set_type(types, &instr->def, TYPE_GENERIC_INT);
|
||||
break;
|
||||
case nir_op_imax:
|
||||
case nir_op_imin:
|
||||
set_type(types, &instr->src[0], TYPE_INT);
|
||||
set_type(types, &instr->def, TYPE_INT);
|
||||
break;
|
||||
case nir_op_umax:
|
||||
case nir_op_umin:
|
||||
set_type(types, &instr->src[0], TYPE_UINT);
|
||||
set_type(types, &instr->def, TYPE_UINT);
|
||||
break;
|
||||
case nir_op_fadd:
|
||||
case nir_op_fmax:
|
||||
case nir_op_fmin:
|
||||
case nir_op_fmul:
|
||||
set_type(types, &instr->src[0], TYPE_FLOAT);
|
||||
set_type(types, &instr->def, TYPE_FLOAT);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
infer_types_from_tex(struct hash_table *types, nir_tex_instr *tex)
|
||||
{
|
||||
set_type(types, &tex->def, ti_type_from_nir(tex->dest_type));
|
||||
for (int i = 0; i < tex->num_srcs; i++) {
|
||||
nir_src *src = &tex->src[i].src;
|
||||
switch (tex->src[i].src_type) {
|
||||
case nir_tex_src_coord:
|
||||
if (tex->op == nir_texop_txf || tex->op == nir_texop_txf_ms)
|
||||
set_type(types, src, TYPE_UINT);
|
||||
else
|
||||
set_type(types, src, TYPE_FLOAT);
|
||||
break;
|
||||
case nir_tex_src_comparator:
|
||||
set_type(types, src, TYPE_FLOAT);
|
||||
break;
|
||||
case nir_tex_src_offset:
|
||||
set_type(types, src, TYPE_INT);
|
||||
break;
|
||||
case nir_tex_src_bias:
|
||||
set_type(types, src, TYPE_FLOAT);
|
||||
break;
|
||||
case nir_tex_src_lod:
|
||||
if (tex->op == nir_texop_txf || tex->op == nir_texop_txf_ms ||
|
||||
tex->op == nir_texop_txs)
|
||||
set_type(types, src, TYPE_UINT);
|
||||
else
|
||||
set_type(types, src, TYPE_FLOAT);
|
||||
break;
|
||||
case nir_tex_src_min_lod:
|
||||
set_type(types, src, TYPE_FLOAT);
|
||||
break;
|
||||
case nir_tex_src_ms_index:
|
||||
set_type(types, src, TYPE_UINT);
|
||||
break;
|
||||
case nir_tex_src_ddx:
|
||||
case nir_tex_src_ddy:
|
||||
set_type(types, src, TYPE_FLOAT);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
infer_types_from_instr(struct hash_table *types, nir_instr *instr)
|
||||
{
|
||||
switch (instr->type) {
|
||||
case nir_instr_type_alu:
|
||||
infer_types_from_alu(types, nir_instr_as_alu(instr));
|
||||
return;
|
||||
case nir_instr_type_intrinsic:
|
||||
infer_types_from_intrinsic(types, nir_instr_as_intrinsic(instr));
|
||||
return;
|
||||
case nir_instr_type_tex:
|
||||
infer_types_from_tex(types, nir_instr_as_tex(instr));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
propagate_types(struct hash_table *types, nir_instr *instr)
|
||||
{
|
||||
bool progress = false;
|
||||
switch (instr->type) {
|
||||
case nir_instr_type_alu: {
|
||||
nir_alu_instr *alu = nir_instr_as_alu(instr);
|
||||
nir_op_info info = nir_op_infos[alu->op];
|
||||
for (int i = 0; i < info.num_inputs; i++) {
|
||||
ti_type src_type = get_type(types, &alu->src[i].src);
|
||||
ti_type def_type = get_type(types, alu->src[i].src.ssa);
|
||||
ti_type unified_type = unify_types(src_type, def_type);
|
||||
nir_instr *parent_instr = nir_def_instr(alu->src[i].src.ssa);
|
||||
if (unified_type > src_type) {
|
||||
progress |= update_instr_type(types, instr, unified_type);
|
||||
} else if (unified_type > def_type) {
|
||||
progress |= update_instr_type(types, parent_instr, unified_type);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case nir_instr_type_intrinsic: {
|
||||
nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr);
|
||||
nir_intrinsic_info info = nir_intrinsic_infos[intr->intrinsic];
|
||||
for (int i = 0; i < info.num_srcs; i++) {
|
||||
ti_type src_type = get_type(types, &intr->src[i]);
|
||||
ti_type def_type = get_type(types, intr->src[i].ssa);
|
||||
ti_type unified_type = unify_types(src_type, def_type);
|
||||
nir_instr *parent_instr = nir_def_instr(intr->src[i].ssa);
|
||||
if (unified_type > src_type) {
|
||||
progress |= update_instr_type(types, instr, unified_type);
|
||||
} else if (unified_type > def_type) {
|
||||
progress |= update_instr_type(types, parent_instr, unified_type);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case nir_instr_type_tex: {
|
||||
nir_tex_instr *tex = nir_instr_as_tex(instr);
|
||||
for (int i = 0; i < tex->num_srcs; i++) {
|
||||
ti_type src_type = get_type(types, &tex->src[i].src);
|
||||
ti_type def_type = get_type(types, tex->src[i].src.ssa);
|
||||
ti_type unified_type = unify_types(src_type, def_type);
|
||||
if (src_type == 0)
|
||||
continue;
|
||||
nir_instr *parent_instr = nir_def_instr(tex->src[i].src.ssa);
|
||||
if (unified_type > def_type) {
|
||||
progress |= update_instr_type(types, parent_instr, unified_type);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return progress;
|
||||
}
|
||||
|
||||
static const char *float_names[] = {"float", "float2", "float3", "float4"};
|
||||
static const char *half_names[] = {"half", "half2", "half3", "half4"};
|
||||
static const char *bool_names[] = {"bool", "bool2", "bool3", "bool4"};
|
||||
static const char *int8_names[] = {"char", "char2", "char3", "char4"};
|
||||
static const char *uint8_names[] = {"uchar", "uchar2", "uchar3", "uchar4"};
|
||||
static const char *int16_names[] = {"short", "short2", "short3", "short4"};
|
||||
static const char *uint16_names[] = {"ushort", "ushort2", "ushort3", "ushort4"};
|
||||
static const char *int32_names[] = {"int", "int2", "int3", "int4"};
|
||||
static const char *uint32_names[] = {"uint", "uint2", "uint3", "uint4"};
|
||||
static const char *int64_names[] = {"long", "long2", "long3", "long4"};
|
||||
static const char *uint64_names[] = {"ulong", "ulong2", "ulong3", "ulong4"};
|
||||
|
||||
static const char *
|
||||
ti_type_to_msl_type(ti_type type, uint8_t bit_width, uint8_t num_components)
|
||||
{
|
||||
switch (type) {
|
||||
case TYPE_GENERIC_DATA:
|
||||
case TYPE_GENERIC_INT:
|
||||
case TYPE_GENERIC_INT_OR_BOOL:
|
||||
case TYPE_UINT:
|
||||
switch (bit_width) {
|
||||
case 1:
|
||||
return bool_names[num_components - 1];
|
||||
case 8:
|
||||
return uint8_names[num_components - 1];
|
||||
case 16:
|
||||
return uint16_names[num_components - 1];
|
||||
case 32:
|
||||
return uint32_names[num_components - 1];
|
||||
case 64:
|
||||
return uint64_names[num_components - 1];
|
||||
default:
|
||||
assert(!"Bad uint length");
|
||||
}
|
||||
break;
|
||||
case TYPE_BOOL:
|
||||
return bool_names[num_components - 1];
|
||||
case TYPE_INT:
|
||||
switch (bit_width) {
|
||||
case 8:
|
||||
return int8_names[num_components - 1];
|
||||
case 16:
|
||||
return int16_names[num_components - 1];
|
||||
case 32:
|
||||
return int32_names[num_components - 1];
|
||||
case 64:
|
||||
return int64_names[num_components - 1];
|
||||
default:
|
||||
assert(!"Bad uint length");
|
||||
}
|
||||
break;
|
||||
case TYPE_FLOAT:
|
||||
switch (bit_width) {
|
||||
case 16:
|
||||
return half_names[num_components - 1];
|
||||
case 32:
|
||||
return float_names[num_components - 1];
|
||||
default:
|
||||
assert(!"Bad float length");
|
||||
}
|
||||
break;
|
||||
case TYPE_SAMPLER:
|
||||
return "sampler";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
msl_uint_type(uint8_t bit_size, uint8_t num_components)
|
||||
{
|
||||
return ti_type_to_msl_type(TYPE_UINT, bit_size, num_components);
|
||||
}
|
||||
|
||||
const char *
|
||||
msl_type_for_def(struct hash_table *types, nir_def *def)
|
||||
{
|
||||
ti_type type = get_type(types, def);
|
||||
return ti_type_to_msl_type(type, def->bit_size, def->num_components);
|
||||
}
|
||||
|
||||
const char *
|
||||
msl_type_for_src(struct hash_table *types, nir_src *src)
|
||||
{
|
||||
ti_type type = get_type(types, src);
|
||||
// This won't necessarily work for alu srcs but for intrinsics it's fine.
|
||||
return ti_type_to_msl_type(type, src->ssa->bit_size,
|
||||
src->ssa->num_components);
|
||||
}
|
||||
|
||||
const char *
|
||||
msl_bitcast_for_src(struct hash_table *types, nir_src *src)
|
||||
{
|
||||
ti_type src_type = get_type(types, src);
|
||||
ti_type def_type = get_type(types, src->ssa);
|
||||
if (nir_src_is_if(src))
|
||||
return NULL;
|
||||
if (src_type != def_type) {
|
||||
/* bool types cannot use as_type casting */
|
||||
if (src_type == TYPE_BOOL || def_type == TYPE_BOOL)
|
||||
return NULL;
|
||||
|
||||
// produce bitcast _into_ src_type
|
||||
return ti_type_to_msl_type(src_type, src->ssa->bit_size,
|
||||
src->ssa->num_components);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
emit_src_component(struct nir_to_msl_ctx *ctx, nir_src *src, unsigned comp)
|
||||
{
|
||||
ti_type type = get_type(ctx->types, src);
|
||||
switch (type) {
|
||||
case TYPE_FLOAT: {
|
||||
double v = nir_src_comp_as_float(*src, comp);
|
||||
if (v == INFINITY) {
|
||||
P(ctx, "(INFINITY");
|
||||
} else if (v == -INFINITY) {
|
||||
P(ctx, "(-INFINITY");
|
||||
} else if (isnan(v)) {
|
||||
P(ctx, "(NAN");
|
||||
} else {
|
||||
/* Building the types explicitly is required since the MSL compiler is
|
||||
* too dumb to understand that "max(as_type<int>(t53), -2147483648)" is
|
||||
* not ambiguous since both are ints and there's no room for longs.
|
||||
* From CTS test:
|
||||
* dEQP-VK.renderpass.suballocation.multisample.r32_sint.samples_2 */
|
||||
if (src->ssa->bit_size == 16) {
|
||||
P(ctx, "half(");
|
||||
} else {
|
||||
P(ctx, "float(");
|
||||
}
|
||||
P(ctx, "%.*le", DBL_DECIMAL_DIG, nir_src_comp_as_float(*src, comp));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TYPE_BOOL:
|
||||
P(ctx, "bool(%d", nir_src_comp_as_bool(*src, comp));
|
||||
break;
|
||||
case TYPE_INT:
|
||||
switch (src->ssa->bit_size) {
|
||||
case 8:
|
||||
P(ctx, "char(");
|
||||
break;
|
||||
case 16:
|
||||
P(ctx, "short(");
|
||||
break;
|
||||
case 32:
|
||||
P(ctx, "int(");
|
||||
break;
|
||||
case 64:
|
||||
P(ctx, "long(");
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE("Incorrect bit_size for TYPE_INT");
|
||||
}
|
||||
P(ctx, "%" PRId64, nir_src_comp_as_int(*src, comp));
|
||||
break;
|
||||
case TYPE_UINT:
|
||||
case TYPE_GENERIC_DATA:
|
||||
case TYPE_GENERIC_INT:
|
||||
case TYPE_GENERIC_INT_OR_BOOL:
|
||||
switch (src->ssa->bit_size) {
|
||||
case 1:
|
||||
P(ctx, "bool(");
|
||||
break;
|
||||
case 8:
|
||||
P(ctx, "uchar(");
|
||||
break;
|
||||
case 16:
|
||||
P(ctx, "ushort(");
|
||||
break;
|
||||
case 32:
|
||||
P(ctx, "uint(");
|
||||
break;
|
||||
case 64:
|
||||
P(ctx, "ulong(");
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE("Incorrect bit_size for TYPE_UINT");
|
||||
}
|
||||
P(ctx, "%" PRIu64 "u", nir_src_comp_as_uint(*src, comp));
|
||||
break;
|
||||
case TYPE_NONE:
|
||||
assert(0);
|
||||
P(ctx, "UNTYPED!");
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
P(ctx, ")");
|
||||
}
|
||||
|
||||
void
|
||||
msl_src_as_const(struct nir_to_msl_ctx *ctx, nir_src *src)
|
||||
{
|
||||
ti_type type = get_type(ctx->types, src);
|
||||
if (src->ssa->num_components == 1) {
|
||||
emit_src_component(ctx, src, 0);
|
||||
} else {
|
||||
P(ctx, "%s(",
|
||||
ti_type_to_msl_type(type, src->ssa->bit_size,
|
||||
src->ssa->num_components));
|
||||
for (int i = 0; i < src->ssa->num_components; i++) {
|
||||
if (i)
|
||||
P(ctx, ", ");
|
||||
emit_src_component(ctx, src, i);
|
||||
}
|
||||
P(ctx, ")");
|
||||
}
|
||||
}
|
||||
|
||||
struct hash_table *
|
||||
msl_infer_types(nir_shader *shader)
|
||||
{
|
||||
struct hash_table *types = _mesa_pointer_hash_table_create(NULL);
|
||||
bool progress = false;
|
||||
// First, seed the types for every instruction for every source and def
|
||||
nir_foreach_function_impl(impl, shader) {
|
||||
nir_foreach_block(block, impl) {
|
||||
nir_foreach_instr(instr, block) {
|
||||
infer_types_from_instr(types, instr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
progress = false;
|
||||
nir_foreach_function_impl(impl, shader) {
|
||||
nir_foreach_block(block, impl) {
|
||||
nir_foreach_instr(instr, block) {
|
||||
progress |= propagate_types(types, instr);
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (progress);
|
||||
return types;
|
||||
}
|
||||
|
||||
bool
|
||||
msl_src_is_float(struct nir_to_msl_ctx *ctx, nir_src *src)
|
||||
{
|
||||
return get_type(ctx->types, src) == TYPE_FLOAT;
|
||||
}
|
||||
|
||||
bool
|
||||
msl_def_is_sampler(struct nir_to_msl_ctx *ctx, nir_def *def)
|
||||
{
|
||||
return get_type(ctx->types, def) == TYPE_SAMPLER;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "nir.h"
|
||||
|
||||
enum pipe_format;
|
||||
|
||||
struct nir_to_msl_options {
|
||||
void *mem_ctx;
|
||||
uint64_t disabled_workarounds;
|
||||
|
||||
/* Required to correctly declare fragment outputs. Shader may contain
|
||||
* shrinked writes which can lead to writting less components than the ones
|
||||
* the render target has. This leads to an incorrect calculation of the
|
||||
* component count for the render target formats. */
|
||||
uint8_t rts_component_count[MAX_DRAW_BUFFERS];
|
||||
};
|
||||
|
||||
/* Assumes nir_shader_gather_info has been called beforehand. */
|
||||
char *nir_to_msl(nir_shader *shader, struct nir_to_msl_options *options);
|
||||
|
||||
/* Call this after all API-specific lowerings. It will bring the NIR out of SSA
|
||||
* at the end */
|
||||
bool msl_optimize_nir(struct nir_shader *nir);
|
||||
|
||||
/* Call this before all API-speicific lowerings, it will */
|
||||
void msl_preprocess_nir(struct nir_shader *nir);
|
||||
|
||||
/* Call this before all API-specific lowerings. It will pre-process with
|
||||
* instruction workarounds based on the disabled workarounds bitmask. */
|
||||
void msl_preprocess_nir_workarounds(struct nir_shader *nir,
|
||||
uint64_t disabled_workarounds);
|
||||
|
||||
enum msl_tex_access_flag {
|
||||
MSL_ACCESS_SAMPLE = 0,
|
||||
MSL_ACCESS_READ,
|
||||
MSL_ACCESS_WRITE,
|
||||
MSL_ACCESS_READ_WRITE,
|
||||
};
|
||||
|
||||
static inline enum msl_tex_access_flag
|
||||
msl_convert_access_flag(enum gl_access_qualifier qual)
|
||||
{
|
||||
enum gl_access_qualifier readwrite =
|
||||
(ACCESS_NON_WRITEABLE | ACCESS_NON_READABLE);
|
||||
if ((qual & readwrite) == readwrite)
|
||||
return MSL_ACCESS_READ_WRITE;
|
||||
if (qual & ACCESS_NON_WRITEABLE)
|
||||
return MSL_ACCESS_READ;
|
||||
if (qual & ACCESS_NON_READABLE)
|
||||
return MSL_ACCESS_WRITE;
|
||||
return MSL_ACCESS_READ_WRITE;
|
||||
}
|
||||
|
||||
bool msl_nir_fs_force_output_signedness(
|
||||
nir_shader *nir, enum pipe_format render_target_formats[MAX_DRAW_BUFFERS]);
|
||||
|
||||
bool msl_nir_vs_remove_point_size_write(nir_builder *b,
|
||||
nir_intrinsic_instr *intrin,
|
||||
void *data);
|
||||
|
||||
bool msl_nir_fs_remove_depth_write(nir_builder *b, nir_intrinsic_instr *intrin,
|
||||
void *data);
|
||||
|
||||
bool msl_lower_textures(nir_shader *s);
|
||||
|
||||
bool msl_lower_static_sample_mask(nir_shader *nir, uint32_t sample_mask);
|
||||
bool msl_ensure_depth_write(nir_shader *nir);
|
||||
bool msl_ensure_vertex_position_output(nir_shader *nir);
|
||||
bool msl_ensure_vertex_point_size_output(nir_shader *nir);
|
||||
bool msl_nir_fs_io_types(nir_shader *nir);
|
||||
bool msl_nir_vs_io_types(nir_shader *nir);
|
||||
bool msl_nir_fake_guard_for_discards(struct nir_shader *nir);
|
||||
bool msl_nir_lower_sample_shading(nir_shader *nir);
|
||||
void msl_nir_lower_clip_cull_distance(nir_shader *nir,
|
||||
unsigned num_cull_distances);
|
||||
bool msl_nir_lower_instance_id(nir_shader *nir);
|
||||
|
||||
bool msl_gather_uses_per_draw_data(nir_shader *nir);
|
||||
|
||||
static const nir_shader_compiler_options kk_nir_options = {
|
||||
.lower_fdph = true,
|
||||
.has_fsub = true,
|
||||
.has_isub = true,
|
||||
.float_mul_add16 = nir_float_muladd_support_has_ffma,
|
||||
.float_mul_add32 = nir_float_muladd_support_has_ffma,
|
||||
.lower_extract_word = true,
|
||||
.lower_extract_byte = true,
|
||||
.lower_insert_word = true,
|
||||
.lower_insert_byte = true,
|
||||
.lower_fmod = true,
|
||||
.discard_is_demote = true,
|
||||
.lower_device_index_to_zero = true,
|
||||
.lower_pack_64_2x32_split = true,
|
||||
.lower_unpack_64_2x32_split = true,
|
||||
.lower_pack_64_2x32 = true,
|
||||
.lower_pack_64_4x16 = true,
|
||||
.lower_pack_half_2x16 = true,
|
||||
.lower_pack_split = true,
|
||||
.lower_unpack_half_2x16 = true,
|
||||
.has_cs_global_id = true,
|
||||
.lower_fquantize2f16 = true,
|
||||
.lower_scmp = true,
|
||||
.lower_ifind_msb = true,
|
||||
.lower_ufind_msb = true,
|
||||
.lower_find_lsb = true,
|
||||
.has_uclz = true,
|
||||
.lower_mul_2x32_64 = true,
|
||||
.lower_uadd_carry = true,
|
||||
.lower_usub_borrow = true,
|
||||
.compact_arrays = true,
|
||||
/* Metal does not support double. */
|
||||
.lower_doubles_options = (nir_lower_doubles_options)(~0),
|
||||
.lower_int64_options = nir_lower_ufind_msb64 | nir_lower_subgroup_shuffle64,
|
||||
.io_options = nir_io_mediump_is_32bit,
|
||||
};
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "compiler/nir_to_msl.h"
|
||||
#include "spirv/nir_spirv.h"
|
||||
|
||||
static int
|
||||
load_spirv(const char *filename, uint32_t **words, size_t *nwords)
|
||||
{
|
||||
const size_t CHUNK_SIZE = 4096;
|
||||
uint32_t buf[CHUNK_SIZE];
|
||||
FILE *input = fopen(filename, "r");
|
||||
if (!input) {
|
||||
fprintf(stderr, "Could not open file %s: %s\n", filename,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
*nwords = 0;
|
||||
*words = malloc(CHUNK_SIZE * sizeof(buf[0]));
|
||||
size_t read_size;
|
||||
while (1) {
|
||||
read_size = fread(buf, sizeof(buf[0]), CHUNK_SIZE, input);
|
||||
if (read_size == 0)
|
||||
break;
|
||||
*words = realloc(*words, (*nwords + read_size) * sizeof(buf[0]));
|
||||
memcpy(*words + *nwords, buf, sizeof(buf[0]) * read_size);
|
||||
*nwords += read_size;
|
||||
};
|
||||
|
||||
if (*words[0] != 0x07230203) {
|
||||
fprintf(stderr, "%s is not a SPIR-V file?\n", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
debug_callback(void *priv, enum nir_spirv_debug_level debuglevel, size_t offset,
|
||||
const char *message)
|
||||
{
|
||||
fprintf(stderr, "<%d> at %zu %s\n", debuglevel, offset, message);
|
||||
}
|
||||
|
||||
static int
|
||||
type_size_vec4(const struct glsl_type *type, bool bindless)
|
||||
{
|
||||
return glsl_count_attribute_slots(type, false);
|
||||
}
|
||||
|
||||
static void
|
||||
shared_var_info(const struct glsl_type *type, unsigned *size, unsigned *align)
|
||||
{
|
||||
assert(glsl_type_is_vector_or_scalar(type));
|
||||
|
||||
uint32_t comp_size =
|
||||
glsl_type_is_boolean(type) ? 4 : glsl_get_bit_size(type) / 8;
|
||||
unsigned length = glsl_get_vector_elements(type);
|
||||
*size = comp_size * length, *align = comp_size;
|
||||
}
|
||||
|
||||
static void
|
||||
optimize(nir_shader *nir)
|
||||
{
|
||||
msl_preprocess_nir(nir);
|
||||
msl_preprocess_nir_workarounds(nir, 0);
|
||||
|
||||
nir_lower_compute_system_values_options csv_options = {
|
||||
.has_base_global_invocation_id = 0,
|
||||
.has_base_workgroup_id = true,
|
||||
};
|
||||
NIR_PASS(_, nir, nir_lower_compute_system_values, &csv_options);
|
||||
|
||||
NIR_PASS(_, nir, nir_lower_explicit_io, nir_var_mem_push_const,
|
||||
nir_address_format_32bit_offset);
|
||||
NIR_PASS(_, nir, nir_lower_explicit_io,
|
||||
nir_var_mem_global | nir_var_mem_ubo | nir_var_mem_ssbo,
|
||||
nir_address_format_64bit_global);
|
||||
if (nir->info.stage == MESA_SHADER_COMPUTE) {
|
||||
if (!nir->info.shared_memory_explicit_layout) {
|
||||
/* There may be garbage in shared_size, but it's the job of
|
||||
* nir_lower_vars_to_explicit_types to allocate it. We have to reset to
|
||||
* avoid overallocation.
|
||||
*/
|
||||
nir->info.shared_size = 0;
|
||||
|
||||
NIR_PASS(_, nir, nir_lower_vars_to_explicit_types, nir_var_mem_shared,
|
||||
shared_var_info);
|
||||
}
|
||||
NIR_PASS(_, nir, nir_lower_explicit_io, nir_var_mem_shared,
|
||||
nir_address_format_32bit_offset);
|
||||
}
|
||||
|
||||
NIR_PASS(_, nir, nir_lower_io, nir_var_shader_in | nir_var_shader_out,
|
||||
type_size_vec4, (nir_lower_io_options)0);
|
||||
|
||||
NIR_PASS(_, nir, nir_lower_variable_initializers, ~nir_var_function_temp);
|
||||
NIR_PASS(_, nir, nir_remove_dead_variables,
|
||||
nir_var_shader_in | nir_var_shader_out | nir_var_system_value,
|
||||
NULL);
|
||||
NIR_PASS(_, nir, nir_lower_io_vars_to_temporaries,
|
||||
nir_shader_get_entrypoint(nir), nir_var_shader_out);
|
||||
nir_lower_compute_system_values_options options = {
|
||||
.has_base_global_invocation_id = 0,
|
||||
};
|
||||
NIR_PASS(_, nir, nir_lower_system_values);
|
||||
NIR_PASS(_, nir, nir_lower_compute_system_values, &options);
|
||||
NIR_PASS(_, nir, nir_lower_global_vars_to_local);
|
||||
NIR_PASS(_, nir, nir_lower_load_const_to_scalar);
|
||||
|
||||
msl_optimize_nir(nir);
|
||||
}
|
||||
|
||||
static mesa_shader_stage
|
||||
stage_from_filename(const char *filename)
|
||||
{
|
||||
struct StageMapping {
|
||||
char *name;
|
||||
mesa_shader_stage stage;
|
||||
};
|
||||
struct StageMapping stage_mappings[] = {
|
||||
{.name = ".frag.", .stage = MESA_SHADER_FRAGMENT},
|
||||
{.name = ".vert.", .stage = MESA_SHADER_VERTEX},
|
||||
{.name = ".comp.", .stage = MESA_SHADER_COMPUTE},
|
||||
};
|
||||
for (int i = 0; i < ARRAY_SIZE(stage_mappings); i++) {
|
||||
if (strstr(filename, stage_mappings[i].name))
|
||||
return stage_mappings[i].stage;
|
||||
}
|
||||
return MESA_SHADER_NONE;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "Usage: kosmicomp filename.spv\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// read file
|
||||
size_t nwords = 0;
|
||||
uint32_t *words = NULL;
|
||||
int result = load_spirv(argv[1], &words, &nwords);
|
||||
if (result == -1) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
// run spirv_to_nir
|
||||
struct spirv_to_nir_options options = {
|
||||
.environment = NIR_SPIRV_VULKAN,
|
||||
.debug =
|
||||
{
|
||||
.func = &debug_callback,
|
||||
.private_data = NULL,
|
||||
},
|
||||
.ubo_addr_format = nir_address_format_64bit_global,
|
||||
.ssbo_addr_format = nir_address_format_64bit_global,
|
||||
.phys_ssbo_addr_format = nir_address_format_64bit_global,
|
||||
};
|
||||
glsl_type_singleton_init_or_ref();
|
||||
struct nir_shader_compiler_options nir_options = {
|
||||
.lower_fdph = 1,
|
||||
};
|
||||
mesa_shader_stage stage = stage_from_filename(argv[1]);
|
||||
if (stage == MESA_SHADER_NONE) {
|
||||
fprintf(stderr, "Couldn't guess shader stage from %s\n", argv[1]);
|
||||
return 4;
|
||||
}
|
||||
nir_shader *shader =
|
||||
spirv_to_nir(words, nwords, NULL, stage, "main", &options, &nir_options);
|
||||
if (!shader) {
|
||||
fprintf(stderr, "Compilation failed!\n");
|
||||
return 3;
|
||||
}
|
||||
// print nir
|
||||
nir_print_shader(shader, stdout);
|
||||
optimize(shader);
|
||||
nir_print_shader(shader, stdout);
|
||||
|
||||
struct nir_to_msl_options translate_options = {
|
||||
.mem_ctx = shader,
|
||||
.disabled_workarounds = 0u,
|
||||
};
|
||||
char *msl_text = nir_to_msl(shader, &translate_options);
|
||||
|
||||
fputs(msl_text, stdout);
|
||||
|
||||
ralloc_free(msl_text);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2026 LunarG, Inc.
|
||||
* Copyright 2026 Google LLC
|
||||
* Copyright 2024 Valve Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include "compiler/libcl/libcl_vk.h"
|
||||
|
||||
#include "poly/cl/restart.h"
|
||||
|
||||
/*
|
||||
* To implement drawIndirectCount generically, we dispatch a kernel to
|
||||
* clone-and-patch the indirect buffer, predicating out draws as appropriate.
|
||||
*/
|
||||
static uint
|
||||
libkk_predicate_draw(global uint32_t *out, constant uint32_t *in,
|
||||
uint32_t out_stride_el, uint32_t in_stride_el,
|
||||
uint draw_id, bool enabled)
|
||||
{
|
||||
out += draw_id * out_stride_el;
|
||||
in += draw_id * in_stride_el;
|
||||
|
||||
/* Copy enabled draws, zero predicated draws. */
|
||||
for (uint i = 0; i < out_stride_el; ++i) {
|
||||
out[i] = enabled ? in[i] : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Indirect Draw predicate: value > draw_id
|
||||
*/
|
||||
KERNEL(32)
|
||||
libkk_predicate_indirect_gt_draw_id(global uint32_t *out,
|
||||
constant uint32_t *in,
|
||||
constant uint32_t *value,
|
||||
uint32_t out_stride_el,
|
||||
uint32_t in_stride_el)
|
||||
{
|
||||
uint draw_id = cl_global_id.x;
|
||||
bool enabled = *value > draw_id;
|
||||
|
||||
libkk_predicate_draw(out, in, out_stride_el, in_stride_el, draw_id, enabled);
|
||||
}
|
||||
|
||||
/*
|
||||
* Indirect Draw predicate: value == 0
|
||||
*/
|
||||
KERNEL(32)
|
||||
libkk_predicate_indirect_eq_zero(global uint32_t *out, constant uint32_t *in,
|
||||
constant uint32_t *value,
|
||||
uint32_t out_stride_el, uint32_t in_stride_el)
|
||||
{
|
||||
uint draw_id = cl_global_id.x;
|
||||
bool enabled = *value == 0;
|
||||
|
||||
libkk_predicate_draw(out, in, out_stride_el, in_stride_el, draw_id, enabled);
|
||||
}
|
||||
|
||||
/*
|
||||
* Indirect Draw predicate: value != 0
|
||||
*/
|
||||
KERNEL(32)
|
||||
libkk_predicate_indirect_neq_zero(global uint32_t *out, constant uint32_t *in,
|
||||
constant uint32_t *value,
|
||||
uint32_t out_stride_el,
|
||||
uint32_t in_stride_el)
|
||||
{
|
||||
uint draw_id = cl_global_id.x;
|
||||
bool enabled = *value != 0;
|
||||
|
||||
libkk_predicate_draw(out, in, out_stride_el, in_stride_el, draw_id, enabled);
|
||||
}
|
||||
|
||||
KERNEL(1024)
|
||||
libkk_unroll_geometry(
|
||||
uint64_t index_buffer, global struct poly_heap *heap,
|
||||
constant uint32_t *in_draw, global uint32_t *out_draw,
|
||||
uint32_t in_draw_stride_el, uint32_t restart_index,
|
||||
uint32_t index_buffer_size_el, uint32_t in_el_size_B,
|
||||
uint32_t out_el_size_B, uint32_t flatshade_first, uint32_t mode)
|
||||
{
|
||||
uint gid = cl_group_id.x;
|
||||
in_draw += gid * in_draw_stride_el;
|
||||
out_draw += gid * 5;
|
||||
|
||||
POLY_DECL_UNROLL_RESTART_SCRATCH(scratch, 1024);
|
||||
poly_unroll_geometry(out_draw, heap, in_draw, index_buffer,
|
||||
index_buffer_size_el, in_el_size_B, out_el_size_B,
|
||||
restart_index, flatshade_first, mode, scratch);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2026 LunarG, Inc.
|
||||
* Copyright 2026 Google LLC
|
||||
* Copyright 2023 Alyssa Rosenzweig
|
||||
* Copyright 2023 Valve Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "compiler/libcl/libcl_vk.h"
|
||||
#include "poly/geometry.h"
|
||||
#include "poly/tessellator.h"
|
||||
|
||||
KERNEL(1)
|
||||
libkk_prefix_sum_tess(global struct poly_tess_params *p)
|
||||
{
|
||||
if (cl_local_id.x != 0)
|
||||
return;
|
||||
|
||||
/* The last element of an inclusive prefix sum is the total sum */
|
||||
uint total = 0;
|
||||
|
||||
if (p->nr_patches > 0) {
|
||||
for (uint32_t i = 0u; i < p->nr_patches; ++i) {
|
||||
total += p->counts[i];
|
||||
p->counts[i] = total;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate 4-byte indices */
|
||||
uint32_t elsize_B = sizeof(uint32_t);
|
||||
uint32_t size_B = total * elsize_B;
|
||||
uint alloc_B = poly_heap_alloc_offs(p->heap, size_B);
|
||||
p->index_buffer = (global uint32_t *)(((uintptr_t)p->heap->base) + alloc_B);
|
||||
|
||||
/* ...and now we can generate the API indexed draw */
|
||||
global uint32_t *desc = p->out_draws;
|
||||
|
||||
desc[0] = total; /* count */
|
||||
desc[1] = 1; /* instance_count */
|
||||
desc[2] = alloc_B / elsize_B; /* start */
|
||||
desc[3] = 0; /* index_bias */
|
||||
desc[4] = 0; /* start_instance */
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2022 Collabora Ltd. and Red Hat Inc.
|
||||
* Copyright 2024 Alyssa Rosenzweig
|
||||
* Copyright 2024 Valve Corporation
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include "compiler/libcl/libcl_vk.h"
|
||||
|
||||
#include "kk_query.h"
|
||||
|
||||
static inline global uint64_t *
|
||||
query_report(global uint64_t *results, global uint16_t *oq_index,
|
||||
uint reports_per_query, uint query)
|
||||
{
|
||||
/* For occlusion queries, results[] points to the device global heap. We
|
||||
* need to remap indices according to the query pool's allocation.
|
||||
*/
|
||||
uint result_index = oq_index ? oq_index[query] : query;
|
||||
|
||||
return results + (result_index * reports_per_query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Goes through a series of consecutive query indices in the given pool,
|
||||
* setting all element values to 0 and emitting them as available.
|
||||
*/
|
||||
KERNEL(1)
|
||||
libkk_reset_query(global uint32_t *availability, global uint64_t *results,
|
||||
global uint16_t *oq_index, uint32_t first_query,
|
||||
uint16_t reports_per_query, int set_available)
|
||||
{
|
||||
uint32_t query = first_query + cl_global_id.x;
|
||||
|
||||
uint64_t value = 0;
|
||||
if (availability) {
|
||||
availability[query] = set_available;
|
||||
} else {
|
||||
value = set_available ? 0 : UINT64_MAX;
|
||||
}
|
||||
|
||||
global uint64_t *report =
|
||||
query_report(results, oq_index, reports_per_query, query);
|
||||
|
||||
/* XXX: is this supposed to happen on the begin? */
|
||||
for (unsigned j = 0; j < reports_per_query; ++j) {
|
||||
report[j] = value;
|
||||
}
|
||||
}
|
||||
|
||||
KERNEL(1)
|
||||
libkk_write_u32_array(global struct libkk_imm_write *write_array)
|
||||
{
|
||||
uint id = cl_global_id.x;
|
||||
*(write_array[id].address) = write_array[id].value;
|
||||
}
|
||||
|
||||
KERNEL(1)
|
||||
libkk_copy_queries(global uint32_t *availability, global uint64_t *results,
|
||||
global uint16_t *oq_index, uint64_t dst_addr,
|
||||
uint64_t dst_stride, uint32_t first_query,
|
||||
VkQueryResultFlagBits flags, uint16_t reports_per_query)
|
||||
{
|
||||
uint index = cl_group_id.x;
|
||||
uint64_t dst = dst_addr + (((uint64_t)index) * dst_stride);
|
||||
uint32_t query = first_query + index;
|
||||
|
||||
bool available;
|
||||
if (availability)
|
||||
available = availability[query];
|
||||
else
|
||||
available = (results[query] != LIBKK_QUERY_UNAVAILABLE);
|
||||
|
||||
if (available || (flags & VK_QUERY_RESULT_PARTIAL_BIT)) {
|
||||
/* For occlusion queries, results[] points to the device global heap. We
|
||||
* need to remap indices according to the query pool's allocation.
|
||||
*/
|
||||
uint result_index = oq_index ? oq_index[query] : query;
|
||||
uint idx = result_index * reports_per_query;
|
||||
|
||||
for (unsigned i = 0; i < reports_per_query; ++i) {
|
||||
vk_write_query(dst, i, flags, results[idx + i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & VK_QUERY_RESULT_WITH_AVAILABILITY_BIT) {
|
||||
vk_write_query(dst, reports_per_query, flags, available);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright © 2022 Collabora Ltd. and Red Hat Inc.
|
||||
* Copyright © 2024 Alyssa Rosenzweig
|
||||
* Copyright © 2024 Valve Corporation
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef KK_QUERY_H
|
||||
#define KK_QUERY_H
|
||||
|
||||
#include "compiler/libcl/libcl.h"
|
||||
|
||||
struct libkk_imm_write {
|
||||
DEVICE(uint32_t) address;
|
||||
uint32_t value;
|
||||
};
|
||||
|
||||
#define LIBKK_QUERY_UNAVAILABLE (uint64_t)((int64_t)-1)
|
||||
|
||||
#endif /* KK_QUERY_H */
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2026 LunarG, Inc.
|
||||
* Copyright 2026 Google LLC
|
||||
* Copyright 2023 Alyssa Rosenzweig
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "poly/geometry.h"
|
||||
#include "poly/tessellator.h"
|
||||
|
||||
KERNEL(1)
|
||||
libkk_tess_setup_indirect(
|
||||
global struct poly_tess_params *p,
|
||||
global uint32_t *grids /* output: VS then TCS then tess */,
|
||||
global struct poly_vertex_params *vp /* output */, global uint32_t *indirect,
|
||||
uint64_t in_index_buffer, uint32_t in_index_buffer_range_el,
|
||||
uint32_t in_index_size_B, uint64_t vertex_outputs /* bitfield */,
|
||||
|
||||
/* Tess control invocation counter if active, else zero */
|
||||
global uint32_t *tcs_statistic)
|
||||
{
|
||||
uint count = indirect[0], instance_count = indirect[1];
|
||||
unsigned in_patches = count / p->input_patch_size;
|
||||
|
||||
/* TCS invocation counter increments once per-patch */
|
||||
if (tcs_statistic) {
|
||||
*tcs_statistic += in_patches;
|
||||
}
|
||||
|
||||
size_t draw_stride = 5 * sizeof(uint32_t);
|
||||
unsigned unrolled_patches = in_patches * instance_count;
|
||||
|
||||
uint32_t alloc = 0;
|
||||
uint32_t tcs_out_offs = alloc;
|
||||
alloc += unrolled_patches * p->tcs_stride_el * 4;
|
||||
|
||||
uint32_t patch_coord_offs = alloc;
|
||||
alloc += unrolled_patches * 4;
|
||||
|
||||
uint32_t count_offs = alloc;
|
||||
alloc += unrolled_patches * sizeof(uint32_t);
|
||||
|
||||
uint vb_offs = alloc;
|
||||
uint vb_size = poly_tcs_in_size(count * instance_count, vertex_outputs);
|
||||
alloc += vb_size;
|
||||
|
||||
/* Allocate all patch calculations in one go */
|
||||
global uchar *blob = poly_heap_alloc(p->heap, alloc);
|
||||
|
||||
p->tcs_buffer = (global float *)(blob + tcs_out_offs);
|
||||
p->patches_per_instance = in_patches;
|
||||
p->coord_allocs = (global uint *)(blob + patch_coord_offs);
|
||||
p->nr_patches = unrolled_patches;
|
||||
|
||||
vp->output_buffer = (uintptr_t)(blob + vb_offs);
|
||||
vp->outputs = vertex_outputs;
|
||||
p->counts = (global uint32_t *)(blob + count_offs);
|
||||
|
||||
if (vp) {
|
||||
vp->verts_per_instance = count;
|
||||
}
|
||||
|
||||
/* If indexing is enabled, the third word is the offset into the index buffer
|
||||
* in elements. Apply that offset now that we have it. For a hardware
|
||||
* indirect draw, the hardware would do this for us, but for software input
|
||||
* assembly we need to do it ourselves.
|
||||
*
|
||||
* XXX: Deduplicate?
|
||||
*/
|
||||
if (in_index_size_B) {
|
||||
/* TODO_KOSMICKRISP Use poly_index_buffer and implement
|
||||
* load_ro_sink_address_poly */
|
||||
vp->index_buffer = in_index_buffer + (indirect[2] * in_index_size_B);
|
||||
|
||||
vp->index_buffer_range_el =
|
||||
poly_index_buffer_range_el(in_index_buffer_range_el, indirect[2]);
|
||||
}
|
||||
|
||||
/* VS grid size */
|
||||
grids[0] = count;
|
||||
grids[1] = instance_count;
|
||||
grids[2] = 1;
|
||||
|
||||
/* TCS grid size */
|
||||
grids[3] = in_patches * p->output_patch_size;
|
||||
grids[4] = instance_count;
|
||||
grids[5] = 1;
|
||||
|
||||
/* Tess grid size */
|
||||
grids[6] = unrolled_patches;
|
||||
grids[7] = 1;
|
||||
grids[8] = 1;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2026 LunarG, Inc.
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "poly/cl/tessellator.h"
|
||||
|
||||
KERNEL(1)
|
||||
libkk_tess_isoline(constant struct poly_tess_params *p,
|
||||
enum poly_tess_mode tess_mode)
|
||||
{
|
||||
uint patch = cl_global_id.x;
|
||||
poly_tess_isoline_process(p, patch, tess_mode);
|
||||
}
|
||||
|
||||
KERNEL(1)
|
||||
libkk_tess_tri(constant struct poly_tess_params *p,
|
||||
enum poly_tess_mode tess_mode)
|
||||
{
|
||||
uint patch = cl_global_id.x;
|
||||
poly_tess_tri_process(p, patch, tess_mode);
|
||||
}
|
||||
|
||||
KERNEL(1)
|
||||
libkk_tess_quad(constant struct poly_tess_params *p,
|
||||
enum poly_tess_mode tess_mode)
|
||||
{
|
||||
uint patch = cl_global_id.x;
|
||||
poly_tess_quad_process(p, patch, tess_mode);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2026 LunarG, Inc.
|
||||
* Copyright 2026 Google LLC
|
||||
* Copyright 2024 Valve Corporation
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "poly/tessellator.h"
|
||||
|
||||
#define libkk_tessellate(context, grid, barrier, prim, mode, state) \
|
||||
if (prim == TESS_PRIMITIVE_QUADS) { \
|
||||
libkk_tess_quad(context, grid, barrier, state, mode); \
|
||||
} else if (prim == TESS_PRIMITIVE_TRIANGLES) { \
|
||||
libkk_tess_tri(context, grid, barrier, state, mode); \
|
||||
} else { \
|
||||
assert(prim == TESS_PRIMITIVE_ISOLINES); \
|
||||
libkk_tess_isoline(context, grid, barrier, state, mode); \
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
# Copyright 2026 LunarG, Inc.
|
||||
# Copyright 2026 Google LLC
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
libkk_shader_files = files(
|
||||
'kk_draws.cl',
|
||||
'kk_geometry.cl',
|
||||
'kk_query.cl',
|
||||
'kk_tessellation.cl',
|
||||
'kk_tessellator.cl',
|
||||
)
|
||||
|
||||
libkk_spv = custom_target(
|
||||
input : libkk_shader_files,
|
||||
output : 'libkk.spv',
|
||||
command : [
|
||||
prog_mesa_clc, '-o', '@OUTPUT@', '--depfile', '@DEPFILE@',
|
||||
libkk_shader_files, '--',
|
||||
'-I' + join_paths(meson.project_source_root(), 'include'),
|
||||
'-I' + join_paths(meson.project_source_root(), 'src/compiler/libcl'),
|
||||
'-I' + join_paths(meson.project_source_root(), 'src/poly/cl'),
|
||||
'-I' + join_paths(meson.current_source_dir(), '.'),
|
||||
'-I' + join_paths(meson.current_source_dir(), '../../'),
|
||||
'-I' + join_paths(meson.current_source_dir(), 'shaders'),
|
||||
cl_args,
|
||||
],
|
||||
depfile : 'libkk_shaders.h.d',
|
||||
)
|
||||
|
||||
libkk = custom_target(
|
||||
input : libkk_spv,
|
||||
output : ['libkk.cpp', 'libkk.h'],
|
||||
command : [prog_vtn_bindgen2, libkk_spv, '@OUTPUT0@', '@OUTPUT1@'],
|
||||
)
|
||||
|
||||
idep_libkk = declare_dependency(
|
||||
sources : [libkk],
|
||||
include_directories : include_directories('.'),
|
||||
)
|
||||
|
||||
libkk_shaders = custom_target(
|
||||
input : libkk_spv,
|
||||
output : ['libkk_shaders.h', 'libkk_shaders.c'],
|
||||
command : [prog_kk_clc, libkk_spv, '@OUTPUT0@', '@OUTPUT1@']
|
||||
)
|
||||
|
||||
# TODO_KOSMICKRISP We can get rid of this library once we remove the generated entrypoints for the trampoline
|
||||
libkk_shaders_lib = static_library(
|
||||
'libkk_shaders',
|
||||
sources : [libkk_shaders],
|
||||
link_with : [libpoly_nir],
|
||||
dependencies : [idep_nir, idep_mesautil, idep_libpoly],
|
||||
)
|
||||
|
||||
idep_libkk_shaders_h = declare_dependency(
|
||||
sources : [libkk_shaders[0]],
|
||||
include_directories : include_directories('.'),
|
||||
link_with : libkk_shaders_lib,
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
# Copyright 2025 LunarG, Inc.
|
||||
# Copyright 2025 Google LLC
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
subdir('bridge')
|
||||
subdir('compiler')
|
||||
subdir('util')
|
||||
subdir('clc')
|
||||
subdir('libkk')
|
||||
subdir('vulkan')
|
||||
|
||||
executable(
|
||||
'kosmicomp',
|
||||
files('kosmicomp.c'),
|
||||
dependencies : [idep_nir, idep_vtn, idep_vulkan_runtime_headers, idep_vulkan_util_headers],
|
||||
link_with: [libkk],
|
||||
link_args: ['-Wl,-undefined,dynamic_lookup'],
|
||||
)
|
||||
@@ -0,0 +1,195 @@
|
||||
# coding=utf-8
|
||||
COPYRIGHT = """\
|
||||
/*
|
||||
* Copyright 2020 Intel Corporation
|
||||
* Copyright 2025 LunarG, Inc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sub license, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial portions
|
||||
* of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||
* IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
|
||||
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
|
||||
from mako.template import Template
|
||||
|
||||
# Mesa-local imports must be declared in meson variable
|
||||
# '{file_without_suffix}_depend_files'.
|
||||
from vk_entrypoints import get_entrypoints_from_xml
|
||||
|
||||
TEMPLATE_H = Template(COPYRIGHT + """\
|
||||
/* This file generated from ${filename}, don't edit directly. */
|
||||
|
||||
#ifndef VK_DISPATCH_TRAMPOLINES_H
|
||||
#define VK_DISPATCH_TRAMPOLINES_H
|
||||
|
||||
#include "vk_dispatch_table.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern struct vk_physical_device_dispatch_table kk_physical_device_trampolines;
|
||||
extern struct vk_device_dispatch_table kk_device_trampolines;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VK_DISPATCH_TRAMPOLINES_H */
|
||||
""")
|
||||
|
||||
TEMPLATE_C = Template(COPYRIGHT + """\
|
||||
/* This file generated from ${filename}, don't edit directly. */
|
||||
|
||||
#include "kk_device.h"
|
||||
#include "kk_dispatch_trampolines.h"
|
||||
#include "vk_object.h"
|
||||
#include "vk_physical_device.h"
|
||||
|
||||
% for e in entrypoints:
|
||||
% if not e.is_physical_device_entrypoint() or e.alias:
|
||||
<% continue %>
|
||||
% endif
|
||||
% if e.guard is not None:
|
||||
#ifdef ${e.guard}
|
||||
% endif
|
||||
static VKAPI_ATTR ${e.return_type} VKAPI_CALL
|
||||
${e.prefixed_name('kk_tramp')}(${e.decl_params()})
|
||||
{
|
||||
<% assert e.params[0].type == 'VkPhysicalDevice' %>
|
||||
VK_FROM_HANDLE(vk_physical_device, vk_physical_device, ${e.params[0].name});
|
||||
% if e.return_type == 'void':
|
||||
vk_physical_device->dispatch_table.${e.name}(${e.call_params()});
|
||||
% else:
|
||||
return vk_physical_device->dispatch_table.${e.name}(${e.call_params()});
|
||||
% endif
|
||||
}
|
||||
% if e.guard is not None:
|
||||
#endif
|
||||
% endif
|
||||
% endfor
|
||||
|
||||
struct vk_physical_device_dispatch_table kk_physical_device_trampolines = {
|
||||
% for e in entrypoints:
|
||||
% if not e.is_physical_device_entrypoint() or e.alias:
|
||||
<% continue %>
|
||||
% endif
|
||||
% if e.guard is not None:
|
||||
#ifdef ${e.guard}
|
||||
% endif
|
||||
.${e.name} = ${e.prefixed_name('kk_tramp')},
|
||||
% if e.guard is not None:
|
||||
#endif
|
||||
% endif
|
||||
% endfor
|
||||
};
|
||||
|
||||
% for e in entrypoints:
|
||||
% if not e.is_device_entrypoint() or e.alias:
|
||||
<% continue %>
|
||||
% endif
|
||||
% if e.guard is not None:
|
||||
#ifdef ${e.guard}
|
||||
% endif
|
||||
static VKAPI_ATTR ${e.return_type} VKAPI_CALL
|
||||
${e.prefixed_name('kk_tramp')}(${e.decl_params()})
|
||||
{
|
||||
% if e.params[0].type == 'VkDevice':
|
||||
VK_FROM_HANDLE(kk_device, kk_device, ${e.params[0].name});
|
||||
% if e.return_type == 'void':
|
||||
kk_device->exposed_dispatch_table.${e.name}(${e.call_params()});
|
||||
% else:
|
||||
return kk_device->exposed_dispatch_table.${e.name}(${e.call_params()});
|
||||
% endif
|
||||
% elif e.params[0].type in ('VkCommandBuffer', 'VkQueue'):
|
||||
struct vk_object_base *vk_object = (struct vk_object_base *)${e.params[0].name};
|
||||
struct kk_device *kk_device = container_of(vk_object->device, struct kk_device, vk);
|
||||
% if e.return_type == 'void':
|
||||
kk_device->exposed_dispatch_table.${e.name}(${e.call_params()});
|
||||
% else:
|
||||
return kk_device->exposed_dispatch_table.${e.name}(${e.call_params()});
|
||||
% endif
|
||||
% else:
|
||||
assert(!"Unhandled device child trampoline case: ${e.params[0].type}");
|
||||
% endif
|
||||
}
|
||||
% if e.guard is not None:
|
||||
#endif
|
||||
% endif
|
||||
% endfor
|
||||
|
||||
struct vk_device_dispatch_table kk_device_trampolines = {
|
||||
% for e in entrypoints:
|
||||
% if not e.is_device_entrypoint() or e.alias:
|
||||
<% continue %>
|
||||
% endif
|
||||
% if e.guard is not None:
|
||||
#ifdef ${e.guard}
|
||||
% endif
|
||||
.${e.name} = ${e.prefixed_name('kk_tramp')},
|
||||
% if e.guard is not None:
|
||||
#endif
|
||||
% endif
|
||||
% endfor
|
||||
};
|
||||
""")
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--out-c', help='Output C file.')
|
||||
parser.add_argument('--out-h', help='Output H file.')
|
||||
parser.add_argument('--beta', required=True, help='Enable beta extensions.')
|
||||
parser.add_argument('--xml',
|
||||
help='Vulkan API XML file.',
|
||||
required=True,
|
||||
action='append',
|
||||
dest='xml_files')
|
||||
args = parser.parse_args()
|
||||
|
||||
entrypoints = get_entrypoints_from_xml(args.xml_files, args.beta)
|
||||
|
||||
# For outputting entrypoints.h we generate a anv_EntryPoint() prototype
|
||||
# per entry point.
|
||||
try:
|
||||
if args.out_h:
|
||||
with open(args.out_h, 'w', encoding='utf-8') as f:
|
||||
f.write(TEMPLATE_H.render(entrypoints=entrypoints,
|
||||
filename=os.path.basename(__file__)))
|
||||
if args.out_c:
|
||||
with open(args.out_c, 'w', encoding='utf-8') as f:
|
||||
f.write(TEMPLATE_C.render(entrypoints=entrypoints,
|
||||
filename=os.path.basename(__file__)))
|
||||
except Exception:
|
||||
# In the event there's an error, this imports some helpers from mako
|
||||
# to print a useful stack trace and prints it, then exits with
|
||||
# status 1, if python is run with debug; otherwise it just raises
|
||||
# the exception
|
||||
if __debug__:
|
||||
import sys
|
||||
from mako import exceptions
|
||||
sys.stderr.write(exceptions.text_error_template().render() + '\n')
|
||||
sys.exit(1)
|
||||
raise
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright © 2025 LunarG, Inc
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
kk_dispatch_trampolines_gen = files('kk_dispatch_trampolines_gen.py')
|
||||
|
||||
kk_dispatch_trampolines = custom_target(
|
||||
'kk_dispatch_trampolines',
|
||||
input : [kk_dispatch_trampolines_gen, vk_api_xml],
|
||||
output : ['kk_dispatch_trampolines.c', 'kk_dispatch_trampolines.h'],
|
||||
command : [
|
||||
prog_python, '@INPUT0@', '--xml', '@INPUT1@',
|
||||
'--out-c', '@OUTPUT0@', '--out-h', '@OUTPUT1@',
|
||||
'--beta', with_vulkan_beta.to_string()
|
||||
],
|
||||
depend_files : vk_dispatch_trampolines_gen_depend_files,
|
||||
)
|
||||
@@ -0,0 +1,147 @@
|
||||
# Copyright 2020 Intel Corporation
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||
# copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sub license, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice (including the
|
||||
# next paragraph) shall be included in all copies or substantial portions
|
||||
# of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||||
# IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
|
||||
# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
import xml.etree.ElementTree as et
|
||||
|
||||
from collections import OrderedDict, namedtuple
|
||||
|
||||
# Mesa-local imports must be declared in meson variable
|
||||
# '{file_without_suffix}_depend_files'.
|
||||
from vk_extensions import get_all_required, filter_api
|
||||
|
||||
EntrypointParam = namedtuple('EntrypointParam', 'type name decl len')
|
||||
|
||||
class EntrypointBase:
|
||||
def __init__(self, name):
|
||||
assert name.startswith('vk')
|
||||
self.name = name[2:]
|
||||
self.alias = None
|
||||
self.guard = None
|
||||
self.entry_table_index = None
|
||||
# Extensions which require this entrypoint
|
||||
self.core_version = None
|
||||
self.extensions = []
|
||||
|
||||
def prefixed_name(self, prefix):
|
||||
return prefix + '_' + self.name
|
||||
|
||||
class Entrypoint(EntrypointBase):
|
||||
def __init__(self, name, return_type, params):
|
||||
super(Entrypoint, self).__init__(name)
|
||||
self.return_type = return_type
|
||||
self.params = params
|
||||
self.guard = None
|
||||
self.aliases = []
|
||||
self.disp_table_index = None
|
||||
|
||||
def is_physical_device_entrypoint(self):
|
||||
return self.params[0].type in ('VkPhysicalDevice', )
|
||||
|
||||
def is_device_entrypoint(self):
|
||||
return self.params[0].type in ('VkDevice', 'VkCommandBuffer', 'VkQueue')
|
||||
|
||||
def decl_params(self, start=0):
|
||||
return ', '.join(p.decl for p in self.params[start:])
|
||||
|
||||
def call_params(self, start=0):
|
||||
return ', '.join(p.name for p in self.params[start:])
|
||||
|
||||
class EntrypointAlias(EntrypointBase):
|
||||
def __init__(self, name, entrypoint):
|
||||
super(EntrypointAlias, self).__init__(name)
|
||||
self.alias = entrypoint
|
||||
entrypoint.aliases.append(self)
|
||||
|
||||
def is_physical_device_entrypoint(self):
|
||||
return self.alias.is_physical_device_entrypoint()
|
||||
|
||||
def is_device_entrypoint(self):
|
||||
return self.alias.is_device_entrypoint()
|
||||
|
||||
def prefixed_name(self, prefix):
|
||||
return self.alias.prefixed_name(prefix)
|
||||
|
||||
@property
|
||||
def params(self):
|
||||
return self.alias.params
|
||||
|
||||
@property
|
||||
def return_type(self):
|
||||
return self.alias.return_type
|
||||
|
||||
@property
|
||||
def disp_table_index(self):
|
||||
return self.alias.disp_table_index
|
||||
|
||||
def decl_params(self):
|
||||
return self.alias.decl_params()
|
||||
|
||||
def call_params(self):
|
||||
return self.alias.call_params()
|
||||
|
||||
def get_entrypoints(doc, api, beta):
|
||||
"""Extract the entry points from the registry."""
|
||||
entrypoints = OrderedDict()
|
||||
|
||||
required = get_all_required(doc, 'command', api, beta)
|
||||
|
||||
for command in doc.findall('./commands/command'):
|
||||
if not filter_api(command, api):
|
||||
continue
|
||||
|
||||
if 'alias' in command.attrib:
|
||||
name = command.attrib['name']
|
||||
target = command.attrib['alias']
|
||||
e = EntrypointAlias(name, entrypoints[target])
|
||||
else:
|
||||
name = command.find('./proto/name').text
|
||||
ret_type = command.find('./proto/type').text
|
||||
params = [EntrypointParam(
|
||||
type=p.find('./type').text,
|
||||
name=p.find('./name').text,
|
||||
decl=''.join(p.itertext()),
|
||||
len=p.attrib.get('altlen', p.attrib.get('len', None))
|
||||
) for p in command.findall('./param') if filter_api(p, api)]
|
||||
# They really need to be unique
|
||||
e = Entrypoint(name, ret_type, params)
|
||||
|
||||
if name not in required:
|
||||
continue
|
||||
|
||||
r = required[name]
|
||||
e.core_version = r.core_version
|
||||
e.extensions = r.extensions
|
||||
e.guard = r.guard
|
||||
|
||||
assert name not in entrypoints, name
|
||||
entrypoints[name] = e
|
||||
|
||||
return entrypoints.values()
|
||||
|
||||
def get_entrypoints_from_xml(xml_files, beta, api='vulkan'):
|
||||
entrypoints = []
|
||||
|
||||
for filename in xml_files:
|
||||
doc = et.parse(filename)
|
||||
entrypoints += get_entrypoints(doc, api, beta)
|
||||
|
||||
return entrypoints
|
||||
@@ -0,0 +1,371 @@
|
||||
import copy
|
||||
import re
|
||||
import xml.etree.ElementTree as et
|
||||
|
||||
def get_api_list(s):
|
||||
apis = []
|
||||
for a in s.split(','):
|
||||
if a == 'disabled':
|
||||
continue
|
||||
assert a in ('vulkan', 'vulkansc')
|
||||
apis.append(a)
|
||||
return apis
|
||||
|
||||
class Extension:
|
||||
def __init__(self, name, number, ext_version):
|
||||
self.name = name
|
||||
self.type = None
|
||||
self.number = number
|
||||
self.platform = None
|
||||
self.provisional = False
|
||||
self.ext_version = int(ext_version)
|
||||
self.supported = []
|
||||
|
||||
def from_xml(ext_elem):
|
||||
name = ext_elem.attrib['name']
|
||||
number = int(ext_elem.attrib['number'])
|
||||
supported = get_api_list(ext_elem.attrib['supported'])
|
||||
if name == 'VK_ANDROID_native_buffer':
|
||||
assert not supported
|
||||
supported = ['vulkan']
|
||||
|
||||
if not supported:
|
||||
return Extension(name, number, 0)
|
||||
|
||||
version = None
|
||||
for enum_elem in ext_elem.findall('.require/enum'):
|
||||
if enum_elem.attrib['name'].endswith('_SPEC_VERSION'):
|
||||
# Skip alias SPEC_VERSIONs
|
||||
if 'value' in enum_elem.attrib:
|
||||
assert version is None
|
||||
version = int(enum_elem.attrib['value'])
|
||||
|
||||
assert version is not None
|
||||
ext = Extension(name, number, version)
|
||||
ext.type = ext_elem.attrib['type']
|
||||
ext.platform = ext_elem.attrib.get('platform', None)
|
||||
ext.provisional = ext_elem.attrib.get('provisional', False)
|
||||
ext.supported = supported
|
||||
|
||||
return ext
|
||||
|
||||
def c_android_condition(self):
|
||||
# if it's an EXT or vendor extension, it's allowed
|
||||
if not self.name.startswith(ANDROID_EXTENSION_WHITELIST_PREFIXES):
|
||||
return 'true'
|
||||
|
||||
allowed_version = ALLOWED_ANDROID_VERSION.get(self.name, None)
|
||||
if allowed_version is None:
|
||||
return 'false'
|
||||
|
||||
return 'ANDROID_API_LEVEL >= %d' % (allowed_version)
|
||||
|
||||
class ApiVersion:
|
||||
def __init__(self, version):
|
||||
self.version = version
|
||||
|
||||
class VkVersion:
|
||||
def __init__(self, string):
|
||||
split = string.split('.')
|
||||
self.major = int(split[0])
|
||||
self.minor = int(split[1])
|
||||
if len(split) > 2:
|
||||
assert len(split) == 3
|
||||
self.patch = int(split[2])
|
||||
else:
|
||||
self.patch = None
|
||||
|
||||
# Sanity check. The range bits are required by the definition of the
|
||||
# VK_MAKE_VERSION macro
|
||||
assert self.major < 1024 and self.minor < 1024
|
||||
assert self.patch is None or self.patch < 4096
|
||||
assert str(self) == string
|
||||
|
||||
def __str__(self):
|
||||
ver_list = [str(self.major), str(self.minor)]
|
||||
if self.patch is not None:
|
||||
ver_list.append(str(self.patch))
|
||||
return '.'.join(ver_list)
|
||||
|
||||
def c_vk_version(self):
|
||||
ver_list = [str(self.major), str(self.minor), str(self.patch or 0)]
|
||||
return 'VK_MAKE_VERSION(' + ', '.join(ver_list) + ')'
|
||||
|
||||
def __int_ver(self):
|
||||
# This is just an expansion of VK_VERSION
|
||||
return (self.major << 22) | (self.minor << 12) | (self.patch or 0)
|
||||
|
||||
def __gt__(self, other):
|
||||
# If only one of them has a patch version, "ignore" it by making
|
||||
# other's patch version match self.
|
||||
if (self.patch is None) != (other.patch is None):
|
||||
other = copy.copy(other)
|
||||
other.patch = self.patch
|
||||
|
||||
return self.__int_ver() > other.__int_ver()
|
||||
|
||||
# Sort the extension list the way we expect: KHR, then EXT, then vendors
|
||||
# alphabetically. For digits, read them as a whole number sort that.
|
||||
# eg.: VK_KHR_8bit_storage < VK_KHR_16bit_storage < VK_EXT_acquire_xlib_display
|
||||
def extension_order(ext):
|
||||
order = []
|
||||
for substring in re.split('(KHR|EXT|[0-9]+)', ext.name):
|
||||
if substring == 'KHR':
|
||||
order.append(1)
|
||||
if substring == 'EXT':
|
||||
order.append(2)
|
||||
elif substring.isdigit():
|
||||
order.append(int(substring))
|
||||
else:
|
||||
order.append(substring)
|
||||
return order
|
||||
|
||||
def get_all_exts_from_xml(xml, api='vulkan'):
|
||||
""" Get a list of all Vulkan extensions. """
|
||||
|
||||
xml = et.parse(xml)
|
||||
|
||||
extensions = []
|
||||
for ext_elem in xml.findall('.extensions/extension'):
|
||||
ext = Extension.from_xml(ext_elem)
|
||||
if api in ext.supported:
|
||||
extensions.append(ext)
|
||||
|
||||
return sorted(extensions, key=extension_order)
|
||||
|
||||
def init_exts_from_xml(xml, extensions, platform_defines):
|
||||
""" Walk the Vulkan XML and fill out extra extension information. """
|
||||
|
||||
xml = et.parse(xml)
|
||||
|
||||
ext_name_map = {}
|
||||
for ext in extensions:
|
||||
ext_name_map[ext.name] = ext
|
||||
|
||||
# KHR_display is missing from the list.
|
||||
platform_defines.append('VK_USE_PLATFORM_DISPLAY_KHR')
|
||||
for platform in xml.findall('./platforms/platform'):
|
||||
platform_defines.append(platform.attrib['protect'])
|
||||
|
||||
for ext_elem in xml.findall('.extensions/extension'):
|
||||
ext_name = ext_elem.attrib['name']
|
||||
if ext_name not in ext_name_map:
|
||||
continue
|
||||
|
||||
ext = ext_name_map[ext_name]
|
||||
ext.type = ext_elem.attrib['type']
|
||||
|
||||
class Requirements:
|
||||
def __init__(self, core_version=None):
|
||||
self.core_version = core_version
|
||||
self.extensions = []
|
||||
self.guard = None
|
||||
|
||||
def add_extension(self, ext):
|
||||
for e in self.extensions:
|
||||
if e == ext:
|
||||
return;
|
||||
assert e.name != ext.name
|
||||
|
||||
self.extensions.append(ext)
|
||||
|
||||
def filter_api(elem, api):
|
||||
if 'api' not in elem.attrib:
|
||||
return True
|
||||
|
||||
return api in elem.attrib['api'].split(',')
|
||||
|
||||
def get_alias(aliases, name):
|
||||
if name in aliases:
|
||||
# in case the spec registry adds an alias chain later
|
||||
return get_alias(aliases, aliases[name])
|
||||
return name
|
||||
|
||||
def get_all_required(xml, thing, api, beta):
|
||||
things = {}
|
||||
aliases = {}
|
||||
for struct in xml.findall('./types/type[@category="struct"][@alias]'):
|
||||
if not filter_api(struct, api):
|
||||
continue
|
||||
|
||||
name = struct.attrib['name']
|
||||
alias = struct.attrib['alias']
|
||||
aliases[name] = alias
|
||||
|
||||
for feature in xml.findall('./feature'):
|
||||
if not filter_api(feature, api):
|
||||
continue
|
||||
|
||||
version = VkVersion(feature.attrib['number'])
|
||||
for t in feature.findall('./require/' + thing):
|
||||
name = t.attrib['name']
|
||||
assert name not in things
|
||||
things[name] = Requirements(core_version=version)
|
||||
|
||||
for extension in xml.findall('.extensions/extension'):
|
||||
ext = Extension.from_xml(extension)
|
||||
if api not in ext.supported:
|
||||
continue
|
||||
|
||||
if beta != 'true' and ext.provisional:
|
||||
continue
|
||||
|
||||
for require in extension.findall('./require'):
|
||||
if not filter_api(require, api):
|
||||
continue
|
||||
|
||||
for t in require.findall('./' + thing):
|
||||
name = get_alias(aliases, t.attrib['name'])
|
||||
r = things.setdefault(name, Requirements())
|
||||
r.add_extension(ext)
|
||||
|
||||
platform_defines = {}
|
||||
for platform in xml.findall('./platforms/platform'):
|
||||
name = platform.attrib['name']
|
||||
define = platform.attrib['protect']
|
||||
platform_defines[name] = define
|
||||
|
||||
for req in things.values():
|
||||
if req.core_version is not None:
|
||||
continue
|
||||
|
||||
for ext in req.extensions:
|
||||
if ext.platform in platform_defines:
|
||||
req.guard = platform_defines[ext.platform]
|
||||
break
|
||||
|
||||
return things
|
||||
|
||||
# Mapping between extension name and the android version in which the extension
|
||||
# was whitelisted in Android CTS's dEQP-VK.info.device_extensions and
|
||||
# dEQP-VK.api.info.android.no_unknown_extensions, excluding those blocked by
|
||||
# android.graphics.cts.VulkanFeaturesTest#testVulkanBlockedExtensions.
|
||||
ALLOWED_ANDROID_VERSION = {
|
||||
# checkInstanceExtensions on oreo-cts-release
|
||||
"VK_KHR_surface": 26,
|
||||
"VK_KHR_display": 26,
|
||||
"VK_KHR_android_surface": 26,
|
||||
"VK_KHR_mir_surface": 26,
|
||||
"VK_KHR_wayland_surface": 26,
|
||||
"VK_KHR_win32_surface": 26,
|
||||
"VK_KHR_xcb_surface": 26,
|
||||
"VK_KHR_xlib_surface": 26,
|
||||
"VK_KHR_get_physical_device_properties2": 26,
|
||||
"VK_KHR_get_surface_capabilities2": 26,
|
||||
"VK_KHR_external_memory_capabilities": 26,
|
||||
"VK_KHR_external_semaphore_capabilities": 26,
|
||||
"VK_KHR_external_fence_capabilities": 26,
|
||||
# on pie-cts-release
|
||||
"VK_KHR_device_group_creation": 28,
|
||||
"VK_KHR_get_display_properties2": 28,
|
||||
# on android10-tests-release
|
||||
"VK_KHR_surface_protected_capabilities": 29,
|
||||
# on android13-tests-release
|
||||
"VK_KHR_portability_enumeration": 33,
|
||||
|
||||
# checkDeviceExtensions on oreo-cts-release
|
||||
"VK_KHR_swapchain": 26,
|
||||
"VK_KHR_display_swapchain": 26,
|
||||
"VK_KHR_sampler_mirror_clamp_to_edge": 26,
|
||||
"VK_KHR_shader_draw_parameters": 26,
|
||||
"VK_KHR_maintenance1": 26,
|
||||
"VK_KHR_push_descriptor": 26,
|
||||
"VK_KHR_descriptor_update_template": 26,
|
||||
"VK_KHR_incremental_present": 26,
|
||||
"VK_KHR_shared_presentable_image": 26,
|
||||
"VK_KHR_storage_buffer_storage_class": 26,
|
||||
"VK_KHR_16bit_storage": 26,
|
||||
"VK_KHR_get_memory_requirements2": 26,
|
||||
"VK_KHR_external_memory": 26,
|
||||
"VK_KHR_external_memory_fd": 26,
|
||||
"VK_KHR_external_memory_win32": 26,
|
||||
"VK_KHR_external_semaphore": 26,
|
||||
"VK_KHR_external_semaphore_fd": 26,
|
||||
"VK_KHR_external_semaphore_win32": 26,
|
||||
"VK_KHR_external_fence": 26,
|
||||
"VK_KHR_external_fence_fd": 26,
|
||||
"VK_KHR_external_fence_win32": 26,
|
||||
"VK_KHR_win32_keyed_mutex": 26,
|
||||
"VK_KHR_dedicated_allocation": 26,
|
||||
"VK_KHR_variable_pointers": 26,
|
||||
"VK_KHR_relaxed_block_layout": 26,
|
||||
"VK_KHR_bind_memory2": 26,
|
||||
"VK_KHR_maintenance2": 26,
|
||||
"VK_KHR_image_format_list": 26,
|
||||
"VK_KHR_sampler_ycbcr_conversion": 26,
|
||||
# on oreo-mr1-cts-release
|
||||
"VK_KHR_draw_indirect_count": 27,
|
||||
# on pie-cts-release
|
||||
"VK_KHR_device_group": 28,
|
||||
"VK_KHR_multiview": 28,
|
||||
"VK_KHR_maintenance3": 28,
|
||||
"VK_KHR_create_renderpass2": 28,
|
||||
"VK_KHR_driver_properties": 28,
|
||||
# on android10-tests-release
|
||||
"VK_KHR_shader_float_controls": 29,
|
||||
"VK_KHR_shader_float16_int8": 29,
|
||||
"VK_KHR_8bit_storage": 29,
|
||||
"VK_KHR_depth_stencil_resolve": 29,
|
||||
"VK_KHR_swapchain_mutable_format": 29,
|
||||
"VK_KHR_shader_atomic_int64": 29,
|
||||
"VK_KHR_vulkan_memory_model": 29,
|
||||
"VK_KHR_swapchain_mutable_format": 29,
|
||||
"VK_KHR_uniform_buffer_standard_layout": 29,
|
||||
# on android11-tests-release
|
||||
"VK_KHR_imageless_framebuffer": 30,
|
||||
"VK_KHR_shader_subgroup_extended_types": 30,
|
||||
"VK_KHR_buffer_device_address": 30,
|
||||
"VK_KHR_separate_depth_stencil_layouts": 30,
|
||||
"VK_KHR_timeline_semaphore": 30,
|
||||
"VK_KHR_spirv_1_4": 30,
|
||||
"VK_KHR_pipeline_executable_properties": 30,
|
||||
"VK_KHR_shader_clock": 30,
|
||||
# blocked by testVulkanBlockedExtensions
|
||||
# "VK_KHR_performance_query": 30,
|
||||
"VK_KHR_shader_non_semantic_info": 30,
|
||||
"VK_KHR_copy_commands2": 30,
|
||||
# on android12-tests-release
|
||||
"VK_KHR_shader_terminate_invocation": 31,
|
||||
"VK_KHR_ray_tracing_pipeline": 31,
|
||||
"VK_KHR_ray_query": 31,
|
||||
"VK_KHR_acceleration_structure": 31,
|
||||
"VK_KHR_pipeline_library": 31,
|
||||
"VK_KHR_deferred_host_operations": 31,
|
||||
"VK_KHR_fragment_shading_rate": 31,
|
||||
"VK_KHR_zero_initialize_workgroup_memory": 31,
|
||||
"VK_KHR_workgroup_memory_explicit_layout": 31,
|
||||
"VK_KHR_synchronization2": 31,
|
||||
"VK_KHR_shader_integer_dot_product": 31,
|
||||
# on android13-tests-release
|
||||
"VK_KHR_dynamic_rendering": 33,
|
||||
"VK_KHR_format_feature_flags2": 33,
|
||||
"VK_KHR_global_priority": 33,
|
||||
"VK_KHR_maintenance4": 33,
|
||||
"VK_KHR_portability_subset": 33,
|
||||
"VK_KHR_present_id": 33,
|
||||
"VK_KHR_present_wait": 33,
|
||||
"VK_KHR_shader_subgroup_uniform_control_flow": 33,
|
||||
|
||||
# testNoUnknownExtensions on oreo-cts-release
|
||||
"VK_GOOGLE_display_timing": 26,
|
||||
# on pie-cts-release
|
||||
"VK_ANDROID_external_memory_android_hardware_buffer": 28,
|
||||
# on android11-tests-release
|
||||
"VK_GOOGLE_decorate_string": 30,
|
||||
"VK_GOOGLE_hlsl_functionality1": 30,
|
||||
# on android13-tests-release
|
||||
"VK_GOOGLE_surfaceless_query": 33,
|
||||
|
||||
# this HAL extension is always allowed and will be filtered out by the
|
||||
# loader
|
||||
"VK_ANDROID_native_buffer": 26,
|
||||
}
|
||||
|
||||
# Extensions with these prefixes are checked in Android CTS, and thus must be
|
||||
# whitelisted per the preceding dict.
|
||||
ANDROID_EXTENSION_WHITELIST_PREFIXES = (
|
||||
"VK_KHX",
|
||||
"VK_KHR",
|
||||
"VK_GOOGLE",
|
||||
"VK_ANDROID"
|
||||
)
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "kk_bo.h"
|
||||
|
||||
#include "kk_device.h"
|
||||
|
||||
#include "kosmickrisp/bridge/mtl_bridge.h"
|
||||
|
||||
#include "util/u_memory.h"
|
||||
|
||||
VkResult
|
||||
kk_alloc_bo(struct kk_device *dev, struct vk_object_base *log_obj,
|
||||
uint64_t size_B, uint64_t align_B, struct kk_bo **bo_out)
|
||||
{
|
||||
VkResult result = VK_SUCCESS;
|
||||
|
||||
// TODO_KOSMICKRISP: Probably requires handling the buffer maximum 256MB
|
||||
uint64_t minimum_alignment = 0u;
|
||||
mtl_heap_buffer_size_and_align_with_length(dev->mtl_handle, &size_B,
|
||||
&minimum_alignment);
|
||||
minimum_alignment = MAX2(minimum_alignment, align_B);
|
||||
|
||||
#if DETECT_ARCH_X86_64
|
||||
/* KK_WORKAROUND_8 */
|
||||
if (!(dev->disabled_workarounds & BITFIELD64_BIT(8))) {
|
||||
minimum_alignment = MAX2(minimum_alignment, 16384);
|
||||
}
|
||||
#endif
|
||||
|
||||
size_B = align64(size_B, minimum_alignment);
|
||||
mtl_heap *handle =
|
||||
mtl_new_heap(dev->mtl_handle, size_B, KK_MTL_RESOURCE_OPTIONS);
|
||||
if (handle == NULL) {
|
||||
result = vk_errorf(log_obj, VK_ERROR_OUT_OF_DEVICE_MEMORY, "%m");
|
||||
goto fail_heap;
|
||||
}
|
||||
|
||||
mtl_buffer *map = mtl_new_buffer_with_length(handle, size_B, 0u);
|
||||
if (map == NULL) {
|
||||
result = vk_errorf(log_obj, VK_ERROR_OUT_OF_DEVICE_MEMORY, "%m");
|
||||
goto fail_map;
|
||||
}
|
||||
|
||||
struct kk_bo *bo = CALLOC_STRUCT(kk_bo);
|
||||
|
||||
if (bo == NULL) {
|
||||
result = vk_errorf(log_obj, VK_ERROR_OUT_OF_HOST_MEMORY, "%m");
|
||||
goto fail_alloc;
|
||||
}
|
||||
|
||||
bo->mtl_handle = handle;
|
||||
bo->size_B = size_B;
|
||||
bo->map = map;
|
||||
bo->gpu = mtl_buffer_get_gpu_address(map);
|
||||
bo->cpu = mtl_get_contents(map);
|
||||
|
||||
kk_device_add_heap_to_residency_set(dev, handle);
|
||||
|
||||
*bo_out = bo;
|
||||
return result;
|
||||
|
||||
fail_alloc:
|
||||
mtl_release(map);
|
||||
fail_map:
|
||||
mtl_release(handle);
|
||||
fail_heap:
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
kk_destroy_bo(struct kk_device *dev, struct kk_bo *bo)
|
||||
{
|
||||
/* We may only have a mapped buffer, for example if the memory was imported
|
||||
* from a host pointer */
|
||||
if (bo->mtl_handle)
|
||||
kk_device_remove_heap_from_residency_set(dev, bo->mtl_handle);
|
||||
else
|
||||
kk_device_remove_buffer_from_residency_set(dev, bo->map);
|
||||
|
||||
mtl_release(bo->map);
|
||||
|
||||
if (bo->mtl_handle)
|
||||
mtl_release(bo->mtl_handle);
|
||||
|
||||
FREE(bo);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright © 2025 LunarG, Inc
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef KK_BO_H
|
||||
#define KK_BO_H 1
|
||||
|
||||
#include "kosmickrisp/bridge/mtl_types.h"
|
||||
|
||||
#include "vulkan/vulkan_core.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
struct kk_device;
|
||||
struct vk_object_base;
|
||||
|
||||
struct kk_bo {
|
||||
mtl_heap *mtl_handle;
|
||||
mtl_buffer *map;
|
||||
uint64_t size_B;
|
||||
uint64_t gpu; // GPU address
|
||||
void *cpu; // CPU address
|
||||
};
|
||||
|
||||
struct kk_ptr {
|
||||
void *cpu;
|
||||
uint64_t gpu;
|
||||
|
||||
/* Pointer in terms of a Metal buffer and offset */
|
||||
mtl_buffer *buffer;
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
VkResult kk_alloc_bo(struct kk_device *dev, struct vk_object_base *log_obj,
|
||||
uint64_t size_B, uint64_t align_B, struct kk_bo **bo_out);
|
||||
|
||||
void kk_destroy_bo(struct kk_device *dev, struct kk_bo *bo);
|
||||
|
||||
#endif /* KK_BO_H */
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright © 2022 Collabora Ltd. and Red Hat Inc.
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "kk_buffer.h"
|
||||
|
||||
#include "kk_device.h"
|
||||
#include "kk_entrypoints.h"
|
||||
#include "kk_physical_device.h"
|
||||
|
||||
#include "kosmickrisp/bridge/mtl_bridge.h"
|
||||
|
||||
static uint64_t
|
||||
kk_get_buffer_alignment(const struct kk_physical_device *pdev, uint64_t size,
|
||||
VkBufferUsageFlags2KHR usage_flags,
|
||||
VkBufferCreateFlags create_flags)
|
||||
{
|
||||
uint64_t alignment;
|
||||
mtl_heap_buffer_size_and_align_with_length(pdev->mtl_dev_handle, &size,
|
||||
&alignment);
|
||||
|
||||
/** TODO_KOSMICKRISP Metal requires that texel buffers be aligned to the
|
||||
* format they'll use. Since we won't be able to know the format until the
|
||||
* view is created, we should align to the worst case scenario. For this, we
|
||||
* need to request all supported format alignments and take the largest one.
|
||||
*/
|
||||
return alignment;
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkResult VKAPI_CALL
|
||||
kk_CreateBuffer(VkDevice device, const VkBufferCreateInfo *pCreateInfo,
|
||||
const VkAllocationCallbacks *pAllocator, VkBuffer *pBuffer)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_device, dev, device);
|
||||
struct kk_physical_device *pdev = kk_device_physical(dev);
|
||||
struct kk_buffer *buffer;
|
||||
|
||||
if (pCreateInfo->size > pdev->info.max_buffer_size)
|
||||
return vk_error(dev, VK_ERROR_OUT_OF_DEVICE_MEMORY);
|
||||
|
||||
buffer =
|
||||
vk_buffer_create(&dev->vk, pCreateInfo, pAllocator, sizeof(*buffer));
|
||||
if (!buffer)
|
||||
return vk_error(dev, VK_ERROR_OUT_OF_HOST_MEMORY);
|
||||
|
||||
*pBuffer = kk_buffer_to_handle(buffer);
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_DestroyBuffer(VkDevice device, VkBuffer _buffer,
|
||||
const VkAllocationCallbacks *pAllocator)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_device, dev, device);
|
||||
VK_FROM_HANDLE(kk_buffer, buffer, _buffer);
|
||||
|
||||
if (!buffer)
|
||||
return;
|
||||
|
||||
if (buffer->mtl_handle)
|
||||
mtl_release(buffer->mtl_handle);
|
||||
|
||||
vk_buffer_destroy(&dev->vk, pAllocator, &buffer->vk);
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_GetDeviceBufferMemoryRequirements(
|
||||
VkDevice device, const VkDeviceBufferMemoryRequirements *pInfo,
|
||||
VkMemoryRequirements2 *pMemoryRequirements)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_device, dev, device);
|
||||
struct kk_physical_device *pdev = kk_device_physical(dev);
|
||||
|
||||
const uint64_t alignment = kk_get_buffer_alignment(
|
||||
pdev, pInfo->pCreateInfo->size, pInfo->pCreateInfo->usage,
|
||||
pInfo->pCreateInfo->flags);
|
||||
|
||||
pMemoryRequirements->memoryRequirements = (VkMemoryRequirements){
|
||||
.size = align64(pInfo->pCreateInfo->size, alignment),
|
||||
.alignment = alignment,
|
||||
.memoryTypeBits = BITFIELD_MASK(pdev->mem_type_count),
|
||||
};
|
||||
|
||||
vk_foreach_struct_const(ext, pMemoryRequirements->pNext) {
|
||||
switch (ext->sType) {
|
||||
case VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS: {
|
||||
VkMemoryDedicatedRequirements *dedicated = (void *)ext;
|
||||
dedicated->prefersDedicatedAllocation = false;
|
||||
dedicated->requiresDedicatedAllocation = false;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
vk_debug_ignored_stype(ext->sType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_GetPhysicalDeviceExternalBufferProperties(
|
||||
VkPhysicalDevice physicalDevice,
|
||||
const VkPhysicalDeviceExternalBufferInfo *pExternalBufferInfo,
|
||||
VkExternalBufferProperties *pExternalBufferProperties)
|
||||
{
|
||||
/* The Vulkan 1.3.256 spec says:
|
||||
*
|
||||
* VUID-VkPhysicalDeviceExternalBufferInfo-handleType-parameter
|
||||
*
|
||||
* "handleType must be a valid VkExternalMemoryHandleTypeFlagBits value"
|
||||
*
|
||||
* This differs from VkPhysicalDeviceExternalImageFormatInfo, which
|
||||
* surprisingly permits handleType == 0.
|
||||
*/
|
||||
assert(pExternalBufferInfo->handleType != 0);
|
||||
|
||||
/* All of the current flags are for sparse which we don't support yet.
|
||||
* Even when we do support it, doing sparse on external memory sounds
|
||||
* sketchy. Also, just disallowing flags is the safe option.
|
||||
*/
|
||||
if (pExternalBufferInfo->flags)
|
||||
goto unsupported;
|
||||
|
||||
switch (pExternalBufferInfo->handleType) {
|
||||
case VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLHEAP_BIT_EXT:
|
||||
pExternalBufferProperties->externalMemoryProperties =
|
||||
kk_mtlheap_mem_props;
|
||||
return;
|
||||
case VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT:
|
||||
case VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_MAPPED_FOREIGN_MEMORY_BIT_EXT:
|
||||
pExternalBufferProperties->externalMemoryProperties = kk_host_mem_props;
|
||||
return;
|
||||
default:
|
||||
goto unsupported;
|
||||
}
|
||||
|
||||
unsupported:
|
||||
/* From the Vulkan 1.3.256 spec:
|
||||
*
|
||||
* compatibleHandleTypes must include at least handleType.
|
||||
*/
|
||||
pExternalBufferProperties->externalMemoryProperties =
|
||||
(VkExternalMemoryProperties){
|
||||
.compatibleHandleTypes = pExternalBufferInfo->handleType,
|
||||
};
|
||||
}
|
||||
|
||||
static VkResult
|
||||
kk_bind_buffer_memory(struct kk_device *dev, const VkBindBufferMemoryInfo *info)
|
||||
{
|
||||
// Do the actual memory binding
|
||||
VK_FROM_HANDLE(kk_device_memory, mem, info->memory);
|
||||
VK_FROM_HANDLE(kk_buffer, buffer, info->buffer);
|
||||
|
||||
if (mem->bo->mtl_handle)
|
||||
buffer->mtl_handle = mtl_new_buffer_with_length(
|
||||
mem->bo->mtl_handle, buffer->vk.size, info->memoryOffset);
|
||||
else {
|
||||
/* If the memory is not heap backed, for example if we imported a host
|
||||
* pointer, use the mapped buffer directly and retain a reference */
|
||||
buffer->mtl_handle = mem->bo->map;
|
||||
mtl_retain(buffer->mtl_handle);
|
||||
}
|
||||
|
||||
buffer->vk.device_address = mtl_buffer_get_gpu_address(buffer->mtl_handle);
|
||||
/* We need Metal to give us a CPU mapping so it correctly captures the
|
||||
* data in the GPU debugger... */
|
||||
mtl_get_contents(buffer->mtl_handle);
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkResult VKAPI_CALL
|
||||
kk_BindBufferMemory2(VkDevice device, uint32_t bindInfoCount,
|
||||
const VkBindBufferMemoryInfo *pBindInfos)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_device, dev, device);
|
||||
VkResult first_error_or_success = VK_SUCCESS;
|
||||
|
||||
for (uint32_t i = 0; i < bindInfoCount; ++i) {
|
||||
VkResult result = kk_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;
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkDeviceAddress VKAPI_CALL
|
||||
kk_GetBufferDeviceAddress(UNUSED VkDevice device,
|
||||
const VkBufferDeviceAddressInfo *pInfo)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_buffer, buffer, pInfo->buffer);
|
||||
|
||||
return vk_buffer_address(&buffer->vk, 0);
|
||||
}
|
||||
|
||||
VKAPI_ATTR uint64_t VKAPI_CALL
|
||||
kk_GetBufferOpaqueCaptureAddress(UNUSED VkDevice device,
|
||||
const VkBufferDeviceAddressInfo *pInfo)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_buffer, buffer, pInfo->buffer);
|
||||
|
||||
return vk_buffer_address(&buffer->vk, 0);
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkResult VKAPI_CALL
|
||||
kk_GetBufferOpaqueCaptureDescriptorDataEXT(
|
||||
VkDevice device, const VkBufferCaptureDescriptorDataInfoEXT *pInfo,
|
||||
void *pData)
|
||||
{
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright © 2022 Collabora Ltd. and Red Hat Inc.
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef KK_BUFFER_H
|
||||
#define KK_BUFFER_H 1
|
||||
|
||||
#include "kk_device_memory.h"
|
||||
#include "kk_private.h"
|
||||
|
||||
#include "kosmickrisp/bridge/mtl_types.h"
|
||||
|
||||
#include "vk_buffer.h"
|
||||
|
||||
struct kk_buffer {
|
||||
struct vk_buffer vk;
|
||||
mtl_buffer *mtl_handle;
|
||||
};
|
||||
|
||||
VK_DEFINE_NONDISP_HANDLE_CASTS(kk_buffer, vk.base, VkBuffer,
|
||||
VK_OBJECT_TYPE_BUFFER)
|
||||
|
||||
static inline struct kk_addr_range
|
||||
kk_buffer_addr_range(const struct kk_buffer *buffer, uint64_t offset,
|
||||
uint64_t range)
|
||||
{
|
||||
if (buffer == NULL)
|
||||
return (struct kk_addr_range){.range = 0};
|
||||
|
||||
return (struct kk_addr_range){
|
||||
.addr = vk_buffer_address(&buffer->vk, offset),
|
||||
.range = vk_buffer_range(&buffer->vk, offset, range),
|
||||
};
|
||||
}
|
||||
|
||||
static inline mtl_resource *
|
||||
kk_buffer_to_mtl_resource(const struct kk_buffer *buffer)
|
||||
{
|
||||
if (buffer != NULL) {
|
||||
return (mtl_resource *)buffer->mtl_handle;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif // KK_BUFFER_H
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright © 2022 Collabora Ltd. and Red Hat Inc.
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "kk_buffer_view.h"
|
||||
|
||||
#include "kk_buffer.h"
|
||||
#include "kk_device.h"
|
||||
#include "kk_entrypoints.h"
|
||||
#include "kk_format.h"
|
||||
#include "kk_image_layout.h"
|
||||
#include "kk_nir_lower_vbo.h"
|
||||
#include "kk_physical_device.h"
|
||||
|
||||
#include "kosmickrisp/bridge/mtl_bridge.h"
|
||||
#include "kosmickrisp/bridge/mtl_format.h"
|
||||
|
||||
#include "vk_format.h"
|
||||
|
||||
VkFormatFeatureFlags2
|
||||
kk_get_buffer_format_features(struct kk_physical_device *pdev,
|
||||
VkFormat vk_format)
|
||||
{
|
||||
VkFormatFeatureFlags2 features = 0;
|
||||
enum pipe_format p_format = vk_format_to_pipe_format(vk_format);
|
||||
|
||||
if (p_format == PIPE_FORMAT_NONE)
|
||||
return 0;
|
||||
|
||||
const struct kk_va_format *format = kk_get_va_format(p_format);
|
||||
if (format) {
|
||||
if (format->texel_buffer.read)
|
||||
features |= VK_FORMAT_FEATURE_2_UNIFORM_TEXEL_BUFFER_BIT;
|
||||
|
||||
if (format->texel_buffer.write) {
|
||||
features |= VK_FORMAT_FEATURE_2_STORAGE_TEXEL_BUFFER_BIT;
|
||||
features |= VK_FORMAT_FEATURE_2_STORAGE_WRITE_WITHOUT_FORMAT_BIT;
|
||||
features |= VK_FORMAT_FEATURE_2_STORAGE_READ_WITHOUT_FORMAT_BIT;
|
||||
}
|
||||
|
||||
/* Only these formats allow atomics for texel buffers */
|
||||
if (vk_format == VK_FORMAT_R32_UINT || vk_format == VK_FORMAT_R32_SINT)
|
||||
features |= VK_FORMAT_FEATURE_2_STORAGE_TEXEL_BUFFER_ATOMIC_BIT;
|
||||
}
|
||||
|
||||
if (kk_vbo_supports_format(p_format))
|
||||
features |= VK_FORMAT_FEATURE_2_VERTEX_BUFFER_BIT;
|
||||
|
||||
return features;
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkResult VKAPI_CALL
|
||||
kk_CreateBufferView(VkDevice _device, const VkBufferViewCreateInfo *pCreateInfo,
|
||||
const VkAllocationCallbacks *pAllocator,
|
||||
VkBufferView *pBufferView)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_device, dev, _device);
|
||||
struct kk_buffer_view *view =
|
||||
vk_buffer_view_create(&dev->vk, pCreateInfo, pAllocator, sizeof(*view));
|
||||
if (!view)
|
||||
return vk_error(dev, VK_ERROR_OUT_OF_HOST_MEMORY);
|
||||
|
||||
enum pipe_format p_format = vk_format_to_pipe_format(view->vk.format);
|
||||
const struct kk_va_format *supported_format = kk_get_va_format(p_format);
|
||||
|
||||
/* If we reached here, we support reading at least */
|
||||
enum mtl_texture_usage usage = MTL_TEXTURE_USAGE_SHADER_READ;
|
||||
if (supported_format->texel_buffer.write)
|
||||
usage |= MTL_TEXTURE_USAGE_SHADER_WRITE;
|
||||
|
||||
/* Only these formats allow atomics for texel buffers */
|
||||
if (view->vk.format == VK_FORMAT_R32_UINT ||
|
||||
view->vk.format == VK_FORMAT_R32_SINT)
|
||||
usage |= MTL_TEXTURE_USAGE_SHADER_ATOMIC;
|
||||
|
||||
struct kk_image_layout layout = {
|
||||
.width_px = view->vk.elements,
|
||||
.height_px = 1u,
|
||||
.depth_px = 1u,
|
||||
.layers = 1u,
|
||||
.type = MTL_TEXTURE_TYPE_TEXTURE_BUFFER,
|
||||
.sample_count_sa = 1u,
|
||||
.levels = 1u,
|
||||
.linear = true,
|
||||
.optimized_layout = false,
|
||||
.usage = usage,
|
||||
.format = {.pipe = p_format, .mtl = supported_format->mtl_pixel_format},
|
||||
.swizzle =
|
||||
{
|
||||
.red = supported_format->unswizzle.red,
|
||||
.green = supported_format->unswizzle.green,
|
||||
.blue = supported_format->unswizzle.blue,
|
||||
.alpha = supported_format->unswizzle.alpha,
|
||||
},
|
||||
.linear_stride_B = view->vk.range,
|
||||
};
|
||||
struct kk_buffer *buffer =
|
||||
container_of(view->vk.buffer, struct kk_buffer, vk);
|
||||
view->mtl_texel_buffer_handle = mtl_new_texture_with_descriptor_linear(
|
||||
buffer->mtl_handle, &layout, view->vk.offset);
|
||||
if (!view->mtl_texel_buffer_handle) {
|
||||
vk_buffer_view_destroy(&dev->vk, pAllocator, &view->vk);
|
||||
return vk_error(dev, VK_ERROR_OUT_OF_DEVICE_MEMORY);
|
||||
}
|
||||
view->texel_buffer_gpu_id =
|
||||
mtl_texture_get_gpu_resource_id(view->mtl_texel_buffer_handle);
|
||||
|
||||
*pBufferView = kk_buffer_view_to_handle(view);
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_DestroyBufferView(VkDevice _device, VkBufferView bufferView,
|
||||
const VkAllocationCallbacks *pAllocator)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_device, dev, _device);
|
||||
VK_FROM_HANDLE(kk_buffer_view, view, bufferView);
|
||||
|
||||
if (!view)
|
||||
return;
|
||||
|
||||
mtl_release(view->mtl_texel_buffer_handle);
|
||||
vk_buffer_view_destroy(&dev->vk, pAllocator, &view->vk);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright © 2022 Collabora Ltd. and Red Hat Inc.
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef KK_BUFFER_VIEW_H
|
||||
#define KK_BUFFER_VIEW_H 1
|
||||
|
||||
#include "kk_private.h"
|
||||
|
||||
#include "kosmickrisp/bridge/mtl_types.h"
|
||||
|
||||
#include "vk_buffer_view.h"
|
||||
|
||||
struct kk_physical_device;
|
||||
|
||||
VkFormatFeatureFlags2
|
||||
kk_get_buffer_format_features(struct kk_physical_device *pdev, VkFormat format);
|
||||
|
||||
struct kk_buffer_view {
|
||||
struct vk_buffer_view vk;
|
||||
mtl_texture *mtl_texel_buffer_handle;
|
||||
uint64_t texel_buffer_gpu_id;
|
||||
};
|
||||
|
||||
VK_DEFINE_NONDISP_HANDLE_CASTS(kk_buffer_view, vk.base, VkBufferView,
|
||||
VK_OBJECT_TYPE_BUFFER_VIEW)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,579 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
kk_cmd_release_resources(struct kk_device *dev, struct kk_cmd_buffer *cmd)
|
||||
{
|
||||
struct kk_cmd_pool *pool = kk_cmd_buffer_pool(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);
|
||||
|
||||
kk_cmd_pool_free_bo_list(pool, &cmd->uploader.bos);
|
||||
|
||||
/* 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);
|
||||
util_dynarray_fini(&cmd->large_bos);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
cmd->large_bos = UTIL_DYNARRAY_INIT;
|
||||
|
||||
cmd->vk.dynamic_graphics_state.vi = &cmd->state.gfx._dynamic_vi;
|
||||
cmd->vk.dynamic_graphics_state.ms.sample_locations =
|
||||
&cmd->state.gfx._dynamic_sl;
|
||||
|
||||
list_inithead(&cmd->uploader.bos);
|
||||
|
||||
*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);
|
||||
|
||||
cmd->uploader.bo = NULL;
|
||||
cmd->uploader.offset = 0;
|
||||
|
||||
memset(&cmd->state, 0, sizeof(cmd->state));
|
||||
cmd->uses_heap = false;
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
uint8_t dyn_buffer_start =
|
||||
pipeline_layout->dynamic_descriptor_offset[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;
|
||||
|
||||
/* 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->vk.dynamic_descriptor_count > 0) {
|
||||
for (uint32_t j = 0; j < set_layout->vk.dynamic_descriptor_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->vk.dynamic_descriptor_count;
|
||||
}
|
||||
|
||||
dyn_buffer_start += set_layout->vk.dynamic_descriptor_count;
|
||||
} else {
|
||||
assert(set == NULL);
|
||||
}
|
||||
}
|
||||
assert(dyn_buffer_start <= KK_MAX_DYNAMIC_BUFFERS);
|
||||
assert(next_dyn_offset <= info->dynamicOffsetCount);
|
||||
|
||||
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)
|
||||
{
|
||||
assert(set < KK_MAX_SETS);
|
||||
if (unlikely(desc->push[set] == NULL)) {
|
||||
size_t size = sizeof(*desc->push[set]);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pushing descriptors replaces whatever sets are bound */
|
||||
desc->push[set]->layout = set_layout;
|
||||
desc->sets[set] = NULL;
|
||||
desc->push_dirty |= 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_ptr 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;
|
||||
}
|
||||
|
||||
static VkResult
|
||||
kk_cmd_buffer_alloc_bo(struct kk_cmd_buffer *cmd, struct kk_cmd_bo **bo_out)
|
||||
{
|
||||
VkResult result = kk_cmd_pool_alloc_bo(kk_cmd_buffer_pool(cmd), bo_out);
|
||||
if (result != VK_SUCCESS)
|
||||
return result;
|
||||
|
||||
list_addtail(&(*bo_out)->link, &cmd->uploader.bos);
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
struct kk_ptr
|
||||
kk_pool_alloc(struct kk_cmd_buffer *cmd, uint32_t size_B, uint32_t alignment_B)
|
||||
{
|
||||
struct kk_device *dev = kk_cmd_buffer_device(cmd);
|
||||
struct kk_uploader *uploader = &cmd->uploader;
|
||||
|
||||
/* Specially handle large allocations owned by the command buffer, e.g. used
|
||||
* for statically allocated vertex output buffers with geometry shaders.
|
||||
*/
|
||||
if (size_B > KK_CMD_BO_SIZE) {
|
||||
struct kk_bo *buffer = NULL;
|
||||
const VkResult result =
|
||||
kk_alloc_bo(dev, &cmd->vk.base, size_B, alignment_B, &buffer);
|
||||
if (result != VK_SUCCESS) {
|
||||
vk_command_buffer_set_error(&cmd->vk, result);
|
||||
return (struct kk_ptr){0};
|
||||
}
|
||||
util_dynarray_append(&cmd->large_bos, buffer);
|
||||
|
||||
return (struct kk_ptr){
|
||||
.gpu = buffer->gpu,
|
||||
.cpu = buffer->cpu,
|
||||
|
||||
.buffer = buffer->map,
|
||||
.offset = 0u,
|
||||
};
|
||||
}
|
||||
|
||||
assert(size_B <= KK_CMD_BO_SIZE);
|
||||
assert(alignment_B > 0);
|
||||
|
||||
const uint32_t offset = align(uploader->offset, alignment_B);
|
||||
|
||||
assert(offset <= KK_CMD_BO_SIZE);
|
||||
if (uploader->bo != NULL && size_B <= KK_CMD_BO_SIZE - offset) {
|
||||
uploader->offset = offset + size_B;
|
||||
|
||||
return (struct kk_ptr){
|
||||
.gpu = uploader->bo->gpu + offset,
|
||||
.cpu = uploader->bo->cpu + offset,
|
||||
|
||||
.buffer = uploader->bo->map,
|
||||
.offset = offset,
|
||||
};
|
||||
}
|
||||
|
||||
struct kk_cmd_bo *bo;
|
||||
const VkResult result = kk_cmd_buffer_alloc_bo(cmd, &bo);
|
||||
if (unlikely(result != VK_SUCCESS)) {
|
||||
vk_command_buffer_set_error(&cmd->vk, result);
|
||||
return (struct kk_ptr){0};
|
||||
}
|
||||
|
||||
/* Pick whichever of the current upload BO and the new BO will have more
|
||||
* room left to be the BO for the next upload. If our upload size is
|
||||
* bigger than the old offset, we're better off burning the whole new
|
||||
* upload BO on this one allocation and continuing on the current upload
|
||||
* BO.
|
||||
*/
|
||||
if (uploader->bo == NULL || size_B < uploader->offset) {
|
||||
uploader->bo = bo->bo;
|
||||
uploader->offset = size_B;
|
||||
}
|
||||
|
||||
return (struct kk_ptr){
|
||||
.gpu = bo->bo->gpu,
|
||||
.cpu = bo->bo->cpu,
|
||||
|
||||
.buffer = bo->bo->map,
|
||||
.offset = 0u,
|
||||
};
|
||||
}
|
||||
|
||||
struct kk_ptr
|
||||
kk_pool_upload(struct kk_cmd_buffer *cmd, const void *data, uint32_t size,
|
||||
uint32_t alignment)
|
||||
{
|
||||
struct kk_ptr T = kk_pool_alloc(cmd, size, alignment);
|
||||
if (unlikely(T.cpu == NULL))
|
||||
return (struct kk_ptr){0};
|
||||
|
||||
memcpy(T.cpu, data, size);
|
||||
return T;
|
||||
}
|
||||
|
||||
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_ptr root_gpu = kk_pool_upload(cmd, root, sizeof(*root), 8u);
|
||||
if (unlikely(!root_gpu.gpu))
|
||||
return 0u;
|
||||
|
||||
root->root_buffer = root_gpu;
|
||||
desc->root_dirty = false;
|
||||
|
||||
return root_gpu.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_ptr push_gpu = kk_pool_upload(
|
||||
cmd, push_set->data, sizeof(push_set->data), KK_MIN_UBO_ALIGNMENT);
|
||||
if (unlikely(!push_gpu.gpu))
|
||||
return;
|
||||
|
||||
desc->root.sets[set_idx] = push_gpu.gpu;
|
||||
desc->set_sizes[set_idx] = sizeof(push_set->data);
|
||||
}
|
||||
|
||||
desc->root_dirty = true;
|
||||
desc->push_dirty = 0;
|
||||
}
|
||||
|
||||
void
|
||||
kk_dispatch_precomp(struct kk_cmd_buffer *cmd, struct kk_grid grid,
|
||||
bool pre_gfx, enum libkk_program idx, void *data,
|
||||
size_t data_size)
|
||||
{
|
||||
struct kk_device *dev = kk_cmd_buffer_device(cmd);
|
||||
struct kk_precompiled_shader *prog = &dev->precompiled_cache.shaders[idx];
|
||||
|
||||
mtl_compute_encoder *encoder =
|
||||
pre_gfx ? kk_encoder_pre_gfx_encoder(cmd) : kk_compute_encoder(cmd);
|
||||
|
||||
struct kk_ptr data_gpu = kk_pool_upload(cmd, data, data_size, 8u);
|
||||
if (unlikely(!data_gpu.gpu))
|
||||
return;
|
||||
|
||||
mtl_compute_set_buffer(encoder, data_gpu.buffer, data_gpu.offset, 0);
|
||||
mtl_compute_set_pipeline_state(encoder, prog->pipeline);
|
||||
|
||||
struct mtl_size local_size = {
|
||||
.x = prog->info.workgroup_size[0],
|
||||
.y = prog->info.workgroup_size[1],
|
||||
.z = prog->info.workgroup_size[2],
|
||||
};
|
||||
|
||||
if (grid.mode == KK_GRID_DIRECT)
|
||||
mtl_dispatch_threads(encoder, grid.size, local_size);
|
||||
else
|
||||
mtl_dispatch_threadgroups_with_indirect_buffer(encoder, grid.indirect,
|
||||
grid.offset, local_size);
|
||||
}
|
||||
|
||||
void
|
||||
kk_cmd_write(struct kk_cmd_buffer *cmd, struct libkk_imm_write write)
|
||||
{
|
||||
util_dynarray_append(&cmd->encoder->imm_writes, write);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_CmdBeginConditionalRendering2EXT(
|
||||
VkCommandBuffer commandBuffer,
|
||||
const VkConditionalRenderingBeginInfo2EXT *pConditionalRenderingBegin)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_cmd_buffer, cmd, commandBuffer);
|
||||
|
||||
cmd->state.cond_render.address =
|
||||
pConditionalRenderingBegin->addressRange.address;
|
||||
cmd->state.cond_render.inverted = pConditionalRenderingBegin->flags &
|
||||
VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT;
|
||||
cmd->state.cond_render.enabled = true;
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_CmdEndConditionalRenderingEXT(VkCommandBuffer commandBuffer)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_cmd_buffer, cmd, commandBuffer);
|
||||
|
||||
cmd->state.cond_render.enabled = false;
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
* Copyright © 2022 Collabora Ltd. and Red Hat Inc.
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef KK_CMD_BUFFER_H
|
||||
#define KK_CMD_BUFFER_H 1
|
||||
|
||||
#include "kk_private.h"
|
||||
|
||||
#include "kk_descriptor_set.h"
|
||||
#include "kk_image.h"
|
||||
#include "kk_nir_lower_vbo.h"
|
||||
#include "kk_shader.h"
|
||||
|
||||
#include "kosmickrisp/bridge/mtl_types.h"
|
||||
|
||||
#include "kosmickrisp/libkk/kk_query.h"
|
||||
|
||||
#include "util/u_dynarray.h"
|
||||
|
||||
#include "vk_command_buffer.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
struct kk_query_pool;
|
||||
|
||||
struct kk_root_descriptor_table {
|
||||
struct kk_ptr root_buffer;
|
||||
|
||||
union {
|
||||
struct {
|
||||
/* Vertex input state */
|
||||
uint32_t buffer_strides[KK_MAX_VBUFS];
|
||||
uint64_t attrib_base[KK_MAX_ATTRIBS];
|
||||
uint32_t attrib_clamps[KK_MAX_ATTRIBS];
|
||||
|
||||
/* Mask of outputs flowing VS->TCS, VS->GS, or TES->GS . */
|
||||
uint64_t vertex_outputs;
|
||||
|
||||
/* Address of vertex param buffer if geom/tess is used, else 0 */
|
||||
uint64_t vertex_params;
|
||||
|
||||
/* Address of tessellation param buffer if tessellation used, else 0 */
|
||||
uint64_t tess_params;
|
||||
|
||||
float blend_constant[4];
|
||||
float clip_z_coeff;
|
||||
uint32_t index_size;
|
||||
uint64_t base_vertex_addr;
|
||||
uint64_t base_instance_addr;
|
||||
} draw;
|
||||
struct {
|
||||
uint32_t base_group[3];
|
||||
} cs;
|
||||
};
|
||||
|
||||
/* Client push constants */
|
||||
uint8_t push[KK_MAX_PUSH_SIZE];
|
||||
|
||||
/* Descriptor set base addresses */
|
||||
uint64_t sets[KK_MAX_SETS];
|
||||
|
||||
/* Dynamic buffer bindings */
|
||||
struct kk_buffer_address dynamic_buffers[KK_MAX_DYNAMIC_BUFFERS];
|
||||
|
||||
/* Start index in dynamic_buffers where each set starts */
|
||||
uint8_t set_dynamic_buffer_start[KK_MAX_SETS];
|
||||
};
|
||||
|
||||
struct kk_descriptor_state {
|
||||
bool root_dirty;
|
||||
struct kk_root_descriptor_table root;
|
||||
|
||||
uint32_t set_sizes[KK_MAX_SETS];
|
||||
struct kk_descriptor_set *sets[KK_MAX_SETS];
|
||||
|
||||
uint32_t push_dirty;
|
||||
struct kk_push_descriptor_set *push[KK_MAX_SETS];
|
||||
};
|
||||
|
||||
struct kk_per_draw_data {
|
||||
uint32_t draw_id;
|
||||
};
|
||||
|
||||
struct kk_attachment {
|
||||
VkRenderingAttachmentFlagsKHR flags;
|
||||
VkFormat vk_format;
|
||||
struct kk_image_view *iview;
|
||||
|
||||
VkResolveModeFlagBits resolve_mode;
|
||||
struct kk_image_view *resolve_iview;
|
||||
|
||||
/* Needed to track the value of storeOp in case we need to copy images for
|
||||
* the DRM_FORMAT_MOD_LINEAR case */
|
||||
VkAttachmentStoreOp store_op;
|
||||
};
|
||||
|
||||
struct kk_rendering_state {
|
||||
VkRenderingFlagBits flags;
|
||||
|
||||
VkRect2D area;
|
||||
uint32_t layer_count;
|
||||
uint32_t view_mask;
|
||||
uint32_t samples;
|
||||
|
||||
uint32_t color_att_count;
|
||||
struct kk_attachment color_att[KK_MAX_RTS];
|
||||
struct kk_attachment depth_att;
|
||||
struct kk_attachment stencil_att;
|
||||
struct kk_attachment fsr_att;
|
||||
};
|
||||
|
||||
/* Dirty tracking bits for state not tracked by vk_dynamic_graphics_state or
|
||||
* shaders_dirty.
|
||||
*/
|
||||
enum kk_dirty {
|
||||
KK_DIRTY_VB = BITFIELD_BIT(0),
|
||||
KK_DIRTY_OCCLUSION = BITFIELD_BIT(1),
|
||||
};
|
||||
|
||||
struct kk_graphics_state {
|
||||
struct kk_rendering_state render;
|
||||
struct kk_descriptor_state descriptors;
|
||||
|
||||
mtl_depth_stencil_state *depth_stencil_state;
|
||||
mtl_render_pass_descriptor *render_pass_descriptor;
|
||||
bool is_depth_stencil_dynamic;
|
||||
bool is_cull_front_and_back;
|
||||
bool is_ms_bresenham_lines;
|
||||
bool need_to_start_render_pass;
|
||||
|
||||
enum kk_dirty dirty;
|
||||
uint32_t pipeline_sample_count;
|
||||
|
||||
struct {
|
||||
enum mtl_visibility_result_mode mode;
|
||||
|
||||
/* If enabled, index of the current occlusion query in the occlusion heap.
|
||||
* There can only be one active at a time (hardware constraint).
|
||||
*/
|
||||
uint16_t index;
|
||||
} occlusion;
|
||||
|
||||
/* Index buffer */
|
||||
struct {
|
||||
mtl_buffer *handle;
|
||||
uint64_t buffer_size;
|
||||
uint64_t range;
|
||||
uint64_t offset;
|
||||
uint8_t bytes_per_index;
|
||||
} index;
|
||||
|
||||
/* Vertex buffers */
|
||||
struct {
|
||||
struct kk_addr_range addr_range[KK_MAX_VBUFS];
|
||||
mtl_buffer *handles[KK_MAX_VBUFS];
|
||||
} vb;
|
||||
|
||||
/* Tessellation state */
|
||||
struct {
|
||||
/* Grid buffer for when the draw is indirect */
|
||||
struct kk_ptr indirect_ptr;
|
||||
mtl_buffer *out_draws_buffer;
|
||||
uint64_t out_draws_offset;
|
||||
struct kk_tess_info info;
|
||||
enum mesa_prim prim;
|
||||
} tess;
|
||||
|
||||
/* Needed by vk_command_buffer::dynamic_graphics_state */
|
||||
struct vk_vertex_input_state _dynamic_vi;
|
||||
struct vk_sample_locations_state _dynamic_sl;
|
||||
};
|
||||
|
||||
struct kk_compute_state {
|
||||
struct kk_descriptor_state descriptors;
|
||||
};
|
||||
|
||||
struct kk_conditional_rendering_state {
|
||||
uint64_t address;
|
||||
bool inverted;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
struct kk_encoder;
|
||||
|
||||
struct kk_uploader {
|
||||
/** List of kk_cmd_bo */
|
||||
struct list_head bos;
|
||||
|
||||
/* Current addresses */
|
||||
struct kk_bo *bo;
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
struct kk_cmd_buffer {
|
||||
struct vk_command_buffer vk;
|
||||
|
||||
struct kk_encoder *encoder;
|
||||
void *drawable;
|
||||
|
||||
struct {
|
||||
struct kk_graphics_state gfx;
|
||||
struct kk_compute_state cs;
|
||||
struct kk_conditional_rendering_state cond_render;
|
||||
struct kk_shader *shaders[MESA_SHADER_STAGES];
|
||||
/* Only tracks graphics shaders since compute is always bound for now. */
|
||||
uint32_t dirty_shaders;
|
||||
} state;
|
||||
|
||||
struct kk_uploader uploader;
|
||||
|
||||
/* Owned large BOs */
|
||||
struct util_dynarray large_bos;
|
||||
|
||||
/* Does the command buffer use the geometry heap? */
|
||||
bool uses_heap;
|
||||
};
|
||||
|
||||
VK_DEFINE_HANDLE_CASTS(kk_cmd_buffer, vk.base, VkCommandBuffer,
|
||||
VK_OBJECT_TYPE_COMMAND_BUFFER)
|
||||
|
||||
extern const struct vk_command_buffer_ops kk_cmd_buffer_ops;
|
||||
|
||||
static inline struct kk_device *
|
||||
kk_cmd_buffer_device(struct kk_cmd_buffer *cmd)
|
||||
{
|
||||
return (struct kk_device *)cmd->vk.base.device;
|
||||
}
|
||||
|
||||
static inline struct kk_cmd_pool *
|
||||
kk_cmd_buffer_pool(struct kk_cmd_buffer *cmd)
|
||||
{
|
||||
return (struct kk_cmd_pool *)cmd->vk.pool;
|
||||
}
|
||||
|
||||
static inline struct kk_descriptor_state *
|
||||
kk_get_descriptors_state(struct kk_cmd_buffer *cmd,
|
||||
VkPipelineBindPoint bind_point)
|
||||
{
|
||||
switch (bind_point) {
|
||||
case VK_PIPELINE_BIND_POINT_GRAPHICS:
|
||||
return &cmd->state.gfx.descriptors;
|
||||
case VK_PIPELINE_BIND_POINT_COMPUTE:
|
||||
return &cmd->state.cs.descriptors;
|
||||
default:
|
||||
UNREACHABLE("Unhandled bind point");
|
||||
}
|
||||
};
|
||||
|
||||
void kk_cmd_release_resources(struct kk_device *dev, struct kk_cmd_buffer *cmd);
|
||||
|
||||
static void
|
||||
kk_cmd_buffer_dirty_all_gfx(struct kk_cmd_buffer *cmd)
|
||||
{
|
||||
/* Ensure we flush all graphics state */
|
||||
vk_dynamic_graphics_state_dirty_all(&cmd->vk.dynamic_graphics_state);
|
||||
cmd->state.dirty_shaders = ~0u;
|
||||
cmd->state.gfx.dirty = ~0u;
|
||||
cmd->state.gfx.descriptors.root_dirty = true;
|
||||
|
||||
/* We just flushed out the heap use. If we want to use it again, we'll need
|
||||
* to queue a free for it again.
|
||||
*/
|
||||
cmd->uses_heap = false;
|
||||
}
|
||||
|
||||
void kk_cmd_release_dynamic_ds_state(struct kk_cmd_buffer *cmd);
|
||||
|
||||
mtl_depth_stencil_state *
|
||||
kk_compile_depth_stencil_state(struct kk_device *device,
|
||||
const struct vk_depth_stencil_state *ds,
|
||||
bool has_depth, bool has_stencil);
|
||||
|
||||
void kk_meta_resolve_rendering(struct kk_cmd_buffer *cmd,
|
||||
const VkRenderingInfo *pRenderingInfo);
|
||||
|
||||
void kk_cmd_buffer_write_descriptor_buffer(struct kk_cmd_buffer *cmd,
|
||||
struct kk_descriptor_state *desc,
|
||||
size_t size, size_t offset);
|
||||
|
||||
struct kk_ptr kk_pool_alloc(struct kk_cmd_buffer *cmd, uint32_t size,
|
||||
uint32_t alignment);
|
||||
|
||||
struct kk_ptr kk_pool_upload(struct kk_cmd_buffer *cmd, const void *data,
|
||||
uint32_t size, uint32_t alignment);
|
||||
|
||||
uint64_t kk_upload_descriptor_root(struct kk_cmd_buffer *cmd,
|
||||
VkPipelineBindPoint bind_point);
|
||||
|
||||
void kk_cmd_buffer_flush_push_descriptors(struct kk_cmd_buffer *cmd,
|
||||
struct kk_descriptor_state *desc);
|
||||
|
||||
enum kk_grid_mode {
|
||||
KK_GRID_DIRECT = 0u,
|
||||
KK_GRID_INDIRECT,
|
||||
};
|
||||
struct kk_grid {
|
||||
enum kk_grid_mode mode;
|
||||
union {
|
||||
struct {
|
||||
uint32_t offset;
|
||||
mtl_buffer *indirect;
|
||||
};
|
||||
struct mtl_size size;
|
||||
};
|
||||
};
|
||||
|
||||
static struct kk_grid
|
||||
kk_grid_3d(uint32_t x, uint32_t y, uint32_t z)
|
||||
{
|
||||
return (struct kk_grid){
|
||||
.mode = KK_GRID_DIRECT,
|
||||
.size = {x, y, z},
|
||||
};
|
||||
}
|
||||
|
||||
static struct kk_grid
|
||||
kk_grid_2d(uint32_t x, uint32_t y)
|
||||
{
|
||||
return kk_grid_3d(x, y, 1u);
|
||||
}
|
||||
|
||||
static struct kk_grid
|
||||
kk_grid_1d(uint32_t x)
|
||||
{
|
||||
return kk_grid_3d(x, 1u, 1u);
|
||||
}
|
||||
|
||||
static struct kk_grid
|
||||
kk_grid_indirect(mtl_buffer *indirect, uint32_t offset)
|
||||
{
|
||||
return (struct kk_grid){
|
||||
.mode = KK_GRID_INDIRECT,
|
||||
.indirect = indirect,
|
||||
.offset = offset,
|
||||
};
|
||||
}
|
||||
|
||||
static bool
|
||||
kk_grid_is_indirect(struct kk_grid grid)
|
||||
{
|
||||
return grid.mode == KK_GRID_INDIRECT;
|
||||
}
|
||||
|
||||
void kk_dispatch_precomp(struct kk_cmd_buffer *cmd, struct kk_grid grid,
|
||||
bool pre_gfx, enum libkk_program idx, void *data,
|
||||
size_t data_size);
|
||||
|
||||
#define MESA_DISPATCH_PRECOMP kk_dispatch_precomp
|
||||
|
||||
void kk_cmd_write(struct kk_cmd_buffer *cmd, struct libkk_imm_write write);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright 2024 Valve Corporation
|
||||
* Copyright 2024 Alyssa Rosenzweig
|
||||
* Copyright 2022-2023 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_device.h"
|
||||
#include "kk_entrypoints.h"
|
||||
#include "kk_image.h"
|
||||
#include "kk_image_view.h"
|
||||
#include "kk_physical_device.h"
|
||||
|
||||
#include "vk_format.h"
|
||||
#include "vk_meta.h"
|
||||
|
||||
static VkImageViewType
|
||||
render_view_type(VkImageType image_type, unsigned layer_count)
|
||||
{
|
||||
switch (image_type) {
|
||||
case VK_IMAGE_TYPE_1D:
|
||||
return layer_count == 1 ? VK_IMAGE_VIEW_TYPE_1D
|
||||
: VK_IMAGE_VIEW_TYPE_1D_ARRAY;
|
||||
case VK_IMAGE_TYPE_2D:
|
||||
return layer_count == 1 ? VK_IMAGE_VIEW_TYPE_2D
|
||||
: VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
||||
case VK_IMAGE_TYPE_3D:
|
||||
return VK_IMAGE_VIEW_TYPE_3D;
|
||||
default:
|
||||
UNREACHABLE("Invalid image type");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
clear_image(struct kk_cmd_buffer *cmd, struct kk_image *image,
|
||||
VkImageLayout image_layout, VkFormat format,
|
||||
const VkClearValue *clear_value, uint32_t range_count,
|
||||
const VkImageSubresourceRange *ranges)
|
||||
{
|
||||
struct kk_device *dev = kk_cmd_buffer_device(cmd);
|
||||
ASSERTED VkResult result;
|
||||
|
||||
for (uint32_t r = 0; r < range_count; r++) {
|
||||
const uint32_t level_count =
|
||||
vk_image_subresource_level_count(&image->vk, &ranges[r]);
|
||||
|
||||
for (uint32_t l = 0; l < level_count; l++) {
|
||||
const uint32_t level = ranges[r].baseMipLevel + l;
|
||||
|
||||
const VkExtent3D level_extent =
|
||||
vk_image_mip_level_extent(&image->vk, level);
|
||||
|
||||
uint32_t base_array_layer, layer_count;
|
||||
if (image->vk.image_type == VK_IMAGE_TYPE_3D) {
|
||||
base_array_layer = 0;
|
||||
layer_count = level_extent.depth;
|
||||
} else {
|
||||
base_array_layer = ranges[r].baseArrayLayer;
|
||||
layer_count =
|
||||
vk_image_subresource_layer_count(&image->vk, &ranges[r]);
|
||||
}
|
||||
|
||||
const VkImageViewUsageCreateInfo view_usage_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO,
|
||||
.usage = (ranges[r].aspectMask & VK_IMAGE_ASPECT_COLOR_BIT)
|
||||
? VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
|
||||
: VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
|
||||
};
|
||||
const VkImageViewCreateInfo view_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.flags = VK_IMAGE_VIEW_CREATE_DRIVER_INTERNAL_BIT_MESA,
|
||||
.pNext = &view_usage_info,
|
||||
.image = kk_image_to_handle(image),
|
||||
.viewType = render_view_type(image->vk.image_type, layer_count),
|
||||
.format = format,
|
||||
.subresourceRange =
|
||||
{
|
||||
.aspectMask = image->vk.aspects,
|
||||
.baseMipLevel = level,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = base_array_layer,
|
||||
.layerCount = layer_count,
|
||||
},
|
||||
};
|
||||
|
||||
/* We use vk_meta_create_image_view here for lifetime managemnt */
|
||||
VkImageView view;
|
||||
result =
|
||||
vk_meta_create_image_view(&cmd->vk, &dev->meta, &view_info, &view);
|
||||
assert(result == VK_SUCCESS);
|
||||
|
||||
VkRenderingInfo render = {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
|
||||
.renderArea =
|
||||
{
|
||||
.offset = {0, 0},
|
||||
.extent = {level_extent.width, level_extent.height},
|
||||
},
|
||||
.layerCount = layer_count,
|
||||
};
|
||||
|
||||
VkRenderingAttachmentInfo vk_att = {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
|
||||
.imageView = view,
|
||||
.imageLayout = image_layout,
|
||||
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
.clearValue = *clear_value,
|
||||
};
|
||||
|
||||
if (ranges[r].aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) {
|
||||
render.colorAttachmentCount = 1;
|
||||
render.pColorAttachments = &vk_att;
|
||||
}
|
||||
if (ranges[r].aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT)
|
||||
render.pDepthAttachment = &vk_att;
|
||||
if (ranges[r].aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT)
|
||||
render.pStencilAttachment = &vk_att;
|
||||
|
||||
kk_CmdBeginRendering(kk_cmd_buffer_to_handle(cmd), &render);
|
||||
|
||||
static const VkRenderingEndInfoKHR end_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDERING_END_INFO_KHR,
|
||||
.pNext = NULL,
|
||||
};
|
||||
kk_CmdEndRendering2KHR(kk_cmd_buffer_to_handle(cmd), &end_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_CmdClearColorImage(VkCommandBuffer commandBuffer, VkImage _image,
|
||||
VkImageLayout imageLayout,
|
||||
const VkClearColorValue *pColor, uint32_t rangeCount,
|
||||
const VkImageSubresourceRange *pRanges)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_cmd_buffer, cmd, commandBuffer);
|
||||
VK_FROM_HANDLE(kk_image, image, _image);
|
||||
|
||||
VkClearValue clear_value = {
|
||||
.color = *pColor,
|
||||
};
|
||||
|
||||
VkFormat vk_format = image->vk.format;
|
||||
if (vk_format == VK_FORMAT_R64_UINT || vk_format == VK_FORMAT_R64_SINT)
|
||||
vk_format = VK_FORMAT_R32G32_UINT;
|
||||
|
||||
enum pipe_format p_format = vk_format_to_pipe_format(vk_format);
|
||||
assert(p_format != PIPE_FORMAT_NONE);
|
||||
|
||||
clear_image(cmd, image, imageLayout, vk_format, &clear_value, rangeCount,
|
||||
pRanges);
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_CmdClearDepthStencilImage(VkCommandBuffer commandBuffer, VkImage _image,
|
||||
VkImageLayout imageLayout,
|
||||
const VkClearDepthStencilValue *pDepthStencil,
|
||||
uint32_t rangeCount,
|
||||
const VkImageSubresourceRange *pRanges)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_cmd_buffer, cmd, commandBuffer);
|
||||
VK_FROM_HANDLE(kk_image, image, _image);
|
||||
|
||||
const VkClearValue clear_value = {
|
||||
.depthStencil = *pDepthStencil,
|
||||
};
|
||||
|
||||
clear_image(cmd, image, imageLayout, image->vk.format, &clear_value,
|
||||
rangeCount, pRanges);
|
||||
}
|
||||
@@ -0,0 +1,398 @@
|
||||
/*
|
||||
* 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_bo.h"
|
||||
#include "kk_buffer.h"
|
||||
#include "kk_device.h"
|
||||
#include "kk_encoder.h"
|
||||
#include "kk_entrypoints.h"
|
||||
#include "kk_physical_device.h"
|
||||
|
||||
#include "kosmickrisp/bridge/mtl_bridge.h"
|
||||
#include "kosmickrisp/bridge/vk_to_mtl_map.h"
|
||||
|
||||
#include "util/format/u_format.h"
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_CmdCopyBuffer2(VkCommandBuffer commandBuffer,
|
||||
const VkCopyBufferInfo2 *pCopyBufferInfo)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_cmd_buffer, cmd, commandBuffer);
|
||||
VK_FROM_HANDLE(kk_buffer, src, pCopyBufferInfo->srcBuffer);
|
||||
VK_FROM_HANDLE(kk_buffer, dst, pCopyBufferInfo->dstBuffer);
|
||||
|
||||
mtl_blit_encoder *blit = kk_blit_encoder(cmd);
|
||||
for (uint32_t i = 0; i < pCopyBufferInfo->regionCount; i++) {
|
||||
const VkBufferCopy2 *region = &pCopyBufferInfo->pRegions[i];
|
||||
mtl_copy_from_buffer_to_buffer(blit, src->mtl_handle, region->srcOffset,
|
||||
dst->mtl_handle, region->dstOffset,
|
||||
region->size);
|
||||
}
|
||||
}
|
||||
|
||||
struct kk_buffer_image_copy_info {
|
||||
struct mtl_buffer_image_copy mtl_data;
|
||||
size_t buffer_slice_size_B;
|
||||
};
|
||||
|
||||
static struct kk_buffer_image_copy_info
|
||||
vk_buffer_image_copy_to_mtl_buffer_image_copy(
|
||||
const VkBufferImageCopy2 *region, const struct kk_image_plane *plane)
|
||||
{
|
||||
struct kk_buffer_image_copy_info copy;
|
||||
enum pipe_format p_format = plane->layout.format.pipe;
|
||||
if (region->imageSubresource.aspectMask == VK_IMAGE_ASPECT_DEPTH_BIT) {
|
||||
copy.mtl_data.options = MTL_BLIT_OPTION_DEPTH_FROM_DEPTH_STENCIL;
|
||||
p_format = util_format_get_depth_only(p_format);
|
||||
} else if (region->imageSubresource.aspectMask ==
|
||||
VK_IMAGE_ASPECT_STENCIL_BIT) {
|
||||
copy.mtl_data.options = MTL_BLIT_OPTION_STENCIL_FROM_DEPTH_STENCIL;
|
||||
p_format = PIPE_FORMAT_S8_UINT;
|
||||
} else
|
||||
copy.mtl_data.options = MTL_BLIT_OPTION_NONE;
|
||||
|
||||
const uint32_t buffer_width = region->bufferRowLength
|
||||
? region->bufferRowLength
|
||||
: region->imageExtent.width;
|
||||
const uint32_t buffer_height = region->bufferImageHeight
|
||||
? region->bufferImageHeight
|
||||
: region->imageExtent.height;
|
||||
|
||||
const uint32_t buffer_stride_B =
|
||||
util_format_get_stride(p_format, buffer_width);
|
||||
const uint32_t buffer_size_2d_B =
|
||||
util_format_get_2d_size(p_format, buffer_stride_B, buffer_height);
|
||||
|
||||
/* Metal requires this value to be 0 for 2D images, otherwise the number of
|
||||
* bytes between each 2D image of a 3D texture */
|
||||
copy.mtl_data.buffer_2d_image_size_B =
|
||||
plane->layout.depth_px == 1u ? 0u : buffer_size_2d_B;
|
||||
copy.mtl_data.buffer_stride_B = buffer_stride_B;
|
||||
copy.mtl_data.image_size = vk_extent_3d_to_mtl_size(®ion->imageExtent);
|
||||
copy.mtl_data.image_origin =
|
||||
vk_offset_3d_to_mtl_origin(®ion->imageOffset);
|
||||
copy.mtl_data.image_level = region->imageSubresource.mipLevel;
|
||||
copy.buffer_slice_size_B = buffer_size_2d_B;
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_CmdCopyBufferToImage2(VkCommandBuffer commandBuffer,
|
||||
const VkCopyBufferToImageInfo2 *pCopyBufferToImageInfo)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_cmd_buffer, cmd, commandBuffer);
|
||||
VK_FROM_HANDLE(kk_buffer, buffer, pCopyBufferToImageInfo->srcBuffer);
|
||||
VK_FROM_HANDLE(kk_image, image, pCopyBufferToImageInfo->dstImage);
|
||||
|
||||
mtl_blit_encoder *blit = kk_blit_encoder(cmd);
|
||||
for (int r = 0; r < pCopyBufferToImageInfo->regionCount; r++) {
|
||||
const VkBufferImageCopy2 *region = &pCopyBufferToImageInfo->pRegions[r];
|
||||
const uint8_t plane_index = kk_image_memory_aspects_to_plane(
|
||||
image, region->imageSubresource.aspectMask);
|
||||
struct kk_image_plane *plane = &image->planes[plane_index];
|
||||
struct kk_buffer_image_copy_info info =
|
||||
vk_buffer_image_copy_to_mtl_buffer_image_copy(region, plane);
|
||||
info.mtl_data.buffer = buffer->mtl_handle;
|
||||
info.mtl_data.image = plane->mtl_handle;
|
||||
size_t buffer_offset = region->bufferOffset;
|
||||
|
||||
kk_foreach_slice(slice, image, imageSubresource)
|
||||
{
|
||||
info.mtl_data.image_slice = slice;
|
||||
info.mtl_data.buffer_offset_B = buffer_offset;
|
||||
mtl_copy_from_buffer_to_texture(blit, &info.mtl_data);
|
||||
buffer_offset += info.buffer_slice_size_B;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_CmdCopyImageToBuffer2(VkCommandBuffer commandBuffer,
|
||||
const VkCopyImageToBufferInfo2 *pCopyImageToBufferInfo)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_cmd_buffer, cmd, commandBuffer);
|
||||
VK_FROM_HANDLE(kk_image, image, pCopyImageToBufferInfo->srcImage);
|
||||
VK_FROM_HANDLE(kk_buffer, buffer, pCopyImageToBufferInfo->dstBuffer);
|
||||
|
||||
mtl_blit_encoder *blit = kk_blit_encoder(cmd);
|
||||
for (unsigned r = 0; r < pCopyImageToBufferInfo->regionCount; r++) {
|
||||
const VkBufferImageCopy2 *region = &pCopyImageToBufferInfo->pRegions[r];
|
||||
const uint8_t plane_index = kk_image_memory_aspects_to_plane(
|
||||
image, region->imageSubresource.aspectMask);
|
||||
struct kk_image_plane *plane = &image->planes[plane_index];
|
||||
struct kk_buffer_image_copy_info info =
|
||||
vk_buffer_image_copy_to_mtl_buffer_image_copy(region, plane);
|
||||
info.mtl_data.buffer = buffer->mtl_handle;
|
||||
info.mtl_data.image = plane->mtl_handle;
|
||||
size_t buffer_offset = region->bufferOffset;
|
||||
|
||||
kk_foreach_slice(slice, image, imageSubresource)
|
||||
{
|
||||
info.mtl_data.image_slice = slice;
|
||||
info.mtl_data.buffer_offset_B = buffer_offset;
|
||||
mtl_copy_from_texture_to_buffer(blit, &info.mtl_data);
|
||||
buffer_offset += info.buffer_slice_size_B;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Copies images by doing a texture->buffer->texture transfer. Returns the new
|
||||
* buffer offset for subsequent copies. */
|
||||
static size_t
|
||||
copy_through_buffer(struct kk_cmd_buffer *cmd, struct kk_image *src,
|
||||
uint32_t src_index, struct kk_image *dst,
|
||||
uint32_t dst_index, mtl_buffer *buffer,
|
||||
size_t buffer_offset, const VkImageCopy2 *region)
|
||||
{
|
||||
struct kk_image_plane *src_plane = &src->planes[src_index];
|
||||
struct kk_image_plane *dst_plane = &dst->planes[dst_index];
|
||||
|
||||
/* Handle depth/stencil for copies to/from compatible color formats */
|
||||
enum pipe_format src_format = src_plane->layout.format.pipe;
|
||||
enum mtl_blit_options src_options = MTL_BLIT_OPTION_NONE;
|
||||
if (region->srcSubresource.aspectMask == VK_IMAGE_ASPECT_DEPTH_BIT) {
|
||||
src_format = util_format_get_depth_only(src_format);
|
||||
src_options = MTL_BLIT_OPTION_DEPTH_FROM_DEPTH_STENCIL;
|
||||
} else if (region->srcSubresource.aspectMask ==
|
||||
VK_IMAGE_ASPECT_STENCIL_BIT) {
|
||||
src_format = PIPE_FORMAT_S8_UINT;
|
||||
src_options = MTL_BLIT_OPTION_STENCIL_FROM_DEPTH_STENCIL;
|
||||
}
|
||||
|
||||
enum pipe_format dst_format = dst_plane->layout.format.pipe;
|
||||
enum mtl_blit_options dst_options = MTL_BLIT_OPTION_NONE;
|
||||
if (region->dstSubresource.aspectMask == VK_IMAGE_ASPECT_DEPTH_BIT) {
|
||||
dst_format = util_format_get_depth_only(dst_format);
|
||||
dst_options = MTL_BLIT_OPTION_DEPTH_FROM_DEPTH_STENCIL;
|
||||
} else if (region->dstSubresource.aspectMask ==
|
||||
VK_IMAGE_ASPECT_STENCIL_BIT) {
|
||||
dst_format = PIPE_FORMAT_S8_UINT;
|
||||
dst_options = MTL_BLIT_OPTION_STENCIL_FROM_DEPTH_STENCIL;
|
||||
}
|
||||
|
||||
bool is_src_compressed = util_format_is_compressed(src_format);
|
||||
bool is_dst_compressed = util_format_is_compressed(dst_format);
|
||||
|
||||
mtl_blit_encoder *blit = kk_blit_encoder(cmd);
|
||||
|
||||
const uint32_t buffer_stride_B =
|
||||
util_format_get_stride(src_format, region->extent.width);
|
||||
const uint32_t buffer_size_2d_B = util_format_get_2d_size(
|
||||
src_format, buffer_stride_B, region->extent.height);
|
||||
|
||||
struct kk_buffer_image_copy_info info;
|
||||
|
||||
/* Metal requires this value to be 0 for 2D images, otherwise the number
|
||||
* of bytes between each 2D image of a 3D texture */
|
||||
info.mtl_data.buffer_2d_image_size_B =
|
||||
src_plane->layout.depth_px == 1u ? 0u : buffer_size_2d_B;
|
||||
info.mtl_data.buffer_stride_B = buffer_stride_B;
|
||||
info.mtl_data.buffer = buffer;
|
||||
info.buffer_slice_size_B = buffer_size_2d_B;
|
||||
|
||||
struct mtl_size src_size = vk_extent_3d_to_mtl_size(®ion->extent);
|
||||
struct mtl_size dst_size = vk_extent_3d_to_mtl_size(®ion->extent);
|
||||
/* Need to adjust size to block dimensions */
|
||||
if (is_src_compressed) {
|
||||
dst_size.x = util_format_get_nblocksx(src_format, dst_size.x);
|
||||
dst_size.y = util_format_get_nblocksy(src_format, dst_size.y);
|
||||
dst_size.z = util_format_get_nblocksz(src_format, dst_size.z);
|
||||
}
|
||||
if (is_dst_compressed) {
|
||||
dst_size.x *= util_format_get_blockwidth(dst_format);
|
||||
dst_size.y *= util_format_get_blockheight(dst_format);
|
||||
dst_size.z *= util_format_get_blockdepth(dst_format);
|
||||
}
|
||||
|
||||
/* After adjusting for compression, sanitize destination extents for 1D/2D */
|
||||
if (dst_plane->layout.height_px == 1)
|
||||
dst_size.y = 1;
|
||||
if (dst_plane->layout.depth_px == 1)
|
||||
dst_size.z = 1;
|
||||
|
||||
struct mtl_origin src_origin =
|
||||
vk_offset_3d_to_mtl_origin(®ion->srcOffset);
|
||||
struct mtl_origin dst_origin =
|
||||
vk_offset_3d_to_mtl_origin(®ion->dstOffset);
|
||||
|
||||
/* Texture->Buffer->Texture */
|
||||
// TODO_KOSMICKRISP We don't handle 3D to 2D array nor vice-versa in this
|
||||
// path. Unsure if it's even needed, can compressed textures be 3D?
|
||||
for (uint32_t slice_idx = 0;
|
||||
slice_idx <
|
||||
vk_image_subresource_layer_count(&src->vk, ®ion->srcSubresource);
|
||||
++slice_idx) {
|
||||
info.mtl_data.image = src_plane->mtl_handle;
|
||||
info.mtl_data.image_size = src_size;
|
||||
info.mtl_data.image_origin = src_origin;
|
||||
info.mtl_data.image_slice =
|
||||
region->srcSubresource.baseArrayLayer + slice_idx;
|
||||
info.mtl_data.image_level = region->srcSubresource.mipLevel;
|
||||
info.mtl_data.buffer_offset_B = buffer_offset;
|
||||
info.mtl_data.options = src_options;
|
||||
mtl_copy_from_texture_to_buffer(blit, &info.mtl_data);
|
||||
|
||||
info.mtl_data.image = dst_plane->mtl_handle;
|
||||
info.mtl_data.image_size = dst_size;
|
||||
info.mtl_data.image_origin = dst_origin;
|
||||
info.mtl_data.image_slice =
|
||||
region->dstSubresource.baseArrayLayer + slice_idx;
|
||||
info.mtl_data.image_level = region->dstSubresource.mipLevel;
|
||||
info.mtl_data.options = dst_options;
|
||||
mtl_copy_from_buffer_to_texture(blit, &info.mtl_data);
|
||||
|
||||
buffer_offset += info.buffer_slice_size_B;
|
||||
}
|
||||
|
||||
return buffer_offset;
|
||||
}
|
||||
|
||||
static bool
|
||||
can_do_image_to_image_copy(struct kk_image *src, uint32_t src_index,
|
||||
struct kk_image *dst, uint32_t dst_index)
|
||||
{
|
||||
struct kk_image_plane *src_plane = &src->planes[src_index];
|
||||
struct kk_image_plane *dst_plane = &dst->planes[dst_index];
|
||||
enum pipe_format src_format = src_plane->layout.format.pipe;
|
||||
enum pipe_format dst_format = dst_plane->layout.format.pipe;
|
||||
|
||||
/* Metal validation fails for image-to-image copies if the dimension is
|
||||
* relevant to the image type and not a multiple of the block dimension.
|
||||
* Since 1D textures are emulated as 2D, this causes problems with
|
||||
* compressed 1D textures.
|
||||
*
|
||||
* However, Metal documentation also states:
|
||||
*
|
||||
* If the block extends outside the bounds of the texture, clamp
|
||||
* sourceSize to the edge of the texture.
|
||||
*
|
||||
* So this may be a bug in the validation layer. Routing to the buffer copy
|
||||
* path avoids it.
|
||||
*/
|
||||
if (src->vk.image_type == VK_IMAGE_TYPE_1D &&
|
||||
util_format_is_compressed(src_format))
|
||||
return false;
|
||||
|
||||
return src_format == dst_format && src_plane->layout.sample_count_sa ==
|
||||
dst_plane->layout.sample_count_sa;
|
||||
}
|
||||
|
||||
/* Copies images through Metal's texture->texture copy mechanism */
|
||||
static void
|
||||
copy_image(struct kk_cmd_buffer *cmd, struct kk_image *src, uint32_t src_index,
|
||||
struct kk_image *dst, uint32_t dst_index, const VkImageCopy2 *region)
|
||||
{
|
||||
mtl_blit_encoder *blit = kk_blit_encoder(cmd);
|
||||
struct kk_image_plane *src_plane = &src->planes[src_index];
|
||||
struct kk_image_plane *dst_plane = &dst->planes[dst_index];
|
||||
|
||||
/* From the Vulkan 1.3.217 spec:
|
||||
*
|
||||
* "When copying between compressed and uncompressed formats the
|
||||
* extent members represent the texel dimensions of the source image
|
||||
* and not the destination."
|
||||
*/
|
||||
const VkExtent3D extent_px =
|
||||
vk_image_sanitize_extent(&src->vk, region->extent);
|
||||
|
||||
size_t src_slice = region->srcSubresource.baseArrayLayer;
|
||||
size_t src_level = region->srcSubresource.mipLevel;
|
||||
struct mtl_origin src_origin =
|
||||
vk_offset_3d_to_mtl_origin(®ion->srcOffset);
|
||||
struct mtl_size size = {.x = extent_px.width,
|
||||
.y = extent_px.height,
|
||||
.z = extent_px.depth};
|
||||
size_t dst_slice = region->dstSubresource.baseArrayLayer;
|
||||
size_t dst_level = region->dstSubresource.mipLevel;
|
||||
struct mtl_origin dst_origin =
|
||||
vk_offset_3d_to_mtl_origin(®ion->dstOffset);
|
||||
|
||||
/* When copying 3D to 2D layered or vice-versa, we need to change the 3D
|
||||
* size to 2D and iterate on the layer count of the 2D image (which is the
|
||||
* same as the depth of the 3D) and adjust origin and slice accordingly */
|
||||
uint32_t layer_count =
|
||||
vk_image_subresource_layer_count(&src->vk, ®ion->srcSubresource);
|
||||
const uint32_t dst_layer_count =
|
||||
vk_image_subresource_layer_count(&dst->vk, ®ion->dstSubresource);
|
||||
size_t *src_increase = &src_slice;
|
||||
size_t *dst_increase = &dst_slice;
|
||||
|
||||
if (layer_count < dst_layer_count) { /* 3D to 2D layered */
|
||||
layer_count = dst_layer_count;
|
||||
src_increase = &src_origin.z;
|
||||
size.z = 1u;
|
||||
} else if (dst_layer_count < layer_count) { /* 2D layered to 3D */
|
||||
dst_increase = &dst_origin.z;
|
||||
size.z = 1u;
|
||||
}
|
||||
for (uint32_t l = 0; l < layer_count;
|
||||
++l, ++(*src_increase), ++(*dst_increase)) {
|
||||
mtl_copy_from_texture_to_texture(
|
||||
blit, src_plane->mtl_handle, src_slice, src_level, src_origin, size,
|
||||
dst_plane->mtl_handle, dst_slice, dst_level, dst_origin);
|
||||
}
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_CmdCopyImage2(VkCommandBuffer commandBuffer,
|
||||
const VkCopyImageInfo2 *pCopyImageInfo)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_cmd_buffer, cmd, commandBuffer);
|
||||
VK_FROM_HANDLE(kk_image, src, pCopyImageInfo->srcImage);
|
||||
VK_FROM_HANDLE(kk_image, dst, pCopyImageInfo->dstImage);
|
||||
|
||||
size_t buffer_size = 0u;
|
||||
|
||||
/* Copy as much as we can through Metal's image to image copy that only
|
||||
* supports same format and sample count while getting the required buffer
|
||||
* size for image->buffer->image copy. */
|
||||
for (uint32_t i = 0u; i < pCopyImageInfo->regionCount; ++i) {
|
||||
const VkImageCopy2 *region = &pCopyImageInfo->pRegions[i];
|
||||
uint8_t src_index =
|
||||
kk_image_aspects_to_plane(src, region->srcSubresource.aspectMask);
|
||||
uint8_t dst_index =
|
||||
kk_image_aspects_to_plane(dst, region->dstSubresource.aspectMask);
|
||||
|
||||
if (can_do_image_to_image_copy(src, src_index, dst, dst_index))
|
||||
copy_image(cmd, src, src_index, dst, dst_index, region);
|
||||
else {
|
||||
struct kk_image_plane *src_plane = &src->planes[src_index];
|
||||
enum pipe_format src_format = src_plane->layout.format.pipe;
|
||||
const uint32_t buffer_stride_B =
|
||||
util_format_get_stride(src_format, region->extent.width);
|
||||
const uint32_t buffer_size_2d_B = util_format_get_2d_size(
|
||||
src_format, buffer_stride_B, region->extent.height);
|
||||
const uint32_t layer_count =
|
||||
vk_image_subresource_layer_count(&src->vk, ®ion->srcSubresource);
|
||||
buffer_size += buffer_size_2d_B * layer_count;
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy source image to buffer then to the destination image for those
|
||||
* regions that image to image was not possible. */
|
||||
if (buffer_size) {
|
||||
struct kk_ptr buf = kk_pool_alloc(cmd, buffer_size, 8);
|
||||
if (unlikely(!buf.gpu))
|
||||
return;
|
||||
|
||||
size_t buffer_offset = buf.offset;
|
||||
for (uint32_t i = 0u; i < pCopyImageInfo->regionCount; ++i) {
|
||||
const VkImageCopy2 *region = &pCopyImageInfo->pRegions[i];
|
||||
uint8_t src_index =
|
||||
kk_image_aspects_to_plane(src, region->srcSubresource.aspectMask);
|
||||
uint8_t dst_index =
|
||||
kk_image_aspects_to_plane(dst, region->dstSubresource.aspectMask);
|
||||
if (!can_do_image_to_image_copy(src, src_index, dst, dst_index))
|
||||
buffer_offset =
|
||||
copy_through_buffer(cmd, src, src_index, dst, dst_index,
|
||||
buf.buffer, buffer_offset, region);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright © 2025 LunarG, Inc
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "vulkan/vulkan_core.h"
|
||||
|
||||
#include "kk_buffer.h"
|
||||
#include "kk_cmd_buffer.h"
|
||||
#include "kk_descriptor_set_layout.h"
|
||||
#include "kk_device.h"
|
||||
#include "kk_encoder.h"
|
||||
#include "kk_entrypoints.h"
|
||||
#include "kk_shader.h"
|
||||
|
||||
#include "kosmickrisp/bridge/mtl_bridge.h"
|
||||
|
||||
#include "vk_common_entrypoints.h"
|
||||
|
||||
static void
|
||||
kk_flush_compute_state(struct kk_cmd_buffer *cmd)
|
||||
{
|
||||
mtl_compute_encoder *enc = kk_compute_encoder(cmd);
|
||||
|
||||
// Fill Metal argument buffer with descriptor set addresses
|
||||
struct kk_descriptor_state *desc = &cmd->state.cs.descriptors;
|
||||
|
||||
if (desc->push_dirty)
|
||||
kk_cmd_buffer_flush_push_descriptors(cmd, desc);
|
||||
if (desc->root_dirty)
|
||||
kk_upload_descriptor_root(cmd, VK_PIPELINE_BIND_POINT_COMPUTE);
|
||||
|
||||
struct kk_ptr root_buffer = desc->root.root_buffer;
|
||||
if (root_buffer.gpu)
|
||||
mtl_compute_set_buffer(enc, root_buffer.buffer, root_buffer.offset, 0);
|
||||
|
||||
mtl_compute_set_pipeline_state(
|
||||
enc, cmd->state.shaders[MESA_SHADER_COMPUTE]->pipeline.cs);
|
||||
}
|
||||
|
||||
static void
|
||||
kk_predicate_compute(struct kk_cmd_buffer *cmd, uint64_t indirect_addr_out,
|
||||
uint64_t indirect_addr_in)
|
||||
{
|
||||
uint64_t cond_addr = cmd->state.cond_render.address;
|
||||
|
||||
/* TODO_KOSMICKRISP: This can be accomplished more efficiently using device
|
||||
* generated commands, constructing an indirect command buffer on the GPU
|
||||
* which only contains the commands to run if the condition is true. For the
|
||||
* time being, we apply predicates by zeroing out disabled indirect data */
|
||||
struct kk_grid grid = kk_grid_1d(1u);
|
||||
if (cmd->state.cond_render.inverted) {
|
||||
libkk_predicate_indirect_eq_zero(cmd, grid, false, indirect_addr_out,
|
||||
indirect_addr_in, cond_addr, 3u, 3u);
|
||||
} else {
|
||||
libkk_predicate_indirect_neq_zero(cmd, grid, false, indirect_addr_out,
|
||||
indirect_addr_in, cond_addr, 3u, 3u);
|
||||
}
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_CmdDispatchBase(VkCommandBuffer commandBuffer, uint32_t baseGroupX,
|
||||
uint32_t baseGroupY, uint32_t baseGroupZ,
|
||||
uint32_t groupCountX, uint32_t groupCountY,
|
||||
uint32_t groupCountZ)
|
||||
{
|
||||
/* Metal validation dislikes empty disptaches */
|
||||
if (groupCountX * groupCountY * groupCountZ == 0)
|
||||
return;
|
||||
|
||||
VK_FROM_HANDLE(kk_cmd_buffer, cmd, commandBuffer);
|
||||
|
||||
struct kk_descriptor_state *desc = &cmd->state.cs.descriptors;
|
||||
desc->root_dirty |= desc->root.cs.base_group[0] != baseGroupX;
|
||||
desc->root_dirty |= desc->root.cs.base_group[1] != baseGroupY;
|
||||
desc->root_dirty |= desc->root.cs.base_group[2] != baseGroupZ;
|
||||
desc->root.cs.base_group[0] = baseGroupX;
|
||||
desc->root.cs.base_group[1] = baseGroupY;
|
||||
desc->root.cs.base_group[2] = baseGroupZ;
|
||||
|
||||
struct kk_shader *cs = cmd->state.shaders[MESA_SHADER_COMPUTE];
|
||||
struct mtl_size local_size = cs->info.cs.local_size;
|
||||
|
||||
mtl_compute_encoder *enc = kk_compute_encoder(cmd);
|
||||
if (cmd->state.cond_render.enabled) {
|
||||
/* Convert to indirect for predication */
|
||||
VkDispatchIndirectCommand indirect = {
|
||||
.x = groupCountX,
|
||||
.y = groupCountY,
|
||||
.z = groupCountZ,
|
||||
};
|
||||
struct kk_ptr patched =
|
||||
kk_pool_upload(cmd, &indirect, sizeof(indirect), 4u);
|
||||
if (unlikely(patched.gpu == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
kk_predicate_compute(cmd, patched.gpu, patched.gpu);
|
||||
|
||||
/* Flush compute state after predication dispatch */
|
||||
kk_flush_compute_state(cmd);
|
||||
mtl_dispatch_threadgroups_with_indirect_buffer(
|
||||
enc, patched.buffer, patched.offset, local_size);
|
||||
} else {
|
||||
struct mtl_size grid_size = {
|
||||
.x = groupCountX * local_size.x,
|
||||
.y = groupCountY * local_size.y,
|
||||
.z = groupCountZ * local_size.z,
|
||||
};
|
||||
|
||||
kk_flush_compute_state(cmd);
|
||||
mtl_dispatch_threads(enc, grid_size, local_size);
|
||||
}
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_CmdDispatchIndirect(VkCommandBuffer commandBuffer, VkBuffer _buffer,
|
||||
VkDeviceSize offset)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_cmd_buffer, cmd, commandBuffer);
|
||||
VK_FROM_HANDLE(kk_buffer, buffer, _buffer);
|
||||
|
||||
struct kk_descriptor_state *desc = &cmd->state.cs.descriptors;
|
||||
desc->root_dirty |= desc->root.cs.base_group[0] != 0;
|
||||
desc->root_dirty |= desc->root.cs.base_group[1] != 0;
|
||||
desc->root_dirty |= desc->root.cs.base_group[2] != 0;
|
||||
desc->root.cs.base_group[0] = 0;
|
||||
desc->root.cs.base_group[1] = 0;
|
||||
desc->root.cs.base_group[2] = 0;
|
||||
|
||||
struct kk_shader *cs = cmd->state.shaders[MESA_SHADER_COMPUTE];
|
||||
struct mtl_size local_size = cs->info.cs.local_size;
|
||||
|
||||
mtl_compute_encoder *enc = kk_compute_encoder(cmd);
|
||||
if (cmd->state.cond_render.enabled) {
|
||||
struct kk_ptr patched =
|
||||
kk_pool_alloc(cmd, sizeof(VkDispatchIndirectCommand), 4u);
|
||||
if (unlikely(patched.gpu == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
kk_predicate_compute(cmd, patched.gpu,
|
||||
vk_buffer_address(&buffer->vk, offset));
|
||||
|
||||
/* Flush compute state after predication dispatch */
|
||||
kk_flush_compute_state(cmd);
|
||||
mtl_dispatch_threadgroups_with_indirect_buffer(
|
||||
enc, patched.buffer, patched.offset, local_size);
|
||||
} else {
|
||||
kk_flush_compute_state(cmd);
|
||||
mtl_dispatch_threadgroups_with_indirect_buffer(enc, buffer->mtl_handle,
|
||||
offset, local_size);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
* Copyright 2024 Valve Corporation
|
||||
* Copyright 2024 Alyssa Rosenzweig
|
||||
* Copyright © 2022 Collabora Ltd. and Red Hat Inc.
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "kk_private.h"
|
||||
|
||||
#include "kk_buffer.h"
|
||||
#include "kk_cmd_buffer.h"
|
||||
#include "kk_encoder.h"
|
||||
|
||||
#include "kosmickrisp/bridge/mtl_bridge.h"
|
||||
|
||||
#include "kk_entrypoints.h"
|
||||
|
||||
static VkResult
|
||||
kk_cmd_bind_map_buffer(struct vk_command_buffer *vk_cmd,
|
||||
struct vk_meta_device *meta, VkBuffer _buffer,
|
||||
void **map_out)
|
||||
{
|
||||
struct kk_cmd_buffer *cmd = container_of(vk_cmd, struct kk_cmd_buffer, vk);
|
||||
VK_FROM_HANDLE(kk_buffer, buffer, _buffer);
|
||||
|
||||
assert(buffer->vk.size < UINT_MAX);
|
||||
struct kk_ptr buf = kk_pool_alloc(cmd, buffer->vk.size, 16u);
|
||||
if (unlikely(!buf.gpu))
|
||||
return VK_ERROR_OUT_OF_POOL_MEMORY;
|
||||
|
||||
/* Need to retain since VkBuffers release the mtl_handle too */
|
||||
mtl_retain(buf.buffer);
|
||||
buffer->mtl_handle = buf.buffer;
|
||||
buffer->vk.device_address = buf.gpu;
|
||||
*map_out = buf.cpu;
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
VkResult
|
||||
kk_device_init_meta(struct kk_device *dev)
|
||||
{
|
||||
VkResult result = vk_meta_device_init(&dev->vk, &dev->meta);
|
||||
if (result != VK_SUCCESS)
|
||||
return result;
|
||||
|
||||
dev->meta.use_gs_for_layer = false;
|
||||
dev->meta.use_stencil_export = true;
|
||||
dev->meta.use_rect_list_pipeline = true;
|
||||
dev->meta.cmd_bind_map_buffer = kk_cmd_bind_map_buffer;
|
||||
dev->meta.max_bind_map_buffer_size_B = 64 * 1024;
|
||||
|
||||
for (unsigned i = 0; i < VK_META_BUFFER_CHUNK_SIZE_COUNT; ++i) {
|
||||
dev->meta.buffer_access.optimal_wg_size[i] = 64;
|
||||
}
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
void
|
||||
kk_device_finish_meta(struct kk_device *dev)
|
||||
{
|
||||
vk_meta_device_finish(&dev->vk, &dev->meta);
|
||||
}
|
||||
|
||||
struct kk_meta_save {
|
||||
struct vk_vertex_input_state _dynamic_vi;
|
||||
struct vk_sample_locations_state _dynamic_sl;
|
||||
struct vk_dynamic_graphics_state dynamic;
|
||||
struct kk_shader *shaders[MESA_SHADER_STAGES];
|
||||
struct {
|
||||
union {
|
||||
struct {
|
||||
mtl_render_pipeline_state *ps;
|
||||
mtl_depth_stencil_state *ds;
|
||||
enum mtl_visibility_result_mode occlusion;
|
||||
bool is_ds_dynamic;
|
||||
} gfx;
|
||||
};
|
||||
} pipeline;
|
||||
struct kk_conditional_rendering_state cond_render;
|
||||
struct kk_descriptor_set *desc0;
|
||||
struct kk_push_descriptor_set *push_desc0;
|
||||
mtl_buffer *vb0_handle;
|
||||
struct kk_addr_range vb0;
|
||||
struct kk_buffer_address desc0_set_addr;
|
||||
bool has_push_desc0;
|
||||
uint8_t push[KK_MAX_PUSH_SIZE];
|
||||
};
|
||||
|
||||
static void
|
||||
kk_meta_begin(struct kk_cmd_buffer *cmd, struct kk_meta_save *save,
|
||||
VkPipelineBindPoint bind_point)
|
||||
{
|
||||
struct kk_descriptor_state *desc = kk_get_descriptors_state(cmd, bind_point);
|
||||
|
||||
if (bind_point == VK_PIPELINE_BIND_POINT_GRAPHICS) {
|
||||
save->dynamic = cmd->vk.dynamic_graphics_state;
|
||||
save->_dynamic_vi = cmd->state.gfx._dynamic_vi;
|
||||
save->_dynamic_sl = cmd->state.gfx._dynamic_sl;
|
||||
|
||||
for (uint32_t stage = 0u; stage < MESA_SHADER_COMPUTE; ++stage) {
|
||||
save->shaders[stage] = cmd->state.shaders[stage];
|
||||
}
|
||||
|
||||
save->pipeline.gfx.ds = cmd->state.gfx.depth_stencil_state;
|
||||
save->pipeline.gfx.occlusion = cmd->state.gfx.occlusion.mode;
|
||||
save->pipeline.gfx.is_ds_dynamic =
|
||||
cmd->state.gfx.is_depth_stencil_dynamic;
|
||||
|
||||
cmd->state.gfx.is_depth_stencil_dynamic = false;
|
||||
cmd->state.gfx.depth_stencil_state = NULL;
|
||||
cmd->state.gfx.occlusion.mode = MTL_VISIBILITY_RESULT_MODE_DISABLED;
|
||||
cmd->state.gfx.dirty |= KK_DIRTY_OCCLUSION;
|
||||
desc->root_dirty = true;
|
||||
} else {
|
||||
save->shaders[MESA_SHADER_COMPUTE] =
|
||||
cmd->state.shaders[MESA_SHADER_COMPUTE];
|
||||
}
|
||||
|
||||
save->cond_render = cmd->state.cond_render;
|
||||
cmd->state.cond_render.enabled = false;
|
||||
|
||||
save->vb0_handle = cmd->state.gfx.vb.handles[0];
|
||||
save->vb0 = cmd->state.gfx.vb.addr_range[0];
|
||||
|
||||
save->desc0 = desc->sets[0];
|
||||
save->has_push_desc0 = desc->push[0];
|
||||
if (save->has_push_desc0)
|
||||
save->push_desc0 = desc->push[0];
|
||||
|
||||
static_assert(sizeof(save->push) == sizeof(desc->root.push),
|
||||
"Size mismatch for push in meta_save");
|
||||
memcpy(save->push, desc->root.push, sizeof(save->push));
|
||||
}
|
||||
|
||||
static void
|
||||
kk_meta_end(struct kk_cmd_buffer *cmd, struct kk_meta_save *save,
|
||||
VkPipelineBindPoint bind_point)
|
||||
{
|
||||
struct kk_descriptor_state *desc = kk_get_descriptors_state(cmd, bind_point);
|
||||
desc->root_dirty = true;
|
||||
|
||||
if (save->desc0) {
|
||||
desc->sets[0] = save->desc0;
|
||||
desc->root.sets[0] = save->desc0->addr;
|
||||
desc->set_sizes[0] = save->desc0->size;
|
||||
desc->push_dirty &= ~BITFIELD_BIT(0);
|
||||
} else if (save->has_push_desc0) {
|
||||
desc->push[0] = save->push_desc0;
|
||||
desc->push_dirty |= BITFIELD_BIT(0);
|
||||
}
|
||||
|
||||
if (bind_point == VK_PIPELINE_BIND_POINT_GRAPHICS) {
|
||||
/* Restore the dynamic state */
|
||||
assert(save->dynamic.vi == &cmd->state.gfx._dynamic_vi);
|
||||
assert(save->dynamic.ms.sample_locations == &cmd->state.gfx._dynamic_sl);
|
||||
cmd->vk.dynamic_graphics_state = save->dynamic;
|
||||
cmd->state.gfx._dynamic_vi = save->_dynamic_vi;
|
||||
cmd->state.gfx._dynamic_sl = save->_dynamic_sl;
|
||||
memcpy(cmd->vk.dynamic_graphics_state.dirty,
|
||||
cmd->vk.dynamic_graphics_state.set,
|
||||
sizeof(cmd->vk.dynamic_graphics_state.set));
|
||||
|
||||
for (uint32_t stage = 0u; stage < MESA_SHADER_COMPUTE; ++stage) {
|
||||
cmd->state.shaders[stage] = save->shaders[stage];
|
||||
}
|
||||
cmd->state.dirty_shaders |= BITFIELD_MASK(MESA_SHADER_COMPUTE);
|
||||
|
||||
if (cmd->state.gfx.is_depth_stencil_dynamic)
|
||||
mtl_release(cmd->state.gfx.depth_stencil_state);
|
||||
cmd->state.gfx.depth_stencil_state = save->pipeline.gfx.ds;
|
||||
cmd->state.gfx.is_depth_stencil_dynamic =
|
||||
save->pipeline.gfx.is_ds_dynamic;
|
||||
|
||||
cmd->state.gfx.vb.addr_range[0] = save->vb0;
|
||||
cmd->state.gfx.vb.handles[0] = save->vb0_handle;
|
||||
cmd->state.gfx.dirty |= KK_DIRTY_VB;
|
||||
|
||||
cmd->state.gfx.occlusion.mode = save->pipeline.gfx.occlusion;
|
||||
cmd->state.gfx.dirty |= KK_DIRTY_OCCLUSION;
|
||||
|
||||
desc->root_dirty = true;
|
||||
} else {
|
||||
kk_cmd_bind_compute_shader(cmd, save->shaders[MESA_SHADER_COMPUTE]);
|
||||
}
|
||||
|
||||
cmd->state.cond_render = save->cond_render;
|
||||
|
||||
memcpy(desc->root.push, save->push, sizeof(save->push));
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_CmdFillBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer,
|
||||
VkDeviceSize dstOffset, VkDeviceSize dstRange, uint32_t data)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_cmd_buffer, cmd, commandBuffer);
|
||||
struct kk_device *dev = kk_cmd_buffer_device(cmd);
|
||||
|
||||
struct kk_meta_save save;
|
||||
kk_meta_begin(cmd, &save, VK_PIPELINE_BIND_POINT_COMPUTE);
|
||||
vk_meta_fill_buffer(&cmd->vk, &dev->meta, dstBuffer, dstOffset, dstRange,
|
||||
data);
|
||||
kk_meta_end(cmd, &save, VK_PIPELINE_BIND_POINT_COMPUTE);
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_CmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer dstBuffer,
|
||||
VkDeviceSize dstOffset, VkDeviceSize dstRange,
|
||||
const void *pData)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_cmd_buffer, cmd, commandBuffer);
|
||||
struct kk_device *dev = kk_cmd_buffer_device(cmd);
|
||||
|
||||
struct kk_meta_save save;
|
||||
kk_meta_begin(cmd, &save, VK_PIPELINE_BIND_POINT_COMPUTE);
|
||||
vk_meta_update_buffer(&cmd->vk, &dev->meta, dstBuffer, dstOffset, dstRange,
|
||||
pData);
|
||||
kk_meta_end(cmd, &save, VK_PIPELINE_BIND_POINT_COMPUTE);
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_CmdBlitImage2(VkCommandBuffer commandBuffer,
|
||||
const VkBlitImageInfo2 *pBlitImageInfo)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_cmd_buffer, cmd, commandBuffer);
|
||||
struct kk_device *dev = kk_cmd_buffer_device(cmd);
|
||||
|
||||
struct kk_meta_save save;
|
||||
kk_meta_begin(cmd, &save, VK_PIPELINE_BIND_POINT_GRAPHICS);
|
||||
vk_meta_blit_image2(&cmd->vk, &dev->meta, pBlitImageInfo);
|
||||
kk_meta_end(cmd, &save, VK_PIPELINE_BIND_POINT_GRAPHICS);
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_CmdResolveImage2(VkCommandBuffer commandBuffer,
|
||||
const VkResolveImageInfo2 *pResolveImageInfo)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_cmd_buffer, cmd, commandBuffer);
|
||||
struct kk_device *dev = kk_cmd_buffer_device(cmd);
|
||||
|
||||
struct kk_meta_save save;
|
||||
kk_meta_begin(cmd, &save, VK_PIPELINE_BIND_POINT_GRAPHICS);
|
||||
vk_meta_resolve_image2(&cmd->vk, &dev->meta, pResolveImageInfo);
|
||||
kk_meta_end(cmd, &save, VK_PIPELINE_BIND_POINT_GRAPHICS);
|
||||
}
|
||||
|
||||
static void
|
||||
kk_meta_init_render(struct kk_cmd_buffer *cmd,
|
||||
struct vk_meta_rendering_info *info)
|
||||
{
|
||||
const struct kk_rendering_state *render = &cmd->state.gfx.render;
|
||||
|
||||
*info = (struct vk_meta_rendering_info){
|
||||
.samples = MAX2(render->samples, 1),
|
||||
.view_mask = render->view_mask,
|
||||
.color_attachment_count = render->color_att_count,
|
||||
.depth_attachment_format = render->depth_att.vk_format,
|
||||
.stencil_attachment_format = render->stencil_att.vk_format,
|
||||
};
|
||||
for (uint32_t a = 0; a < render->color_att_count; a++) {
|
||||
info->color_attachment_formats[a] = render->color_att[a].vk_format;
|
||||
info->color_attachment_write_masks[a] =
|
||||
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
|
||||
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_CmdClearAttachments(VkCommandBuffer commandBuffer, uint32_t attachmentCount,
|
||||
const VkClearAttachment *pAttachments,
|
||||
uint32_t rectCount, const VkClearRect *pRects)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_cmd_buffer, cmd, commandBuffer);
|
||||
struct kk_device *dev = kk_cmd_buffer_device(cmd);
|
||||
|
||||
struct vk_meta_rendering_info render_info;
|
||||
kk_meta_init_render(cmd, &render_info);
|
||||
|
||||
uint32_t view_mask = cmd->state.gfx.render.view_mask;
|
||||
struct kk_encoder *encoder = cmd->encoder;
|
||||
uint32_t layer_ids[KK_MAX_MULTIVIEW_VIEW_COUNT] = {};
|
||||
mtl_set_vertex_amplification_count(encoder->main.encoder, layer_ids, 1u);
|
||||
|
||||
/* Preserve conditional rendering state for clearing attachments */
|
||||
struct kk_conditional_rendering_state cond_render = cmd->state.cond_render;
|
||||
|
||||
struct kk_meta_save save;
|
||||
kk_meta_begin(cmd, &save, VK_PIPELINE_BIND_POINT_GRAPHICS);
|
||||
cmd->state.cond_render = cond_render;
|
||||
vk_meta_clear_attachments(&cmd->vk, &dev->meta, &render_info,
|
||||
attachmentCount, pAttachments, rectCount, pRects);
|
||||
kk_meta_end(cmd, &save, VK_PIPELINE_BIND_POINT_GRAPHICS);
|
||||
|
||||
uint32_t count = 0u;
|
||||
u_foreach_bit(id, view_mask)
|
||||
layer_ids[count++] = id;
|
||||
if (view_mask == 0u) {
|
||||
layer_ids[count++] = 0;
|
||||
}
|
||||
mtl_set_vertex_amplification_count(encoder->main.encoder, layer_ids, count);
|
||||
}
|
||||
|
||||
void
|
||||
kk_meta_resolve_rendering(struct kk_cmd_buffer *cmd,
|
||||
const VkRenderingInfo *pRenderingInfo)
|
||||
{
|
||||
struct kk_device *dev = kk_cmd_buffer_device(cmd);
|
||||
|
||||
struct kk_meta_save save;
|
||||
kk_meta_begin(cmd, &save, VK_PIPELINE_BIND_POINT_GRAPHICS);
|
||||
vk_meta_resolve_rendering(&cmd->vk, &dev->meta, pRenderingInfo);
|
||||
kk_meta_end(cmd, &save, VK_PIPELINE_BIND_POINT_GRAPHICS);
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright © 2022 Collabora Ltd. and Red Hat Inc.
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "kk_cmd_pool.h"
|
||||
|
||||
#include "kk_device.h"
|
||||
#include "kk_entrypoints.h"
|
||||
#include "kk_physical_device.h"
|
||||
|
||||
static VkResult
|
||||
kk_cmd_bo_create(struct kk_cmd_pool *pool, struct kk_cmd_bo **bo_out)
|
||||
{
|
||||
struct kk_device *dev = kk_cmd_pool_device(pool);
|
||||
|
||||
struct kk_cmd_bo *bo = vk_zalloc(&pool->vk.alloc, sizeof(*bo), 8,
|
||||
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
|
||||
if (bo == NULL)
|
||||
return vk_error(pool, VK_ERROR_OUT_OF_HOST_MEMORY);
|
||||
|
||||
const VkResult result =
|
||||
kk_alloc_bo(dev, &pool->vk.base, KK_CMD_BO_SIZE, 0, &bo->bo);
|
||||
if (result != VK_SUCCESS) {
|
||||
vk_free(&pool->vk.alloc, bo);
|
||||
return vk_error(pool, VK_ERROR_OUT_OF_DEVICE_MEMORY);
|
||||
}
|
||||
|
||||
*bo_out = bo;
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
static void
|
||||
kk_cmd_bo_destroy(struct kk_cmd_pool *pool, struct kk_cmd_bo *bo)
|
||||
{
|
||||
struct kk_device *dev = kk_cmd_pool_device(pool);
|
||||
kk_destroy_bo(dev, bo->bo);
|
||||
vk_free(&pool->vk.alloc, bo);
|
||||
}
|
||||
|
||||
static void
|
||||
kk_cmd_pool_destroy_bos(struct kk_cmd_pool *pool)
|
||||
{
|
||||
list_for_each_entry_safe(struct kk_cmd_bo, bo, &pool->free_bos, link)
|
||||
kk_cmd_bo_destroy(pool, bo);
|
||||
|
||||
list_inithead(&pool->free_bos);
|
||||
}
|
||||
|
||||
VkResult
|
||||
kk_cmd_pool_alloc_bo(struct kk_cmd_pool *pool, struct kk_cmd_bo **bo_out)
|
||||
{
|
||||
struct kk_cmd_bo *bo = NULL;
|
||||
if (!list_is_empty(&pool->free_bos)) {
|
||||
bo = list_first_entry(&pool->free_bos, struct kk_cmd_bo, link);
|
||||
pool->num_free_bos--;
|
||||
}
|
||||
|
||||
if (bo) {
|
||||
list_del(&bo->link);
|
||||
*bo_out = bo;
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
return kk_cmd_bo_create(pool, bo_out);
|
||||
}
|
||||
|
||||
void
|
||||
kk_cmd_pool_free_bo_list(struct kk_cmd_pool *pool, struct list_head *bos)
|
||||
{
|
||||
list_for_each_entry_safe(struct kk_cmd_bo, bo, bos, link) {
|
||||
list_del(&bo->link);
|
||||
if (pool->num_free_bos > KK_CMD_POOL_BO_MAX) {
|
||||
kk_cmd_bo_destroy(pool, bo);
|
||||
} else {
|
||||
list_addtail(&bo->link, &pool->free_bos);
|
||||
pool->num_free_bos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkResult VKAPI_CALL
|
||||
kk_CreateCommandPool(VkDevice _device,
|
||||
const VkCommandPoolCreateInfo *pCreateInfo,
|
||||
const VkAllocationCallbacks *pAllocator,
|
||||
VkCommandPool *pCmdPool)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_device, device, _device);
|
||||
struct kk_cmd_pool *pool;
|
||||
|
||||
pool = vk_alloc2(&device->vk.alloc, pAllocator, sizeof(*pool), 8,
|
||||
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
|
||||
if (pool == NULL)
|
||||
return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
|
||||
|
||||
VkResult result =
|
||||
vk_command_pool_init(&device->vk, &pool->vk, pCreateInfo, pAllocator);
|
||||
if (result != VK_SUCCESS) {
|
||||
vk_free2(&device->vk.alloc, pAllocator, pool);
|
||||
return result;
|
||||
}
|
||||
|
||||
list_inithead(&pool->free_bos);
|
||||
|
||||
*pCmdPool = kk_cmd_pool_to_handle(pool);
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_DestroyCommandPool(VkDevice _device, VkCommandPool commandPool,
|
||||
const VkAllocationCallbacks *pAllocator)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_device, device, _device);
|
||||
VK_FROM_HANDLE(kk_cmd_pool, pool, commandPool);
|
||||
|
||||
if (!pool)
|
||||
return;
|
||||
|
||||
vk_command_pool_finish(&pool->vk);
|
||||
kk_cmd_pool_destroy_bos(pool);
|
||||
vk_free2(&device->vk.alloc, pAllocator, pool);
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_TrimCommandPool(VkDevice device, VkCommandPool commandPool,
|
||||
VkCommandPoolTrimFlags flags)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_cmd_pool, pool, commandPool);
|
||||
|
||||
vk_command_pool_trim(&pool->vk, flags);
|
||||
kk_cmd_pool_destroy_bos(pool);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright © 2022 Collabora Ltd. and Red Hat Inc.
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef KK_CMD_POOL_H
|
||||
#define KK_CMD_POOL_H
|
||||
|
||||
#include "kk_private.h"
|
||||
|
||||
#include "vk_command_pool.h"
|
||||
|
||||
#define KK_CMD_BO_SIZE (1024 * 128)
|
||||
#define KK_CMD_POOL_BO_MAX 32
|
||||
|
||||
/* Recyclable command buffer BO, used for both push buffers and upload */
|
||||
struct kk_cmd_bo {
|
||||
struct kk_bo *bo;
|
||||
|
||||
/** Link in kk_cmd_pool::free_bos or kk_cmd_buffer::bos */
|
||||
struct list_head link;
|
||||
};
|
||||
|
||||
struct kk_cmd_pool {
|
||||
struct vk_command_pool vk;
|
||||
|
||||
/** List of kk_cmd_bo */
|
||||
struct list_head free_bos;
|
||||
uint32_t num_free_bos;
|
||||
};
|
||||
|
||||
VK_DEFINE_NONDISP_HANDLE_CASTS(kk_cmd_pool, vk.base, VkCommandPool,
|
||||
VK_OBJECT_TYPE_COMMAND_POOL)
|
||||
|
||||
static inline struct kk_device *
|
||||
kk_cmd_pool_device(struct kk_cmd_pool *pool)
|
||||
{
|
||||
return (struct kk_device *)pool->vk.base.device;
|
||||
}
|
||||
|
||||
VkResult kk_cmd_pool_alloc_bo(struct kk_cmd_pool *pool,
|
||||
struct kk_cmd_bo **bo_out);
|
||||
|
||||
void kk_cmd_pool_free_bo_list(struct kk_cmd_pool *pool, struct list_head *bos);
|
||||
|
||||
#endif /* KK_CMD_POOL_H */
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include "kk_debug.h"
|
||||
#include "util/u_debug.h"
|
||||
|
||||
enum kk_debug kk_mesa_debug_flags = 0;
|
||||
|
||||
const struct debug_named_value flags[] = {
|
||||
{"nir", KK_DEBUG_NIR},
|
||||
{"msl", KK_DEBUG_MSL},
|
||||
{"force_robustness", KK_DEBUG_FORCE_ROBUSTNESS},
|
||||
{NULL, 0},
|
||||
};
|
||||
|
||||
DEBUG_GET_ONCE_FLAGS_OPTION(mesa_kk_debug, "MESA_KK_DEBUG", flags, 0);
|
||||
|
||||
void
|
||||
kk_process_debug_variable(void)
|
||||
{
|
||||
kk_mesa_debug_flags = debug_get_option_mesa_kk_debug();
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef KK_DEBUG_H
|
||||
#define KK_DEBUG_H 1
|
||||
|
||||
enum kk_debug {
|
||||
/* Print out the NIR from the compiler */
|
||||
KK_DEBUG_NIR = 1ull << 0,
|
||||
/* Print out the generated MSL source code from the compiler */
|
||||
KK_DEBUG_MSL = 1ull << 1,
|
||||
/* Forces robustness for all shaders */
|
||||
KK_DEBUG_FORCE_ROBUSTNESS = 1ull << 2,
|
||||
};
|
||||
|
||||
extern enum kk_debug kk_mesa_debug_flags;
|
||||
|
||||
#define KK_DEBUG(flag) unlikely(kk_mesa_debug_flags &KK_DEBUG_##flag)
|
||||
|
||||
extern void kk_process_debug_variable(void);
|
||||
|
||||
#endif /* KK_DEBUG_H */
|
||||
@@ -0,0 +1,805 @@
|
||||
/*
|
||||
* Copyright © 2022 Collabora Ltd. and Red Hat Inc.
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "kk_descriptor_set.h"
|
||||
|
||||
#include "kk_bo.h"
|
||||
#include "kk_buffer.h"
|
||||
#include "kk_buffer_view.h"
|
||||
#include "kk_descriptor_set_layout.h"
|
||||
#include "kk_device.h"
|
||||
#include "kk_entrypoints.h"
|
||||
#include "kk_image_view.h"
|
||||
#include "kk_physical_device.h"
|
||||
#include "kk_sampler.h"
|
||||
|
||||
#include "util/format/u_format.h"
|
||||
|
||||
static inline uint32_t
|
||||
align_u32(uint32_t v, uint32_t a)
|
||||
{
|
||||
assert(a != 0 && a == (a & -a));
|
||||
return (v + a - 1) & ~(a - 1);
|
||||
}
|
||||
|
||||
static inline void *
|
||||
desc_ubo_data(struct kk_descriptor_set *set, uint32_t binding, uint32_t elem,
|
||||
uint32_t *size_out)
|
||||
{
|
||||
const struct kk_descriptor_set_binding_layout *binding_layout =
|
||||
&set->layout->binding[binding];
|
||||
|
||||
uint32_t offset = binding_layout->offset + elem * binding_layout->stride;
|
||||
assert(offset < set->size);
|
||||
|
||||
if (size_out != NULL)
|
||||
*size_out = set->size - offset;
|
||||
|
||||
return (char *)set->mapped_ptr + offset;
|
||||
}
|
||||
|
||||
static void
|
||||
write_desc(struct kk_descriptor_set *set, uint32_t binding, uint32_t elem,
|
||||
const void *desc_data, size_t desc_size)
|
||||
{
|
||||
ASSERTED uint32_t dst_size;
|
||||
void *dst = desc_ubo_data(set, binding, elem, &dst_size);
|
||||
assert(desc_size <= dst_size);
|
||||
memcpy(dst, desc_data, desc_size);
|
||||
}
|
||||
|
||||
static void
|
||||
get_sampled_image_view_desc(VkDescriptorType descriptor_type,
|
||||
const VkDescriptorImageInfo *const info, void *dst,
|
||||
size_t dst_size, bool is_input_attachment)
|
||||
{
|
||||
struct kk_sampled_image_descriptor desc[3] = {};
|
||||
uint8_t plane_count = 1;
|
||||
|
||||
if (descriptor_type != VK_DESCRIPTOR_TYPE_SAMPLER && info &&
|
||||
info->imageView != VK_NULL_HANDLE) {
|
||||
VK_FROM_HANDLE(kk_image_view, view, info->imageView);
|
||||
|
||||
plane_count = view->plane_count;
|
||||
for (uint8_t plane = 0; plane < plane_count; plane++) {
|
||||
if (is_input_attachment) {
|
||||
assert(view->planes[plane].sampled_gpu_resource_id);
|
||||
desc[plane].image_gpu_resource_id =
|
||||
view->planes[plane].input_gpu_resource_id;
|
||||
} else {
|
||||
assert(view->planes[plane].sampled_gpu_resource_id);
|
||||
desc[plane].image_gpu_resource_id =
|
||||
view->planes[plane].sampled_gpu_resource_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (descriptor_type == VK_DESCRIPTOR_TYPE_SAMPLER ||
|
||||
descriptor_type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) {
|
||||
VK_FROM_HANDLE(kk_sampler, sampler, info->sampler);
|
||||
|
||||
plane_count = MAX2(plane_count, sampler->plane_count);
|
||||
|
||||
for (uint8_t plane = 0; plane < plane_count; plane++) {
|
||||
/* We need to replicate the last sampler plane out to all image
|
||||
* planes due to sampler table entry limitations. See
|
||||
* nvk_CreateSampler in nvk_sampler.c for more details.
|
||||
*/
|
||||
uint8_t sampler_plane = MIN2(plane, sampler->plane_count - 1u);
|
||||
assert(sampler->planes[sampler_plane].hw->handle);
|
||||
desc[plane].sampler_index = sampler->planes[sampler_plane].hw->index;
|
||||
desc[plane].lod_bias_fp16 = sampler->lod_bias_fp16;
|
||||
desc[plane].lod_min_fp16 = sampler->lod_min_fp16;
|
||||
desc[plane].lod_max_fp16 = sampler->lod_max_fp16;
|
||||
}
|
||||
}
|
||||
|
||||
assert(sizeof(desc[0]) * plane_count <= dst_size);
|
||||
memcpy(dst, desc, sizeof(desc[0]) * plane_count);
|
||||
}
|
||||
|
||||
static void
|
||||
write_sampled_image_view_desc(struct kk_descriptor_set *set,
|
||||
const VkDescriptorImageInfo *const _info,
|
||||
uint32_t binding, uint32_t elem,
|
||||
VkDescriptorType descriptor_type)
|
||||
{
|
||||
VkDescriptorImageInfo info = *_info;
|
||||
|
||||
struct kk_descriptor_set_binding_layout *binding_layout =
|
||||
&set->layout->binding[binding];
|
||||
if (descriptor_type == VK_DESCRIPTOR_TYPE_SAMPLER ||
|
||||
descriptor_type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) {
|
||||
if (binding_layout->immutable_samplers != NULL) {
|
||||
info.sampler =
|
||||
kk_sampler_to_handle(binding_layout->immutable_samplers[elem]);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t dst_size;
|
||||
void *dst = desc_ubo_data(set, binding, elem, &dst_size);
|
||||
get_sampled_image_view_desc(
|
||||
descriptor_type, &info, dst, dst_size,
|
||||
descriptor_type == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT);
|
||||
}
|
||||
|
||||
static void
|
||||
get_storage_image_view_desc(
|
||||
struct kk_descriptor_set_binding_layout *binding_layout,
|
||||
const VkDescriptorImageInfo *const info, void *dst, size_t dst_size)
|
||||
{
|
||||
struct kk_storage_image_descriptor desc = {};
|
||||
|
||||
if (info && info->imageView != VK_NULL_HANDLE) {
|
||||
VK_FROM_HANDLE(kk_image_view, view, info->imageView);
|
||||
|
||||
/* Storage images are always single plane */
|
||||
assert(view->plane_count == 1);
|
||||
uint8_t plane = 0;
|
||||
|
||||
assert(view->planes[plane].storage_gpu_resource_id);
|
||||
desc.image_gpu_resource_id = view->planes[plane].storage_gpu_resource_id;
|
||||
}
|
||||
|
||||
assert(sizeof(desc) <= dst_size);
|
||||
memcpy(dst, &desc, sizeof(desc));
|
||||
}
|
||||
|
||||
static void
|
||||
write_storage_image_view_desc(struct kk_descriptor_set *set,
|
||||
const VkDescriptorImageInfo *const info,
|
||||
uint32_t binding, uint32_t elem)
|
||||
{
|
||||
uint32_t dst_size;
|
||||
void *dst = desc_ubo_data(set, binding, elem, &dst_size);
|
||||
struct kk_descriptor_set_binding_layout *binding_layout =
|
||||
&set->layout->binding[binding];
|
||||
get_storage_image_view_desc(binding_layout, info, dst, dst_size);
|
||||
}
|
||||
|
||||
static void
|
||||
write_buffer_desc(struct kk_descriptor_set *set,
|
||||
const VkDescriptorBufferInfo *const info, uint32_t binding,
|
||||
uint32_t elem)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_buffer, buffer, info->buffer);
|
||||
|
||||
const struct kk_addr_range addr_range =
|
||||
kk_buffer_addr_range(buffer, info->offset, info->range);
|
||||
assert(addr_range.range <= UINT32_MAX);
|
||||
|
||||
const struct kk_buffer_address desc = {
|
||||
.base_addr = addr_range.addr,
|
||||
.size = addr_range.range,
|
||||
};
|
||||
write_desc(set, binding, elem, &desc, sizeof(desc));
|
||||
}
|
||||
|
||||
static void
|
||||
write_dynamic_buffer_desc(struct kk_descriptor_set *set,
|
||||
const VkDescriptorBufferInfo *const info,
|
||||
uint32_t binding, uint32_t elem)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_buffer, buffer, info->buffer);
|
||||
const struct kk_descriptor_set_binding_layout *binding_layout =
|
||||
&set->layout->binding[binding];
|
||||
|
||||
const struct kk_addr_range addr_range =
|
||||
kk_buffer_addr_range(buffer, info->offset, info->range);
|
||||
assert(addr_range.range <= UINT32_MAX);
|
||||
|
||||
struct kk_buffer_address *desc =
|
||||
&set->dynamic_buffers[binding_layout->dynamic_buffer_index + elem];
|
||||
*desc = (struct kk_buffer_address){
|
||||
.base_addr = addr_range.addr,
|
||||
.size = addr_range.range,
|
||||
};
|
||||
}
|
||||
|
||||
static void
|
||||
write_buffer_view_desc(struct kk_descriptor_set *set,
|
||||
const VkBufferView bufferView, uint32_t binding,
|
||||
uint32_t elem)
|
||||
{
|
||||
struct kk_storage_image_descriptor desc = {};
|
||||
if (bufferView != VK_NULL_HANDLE) {
|
||||
VK_FROM_HANDLE(kk_buffer_view, view, bufferView);
|
||||
|
||||
assert(view->mtl_texel_buffer_handle);
|
||||
assert(view->texel_buffer_gpu_id);
|
||||
|
||||
desc.image_gpu_resource_id = view->texel_buffer_gpu_id;
|
||||
}
|
||||
write_desc(set, binding, elem, &desc, sizeof(desc));
|
||||
}
|
||||
|
||||
static void
|
||||
write_inline_uniform_data(struct kk_descriptor_set *set,
|
||||
const VkWriteDescriptorSetInlineUniformBlock *info,
|
||||
uint32_t binding, uint32_t offset)
|
||||
{
|
||||
assert(set->layout->binding[binding].stride == 1);
|
||||
write_desc(set, binding, offset, info->pData, info->dataSize);
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_UpdateDescriptorSets(VkDevice device, uint32_t descriptorWriteCount,
|
||||
const VkWriteDescriptorSet *pDescriptorWrites,
|
||||
uint32_t descriptorCopyCount,
|
||||
const VkCopyDescriptorSet *pDescriptorCopies)
|
||||
{
|
||||
for (uint32_t w = 0; w < descriptorWriteCount; w++) {
|
||||
const VkWriteDescriptorSet *write = &pDescriptorWrites[w];
|
||||
VK_FROM_HANDLE(kk_descriptor_set, set, write->dstSet);
|
||||
|
||||
switch (write->descriptorType) {
|
||||
case VK_DESCRIPTOR_TYPE_SAMPLER:
|
||||
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
|
||||
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
||||
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
|
||||
for (uint32_t j = 0; j < write->descriptorCount; j++) {
|
||||
write_sampled_image_view_desc(
|
||||
set, write->pImageInfo + j, write->dstBinding,
|
||||
write->dstArrayElement + j, write->descriptorType);
|
||||
}
|
||||
break;
|
||||
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
||||
for (uint32_t j = 0; j < write->descriptorCount; j++) {
|
||||
write_storage_image_view_desc(set, write->pImageInfo + j,
|
||||
write->dstBinding,
|
||||
write->dstArrayElement + j);
|
||||
}
|
||||
break;
|
||||
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
|
||||
for (uint32_t j = 0; j < write->descriptorCount; j++) {
|
||||
write_buffer_view_desc(set, write->pTexelBufferView[j],
|
||||
write->dstBinding,
|
||||
write->dstArrayElement + j);
|
||||
}
|
||||
break;
|
||||
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
|
||||
for (uint32_t j = 0; j < write->descriptorCount; j++) {
|
||||
write_buffer_desc(set, write->pBufferInfo + j, write->dstBinding,
|
||||
write->dstArrayElement + j);
|
||||
}
|
||||
break;
|
||||
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
|
||||
for (uint32_t j = 0; j < write->descriptorCount; j++) {
|
||||
write_dynamic_buffer_desc(set, write->pBufferInfo + j,
|
||||
write->dstBinding,
|
||||
write->dstArrayElement + j);
|
||||
}
|
||||
break;
|
||||
|
||||
case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK: {
|
||||
const VkWriteDescriptorSetInlineUniformBlock *write_inline =
|
||||
vk_find_struct_const(write->pNext,
|
||||
WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK);
|
||||
assert(write_inline->dataSize == write->descriptorCount);
|
||||
write_inline_uniform_data(set, write_inline, write->dstBinding,
|
||||
write->dstArrayElement);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < descriptorCopyCount; i++) {
|
||||
const VkCopyDescriptorSet *copy = &pDescriptorCopies[i];
|
||||
VK_FROM_HANDLE(kk_descriptor_set, src, copy->srcSet);
|
||||
VK_FROM_HANDLE(kk_descriptor_set, dst, copy->dstSet);
|
||||
|
||||
const struct kk_descriptor_set_binding_layout *src_binding_layout =
|
||||
&src->layout->binding[copy->srcBinding];
|
||||
const struct kk_descriptor_set_binding_layout *dst_binding_layout =
|
||||
&dst->layout->binding[copy->dstBinding];
|
||||
|
||||
if (dst_binding_layout->stride > 0 && src_binding_layout->stride > 0) {
|
||||
for (uint32_t j = 0; j < copy->descriptorCount; j++) {
|
||||
ASSERTED uint32_t dst_max_size, src_max_size;
|
||||
void *dst_map = desc_ubo_data(
|
||||
dst, copy->dstBinding, copy->dstArrayElement + j, &dst_max_size);
|
||||
const void *src_map = desc_ubo_data(
|
||||
src, copy->srcBinding, copy->srcArrayElement + j, &src_max_size);
|
||||
const uint32_t copy_size =
|
||||
MIN2(dst_binding_layout->stride, src_binding_layout->stride);
|
||||
assert(copy_size <= dst_max_size && copy_size <= src_max_size);
|
||||
memcpy(dst_map, src_map, copy_size);
|
||||
}
|
||||
}
|
||||
|
||||
switch (src_binding_layout->type) {
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: {
|
||||
const uint32_t dst_dyn_start =
|
||||
dst_binding_layout->dynamic_buffer_index + copy->dstArrayElement;
|
||||
const uint32_t src_dyn_start =
|
||||
src_binding_layout->dynamic_buffer_index + copy->srcArrayElement;
|
||||
typed_memcpy(&dst->dynamic_buffers[dst_dyn_start],
|
||||
&src->dynamic_buffers[src_dyn_start],
|
||||
copy->descriptorCount);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
kk_push_descriptor_set_update(struct kk_push_descriptor_set *push_set,
|
||||
uint32_t write_count,
|
||||
const VkWriteDescriptorSet *writes)
|
||||
{
|
||||
struct kk_descriptor_set_layout *layout = push_set->layout;
|
||||
assert(layout->non_variable_descriptor_buffer_size < sizeof(push_set->data));
|
||||
struct kk_descriptor_set set = {
|
||||
.layout = push_set->layout,
|
||||
.size = sizeof(push_set->data),
|
||||
.mapped_ptr = push_set->data,
|
||||
};
|
||||
|
||||
for (uint32_t w = 0; w < write_count; w++) {
|
||||
const VkWriteDescriptorSet *write = &writes[w];
|
||||
assert(write->dstSet == VK_NULL_HANDLE);
|
||||
|
||||
switch (write->descriptorType) {
|
||||
case VK_DESCRIPTOR_TYPE_SAMPLER:
|
||||
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
|
||||
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
||||
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
|
||||
for (uint32_t j = 0; j < write->descriptorCount; j++) {
|
||||
write_sampled_image_view_desc(
|
||||
&set, write->pImageInfo + j, write->dstBinding,
|
||||
write->dstArrayElement + j, write->descriptorType);
|
||||
}
|
||||
break;
|
||||
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
||||
for (uint32_t j = 0; j < write->descriptorCount; j++) {
|
||||
write_storage_image_view_desc(&set, write->pImageInfo + j,
|
||||
write->dstBinding,
|
||||
write->dstArrayElement + j);
|
||||
}
|
||||
break;
|
||||
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
|
||||
for (uint32_t j = 0; j < write->descriptorCount; j++) {
|
||||
write_buffer_view_desc(&set, write->pTexelBufferView[j],
|
||||
write->dstBinding,
|
||||
write->dstArrayElement + j);
|
||||
}
|
||||
break;
|
||||
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
|
||||
for (uint32_t j = 0; j < write->descriptorCount; j++) {
|
||||
write_buffer_desc(&set, write->pBufferInfo + j, write->dstBinding,
|
||||
write->dstArrayElement + j);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void kk_descriptor_pool_free(struct kk_descriptor_pool *pool,
|
||||
uint64_t addr, uint64_t size);
|
||||
|
||||
static void
|
||||
kk_descriptor_set_destroy(struct kk_device *dev,
|
||||
struct kk_descriptor_pool *pool,
|
||||
struct kk_descriptor_set *set)
|
||||
{
|
||||
list_del(&set->link);
|
||||
if (set->size > 0)
|
||||
kk_descriptor_pool_free(pool, set->addr, set->size);
|
||||
vk_descriptor_set_layout_unref(&dev->vk, &set->layout->vk);
|
||||
|
||||
vk_object_free(&dev->vk, NULL, set);
|
||||
}
|
||||
|
||||
static void
|
||||
kk_destroy_descriptor_pool(struct kk_device *dev,
|
||||
const VkAllocationCallbacks *pAllocator,
|
||||
struct kk_descriptor_pool *pool)
|
||||
{
|
||||
list_for_each_entry_safe(struct kk_descriptor_set, set, &pool->sets, link)
|
||||
kk_descriptor_set_destroy(dev, pool, set);
|
||||
|
||||
util_vma_heap_finish(&pool->heap);
|
||||
|
||||
if (pool->bo != NULL)
|
||||
kk_destroy_bo(dev, pool->bo);
|
||||
|
||||
vk_object_free(&dev->vk, pAllocator, pool);
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkResult VKAPI_CALL
|
||||
kk_CreateDescriptorPool(VkDevice _device,
|
||||
const VkDescriptorPoolCreateInfo *pCreateInfo,
|
||||
const VkAllocationCallbacks *pAllocator,
|
||||
VkDescriptorPool *pDescriptorPool)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_device, dev, _device);
|
||||
struct kk_descriptor_pool *pool;
|
||||
VkResult result = VK_SUCCESS;
|
||||
|
||||
pool = vk_object_zalloc(&dev->vk, pAllocator, sizeof(*pool),
|
||||
VK_OBJECT_TYPE_DESCRIPTOR_POOL);
|
||||
if (!pool)
|
||||
return vk_error(dev, VK_ERROR_OUT_OF_HOST_MEMORY);
|
||||
|
||||
list_inithead(&pool->sets);
|
||||
|
||||
const VkMutableDescriptorTypeCreateInfoEXT *mutable_info =
|
||||
vk_find_struct_const(pCreateInfo->pNext,
|
||||
MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_EXT);
|
||||
|
||||
uint32_t max_align = 0;
|
||||
for (unsigned i = 0; i < pCreateInfo->poolSizeCount; ++i) {
|
||||
const VkMutableDescriptorTypeListEXT *type_list = NULL;
|
||||
if (pCreateInfo->pPoolSizes[i].type == VK_DESCRIPTOR_TYPE_MUTABLE_EXT &&
|
||||
mutable_info && i < mutable_info->mutableDescriptorTypeListCount)
|
||||
type_list = &mutable_info->pMutableDescriptorTypeLists[i];
|
||||
|
||||
uint32_t stride, alignment;
|
||||
kk_descriptor_stride_align_for_type(pCreateInfo->pPoolSizes[i].type,
|
||||
type_list, &stride, &alignment);
|
||||
max_align = MAX2(max_align, alignment);
|
||||
}
|
||||
|
||||
uint64_t mem_size = 0;
|
||||
for (unsigned i = 0; i < pCreateInfo->poolSizeCount; ++i) {
|
||||
const VkMutableDescriptorTypeListEXT *type_list = NULL;
|
||||
if (pCreateInfo->pPoolSizes[i].type == VK_DESCRIPTOR_TYPE_MUTABLE_EXT &&
|
||||
mutable_info && i < mutable_info->mutableDescriptorTypeListCount)
|
||||
type_list = &mutable_info->pMutableDescriptorTypeLists[i];
|
||||
|
||||
uint32_t stride, alignment;
|
||||
kk_descriptor_stride_align_for_type(pCreateInfo->pPoolSizes[i].type,
|
||||
type_list, &stride, &alignment);
|
||||
mem_size +=
|
||||
MAX2(stride, max_align) * pCreateInfo->pPoolSizes[i].descriptorCount;
|
||||
}
|
||||
|
||||
/* Individual descriptor sets are aligned to the min UBO alignment to
|
||||
* ensure that we don't end up with unaligned data access in any shaders.
|
||||
* This means that each descriptor buffer allocated may burn up to 16B of
|
||||
* extra space to get the right alignment. (Technically, it's at most 28B
|
||||
* because we're always going to start at least 4B aligned but we're being
|
||||
* conservative here.) Allocate enough extra space that we can chop it
|
||||
* into maxSets pieces and align each one of them to 32B.
|
||||
*/
|
||||
mem_size += kk_min_cbuf_alignment() * pCreateInfo->maxSets;
|
||||
|
||||
if (mem_size) {
|
||||
result = kk_alloc_bo(dev, &dev->vk.base, mem_size, 0u, &pool->bo);
|
||||
if (result != VK_SUCCESS) {
|
||||
kk_destroy_descriptor_pool(dev, pAllocator, pool);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* The BO may be larger thanks to GPU page alignment. We may as well
|
||||
* make that extra space available to the client.
|
||||
*/
|
||||
assert(pool->bo->size_B >= mem_size);
|
||||
util_vma_heap_init(&pool->heap, pool->bo->gpu, pool->bo->size_B);
|
||||
} else {
|
||||
util_vma_heap_init(&pool->heap, 0, 0);
|
||||
}
|
||||
|
||||
*pDescriptorPool = kk_descriptor_pool_to_handle(pool);
|
||||
return result;
|
||||
}
|
||||
|
||||
static VkResult
|
||||
kk_descriptor_pool_alloc(struct kk_descriptor_pool *pool, uint64_t size,
|
||||
uint64_t alignment, uint64_t *addr_out, void **map_out)
|
||||
{
|
||||
assert(size > 0);
|
||||
assert(size % alignment == 0);
|
||||
|
||||
if (size > pool->heap.free_size)
|
||||
return VK_ERROR_OUT_OF_POOL_MEMORY;
|
||||
|
||||
uint64_t addr = util_vma_heap_alloc(&pool->heap, size, alignment);
|
||||
if (addr == 0)
|
||||
return VK_ERROR_FRAGMENTED_POOL;
|
||||
|
||||
assert(addr >= pool->bo->gpu);
|
||||
assert(addr + size <= pool->bo->gpu + pool->bo->size_B);
|
||||
uint64_t offset = addr - pool->bo->gpu;
|
||||
|
||||
*addr_out = addr;
|
||||
*map_out = pool->bo->cpu + offset;
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
static void
|
||||
kk_descriptor_pool_free(struct kk_descriptor_pool *pool, uint64_t addr,
|
||||
uint64_t size)
|
||||
{
|
||||
assert(size > 0);
|
||||
assert(addr >= pool->bo->gpu);
|
||||
assert(addr + size <= pool->bo->gpu + pool->bo->size_B);
|
||||
util_vma_heap_free(&pool->heap, addr, size);
|
||||
}
|
||||
|
||||
static VkResult
|
||||
kk_descriptor_set_create(struct kk_device *dev, struct kk_descriptor_pool *pool,
|
||||
struct kk_descriptor_set_layout *layout,
|
||||
uint32_t variable_count,
|
||||
struct kk_descriptor_set **out_set)
|
||||
{
|
||||
struct kk_descriptor_set *set;
|
||||
VkResult result = VK_SUCCESS;
|
||||
|
||||
uint32_t mem_size =
|
||||
sizeof(struct kk_descriptor_set) +
|
||||
layout->vk.dynamic_descriptor_count * sizeof(struct kk_buffer_address);
|
||||
set =
|
||||
vk_object_zalloc(&dev->vk, NULL, mem_size, VK_OBJECT_TYPE_DESCRIPTOR_SET);
|
||||
if (!set)
|
||||
return vk_error(dev, VK_ERROR_OUT_OF_HOST_MEMORY);
|
||||
|
||||
set->size = layout->non_variable_descriptor_buffer_size;
|
||||
|
||||
if (layout->binding_count > 0 &&
|
||||
(layout->binding[layout->binding_count - 1].flags &
|
||||
VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT)) {
|
||||
uint32_t stride = layout->binding[layout->binding_count - 1].stride;
|
||||
set->size += stride * variable_count;
|
||||
}
|
||||
|
||||
uint32_t alignment = kk_min_cbuf_alignment();
|
||||
set->size = align64(set->size, alignment);
|
||||
|
||||
if (set->size > 0) {
|
||||
result = kk_descriptor_pool_alloc(pool, set->size, alignment, &set->addr,
|
||||
&set->mapped_ptr);
|
||||
if (result != VK_SUCCESS) {
|
||||
vk_object_free(&dev->vk, NULL, set);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
vk_descriptor_set_layout_ref(&layout->vk);
|
||||
set->layout = layout;
|
||||
|
||||
for (uint32_t b = 0; b < layout->binding_count; b++) {
|
||||
if (layout->binding[b].type != VK_DESCRIPTOR_TYPE_SAMPLER &&
|
||||
layout->binding[b].type != VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
|
||||
continue;
|
||||
|
||||
if (layout->binding[b].immutable_samplers == NULL)
|
||||
continue;
|
||||
|
||||
uint32_t array_size = layout->binding[b].array_size;
|
||||
if (layout->binding[b].flags &
|
||||
VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT)
|
||||
array_size = variable_count;
|
||||
|
||||
const VkDescriptorImageInfo empty = {};
|
||||
for (uint32_t j = 0; j < array_size; j++) {
|
||||
write_sampled_image_view_desc(set, &empty, b, j,
|
||||
layout->binding[b].type);
|
||||
}
|
||||
}
|
||||
|
||||
list_addtail(&set->link, &pool->sets);
|
||||
*out_set = set;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkResult VKAPI_CALL
|
||||
kk_AllocateDescriptorSets(VkDevice device,
|
||||
const VkDescriptorSetAllocateInfo *pAllocateInfo,
|
||||
VkDescriptorSet *pDescriptorSets)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_device, dev, device);
|
||||
VK_FROM_HANDLE(kk_descriptor_pool, pool, pAllocateInfo->descriptorPool);
|
||||
|
||||
VkResult result = VK_SUCCESS;
|
||||
uint32_t i;
|
||||
|
||||
struct kk_descriptor_set *set = NULL;
|
||||
|
||||
const VkDescriptorSetVariableDescriptorCountAllocateInfo *var_desc_count =
|
||||
vk_find_struct_const(
|
||||
pAllocateInfo->pNext,
|
||||
DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO);
|
||||
|
||||
/* allocate a set of buffers for each shader to contain descriptors */
|
||||
for (i = 0; i < pAllocateInfo->descriptorSetCount; i++) {
|
||||
VK_FROM_HANDLE(kk_descriptor_set_layout, layout,
|
||||
pAllocateInfo->pSetLayouts[i]);
|
||||
/* If descriptorSetCount is zero or this structure is not included in
|
||||
* the pNext chain, then the variable lengths are considered to be zero.
|
||||
*/
|
||||
const uint32_t variable_count =
|
||||
var_desc_count && var_desc_count->descriptorSetCount > 0
|
||||
? var_desc_count->pDescriptorCounts[i]
|
||||
: 0;
|
||||
|
||||
result =
|
||||
kk_descriptor_set_create(dev, pool, layout, variable_count, &set);
|
||||
if (result != VK_SUCCESS)
|
||||
break;
|
||||
|
||||
pDescriptorSets[i] = kk_descriptor_set_to_handle(set);
|
||||
}
|
||||
|
||||
if (result != VK_SUCCESS) {
|
||||
kk_FreeDescriptorSets(device, pAllocateInfo->descriptorPool, i,
|
||||
pDescriptorSets);
|
||||
for (i = 0; i < pAllocateInfo->descriptorSetCount; i++) {
|
||||
pDescriptorSets[i] = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkResult VKAPI_CALL
|
||||
kk_FreeDescriptorSets(VkDevice device, VkDescriptorPool descriptorPool,
|
||||
uint32_t descriptorSetCount,
|
||||
const VkDescriptorSet *pDescriptorSets)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_device, dev, device);
|
||||
VK_FROM_HANDLE(kk_descriptor_pool, pool, descriptorPool);
|
||||
|
||||
for (uint32_t i = 0; i < descriptorSetCount; i++) {
|
||||
VK_FROM_HANDLE(kk_descriptor_set, set, pDescriptorSets[i]);
|
||||
|
||||
if (set)
|
||||
kk_descriptor_set_destroy(dev, pool, set);
|
||||
}
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_DestroyDescriptorPool(VkDevice device, VkDescriptorPool _pool,
|
||||
const VkAllocationCallbacks *pAllocator)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_device, dev, device);
|
||||
VK_FROM_HANDLE(kk_descriptor_pool, pool, _pool);
|
||||
|
||||
if (!_pool)
|
||||
return;
|
||||
|
||||
kk_destroy_descriptor_pool(dev, pAllocator, pool);
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkResult VKAPI_CALL
|
||||
kk_ResetDescriptorPool(VkDevice device, VkDescriptorPool descriptorPool,
|
||||
VkDescriptorPoolResetFlags flags)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_device, dev, device);
|
||||
VK_FROM_HANDLE(kk_descriptor_pool, pool, descriptorPool);
|
||||
|
||||
list_for_each_entry_safe(struct kk_descriptor_set, set, &pool->sets, link)
|
||||
kk_descriptor_set_destroy(dev, pool, set);
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
static void
|
||||
kk_descriptor_set_write_template(
|
||||
struct kk_descriptor_set *set,
|
||||
const struct vk_descriptor_update_template *template, const void *data)
|
||||
{
|
||||
for (uint32_t i = 0; i < template->entry_count; i++) {
|
||||
const struct vk_descriptor_template_entry *entry = &template->entries[i];
|
||||
|
||||
switch (entry->type) {
|
||||
case VK_DESCRIPTOR_TYPE_SAMPLER:
|
||||
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
|
||||
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
||||
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
|
||||
for (uint32_t j = 0; j < entry->array_count; j++) {
|
||||
const VkDescriptorImageInfo *info =
|
||||
data + entry->offset + j * entry->stride;
|
||||
|
||||
write_sampled_image_view_desc(set, info, entry->binding,
|
||||
entry->array_element + j,
|
||||
entry->type);
|
||||
}
|
||||
break;
|
||||
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
||||
for (uint32_t j = 0; j < entry->array_count; j++) {
|
||||
const VkDescriptorImageInfo *info =
|
||||
data + entry->offset + j * entry->stride;
|
||||
|
||||
write_storage_image_view_desc(set, info, entry->binding,
|
||||
entry->array_element + j);
|
||||
}
|
||||
break;
|
||||
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
|
||||
for (uint32_t j = 0; j < entry->array_count; j++) {
|
||||
const VkBufferView *bview =
|
||||
data + entry->offset + j * entry->stride;
|
||||
|
||||
write_buffer_view_desc(set, *bview, entry->binding,
|
||||
entry->array_element + j);
|
||||
}
|
||||
break;
|
||||
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
|
||||
for (uint32_t j = 0; j < entry->array_count; j++) {
|
||||
const VkDescriptorBufferInfo *info =
|
||||
data + entry->offset + j * entry->stride;
|
||||
|
||||
write_buffer_desc(set, info, entry->binding,
|
||||
entry->array_element + j);
|
||||
}
|
||||
break;
|
||||
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
|
||||
for (uint32_t j = 0; j < entry->array_count; j++) {
|
||||
const VkDescriptorBufferInfo *info =
|
||||
data + entry->offset + j * entry->stride;
|
||||
|
||||
write_dynamic_buffer_desc(set, info, entry->binding,
|
||||
entry->array_element + j);
|
||||
}
|
||||
break;
|
||||
|
||||
case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK:
|
||||
write_desc(set, entry->binding, entry->array_element,
|
||||
data + entry->offset, entry->array_count);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_UpdateDescriptorSetWithTemplate(
|
||||
VkDevice device, VkDescriptorSet descriptorSet,
|
||||
VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void *pData)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_descriptor_set, set, descriptorSet);
|
||||
VK_FROM_HANDLE(vk_descriptor_update_template, template,
|
||||
descriptorUpdateTemplate);
|
||||
|
||||
kk_descriptor_set_write_template(set, template, pData);
|
||||
}
|
||||
|
||||
void
|
||||
kk_push_descriptor_set_update_template(
|
||||
struct kk_push_descriptor_set *push_set,
|
||||
struct kk_descriptor_set_layout *layout,
|
||||
const struct vk_descriptor_update_template *template, const void *data)
|
||||
{
|
||||
struct kk_descriptor_set tmp_set = {
|
||||
.layout = layout,
|
||||
.size = sizeof(push_set->data),
|
||||
.mapped_ptr = push_set->data,
|
||||
};
|
||||
kk_descriptor_set_write_template(&tmp_set, template, data);
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright © 2022 Collabora Ltd. and Red Hat Inc.
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef KK_DESCRIPTOR_SET
|
||||
#define KK_DESCRIPTOR_SET 1
|
||||
|
||||
#include "kk_private.h"
|
||||
|
||||
#include "kk_descriptor_types.h"
|
||||
#include "kk_device.h"
|
||||
|
||||
#include "vk_descriptor_update_template.h"
|
||||
#include "vk_object.h"
|
||||
|
||||
#include "util/list.h"
|
||||
#include "util/vma.h"
|
||||
|
||||
struct kk_descriptor_set_layout;
|
||||
struct kk_bo;
|
||||
|
||||
struct kk_descriptor_pool {
|
||||
struct vk_object_base base;
|
||||
|
||||
struct list_head sets;
|
||||
|
||||
struct kk_bo *bo;
|
||||
struct util_vma_heap heap;
|
||||
};
|
||||
|
||||
VK_DEFINE_NONDISP_HANDLE_CASTS(kk_descriptor_pool, base, VkDescriptorPool,
|
||||
VK_OBJECT_TYPE_DESCRIPTOR_POOL)
|
||||
|
||||
struct kk_descriptor_set {
|
||||
struct vk_object_base base;
|
||||
|
||||
/* Link in kk_descriptor_pool::sets */
|
||||
struct list_head link;
|
||||
|
||||
struct kk_descriptor_set_layout *layout;
|
||||
void *mapped_ptr;
|
||||
uint64_t addr;
|
||||
uint32_t size;
|
||||
|
||||
struct kk_buffer_address dynamic_buffers[];
|
||||
};
|
||||
|
||||
VK_DEFINE_NONDISP_HANDLE_CASTS(kk_descriptor_set, base, VkDescriptorSet,
|
||||
VK_OBJECT_TYPE_DESCRIPTOR_SET)
|
||||
|
||||
static inline struct kk_buffer_address
|
||||
kk_descriptor_set_addr(const struct kk_descriptor_set *set)
|
||||
{
|
||||
return (struct kk_buffer_address){
|
||||
.base_addr = set->addr,
|
||||
.size = set->size,
|
||||
};
|
||||
}
|
||||
|
||||
struct kk_push_descriptor_set {
|
||||
uint8_t data[KK_PUSH_DESCRIPTOR_SET_SIZE];
|
||||
struct kk_descriptor_set_layout *layout;
|
||||
};
|
||||
|
||||
void kk_push_descriptor_set_update(struct kk_push_descriptor_set *push_set,
|
||||
uint32_t write_count,
|
||||
const VkWriteDescriptorSet *writes);
|
||||
|
||||
void kk_push_descriptor_set_update_template(
|
||||
struct kk_push_descriptor_set *push_set,
|
||||
struct kk_descriptor_set_layout *layout,
|
||||
const struct vk_descriptor_update_template *template, const void *data);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,507 @@
|
||||
/*
|
||||
* Copyright © 2022 Collabora Ltd. and Red Hat Inc.
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "kk_descriptor_set_layout.h"
|
||||
|
||||
#include "kk_descriptor_types.h"
|
||||
#include "kk_device.h"
|
||||
#include "kk_entrypoints.h"
|
||||
#include "kk_physical_device.h"
|
||||
#include "kk_sampler.h"
|
||||
|
||||
#include "vk_pipeline_layout.h"
|
||||
|
||||
static bool
|
||||
binding_has_immutable_samplers(const VkDescriptorSetLayoutBinding *binding)
|
||||
{
|
||||
switch (binding->descriptorType) {
|
||||
case VK_DESCRIPTOR_TYPE_SAMPLER:
|
||||
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
|
||||
return binding->pImmutableSamplers != NULL;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
kk_descriptor_stride_align_for_type(
|
||||
VkDescriptorType type, const VkMutableDescriptorTypeListEXT *type_list,
|
||||
uint32_t *stride, uint32_t *alignment)
|
||||
{
|
||||
switch (type) {
|
||||
case VK_DESCRIPTOR_TYPE_SAMPLER:
|
||||
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
|
||||
/* TODO: How do samplers work? */
|
||||
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
||||
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
|
||||
*stride = *alignment = sizeof(struct kk_sampled_image_descriptor);
|
||||
break;
|
||||
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
||||
*stride = *alignment = sizeof(struct kk_storage_image_descriptor);
|
||||
break;
|
||||
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
|
||||
*stride = *alignment = sizeof(struct kk_buffer_address);
|
||||
break;
|
||||
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
|
||||
*stride = *alignment = 0; /* These don't take up buffer space */
|
||||
break;
|
||||
|
||||
case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK:
|
||||
*stride = 1; /* Array size is bytes */
|
||||
*alignment = kk_min_cbuf_alignment();
|
||||
break;
|
||||
|
||||
case VK_DESCRIPTOR_TYPE_MUTABLE_EXT:
|
||||
*stride = *alignment = 0;
|
||||
if (type_list == NULL || type_list->descriptorTypeCount == 0u)
|
||||
*stride = *alignment = KK_MAX_DESCRIPTOR_SIZE;
|
||||
for (unsigned i = 0; type_list && i < type_list->descriptorTypeCount;
|
||||
i++) {
|
||||
/* This shouldn't recurse */
|
||||
assert(type_list->pDescriptorTypes[i] !=
|
||||
VK_DESCRIPTOR_TYPE_MUTABLE_EXT);
|
||||
uint32_t desc_stride, desc_align;
|
||||
kk_descriptor_stride_align_for_type(type_list->pDescriptorTypes[i],
|
||||
NULL, &desc_stride, &desc_align);
|
||||
*stride = MAX2(*stride, desc_stride);
|
||||
*alignment = MAX2(*alignment, desc_align);
|
||||
}
|
||||
*stride = align(*stride, *alignment);
|
||||
break;
|
||||
|
||||
default:
|
||||
UNREACHABLE("Invalid descriptor type");
|
||||
}
|
||||
|
||||
assert(*stride <= KK_MAX_DESCRIPTOR_SIZE);
|
||||
}
|
||||
|
||||
static const VkMutableDescriptorTypeListEXT *
|
||||
kk_descriptor_get_type_list(VkDescriptorType type,
|
||||
const VkMutableDescriptorTypeCreateInfoEXT *info,
|
||||
const uint32_t info_idx)
|
||||
{
|
||||
const VkMutableDescriptorTypeListEXT *type_list = NULL;
|
||||
if (type == VK_DESCRIPTOR_TYPE_MUTABLE_EXT) {
|
||||
assert(info != NULL);
|
||||
assert(info_idx < info->mutableDescriptorTypeListCount);
|
||||
type_list = &info->pMutableDescriptorTypeLists[info_idx];
|
||||
}
|
||||
return type_list;
|
||||
}
|
||||
|
||||
static void
|
||||
kk_descriptor_set_layout_destroy(struct vk_device *vk_dev,
|
||||
struct vk_descriptor_set_layout *vk_layout)
|
||||
{
|
||||
struct kk_device *dev = container_of(vk_dev, struct kk_device, vk);
|
||||
struct kk_descriptor_set_layout *layout =
|
||||
vk_to_kk_descriptor_set_layout(vk_layout);
|
||||
|
||||
vk_object_free(&dev->vk, NULL, layout);
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkResult VKAPI_CALL
|
||||
kk_CreateDescriptorSetLayout(VkDevice device,
|
||||
const VkDescriptorSetLayoutCreateInfo *pCreateInfo,
|
||||
const VkAllocationCallbacks *pAllocator,
|
||||
VkDescriptorSetLayout *pSetLayout)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_device, dev, device);
|
||||
|
||||
uint32_t num_bindings = 0;
|
||||
uint32_t immutable_sampler_count = 0;
|
||||
for (uint32_t j = 0; j < pCreateInfo->bindingCount; j++) {
|
||||
const VkDescriptorSetLayoutBinding *binding = &pCreateInfo->pBindings[j];
|
||||
num_bindings = MAX2(num_bindings, binding->binding + 1);
|
||||
|
||||
/* From the Vulkan 1.1.97 spec for VkDescriptorSetLayoutBinding:
|
||||
*
|
||||
* "If descriptorType specifies a VK_DESCRIPTOR_TYPE_SAMPLER or
|
||||
* VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER type descriptor, then
|
||||
* pImmutableSamplers can be used to initialize a set of immutable
|
||||
* samplers. [...] If descriptorType is not one of these descriptor
|
||||
* types, then pImmutableSamplers is ignored.
|
||||
*
|
||||
* We need to be careful here and only parse pImmutableSamplers if we
|
||||
* have one of the right descriptor types.
|
||||
*/
|
||||
if (binding_has_immutable_samplers(binding))
|
||||
immutable_sampler_count += binding->descriptorCount;
|
||||
}
|
||||
|
||||
VK_MULTIALLOC(ma);
|
||||
VK_MULTIALLOC_DECL(&ma, struct kk_descriptor_set_layout, layout, 1);
|
||||
VK_MULTIALLOC_DECL(&ma, struct kk_descriptor_set_binding_layout, bindings,
|
||||
num_bindings);
|
||||
VK_MULTIALLOC_DECL(&ma, struct kk_sampler *, samplers,
|
||||
immutable_sampler_count);
|
||||
|
||||
if (!vk_descriptor_set_layout_multizalloc(&dev->vk, &ma, pCreateInfo))
|
||||
return vk_error(dev, VK_ERROR_OUT_OF_HOST_MEMORY);
|
||||
|
||||
layout->vk.destroy = kk_descriptor_set_layout_destroy;
|
||||
layout->flags = pCreateInfo->flags;
|
||||
layout->binding_count = num_bindings;
|
||||
|
||||
for (uint32_t j = 0; j < pCreateInfo->bindingCount; j++) {
|
||||
const VkDescriptorSetLayoutBinding *binding = &pCreateInfo->pBindings[j];
|
||||
uint32_t b = binding->binding;
|
||||
/* We temporarily store pCreateInfo->pBindings[] index (plus one) in the
|
||||
* immutable_samplers pointer. This provides us with a quick-and-dirty
|
||||
* way to sort the bindings by binding number.
|
||||
*/
|
||||
layout->binding[b].immutable_samplers = (void *)(uintptr_t)(j + 1);
|
||||
}
|
||||
|
||||
const VkDescriptorSetLayoutBindingFlagsCreateInfo *binding_flags_info =
|
||||
vk_find_struct_const(pCreateInfo->pNext,
|
||||
DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO);
|
||||
const VkMutableDescriptorTypeCreateInfoEXT *mutable_info =
|
||||
vk_find_struct_const(pCreateInfo->pNext,
|
||||
MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_EXT);
|
||||
|
||||
uint32_t buffer_size = 0;
|
||||
uint32_t max_variable_descriptor_size = 0;
|
||||
uint8_t dynamic_buffer_count = 0;
|
||||
for (uint32_t b = 0; b < num_bindings; b++) {
|
||||
/* We stashed the pCreateInfo->pBindings[] index (plus one) in the
|
||||
* immutable_samplers pointer. Check for NULL (empty binding) and then
|
||||
* reset it and compute the index.
|
||||
*/
|
||||
if (layout->binding[b].immutable_samplers == NULL)
|
||||
continue;
|
||||
const uint32_t info_idx =
|
||||
(uintptr_t)(void *)layout->binding[b].immutable_samplers - 1;
|
||||
layout->binding[b].immutable_samplers = NULL;
|
||||
|
||||
const VkDescriptorSetLayoutBinding *binding =
|
||||
&pCreateInfo->pBindings[info_idx];
|
||||
|
||||
if (binding->descriptorCount == 0)
|
||||
continue;
|
||||
|
||||
layout->binding[b].type = binding->descriptorType;
|
||||
layout->descriptor_count += binding->descriptorCount;
|
||||
|
||||
if (binding_flags_info && binding_flags_info->bindingCount > 0) {
|
||||
assert(binding_flags_info->bindingCount == pCreateInfo->bindingCount);
|
||||
layout->binding[b].flags = binding_flags_info->pBindingFlags[info_idx];
|
||||
}
|
||||
|
||||
layout->binding[b].array_size = binding->descriptorCount;
|
||||
|
||||
switch (binding->descriptorType) {
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
|
||||
layout->binding[b].dynamic_buffer_index = dynamic_buffer_count;
|
||||
BITSET_SET_COUNT(layout->dynamic_ubos, dynamic_buffer_count,
|
||||
binding->descriptorCount);
|
||||
dynamic_buffer_count += binding->descriptorCount;
|
||||
break;
|
||||
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
|
||||
layout->binding[b].dynamic_buffer_index = dynamic_buffer_count;
|
||||
dynamic_buffer_count += binding->descriptorCount;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const VkMutableDescriptorTypeListEXT *type_list =
|
||||
kk_descriptor_get_type_list(binding->descriptorType, mutable_info,
|
||||
info_idx);
|
||||
|
||||
uint32_t stride, alignment;
|
||||
kk_descriptor_stride_align_for_type(binding->descriptorType, type_list,
|
||||
&stride, &alignment);
|
||||
|
||||
uint8_t max_plane_count = 1;
|
||||
|
||||
if (binding_has_immutable_samplers(binding)) {
|
||||
layout->binding[b].immutable_samplers = samplers;
|
||||
samplers += binding->descriptorCount;
|
||||
for (uint32_t i = 0; i < binding->descriptorCount; i++) {
|
||||
VK_FROM_HANDLE(kk_sampler, sampler, binding->pImmutableSamplers[i]);
|
||||
layout->binding[b].immutable_samplers[i] = sampler;
|
||||
const uint8_t sampler_plane_count =
|
||||
sampler->vk.ycbcr_conversion
|
||||
? vk_format_get_plane_count(
|
||||
sampler->vk.ycbcr_conversion->state.format)
|
||||
: 1;
|
||||
if (max_plane_count < sampler_plane_count)
|
||||
max_plane_count = sampler_plane_count;
|
||||
}
|
||||
}
|
||||
|
||||
stride *= max_plane_count;
|
||||
|
||||
if (stride > 0) {
|
||||
assert(stride <= UINT8_MAX);
|
||||
assert(util_is_power_of_two_nonzero(alignment));
|
||||
|
||||
buffer_size = align64(buffer_size, alignment);
|
||||
layout->binding[b].offset = buffer_size;
|
||||
layout->binding[b].stride = stride;
|
||||
|
||||
if (layout->binding[b].flags &
|
||||
VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT) {
|
||||
/* From the Vulkan 1.3.256 spec:
|
||||
*
|
||||
* VUID-VkDescriptorSetLayoutBindingFlagsCreateInfo-pBindingFlags-03004
|
||||
* "If an element of pBindingFlags includes
|
||||
* VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT, then
|
||||
* all other elements of
|
||||
* VkDescriptorSetLayoutCreateInfo::pBindings must have a
|
||||
* smaller value of binding"
|
||||
*
|
||||
* In other words, it has to be the last binding.
|
||||
*/
|
||||
assert(b == num_bindings - 1);
|
||||
assert(max_variable_descriptor_size == 0);
|
||||
max_variable_descriptor_size = stride * binding->descriptorCount;
|
||||
} else {
|
||||
/* the allocation size will be computed at descriptor allocation,
|
||||
* but the buffer size will be already aligned as this binding will
|
||||
* be the last
|
||||
*/
|
||||
buffer_size += stride * binding->descriptorCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layout->non_variable_descriptor_buffer_size = buffer_size;
|
||||
layout->max_buffer_size = buffer_size + max_variable_descriptor_size;
|
||||
layout->vk.dynamic_descriptor_count = dynamic_buffer_count;
|
||||
|
||||
struct mesa_blake3 blake3_ctx;
|
||||
_mesa_blake3_init(&blake3_ctx);
|
||||
|
||||
#define BLAKE3_UPDATE_VALUE(x) \
|
||||
_mesa_blake3_update(&blake3_ctx, &(x), sizeof(x));
|
||||
BLAKE3_UPDATE_VALUE(layout->non_variable_descriptor_buffer_size);
|
||||
BLAKE3_UPDATE_VALUE(layout->vk.dynamic_descriptor_count);
|
||||
BLAKE3_UPDATE_VALUE(layout->binding_count);
|
||||
|
||||
for (uint32_t b = 0; b < num_bindings; b++) {
|
||||
BLAKE3_UPDATE_VALUE(layout->binding[b].type);
|
||||
BLAKE3_UPDATE_VALUE(layout->binding[b].flags);
|
||||
BLAKE3_UPDATE_VALUE(layout->binding[b].array_size);
|
||||
BLAKE3_UPDATE_VALUE(layout->binding[b].offset);
|
||||
BLAKE3_UPDATE_VALUE(layout->binding[b].stride);
|
||||
BLAKE3_UPDATE_VALUE(layout->binding[b].dynamic_buffer_index);
|
||||
|
||||
if (layout->binding[b].immutable_samplers != NULL) {
|
||||
for (uint32_t i = 0; i < layout->binding[b].array_size; i++) {
|
||||
const struct kk_sampler *sampler =
|
||||
layout->binding[b].immutable_samplers[i];
|
||||
|
||||
/* We zalloc the object, so it's safe to hash the whole thing */
|
||||
if (sampler != NULL && sampler->vk.ycbcr_conversion != NULL)
|
||||
BLAKE3_UPDATE_VALUE(sampler->vk.ycbcr_conversion->state);
|
||||
}
|
||||
}
|
||||
}
|
||||
#undef BLAKE3_UPDATE_VALUE
|
||||
|
||||
_mesa_blake3_final(&blake3_ctx, layout->vk.blake3);
|
||||
|
||||
if (pCreateInfo->flags &
|
||||
VK_DESCRIPTOR_SET_LAYOUT_CREATE_EMBEDDED_IMMUTABLE_SAMPLERS_BIT_EXT) {
|
||||
void *sampler_desc_data =
|
||||
vk_alloc2(&dev->vk.alloc, pAllocator, buffer_size, 4,
|
||||
VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
|
||||
if (sampler_desc_data == NULL) {
|
||||
kk_descriptor_set_layout_destroy(&dev->vk, &layout->vk);
|
||||
return vk_error(dev, VK_ERROR_OUT_OF_HOST_MEMORY);
|
||||
}
|
||||
|
||||
for (uint32_t b = 0; b < num_bindings; b++) {
|
||||
assert(layout->binding[b].type == VK_DESCRIPTOR_TYPE_SAMPLER);
|
||||
assert(layout->binding[b].array_size == 1);
|
||||
assert(layout->binding[b].immutable_samplers != NULL);
|
||||
assert(!(layout->binding[b].flags &
|
||||
VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT));
|
||||
|
||||
/* I'm paranoid */
|
||||
if (layout->binding[b].immutable_samplers == NULL)
|
||||
continue;
|
||||
|
||||
struct kk_sampler *sampler = layout->binding[b].immutable_samplers[0];
|
||||
|
||||
/* YCbCr has to come in through a combined image/sampler */
|
||||
assert(sampler->plane_count == 1);
|
||||
|
||||
assert(sampler->planes[0].hw->handle);
|
||||
}
|
||||
|
||||
vk_free2(&dev->vk.alloc, pAllocator, sampler_desc_data);
|
||||
}
|
||||
|
||||
*pSetLayout = kk_descriptor_set_layout_to_handle(layout);
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_GetDescriptorSetLayoutSupport(
|
||||
VkDevice device, const VkDescriptorSetLayoutCreateInfo *pCreateInfo,
|
||||
VkDescriptorSetLayoutSupport *pSupport)
|
||||
{
|
||||
const VkMutableDescriptorTypeCreateInfoEXT *mutable_info =
|
||||
vk_find_struct_const(pCreateInfo->pNext,
|
||||
MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_EXT);
|
||||
const VkDescriptorSetLayoutBindingFlagsCreateInfo *binding_flags =
|
||||
vk_find_struct_const(pCreateInfo->pNext,
|
||||
DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO);
|
||||
|
||||
/* Figure out the maximum alignment up-front. Otherwise, we need to sort
|
||||
* the list of descriptors by binding number in order to get the size
|
||||
* accumulation right.
|
||||
*/
|
||||
uint32_t max_align = 0;
|
||||
for (uint32_t i = 0; i < pCreateInfo->bindingCount; i++) {
|
||||
const VkDescriptorSetLayoutBinding *binding = &pCreateInfo->pBindings[i];
|
||||
const VkMutableDescriptorTypeListEXT *type_list =
|
||||
kk_descriptor_get_type_list(binding->descriptorType, mutable_info, i);
|
||||
|
||||
uint32_t stride, alignment;
|
||||
kk_descriptor_stride_align_for_type(binding->descriptorType, type_list,
|
||||
&stride, &alignment);
|
||||
max_align = MAX2(max_align, alignment);
|
||||
}
|
||||
|
||||
uint64_t non_variable_size = 0;
|
||||
uint32_t variable_stride = 0;
|
||||
uint32_t variable_count = 0;
|
||||
uint8_t dynamic_buffer_count = 0;
|
||||
bool variable_is_inline_uniform_block = false;
|
||||
|
||||
for (uint32_t i = 0; i < pCreateInfo->bindingCount; i++) {
|
||||
const VkDescriptorSetLayoutBinding *binding = &pCreateInfo->pBindings[i];
|
||||
|
||||
VkDescriptorBindingFlags flags = 0;
|
||||
if (binding_flags != NULL && binding_flags->bindingCount > 0)
|
||||
flags = binding_flags->pBindingFlags[i];
|
||||
|
||||
switch (binding->descriptorType) {
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
|
||||
dynamic_buffer_count += binding->descriptorCount;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const VkMutableDescriptorTypeListEXT *type_list =
|
||||
kk_descriptor_get_type_list(binding->descriptorType, mutable_info, i);
|
||||
|
||||
uint32_t stride, alignment;
|
||||
kk_descriptor_stride_align_for_type(binding->descriptorType, type_list,
|
||||
&stride, &alignment);
|
||||
|
||||
if (stride > 0) {
|
||||
assert(stride <= UINT8_MAX);
|
||||
assert(util_is_power_of_two_nonzero(alignment));
|
||||
|
||||
if (flags & VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT) {
|
||||
/* From the Vulkan 1.3.256 spec:
|
||||
*
|
||||
* "For the purposes of this command, a variable-sized
|
||||
* descriptor binding with a descriptorCount of zero is treated
|
||||
* as if the descriptorCount is one"
|
||||
*/
|
||||
variable_count = MAX2(1, binding->descriptorCount);
|
||||
variable_stride = stride;
|
||||
|
||||
variable_is_inline_uniform_block =
|
||||
binding->descriptorType ==
|
||||
VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK;
|
||||
|
||||
} else {
|
||||
/* Since we're aligning to the maximum and since this is just a
|
||||
* check for whether or not the max buffer size is big enough, we
|
||||
* keep non_variable_size aligned to max_align.
|
||||
*/
|
||||
non_variable_size += stride * binding->descriptorCount;
|
||||
non_variable_size = align64(non_variable_size, max_align);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t buffer_size = non_variable_size;
|
||||
if (variable_stride > 0) {
|
||||
buffer_size += variable_stride * variable_count;
|
||||
buffer_size = align64(buffer_size, max_align);
|
||||
}
|
||||
|
||||
uint32_t max_buffer_size;
|
||||
if (pCreateInfo->flags &
|
||||
VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR)
|
||||
max_buffer_size = KK_PUSH_DESCRIPTOR_SET_SIZE;
|
||||
else
|
||||
max_buffer_size = KK_MAX_DESCRIPTOR_SET_SIZE;
|
||||
|
||||
pSupport->supported = dynamic_buffer_count <= KK_MAX_DYNAMIC_BUFFERS &&
|
||||
buffer_size <= max_buffer_size;
|
||||
|
||||
vk_foreach_struct(ext, pSupport->pNext) {
|
||||
switch (ext->sType) {
|
||||
case VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_LAYOUT_SUPPORT: {
|
||||
VkDescriptorSetVariableDescriptorCountLayoutSupport *vs = (void *)ext;
|
||||
uint32_t max_var_count;
|
||||
|
||||
if (variable_stride > 0) {
|
||||
max_var_count =
|
||||
(max_buffer_size - non_variable_size) / variable_stride;
|
||||
} else {
|
||||
max_var_count = 0;
|
||||
}
|
||||
|
||||
if (variable_is_inline_uniform_block) {
|
||||
max_var_count =
|
||||
MIN2(max_var_count, KK_MAX_INLINE_UNIFORM_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
vs->maxVariableDescriptorCount = max_var_count;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
vk_debug_ignored_stype(ext->sType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_GetDescriptorSetLayoutSizeEXT(VkDevice device, VkDescriptorSetLayout _layout,
|
||||
VkDeviceSize *pLayoutSizeInBytes)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_descriptor_set_layout, layout, _layout);
|
||||
|
||||
*pLayoutSizeInBytes = layout->max_buffer_size;
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_GetDescriptorSetLayoutBindingOffsetEXT(VkDevice device,
|
||||
VkDescriptorSetLayout _layout,
|
||||
uint32_t binding,
|
||||
VkDeviceSize *pOffset)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_descriptor_set_layout, layout, _layout);
|
||||
|
||||
*pOffset = layout->binding[binding].offset;
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright © 2022 Collabora Ltd. and Red Hat Inc.
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef KK_DESCRIPTOR_SET_LAYOUT
|
||||
#define KK_DESCRIPTOR_SET_LAYOUT 1
|
||||
|
||||
#include "kk_private.h"
|
||||
|
||||
#include "vk_descriptor_set_layout.h"
|
||||
#include "vk_object.h"
|
||||
|
||||
#include "util/bitset.h"
|
||||
|
||||
struct kk_device;
|
||||
struct kk_physical_device;
|
||||
struct kk_sampler;
|
||||
struct vk_pipeline_layout;
|
||||
|
||||
struct kk_descriptor_set_binding_layout {
|
||||
/* The type of the descriptors in this binding */
|
||||
VkDescriptorType type;
|
||||
|
||||
/* Flags provided when this binding was created */
|
||||
VkDescriptorBindingFlags flags;
|
||||
|
||||
/* Number of array elements in this binding (or size in bytes for inline
|
||||
* uniform data)
|
||||
*/
|
||||
uint32_t array_size;
|
||||
|
||||
/* Offset into the descriptor buffer where this descriptor lives */
|
||||
uint32_t offset;
|
||||
|
||||
/* Stride between array elements in the descriptor buffer */
|
||||
uint8_t stride;
|
||||
|
||||
/* Index into the dynamic buffer binding array */
|
||||
uint8_t dynamic_buffer_index;
|
||||
|
||||
/* Immutable samplers (or NULL if no immutable samplers) */
|
||||
struct kk_sampler **immutable_samplers;
|
||||
};
|
||||
|
||||
struct kk_descriptor_set_layout {
|
||||
struct vk_descriptor_set_layout vk;
|
||||
|
||||
VkDescriptorSetLayoutCreateFlagBits flags;
|
||||
|
||||
/* Size of the descriptor buffer for this descriptor set */
|
||||
/* Does not contain the size needed for variable count descriptors */
|
||||
uint32_t non_variable_descriptor_buffer_size;
|
||||
|
||||
/* Maximum possible buffer size for this descriptor set */
|
||||
uint32_t max_buffer_size;
|
||||
|
||||
/* Which dynamic buffers are UBOs */
|
||||
BITSET_DECLARE(dynamic_ubos, KK_MAX_DYNAMIC_BUFFERS);
|
||||
|
||||
/* Number of bindings in this descriptor set */
|
||||
uint32_t binding_count;
|
||||
|
||||
/* Number of descriptors in the layout */
|
||||
uint32_t descriptor_count;
|
||||
|
||||
/* Address to the embedded sampler descriptor buffer.
|
||||
*
|
||||
* This is allocated from nvk_device::heap and has the size
|
||||
* non_variable_descriptor_buffer_size.
|
||||
*/
|
||||
uint64_t embedded_samplers_addr;
|
||||
|
||||
/* Bindings in this descriptor set */
|
||||
struct kk_descriptor_set_binding_layout binding[0];
|
||||
};
|
||||
|
||||
VK_DEFINE_NONDISP_HANDLE_CASTS(kk_descriptor_set_layout, vk.base,
|
||||
VkDescriptorSetLayout,
|
||||
VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT)
|
||||
|
||||
void kk_descriptor_stride_align_for_type(
|
||||
VkDescriptorType type, const VkMutableDescriptorTypeListEXT *type_list,
|
||||
uint32_t *stride, uint32_t *alignment);
|
||||
|
||||
static inline struct kk_descriptor_set_layout *
|
||||
vk_to_kk_descriptor_set_layout(struct vk_descriptor_set_layout *layout)
|
||||
{
|
||||
return container_of(layout, struct kk_descriptor_set_layout, vk);
|
||||
}
|
||||
|
||||
#endif /* KK_DESCRIPTOR_SET_LAYOUT */
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright © 2024 Collabora Ltd. and Red Hat Inc.
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef KK_DESCRIPTOR_TYPES
|
||||
#define KK_DESCRIPTOR_TYPES 1
|
||||
|
||||
#include "kk_private.h"
|
||||
|
||||
/* TODO_KOSMICKRISP Reduce size to 32 bytes by moving border to a heap. */
|
||||
struct kk_sampled_image_descriptor {
|
||||
uint64_t image_gpu_resource_id;
|
||||
uint16_t sampler_index;
|
||||
uint16_t lod_bias_fp16;
|
||||
uint16_t lod_min_fp16;
|
||||
uint16_t lod_max_fp16;
|
||||
uint32_t has_border;
|
||||
uint32_t pad_to_64_bits;
|
||||
uint32_t border[4];
|
||||
uint64_t pad_to_power_2[3];
|
||||
};
|
||||
|
||||
static_assert(sizeof(struct kk_sampled_image_descriptor) == 64,
|
||||
"kk_sampled_image_descriptor has no holes");
|
||||
|
||||
struct kk_storage_image_descriptor {
|
||||
uint64_t image_gpu_resource_id;
|
||||
};
|
||||
|
||||
static_assert(sizeof(struct kk_storage_image_descriptor) == 8,
|
||||
"kk_storage_image_descriptor has no holes");
|
||||
|
||||
/* This has to match nir_address_format_64bit_bounded_global */
|
||||
struct kk_buffer_address {
|
||||
uint64_t base_addr;
|
||||
uint32_t size;
|
||||
uint32_t zero; /* Must be zero! */
|
||||
};
|
||||
|
||||
static_assert(sizeof(struct kk_buffer_address) == 16,
|
||||
"kk_buffer_address has no holes");
|
||||
|
||||
#endif /* KK_DESCRIPTOR_TYPES */
|
||||
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
* Copyright © 2022 Collabora Ltd. and Red Hat Inc.
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "kk_device.h"
|
||||
|
||||
#include "kk_cmd_buffer.h"
|
||||
#include "kk_entrypoints.h"
|
||||
#include "kk_instance.h"
|
||||
#include "kk_physical_device.h"
|
||||
#include "kk_shader.h"
|
||||
|
||||
#include "kosmickrisp/bridge/mtl_bridge.h"
|
||||
|
||||
#include "vk_cmd_enqueue_entrypoints.h"
|
||||
#include "vk_common_entrypoints.h"
|
||||
|
||||
#include "vulkan/wsi/wsi_common.h"
|
||||
#include "vk_pipeline_cache.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
DERIVE_HASH_TABLE(mtl_sampler_packed);
|
||||
|
||||
static VkResult
|
||||
kk_init_sampler_heap(struct kk_device *dev, struct kk_sampler_heap *h)
|
||||
{
|
||||
h->ht = mtl_sampler_packed_table_create(NULL);
|
||||
if (!h->ht)
|
||||
return VK_ERROR_OUT_OF_HOST_MEMORY;
|
||||
|
||||
VkResult result = kk_query_table_init(dev, &h->table, 1024);
|
||||
|
||||
if (result != VK_SUCCESS) {
|
||||
ralloc_free(h->ht);
|
||||
return result;
|
||||
}
|
||||
|
||||
simple_mtx_init(&h->lock, mtx_plain);
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
static void
|
||||
kk_destroy_sampler_heap(struct kk_device *dev, struct kk_sampler_heap *h)
|
||||
{
|
||||
struct hash_entry *entry = _mesa_hash_table_next_entry(h->ht, NULL);
|
||||
while (entry) {
|
||||
struct kk_rc_sampler *sampler = (struct kk_rc_sampler *)entry->data;
|
||||
mtl_release(sampler->handle);
|
||||
entry = _mesa_hash_table_next_entry(h->ht, entry);
|
||||
}
|
||||
kk_query_table_finish(dev, &h->table);
|
||||
ralloc_free(h->ht);
|
||||
simple_mtx_destroy(&h->lock);
|
||||
}
|
||||
|
||||
static VkResult
|
||||
kk_sampler_heap_add_locked(struct kk_device *dev, struct kk_sampler_heap *h,
|
||||
struct mtl_sampler_packed desc,
|
||||
struct kk_rc_sampler **out)
|
||||
{
|
||||
struct hash_entry *ent = _mesa_hash_table_search(h->ht, &desc);
|
||||
if (ent != NULL) {
|
||||
*out = ent->data;
|
||||
|
||||
assert((*out)->refcount != 0);
|
||||
(*out)->refcount++;
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
struct kk_rc_sampler *rc = ralloc(h->ht, struct kk_rc_sampler);
|
||||
if (!rc)
|
||||
return VK_ERROR_OUT_OF_HOST_MEMORY;
|
||||
|
||||
mtl_sampler *handle = kk_sampler_create(dev, &desc);
|
||||
uint64_t gpu_id = mtl_sampler_get_gpu_resource_id(handle);
|
||||
|
||||
uint32_t index;
|
||||
VkResult result = kk_query_table_add(dev, &h->table, gpu_id, &index);
|
||||
if (result != VK_SUCCESS) {
|
||||
mtl_release(handle);
|
||||
ralloc_free(rc);
|
||||
return result;
|
||||
}
|
||||
|
||||
*rc = (struct kk_rc_sampler){
|
||||
.key = desc,
|
||||
.handle = handle,
|
||||
.refcount = 1,
|
||||
.index = index,
|
||||
};
|
||||
|
||||
_mesa_hash_table_insert(h->ht, &rc->key, rc);
|
||||
*out = rc;
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
VkResult
|
||||
kk_sampler_heap_add(struct kk_device *dev, struct mtl_sampler_packed desc,
|
||||
struct kk_rc_sampler **out)
|
||||
{
|
||||
struct kk_sampler_heap *h = &dev->samplers;
|
||||
|
||||
simple_mtx_lock(&h->lock);
|
||||
VkResult result = kk_sampler_heap_add_locked(dev, h, desc, out);
|
||||
simple_mtx_unlock(&h->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
kk_sampler_heap_remove_locked(struct kk_device *dev, struct kk_sampler_heap *h,
|
||||
struct kk_rc_sampler *rc)
|
||||
{
|
||||
assert(rc->refcount != 0);
|
||||
rc->refcount--;
|
||||
|
||||
if (rc->refcount == 0) {
|
||||
mtl_release(rc->handle);
|
||||
kk_query_table_remove(dev, &h->table, rc->index);
|
||||
_mesa_hash_table_remove_key(h->ht, &rc->key);
|
||||
ralloc_free(rc);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
kk_sampler_heap_remove(struct kk_device *dev, struct kk_rc_sampler *rc)
|
||||
{
|
||||
struct kk_sampler_heap *h = &dev->samplers;
|
||||
|
||||
simple_mtx_lock(&h->lock);
|
||||
kk_sampler_heap_remove_locked(dev, h, rc);
|
||||
simple_mtx_unlock(&h->lock);
|
||||
}
|
||||
|
||||
static void
|
||||
kk_parse_device_environment_options(struct kk_device *dev)
|
||||
{
|
||||
dev->gpu_capture_enabled =
|
||||
debug_get_bool_option("MESA_KK_GPU_CAPTURE", false);
|
||||
if (dev->gpu_capture_enabled) {
|
||||
const char *capture_directory =
|
||||
debug_get_option("MESA_KK_GPU_CAPTURE_DIRECTORY", NULL);
|
||||
mtl_start_gpu_capture(dev->mtl_handle, capture_directory);
|
||||
}
|
||||
|
||||
const char *list = debug_get_option("MESA_KK_DISABLE_WORKAROUNDS", "");
|
||||
const char *all_workarounds = "all";
|
||||
const size_t all_len = strlen(all_workarounds);
|
||||
for (unsigned n; n = strcspn(list, ","), *list; list += MAX2(1, n)) {
|
||||
if (n == all_len && !strncmp(list, all_workarounds, n)) {
|
||||
dev->disabled_workarounds = UINT64_MAX;
|
||||
break;
|
||||
}
|
||||
|
||||
int index = atoi(list);
|
||||
dev->disabled_workarounds |= BITFIELD64_BIT(index);
|
||||
}
|
||||
}
|
||||
|
||||
static VkResult
|
||||
kk_get_timestamp(struct vk_device *device, uint64_t *timestamp)
|
||||
{
|
||||
struct kk_device *dev = container_of(device, struct kk_device, vk);
|
||||
|
||||
*timestamp = mtl_device_get_gpu_timestamp(dev->mtl_handle);
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkResult VKAPI_CALL
|
||||
kk_CreateDevice(VkPhysicalDevice physicalDevice,
|
||||
const VkDeviceCreateInfo *pCreateInfo,
|
||||
const VkAllocationCallbacks *pAllocator, VkDevice *pDevice)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_physical_device, pdev, physicalDevice);
|
||||
VkResult result = VK_ERROR_OUT_OF_HOST_MEMORY;
|
||||
struct kk_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);
|
||||
|
||||
/* Fill the dispatch table we will expose to the users */
|
||||
vk_device_dispatch_table_from_entrypoints(
|
||||
&dev->exposed_dispatch_table, &vk_cmd_enqueue_device_entrypoints, true);
|
||||
vk_device_dispatch_table_from_entrypoints(&dev->exposed_dispatch_table,
|
||||
&kk_device_entrypoints, false);
|
||||
vk_device_dispatch_table_from_entrypoints(&dev->exposed_dispatch_table,
|
||||
&wsi_device_entrypoints, false);
|
||||
vk_device_dispatch_table_from_entrypoints(
|
||||
&dev->exposed_dispatch_table, &vk_common_device_entrypoints, false);
|
||||
|
||||
struct vk_device_dispatch_table dispatch_table;
|
||||
vk_device_dispatch_table_from_entrypoints(&dispatch_table,
|
||||
&kk_device_entrypoints, true);
|
||||
vk_device_dispatch_table_from_entrypoints(
|
||||
&dispatch_table, &vk_common_device_entrypoints, false);
|
||||
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 = &kk_device_shader_ops;
|
||||
dev->mtl_handle = pdev->mtl_dev_handle;
|
||||
dev->vk.command_buffer_ops = &kk_cmd_buffer_ops;
|
||||
dev->vk.command_dispatch_table = &dev->vk.dispatch_table;
|
||||
dev->vk.get_timestamp = kk_get_timestamp;
|
||||
|
||||
/* We need to initialize the device residency set before any bo is created. */
|
||||
simple_mtx_init(&dev->residency_set.mutex, mtx_plain);
|
||||
dev->residency_set.handle = mtl_new_residency_set(dev->mtl_handle);
|
||||
if (dev->residency_set.handle == NULL)
|
||||
goto fail_init;
|
||||
|
||||
if (pCreateInfo->queueCreateInfoCount > 0) {
|
||||
result =
|
||||
kk_queue_init(dev, &dev->queue, &pCreateInfo->pQueueCreateInfos[0], 0);
|
||||
if (result != VK_SUCCESS)
|
||||
goto fail_vab_memory;
|
||||
dev->has_queue = true;
|
||||
}
|
||||
|
||||
result = kk_device_init_meta(dev);
|
||||
if (result != VK_SUCCESS)
|
||||
goto fail_mem_cache;
|
||||
|
||||
result = kk_query_table_init(dev, &dev->occlusion_queries,
|
||||
KK_MAX_OCCLUSION_QUERIES);
|
||||
if (result != VK_SUCCESS)
|
||||
goto fail_meta;
|
||||
|
||||
result = kk_init_sampler_heap(dev, &dev->samplers);
|
||||
if (result != VK_SUCCESS)
|
||||
goto fail_query_table;
|
||||
|
||||
result = kk_device_init_lib(dev);
|
||||
if (result != VK_SUCCESS)
|
||||
goto fail_sampler_heap;
|
||||
|
||||
kk_parse_device_environment_options(dev);
|
||||
|
||||
*pDevice = kk_device_to_handle(dev);
|
||||
|
||||
return VK_SUCCESS;
|
||||
|
||||
fail_sampler_heap:
|
||||
kk_destroy_sampler_heap(dev, &dev->samplers);
|
||||
fail_query_table:
|
||||
kk_query_table_finish(dev, &dev->occlusion_queries);
|
||||
fail_meta:
|
||||
kk_device_finish_meta(dev);
|
||||
fail_mem_cache:
|
||||
if (dev->has_queue) {
|
||||
kk_queue_finish(dev, &dev->queue);
|
||||
dev->has_queue = false;
|
||||
}
|
||||
fail_vab_memory:
|
||||
mtl_release(dev->residency_set.handle);
|
||||
simple_mtx_destroy(&dev->residency_set.mutex);
|
||||
fail_init:
|
||||
vk_device_finish(&dev->vk);
|
||||
fail_alloc:
|
||||
vk_free(&dev->vk.alloc, dev);
|
||||
return result;
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL
|
||||
kk_DestroyDevice(VkDevice _device, const VkAllocationCallbacks *pAllocator)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_device, dev, _device);
|
||||
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
/* End capture before we start releasing resources. Otherwise, Metal capture
|
||||
* may run into issues. */
|
||||
if (dev->gpu_capture_enabled) {
|
||||
mtl_stop_gpu_capture();
|
||||
}
|
||||
|
||||
/* Meta first since it may destroy Vulkan objects */
|
||||
kk_device_finish_meta(dev);
|
||||
kk_device_finish_lib(dev);
|
||||
kk_query_table_finish(dev, &dev->occlusion_queries);
|
||||
kk_destroy_sampler_heap(dev, &dev->samplers);
|
||||
|
||||
/* Geometry heap */
|
||||
if (dev->heap)
|
||||
kk_destroy_bo(dev, dev->heap);
|
||||
|
||||
if (dev->has_queue) {
|
||||
kk_queue_finish(dev, &dev->queue);
|
||||
dev->has_queue = false;
|
||||
}
|
||||
|
||||
/* Release the residency set last once all BOs are released. */
|
||||
mtl_release(dev->residency_set.handle);
|
||||
simple_mtx_destroy(&dev->residency_set.mutex);
|
||||
|
||||
vk_device_finish(&dev->vk);
|
||||
|
||||
vk_free(&dev->vk.alloc, dev);
|
||||
}
|
||||
|
||||
/* We need to implement this ourselves so we give the fake ones for vk_common_*
|
||||
* to work when executing actual commands */
|
||||
static PFN_vkVoidFunction
|
||||
kk_device_get_proc_addr(const struct kk_device *device, const char *name)
|
||||
{
|
||||
if (device == NULL || name == NULL)
|
||||
return NULL;
|
||||
|
||||
struct vk_instance *instance = device->vk.physical->instance;
|
||||
return vk_device_dispatch_table_get_if_supported(
|
||||
&device->exposed_dispatch_table, name, instance->app_info.api_version,
|
||||
&instance->enabled_extensions, &device->vk.enabled_extensions);
|
||||
}
|
||||
|
||||
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL
|
||||
kk_GetDeviceProcAddr(VkDevice _device, const char *pName)
|
||||
{
|
||||
VK_FROM_HANDLE(kk_device, device, _device);
|
||||
return kk_device_get_proc_addr(device, pName);
|
||||
}
|
||||
|
||||
void
|
||||
kk_device_add_heap_to_residency_set(struct kk_device *dev, mtl_heap *heap)
|
||||
{
|
||||
simple_mtx_lock(&dev->residency_set.mutex);
|
||||
mtl_residency_set_add_allocation(dev->residency_set.handle, heap);
|
||||
simple_mtx_unlock(&dev->residency_set.mutex);
|
||||
}
|
||||
|
||||
void
|
||||
kk_device_remove_heap_from_residency_set(struct kk_device *dev, mtl_heap *heap)
|
||||
{
|
||||
simple_mtx_lock(&dev->residency_set.mutex);
|
||||
mtl_residency_set_remove_allocation(dev->residency_set.handle, heap);
|
||||
simple_mtx_unlock(&dev->residency_set.mutex);
|
||||
}
|
||||
|
||||
void
|
||||
kk_device_add_buffer_to_residency_set(struct kk_device *dev, mtl_buffer *buffer)
|
||||
{
|
||||
simple_mtx_lock(&dev->residency_set.mutex);
|
||||
mtl_residency_set_add_allocation(dev->residency_set.handle, buffer);
|
||||
simple_mtx_unlock(&dev->residency_set.mutex);
|
||||
}
|
||||
|
||||
void
|
||||
kk_device_remove_buffer_from_residency_set(struct kk_device *dev,
|
||||
mtl_buffer *buffer)
|
||||
{
|
||||
simple_mtx_lock(&dev->residency_set.mutex);
|
||||
mtl_residency_set_remove_allocation(dev->residency_set.handle, buffer);
|
||||
simple_mtx_unlock(&dev->residency_set.mutex);
|
||||
}
|
||||
|
||||
void
|
||||
kk_device_make_resources_resident(struct kk_device *dev)
|
||||
{
|
||||
simple_mtx_lock(&dev->residency_set.mutex);
|
||||
mtl_residency_set_commit(dev->residency_set.handle);
|
||||
mtl_residency_set_request_residency(dev->residency_set.handle);
|
||||
simple_mtx_unlock(&dev->residency_set.mutex);
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright © 2022 Collabora Ltd. and Red Hat Inc.
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef KK_DEVICE_H
|
||||
#define KK_DEVICE_H 1
|
||||
|
||||
#include "kk_private.h"
|
||||
|
||||
#include "kk_query_table.h"
|
||||
#include "kk_queue.h"
|
||||
|
||||
#include "kosmickrisp/bridge/mtl_types.h"
|
||||
|
||||
#include "kosmickrisp/clc/kk_precompiled_shader.h"
|
||||
#include "libkk_shaders.h"
|
||||
|
||||
#include "util/u_dynarray.h"
|
||||
|
||||
#include "vk_device.h"
|
||||
#include "vk_meta.h"
|
||||
#include "vk_queue.h"
|
||||
|
||||
struct kk_bo;
|
||||
struct kk_physical_device;
|
||||
struct vk_pipeline_cache;
|
||||
|
||||
struct kk_residency_set {
|
||||
simple_mtx_t mutex;
|
||||
mtl_residency_set *handle;
|
||||
};
|
||||
|
||||
struct mtl_sampler_packed {
|
||||
enum mtl_sampler_address_mode mode_u;
|
||||
enum mtl_sampler_address_mode mode_v;
|
||||
enum mtl_sampler_address_mode mode_w;
|
||||
enum mtl_sampler_border_color border_color;
|
||||
|
||||
enum mtl_sampler_min_mag_filter min_filter;
|
||||
enum mtl_sampler_min_mag_filter mag_filter;
|
||||
enum mtl_sampler_mip_filter mip_filter;
|
||||
|
||||
enum mtl_compare_function compare_func;
|
||||
float min_lod;
|
||||
float max_lod;
|
||||
uint32_t max_anisotropy;
|
||||
bool normalized_coordinates;
|
||||
};
|
||||
|
||||
struct kk_rc_sampler {
|
||||
struct mtl_sampler_packed key;
|
||||
|
||||
mtl_sampler *handle;
|
||||
|
||||
/* Reference count for this hardware sampler, protected by the heap mutex */
|
||||
uint16_t refcount;
|
||||
|
||||
/* Index of this hardware sampler in the hardware sampler heap */
|
||||
uint16_t index;
|
||||
};
|
||||
|
||||
struct kk_sampler_heap {
|
||||
simple_mtx_t lock;
|
||||
|
||||
struct kk_query_table table;
|
||||
|
||||
/* Map of mtl_sampler_packed to kk_rc_sampler */
|
||||
struct hash_table *ht;
|
||||
};
|
||||
|
||||
struct kk_precompiled_cache {
|
||||
struct kk_precompiled_shader shaders[LIBKK_NUM_PROGRAMS];
|
||||
};
|
||||
|
||||
struct kk_device {
|
||||
struct vk_device vk;
|
||||
|
||||
mtl_device *mtl_handle;
|
||||
|
||||
/* Dispatch table exposed to the user. Required since we need to record all
|
||||
* commands due to Metal limitations */
|
||||
struct vk_device_dispatch_table exposed_dispatch_table;
|
||||
|
||||
struct kk_sampler_heap samplers;
|
||||
struct kk_query_table occlusion_queries;
|
||||
|
||||
/* Track all heaps the user allocated so we can set them all as resident when
|
||||
* recording as required by Metal. */
|
||||
struct kk_residency_set residency_set;
|
||||
|
||||
struct kk_precompiled_cache precompiled_cache;
|
||||
|
||||
bool has_queue;
|
||||
struct kk_queue queue;
|
||||
|
||||
struct vk_meta_device meta;
|
||||
|
||||
/* Geomtry heap */
|
||||
struct kk_bo *heap;
|
||||
util_once_flag heap_init_once;
|
||||
|
||||
uint64_t disabled_workarounds;
|
||||
bool gpu_capture_enabled;
|
||||
};
|
||||
|
||||
VK_DEFINE_HANDLE_CASTS(kk_device, vk.base, VkDevice, VK_OBJECT_TYPE_DEVICE)
|
||||
|
||||
static inline struct kk_physical_device *
|
||||
kk_device_physical(const struct kk_device *dev)
|
||||
{
|
||||
return (struct kk_physical_device *)dev->vk.physical;
|
||||
}
|
||||
|
||||
VkResult kk_device_init_meta(struct kk_device *dev);
|
||||
void kk_device_finish_meta(struct kk_device *dev);
|
||||
VkResult kk_device_init_lib(struct kk_device *dev);
|
||||
void kk_device_finish_lib(struct kk_device *dev);
|
||||
void kk_device_add_heap_to_residency_set(struct kk_device *dev, mtl_heap *heap);
|
||||
void kk_device_remove_heap_from_residency_set(struct kk_device *dev,
|
||||
mtl_heap *heap);
|
||||
void kk_device_add_buffer_to_residency_set(struct kk_device *dev,
|
||||
mtl_buffer *buffer);
|
||||
void kk_device_remove_buffer_from_residency_set(struct kk_device *dev,
|
||||
mtl_buffer *buffer);
|
||||
void kk_device_make_resources_resident(struct kk_device *dev);
|
||||
|
||||
/* Required to create a sampler */
|
||||
mtl_sampler *kk_sampler_create(struct kk_device *dev,
|
||||
const struct mtl_sampler_packed *packed);
|
||||
VkResult kk_sampler_heap_add(struct kk_device *dev,
|
||||
struct mtl_sampler_packed desc,
|
||||
struct kk_rc_sampler **out);
|
||||
void kk_sampler_heap_remove(struct kk_device *dev, struct kk_rc_sampler *rc);
|
||||
|
||||
#endif // KK_DEVICE_H
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2025 LunarG, Inc.
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "kk_device.h"
|
||||
|
||||
#include "kk_shader.h"
|
||||
|
||||
#include "kosmickrisp/clc/kk_precompiled_shader.h"
|
||||
|
||||
#include "libkk_shaders.h"
|
||||
|
||||
#include "kosmickrisp/bridge/mtl_bridge.h"
|
||||
|
||||
#include "nir/nir.h"
|
||||
#include "nir/nir_builder.h"
|
||||
|
||||
static VkResult
|
||||
build_precompiled_shaders(struct kk_device *dev)
|
||||
{
|
||||
uint32_t i = 0u;
|
||||
for (; i < LIBKK_NUM_PROGRAMS; ++i) {
|
||||
const uint32_t *bin = libkk_AppleSilicon[i];
|
||||
const struct kk_precompiled_info *info = (void *)bin;
|
||||
const char *msl = (const char *)bin + sizeof(*info);
|
||||
|
||||
mtl_library *library = mtl_new_library(dev->mtl_handle, msl);
|
||||
if (library == NULL)
|
||||
goto fail;
|
||||
|
||||
struct kk_precompiled_shader *shader = &dev->precompiled_cache.shaders[i];
|
||||
|
||||
/* TODO_KOSMICKRISP Do not hardcode the entrypoint */
|
||||
mtl_function *function =
|
||||
mtl_new_function_with_name(library, "main_entrypoint");
|
||||
uint32_t local_size_threads = info->workgroup_size[0] *
|
||||
info->workgroup_size[1] *
|
||||
info->workgroup_size[2];
|
||||
shader->pipeline = mtl_new_compute_pipeline_state(
|
||||
dev->mtl_handle, function, local_size_threads);
|
||||
mtl_release(function);
|
||||
mtl_release(library);
|
||||
|
||||
if (!shader->pipeline)
|
||||
goto fail;
|
||||
|
||||
memcpy(&shader->info, info, sizeof(*info));
|
||||
}
|
||||
|
||||
return VK_SUCCESS;
|
||||
|
||||
fail:
|
||||
for (uint32_t j = 0u; j < i; ++j)
|
||||
mtl_release(dev->precompiled_cache.shaders[j].pipeline);
|
||||
return vk_error(dev, VK_ERROR_INVALID_SHADER_NV);
|
||||
}
|
||||
|
||||
VkResult
|
||||
kk_device_init_lib(struct kk_device *dev)
|
||||
{
|
||||
return build_precompiled_shaders(dev);
|
||||
}
|
||||
|
||||
void
|
||||
kk_device_finish_lib(struct kk_device *dev)
|
||||
{
|
||||
for (uint32_t i = 0; i < LIBKK_NUM_PROGRAMS; ++i)
|
||||
mtl_release(dev->precompiled_cache.shaders[i].pipeline);
|
||||
}
|
||||
Loaded 100 of 12511 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user