SDL_gpu_shadercross: Rework Compile APIs

From 3acf9c17e5c6de6b7e6d6627f3db28ad87c1b148 Mon Sep 17 00:00:00 2001
From: cosmonaut <[EMAIL REDACTED]>
Date: Thu, 7 Nov 2024 14:10:35 -0800
Subject: [PATCH] Rework Compile APIs

---
 CMakeLists.txt                                |   1 +
 .../SDL_gpu_shadercross.h                     |  59 +++++-
 src/SDL_gpu_shadercross.c                     | 190 +++++++++++++++---
 3 files changed, 214 insertions(+), 36 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index ee61f15..8c5e51e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -138,6 +138,7 @@ else()
 		endif()
 	endif()
 
+	set(DirectXShaderCompiler_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/external/DirectXShaderCompiler-binaries")
 	find_package(DirectXShaderCompiler REQUIRED)
 endif()
 
diff --git a/include/SDL3_gpu_shadercross/SDL_gpu_shadercross.h b/include/SDL3_gpu_shadercross/SDL_gpu_shadercross.h
index e4b873a..e929111 100644
--- a/include/SDL3_gpu_shadercross/SDL_gpu_shadercross.h
+++ b/include/SDL3_gpu_shadercross/SDL_gpu_shadercross.h
@@ -60,6 +60,25 @@ typedef enum SDL_ShaderCross_ShaderModel
     SDL_SHADERCROSS_SHADERMODEL_6_0
 } SDL_ShaderCross_ShaderModel;
 
+typedef struct SDL_ShaderCross_ShaderResourceInfo {
+    Uint32 num_samplers;         /**< The number of samplers defined in the shader. */
+    Uint32 num_storage_textures; /**< The number of storage textures defined in the shader. */
+    Uint32 num_storage_buffers;  /**< The number of storage buffers defined in the shader. */
+    Uint32 num_uniform_buffers;  /**< The number of uniform buffers defined in the shader. */
+} SDL_ShaderCross_ShaderResourceInfo;
+
+typedef struct SDL_ShaderCross_ComputeResourceInfo {
+    Uint32 num_samplers;                   /**< The number of samplers defined in the shader. */
+    Uint32 num_readonly_storage_textures;  /**< The number of storage textures defined in the shader. */
+    Uint32 num_readonly_storage_buffers;   /**< The number of storage buffers defined in the shader. */
+    Uint32 num_readwrite_storage_textures; /**< The number of read-write storage textures defined in the shader. */
+    Uint32 num_readwrite_storage_buffers;  /**< The number of read-write storage buffers defined in the shader. */
+    Uint32 num_uniform_buffers;            /**< The number of uniform buffers defined in the shader. */
+    Uint32 threadcount_x;                  /**< The number of threads in the X dimension. This should match the value in the shader. */
+    Uint32 threadcount_y;                  /**< The number of threads in the Y dimension. This should match the value in the shader. */
+    Uint32 threadcount_z;                  /**< The number of threads in the Z dimension. This should match the value in the shader. */
+} SDL_ShaderCross_ComputeResourceInfo;
+
 /**
  * Initializes SDL_gpu_shadercross
  *
@@ -157,27 +176,42 @@ extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_CompileDXILFromSPIRV(
  * Compile an SDL GPU shader from SPIRV code.
  *
  * \param device the SDL GPU device.
- * \param createInfo a pointer to an SDL_GPUShaderCreateInfo.
+ * \param bytecode the SPIRV bytecode.
+ * \param bytecodeSize the length of the SPIRV bytecode.
+ * \param entrypoint the entry point function name for the shader in UTF-8.
+ * \param shaderStage the shader stage to compile the shader with.
+ * \param resourceInfo a pointer to an SDL_ShaderCross_ShaderResourceInfo.
  * \returns a compiled SDL_GPUShader
  *
  * \threadsafety It is safe to call this function from any thread.
  */
 extern SDL_DECLSPEC SDL_GPUShader * SDLCALL SDL_ShaderCross_CompileGraphicsShaderFromSPIRV(
     SDL_GPUDevice *device,
-    const SDL_GPUShaderCreateInfo *createInfo);
+    const Uint8 *bytecode,
+    size_t bytecodeSize,
+    const char *entrypoint,
+    SDL_GPUShaderStage shaderStage,
+    const SDL_ShaderCross_ShaderResourceInfo *resourceInfo);
 
 /**
  * Compile an SDL GPU compute pipeline from SPIRV code.
  *
  * \param device the SDL GPU device.
- * \param createInfo a pointer to an SDL_GPUComputePipelineCreateInfo.
+ * \param bytecode the SPIRV bytecode.
+ * \param bytecodeSize the length of the SPIRV bytecode.
+ * \param entrypoint the entry point function name for the shader in UTF-8.
+ * \param resourceInfo a pointer to an SDL_ShaderCross_ShaderResourceInfo.
  * \returns a compiled SDL_GPUComputePipeline
  *
  * \threadsafety It is safe to call this function from any thread.
  */
 extern SDL_DECLSPEC SDL_GPUComputePipeline * SDLCALL SDL_ShaderCross_CompileComputePipelineFromSPIRV(
     SDL_GPUDevice *device,
-    const SDL_GPUComputePipelineCreateInfo *createInfo);
+    const Uint8 *bytecode,
+    size_t bytecodeSize,
+    const char *entrypoint,
+    const SDL_ShaderCross_ComputeResourceInfo *resourceInfo);
+
 #endif /* SDL_GPU_SHADERCROSS_SPIRVCROSS */
 
 #if SDL_GPU_SHADERCROSS_HLSL
@@ -249,34 +283,37 @@ extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_CompileSPIRVFromHLSL(
  * Compile an SDL GPU shader from HLSL Shader Model 6.0 code.
  *
  * \param device the SDL GPU device.
- * \param createInfo a pointer to an SDL_GPUShaderCreateInfo.
  * \param hlslSource the HLSL source code for the shader.
+ * \param entrypoint the entry point function name for the shader in UTF-8.
  * \param graphicsShaderStage the shader stage to compile the shader with.
+ * \param resourceInfo a pointer to an SDL_ShaderCross_ShaderResourceInfo.
  * \returns a compiled SDL_GPUShader
  *
  * \threadsafety It is safe to call this function from any thread.
  */
 extern SDL_DECLSPEC SDL_GPUShader * SDLCALL SDL_ShaderCross_CompileGraphicsShaderFromHLSL(
     SDL_GPUDevice *device,
-    const SDL_GPUShaderCreateInfo *createInfo,
     const char *hlslSource,
-    SDL_GPUShaderStage graphicsShaderStage);
+    const char *entrypoint,
+    SDL_GPUShaderStage graphicsShaderStage,
+    const SDL_ShaderCross_ShaderResourceInfo *resourceInfo);
 
 /**
  * Compile an SDL GPU compute pipeline from HLSL Shader Model 6.0 code.
  *
  * \param device the SDL GPU device.
- * \param createInfo a pointer to an SDL_GPUComputePipelineCreateInfo.
  * \param hlslSource the HLSL source code for the shader.
- * \param shaderStage the shader stage to compile the shader with.
+ * \param entrypoint the entry point function name for the shader in UTF-8.
+ * \param resourceInfo a pointer to an SDL_ShaderCross_ComputeResourceInfo.
  * \returns a compiled SDL_GPUComputePipeline
  *
  * \threadsafety It is safe to call this function from any thread.
  */
 extern SDL_DECLSPEC SDL_GPUComputePipeline * SDLCALL SDL_ShaderCross_CompileComputePipelineFromHLSL(
     SDL_GPUDevice *device,
-    const SDL_GPUComputePipelineCreateInfo *createInfo,
-    const char *hlslSource);
+    const char *hlslSource,
+    const char *entrypoint,
+    const SDL_ShaderCross_ComputeResourceInfo *resourceInfo);
 
 #endif /* SDL_GPU_SHADERCROSS_HLSL */
 
diff --git a/src/SDL_gpu_shadercross.c b/src/SDL_gpu_shadercross.c
index 2be9df8..8a466f5 100644
--- a/src/SDL_gpu_shadercross.c
+++ b/src/SDL_gpu_shadercross.c
@@ -454,9 +454,10 @@ void *SDL_ShaderCross_CompileSPIRVFromHLSL(
 
 static void *SDL_ShaderCross_INTERNAL_CreateShaderFromDXC(
     SDL_GPUDevice *device,
-    const void *createInfo,
     const char *hlslSource,
+    const char *entrypoint,
     SDL_ShaderCross_ShaderStage shaderStage,
+    const void *resourceInfo,
     bool spirv)
 {
     void *result;
@@ -467,14 +468,14 @@ static void *SDL_ShaderCross_INTERNAL_CreateShaderFromDXC(
         // If destination is DXIL, force roundtrip through SPIRV-Cross.
         bytecode = SDL_ShaderCross_CompileDXILFromHLSL(
             hlslSource,
-            ((const SDL_GPUShaderCreateInfo *)createInfo)->entrypoint,
+            entrypoint,
             shaderStage,
             &bytecodeSize);
     } else {
         // Otherwise just compile straight to SPIRV.
         bytecode = SDL_ShaderCross_INTERNAL_CompileUsingDXC(
             hlslSource,
-            ((const SDL_GPUShaderCreateInfo *)createInfo)->entrypoint,
+            entrypoint,
             shaderStage,
             spirv,
             &bytecodeSize);
@@ -485,19 +486,37 @@ static void *SDL_ShaderCross_INTERNAL_CreateShaderFromDXC(
     }
 
     if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_COMPUTE) {
+        SDL_ShaderCross_ComputeResourceInfo *info = (SDL_ShaderCross_ComputeResourceInfo *)resourceInfo;
         SDL_GPUComputePipelineCreateInfo newCreateInfo;
-        newCreateInfo = *(const SDL_GPUComputePipelineCreateInfo *)createInfo;
         newCreateInfo.code = (const Uint8 *)bytecode;
         newCreateInfo.code_size = bytecodeSize;
+        newCreateInfo.entrypoint = entrypoint;
         newCreateInfo.format = spirv ? SDL_GPU_SHADERFORMAT_SPIRV : SDL_GPU_SHADERFORMAT_DXIL;
+        newCreateInfo.num_samplers = info->num_samplers;
+        newCreateInfo.num_readonly_storage_textures = info->num_readonly_storage_textures;
+        newCreateInfo.num_readonly_storage_buffers = info->num_readonly_storage_buffers;
+        newCreateInfo.num_readwrite_storage_textures = info->num_readwrite_storage_textures;
+        newCreateInfo.num_readwrite_storage_buffers = info->num_readwrite_storage_buffers;
+        newCreateInfo.num_uniform_buffers = info->num_uniform_buffers;
+        newCreateInfo.threadcount_x = info->threadcount_x;
+        newCreateInfo.threadcount_y = info->threadcount_y;
+        newCreateInfo.threadcount_z = info->threadcount_z;
+        newCreateInfo.props = 0;
 
         result = SDL_CreateGPUComputePipeline(device, &newCreateInfo);
     } else {
+        SDL_ShaderCross_ShaderResourceInfo *info = (SDL_ShaderCross_ShaderResourceInfo *)resourceInfo;
         SDL_GPUShaderCreateInfo newCreateInfo;
-        newCreateInfo = *(const SDL_GPUShaderCreateInfo *)createInfo;
         newCreateInfo.code = (const Uint8 *)bytecode;
         newCreateInfo.code_size = bytecodeSize;
+        newCreateInfo.entrypoint = entrypoint;
         newCreateInfo.format = spirv ? SDL_GPU_SHADERFORMAT_SPIRV : SDL_GPU_SHADERFORMAT_DXIL;
+        newCreateInfo.stage = (SDL_GPUShaderStage)shaderStage;
+        newCreateInfo.num_samplers = info->num_samplers;
+        newCreateInfo.num_storage_textures = info->num_storage_textures;
+        newCreateInfo.num_storage_buffers = info->num_storage_buffers;
+        newCreateInfo.num_uniform_buffers = info->num_uniform_buffers;
+        newCreateInfo.props = 0;
 
         result = SDL_CreateGPUShader(device, &newCreateInfo);
     }
@@ -675,16 +694,17 @@ void *SDL_ShaderCross_CompileDXBCFromHLSL(
 
 static void *SDL_ShaderCross_INTERNAL_CreateShaderFromDXBC(
     SDL_GPUDevice *device,
-    const void *createInfo,
     const char *hlslSource,
-    SDL_ShaderCross_ShaderStage shaderStage)
+    const char *entrypoint,
+    SDL_ShaderCross_ShaderStage shaderStage,
+    const void *resourceInfo)
 {
     void *result;
     size_t bytecodeSize;
 
     void *bytecode = SDL_ShaderCross_CompileDXBCFromHLSL(
         hlslSource,
-        ((const SDL_GPUShaderCreateInfo *)createInfo)->entrypoint,
+        entrypoint,
         shaderStage,
         &bytecodeSize);
 
@@ -693,19 +713,37 @@ static void *SDL_ShaderCross_INTERNAL_CreateShaderFromDXBC(
     }
 
     if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_COMPUTE) {
+        SDL_ShaderCross_ComputeResourceInfo *info = (SDL_ShaderCross_ComputeResourceInfo *)resourceInfo;
         SDL_GPUComputePipelineCreateInfo newCreateInfo;
-        newCreateInfo = *(const SDL_GPUComputePipelineCreateInfo *)createInfo;
         newCreateInfo.code = (const Uint8 *)bytecode;
         newCreateInfo.code_size = bytecodeSize;
+        newCreateInfo.entrypoint = entrypoint;
         newCreateInfo.format = SDL_GPU_SHADERFORMAT_DXBC;
+        newCreateInfo.num_samplers = info->num_samplers;
+        newCreateInfo.num_readonly_storage_textures = info->num_readonly_storage_textures;
+        newCreateInfo.num_readonly_storage_buffers = info->num_readonly_storage_buffers;
+        newCreateInfo.num_readwrite_storage_textures = info->num_readwrite_storage_textures;
+        newCreateInfo.num_readwrite_storage_buffers = info->num_readwrite_storage_buffers;
+        newCreateInfo.num_uniform_buffers = info->num_uniform_buffers;
+        newCreateInfo.threadcount_x = info->threadcount_x;
+        newCreateInfo.threadcount_y = info->threadcount_y;
+        newCreateInfo.threadcount_z = info->threadcount_z;
+        newCreateInfo.props = 0;
 
         result = SDL_CreateGPUComputePipeline(device, &newCreateInfo);
     } else {
+        SDL_ShaderCross_ShaderResourceInfo *info = (SDL_ShaderCross_ShaderResourceInfo *)resourceInfo;
         SDL_GPUShaderCreateInfo newCreateInfo;
-        newCreateInfo = *(const SDL_GPUShaderCreateInfo *)createInfo;
         newCreateInfo.code = (const Uint8 *)bytecode;
         newCreateInfo.code_size = bytecodeSize;
+        newCreateInfo.entrypoint = entrypoint;
         newCreateInfo.format = SDL_GPU_SHADERFORMAT_DXBC;
+        newCreateInfo.stage = (SDL_GPUShaderStage)shaderStage;
+        newCreateInfo.num_samplers = info->num_samplers;
+        newCreateInfo.num_storage_textures = info->num_storage_textures;
+        newCreateInfo.num_storage_buffers = info->num_storage_buffers;
+        newCreateInfo.num_uniform_buffers = info->num_uniform_buffers;
+        newCreateInfo.props = 0;
 
         result = SDL_CreateGPUShader(device, &newCreateInfo);
     }
@@ -716,19 +754,68 @@ static void *SDL_ShaderCross_INTERNAL_CreateShaderFromDXBC(
 
 static void *SDL_ShaderCross_INTERNAL_CreateShaderFromHLSL(
     SDL_GPUDevice *device,
-    const void *createInfo,
     const char *hlslSource,
-    SDL_ShaderCross_ShaderStage shaderStage)
+    const char *entrypoint,
+    SDL_ShaderCross_ShaderStage shaderStage,
+    const void *resourceInfo)
 {
     SDL_GPUShaderFormat format = SDL_GetGPUShaderFormats(device);
     if (format & SDL_GPU_SHADERFORMAT_DXBC) {
-        return SDL_ShaderCross_INTERNAL_CreateShaderFromDXBC(device, createInfo, hlslSource, shaderStage);
+        return SDL_ShaderCross_INTERNAL_CreateShaderFromDXBC(
+            device,
+            hlslSource,
+            entrypoint,
+            shaderStage,
+            resourceInfo);
     }
     if (format & SDL_GPU_SHADERFORMAT_DXIL) {
-        return SDL_ShaderCross_INTERNAL_CreateShaderFromDXC(device, createInfo, hlslSource, shaderStage, false);
+        return SDL_ShaderCross_INTERNAL_CreateShaderFromDXC(
+            device,
+            hlslSource,
+            entrypoint,
+            shaderStage,
+            resourceInfo,
+            false);
     }
     if (format & SDL_GPU_SHADERFORMAT_SPIRV) {
-        return SDL_ShaderCross_INTERNAL_CreateShaderFromDXC(device, createInfo, hlslSource, shaderStage, true);
+        return SDL_ShaderCross_INTERNAL_CreateShaderFromDXC(
+            device,
+            hlslSource,
+            entrypoint,
+            shaderStage,
+            resourceInfo,
+            true);
+    }
+    if (format & SDL_GPU_SHADERFORMAT_MSL) {
+        size_t bytecodeSize;
+        void *spirv = SDL_ShaderCross_CompileSPIRVFromHLSL(
+            hlslSource,
+            entrypoint,
+            shaderStage,
+            &bytecodeSize);
+        if (spirv == NULL) {
+            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", "Failed to compile SPIR-V!");
+            return NULL;
+        }
+        void *result;
+        if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_COMPUTE) {
+            result = SDL_ShaderCross_CompileComputePipelineFromSPIRV(
+                device,
+                spirv,
+                bytecodeSize,
+                entrypoint,
+                (SDL_ShaderCross_ComputeResourceInfo *)resourceInfo);
+        } else {
+            result = SDL_ShaderCross_CompileGraphicsShaderFromSPIRV(
+                device,
+                spirv,
+                bytecodeSize,
+                entrypoint,
+                (SDL_GPUShaderStage)shaderStage,
+                (SDL_ShaderCross_ShaderResourceInfo *)resourceInfo);
+        }
+        SDL_free(spirv);
+        return result;
     }
 
     SDL_SetError("SDL_ShaderCross_INTERNAL_CreateShaderFromHLSL: Unexpected SDL_GPUShaderFormat");
@@ -737,19 +824,31 @@ static void *SDL_ShaderCross_INTERNAL_CreateShaderFromHLSL(
 
 SDL_GPUShader *SDL_ShaderCross_CompileGraphicsShaderFromHLSL(
     SDL_GPUDevice *device,
-    const SDL_GPUShaderCreateInfo *createInfo,
     const char *hlslSource,
-    SDL_GPUShaderStage graphicsShaderStage)
+    const char *entrypoint,
+    SDL_GPUShaderStage graphicsShaderStage,
+    const SDL_ShaderCross_ShaderResourceInfo *resourceInfo)
 {
-    return (SDL_GPUShader *)SDL_ShaderCross_INTERNAL_CreateShaderFromHLSL(device, createInfo, hlslSource, (SDL_ShaderCross_ShaderStage)graphicsShaderStage);
+    return (SDL_GPUShader *)SDL_ShaderCross_INTERNAL_CreateShaderFromHLSL(
+        device,
+        hlslSource,
+        entrypoint,
+        (SDL_ShaderCross_ShaderStage)graphicsShaderStage,
+        (const void *)resourceInfo);
 }
 
 SDL_GPUComputePipeline *SDL_ShaderCross_CompileComputePipelineFromHLSL(
     SDL_GPUDevice *device,
-    const SDL_GPUComputePipelineCreateInfo *createInfo,
-    const char *hlslSource)
+    const char *hlslSource,
+    const char *entrypoint,
+    const SDL_ShaderCross_ComputeResourceInfo *resourceInfo)
 {
-    return (SDL_GPUComputePipeline *)SDL_ShaderCross_INTERNAL_CreateShaderFromHLSL(device, createInfo, hlslSource, SDL_SHADERCROSS_SHADERSTAGE_COMPUTE);
+    return (SDL_GPUComputePipeline *)SDL_ShaderCross_INTERNAL_CreateShaderFromHLSL(
+        device,
+        hlslSource,
+        entrypoint,
+        SDL_SHADERCROSS_SHADERSTAGE_COMPUTE,
+        (const void *)resourceInfo);
 }
 
 #endif /* SDL_GPU_SHADERCROSS_HLSL */
@@ -1529,16 +1628,57 @@ static void *SDL_ShaderCross_INTERNAL_CreateShaderFromSPIRV(
 
 SDL_GPUShader *SDL_ShaderCross_CompileGraphicsShaderFromSPIRV(
     SDL_GPUDevice *device,
-    const SDL_GPUShaderCreateInfo *createInfo)
+    const Uint8 *bytecode,
+    size_t bytecodeSize,
+    const char *entrypoint,
+    SDL_GPUShaderStage shaderStage,
+    const SDL_ShaderCross_ShaderResourceInfo *resourceInfo)
 {
-    return (SDL_GPUShader *)SDL_ShaderCross_INTERNAL_CreateShaderFromSPIRV(device, createInfo, (SDL_ShaderCross_ShaderStage)createInfo->stage);
+    SDL_GPUShaderCreateInfo createInfo;
+    createInfo.code = bytecode;
+    createInfo.code_size = bytecodeSize;
+    createInfo.entrypoint = entrypoint;
+    createInfo.format = SDL_GPU_SHADERFORMAT_SPIRV;
+    createInfo.stage = shaderStage;
+    createInfo.num_samplers = resourceInfo->num_samplers;
+    createInfo.num_storage_textures = resourceInfo->num_storage_textures;
+    createInfo.num_storage_buffers = resourceInfo->num_storage_buffers;
+    createInfo.num_uniform_buffers = resourceInfo->num_uniform_buffers;
+    createInfo.props = 0;
+
+    return (SDL_GPUShader *)SDL_ShaderCross_INTERNAL_CreateShaderFromSPIRV(
+        device,
+        &createInfo,
+        (SDL_ShaderCross_ShaderStage)shaderStage);
 }
 
 SDL_GPUComputePipeline *SDL_ShaderCross_CompileComputePipelineFromSPIRV(
     SDL_GPUDevice *device,
-    const SDL_GPUComputePipelineCreateInfo *createInfo)
+    const Uint8 *bytecode,
+    size_t bytecodeSize,
+    const char *entrypoint,
+    const SDL_ShaderCross_ComputeResourceInfo *resourceInfo)
 {
-    return (SDL_GPUComputePipeline *)SDL_ShaderCross_INTERNAL_CreateShaderFromSPIRV(device, createInfo, SDL_SHADERCROSS_SHADERSTAGE_COMPUTE);
+    SDL_GPUComputePipelineCreateInfo createInfo;
+    createInfo.code = bytecode;
+    createInfo.code_size = bytecodeSize;
+    createInfo.entrypoint = entrypoint;
+    createInfo.format = SDL_GPU_SHADERFORMAT_SPIRV;
+    createInfo.num_samplers = resourceInfo->num_samplers;
+    createInfo.num_readonly_storage_textures = resourceInfo->num_readonly_storage_textures;
+    createInfo.num_readonly_storage_buffers = resourceInfo->num_readonly_storage_buffers;
+    createInfo.num_readwrite_storage_textures = resourceInfo->num_readwrite_storage_textures;
+    createInfo.num_readwrite_storage_buffers = resourceInfo->num_readwrite_storage_buffers;
+    createInfo.num_uniform_buffers = resourceInfo->num_uniform_buffers;
+    createInfo.threadcount_x = resourceInfo->threadcount_x;
+    createInfo.threadcount_y = resourceInfo->threadcount_y;
+    createInfo.threadcount_z = resourceInfo->threadcount_z;
+    createInfo.props = 0;
+
+    return (SDL_GPUComputePipeline *)SDL_ShaderCross_INTERNAL_CreateShaderFromSPIRV(
+        device,
+        &createInfo,
+        SDL_SHADERCROSS_SHADERSTAGE_COMPUTE);
 }
 
 #endif /* SDL_GPU_SHADERCROSS_SPIRVCROSS */