adding lots of unit tests, improving image sampling, adding image sampling deref
This commit is contained in:
@@ -190,6 +190,12 @@ try rt.addSpecializationInfo(
|
||||
|
||||
Add specialization constants before calling the entry point.
|
||||
|
||||
To copy specialization constants between runtimes:
|
||||
|
||||
```zig
|
||||
try rt.copySpecializationConstantsFrom(allocator, &source_rt);
|
||||
```
|
||||
|
||||
## Entry points and barriers
|
||||
|
||||
For most shaders, `callEntryPoint` is enough:
|
||||
@@ -235,13 +241,50 @@ const image_api = spv.Runtime.ImageAPI{
|
||||
.writeImageInt4 = writeImageInt4,
|
||||
.sampleImageFloat4 = sampleImageFloat4,
|
||||
.sampleImageInt4 = sampleImageInt4,
|
||||
.sampleImageDref = sampleImageDref,
|
||||
.queryImageSize = queryImageSize,
|
||||
};
|
||||
|
||||
var rt = try spv.Runtime.init(allocator, &module, image_api);
|
||||
```
|
||||
|
||||
Each callback receives your driver-side image or sampler pointer and returns either a `Vec4` value or an error.
|
||||
Sample callbacks receive optional explicit LOD and an integer texel offset:
|
||||
|
||||
```zig
|
||||
fn sampleImageFloat4(
|
||||
driver_image: *anyopaque,
|
||||
driver_sampler: *anyopaque,
|
||||
dim: spv.SpvDim,
|
||||
x: f32,
|
||||
y: f32,
|
||||
z: f32,
|
||||
lod: ?f32,
|
||||
offset: spv.Runtime.ImageOffset,
|
||||
) spv.Runtime.RuntimeError!spv.Runtime.Vec4(f32) {
|
||||
_ = .{ driver_image, driver_sampler, dim, x, y, z, lod, offset };
|
||||
return .{ .x = 0, .y = 0, .z = 0, .w = 1 };
|
||||
}
|
||||
```
|
||||
|
||||
Depth-comparison samplers call `sampleImageDref` and return a scalar `f32`.
|
||||
|
||||
## Derivatives
|
||||
|
||||
Fragment shaders using derivative operations need derivative data on the source result:
|
||||
|
||||
```zig
|
||||
try rt.setDerivativeFromMemory(
|
||||
allocator,
|
||||
input_result,
|
||||
std.mem.asBytes(&dx),
|
||||
std.mem.asBytes(&dy),
|
||||
);
|
||||
|
||||
try rt.copyDerivative(allocator, dst_result, input_result);
|
||||
rt.clearDerivative(allocator, dst_result);
|
||||
```
|
||||
|
||||
For low-level integrations, `setDerivative` accepts interpreter `Value` objects directly.
|
||||
|
||||
---
|
||||
|
||||
@@ -294,7 +337,7 @@ ffi/SpirvInterpreter.h
|
||||
|
||||
static const unsigned char shader_source[] = {
|
||||
/* Shader bytecode */
|
||||
}
|
||||
};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
@@ -310,7 +353,7 @@ int main(void)
|
||||
* A zeroed image API is only safe when the shader does not execute image
|
||||
* load/store/sample/query operations.
|
||||
*/
|
||||
if(SpvInitRuntime(&runtime, module) != SPV_RESULT_SUCCESS)
|
||||
if(SpvInitRuntime(&runtime, module, (SpvImageAPI){0}) != SPV_RESULT_SUCCESS)
|
||||
return -1;
|
||||
|
||||
SpvWord main_entry_index;
|
||||
@@ -337,25 +380,13 @@ Write by result id:
|
||||
```c
|
||||
SpvWord pos_result = 0;
|
||||
|
||||
if (!CheckSpv(
|
||||
SpvGetResultByName(runtime, "pos", &pos_result),
|
||||
"SpvGetResultByName"))
|
||||
{
|
||||
if (SpvGetResultByName(runtime, "pos", &pos_result) != SPV_RESULT_SUCCESS)
|
||||
return 1;
|
||||
}
|
||||
|
||||
float pos[2] = {10.0f, 20.0f};
|
||||
|
||||
if (!CheckSpv(
|
||||
SpvWriteInput(
|
||||
runtime,
|
||||
(const SpvByte*)pos,
|
||||
sizeof(pos),
|
||||
pos_result),
|
||||
"SpvWriteInput"))
|
||||
{
|
||||
if (SpvWriteInput(runtime, (const SpvByte*)pos, sizeof(pos), pos_result) != SPV_RESULT_SUCCESS)
|
||||
return 1;
|
||||
}
|
||||
```
|
||||
|
||||
Write by input location:
|
||||
@@ -363,10 +394,8 @@ Write by input location:
|
||||
```c
|
||||
float uv[2] = {0.25f, 0.75f};
|
||||
|
||||
if (!SpvWriteInputLocation(runtime, (const SpvByte*)uv, sizeof(uv), 0))
|
||||
{
|
||||
if (SpvWriteInputLocation(runtime, (const SpvByte*)uv, sizeof(uv), 0) != SPV_RESULT_SUCCESS)
|
||||
return 1;
|
||||
}
|
||||
```
|
||||
|
||||
## Reading outputs from C
|
||||
@@ -411,7 +440,7 @@ PushConstants push_constants = {
|
||||
.scale = 2.0f,
|
||||
};
|
||||
|
||||
SpvPopulatePushConstants(runtime, (const SpvByte*)&push_constants, sizeof(push_constants);
|
||||
SpvPopulatePushConstants(runtime, (const SpvByte*)&push_constants, sizeof(push_constants));
|
||||
```
|
||||
|
||||
## Descriptor sets from C
|
||||
@@ -426,6 +455,13 @@ SpvWriteDescriptorSet(runtime, (const SpvByte*)buffer, buffer_size,
|
||||
|
||||
For non-array descriptors, use descriptor index `0`.
|
||||
|
||||
You can query a descriptor binding from a module:
|
||||
|
||||
```c
|
||||
SpvWord result = 0;
|
||||
SpvResult status = SpvModuleGetBindingResult(module, 0, 1, &result);
|
||||
```
|
||||
|
||||
After a shader writes through descriptor-backed memory, flush descriptor sets before reading the backing data:
|
||||
|
||||
```c
|
||||
@@ -449,6 +485,30 @@ SpvAddSpecializationInfo(runtime, entry, (const SpvByte*)&value, sizeof(value));
|
||||
|
||||
Add specialization constants before calling the entry point.
|
||||
|
||||
To duplicate specialization constants from another runtime:
|
||||
|
||||
```c
|
||||
SpvCopySpecializationConstantsFrom(runtime, source_runtime);
|
||||
```
|
||||
|
||||
## Derivatives from C
|
||||
|
||||
```c
|
||||
float dx[4] = {1.0f, 0.0f, 0.0f, 0.0f};
|
||||
float dy[4] = {0.0f, 1.0f, 0.0f, 0.0f};
|
||||
|
||||
SpvSetDerivativeFromMemory(
|
||||
runtime,
|
||||
result,
|
||||
(const SpvByte*)dx,
|
||||
sizeof(dx),
|
||||
(const SpvByte*)dy,
|
||||
sizeof(dy));
|
||||
|
||||
SpvCopyDerivative(runtime, dst_result, result);
|
||||
SpvClearDerivative(runtime, dst_result);
|
||||
```
|
||||
|
||||
## Barriers from C
|
||||
|
||||
For most shaders:
|
||||
@@ -499,6 +559,67 @@ static SpvResult ReadImageFloat4(
|
||||
}
|
||||
```
|
||||
|
||||
Sampling callbacks include an explicit-LOD flag/value and an offset:
|
||||
|
||||
```c
|
||||
static SpvResult SampleImageFloat4(
|
||||
void* driver_image,
|
||||
void* driver_sampler,
|
||||
SpvDim dim,
|
||||
float x,
|
||||
float y,
|
||||
float z,
|
||||
SpvBool has_lod,
|
||||
float lod,
|
||||
SpvImageOffset offset,
|
||||
SpvVec4f* dst)
|
||||
{
|
||||
(void)driver_image;
|
||||
(void)driver_sampler;
|
||||
(void)dim;
|
||||
(void)has_lod;
|
||||
(void)lod;
|
||||
(void)offset;
|
||||
|
||||
dst->x = x;
|
||||
dst->y = y;
|
||||
dst->z = z;
|
||||
dst->w = 1.0f;
|
||||
return SPV_RESULT_SUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
Depth-comparison samplers add `dref` and write one `float`:
|
||||
|
||||
```c
|
||||
static SpvResult SampleImageDref(
|
||||
void* driver_image,
|
||||
void* driver_sampler,
|
||||
SpvDim dim,
|
||||
float x,
|
||||
float y,
|
||||
float z,
|
||||
float dref,
|
||||
SpvBool has_lod,
|
||||
float lod,
|
||||
SpvImageOffset offset,
|
||||
float* dst)
|
||||
{
|
||||
(void)driver_image;
|
||||
(void)driver_sampler;
|
||||
(void)dim;
|
||||
(void)x;
|
||||
(void)y;
|
||||
(void)z;
|
||||
(void)has_lod;
|
||||
(void)lod;
|
||||
(void)offset;
|
||||
|
||||
*dst = dref;
|
||||
return SPV_RESULT_SUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
The image API table contains these callbacks:
|
||||
|
||||
```c
|
||||
@@ -509,6 +630,7 @@ SpvImageAPI image_api = {
|
||||
.SpvWriteImageInt4 = WriteImageInt4,
|
||||
.SpvSampleImageFloat4 = SampleImageFloat4,
|
||||
.SpvSampleImageInt4 = SampleImageInt4,
|
||||
.SpvSampleImageDref = SampleImageDref,
|
||||
.SpvQueryImageSize = QueryImageSize,
|
||||
};
|
||||
```
|
||||
|
||||
+16
-3
@@ -497,12 +497,20 @@ typedef struct
|
||||
unsigned int w;
|
||||
} SpvVec4u;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
int z;
|
||||
} SpvImageOffset;
|
||||
|
||||
typedef SpvResult (*SpvReadImageFloat4_PFN)(void* driver_image, SpvDim dim, int x, int y, int z, SpvVec4f* dst);
|
||||
typedef SpvResult (*SpvReadImageInt4_PFN)(void* driver_image, SpvDim dim, int x, int y, int z, SpvVec4u* dst);
|
||||
typedef SpvResult (*SpvWriteImageFloat4_PFN)(void* driver_image, SpvDim dim, int x, int y, int z, SpvVec4f src);
|
||||
typedef SpvResult (*SpvWriteImageInt4_PFN)(void* driver_image, SpvDim dim, int x, int y, int z, SpvVec4u src);
|
||||
typedef SpvResult (*SpvSampleImageFloat4_PFN)(void* driver_image, void* driver_sampler, SpvDim dim, float x, float y, float z, float lod, SpvVec4f* dst);
|
||||
typedef SpvResult (*SpvSampleImageInt4_PFN)(void* driver_image, void* driver_sampler, SpvDim dim, float x, float y, float z, float lod, SpvVec4u* dst);
|
||||
typedef SpvResult (*SpvSampleImageFloat4_PFN)(void* driver_image, void* driver_sampler, SpvDim dim, float x, float y, float z, SpvBool has_lod, float lod, SpvImageOffset offset, SpvVec4f* dst);
|
||||
typedef SpvResult (*SpvSampleImageInt4_PFN)(void* driver_image, void* driver_sampler, SpvDim dim, float x, float y, float z, SpvBool has_lod, float lod, SpvImageOffset offset, SpvVec4u* dst);
|
||||
typedef SpvResult (*SpvSampleImageDref_PFN)(void* driver_image, void* driver_sampler, SpvDim dim, float x, float y, float z, float dref, SpvBool has_lod, float lod, SpvImageOffset offset, float* dst);
|
||||
typedef SpvResult (*SpvQueryImageSize_PFN)(void* driver_image, SpvDim dim, SpvBool arrayed, SpvVec4u* dst);
|
||||
|
||||
typedef struct
|
||||
@@ -513,6 +521,7 @@ typedef struct
|
||||
SpvWriteImageInt4_PFN SpvWriteImageInt4;
|
||||
SpvSampleImageFloat4_PFN SpvSampleImageFloat4;
|
||||
SpvSampleImageInt4_PFN SpvSampleImageInt4;
|
||||
SpvSampleImageDref_PFN SpvSampleImageDref;
|
||||
SpvQueryImageSize_PFN SpvQueryImageSize;
|
||||
} SpvImageAPI;
|
||||
|
||||
@@ -523,6 +532,7 @@ SPV_API SpvResult SpvInitModule(SpvModule* module, const SpvWord* source, SpvSiz
|
||||
SPV_API void SpvDeinitModule(SpvModule module);
|
||||
|
||||
SPV_API SpvModuleReflectionInfos SpvModuleGetReflectionInfos(SpvModule module);
|
||||
SPV_API SpvResult SpvModuleGetBindingResult(SpvModule module, SpvWord set, SpvWord binding, SpvWord* result);
|
||||
|
||||
SPV_API SpvResult SpvInitRuntime(SpvRuntime* runtime, SpvModule module, SpvImageAPI image_api);
|
||||
SPV_API void SpvDeinitRuntime(SpvRuntime runtime);
|
||||
@@ -530,6 +540,10 @@ SPV_API void SpvDeinitRuntime(SpvRuntime runtime);
|
||||
SPV_API SpvResult SpvFlushDescriptorSets(SpvRuntime runtime);
|
||||
|
||||
SPV_API SpvResult SpvAddSpecializationInfo(SpvRuntime runtime, SpvRuntimeSpecializationEntry entry, const SpvByte* data, SpvSize data_size);
|
||||
SPV_API SpvResult SpvCopySpecializationConstantsFrom(SpvRuntime runtime, SpvRuntime other);
|
||||
SPV_API SpvResult SpvSetDerivativeFromMemory(SpvRuntime runtime, SpvWord result, const SpvByte* dx, SpvSize dx_size, const SpvByte* dy, SpvSize dy_size);
|
||||
SPV_API void SpvClearDerivative(SpvRuntime runtime, SpvWord result);
|
||||
SPV_API SpvResult SpvCopyDerivative(SpvRuntime runtime, SpvWord dst, SpvWord src);
|
||||
SPV_API SpvResult SpvPopulatePushConstants(SpvRuntime runtime, const SpvByte* data, SpvSize data_size);
|
||||
|
||||
SPV_API SpvResult SpvGetResultByName(SpvRuntime runtime, const char* name, SpvWord* result);
|
||||
@@ -540,7 +554,6 @@ SPV_API SpvResult SpvGetEntryPointByName(SpvRuntime runtime, const char* name, S
|
||||
SPV_API SpvResult SpvGetResultMemorySize(SpvRuntime runtime, SpvWord result, SpvSize* size);
|
||||
SPV_API SpvResult SpvGetInputLocationMemorySize(SpvRuntime runtime, SpvWord location, SpvSize* size);
|
||||
SPV_API SpvResult SpvGetResultPrimitiveType(SpvRuntime runtime, SpvWord result, SpvPrimitiveType* primitive_type);
|
||||
SPV_API SpvBool SpvResultIsInteger(SpvRuntime runtime, SpvWord result);
|
||||
SPV_API SpvBool SpvHasResultDecoration(SpvRuntime runtime, SpvWord result, SpvDecoration decoration);
|
||||
|
||||
SPV_API SpvResult SpvCallEntryPoint(SpvRuntime runtime, SpvWord entry_point_index);
|
||||
|
||||
@@ -68,3 +68,8 @@ export fn SpvModuleGetReflectionInfos(module: *spv.Module) callconv(.c) Reflecti
|
||||
.has_control_barriers = if (module.reflection_infos.has_control_barriers) 1 else 0,
|
||||
};
|
||||
}
|
||||
|
||||
export fn SpvModuleGetBindingResult(module: *const spv.Module, set: ffi.SpvCWord, binding: ffi.SpvCWord, result: *ffi.SpvCWord) callconv(.c) ffi.Result {
|
||||
result.* = module.getBindingResult(@intCast(set), @intCast(binding)) orelse return .NotFound;
|
||||
return .Success;
|
||||
}
|
||||
|
||||
+57
-10
@@ -39,12 +39,19 @@ const Vec4u = extern struct {
|
||||
w: c_uint,
|
||||
};
|
||||
|
||||
const ImageOffset = extern struct {
|
||||
x: c_int,
|
||||
y: c_int,
|
||||
z: c_int,
|
||||
};
|
||||
|
||||
const readImageFloat4_PFN = *const fn (driver_image: ?*anyopaque, dim: spv.spv.SpvDim, x: c_int, y: c_int, z: c_int, dst: *Vec4f) callconv(.c) ffi.Result;
|
||||
const readImageInt4_PFN = *const fn (driver_image: ?*anyopaque, dim: spv.spv.SpvDim, x: c_int, y: c_int, z: c_int, dst: *Vec4u) callconv(.c) ffi.Result;
|
||||
const writeImageFloat4_PFN = *const fn (driver_image: ?*anyopaque, dim: spv.spv.SpvDim, x: c_int, y: c_int, z: c_int, src: Vec4f) callconv(.c) ffi.Result;
|
||||
const writeImageInt4_PFN = *const fn (driver_image: ?*anyopaque, dim: spv.spv.SpvDim, x: c_int, y: c_int, z: c_int, src: Vec4u) callconv(.c) ffi.Result;
|
||||
const sampleImageFloat4_PFN = *const fn (driver_image: ?*anyopaque, driver_sampler: ?*anyopaque, dim: spv.spv.SpvDim, x: f32, y: f32, z: f32, lod: f32, dst: *Vec4f) callconv(.c) ffi.Result;
|
||||
const sampleImageInt4_PFN = *const fn (driver_image: ?*anyopaque, driver_sampler: ?*anyopaque, dim: spv.spv.SpvDim, x: f32, y: f32, z: f32, lod: f32, dst: *Vec4u) callconv(.c) ffi.Result;
|
||||
const sampleImageFloat4_PFN = *const fn (driver_image: ?*anyopaque, driver_sampler: ?*anyopaque, dim: spv.spv.SpvDim, x: f32, y: f32, z: f32, has_lod: ffi.SpvCBool, lod: f32, offset: ImageOffset, dst: *Vec4f) callconv(.c) ffi.Result;
|
||||
const sampleImageInt4_PFN = *const fn (driver_image: ?*anyopaque, driver_sampler: ?*anyopaque, dim: spv.spv.SpvDim, x: f32, y: f32, z: f32, has_lod: ffi.SpvCBool, lod: f32, offset: ImageOffset, dst: *Vec4u) callconv(.c) ffi.Result;
|
||||
const sampleImageDref_PFN = *const fn (driver_image: ?*anyopaque, driver_sampler: ?*anyopaque, dim: spv.spv.SpvDim, x: f32, y: f32, z: f32, dref: f32, has_lod: ffi.SpvCBool, lod: f32, offset: ImageOffset, dst: *f32) callconv(.c) ffi.Result;
|
||||
const queryImageSize_PFN = *const fn (driver_image: ?*anyopaque, dim: spv.spv.SpvDim, arrayed: ffi.SpvCBool, dst: *Vec4u) callconv(.c) ffi.Result;
|
||||
|
||||
const ImageAPI = extern struct {
|
||||
@@ -54,6 +61,7 @@ const ImageAPI = extern struct {
|
||||
writeImageInt4: writeImageInt4_PFN,
|
||||
sampleImageFloat4: sampleImageFloat4_PFN,
|
||||
sampleImageInt4: sampleImageInt4_PFN,
|
||||
sampleImageDref: sampleImageDref_PFN,
|
||||
queryImageSize: queryImageSize_PFN,
|
||||
};
|
||||
|
||||
@@ -159,11 +167,19 @@ const ImageAPIBridge = struct {
|
||||
try fromCResult(result);
|
||||
}
|
||||
|
||||
fn sampleImageFloat4(driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.spv.SpvDim, x: f32, y: f32, z: f32, lod: ?f32) spv.Runtime.RuntimeError!spv.Runtime.Vec4(f32) {
|
||||
fn toCImageOffset(offset: spv.Runtime.ImageOffset) ImageOffset {
|
||||
return .{
|
||||
.x = @intCast(offset.x),
|
||||
.y = @intCast(offset.y),
|
||||
.z = @intCast(offset.z),
|
||||
};
|
||||
}
|
||||
|
||||
fn sampleImageFloat4(driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.spv.SpvDim, x: f32, y: f32, z: f32, lod: ?f32, offset: spv.Runtime.ImageOffset) spv.Runtime.RuntimeError!spv.Runtime.Vec4(f32) {
|
||||
const image_api = try getImageAPI();
|
||||
|
||||
var dst: Vec4f = undefined;
|
||||
const result = image_api.sampleImageFloat4(driver_image, driver_sampler, dim, x, y, z, lod orelse 0.0, &dst);
|
||||
const result = image_api.sampleImageFloat4(driver_image, driver_sampler, dim, x, y, z, if (lod == null) 0 else 1, lod orelse 0.0, toCImageOffset(offset), &dst);
|
||||
|
||||
try fromCResult(result);
|
||||
|
||||
@@ -175,11 +191,11 @@ const ImageAPIBridge = struct {
|
||||
};
|
||||
}
|
||||
|
||||
fn sampleImageInt4(driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.spv.SpvDim, x: f32, y: f32, z: f32, lod: ?f32) spv.Runtime.RuntimeError!spv.Runtime.Vec4(u32) {
|
||||
fn sampleImageInt4(driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.spv.SpvDim, x: f32, y: f32, z: f32, lod: ?f32, offset: spv.Runtime.ImageOffset) spv.Runtime.RuntimeError!spv.Runtime.Vec4(u32) {
|
||||
const image_api = try getImageAPI();
|
||||
|
||||
var dst: Vec4u = undefined;
|
||||
const result = image_api.sampleImageInt4(driver_image, driver_sampler, dim, x, y, z, lod orelse 0.0, &dst);
|
||||
const result = image_api.sampleImageInt4(driver_image, driver_sampler, dim, x, y, z, if (lod == null) 0 else 1, lod orelse 0.0, toCImageOffset(offset), &dst);
|
||||
|
||||
try fromCResult(result);
|
||||
|
||||
@@ -191,6 +207,17 @@ const ImageAPIBridge = struct {
|
||||
};
|
||||
}
|
||||
|
||||
fn sampleImageDref(driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.spv.SpvDim, x: f32, y: f32, z: f32, dref: f32, lod: ?f32, offset: spv.Runtime.ImageOffset) spv.Runtime.RuntimeError!f32 {
|
||||
const image_api = try getImageAPI();
|
||||
|
||||
var dst: f32 = undefined;
|
||||
const result = image_api.sampleImageDref(driver_image, driver_sampler, dim, x, y, z, dref, if (lod == null) 0 else 1, lod orelse 0.0, toCImageOffset(offset), &dst);
|
||||
|
||||
try fromCResult(result);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
fn queryImageSize(driver_image: *anyopaque, dim: spv.spv.SpvDim, arrayed: bool) spv.Runtime.RuntimeError!spv.Runtime.Vec4(u32) {
|
||||
const image_api = try getImageAPI();
|
||||
|
||||
@@ -229,6 +256,7 @@ export fn SpvInitRuntime(rt: **RuntimeWrapper, module: *spv.Module, image_api: I
|
||||
.writeImageInt4 = ImageAPIBridge.writeImageInt4,
|
||||
.sampleImageFloat4 = ImageAPIBridge.sampleImageFloat4,
|
||||
.sampleImageInt4 = ImageAPIBridge.sampleImageInt4,
|
||||
.sampleImageDref = ImageAPIBridge.sampleImageDref,
|
||||
.queryImageSize = ImageAPIBridge.queryImageSize,
|
||||
},
|
||||
) catch |err| {
|
||||
@@ -264,6 +292,29 @@ export fn SpvAddSpecializationInfo(rt: *RuntimeWrapper, entry: CSpecializationEn
|
||||
return .Success;
|
||||
}
|
||||
|
||||
export fn SpvCopySpecializationConstantsFrom(rt: *RuntimeWrapper, other: *const RuntimeWrapper) callconv(.c) ffi.Result {
|
||||
const allocator = std.heap.c_allocator;
|
||||
rt.rt.copySpecializationConstantsFrom(allocator, &other.rt) catch |err| return toCResult(err);
|
||||
return .Success;
|
||||
}
|
||||
|
||||
export fn SpvSetDerivativeFromMemory(rt: *RuntimeWrapper, result: spv.SpvWord, dx: [*]const u8, dx_size: c_ulong, dy: [*]const u8, dy_size: c_ulong) callconv(.c) ffi.Result {
|
||||
const allocator = std.heap.c_allocator;
|
||||
rt.rt.setDerivativeFromMemory(allocator, result, dx[0..dx_size], dy[0..dy_size]) catch |err| return toCResult(err);
|
||||
return .Success;
|
||||
}
|
||||
|
||||
export fn SpvClearDerivative(rt: *RuntimeWrapper, result: spv.SpvWord) callconv(.c) void {
|
||||
const allocator = std.heap.c_allocator;
|
||||
rt.rt.clearDerivative(allocator, result);
|
||||
}
|
||||
|
||||
export fn SpvCopyDerivative(rt: *RuntimeWrapper, dst: spv.SpvWord, src: spv.SpvWord) callconv(.c) ffi.Result {
|
||||
const allocator = std.heap.c_allocator;
|
||||
rt.rt.copyDerivative(allocator, dst, src) catch |err| return toCResult(err);
|
||||
return .Success;
|
||||
}
|
||||
|
||||
export fn SpvPopulatePushConstants(rt: *RuntimeWrapper, data: [*]const u8, data_size: c_ulong) callconv(.c) ffi.Result {
|
||||
rt.rt.populatePushConstants(data[0..data_size]) catch |err| return toCResult(err);
|
||||
return .Success;
|
||||
@@ -319,10 +370,6 @@ export fn SpvGetResultPrimitiveType(rt: *RuntimeWrapper, result: spv.SpvWord, pr
|
||||
return .Success;
|
||||
}
|
||||
|
||||
export fn SpvResultIsInteger(rt: *RuntimeWrapper, result: spv.SpvWord) callconv(.c) c_int {
|
||||
return if (rt.rt.resultIsInteger(result)) 1 else 0;
|
||||
}
|
||||
|
||||
export fn SpvHasResultDecoration(rt: *RuntimeWrapper, result: spv.SpvWord, decoration: spv.spv.SpvDecoration) callconv(.c) c_int {
|
||||
return if (rt.rt.hasResultDecoration(result, decoration)) 1 else 0;
|
||||
}
|
||||
|
||||
+8
-2
@@ -478,6 +478,12 @@ pub fn getMemberCounts(self: *const Self) usize {
|
||||
}
|
||||
|
||||
pub inline fn flushPtr(self: *Self, allocator: std.mem.Allocator) RuntimeError!void {
|
||||
const value = self.getValue() catch return;
|
||||
try value.flushPtr(allocator);
|
||||
if (self.variant) |*variant| switch (variant.*) {
|
||||
.Variable => |*v| try v.value.flushPtr(allocator),
|
||||
.AccessChain => |*a| {
|
||||
if (!std.mem.allEqual(u8, std.mem.asBytes(&a.value), 0xaa))
|
||||
try a.value.flushPtr(allocator);
|
||||
},
|
||||
else => {},
|
||||
};
|
||||
}
|
||||
|
||||
+13
-19
@@ -81,13 +81,20 @@ pub fn Vec4(comptime T: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
pub const ImageOffset = struct {
|
||||
x: i32 = 0,
|
||||
y: i32 = 0,
|
||||
z: i32 = 0,
|
||||
};
|
||||
|
||||
pub const ImageAPI = struct {
|
||||
readImageFloat4: *const fn (driver_image: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32) RuntimeError!Vec4(f32),
|
||||
readImageInt4: *const fn (driver_image: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32) RuntimeError!Vec4(u32),
|
||||
writeImageFloat4: *const fn (driver_image: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, pixel: Vec4(f32)) RuntimeError!void,
|
||||
writeImageInt4: *const fn (driver_image: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, pixel: Vec4(u32)) RuntimeError!void,
|
||||
sampleImageFloat4: *const fn (driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32, lod: ?f32) RuntimeError!Vec4(f32),
|
||||
sampleImageInt4: *const fn (driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32, lod: ?f32) RuntimeError!Vec4(u32),
|
||||
sampleImageFloat4: *const fn (driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32, lod: ?f32, offset: ImageOffset) RuntimeError!Vec4(f32),
|
||||
sampleImageInt4: *const fn (driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32, lod: ?f32, offset: ImageOffset) RuntimeError!Vec4(u32),
|
||||
sampleImageDref: *const fn (driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32, dref: f32, lod: ?f32, offset: ImageOffset) RuntimeError!f32,
|
||||
queryImageSize: *const fn (driver_image: *anyopaque, dim: spv.SpvDim, arrayed: bool) RuntimeError!Vec4(u32),
|
||||
};
|
||||
|
||||
@@ -287,13 +294,6 @@ pub fn getResultPrimitiveType(self: *const Self, result: SpvWord) RuntimeError!P
|
||||
return (try self.results[result].getConstValue()).resolvePrimitiveType();
|
||||
}
|
||||
|
||||
pub fn resultIsInteger(self: *const Self, result: SpvWord) bool {
|
||||
return switch (self.getResultPrimitiveType(result) catch return false) {
|
||||
.SInt, .UInt => true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn dumpResultsTable(self: *Self, allocator: std.mem.Allocator, writer: *std.Io.Writer) RuntimeError!void {
|
||||
const dump = pretty.dump(allocator, self.results, .{
|
||||
.tab_size = 4,
|
||||
@@ -618,17 +618,10 @@ pub fn resetInvocation(self: *Self, allocator: std.mem.Allocator) void {
|
||||
if (result.variant) |*variant| {
|
||||
switch (variant.*) {
|
||||
.AccessChain => |*access_chain| {
|
||||
access_chain.value.flushPtr(allocator) catch {};
|
||||
},
|
||||
else => {},
|
||||
if (std.mem.allEqual(u8, std.mem.asBytes(&access_chain.value), 0xaa)) {
|
||||
result.variant = null;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (self.results) |*result| {
|
||||
if (result.variant) |*variant| {
|
||||
switch (variant.*) {
|
||||
.AccessChain => |*access_chain| {
|
||||
access_chain.value.deinit(allocator);
|
||||
allocator.free(access_chain.indexes);
|
||||
result.variant = null;
|
||||
@@ -645,6 +638,7 @@ pub fn resetInvocation(self: *Self, allocator: std.mem.Allocator) void {
|
||||
}
|
||||
|
||||
fn reset(self: *Self) void {
|
||||
self.it = self.mod.it;
|
||||
self.function_stack.clearRetainingCapacity();
|
||||
self.current_parameter_index = 0;
|
||||
self.current_function = null;
|
||||
|
||||
+404
-81
@@ -74,8 +74,14 @@ const ImageOp = enum {
|
||||
QuerySizeLod,
|
||||
Read,
|
||||
Resolve,
|
||||
SampleDrefExplicitLod,
|
||||
SampleDrefImplicitLod,
|
||||
SampleExplicitLod,
|
||||
SampleImplicitLod,
|
||||
SampleProjDrefExplicitLod,
|
||||
SampleProjDrefImplicitLod,
|
||||
SampleProjExplicitLod,
|
||||
SampleProjImplicitLod,
|
||||
Write,
|
||||
};
|
||||
|
||||
@@ -198,6 +204,12 @@ pub const SetupDispatcher = block: {
|
||||
.ImageRead = autoSetupConstant,
|
||||
.ImageSampleExplicitLod = autoSetupConstant,
|
||||
.ImageSampleImplicitLod = autoSetupConstant,
|
||||
.ImageSampleDrefExplicitLod = autoSetupConstant,
|
||||
.ImageSampleDrefImplicitLod = autoSetupConstant,
|
||||
.ImageSampleProjDrefExplicitLod = autoSetupConstant,
|
||||
.ImageSampleProjDrefImplicitLod = autoSetupConstant,
|
||||
.ImageSampleProjExplicitLod = autoSetupConstant,
|
||||
.ImageSampleProjImplicitLod = autoSetupConstant,
|
||||
.ImageTexelPointer = autoSetupConstant,
|
||||
.InBoundsAccessChain = setupAccessChain,
|
||||
.IsFinite = autoSetupConstant,
|
||||
@@ -366,6 +378,12 @@ pub fn initRuntimeDispatcher() void {
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageRead)] = ImageEngine(.Read).op;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageSampleExplicitLod)] = ImageEngine(.SampleExplicitLod).op;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageSampleImplicitLod)] = ImageEngine(.SampleImplicitLod).op;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageSampleDrefExplicitLod)] = ImageEngine(.SampleDrefExplicitLod).op;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageSampleDrefImplicitLod)] = ImageEngine(.SampleDrefImplicitLod).op;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageSampleProjDrefExplicitLod)] = ImageEngine(.SampleProjDrefExplicitLod).op;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageSampleProjDrefImplicitLod)] = ImageEngine(.SampleProjDrefImplicitLod).op;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageSampleProjExplicitLod)] = ImageEngine(.SampleProjExplicitLod).op;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageSampleProjImplicitLod)] = ImageEngine(.SampleProjImplicitLod).op;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageTexelPointer)] = opImageTexelPointer;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageWrite)] = ImageEngine(.Write).op;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.InBoundsAccessChain)] = opAccessChain;
|
||||
@@ -1332,6 +1350,94 @@ fn ImageEngine(comptime Op: ImageOp) type {
|
||||
};
|
||||
}
|
||||
|
||||
fn imageOperandPresent(image_operands: SpvWord, mask: spv.SpvImageOperandsMask) bool {
|
||||
return (image_operands & @intFromEnum(mask)) != 0;
|
||||
}
|
||||
|
||||
fn readImageOffset(rt: *Runtime, offset_id: SpvWord) RuntimeError!Runtime.ImageOffset {
|
||||
const offset = try rt.results[offset_id].getValue();
|
||||
return .{
|
||||
.x = try readStorageCoordLane(offset, 0),
|
||||
.y = readStorageCoordLane(offset, 1) catch 0,
|
||||
.z = readStorageCoordLane(offset, 2) catch 0,
|
||||
};
|
||||
}
|
||||
|
||||
fn valueLaneCount(value: *const Value) RuntimeError!usize {
|
||||
return switch (value.*) {
|
||||
.Vector => |lanes| lanes.len,
|
||||
.Vector2f32, .Vector2i32, .Vector2u32 => 2,
|
||||
.Vector3f32, .Vector3i32, .Vector3u32 => 3,
|
||||
.Vector4f32, .Vector4i32, .Vector4u32 => 4,
|
||||
.Float, .Int => 1,
|
||||
else => RuntimeError.InvalidValueType,
|
||||
};
|
||||
}
|
||||
|
||||
fn readProjectedSampleCoords(coordinate: *const Value) RuntimeError!struct { x: f32, y: f32, z: f32 } {
|
||||
const lane_count = try valueLaneCount(coordinate);
|
||||
if (lane_count < 2)
|
||||
return RuntimeError.InvalidSpirV;
|
||||
|
||||
const q_lane = lane_count - 1;
|
||||
const q = try readProjectionDivisor(coordinate);
|
||||
return .{
|
||||
.x = try readSampleCoordLane(coordinate, 0) / q,
|
||||
.y = if (q_lane > 1) (readSampleCoordLane(coordinate, 1) catch 0.0) / q else 0.0,
|
||||
.z = if (q_lane > 2) (readSampleCoordLane(coordinate, 2) catch 0.0) / q else 0.0,
|
||||
};
|
||||
}
|
||||
|
||||
fn readProjectionDivisor(coordinate: *const Value) RuntimeError!f32 {
|
||||
const lane_count = try valueLaneCount(coordinate);
|
||||
if (lane_count < 2)
|
||||
return RuntimeError.InvalidSpirV;
|
||||
return readSampleCoordLane(coordinate, lane_count - 1);
|
||||
}
|
||||
|
||||
const ParsedImageOperands = struct {
|
||||
lod: ?f32 = null,
|
||||
offset: Runtime.ImageOffset = .{},
|
||||
};
|
||||
|
||||
fn parseImageOperands(rt: *Runtime, image_operands: SpvWord) RuntimeError!ParsedImageOperands {
|
||||
var parsed: ParsedImageOperands = .{};
|
||||
|
||||
if (imageOperandPresent(image_operands, .BiasMask)) {
|
||||
_ = try rt.it.next();
|
||||
}
|
||||
if (imageOperandPresent(image_operands, .LodMask)) {
|
||||
parsed.lod = try readFloatLane(try rt.results[try rt.it.next()].getValue(), 0);
|
||||
}
|
||||
if (imageOperandPresent(image_operands, .GradMask)) {
|
||||
_ = try rt.it.next();
|
||||
_ = try rt.it.next();
|
||||
}
|
||||
if (imageOperandPresent(image_operands, .ConstOffsetMask) or imageOperandPresent(image_operands, .OffsetMask)) {
|
||||
parsed.offset = try readImageOffset(rt, try rt.it.next());
|
||||
}
|
||||
if (imageOperandPresent(image_operands, .ConstOffsetsMask)) {
|
||||
_ = try rt.it.next();
|
||||
}
|
||||
if (imageOperandPresent(image_operands, .SampleMask)) {
|
||||
_ = try rt.it.next();
|
||||
}
|
||||
if (imageOperandPresent(image_operands, .MinLodMask)) {
|
||||
_ = try rt.it.next();
|
||||
}
|
||||
if (imageOperandPresent(image_operands, .MakeTexelAvailableMask)) {
|
||||
_ = try rt.it.next();
|
||||
}
|
||||
if (imageOperandPresent(image_operands, .MakeTexelVisibleMask)) {
|
||||
_ = try rt.it.next();
|
||||
}
|
||||
if (imageOperandPresent(image_operands, .OffsetsMask)) {
|
||||
_ = try rt.it.next();
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
fn writeFloatTexel(dst: *Value, texel: Runtime.Vec4(f32)) RuntimeError!void {
|
||||
switch (dst.*) {
|
||||
.Vector4f32 => |*v| v.* = .{ texel.x, texel.y, texel.z, texel.w },
|
||||
@@ -1377,6 +1483,20 @@ fn ImageEngine(comptime Op: ImageOp) type {
|
||||
}
|
||||
}
|
||||
|
||||
fn writeFloatScalar(dst: *Value, value: f32) RuntimeError!void {
|
||||
switch (dst.*) {
|
||||
.Float => |*f| f.value.float32 = value,
|
||||
.Vector => |lanes| {
|
||||
if (lanes.len != 1) return RuntimeError.InvalidValueType;
|
||||
switch (lanes[0]) {
|
||||
.Float => |*f| f.value.float32 = value,
|
||||
else => return RuntimeError.InvalidValueType,
|
||||
}
|
||||
},
|
||||
else => return RuntimeError.InvalidValueType,
|
||||
}
|
||||
}
|
||||
|
||||
fn readImage(rt: *Runtime, dst: *Value, driver_image: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32) RuntimeError!void {
|
||||
switch (dst.*) {
|
||||
.Vector4f32,
|
||||
@@ -1405,25 +1525,25 @@ fn ImageEngine(comptime Op: ImageOp) type {
|
||||
}
|
||||
}
|
||||
|
||||
fn sampleImageImplicitLod(rt: *Runtime, dst: *Value, driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32) RuntimeError!void {
|
||||
fn sampleImageImplicitLod(rt: *Runtime, dst: *Value, driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32, offset: Runtime.ImageOffset) RuntimeError!void {
|
||||
switch (dst.*) {
|
||||
.Vector4f32,
|
||||
.Vector3f32,
|
||||
.Vector2f32,
|
||||
=> try writeFloatTexel(dst, try rt.image_api.sampleImageFloat4(driver_image, driver_sampler, dim, x, y, z, null)),
|
||||
=> try writeFloatTexel(dst, try rt.image_api.sampleImageFloat4(driver_image, driver_sampler, dim, x, y, z, null, offset)),
|
||||
.Vector4i32,
|
||||
.Vector3i32,
|
||||
.Vector2i32,
|
||||
.Vector4u32,
|
||||
.Vector3u32,
|
||||
.Vector2u32,
|
||||
=> try writeIntTexel(dst, try rt.image_api.sampleImageInt4(driver_image, driver_sampler, dim, x, y, z, null)),
|
||||
=> try writeIntTexel(dst, try rt.image_api.sampleImageInt4(driver_image, driver_sampler, dim, x, y, z, null, offset)),
|
||||
|
||||
.Vector => |lanes| {
|
||||
if (lanes.len == 0) return RuntimeError.InvalidSpirV;
|
||||
switch (lanes[0]) {
|
||||
.Float => try writeFloatTexel(dst, try rt.image_api.sampleImageFloat4(driver_image, driver_sampler, dim, x, y, z, null)),
|
||||
.Int => try writeIntTexel(dst, try rt.image_api.sampleImageInt4(driver_image, driver_sampler, dim, x, y, z, null)),
|
||||
.Float => try writeFloatTexel(dst, try rt.image_api.sampleImageFloat4(driver_image, driver_sampler, dim, x, y, z, null, offset)),
|
||||
.Int => try writeIntTexel(dst, try rt.image_api.sampleImageInt4(driver_image, driver_sampler, dim, x, y, z, null, offset)),
|
||||
else => return RuntimeError.InvalidValueType,
|
||||
}
|
||||
},
|
||||
@@ -1445,6 +1565,7 @@ fn ImageEngine(comptime Op: ImageOp) type {
|
||||
x: f32,
|
||||
y: f32,
|
||||
z: f32,
|
||||
offset: Runtime.ImageOffset,
|
||||
) RuntimeError!void {
|
||||
const coord_derivative = rt.derivatives.get(coordinate_id) orelse {
|
||||
rt.clearDerivative(allocator, result_id);
|
||||
@@ -1463,8 +1584,8 @@ fn ImageEngine(comptime Op: ImageOp) type {
|
||||
const coord_dy_y = readSampleCoordLane(&coord_derivative.dy, 1) catch 0.0;
|
||||
const coord_dy_z = readSampleCoordLane(&coord_derivative.dy, 2) catch 0.0;
|
||||
|
||||
try sampleImageImplicitLod(rt, &dx_sample, driver_image, driver_sampler, dim, x + coord_dx_x, y + coord_dx_y, z + coord_dx_z);
|
||||
try sampleImageImplicitLod(rt, &dy_sample, driver_image, driver_sampler, dim, x + coord_dy_x, y + coord_dy_y, z + coord_dy_z);
|
||||
try sampleImageImplicitLod(rt, &dx_sample, driver_image, driver_sampler, dim, x + coord_dx_x, y + coord_dx_y, z + coord_dx_z, offset);
|
||||
try sampleImageImplicitLod(rt, &dy_sample, driver_image, driver_sampler, dim, x + coord_dy_x, y + coord_dy_y, z + coord_dy_z, offset);
|
||||
|
||||
var dx = try Value.init(allocator, rt.results, result_type_word, false);
|
||||
defer dx.deinit(allocator);
|
||||
@@ -1491,25 +1612,25 @@ fn ImageEngine(comptime Op: ImageOp) type {
|
||||
try rt.setDerivative(allocator, result_id, &dx, &dy);
|
||||
}
|
||||
|
||||
fn sampleImageExplicitLod(rt: *Runtime, dst: *Value, driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32, lod: ?f32) RuntimeError!void {
|
||||
fn sampleImageExplicitLod(rt: *Runtime, dst: *Value, driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32, lod: ?f32, offset: Runtime.ImageOffset) RuntimeError!void {
|
||||
switch (dst.*) {
|
||||
.Vector4f32,
|
||||
.Vector3f32,
|
||||
.Vector2f32,
|
||||
=> try writeFloatTexel(dst, try rt.image_api.sampleImageFloat4(driver_image, driver_sampler, dim, x, y, z, lod)),
|
||||
=> try writeFloatTexel(dst, try rt.image_api.sampleImageFloat4(driver_image, driver_sampler, dim, x, y, z, lod, offset)),
|
||||
.Vector4i32,
|
||||
.Vector3i32,
|
||||
.Vector2i32,
|
||||
.Vector4u32,
|
||||
.Vector3u32,
|
||||
.Vector2u32,
|
||||
=> try writeIntTexel(dst, try rt.image_api.sampleImageInt4(driver_image, driver_sampler, dim, x, y, z, lod)),
|
||||
=> try writeIntTexel(dst, try rt.image_api.sampleImageInt4(driver_image, driver_sampler, dim, x, y, z, lod, offset)),
|
||||
|
||||
.Vector => |lanes| {
|
||||
if (lanes.len == 0) return RuntimeError.InvalidSpirV;
|
||||
switch (lanes[0]) {
|
||||
.Float => try writeFloatTexel(dst, try rt.image_api.sampleImageFloat4(driver_image, driver_sampler, dim, x, y, z, lod)),
|
||||
.Int => try writeIntTexel(dst, try rt.image_api.sampleImageInt4(driver_image, driver_sampler, dim, x, y, z, lod)),
|
||||
.Float => try writeFloatTexel(dst, try rt.image_api.sampleImageFloat4(driver_image, driver_sampler, dim, x, y, z, lod, offset)),
|
||||
.Int => try writeIntTexel(dst, try rt.image_api.sampleImageInt4(driver_image, driver_sampler, dim, x, y, z, lod, offset)),
|
||||
else => return RuntimeError.InvalidValueType,
|
||||
}
|
||||
},
|
||||
@@ -1518,6 +1639,10 @@ fn ImageEngine(comptime Op: ImageOp) type {
|
||||
}
|
||||
}
|
||||
|
||||
fn sampleImageDref(rt: *Runtime, dst: *Value, driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32, dref: f32, lod: ?f32, offset: Runtime.ImageOffset) RuntimeError!void {
|
||||
try writeFloatScalar(dst, try rt.image_api.sampleImageDref(driver_image, driver_sampler, dim, x, y, z, dref, lod, offset));
|
||||
}
|
||||
|
||||
fn writeImage(rt: *Runtime, texel: *const Value, driver_image: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32) RuntimeError!void {
|
||||
switch (texel.*) {
|
||||
.Float,
|
||||
@@ -1629,11 +1754,20 @@ fn ImageEngine(comptime Op: ImageOp) type {
|
||||
try readImage(rt, dst, image_operand.driver_image, image_operand.dim, x, y, z);
|
||||
},
|
||||
|
||||
.SampleImplicitLod => {
|
||||
.SampleImplicitLod,
|
||||
.SampleProjImplicitLod,
|
||||
=> {
|
||||
const sampled_image_operand = try resolveSampledImage(image, rt);
|
||||
const x = try readSampleCoordLane(coordinate, 0);
|
||||
const y = readSampleCoordLane(coordinate, 1) catch 0;
|
||||
const z = readSampleCoordLane(coordinate, 2) catch 0;
|
||||
const coords = if (comptime Op == .SampleProjImplicitLod)
|
||||
try readProjectedSampleCoords(coordinate)
|
||||
else
|
||||
.{
|
||||
.x = try readSampleCoordLane(coordinate, 0),
|
||||
.y = readSampleCoordLane(coordinate, 1) catch 0,
|
||||
.z = readSampleCoordLane(coordinate, 2) catch 0,
|
||||
};
|
||||
const image_operands = if (word_count > 4) try rt.it.next() else 0;
|
||||
const parsed_operands = try parseImageOperands(rt, image_operands);
|
||||
|
||||
try sampleImageImplicitLod(
|
||||
rt,
|
||||
@@ -1641,9 +1775,10 @@ fn ImageEngine(comptime Op: ImageOp) type {
|
||||
sampled_image_operand.driver_image,
|
||||
sampled_image_operand.driver_sampler,
|
||||
sampled_image_operand.dim,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
coords.x,
|
||||
coords.y,
|
||||
coords.z,
|
||||
parsed_operands.offset,
|
||||
);
|
||||
try setImplicitSampleDerivative(
|
||||
allocator,
|
||||
@@ -1655,22 +1790,62 @@ fn ImageEngine(comptime Op: ImageOp) type {
|
||||
sampled_image_operand.driver_image,
|
||||
sampled_image_operand.driver_sampler,
|
||||
sampled_image_operand.dim,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
coords.x,
|
||||
coords.y,
|
||||
coords.z,
|
||||
parsed_operands.offset,
|
||||
);
|
||||
},
|
||||
|
||||
.SampleExplicitLod => {
|
||||
.SampleDrefImplicitLod,
|
||||
.SampleProjDrefImplicitLod,
|
||||
=> {
|
||||
const sampled_image_operand = try resolveSampledImage(image, rt);
|
||||
const x = try readSampleCoordLane(coordinate, 0);
|
||||
const y = readSampleCoordLane(coordinate, 1) catch 0;
|
||||
const z = readSampleCoordLane(coordinate, 2) catch 0;
|
||||
const image_operands = if (word_count > 4) try rt.it.next() else 0;
|
||||
const lod = if ((image_operands & @intFromEnum(spv.SpvImageOperandsMask.LodMask)) != 0)
|
||||
try readFloatLane(try rt.results[try rt.it.next()].getValue(), 0)
|
||||
const coords = if (comptime Op == .SampleProjDrefImplicitLod)
|
||||
try readProjectedSampleCoords(coordinate)
|
||||
else
|
||||
null;
|
||||
.{
|
||||
.x = try readSampleCoordLane(coordinate, 0),
|
||||
.y = readSampleCoordLane(coordinate, 1) catch 0,
|
||||
.z = readSampleCoordLane(coordinate, 2) catch 0,
|
||||
};
|
||||
const raw_dref = try readFloatLane(try rt.results[try rt.it.next()].getValue(), 0);
|
||||
const dref = if (comptime Op == .SampleProjDrefImplicitLod)
|
||||
raw_dref / try readProjectionDivisor(coordinate)
|
||||
else
|
||||
raw_dref;
|
||||
const image_operands = if (word_count > 5) try rt.it.next() else 0;
|
||||
const parsed_operands = try parseImageOperands(rt, image_operands);
|
||||
|
||||
try sampleImageDref(
|
||||
rt,
|
||||
dst,
|
||||
sampled_image_operand.driver_image,
|
||||
sampled_image_operand.driver_sampler,
|
||||
sampled_image_operand.dim,
|
||||
coords.x,
|
||||
coords.y,
|
||||
coords.z,
|
||||
dref,
|
||||
null,
|
||||
parsed_operands.offset,
|
||||
);
|
||||
},
|
||||
|
||||
.SampleExplicitLod,
|
||||
.SampleProjExplicitLod,
|
||||
=> {
|
||||
const sampled_image_operand = try resolveSampledImage(image, rt);
|
||||
const coords = if (comptime Op == .SampleProjExplicitLod)
|
||||
try readProjectedSampleCoords(coordinate)
|
||||
else
|
||||
.{
|
||||
.x = try readSampleCoordLane(coordinate, 0),
|
||||
.y = readSampleCoordLane(coordinate, 1) catch 0,
|
||||
.z = readSampleCoordLane(coordinate, 2) catch 0,
|
||||
};
|
||||
const image_operands = if (word_count > 4) try rt.it.next() else 0;
|
||||
const parsed_operands = try parseImageOperands(rt, image_operands);
|
||||
|
||||
try sampleImageExplicitLod(
|
||||
rt,
|
||||
@@ -1678,10 +1853,46 @@ fn ImageEngine(comptime Op: ImageOp) type {
|
||||
sampled_image_operand.driver_image,
|
||||
sampled_image_operand.driver_sampler,
|
||||
sampled_image_operand.dim,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
lod,
|
||||
coords.x,
|
||||
coords.y,
|
||||
coords.z,
|
||||
parsed_operands.lod,
|
||||
parsed_operands.offset,
|
||||
);
|
||||
},
|
||||
|
||||
.SampleDrefExplicitLod,
|
||||
.SampleProjDrefExplicitLod,
|
||||
=> {
|
||||
const sampled_image_operand = try resolveSampledImage(image, rt);
|
||||
const coords = if (comptime Op == .SampleProjDrefExplicitLod)
|
||||
try readProjectedSampleCoords(coordinate)
|
||||
else
|
||||
.{
|
||||
.x = try readSampleCoordLane(coordinate, 0),
|
||||
.y = readSampleCoordLane(coordinate, 1) catch 0,
|
||||
.z = readSampleCoordLane(coordinate, 2) catch 0,
|
||||
};
|
||||
const raw_dref = try readFloatLane(try rt.results[try rt.it.next()].getValue(), 0);
|
||||
const dref = if (comptime Op == .SampleProjDrefExplicitLod)
|
||||
raw_dref / try readProjectionDivisor(coordinate)
|
||||
else
|
||||
raw_dref;
|
||||
const image_operands = if (word_count > 5) try rt.it.next() else 0;
|
||||
const parsed_operands = try parseImageOperands(rt, image_operands);
|
||||
|
||||
try sampleImageDref(
|
||||
rt,
|
||||
dst,
|
||||
sampled_image_operand.driver_image,
|
||||
sampled_image_operand.driver_sampler,
|
||||
sampled_image_operand.dim,
|
||||
coords.x,
|
||||
coords.y,
|
||||
coords.z,
|
||||
dref,
|
||||
parsed_operands.lod,
|
||||
parsed_operands.offset,
|
||||
);
|
||||
},
|
||||
|
||||
@@ -1782,24 +1993,10 @@ fn opImageTexelPointer(allocator: std.mem.Allocator, word_count: SpvWord, rt: *R
|
||||
};
|
||||
errdefer backing.deinit(allocator);
|
||||
|
||||
if (rt.results[result_id].variant) |*variant| {
|
||||
switch (variant.*) {
|
||||
.AccessChain => |*a| {
|
||||
try a.value.flushPtr(allocator);
|
||||
allocator.free(a.indexes);
|
||||
a.value.deinit(allocator);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
const indexes = allocator.alloc(SpvWord, 0) catch return RuntimeError.OutOfMemory;
|
||||
rt.results[result_id].variant = .{
|
||||
.AccessChain = .{
|
||||
.target = result_type,
|
||||
.base = image_id,
|
||||
.indexes = indexes,
|
||||
.value = .{ .Pointer = .{
|
||||
errdefer allocator.free(indexes);
|
||||
|
||||
const new_value: Value = .{ .Pointer = .{
|
||||
.ptr = .{ .common = backing },
|
||||
.image_texel = .{
|
||||
.driver_image = image.driver_image,
|
||||
@@ -1810,7 +2007,27 @@ fn opImageTexelPointer(allocator: std.mem.Allocator, word_count: SpvWord, rt: *R
|
||||
},
|
||||
.uniform_backing_value = backing,
|
||||
.owns_uniform_backing_value = true,
|
||||
} },
|
||||
} };
|
||||
|
||||
if (rt.results[result_id].variant) |variant| {
|
||||
rt.results[result_id].variant = null;
|
||||
var old_variant = variant;
|
||||
switch (old_variant) {
|
||||
.AccessChain => |*a| {
|
||||
try a.value.flushPtr(allocator);
|
||||
allocator.free(a.indexes);
|
||||
a.value.deinit(allocator);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
rt.results[result_id].variant = .{
|
||||
.AccessChain = .{
|
||||
.target = result_type,
|
||||
.base = image_id,
|
||||
.indexes = indexes,
|
||||
.value = new_value,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -2003,7 +2220,9 @@ fn MathEngine(comptime T: PrimitiveType, comptime Op: MathOp, comptime IsAtomic:
|
||||
.Sub => if (comptime is_int) @subWithOverflow(op1, op2)[0] else op1 - op2,
|
||||
.Mul,
|
||||
.MatrixTimesMatrix,
|
||||
.MatrixTimesScalar,
|
||||
.MatrixTimesVector,
|
||||
.VectorTimesMatrix,
|
||||
=> if (comptime is_int) @mulWithOverflow(op1, op2)[0] else op1 * op2,
|
||||
.Div => blk: {
|
||||
if (op2_is_zero) return RuntimeError.DivisionByZero;
|
||||
@@ -2044,18 +2263,123 @@ fn MathEngine(comptime T: PrimitiveType, comptime Op: MathOp, comptime IsAtomic:
|
||||
}
|
||||
}
|
||||
|
||||
inline fn matrixRows(matrix: *const Value) RuntimeError!usize {
|
||||
const columns = switch (matrix.*) {
|
||||
.Matrix => |columns| columns,
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
};
|
||||
if (columns.len == 0) return RuntimeError.InvalidSpirV;
|
||||
return try columns[0].resolveLaneCount();
|
||||
}
|
||||
|
||||
fn applyMatrixTimesVectorFloat(comptime bits: SpvWord, dst_vec: []Value, matrix: *const Value, vector: *const Value) RuntimeError!void {
|
||||
const columns = switch (matrix.*) {
|
||||
.Matrix => |columns| columns,
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
};
|
||||
const rows = try matrixRows(matrix);
|
||||
if (dst_vec.len != rows or try vector.resolveLaneCount() != columns.len) return RuntimeError.InvalidSpirV;
|
||||
|
||||
const FloatT = Value.getPrimitiveFieldType(.Float, bits);
|
||||
for (dst_vec, 0..) |*dst_lane, row_index| {
|
||||
var sum: FloatT = 0;
|
||||
for (columns, 0..) |*column, column_index| {
|
||||
const l = try Value.readLane(.Float, bits, column, row_index);
|
||||
const r = try Value.readLane(.Float, bits, vector, column_index);
|
||||
sum += l * r;
|
||||
}
|
||||
try Value.writeLane(.Float, bits, dst_lane, 0, sum);
|
||||
}
|
||||
}
|
||||
|
||||
fn applyVectorTimesMatrixFloat(comptime bits: SpvWord, dst_vec: []Value, vector: *const Value, matrix: *const Value) RuntimeError!void {
|
||||
const columns = switch (matrix.*) {
|
||||
.Matrix => |columns| columns,
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
};
|
||||
if (dst_vec.len != columns.len) return RuntimeError.InvalidSpirV;
|
||||
const rows = try matrixRows(matrix);
|
||||
if (try vector.resolveLaneCount() != rows) return RuntimeError.InvalidSpirV;
|
||||
|
||||
const FloatT = Value.getPrimitiveFieldType(.Float, bits);
|
||||
for (dst_vec, columns, 0..) |*dst_lane, *column, column_index| {
|
||||
_ = column_index;
|
||||
var sum: FloatT = 0;
|
||||
for (0..rows) |row_index| {
|
||||
const l = try Value.readLane(.Float, bits, vector, row_index);
|
||||
const r = try Value.readLane(.Float, bits, column, row_index);
|
||||
sum += l * r;
|
||||
}
|
||||
try Value.writeLane(.Float, bits, dst_lane, 0, sum);
|
||||
}
|
||||
}
|
||||
|
||||
fn applyMatrixTimesMatrixFloat(comptime bits: SpvWord, dst_matrix: []Value, lhs_matrix: *const Value, rhs_matrix: *const Value) RuntimeError!void {
|
||||
const lhs_columns = switch (lhs_matrix.*) {
|
||||
.Matrix => |columns| columns,
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
};
|
||||
const rhs_columns = switch (rhs_matrix.*) {
|
||||
.Matrix => |columns| columns,
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
};
|
||||
if (dst_matrix.len != rhs_columns.len) return RuntimeError.InvalidSpirV;
|
||||
|
||||
const rows = try matrixRows(lhs_matrix);
|
||||
if (lhs_columns.len != try matrixRows(rhs_matrix)) return RuntimeError.InvalidSpirV;
|
||||
|
||||
const FloatT = Value.getPrimitiveFieldType(.Float, bits);
|
||||
for (dst_matrix, rhs_columns) |*dst_column, *rhs_column| {
|
||||
if (try dst_column.resolveLaneCount() != rows) return RuntimeError.InvalidSpirV;
|
||||
|
||||
for (0..rows) |row_index| {
|
||||
var sum: FloatT = 0;
|
||||
for (lhs_columns, 0..) |*lhs_column, inner_index| {
|
||||
const l = try Value.readLane(.Float, bits, lhs_column, row_index);
|
||||
const r = try Value.readLane(.Float, bits, rhs_column, inner_index);
|
||||
sum += l * r;
|
||||
}
|
||||
try Value.writeLane(.Float, bits, dst_column, row_index, sum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fn applySIMDVector(comptime ElemT: type, comptime N: usize, d: *@Vector(N, ElemT), l: @Vector(N, ElemT), r: @Vector(N, ElemT)) RuntimeError!void {
|
||||
d.* = try operation(@Vector(N, ElemT), l, r);
|
||||
}
|
||||
|
||||
fn applySIMDVectorf32(comptime N: usize, d: *@Vector(N, f32), l: *const Value, r: *const Value) RuntimeError!void {
|
||||
switch (Op) {
|
||||
.MatrixTimesVector => inline for (0..N) |i| {
|
||||
const l_vec = l.Matrix[i].getVectorSpecialization(N, f32);
|
||||
const r_vec = r.getVectorSpecialization(N, f32);
|
||||
d[i] = 0;
|
||||
inline for (0..N) |j| {
|
||||
d[i] += l_vec[j] * r_vec[j];
|
||||
.MatrixTimesVector => {
|
||||
const columns = switch (l.*) {
|
||||
.Matrix => |columns| columns,
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
};
|
||||
if (try r.resolveLaneCount() != columns.len) return RuntimeError.InvalidSpirV;
|
||||
|
||||
inline for (0..N) |row_index| {
|
||||
d[row_index] = 0;
|
||||
for (columns, 0..) |*column, column_index| {
|
||||
d[row_index] += try Value.readLane(.Float, 32, column, row_index) *
|
||||
try Value.readLane(.Float, 32, r, column_index);
|
||||
}
|
||||
}
|
||||
},
|
||||
.VectorTimesMatrix => {
|
||||
const columns = switch (r.*) {
|
||||
.Matrix => |columns| columns,
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
};
|
||||
if (columns.len != N) return RuntimeError.InvalidSpirV;
|
||||
const rows = try matrixRows(r);
|
||||
if (try l.resolveLaneCount() != rows) return RuntimeError.InvalidSpirV;
|
||||
|
||||
inline for (0..N) |column_index| {
|
||||
d[column_index] = 0;
|
||||
for (0..rows) |row_index| {
|
||||
d[column_index] += try Value.readLane(.Float, 32, l, row_index) *
|
||||
try Value.readLane(.Float, 32, &columns[column_index], row_index);
|
||||
}
|
||||
}
|
||||
},
|
||||
else => try applyDirectSIMDVectorf32(N, d, l.getVectorSpecialization(N, f32), r),
|
||||
@@ -2214,24 +2538,17 @@ fn MathEngine(comptime T: PrimitiveType, comptime Op: MathOp, comptime IsAtomic:
|
||||
fn routines(dst2: *Value, lhs2: *const Value, rhs2: *const Value, lane_bits2: SpvWord) RuntimeError!void {
|
||||
switch (dst2.*) {
|
||||
.Vector => |dst_vec| switch (Op) {
|
||||
.VectorTimesScalar => switch (lane_bits2) {
|
||||
.VectorTimesScalar, .MatrixTimesScalar => switch (lane_bits2) {
|
||||
inline 16, 32, 64 => |bits_count| try applyVectorTimesScalarFloat(bits_count, dst_vec, lhs2.Vector, rhs2),
|
||||
else => return RuntimeError.UnsupportedSpirV,
|
||||
},
|
||||
.MatrixTimesVector => for (dst_vec, lhs2.Matrix) |*d_lane, *l_mat| {
|
||||
switch (lane_bits2) {
|
||||
inline 8, 16, 32, 64 => |bits| {
|
||||
if (comptime bits == 8 and T == .Float) return RuntimeError.UnsupportedSpirV;
|
||||
const d_field = try Value.getPrimitiveField(T, bits, d_lane);
|
||||
|
||||
d_field.* = 0;
|
||||
|
||||
for (l_mat.Vector[0..], rhs2.Vector) |*l_lane, *r_lane| {
|
||||
d_field.* += try applyScalarRaw(bits, l_lane, r_lane);
|
||||
}
|
||||
},
|
||||
.MatrixTimesVector => switch (lane_bits2) {
|
||||
inline 16, 32, 64 => |bits_count| try applyMatrixTimesVectorFloat(bits_count, dst_vec, lhs2, rhs2),
|
||||
else => return RuntimeError.UnsupportedSpirV,
|
||||
},
|
||||
.VectorTimesMatrix => switch (lane_bits2) {
|
||||
inline 16, 32, 64 => |bits_count| try applyVectorTimesMatrixFloat(bits_count, dst_vec, lhs2, rhs2),
|
||||
else => return RuntimeError.UnsupportedSpirV,
|
||||
}
|
||||
},
|
||||
else => for (dst_vec, lhs2.Vector, rhs2.Vector) |*d_lane, *l_lane, *r_lane| {
|
||||
try applyScalar(lane_bits2, d_lane, l_lane, r_lane);
|
||||
@@ -2259,10 +2576,9 @@ fn MathEngine(comptime T: PrimitiveType, comptime Op: MathOp, comptime IsAtomic:
|
||||
.Int, .Float => try applyScalar(lane_bits, dst, lhs, rhs),
|
||||
|
||||
.Matrix => |dst_m| switch (Op) {
|
||||
.MatrixTimesMatrix => {
|
||||
for (dst_m, lhs.Matrix, rhs.Matrix) |*dst_vec, *lhs_vec, *rhs_vec| {
|
||||
try vectorRoutines(dst_vec, lhs_vec, rhs_vec, lane_bits);
|
||||
}
|
||||
.MatrixTimesMatrix => switch (lane_bits) {
|
||||
inline 16, 32, 64 => |bits_count| try applyMatrixTimesMatrixFloat(bits_count, dst_m, lhs, rhs),
|
||||
else => return RuntimeError.UnsupportedSpirV,
|
||||
},
|
||||
.MatrixTimesScalar => {
|
||||
for (dst_m, lhs.Matrix) |*dst_vec, *lhs_vec| {
|
||||
@@ -2385,8 +2701,13 @@ fn setupAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runt
|
||||
index.* = try rt.it.next();
|
||||
}
|
||||
|
||||
if (rt.results[id].variant) |*variant| {
|
||||
switch (variant.*) {
|
||||
var new_value = try Value.initUnresolved(allocator, rt.results, var_type, false);
|
||||
errdefer new_value.deinit(allocator);
|
||||
|
||||
if (rt.results[id].variant) |variant| {
|
||||
rt.results[id].variant = null;
|
||||
var old_variant = variant;
|
||||
switch (old_variant) {
|
||||
.AccessChain => |*a| {
|
||||
try a.value.flushPtr(allocator);
|
||||
allocator.free(a.indexes);
|
||||
@@ -2401,7 +2722,7 @@ fn setupAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runt
|
||||
.target = var_type,
|
||||
.base = base_id,
|
||||
.indexes = indexes,
|
||||
.value = try Value.initUnresolved(allocator, rt.results, var_type, false),
|
||||
.value = new_value,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -2587,8 +2908,10 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime
|
||||
const index_count: usize = @intCast(word_count - 3);
|
||||
|
||||
const indexes, const free_responsability = blk: {
|
||||
if (rt.results[id].variant) |*variant| {
|
||||
switch (variant.*) {
|
||||
if (rt.results[id].variant) |variant| {
|
||||
rt.results[id].variant = null;
|
||||
var old_variant = variant;
|
||||
switch (old_variant) {
|
||||
.AccessChain => |*a| {
|
||||
if (a.indexes.len != index_count)
|
||||
return RuntimeError.InvalidSpirV;
|
||||
|
||||
+510
@@ -0,0 +1,510 @@
|
||||
const std = @import("std");
|
||||
const spv = @import("spv");
|
||||
const root = @import("root.zig");
|
||||
|
||||
const compileNzsl = root.compileNzsl;
|
||||
|
||||
const ImageState = struct {
|
||||
expected_sampler: *anyopaque,
|
||||
sample_calls: usize = 0,
|
||||
dref_calls: usize = 0,
|
||||
last_x: f32 = 0,
|
||||
last_y: f32 = 0,
|
||||
last_z: f32 = 0,
|
||||
last_dref: f32 = 0,
|
||||
last_lod: ?f32 = null,
|
||||
last_offset: spv.Runtime.ImageOffset = .{},
|
||||
};
|
||||
|
||||
fn readImageFloat4(_: *anyopaque, _: spv.spv.SpvDim, _: i32, _: i32, _: i32) spv.Runtime.RuntimeError!spv.Runtime.Vec4(f32) {
|
||||
return spv.Runtime.RuntimeError.UnsupportedSpirV;
|
||||
}
|
||||
|
||||
fn readImageInt4(_: *anyopaque, _: spv.spv.SpvDim, _: i32, _: i32, _: i32) spv.Runtime.RuntimeError!spv.Runtime.Vec4(u32) {
|
||||
return spv.Runtime.RuntimeError.UnsupportedSpirV;
|
||||
}
|
||||
|
||||
fn writeImageFloat4(_: *anyopaque, _: spv.spv.SpvDim, _: i32, _: i32, _: i32, _: spv.Runtime.Vec4(f32)) spv.Runtime.RuntimeError!void {
|
||||
return spv.Runtime.RuntimeError.UnsupportedSpirV;
|
||||
}
|
||||
|
||||
fn writeImageInt4(_: *anyopaque, _: spv.spv.SpvDim, _: i32, _: i32, _: i32, _: spv.Runtime.Vec4(u32)) spv.Runtime.RuntimeError!void {
|
||||
return spv.Runtime.RuntimeError.UnsupportedSpirV;
|
||||
}
|
||||
|
||||
fn sampleImageFloat4(driver_image: *anyopaque, driver_sampler: *anyopaque, _: spv.spv.SpvDim, x: f32, y: f32, z: f32, lod: ?f32, offset: spv.Runtime.ImageOffset) spv.Runtime.RuntimeError!spv.Runtime.Vec4(f32) {
|
||||
const state: *ImageState = @ptrCast(@alignCast(driver_image));
|
||||
if (state.expected_sampler != driver_sampler) return spv.Runtime.RuntimeError.InvalidSpirV;
|
||||
state.sample_calls += 1;
|
||||
state.last_x = x;
|
||||
state.last_y = y;
|
||||
state.last_z = z;
|
||||
state.last_lod = lod;
|
||||
state.last_offset = offset;
|
||||
return .{ .x = x, .y = y, .z = 9.0, .w = 1.0 };
|
||||
}
|
||||
|
||||
fn sampleImageInt4(_: *anyopaque, _: *anyopaque, _: spv.spv.SpvDim, _: f32, _: f32, _: f32, _: ?f32, _: spv.Runtime.ImageOffset) spv.Runtime.RuntimeError!spv.Runtime.Vec4(u32) {
|
||||
return spv.Runtime.RuntimeError.UnsupportedSpirV;
|
||||
}
|
||||
|
||||
fn sampleImageDref(driver_image: *anyopaque, driver_sampler: *anyopaque, _: spv.spv.SpvDim, x: f32, y: f32, z: f32, dref: f32, lod: ?f32, offset: spv.Runtime.ImageOffset) spv.Runtime.RuntimeError!f32 {
|
||||
const state: *ImageState = @ptrCast(@alignCast(driver_image));
|
||||
if (state.expected_sampler != driver_sampler) return spv.Runtime.RuntimeError.InvalidSpirV;
|
||||
state.dref_calls += 1;
|
||||
state.last_x = x;
|
||||
state.last_y = y;
|
||||
state.last_z = z;
|
||||
state.last_dref = dref;
|
||||
state.last_lod = lod;
|
||||
state.last_offset = offset;
|
||||
return dref + x + y;
|
||||
}
|
||||
|
||||
fn queryImageSize(_: *anyopaque, _: spv.spv.SpvDim, _: bool) spv.Runtime.RuntimeError!spv.Runtime.Vec4(u32) {
|
||||
return spv.Runtime.RuntimeError.UnsupportedSpirV;
|
||||
}
|
||||
|
||||
const image_api: spv.Runtime.ImageAPI = .{
|
||||
.readImageFloat4 = readImageFloat4,
|
||||
.readImageInt4 = readImageInt4,
|
||||
.writeImageFloat4 = writeImageFloat4,
|
||||
.writeImageInt4 = writeImageInt4,
|
||||
.sampleImageFloat4 = sampleImageFloat4,
|
||||
.sampleImageInt4 = sampleImageInt4,
|
||||
.sampleImageDref = sampleImageDref,
|
||||
.queryImageSize = queryImageSize,
|
||||
};
|
||||
|
||||
fn initModule(allocator: std.mem.Allocator, shader: []const u8) !struct { code: []const u32, module: spv.Module } {
|
||||
const code = try compileNzsl(allocator, shader);
|
||||
errdefer allocator.free(code);
|
||||
|
||||
const module = try spv.Module.init(allocator, code, .{
|
||||
.use_simd_vectors_specializations = false,
|
||||
});
|
||||
return .{ .code = code, .module = module };
|
||||
}
|
||||
|
||||
test "Runtime API lifecycle" {
|
||||
const allocator = std.testing.allocator;
|
||||
const shader =
|
||||
\\ [nzsl_version("1.1")]
|
||||
\\ module;
|
||||
\\
|
||||
\\ struct FragIn
|
||||
\\ {
|
||||
\\ [location(0)] color: vec4[f32]
|
||||
\\ }
|
||||
\\
|
||||
\\ struct FragOut
|
||||
\\ {
|
||||
\\ [location(0)] color: vec4[f32]
|
||||
\\ }
|
||||
\\
|
||||
\\ [entry(frag)]
|
||||
\\ fn main(input: FragIn) -> FragOut
|
||||
\\ {
|
||||
\\ let output: FragOut;
|
||||
\\ output.color = input.color;
|
||||
\\ return output;
|
||||
\\ }
|
||||
;
|
||||
|
||||
var compiled = try initModule(allocator, shader);
|
||||
defer allocator.free(compiled.code);
|
||||
defer compiled.module.deinit(allocator);
|
||||
|
||||
var rt = try spv.Runtime.init(allocator, &compiled.module, image_api);
|
||||
defer rt.deinit(allocator);
|
||||
|
||||
const input_result = try rt.getResultByLocationComponent(0, 0, .input);
|
||||
const output_result = try rt.getResultByName("color");
|
||||
const output_location_result = try rt.getResultByLocationComponent(0, 0, .output);
|
||||
try std.testing.expectEqual(input_result, try rt.getResultByLocation(0, .input));
|
||||
try std.testing.expectEqual(output_location_result, try rt.getResultByLocation(0, .output));
|
||||
try std.testing.expectEqual(@as(usize, 16), try rt.getInputLocationMemorySize(0));
|
||||
try std.testing.expectEqual(@as(usize, 16), try rt.getResultMemorySize(output_result));
|
||||
try std.testing.expectEqual(@as(usize, 16), try rt.getResultMemorySize(output_location_result));
|
||||
try std.testing.expectEqual(.Float, try rt.getResultPrimitiveType(output_result));
|
||||
try std.testing.expect(rt.hasResultDecoration(output_result, .Location));
|
||||
|
||||
const input = [_]f32{ 10.0, 20.0, 30.0, 40.0 };
|
||||
try rt.writeInputLocation(std.mem.asBytes(&input), 0);
|
||||
|
||||
const entry = try rt.getEntryPointByName("main");
|
||||
try std.testing.expectEqual(.completed, try rt.beginEntryPoint(allocator, entry));
|
||||
|
||||
var output: [4]f32 = undefined;
|
||||
try rt.readOutput(std.mem.asBytes(&output), output_location_result);
|
||||
try std.testing.expectEqualSlices(f32, &input, &output);
|
||||
|
||||
rt.resetInvocation(allocator);
|
||||
}
|
||||
|
||||
test "Module binding writes" {
|
||||
const allocator = std.testing.allocator;
|
||||
const shader =
|
||||
\\ [nzsl_version("1.1")]
|
||||
\\ module;
|
||||
\\
|
||||
\\ [layout(std430)]
|
||||
\\ struct SSBO
|
||||
\\ {
|
||||
\\ value: u32
|
||||
\\ }
|
||||
\\
|
||||
\\ external
|
||||
\\ {
|
||||
\\ [set(2), binding(3)] ssbo: storage[SSBO],
|
||||
\\ }
|
||||
\\
|
||||
\\ [entry(compute)]
|
||||
\\ [workgroup(1, 1, 1)]
|
||||
\\ fn main()
|
||||
\\ {
|
||||
\\ ssbo.value = ssbo.value + 7;
|
||||
\\ }
|
||||
;
|
||||
|
||||
var compiled = try initModule(allocator, shader);
|
||||
defer allocator.free(compiled.code);
|
||||
defer compiled.module.deinit(allocator);
|
||||
|
||||
const binding_result = compiled.module.getBindingResult(2, 3) orelse return error.TestExpectedEqual;
|
||||
try std.testing.expectEqual(@as(?spv.SpvWord, null), compiled.module.getBindingResult(0, 0));
|
||||
|
||||
var rt = try spv.Runtime.init(allocator, &compiled.module, image_api);
|
||||
defer rt.deinit(allocator);
|
||||
|
||||
var storage: u32 = 35;
|
||||
try rt.writeDescriptorSet(std.mem.asBytes(&storage), 2, 3, 0);
|
||||
try std.testing.expectEqual(error.NotFound, rt.writeDescriptorSet(std.mem.asBytes(&storage), 2, 4, 0));
|
||||
|
||||
try rt.callEntryPoint(allocator, try rt.getEntryPointByName("main"));
|
||||
try rt.flushDescriptorSets(allocator);
|
||||
|
||||
_ = binding_result;
|
||||
try std.testing.expectEqual(@as(u32, 42), storage);
|
||||
}
|
||||
|
||||
test "Push constants" {
|
||||
const allocator = std.testing.allocator;
|
||||
const shader =
|
||||
\\ [nzsl_version("1.1")]
|
||||
\\ module;
|
||||
\\
|
||||
\\ struct Data
|
||||
\\ {
|
||||
\\ color: vec4[f32]
|
||||
\\ }
|
||||
\\
|
||||
\\ external
|
||||
\\ {
|
||||
\\ data: push_constant[Data]
|
||||
\\ }
|
||||
\\
|
||||
\\ struct FragOut
|
||||
\\ {
|
||||
\\ [location(0)] color: vec4[f32]
|
||||
\\ }
|
||||
\\
|
||||
\\ [entry(frag)]
|
||||
\\ fn main() -> FragOut
|
||||
\\ {
|
||||
\\ let output: FragOut;
|
||||
\\ output.color = data.color;
|
||||
\\ return output;
|
||||
\\ }
|
||||
;
|
||||
|
||||
var compiled = try initModule(allocator, shader);
|
||||
defer allocator.free(compiled.code);
|
||||
defer compiled.module.deinit(allocator);
|
||||
|
||||
var rt = try spv.Runtime.init(allocator, &compiled.module, image_api);
|
||||
defer rt.deinit(allocator);
|
||||
|
||||
const push_constants = [_]f32{ 0.125, 0.25, 0.5, 1.0 };
|
||||
try rt.populatePushConstants(std.mem.asBytes(&push_constants));
|
||||
try rt.callEntryPoint(allocator, try rt.getEntryPointByName("main"));
|
||||
|
||||
var output: [4]f32 = undefined;
|
||||
try rt.readOutput(std.mem.asBytes(&output), try rt.getResultByLocation(0, .output));
|
||||
try std.testing.expectEqualSlices(f32, &push_constants, &output);
|
||||
}
|
||||
|
||||
test "Built-in inputs and outputs" {
|
||||
const allocator = std.testing.allocator;
|
||||
const shader =
|
||||
\\ [nzsl_version("1.1")]
|
||||
\\ module;
|
||||
\\
|
||||
\\ struct VertIn
|
||||
\\ {
|
||||
\\ [builtin(vertex_index)] vertex_index: i32
|
||||
\\ }
|
||||
\\
|
||||
\\ struct VertOut
|
||||
\\ {
|
||||
\\ [builtin(position)] position: vec4[f32]
|
||||
\\ }
|
||||
\\
|
||||
\\ [entry(vert)]
|
||||
\\ fn main(input: VertIn) -> VertOut
|
||||
\\ {
|
||||
\\ let value = f32(input.vertex_index);
|
||||
\\ let output: VertOut;
|
||||
\\ output.position = vec4[f32](value, value + 1.0, value + 2.0, 1.0);
|
||||
\\ return output;
|
||||
\\ }
|
||||
;
|
||||
|
||||
var compiled = try initModule(allocator, shader);
|
||||
defer allocator.free(compiled.code);
|
||||
defer compiled.module.deinit(allocator);
|
||||
|
||||
var rt = try spv.Runtime.init(allocator, &compiled.module, image_api);
|
||||
defer rt.deinit(allocator);
|
||||
|
||||
const vertex_index: i32 = 7;
|
||||
try rt.writeBuiltIn(std.mem.asBytes(&vertex_index), .VertexIndex);
|
||||
try rt.callEntryPoint(allocator, try rt.getEntryPointByName("main"));
|
||||
|
||||
var position: [4]f32 = undefined;
|
||||
try rt.readBuiltIn(std.mem.asBytes(&position), .Position);
|
||||
try std.testing.expectEqualSlices(f32, &.{ 7.0, 8.0, 9.0, 1.0 }, &position);
|
||||
}
|
||||
|
||||
test "Integer output metadata" {
|
||||
const allocator = std.testing.allocator;
|
||||
const shader =
|
||||
\\ [nzsl_version("1.1")]
|
||||
\\ module;
|
||||
\\
|
||||
\\ struct FragOut
|
||||
\\ {
|
||||
\\ [location(0)] value: u32
|
||||
\\ }
|
||||
\\
|
||||
\\ [entry(frag)]
|
||||
\\ fn main() -> FragOut
|
||||
\\ {
|
||||
\\ let output: FragOut;
|
||||
\\ output.value = 0xA5A5_A5A5;
|
||||
\\ return output;
|
||||
\\ }
|
||||
;
|
||||
|
||||
var compiled = try initModule(allocator, shader);
|
||||
defer allocator.free(compiled.code);
|
||||
defer compiled.module.deinit(allocator);
|
||||
|
||||
var rt = try spv.Runtime.init(allocator, &compiled.module, image_api);
|
||||
defer rt.deinit(allocator);
|
||||
|
||||
const output_result = try rt.getResultByLocation(0, .output);
|
||||
try std.testing.expectEqual(.UInt, try rt.getResultPrimitiveType(output_result));
|
||||
try std.testing.expectEqual(@as(usize, 4), try rt.getResultMemorySize(output_result));
|
||||
|
||||
try rt.callEntryPoint(allocator, try rt.getEntryPointByName("main"));
|
||||
|
||||
var output: u32 = 0;
|
||||
try rt.readOutput(std.mem.asBytes(&output), output_result);
|
||||
try std.testing.expectEqual(@as(u32, 0xA5A5_A5A5), output);
|
||||
}
|
||||
|
||||
test "Runtime API error paths" {
|
||||
const allocator = std.testing.allocator;
|
||||
const shader =
|
||||
\\ [nzsl_version("1.1")]
|
||||
\\ module;
|
||||
\\
|
||||
\\ struct FragOut
|
||||
\\ {
|
||||
\\ [location(0)] color: vec4[f32]
|
||||
\\ }
|
||||
\\
|
||||
\\ [entry(frag)]
|
||||
\\ fn main() -> FragOut
|
||||
\\ {
|
||||
\\ let output: FragOut;
|
||||
\\ output.color = vec4[f32](1.0, 2.0, 3.0, 4.0);
|
||||
\\ return output;
|
||||
\\ }
|
||||
;
|
||||
|
||||
var compiled = try initModule(allocator, shader);
|
||||
defer allocator.free(compiled.code);
|
||||
defer compiled.module.deinit(allocator);
|
||||
|
||||
var rt = try spv.Runtime.init(allocator, &compiled.module, image_api);
|
||||
defer rt.deinit(allocator);
|
||||
|
||||
try std.testing.expectEqual(error.NotFound, rt.getEntryPointByName("missing"));
|
||||
try std.testing.expectEqual(error.NotFound, rt.getResultByName("missing"));
|
||||
try std.testing.expectEqual(error.NotFound, rt.getResultByLocation(31, .input));
|
||||
try std.testing.expectEqual(error.NotFound, rt.getInputLocationMemorySize(31));
|
||||
|
||||
try rt.callEntryPoint(allocator, try rt.getEntryPointByName("main"));
|
||||
|
||||
var too_small: [3]u8 = undefined;
|
||||
try std.testing.expectEqual(error.OutOfBounds, rt.readOutput(&too_small, try rt.getResultByLocation(0, .output)));
|
||||
}
|
||||
|
||||
test "Derivative memory buffers" {
|
||||
const allocator = std.testing.allocator;
|
||||
const shader =
|
||||
\\ [nzsl_version("1.1")]
|
||||
\\ module;
|
||||
\\
|
||||
\\ struct FragIn
|
||||
\\ {
|
||||
\\ [location(0)] normal: vec3[f32]
|
||||
\\ }
|
||||
\\
|
||||
\\ struct FragOut
|
||||
\\ {
|
||||
\\ [location(0)] color: vec4[f32]
|
||||
\\ }
|
||||
\\
|
||||
\\ [entry(frag)]
|
||||
\\ fn main(input: FragIn) -> FragOut
|
||||
\\ {
|
||||
\\ let output: FragOut;
|
||||
\\ output.color = vec4[f32](input.normal.x, input.normal.y, input.normal.z, 1.0);
|
||||
\\ return output;
|
||||
\\ }
|
||||
;
|
||||
|
||||
var compiled = try initModule(allocator, shader);
|
||||
defer allocator.free(compiled.code);
|
||||
defer compiled.module.deinit(allocator);
|
||||
|
||||
var rt = try spv.Runtime.init(allocator, &compiled.module, image_api);
|
||||
defer rt.deinit(allocator);
|
||||
|
||||
const input_result = try rt.getResultByLocation(0, .input);
|
||||
const dx = [_]f32{ -1.0, 2.0, -3.0 };
|
||||
const dy = [_]f32{ 4.0, -5.0, 6.0 };
|
||||
const short_dx = [_]f32{ -1.0, 2.0 };
|
||||
try std.testing.expectEqual(error.OutOfBounds, rt.setDerivativeFromMemory(allocator, input_result, std.mem.asBytes(&short_dx), std.mem.asBytes(&dy)));
|
||||
|
||||
const output_result = try rt.getResultByName("color");
|
||||
try rt.setDerivativeFromMemory(allocator, input_result, std.mem.asBytes(&dx), std.mem.asBytes(&dy));
|
||||
try rt.copyDerivative(allocator, output_result, input_result);
|
||||
rt.clearDerivative(allocator, input_result);
|
||||
rt.clearDerivative(allocator, output_result);
|
||||
|
||||
const input = [_]f32{ 1.0, 2.0, 3.0 };
|
||||
try rt.writeInput(std.mem.asBytes(&input), input_result);
|
||||
try rt.callEntryPoint(allocator, try rt.getEntryPointByName("main"));
|
||||
|
||||
var output: [4]f32 = undefined;
|
||||
try rt.readOutput(std.mem.asBytes(&output), try rt.getResultByLocation(0, .output));
|
||||
try std.testing.expectEqualSlices(f32, &.{ 1.0, 2.0, 3.0, 1.0 }, &output);
|
||||
|
||||
try rt.setDerivativeFromMemory(allocator, input_result, std.mem.asBytes(&dx), std.mem.asBytes(&dy));
|
||||
try rt.copyDerivative(allocator, output_result, input_result);
|
||||
rt.clearDerivative(allocator, output_result);
|
||||
}
|
||||
|
||||
test "Image sampling callback" {
|
||||
const allocator = std.testing.allocator;
|
||||
const shader =
|
||||
\\ [nzsl_version("1.1")]
|
||||
\\ module;
|
||||
\\
|
||||
\\ external
|
||||
\\ {
|
||||
\\ [set(0), binding(0)] tex: sampler2D[f32],
|
||||
\\ }
|
||||
\\
|
||||
\\ struct FragOut
|
||||
\\ {
|
||||
\\ [location(0)] color: vec4[f32]
|
||||
\\ }
|
||||
\\
|
||||
\\ [entry(frag)]
|
||||
\\ fn main() -> FragOut
|
||||
\\ {
|
||||
\\ let output: FragOut;
|
||||
\\ output.color = tex.Sample(vec2[f32](0.25, 0.75));
|
||||
\\ return output;
|
||||
\\ }
|
||||
;
|
||||
|
||||
var compiled = try initModule(allocator, shader);
|
||||
defer allocator.free(compiled.code);
|
||||
defer compiled.module.deinit(allocator);
|
||||
|
||||
var sampler: u8 = 0;
|
||||
var image_state: ImageState = .{ .expected_sampler = &sampler };
|
||||
var descriptor = [_]usize{
|
||||
@intFromPtr(&image_state),
|
||||
@intFromPtr(&sampler),
|
||||
};
|
||||
|
||||
var rt = try spv.Runtime.init(allocator, &compiled.module, image_api);
|
||||
defer rt.deinit(allocator);
|
||||
|
||||
try rt.writeDescriptorSet(std.mem.asBytes(&descriptor), 0, 0, 0);
|
||||
try rt.callEntryPoint(allocator, try rt.getEntryPointByName("main"));
|
||||
|
||||
var output: [4]f32 = undefined;
|
||||
try rt.readOutput(std.mem.asBytes(&output), try rt.getResultByName("color"));
|
||||
try std.testing.expectEqualSlices(f32, &.{ 0.25, 0.75, 9.0, 1.0 }, &output);
|
||||
try std.testing.expectEqual(@as(usize, 1), image_state.sample_calls);
|
||||
try std.testing.expectEqual(@as(?f32, null), image_state.last_lod);
|
||||
try std.testing.expectEqual(spv.Runtime.ImageOffset{}, image_state.last_offset);
|
||||
}
|
||||
|
||||
test "Depth sampling dref callback" {
|
||||
const allocator = std.testing.allocator;
|
||||
const shader =
|
||||
\\ [nzsl_version("1.1")]
|
||||
\\ module;
|
||||
\\
|
||||
\\ external
|
||||
\\ {
|
||||
\\ [set(0), binding(0)] tex: depth_sampler2D[f32],
|
||||
\\ }
|
||||
\\
|
||||
\\ struct FragOut
|
||||
\\ {
|
||||
\\ [location(0)] color: vec4[f32]
|
||||
\\ }
|
||||
\\
|
||||
\\ [entry(frag)]
|
||||
\\ fn main() -> FragOut
|
||||
\\ {
|
||||
\\ let value = tex.SampleDepthComp(vec2[f32](0.25, 0.75), 0.5);
|
||||
\\ let output: FragOut;
|
||||
\\ output.color = vec4[f32](value, 0.0, 0.0, 1.0);
|
||||
\\ return output;
|
||||
\\ }
|
||||
;
|
||||
|
||||
var compiled = try initModule(allocator, shader);
|
||||
defer allocator.free(compiled.code);
|
||||
defer compiled.module.deinit(allocator);
|
||||
|
||||
var sampler: u8 = 0;
|
||||
var image_state: ImageState = .{ .expected_sampler = &sampler };
|
||||
var descriptor = [_]usize{
|
||||
@intFromPtr(&image_state),
|
||||
@intFromPtr(&sampler),
|
||||
};
|
||||
|
||||
var rt = try spv.Runtime.init(allocator, &compiled.module, image_api);
|
||||
defer rt.deinit(allocator);
|
||||
|
||||
try rt.writeDescriptorSet(std.mem.asBytes(&descriptor), 0, 0, 0);
|
||||
try rt.callEntryPoint(allocator, try rt.getEntryPointByName("main"));
|
||||
|
||||
var output: [4]f32 = undefined;
|
||||
try rt.readOutput(std.mem.asBytes(&output), try rt.getResultByName("color"));
|
||||
try std.testing.expectEqualSlices(f32, &.{ 1.5, 0.0, 0.0, 1.0 }, &output);
|
||||
try std.testing.expectEqual(@as(usize, 1), image_state.dref_calls);
|
||||
try std.testing.expectEqual(@as(f32, 0.5), image_state.last_dref);
|
||||
}
|
||||
@@ -33,3 +33,42 @@ test "Simple array" {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
test "Array fold" {
|
||||
const allocator = std.testing.allocator;
|
||||
const shader =
|
||||
\\ [nzsl_version("1.1")]
|
||||
\\ module;
|
||||
\\
|
||||
\\ struct FragOut
|
||||
\\ {
|
||||
\\ [location(0)] color: vec4[f32]
|
||||
\\ }
|
||||
\\
|
||||
\\ [entry(frag)]
|
||||
\\ fn main() -> FragOut
|
||||
\\ {
|
||||
\\ let values = array[f32](1.0, 2.0, 3.0, 4.0);
|
||||
\\ let sum = 0.0;
|
||||
\\ let weighted = 0.0;
|
||||
\\ for i in u32(0) -> values.Size()
|
||||
\\ {
|
||||
\\ sum += values[i];
|
||||
\\ weighted += values[i] * f32(i + 1);
|
||||
\\ }
|
||||
\\
|
||||
\\ let output: FragOut;
|
||||
\\ output.color = vec4[f32](sum, weighted, values[2], f32(values.Size()));
|
||||
\\ return output;
|
||||
\\ }
|
||||
;
|
||||
const code = try compileNzsl(allocator, shader);
|
||||
defer allocator.free(code);
|
||||
|
||||
try case.expect(.{
|
||||
.source = code,
|
||||
.expected_outputs = &.{
|
||||
std.mem.asBytes(&[_]f32{ 10.0, 30.0, 3.0, 4.0 }),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -157,3 +157,42 @@ test "Bitwise vectors" {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "Bit mix" {
|
||||
const allocator = std.testing.allocator;
|
||||
const shader =
|
||||
\\ [nzsl_version("1.1")]
|
||||
\\ module;
|
||||
\\
|
||||
\\ struct FragOut
|
||||
\\ {
|
||||
\\ [location(0)] color: vec4[u32]
|
||||
\\ }
|
||||
\\
|
||||
\\ [entry(frag)]
|
||||
\\ fn main() -> FragOut
|
||||
\\ {
|
||||
\\ let a: u32 = 0xF0F0_F0F0;
|
||||
\\ let b: u32 = 0x0F0F_00FF;
|
||||
\\ let c = ((a & b) << 4) | ((a ^ b) >> 8);
|
||||
\\ let d = (c & 0xFFFF) ^ 0x55AA;
|
||||
\\ let output: FragOut;
|
||||
\\ output.color = vec4[u32](a & b, a | b, c, d);
|
||||
\\ return output;
|
||||
\\ }
|
||||
;
|
||||
const code = try compileNzsl(allocator, shader);
|
||||
defer allocator.free(code);
|
||||
|
||||
try case.expect(.{
|
||||
.source = code,
|
||||
.expected_outputs = &.{
|
||||
std.mem.asBytes(&[_]u32{
|
||||
0x0000_00F0,
|
||||
0xFFFF_F0FF,
|
||||
((0xF0F0_F0F0 & 0x0F0F_00FF) << 4) | ((0xF0F0_F0F0 ^ 0x0F0F_00FF) >> 8),
|
||||
((((0xF0F0_F0F0 & 0x0F0F_00FF) << 4) | ((0xF0F0_F0F0 ^ 0x0F0F_00FF) >> 8)) & 0xFFFF) ^ 0x55AA,
|
||||
}),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -103,3 +103,53 @@ test "Simple branching" {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "Nested if" {
|
||||
const allocator = std.testing.allocator;
|
||||
const shader =
|
||||
\\ [nzsl_version("1.1")]
|
||||
\\ module;
|
||||
\\
|
||||
\\ struct FragOut
|
||||
\\ {
|
||||
\\ [location(0)] color: vec4[i32]
|
||||
\\ }
|
||||
\\
|
||||
\\ fn classify(value: i32) -> i32
|
||||
\\ {
|
||||
\\ if (value < 0)
|
||||
\\ {
|
||||
\\ if ((value % 2) == 0)
|
||||
\\ return -2;
|
||||
\\ else
|
||||
\\ return -1;
|
||||
\\ }
|
||||
\\ else if (value == 0)
|
||||
\\ return 0;
|
||||
\\ else
|
||||
\\ {
|
||||
\\ if ((value % 2) == 0)
|
||||
\\ return 2;
|
||||
\\ else
|
||||
\\ return 1;
|
||||
\\ }
|
||||
\\ }
|
||||
\\
|
||||
\\ [entry(frag)]
|
||||
\\ fn main() -> FragOut
|
||||
\\ {
|
||||
\\ let output: FragOut;
|
||||
\\ output.color = vec4[i32](classify(-4), classify(-3), classify(0), classify(5));
|
||||
\\ return output;
|
||||
\\ }
|
||||
;
|
||||
const code = try compileNzsl(allocator, shader);
|
||||
defer allocator.free(code);
|
||||
|
||||
try case.expect(.{
|
||||
.source = code,
|
||||
.expected_outputs = &.{
|
||||
std.mem.asBytes(&[_]i32{ -2, -1, 0, 1 }),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -116,3 +116,35 @@ test "Primitives bitcasts" {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
test "Cast chain" {
|
||||
const allocator = std.testing.allocator;
|
||||
const shader =
|
||||
\\ [nzsl_version("1.1")]
|
||||
\\ module;
|
||||
\\
|
||||
\\ struct FragOut
|
||||
\\ {
|
||||
\\ [location(0)] color: vec4[i32]
|
||||
\\ }
|
||||
\\
|
||||
\\ [entry(frag)]
|
||||
\\ fn main() -> FragOut
|
||||
\\ {
|
||||
\\ let v = vec4[f32](1.25, 2.75, -3.25, 4.5);
|
||||
\\ let a = vec4[i32](v);
|
||||
\\ let output: FragOut;
|
||||
\\ output.color = vec4[i32](a.x, a.y, a.z, a.w);
|
||||
\\ return output;
|
||||
\\ }
|
||||
;
|
||||
const code = try compileNzsl(allocator, shader);
|
||||
defer allocator.free(code);
|
||||
|
||||
try case.expect(.{
|
||||
.source = code,
|
||||
.expected_outputs = &.{
|
||||
std.mem.asBytes(&[_]i32{ 1, 2, -3, 4 }),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -108,3 +108,90 @@ test "Nested function calls" {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
test "Function params" {
|
||||
const allocator = std.testing.allocator;
|
||||
const shader =
|
||||
\\ [nzsl_version("1.1")]
|
||||
\\ module;
|
||||
\\
|
||||
\\ struct FragOut
|
||||
\\ {
|
||||
\\ [location(0)] color: vec4[f32]
|
||||
\\ }
|
||||
\\
|
||||
\\ fn affine(value: f32, scale: f32, bias: f32) -> f32
|
||||
\\ {
|
||||
\\ return value * scale + bias;
|
||||
\\ }
|
||||
\\
|
||||
\\ fn combine(a: vec2[f32], b: vec2[f32]) -> vec4[f32]
|
||||
\\ {
|
||||
\\ let left = affine(a.x, b.x, b.y);
|
||||
\\ let right = affine(a.y, b.y, b.x);
|
||||
\\ return vec4[f32](left, right, left + right, left - right);
|
||||
\\ }
|
||||
\\
|
||||
\\ [entry(frag)]
|
||||
\\ fn main() -> FragOut
|
||||
\\ {
|
||||
\\ let output: FragOut;
|
||||
\\ output.color = combine(vec2[f32](2.0, 3.0), vec2[f32](4.0, 5.0));
|
||||
\\ return output;
|
||||
\\ }
|
||||
;
|
||||
const code = try compileNzsl(allocator, shader);
|
||||
defer allocator.free(code);
|
||||
|
||||
try case.expect(.{
|
||||
.source = code,
|
||||
.expected_outputs = &.{
|
||||
std.mem.asBytes(&[_]f32{ 13.0, 19.0, 32.0, -6.0 }),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
test "Struct logic" {
|
||||
const allocator = std.testing.allocator;
|
||||
const shader =
|
||||
\\ [nzsl_version("1.1")]
|
||||
\\ module;
|
||||
\\
|
||||
\\ struct Pair
|
||||
\\ {
|
||||
\\ a: f32,
|
||||
\\ b: f32
|
||||
\\ }
|
||||
\\
|
||||
\\ struct FragOut
|
||||
\\ {
|
||||
\\ [location(0)] color: vec4[f32]
|
||||
\\ }
|
||||
\\
|
||||
\\ fn eval(pair: Pair) -> vec2[f32]
|
||||
\\ {
|
||||
\\ return vec2[f32](pair.a + pair.b, pair.a * pair.b);
|
||||
\\ }
|
||||
\\
|
||||
\\ [entry(frag)]
|
||||
\\ fn main() -> FragOut
|
||||
\\ {
|
||||
\\ let pair: Pair;
|
||||
\\ pair.a = 3.0;
|
||||
\\ pair.b = 4.0;
|
||||
\\ let v = eval(pair);
|
||||
\\ let output: FragOut;
|
||||
\\ output.color = vec4[f32](v.x, v.y, v.y - v.x, v.x + v.y);
|
||||
\\ return output;
|
||||
\\ }
|
||||
;
|
||||
const code = try compileNzsl(allocator, shader);
|
||||
defer allocator.free(code);
|
||||
|
||||
try case.expect(.{
|
||||
.source = code,
|
||||
.expected_outputs = &.{
|
||||
std.mem.asBytes(&[_]f32{ 7.0, 12.0, 5.0, 19.0 }),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -54,3 +54,47 @@ test "Simple while loop" {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
test "For filter" {
|
||||
const allocator = std.testing.allocator;
|
||||
const shader =
|
||||
\\ [nzsl_version("1.1")]
|
||||
\\ module;
|
||||
\\
|
||||
\\ struct FragOut
|
||||
\\ {
|
||||
\\ [location(0)] color: vec4[u32]
|
||||
\\ }
|
||||
\\
|
||||
\\ [entry(frag)]
|
||||
\\ fn main() -> FragOut
|
||||
\\ {
|
||||
\\ let even_sum: u32 = 0;
|
||||
\\ let odd_sum: u32 = 0;
|
||||
\\ let product: u32 = 1;
|
||||
\\ for i in u32(1) -> u32(8)
|
||||
\\ {
|
||||
\\ if ((i % u32(2)) == u32(0))
|
||||
\\ {
|
||||
\\ even_sum += i;
|
||||
\\ product *= i;
|
||||
\\ }
|
||||
\\ else
|
||||
\\ odd_sum += i;
|
||||
\\ }
|
||||
\\
|
||||
\\ let output: FragOut;
|
||||
\\ output.color = vec4[u32](even_sum, odd_sum, product, even_sum + odd_sum);
|
||||
\\ return output;
|
||||
\\ }
|
||||
;
|
||||
const code = try compileNzsl(allocator, shader);
|
||||
defer allocator.free(code);
|
||||
|
||||
try case.expect(.{
|
||||
.source = code,
|
||||
.expected_outputs = &.{
|
||||
std.mem.asBytes(&[_]u32{ 12, 16, 48, 28 }),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
+50
-6
@@ -245,12 +245,22 @@ test "Maths matrices" {
|
||||
e.* = switch (op.key) {
|
||||
.Add => b + r,
|
||||
.Sub => b - r,
|
||||
.Mul => b * r,
|
||||
.Mul => 0,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (op.key == .Mul) {
|
||||
for (0..L) |column_index| {
|
||||
for (0..L) |row_index| {
|
||||
for (0..L) |inner_index| {
|
||||
expected.val[column_index][row_index] += base.val[inner_index][row_index] * ratio.val[column_index][inner_index];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const shader = try std.fmt.allocPrint(
|
||||
allocator,
|
||||
\\ [nzsl_version("1.1")]
|
||||
@@ -307,11 +317,12 @@ test "Maths matrices with vectors" {
|
||||
const ratio: case.Vec(L, T) = .{ .val = case.random(@Vector(L, T)) };
|
||||
var expected: @Vector(L, T) = undefined;
|
||||
|
||||
expected[0] = (base.val[0][0] * ratio.val[0]) + (base.val[0][1] * ratio.val[1]) + (base.val[0][2] * ratio.val[2]) + if (L == 4) (base.val[0][3] * ratio.val[3]) else 0.0;
|
||||
expected[1] = (base.val[1][0] * ratio.val[0]) + (base.val[1][1] * ratio.val[1]) + (base.val[1][2] * ratio.val[2]) + if (L == 4) (base.val[1][3] * ratio.val[3]) else 0.0;
|
||||
expected[2] = (base.val[2][0] * ratio.val[0]) + (base.val[2][1] * ratio.val[1]) + (base.val[2][2] * ratio.val[2]) + if (L == 4) (base.val[2][3] * ratio.val[3]) else 0.0;
|
||||
if (L == 4)
|
||||
expected[3] = (base.val[3][0] * ratio.val[0]) + (base.val[3][1] * ratio.val[1]) + (base.val[3][2] * ratio.val[2]) + (base.val[3][3] * ratio.val[3]);
|
||||
expected = @splat(0);
|
||||
inline for (0..L) |row_index| {
|
||||
inline for (0..L) |column_index| {
|
||||
expected[row_index] += base.val[column_index][row_index] * ratio.val[column_index];
|
||||
}
|
||||
}
|
||||
|
||||
const shader = try std.fmt.allocPrint(
|
||||
allocator,
|
||||
@@ -355,3 +366,36 @@ test "Maths matrices with vectors" {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "Swizzle" {
|
||||
const allocator = std.testing.allocator;
|
||||
const shader =
|
||||
\\ [nzsl_version("1.1")]
|
||||
\\ module;
|
||||
\\
|
||||
\\ struct FragOut
|
||||
\\ {
|
||||
\\ [location(0)] color: vec4[f32]
|
||||
\\ }
|
||||
\\
|
||||
\\ [entry(frag)]
|
||||
\\ fn main() -> FragOut
|
||||
\\ {
|
||||
\\ let v = vec4[f32](1.0, 2.0, 3.0, 4.0);
|
||||
\\ let a = v.yx;
|
||||
\\ let b = v.wz;
|
||||
\\ let output: FragOut;
|
||||
\\ output.color = vec4[f32](a.x, a.y, b.x, b.y);
|
||||
\\ return output;
|
||||
\\ }
|
||||
;
|
||||
const code = try compileNzsl(allocator, shader);
|
||||
defer allocator.free(code);
|
||||
|
||||
try case.expect(.{
|
||||
.source = code,
|
||||
.expected_outputs = &.{
|
||||
std.mem.asBytes(&[_]f32{ 2.0, 1.0, 4.0, 3.0 }),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -132,6 +132,7 @@ pub const case = struct {
|
||||
};
|
||||
|
||||
test {
|
||||
std.testing.refAllDecls(@import("api.zig"));
|
||||
std.testing.refAllDecls(@import("arrays.zig"));
|
||||
std.testing.refAllDecls(@import("basics.zig"));
|
||||
std.testing.refAllDecls(@import("bitwise.zig"));
|
||||
|
||||
+49
-1
@@ -3,7 +3,7 @@ const root = @import("root.zig");
|
||||
const compileNzsl = root.compileNzsl;
|
||||
const case = root.case;
|
||||
|
||||
test "Simple SSBO" {
|
||||
test "SSBO read" {
|
||||
const allocator = std.testing.allocator;
|
||||
const shader =
|
||||
\\ [nzsl_version("1.1")]
|
||||
@@ -58,3 +58,51 @@ test "Simple SSBO" {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
test "SSBO write" {
|
||||
const allocator = std.testing.allocator;
|
||||
const shader =
|
||||
\\ [nzsl_version("1.1")]
|
||||
\\ module;
|
||||
\\
|
||||
\\ [layout(std430)]
|
||||
\\ struct SSBO
|
||||
\\ {
|
||||
\\ data: dyn_array[u32]
|
||||
\\ }
|
||||
\\
|
||||
\\ external
|
||||
\\ {
|
||||
\\ [set(0), binding(0)] ssbo: storage[SSBO],
|
||||
\\ }
|
||||
\\
|
||||
\\ [entry(compute)]
|
||||
\\ [workgroup(1, 1, 1)]
|
||||
\\ fn main()
|
||||
\\ {
|
||||
\\ for i in u32(2) -> u32(8)
|
||||
\\ {
|
||||
\\ ssbo.data[i] = ssbo.data[i - u32(1)] + ssbo.data[i - u32(2)];
|
||||
\\ }
|
||||
\\ }
|
||||
;
|
||||
const code = try compileNzsl(allocator, shader);
|
||||
defer allocator.free(code);
|
||||
|
||||
var ssbo = [_]u32{ 1, 1, 0, 0, 0, 0, 0, 0 };
|
||||
const expected = [_]u32{ 1, 1, 2, 3, 5, 8, 13, 21 };
|
||||
|
||||
try case.expect(.{
|
||||
.source = code,
|
||||
.descriptor_sets = &.{
|
||||
&.{
|
||||
std.mem.asBytes(&ssbo),
|
||||
},
|
||||
},
|
||||
.expected_descriptor_sets = &.{
|
||||
&.{
|
||||
std.mem.asBytes(&expected),
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
+168
-5
@@ -1,6 +1,114 @@
|
||||
#include <stdio.h>
|
||||
#include <SpirvInterpreter.h>
|
||||
|
||||
#define CHECK_RESULT(expr) do { \
|
||||
SpvResult check_result = (expr); \
|
||||
if (check_result != SPV_RESULT_SUCCESS) \
|
||||
{ \
|
||||
fprintf(stderr, "%s failed with %d\n", #expr, check_result); \
|
||||
return -1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static SpvResult ReadImageFloat4(void* driver_image, SpvDim dim, int x, int y, int z, SpvVec4f* dst)
|
||||
{
|
||||
(void)driver_image;
|
||||
(void)dim;
|
||||
(void)x;
|
||||
(void)y;
|
||||
(void)z;
|
||||
(void)dst;
|
||||
return SPV_RESULT_UNSUPPORTED_SPIRV;
|
||||
}
|
||||
|
||||
static SpvResult ReadImageInt4(void* driver_image, SpvDim dim, int x, int y, int z, SpvVec4u* dst)
|
||||
{
|
||||
(void)driver_image;
|
||||
(void)dim;
|
||||
(void)x;
|
||||
(void)y;
|
||||
(void)z;
|
||||
(void)dst;
|
||||
return SPV_RESULT_UNSUPPORTED_SPIRV;
|
||||
}
|
||||
|
||||
static SpvResult WriteImageFloat4(void* driver_image, SpvDim dim, int x, int y, int z, SpvVec4f src)
|
||||
{
|
||||
(void)driver_image;
|
||||
(void)dim;
|
||||
(void)x;
|
||||
(void)y;
|
||||
(void)z;
|
||||
(void)src;
|
||||
return SPV_RESULT_UNSUPPORTED_SPIRV;
|
||||
}
|
||||
|
||||
static SpvResult WriteImageInt4(void* driver_image, SpvDim dim, int x, int y, int z, SpvVec4u src)
|
||||
{
|
||||
(void)driver_image;
|
||||
(void)dim;
|
||||
(void)x;
|
||||
(void)y;
|
||||
(void)z;
|
||||
(void)src;
|
||||
return SPV_RESULT_UNSUPPORTED_SPIRV;
|
||||
}
|
||||
|
||||
static SpvResult SampleImageFloat4(void* driver_image, void* driver_sampler, SpvDim dim, float x, float y, float z, SpvBool has_lod, float lod, SpvImageOffset offset, SpvVec4f* dst)
|
||||
{
|
||||
(void)driver_image;
|
||||
(void)driver_sampler;
|
||||
(void)dim;
|
||||
(void)x;
|
||||
(void)y;
|
||||
(void)z;
|
||||
(void)has_lod;
|
||||
(void)lod;
|
||||
(void)offset;
|
||||
(void)dst;
|
||||
return SPV_RESULT_UNSUPPORTED_SPIRV;
|
||||
}
|
||||
|
||||
static SpvResult SampleImageInt4(void* driver_image, void* driver_sampler, SpvDim dim, float x, float y, float z, SpvBool has_lod, float lod, SpvImageOffset offset, SpvVec4u* dst)
|
||||
{
|
||||
(void)driver_image;
|
||||
(void)driver_sampler;
|
||||
(void)dim;
|
||||
(void)x;
|
||||
(void)y;
|
||||
(void)z;
|
||||
(void)has_lod;
|
||||
(void)lod;
|
||||
(void)offset;
|
||||
(void)dst;
|
||||
return SPV_RESULT_UNSUPPORTED_SPIRV;
|
||||
}
|
||||
|
||||
static SpvResult SampleImageDref(void* driver_image, void* driver_sampler, SpvDim dim, float x, float y, float z, float dref, SpvBool has_lod, float lod, SpvImageOffset offset, float* dst)
|
||||
{
|
||||
(void)driver_image;
|
||||
(void)driver_sampler;
|
||||
(void)dim;
|
||||
(void)x;
|
||||
(void)y;
|
||||
(void)z;
|
||||
(void)dref;
|
||||
(void)has_lod;
|
||||
(void)lod;
|
||||
(void)offset;
|
||||
(void)dst;
|
||||
return SPV_RESULT_UNSUPPORTED_SPIRV;
|
||||
}
|
||||
|
||||
static SpvResult QueryImageSize(void* driver_image, SpvDim dim, SpvBool arrayed, SpvVec4u* dst)
|
||||
{
|
||||
(void)driver_image;
|
||||
(void)dim;
|
||||
(void)arrayed;
|
||||
(void)dst;
|
||||
return SPV_RESULT_UNSUPPORTED_SPIRV;
|
||||
}
|
||||
|
||||
static const unsigned char shader_source[] = {
|
||||
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x82, 0x10, 0x27, 0x00, 0x17, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00,
|
||||
@@ -53,24 +161,79 @@ int main(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
SpvWord binding_result = 0;
|
||||
if (SpvModuleGetBindingResult(module, 0, 0, &binding_result) != SPV_RESULT_NOT_FOUND)
|
||||
{
|
||||
fprintf(stderr, "Unexpected binding lookup result\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
SpvImageAPI image_api = {
|
||||
.SpvReadImageFloat4 = ReadImageFloat4,
|
||||
.SpvReadImageInt4 = ReadImageInt4,
|
||||
.SpvWriteImageFloat4 = WriteImageFloat4,
|
||||
.SpvWriteImageInt4 = WriteImageInt4,
|
||||
.SpvSampleImageFloat4 = SampleImageFloat4,
|
||||
.SpvSampleImageInt4 = SampleImageInt4,
|
||||
.SpvSampleImageDref = SampleImageDref,
|
||||
.SpvQueryImageSize = QueryImageSize
|
||||
};
|
||||
|
||||
SpvRuntime runtime;
|
||||
if(SpvInitRuntime(&runtime, module, (SpvImageAPI){0}) != SPV_RESULT_SUCCESS)
|
||||
if(SpvInitRuntime(&runtime, module, image_api) != SPV_RESULT_SUCCESS)
|
||||
{
|
||||
fprintf(stderr, "Runtime init failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
SpvRuntime second_runtime;
|
||||
if(SpvInitRuntime(&second_runtime, module, image_api) != SPV_RESULT_SUCCESS)
|
||||
{
|
||||
fprintf(stderr, "Second runtime init failed\n");
|
||||
SpvDeinitRuntime(runtime);
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned int spec_value = 64;
|
||||
SpvRuntimeSpecializationEntry spec_entry = {
|
||||
.id = 0,
|
||||
.offset = 0,
|
||||
.size = sizeof(spec_value)
|
||||
};
|
||||
CHECK_RESULT(SpvAddSpecializationInfo(runtime, spec_entry, (const SpvByte*)&spec_value, sizeof(spec_value)));
|
||||
CHECK_RESULT(SpvCopySpecializationConstantsFrom(second_runtime, runtime));
|
||||
|
||||
SpvWord main_entry_index;
|
||||
SpvGetEntryPointByName(runtime, "main", &main_entry_index);
|
||||
SpvCallEntryPoint(runtime, main_entry_index);
|
||||
CHECK_RESULT(SpvGetEntryPointByName(runtime, "main", &main_entry_index));
|
||||
CHECK_RESULT(SpvCallEntryPoint(runtime, main_entry_index));
|
||||
|
||||
float output[4];
|
||||
SpvWord output_result;
|
||||
SpvGetResultByName(runtime, "color", &output_result);
|
||||
SpvReadOutput(runtime, (SpvByte*)output, sizeof(output), output_result);
|
||||
CHECK_RESULT(SpvGetResultByName(runtime, "color", &output_result));
|
||||
CHECK_RESULT(SpvReadOutput(runtime, (SpvByte*)output, sizeof(output), output_result));
|
||||
|
||||
SpvSize output_size = 0;
|
||||
SpvPrimitiveType primitive_type;
|
||||
CHECK_RESULT(SpvGetResultMemorySize(runtime, output_result, &output_size));
|
||||
CHECK_RESULT(SpvGetResultPrimitiveType(runtime, output_result, &primitive_type));
|
||||
if (output_size != sizeof(output) || primitive_type != SPV_PRIMITIVE_FLOAT || primitive_type == SPV_PRIMITIVE_SINT || primitive_type == SPV_PRIMITIVE_UINT)
|
||||
{
|
||||
fprintf(stderr, "Unexpected output metadata\n");
|
||||
SpvDeinitRuntime(second_runtime);
|
||||
SpvDeinitRuntime(runtime);
|
||||
SpvDeinitModule(module);
|
||||
return -1;
|
||||
}
|
||||
|
||||
float dx[4] = { 1.0f, 2.0f, 3.0f, 4.0f };
|
||||
float dy[4] = { 5.0f, 6.0f, 7.0f, 8.0f };
|
||||
CHECK_RESULT(SpvSetDerivativeFromMemory(runtime, output_result, (const SpvByte*)dx, sizeof(dx), (const SpvByte*)dy, sizeof(dy)));
|
||||
CHECK_RESULT(SpvCopyDerivative(runtime, output_result, output_result));
|
||||
SpvClearDerivative(runtime, output_result);
|
||||
|
||||
printf("Output: Vec4[%f, %f, %f, %f]\n", output[0], output[1], output[2], output[3]);
|
||||
|
||||
SpvDeinitRuntime(second_runtime);
|
||||
SpvDeinitRuntime(runtime);
|
||||
SpvDeinitModule(module);
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user