Skip to content

Commit ba17c0b

Browse files
committed
[mlir][spirv] Add mgpu* wrappers for Vulkan runtime, migrate some tests
This commit adds new wrappers around the MLIR Vulkan runtime which implement the mgpu* APIs (as generated by GPUToLLVMConversionPass), adds an optional LLVM lowering to the Vulkan runner mlir-opt pipeline based on GPUToLLVMConversionPass, and migrates several of the mlir-vulkan-runner tests to use mlir-cpu-runner instead, together with the new pipeline and wrappers. This is a further incremental step towards eliminating mlir-vulkan-runner and its associated pipeline, passes and wrappers (#73457). This commit does not migrate all of the tests to the new system, because changes to the mgpuLaunchKernel ABI will be necessary to support the tests that use multi-dimensional memref arguments.
1 parent 7402521 commit ba17c0b

File tree

14 files changed

+198
-32
lines changed

14 files changed

+198
-32
lines changed

mlir/include/mlir/Conversion/GPUCommon/GPUCommonPass.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ struct FunctionCallBuilder {
6464
/// populate converter for gpu types.
6565
void populateGpuToLLVMConversionPatterns(LLVMTypeConverter &converter,
6666
RewritePatternSet &patterns,
67-
bool kernelBarePtrCallConv = false);
67+
bool kernelBarePtrCallConv = false,
68+
bool typeCheckKernelArgs = false);
6869

6970
/// A function that maps a MemorySpace enum to a target-specific integer value.
7071
using MemorySpaceMapping = std::function<unsigned(gpu::AddressSpace)>;

mlir/include/mlir/Conversion/Passes.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,11 @@ def GpuToLLVMConversionPass : Pass<"gpu-to-llvm", "ModuleOp"> {
517517
/*default=*/"false",
518518
"Use bare pointers to pass memref arguments to kernels. "
519519
"The kernel must use the same setting for this option."
520+
>,
521+
Option<"typeCheckKernelArgs", "type-check-kernel-args", "bool",
522+
/*default=*/"false",
523+
"Require all kernel arguments to be memrefs of rank 1 and with a "
524+
"32-bit element size."
520525
>
521526
];
522527

mlir/lib/Conversion/GPUCommon/GPUToLLVMConversion.cpp

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -427,16 +427,19 @@ class LegalizeLaunchFuncOpPattern
427427
: public ConvertOpToGpuRuntimeCallPattern<gpu::LaunchFuncOp> {
428428
public:
429429
LegalizeLaunchFuncOpPattern(const LLVMTypeConverter &typeConverter,
430-
bool kernelBarePtrCallConv)
430+
bool kernelBarePtrCallConv,
431+
bool typeCheckKernelArgs)
431432
: ConvertOpToGpuRuntimeCallPattern<gpu::LaunchFuncOp>(typeConverter),
432-
kernelBarePtrCallConv(kernelBarePtrCallConv) {}
433+
kernelBarePtrCallConv(kernelBarePtrCallConv),
434+
typeCheckKernelArgs(typeCheckKernelArgs) {}
433435

434436
private:
435437
LogicalResult
436438
matchAndRewrite(gpu::LaunchFuncOp launchOp, OpAdaptor adaptor,
437439
ConversionPatternRewriter &rewriter) const override;
438440

439441
bool kernelBarePtrCallConv;
442+
bool typeCheckKernelArgs;
440443
};
441444

442445
/// A rewrite pattern to convert gpu.memcpy operations into a GPU runtime
@@ -563,8 +566,8 @@ void GpuToLLVMConversionPass::runOnOperation() {
563566
populateFinalizeMemRefToLLVMConversionPatterns(converter, patterns);
564567
populateAsyncStructuralTypeConversionsAndLegality(converter, patterns,
565568
target);
566-
populateGpuToLLVMConversionPatterns(converter, patterns,
567-
kernelBarePtrCallConv);
569+
populateGpuToLLVMConversionPatterns(
570+
converter, patterns, kernelBarePtrCallConv, typeCheckKernelArgs);
568571

569572
if (failed(
570573
applyPartialConversion(getOperation(), target, std::move(patterns))))
@@ -966,6 +969,26 @@ LogicalResult LegalizeLaunchFuncOpPattern::matchAndRewrite(
966969
// stream must be created to pass to subsequent operations.
967970
else if (launchOp.getAsyncToken())
968971
stream = streamCreateCallBuilder.create(loc, rewriter, {}).getResult();
972+
973+
if (typeCheckKernelArgs) {
974+
// The current non-bare-pointer ABI is a bad fit for `mgpuLaunchKernel`,
975+
// which takes an untyped list of arguments. The type check here prevents
976+
// accidentally violating the assumption made in vulkan-runtime-wrappers.cpp
977+
// and creating a unchecked runtime ABI mismatch.
978+
// TODO: Change the ABI here to remove the need for this type check.
979+
for (Value arg : launchOp.getKernelOperands()) {
980+
if (auto t = dyn_cast<MemRefType>(arg.getType())) {
981+
if (t.getRank() != 1 || t.getElementTypeBitWidth() != 32) {
982+
return rewriter.notifyMatchFailure(
983+
launchOp, "Operand to launch op is not a rank-1 memref with "
984+
"32-bit element type.");
985+
}
986+
} else {
987+
return rewriter.notifyMatchFailure(
988+
launchOp, "Operand to launch op is not a memref.");
989+
}
990+
}
991+
}
969992
// Lower the kernel operands to match kernel parameters.
970993
// Note: If `useBarePtrCallConv` is set in the type converter's options,
971994
// the value of `kernelBarePtrCallConv` will be ignored.
@@ -1737,7 +1760,8 @@ LogicalResult ConvertCreateBsrOpToGpuRuntimeCallPattern::matchAndRewrite(
17371760

17381761
void mlir::populateGpuToLLVMConversionPatterns(LLVMTypeConverter &converter,
17391762
RewritePatternSet &patterns,
1740-
bool kernelBarePtrCallConv) {
1763+
bool kernelBarePtrCallConv,
1764+
bool typeCheckKernelArgs) {
17411765
addOpaquePointerConversion<gpu::AsyncTokenType>(converter);
17421766
addOpaquePointerConversion<gpu::SparseDnTensorHandleType>(converter);
17431767
addOpaquePointerConversion<gpu::SparseSpMatHandleType>(converter);
@@ -1774,7 +1798,8 @@ void mlir::populateGpuToLLVMConversionPatterns(LLVMTypeConverter &converter,
17741798
ConvertSpGEMMCopyOpToGpuRuntimeCallPattern,
17751799
ConvertSpMatGetSizeOpToGpuRuntimeCallPattern,
17761800
ConvertSetCsrPointersOpToGpuRuntimeCallPattern>(converter);
1777-
patterns.add<LegalizeLaunchFuncOpPattern>(converter, kernelBarePtrCallConv);
1801+
patterns.add<LegalizeLaunchFuncOpPattern>(converter, kernelBarePtrCallConv,
1802+
typeCheckKernelArgs);
17781803
}
17791804

17801805
//===----------------------------------------------------------------------===//

mlir/test/lib/Pass/TestVulkanRunnerPipeline.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,13 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "mlir/Conversion/ConvertToSPIRV/ConvertToSPIRVPass.h"
14+
#include "mlir/Conversion/GPUCommon/GPUCommonPass.h"
1415
#include "mlir/Conversion/GPUToSPIRV/GPUToSPIRVPass.h"
16+
#include "mlir/Conversion/MemRefToLLVM/MemRefToLLVM.h"
17+
#include "mlir/Dialect/Func/IR/FuncOps.h"
1518
#include "mlir/Dialect/GPU/IR/GPUDialect.h"
1619
#include "mlir/Dialect/GPU/Transforms/Passes.h"
20+
#include "mlir/Dialect/LLVMIR/Transforms/RequestCWrappers.h"
1721
#include "mlir/Dialect/MemRef/Transforms/Passes.h"
1822
#include "mlir/Dialect/SPIRV/IR/SPIRVOps.h"
1923
#include "mlir/Dialect/SPIRV/Transforms/Passes.h"
@@ -29,6 +33,9 @@ struct VulkanRunnerPipelineOptions
2933
Option<bool> spirvWebGPUPrepare{
3034
*this, "spirv-webgpu-prepare",
3135
llvm::cl::desc("Run MLIR transforms used when targetting WebGPU")};
36+
Option<bool> toLlvm{*this, "to-llvm",
37+
llvm::cl::desc("Run MLIR transforms to lower host code "
38+
"to LLVM, intended for mlir-cpu-runner")};
3239
};
3340

3441
void buildTestVulkanRunnerPipeline(OpPassManager &passManager,
@@ -56,6 +63,19 @@ void buildTestVulkanRunnerPipeline(OpPassManager &passManager,
5663
spirvModulePM.addPass(spirv::createSPIRVWebGPUPreparePass());
5764

5865
passManager.addPass(createGpuModuleToBinaryPass());
66+
67+
if (options.toLlvm) {
68+
passManager.addPass(createFinalizeMemRefToLLVMConversionPass());
69+
passManager.nest<func::FuncOp>().addPass(
70+
LLVM::createRequestCWrappersPass());
71+
// vulkan-runtime-wrappers.cpp uses the non-bare-pointer calling convention,
72+
// and the type check is needed to prevent accidental ABI mismatches.
73+
GpuToLLVMConversionPassOptions opt;
74+
opt.hostBarePtrCallConv = false;
75+
opt.kernelBarePtrCallConv = false;
76+
opt.typeCheckKernelArgs = true;
77+
passManager.addPass(createGpuToLLVMConversionPass(opt));
78+
}
5979
}
6080

6181
} // namespace
@@ -65,7 +85,7 @@ void registerTestVulkanRunnerPipeline() {
6585
PassPipelineRegistration<VulkanRunnerPipelineOptions>(
6686
"test-vulkan-runner-pipeline",
6787
"Runs a series of passes for lowering GPU-dialect MLIR to "
68-
"SPIR-V-dialect MLIR intended for mlir-vulkan-runner.",
88+
"SPIR-V-dialect MLIR intended for mlir-vulkan-runner or mlir-cpu-runner.",
6989
buildTestVulkanRunnerPipeline);
7090
}
7191
} // namespace mlir::test

mlir/test/mlir-vulkan-runner/addf.mlir

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: mlir-opt %s -test-vulkan-runner-pipeline \
2-
// RUN: | mlir-vulkan-runner - --shared-libs=%vulkan-runtime-wrappers,%mlir_runner_utils --entry-point-result=void | FileCheck %s
1+
// RUN: mlir-opt %s -test-vulkan-runner-pipeline=to-llvm \
2+
// RUN: | mlir-cpu-runner - --shared-libs=%vulkan-runtime-wrappers,%mlir_runner_utils --entry-point-result=void | FileCheck %s
33

44
// CHECK: [3.3, 3.3, 3.3, 3.3, 3.3, 3.3, 3.3, 3.3]
55
module attributes {

mlir/test/mlir-vulkan-runner/addf_if.mlir

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: mlir-opt %s -test-vulkan-runner-pipeline \
2-
// RUN: | mlir-vulkan-runner - --shared-libs=%vulkan-runtime-wrappers,%mlir_runner_utils --entry-point-result=void | FileCheck %s
1+
// RUN: mlir-opt %s -test-vulkan-runner-pipeline=to-llvm \
2+
// RUN: | mlir-cpu-runner - --shared-libs=%vulkan-runtime-wrappers,%mlir_runner_utils --entry-point-result=void | FileCheck %s
33

44
// CHECK: [3.3, 3.3, 3.3, 3.3, 0, 0, 0, 0]
55
module attributes {

mlir/test/mlir-vulkan-runner/addui_extended.mlir

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
// Make sure that addition with carry produces expected results
22
// with and without expansion to primitive add/cmp ops for WebGPU.
33

4-
// RUN: mlir-opt %s -test-vulkan-runner-pipeline \
5-
// RUN: | mlir-vulkan-runner - \
4+
// RUN: mlir-opt %s -test-vulkan-runner-pipeline=to-llvm \
5+
// RUN: | mlir-cpu-runner - \
66
// RUN: --shared-libs=%vulkan-runtime-wrappers,%mlir_runner_utils \
77
// RUN: --entry-point-result=void | FileCheck %s
88

9-
// RUN: mlir-opt %s -test-vulkan-runner-pipeline=spirv-webgpu-prepare \
10-
// RUN: | mlir-vulkan-runner - \
9+
// RUN: mlir-opt %s -test-vulkan-runner-pipeline="spirv-webgpu-prepare to-llvm" \
10+
// RUN: | mlir-cpu-runner - \
1111
// RUN: --shared-libs=%vulkan-runtime-wrappers,%mlir_runner_utils \
1212
// RUN: --entry-point-result=void | FileCheck %s
1313

mlir/test/mlir-vulkan-runner/smul_extended.mlir

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
// Make sure that signed extended multiplication produces expected results
22
// with and without expansion to primitive mul/add ops for WebGPU.
33

4-
// RUN: mlir-opt %s -test-vulkan-runner-pipeline \
5-
// RUN: | mlir-vulkan-runner - \
4+
// RUN: mlir-opt %s -test-vulkan-runner-pipeline=to-llvm \
5+
// RUN: | mlir-cpu-runner - \
66
// RUN: --shared-libs=%vulkan-runtime-wrappers,%mlir_runner_utils \
77
// RUN: --entry-point-result=void | FileCheck %s
88

9-
// RUN: mlir-opt %s -test-vulkan-runner-pipeline=spirv-webgpu-prepare \
10-
// RUN: | mlir-vulkan-runner - \
9+
// RUN: mlir-opt %s -test-vulkan-runner-pipeline="spirv-webgpu-prepare to-llvm" \
10+
// RUN: | mlir-cpu-runner - \
1111
// RUN: --shared-libs=%vulkan-runtime-wrappers,%mlir_runner_utils \
1212
// RUN: --entry-point-result=void | FileCheck %s
1313

mlir/test/mlir-vulkan-runner/time.mlir

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: mlir-opt %s -test-vulkan-runner-pipeline \
2-
// RUN: | mlir-vulkan-runner - --shared-libs=%vulkan-runtime-wrappers,%mlir_runner_utils --entry-point-result=void | FileCheck %s
1+
// RUN: mlir-opt %s -test-vulkan-runner-pipeline=to-llvm \
2+
// RUN: | mlir-cpu-runner - --shared-libs=%vulkan-runtime-wrappers,%mlir_runner_utils --entry-point-result=void | FileCheck %s
33

44
// CHECK: Compute shader execution time
55
// CHECK: Command buffer submit time

mlir/test/mlir-vulkan-runner/umul_extended.mlir

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
// Make sure that unsigned extended multiplication produces expected results
22
// with and without expansion to primitive mul/add ops for WebGPU.
33

4-
// RUN: mlir-opt %s -test-vulkan-runner-pipeline \
5-
// RUN: | mlir-vulkan-runner - \
4+
// RUN: mlir-opt %s -test-vulkan-runner-pipeline=to-llvm \
5+
// RUN: | mlir-cpu-runner - \
66
// RUN: --shared-libs=%vulkan-runtime-wrappers,%mlir_runner_utils \
77
// RUN: --entry-point-result=void | FileCheck %s
88

9-
// RUN: mlir-opt %s -test-vulkan-runner-pipeline=spirv-webgpu-prepare \
10-
// RUN: | mlir-vulkan-runner - \
9+
// RUN: mlir-opt %s -test-vulkan-runner-pipeline="spirv-webgpu-prepare to-llvm" \
10+
// RUN: | mlir-cpu-runner - \
1111
// RUN: --shared-libs=%vulkan-runtime-wrappers,%mlir_runner_utils \
1212
// RUN: --entry-point-result=void | FileCheck %s
1313

mlir/test/mlir-vulkan-runner/vector-deinterleave.mlir

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: mlir-opt %s -test-vulkan-runner-pipeline \
2-
// RUN: | mlir-vulkan-runner - \
1+
// RUN: mlir-opt %s -test-vulkan-runner-pipeline=to-llvm \
2+
// RUN: | mlir-cpu-runner - \
33
// RUN: --shared-libs=%vulkan-runtime-wrappers,%mlir_runner_utils \
44
// RUN: --entry-point-result=void | FileCheck %s
55

mlir/test/mlir-vulkan-runner/vector-interleave.mlir

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: mlir-opt %s -test-vulkan-runner-pipeline \
2-
// RUN: | mlir-vulkan-runner - \
1+
// RUN: mlir-opt %s -test-vulkan-runner-pipeline=to-llvm \
2+
// RUN: | mlir-cpu-runner - \
33
// RUN: --shared-libs=%vulkan-runtime-wrappers,%mlir_runner_utils \
44
// RUN: --entry-point-result=void | FileCheck %s
55

mlir/test/mlir-vulkan-runner/vector-shuffle.mlir

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: mlir-opt %s -test-vulkan-runner-pipeline \
2-
// RUN: | mlir-vulkan-runner - \
1+
// RUN: mlir-opt %s -test-vulkan-runner-pipeline=to-llvm \
2+
// RUN: | mlir-cpu-runner - \
33
// RUN: --shared-libs=%vulkan-runtime-wrappers,%mlir_runner_utils \
44
// RUN: --entry-point-result=void | FileCheck %s
55

mlir/tools/mlir-vulkan-runner/vulkan-runtime-wrappers.cpp

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include <iostream>
1414
#include <mutex>
1515
#include <numeric>
16+
#include <string>
17+
#include <vector>
1618

1719
#include "VulkanRuntime.h"
1820

@@ -26,6 +28,37 @@
2628

2729
namespace {
2830

31+
class VulkanModule;
32+
33+
// Class to be a thing that can be returned from `mgpuModuleGetFunction`.
34+
struct VulkanFunction {
35+
VulkanModule *module;
36+
std::string name;
37+
38+
VulkanFunction(VulkanModule *module, const char *name)
39+
: module(module), name(name) {}
40+
};
41+
42+
// Class to own a copy of the SPIR-V provided to `mgpuModuleLoad` and to manage
43+
// allocation of pointers returned from `mgpuModuleGetFunction`.
44+
class VulkanModule {
45+
public:
46+
VulkanModule(const char *ptr, size_t size) : blob(ptr, ptr + size) {}
47+
~VulkanModule() = default;
48+
49+
VulkanFunction *getFunction(const char *name) {
50+
return functions.emplace_back(std::make_unique<VulkanFunction>(this, name))
51+
.get();
52+
}
53+
54+
uint8_t *blobData() { return reinterpret_cast<uint8_t *>(blob.data()); }
55+
uint32_t blobSize() { return static_cast<uint32_t>(blob.size()); }
56+
57+
private:
58+
std::vector<char> blob;
59+
std::vector<std::unique_ptr<VulkanFunction>> functions;
60+
};
61+
2962
class VulkanRuntimeManager {
3063
public:
3164
VulkanRuntimeManager() = default;
@@ -91,6 +124,88 @@ void bindMemRef(void *vkRuntimeManager, DescriptorSetIndex setIndex,
91124
}
92125

93126
extern "C" {
127+
128+
//===----------------------------------------------------------------------===//
129+
//
130+
// New wrappers, intended for mlir-cpu-runner. Calls to these are generated by
131+
// GPUToLLVMConversionPass.
132+
//
133+
//===----------------------------------------------------------------------===//
134+
135+
VULKAN_WRAPPER_SYMBOL_EXPORT void *mgpuStreamCreate() {
136+
return new VulkanRuntimeManager();
137+
}
138+
139+
VULKAN_WRAPPER_SYMBOL_EXPORT void mgpuStreamDestroy(void *vkRuntimeManager) {
140+
delete static_cast<VulkanRuntimeManager *>(vkRuntimeManager);
141+
}
142+
143+
VULKAN_WRAPPER_SYMBOL_EXPORT void mgpuStreamSynchronize(void *) {
144+
// Currently a no-op as the other operations are synchronous.
145+
}
146+
147+
VULKAN_WRAPPER_SYMBOL_EXPORT void *mgpuModuleLoad(const void *data,
148+
size_t gpuBlobSize) {
149+
return new VulkanModule(static_cast<const char *>(data), gpuBlobSize);
150+
}
151+
152+
VULKAN_WRAPPER_SYMBOL_EXPORT void mgpuModuleUnload(void *vkModule) {
153+
delete static_cast<VulkanModule *>(vkModule);
154+
}
155+
156+
VULKAN_WRAPPER_SYMBOL_EXPORT void *mgpuModuleGetFunction(void *vkModule,
157+
const char *name) {
158+
return static_cast<VulkanModule *>(vkModule)->getFunction(name);
159+
}
160+
161+
VULKAN_WRAPPER_SYMBOL_EXPORT void
162+
mgpuLaunchKernel(void *vkKernel, size_t gridX, size_t gridY, size_t gridZ,
163+
size_t /*blockX*/, size_t /*blockY*/, size_t /*blockZ*/,
164+
size_t /*smem*/, void *vkRuntimeManager, void **params,
165+
void ** /*extra*/, size_t paramsCount) {
166+
auto manager = static_cast<VulkanRuntimeManager *>(vkRuntimeManager);
167+
168+
// The non-bare-pointer memref ABI interacts badly with mgpuLaunchKernel's
169+
// signature:
170+
// - The memref descriptor struct gets split into several elements, each
171+
// passed as their own "param".
172+
// - No metadata is provided as to the rank or element type/size of a memref.
173+
// Here we assume that all MemRefs have rank 1 and an element size of
174+
// 4 bytes. This means each descriptor struct will have five members.
175+
// TODO: Refactor the ABI/API of mgpuLaunchKernel to use a different ABI for
176+
// memrefs, so that other memref types can also be used. This will allow
177+
// migrating the remaining tests and removal of mlir-vulkan-runner.
178+
const size_t paramsPerMemRef = 5;
179+
if (paramsCount % paramsPerMemRef) {
180+
abort();
181+
}
182+
const DescriptorSetIndex setIndex = 0;
183+
BindingIndex bindIndex = 0;
184+
for (size_t i = 0; i < paramsCount; i += paramsPerMemRef) {
185+
auto memref = static_cast<MemRefDescriptor<uint32_t, 1> *>(params[i]);
186+
bindMemRef<uint32_t, 1>(manager, setIndex, bindIndex, memref);
187+
bindIndex++;
188+
}
189+
190+
manager->setNumWorkGroups(NumWorkGroups{static_cast<uint32_t>(gridX),
191+
static_cast<uint32_t>(gridY),
192+
static_cast<uint32_t>(gridZ)});
193+
194+
auto function = static_cast<VulkanFunction *>(vkKernel);
195+
manager->setShaderModule(function->module->blobData(),
196+
function->module->blobSize());
197+
manager->setEntryPoint(function->name.c_str());
198+
199+
manager->runOnVulkan();
200+
}
201+
202+
//===----------------------------------------------------------------------===//
203+
//
204+
// Old wrappers, intended for mlir-vulkan-runner. Calls to these are generated
205+
// by LaunchFuncToVulkanCallsPass.
206+
//
207+
//===----------------------------------------------------------------------===//
208+
94209
/// Initializes `VulkanRuntimeManager` and returns a pointer to it.
95210
VULKAN_WRAPPER_SYMBOL_EXPORT void *initVulkan() {
96211
return new VulkanRuntimeManager();

0 commit comments

Comments
 (0)