tiny.gpu.BackendCapabilities
Defined in contract.
API (34)
Actions
Public operations.
supportsArtifactFormatsupportsDTypesupportsFeaturessupportsRenderArtifactFormatsupportsRuntimesupportsSubgroupsupportsSurfaceFormatsupportsTextureFormatvalidateBufferAllocationvalidateCompileRequestvalidateLaunchGeometryvalidateLaunchRuntimevalidateRenderBindings: Checks thatrequestnames one resource of the right kind for each pipeline binding.validateRenderPass: Checks a pass against these capabilities and against the facts each draw's pipeline reported.validateRenderPipelineDescvalidateRuntimeRequirementsvalidateSubmitRuntimevalidateSurfaceCreationvalidateSyncRuntimevalidateTextureAllocationvalidateTextureTransfer: Checks thatbyte_countcovers every texel oftextureexactly once.
Fields and members
Public fields and members.
artifact_formatsdtypesfeaturesfloat_controlsidentitylayoutsmemoryrasterruntimesubgroupsurfacestexturesthreadgroup
Source
Source: lib/gpu/src/contract.zig:1037
zig
pub const BackendCapabilities = struct { identity: DeviceIdentity, memory: MemoryLimits = .{}, subgroup: SubgroupFacts = .{}, float_controls: FloatControlFacts = .{}, threadgroup: ThreadgroupFacts = .{}, dtypes: DTypeSet = .{}, layouts: LayoutFeatures = .{}, runtime: RuntimeRequirements = .{}, features: choir_abi.Features = .{}, artifact_formats: ArtifactFormatSet = .{}, textures: TextureCapabilities = .{}, surfaces: SurfaceCapabilities = .{}, raster: RasterCapabilities = .{}, pub fn supportsDType(self: BackendCapabilities, value: DType) bool { return self.dtypes.contains(value); } pub fn supportsArtifactFormat(self: BackendCapabilities, format: ArtifactFormat) bool { return self.artifact_formats.contains(format); } pub fn supportsTextureFormat(self: BackendCapabilities, format: TextureFormat) bool { return self.textures.supportsFormat(format); } pub fn supportsSurfaceFormat(self: BackendCapabilities, format: TextureFormat) bool { return self.surfaces.supportsFormat(format); } pub fn supportsRenderArtifactFormat(self: BackendCapabilities, format: RenderArtifactFormat) bool { return self.raster.supportsArtifactFormat(format); } pub fn supportsFeatures(self: BackendCapabilities, required: choir_abi.Features) bool { return self.features.containsAll(required); } pub fn supportsSubgroup(self: BackendCapabilities, required: choir_abi.SubgroupRequirements) bool { return self.subgroup.satisfies(required); } pub fn supportsRuntime(self: BackendCapabilities, required: RuntimeRequirements) bool { return self.runtime.containsAll(required); } pub fn validateCompileRequest(self: BackendCapabilities, request: CompileRequest) BackendError!void { if (!self.supportsArtifactFormat(request.requested_format)) return error.UnsupportedArtifactFormat; if (!self.dtypes.containsAll(request.required_dtypes)) return error.CapabilityMismatch; if (!self.supportsFeatures(request.required_features)) return error.CapabilityMismatch; if (!self.supportsSubgroup(request.required_subgroup)) return error.CapabilityMismatch; } pub fn validateRuntimeRequirements(self: BackendCapabilities, required: RuntimeRequirements) BackendError!void { if (!self.supportsRuntime(required)) return error.CapabilityMismatch; } pub fn validateBufferAllocation(self: BackendCapabilities, request: BufferAllocation) BackendError!void { if (request.byte_size == 0) return error.InvalidBuffer; if (request.alignment == 0) return error.InvalidBuffer; if ((request.alignment & (request.alignment - 1)) != 0) return error.InvalidBuffer; if (request.alignment < self.memory.min_buffer_alignment) return error.CapabilityMismatch; const byte_size = std.math.cast(u64, request.byte_size) orelse return error.CapabilityMismatch; if (self.memory.max_allocation_bytes) |limit| { if (byte_size > limit) return error.CapabilityMismatch; } if (self.memory.global_bytes) |limit| { if (byte_size > limit) return error.CapabilityMismatch; } if (request.dtype) |dtype| { if (!self.supportsDType(dtype)) return error.CapabilityMismatch; if (request.element_count) |count| { const required_bytes = std.math.mul(u64, count, dtype.sizeOf()) catch return error.CapabilityMismatch; if (required_bytes > byte_size) return error.InvalidBuffer; } } } pub fn validateTextureAllocation(self: BackendCapabilities, request: TextureAllocation) BackendError!void { if (!request.extent.valid()) return error.InvalidTexture; if (!request.usage.any()) return error.InvalidTexture; if (request.sample_count == 0) return error.InvalidTexture; if (!self.textures.supported) return error.CapabilityMismatch; if (!self.textures.supportsFormat(request.format)) return error.CapabilityMismatch; if (!self.textures.supportsUsage(request.usage)) return error.CapabilityMismatch; if (!self.textures.supportsExtent(request.extent)) return error.CapabilityMismatch; if (request.sample_count > self.textures.max_sample_count) return error.CapabilityMismatch; } pub fn validateSurfaceCreation(self: BackendCapabilities, request: SurfaceCreationRequest) BackendError!void { if (!request.extent.valid()) return error.InvalidSurface; if (!request.usage.any() or !request.usage.present) return error.InvalidSurface; if (request.max_frames_in_flight == 0) return error.InvalidSurface; if (!self.surfaces.supported) return error.CapabilityMismatch; if (!self.surfaces.supportsPlatform(request.platform)) return error.CapabilityMismatch; if (!self.surfaces.supportsFormat(request.format)) return error.CapabilityMismatch; if (!self.surfaces.supportsColorSpace(request.color_space)) return error.CapabilityMismatch; if (!self.surfaces.supportsPresentMode(request.present_mode)) return error.CapabilityMismatch; if (!self.surfaces.supportsUsage(request.usage)) return error.CapabilityMismatch; if (!self.surfaces.supportsExtent(request.extent)) return error.CapabilityMismatch; if (request.max_frames_in_flight > self.surfaces.max_frames_in_flight) return error.CapabilityMismatch; } pub fn validateRenderPipelineDesc(self: BackendCapabilities, desc: RenderPipelineDesc) BackendError!void { if (desc.vertex_entry_name.len == 0 or desc.fragment_entry_name.len == 0) return error.InvalidRenderArtifact; if (!self.raster.supported) return error.CapabilityMismatch; if (!self.raster.supportsArtifactFormat(desc.format)) return error.CapabilityMismatch; if (desc.target_format.isDepth()) return error.InvalidRenderArtifact; if (!self.raster.supportsTargetFormat(desc.target_format)) return error.CapabilityMismatch; if (desc.depth) |depth| { if (!depth.format.isDepth()) return error.InvalidRenderArtifact; if (!self.raster.supportsDepthFormat(depth.format)) return error.CapabilityMismatch; if (!depth.bias.valid()) return error.InvalidRenderArtifact; if (depth.bias.enabled() and !self.raster.depth_bias) return error.CapabilityMismatch; if (depth.bias.clamp != 0 and !self.raster.depth_bias_clamp) return error.CapabilityMismatch; } if (desc.push_constant_bytes % 4 != 0) return error.InvalidRenderArtifact; if (desc.push_constant_bytes > self.raster.max_push_constant_bytes) return error.CapabilityMismatch; if (desc.push_extent > desc.push_constant_bytes) return error.PushConstantRangeExceeded; if (!self.raster.supportsBlendMode(desc.blend_mode)) return error.CapabilityMismatch; if (!self.raster.supportsTopology(desc.topology)) return error.CapabilityMismatch; if (desc.vertex_layouts.len > self.raster.max_vertex_buffers) return error.CapabilityMismatch; if (desc.vertex_attributes.len > self.raster.max_vertex_attributes) return error.CapabilityMismatch; if (desc.bindings.len > self.raster.max_bindings) return error.CapabilityMismatch; for (desc.vertex_layouts) |layout| { if (layout.stride == 0) return error.InvalidRenderArtifact; if (layout.step_mode == .instance and !self.raster.instancing) return error.CapabilityMismatch; const start: usize = @intCast(layout.attribute_start); const count: usize = @intCast(layout.attribute_count); if (count == 0) return error.InvalidRenderArtifact; if (start > desc.vertex_attributes.len or count > desc.vertex_attributes.len - start) return error.InvalidRenderArtifact; for (desc.vertex_attributes[start..][0..count]) |attribute| { if (!self.raster.supportsVertexFormat(attribute.format)) return error.CapabilityMismatch; const size = renderVertexFormatByteSize(attribute.format); if (attribute.offset > layout.stride or size > layout.stride - attribute.offset) return error.InvalidRenderArtifact; } } for (desc.bindings) |binding| { if (!self.raster.supportsBindingKind(binding.kind)) return error.CapabilityMismatch; } } /// Checks a pass against these capabilities and against the facts each draw's pipeline /// reported. A draw's pipeline declares a depth state exactly when the pass has a depth /// attachment, because a pipeline is built for the attachments it draws into. pub fn validateRenderPass(self: BackendCapabilities, pass: RenderPass) BackendError!void { if (!self.raster.supported) return error.CapabilityMismatch; if (!pass.viewport.valid()) return error.RenderArgumentMismatch; if (!pass.scissor.valid()) return error.RenderArgumentMismatch; const color = pass.color.view; if (color.texture.format != color.format) return error.InvalidTexture; if (!color.texture.usage.color_attachment) return error.InvalidTexture; if (!self.raster.supportsTargetFormat(color.format)) return error.CapabilityMismatch; switch (pass.color.load) { .load => {}, .clear => |clear| if (!clear.valid()) return error.RenderArgumentMismatch, } const extent = color.texture.extent; if (!scissorWithin(pass.scissor, extent)) return error.RenderArgumentMismatch; if (pass.depth) |depth| { if (depth.view.texture.format != depth.view.format) return error.InvalidTexture; if (!depth.view.texture.usage.depth_attachment) return error.InvalidTexture; if (!self.raster.supportsDepthFormat(depth.view.format)) return error.CapabilityMismatch; if (!sameTextureExtent(depth.view.texture.extent, extent)) return error.RenderArgumentMismatch; switch (depth.load) { .load => {}, .clear => |clear| if (!(clear >= 0 and clear <= 1)) return error.RenderArgumentMismatch, } } for (pass.draws) |draw| try self.validateRenderDraw(pass, draw); } fn validateRenderDraw(self: BackendCapabilities, pass: RenderPass, draw: RenderDraw) BackendError!void { const pipeline = draw.pipeline; if (!draw.range.valid()) return error.RenderArgumentMismatch; if (pipeline.target_format != pass.color.view.format) return error.RenderArgumentMismatch; if ((pipeline.depth == null) != (pass.depth == null)) return error.RenderArgumentMismatch; if (pipeline.depth) |depth| { if (depth.format != pass.depth.?.view.format) return error.RenderArgumentMismatch; } if (draw.vertex_buffers.len != pipeline.vertex_buffer_count) return error.RenderArgumentMismatch; if ((pipeline.binding_count == 0) != (draw.bindings == null)) return error.RenderArgumentMismatch; if (draw.bindings) |bindings| { if (bindings.pipeline != pipeline.id) return error.RenderArgumentMismatch; } const range = draw.range; if (range.instance_count > 1 and !self.raster.instancing) return error.CapabilityMismatch; if (range.index_format != .none and !self.raster.supportsIndexFormat(range.index_format)) { return error.CapabilityMismatch; } if ((range.index_count != 0) != (draw.index_buffer != null)) return error.RenderArgumentMismatch; if (draw.push_constants.len != pipeline.push_constant_bytes) return error.RenderArgumentMismatch; } /// Checks that `request` names one resource of the right kind for each pipeline binding. pub fn validateRenderBindings(self: BackendCapabilities, request: RenderBindingsRequest) BackendError!void { if (!self.raster.supported) return error.CapabilityMismatch; const bindings = request.artifact.bindings; if (request.pipeline.binding_count != bindings.len) return error.RenderArgumentMismatch; if (request.resources.len != bindings.len) return error.RenderArgumentMismatch; for (bindings, request.resources) |binding, resource| { if (std.meta.activeTag(resource) != binding.kind) return error.RenderArgumentMismatch; if (!self.raster.supportsBindingKind(binding.kind)) return error.CapabilityMismatch; switch (resource) { .uniform_buffer, .storage_buffer => {}, .sampled_texture => |sampled| if (!sampled.texture.usage.sampled) return error.InvalidTexture, .storage_texture => |texture| if (!texture.usage.storage) return error.InvalidTexture, } } } /// Checks that `byte_count` covers every texel of `texture` exactly once. pub fn validateTextureTransfer(texture: TextureHandle, byte_count: usize, usage: TextureUsage) BackendError!void { if (!texture.usage.containsAll(usage)) return error.InvalidTexture; if (byte_count != try textureByteSize(texture)) return error.InvalidTexture; } pub fn validateLaunchRuntime(self: BackendCapabilities, request: LaunchRequest) BackendError!void { if (request.stream != null) try self.validateRuntimeRequirements(.{ .streams = true }); if (request.wait_events.len != 0 or request.signal_event != null) try self.validateRuntimeRequirements(.{ .events = true }); } pub fn validateSubmitRuntime( self: BackendCapabilities, stream: ?StreamHandle, wait_events: []const EventHandle, signal_event: ?EventHandle, ) BackendError!void { if (stream != null) try self.validateRuntimeRequirements(.{ .streams = true }); if (wait_events.len != 0 or signal_event != null) try self.validateRuntimeRequirements(.{ .events = true }); } pub fn validateSyncRuntime(self: BackendCapabilities, request: SyncRequest) BackendError!void { switch (request.scope) { .default_stream, .device => {}, .stream => try self.validateRuntimeRequirements(.{ .streams = true }), .event => try self.validateRuntimeRequirements(.{ .events = true }), } } pub fn validateLaunchGeometry(self: BackendCapabilities, geometry: choir_abi.LaunchGeometry) BackendError!void { if (geometry.grid[0] == 0 or geometry.grid[1] == 0 or geometry.grid[2] == 0) { return error.LaunchArgumentMismatch; } if (geometry.threadgroup[0] == 0 or geometry.threadgroup[1] == 0 or geometry.threadgroup[2] == 0) { return error.LaunchArgumentMismatch; } const total_threads = @as(u64, geometry.threadgroup[0]) * @as(u64, geometry.threadgroup[1]) * @as(u64, geometry.threadgroup[2]); if (total_threads > self.threadgroup.max_threads) return error.CapabilityMismatch; for (geometry.threadgroup, self.threadgroup.max_threads_per_dim) |requested, limit| { if (requested > limit) return error.CapabilityMismatch; } for (geometry.grid, self.threadgroup.max_blocks) |requested, limit| { if (requested > limit) return error.CapabilityMismatch; } for (geometry.grid, self.threadgroup.max_grid_per_dim) |requested, limit| { if (requested > limit) return error.CapabilityMismatch; } if (geometry.dynamic_shared_memory_bytes != 0 and !self.features.dynamic_shared_memory) { return error.CapabilityMismatch; } if (geometry.dynamic_shared_memory_bytes > self.threadgroup.shared_memory_bytes) { return error.CapabilityMismatch; } }};Source: lib/gpu/src/root.zig:100
zig
pub const BackendCapabilities = contract.BackendCapabilities;Complete call list for BackendCapabilities.validateRenderPipelineDesc
8 direct calls.
tiny.gpu.RasterCapabilities.supportsArtifactFormat[method] atlib/gpu/src/contract.zig:959tiny.gpu.RasterCapabilities.supportsBindingKind[method] atlib/gpu/src/contract.zig:983tiny.gpu.RasterCapabilities.supportsBlendMode[method] atlib/gpu/src/contract.zig:971tiny.gpu.RasterCapabilities.supportsDepthFormat[method] atlib/gpu/src/contract.zig:967tiny.gpu.RasterCapabilities.supportsTargetFormat[method] atlib/gpu/src/contract.zig:963tiny.gpu.RasterCapabilities.supportsTopology[method] atlib/gpu/src/contract.zig:975tiny.gpu.RasterCapabilities.supportsVertexFormat[method] atlib/gpu/src/contract.zig:979lib.gpu.src.contract.renderVertexFormatByteSize[function] — private source atlib/gpu/src/contract.zig:2496in nearest public ownertiny.gpu.contract
Audit
| Definitions | 22 |
|---|---|
| Public names | 44 |
| Members | 13 |
| Version | 26.7.0 |
| Revision | daab053ee433 |