fixes alignment issues with getMappedRange, getConstMappedRange
Prior to this change `getMappedRange` and `getConstMappedRange` would not handle alignment to `COPY_BUFFER_ALIGNMENT` for users, and so invocation could result in an unhelpful error message:
```
thread 254201 panic: attempt to use null value
.../mach/gpu/src/NativeInstance.zig:1721:42: 0x480747
in .gpu.NativeInstance.buffer_vtable.getMappedRange (game)
return @ptrCast([*c]u8, range.?)[0..size];
^
```
To address this:
1. Ensure we always request a 4-byte aligned buffer when `createBuffer` is called.
2. Ensure we always map 4-byte aligned buffers.
Co-authored-by: d3m1gd <mach+d3m1gd@users.noreply.github.com>
292 lines
11 KiB
Zig
292 lines
11 KiB
Zig
//! A GPUDevice / logical instantiation of an adapter.
|
|
//!
|
|
//! A device is the exclusive owner of all internal objects created from it: when the device is
|
|
//! lost or destroyed, it and all objects created on it (directly, e.g. createTexture(), or
|
|
//! indirectly, e.g. createView()) become implicitly unusable.
|
|
//!
|
|
//! https://gpuweb.github.io/gpuweb/#devices
|
|
//! https://gpuweb.github.io/gpuweb/#gpuadapter
|
|
const std = @import("std");
|
|
|
|
const Feature = @import("enums.zig").Feature;
|
|
const ErrorType = @import("enums.zig").ErrorType;
|
|
const ErrorFilter = @import("enums.zig").ErrorFilter;
|
|
const Limits = @import("data.zig").Limits;
|
|
const LoggingType = @import("enums.zig").LoggingType;
|
|
const ErrorCallback = @import("structs.zig").ErrorCallback;
|
|
const LoggingCallback = @import("structs.zig").LoggingCallback;
|
|
const Queue = @import("Queue.zig");
|
|
const ShaderModule = @import("ShaderModule.zig");
|
|
const Surface = @import("Surface.zig");
|
|
const SwapChain = @import("SwapChain.zig");
|
|
const RenderPipeline = @import("RenderPipeline.zig");
|
|
const CommandEncoder = @import("CommandEncoder.zig");
|
|
const ComputePipeline = @import("ComputePipeline.zig");
|
|
const BindGroup = @import("BindGroup.zig");
|
|
const BindGroupLayout = @import("BindGroupLayout.zig");
|
|
const Buffer = @import("Buffer.zig");
|
|
const ExternalTexture = @import("ExternalTexture.zig");
|
|
const PipelineLayout = @import("PipelineLayout.zig");
|
|
const QuerySet = @import("QuerySet.zig");
|
|
const RenderBundleEncoder = @import("RenderBundleEncoder.zig");
|
|
const Sampler = @import("Sampler.zig");
|
|
const Texture = @import("Texture.zig");
|
|
|
|
const Device = @This();
|
|
|
|
/// The features supported by the device (i.e. the ones with which it was created).
|
|
features: []Feature,
|
|
_features: [std.enums.values(Feature).len]Feature = undefined,
|
|
|
|
/// The limits supported by the device (which are exactly the ones with which it was created).
|
|
limits: Limits,
|
|
|
|
/// The type erased pointer to the Device implementation
|
|
/// Equal to c.WGPUDevice for NativeInstance.
|
|
ptr: *anyopaque,
|
|
vtable: *const VTable,
|
|
|
|
pub const VTable = struct {
|
|
reference: fn (ptr: *anyopaque) void,
|
|
release: fn (ptr: *anyopaque) void,
|
|
createBindGroup: fn (ptr: *anyopaque, descriptor: *const BindGroup.Descriptor) BindGroup,
|
|
createBindGroupLayout: fn (ptr: *anyopaque, descriptor: *const BindGroupLayout.Descriptor) BindGroupLayout,
|
|
createBuffer: fn (ptr: *anyopaque, descriptor: *const Buffer.Descriptor) Buffer,
|
|
createCommandEncoder: fn (ptr: *anyopaque, descriptor: ?*const CommandEncoder.Descriptor) CommandEncoder,
|
|
createComputePipeline: fn (ptr: *anyopaque, descriptor: *const ComputePipeline.Descriptor) ComputePipeline,
|
|
createComputePipelineAsync: fn (
|
|
ptr: *anyopaque,
|
|
descriptor: *const ComputePipeline.Descriptor,
|
|
callback: *ComputePipeline.CreateCallback,
|
|
) void,
|
|
createErrorBuffer: fn (ptr: *anyopaque) Buffer,
|
|
createExternalTexture: fn (ptr: *anyopaque, descriptor: *const ExternalTexture.Descriptor) ExternalTexture,
|
|
createPipelineLayout: fn (ptr: *anyopaque, descriptor: *const PipelineLayout.Descriptor) PipelineLayout,
|
|
createQuerySet: fn (ptr: *anyopaque, descriptor: *const QuerySet.Descriptor) QuerySet,
|
|
createRenderBundleEncoder: fn (ptr: *anyopaque, descriptor: *const RenderBundleEncoder.Descriptor) RenderBundleEncoder,
|
|
createRenderPipeline: fn (ptr: *anyopaque, descriptor: *const RenderPipeline.Descriptor) RenderPipeline,
|
|
createRenderPipelineAsync: fn (
|
|
ptr: *anyopaque,
|
|
descriptor: *const RenderPipeline.Descriptor,
|
|
callback: *RenderPipeline.CreateCallback,
|
|
) void,
|
|
createSampler: fn (ptr: *anyopaque, descriptor: *const Sampler.Descriptor) Sampler,
|
|
createShaderModule: fn (ptr: *anyopaque, descriptor: *const ShaderModule.Descriptor) ShaderModule,
|
|
nativeCreateSwapChain: fn (ptr: *anyopaque, surface: ?Surface, descriptor: *const SwapChain.Descriptor) SwapChain,
|
|
createTexture: fn (ptr: *anyopaque, descriptor: *const Texture.Descriptor) Texture,
|
|
destroy: fn (ptr: *anyopaque) void,
|
|
getQueue: fn (ptr: *anyopaque) Queue,
|
|
injectError: fn (ptr: *anyopaque, type: ErrorType, message: [*:0]const u8) void,
|
|
loseForTesting: fn (ptr: *anyopaque) void,
|
|
popErrorScope: fn (ptr: *anyopaque, callback: *ErrorCallback) bool,
|
|
pushErrorScope: fn (ptr: *anyopaque, filter: ErrorFilter) void,
|
|
setLostCallback: fn (ptr: *anyopaque, callback: *LostCallback) void,
|
|
setLoggingCallback: fn (ptr: *anyopaque, callback: *LoggingCallback) void,
|
|
setUncapturedErrorCallback: fn (ptr: *anyopaque, callback: *ErrorCallback) void,
|
|
tick: fn (ptr: *anyopaque) void,
|
|
};
|
|
|
|
pub inline fn reference(device: Device) void {
|
|
device.vtable.reference(device.ptr);
|
|
}
|
|
|
|
pub inline fn release(device: Device) void {
|
|
device.vtable.release(device.ptr);
|
|
}
|
|
|
|
/// Tests of the device has this feature & was created with it.
|
|
pub fn hasFeature(device: Device, feature: Feature) bool {
|
|
for (device.features) |f| {
|
|
if (f == feature) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
pub inline fn getQueue(device: Device) Queue {
|
|
return device.vtable.getQueue(device.ptr);
|
|
}
|
|
|
|
pub inline fn injectError(device: Device, typ: ErrorType, message: [*:0]const u8) void {
|
|
device.vtable.injectError(device.ptr, typ, message);
|
|
}
|
|
|
|
pub inline fn loseForTesting(device: Device) void {
|
|
device.vtable.loseForTesting(device.ptr);
|
|
}
|
|
|
|
pub inline fn popErrorScope(device: Device, callback: *ErrorCallback) bool {
|
|
return device.vtable.popErrorScope(device.ptr, callback);
|
|
}
|
|
|
|
pub inline fn pushErrorScope(device: Device, filter: ErrorFilter) void {
|
|
device.vtable.pushErrorScope(device.ptr, filter);
|
|
}
|
|
|
|
pub inline fn setLostCallback(device: Device, callback: *LostCallback) void {
|
|
device.vtable.setLostCallback(device.ptr, callback);
|
|
}
|
|
|
|
pub const LostCallback = struct {
|
|
type_erased_ctx: *anyopaque,
|
|
type_erased_callback: fn (ctx: *anyopaque, reason: LostReason, message: [*:0]const u8) callconv(.Inline) void,
|
|
|
|
pub fn init(
|
|
comptime Context: type,
|
|
ctx: Context,
|
|
comptime callback: fn (ctx: Context, reason: LostReason, message: [*:0]const u8) void,
|
|
) LostCallback {
|
|
const erased = (struct {
|
|
pub inline fn erased(type_erased_ctx: *anyopaque, reason: LostReason, message: [*:0]const u8) void {
|
|
callback(if (Context == void) {} else @ptrCast(Context, @alignCast(@alignOf(Context), type_erased_ctx)), reason, message);
|
|
}
|
|
}).erased;
|
|
|
|
return .{
|
|
.type_erased_ctx = if (Context == void) undefined else ctx,
|
|
.type_erased_callback = erased,
|
|
};
|
|
}
|
|
};
|
|
|
|
pub inline fn createBindGroup(device: Device, descriptor: *const BindGroup.Descriptor) BindGroup {
|
|
return device.vtable.createBindGroup(device.ptr, descriptor);
|
|
}
|
|
|
|
pub inline fn createBindGroupLayout(device: Device, descriptor: *const BindGroupLayout.Descriptor) BindGroupLayout {
|
|
return device.vtable.createBindGroupLayout(device.ptr, descriptor);
|
|
}
|
|
|
|
pub inline fn createSampler(device: Device, descriptor: *const Sampler.Descriptor) Sampler {
|
|
return device.vtable.createSampler(device.ptr, descriptor);
|
|
}
|
|
|
|
pub inline fn createShaderModule(device: Device, descriptor: *const ShaderModule.Descriptor) ShaderModule {
|
|
return device.vtable.createShaderModule(device.ptr, descriptor);
|
|
}
|
|
|
|
pub inline fn nativeCreateSwapChain(device: Device, surface: ?Surface, descriptor: *const SwapChain.Descriptor) SwapChain {
|
|
return device.vtable.nativeCreateSwapChain(device.ptr, surface, descriptor);
|
|
}
|
|
|
|
pub inline fn createTexture(device: Device, descriptor: *const Texture.Descriptor) Texture {
|
|
return device.vtable.createTexture(device.ptr, descriptor);
|
|
}
|
|
|
|
pub inline fn destroy(device: Device) void {
|
|
device.vtable.destroy(device.ptr);
|
|
}
|
|
|
|
pub inline fn createBuffer(device: Device, descriptor: *const Buffer.Descriptor) Buffer {
|
|
var local_descriptor = descriptor.*;
|
|
local_descriptor.size += local_descriptor.size % 4;
|
|
return device.vtable.createBuffer(device.ptr, &local_descriptor);
|
|
}
|
|
|
|
pub inline fn createCommandEncoder(device: Device, descriptor: ?*const CommandEncoder.Descriptor) CommandEncoder {
|
|
return device.vtable.createCommandEncoder(device.ptr, descriptor);
|
|
}
|
|
|
|
pub inline fn createComputePipeline(
|
|
device: Device,
|
|
descriptor: *const ComputePipeline.Descriptor,
|
|
) ComputePipeline {
|
|
return device.vtable.createComputePipeline(device.ptr, descriptor);
|
|
}
|
|
|
|
pub inline fn createComputePipelineAsync(
|
|
device: Device,
|
|
descriptor: *const ComputePipeline.Descriptor,
|
|
callback: *ComputePipeline.CreateCallback,
|
|
) void {
|
|
device.vtable.createComputePipelineAsync(device.ptr, descriptor, callback);
|
|
}
|
|
|
|
pub inline fn createErrorBuffer(device: Device) Buffer {
|
|
return device.vtable.createErrorBuffer(device.ptr);
|
|
}
|
|
|
|
pub inline fn createExternalTexture(device: Device, descriptor: *const ExternalTexture.Descriptor) ExternalTexture {
|
|
return device.vtable.createExternalTexture(device.ptr, descriptor);
|
|
}
|
|
|
|
pub inline fn createPipelineLayout(device: Device, descriptor: *const PipelineLayout.Descriptor) PipelineLayout {
|
|
return device.vtable.createPipelineLayout(device.ptr, descriptor);
|
|
}
|
|
|
|
pub inline fn createQuerySet(device: Device, descriptor: *const QuerySet.Descriptor) QuerySet {
|
|
return device.vtable.createQuerySet(device.ptr, descriptor);
|
|
}
|
|
|
|
pub inline fn createRenderBundleEncoder(device: Device, descriptor: *const RenderBundleEncoder.Descriptor) RenderBundleEncoder {
|
|
return device.vtable.createRenderBundleEncoder(device.ptr, descriptor);
|
|
}
|
|
|
|
pub inline fn createRenderPipeline(device: Device, descriptor: *const RenderPipeline.Descriptor) RenderPipeline {
|
|
return device.vtable.createRenderPipeline(device.ptr, descriptor);
|
|
}
|
|
|
|
pub inline fn createRenderPipelineAsync(
|
|
device: Device,
|
|
descriptor: *const RenderPipeline.Descriptor,
|
|
callback: *RenderPipeline.CreateCallback,
|
|
) void {
|
|
device.vtable.createRenderPipelineAsync(device.ptr, descriptor, callback);
|
|
}
|
|
|
|
pub inline fn setLoggingCallback(device: Device, callback: *LoggingCallback) void {
|
|
device.vtable.setLoggingCallback(device.ptr, callback);
|
|
}
|
|
|
|
pub inline fn setUncapturedErrorCallback(device: Device, callback: *ErrorCallback) void {
|
|
device.vtable.setUncapturedErrorCallback(device.ptr, callback);
|
|
}
|
|
|
|
pub inline fn tick(device: Device) void {
|
|
device.vtable.tick(device.ptr);
|
|
}
|
|
|
|
pub const Descriptor = struct {
|
|
label: ?[*:0]const u8 = null,
|
|
required_features: ?[]Feature = null,
|
|
required_limits: ?Limits = null,
|
|
};
|
|
|
|
pub const LostReason = enum(u32) {
|
|
none = 0x00000000,
|
|
destroyed = 0x00000001,
|
|
};
|
|
|
|
test {
|
|
_ = VTable;
|
|
_ = reference;
|
|
_ = release;
|
|
_ = getQueue;
|
|
_ = injectError;
|
|
_ = loseForTesting;
|
|
_ = popErrorScope;
|
|
_ = setLostCallback;
|
|
_ = createBindGroup;
|
|
_ = pushErrorScope;
|
|
_ = createBindGroupLayout;
|
|
_ = createSampler;
|
|
_ = createShaderModule;
|
|
_ = nativeCreateSwapChain;
|
|
_ = createTexture;
|
|
_ = destroy;
|
|
_ = createBuffer;
|
|
_ = createCommandEncoder;
|
|
_ = createComputePipeline;
|
|
_ = createComputePipelineAsync;
|
|
_ = createErrorBuffer;
|
|
_ = createExternalTexture;
|
|
_ = createPipelineLayout;
|
|
_ = createQuerySet;
|
|
_ = createRenderBundleEncoder;
|
|
_ = createRenderPipeline;
|
|
_ = createRenderPipelineAsync;
|
|
_ = setLoggingCallback;
|
|
_ = setUncapturedErrorCallback;
|
|
_ = tick;
|
|
_ = Descriptor;
|
|
_ = LostReason;
|
|
}
|