src/sysgpu: move mach-sysgpu@d6ed118f54c4784f7ce01b70fc1b94f887fae1a8 package to here

Helps hexops/mach#1165

Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
Stephen Gutekanst 2024-03-04 21:41:23 -07:00 committed by Stephen Gutekanst
parent e38a441b69
commit e7b2ebef70
97 changed files with 51261 additions and 0 deletions

30
src/sysgpu/conventions.md Normal file
View file

@ -0,0 +1,30 @@
### Object ordering
Backends should be a single file with object in the following order:
- Instance
- Adapter
- Surface
- SurfaceCapabilities
- Device
- SwapChain
- Buffer
- Texture
- TextureView
- Sampler
- BindGroupLayout
- BindGroup
- PipelineLayout
- ShaderModule
- ComputePipeline
- RenderPipeline
- CommandBuffer
- CommandEncoder
- ComputePassEncoder
- RenderPassEncoder
- RenderBundle
- RenderBundleEncoder
- Queue
- QuerySet
Utility objects (e.g. StateTracker should come after the closest object that "owns" them.

4088
src/sysgpu/d3d12.zig Normal file

File diff suppressed because it is too large Load diff

6
src/sysgpu/d3d12/c.zig Normal file
View file

@ -0,0 +1,6 @@
pub usingnamespace @cImport({
@cInclude("d3d12.h");
@cInclude("dxgi1_6.h");
@cInclude("d3dcompiler.h");
@cInclude("dxgidebug.h");
});

737
src/sysgpu/d3d12/conv.zig Normal file
View file

@ -0,0 +1,737 @@
const sysgpu = @import("../sysgpu/main.zig");
const utils = @import("../utils.zig");
const c = @import("c.zig");
fn stencilEnable(stencil: sysgpu.StencilFaceState) bool {
return stencil.compare != .always or stencil.fail_op != .keep or stencil.depth_fail_op != .keep or stencil.pass_op != .keep;
}
pub fn winBool(b: bool) c.BOOL {
return if (b) c.TRUE else c.FALSE;
}
pub fn d3d12Blend(factor: sysgpu.BlendFactor) c.D3D12_BLEND {
return switch (factor) {
.zero => c.D3D12_BLEND_ZERO,
.one => c.D3D12_BLEND_ONE,
.src => c.D3D12_BLEND_SRC_COLOR,
.one_minus_src => c.D3D12_BLEND_INV_SRC_COLOR,
.src_alpha => c.D3D12_BLEND_SRC_ALPHA,
.one_minus_src_alpha => c.D3D12_BLEND_INV_SRC_ALPHA,
.dst => c.D3D12_BLEND_DEST_COLOR,
.one_minus_dst => c.D3D12_BLEND_INV_DEST_COLOR,
.dst_alpha => c.D3D12_BLEND_DEST_ALPHA,
.one_minus_dst_alpha => c.D3D12_BLEND_INV_DEST_ALPHA,
.src_alpha_saturated => c.D3D12_BLEND_SRC_ALPHA_SAT,
.constant => c.D3D12_BLEND_BLEND_FACTOR,
.one_minus_constant => c.D3D12_BLEND_INV_BLEND_FACTOR,
.src1 => c.D3D12_BLEND_SRC1_COLOR,
.one_minus_src1 => c.D3D12_BLEND_INV_SRC1_COLOR,
.src1_alpha => c.D3D12_BLEND_SRC1_ALPHA,
.one_minus_src1_alpha => c.D3D12_BLEND_INV_SRC1_ALPHA,
};
}
pub fn d3d12BlendDesc(desc: *const sysgpu.RenderPipeline.Descriptor) c.D3D12_BLEND_DESC {
var d3d12_targets = [_]c.D3D12_RENDER_TARGET_BLEND_DESC{d3d12RenderTargetBlendDesc(null)} ** 8;
if (desc.fragment) |frag| {
for (0..frag.target_count) |i| {
const target = frag.targets.?[i];
d3d12_targets[i] = d3d12RenderTargetBlendDesc(target);
}
}
return .{
.AlphaToCoverageEnable = winBool(desc.multisample.alpha_to_coverage_enabled == .true),
.IndependentBlendEnable = c.TRUE,
.RenderTarget = d3d12_targets,
};
}
pub fn d3d12BlendOp(op: sysgpu.BlendOperation) c.D3D12_BLEND_OP {
return switch (op) {
.add => c.D3D12_BLEND_OP_ADD,
.subtract => c.D3D12_BLEND_OP_SUBTRACT,
.reverse_subtract => c.D3D12_BLEND_OP_REV_SUBTRACT,
.min => c.D3D12_BLEND_OP_MIN,
.max => c.D3D12_BLEND_OP_MAX,
};
}
pub fn d3d12ComparisonFunc(func: sysgpu.CompareFunction) c.D3D12_COMPARISON_FUNC {
return switch (func) {
.undefined => unreachable,
.never => c.D3D12_COMPARISON_FUNC_NEVER,
.less => c.D3D12_COMPARISON_FUNC_LESS,
.less_equal => c.D3D12_COMPARISON_FUNC_LESS_EQUAL,
.greater => c.D3D12_COMPARISON_FUNC_GREATER,
.greater_equal => c.D3D12_COMPARISON_FUNC_GREATER_EQUAL,
.equal => c.D3D12_COMPARISON_FUNC_EQUAL,
.not_equal => c.D3D12_COMPARISON_FUNC_NOT_EQUAL,
.always => c.D3D12_COMPARISON_FUNC_ALWAYS,
};
}
pub fn d3d12CullMode(mode: sysgpu.CullMode) c.D3D12_CULL_MODE {
return switch (mode) {
.none => c.D3D12_CULL_MODE_NONE,
.front => c.D3D12_CULL_MODE_FRONT,
.back => c.D3D12_CULL_MODE_BACK,
};
}
pub fn d3d12DepthStencilDesc(depth_stencil: ?*const sysgpu.DepthStencilState) c.D3D12_DEPTH_STENCIL_DESC {
return if (depth_stencil) |ds| .{
.DepthEnable = winBool(ds.depth_compare != .always or ds.depth_write_enabled == .true),
.DepthWriteMask = if (ds.depth_write_enabled == .true) c.D3D12_DEPTH_WRITE_MASK_ALL else c.D3D12_DEPTH_WRITE_MASK_ZERO,
.DepthFunc = d3d12ComparisonFunc(ds.depth_compare),
.StencilEnable = winBool(stencilEnable(ds.stencil_front) or stencilEnable(ds.stencil_back)),
.StencilReadMask = @intCast(ds.stencil_read_mask & 0xff),
.StencilWriteMask = @intCast(ds.stencil_write_mask & 0xff),
.FrontFace = d3d12DepthStencilOpDesc(ds.stencil_front),
.BackFace = d3d12DepthStencilOpDesc(ds.stencil_back),
} else .{
.DepthEnable = c.FALSE,
.DepthWriteMask = c.D3D12_DEPTH_WRITE_MASK_ZERO,
.DepthFunc = c.D3D12_COMPARISON_FUNC_LESS,
.StencilEnable = c.FALSE,
.StencilReadMask = 0xff,
.StencilWriteMask = 0xff,
.FrontFace = d3d12DepthStencilOpDesc(null),
.BackFace = d3d12DepthStencilOpDesc(null),
};
}
pub fn d3d12DepthStencilOpDesc(opt_stencil: ?sysgpu.StencilFaceState) c.D3D12_DEPTH_STENCILOP_DESC {
return if (opt_stencil) |stencil| .{
.StencilFailOp = d3d12StencilOp(stencil.fail_op),
.StencilDepthFailOp = d3d12StencilOp(stencil.depth_fail_op),
.StencilPassOp = d3d12StencilOp(stencil.pass_op),
.StencilFunc = d3d12ComparisonFunc(stencil.compare),
} else .{
.StencilFailOp = c.D3D12_STENCIL_OP_KEEP,
.StencilDepthFailOp = c.D3D12_STENCIL_OP_KEEP,
.StencilPassOp = c.D3D12_STENCIL_OP_KEEP,
.StencilFunc = c.D3D12_COMPARISON_FUNC_ALWAYS,
};
}
pub fn d3d12DescriptorRangeType(entry: sysgpu.BindGroupLayout.Entry) c.D3D12_DESCRIPTOR_RANGE_TYPE {
if (entry.buffer.type != .undefined) {
return switch (entry.buffer.type) {
.undefined => unreachable,
.uniform => c.D3D12_DESCRIPTOR_RANGE_TYPE_CBV,
.storage => c.D3D12_DESCRIPTOR_RANGE_TYPE_UAV,
.read_only_storage => c.D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
};
} else if (entry.sampler.type != .undefined) {
return c.D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
} else if (entry.texture.sample_type != .undefined) {
return c.D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
} else {
// storage_texture
return c.D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
}
unreachable;
}
pub fn d3d12FilterType(filter: sysgpu.FilterMode) c.D3D12_FILTER_TYPE {
return switch (filter) {
.nearest => c.D3D12_FILTER_TYPE_POINT,
.linear => c.D3D12_FILTER_TYPE_LINEAR,
};
}
pub fn d3d12FilterTypeForMipmap(filter: sysgpu.MipmapFilterMode) c.D3D12_FILTER_TYPE {
return switch (filter) {
.nearest => c.D3D12_FILTER_TYPE_POINT,
.linear => c.D3D12_FILTER_TYPE_LINEAR,
};
}
pub fn d3d12Filter(
mag_filter: sysgpu.FilterMode,
min_filter: sysgpu.FilterMode,
mipmap_filter: sysgpu.MipmapFilterMode,
max_anisotropy: u16,
) c.D3D12_FILTER {
var filter: c.D3D12_FILTER = 0;
filter |= d3d12FilterType(min_filter) << c.D3D12_MIN_FILTER_SHIFT;
filter |= d3d12FilterType(mag_filter) << c.D3D12_MAG_FILTER_SHIFT;
filter |= d3d12FilterTypeForMipmap(mipmap_filter) << c.D3D12_MIP_FILTER_SHIFT;
filter |= c.D3D12_FILTER_REDUCTION_TYPE_STANDARD << c.D3D12_FILTER_REDUCTION_TYPE_SHIFT;
if (max_anisotropy > 1)
filter |= c.D3D12_ANISOTROPIC_FILTERING_BIT;
return filter;
}
pub fn d3d12FrontCounterClockwise(face: sysgpu.FrontFace) c.BOOL {
return switch (face) {
.ccw => c.TRUE,
.cw => c.FALSE,
};
}
pub fn d3d12HeapType(usage: sysgpu.Buffer.UsageFlags) c.D3D12_HEAP_TYPE {
return if (usage.map_write)
c.D3D12_HEAP_TYPE_UPLOAD
else if (usage.map_read)
c.D3D12_HEAP_TYPE_READBACK
else
c.D3D12_HEAP_TYPE_DEFAULT;
}
pub fn d3d12IndexBufferStripCutValue(strip_index_format: sysgpu.IndexFormat) c.D3D12_INDEX_BUFFER_STRIP_CUT_VALUE {
return switch (strip_index_format) {
.undefined => c.D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_DISABLED,
.uint16 => c.D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFF,
.uint32 => c.D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_0xFFFFFFFF,
};
}
pub fn d3d12InputClassification(mode: sysgpu.VertexStepMode) c.D3D12_INPUT_CLASSIFICATION {
return switch (mode) {
.vertex => c.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA,
.instance => c.D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA,
.vertex_buffer_not_used => undefined,
};
}
pub fn d3d12InputElementDesc(
buffer_index: usize,
layout: sysgpu.VertexBufferLayout,
attr: sysgpu.VertexAttribute,
) c.D3D12_INPUT_ELEMENT_DESC {
return .{
.SemanticName = "ATTR",
.SemanticIndex = attr.shader_location,
.Format = dxgiFormatForVertex(attr.format),
.InputSlot = @intCast(buffer_index),
.AlignedByteOffset = @intCast(attr.offset),
.InputSlotClass = d3d12InputClassification(layout.step_mode),
.InstanceDataStepRate = if (layout.step_mode == .instance) 1 else 0,
};
}
pub fn d3d12PrimitiveTopology(topology: sysgpu.PrimitiveTopology) c.D3D12_PRIMITIVE_TOPOLOGY {
return switch (topology) {
.point_list => c.D3D_PRIMITIVE_TOPOLOGY_POINTLIST,
.line_list => c.D3D_PRIMITIVE_TOPOLOGY_LINELIST,
.line_strip => c.D3D_PRIMITIVE_TOPOLOGY_LINESTRIP,
.triangle_list => c.D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
.triangle_strip => c.D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
};
}
pub fn d3d12PrimitiveTopologyType(topology: sysgpu.PrimitiveTopology) c.D3D12_PRIMITIVE_TOPOLOGY_TYPE {
return switch (topology) {
.point_list => c.D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT,
.line_list, .line_strip => c.D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE,
.triangle_list, .triangle_strip => c.D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,
};
}
pub fn d3d12RasterizerDesc(desc: *const sysgpu.RenderPipeline.Descriptor) c.D3D12_RASTERIZER_DESC {
const primitive_depth_control = utils.findChained(
sysgpu.PrimitiveDepthClipControl,
desc.primitive.next_in_chain.generic,
);
return .{
.FillMode = c.D3D12_FILL_MODE_SOLID,
.CullMode = d3d12CullMode(desc.primitive.cull_mode),
.FrontCounterClockwise = d3d12FrontCounterClockwise(desc.primitive.front_face),
.DepthBias = if (desc.depth_stencil) |ds| ds.depth_bias else 0,
.DepthBiasClamp = if (desc.depth_stencil) |ds| ds.depth_bias_clamp else 0.0,
.SlopeScaledDepthBias = if (desc.depth_stencil) |ds| ds.depth_bias_slope_scale else 0.0,
.DepthClipEnable = winBool(if (primitive_depth_control) |x| x.unclipped_depth == .false else true),
.MultisampleEnable = winBool(desc.multisample.count > 1),
.AntialiasedLineEnable = c.FALSE,
.ForcedSampleCount = 0,
.ConservativeRaster = c.D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF,
};
}
pub fn d3d12RenderTargetBlendDesc(opt_target: ?sysgpu.ColorTargetState) c.D3D12_RENDER_TARGET_BLEND_DESC {
var desc = c.D3D12_RENDER_TARGET_BLEND_DESC{
.BlendEnable = c.FALSE,
.LogicOpEnable = c.FALSE,
.SrcBlend = c.D3D12_BLEND_ONE,
.DestBlend = c.D3D12_BLEND_ZERO,
.BlendOp = c.D3D12_BLEND_OP_ADD,
.SrcBlendAlpha = c.D3D12_BLEND_ONE,
.DestBlendAlpha = c.D3D12_BLEND_ZERO,
.BlendOpAlpha = c.D3D12_BLEND_OP_ADD,
.LogicOp = c.D3D12_LOGIC_OP_NOOP,
.RenderTargetWriteMask = 0xf,
};
if (opt_target) |target| {
desc.RenderTargetWriteMask = d3d12RenderTargetWriteMask(target.write_mask);
if (target.blend) |blend| {
desc.BlendEnable = c.TRUE;
desc.SrcBlend = d3d12Blend(blend.color.src_factor);
desc.DestBlend = d3d12Blend(blend.color.dst_factor);
desc.BlendOp = d3d12BlendOp(blend.color.operation);
desc.SrcBlendAlpha = d3d12Blend(blend.alpha.src_factor);
desc.DestBlendAlpha = d3d12Blend(blend.alpha.dst_factor);
desc.BlendOpAlpha = d3d12BlendOp(blend.alpha.operation);
}
}
return desc;
}
pub fn d3d12RenderTargetWriteMask(mask: sysgpu.ColorWriteMaskFlags) c.UINT8 {
var writeMask: c.INT = 0;
if (mask.red)
writeMask |= c.D3D12_COLOR_WRITE_ENABLE_RED;
if (mask.green)
writeMask |= c.D3D12_COLOR_WRITE_ENABLE_GREEN;
if (mask.blue)
writeMask |= c.D3D12_COLOR_WRITE_ENABLE_BLUE;
if (mask.alpha)
writeMask |= c.D3D12_COLOR_WRITE_ENABLE_ALPHA;
return @intCast(writeMask);
}
pub fn d3d12ResourceSizeForBuffer(size: u64, usage: sysgpu.Buffer.UsageFlags) c.UINT64 {
var resource_size = size;
if (usage.uniform)
resource_size = utils.alignUp(resource_size, 256);
return resource_size;
}
pub fn d3d12ResourceStatesInitial(heap_type: c.D3D12_HEAP_TYPE, read_state: c.D3D12_RESOURCE_STATES) c.D3D12_RESOURCE_STATES {
return switch (heap_type) {
c.D3D12_HEAP_TYPE_UPLOAD => c.D3D12_RESOURCE_STATE_GENERIC_READ,
c.D3D12_HEAP_TYPE_READBACK => c.D3D12_RESOURCE_STATE_COPY_DEST,
else => read_state,
};
}
pub fn d3d12ResourceStatesForBufferRead(usage: sysgpu.Buffer.UsageFlags) c.D3D12_RESOURCE_STATES {
var states: c.D3D12_RESOURCE_STATES = c.D3D12_RESOURCE_STATE_COMMON;
if (usage.copy_src)
states |= c.D3D12_RESOURCE_STATE_COPY_SOURCE;
if (usage.index)
states |= c.D3D12_RESOURCE_STATE_INDEX_BUFFER;
if (usage.vertex or usage.uniform)
states |= c.D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER;
if (usage.storage)
states |= c.D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE;
if (usage.indirect)
states |= c.D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT;
return states;
}
pub fn d3d12ResourceStatesForTextureRead(usage: sysgpu.Texture.UsageFlags) c.D3D12_RESOURCE_STATES {
var states: c.D3D12_RESOURCE_STATES = c.D3D12_RESOURCE_STATE_COMMON;
if (usage.copy_src)
states |= c.D3D12_RESOURCE_STATE_COPY_SOURCE;
if (usage.texture_binding or usage.storage_binding)
states |= c.D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE;
return states;
}
pub fn d3d12ResourceFlagsForBuffer(usage: sysgpu.Buffer.UsageFlags) c.D3D12_RESOURCE_FLAGS {
var flags: c.D3D12_RESOURCE_FLAGS = c.D3D12_RESOURCE_FLAG_NONE;
if (usage.storage)
flags |= c.D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
return flags;
}
pub fn d3d12ResourceFlagsForTexture(
usage: sysgpu.Texture.UsageFlags,
format: sysgpu.Texture.Format,
) c.D3D12_RESOURCE_FLAGS {
var flags: c.D3D12_RESOURCE_FLAGS = c.D3D12_RESOURCE_FLAG_NONE;
if (usage.render_attachment) {
if (utils.formatHasDepthOrStencil(format)) {
flags |= c.D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
} else {
flags |= c.D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
}
}
if (usage.storage_binding)
flags |= c.D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
if (!usage.texture_binding and usage.render_attachment and utils.formatHasDepthOrStencil(format))
flags |= c.D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE;
return flags;
}
pub fn d3d12ResourceDimension(dimension: sysgpu.Texture.Dimension) c.D3D12_RESOURCE_DIMENSION {
return switch (dimension) {
.dimension_1d => c.D3D12_RESOURCE_DIMENSION_TEXTURE1D,
.dimension_2d => c.D3D12_RESOURCE_DIMENSION_TEXTURE2D,
.dimension_3d => c.D3D12_RESOURCE_DIMENSION_TEXTURE3D,
};
}
pub fn d3d12RootParameterType(entry: sysgpu.BindGroupLayout.Entry) c.D3D12_ROOT_PARAMETER_TYPE {
return switch (entry.buffer.type) {
.undefined => unreachable,
.uniform => c.D3D12_ROOT_PARAMETER_TYPE_CBV,
.storage => c.D3D12_ROOT_PARAMETER_TYPE_UAV,
.read_only_storage => c.D3D12_ROOT_PARAMETER_TYPE_SRV,
};
}
pub fn d3d12ShaderBytecode(opt_blob: ?*c.ID3DBlob) c.D3D12_SHADER_BYTECODE {
return if (opt_blob) |blob| .{
.pShaderBytecode = blob.lpVtbl.*.GetBufferPointer.?(blob),
.BytecodeLength = blob.lpVtbl.*.GetBufferSize.?(blob),
} else .{ .pShaderBytecode = null, .BytecodeLength = 0 };
}
pub fn d3d12SrvDimension(dimension: sysgpu.TextureView.Dimension, sample_count: u32) c.D3D12_SRV_DIMENSION {
return switch (dimension) {
.dimension_undefined => unreachable,
.dimension_1d => c.D3D12_SRV_DIMENSION_TEXTURE1D,
.dimension_2d => if (sample_count == 1) c.D3D12_SRV_DIMENSION_TEXTURE2D else c.D3D12_SRV_DIMENSION_TEXTURE2DMS,
.dimension_2d_array => if (sample_count == 1) c.D3D12_SRV_DIMENSION_TEXTURE2DARRAY else c.D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY,
.dimension_cube => c.D3D12_SRV_DIMENSION_TEXTURECUBE,
.dimension_cube_array => c.D3D12_SRV_DIMENSION_TEXTURECUBEARRAY,
.dimension_3d => c.D3D12_SRV_DIMENSION_TEXTURE3D,
};
}
pub fn d3d12StencilOp(op: sysgpu.StencilOperation) c.D3D12_STENCIL_OP {
return switch (op) {
.keep => c.D3D12_STENCIL_OP_KEEP,
.zero => c.D3D12_STENCIL_OP_ZERO,
.replace => c.D3D12_STENCIL_OP_REPLACE,
.invert => c.D3D12_STENCIL_OP_INVERT,
.increment_clamp => c.D3D12_STENCIL_OP_INCR_SAT,
.decrement_clamp => c.D3D12_STENCIL_OP_DECR_SAT,
.increment_wrap => c.D3D12_STENCIL_OP_INCR,
.decrement_wrap => c.D3D12_STENCIL_OP_DECR,
};
}
pub fn d3d12StreamOutputDesc() c.D3D12_STREAM_OUTPUT_DESC {
return .{
.pSODeclaration = null,
.NumEntries = 0,
.pBufferStrides = null,
.NumStrides = 0,
.RasterizedStream = 0,
};
}
pub fn d3d12TextureAddressMode(address_mode: sysgpu.Sampler.AddressMode) c.D3D12_TEXTURE_ADDRESS_MODE {
return switch (address_mode) {
.repeat => c.D3D12_TEXTURE_ADDRESS_MODE_WRAP,
.mirror_repeat => c.D3D12_TEXTURE_ADDRESS_MODE_MIRROR,
.clamp_to_edge => c.D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
};
}
pub fn d3d12UavDimension(dimension: sysgpu.TextureView.Dimension) c.D3D12_UAV_DIMENSION {
return switch (dimension) {
.dimension_undefined => unreachable,
.dimension_1d => c.D3D12_UAV_DIMENSION_TEXTURE1D,
.dimension_2d => c.D3D12_UAV_DIMENSION_TEXTURE2D,
.dimension_2d_array => c.D3D12_UAV_DIMENSION_TEXTURE2DARRAY,
.dimension_3d => c.D3D12_UAV_DIMENSION_TEXTURE3D,
else => unreachable, // TODO - UAV cube maps?
};
}
pub fn dxgiFormatForIndex(format: sysgpu.IndexFormat) c.DXGI_FORMAT {
return switch (format) {
.undefined => unreachable,
.uint16 => c.DXGI_FORMAT_R16_UINT,
.uint32 => c.DXGI_FORMAT_R32_UINT,
};
}
pub fn dxgiFormatForTexture(format: sysgpu.Texture.Format) c.DXGI_FORMAT {
return switch (format) {
.undefined => unreachable,
.r8_unorm => c.DXGI_FORMAT_R8_UNORM,
.r8_snorm => c.DXGI_FORMAT_R8_SNORM,
.r8_uint => c.DXGI_FORMAT_R8_UINT,
.r8_sint => c.DXGI_FORMAT_R8_SINT,
.r16_uint => c.DXGI_FORMAT_R16_UINT,
.r16_sint => c.DXGI_FORMAT_R16_SINT,
.r16_float => c.DXGI_FORMAT_R16_FLOAT,
.rg8_unorm => c.DXGI_FORMAT_R8G8_UNORM,
.rg8_snorm => c.DXGI_FORMAT_R8G8_SNORM,
.rg8_uint => c.DXGI_FORMAT_R8G8_UINT,
.rg8_sint => c.DXGI_FORMAT_R8G8_SINT,
.r32_float => c.DXGI_FORMAT_R32_FLOAT,
.r32_uint => c.DXGI_FORMAT_R32_UINT,
.r32_sint => c.DXGI_FORMAT_R32_SINT,
.rg16_uint => c.DXGI_FORMAT_R16G16_UINT,
.rg16_sint => c.DXGI_FORMAT_R16G16_SINT,
.rg16_float => c.DXGI_FORMAT_R16G16_FLOAT,
.rgba8_unorm => c.DXGI_FORMAT_R8G8B8A8_UNORM,
.rgba8_unorm_srgb => c.DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
.rgba8_snorm => c.DXGI_FORMAT_R8G8B8A8_SNORM,
.rgba8_uint => c.DXGI_FORMAT_R8G8B8A8_UINT,
.rgba8_sint => c.DXGI_FORMAT_R8G8B8A8_SINT,
.bgra8_unorm => c.DXGI_FORMAT_B8G8R8A8_UNORM,
.bgra8_unorm_srgb => c.DXGI_FORMAT_B8G8R8A8_UNORM_SRGB,
.rgb10_a2_unorm => c.DXGI_FORMAT_R10G10B10A2_UNORM,
.rg11_b10_ufloat => c.DXGI_FORMAT_R11G11B10_FLOAT,
.rgb9_e5_ufloat => c.DXGI_FORMAT_R9G9B9E5_SHAREDEXP,
.rg32_float => c.DXGI_FORMAT_R32G32_FLOAT,
.rg32_uint => c.DXGI_FORMAT_R32G32_UINT,
.rg32_sint => c.DXGI_FORMAT_R32G32_SINT,
.rgba16_uint => c.DXGI_FORMAT_R16G16B16A16_UINT,
.rgba16_sint => c.DXGI_FORMAT_R16G16B16A16_SINT,
.rgba16_float => c.DXGI_FORMAT_R16G16B16A16_FLOAT,
.rgba32_float => c.DXGI_FORMAT_R32G32B32A32_FLOAT,
.rgba32_uint => c.DXGI_FORMAT_R32G32B32A32_UINT,
.rgba32_sint => c.DXGI_FORMAT_R32G32B32A32_SINT,
.stencil8 => c.DXGI_FORMAT_D24_UNORM_S8_UINT,
.depth16_unorm => c.DXGI_FORMAT_D16_UNORM,
.depth24_plus => c.DXGI_FORMAT_D24_UNORM_S8_UINT,
.depth24_plus_stencil8 => c.DXGI_FORMAT_D24_UNORM_S8_UINT,
.depth32_float => c.DXGI_FORMAT_D32_FLOAT,
.depth32_float_stencil8 => c.DXGI_FORMAT_D32_FLOAT_S8X24_UINT,
.bc1_rgba_unorm => c.DXGI_FORMAT_BC1_UNORM,
.bc1_rgba_unorm_srgb => c.DXGI_FORMAT_BC1_UNORM_SRGB,
.bc2_rgba_unorm => c.DXGI_FORMAT_BC2_UNORM,
.bc2_rgba_unorm_srgb => c.DXGI_FORMAT_BC2_UNORM_SRGB,
.bc3_rgba_unorm => c.DXGI_FORMAT_BC3_UNORM,
.bc3_rgba_unorm_srgb => c.DXGI_FORMAT_BC3_UNORM_SRGB,
.bc4_runorm => c.DXGI_FORMAT_BC4_UNORM,
.bc4_rsnorm => c.DXGI_FORMAT_BC4_SNORM,
.bc5_rg_unorm => c.DXGI_FORMAT_BC5_UNORM,
.bc5_rg_snorm => c.DXGI_FORMAT_BC5_SNORM,
.bc6_hrgb_ufloat => c.DXGI_FORMAT_BC6H_UF16,
.bc6_hrgb_float => c.DXGI_FORMAT_BC6H_SF16,
.bc7_rgba_unorm => c.DXGI_FORMAT_BC7_UNORM,
.bc7_rgba_unorm_srgb => c.DXGI_FORMAT_BC7_UNORM_SRGB,
.etc2_rgb8_unorm,
.etc2_rgb8_unorm_srgb,
.etc2_rgb8_a1_unorm,
.etc2_rgb8_a1_unorm_srgb,
.etc2_rgba8_unorm,
.etc2_rgba8_unorm_srgb,
.eacr11_unorm,
.eacr11_snorm,
.eacrg11_unorm,
.eacrg11_snorm,
.astc4x4_unorm,
.astc4x4_unorm_srgb,
.astc5x4_unorm,
.astc5x4_unorm_srgb,
.astc5x5_unorm,
.astc5x5_unorm_srgb,
.astc6x5_unorm,
.astc6x5_unorm_srgb,
.astc6x6_unorm,
.astc6x6_unorm_srgb,
.astc8x5_unorm,
.astc8x5_unorm_srgb,
.astc8x6_unorm,
.astc8x6_unorm_srgb,
.astc8x8_unorm,
.astc8x8_unorm_srgb,
.astc10x5_unorm,
.astc10x5_unorm_srgb,
.astc10x6_unorm,
.astc10x6_unorm_srgb,
.astc10x8_unorm,
.astc10x8_unorm_srgb,
.astc10x10_unorm,
.astc10x10_unorm_srgb,
.astc12x10_unorm,
.astc12x10_unorm_srgb,
.astc12x12_unorm,
.astc12x12_unorm_srgb,
=> unreachable,
.r8_bg8_biplanar420_unorm => c.DXGI_FORMAT_NV12,
};
}
pub fn dxgiFormatForTextureResource(
format: sysgpu.Texture.Format,
usage: sysgpu.Texture.UsageFlags,
view_format_count: usize,
) c.DXGI_FORMAT {
_ = usage;
return if (view_format_count > 0)
dxgiFormatTypeless(format)
else
dxgiFormatForTexture(format);
}
pub fn dxgiFormatForTextureView(format: sysgpu.Texture.Format, aspect: sysgpu.Texture.Aspect) c.DXGI_FORMAT {
return switch (aspect) {
.all => switch (format) {
.stencil8 => c.DXGI_FORMAT_X24_TYPELESS_G8_UINT,
.depth16_unorm => c.DXGI_FORMAT_R16_UNORM,
.depth24_plus => c.DXGI_FORMAT_R24_UNORM_X8_TYPELESS,
.depth32_float => c.DXGI_FORMAT_R32_FLOAT,
else => dxgiFormatForTexture(format),
},
.stencil_only => switch (format) {
.stencil8 => c.DXGI_FORMAT_X24_TYPELESS_G8_UINT,
.depth24_plus_stencil8 => c.DXGI_FORMAT_X24_TYPELESS_G8_UINT,
.depth32_float_stencil8 => c.DXGI_FORMAT_X32_TYPELESS_G8X24_UINT,
else => unreachable,
},
.depth_only => switch (format) {
.depth16_unorm => c.DXGI_FORMAT_R16_UNORM,
.depth24_plus => c.DXGI_FORMAT_R24_UNORM_X8_TYPELESS,
.depth24_plus_stencil8 => c.DXGI_FORMAT_R24_UNORM_X8_TYPELESS,
.depth32_float => c.DXGI_FORMAT_R32_FLOAT,
.depth32_float_stencil8 => c.DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS,
else => unreachable,
},
.plane0_only => unreachable,
.plane1_only => unreachable,
};
}
pub fn dxgiFormatForVertex(format: sysgpu.VertexFormat) c.DXGI_FORMAT {
return switch (format) {
.undefined => unreachable,
.uint8x2 => c.DXGI_FORMAT_R8G8_UINT,
.uint8x4 => c.DXGI_FORMAT_R8G8B8A8_UINT,
.sint8x2 => c.DXGI_FORMAT_R8G8_SINT,
.sint8x4 => c.DXGI_FORMAT_R8G8B8A8_SINT,
.unorm8x2 => c.DXGI_FORMAT_R8G8_UNORM,
.unorm8x4 => c.DXGI_FORMAT_R8G8B8A8_UNORM,
.snorm8x2 => c.DXGI_FORMAT_R8G8_SNORM,
.snorm8x4 => c.DXGI_FORMAT_R8G8B8A8_SNORM,
.uint16x2 => c.DXGI_FORMAT_R16G16_UINT,
.uint16x4 => c.DXGI_FORMAT_R16G16B16A16_UINT,
.sint16x2 => c.DXGI_FORMAT_R16G16_SINT,
.sint16x4 => c.DXGI_FORMAT_R16G16B16A16_SINT,
.unorm16x2 => c.DXGI_FORMAT_R16G16_UNORM,
.unorm16x4 => c.DXGI_FORMAT_R16G16B16A16_UNORM,
.snorm16x2 => c.DXGI_FORMAT_R16G16_SNORM,
.snorm16x4 => c.DXGI_FORMAT_R16G16B16A16_SNORM,
.float16x2 => c.DXGI_FORMAT_R16G16_FLOAT,
.float16x4 => c.DXGI_FORMAT_R16G16B16A16_FLOAT,
.float32 => c.DXGI_FORMAT_R32_FLOAT,
.float32x2 => c.DXGI_FORMAT_R32G32_FLOAT,
.float32x3 => c.DXGI_FORMAT_R32G32B32_FLOAT,
.float32x4 => c.DXGI_FORMAT_R32G32B32A32_FLOAT,
.uint32 => c.DXGI_FORMAT_R32_UINT,
.uint32x2 => c.DXGI_FORMAT_R32G32_UINT,
.uint32x3 => c.DXGI_FORMAT_R32G32B32_UINT,
.uint32x4 => c.DXGI_FORMAT_R32G32B32A32_UINT,
.sint32 => c.DXGI_FORMAT_R32_SINT,
.sint32x2 => c.DXGI_FORMAT_R32G32_SINT,
.sint32x3 => c.DXGI_FORMAT_R32G32B32_SINT,
.sint32x4 => c.DXGI_FORMAT_R32G32B32A32_SINT,
};
}
pub fn dxgiFormatTypeless(format: sysgpu.Texture.Format) c.DXGI_FORMAT {
return switch (format) {
.undefined => unreachable,
.r8_unorm, .r8_snorm, .r8_uint, .r8_sint => c.DXGI_FORMAT_R8_TYPELESS,
.r16_uint, .r16_sint, .r16_float => c.DXGI_FORMAT_R16_TYPELESS,
.rg8_unorm, .rg8_snorm, .rg8_uint, .rg8_sint => c.DXGI_FORMAT_R8G8_TYPELESS,
.r32_float, .r32_uint, .r32_sint => c.DXGI_FORMAT_R32_TYPELESS,
.rg16_uint, .rg16_sint, .rg16_float => c.DXGI_FORMAT_R16G16_TYPELESS,
.rgba8_unorm, .rgba8_unorm_srgb, .rgba8_snorm, .rgba8_uint, .rgba8_sint => c.DXGI_FORMAT_R8G8B8A8_TYPELESS,
.bgra8_unorm, .bgra8_unorm_srgb => c.DXGI_FORMAT_B8G8R8A8_TYPELESS,
.rgb10_a2_unorm => c.DXGI_FORMAT_R10G10B10A2_TYPELESS,
.rg11_b10_ufloat => c.DXGI_FORMAT_R11G11B10_FLOAT,
.rgb9_e5_ufloat => c.DXGI_FORMAT_R9G9B9E5_SHAREDEXP,
.rg32_float, .rg32_uint, .rg32_sint => c.DXGI_FORMAT_R32G32_TYPELESS,
.rgba16_uint, .rgba16_sint, .rgba16_float => c.DXGI_FORMAT_R16G16B16A16_TYPELESS,
.rgba32_float, .rgba32_uint, .rgba32_sint => c.DXGI_FORMAT_R32G32B32A32_TYPELESS,
.stencil8 => c.DXGI_FORMAT_R24G8_TYPELESS,
.depth16_unorm => c.DXGI_FORMAT_R16_TYPELESS,
.depth24_plus => c.DXGI_FORMAT_R24G8_TYPELESS,
.depth24_plus_stencil8 => c.DXGI_FORMAT_R24G8_TYPELESS,
.depth32_float => c.DXGI_FORMAT_R32_TYPELESS,
.depth32_float_stencil8 => c.DXGI_FORMAT_R32G8X24_TYPELESS,
.bc1_rgba_unorm, .bc1_rgba_unorm_srgb => c.DXGI_FORMAT_BC1_TYPELESS,
.bc2_rgba_unorm, .bc2_rgba_unorm_srgb => c.DXGI_FORMAT_BC2_TYPELESS,
.bc3_rgba_unorm, .bc3_rgba_unorm_srgb => c.DXGI_FORMAT_BC3_TYPELESS,
.bc4_runorm, .bc4_rsnorm => c.DXGI_FORMAT_BC4_TYPELESS,
.bc5_rg_unorm, .bc5_rg_snorm => c.DXGI_FORMAT_BC5_TYPELESS,
.bc6_hrgb_ufloat, .bc6_hrgb_float => c.DXGI_FORMAT_BC6H_TYPELESS,
.bc7_rgba_unorm, .bc7_rgba_unorm_srgb => c.DXGI_FORMAT_BC7_TYPELESS,
.etc2_rgb8_unorm,
.etc2_rgb8_unorm_srgb,
.etc2_rgb8_a1_unorm,
.etc2_rgb8_a1_unorm_srgb,
.etc2_rgba8_unorm,
.etc2_rgba8_unorm_srgb,
.eacr11_unorm,
.eacr11_snorm,
.eacrg11_unorm,
.eacrg11_snorm,
.astc4x4_unorm,
.astc4x4_unorm_srgb,
.astc5x4_unorm,
.astc5x4_unorm_srgb,
.astc5x5_unorm,
.astc5x5_unorm_srgb,
.astc6x5_unorm,
.astc6x5_unorm_srgb,
.astc6x6_unorm,
.astc6x6_unorm_srgb,
.astc8x5_unorm,
.astc8x5_unorm_srgb,
.astc8x6_unorm,
.astc8x6_unorm_srgb,
.astc8x8_unorm,
.astc8x8_unorm_srgb,
.astc10x5_unorm,
.astc10x5_unorm_srgb,
.astc10x6_unorm,
.astc10x6_unorm_srgb,
.astc10x8_unorm,
.astc10x8_unorm_srgb,
.astc10x10_unorm,
.astc10x10_unorm_srgb,
.astc12x10_unorm,
.astc12x10_unorm_srgb,
.astc12x12_unorm,
.astc12x12_unorm_srgb,
=> unreachable,
.r8_bg8_biplanar420_unorm => c.DXGI_FORMAT_NV12,
};
}
pub fn dxgiFormatIsTypeless(format: c.DXGI_FORMAT) bool {
return switch (format) {
c.DXGI_FORMAT_R32G32B32A32_TYPELESS,
c.DXGI_FORMAT_R32G32B32_TYPELESS,
c.DXGI_FORMAT_R16G16B16A16_TYPELESS,
c.DXGI_FORMAT_R32G32_TYPELESS,
c.DXGI_FORMAT_R32G8X24_TYPELESS,
c.DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS,
c.DXGI_FORMAT_R10G10B10A2_TYPELESS,
c.DXGI_FORMAT_R8G8B8A8_TYPELESS,
c.DXGI_FORMAT_R16G16_TYPELESS,
c.DXGI_FORMAT_R32_TYPELESS,
c.DXGI_FORMAT_R24G8_TYPELESS,
c.DXGI_FORMAT_R8G8_TYPELESS,
c.DXGI_FORMAT_R16_TYPELESS,
c.DXGI_FORMAT_R8_TYPELESS,
c.DXGI_FORMAT_BC1_TYPELESS,
c.DXGI_FORMAT_BC2_TYPELESS,
c.DXGI_FORMAT_BC3_TYPELESS,
c.DXGI_FORMAT_BC4_TYPELESS,
c.DXGI_FORMAT_BC5_TYPELESS,
c.DXGI_FORMAT_B8G8R8A8_TYPELESS,
c.DXGI_FORMAT_BC6H_TYPELESS,
c.DXGI_FORMAT_BC7_TYPELESS,
=> true,
else => false,
};
}
pub fn dxgiUsage(usage: sysgpu.Texture.UsageFlags) c.DXGI_USAGE {
var dxgi_usage: c.DXGI_USAGE = 0;
if (usage.texture_binding)
dxgi_usage |= c.DXGI_USAGE_SHADER_INPUT;
if (usage.storage_binding)
dxgi_usage |= c.DXGI_USAGE_UNORDERED_ACCESS;
if (usage.render_attachment)
dxgi_usage |= c.DXGI_USAGE_RENDER_TARGET_OUTPUT;
return dxgi_usage;
}

44
src/sysgpu/d3d12/notes.md Normal file
View file

@ -0,0 +1,44 @@
### Interface Support
Windows release version needed to use various functionality in DXGI and D3D12.
DXGI
- 1.4 - baseline
- 1.5 - 1607
- 1.6 - 1703/1803/1809
CreateDXGIFactory
- CreateDXGIFactory2 - baseline
DXGIGetDebugInterface
- DXGIGetDebugInterface1 - baseline
IDXGIAdapter
- IDXGIAdapter3 - baseline
- IDXGIAdapter4 - 1703
IDXGIDevice
- IDXGIDevice3 - baseline
- IDXGIDevice4 - 1607
IDXGIFactory
- IDXGIFactory4 - baseline
- IDXGIFactory5 - 1607
- IDXGIFactory6 - 1803
- IDXGIFactory7 - 1809
IDXGIOutput
- IDXGIOutput4 - baseline
- IDXGIOutput5 - 1607
- IDXGIOutput6 - 1703
IDXGISwapChain
- IDXGISwapChain3 - baseline
- IDXGISwapChain4 - 1607

View file

@ -0,0 +1,576 @@
const std = @import("std");
pub const Error = error{
OutOfMemory,
FreeingZeroSizeAllocation,
InvalidAllocation,
NoCompatibleMemoryFound,
Other,
};
pub const Allocation = struct {
offset: u64,
chunk: u64,
};
pub const Allocator = union(enum) {
offset_allocator: OffsetAllocator,
dedicated_block_allocator: DedicatedBlockAllocator,
pub fn initOffsetAllocator(
allocator: std.mem.Allocator,
size: u32,
max_allocations: ?u32,
) std.mem.Allocator.Error!Allocator {
return .{
.offset_allocator = try OffsetAllocator.init(
allocator,
size,
max_allocations,
),
};
}
pub fn initDedicatedBlockAllocator(
size: u64,
) std.mem.Allocator.Error!Allocator {
return .{
.dedicated_block_allocator = try DedicatedBlockAllocator.init(
size,
),
};
}
pub fn deinit(self: *Allocator) void {
switch (self.*) {
inline else => |*allocator| allocator.deinit(),
}
}
pub fn reset(self: *Allocator) std.mem.Allocator.Error!void {
switch (self.*) {
inline else => |allocator| allocator.reset(),
}
}
pub fn allocate(
self: *Allocator,
size: u32,
) Error!Allocation {
return switch (self.*) {
inline else => |*allocator| allocator.allocate(size),
};
}
pub fn free(self: *Allocator, allocation: Allocation) Error!void {
return switch (self.*) {
inline else => |*allocator| allocator.free(allocation),
};
}
pub fn getSize(self: *const Allocator) u64 {
return switch (self.*) {
inline else => |allocator| allocator.getSize(),
};
}
pub fn getAllocated(self: *const Allocator) u64 {
return switch (self.*) {
inline else => |allocator| allocator.getAllocated(),
};
}
pub fn availableMemory(self: *const Allocator) u64 {
return self.getSize() - self.getAllocated();
}
pub fn isEmpty(self: *const Allocator) bool {
return self.getAllocated() == 0;
}
};
pub const DedicatedBlockAllocator = struct {
size: u64,
allocated: u64,
pub fn init(
size: u64,
) std.mem.Allocator.Error!DedicatedBlockAllocator {
return .{
.size = size,
.allocated = 0,
};
}
pub fn deinit(self: *DedicatedBlockAllocator) void {
_ = self;
}
pub fn allocate(
self: *DedicatedBlockAllocator,
size: u32,
) Error!Allocation {
if (self.allocated != 0) {
return Error.OutOfMemory;
}
if (self.size != size) {
return Error.OutOfMemory;
}
self.allocated = size;
return .{
.offset = 0,
.chunk = 1,
};
}
pub fn free(self: *DedicatedBlockAllocator, allocation: Allocation) Error!void {
_ = allocation;
self.allocated = 0;
}
pub fn getSize(self: *const DedicatedBlockAllocator) u64 {
return self.size;
}
pub fn getAllocated(self: *const DedicatedBlockAllocator) u64 {
return self.allocated;
}
};
// OffsetAllocator from https://github.com/sebbbi/OffsetAllocator
// rewritten in zig
pub const OffsetAllocator = struct {
const NodeIndex = u32;
const Node = struct {
data_offset: u32 = 0,
data_size: u32 = 0,
bin_list_prev: ?NodeIndex = null,
bin_list_next: ?NodeIndex = null,
neighbour_prev: ?NodeIndex = null,
neighbour_next: ?NodeIndex = null,
used: bool = false,
};
const num_top_bins: u32 = 32;
const bins_per_leaf: u32 = 8;
const top_bins_index_shift: u32 = 3;
const lead_bins_index_mask: u32 = 0x7;
const num_leaf_bins: u32 = num_top_bins * bins_per_leaf;
allocator: std.mem.Allocator,
size: u32,
max_allocations: u32,
free_storage: u32 = 0,
used_bins_top: u32 = 0,
used_bins: [num_top_bins]u8 = undefined,
bin_indices: [num_leaf_bins]?NodeIndex = undefined,
nodes: ?[]Node,
free_nodes: ?[]NodeIndex,
free_offset: u32 = 0,
const SmallFloat = struct {
const mantissa_bits: u32 = 3;
const mantissa_value: u32 = 1 << mantissa_bits;
const mantissa_mask: u32 = mantissa_value - 1;
pub fn toFloatRoundUp(size: u32) u32 {
var exp: u32 = 0;
var mantissa: u32 = 0;
if (size < mantissa_value) {
mantissa = size;
} else {
const leading_zeros = @clz(size);
const highestSetBit = 31 - leading_zeros;
const mantissa_start_bit = highestSetBit - mantissa_bits;
exp = mantissa_start_bit + 1;
mantissa = (size >> @as(u5, @truncate(mantissa_start_bit))) & mantissa_mask;
const low_bits_mask = (@as(u32, 1) << @as(u5, @truncate(mantissa_start_bit))) - 1;
if ((size & low_bits_mask) != 0) {
mantissa += 1;
}
}
return (exp << mantissa_bits) + mantissa;
}
pub fn toFloatRoundDown(size: u32) u32 {
var exp: u32 = 0;
var mantissa: u32 = 0;
if (size < mantissa_value) {
mantissa = size;
} else {
const leading_zeros = @clz(size);
const highestSetBit = 31 - leading_zeros;
const mantissa_start_bit = highestSetBit - mantissa_bits;
exp = mantissa_start_bit + 1;
mantissa = (size >> @as(u5, @truncate(mantissa_start_bit))) & mantissa_mask;
}
return (exp << mantissa_bits) | mantissa;
}
};
fn findLowestSetBitAfter(v: u32, start_idx: u32) ?u32 {
const mask_before_start_index: u32 = (@as(u32, 1) << @as(u5, @truncate(start_idx))) - 1;
const mask_after_start_index: u32 = ~mask_before_start_index;
const bits_after: u32 = v & mask_after_start_index;
if (bits_after == 0) return null;
return @ctz(bits_after);
}
pub fn init(allocator: std.mem.Allocator, size: u32, max_allocations: ?u32) std.mem.Allocator.Error!OffsetAllocator {
var self = OffsetAllocator{
.allocator = allocator,
.size = size,
.max_allocations = max_allocations orelse 128 * 1024,
.nodes = null,
.free_nodes = null,
};
try self.reset();
return self;
}
pub fn reset(self: *OffsetAllocator) std.mem.Allocator.Error!void {
self.free_storage = 0;
self.used_bins_top = 0;
self.free_offset = self.max_allocations - 1;
for (0..num_top_bins) |i| {
self.used_bins[i] = 0;
}
for (0..num_leaf_bins) |i| {
self.bin_indices[i] = null;
}
if (self.nodes) |nodes| {
self.allocator.free(nodes);
self.nodes = null;
}
if (self.free_nodes) |free_nodes| {
self.allocator.free(free_nodes);
self.free_nodes = null;
}
self.nodes = try self.allocator.alloc(Node, self.max_allocations);
self.free_nodes = try self.allocator.alloc(NodeIndex, self.max_allocations);
for (0..self.max_allocations) |i| {
self.free_nodes.?[i] = self.max_allocations - @as(u32, @truncate(i)) - 1;
}
_ = self.insertNodeIntoBin(self.size, 0);
}
pub fn deinit(self: *OffsetAllocator) void {
if (self.nodes) |nodes| {
self.allocator.free(nodes);
self.nodes = null;
}
if (self.free_nodes) |free_nodes| {
self.allocator.free(free_nodes);
self.free_nodes = null;
}
}
pub fn allocate(
self: *OffsetAllocator,
size: u32,
) Error!Allocation {
if (self.free_offset == 0) {
return Error.OutOfMemory;
}
const min_bin_index = SmallFloat.toFloatRoundUp(@intCast(size));
const min_top_bin_index: u32 = min_bin_index >> top_bins_index_shift;
const min_leaf_bin_index: u32 = min_bin_index & lead_bins_index_mask;
var top_bin_index = min_top_bin_index;
var leaf_bin_index: ?u32 = null;
if ((self.used_bins_top & (@as(u32, 1) << @as(u5, @truncate(top_bin_index)))) != 0) {
leaf_bin_index = findLowestSetBitAfter(self.used_bins[top_bin_index], min_leaf_bin_index);
}
if (leaf_bin_index == null) {
const found_top_bin_index = findLowestSetBitAfter(self.used_bins_top, min_top_bin_index + 1);
if (found_top_bin_index == null) {
return Error.OutOfMemory;
}
top_bin_index = found_top_bin_index.?;
leaf_bin_index = @ctz(self.used_bins[top_bin_index]);
}
const bin_index = (top_bin_index << top_bins_index_shift) | leaf_bin_index.?;
const node_index = self.bin_indices[bin_index].?;
const node = &self.nodes.?[node_index];
const node_total_size = node.data_size;
node.data_size = @intCast(size);
node.used = true;
self.bin_indices[bin_index] = node.bin_list_next;
if (node.bin_list_next) |bln| self.nodes.?[bln].bin_list_prev = null;
self.free_storage -= node_total_size;
// debug
// std.debug.print("free storage: {} ({}) (allocate)\n", .{ self.free_storage, node_total_size });
if (self.bin_indices[bin_index] == null) {
self.used_bins[top_bin_index] &= @as(u8, @truncate(~(@as(u32, 1) << @as(u5, @truncate(leaf_bin_index.?)))));
if (self.used_bins[top_bin_index] == 0) {
self.used_bins_top &= ~(@as(u32, 1) << @as(u5, @truncate(top_bin_index)));
}
}
const remainder_size = node_total_size - size;
if (remainder_size > 0) {
const new_node_index = self.insertNodeIntoBin(@intCast(remainder_size), @intCast(node.data_offset + size));
if (node.neighbour_next) |nnn| self.nodes.?[nnn].neighbour_prev = new_node_index;
self.nodes.?[new_node_index].neighbour_prev = node_index;
self.nodes.?[new_node_index].neighbour_next = node.neighbour_next;
node.neighbour_next = new_node_index;
}
return .{
.offset = node.data_offset,
.chunk = node_index,
};
}
pub fn free(self: *OffsetAllocator, allocation: Allocation) Error!void {
if (self.nodes == null) {
return Error.InvalidAllocation;
}
const node_index = allocation.chunk;
const node = &self.nodes.?[node_index];
if (!node.used) {
return Error.InvalidAllocation;
}
var offset = node.data_offset;
var size = node.data_size;
if (node.neighbour_prev != null and self.nodes.?[node.neighbour_prev.?].used == false) {
const prev_node = &self.nodes.?[node.neighbour_prev.?];
offset = prev_node.data_offset;
size += prev_node.data_size;
self.removeNodeFromBin(node.neighbour_prev.?);
std.debug.assert(prev_node.neighbour_next == @as(u32, @truncate(node_index)));
node.neighbour_prev = prev_node.neighbour_prev;
}
if (node.neighbour_next != null and self.nodes.?[node.neighbour_next.?].used == false) {
const next_node = &self.nodes.?[node.neighbour_next.?];
size += next_node.data_size;
self.removeNodeFromBin(node.neighbour_next.?);
std.debug.assert(next_node.neighbour_prev == @as(u32, @truncate(node_index)));
node.neighbour_next = next_node.neighbour_next;
}
const neighbour_prev = node.neighbour_prev;
const neighbour_next = node.neighbour_next;
// debug
// std.debug.print("putting node {} into freelist[{}] (free)\n", .{ node_index, self.free_offset + 1 });
self.free_offset += 1;
self.free_nodes.?[self.free_offset] = @intCast(node_index);
const combined_node_index = self.insertNodeIntoBin(size, offset);
if (neighbour_next) |nn| {
self.nodes.?[combined_node_index].neighbour_next = neighbour_next;
self.nodes.?[nn].neighbour_prev = combined_node_index;
}
if (neighbour_prev) |np| {
self.nodes.?[combined_node_index].neighbour_prev = neighbour_prev;
self.nodes.?[np].neighbour_next = combined_node_index;
}
}
pub fn insertNodeIntoBin(self: *OffsetAllocator, size: u32, data_offset: u32) u32 {
const bin_index = SmallFloat.toFloatRoundDown(size);
const top_bin_index: u32 = bin_index >> top_bins_index_shift;
const leaf_bin_index: u32 = bin_index & lead_bins_index_mask;
if (self.bin_indices[bin_index] == null) {
self.used_bins[top_bin_index] |= @as(u8, @truncate(@as(u32, 1) << @as(u5, @truncate(leaf_bin_index))));
self.used_bins_top |= @as(u32, 1) << @as(u5, @truncate(top_bin_index));
}
const top_node_index = self.bin_indices[bin_index];
const node_index = self.free_nodes.?[self.free_offset];
self.free_offset -= 1;
// debug
// std.debug.print("getting node {} from freelist[{}]\n", .{ node_index, self.free_offset + 1 });
self.nodes.?[node_index] = .{
.data_offset = data_offset,
.data_size = size,
.bin_list_next = top_node_index,
};
if (top_node_index) |tni| self.nodes.?[tni].bin_list_prev = node_index;
self.bin_indices[bin_index] = node_index;
self.free_storage += size;
// debug
// std.debug.print("free storage: {} ({}) (insertNodeIntoBin)\n", .{ self.free_storage, size });
return node_index;
}
pub fn removeNodeFromBin(self: *OffsetAllocator, node_index: NodeIndex) void {
const node = &self.nodes.?[node_index];
if (node.bin_list_prev) |blp| {
self.nodes.?[blp].bin_list_next = node.bin_list_next;
if (node.bin_list_next) |bln| self.nodes.?[bln].bin_list_prev = node.bin_list_prev;
} else {
const bin_index = SmallFloat.toFloatRoundDown(node.data_size);
const top_bin_index: u32 = bin_index >> top_bins_index_shift;
const leaf_bin_index: u32 = bin_index & lead_bins_index_mask;
self.bin_indices[bin_index] = node.bin_list_next;
if (node.bin_list_next) |bln| self.nodes.?[bln].bin_list_prev = null;
if (self.bin_indices[bin_index] == null) {
self.used_bins[top_bin_index] &= @as(u8, @truncate(~(@as(u32, 1) << @as(u5, @truncate(leaf_bin_index)))));
if (self.used_bins[top_bin_index] == 0) {
self.used_bins_top &= ~(@as(u32, 1) << @as(u5, @truncate(top_bin_index)));
}
}
}
// debug
// std.debug.print("putting node {} into freelist[{}] (removeNodeFromBin)\n", .{ node_index, self.free_offset + 1 });
self.free_offset += 1;
self.free_nodes.?[self.free_offset] = node_index;
self.free_storage -= node.data_size;
// debug
// std.debug.print("free storage: {} ({}) (removeNodeFromBin)\n", .{ self.free_storage, node.data_size });
}
pub fn getSize(self: *const OffsetAllocator) u64 {
return self.size;
}
pub fn getAllocated(self: *const OffsetAllocator) u64 {
return self.size - self.free_storage;
}
};
test "basic" {
var allocator = try Allocator.initOffsetAllocator(
std.testing.allocator,
1024 * 1024 * 256,
null,
);
defer allocator.deinit();
const a = try allocator.allocate(1337);
const offset = a.offset;
try std.testing.expectEqual(@as(u64, 0), offset);
try allocator.free(a);
}
test "allocate" {
var allocator = try Allocator.initOffsetAllocator(
std.testing.allocator,
1024 * 1024 * 256,
null,
);
defer allocator.deinit();
{
const a = try allocator.allocate(0);
try std.testing.expectEqual(@as(u64, 0), a.offset);
const b = try allocator.allocate(1);
try std.testing.expectEqual(@as(u64, 0), b.offset);
const c = try allocator.allocate(123);
try std.testing.expectEqual(@as(u64, 1), c.offset);
const d = try allocator.allocate(1234);
try std.testing.expectEqual(@as(u64, 124), d.offset);
try allocator.free(a);
try allocator.free(b);
try allocator.free(c);
try allocator.free(d);
const validate = try allocator.allocate(1024 * 1024 * 256);
try std.testing.expectEqual(@as(u64, 0), validate.offset);
try allocator.free(validate);
}
{
const a = try allocator.allocate(1024);
try std.testing.expectEqual(@as(u64, 0), a.offset);
const b = try allocator.allocate(3456);
try std.testing.expectEqual(@as(u64, 1024), b.offset);
try allocator.free(a);
const c = try allocator.allocate(1024);
try std.testing.expectEqual(@as(u64, 0), c.offset);
try allocator.free(b);
try allocator.free(c);
const validate = try allocator.allocate(1024 * 1024 * 256);
try std.testing.expectEqual(@as(u64, 0), validate.offset);
try allocator.free(validate);
}
{
const a = try allocator.allocate(1024);
try std.testing.expectEqual(@as(u64, 0), a.offset);
const b = try allocator.allocate(3456);
try std.testing.expectEqual(@as(u64, 1024), b.offset);
try allocator.free(a);
const c = try allocator.allocate(2345);
try std.testing.expectEqual(@as(u64, 1024 + 3456), c.offset);
const d = try allocator.allocate(456);
try std.testing.expectEqual(@as(u64, 0), d.offset);
const e = try allocator.allocate(512);
try std.testing.expectEqual(@as(u64, 456), e.offset);
try allocator.free(b);
try allocator.free(c);
try allocator.free(d);
try allocator.free(e);
const validate = try allocator.allocate(1024 * 1024 * 256);
try std.testing.expectEqual(@as(u64, 0), validate.offset);
try allocator.free(validate);
}
}

34
src/sysgpu/limits.zig Normal file
View file

@ -0,0 +1,34 @@
pub const max_texture_dimension1d: u32 = 8192;
pub const max_texture_dimension2d: u32 = 8192;
pub const max_texture_dimension3d: u32 = 2048;
pub const max_texture_array_layers: u32 = 256;
pub const max_bind_groups: u32 = 4;
pub const max_bind_groups_plus_vertex_buffers: u32 = 24;
pub const max_bindings_per_bind_group: u32 = 1000;
pub const max_dynamic_uniform_buffers_per_pipeline_layout: u32 = 8;
pub const max_dynamic_storage_buffers_per_pipeline_layout: u32 = 4;
pub const max_sampled_textures_per_shader_stage: u32 = 16;
pub const max_samplers_per_shader_stage: u32 = 16;
pub const max_storage_buffers_per_shader_stage: u32 = 8;
pub const max_storage_textures_per_shader_stage: u32 = 4;
pub const max_uniform_buffers_per_shader_stage: u32 = 12;
pub const max_uniform_buffer_binding_size: u64 = 65536;
pub const max_storage_buffer_binding_size: u64 = 134217728;
pub const min_uniform_buffer_offset_alignment: u32 = 256;
pub const min_storage_buffer_offset_alignment: u32 = 256;
pub const max_vertex_buffers: u32 = 8;
pub const max_buffer_size: u64 = 268435456;
pub const max_vertex_attributes: u32 = 16;
pub const max_vertex_buffer_array_stride: u32 = 2048;
pub const max_inter_stage_shader_components: u32 = 60;
pub const max_inter_stage_shader_variables: u32 = 16;
pub const max_color_attachments: u32 = 8;
pub const max_color_attachment_bytes_per_sample: u32 = 32;
pub const max_compute_workgroup_storage_size: u32 = 16384;
pub const max_compute_invocations_per_workgroup: u32 = 256;
pub const max_compute_workgroup_size_x: u32 = 256;
pub const max_compute_workgroup_size_y: u32 = 256;
pub const max_compute_workgroup_size_z: u32 = 64;
pub const max_compute_workgroups_per_dimension: u32 = 65535;
pub const max_buffers_per_shader_stage = max_storage_buffers_per_shader_stage + max_uniform_buffers_per_shader_stage;

1373
src/sysgpu/main.zig Normal file

File diff suppressed because it is too large Load diff

2221
src/sysgpu/metal.zig Normal file

File diff suppressed because it is too large Load diff

382
src/sysgpu/metal/conv.zig Normal file
View file

@ -0,0 +1,382 @@
const mtl = @import("objc").metal.mtl;
const sysgpu = @import("../sysgpu/main.zig");
pub fn metalBlendFactor(factor: sysgpu.BlendFactor, color: bool) mtl.BlendFactor {
return switch (factor) {
.zero => mtl.BlendFactorZero,
.one => mtl.BlendFactorOne,
.src => mtl.BlendFactorSourceColor,
.one_minus_src => mtl.BlendFactorOneMinusSourceColor,
.src_alpha => mtl.BlendFactorSourceAlpha,
.one_minus_src_alpha => mtl.BlendFactorOneMinusSourceAlpha,
.dst => mtl.BlendFactorDestinationColor,
.one_minus_dst => mtl.BlendFactorOneMinusDestinationColor,
.dst_alpha => mtl.BlendFactorDestinationAlpha,
.one_minus_dst_alpha => mtl.BlendFactorOneMinusDestinationAlpha,
.src_alpha_saturated => mtl.BlendFactorSourceAlphaSaturated,
.constant => if (color) mtl.BlendFactorBlendColor else mtl.BlendFactorBlendAlpha,
.one_minus_constant => if (color) mtl.BlendFactorOneMinusBlendColor else mtl.BlendFactorOneMinusBlendAlpha,
.src1 => mtl.BlendFactorSource1Color,
.one_minus_src1 => mtl.BlendFactorOneMinusSource1Color,
.src1_alpha => mtl.BlendFactorSource1Alpha,
.one_minus_src1_alpha => mtl.BlendFactorOneMinusSource1Alpha,
};
}
pub fn metalBlendOperation(op: sysgpu.BlendOperation) mtl.BlendOperation {
return switch (op) {
.add => mtl.BlendOperationAdd,
.subtract => mtl.BlendOperationSubtract,
.reverse_subtract => mtl.BlendOperationReverseSubtract,
.min => mtl.BlendOperationMin,
.max => mtl.BlendOperationMax,
};
}
pub fn metalColorWriteMask(mask: sysgpu.ColorWriteMaskFlags) mtl.ColorWriteMask {
var writeMask = mtl.ColorWriteMaskNone;
if (mask.red)
writeMask |= mtl.ColorWriteMaskRed;
if (mask.green)
writeMask |= mtl.ColorWriteMaskGreen;
if (mask.blue)
writeMask |= mtl.ColorWriteMaskBlue;
if (mask.alpha)
writeMask |= mtl.ColorWriteMaskAlpha;
return writeMask;
}
pub fn metalCommonCounter(name: sysgpu.PipelineStatisticName) mtl.CommonCounter {
return switch (name) {
.vertex_shader_invocations => mtl.CommonCounterVertexInvocations,
.cliiper_invocations => mtl.CommonCounterClipperInvocations,
.clipper_primitives_out => mtl.CommonCounterClipperPrimitivesOut,
.fragment_shader_invocations => mtl.CommonCounterFragmentInvocations,
.compute_shader_invocations => mtl.CommonCounterComputeKernelInvocations,
};
}
pub fn metalCompareFunction(func: sysgpu.CompareFunction) mtl.CompareFunction {
return switch (func) {
.undefined => unreachable,
.never => mtl.CompareFunctionNever,
.less => mtl.CompareFunctionLess,
.less_equal => mtl.CompareFunctionLessEqual,
.greater => mtl.CompareFunctionGreater,
.greater_equal => mtl.CompareFunctionGreaterEqual,
.equal => mtl.CompareFunctionEqual,
.not_equal => mtl.CompareFunctionNotEqual,
.always => mtl.CompareFunctionAlways,
};
}
pub fn metalCullMode(mode: sysgpu.CullMode) mtl.CullMode {
return switch (mode) {
.none => mtl.CullModeNone,
.front => mtl.CullModeFront,
.back => mtl.CullModeBack,
};
}
pub fn metalIndexType(format: sysgpu.IndexFormat) mtl.IndexType {
return switch (format) {
.undefined => unreachable,
.uint16 => mtl.IndexTypeUInt16,
.uint32 => mtl.IndexTypeUInt32,
};
}
pub fn metalIndexElementSize(format: sysgpu.IndexFormat) usize {
return switch (format) {
.undefined => unreachable,
.uint16 => 2,
.uint32 => 4,
};
}
pub fn metalLoadAction(op: sysgpu.LoadOp) mtl.LoadAction {
return switch (op) {
.undefined => unreachable,
.load => mtl.LoadActionLoad,
.clear => mtl.LoadActionClear,
};
}
pub fn metalPixelFormat(format: sysgpu.Texture.Format) mtl.PixelFormat {
return switch (format) {
.undefined => mtl.PixelFormatInvalid,
.r8_unorm => mtl.PixelFormatR8Unorm,
.r8_snorm => mtl.PixelFormatR8Snorm,
.r8_uint => mtl.PixelFormatR8Uint,
.r8_sint => mtl.PixelFormatR8Sint,
.r16_uint => mtl.PixelFormatR16Uint,
.r16_sint => mtl.PixelFormatR16Sint,
.r16_float => mtl.PixelFormatR16Float,
.rg8_unorm => mtl.PixelFormatRG8Unorm,
.rg8_snorm => mtl.PixelFormatRG8Snorm,
.rg8_uint => mtl.PixelFormatRG8Uint,
.rg8_sint => mtl.PixelFormatRG8Sint,
.r32_float => mtl.PixelFormatR32Float,
.r32_uint => mtl.PixelFormatR32Uint,
.r32_sint => mtl.PixelFormatR32Sint,
.rg16_uint => mtl.PixelFormatRG16Uint,
.rg16_sint => mtl.PixelFormatRG16Sint,
.rg16_float => mtl.PixelFormatRG16Float,
.rgba8_unorm => mtl.PixelFormatRGBA8Unorm,
.rgba8_unorm_srgb => mtl.PixelFormatRGBA8Unorm_sRGB,
.rgba8_snorm => mtl.PixelFormatRGBA8Snorm,
.rgba8_uint => mtl.PixelFormatRGBA8Uint,
.rgba8_sint => mtl.PixelFormatRGBA8Sint,
.bgra8_unorm => mtl.PixelFormatBGRA8Unorm,
.bgra8_unorm_srgb => mtl.PixelFormatBGRA8Unorm_sRGB,
.rgb10_a2_unorm => mtl.PixelFormatRGB10A2Unorm,
.rg11_b10_ufloat => mtl.PixelFormatRG11B10Float,
.rgb9_e5_ufloat => mtl.PixelFormatRGB9E5Float,
.rg32_float => mtl.PixelFormatRG32Float,
.rg32_uint => mtl.PixelFormatRG32Uint,
.rg32_sint => mtl.PixelFormatRG32Sint,
.rgba16_uint => mtl.PixelFormatRGBA16Uint,
.rgba16_sint => mtl.PixelFormatRGBA16Sint,
.rgba16_float => mtl.PixelFormatRGBA16Float,
.rgba32_float => mtl.PixelFormatRGBA32Float,
.rgba32_uint => mtl.PixelFormatRGBA32Uint,
.rgba32_sint => mtl.PixelFormatRGBA32Sint,
.stencil8 => mtl.PixelFormatStencil8,
.depth16_unorm => mtl.PixelFormatDepth16Unorm,
.depth24_plus => mtl.PixelFormatDepth32Float, // mtl.PixelFormatDepth24Unorm_Stencil8 only for non-Apple Silicon
.depth24_plus_stencil8 => mtl.PixelFormatDepth32Float_Stencil8, // mtl.PixelFormatDepth24Unorm_Stencil8 only for non-Apple Silicon
.depth32_float => mtl.PixelFormatDepth32Float,
.depth32_float_stencil8 => mtl.PixelFormatDepth32Float_Stencil8,
.bc1_rgba_unorm => mtl.PixelFormatBC1_RGBA,
.bc1_rgba_unorm_srgb => mtl.PixelFormatBC1_RGBA_sRGB,
.bc2_rgba_unorm => mtl.PixelFormatBC2_RGBA,
.bc2_rgba_unorm_srgb => mtl.PixelFormatBC2_RGBA_sRGB,
.bc3_rgba_unorm => mtl.PixelFormatBC3_RGBA,
.bc3_rgba_unorm_srgb => mtl.PixelFormatBC3_RGBA_sRGB,
.bc4_runorm => mtl.PixelFormatBC4_RUnorm,
.bc4_rsnorm => mtl.PixelFormatBC4_RSnorm,
.bc5_rg_unorm => mtl.PixelFormatBC5_RGUnorm,
.bc5_rg_snorm => mtl.PixelFormatBC5_RGSnorm,
.bc6_hrgb_ufloat => mtl.PixelFormatBC6H_RGBUfloat,
.bc6_hrgb_float => mtl.PixelFormatBC6H_RGBFloat,
.bc7_rgba_unorm => mtl.PixelFormatBC7_RGBAUnorm,
.bc7_rgba_unorm_srgb => mtl.PixelFormatBC7_RGBAUnorm_sRGB,
.etc2_rgb8_unorm => mtl.PixelFormatETC2_RGB8,
.etc2_rgb8_unorm_srgb => mtl.PixelFormatETC2_RGB8_sRGB,
.etc2_rgb8_a1_unorm => mtl.PixelFormatETC2_RGB8A1,
.etc2_rgb8_a1_unorm_srgb => mtl.PixelFormatETC2_RGB8A1_sRGB,
.etc2_rgba8_unorm => mtl.PixelFormatEAC_RGBA8,
.etc2_rgba8_unorm_srgb => mtl.PixelFormatEAC_RGBA8_sRGB,
.eacr11_unorm => mtl.PixelFormatEAC_R11Unorm,
.eacr11_snorm => mtl.PixelFormatEAC_R11Snorm,
.eacrg11_unorm => mtl.PixelFormatEAC_RG11Unorm,
.eacrg11_snorm => mtl.PixelFormatEAC_RG11Snorm,
.astc4x4_unorm => mtl.PixelFormatASTC_4x4_LDR,
.astc4x4_unorm_srgb => mtl.PixelFormatASTC_4x4_sRGB,
.astc5x4_unorm => mtl.PixelFormatASTC_5x4_LDR,
.astc5x4_unorm_srgb => mtl.PixelFormatASTC_5x4_sRGB,
.astc5x5_unorm => mtl.PixelFormatASTC_5x5_LDR,
.astc5x5_unorm_srgb => mtl.PixelFormatASTC_5x5_sRGB,
.astc6x5_unorm => mtl.PixelFormatASTC_6x5_LDR,
.astc6x5_unorm_srgb => mtl.PixelFormatASTC_6x5_sRGB,
.astc6x6_unorm => mtl.PixelFormatASTC_6x6_LDR,
.astc6x6_unorm_srgb => mtl.PixelFormatASTC_6x6_sRGB,
.astc8x5_unorm => mtl.PixelFormatASTC_8x5_LDR,
.astc8x5_unorm_srgb => mtl.PixelFormatASTC_8x5_sRGB,
.astc8x6_unorm => mtl.PixelFormatASTC_8x6_LDR,
.astc8x6_unorm_srgb => mtl.PixelFormatASTC_8x6_sRGB,
.astc8x8_unorm => mtl.PixelFormatASTC_8x8_LDR,
.astc8x8_unorm_srgb => mtl.PixelFormatASTC_8x8_sRGB,
.astc10x5_unorm => mtl.PixelFormatASTC_10x5_LDR,
.astc10x5_unorm_srgb => mtl.PixelFormatASTC_10x5_sRGB,
.astc10x6_unorm => mtl.PixelFormatASTC_10x6_LDR,
.astc10x6_unorm_srgb => mtl.PixelFormatASTC_10x6_sRGB,
.astc10x8_unorm => mtl.PixelFormatASTC_10x8_LDR,
.astc10x8_unorm_srgb => mtl.PixelFormatASTC_10x8_sRGB,
.astc10x10_unorm => mtl.PixelFormatASTC_10x10_LDR,
.astc10x10_unorm_srgb => mtl.PixelFormatASTC_10x10_sRGB,
.astc12x10_unorm => mtl.PixelFormatASTC_12x10_LDR,
.astc12x10_unorm_srgb => mtl.PixelFormatASTC_12x10_sRGB,
.astc12x12_unorm => mtl.PixelFormatASTC_12x12_LDR,
.astc12x12_unorm_srgb => mtl.PixelFormatASTC_12x12_sRGB,
.r8_bg8_biplanar420_unorm => unreachable,
};
}
pub fn metalPixelFormatForView(viewFormat: sysgpu.Texture.Format, textureFormat: mtl.PixelFormat, aspect: sysgpu.Texture.Aspect) mtl.PixelFormat {
// TODO - depth/stencil only views
_ = aspect;
_ = textureFormat;
return metalPixelFormat(viewFormat);
}
pub fn metalPrimitiveTopologyClass(topology: sysgpu.PrimitiveTopology) mtl.PrimitiveTopologyClass {
return switch (topology) {
.point_list => mtl.PrimitiveTopologyClassPoint,
.line_list => mtl.PrimitiveTopologyClassLine,
.line_strip => mtl.PrimitiveTopologyClassLine,
.triangle_list => mtl.PrimitiveTopologyClassTriangle,
.triangle_strip => mtl.PrimitiveTopologyClassTriangle,
};
}
pub fn metalPrimitiveType(topology: sysgpu.PrimitiveTopology) mtl.PrimitiveType {
return switch (topology) {
.point_list => mtl.PrimitiveTypePoint,
.line_list => mtl.PrimitiveTypeLine,
.line_strip => mtl.PrimitiveTypeLineStrip,
.triangle_list => mtl.PrimitiveTypeTriangle,
.triangle_strip => mtl.PrimitiveTypeTriangleStrip,
};
}
pub fn metalResourceOptionsForBuffer(usage: sysgpu.Buffer.UsageFlags) mtl.ResourceOptions {
const cpu_cache_mode = if (usage.map_write and !usage.map_read) mtl.ResourceCPUCacheModeWriteCombined else mtl.ResourceCPUCacheModeDefaultCache;
const storage_mode = mtl.ResourceStorageModeShared; // optimizing for UMA only
const hazard_tracking_mode = mtl.ResourceHazardTrackingModeDefault;
return cpu_cache_mode | storage_mode | hazard_tracking_mode;
}
pub fn metalSamplerAddressMode(mode: sysgpu.Sampler.AddressMode) mtl.SamplerAddressMode {
return switch (mode) {
.repeat => mtl.SamplerAddressModeRepeat,
.mirror_repeat => mtl.SamplerAddressModeMirrorRepeat,
.clamp_to_edge => mtl.SamplerAddressModeClampToEdge,
};
}
pub fn metalSamplerMinMagFilter(mode: sysgpu.FilterMode) mtl.SamplerMinMagFilter {
return switch (mode) {
.nearest => mtl.SamplerMinMagFilterNearest,
.linear => mtl.SamplerMinMagFilterLinear,
};
}
pub fn metalSamplerMipFilter(mode: sysgpu.MipmapFilterMode) mtl.SamplerMipFilter {
return switch (mode) {
.nearest => mtl.SamplerMipFilterNearest,
.linear => mtl.SamplerMipFilterLinear,
};
}
pub fn metalStencilOperation(op: sysgpu.StencilOperation) mtl.StencilOperation {
return switch (op) {
.keep => mtl.StencilOperationKeep,
.zero => mtl.StencilOperationZero,
.replace => mtl.StencilOperationReplace,
.invert => mtl.StencilOperationInvert,
.increment_clamp => mtl.StencilOperationIncrementClamp,
.decrement_clamp => mtl.StencilOperationDecrementClamp,
.increment_wrap => mtl.StencilOperationIncrementWrap,
.decrement_wrap => mtl.StencilOperationDecrementWrap,
};
}
pub fn metalStorageModeForTexture(usage: sysgpu.Texture.UsageFlags) mtl.StorageMode {
if (usage.transient_attachment) {
return mtl.StorageModeMemoryless;
} else {
return mtl.StorageModePrivate;
}
}
pub fn metalStoreAction(op: sysgpu.StoreOp, has_resolve_target: bool) mtl.StoreAction {
return switch (op) {
.undefined => unreachable,
.store => if (has_resolve_target) mtl.StoreActionStoreAndMultisampleResolve else mtl.StoreActionStore,
.discard => if (has_resolve_target) mtl.StoreActionMultisampleResolve else mtl.StoreActionDontCare,
};
}
pub fn metalTextureType(dimension: sysgpu.Texture.Dimension, size: sysgpu.Extent3D, sample_count: u32) mtl.TextureType {
return switch (dimension) {
.dimension_1d => if (size.depth_or_array_layers > 1) mtl.TextureType1DArray else mtl.TextureType1D,
.dimension_2d => if (sample_count > 1)
if (size.depth_or_array_layers > 1)
mtl.TextureType2DMultisampleArray
else
mtl.TextureType2DMultisample
else if (size.depth_or_array_layers > 1)
mtl.TextureType2DArray
else
mtl.TextureType2D,
.dimension_3d => mtl.TextureType3D,
};
}
pub fn metalTextureTypeForView(dimension: sysgpu.TextureView.Dimension) mtl.TextureType {
return switch (dimension) {
.dimension_undefined => unreachable,
.dimension_1d => mtl.TextureType1D,
.dimension_2d => mtl.TextureType2D,
.dimension_2d_array => mtl.TextureType2DArray,
.dimension_cube => mtl.TextureTypeCube,
.dimension_cube_array => mtl.TextureTypeCubeArray,
.dimension_3d => mtl.TextureType3D,
};
}
pub fn metalTextureUsage(usage: sysgpu.Texture.UsageFlags, view_format_count: usize) mtl.TextureUsage {
var mtl_usage = mtl.TextureUsageUnknown;
if (usage.texture_binding)
mtl_usage |= mtl.TextureUsageShaderRead;
if (usage.storage_binding)
mtl_usage |= mtl.TextureUsageShaderWrite;
if (usage.render_attachment)
mtl_usage |= mtl.TextureUsageRenderTarget;
if (view_format_count > 0)
mtl_usage |= mtl.TextureUsagePixelFormatView;
return mtl_usage;
}
pub fn metalVertexFormat(format: sysgpu.VertexFormat) mtl.VertexFormat {
return switch (format) {
.undefined => mtl.VertexFormatInvalid,
.uint8x2 => mtl.VertexFormatUChar2,
.uint8x4 => mtl.VertexFormatUChar4,
.sint8x2 => mtl.VertexFormatChar2,
.sint8x4 => mtl.VertexFormatChar4,
.unorm8x2 => mtl.VertexFormatUChar2Normalized,
.unorm8x4 => mtl.VertexFormatUChar4Normalized,
.snorm8x2 => mtl.VertexFormatChar2Normalized,
.snorm8x4 => mtl.VertexFormatChar4Normalized,
.uint16x2 => mtl.VertexFormatUShort2,
.uint16x4 => mtl.VertexFormatUShort4,
.sint16x2 => mtl.VertexFormatShort2,
.sint16x4 => mtl.VertexFormatShort4,
.unorm16x2 => mtl.VertexFormatUShort2Normalized,
.unorm16x4 => mtl.VertexFormatUShort4Normalized,
.snorm16x2 => mtl.VertexFormatShort2Normalized,
.snorm16x4 => mtl.VertexFormatShort4Normalized,
.float16x2 => mtl.VertexFormatHalf2,
.float16x4 => mtl.VertexFormatHalf4,
.float32 => mtl.VertexFormatFloat,
.float32x2 => mtl.VertexFormatFloat2,
.float32x3 => mtl.VertexFormatFloat3,
.float32x4 => mtl.VertexFormatFloat4,
.uint32 => mtl.VertexFormatUInt,
.uint32x2 => mtl.VertexFormatUInt2,
.uint32x3 => mtl.VertexFormatUInt3,
.uint32x4 => mtl.VertexFormatUInt4,
.sint32 => mtl.VertexFormatInt,
.sint32x2 => mtl.VertexFormatInt2,
.sint32x3 => mtl.VertexFormatInt3,
.sint32x4 => mtl.VertexFormatInt4,
};
}
pub fn metalVertexStepFunction(mode: sysgpu.VertexStepMode) mtl.VertexStepFunction {
return switch (mode) {
.vertex => mtl.VertexStepFunctionPerVertex,
.instance => mtl.VertexStepFunctionPerInstance,
.vertex_buffer_not_used => undefined,
};
}
pub fn metalWinding(face: sysgpu.FrontFace) mtl.Winding {
return switch (face) {
.ccw => mtl.WindingCounterClockwise,
.cw => mtl.WindingClockwise,
};
}

2626
src/sysgpu/opengl.zig Normal file

File diff suppressed because it is too large Load diff

6
src/sysgpu/opengl/c.zig Normal file
View file

@ -0,0 +1,6 @@
pub usingnamespace @cImport({
@cInclude("windows.h");
@cInclude("GL/glcorearb.h");
@cInclude("GL/glext.h");
@cInclude("GL/wglext.h");
});

263
src/sysgpu/opengl/conv.zig Normal file
View file

@ -0,0 +1,263 @@
const sysgpu = @import("../sysgpu/main.zig");
const utils = @import("../utils.zig");
const c = @import("c.zig");
fn stencilEnable(stencil: sysgpu.StencilFaceState) bool {
return stencil.compare != .always or stencil.fail_op != .keep or stencil.depth_fail_op != .keep or stencil.pass_op != .keep;
}
pub fn glAttributeCount(format: sysgpu.VertexFormat) c.GLint {
return switch (format) {
.undefined => unreachable,
.uint8x2 => 2,
.uint8x4 => 4,
.sint8x2 => 2,
.sint8x4 => 4,
.unorm8x2 => 2,
.unorm8x4 => 4,
.snorm8x2 => 2,
.snorm8x4 => 4,
.uint16x2 => 2,
.uint16x4 => 4,
.sint16x2 => 2,
.sint16x4 => 4,
.unorm16x2 => 2,
.unorm16x4 => 4,
.snorm16x2 => 2,
.snorm16x4 => 4,
.float16x2 => 2,
.float16x4 => 4,
.float32 => 1,
.float32x2 => 2,
.float32x3 => 3,
.float32x4 => 4,
.uint32 => 1,
.uint32x2 => 2,
.uint32x3 => 3,
.uint32x4 => 4,
.sint32 => 1,
.sint32x2 => 2,
.sint32x3 => 3,
.sint32x4 => 4,
};
}
pub fn glAttributeIsNormalized(format_type: utils.FormatType) c.GLboolean {
return switch (format_type) {
.unorm, .unorm_srgb, .snorm => c.GL_TRUE,
else => c.GL_FALSE,
};
}
pub fn glAttributeIsInt(format_type: utils.FormatType) bool {
return switch (format_type) {
.uint, .sint => true,
else => false,
};
}
pub fn glAttributeType(format: sysgpu.VertexFormat) c.GLenum {
return switch (format) {
.undefined => unreachable,
.uint8x2 => c.GL_UNSIGNED_BYTE,
.uint8x4 => c.GL_UNSIGNED_BYTE,
.sint8x2 => c.GL_BYTE,
.sint8x4 => c.GL_BYTE,
.unorm8x2 => c.GL_UNSIGNED_BYTE,
.unorm8x4 => c.GL_UNSIGNED_BYTE,
.snorm8x2 => c.GL_BYTE,
.snorm8x4 => c.GL_BYTE,
.uint16x2 => c.GL_UNSIGNED_SHORT,
.uint16x4 => c.GL_UNSIGNED_SHORT,
.sint16x2 => c.GL_SHORT,
.sint16x4 => c.GL_SHORT,
.unorm16x2 => c.GL_UNSIGNED_SHORT,
.unorm16x4 => c.GL_UNSIGNED_SHORT,
.snorm16x2 => c.GL_SHORT,
.snorm16x4 => c.GL_SHORT,
.float16x2 => c.GL_HALF_FLOAT,
.float16x4 => c.GL_HALF_FLOAT,
.float32 => c.GL_FLOAT,
.float32x2 => c.GL_FLOAT,
.float32x3 => c.GL_FLOAT,
.float32x4 => c.GL_FLOAT,
.uint32 => c.GL_UNSIGNED_INT,
.uint32x2 => c.GL_UNSIGNED_INT,
.uint32x3 => c.GL_UNSIGNED_INT,
.uint32x4 => c.GL_UNSIGNED_INT,
.sint32 => c.GL_INT,
.sint32x2 => c.GL_INT,
.sint32x3 => c.GL_INT,
.sint32x4 => c.GL_INT,
};
}
pub fn glBlendFactor(factor: sysgpu.BlendFactor, color: bool) c.GLenum {
return switch (factor) {
.zero => c.GL_ZERO,
.one => c.GL_ONE,
.src => c.GL_SRC_COLOR,
.one_minus_src => c.GL_ONE_MINUS_SRC_COLOR,
.src_alpha => c.GL_SRC_ALPHA,
.one_minus_src_alpha => c.GL_ONE_MINUS_SRC_ALPHA,
.dst => c.GL_DST_COLOR,
.one_minus_dst => c.GL_ONE_MINUS_DST_COLOR,
.dst_alpha => c.GL_DST_ALPHA,
.one_minus_dst_alpha => c.GL_ONE_MINUS_DST_ALPHA,
.src_alpha_saturated => c.GL_SRC_ALPHA_SATURATE,
.constant => if (color) c.GL_CONSTANT_COLOR else c.GL_CONSTANT_ALPHA,
.one_minus_constant => if (color) c.GL_ONE_MINUS_CONSTANT_COLOR else c.GL_ONE_MINUS_CONSTANT_ALPHA,
.src1 => c.GL_SRC1_COLOR,
.one_minus_src1 => c.GL_ONE_MINUS_SRC1_COLOR,
.src1_alpha => c.GL_SRC1_ALPHA,
.one_minus_src1_alpha => c.GL_ONE_MINUS_SRC1_ALPHA,
};
}
pub fn glBlendOp(op: sysgpu.BlendOperation) c.GLenum {
return switch (op) {
.add => c.GL_FUNC_ADD,
.subtract => c.GL_FUNC_SUBTRACT,
.reverse_subtract => c.GL_FUNC_REVERSE_SUBTRACT,
.min => c.GL_MIN,
.max => c.GL_MAX,
};
}
//pub fn glBufferDataUsage(usage: sysgpu.Buffer.UsageFlags, mapped_at_creation: sysgpu.Bool32) c.GLenum {}
pub fn glBufferStorageFlags(usage: sysgpu.Buffer.UsageFlags, mapped_at_creation: sysgpu.Bool32) c.GLbitfield {
var flags: c.GLbitfield = 0;
if (mapped_at_creation == .true)
flags |= c.GL_MAP_WRITE_BIT;
if (usage.map_read)
flags |= c.GL_MAP_PERSISTENT_BIT | c.GL_MAP_READ_BIT;
if (usage.map_write)
flags |= c.GL_MAP_PERSISTENT_BIT | c.GL_MAP_COHERENT_BIT | c.GL_MAP_WRITE_BIT;
return flags;
}
pub fn glCompareFunc(func: sysgpu.CompareFunction) c.GLenum {
return switch (func) {
.undefined => unreachable,
.never => c.GL_NEVER,
.less => c.GL_LESS,
.less_equal => c.GL_LEQUAL,
.greater => c.GL_GREATER,
.greater_equal => c.GL_GEQUAL,
.equal => c.GL_EQUAL,
.not_equal => c.GL_NOTEQUAL,
.always => c.GL_ALWAYS,
};
}
pub fn glCullEnabled(cull_mode: sysgpu.CullMode) bool {
return switch (cull_mode) {
.none => false,
else => true,
};
}
pub fn glCullFace(cull_mode: sysgpu.CullMode) c.GLenum {
return switch (cull_mode) {
.none => c.GL_BACK,
.front => c.GL_FRONT,
.back => c.GL_BACK,
};
}
pub fn glDepthMask(ds: *const sysgpu.DepthStencilState) c.GLboolean {
return if (ds.depth_write_enabled == .true) c.GL_TRUE else c.GL_FALSE;
}
pub fn glDepthTestEnabled(ds: *const sysgpu.DepthStencilState) bool {
return ds.depth_compare != .always or ds.depth_write_enabled == .true;
}
pub fn glFrontFace(front_face: sysgpu.FrontFace) c.GLenum {
return switch (front_face) {
.ccw => c.GL_CCW,
.cw => c.GL_CW,
};
}
pub fn glIndexType(format: sysgpu.IndexFormat) c.GLenum {
return switch (format) {
.undefined => unreachable,
.uint16 => c.GL_UNSIGNED_SHORT,
.uint32 => c.GL_UNSIGNED_INT,
};
}
pub fn glIndexElementSize(format: sysgpu.IndexFormat) usize {
return switch (format) {
.undefined => unreachable,
.uint16 => 2,
.uint32 => 4,
};
}
pub fn glMapAccess(usage: sysgpu.Buffer.UsageFlags, mapped_at_creation: sysgpu.Bool32) c.GLbitfield {
var flags: c.GLbitfield = 0;
if (mapped_at_creation == .true)
flags |= c.GL_MAP_WRITE_BIT;
if (usage.map_read)
flags |= c.GL_MAP_PERSISTENT_BIT | c.GL_MAP_READ_BIT;
if (usage.map_write)
flags |= c.GL_MAP_PERSISTENT_BIT | c.GL_MAP_WRITE_BIT;
return flags;
}
pub fn glPrimitiveMode(topology: sysgpu.PrimitiveTopology) c.GLenum {
return switch (topology) {
.point_list => c.GL_POINTS,
.line_list => c.GL_LINES,
.line_strip => c.GL_LINE_STRIP,
.triangle_list => c.GL_TRIANGLES,
.triangle_strip => c.GL_TRIANGLE_STRIP,
};
}
pub fn glStencilOp(op: sysgpu.StencilOperation) c.GLenum {
return switch (op) {
.keep => c.GL_KEEP,
.zero => c.GL_ZERO,
.replace => c.GL_REPLACE,
.invert => c.GL_INVERT,
.increment_clamp => c.GL_INCR,
.decrement_clamp => c.GL_DECR,
.increment_wrap => c.GL_INCR_WRAP,
.decrement_wrap => c.GL_DECR_WRAP,
};
}
pub fn glStencilTestEnabled(ds: *const sysgpu.DepthStencilState) bool {
return stencilEnable(ds.stencil_front) or stencilEnable(ds.stencil_back);
}
pub fn glTargetForBuffer(usage: sysgpu.Buffer.UsageFlags) c.GLenum {
// Not sure if this matters anymore - only get to pick one anyway
if (usage.index)
return c.GL_ELEMENT_ARRAY_BUFFER;
if (usage.vertex)
return c.GL_ARRAY_BUFFER;
if (usage.uniform)
return c.GL_UNIFORM_BUFFER;
if (usage.storage)
return c.GL_SHADER_STORAGE_BUFFER;
if (usage.indirect)
return c.GL_DRAW_INDIRECT_BUFFER;
if (usage.query_resolve)
return c.GL_QUERY_BUFFER;
return c.GL_ARRAY_BUFFER;
}
pub fn glTargetForBufferBinding(binding_type: sysgpu.Buffer.BindingType) c.GLenum {
return switch (binding_type) {
.undefined => unreachable,
.uniform => c.GL_UNIFORM_BUFFER,
.storage => c.GL_SHADER_STORAGE_BUFFER,
.read_only_storage => c.GL_SHADER_STORAGE_BUFFER,
};
}

1459
src/sysgpu/opengl/proc.zig Normal file

File diff suppressed because it is too large Load diff

22
src/sysgpu/shader.zig Normal file
View file

@ -0,0 +1,22 @@
const std = @import("std");
pub const CodeGen = @import("shader/CodeGen.zig");
pub const Air = @import("shader/Air.zig");
pub const Ast = @import("shader/Ast.zig");
pub const Parser = @import("shader/Parser.zig");
pub const Token = @import("shader/Token.zig");
pub const Tokenizer = @import("shader/Tokenizer.zig");
pub const ErrorList = @import("shader/ErrorList.zig");
pub const printAir = @import("shader/print_air.zig").printAir;
test "reference declarations" {
std.testing.refAllDecls(CodeGen);
std.testing.refAllDecls(Air);
std.testing.refAllDecls(Ast);
std.testing.refAllDecls(Parser);
std.testing.refAllDecls(Token);
std.testing.refAllDecls(Tokenizer);
std.testing.refAllDecls(ErrorList);
_ = printAir;
_ = @import("shader/test.zig");
}

1044
src/sysgpu/shader/Air.zig Normal file

File diff suppressed because it is too large Load diff

764
src/sysgpu/shader/Ast.zig Normal file
View file

@ -0,0 +1,764 @@
const std = @import("std");
const Parser = @import("Parser.zig");
const Token = @import("Token.zig");
const Tokenizer = @import("Tokenizer.zig");
const ErrorList = @import("ErrorList.zig");
const Extensions = @import("wgsl.zig").Extensions;
const Ast = @This();
pub const NodeList = std.MultiArrayList(Node);
pub const TokenList = std.MultiArrayList(Token);
source: []const u8,
tokens: TokenList.Slice,
nodes: NodeList.Slice,
extra: []const u32,
extensions: Extensions,
pub fn deinit(tree: *Ast, allocator: std.mem.Allocator) void {
tree.tokens.deinit(allocator);
tree.nodes.deinit(allocator);
allocator.free(tree.extra);
tree.* = undefined;
}
/// parses a TranslationUnit (WGSL Program)
pub fn parse(allocator: std.mem.Allocator, errors: *ErrorList, source: [:0]const u8) error{ OutOfMemory, Parsing }!Ast {
var p = Parser{
.allocator = allocator,
.source = source,
.tokens = blk: {
const estimated_tokens = source.len / 8;
var tokens = std.MultiArrayList(Token){};
errdefer tokens.deinit(allocator);
try tokens.ensureTotalCapacity(allocator, estimated_tokens);
var tokenizer = Tokenizer.init(source);
while (true) {
const tok = tokenizer.next();
try tokens.append(allocator, tok);
if (tok.tag == .eof) break;
}
break :blk tokens;
},
.errors = errors,
};
defer p.scratch.deinit(allocator);
errdefer {
p.tokens.deinit(allocator);
p.nodes.deinit(allocator);
p.extra.deinit(allocator);
}
const estimated_nodes = p.tokens.len / 2 + 1;
try p.nodes.ensureTotalCapacity(allocator, estimated_nodes);
try p.translationUnit();
return .{
.source = source,
.tokens = p.tokens.toOwnedSlice(),
.nodes = p.nodes.toOwnedSlice(),
.extra = try p.extra.toOwnedSlice(allocator),
.extensions = p.extensions,
};
}
pub fn spanToList(tree: Ast, span: NodeIndex) []const NodeIndex {
std.debug.assert(tree.nodeTag(span) == .span);
return @ptrCast(tree.extra[@intFromEnum(tree.nodeLHS(span))..@intFromEnum(tree.nodeRHS(span))]);
}
pub fn extraData(tree: Ast, comptime T: type, index: NodeIndex) T {
const fields = std.meta.fields(T);
var result: T = undefined;
inline for (fields, 0..) |field, i| {
@field(result, field.name) = @enumFromInt(tree.extra[@intFromEnum(index) + i]);
}
return result;
}
pub fn tokenTag(tree: Ast, i: TokenIndex) Token.Tag {
return tree.tokens.items(.tag)[@intFromEnum(i)];
}
pub fn tokenLoc(tree: Ast, i: TokenIndex) Token.Loc {
return tree.tokens.items(.loc)[@intFromEnum(i)];
}
pub fn nodeTag(tree: Ast, i: NodeIndex) Node.Tag {
return tree.nodes.items(.tag)[@intFromEnum(i)];
}
pub fn nodeToken(tree: Ast, i: NodeIndex) TokenIndex {
return tree.nodes.items(.main_token)[@intFromEnum(i)];
}
pub fn nodeLHS(tree: Ast, i: NodeIndex) NodeIndex {
return tree.nodes.items(.lhs)[@intFromEnum(i)];
}
pub fn nodeRHS(tree: Ast, i: NodeIndex) NodeIndex {
return tree.nodes.items(.rhs)[@intFromEnum(i)];
}
pub fn nodeLoc(tree: Ast, i: NodeIndex) Token.Loc {
var loc = tree.tokenLoc(tree.nodeToken(i));
switch (tree.nodeTag(i)) {
.deref, .addr_of => {
const lhs_loc = tree.tokenLoc(tree.nodeToken(tree.nodeLHS(i)));
loc.end = lhs_loc.end;
},
.field_access => {
const component_loc = tree.tokenLoc(@enumFromInt(@intFromEnum(tree.nodeToken(i)) + 1));
loc.end = component_loc.end;
},
else => {},
}
return loc;
}
pub fn declNameLoc(tree: Ast, node: NodeIndex) ?Token.Loc {
const token: TokenIndex = switch (tree.nodeTag(node)) {
.global_var => tree.extraData(Node.GlobalVar, tree.nodeLHS(node)).name,
.@"var" => tree.extraData(Node.Var, tree.nodeLHS(node)).name,
.@"struct",
.@"fn",
.@"const",
.let,
.override,
.type_alias,
=> @enumFromInt(@intFromEnum(tree.nodeToken(node)) + 1),
.struct_member, .fn_param => tree.nodeToken(node),
else => return null,
};
return tree.tokenLoc(token);
}
pub const NodeIndex = enum(u32) {
none = std.math.maxInt(u32),
globals = 0,
_,
pub fn asTokenIndex(self: NodeIndex) TokenIndex {
return @enumFromInt(@intFromEnum(self));
}
};
pub const TokenIndex = enum(u32) {
none = std.math.maxInt(u32),
_,
pub fn asNodeIndex(self: TokenIndex) NodeIndex {
return @enumFromInt(@intFromEnum(self));
}
};
pub const Node = struct {
tag: Tag,
main_token: TokenIndex,
lhs: NodeIndex = .none,
rhs: NodeIndex = .none,
pub const Tag = enum {
/// an slice NodeIndex in extra [LHS..RHS]
/// TOK : undefined
/// LHS : NodeIndex
/// RHS : NodeIndex
span,
/// TOK : k_var
/// LHS : GlobalVar
/// RHS : Expr?
global_var,
/// TOK : k_override
/// LHS : Override
/// RHS : Expr
override,
/// TOK : k_type
/// LHS : Type
/// RHS : --
type_alias,
/// TOK : k_const_assert
/// LHS : Expr
/// RHS : --
const_assert,
/// TOK : k_struct
/// LHS : span(struct_member)
/// RHS : --
@"struct",
/// TOK : ident
/// LHS : span(Attribute)
/// RHS : Type
struct_member,
/// TOK : k_fn
/// LHS : FnProto
/// RHS : block
@"fn",
/// TOK : ident
/// LHS : ?span(Attribute)
/// RHS : type
fn_param,
/// TOK : brace_left
/// LHS : span(Statement)?
/// RHS : --
block,
/// TOK : k_return
/// LHS : Expr?
/// RHS : --
@"return",
/// TOK : k_discard
/// LHS : --
/// RHS : --
discard,
/// TOK : k_loop
/// LHS : block
/// RHS : --
loop,
/// TOK : k_continuing
/// LHS : block
/// RHS : --
continuing,
/// TOK : k_break
/// LHS : Expr
/// RHS : --
break_if,
/// TOK : k_break
/// LHS : --
/// RHS : --
@"break",
/// TOK : k_continue
/// LHS : --
/// RHS : --
@"continue",
/// TOK : k_if
/// LHS : Expr
/// RHS : block
@"if",
/// RHS is else body
/// TOK : k_if
/// LHS : if
/// RHS : block
if_else,
/// TOK : k_if
/// LHS : if
/// RHS : if, if_else, if_else_if
if_else_if,
/// TOK : k_switch
/// LHS : Expr
/// RHS : span(switch_case, switch_default, switch_case_default)
@"switch",
/// TOK : k_case
/// LHS : span(Expr)
/// RHS : block
switch_case,
/// TOK : k_default
/// LHS : --
/// RHS : block
switch_default,
/// switch_case with default (`case 1, 2, default {}`)
/// TOK : k_case
/// LHS : span(Expr)
/// RHS : block
switch_case_default,
/// TOK : k_var
/// LHS : Var
/// RHS : Expr?
@"var",
/// TOK : k_const
/// LHS : Type?
/// RHS : Expr
@"const",
/// TOK : k_let
/// LHS : Type?
/// RHS : Expr
let,
/// TOK : k_while
/// LHS : Expr
/// RHS : block
@"while",
/// TOK : k_for
/// LHS : ForHeader
/// RHS : block
@"for",
/// TOK : plus_plus
/// LHS : Expr
increase,
/// TOK : minus_minus
/// LHS : Expr
decrease,
/// TOK : plus_equal, minus_equal,
/// times_equal, division_equal,
/// modulo_equal, and_equal,
/// or_equal, xor_equal,
/// shl_equal, shl_equal
/// LHS : Expr
/// RHS : Expr
compound_assign,
/// TOK : equal
/// LHS : Expr
/// RHS : --
phony_assign,
/// TOK : k_i32, k_u32, k_f32, k_f16, k_bool
/// LHS : --
/// RHS : --
number_type,
/// TOK : k_bool
/// LHS : --
/// RHS : --
bool_type,
/// TOK : k_sampler, k_sampler_comparison
/// LHS : --
/// RHS : --
sampler_type,
/// TOK : k_vec2, k_vec3, k_vec4
/// LHS : Type?
/// RHS : --
vector_type,
/// TOK : k_mat2x2, k_mat2x3, k_mat2x4,
/// k_mat3x2, k_mat3x3, k_mat3x4,
/// k_mat4x2, k_mat4x3, k_mat4x4
/// LHS : Type?
/// RHS : --
matrix_type,
/// TOK : k_atomic
/// LHS : Type
/// RHS : --
atomic_type,
/// TOK : k_array
/// LHS : Type?
/// RHS : Expr?
array_type,
/// TOK : k_ptr
/// LHS : Type
/// RHS : PtrType
ptr_type,
/// TOK : k_texture_1d, k_texture_2d, k_texture_2d_array,
/// k_texture_3d, k_texture_cube, k_texture_cube_array
/// LHS : Type
/// RHS : --
sampled_texture_type,
/// TOK : k_texture_multisampled_2d, k_texture_depth_multisampled_2d
/// LHS : Type?
/// RHS : --
multisampled_texture_type,
/// TOK : k_texture_external
/// LHS : Type
/// RHS : --
external_texture_type,
/// TOK : k_texture_storage_1d, k_texture_storage_2d,
/// k_texture_storage_2d_array, k_texture_storage_3d
/// LHS : Token(TexelFormat)
/// RHS : Token(AccessMode)
storage_texture_type,
/// TOK : k_texture_depth_2d, k_texture_depth_2d_array
/// k_texture_depth_cube, k_texture_depth_cube_array
/// LHS : --
/// RHS : --
depth_texture_type,
/// TOK : attr
attr_const,
/// TOK : attr
attr_invariant,
/// TOK : attr
attr_must_use,
/// TOK : attr
attr_vertex,
/// TOK : attr
attr_fragment,
/// TOK : attr
attr_compute,
/// TOK : attr
/// LHS : Expr
/// RHS : --
attr_align,
/// TOK : attr
/// LHS : Expr
/// RHS : --
attr_binding,
/// TOK : attr
/// LHS : Expr
/// RHS : --
attr_group,
/// TOK : attr
/// LHS : Expr
/// RHS : --
attr_id,
/// TOK : attr
/// LHS : Expr
/// RHS : --
attr_location,
/// TOK : attr
/// LHS : Expr
/// RHS : --
attr_size,
/// TOK : attr
/// LHS : Token(Builtin)
/// RHS : --
attr_builtin,
/// TOK : attr
/// LHS : WorkgroupSize
/// RHS : --
attr_workgroup_size,
/// TOK : attr
/// LHS : Token(InterpolationType)
/// RHS : Token(InterpolationSample))
attr_interpolate,
/// TOK : *
/// LHS : Expr
/// RHS : Expr
mul,
/// TOK : /
/// LHS : Expr
/// RHS : Expr
div,
/// TOK : %
/// LHS : Expr
/// RHS : Expr
mod,
/// TOK : +
/// LHS : Expr
/// RHS : Expr
add,
/// TOK : -
/// LHS : Expr
/// RHS : Expr
sub,
/// TOK : <<
/// LHS : Expr
/// RHS : Expr
shl,
/// TOK : >>
/// LHS : Expr
/// RHS : Expr
shr,
/// TOK : &
/// LHS : Expr
/// RHS : Expr
@"and",
/// TOK : |
/// LHS : Expr
/// RHS : Expr
@"or",
/// TOK : ^
/// LHS : Expr
/// RHS : Expr
xor,
/// TOK : &&
/// LHS : Expr
/// RHS : Expr
logical_and,
/// TOK : ||
/// LHS : Expr
/// RHS : Expr
logical_or,
/// TOK : !
/// LHS : Expr
/// RHS : --
not,
/// TOK : -
/// LHS : Expr
/// RHS : --
negate,
/// TOK : *
/// LHS : Expr
/// RHS : --
deref,
/// TOK : &
/// LHS : Expr
/// RHS : --
addr_of,
/// TOK : ==
/// LHS : Expr
/// RHS : Expr
equal,
/// TOK : !=
/// LHS : Expr
/// RHS : Expr
not_equal,
/// TOK : <
/// LHS : Expr
/// RHS : Expr
less_than,
/// TOK : <=
/// LHS : Expr
/// RHS : Expr
less_than_equal,
/// TOK : >
/// LHS : Expr
/// RHS : Expr
greater_than,
/// TOK : >=
/// LHS : Expr
/// RHS : Expr
greater_than_equal,
/// for identifier, array without element type specified,
/// vector prefix (e.g. vec2) and matrix prefix (e.g. mat2x2) RHS is null
/// see callExpr in Parser.zig if you don't understand this
///
/// TOK : ident, k_array, k_bool, 'number type keywords', 'vector keywords', 'matrix keywords'
/// LHS : span(Arguments Expr)
/// RHS : (number_type, bool_type, vector_type, matrix_type, array_type)?
call,
/// TOK : k_bitcast
/// LHS : Type
/// RHS : Expr
bitcast,
/// TOK : ident
/// LHS : --
/// RHS : --
ident,
/// LHS is prefix expression
/// TOK : ident
/// LHS : Expr
/// RHS : Token(NodeIndex(ident))
field_access,
/// LHS is prefix expression
/// TOK : bracket_left
/// LHS : Expr
/// RHS : Expr
index_access,
/// TOK : k_true
/// LHS : --
/// RHS : --
true,
/// TOK : k_false
/// LHS : --
/// RHS : --
false,
/// TOK : number
/// LHS : --
/// RHS : --
number,
};
pub const GlobalVar = struct {
/// span(Attr)?
attrs: NodeIndex = .none,
/// Token(ident)
name: TokenIndex,
/// Token(AddressSpace)?
addr_space: TokenIndex = .none,
/// Token(AccessMode)?
access_mode: TokenIndex = .none,
/// Type?
type: NodeIndex = .none,
};
pub const Var = struct {
/// Token(ident)
name: TokenIndex,
/// Token(AddressSpace)?
addr_space: TokenIndex = .none,
/// Token(AccessMode)?
access_mode: TokenIndex = .none,
/// Type?
type: NodeIndex = .none,
};
pub const Override = struct {
/// span(Attr)?
attrs: NodeIndex = .none,
/// Type?
type: NodeIndex = .none,
};
pub const PtrType = struct {
/// Token(AddressSpace)
addr_space: TokenIndex,
/// Token(AccessMode)
access_mode: TokenIndex,
};
pub const WorkgroupSize = struct {
/// Expr
x: NodeIndex,
/// Expr?
y: NodeIndex = .none,
/// Expr?
z: NodeIndex = .none,
};
pub const FnProto = struct {
/// span(Attr)?
attrs: NodeIndex = .none,
/// span(fn_param)?
params: NodeIndex = .none,
/// span(Attr)?
return_attrs: NodeIndex = .none,
/// Type?
return_type: NodeIndex = .none,
};
pub const ForHeader = struct {
/// var, const, let, phony_assign, compound_assign
init: NodeIndex = .none,
/// Expr
cond: NodeIndex = .none,
/// call, phony_assign, compound_assign
update: NodeIndex = .none,
};
};
pub const Builtin = enum {
vertex_index,
instance_index,
position,
front_facing,
frag_depth,
local_invocation_id,
local_invocation_index,
global_invocation_id,
workgroup_id,
num_workgroups,
sample_index,
sample_mask,
};
pub const InterpolationType = enum {
perspective,
linear,
flat,
};
pub const InterpolationSample = enum {
center,
centroid,
sample,
};
pub const AddressSpace = enum {
function,
private,
workgroup,
uniform,
storage,
};
pub const AccessMode = enum {
read,
write,
read_write,
};
pub const Attribute = enum {
invariant,
@"const",
must_use,
vertex,
fragment,
compute,
@"align",
binding,
group,
id,
location,
size,
builtin,
workgroup_size,
interpolate,
};
pub const TexelFormat = enum {
rgba8unorm,
rgba8snorm,
rgba8uint,
rgba8sint,
rgba16uint,
rgba16sint,
rgba16float,
r32uint,
r32sint,
r32float,
rg32uint,
rg32sint,
rg32float,
rgba32uint,
rgba32sint,
rgba32float,
bgra8unorm,
};

4568
src/sysgpu/shader/AstGen.zig Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,236 @@
const std = @import("std");
const Air = @import("Air.zig");
const c = @cImport({
@cInclude("spirv-cross/spirv_cross_c.h");
@cInclude("spirv-tools/libspirv.h");
});
const genGlsl = @import("codegen/glsl.zig").gen;
const genHlsl = @import("codegen/hlsl.zig").gen;
const genMsl = @import("codegen/msl.zig").gen;
const genSpirv = @import("codegen/spirv.zig").gen;
pub const Language = enum {
glsl,
hlsl,
msl,
spirv,
};
pub const DebugInfo = struct {
emit_source_file: ?[]const u8 = null,
emit_names: bool = true,
};
pub const Stage = enum {
vertex,
fragment,
compute,
};
pub const Entrypoint = struct {
stage: Stage,
name: [*:0]const u8,
};
pub const BindingPoint = struct { group: u32, binding: u32 };
pub const BindingTable = std.AutoHashMapUnmanaged(BindingPoint, u32);
pub fn generate(
allocator: std.mem.Allocator,
air: *const Air,
out_lang: Language,
use_spirv_cross: bool,
debug_info: DebugInfo,
entrypoint: ?Entrypoint,
bindings: ?*const BindingTable,
label: ?[*:0]const u8,
) ![]const u8 {
_ = use_spirv_cross;
// if (!use_spirv_cross) {
// const spirv_data = try genSpirv(allocator, air, .{});
// const spirv_data_z = try allocator.dupeZ(u8, spirv_data);
// defer allocator.free(spirv_data_z);
// allocator.free(spirv_data);
// const spirv_words_ptr = @as([*]const u32, @ptrCast(@alignCast(spirv_data_z.ptr)));
// const spirv_words = spirv_words_ptr[0 .. spirv_data_z.len / @sizeOf(u32)];
// // Optimize
// var optimized_spirv: c.spv_binary = undefined;
// const target_env = spvTargetEnv(out_lang);
// const optimizer = c.spvOptimizerCreate(target_env);
// defer c.spvOptimizerDestroy(optimizer);
// c.spvOptimizerSetMessageConsumer(optimizer, spvMessageConsumer);
// c.spvOptimizerRegisterPerformancePasses(optimizer);
// c.spvOptimizerRegisterLegalizationPasses(optimizer);
// const opt_options = c.spvOptimizerOptionsCreate();
// defer c.spvOptimizerOptionsDestroy(opt_options);
// c.spvOptimizerOptionsSetRunValidator(opt_options, false);
// var res = c.spvOptimizerRun(
// optimizer,
// spirv_words.ptr,
// spirv_words.len,
// &optimized_spirv,
// opt_options,
// );
// switch (res) {
// c.SPV_SUCCESS => {},
// else => return error.SpvOptimizerFailed,
// }
// if (out_lang == .spirv) {
// const code_bytes_ptr = @as([*]const u8, @ptrCast(optimized_spirv.*.code));
// const code_bytes = code_bytes_ptr[0 .. optimized_spirv.*.wordCount * @sizeOf(u32)];
// return allocator.dupe(u8, code_bytes);
// }
// // Translate
// var context: c.spvc_context = undefined;
// _ = c.spvc_context_create(&context);
// defer c.spvc_context_destroy(context);
// c.spvc_context_set_error_callback(context, spvcErrorCallback, null);
// var ir: c.spvc_parsed_ir = undefined;
// _ = c.spvc_context_parse_spirv(context, optimized_spirv.*.code, optimized_spirv.*.wordCount, &ir);
// var compiler: c.spvc_compiler = undefined;
// _ = c.spvc_context_create_compiler(
// context,
// spvcBackend(out_lang),
// ir,
// c.SPVC_CAPTURE_MODE_TAKE_OWNERSHIP,
// &compiler,
// );
// var resources: c.spvc_resources = undefined;
// _ = c.spvc_compiler_create_shader_resources(compiler, &resources);
// var options: c.spvc_compiler_options = undefined;
// _ = c.spvc_compiler_create_compiler_options(compiler, &options);
// switch (out_lang) {
// .glsl => {
// const resource_types = [_]c.spvc_resource_type{
// c.SPVC_RESOURCE_TYPE_UNIFORM_BUFFER,
// c.SPVC_RESOURCE_TYPE_STORAGE_BUFFER,
// c.SPVC_RESOURCE_TYPE_STORAGE_IMAGE,
// c.SPVC_RESOURCE_TYPE_SAMPLED_IMAGE,
// c.SPVC_RESOURCE_TYPE_SEPARATE_IMAGE,
// c.SPVC_RESOURCE_TYPE_SEPARATE_SAMPLERS,
// };
// for (resource_types) |resource_type| {
// glslRemapResources(compiler, resources, resource_type, bindings orelse &.{});
// }
// _ = c.spvc_compiler_options_set_uint(options, c.SPVC_COMPILER_OPTION_GLSL_VERSION, 450);
// _ = c.spvc_compiler_options_set_bool(options, c.SPVC_COMPILER_OPTION_GLSL_ES, c.SPVC_FALSE);
// if (entrypoint) |e| {
// _ = c.spvc_compiler_set_entry_point(compiler, e.name, spvExecutionModel(e.stage));
// }
// // combiner samplers/textures
// var id: c.spvc_variable_id = undefined;
// res = c.spvc_compiler_build_dummy_sampler_for_combined_images(compiler, &id);
// if (res == c.SPVC_SUCCESS) {
// c.spvc_compiler_set_decoration(compiler, id, c.SpvDecorationDescriptorSet, 0);
// c.spvc_compiler_set_decoration(compiler, id, c.SpvDecorationBinding, 0);
// }
// _ = c.spvc_compiler_build_combined_image_samplers(compiler);
// },
// else => @panic("TODO"),
// }
// _ = c.spvc_compiler_install_compiler_options(compiler, options);
// var source: [*c]const u8 = undefined;
// _ = c.spvc_compiler_compile(compiler, &source);
// return allocator.dupe(u8, std.mem.span(source));
// }
// Direct translation
return switch (out_lang) {
.spirv => try genSpirv(allocator, air, debug_info),
.hlsl => try genHlsl(allocator, air, debug_info),
.msl => try genMsl(allocator, air, debug_info, entrypoint, bindings, label orelse "<ShaderModule label not specified>"),
.glsl => try genGlsl(allocator, air, debug_info, entrypoint, bindings),
};
}
fn spvMessageConsumer(
level: c.spv_message_level_t,
src: [*c]const u8,
pos: [*c]const c.spv_position_t,
msg: [*c]const u8,
) callconv(.C) void {
switch (level) {
c.SPV_MSG_FATAL,
c.SPV_MSG_INTERNAL_ERROR,
c.SPV_MSG_ERROR,
=> {
// TODO - don't panic
std.debug.panic("{s} at :{d}:{d}\n{s}", .{
std.mem.span(msg),
pos.*.line,
pos.*.column,
std.mem.span(src),
});
},
else => {},
}
}
fn spvTargetEnv(language: Language) c.spv_target_env {
return switch (language) {
.glsl => c.SPV_ENV_OPENGL_4_5,
.spirv => c.SPV_ENV_VULKAN_1_0,
else => unreachable,
};
}
fn spvExecutionModel(stage: Stage) c.SpvExecutionModel {
return switch (stage) {
.vertex => c.SpvExecutionModelVertex,
.fragment => c.SpvExecutionModelFragment,
.compute => c.SpvExecutionModelGLCompute,
};
}
fn spvcErrorCallback(userdata: ?*anyopaque, err: [*c]const u8) callconv(.C) void {
_ = userdata;
// TODO - don't panic
@panic(std.mem.span(err));
}
fn spvcBackend(language: Language) c_uint {
return switch (language) {
.glsl => c.SPVC_BACKEND_GLSL,
.hlsl => c.SPVC_BACKEND_HLSL,
.msl => c.SPVC_BACKEND_MSL,
.spirv => unreachable,
};
}
fn glslRemapResources(
compiler: c.spvc_compiler,
resources: c.spvc_resources,
resource_type: c.spvc_resource_type,
bindings: *const BindingTable,
) void {
var resource_list: [*c]c.spvc_reflected_resource = undefined;
var resource_size: usize = undefined;
_ = c.spvc_resources_get_resource_list_for_type(resources, resource_type, &resource_list, &resource_size);
for (resource_list[0..resource_size]) |resource| {
const key = BindingPoint{
.group = c.spvc_compiler_get_decoration(compiler, resource.id, c.SpvDecorationDescriptorSet),
.binding = c.spvc_compiler_get_decoration(compiler, resource.id, c.SpvDecorationBinding),
};
if (bindings.get(key)) |slot| {
_ = c.spvc_compiler_unset_decoration(compiler, resource.id, c.SpvDecorationDescriptorSet);
_ = c.spvc_compiler_set_decoration(compiler, resource.id, c.SpvDecorationBinding, slot);
}
}
}

View file

@ -0,0 +1,128 @@
const std = @import("std");
const Token = @import("Token.zig");
pub const ErrorList = @This();
pub const ErrorMsg = struct {
loc: Token.Loc,
msg: []const u8,
note: ?Note = null,
pub const Note = struct {
loc: ?Token.Loc = null,
msg: []const u8,
};
};
arena: std.heap.ArenaAllocator,
list: std.ArrayListUnmanaged(ErrorMsg) = .{},
pub fn init(allocator: std.mem.Allocator) !ErrorList {
return .{
.arena = std.heap.ArenaAllocator.init(allocator),
};
}
pub fn deinit(self: *ErrorList) void {
self.arena.deinit();
self.* = undefined;
}
pub fn add(
self: *ErrorList,
loc: Token.Loc,
comptime format: []const u8,
args: anytype,
note: ?ErrorMsg.Note,
) !void {
const err_msg = .{
.loc = loc,
.msg = try std.fmt.allocPrint(self.arena.allocator(), comptime format, args),
.note = note,
};
try self.list.append(self.arena.allocator(), err_msg);
}
pub fn createNote(
self: *ErrorList,
loc: ?Token.Loc,
comptime format: []const u8,
args: anytype,
) !ErrorMsg.Note {
return .{
.loc = loc,
.msg = try std.fmt.allocPrint(self.arena.allocator(), comptime format, args),
};
}
pub fn print(self: ErrorList, source: []const u8, file_path: ?[]const u8) !void {
const stderr = std.io.getStdErr();
var bw = std.io.bufferedWriter(stderr.writer());
const b = bw.writer();
const term = std.io.tty.detectConfig(stderr);
for (self.list.items) |*err| {
const loc_extra = err.loc.extraInfo(source);
// 'file:line:column error: MSG'
try term.setColor(b, .bold);
try b.print("{?s}:{d}:{d} ", .{ file_path, loc_extra.line, loc_extra.col });
try term.setColor(b, .bright_red);
try b.writeAll("error: ");
try term.setColor(b, .reset);
try term.setColor(b, .bold);
try b.writeAll(err.msg);
try b.writeByte('\n');
try printCode(b, term, source, err.loc);
// note
if (err.note) |note| {
if (note.loc) |note_loc| {
const note_loc_extra = note_loc.extraInfo(source);
try term.setColor(b, .reset);
try term.setColor(b, .bold);
try b.print("{?s}:{d}:{d} ", .{ file_path, note_loc_extra.line, note_loc_extra.col });
}
try term.setColor(b, .cyan);
try b.writeAll("note: ");
try term.setColor(b, .reset);
try term.setColor(b, .bold);
try b.writeAll(note.msg);
try b.writeByte('\n');
if (note.loc) |note_loc| {
try printCode(b, term, source, note_loc);
}
}
try term.setColor(b, .reset);
}
try bw.flush();
}
fn printCode(writer: anytype, term: std.io.tty.Config, source: []const u8, loc: Token.Loc) !void {
const loc_extra = loc.extraInfo(source);
try term.setColor(writer, .dim);
try writer.print("{d} │ ", .{loc_extra.line});
try term.setColor(writer, .reset);
try writer.writeAll(source[loc_extra.line_start..loc.start]);
try term.setColor(writer, .green);
try writer.writeAll(source[loc.start..loc.end]);
try term.setColor(writer, .reset);
try writer.writeAll(source[loc.end..loc_extra.line_end]);
try writer.writeByte('\n');
// location pointer
const line_number_len = (std.math.log10(loc_extra.line) + 1) + 3;
try writer.writeByteNTimes(
' ',
line_number_len + (loc_extra.col - 1),
);
try term.setColor(writer, .bold);
try term.setColor(writer, .green);
try writer.writeByte('^');
try writer.writeByteNTimes('~', loc.end - loc.start - 1);
try writer.writeByte('\n');
}

2054
src/sysgpu/shader/Parser.zig Normal file

File diff suppressed because it is too large Load diff

518
src/sysgpu/shader/Token.zig Normal file
View file

@ -0,0 +1,518 @@
const std = @import("std");
tag: Tag,
loc: Loc,
pub const Loc = struct {
start: u32,
end: u32,
const Extra = struct {
line: u32,
col: u32,
line_start: u32,
line_end: u32,
};
pub fn slice(self: Loc, source: []const u8) []const u8 {
return source[self.start..self.end];
}
pub fn extraInfo(self: Loc, source: []const u8) Extra {
var result = Extra{
.line = 1,
.col = 1,
.line_start = 0,
.line_end = @intCast(source.len),
};
for (source[0..self.start], 0..) |c, i| {
if (c == '\n') {
result.line += 1;
result.line_start = @as(u32, @intCast(i)) + 1;
}
}
for (source[self.end..], 0..) |c, i| {
if (c == '\n') {
result.line_end = self.end + @as(u32, @intCast(i));
break;
}
}
result.col += self.start - result.line_start;
return result;
}
};
pub const Tag = enum {
eof,
invalid,
ident,
number,
paren_left,
paren_right,
brace_left,
brace_right,
bracket_left,
bracket_right,
dot,
comma,
colon,
semicolon,
arrow,
attr,
equal,
equal_equal,
bang,
bang_equal,
ampersand,
ampersand_equal,
ampersand_ampersand,
pipe,
pipe_equal,
pipe_pipe,
tilde,
plus,
plus_equal,
plus_plus,
minus,
minus_equal,
minus_minus,
asterisk,
asterisk_equal,
slash,
slash_equal,
percent,
percent_equal,
xor,
xor_equal,
angle_bracket_left,
angle_bracket_left_equal,
angle_bracket_angle_bracket_left,
angle_bracket_angle_bracket_left_equal,
angle_bracket_right,
angle_bracket_right_equal,
angle_bracket_angle_bracket_right,
angle_bracket_angle_bracket_right_equal,
underscore,
k_enable,
k_requires,
k_fn,
k_var,
k_let,
k_const,
k_override,
k_type,
k_if,
k_else,
k_loop,
k_while,
k_for,
k_break,
k_continue,
k_continuing,
k_discard,
k_switch,
k_case,
k_default,
k_return,
k_const_assert,
k_bitcast,
k_bool,
k_u32,
k_i32,
k_f16,
k_f32,
k_vec2,
k_vec3,
k_vec4,
k_mat2x2,
k_mat2x3,
k_mat2x4,
k_mat3x2,
k_mat3x3,
k_mat3x4,
k_mat4x2,
k_mat4x3,
k_mat4x4,
k_ptr,
k_array,
k_atomic,
k_struct,
k_sampler,
k_sampler_comparison,
k_texture_depth_2d,
k_texture_depth_2d_array,
k_texture_depth_cube,
k_texture_depth_cube_array,
k_texture_depth_multisampled_2d,
k_texture_external,
k_texture_multisampled_2d,
k_texture_1d,
k_texture_2d,
k_texture_2d_array,
k_texture_3d,
k_texture_cube,
k_texture_cube_array,
k_texture_storage_1d,
k_texture_storage_2d,
k_texture_storage_2d_array,
k_texture_storage_3d,
k_false,
k_true,
template_left,
template_right,
pub fn symbol(self: Tag) []const u8 {
return switch (self) {
.eof => "EOF",
.invalid => "invalid bytes",
.ident => "an identifier",
.number => "a number literal",
.paren_left => "(",
.paren_right => ")",
.brace_left => "{",
.brace_right => "}",
.bracket_left => "[",
.bracket_right => "]",
.dot => ".",
.comma => ",",
.colon => ":",
.semicolon => ";",
.arrow => "->",
.attr => "@",
.equal => "=",
.equal_equal => "==",
.bang => "!",
.bang_equal => "!=",
.ampersand => "&",
.ampersand_equal => "&=",
.ampersand_ampersand => "&&",
.pipe => "|",
.pipe_equal => "|=",
.pipe_pipe => "||",
.tilde => "~",
.plus => "+",
.plus_equal => "+=",
.plus_plus => "++",
.minus => "-",
.minus_equal => "-=",
.minus_minus => "--",
.asterisk => "*",
.asterisk_equal => "*=",
.slash => "/",
.slash_equal => "/=",
.percent => "%",
.percent_equal => "%=",
.xor => "^",
.xor_equal => "^=",
.angle_bracket_left => "<",
.angle_bracket_left_equal => "<=",
.angle_bracket_angle_bracket_left => "<<",
.angle_bracket_angle_bracket_left_equal => "<<=",
.angle_bracket_right => ">",
.angle_bracket_right_equal => ">=",
.angle_bracket_angle_bracket_right => ">>",
.angle_bracket_angle_bracket_right_equal => ">>=",
.underscore => "_",
.k_enable => "enable",
.k_requires => "requires",
.k_fn => "fn",
.k_var => "var",
.k_let => "let",
.k_const => "const",
.k_override => "override",
.k_type => "type",
.k_if => "if",
.k_else => "else",
.k_loop => "loop",
.k_while => "while",
.k_for => "for",
.k_break => "break",
.k_continue => "continue",
.k_continuing => "continuing",
.k_discard => "discard",
.k_switch => "switch",
.k_case => "case",
.k_default => "default",
.k_return => "return",
.k_const_assert => "const_assert",
.k_bitcast => "bitcast",
.k_bool => "bool",
.k_u32 => "u32",
.k_i32 => "i32",
.k_f16 => "f16",
.k_f32 => "f32",
.k_vec2 => "vec2",
.k_vec3 => "vec3",
.k_vec4 => "vec4",
.k_mat2x2 => "mat2x2",
.k_mat2x3 => "mat2x3",
.k_mat2x4 => "mat2x4",
.k_mat3x2 => "mat3x2",
.k_mat3x3 => "mat3x3",
.k_mat3x4 => "mat3x4",
.k_mat4x2 => "mat4x2",
.k_mat4x3 => "mat4x3",
.k_mat4x4 => "mat4x4",
.k_ptr => "ptr",
.k_array => "array",
.k_atomic => "atomic",
.k_struct => "struct",
.k_sampler => "sampler",
.k_sampler_comparison => "sampler_comparison",
.k_texture_depth_2d => "texture_depth_2d",
.k_texture_depth_2d_array => "texture_depth_2d_array",
.k_texture_depth_cube => "texture_depth_cube",
.k_texture_depth_cube_array => "texture_depth_cube_array",
.k_texture_depth_multisampled_2d => "texture_depth_multisampled_2d",
.k_texture_external => "texture_external",
.k_texture_multisampled_2d => "texture_multisampled_2d",
.k_texture_1d => "texture_1d",
.k_texture_2d => "texture_2d",
.k_texture_2d_array => "texture_2d_array",
.k_texture_3d => "texture_3d",
.k_texture_cube => "texture_cube",
.k_texture_cube_array => "texture_cube_array",
.k_texture_storage_1d => "texture_storage_1d",
.k_texture_storage_2d => "texture_storage_2d",
.k_texture_storage_2d_array => "texture_storage_2d_array",
.k_texture_storage_3d => "texture_storage_3d",
.k_false => "false",
.k_true => "true",
.template_left => "<",
.template_right => ">",
};
}
};
pub const keywords = std.ComptimeStringMap(Tag, .{
.{ "enable", .k_enable },
.{ "requires", .k_requires },
.{ "fn", .k_fn },
.{ "var", .k_var },
.{ "let", .k_let },
.{ "const", .k_const },
.{ "override", .k_override },
.{ "type", .k_type },
.{ "if", .k_if },
.{ "else", .k_else },
.{ "loop", .k_loop },
.{ "while", .k_while },
.{ "for", .k_for },
.{ "break", .k_break },
.{ "continue", .k_continue },
.{ "continuing", .k_continuing },
.{ "discard", .k_discard },
.{ "switch", .k_switch },
.{ "case", .k_case },
.{ "default", .k_default },
.{ "return", .k_return },
.{ "const_assert", .k_const_assert },
.{ "bitcast", .k_bitcast },
.{ "bool", .k_bool },
.{ "u32", .k_u32 },
.{ "i32", .k_i32 },
.{ "f16", .k_f16 },
.{ "f32", .k_f32 },
.{ "vec2", .k_vec2 },
.{ "vec3", .k_vec3 },
.{ "vec4", .k_vec4 },
.{ "mat2x2", .k_mat2x2 },
.{ "mat2x3", .k_mat2x3 },
.{ "mat2x4", .k_mat2x4 },
.{ "mat3x2", .k_mat3x2 },
.{ "mat3x3", .k_mat3x3 },
.{ "mat3x4", .k_mat3x4 },
.{ "mat4x2", .k_mat4x2 },
.{ "mat4x3", .k_mat4x3 },
.{ "mat4x4", .k_mat4x4 },
.{ "ptr", .k_ptr },
.{ "array", .k_array },
.{ "atomic", .k_atomic },
.{ "struct", .k_struct },
.{ "sampler", .k_sampler },
.{ "sampler_comparison", .k_sampler_comparison },
.{ "texture_depth_2d", .k_texture_depth_2d },
.{ "texture_depth_2d_array", .k_texture_depth_2d_array },
.{ "texture_depth_cube", .k_texture_depth_cube },
.{ "texture_depth_cube_array", .k_texture_depth_cube_array },
.{ "texture_depth_multisampled_2d", .k_texture_depth_multisampled_2d },
.{ "texture_external", .k_texture_external },
.{ "texture_multisampled_2d", .k_texture_multisampled_2d },
.{ "texture_1d", .k_texture_1d },
.{ "texture_2d", .k_texture_2d },
.{ "texture_2d_array", .k_texture_2d_array },
.{ "texture_3d", .k_texture_3d },
.{ "texture_cube", .k_texture_cube },
.{ "texture_cube_array", .k_texture_cube_array },
.{ "texture_storage_1d", .k_texture_storage_1d },
.{ "texture_storage_2d", .k_texture_storage_2d },
.{ "texture_storage_2d_array", .k_texture_storage_2d_array },
.{ "texture_storage_3d", .k_texture_storage_3d },
.{ "false", .k_false },
.{ "true", .k_true },
});
pub const reserved = blk: {
@setEvalBranchQuota(3000);
break :blk std.ComptimeStringMap(void, .{
.{ "NULL", {} },
.{ "Self", {} },
.{ "abstract", {} },
.{ "active", {} },
.{ "alignas", {} },
.{ "alignof", {} },
.{ "as", {} },
.{ "asm", {} },
.{ "asm_fragment", {} },
.{ "async", {} },
.{ "attribute", {} },
.{ "auto", {} },
.{ "await", {} },
.{ "become", {} },
.{ "binding_array", {} },
.{ "cast", {} },
.{ "catch", {} },
.{ "class", {} },
.{ "co_await", {} },
.{ "co_return", {} },
.{ "co_yield", {} },
.{ "coherent", {} },
.{ "column_major", {} },
.{ "common", {} },
.{ "compile", {} },
.{ "compile_fragment", {} },
.{ "concept", {} },
.{ "const_cast", {} },
.{ "consteval", {} },
.{ "constexpr", {} },
.{ "constinit", {} },
.{ "crate", {} },
.{ "debugger", {} },
.{ "decltype", {} },
.{ "delete", {} },
.{ "demote", {} },
.{ "demote_to_helper", {} },
.{ "do", {} },
.{ "dynamic_cast", {} },
.{ "enum", {} },
.{ "explicit", {} },
.{ "export", {} },
.{ "extends", {} },
.{ "extern", {} },
.{ "external", {} },
.{ "fallthrough", {} },
.{ "filter", {} },
.{ "final", {} },
.{ "finally", {} },
.{ "friend", {} },
.{ "from", {} },
.{ "fxgroup", {} },
.{ "get", {} },
.{ "goto", {} },
.{ "groupshared", {} },
.{ "highp", {} },
.{ "impl", {} },
.{ "implements", {} },
.{ "import", {} },
.{ "inline", {} },
.{ "instanceof", {} },
.{ "interface", {} },
.{ "layout", {} },
.{ "lowp", {} },
.{ "macro", {} },
.{ "macro_rules", {} },
.{ "match", {} },
.{ "mediump", {} },
.{ "meta", {} },
.{ "mod", {} },
.{ "module", {} },
.{ "move", {} },
.{ "mut", {} },
.{ "mutable", {} },
.{ "namespace", {} },
.{ "new", {} },
.{ "nil", {} },
.{ "noexcept", {} },
.{ "noinline", {} },
.{ "nointerpolation", {} },
.{ "noperspective", {} },
.{ "null", {} },
.{ "nullptr", {} },
.{ "of", {} },
.{ "operator", {} },
.{ "package", {} },
.{ "packoffset", {} },
.{ "partition", {} },
.{ "pass", {} },
.{ "patch", {} },
.{ "pixelfragment", {} },
.{ "precise", {} },
.{ "precision", {} },
.{ "premerge", {} },
.{ "priv", {} },
.{ "protected", {} },
.{ "pub", {} },
.{ "public", {} },
.{ "readonly", {} },
.{ "ref", {} },
.{ "regardless", {} },
.{ "register", {} },
.{ "reinterpret_cast", {} },
.{ "require", {} },
.{ "resource", {} },
.{ "restrict", {} },
.{ "self", {} },
.{ "set", {} },
.{ "shared", {} },
.{ "sizeof", {} },
.{ "smooth", {} },
.{ "snorm", {} },
.{ "static", {} },
.{ "static_assert", {} },
.{ "static_cast", {} },
.{ "std", {} },
.{ "subroutine", {} },
.{ "super", {} },
.{ "target", {} },
.{ "template", {} },
.{ "this", {} },
.{ "thread_local", {} },
.{ "throw", {} },
.{ "trait", {} },
.{ "try", {} },
.{ "type", {} },
.{ "typedef", {} },
.{ "typeid", {} },
.{ "typename", {} },
.{ "typeof", {} },
.{ "union", {} },
.{ "unless", {} },
.{ "unorm", {} },
.{ "unsafe", {} },
.{ "unsized", {} },
.{ "use", {} },
.{ "using", {} },
.{ "varying", {} },
.{ "virtual", {} },
.{ "volatile", {} },
.{ "wgsl", {} },
.{ "where", {} },
.{ "with", {} },
.{ "writeonly", {} },
.{ "yield", {} },
});
};

View file

@ -0,0 +1,437 @@
const std = @import("std");
const Token = @import("Token.zig");
const Tokenizer = @This();
source: [:0]const u8,
index: u32 = 0,
const State = union(enum) {
start,
ident,
underscore,
number: struct {
is_hex: bool = false,
allow_leading_sign: bool = false,
has_dot: bool = false,
},
block_comment,
ampersand,
bang,
equal,
angle_bracket_left,
angle_bracket_angle_bracket_left,
angle_bracket_right,
angle_bracket_angle_bracket_right,
minus,
percent,
dot,
pipe,
plus,
slash,
asterisk,
xor,
};
pub fn init(source: [:0]const u8) Tokenizer {
// skip the UTF-8 BOM if present
const src_start: u32 = if (std.mem.startsWith(u8, source, "\xEF\xBB\xBF")) 3 else 0;
return Tokenizer{ .source = source[src_start..] };
}
pub fn peek(tokenizer: *Tokenizer) Token {
var index = tokenizer.index;
var state: State = .start;
var result = Token{
.tag = .eof,
.loc = .{
.start = index,
.end = undefined,
},
};
while (true) : (index += 1) {
const c = tokenizer.source[index];
switch (state) {
.start => switch (c) {
0 => {
if (index != tokenizer.source.len) {
result.tag = .invalid;
index += 1;
}
break;
},
' ', '\n', '\t', '\r' => result.loc.start = index + 1,
'a'...'z', 'A'...'Z' => state = .ident,
'0'...'9' => state = .{ .number = .{} },
'&' => state = .ampersand,
'!' => state = .bang,
'=' => state = .equal,
'<' => state = .angle_bracket_left,
'>' => state = .angle_bracket_right,
'-' => state = .minus,
'%' => state = .percent,
'.' => state = .dot,
'|' => state = .pipe,
'+' => state = .plus,
'/' => state = .slash,
'*' => state = .asterisk,
'_' => state = .underscore,
'^' => state = .xor,
'@' => {
result.tag = .attr;
index += 1;
break;
},
'[' => {
result.tag = .bracket_left;
index += 1;
break;
},
']' => {
result.tag = .bracket_right;
index += 1;
break;
},
'{' => {
result.tag = .brace_left;
index += 1;
break;
},
'}' => {
result.tag = .brace_right;
index += 1;
break;
},
':' => {
result.tag = .colon;
index += 1;
break;
},
',' => {
result.tag = .comma;
index += 1;
break;
},
'(' => {
result.tag = .paren_left;
index += 1;
break;
},
')' => {
result.tag = .paren_right;
index += 1;
break;
},
';' => {
result.tag = .semicolon;
index += 1;
break;
},
'~' => {
result.tag = .tilde;
index += 1;
break;
},
else => {
result.tag = .invalid;
index += 1;
break;
},
},
.ident => switch (c) {
'a'...'z', 'A'...'Z', '0'...'9', '_' => {},
else => {
result.tag = .ident;
if (Token.keywords.get(tokenizer.source[result.loc.start..index])) |tag| {
result.tag = tag;
} else if (Token.reserved.get(tokenizer.source[result.loc.start..index])) |_| {
result.tag = .invalid;
}
break;
},
},
.underscore => switch (c) { // TODO: two underscore `__` https://www.w3.org/TR/WGSL/#identifiers
'a'...'z', 'A'...'Z', '_', '0'...'9' => state = .ident,
else => {
result.tag = .underscore;
break;
},
},
.number => |*number| {
result.tag = .number;
switch (c) {
'0'...'9' => {},
'a'...'d', 'A'...'D' => if (!number.is_hex) break,
'x', 'X' => number.is_hex = true,
'.' => {
if (number.has_dot) break;
number.has_dot = true;
},
'+', '-' => {
if (!number.allow_leading_sign) break;
number.allow_leading_sign = false;
number.is_hex = false;
},
'e', 'E' => if (!number.is_hex) {
number.allow_leading_sign = true;
},
'p', 'P' => if (number.is_hex) {
number.allow_leading_sign = true;
},
'i', 'u' => {
index += 1;
break;
},
'f', 'h' => if (!number.is_hex) {
index += 1;
break;
},
else => break,
}
},
.block_comment => switch (c) {
0 => break,
'\n' => {
state = .start;
result.loc.start = index + 1;
},
else => {},
},
.ampersand => switch (c) {
'&' => {
result.tag = .ampersand_ampersand;
index += 1;
break;
},
'=' => {
result.tag = .ampersand_equal;
index += 1;
break;
},
else => {
result.tag = .ampersand;
break;
},
},
.bang => switch (c) {
'=' => {
result.tag = .bang_equal;
index += 1;
break;
},
else => {
result.tag = .bang;
break;
},
},
.equal => switch (c) {
'=' => {
result.tag = .equal_equal;
index += 1;
break;
},
else => {
result.tag = .equal;
break;
},
},
.angle_bracket_left => switch (c) {
'<' => state = .angle_bracket_angle_bracket_left,
'=' => {
result.tag = .angle_bracket_left_equal;
index += 1;
break;
},
else => {
result.tag = .angle_bracket_left;
break;
},
},
.angle_bracket_angle_bracket_left => switch (c) {
'=' => {
result.tag = .angle_bracket_angle_bracket_left_equal;
index += 1;
break;
},
else => {
result.tag = .angle_bracket_angle_bracket_left;
break;
},
},
.angle_bracket_right => switch (c) {
'>' => state = .angle_bracket_angle_bracket_right,
'=' => {
result.tag = .angle_bracket_right_equal;
index += 1;
break;
},
else => {
result.tag = .angle_bracket_right;
break;
},
},
.angle_bracket_angle_bracket_right => switch (c) {
'=' => {
result.tag = .angle_bracket_angle_bracket_right_equal;
index += 1;
break;
},
else => {
result.tag = .angle_bracket_angle_bracket_right;
break;
},
},
.minus => switch (c) {
'-' => {
result.tag = .minus_minus;
index += 1;
break;
},
'=' => {
result.tag = .minus_equal;
index += 1;
break;
},
'>' => {
result.tag = .arrow;
index += 1;
break;
},
'0'...'9' => {
// workaround for x-1 being tokenized as [x] [-1]
// TODO: maybe it's user fault? :^)
// duplicated at .plus too
if (index >= 2 and std.ascii.isAlphabetic(tokenizer.source[index - 2])) {
result.tag = .minus;
break;
}
state = .{ .number = .{} };
},
else => {
result.tag = .minus;
break;
},
},
.percent => switch (c) {
'=' => {
result.tag = .percent_equal;
index += 1;
break;
},
else => {
result.tag = .percent;
break;
},
},
.pipe => switch (c) {
'|' => {
result.tag = .pipe_pipe;
index += 1;
break;
},
'=' => {
result.tag = .pipe_equal;
index += 1;
break;
},
else => {
result.tag = .pipe;
break;
},
},
.dot => switch (c) {
'0'...'9' => state = .{ .number = .{} },
else => {
result.tag = .dot;
break;
},
},
.plus => switch (c) {
'+' => {
result.tag = .plus_plus;
index += 1;
break;
},
'=' => {
result.tag = .plus_equal;
index += 1;
break;
},
'0'...'9' => {
if (index >= 2 and std.ascii.isAlphabetic(tokenizer.source[index - 2])) {
result.tag = .plus;
break;
}
state = .{ .number = .{} };
},
else => {
result.tag = .plus;
break;
},
},
.slash => switch (c) {
'/' => state = .block_comment,
'=' => {
result.tag = .slash_equal;
index += 1;
break;
},
else => {
result.tag = .slash;
break;
},
},
.asterisk => switch (c) {
'=' => {
result.tag = .asterisk_equal;
index += 1;
break;
},
else => {
result.tag = .asterisk;
break;
},
},
.xor => switch (c) {
'=' => {
result.tag = .xor_equal;
index += 1;
break;
},
else => {
result.tag = .xor;
break;
},
},
}
}
result.loc.end = index;
return result;
}
pub fn next(tokenizer: *Tokenizer) Token {
const tok = tokenizer.peek();
tokenizer.index = tok.loc.end;
return tok;
}
// test "tokenize identifier and numbers" {
// const str =
// \\_ __ _iden iden -100i 100.8i // cc
// \\// comment
// \\
// ;
// var tokenizer = Tokenizer.init(str);
// try std.testing.expect(tokenizer.next().tag == .underscore);
// try std.testing.expect(tokenizer.next().tag == .ident);
// try std.testing.expect(tokenizer.next().tag == .ident);
// try std.testing.expect(tokenizer.next().tag == .ident);
// try std.testing.expectEqualStrings("-100i", tokenizer.next().loc.slice(str));
// try std.testing.expect(tokenizer.next().tag == .number);
// try std.testing.expect(tokenizer.next().tag == .eof);
// }

View file

@ -0,0 +1,903 @@
const std = @import("std");
const Air = @import("../Air.zig");
const DebugInfo = @import("../CodeGen.zig").DebugInfo;
const Entrypoint = @import("../CodeGen.zig").Entrypoint;
const BindingPoint = @import("../CodeGen.zig").BindingPoint;
const BindingTable = @import("../CodeGen.zig").BindingTable;
const Inst = Air.Inst;
const InstIndex = Air.InstIndex;
const Builtin = Air.Inst.Builtin;
const Glsl = @This();
air: *const Air,
allocator: std.mem.Allocator,
storage: std.ArrayListUnmanaged(u8),
writer: std.ArrayListUnmanaged(u8).Writer,
bindings: *const BindingTable,
entrypoint_inst: ?Inst.Fn = null,
indent: u32 = 0,
pub fn gen(
allocator: std.mem.Allocator,
air: *const Air,
debug_info: DebugInfo,
entrypoint: ?Entrypoint,
bindings: ?*const BindingTable,
) ![]const u8 {
_ = debug_info;
var storage = std.ArrayListUnmanaged(u8){};
var glsl = Glsl{
.air = air,
.allocator = allocator,
.storage = storage,
.writer = storage.writer(allocator),
.bindings = bindings orelse &.{},
};
defer {
storage.deinit(allocator);
}
try glsl.writeAll("#version 450\n\n");
for (air.refToList(air.globals_index)) |inst_idx| {
switch (air.getInst(inst_idx)) {
.@"struct" => |inst| try glsl.emitStruct(inst),
else => {},
}
}
// GLSL deosn't support multiple entrypoints so we only generate
// when `entrypoint` is specified OR there's only one entrypoint
var entrypoint_name: ?[]const u8 = null;
if (entrypoint != null) {
entrypoint_name = std.mem.span(entrypoint.?.name);
} else {
const has_multiple_entrypoints = @intFromBool(air.vertex_stage == .none) &
@intFromBool(air.fragment_stage == .none) &
@intFromBool(air.compute_stage == .none);
if (has_multiple_entrypoints == 1) {
return error.MultipleEntrypoints;
}
}
for (air.refToList(air.globals_index)) |inst_idx| {
switch (air.getInst(inst_idx)) {
.@"var" => |inst| try glsl.emitGlobalVar(inst),
.@"fn" => |inst| {
const name = glsl.air.getStr(inst.name);
if (entrypoint_name) |_| {
if (std.mem.eql(u8, entrypoint_name.?, name)) {
try glsl.emitFn(inst);
}
} else if (inst.stage != .none) {
try glsl.emitFn(inst);
}
},
.@"struct" => {},
else => |inst| try glsl.print("TopLevel: {}\n", .{inst}), // TODO
}
}
return storage.toOwnedSlice(allocator);
}
fn emitElemType(glsl: *Glsl, inst_idx: InstIndex) !void {
switch (glsl.air.getInst(inst_idx)) {
.bool => |inst| try glsl.emitBoolElemType(inst),
.int => |inst| try glsl.emitIntElemType(inst),
.float => |inst| try glsl.emitFloatElemType(inst),
else => unreachable,
}
}
fn emitBoolElemType(glsl: *Glsl, inst: Inst.Bool) !void {
_ = inst;
try glsl.writeAll("b");
}
fn emitIntElemType(glsl: *Glsl, inst: Inst.Int) !void {
try glsl.writeAll(switch (inst.type) {
.u32 => "u",
.i32 => "i",
});
}
fn emitFloatElemType(glsl: *Glsl, inst: Inst.Float) !void {
try glsl.writeAll(switch (inst.type) {
.f32 => "",
.f16 => "", // TODO - extension for half support?
});
}
fn emitType(glsl: *Glsl, inst_idx: InstIndex) error{OutOfMemory}!void {
if (inst_idx == .none) {
try glsl.writeAll("void");
} else {
switch (glsl.air.getInst(inst_idx)) {
.bool => |inst| try glsl.emitBoolType(inst),
.int => |inst| try glsl.emitIntType(inst),
.float => |inst| try glsl.emitFloatType(inst),
.vector => |inst| try glsl.emitVectorType(inst),
.matrix => |inst| try glsl.emitMatrixType(inst),
.array => |inst| try glsl.emitType(inst.elem_type),
.@"struct" => |inst| try glsl.writeName(inst.name),
else => |inst| try glsl.print("Type: {}", .{inst}), // TODO
}
}
}
fn emitTypeSuffix(glsl: *Glsl, inst_idx: InstIndex) error{OutOfMemory}!void {
if (inst_idx != .none) {
switch (glsl.air.getInst(inst_idx)) {
.array => |inst| try glsl.emitArrayTypeSuffix(inst),
else => {},
}
}
}
fn emitArrayTypeSuffix(glsl: *Glsl, inst: Inst.Array) !void {
if (inst.len != .none) {
if (glsl.air.resolveInt(inst.len)) |len| {
try glsl.print("[{}]", .{len});
}
} else {
try glsl.writeAll("[]");
}
try glsl.emitTypeSuffix(inst.elem_type);
}
fn emitBoolType(glsl: *Glsl, inst: Inst.Bool) !void {
_ = inst;
try glsl.writeAll("bool");
}
fn emitIntType(glsl: *Glsl, inst: Inst.Int) !void {
try glsl.writeAll(switch (inst.type) {
.u32 => "uint",
.i32 => "int",
});
}
fn emitFloatType(glsl: *Glsl, inst: Inst.Float) !void {
try glsl.writeAll(switch (inst.type) {
.f32 => "float",
.f16 => "half",
});
}
fn emitVectorSize(glsl: *Glsl, size: Inst.Vector.Size) !void {
try glsl.writeAll(switch (size) {
.two => "2",
.three => "3",
.four => "4",
});
}
fn emitVectorType(glsl: *Glsl, inst: Inst.Vector) !void {
try glsl.emitElemType(inst.elem_type);
try glsl.writeAll("vec");
try glsl.emitVectorSize(inst.size);
}
fn emitMatrixType(glsl: *Glsl, inst: Inst.Matrix) !void {
// TODO - verify dimension order
try glsl.emitElemType(inst.elem_type);
try glsl.writeAll("mat");
try glsl.emitVectorSize(inst.cols);
try glsl.writeAll("x");
try glsl.emitVectorSize(inst.rows);
}
fn emitStruct(glsl: *Glsl, inst: Inst.Struct) !void {
// Workaround - structures with runtime arrays are not generally supported but can exist directly
// in a block context which we inline in emitGlobalVar
for (glsl.air.refToList(inst.members)) |member_index| {
const member = glsl.air.getInst(member_index).struct_member;
switch (glsl.air.getInst(member.type)) {
.array => |array_type| {
if (array_type.len == .none) {
return;
}
},
else => {},
}
}
try glsl.writeAll("struct ");
try glsl.writeName(inst.name);
try glsl.writeAll(" {\n");
glsl.enterScope();
defer glsl.exitScope();
for (glsl.air.refToList(inst.members)) |member_index| {
const member = glsl.air.getInst(member_index).struct_member;
try glsl.writeIndent();
try glsl.emitType(member.type);
try glsl.writeAll(" ");
try glsl.writeName(member.name);
try glsl.emitTypeSuffix(member.type);
try glsl.writeAll(";\n");
}
try glsl.writeAll("};\n");
}
fn emitBuiltin(glsl: *Glsl, builtin: Builtin) !void {
const stage = glsl.entrypoint_inst.?.stage;
try glsl.writeAll(switch (builtin) {
.vertex_index => "gl_VertexID",
.instance_index => "gl_InstanceID",
.position => if (stage == .vertex) "gl_Position" else "gl_FragCoord",
.front_facing => "gl_FrontFacing",
.frag_depth => "gl_FragDepth",
.local_invocation_id => "gl_LocalInvocationID",
.local_invocation_index => "gl_LocalInvocationIndex",
.global_invocation_id => "gl_GlobalInvocationID",
.workgroup_id => "gl_WorkGroupID",
.num_workgroups => "gl_NumWorkGroups",
.sample_index => "gl_SampleID",
.sample_mask => "gl_SampleMask", // TODO - gl_SampleMaskIn
});
}
fn emitGlobalVar(glsl: *Glsl, inst: Inst.Var) !void {
const group = glsl.air.resolveInt(inst.group) orelse return error.ConstExpr;
const binding = glsl.air.resolveInt(inst.binding) orelse return error.ConstExpr;
const key = BindingPoint{ .group = @intCast(group), .binding = @intCast(binding) };
const slot = glsl.bindings.get(key) orelse return error.NoBinding;
try glsl.print("layout(binding = {}, ", .{slot});
try glsl.writeAll(if (inst.addr_space == .uniform) "std140" else "std430");
try glsl.writeAll(") ");
if (inst.access_mode == .read)
try glsl.writeAll("readonly ");
try glsl.writeAll(if (inst.addr_space == .uniform) "uniform" else "buffer");
try glsl.print(" Block{}", .{slot});
const var_type = glsl.air.getInst(inst.type);
switch (var_type) {
.@"struct" => |struct_inst| {
// Inline struct to support runtime arrays
try glsl.writeAll("\n");
try glsl.writeAll("{\n");
glsl.enterScope();
defer glsl.exitScope();
for (glsl.air.refToList(struct_inst.members)) |member_index| {
const member = glsl.air.getInst(member_index).struct_member;
try glsl.writeIndent();
try glsl.emitType(member.type);
try glsl.writeAll(" ");
try glsl.writeName(member.name);
try glsl.emitTypeSuffix(member.type);
try glsl.writeAll(";\n");
}
try glsl.writeAll("} ");
try glsl.writeName(inst.name);
try glsl.writeAll(";\n");
},
else => {
try glsl.writeAll(" { ");
try glsl.emitType(inst.type);
try glsl.writeAll(" ");
try glsl.writeName(inst.name);
try glsl.emitTypeSuffix(inst.type);
try glsl.writeAll("; };\n");
},
}
}
fn emitGlobal(glsl: *Glsl, location: ?u16, in_out: []const u8, var_type: InstIndex, name: Air.StringIndex) !void {
try glsl.print("layout(location = {}) {s} ", .{ location.?, in_out });
try glsl.emitType(var_type);
try glsl.writeAll(" ");
try glsl.writeName(name);
try glsl.emitTypeSuffix(var_type);
try glsl.writeAll(";\n");
}
fn emitGlobalFnParam(glsl: *Glsl, inst_idx: InstIndex) !void {
const inst = glsl.air.getInst(inst_idx).fn_param;
if (inst.builtin == null) {
try glsl.emitGlobal(inst.location, "in", inst.type, inst.name);
}
}
fn emitGlobalStructOutputs(glsl: *Glsl, inst: Inst.Struct) !void {
for (glsl.air.refToList(inst.members)) |member_index| {
const member = glsl.air.getInst(member_index).struct_member;
if (member.builtin == null) {
try glsl.emitGlobal(member.location, "out", member.type, member.name);
}
}
}
fn emitGlobalScalarOutput(glsl: *Glsl, inst: Inst.Fn) !void {
if (inst.return_attrs.builtin == null) {
try glsl.print("layout(location = {}) out ", .{0});
try glsl.emitType(inst.return_type);
try glsl.writeAll(" ");
try glsl.writeAll("main_output");
try glsl.emitTypeSuffix(inst.return_type);
try glsl.writeAll(";\n");
}
}
fn emitFn(glsl: *Glsl, inst: Inst.Fn) !void {
if (inst.stage != .none) {
glsl.entrypoint_inst = inst;
if (inst.params != .none) {
const param_list = glsl.air.refToList(inst.params);
for (param_list) |param_inst_idx| {
try glsl.emitGlobalFnParam(param_inst_idx);
}
}
if (inst.return_type != .none) {
switch (glsl.air.getInst(inst.return_type)) {
.@"struct" => |struct_inst| try glsl.emitGlobalStructOutputs(struct_inst),
else => try glsl.emitGlobalScalarOutput(inst),
}
}
switch (inst.stage) {
.compute => |workgroup_size| {
try glsl.print("layout(local_size_x = {}, local_size_y = {}, local_size_z = {}) in;\n", .{
glsl.air.resolveInt(workgroup_size.x) orelse 1,
glsl.air.resolveInt(workgroup_size.y) orelse 1,
glsl.air.resolveInt(workgroup_size.z) orelse 1,
});
},
else => {},
}
try glsl.emitType(.none);
} else {
try glsl.emitType(inst.return_type);
}
try glsl.writeAll(" ");
if (inst.stage != .none) {
try glsl.writeEntrypoint();
} else {
try glsl.writeName(inst.name);
}
try glsl.writeAll("(");
if (inst.stage == .none) {
glsl.enterScope();
defer glsl.exitScope();
var add_comma = false;
if (inst.params != .none) {
for (glsl.air.refToList(inst.params)) |param_inst_idx| {
try glsl.writeAll(if (add_comma) ",\n" else "\n");
add_comma = true;
try glsl.writeIndent();
try glsl.emitFnParam(param_inst_idx);
}
}
}
try glsl.writeAll(")\n");
const block = glsl.air.getInst(inst.block).block;
try glsl.writeAll("{\n");
{
glsl.enterScope();
defer glsl.exitScope();
for (glsl.air.refToList(block)) |statement| {
try glsl.emitStatement(statement);
}
}
try glsl.writeAll("}\n");
glsl.entrypoint_inst = null;
}
fn emitFnParam(glsl: *Glsl, inst_idx: InstIndex) !void {
const inst = glsl.air.getInst(inst_idx).fn_param;
try glsl.emitType(inst.type);
try glsl.writeAll(" ");
try glsl.writeName(inst.name);
}
fn emitStatement(glsl: *Glsl, inst_idx: InstIndex) error{OutOfMemory}!void {
try glsl.writeIndent();
switch (glsl.air.getInst(inst_idx)) {
.@"var" => |inst| try glsl.emitVar(inst),
//.@"const" => |inst| try glsl.emitConst(inst),
.block => |block| try glsl.emitBlock(block),
// .loop => |inst| try glsl.emitLoop(inst),
// .continuing
.@"return" => |return_inst_idx| try glsl.emitReturn(return_inst_idx),
// .break_if
.@"if" => |inst| try glsl.emitIf(inst),
// .@"while" => |inst| try glsl.emitWhile(inst),
.@"for" => |inst| try glsl.emitFor(inst),
// .switch
//.discard => try glsl.emitDiscard(),
// .@"break" => try glsl.emitBreak(),
.@"continue" => try glsl.writeAll("continue;\n"),
// .call => |inst| try glsl.emitCall(inst),
.assign,
.nil_intrinsic,
.texture_store,
=> {
try glsl.emitExpr(inst_idx);
try glsl.writeAll(";\n");
},
//else => |inst| std.debug.panic("TODO: implement Air tag {s}", .{@tagName(inst)}),
else => |inst| try glsl.print("Statement: {}\n", .{inst}), // TODO
}
}
fn emitVar(glsl: *Glsl, inst: Inst.Var) !void {
const t = if (inst.type != .none) inst.type else inst.init;
try glsl.emitType(t);
try glsl.writeAll(" ");
try glsl.writeName(inst.name);
try glsl.emitTypeSuffix(t);
if (inst.init != .none) {
try glsl.writeAll(" = ");
try glsl.emitExpr(inst.init);
}
try glsl.writeAll(";\n");
}
fn emitBlock(glsl: *Glsl, block: Air.RefIndex) !void {
try glsl.writeAll("{\n");
{
glsl.enterScope();
defer glsl.exitScope();
for (glsl.air.refToList(block)) |statement| {
try glsl.emitStatement(statement);
}
}
try glsl.writeIndent();
try glsl.writeAll("}\n");
}
fn emitReturn(glsl: *Glsl, inst_idx: InstIndex) !void {
if (glsl.entrypoint_inst) |fn_inst| {
if (fn_inst.return_type != .none) {
switch (glsl.air.getInst(fn_inst.return_type)) {
.@"struct" => |struct_inst| try glsl.emitGlobalStructReturn(struct_inst, inst_idx),
else => try glsl.emitGlobalScalarReturn(fn_inst, inst_idx),
}
try glsl.writeIndent();
}
try glsl.writeAll("return;\n");
} else {
try glsl.writeAll("return");
if (inst_idx != .none) {
try glsl.writeAll(" ");
try glsl.emitExpr(inst_idx);
}
try glsl.writeAll(";\n");
}
}
fn emitGlobalStructReturn(glsl: *Glsl, inst: Inst.Struct, inst_idx: InstIndex) !void {
for (glsl.air.refToList(inst.members), 0..) |member_index, i| {
const member = glsl.air.getInst(member_index).struct_member;
if (i > 0) try glsl.writeIndent();
if (member.builtin) |builtin| {
try glsl.emitBuiltin(builtin);
} else {
try glsl.writeName(member.name);
}
try glsl.writeAll(" = ");
try glsl.emitExpr(inst_idx);
try glsl.writeAll(".");
try glsl.writeName(member.name);
try glsl.writeAll(";\n");
}
}
fn emitGlobalScalarReturn(glsl: *Glsl, inst: Inst.Fn, inst_idx: InstIndex) !void {
if (inst.return_attrs.builtin) |builtin| {
try glsl.emitBuiltin(builtin);
} else {
try glsl.writeAll("main_output");
}
if (inst_idx != .none) {
try glsl.writeAll(" = ");
try glsl.emitExpr(inst_idx);
}
try glsl.writeAll(";\n");
}
fn emitIf(glsl: *Glsl, inst: Inst.If) !void {
try glsl.writeAll("if (");
try glsl.emitExpr(inst.cond);
try glsl.writeAll(")\n");
{
const body_inst = glsl.air.getInst(inst.body);
if (body_inst != .block)
glsl.enterScope();
try glsl.emitStatement(inst.body);
if (body_inst != .block)
glsl.exitScope();
}
if (inst.@"else" != .none) {
try glsl.writeIndent();
try glsl.writeAll("else\n");
try glsl.emitStatement(inst.@"else");
}
try glsl.writeAll("\n");
}
fn emitFor(glsl: *Glsl, inst: Inst.For) !void {
try glsl.writeAll("for (\n");
{
glsl.enterScope();
defer glsl.exitScope();
try glsl.emitStatement(inst.init);
try glsl.writeIndent();
try glsl.emitExpr(inst.cond);
try glsl.writeAll(";\n");
try glsl.writeIndent();
try glsl.emitExpr(inst.update);
try glsl.writeAll(")\n");
}
try glsl.emitStatement(inst.body);
}
fn emitExpr(glsl: *Glsl, inst_idx: InstIndex) error{OutOfMemory}!void {
switch (glsl.air.getInst(inst_idx)) {
.var_ref => |inst| try glsl.emitVarRef(inst),
.bool => |inst| try glsl.emitBool(inst),
.int => |inst| try glsl.emitInt(inst),
.float => |inst| try glsl.emitFloat(inst),
.vector => |inst| try glsl.emitVector(inst),
//.matrix => |inst| try glsl.emitMatrix(inst),
.array => |inst| try glsl.emitArray(inst),
//.nil_intrinsic => |inst| try glsl.emitNilIntrinsic(inst),
.unary => |inst| try glsl.emitUnary(inst),
.unary_intrinsic => |inst| try glsl.emitUnaryIntrinsic(inst),
.binary => |inst| try glsl.emitBinary(inst),
.binary_intrinsic => |inst| try glsl.emitBinaryIntrinsic(inst),
.triple_intrinsic => |inst| try glsl.emitTripleIntrinsic(inst),
.assign => |inst| try glsl.emitAssign(inst),
.field_access => |inst| try glsl.emitFieldAccess(inst),
.swizzle_access => |inst| try glsl.emitSwizzleAccess(inst),
.index_access => |inst| try glsl.emitIndexAccess(inst),
//.call => |inst| try glsl.emitCall(inst),
//.struct_construct: StructConstruct,
//.bitcast: Bitcast,
//.texture_sample => |inst| try glsl.emitTextureSample(inst),
//.texture_dimension => |inst| try glsl.emitTextureDimension(inst),
//.texture_load => |inst| try glsl.emitTextureLoad(inst),
//.texture_store => |inst| try glsl.emitTextureStore(inst),
//else => |inst| std.debug.panic("TODO: implement Air tag {s}", .{@tagName(inst)}),
else => |inst| std.debug.panic("Expr: {}", .{inst}), // TODO
}
}
fn emitVarRef(glsl: *Glsl, inst_idx: InstIndex) !void {
switch (glsl.air.getInst(inst_idx)) {
.@"var" => |v| try glsl.writeName(v.name),
.@"const" => |c| try glsl.writeName(c.name),
.fn_param => |p| {
if (p.builtin) |builtin| {
try glsl.emitBuiltin(builtin);
} else {
try glsl.writeName(p.name);
}
},
else => |x| std.debug.panic("VarRef: {}", .{x}), // TODO
}
}
fn emitBool(glsl: *Glsl, inst: Inst.Bool) !void {
switch (inst.value.?) {
.literal => |lit| try glsl.print("{}", .{lit}),
.cast => @panic("TODO"),
}
}
fn emitInt(glsl: *Glsl, inst: Inst.Int) !void {
switch (glsl.air.getValue(Inst.Int.Value, inst.value.?)) {
.literal => |lit| try glsl.print("{}", .{lit}),
.cast => |cast| try glsl.emitIntCast(inst, cast),
}
}
fn emitIntCast(glsl: *Glsl, dest_type: Inst.Int, cast: Inst.ScalarCast) !void {
try glsl.emitIntType(dest_type);
try glsl.writeAll("(");
try glsl.emitExpr(cast.value);
try glsl.writeAll(")");
}
fn emitFloat(glsl: *Glsl, inst: Inst.Float) !void {
switch (glsl.air.getValue(Inst.Float.Value, inst.value.?)) {
.literal => |lit| try glsl.print("{}", .{lit}),
.cast => |cast| try glsl.emitFloatCast(inst, cast),
}
}
fn emitFloatCast(glsl: *Glsl, dest_type: Inst.Float, cast: Inst.ScalarCast) !void {
try glsl.emitFloatType(dest_type);
try glsl.writeAll("(");
try glsl.emitExpr(cast.value);
try glsl.writeAll(")");
}
fn emitVector(glsl: *Glsl, inst: Inst.Vector) !void {
try glsl.emitVectorType(inst);
try glsl.writeAll("(");
const value = glsl.air.getValue(Inst.Vector.Value, inst.value.?);
switch (value) {
.literal => |literal| try glsl.emitVectorElems(inst.size, literal),
.cast => |cast| try glsl.emitVectorElems(inst.size, cast.value),
}
try glsl.writeAll(")");
}
fn emitVectorElems(glsl: *Glsl, size: Inst.Vector.Size, value: [4]InstIndex) !void {
for (value[0..@intFromEnum(size)], 0..) |elem_inst, i| {
try glsl.writeAll(if (i == 0) "" else ", ");
try glsl.emitExpr(elem_inst);
}
}
fn emitArray(glsl: *Glsl, inst: Inst.Array) !void {
try glsl.emitType(inst.elem_type);
try glsl.writeAll("[](");
{
glsl.enterScope();
defer glsl.exitScope();
const value = glsl.air.refToList(inst.value.?);
for (value, 0..) |elem_inst, i| {
try glsl.writeAll(if (i == 0) "\n" else ",\n");
try glsl.writeIndent();
try glsl.emitExpr(elem_inst);
}
}
try glsl.writeAll(")");
}
fn emitUnary(glsl: *Glsl, inst: Inst.Unary) !void {
try glsl.writeAll(switch (inst.op) {
.not => "!",
.negate => "-",
.deref => "*",
.addr_of => @panic("unsupported"),
});
try glsl.emitExpr(inst.expr);
}
fn emitUnaryIntrinsic(glsl: *Glsl, inst: Inst.UnaryIntrinsic) !void {
switch (inst.op) {
.array_length => try glsl.emitArrayLength(inst),
else => {
try glsl.writeAll(switch (inst.op) {
.array_length => unreachable,
.degrees => "radians",
.radians => "degrees",
.all => "all",
.any => "any",
.abs => "abs",
.acos => "acos",
.acosh => "acosh",
.asin => "asin",
.asinh => "asinh",
.atan => "atan",
.atanh => "atanh",
.ceil => "ceil",
.cos => "cos",
.cosh => "cosh",
//.count_leading_zeros => "count_leading_zeros",
.count_one_bits => "bitCount",
//.count_trailing_zeros => "count_trailing_zeros",
.exp => "exp",
.exp2 => "exp2",
//.first_leading_bit => "first_leading_bit",
//.first_trailing_bit => "first_trailing_bit",
.floor => "floor",
.fract => "fract",
.inverse_sqrt => "inversesqrt",
.length => "length",
.log => "log",
.log2 => "log2",
//.quantize_to_F16 => "quantize_to_F16",
.reverseBits => "bitfieldReverse",
.round => "round",
//.saturate => "saturate",
.sign => "sign",
.sin => "sin",
.sinh => "sinh",
.sqrt => "sqrt",
.tan => "tan",
.tanh => "tanh",
.trunc => "trunc",
.dpdx => "dFdx",
.dpdx_coarse => "dFdxCoarse",
.dpdx_fine => "dFdxFine",
.dpdy => "dFdy",
.dpdy_coarse => "dFdyCoarse",
.dpdy_fine => "dFdyFine",
.fwidth => "fwidth",
.fwidth_coarse => "fwidthCoarse",
.fwidth_fine => "fwidthFine",
.normalize => "normalize",
else => std.debug.panic("TODO: implement Air tag {s}", .{@tagName(inst.op)}),
});
try glsl.writeAll("(");
try glsl.emitExpr(inst.expr);
try glsl.writeAll(")");
},
}
}
fn emitArrayLength(glsl: *Glsl, inst: Inst.UnaryIntrinsic) !void {
switch (glsl.air.getInst(inst.expr)) {
.unary => |un| switch (un.op) {
.addr_of => try glsl.emitArrayLengthTarget(un.expr, 0),
else => try glsl.print("ArrayLength (unary_op): {}", .{un.op}),
},
else => |array_length_expr| try glsl.print("ArrayLength (array_length_expr): {}", .{array_length_expr}),
}
}
fn emitArrayLengthTarget(glsl: *Glsl, inst_idx: InstIndex, offset: usize) error{OutOfMemory}!void {
try glsl.writeAll("(");
try glsl.emitExpr(inst_idx);
try glsl.print(".length() - {}", .{offset});
try glsl.writeAll(")");
}
fn emitBinary(glsl: *Glsl, inst: Inst.Binary) !void {
try glsl.writeAll("(");
try glsl.emitExpr(inst.lhs);
try glsl.print(" {s} ", .{switch (inst.op) {
.mul => "*",
.div => "/",
.mod => "%",
.add => "+",
.sub => "-",
.shl => "<<",
.shr => ">>",
.@"and" => "&",
.@"or" => "|",
.xor => "^",
.logical_and => "&&",
.logical_or => "||",
.equal => "==",
.not_equal => "!=",
.less_than => "<",
.less_than_equal => "<=",
.greater_than => ">",
.greater_than_equal => ">=",
}});
try glsl.emitExpr(inst.rhs);
try glsl.writeAll(")");
}
fn emitBinaryIntrinsic(glsl: *Glsl, inst: Inst.BinaryIntrinsic) !void {
try glsl.writeAll(switch (inst.op) {
.min => "min",
.max => "max",
.atan2 => "atan",
.distance => "distance",
.dot => "dot",
.pow => "pow",
.step => "step",
});
try glsl.writeAll("(");
try glsl.emitExpr(inst.lhs);
try glsl.writeAll(", ");
try glsl.emitExpr(inst.rhs);
try glsl.writeAll(")");
}
fn emitTripleIntrinsic(glsl: *Glsl, inst: Inst.TripleIntrinsic) !void {
try glsl.writeAll(switch (inst.op) {
.smoothstep => "smoothstep",
.clamp => "clamp",
.mix => "mix",
});
try glsl.writeAll("(");
try glsl.emitExpr(inst.a1);
try glsl.writeAll(", ");
try glsl.emitExpr(inst.a2);
try glsl.writeAll(", ");
try glsl.emitExpr(inst.a3);
try glsl.writeAll(")");
}
fn emitAssign(glsl: *Glsl, inst: Inst.Assign) !void {
try glsl.emitExpr(inst.lhs);
try glsl.print(" {s}= ", .{switch (inst.mod) {
.none => "",
.add => "+",
.sub => "-",
.mul => "*",
.div => "/",
.mod => "%",
.@"and" => "&",
.@"or" => "|",
.xor => "^",
.shl => "<<",
.shr => ">>",
}});
try glsl.emitExpr(inst.rhs);
}
fn emitFieldAccess(glsl: *Glsl, inst: Inst.FieldAccess) !void {
try glsl.emitExpr(inst.base);
try glsl.writeAll(".");
try glsl.writeName(inst.name);
}
fn emitSwizzleAccess(glsl: *Glsl, inst: Inst.SwizzleAccess) !void {
try glsl.emitExpr(inst.base);
try glsl.writeAll(".");
for (0..@intFromEnum(inst.size)) |i| {
switch (inst.pattern[i]) {
.x => try glsl.writeAll("x"),
.y => try glsl.writeAll("y"),
.z => try glsl.writeAll("z"),
.w => try glsl.writeAll("w"),
}
}
}
fn emitIndexAccess(glsl: *Glsl, inst: Inst.IndexAccess) !void {
try glsl.emitExpr(inst.base);
try glsl.writeAll("[");
try glsl.emitExpr(inst.index);
try glsl.writeAll("]");
}
fn enterScope(glsl: *Glsl) void {
glsl.indent += 4;
}
fn exitScope(glsl: *Glsl) void {
glsl.indent -= 4;
}
fn writeIndent(glsl: *Glsl) !void {
try glsl.writer.writeByteNTimes(' ', glsl.indent);
}
fn writeEntrypoint(glsl: *Glsl) !void {
try glsl.writeAll("main");
}
fn writeName(glsl: *Glsl, name: Air.StringIndex) !void {
// Suffix with index as WGSL has different scoping rules and to avoid conflicts with keywords
const str = glsl.air.getStr(name);
try glsl.print("{s}_{}", .{ str, @intFromEnum(name) });
}
fn writeAll(glsl: *Glsl, bytes: []const u8) !void {
try glsl.writer.writeAll(bytes);
}
fn print(glsl: *Glsl, comptime format: []const u8, args: anytype) !void {
return std.fmt.format(glsl.writer, format, args);
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,302 @@
//! Borrowed from Zig compiler codebase with changes.
//! Licensed under LICENSE-ZIG
//!
//! Represents a section or subsection of instructions in a SPIR-V binary. Instructions can be append
//! to separate sections, which can then later be merged into the final binary.
const std = @import("std");
const spec = @import("spec.zig");
const Opcode = spec.Opcode;
const Word = spec.Word;
const DoubleWord = std.meta.Int(.unsigned, @bitSizeOf(Word) * 2);
const Log2Word = std.math.Log2Int(Word);
const Section = @This();
allocator: std.mem.Allocator,
words: std.ArrayListUnmanaged(Word) = .{},
pub fn deinit(section: *Section) void {
section.words.deinit(section.allocator);
}
pub fn toWords(section: Section) []Word {
return section.words.items;
}
/// Append the words from another section into this section.
pub fn append(section: *Section, other_section: Section) !void {
try section.words.appendSlice(section.allocator, other_section.words.items);
}
/// Ensure capacity of at least `capacity` more words in this section.
pub fn ensureUnusedCapacity(section: *Section, capacity: usize) !void {
try section.words.ensureUnusedCapacity(section.allocator, capacity);
}
/// Write an instruction and size, operands are to be inserted manually.
pub fn emitRaw(
section: *Section,
opcode: Opcode,
operand_words: usize, // opcode itself not included
) !void {
const word_count = 1 + operand_words;
try section.words.ensureUnusedCapacity(section.allocator, word_count);
section.writeWord(@as(Word, @intCast(word_count << 16)) | @intFromEnum(opcode));
}
pub fn emit(
section: *Section,
comptime opcode: spec.Opcode,
operands: opcode.Operands(),
) !void {
const word_count = instructionSize(opcode, operands);
try section.ensureUnusedCapacity(word_count);
section.writeWord(@as(Word, @intCast(word_count << 16)) | @intFromEnum(opcode));
section.writeOperands(opcode.Operands(), operands);
}
pub fn emitSpecConstantOp(
section: *Section,
comptime opcode: spec.Opcode,
operands: opcode.Operands(),
) !void {
const word_count = operandsSize(opcode.Operands(), operands);
try section.emitRaw(.OpSpecConstantOp, 1 + word_count);
section.writeOperand(spec.IdRef, operands.id_result_type);
section.writeOperand(spec.IdRef, operands.id_result);
section.writeOperand(Opcode, opcode);
const fields = @typeInfo(opcode.Operands()).Struct.fields;
// First 2 fields are always id_result_type and id_result.
inline for (fields[2..]) |field| {
section.writeOperand(field.type, @field(operands, field.name));
}
}
pub fn writeWord(section: *Section, word: Word) void {
section.words.appendAssumeCapacity(word);
}
pub fn writeWords(section: *Section, words: []const Word) void {
section.words.appendSliceAssumeCapacity(words);
}
pub fn writeDoubleWord(section: *Section, dword: DoubleWord) void {
section.writeWords(&[_]Word{
@truncate(dword),
@truncate(dword >> @bitSizeOf(Word)),
});
}
fn writeOperands(section: *Section, comptime Operands: type, operands: Operands) void {
const fields = switch (@typeInfo(Operands)) {
.Struct => |info| info.fields,
.Void => return,
else => unreachable,
};
inline for (fields) |field| {
section.writeOperand(field.type, @field(operands, field.name));
}
}
pub fn writeOperand(section: *Section, comptime Operand: type, operand: Operand) void {
switch (Operand) {
spec.IdResult => section.writeWord(operand.id),
spec.LiteralInteger => section.writeWord(operand),
spec.LiteralString => section.writeString(operand),
spec.LiteralContextDependentNumber => section.writeContextDependentNumber(operand),
spec.LiteralExtInstInteger => section.writeWord(operand.inst),
// TODO: Where this type is used (OpSpecConstantOp) is currently not correct in the spec json,
// so it most likely needs to be altered into something that can actually describe the entire
// instruction in which it is used.
spec.LiteralSpecConstantOpInteger => section.writeWord(@intFromEnum(operand.opcode)),
spec.PairLiteralIntegerIdRef => section.writeWords(&.{ operand.value, operand.label.id }),
spec.PairIdRefLiteralInteger => section.writeWords(&.{ operand.target.id, operand.member }),
spec.PairIdRefIdRef => section.writeWords(&.{ operand[0].id, operand[1].id }),
else => switch (@typeInfo(Operand)) {
.Enum => section.writeWord(@intFromEnum(operand)),
.Optional => |info| if (operand) |child| {
section.writeOperand(info.child, child);
},
.Pointer => |info| {
std.debug.assert(info.size == .Slice); // Should be no other pointer types in the spec.
for (operand) |item| {
section.writeOperand(info.child, item);
}
},
.Struct => |info| {
if (info.layout == .Packed) {
section.writeWord(@bitCast(operand));
} else {
section.writeExtendedMask(Operand, operand);
}
},
.Union => section.writeExtendedUnion(Operand, operand),
else => unreachable,
},
}
}
fn writeString(section: *Section, str: []const u8) void {
// TODO: Not actually sure whether this is correct for big-endian.
// See https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#Literal
const zero_terminated_len = str.len + 1;
var i: usize = 0;
while (i < zero_terminated_len) : (i += @sizeOf(Word)) {
var word: Word = 0;
var j: usize = 0;
while (j < @sizeOf(Word) and i + j < str.len) : (j += 1) {
word |= @as(Word, str[i + j]) << @as(Log2Word, @intCast(j * @bitSizeOf(u8)));
}
section.words.appendAssumeCapacity(word);
}
}
fn writeContextDependentNumber(section: *Section, operand: spec.LiteralContextDependentNumber) void {
switch (operand) {
.int32 => |int| section.writeWord(@bitCast(int)),
.uint32 => |int| section.writeWord(@bitCast(int)),
.int64 => |int| section.writeDoubleWord(@bitCast(int)),
.uint64 => |int| section.writeDoubleWord(@bitCast(int)),
.float32 => |float| section.writeWord(@bitCast(float)),
.float64 => |float| section.writeDoubleWord(@bitCast(float)),
}
}
fn writeExtendedMask(section: *Section, comptime Operand: type, operand: Operand) void {
var mask: Word = 0;
inline for (@typeInfo(Operand).Struct.fields, 0..) |field, bit| {
switch (@typeInfo(field.type)) {
.Optional => if (@field(operand, field.name) != null) {
mask |= 1 << @intCast(bit);
},
.Bool => if (@field(operand, field.name)) {
mask |= 1 << @intCast(bit);
},
else => unreachable,
}
}
section.writeWord(mask);
inline for (@typeInfo(Operand).Struct.fields) |field| {
switch (@typeInfo(field.type)) {
.Optional => |info| if (@field(operand, field.name)) |child| {
section.writeOperands(info.child, child);
},
.Bool => {},
else => unreachable,
}
}
}
fn writeExtendedUnion(section: *Section, comptime Operand: type, operand: Operand) void {
const tag = std.meta.activeTag(operand);
section.writeWord(@intFromEnum(tag));
inline for (@typeInfo(Operand).Union.fields) |field| {
if (@field(Operand, field.name) == tag) {
section.writeOperands(field.type, @field(operand, field.name));
return;
}
}
unreachable;
}
fn instructionSize(comptime opcode: spec.Opcode, operands: opcode.Operands()) usize {
return 1 + operandsSize(opcode.Operands(), operands);
}
fn operandsSize(comptime Operands: type, operands: Operands) usize {
const fields = switch (@typeInfo(Operands)) {
.Struct => |info| info.fields,
.Void => return 0,
else => unreachable,
};
var total: usize = 0;
inline for (fields) |field| {
total += operandSize(field.type, @field(operands, field.name));
}
return total;
}
fn operandSize(comptime Operand: type, operand: Operand) usize {
return switch (Operand) {
spec.IdResult,
spec.LiteralInteger,
spec.LiteralExtInstInteger,
=> 1,
// Add one for zero-terminator
spec.LiteralString => std.math.divCeil(usize, operand.len + 1, @sizeOf(Word)) catch unreachable,
spec.LiteralContextDependentNumber => switch (operand) {
.int32, .uint32, .float32 => @as(usize, 1),
.int64, .uint64, .float64 => @as(usize, 2),
},
// TODO: Where this type is used (OpSpecConstantOp) is currently not correct in the spec
// json, so it most likely needs to be altered into something that can actually
// describe the entire insturction in which it is used.
spec.LiteralSpecConstantOpInteger => 1,
spec.PairLiteralIntegerIdRef,
spec.PairIdRefLiteralInteger,
spec.PairIdRefIdRef,
=> 2,
else => switch (@typeInfo(Operand)) {
.Enum => 1,
.Optional => |info| if (operand) |child| operandSize(info.child, child) else 0,
.Pointer => |info| blk: {
std.debug.assert(info.size == .Slice); // Should be no other pointer types in the spec.
var total: usize = 0;
for (operand) |item| {
total += operandSize(info.child, item);
}
break :blk total;
},
.Struct => |info| if (info.layout == .Packed) 1 else extendedMaskSize(Operand, operand),
.Union => extendedUnionSize(Operand, operand),
else => unreachable,
},
};
}
fn extendedMaskSize(comptime Operand: type, operand: Operand) usize {
var total: usize = 0;
var any_set = false;
inline for (@typeInfo(Operand).Struct.fields) |field| {
switch (@typeInfo(field.type)) {
.Optional => |info| if (@field(operand, field.name)) |child| {
total += operandsSize(info.child, child);
any_set = true;
},
.Bool => if (@field(operand, field.name)) {
any_set = true;
},
else => unreachable,
}
}
if (!any_set) {
return 0;
}
return total + 1; // Add one for the mask itself.
}
fn extendedUnionSize(comptime Operand: type, operand: Operand) usize {
const tag = std.meta.activeTag(operand);
inline for (@typeInfo(Operand).Union.fields) |field| {
if (@field(Operand, field.name) == tag) {
// Add one for the tag itself.
return 1 + operandsSize(field.type, @field(operand, field.name));
}
}
unreachable;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,528 @@
const std = @import("std");
const Air = @import("Air.zig");
const indention_size = 2;
pub fn printAir(ir: Air, writer: anytype) !void {
var p = Printer(@TypeOf(writer)){
.ir = ir,
.writer = writer,
.tty = std.io.tty.Config{ .escape_codes = {} },
};
const globals = ir.refToList(ir.globals_index);
for (globals) |ref| {
try p.printInst(0, ref);
}
}
fn Printer(comptime Writer: type) type {
return struct {
ir: Air,
writer: Writer,
tty: std.io.tty.Config,
fn printInst(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
const inst = self.ir.getInst(index);
switch (inst) {
.@"const" => {
try self.printConst(indent, index);
if (indent == 0) {
try self.printFieldEnd();
}
},
.@"struct" => {
try self.printStruct(0, index);
try self.printFieldEnd();
},
.@"fn" => {
std.debug.assert(indent == 0);
try self.printFn(indent, index);
try self.printFieldEnd();
},
.@"var" => try self.printVar(indent, index),
.bool => try self.printBool(indent, index),
.int, .float => try self.printNumber(indent, index),
.vector => try self.printVector(indent, index),
.matrix => try self.printMatrix(indent, index),
.sampler_type,
.comparison_sampler_type,
.external_texture_type,
=> {
try self.tty.setColor(self.writer, .bright_magenta);
try self.writer.print(".{s}", .{@tagName(inst)});
try self.tty.setColor(self.writer, .reset);
},
.binary => |bin| {
try self.instBlockStart(index);
try self.printFieldInst(indent + 1, "lhs", bin.lhs);
try self.printFieldInst(indent + 1, "rhs", bin.rhs);
try self.instBlockEnd(indent);
},
.unary_intrinsic => |un| {
try self.instBlockStart(index);
try self.printFieldInst(indent + 1, "expr", un.expr);
try self.printFieldInst(indent + 1, "res_ty", un.result_type);
try self.printFieldEnum(indent + 1, "op", un.op);
try self.instBlockEnd(indent);
},
.increase,
.decrease,
.loop,
.continuing,
.@"return",
.break_if,
=> |un| {
try self.instStart(index);
if (un != .none) {
try self.printInst(indent, un);
}
try self.instEnd();
},
.block => try self.printBlock(indent, index),
.@"if" => try self.printIf(indent, index),
.@"while" => try self.printWhile(indent, index),
.field_access => try self.printFieldAccess(indent, index),
.index_access => try self.printIndexAccess(indent, index),
.var_ref => |ref| {
try self.instStart(index);
try self.tty.setColor(self.writer, .yellow);
try self.writer.print("{d}", .{@intFromEnum(ref)});
try self.tty.setColor(self.writer, .reset);
try self.instEnd();
},
else => {
try self.instStart(index);
try self.writer.writeAll("TODO");
try self.instEnd();
},
}
}
fn printGlobalVar(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
const inst = self.ir.getInst(index).global_var;
try self.instBlockStart(index);
try self.printFieldString(indent + 1, "name", inst.name);
if (inst.addr_space) |addr_space| {
try self.printFieldEnum(indent + 1, "addr_space", addr_space);
}
if (inst.access_mode) |access_mode| {
try self.printFieldEnum(indent + 1, "access_mode", access_mode);
}
if (inst.type != .none) {
try self.printFieldInst(indent + 1, "type", inst.type);
}
if (inst.expr != .none) {
try self.printFieldInst(indent + 1, "value", inst.expr);
}
try self.instBlockEnd(indent);
}
fn printVar(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
const inst = self.ir.getInst(index).@"var";
try self.instBlockStart(index);
try self.printFieldString(indent + 1, "name", inst.name);
try self.printFieldEnum(indent + 1, "addr_space", inst.addr_space);
try self.printFieldEnum(indent + 1, "access_mode", inst.access_mode);
if (inst.type != .none) {
try self.printFieldInst(indent + 1, "type", inst.type);
}
if (inst.expr != .none) {
try self.printFieldInst(indent + 1, "value", inst.expr);
}
try self.instBlockEnd(indent);
}
fn printConst(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
const inst = self.ir.getInst(index).@"const";
try self.instBlockStart(index);
try self.printFieldString(indent + 1, "name", inst.name);
if (inst.type != .none) {
try self.printFieldInst(indent + 1, "type", inst.type);
}
try self.printFieldInst(indent + 1, "value", inst.expr);
try self.instBlockEnd(indent);
}
fn printLet(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
const inst = self.ir.getInst(index).let;
try self.instBlockStart(index);
try self.printFieldString(indent + 1, "name", inst.name);
if (inst.type != .none) {
try self.printFieldInst(indent + 1, "type", inst.type);
}
try self.printFieldInst(indent + 1, "value", inst.expr);
try self.instBlockEnd(indent);
}
fn printStruct(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
const inst = self.ir.getInst(index);
try self.instBlockStart(index);
try self.printFieldString(indent + 1, "name", inst.@"struct".name);
try self.printFieldName(indent + 1, "members");
try self.listStart();
const members = self.ir.refToList(inst.@"struct".members);
for (members) |member| {
const member_index = member;
const member_inst = self.ir.getInst(member_index);
try self.printIndent(indent + 2);
try self.instBlockStart(member_index);
try self.printFieldString(indent + 3, "name", member_inst.struct_member.name);
try self.printFieldInst(indent + 3, "type", member_inst.struct_member.type);
if (member_inst.struct_member.@"align") |@"align"| {
try self.printFieldAny(indent + 3, "align", @"align");
}
if (member_inst.struct_member.size) |size| {
try self.printFieldAny(indent + 3, "size", size);
}
if (member_inst.struct_member.builtin) |builtin| {
try self.printFieldAny(indent + 3, "builtin", builtin);
}
if (member_inst.struct_member.location) |location| {
try self.printFieldAny(indent + 3, "location", location);
}
try self.instBlockEnd(indent + 2);
try self.printFieldEnd();
}
try self.listEnd(indent + 1);
try self.printFieldEnd();
try self.instBlockEnd(indent);
}
fn printFn(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
const inst = self.ir.getInst(index);
try self.instBlockStart(index);
try self.printFieldString(indent + 1, "name", inst.@"fn".name);
if (inst.@"fn".params != .none) {
try self.printFieldName(indent + 1, "params");
try self.listStart();
const params = self.ir.refToList(inst.@"fn".params);
for (params) |arg| {
const arg_index = arg;
const arg_inst = self.ir.getInst(arg_index);
try self.printIndent(indent + 2);
try self.instBlockStart(arg_index);
try self.printFieldString(indent + 3, "name", arg_inst.fn_param.name);
try self.printFieldInst(indent + 3, "type", arg_inst.fn_param.type);
if (arg_inst.fn_param.builtin) |builtin| {
try self.printFieldEnum(indent + 3, "builtin", builtin);
}
if (arg_inst.fn_param.interpolate) |interpolate| {
try self.printFieldName(indent + 3, "interpolate");
try self.instBlockStart(index);
try self.printFieldEnum(indent + 4, "type", interpolate.type);
if (interpolate.sample != .none) {
try self.printFieldEnum(indent + 4, "sample", interpolate.sample);
}
try self.instBlockEnd(indent + 4);
try self.printFieldEnd();
}
if (arg_inst.fn_param.location) |location| {
try self.printFieldAny(indent + 3, "location", location);
}
if (arg_inst.fn_param.invariant) {
try self.printFieldAny(indent + 3, "invariant", arg_inst.fn_param.invariant);
}
try self.instBlockEnd(indent + 2);
try self.printFieldEnd();
}
try self.listEnd(indent + 1);
try self.printFieldEnd();
}
if (inst.@"fn".block != .none) {
try self.printFieldName(indent + 1, "block");
try self.printBlock(indent + 1, inst.@"fn".block);
try self.printFieldEnd();
}
try self.instBlockEnd(indent);
}
fn printBlock(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
const inst = self.ir.getInst(index).block;
const statements = self.ir.refToList(inst);
try self.listStart();
for (statements) |statement| {
try self.printIndent(indent + 1);
try self.printInst(indent + 1, statement);
try self.printFieldEnd();
}
try self.listEnd(indent);
}
fn printIf(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
const inst = self.ir.getInst(index).@"if";
try self.instBlockStart(index);
try self.printFieldInst(indent + 1, "cond", inst.cond);
if (inst.body != .none) {
try self.printFieldInst(indent + 1, "body", inst.body);
}
if (inst.@"else" != .none) {
try self.printFieldInst(indent + 1, "else", inst.@"else");
}
try self.instBlockEnd(indent);
}
fn printWhile(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
const inst = self.ir.getInst(index).@"while";
try self.instBlockStart(index);
try self.printFieldInst(indent + 1, "cond", inst.cond);
if (inst.body != .none) {
try self.printFieldInst(indent + 1, "body", inst.body);
}
try self.instBlockEnd(indent);
}
fn printBool(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
const inst = self.ir.getInst(index);
if (inst.bool.value) |value| {
switch (value) {
.literal => |lit| {
try self.instStart(index);
try self.tty.setColor(self.writer, .cyan);
try self.writer.print("{}", .{lit});
try self.tty.setColor(self.writer, .reset);
try self.instEnd();
},
.cast => |cast| {
try self.instBlockStart(index);
try self.printFieldInst(indent + 1, "type", cast.type);
try self.printFieldInst(indent + 1, "value", cast.type);
try self.instBlockEnd(indent);
},
}
} else {
try self.instStart(index);
try self.instEnd();
}
}
fn printNumber(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
const inst = self.ir.getInst(index);
try self.instBlockStart(index);
switch (inst) {
.int => |int| {
try self.printFieldEnum(indent + 1, "type", int.type);
if (int.value) |value| {
switch (self.ir.getValue(Air.Inst.Int.Value, value)) {
.literal => |lit| try self.printFieldAny(indent + 1, "value", lit),
.cast => |cast| {
try self.printFieldName(indent + 1, "cast");
try self.instBlockStart(index);
try self.printFieldInst(indent + 2, "type", cast.type);
try self.printFieldInst(indent + 2, "value", cast.value);
try self.instBlockEnd(indent);
try self.printFieldEnd();
},
}
}
},
.float => |float| {
try self.printFieldEnum(indent + 1, "type", float.type);
if (float.value) |value| {
switch (self.ir.getValue(Air.Inst.Float.Value, value)) {
.literal => |lit| try self.printFieldAny(indent + 1, "value", lit),
.cast => |cast| {
try self.printFieldName(indent + 1, "cast");
try self.instBlockStart(index);
try self.printFieldInst(indent + 2, "type", cast.type);
try self.printFieldInst(indent + 2, "value", cast.value);
try self.instBlockEnd(indent);
try self.printFieldEnd();
},
}
}
},
else => unreachable,
}
try self.instBlockEnd(indent);
}
fn printVector(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
const vec = self.ir.getInst(index).vector;
try self.instBlockStart(index);
try self.printFieldInst(indent + 1, "type", vec.elem_type);
if (vec.value) |value_idx| {
if (value_idx == .none) {
try self.printFieldAny(indent + 1, "value", "null");
} else {
const value = self.ir.getValue(Air.Inst.Vector.Value, value_idx);
switch (value) {
.literal => |lit| {
try self.printFieldName(indent + 1, "literal");
try self.listStart();
for (0..@intFromEnum(vec.size)) |i| {
try self.printIndent(indent + 2);
try self.printInst(indent + 2, lit[i]);
try self.printFieldEnd();
}
try self.listEnd(indent + 1);
try self.printFieldEnd();
},
.cast => |cast| {
try self.printFieldName(indent + 1, "cast");
try self.listStart();
for (0..@intFromEnum(vec.size)) |i| {
try self.printIndent(indent + 2);
try self.printInst(indent + 2, cast.value[i]);
try self.printFieldEnd();
}
try self.listEnd(indent + 1);
try self.printFieldEnd();
},
}
}
}
try self.instBlockEnd(indent);
}
fn printMatrix(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
const mat = self.ir.getInst(index).matrix;
try self.instBlockStart(index);
try self.printFieldInst(indent + 1, "type", mat.elem_type);
if (mat.value) |value_idx| {
const value = self.ir.getValue(Air.Inst.Matrix.Value, value_idx);
try self.printFieldName(indent + 1, "value");
try self.listStart();
for (0..@intFromEnum(mat.cols) * @intFromEnum(mat.rows)) |i| {
try self.printIndent(indent + 2);
try self.printInst(indent + 2, value[i]);
try self.printFieldEnd();
}
try self.listEnd(indent + 1);
try self.printFieldEnd();
}
try self.instBlockEnd(indent);
}
fn printFieldAccess(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
const inst = self.ir.getInst(index);
try self.instBlockStart(index);
try self.printFieldInst(indent + 1, "base", inst.field_access.base);
try self.printFieldString(indent + 1, "name", inst.field_access.name);
try self.instBlockEnd(indent);
}
fn printIndexAccess(self: @This(), indent: u16, index: Air.InstIndex) Writer.Error!void {
const inst = self.ir.getInst(index);
try self.instBlockStart(index);
try self.printFieldInst(indent + 1, "base", inst.index_access.base);
try self.printFieldInst(indent + 1, "type", inst.index_access.type);
try self.printFieldInst(indent + 1, "index", inst.index_access.index);
try self.instBlockEnd(indent);
}
fn instStart(self: @This(), index: Air.InstIndex) !void {
const inst = self.ir.getInst(index);
try self.tty.setColor(self.writer, .bold);
try self.writer.print("{s}", .{@tagName(inst)});
try self.tty.setColor(self.writer, .reset);
try self.tty.setColor(self.writer, .dim);
try self.writer.writeAll("<");
try self.tty.setColor(self.writer, .reset);
try self.tty.setColor(self.writer, .blue);
try self.writer.print("{d}", .{@intFromEnum(index)});
try self.tty.setColor(self.writer, .reset);
try self.tty.setColor(self.writer, .dim);
try self.writer.writeAll(">");
try self.writer.writeAll("(");
try self.tty.setColor(self.writer, .reset);
}
fn instEnd(self: @This()) !void {
try self.tty.setColor(self.writer, .dim);
try self.writer.writeAll(")");
try self.tty.setColor(self.writer, .reset);
}
fn instBlockStart(self: @This(), index: Air.InstIndex) !void {
const inst = self.ir.getInst(index);
try self.tty.setColor(self.writer, .bold);
try self.writer.print("{s}", .{@tagName(inst)});
try self.tty.setColor(self.writer, .reset);
try self.tty.setColor(self.writer, .dim);
try self.writer.writeAll("<");
try self.tty.setColor(self.writer, .reset);
try self.tty.setColor(self.writer, .blue);
try self.writer.print("{d}", .{@intFromEnum(index)});
try self.tty.setColor(self.writer, .reset);
try self.tty.setColor(self.writer, .dim);
try self.writer.writeAll(">");
try self.writer.writeAll("{\n");
try self.tty.setColor(self.writer, .reset);
}
fn instBlockEnd(self: @This(), indent: u16) !void {
try self.printIndent(indent);
try self.tty.setColor(self.writer, .dim);
try self.writer.writeAll("}");
try self.tty.setColor(self.writer, .reset);
}
fn listStart(self: @This()) !void {
try self.tty.setColor(self.writer, .dim);
try self.writer.writeAll("[\n");
try self.tty.setColor(self.writer, .reset);
}
fn listEnd(self: @This(), indent: u16) !void {
try self.printIndent(indent);
try self.tty.setColor(self.writer, .dim);
try self.writer.writeAll("]");
try self.tty.setColor(self.writer, .reset);
}
fn printFieldName(self: @This(), indent: u16, name: []const u8) !void {
try self.printIndent(indent);
try self.tty.setColor(self.writer, .reset);
try self.writer.print("{s}", .{name});
try self.tty.setColor(self.writer, .dim);
try self.writer.print(": ", .{});
try self.tty.setColor(self.writer, .reset);
}
fn printFieldString(self: @This(), indent: u16, name: []const u8, value: Air.StringIndex) !void {
try self.printFieldName(indent, name);
try self.tty.setColor(self.writer, .green);
try self.writer.print("'{s}'", .{self.ir.getStr(value)});
try self.tty.setColor(self.writer, .reset);
try self.printFieldEnd();
}
fn printFieldInst(self: @This(), indent: u16, name: []const u8, value: Air.InstIndex) !void {
try self.printFieldName(indent, name);
try self.printInst(indent, value);
try self.printFieldEnd();
}
fn printFieldEnum(self: @This(), indent: u16, name: []const u8, value: anytype) !void {
try self.printFieldName(indent, name);
try self.tty.setColor(self.writer, .magenta);
try self.writer.print(".{s}", .{@tagName(value)});
try self.tty.setColor(self.writer, .reset);
try self.printFieldEnd();
}
fn printFieldAny(self: @This(), indent: u16, name: []const u8, value: anytype) !void {
try self.printFieldName(indent, name);
try self.tty.setColor(self.writer, .cyan);
if (@typeInfo(@TypeOf(value)) == .Pointer) {
// assume string
try self.writer.print("{s}", .{value});
} else {
try self.writer.print("{}", .{value});
}
try self.tty.setColor(self.writer, .reset);
try self.printFieldEnd();
}
fn printFieldEnd(self: @This()) !void {
try self.writer.writeAll(",\n");
}
fn printIndent(self: @This(), indent: u16) !void {
try self.writer.writeByteNTimes(' ', indent * indention_size);
}
};
}

294
src/sysgpu/shader/test.zig Normal file
View file

@ -0,0 +1,294 @@
const std = @import("std");
const ErrorList = @import("ErrorList.zig");
const Ast = @import("Ast.zig");
const Air = @import("Air.zig");
const CodeGen = @import("CodeGen.zig");
const printAir = @import("print_air.zig").printAir;
const expect = std.testing.expect;
const expectEqual = std.testing.expectEqual;
const allocator = std.testing.allocator;
test "builtins" {
const builtins = @embedFile("test/builtins.wgsl");
try expectCodegen(builtins, "builtins.spv", .spirv, false);
try expectCodegen(builtins, "builtins.hlsl", .hlsl, false);
try expectCodegen(builtins, "builtins.msl", .msl, false);
try expectCodegen(builtins, "builtins-spirvcross.glsl", .glsl, true);
// try expectCodegen(if_else, "if-else.glsl", .glsl, false);
}
test "if-else" {
const if_else = @embedFile("test/if-else.wgsl");
try expectCodegen(if_else, "if-else.spv", .spirv, false);
try expectCodegen(if_else, "if-else.hlsl", .hlsl, false);
try expectCodegen(if_else, "if-else.msl", .msl, false);
try expectCodegen(if_else, "if-else-spirvcross.glsl", .glsl, true);
// try expectCodegen(if_else, "if-else.glsl", .glsl, false);
}
test "boids-sprite" {
const boids_sprite = @embedFile("test/boids-sprite.wgsl");
try expectCodegen(boids_sprite, "boids-sprite.spv", .spirv, false);
try expectCodegen(boids_sprite, "boids-sprite.hlsl", .hlsl, false);
try expectCodegen(boids_sprite, "boids-sprite.msl", .msl, false);
try expectCodegen(boids_sprite, "boids-sprite-spirvcross.glsl", .glsl, true);
// try expectCodegen(boids_sprite, "boids-sprite.glsl", .glsl, false);
}
test "boids-sprite-update" {
const boids_sprite_update = @embedFile("test/boids-sprite-update.wgsl");
try expectCodegen(boids_sprite_update, "boids-sprite-update.spv", .spirv, false);
try expectCodegen(boids_sprite_update, "boids-sprite-update.hlsl", .hlsl, false);
try expectCodegen(boids_sprite_update, "boids-sprite-update.msl", .msl, false);
try expectCodegen(boids_sprite_update, "boids-sprite-update-spirvcross.glsl", .glsl, true);
// try expectCodegen(boids_sprite_update, "boids-sprite-update.glsl", .glsl, false);
}
test "cube-map" {
const cube_map = @embedFile("test/cube-map.wgsl");
try expectCodegen(cube_map, "cube-map.spv", .spirv, false);
try expectCodegen(cube_map, "cube-map.hlsl", .hlsl, false);
try expectCodegen(cube_map, "cube-map.msl", .msl, false);
try expectCodegen(cube_map, "cube-map-spirvcross.glsl", .glsl, true);
// try expectCodegen(cube_map, "cube-map.glsl", .glsl, false);
}
test "fractal-cube" {
const fractal_cube = @embedFile("test/fractal-cube.wgsl");
try expectCodegen(fractal_cube, "fractal-cube.spv", .spirv, false);
try expectCodegen(fractal_cube, "fractal-cube.hlsl", .hlsl, false);
try expectCodegen(fractal_cube, "fractal-cube.msl", .msl, false);
try expectCodegen(fractal_cube, "fractal-cube-spirvcross.glsl", .glsl, true);
// try expectCodegen(fractal_cube, "fractal-cube.glsl", .glsl, false);
}
test "gen-texture-light" {
const gen_texture_light = @embedFile("test/gen-texture-light.wgsl");
try expectCodegen(gen_texture_light, "gen-texture-light.spv", .spirv, false);
try expectCodegen(gen_texture_light, "gen-texture-light.hlsl", .hlsl, false);
try expectCodegen(gen_texture_light, "gen-texture-light.msl", .msl, false);
try expectCodegen(gen_texture_light, "gen-texture-light-spirvcross.glsl", .glsl, true);
// try expectCodegen(gen_texture_light, "gen-texture-light.glsl", .glsl, false);
}
test "gen-texture-light-cube" {
const gen_texture_light_cube = @embedFile("test/gen-texture-light-cube.wgsl");
try expectCodegen(gen_texture_light_cube, "gen-texture-light-cube.spv", .spirv, false);
try expectCodegen(gen_texture_light_cube, "gen-texture-light-cube.hlsl", .hlsl, false);
try expectCodegen(gen_texture_light_cube, "gen-texture-light-cube.msl", .msl, false);
try expectCodegen(gen_texture_light_cube, "gen-texture-light-cube-spirvcross.glsl", .glsl, true);
// try expectCodegen(gen_texture_light_cube, "gen-texture-light-cube.glsl", .glsl, false);
}
test "sprite2d" {
const sprite2d = @embedFile("test/sprite2d.wgsl");
try expectCodegen(sprite2d, "sprite2d.spv", .spirv, false);
try expectCodegen(sprite2d, "sprite2d.hlsl", .hlsl, false);
try expectCodegen(sprite2d, "sprite2d.msl", .msl, false);
try expectCodegen(sprite2d, "sprite2d-spirvcross.glsl", .glsl, true);
// try expectCodegen(sprite2d, "sprite2d.glsl", .glsl, false);
}
test "two-cubes" {
const two_cubes = @embedFile("test/two-cubes.wgsl");
try expectCodegen(two_cubes, "two-cubes.spv", .spirv, false);
try expectCodegen(two_cubes, "two-cubes.hlsl", .hlsl, false);
try expectCodegen(two_cubes, "two-cubes.msl", .msl, false);
try expectCodegen(two_cubes, "two-cubes-spirvcross.glsl", .glsl, true);
// try expectCodegen(two_cubes, "two-cubes.glsl", .glsl, false);
}
test "fullscreen-textured-quad" {
const fullscreen_textured_quad = @embedFile("test/fullscreen-textured-quad.wgsl");
try expectCodegen(fullscreen_textured_quad, "fullscreen-textured-quad.spv", .spirv, false);
try expectCodegen(fullscreen_textured_quad, "fullscreen-textured-quad.hlsl", .hlsl, false);
try expectCodegen(fullscreen_textured_quad, "fullscreen-textured-quad.msl", .msl, false);
try expectCodegen(fullscreen_textured_quad, "fullscreen-textured-quad-spirvcross.glsl", .glsl, true);
// try expectCodegen(fullscreen_textured_quad, "fullscreen-textured-quad.glsl", .glsl, false);
}
test "image-blur" {
const image_blur = @embedFile("test/image-blur.wgsl");
try expectCodegen(image_blur, "image-blur.spv", .spirv, false);
try expectCodegen(image_blur, "image-blur.hlsl", .hlsl, false);
try expectCodegen(image_blur, "image-blur.msl", .msl, false);
try expectCodegen(image_blur, "image-blur-spirvcross.glsl", .glsl, true);
// try expectCodegen(image_blur, "image-blur.glsl", .glsl, false);
}
test "instanced-cube" {
const instanced_cube = @embedFile("test/instanced-cube.wgsl");
try expectCodegen(instanced_cube, "instanced-cube.spv", .spirv, false);
try expectCodegen(instanced_cube, "instanced-cube.hlsl", .hlsl, false);
try expectCodegen(instanced_cube, "instanced-cube.msl", .msl, false);
// TODO
// try expectCodegen(instanced_cube, "instanced-cube-spirvcross.glsl", .glsl, true);
// try expectCodegen(instanced_cube, "instanced-cube.glsl", .glsl, false);
}
test "map-async" {
const map_async = @embedFile("test/map-async.wgsl");
try expectCodegen(map_async, "map-async.spv", .spirv, false);
try expectCodegen(map_async, "map-async.hlsl", .hlsl, false);
try expectCodegen(map_async, "map-async.msl", .msl, false);
try expectCodegen(map_async, "map-async-spirvcross.glsl", .glsl, true);
// try expectCodegen(map_async, "map-async.glsl", .glsl, false);
}
test "pbr-basic" {
const pbr_basic = @embedFile("test/pbr-basic.wgsl");
try expectCodegen(pbr_basic, "pbr-basic.spv", .spirv, false);
try expectCodegen(pbr_basic, "pbr-basic.hlsl", .hlsl, false);
try expectCodegen(pbr_basic, "pbr-basic.msl", .msl, false);
try expectCodegen(pbr_basic, "pbr-basic-spirvcross.glsl", .glsl, true);
// try expectCodegen(pbr_basic, "pbr-basic.glsl", .glsl, false);
}
test "pixel-post-process-normal-frag" {
const pixel_post_process_normal_frag = @embedFile("test/pixel-post-process-normal-frag.wgsl");
try expectCodegen(pixel_post_process_normal_frag, "pixel-post-process-normal-frag.spv", .spirv, false);
try expectCodegen(pixel_post_process_normal_frag, "pixel-post-process-normal-frag.hlsl", .hlsl, false);
try expectCodegen(pixel_post_process_normal_frag, "pixel-post-process-normal-frag.msl", .msl, false);
try expectCodegen(pixel_post_process_normal_frag, "pixel-post-process-normal-frag-spirvcross.glsl", .glsl, true);
// try expectCodegen(pixel_post_process_normal_frag, "pixel-post-process-normal-frag.glsl", .glsl, false);
}
test "pixel-post-process-pixel-vert" {
const pixel_post_process_pixel_vert = @embedFile("test/pixel-post-process-pixel-vert.wgsl");
try expectCodegen(pixel_post_process_pixel_vert, "pixel-post-process-pixel-vert.spv", .spirv, false);
try expectCodegen(pixel_post_process_pixel_vert, "pixel-post-process-pixel-vert.hlsl", .hlsl, false);
try expectCodegen(pixel_post_process_pixel_vert, "pixel-post-process-pixel-vert.msl", .msl, false);
try expectCodegen(pixel_post_process_pixel_vert, "pixel-post-process-pixel-vert-spirvcross.glsl", .glsl, true);
// try expectCodegen(pixel_post_process_pixel_vert, "pixel-post-process-pixel-vert.glsl", .glsl, false);
}
test "pixel-post-process-pixel-frag" {
const pixel_post_process_pixel_frag = @embedFile("test/pixel-post-process-pixel-frag.wgsl");
try expectCodegen(pixel_post_process_pixel_frag, "pixel-post-process-pixel-frag.spv", .spirv, false);
try expectCodegen(pixel_post_process_pixel_frag, "pixel-post-process-pixel-frag.hlsl", .hlsl, false);
try expectCodegen(pixel_post_process_pixel_frag, "pixel-post-process-pixel-frag.msl", .msl, false);
try expectCodegen(pixel_post_process_pixel_frag, "pixel-post-process-pixel-frag-spirvcross.glsl", .glsl, true);
// try expectCodegen(pixel_post_process_pixel_frag, "pixel-post-process-pixel-frag.glsl", .glsl, false);
}
test "pixel-post-process" {
const pixel_post_process = @embedFile("test/pixel-post-process.wgsl");
try expectCodegen(pixel_post_process, "pixel-post-process.spv", .spirv, false);
try expectCodegen(pixel_post_process, "pixel-post-process.hlsl", .hlsl, false);
try expectCodegen(pixel_post_process, "pixel-post-process.msl", .msl, false);
try expectCodegen(pixel_post_process, "pixel-post-process-spirvcross.glsl", .glsl, true);
// try expectCodegen(pixel_post_process, "pixel-post-process.glsl", .glsl, false);
}
test "procedural-primitives" {
const procedural_primitives = @embedFile("test/procedural-primitives.wgsl");
try expectCodegen(procedural_primitives, "procedural-primitives.spv", .spirv, false);
try expectCodegen(procedural_primitives, "procedural-primitives.hlsl", .hlsl, false);
try expectCodegen(procedural_primitives, "procedural-primitives.msl", .msl, false);
try expectCodegen(procedural_primitives, "procedural-primitives-spirvcross.glsl", .glsl, true);
// try expectCodegen(procedural_primitives, "procedural-primitives.glsl", .glsl, false);
}
test "rotating-cube" {
const rotating_cube = @embedFile("test/rotating-cube.wgsl");
try expectCodegen(rotating_cube, "rotating-cube.spv", .spirv, false);
try expectCodegen(rotating_cube, "rotating-cube.hlsl", .hlsl, false);
try expectCodegen(rotating_cube, "rotating-cube.msl", .msl, false);
try expectCodegen(rotating_cube, "rotating-cube-spirvcross.glsl", .glsl, true);
// try expectCodegen(rotating_cube, "rotating-cube.glsl", .glsl, false);
}
test "triangle" {
const triangle = @embedFile("test/triangle.wgsl");
try expectCodegen(triangle, "triangle.spv", .spirv, false);
try expectCodegen(triangle, "triangle.hlsl", .hlsl, false);
try expectCodegen(triangle, "triangle.msl", .msl, false);
try expectCodegen(triangle, "triangle-spirvcross.glsl", .glsl, true);
// try expectCodegen(triangle, "triangle.glsl", .glsl, false);
}
test "fragmentDeferredRendering" {
const fragmentDeferredRendering = @embedFile("test/fragmentDeferredRendering.wgsl");
try expectCodegen(fragmentDeferredRendering, "fragmentDeferredRendering.spv", .spirv, false);
try expectCodegen(fragmentDeferredRendering, "fragmentDeferredRendering.hlsl", .hlsl, false);
try expectCodegen(fragmentDeferredRendering, "triangle.msl", .msl, false);
try expectCodegen(fragmentDeferredRendering, "triangle-spirvcross.glsl", .glsl, true);
// try expectCodegen(fragmentDeferredRendering, "triangle.glsl", .glsl, false);
}
test "fragmentGBuffersDebugView" {
const fragmentGBuffersDebugView = @embedFile("test/fragmentGBuffersDebugView.wgsl");
try expectCodegen(fragmentGBuffersDebugView, "fragmentGBuffersDebugView.spv", .spirv, false);
try expectCodegen(fragmentGBuffersDebugView, "fragmentGBuffersDebugView.hlsl", .hlsl, false);
try expectCodegen(fragmentGBuffersDebugView, "triangle.msl", .msl, false);
try expectCodegen(fragmentGBuffersDebugView, "triangle-spirvcross.glsl", .glsl, true);
// try expectCodegen(fragmentGBuffersDebugView, "triangle.glsl", .glsl, false);
}
test "fragmentWriteGBuffers" {
const fragmentWriteGBuffers = @embedFile("test/fragmentWriteGBuffers.wgsl");
try expectCodegen(fragmentWriteGBuffers, "fragmentWriteGBuffers.spv", .spirv, false);
try expectCodegen(fragmentWriteGBuffers, "fragmentWriteGBuffers.hlsl", .hlsl, false);
try expectCodegen(fragmentWriteGBuffers, "triangle.msl", .msl, false);
try expectCodegen(fragmentWriteGBuffers, "triangle-spirvcross.glsl", .glsl, true);
// try expectCodegen(fragmentWriteGBuffers, "triangle.glsl", .glsl, false);
}
test "lightUpdate" {
const lightUpdate = @embedFile("test/lightUpdate.wgsl");
try expectCodegen(lightUpdate, "lightUpdate.spv", .spirv, false);
try expectCodegen(lightUpdate, "lightUpdate.hlsl", .hlsl, false);
try expectCodegen(lightUpdate, "triangle.msl", .msl, false);
try expectCodegen(lightUpdate, "triangle-spirvcross.glsl", .glsl, true);
// try expectCodegen(lightUpdate, "triangle.glsl", .glsl, false);
}
test "vertexTextureQuad" {
const vertexTextureQuad = @embedFile("test/vertexTextureQuad.wgsl");
try expectCodegen(vertexTextureQuad, "vertexTextureQuad.spv", .spirv, false);
try expectCodegen(vertexTextureQuad, "vertexTextureQuad.hlsl", .hlsl, false);
try expectCodegen(vertexTextureQuad, "triangle.msl", .msl, false);
try expectCodegen(vertexTextureQuad, "triangle-spirvcross.glsl", .glsl, true);
// try expectCodegen(vertexTextureQuad, "triangle.glsl", .glsl, false);
}
test "vertexWriteGBuffers" {
const vertexWriteGBuffers = @embedFile("test/vertexWriteGBuffers.wgsl");
try expectCodegen(vertexWriteGBuffers, "vertexWriteGBuffers.spv", .spirv, false);
try expectCodegen(vertexWriteGBuffers, "vertexWriteGBuffers.hlsl", .hlsl, false);
try expectCodegen(vertexWriteGBuffers, "triangle.msl", .msl, false);
try expectCodegen(vertexWriteGBuffers, "triangle-spirvcross.glsl", .glsl, true);
// try expectCodegen(vertexWriteGBuffers, "triangle.glsl", .glsl, false);
}
fn expectCodegen(
source: [:0]const u8,
comptime file_name: []const u8,
lang: CodeGen.Language,
use_spirv_cross: bool,
) !void {
var errors = try ErrorList.init(allocator);
defer errors.deinit();
var tree = Ast.parse(allocator, &errors, source) catch |err| {
if (err == error.Parsing) {
try errors.print(source, null);
}
return err;
};
defer tree.deinit(allocator);
var ir = Air.generate(allocator, &tree, &errors, null) catch |err| {
if (err == error.AnalysisFail) {
try errors.print(source, null);
}
return err;
};
defer ir.deinit(allocator);
const out = try CodeGen.generate(allocator, &ir, lang, use_spirv_cross, .{}, null, null, null);
defer allocator.free(out);
try std.fs.cwd().makePath("zig-out/shader/");
try std.fs.cwd().writeFile("zig-out/shader/" ++ file_name, out);
}

View file

@ -0,0 +1,90 @@
struct Particle {
pos : vec2<f32>,
vel : vec2<f32>,
};
struct SimParams {
deltaT : f32,
rule1Distance : f32,
rule2Distance : f32,
rule3Distance : f32,
rule1Scale : f32,
rule2Scale : f32,
rule3Scale : f32,
};
struct Particles {
particles : array<Particle>,
};
@binding(0) @group(0) var<uniform> params : SimParams;
@binding(1) @group(0) var<storage, read> particlesA : Particles;
@binding(2) @group(0) var<storage, read_write> particlesB : Particles;
// https://github.com/austinEng/Project6-Vulkan-Flocking/blob/master/data/shaders/computeparticles/particle.comp
@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) GlobalInvocationID : vec3<u32>) {
var index : u32 = GlobalInvocationID.x;
if (index >= arrayLength(&particlesA.particles)) {
return;
}
var vPos = particlesA.particles[index].pos;
var vVel = particlesA.particles[index].vel;
var cMass = vec2<f32>(0.0, 0.0);
var cVel = vec2<f32>(0.0, 0.0);
var colVel = vec2<f32>(0.0, 0.0);
var cMassCount : u32 = 0u;
var cVelCount : u32 = 0u;
var pos : vec2<f32>;
var vel : vec2<f32>;
for (var i : u32 = 0u; i < arrayLength(&particlesA.particles); i = i + 1u) {
if (i == index) {
continue;
}
pos = particlesA.particles[i].pos.xy;
vel = particlesA.particles[i].vel.xy;
if (distance(pos, vPos) < params.rule1Distance) {
cMass = cMass + pos;
cMassCount = cMassCount + 1u;
}
if (distance(pos, vPos) < params.rule2Distance) {
colVel = colVel - (pos - vPos);
}
if (distance(pos, vPos) < params.rule3Distance) {
cVel = cVel + vel;
cVelCount = cVelCount + 1u;
}
}
if (cMassCount > 0u) {
var temp = f32(cMassCount);
cMass = (cMass / vec2<f32>(temp, temp)) - vPos;
}
if (cVelCount > 0u) {
var temp = f32(cVelCount);
cVel = cVel / vec2<f32>(temp, temp);
}
vVel = vVel + (cMass * params.rule1Scale) + (colVel * params.rule2Scale) +
(cVel * params.rule3Scale);
// clamp velocity for a more pleasing simulation
vVel = normalize(vVel) * clamp(length(vVel), 0.0, 0.1);
// kinematic update
vPos = vPos + (vVel * params.deltaT);
// Wrap around boundary
if (vPos.x < -1.0) {
vPos.x = 1.0;
}
if (vPos.x > 1.0) {
vPos.x = -1.0;
}
if (vPos.y < -1.0) {
vPos.y = 1.0;
}
if (vPos.y > 1.0) {
vPos.y = -1.0;
}
// Write back
particlesB.particles[index].pos = vPos;
particlesB.particles[index].vel = vVel;
}

View file

@ -0,0 +1,15 @@
@vertex
fn vert_main(@location(0) a_particlePos : vec2<f32>,
@location(1) a_particleVel : vec2<f32>,
@location(2) a_pos : vec2<f32>) -> @builtin(position) vec4<f32> {
let angle = -atan2(a_particleVel.x, a_particleVel.y);
let pos = vec2<f32>(
(a_pos.x * cos(angle)) - (a_pos.y * sin(angle)),
(a_pos.x * sin(angle)) + (a_pos.y * cos(angle)));
return vec4<f32>(pos + a_particlePos, 0.0, 1.0);
}
@fragment
fn frag_main() -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 1.0, 1.0, 1.0);
}

View file

@ -0,0 +1,27 @@
@group(0) @binding(0) var<storage> _runtime_array : array<u32>;
@fragment
fn main() {
// TODO: Add all builtins
let _array_length = arrayLength(&_runtime_array);
let _sin = sin(1.0);
let _cos = cos(1.0);
let _normalize = normalize(vec3(1.0));
let _length = length(1.0);
let _floor = floor(1.0);
let _abs = abs(1.0);
let _all = all(vec3(true));
let _dpdx = dpdx(1.0);
let _dpdy = dpdy(1.0);
let _fwidth = fwidth(1.0);
let _min = min(1.0, 1.0);
let _max = max(1.0, 1.0);
let _atan2 = atan2(1.0, 1.0);
let _distance = distance(1.0, 1.0);
let _dot = dot(vec3(1.0), vec3(1.0));
let _pow = pow(1.0, 1.0);
let _step = step(1.0, 1.0);
let _mix = mix(1.0, 1.0, 1.0);
let _clamp = clamp(1.0, 1.0, 1.0);
let _smoothstep = smoothstep(1.0, 1.0, 1.0);
}

View file

@ -0,0 +1,34 @@
struct Uniforms {
modelViewProjectionMatrix : mat4x4<f32>,
}
@binding(0) @group(0) var<uniform> uniforms : Uniforms;
struct VertexOutput {
@builtin(position) Position : vec4<f32>,
@location(0) fragUV : vec2<f32>,
@location(1) fragPosition: vec4<f32>,
}
@vertex
fn vertex_main(
@location(0) position : vec4<f32>,
@location(1) uv : vec2<f32>
) -> VertexOutput {
var output : VertexOutput;
output.Position = uniforms.modelViewProjectionMatrix * position;
output.fragUV = uv;
output.fragPosition = 0.5 * (position + vec4<f32>(1.0, 1.0, 1.0, 1.0));
return output;
}
@group(0) @binding(1) var mySampler: sampler;
@group(0) @binding(2) var myTexture: texture_cube<f32>;
@fragment
fn frag_main(
@location(0) fragUV: vec2<f32>,
@location(1) fragPosition: vec4<f32>
) -> @location(0) vec4<f32> {
var cubemapVec = fragPosition.xyz - vec3<f32>(0.5, 0.5, 0.5);
return textureSample(myTexture, mySampler, cubemapVec);
}

View file

@ -0,0 +1,35 @@
struct Uniforms {
matrix : mat4x4<f32>,
};
@binding(0) @group(0) var<uniform> ubo : Uniforms;
struct VertexOut {
@builtin(position) Position : vec4<f32>,
@location(0) fragUV : vec2<f32>,
@location(1) fragPosition: vec4<f32>,
}
@vertex fn vertex_main(
@location(0) position : vec4<f32>,
@location(1) uv: vec2<f32>
) -> VertexOut {
var output : VertexOut;
output.Position = position * ubo.matrix;
output.fragUV = uv;
output.fragPosition = 0.5 * (position + vec4<f32>(1.0, 1.0, 1.0, 1.0));
return output;
}
@binding(1) @group(0) var mySampler: sampler;
@binding(2) @group(0) var myTexture: texture_2d<f32>;
@fragment fn frag_main(
@location(0) fragUV: vec2<f32>,
@location(1) fragPosition: vec4<f32>
) -> @location(0) vec4<f32> {
let texColor = textureSample(myTexture, mySampler, fragUV * 0.8 + vec2<f32>(0.1, 0.1));
let f = f32(length(texColor.rgb - vec3<f32>(0.5, 0.5, 0.5)) < 0.01);
return (1.0 - f) * texColor + f * fragPosition;
// return vec4<f32>(texColor.rgb,1.0);
}

View file

@ -0,0 +1,83 @@
@group(0) @binding(0) var gBufferNormal: texture_2d<f32>;
@group(0) @binding(1) var gBufferAlbedo: texture_2d<f32>;
@group(0) @binding(2) var gBufferDepth: texture_depth_2d;
struct LightData {
position : vec4<f32>,
color : vec3<f32>,
radius : f32,
}
struct LightsBuffer {
lights: array<LightData>,
}
@group(1) @binding(0) var<storage, read> lightsBuffer: LightsBuffer;
struct Config {
numLights : u32,
}
struct Camera {
viewProjectionMatrix : mat4x4<f32>,
invViewProjectionMatrix : mat4x4<f32>,
}
@group(1) @binding(1) var<uniform> config: Config;
@group(1) @binding(2) var<uniform> camera: Camera;
fn world_from_screen_coord(coord : vec2<f32>, depth_sample: f32) -> vec3<f32> {
// reconstruct world-space position from the screen coordinate.
let posClip = vec4(coord.x * 2.0 - 1.0, (1.0 - coord.y) * 2.0 - 1.0, depth_sample, 1.0);
let posWorldW = camera.invViewProjectionMatrix * posClip;
let posWorld = posWorldW.xyz / posWorldW.www;
return posWorld;
}
@fragment
fn main(
@builtin(position) coord : vec4<f32>
) -> @location(0) vec4<f32> {
var result : vec3<f32>;
let depth = textureLoad(
gBufferDepth,
vec2<i32>(floor(coord.xy)),
0
);
// Don't light the sky.
if (depth >= 1.0) {
discard;
}
let bufferSize = textureDimensions(gBufferDepth);
let coordUV = coord.xy / vec2<f32>(bufferSize);
let position = world_from_screen_coord(coordUV, depth);
let normal = textureLoad(
gBufferNormal,
vec2<i32>(floor(coord.xy)),
0
).xyz;
let albedo = textureLoad(
gBufferAlbedo,
vec2<i32>(floor(coord.xy)),
0
).rgb;
for (var i = 0u; i < config.numLights; i++) {
let L = lightsBuffer.lights[i].position.xyz - position;
let distance = length(L);
if (distance > lightsBuffer.lights[i].radius) {
continue;
}
let lambert = max(dot(normal, normalize(L)), 0.0);
result += vec3<f32>(
lambert * pow(1.0 - distance / lightsBuffer.lights[i].radius, 2.0) * lightsBuffer.lights[i].color * albedo
);
}
// some manual ambient
result += vec3(0.2);
return vec4(result, 1.0);
}

View file

@ -0,0 +1,44 @@
@group(0) @binding(0) var gBufferNormal: texture_2d<f32>;
@group(0) @binding(1) var gBufferAlbedo: texture_2d<f32>;
@group(0) @binding(2) var gBufferDepth: texture_depth_2d;
@group(1) @binding(0) var<uniform> canvas : CanvasConstants;
struct CanvasConstants {
size: vec2<f32>,
}
@fragment
fn main(
@builtin(position) coord : vec4<f32>
) -> @location(0) vec4<f32> {
var result : vec4<f32>;
let c = coord.xy / vec2<f32>(canvas.size.x, canvas.size.y);
if (c.x < 0.33333) {
let rawDepth = textureLoad(
gBufferDepth,
vec2<i32>(floor(coord.xy)),
0
);
// remap depth into something a bit more visible
let depth = (1.0 - rawDepth) * 50.0;
result = vec4(depth);
} else if (c.x < 0.66667) {
result = textureLoad(
gBufferNormal,
vec2<i32>(floor(coord.xy)),
0
);
result.x = (result.x + 1.0) * 0.5;
result.y = (result.y + 1.0) * 0.5;
result.z = (result.z + 1.0) * 0.5;
} else {
result = textureLoad(
gBufferAlbedo,
vec2<i32>(floor(coord.xy)),
0
);
}
return result;
}

View file

@ -0,0 +1,22 @@
struct GBufferOutput {
@location(0) normal : vec4<f32>,
// Textures: diffuse color, specular color, smoothness, emissive etc. could go here
@location(1) albedo : vec4<f32>,
}
@fragment
fn main(
@location(0) fragNormal: vec3<f32>,
@location(1) fragUV : vec2<f32>
) -> GBufferOutput {
// faking some kind of checkerboard texture
let uv = floor(30.0 * fragUV);
let c = 0.2 + 0.5 * ((uv.x + uv.y) - 2.0 * floor((uv.x + uv.y) / 2.0));
var output : GBufferOutput;
output.normal = vec4(fragNormal, 1.0);
output.albedo = vec4(c, c, c, 1.0);
return output;
}

View file

@ -0,0 +1,38 @@
@group(0) @binding(0) var mySampler : sampler;
@group(0) @binding(1) var myTexture : texture_2d<f32>;
struct VertexOutput {
@builtin(position) Position : vec4<f32>,
@location(0) fragUV : vec2<f32>,
}
@vertex
fn vert_main(@builtin(vertex_index) VertexIndex : u32) -> VertexOutput {
var pos = array<vec2<f32>, 6>(
vec2<f32>( 1.0, 1.0),
vec2<f32>( 1.0, -1.0),
vec2<f32>(-1.0, -1.0),
vec2<f32>( 1.0, 1.0),
vec2<f32>(-1.0, -1.0),
vec2<f32>(-1.0, 1.0)
);
var uv = array<vec2<f32>, 6>(
vec2<f32>(1.0, 0.0),
vec2<f32>(1.0, 1.0),
vec2<f32>(0.0, 1.0),
vec2<f32>(1.0, 0.0),
vec2<f32>(0.0, 1.0),
vec2<f32>(0.0, 0.0)
);
var output : VertexOutput;
output.Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
output.fragUV = uv[VertexIndex];
return output;
}
@fragment
fn frag_main(@location(0) fragUV : vec2<f32>) -> @location(0) vec4<f32> {
return textureSample(myTexture, mySampler, fragUV);
}

View file

@ -0,0 +1,75 @@
struct CameraUniform {
pos: vec4<f32>,
view_proj: mat4x4<f32>,
};
struct InstanceInput {
@location(3) model_matrix_0: vec4<f32>,
@location(4) model_matrix_1: vec4<f32>,
@location(5) model_matrix_2: vec4<f32>,
@location(6) model_matrix_3: vec4<f32>,
};
struct VertexInput {
@location(0) position: vec3<f32>,
@location(1) normal: vec3<f32>,
@location(2) tex_coords: vec2<f32>,
};
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,
@location(0) tex_coords: vec2<f32>,
@location(1) normal: vec3<f32>,
@location(2) position: vec3<f32>,
};
struct Light {
position: vec4<f32>,
color: vec4<f32>,
};
@group(0) @binding(0) var<uniform> camera: CameraUniform;
@group(1) @binding(0) var t_diffuse: texture_2d<f32>;
@group(1) @binding(1) var s_diffuse: sampler;
@group(2) @binding(0) var<uniform> light: Light;
@vertex
fn vs_main(model: VertexInput, instance: InstanceInput) -> VertexOutput {
let model_matrix = mat4x4<f32>(
instance.model_matrix_0,
instance.model_matrix_1,
instance.model_matrix_2,
instance.model_matrix_3,
);
var out: VertexOutput;
let world_pos = model_matrix * vec4<f32>(model.position, 1.0);
out.position = world_pos.xyz;
out.normal = (model_matrix * vec4<f32>(model.normal, 0.0)).xyz;
out.clip_position = camera.view_proj * world_pos;
out.tex_coords = model.tex_coords;
return out;
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
let object_color = textureSample(t_diffuse, s_diffuse, in.tex_coords);
let ambient = 0.1;
let ambient_color = light.color.rbg * ambient;
let light_dir = normalize(light.position.xyz - in.position);
let diffuse = max(dot(in.normal, light_dir), 0.0);
let diffuse_color = light.color.rgb * diffuse;
let view_dir = normalize(camera.pos.xyz - in.position);
let half_dir = normalize(view_dir + light_dir);
let specular = pow(max(dot(in.normal, half_dir), 0.0), 32.0);
let specular_color = light.color.rbg * specular;
let all = ambient_color + diffuse_color + specular_color;
let result = all * object_color.rgb;
return vec4<f32>(result, object_color.a);
}

View file

@ -0,0 +1,35 @@
struct CameraUniform {
view_pos: vec4<f32>,
view_proj: mat4x4<f32>,
};
struct VertexInput {
@location(0) position: vec3<f32>,
@location(1) normal: vec3<f32>,
@location(2) tex_coords: vec2<f32>,
};
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,
};
struct Light {
position: vec4<f32>,
color: vec4<f32>,
};
@group(0) @binding(0) var<uniform> camera: CameraUniform;
@group(1) @binding(0) var<uniform> light: Light;
@vertex
fn vs_main(model: VertexInput) -> VertexOutput {
var out: VertexOutput;
let world_pos = vec4<f32>(model.position + light.position.xyz, 1.0);
out.clip_position = camera.view_proj * world_pos;
return out;
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 1.0, 1.0, 0.5);
}

View file

@ -0,0 +1,11 @@
@fragment fn fs_main() -> @location(0) vec4<f32> {
var dummy = false;
if dummy {
let dummy_var_1 = 0.0;
return vec4<f32>(dummy_var_1, 1, 1, 1);
} else if !dummy {
let dummy_var_2 = 0.0;
return vec4<f32>(dummy_var_2, 1, 1, 1);
}
return vec4<f32>(0.0, 1, 1, 1);
}

View file

@ -0,0 +1,89 @@
struct Params {
filterDim : i32,
blockDim : u32,
}
@group(0) @binding(0) var samp : sampler;
@group(0) @binding(1) var<uniform> params : Params;
@group(1) @binding(1) var inputTex : texture_2d<f32>;
@group(1) @binding(2) var outputTex : texture_storage_2d<rgba8unorm, write>;
struct Flip {
value : u32,
}
@group(1) @binding(3) var<uniform> flip : Flip;
// This shader blurs the input texture in one direction, depending on whether
// |flip.value| is 0 or 1.
// It does so by running (128 / 4) threads per workgroup to load 128
// texels into 4 rows of shared memory. Each thread loads a
// 4 x 4 block of texels to take advantage of the texture sampling
// hardware.
// Then, each thread computes the blur result by averaging the adjacent texel values
// in shared memory.
// Because we're operating on a subset of the texture, we cannot compute all of the
// results since not all of the neighbors are available in shared memory.
// Specifically, with 128 x 128 tiles, we can only compute and write out
// square blocks of size 128 - (filterSize - 1). We compute the number of blocks
// needed in Javascript and dispatch that amount.
var<workgroup> tile : array<array<vec3<f32>, 128>, 4>;
@compute @workgroup_size(32, 1, 1)
fn main(
@builtin(workgroup_id) WorkGroupID : vec3<u32>,
@builtin(local_invocation_id) LocalInvocationID : vec3<u32>,
@builtin(local_invocation_index) LocalInvocationIndex : u32
) {
for (var idx = LocalInvocationIndex; idx < 512u; idx+=32)
{
tile[idx / 128u][idx % 128u] = vec3(0.0);
}
workgroupBarrier();
// TODO - mixed vector arithmetic (vec2<u32> and vec2<i32>)
let filterOffset = (params.filterDim - 1) / 2;
let dims = textureDimensions(inputTex, 0);
let baseIndex = vec2<u32>(WorkGroupID.xy * vec2(params.blockDim, 4) +
LocalInvocationID.xy * vec2<u32>(4, 1))
- vec2<u32>(u32(filterOffset), 0);
for (var r: u32 = 0; r < 4; r++) {
for (var c: u32 = 0; c < 4; c++) {
var loadIndex = baseIndex + vec2<u32>(c, r);
if (flip.value != 0u) {
loadIndex = loadIndex.yx;
}
tile[r][4 * LocalInvocationID.x + c] = textureSampleLevel(
inputTex,
samp,
(vec2<f32>(loadIndex) + vec2<f32>(0.25, 0.25)) / vec2<f32>(dims),
0.0
).rgb;
}
}
workgroupBarrier();
for (var r: u32 = 0; r < 4; r++) {
for (var c: u32 = 0; c < 4; c++) {
var writeIndex = baseIndex + vec2<u32>(c, r);
if (flip.value != 0) {
writeIndex = writeIndex.yx;
}
let center = u32(4 * LocalInvocationID.x) + c;
if (center >= u32(filterOffset) &&
center < 128 - u32(filterOffset) &&
all(writeIndex < dims)) {
var acc = vec3(0.0, 0.0, 0.0);
for (var f = 0; f < params.filterDim; f++) {
var i = i32(center) + f - filterOffset;
acc = acc + (1.0 / f32(params.filterDim)) * tile[r][i];
}
textureStore(outputTex, writeIndex, vec4(acc, 1.0));
}
}
}
}

View file

@ -0,0 +1,25 @@
@binding(0) @group(0) var<uniform> ubos : array<mat4x4<f32>, 16>;
struct VertexOutput {
@builtin(position) position_clip : vec4<f32>,
@location(0) fragUV : vec2<f32>,
@location(1) fragPosition: vec4<f32>,
};
@vertex
fn vertex_main(@builtin(instance_index) instanceIdx : u32,
@location(0) position : vec4<f32>,
@location(1) uv : vec2<f32>) -> VertexOutput {
var output : VertexOutput;
output.position_clip = ubos[instanceIdx] * position;
output.fragUV = uv;
output.fragPosition = 0.5 * (position + vec4<f32>(1.0, 1.0, 1.0, 1.0));
return output;
}
@fragment fn frag_main(
@location(0) fragUV: vec2<f32>,
@location(1) fragPosition: vec4<f32>
) -> @location(0) vec4<f32> {
return fragPosition;
}

View file

@ -0,0 +1,34 @@
struct LightData {
position : vec4<f32>,
color : vec3<f32>,
radius : f32,
}
struct LightsBuffer {
lights: array<LightData>,
}
@group(0) @binding(0) var<storage, read_write> lightsBuffer: LightsBuffer;
struct Config {
numLights : u32,
}
@group(0) @binding(1) var<uniform> config: Config;
struct LightExtent {
min : vec4<f32>,
max : vec4<f32>,
}
@group(0) @binding(2) var<uniform> lightExtent: LightExtent;
@compute @workgroup_size(64, 1, 1)
fn main(@builtin(global_invocation_id) GlobalInvocationID : vec3<u32>) {
var index = GlobalInvocationID.x;
if (index >= config.numLights) {
return;
}
lightsBuffer.lights[index].position.y = lightsBuffer.lights[index].position.y - 0.5 - 0.003 * (f32(index) - 64.0 * floor(f32(index) / 64.0));
if (lightsBuffer.lights[index].position.y < lightExtent.min.y) {
lightsBuffer.lights[index].position.y = lightExtent.max.y;
}
}

View file

@ -0,0 +1,16 @@
@group(0) @binding(0) var<storage, read_write> output: array<f32>;
@compute @workgroup_size(64, 1, 1)
fn main(
@builtin(global_invocation_id)
global_id : vec3<u32>,
@builtin(local_invocation_id)
local_id : vec3<u32>,
) {
if (global_id.x >= arrayLength(&output)) {
return;
}
output[global_id.x] =
f32(global_id.x) * 1000. + f32(local_id.x);
}

View file

@ -0,0 +1,119 @@
@group(0) @binding(0) var<uniform> ubo : UBO;
@group(0) @binding(1) var<uniform> uboParams : UBOShared;
@group(0) @binding(2) var<uniform> material : MaterialParams;
@group(0) @binding(3) var<uniform> object : ObjectParams;
struct VertexOut {
@builtin(position) position_clip : vec4<f32>,
@location(0) fragPosition : vec3<f32>,
@location(1) fragNormal : vec3<f32>,
}
struct MaterialParams {
roughness : f32,
metallic : f32,
r : f32,
g : f32,
b : f32
}
struct UBOShared {
lights : array<vec4<f32>, 4>,
}
struct UBO {
projection : mat4x4<f32>,
model : mat4x4<f32>,
view : mat4x4<f32>,
camPos : vec3<f32>,
}
struct ObjectParams {
position : vec3<f32>
}
@vertex fn vertex_main(
@location(0) position : vec3<f32>,
@location(1) normal : vec3<f32>
) -> VertexOut {
var output : VertexOut;
var locPos = vec4<f32>(ubo.model * vec4<f32>(position, 1.0));
output.fragPosition = locPos.xyz + object.position;
output.fragNormal = mat3x3<f32>(ubo.model[0].xyz, ubo.model[1].xyz, ubo.model[2].xyz) * normal;
output.position_clip = ubo.projection * ubo.view * vec4<f32>(output.fragPosition, 1.0);
return output;
}
const PI : f32 = 3.14159265359;
fn material_color() -> vec3<f32> {
return vec3<f32>(material.r, material.g, material.b);
}
// Normal Distribution function --------------------------------------
fn D_GGX(dotNH : f32, roughness : f32) -> f32 {
var alpha : f32 = roughness * roughness;
var alpha2 : f32 = alpha * alpha;
var denom : f32 = dotNH * dotNH * (alpha2 - 1.0) + 1.0;
return alpha2 / (PI * denom * denom);
}
// Geometric Shadowing function --------------------------------------
fn G_SchlicksmithGGX(dotNL : f32, dotNV : f32, roughness : f32) -> f32 {
var r : f32 = roughness + 1.0;
var k : f32 = (r * r) / 8.0;
var GL : f32 = dotNL / (dotNL * (1.0 - k) + k);
var GV : f32 = dotNV / (dotNV * (1.0 - k) + k);
return GL * GV;
}
// Fresnel function ----------------------------------------------------
fn F_Schlick(cosTheta : f32, metallic : f32) -> vec3<f32> {
var F0 : vec3<f32> = mix(vec3<f32>(0.04), material_color(), metallic);
var F : vec3<f32> = F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
return F;
}
// Specular BRDF composition --------------------------------------------
fn BRDF(L : vec3<f32>, V : vec3<f32>, N : vec3<f32>, metallic : f32, roughness : f32) -> vec3<f32> {
var H : vec3<f32> = normalize(V + L);
var dotNV : f32 = clamp(dot(N, V), 0.0, 1.0);
var dotNL : f32 = clamp(dot(N, L), 0.0, 1.0);
var dotLH : f32 = clamp(dot(L, H), 0.0, 1.0);
var dotNH : f32 = clamp(dot(N, H), 0.0, 1.0);
var lightColor = vec3<f32>(1.0);
var color = vec3<f32>(0.0);
if(dotNL > 0.0) {
var rroughness : f32 = max(0.05, roughness);
// D = Normal distribution (Distribution of the microfacets)
var D : f32 = D_GGX(dotNH, roughness);
// G = Geometric shadowing term (Microfacets shadowing)
var G : f32 = G_SchlicksmithGGX(dotNL, dotNV, roughness);
// F = Fresnel factor (Reflectance depending on angle of incidence)
var F : vec3<f32> = F_Schlick(dotNV, metallic);
var spec : vec3<f32> = (D * F * G) / (4.0 * dotNL * dotNV);
color += spec * dotNL * lightColor;
}
return color;
}
// TODO - global variable declaration order
@fragment fn frag_main(
@location(0) position : vec3<f32>,
@location(1) normal: vec3<f32>
) -> @location(0) vec4<f32> {
var N : vec3<f32> = normalize(normal);
var V : vec3<f32> = normalize(ubo.camPos - position);
var Lo = vec3<f32>(0.0);
// Specular contribution
for(var i: i32 = 0; i < 4; i++) {
var L : vec3<f32> = normalize(uboParams.lights[i].xyz - position);
Lo += BRDF(L, V, N, material.metallic, material.roughness);
}
// Combine with ambient
var color : vec3<f32> = material_color() * 0.02;
color += Lo;
// Gamma correct
color = pow(color, vec3<f32>(0.4545));
return vec4<f32>(color, 1.0);
}

View file

@ -0,0 +1,6 @@
@fragment fn main(
@location(0) normal: vec3<f32>,
@location(1) uv: vec2<f32>,
) -> @location(0) vec4<f32> {
return vec4<f32>(normal / 2 + 0.5, 1.0);
}

View file

@ -0,0 +1,77 @@
@group(0) @binding(0)
var draw_texture: texture_2d<f32>;
@group(0) @binding(1)
var draw_texture_sampler: sampler;
@group(0) @binding(2)
var depth_texture: texture_depth_2d;
@group(0) @binding(3)
var depth_texture_sampler: sampler;
@group(0) @binding(4)
var normal_texture: texture_2d<f32>;
@group(0) @binding(5)
var normal_texture_sampler: sampler;
struct View {
@location(0) width: u32,
@location(1) height: u32,
@location(2) pixel_size: u32,
}
@group(0) @binding(6)
var<uniform> view: View;
fn sample_depth(uv: vec2<f32>, x: f32, y: f32) -> f32 {
return textureSample(
depth_texture,
depth_texture_sampler,
uv + vec2<f32>(x * f32(view.pixel_size) / f32(view.width), y * f32(view.pixel_size) / f32(view.height))
);
}
fn sample_normal(uv: vec2<f32>, x: f32, y: f32) -> vec3<f32> {
return textureSample(
normal_texture,
normal_texture_sampler,
uv + vec2<f32>(x * f32(view.pixel_size) / f32(view.width), y * f32(view.pixel_size) / f32(view.height))
).xyz;
}
fn normal_indicator(uv: vec2<f32>, x: f32, y: f32) -> f32 {
// TODO - integer promotion to float argument
var depth_diff = sample_depth(uv, 0.0, 0.0) - sample_depth(uv, x, y);
var dx = sample_normal(uv, 0.0, 0.0);
var dy = sample_normal(uv, x, y);
if (depth_diff > 0) {
// only sample normals from closest pixel
return 0;
}
return distance(dx, dy);
}
@fragment fn main(
// TODO - vertex/fragment linkage
@location(0) uv: vec2<f32>,
@builtin(position) position: vec4<f32>
) -> @location(0) vec4<f32> {
// TODO - integer promotion to float argument
var depth = sample_depth(uv, 0.0, 0.0);
var depth_diff: f32 = 0;
depth_diff += abs(depth - sample_depth(uv, -1.0, 0.0));
depth_diff += abs(depth - sample_depth(uv, 1.0, 0.0));
depth_diff += abs(depth - sample_depth(uv, 0.0, -1.0));
depth_diff += abs(depth - sample_depth(uv, 0.0, 1.0));
var normal_diff: f32 = 0;
normal_diff += normal_indicator(uv, -1.0, 0.0);
normal_diff += normal_indicator(uv, 1.0, 0.0);
normal_diff += normal_indicator(uv, 0.0, -1.0);
normal_diff += normal_indicator(uv, 0.0, 1.0);
var color = textureSample(draw_texture, draw_texture_sampler, uv);
if (depth_diff > 0.007) { // magic number from testing
return color * 0.7;
}
// add instead of multiply so really dark pixels get brighter
return color + (vec4<f32>(1) * step(0.1, normal_diff) * 0.7);
}

View file

@ -0,0 +1,14 @@
struct VertexOut {
@builtin(position) position_clip: vec4<f32>,
@location(0) uv: vec2<f32>
}
@vertex fn main(
@location(0) position: vec3<f32>,
@location(1) uv: vec2<f32>
) -> VertexOut {
var output : VertexOut;
output.position_clip = vec4<f32>(position.xy, 0.0, 1.0);
output.uv = uv;
return output;
}

View file

@ -0,0 +1,27 @@
@group(0) @binding(0) var<uniform> ubo: mat4x4<f32>;
struct VertexOut {
@builtin(position) position_clip: vec4<f32>,
@location(0) normal: vec3<f32>,
@location(1) uv: vec2<f32>,
}
@vertex fn vertex_main(
@location(0) position: vec3<f32>,
@location(1) normal: vec3<f32>,
@location(2) uv: vec2<f32>
) -> VertexOut {
var output: VertexOut;
output.position_clip = vec4<f32>(position, 1) * ubo;
output.normal = (vec4<f32>(normal, 0) * ubo).xyz;
output.uv = uv;
return output;
}
@fragment fn frag_main(
@location(0) normal: vec3<f32>,
@location(1) uv: vec2<f32>,
) -> @location(0) vec4<f32> {
var color = floor((uv * 0.5 + 0.25) * 32) / 32;
return vec4<f32>(color, 1, 1);
}

View file

@ -0,0 +1,32 @@
struct Uniforms {
mvp_matrix : mat4x4<f32>,
};
@binding(0) @group(0) var<uniform> ubo : Uniforms;
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) normal: vec3<f32>,
};
@vertex fn vertex_main(
// TODO - struct input
@location(0) position: vec3<f32>,
@location(1) normal: vec3<f32>,
) -> VertexOutput {
var out: VertexOutput;
out.position = vec4<f32>(position, 1.0) * ubo.mvp_matrix;
out.normal = normal;
return out;
}
struct FragmentOutput {
@location(0) pixel_color: vec4<f32>
};
@fragment fn frag_main(in: VertexOutput) -> FragmentOutput {
var out : FragmentOutput;
out.pixel_color = vec4<f32>((in.normal + 1) / 2, 1.0);
return out;
}

View file

@ -0,0 +1,24 @@
@group(0) @binding(0) var<uniform> ubo : mat4x4<f32>;
struct VertexOut {
@builtin(position) position_clip : vec4<f32>,
@location(0) fragUV : vec2<f32>,
@location(1) fragPosition: vec4<f32>,
}
@vertex fn vertex_main(
@location(0) position : vec4<f32>,
@location(1) uv: vec2<f32>
) -> VertexOut {
var output : VertexOut;
output.position_clip = position * ubo;
output.fragUV = uv;
output.fragPosition = 0.5 * (position + vec4<f32>(1.0, 1.0, 1.0, 1.0));
return output;
}
@fragment fn frag_main(
@location(0) fragUV: vec2<f32>,
@location(1) fragPosition: vec4<f32>
) -> @location(0) vec4<f32> {
return fragPosition;
}

View file

@ -0,0 +1,82 @@
struct Uniforms {
modelViewProjectionMatrix : mat4x4<f32>,
};
@binding(0) @group(0) var<uniform> uniforms : Uniforms;
struct VertexOutput {
@builtin(position) Position : vec4<f32>,
@location(0) fragUV : vec2<f32>,
@location(1) spriteIndex : f32,
};
struct Sprite {
pos: vec2<f32>,
size: vec2<f32>,
world_pos: vec2<f32>,
sheet_size: vec2<f32>,
};
@binding(3) @group(0) var<storage, read> sprites: array<Sprite>;
@vertex
fn vertex_main(
@builtin(vertex_index) VertexIndex : u32
) -> VertexOutput {
var sprite = sprites[VertexIndex / 6];
// Calculate the vertex position
var positions = array<vec2<f32>, 6>(
vec2<f32>(0.0, 0.0), // bottom-left
vec2<f32>(0.0, 1.0), // top-left
vec2<f32>(1.0, 0.0), // bottom-right
vec2<f32>(1.0, 0.0), // bottom-right
vec2<f32>(0.0, 1.0), // top-left
vec2<f32>(1.0, 1.0), // top-right
);
var pos = positions[VertexIndex % 6];
pos.x *= sprite.size.x;
pos.y *= sprite.size.y;
pos.x += sprite.world_pos.x;
pos.y += sprite.world_pos.y;
// Calculate the UV coordinate
var uvs = array<vec2<f32>, 6>(
vec2<f32>(0.0, 1.0), // bottom-left
vec2<f32>(0.0, 0.0), // top-left
vec2<f32>(1.0, 1.0), // bottom-right
vec2<f32>(1.0, 1.0), // bottom-right
vec2<f32>(0.0, 0.0), // top-left
vec2<f32>(1.0, 0.0), // top-right
);
var uv = uvs[VertexIndex % 6];
uv.x *= sprite.size.x / sprite.sheet_size.x;
uv.y *= sprite.size.y / sprite.sheet_size.y;
uv.x += sprite.pos.x / sprite.sheet_size.x;
uv.y += sprite.pos.y / sprite.sheet_size.y;
var output : VertexOutput;
output.Position = vec4<f32>(pos.x, 0.0, pos.y, 1.0) * uniforms.modelViewProjectionMatrix;
output.fragUV = uv;
output.spriteIndex = f32(VertexIndex / 6);
return output;
}
@group(0) @binding(1) var spriteSampler: sampler;
@group(0) @binding(2) var spriteTexture: texture_2d<f32>;
@fragment
fn frag_main(
@location(0) fragUV: vec2<f32>,
@location(1) spriteIndex: f32
) -> @location(0) vec4<f32> {
var color = textureSample(spriteTexture, spriteSampler, fragUV);
if (spriteIndex == 0.0) {
if (color[3] > 0.0) {
color[0] = 0.3;
color[1] = 0.2;
color[2] = 0.5;
color[3] = 1.0;
}
}
return color;
}

View file

@ -0,0 +1,29 @@
struct Uniforms {
modelViewProjectionMatrix : mat4x4<f32>,
};
@binding(0) @group(0) var<uniform> uniforms : Uniforms;
struct VertexOutput {
@builtin(position) Position : vec4<f32>,
@location(0) fragUV : vec2<f32>,
@location(1) fragPosition: vec4<f32>,
};
@vertex
fn vertex_main(@location(0) position : vec4<f32>,
@location(1) uv : vec2<f32>) -> VertexOutput {
var output : VertexOutput;
output.Position = position * uniforms.modelViewProjectionMatrix;
output.fragUV = uv;
output.fragPosition = 0.5 * (position + vec4<f32>(1.0, 1.0, 1.0, 1.0));
return output;
}
@group(0) @binding(1) var mySampler: sampler;
@group(0) @binding(2) var myTexture: texture_2d<f32>;
@fragment
fn frag_main(@location(0) fragUV: vec2<f32>,
@location(1) fragPosition: vec4<f32>) -> @location(0) vec4<f32> {
return textureSample(myTexture, mySampler, fragUV);
}

View file

@ -0,0 +1,14 @@
@vertex fn vertex_main(
@builtin(vertex_index) VertexIndex : u32
) -> @builtin(position) vec4<f32> {
var pos = array<vec2<f32>, 3>(
vec2<f32>( 0.0, 0.5),
vec2<f32>(-0.5, -0.5),
vec2<f32>( 0.5, -0.5)
);
return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
}
@fragment fn frag_main() -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
}

View file

@ -0,0 +1,24 @@
@group(0) @binding(0) var<uniform> ubo : mat4x4<f32>;
struct VertexOut {
@builtin(position) position_clip : vec4<f32>,
@location(0) fragUV : vec2<f32>,
@location(1) fragPosition: vec4<f32>,
}
@vertex fn vertex_main(
@location(0) position : vec4<f32>,
@location(1) uv: vec2<f32>
) -> VertexOut {
var output : VertexOut;
output.position_clip = position * ubo;
output.fragUV = uv;
output.fragPosition = 0.5 * (position + vec4<f32>(1.0, 1.0, 1.0, 1.0));
return output;
}
@fragment fn frag_main(
@location(0) fragUV: vec2<f32>,
@location(1) fragPosition: vec4<f32>
) -> @location(0) vec4<f32> {
return fragPosition;
}

View file

@ -0,0 +1,11 @@
@vertex
fn main(
@builtin(vertex_index) VertexIndex : u32
) -> @builtin(position) vec4<f32> {
const pos = array(
vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0),
vec2(-1.0, 1.0), vec2(1.0, -1.0), vec2(1.0, 1.0),
);
return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
}

View file

@ -0,0 +1,30 @@
struct Uniforms {
modelMatrix : mat4x4<f32>,
normalModelMatrix : mat4x4<f32>,
}
struct Camera {
viewProjectionMatrix : mat4x4<f32>,
invViewProjectionMatrix : mat4x4<f32>,
}
@group(0) @binding(0) var<uniform> uniforms : Uniforms;
@group(0) @binding(1) var<uniform> camera : Camera;
struct VertexOutput {
@builtin(position) Position : vec4<f32>,
@location(0) fragNormal: vec3<f32>, // normal in world space
@location(1) fragUV: vec2<f32>,
}
@vertex
fn main(
@location(0) position : vec3<f32>,
@location(1) normal : vec3<f32>,
@location(2) uv : vec2<f32>
) -> VertexOutput {
var output : VertexOutput;
let worldPosition = (uniforms.modelMatrix * vec4(position, 1.0)).xyz;
output.Position = camera.viewProjectionMatrix * vec4(worldPosition, 1.0);
output.fragNormal = normalize((uniforms.normalModelMatrix * vec4(normal, 1.0)).xyz);
output.fragUV = uv;
return output;
}

View file

@ -0,0 +1,3 @@
pub const Extensions = struct {
f16: bool = false,
};

View file

@ -0,0 +1,122 @@
const std = @import("std");
const testing = std.testing;
const dawn = @import("dawn.zig");
const Bool32 = @import("main.zig").Bool32;
const ChainedStructOut = @import("main.zig").ChainedStructOut;
const Device = @import("device.zig").Device;
const Instance = @import("instance.zig").Instance;
const FeatureName = @import("main.zig").FeatureName;
const SupportedLimits = @import("main.zig").SupportedLimits;
const RequestDeviceStatus = @import("main.zig").RequestDeviceStatus;
const BackendType = @import("main.zig").BackendType;
const RequestDeviceCallback = @import("main.zig").RequestDeviceCallback;
const Impl = @import("interface.zig").Impl;
pub const Adapter = opaque {
pub const Type = enum(u32) {
discrete_gpu,
integrated_gpu,
cpu,
unknown,
pub fn name(t: Type) []const u8 {
return switch (t) {
.discrete_gpu => "Discrete GPU",
.integrated_gpu => "Integrated GPU",
.cpu => "CPU",
.unknown => "Unknown",
};
}
};
pub const Properties = extern struct {
pub const NextInChain = extern union {
generic: ?*const ChainedStructOut,
dawn_adapter_properties_power_preference: *const dawn.AdapterPropertiesPowerPreference,
};
next_in_chain: NextInChain = .{ .generic = null },
vendor_id: u32,
vendor_name: [*:0]const u8,
architecture: [*:0]const u8,
device_id: u32,
name: [*:0]const u8,
driver_description: [*:0]const u8,
adapter_type: Type,
backend_type: BackendType,
compatibility_mode: Bool32 = .false,
};
pub inline fn createDevice(adapter: *Adapter, descriptor: ?*const Device.Descriptor) ?*Device {
return Impl.adapterCreateDevice(adapter, descriptor);
}
/// Call once with null to determine the array length, and again to fetch the feature list.
///
/// Consider using the enumerateFeaturesOwned helper.
pub inline fn enumerateFeatures(adapter: *Adapter, features: ?[*]FeatureName) usize {
return Impl.adapterEnumerateFeatures(adapter, features);
}
/// Enumerates the adapter features, storing the result in an allocated slice which is owned by
/// the caller.
pub inline fn enumerateFeaturesOwned(adapter: *Adapter, allocator: std.mem.Allocator) ![]FeatureName {
const count = adapter.enumerateFeatures(null);
const data = try allocator.alloc(FeatureName, count);
_ = adapter.enumerateFeatures(data.ptr);
return data;
}
pub inline fn getInstance(adapter: *Adapter) *Instance {
return Impl.adapterGetInstance(adapter);
}
pub inline fn getLimits(adapter: *Adapter, limits: *SupportedLimits) bool {
return Impl.adapterGetLimits(adapter, limits);
}
pub inline fn getProperties(adapter: *Adapter, properties: *Adapter.Properties) void {
Impl.adapterGetProperties(adapter, properties);
}
pub inline fn hasFeature(adapter: *Adapter, feature: FeatureName) bool {
return Impl.adapterHasFeature(adapter, feature);
}
pub inline fn requestDevice(
adapter: *Adapter,
descriptor: ?*const Device.Descriptor,
context: anytype,
comptime callback: fn (
ctx: @TypeOf(context),
status: RequestDeviceStatus,
device: *Device,
message: ?[*:0]const u8,
) callconv(.Inline) void,
) void {
const Context = @TypeOf(context);
const Helper = struct {
pub fn cCallback(status: RequestDeviceStatus, device: *Device, message: ?[*:0]const u8, userdata: ?*anyopaque) callconv(.C) void {
callback(
if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))),
status,
device,
message,
);
}
};
Impl.adapterRequestDevice(adapter, descriptor, Helper.cCallback, if (Context == void) null else context);
}
pub inline fn reference(adapter: *Adapter) void {
Impl.adapterReference(adapter);
}
pub inline fn release(adapter: *Adapter) void {
Impl.adapterRelease(adapter);
}
};
test "Adapter.Type name" {
try testing.expectEqualStrings("Discrete GPU", Adapter.Type.discrete_gpu.name());
}

View file

@ -0,0 +1,90 @@
const Buffer = @import("buffer.zig").Buffer;
const Sampler = @import("sampler.zig").Sampler;
const TextureView = @import("texture_view.zig").TextureView;
const ChainedStruct = @import("main.zig").ChainedStruct;
const BindGroupLayout = @import("bind_group_layout.zig").BindGroupLayout;
const ExternalTexture = @import("external_texture.zig").ExternalTexture;
const Impl = @import("interface.zig").Impl;
pub const BindGroup = opaque {
pub const Entry = extern struct {
pub const NextInChain = extern union {
generic: ?*const ChainedStruct,
external_texture_binding_entry: *const ExternalTexture.BindingEntry,
};
next_in_chain: NextInChain = .{ .generic = null },
binding: u32,
buffer: ?*Buffer = null,
offset: u64 = 0,
size: u64,
elem_size: u32 = 0, // TEMP - using StructuredBuffer until we switch to DXC for templatized raw buffers
sampler: ?*Sampler = null,
texture_view: ?*TextureView = null,
/// Helper to create a buffer BindGroup.Entry.
pub fn buffer(binding: u32, buf: *Buffer, offset: u64, size: u64, elem_size: u32) Entry {
return .{
.binding = binding,
.buffer = buf,
.offset = offset,
.size = size,
.elem_size = elem_size,
};
}
/// Helper to create a sampler BindGroup.Entry.
pub fn sampler(binding: u32, _sampler: *Sampler) Entry {
return .{
.binding = binding,
.sampler = _sampler,
.size = 0,
};
}
/// Helper to create a texture view BindGroup.Entry.
pub fn textureView(binding: u32, texture_view: *TextureView) Entry {
return .{
.binding = binding,
.texture_view = texture_view,
.size = 0,
};
}
};
pub const Descriptor = extern struct {
next_in_chain: ?*const ChainedStruct = null,
label: ?[*:0]const u8 = null,
layout: *BindGroupLayout,
entry_count: usize = 0,
entries: ?[*]const Entry = null,
/// Provides a slightly friendlier Zig API to initialize this structure.
pub inline fn init(v: struct {
next_in_chain: ?*const ChainedStruct = null,
label: ?[*:0]const u8 = null,
layout: *BindGroupLayout,
entries: ?[]const Entry = null,
}) Descriptor {
return .{
.next_in_chain = v.next_in_chain,
.label = v.label,
.layout = v.layout,
.entry_count = if (v.entries) |e| e.len else 0,
.entries = if (v.entries) |e| e.ptr else null,
};
}
};
pub inline fn setLabel(bind_group: *BindGroup, label: [*:0]const u8) void {
Impl.bindGroupSetLabel(bind_group, label);
}
pub inline fn reference(bind_group: *BindGroup) void {
Impl.bindGroupReference(bind_group);
}
pub inline fn release(bind_group: *BindGroup) void {
Impl.bindGroupRelease(bind_group);
}
};

View file

@ -0,0 +1,131 @@
const Bool32 = @import("main.zig").Bool32;
const ChainedStruct = @import("main.zig").ChainedStruct;
const ShaderStageFlags = @import("main.zig").ShaderStageFlags;
const Buffer = @import("buffer.zig").Buffer;
const Sampler = @import("sampler.zig").Sampler;
const Texture = @import("texture.zig").Texture;
const TextureView = @import("texture_view.zig").TextureView;
const StorageTextureBindingLayout = @import("main.zig").StorageTextureBindingLayout;
const StorageTextureAccess = @import("main.zig").StorageTextureAccess;
const ExternalTexture = @import("external_texture.zig").ExternalTexture;
const Impl = @import("interface.zig").Impl;
pub const BindGroupLayout = opaque {
pub const Entry = extern struct {
pub const NextInChain = extern union {
generic: ?*const ChainedStruct,
external_texture_binding_layout: *const ExternalTexture.BindingLayout,
};
next_in_chain: NextInChain = .{ .generic = null },
binding: u32,
visibility: ShaderStageFlags,
buffer: Buffer.BindingLayout = .{},
sampler: Sampler.BindingLayout = .{},
texture: Texture.BindingLayout = .{},
storage_texture: StorageTextureBindingLayout = .{},
/// Helper to create a buffer BindGroupLayout.Entry.
pub fn buffer(
binding: u32,
visibility: ShaderStageFlags,
binding_type: Buffer.BindingType,
has_dynamic_offset: bool,
min_binding_size: u64,
) Entry {
return .{
.binding = binding,
.visibility = visibility,
.buffer = .{
.type = binding_type,
.has_dynamic_offset = Bool32.from(has_dynamic_offset),
.min_binding_size = min_binding_size,
},
};
}
/// Helper to create a sampler BindGroupLayout.Entry.
pub fn sampler(
binding: u32,
visibility: ShaderStageFlags,
binding_type: Sampler.BindingType,
) Entry {
return .{
.binding = binding,
.visibility = visibility,
.sampler = .{ .type = binding_type },
};
}
/// Helper to create a texture BindGroupLayout.Entry.
pub fn texture(
binding: u32,
visibility: ShaderStageFlags,
sample_type: Texture.SampleType,
view_dimension: TextureView.Dimension,
multisampled: bool,
) Entry {
return .{
.binding = binding,
.visibility = visibility,
.texture = .{
.sample_type = sample_type,
.view_dimension = view_dimension,
.multisampled = Bool32.from(multisampled),
},
};
}
/// Helper to create a storage texture BindGroupLayout.Entry.
pub fn storageTexture(
binding: u32,
visibility: ShaderStageFlags,
access: StorageTextureAccess,
format: Texture.Format,
view_dimension: TextureView.Dimension,
) Entry {
return .{
.binding = binding,
.visibility = visibility,
.storage_texture = .{
.access = access,
.format = format,
.view_dimension = view_dimension,
},
};
}
};
pub const Descriptor = extern struct {
next_in_chain: ?*const ChainedStruct = null,
label: ?[*:0]const u8 = null,
entry_count: usize = 0,
entries: ?[*]const Entry = null,
/// Provides a slightly friendlier Zig API to initialize this structure.
pub inline fn init(v: struct {
next_in_chain: ?*const ChainedStruct = null,
label: ?[*:0]const u8 = null,
entries: ?[]const Entry = null,
}) Descriptor {
return .{
.next_in_chain = v.next_in_chain,
.label = v.label,
.entry_count = if (v.entries) |e| e.len else 0,
.entries = if (v.entries) |e| e.ptr else null,
};
}
};
pub inline fn setLabel(bind_group_layout: *BindGroupLayout, label: [*:0]const u8) void {
Impl.bindGroupLayoutSetLabel(bind_group_layout, label);
}
pub inline fn reference(bind_group_layout: *BindGroupLayout) void {
Impl.bindGroupLayoutReference(bind_group_layout);
}
pub inline fn release(bind_group_layout: *BindGroupLayout) void {
Impl.bindGroupLayoutRelease(bind_group_layout);
}
};

View file

@ -0,0 +1,166 @@
const std = @import("std");
const Bool32 = @import("main.zig").Bool32;
const ChainedStruct = @import("main.zig").ChainedStruct;
const dawn = @import("dawn.zig");
const MapModeFlags = @import("main.zig").MapModeFlags;
const Impl = @import("interface.zig").Impl;
pub const Buffer = opaque {
pub const MapCallback = *const fn (status: MapAsyncStatus, userdata: ?*anyopaque) callconv(.C) void;
pub const BindingType = enum(u32) {
undefined = 0x00000000,
uniform = 0x00000001,
storage = 0x00000002,
read_only_storage = 0x00000003,
};
pub const MapState = enum(u32) {
unmapped = 0x00000000,
pending = 0x00000001,
mapped = 0x00000002,
};
pub const MapAsyncStatus = enum(u32) {
success = 0x00000000,
validation_error = 0x00000001,
unknown = 0x00000002,
device_lost = 0x00000003,
destroyed_before_callback = 0x00000004,
unmapped_before_callback = 0x00000005,
mapping_already_pending = 0x00000006,
offset_out_of_range = 0x00000007,
size_out_of_range = 0x00000008,
};
pub const UsageFlags = packed struct(u32) {
map_read: bool = false,
map_write: bool = false,
copy_src: bool = false,
copy_dst: bool = false,
index: bool = false,
vertex: bool = false,
uniform: bool = false,
storage: bool = false,
indirect: bool = false,
query_resolve: bool = false,
_padding: u22 = 0,
comptime {
std.debug.assert(
@sizeOf(@This()) == @sizeOf(u32) and
@bitSizeOf(@This()) == @bitSizeOf(u32),
);
}
pub const none = UsageFlags{};
pub fn equal(a: UsageFlags, b: UsageFlags) bool {
return @as(u10, @truncate(@as(u32, @bitCast(a)))) == @as(u10, @truncate(@as(u32, @bitCast(b))));
}
};
pub const BindingLayout = extern struct {
next_in_chain: ?*const ChainedStruct = null,
type: BindingType = .undefined,
has_dynamic_offset: Bool32 = .false,
min_binding_size: u64 = 0,
};
pub const Descriptor = extern struct {
pub const NextInChain = extern union {
generic: ?*const ChainedStruct,
dawn_buffer_descriptor_error_info_from_wire_client: *const dawn.BufferDescriptorErrorInfoFromWireClient,
};
next_in_chain: NextInChain = .{ .generic = null },
label: ?[*:0]const u8 = null,
usage: UsageFlags,
size: u64,
mapped_at_creation: Bool32 = .false,
};
pub inline fn destroy(buffer: *Buffer) void {
Impl.bufferDestroy(buffer);
}
pub inline fn getMapState(buffer: *Buffer) MapState {
return Impl.bufferGetMapState(buffer);
}
/// Default `offset_bytes`: 0
/// Default `len`: `gpu.whole_map_size` / `std.math.maxint(usize)` (whole range)
pub inline fn getConstMappedRange(
buffer: *Buffer,
comptime T: type,
offset_bytes: usize,
len: usize,
) ?[]const T {
const size = @sizeOf(T) * len;
const data = Impl.bufferGetConstMappedRange(
buffer,
offset_bytes,
size + size % 4,
);
return if (data) |d| @as([*]const T, @ptrCast(@alignCast(d)))[0..len] else null;
}
/// Default `offset_bytes`: 0
/// Default `len`: `gpu.whole_map_size` / `std.math.maxint(usize)` (whole range)
pub inline fn getMappedRange(
buffer: *Buffer,
comptime T: type,
offset_bytes: usize,
len: usize,
) ?[]T {
const size = @sizeOf(T) * len;
const data = Impl.bufferGetMappedRange(
buffer,
offset_bytes,
size + size % 4,
);
return if (data) |d| @as([*]T, @ptrCast(@alignCast(d)))[0..len] else null;
}
pub inline fn getSize(buffer: *Buffer) u64 {
return Impl.bufferGetSize(buffer);
}
pub inline fn getUsage(buffer: *Buffer) Buffer.UsageFlags {
return Impl.bufferGetUsage(buffer);
}
pub inline fn mapAsync(
buffer: *Buffer,
mode: MapModeFlags,
offset: usize,
size: usize,
context: anytype,
comptime callback: fn (ctx: @TypeOf(context), status: MapAsyncStatus) callconv(.Inline) void,
) void {
const Context = @TypeOf(context);
const Helper = struct {
pub fn cCallback(status: MapAsyncStatus, userdata: ?*anyopaque) callconv(.C) void {
callback(if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))), status);
}
};
Impl.bufferMapAsync(buffer, mode, offset, size, Helper.cCallback, if (Context == void) null else context);
}
pub inline fn setLabel(buffer: *Buffer, label: [*:0]const u8) void {
Impl.bufferSetLabel(buffer, label);
}
pub inline fn unmap(buffer: *Buffer) void {
Impl.bufferUnmap(buffer);
}
pub inline fn reference(buffer: *Buffer) void {
Impl.bufferReference(buffer);
}
pub inline fn release(buffer: *Buffer) void {
Impl.bufferRelease(buffer);
}
};

View file

@ -0,0 +1,21 @@
const ChainedStruct = @import("main.zig").ChainedStruct;
const Impl = @import("interface.zig").Impl;
pub const CommandBuffer = opaque {
pub const Descriptor = extern struct {
next_in_chain: ?*const ChainedStruct = null,
label: ?[*:0]const u8 = null,
};
pub inline fn setLabel(command_buffer: *CommandBuffer, label: [*:0]const u8) void {
Impl.commandBufferSetLabel(command_buffer, label);
}
pub inline fn reference(command_buffer: *CommandBuffer) void {
Impl.commandBufferReference(command_buffer);
}
pub inline fn release(command_buffer: *CommandBuffer) void {
Impl.commandBufferRelease(command_buffer);
}
};

View file

@ -0,0 +1,111 @@
const std = @import("std");
const ComputePassEncoder = @import("compute_pass_encoder.zig").ComputePassEncoder;
const RenderPassEncoder = @import("render_pass_encoder.zig").RenderPassEncoder;
const CommandBuffer = @import("command_buffer.zig").CommandBuffer;
const Buffer = @import("buffer.zig").Buffer;
const QuerySet = @import("query_set.zig").QuerySet;
const RenderPassDescriptor = @import("main.zig").RenderPassDescriptor;
const ComputePassDescriptor = @import("main.zig").ComputePassDescriptor;
const ChainedStruct = @import("main.zig").ChainedStruct;
const ImageCopyBuffer = @import("main.zig").ImageCopyBuffer;
const ImageCopyTexture = @import("main.zig").ImageCopyTexture;
const Extent3D = @import("main.zig").Extent3D;
const Impl = @import("interface.zig").Impl;
const dawn = @import("dawn.zig");
pub const CommandEncoder = opaque {
pub const Descriptor = extern struct {
pub const NextInChain = extern union {
generic: ?*const ChainedStruct,
dawn_encoder_internal_usage_descriptor: *const dawn.EncoderInternalUsageDescriptor,
};
next_in_chain: NextInChain = .{ .generic = null },
label: ?[*:0]const u8 = null,
};
pub inline fn beginComputePass(command_encoder: *CommandEncoder, descriptor: ?*const ComputePassDescriptor) *ComputePassEncoder {
return Impl.commandEncoderBeginComputePass(command_encoder, descriptor);
}
pub inline fn beginRenderPass(command_encoder: *CommandEncoder, descriptor: *const RenderPassDescriptor) *RenderPassEncoder {
return Impl.commandEncoderBeginRenderPass(command_encoder, descriptor);
}
/// Default `offset`: 0
/// Default `size`: `gpu.whole_size`
pub inline fn clearBuffer(command_encoder: *CommandEncoder, buffer: *Buffer, offset: u64, size: u64) void {
Impl.commandEncoderClearBuffer(command_encoder, buffer, offset, size);
}
pub inline fn copyBufferToBuffer(command_encoder: *CommandEncoder, source: *Buffer, source_offset: u64, destination: *Buffer, destination_offset: u64, size: u64) void {
Impl.commandEncoderCopyBufferToBuffer(command_encoder, source, source_offset, destination, destination_offset, size);
}
pub inline fn copyBufferToTexture(command_encoder: *CommandEncoder, source: *const ImageCopyBuffer, destination: *const ImageCopyTexture, copy_size: *const Extent3D) void {
Impl.commandEncoderCopyBufferToTexture(command_encoder, source, destination, copy_size);
}
pub inline fn copyTextureToBuffer(command_encoder: *CommandEncoder, source: *const ImageCopyTexture, destination: *const ImageCopyBuffer, copy_size: *const Extent3D) void {
Impl.commandEncoderCopyTextureToBuffer(command_encoder, source, destination, copy_size);
}
pub inline fn copyTextureToTexture(command_encoder: *CommandEncoder, source: *const ImageCopyTexture, destination: *const ImageCopyTexture, copy_size: *const Extent3D) void {
Impl.commandEncoderCopyTextureToTexture(command_encoder, source, destination, copy_size);
}
pub inline fn finish(command_encoder: *CommandEncoder, descriptor: ?*const CommandBuffer.Descriptor) *CommandBuffer {
return Impl.commandEncoderFinish(command_encoder, descriptor);
}
pub inline fn injectValidationError(command_encoder: *CommandEncoder, message: [*:0]const u8) void {
Impl.commandEncoderInjectValidationError(command_encoder, message);
}
pub inline fn insertDebugMarker(command_encoder: *CommandEncoder, marker_label: [*:0]const u8) void {
Impl.commandEncoderInsertDebugMarker(command_encoder, marker_label);
}
pub inline fn popDebugGroup(command_encoder: *CommandEncoder) void {
Impl.commandEncoderPopDebugGroup(command_encoder);
}
pub inline fn pushDebugGroup(command_encoder: *CommandEncoder, group_label: [*:0]const u8) void {
Impl.commandEncoderPushDebugGroup(command_encoder, group_label);
}
pub inline fn resolveQuerySet(command_encoder: *CommandEncoder, query_set: *QuerySet, first_query: u32, query_count: u32, destination: *Buffer, destination_offset: u64) void {
Impl.commandEncoderResolveQuerySet(command_encoder, query_set, first_query, query_count, destination, destination_offset);
}
pub inline fn setLabel(command_encoder: *CommandEncoder, label: [*:0]const u8) void {
Impl.commandEncoderSetLabel(command_encoder, label);
}
pub inline fn writeBuffer(
command_encoder: *CommandEncoder,
buffer: *Buffer,
buffer_offset_bytes: u64,
data_slice: anytype,
) void {
Impl.commandEncoderWriteBuffer(
command_encoder,
buffer,
buffer_offset_bytes,
@as([*]const u8, @ptrCast(std.mem.sliceAsBytes(data_slice).ptr)),
@as(u64, @intCast(data_slice.len)) * @sizeOf(std.meta.Elem(@TypeOf(data_slice))),
);
}
pub inline fn writeTimestamp(command_encoder: *CommandEncoder, query_set: *QuerySet, query_index: u32) void {
Impl.commandEncoderWriteTimestamp(command_encoder, query_set, query_index);
}
pub inline fn reference(command_encoder: *CommandEncoder) void {
Impl.commandEncoderReference(command_encoder);
}
pub inline fn release(command_encoder: *CommandEncoder) void {
Impl.commandEncoderRelease(command_encoder);
}
};

View file

@ -0,0 +1,64 @@
const Buffer = @import("buffer.zig").Buffer;
const BindGroup = @import("bind_group.zig").BindGroup;
const ComputePipeline = @import("compute_pipeline.zig").ComputePipeline;
const QuerySet = @import("query_set.zig").QuerySet;
const Impl = @import("interface.zig").Impl;
pub const ComputePassEncoder = opaque {
/// Default `workgroup_count_y`: 1
/// Default `workgroup_count_z`: 1
pub inline fn dispatchWorkgroups(compute_pass_encoder: *ComputePassEncoder, workgroup_count_x: u32, workgroup_count_y: u32, workgroup_count_z: u32) void {
Impl.computePassEncoderDispatchWorkgroups(compute_pass_encoder, workgroup_count_x, workgroup_count_y, workgroup_count_z);
}
pub inline fn dispatchWorkgroupsIndirect(compute_pass_encoder: *ComputePassEncoder, indirect_buffer: *Buffer, indirect_offset: u64) void {
Impl.computePassEncoderDispatchWorkgroupsIndirect(compute_pass_encoder, indirect_buffer, indirect_offset);
}
pub inline fn end(compute_pass_encoder: *ComputePassEncoder) void {
Impl.computePassEncoderEnd(compute_pass_encoder);
}
pub inline fn insertDebugMarker(compute_pass_encoder: *ComputePassEncoder, marker_label: [*:0]const u8) void {
Impl.computePassEncoderInsertDebugMarker(compute_pass_encoder, marker_label);
}
pub inline fn popDebugGroup(compute_pass_encoder: *ComputePassEncoder) void {
Impl.computePassEncoderPopDebugGroup(compute_pass_encoder);
}
pub inline fn pushDebugGroup(compute_pass_encoder: *ComputePassEncoder, group_label: [*:0]const u8) void {
Impl.computePassEncoderPushDebugGroup(compute_pass_encoder, group_label);
}
/// Default `dynamic_offsets`: null
pub inline fn setBindGroup(compute_pass_encoder: *ComputePassEncoder, group_index: u32, group: *BindGroup, dynamic_offsets: ?[]const u32) void {
Impl.computePassEncoderSetBindGroup(
compute_pass_encoder,
group_index,
group,
if (dynamic_offsets) |v| v.len else 0,
if (dynamic_offsets) |v| v.ptr else null,
);
}
pub inline fn setLabel(compute_pass_encoder: *ComputePassEncoder, label: [*:0]const u8) void {
Impl.computePassEncoderSetLabel(compute_pass_encoder, label);
}
pub inline fn setPipeline(compute_pass_encoder: *ComputePassEncoder, pipeline: *ComputePipeline) void {
Impl.computePassEncoderSetPipeline(compute_pass_encoder, pipeline);
}
pub inline fn writeTimestamp(compute_pass_encoder: *ComputePassEncoder, query_set: *QuerySet, query_index: u32) void {
Impl.computePassEncoderWriteTimestamp(compute_pass_encoder, query_set, query_index);
}
pub inline fn reference(compute_pass_encoder: *ComputePassEncoder) void {
Impl.computePassEncoderReference(compute_pass_encoder);
}
pub inline fn release(compute_pass_encoder: *ComputePassEncoder) void {
Impl.computePassEncoderRelease(compute_pass_encoder);
}
};

View file

@ -0,0 +1,30 @@
const ChainedStruct = @import("main.zig").ChainedStruct;
const ProgrammableStageDescriptor = @import("main.zig").ProgrammableStageDescriptor;
const PipelineLayout = @import("pipeline_layout.zig").PipelineLayout;
const BindGroupLayout = @import("bind_group_layout.zig").BindGroupLayout;
const Impl = @import("interface.zig").Impl;
pub const ComputePipeline = opaque {
pub const Descriptor = extern struct {
next_in_chain: ?*const ChainedStruct = null,
label: ?[*:0]const u8 = null,
layout: ?*PipelineLayout = null,
compute: ProgrammableStageDescriptor,
};
pub inline fn getBindGroupLayout(compute_pipeline: *ComputePipeline, group_index: u32) *BindGroupLayout {
return Impl.computePipelineGetBindGroupLayout(compute_pipeline, group_index);
}
pub inline fn setLabel(compute_pipeline: *ComputePipeline, label: [*:0]const u8) void {
Impl.computePipelineSetLabel(compute_pipeline, label);
}
pub inline fn reference(compute_pipeline: *ComputePipeline) void {
Impl.computePipelineReference(compute_pipeline);
}
pub inline fn release(compute_pipeline: *ComputePipeline) void {
Impl.computePipelineRelease(compute_pipeline);
}
};

View file

@ -0,0 +1,74 @@
const Bool32 = @import("main.zig").Bool32;
const ChainedStruct = @import("main.zig").ChainedStruct;
const ChainedStructOut = @import("main.zig").ChainedStructOut;
const PowerPreference = @import("main.zig").PowerPreference;
const Texture = @import("texture.zig").Texture;
pub const CacheDeviceDescriptor = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .dawn_cache_device_descriptor },
isolation_key: [*:0]const u8 = "",
};
pub const EncoderInternalUsageDescriptor = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .dawn_encoder_internal_usage_descriptor },
use_internal_usages: Bool32 = .false,
};
pub const MultisampleStateRenderToSingleSampled = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .dawn_multisample_state_render_to_single_sampled },
enabled: Bool32 = .false,
};
pub const RenderPassColorAttachmentRenderToSingleSampled = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .dawn_render_pass_color_attachment_render_to_single_sampled },
implicit_sample_count: u32 = 1,
};
pub const TextureInternalUsageDescriptor = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .dawn_texture_internal_usage_descriptor },
internal_usage: Texture.UsageFlags = Texture.UsageFlags.none,
};
pub const TogglesDescriptor = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .dawn_toggles_descriptor },
enabled_toggles_count: usize = 0,
enabled_toggles: ?[*]const [*:0]const u8 = null,
disabled_toggles_count: usize = 0,
disabled_toggles: ?[*]const [*:0]const u8 = null,
/// Provides a slightly friendlier Zig API to initialize this structure.
pub inline fn init(v: struct {
chain: ChainedStruct = .{ .next = null, .s_type = .dawn_toggles_descriptor },
enabled_toggles: ?[]const [*:0]const u8 = null,
disabled_toggles: ?[]const [*:0]const u8 = null,
}) TogglesDescriptor {
return .{
.chain = v.chain,
.enabled_toggles_count = if (v.enabled_toggles) |e| e.len else 0,
.enabled_toggles = if (v.enabled_toggles) |e| e.ptr else null,
.disabled_toggles_count = if (v.disabled_toggles) |e| e.len else 0,
.disabled_toggles = if (v.disabled_toggles) |e| e.ptr else null,
};
}
};
pub const ShaderModuleSPIRVOptionsDescriptor = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .dawn_shader_module_spirv_options_descriptor },
allow_non_uniform_derivatives: Bool32 = .false,
};
pub const AdapterPropertiesPowerPreference = extern struct {
chain: ChainedStructOut = .{
.next = null,
.s_type = .dawn_adapter_properties_power_preference,
},
power_preference: PowerPreference = .undefined,
};
pub const BufferDescriptorErrorInfoFromWireClient = extern struct {
chain: ChainedStruct = .{
.next = null,
.s_type = .dawn_buffer_descriptor_error_info_from_wire_client,
},
out_of_memory: Bool32 = .false,
};

View file

@ -0,0 +1,412 @@
const std = @import("std");
const Adapter = @import("adapter.zig").Adapter;
const Queue = @import("queue.zig").Queue;
const BindGroup = @import("bind_group.zig").BindGroup;
const BindGroupLayout = @import("bind_group_layout.zig").BindGroupLayout;
const Buffer = @import("buffer.zig").Buffer;
const CommandEncoder = @import("command_encoder.zig").CommandEncoder;
const ComputePipeline = @import("compute_pipeline.zig").ComputePipeline;
const ExternalTexture = @import("external_texture.zig").ExternalTexture;
const PipelineLayout = @import("pipeline_layout.zig").PipelineLayout;
const QuerySet = @import("query_set.zig").QuerySet;
const RenderBundleEncoder = @import("render_bundle_encoder.zig").RenderBundleEncoder;
const RenderPipeline = @import("render_pipeline.zig").RenderPipeline;
const Sampler = @import("sampler.zig").Sampler;
const ShaderModule = @import("shader_module.zig").ShaderModule;
const Surface = @import("surface.zig").Surface;
const SwapChain = @import("swap_chain.zig").SwapChain;
const Texture = @import("texture.zig").Texture;
const ChainedStruct = @import("main.zig").ChainedStruct;
const FeatureName = @import("main.zig").FeatureName;
const RequiredLimits = @import("main.zig").RequiredLimits;
const SupportedLimits = @import("main.zig").SupportedLimits;
const ErrorType = @import("main.zig").ErrorType;
const ErrorFilter = @import("main.zig").ErrorFilter;
const LoggingType = @import("main.zig").LoggingType;
const CreatePipelineAsyncStatus = @import("main.zig").CreatePipelineAsyncStatus;
const LoggingCallback = @import("main.zig").LoggingCallback;
const ErrorCallback = @import("main.zig").ErrorCallback;
const CreateComputePipelineAsyncCallback = @import("main.zig").CreateComputePipelineAsyncCallback;
const CreateRenderPipelineAsyncCallback = @import("main.zig").CreateRenderPipelineAsyncCallback;
const Impl = @import("interface.zig").Impl;
const dawn = @import("dawn.zig");
pub const Device = opaque {
pub const LostCallback = *const fn (
reason: LostReason,
message: [*:0]const u8,
userdata: ?*anyopaque,
) callconv(.C) void;
pub const LostReason = enum(u32) {
undefined = 0x00000000,
destroyed = 0x00000001,
};
pub const Descriptor = extern struct {
pub const NextInChain = extern union {
generic: ?*const ChainedStruct,
dawn_toggles_descriptor: *const dawn.TogglesDescriptor,
dawn_cache_device_descriptor: *const dawn.CacheDeviceDescriptor,
};
next_in_chain: NextInChain = .{ .generic = null },
label: ?[*:0]const u8 = null,
required_features_count: usize = 0,
required_features: ?[*]const FeatureName = null,
required_limits: ?*const RequiredLimits = null,
default_queue: Queue.Descriptor = Queue.Descriptor{},
device_lost_callback: LostCallback,
device_lost_userdata: ?*anyopaque,
/// Provides a slightly friendlier Zig API to initialize this structure.
pub inline fn init(v: struct {
next_in_chain: NextInChain = .{ .generic = null },
label: ?[*:0]const u8 = null,
required_features: ?[]const FeatureName = null,
required_limits: ?*const RequiredLimits = null,
default_queue: Queue.Descriptor = Queue.Descriptor{},
}) Descriptor {
return .{
.next_in_chain = v.next_in_chain,
.label = v.label,
.required_features_count = if (v.required_features) |e| e.len else 0,
.required_features = if (v.required_features) |e| e.ptr else null,
.default_queue = v.default_queue,
};
}
};
pub inline fn createBindGroup(device: *Device, descriptor: *const BindGroup.Descriptor) *BindGroup {
return Impl.deviceCreateBindGroup(device, descriptor);
}
pub inline fn createBindGroupLayout(device: *Device, descriptor: *const BindGroupLayout.Descriptor) *BindGroupLayout {
return Impl.deviceCreateBindGroupLayout(device, descriptor);
}
pub inline fn createBuffer(device: *Device, descriptor: *const Buffer.Descriptor) *Buffer {
return Impl.deviceCreateBuffer(device, descriptor);
}
pub inline fn createCommandEncoder(device: *Device, descriptor: ?*const CommandEncoder.Descriptor) *CommandEncoder {
return Impl.deviceCreateCommandEncoder(device, descriptor);
}
pub inline fn createComputePipeline(device: *Device, descriptor: *const ComputePipeline.Descriptor) *ComputePipeline {
return Impl.deviceCreateComputePipeline(device, descriptor);
}
pub inline fn createComputePipelineAsync(
device: *Device,
descriptor: *const ComputePipeline.Descriptor,
context: anytype,
comptime callback: fn (
status: CreatePipelineAsyncStatus,
compute_pipeline: *ComputePipeline,
message: [*:0]const u8,
ctx: @TypeOf(context),
) callconv(.Inline) void,
) void {
const Context = @TypeOf(context);
const Helper = struct {
pub fn cCallback(
status: CreatePipelineAsyncStatus,
compute_pipeline: *ComputePipeline,
message: [*:0]const u8,
userdata: ?*anyopaque,
) callconv(.C) void {
callback(
status,
compute_pipeline,
message,
if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))),
);
}
};
Impl.deviceCreateComputePipelineAsync(device, descriptor, Helper.cCallback, if (Context == void) null else context);
}
pub inline fn createErrorBuffer(device: *Device, descriptor: *const Buffer.Descriptor) *Buffer {
return Impl.deviceCreateErrorBuffer(device, descriptor);
}
pub inline fn createErrorExternalTexture(device: *Device) *ExternalTexture {
return Impl.deviceCreateErrorExternalTexture(device);
}
pub inline fn createErrorTexture(device: *Device, descriptor: *const Texture.Descriptor) *Texture {
return Impl.deviceCreateErrorTexture(device, descriptor);
}
pub inline fn createExternalTexture(device: *Device, external_texture_descriptor: *const ExternalTexture.Descriptor) *ExternalTexture {
return Impl.deviceCreateExternalTexture(device, external_texture_descriptor);
}
pub inline fn createPipelineLayout(device: *Device, pipeline_layout_descriptor: *const PipelineLayout.Descriptor) *PipelineLayout {
return Impl.deviceCreatePipelineLayout(device, pipeline_layout_descriptor);
}
pub inline fn createQuerySet(device: *Device, descriptor: *const QuerySet.Descriptor) *QuerySet {
return Impl.deviceCreateQuerySet(device, descriptor);
}
pub inline fn createRenderBundleEncoder(device: *Device, descriptor: *const RenderBundleEncoder.Descriptor) *RenderBundleEncoder {
return Impl.deviceCreateRenderBundleEncoder(device, descriptor);
}
pub inline fn createRenderPipeline(device: *Device, descriptor: *const RenderPipeline.Descriptor) *RenderPipeline {
return Impl.deviceCreateRenderPipeline(device, descriptor);
}
pub inline fn createRenderPipelineAsync(
device: *Device,
descriptor: *const RenderPipeline.Descriptor,
context: anytype,
comptime callback: fn (
ctx: @TypeOf(context),
status: CreatePipelineAsyncStatus,
pipeline: *RenderPipeline,
message: [*:0]const u8,
) callconv(.Inline) void,
) void {
const Context = @TypeOf(context);
const Helper = struct {
pub fn cCallback(
status: CreatePipelineAsyncStatus,
pipeline: *RenderPipeline,
message: [*:0]const u8,
userdata: ?*anyopaque,
) callconv(.C) void {
callback(
if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))),
status,
pipeline,
message,
);
}
};
Impl.deviceCreateRenderPipelineAsync(device, descriptor, Helper.cCallback, if (Context == void) null else context);
}
pub inline fn createSampler(device: *Device, descriptor: ?*const Sampler.Descriptor) *Sampler {
return Impl.deviceCreateSampler(device, descriptor);
}
pub inline fn createShaderModule(device: *Device, descriptor: *const ShaderModule.Descriptor) *ShaderModule {
return Impl.deviceCreateShaderModule(device, descriptor);
}
/// Helper to make createShaderModule invocations slightly nicer.
pub inline fn createShaderModuleWGSL(
device: *Device,
label: ?[*:0]const u8,
code: [*:0]const u8,
) *ShaderModule {
return device.createShaderModule(&ShaderModule.Descriptor{
.next_in_chain = .{ .wgsl_descriptor = &.{ .code = code } },
.label = label,
});
}
/// Helper to make createShaderModule invocations slightly nicer.
pub inline fn createShaderModuleSpirV(
device: *Device,
label: ?[*:0]const u8,
code: [*]const u32,
code_size: u32,
) *ShaderModule {
return device.createShaderModule(&ShaderModule.Descriptor{
.next_in_chain = .{ .spirv_descriptor = &.{
.code_size = code_size,
.code = code,
} },
.label = label,
});
}
/// Helper to make createShaderModule invocations slightly nicer.
pub inline fn createShaderModuleHLSL(
device: *Device,
label: ?[*:0]const u8,
code: []const u8,
) *ShaderModule {
return device.createShaderModule(&ShaderModule.Descriptor{
.next_in_chain = .{ .hlsl_descriptor = &.{
.code = code.ptr,
.code_size = code.len,
} },
.label = label,
});
}
/// Helper to make createShaderModule invocations slightly nicer.
pub inline fn createShaderModuleMSL(
device: *Device,
label: ?[*:0]const u8,
code: []const u8,
) *ShaderModule {
return device.createShaderModule(&ShaderModule.Descriptor{
.next_in_chain = .{ .msl_descriptor = &.{
.code = code.ptr,
.code_size = code.len,
} },
.label = label,
});
}
pub inline fn createSwapChain(device: *Device, surface: ?*Surface, descriptor: *const SwapChain.Descriptor) *SwapChain {
return Impl.deviceCreateSwapChain(device, surface, descriptor);
}
pub inline fn createTexture(device: *Device, descriptor: *const Texture.Descriptor) *Texture {
return Impl.deviceCreateTexture(device, descriptor);
}
pub inline fn destroy(device: *Device) void {
Impl.deviceDestroy(device);
}
/// Call once with null to determine the array length, and again to fetch the feature list.
///
/// Consider using the enumerateFeaturesOwned helper.
pub inline fn enumerateFeatures(device: *Device, features: ?[*]FeatureName) usize {
return Impl.deviceEnumerateFeatures(device, features);
}
/// Enumerates the adapter features, storing the result in an allocated slice which is owned by
/// the caller.
pub inline fn enumerateFeaturesOwned(device: *Device, allocator: std.mem.Allocator) ![]FeatureName {
const count = device.enumerateFeatures(null);
const data = try allocator.alloc(FeatureName, count);
_ = device.enumerateFeatures(data.ptr);
return data;
}
pub inline fn forceLoss(device: *Device, reason: LostReason, message: [*:0]const u8) void {
return Impl.deviceForceLoss(device, reason, message);
}
pub inline fn getAdapter(device: *Device) *Adapter {
return Impl.deviceGetAdapter(device);
}
pub inline fn getLimits(device: *Device, limits: *SupportedLimits) bool {
return Impl.deviceGetLimits(device, limits);
}
pub inline fn getQueue(device: *Device) *Queue {
return Impl.deviceGetQueue(device);
}
pub inline fn hasFeature(device: *Device, feature: FeatureName) bool {
return Impl.deviceHasFeature(device, feature);
}
pub inline fn injectError(device: *Device, typ: ErrorType, message: [*:0]const u8) void {
Impl.deviceInjectError(device, typ, message);
}
pub inline fn popErrorScope(
device: *Device,
context: anytype,
comptime callback: fn (ctx: @TypeOf(context), typ: ErrorType, message: [*:0]const u8) callconv(.Inline) void,
) void {
const Context = @TypeOf(context);
const Helper = struct {
pub fn cCallback(typ: ErrorType, message: [*:0]const u8, userdata: ?*anyopaque) callconv(.C) void {
callback(if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))), typ, message);
}
};
Impl.devicePopErrorScope(device, Helper.cCallback, if (Context == void) null else context);
}
pub inline fn pushErrorScope(device: *Device, filter: ErrorFilter) void {
Impl.devicePushErrorScope(device, filter);
}
pub inline fn setDeviceLostCallback(
device: *Device,
context: anytype,
comptime callback: ?fn (ctx: @TypeOf(context), reason: LostReason, message: [*:0]const u8) callconv(.Inline) void,
) void {
if (callback) |cb| {
const Context = @TypeOf(context);
const Helper = struct {
pub fn cCallback(reason: LostReason, message: [*:0]const u8, userdata: ?*anyopaque) callconv(.C) void {
cb(if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))), reason, message);
}
};
Impl.deviceSetDeviceLostCallback(device, Helper.cCallback, if (Context == void) null else context);
} else {
Impl.deviceSetDeviceLostCallback(device, null, null);
}
}
pub inline fn setLabel(device: *Device, label: [*:0]const u8) void {
Impl.deviceSetLabel(device, label);
}
pub inline fn setLoggingCallback(
device: *Device,
context: anytype,
comptime callback: ?fn (ctx: @TypeOf(context), typ: LoggingType, message: [*:0]const u8) callconv(.Inline) void,
) void {
if (callback) |cb| {
const Context = @TypeOf(context);
const Helper = struct {
pub fn cCallback(typ: LoggingType, message: [*:0]const u8, userdata: ?*anyopaque) callconv(.C) void {
cb(if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))), typ, message);
}
};
Impl.deviceSetLoggingCallback(device, Helper.cCallback, if (Context == void) null else context);
} else {
Impl.deviceSetLoggingCallback(device, null, null);
}
}
pub inline fn setUncapturedErrorCallback(
device: *Device,
context: anytype,
comptime callback: ?fn (ctx: @TypeOf(context), typ: ErrorType, message: [*:0]const u8) callconv(.Inline) void,
) void {
if (callback) |cb| {
const Context = @TypeOf(context);
const Helper = struct {
pub fn cCallback(typ: ErrorType, message: [*:0]const u8, userdata: ?*anyopaque) callconv(.C) void {
cb(if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))), typ, message);
}
};
Impl.deviceSetUncapturedErrorCallback(device, Helper.cCallback, if (Context == void) null else context);
} else {
Impl.deviceSetUncapturedErrorCallback(device, null, null);
}
}
pub inline fn tick(device: *Device) void {
Impl.deviceTick(device);
}
// Mach WebGPU extension. Supported with mach-gpu-dawn.
//
// When making Metal interop with other APIs, we need to be careful that QueueSubmit doesn't
// mean that the operations will be visible to other APIs/Metal devices right away. macOS
// does have a global queue of graphics operations, but the command buffers are inserted there
// when they are "scheduled". Submitting other operations before the command buffer is
// scheduled could lead to races in who gets scheduled first and incorrect rendering.
pub inline fn machWaitForCommandsToBeScheduled(device: *Device) void {
Impl.machDeviceWaitForCommandsToBeScheduled(device);
}
pub inline fn validateTextureDescriptor(device: *Device, descriptor: *const Texture.Descriptor) void {
Impl.deviceVlidateTextureDescriptor(device, descriptor);
}
pub inline fn reference(device: *Device) void {
Impl.deviceReference(device);
}
pub inline fn release(device: *Device) void {
Impl.deviceRelease(device);
}
};

View file

@ -0,0 +1,56 @@
const Bool32 = @import("main.zig").Bool32;
const ChainedStruct = @import("main.zig").ChainedStruct;
const TextureView = @import("texture_view.zig").TextureView;
const Origin2D = @import("main.zig").Origin2D;
const Extent2D = @import("main.zig").Extent2D;
const Impl = @import("interface.zig").Impl;
pub const ExternalTexture = opaque {
pub const BindingEntry = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .external_texture_binding_entry },
external_texture: *ExternalTexture,
};
pub const BindingLayout = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .external_texture_binding_layout },
};
const Rotation = enum(u32) {
rotate_0_degrees = 0x00000000,
rotate_90_degrees = 0x00000001,
rotate_180_degrees = 0x00000002,
rotate_270_degrees = 0x00000003,
};
pub const Descriptor = extern struct {
next_in_chain: ?*const ChainedStruct = null,
label: ?[*:0]const u8 = null,
plane0: *TextureView,
plane1: ?*TextureView = null,
visible_origin: Origin2D,
visible_size: Extent2D,
do_yuv_to_rgb_conversion_only: Bool32 = .false,
yuv_to_rgb_conversion_matrix: ?*const [12]f32 = null,
src_transform_function_parameters: *const [7]f32,
dst_transform_function_parameters: *const [7]f32,
gamut_conversion_matrix: *const [9]f32,
flip_y: Bool32,
rotation: Rotation,
};
pub inline fn destroy(external_texture: *ExternalTexture) void {
Impl.externalTextureDestroy(external_texture);
}
pub inline fn setLabel(external_texture: *ExternalTexture, label: [*:0]const u8) void {
Impl.externalTextureSetLabel(external_texture, label);
}
pub inline fn reference(external_texture: *ExternalTexture) void {
Impl.externalTextureReference(external_texture);
}
pub inline fn release(external_texture: *ExternalTexture) void {
Impl.externalTextureRelease(external_texture);
}
};

View file

@ -0,0 +1,65 @@
const ChainedStruct = @import("main.zig").ChainedStruct;
const RequestAdapterStatus = @import("main.zig").RequestAdapterStatus;
const Surface = @import("surface.zig").Surface;
const Adapter = @import("adapter.zig").Adapter;
const RequestAdapterOptions = @import("main.zig").RequestAdapterOptions;
const RequestAdapterCallback = @import("main.zig").RequestAdapterCallback;
const Impl = @import("interface.zig").Impl;
const dawn = @import("dawn.zig");
pub const Instance = opaque {
pub const Descriptor = extern struct {
pub const NextInChain = extern union {
generic: ?*const ChainedStruct,
dawn_toggles_descriptor: *const dawn.TogglesDescriptor,
};
next_in_chain: NextInChain = .{ .generic = null },
};
pub inline fn createSurface(instance: *Instance, descriptor: *const Surface.Descriptor) *Surface {
return Impl.instanceCreateSurface(instance, descriptor);
}
pub inline fn processEvents(instance: *Instance) void {
Impl.instanceProcessEvents(instance);
}
pub inline fn requestAdapter(
instance: *Instance,
options: ?*const RequestAdapterOptions,
context: anytype,
comptime callback: fn (
ctx: @TypeOf(context),
status: RequestAdapterStatus,
adapter: ?*Adapter,
message: ?[*:0]const u8,
) callconv(.Inline) void,
) void {
const Context = @TypeOf(context);
const Helper = struct {
pub fn cCallback(
status: RequestAdapterStatus,
adapter: ?*Adapter,
message: ?[*:0]const u8,
userdata: ?*anyopaque,
) callconv(.C) void {
callback(
if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))),
status,
adapter,
message,
);
}
};
Impl.instanceRequestAdapter(instance, options, Helper.cCallback, if (Context == void) null else context);
}
pub inline fn reference(instance: *Instance) void {
Impl.instanceReference(instance);
}
pub inline fn release(instance: *Instance) void {
Impl.instanceRelease(instance);
}
};

File diff suppressed because it is too large Load diff

1027
src/sysgpu/sysgpu/main.zig Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,38 @@
const ChainedStruct = @import("main.zig").ChainedStruct;
const BindGroupLayout = @import("bind_group_layout.zig").BindGroupLayout;
const Impl = @import("interface.zig").Impl;
pub const PipelineLayout = opaque {
pub const Descriptor = extern struct {
next_in_chain: ?*const ChainedStruct = null,
label: ?[*:0]const u8 = null,
bind_group_layout_count: usize = 0,
bind_group_layouts: ?[*]const *BindGroupLayout = null,
/// Provides a slightly friendlier Zig API to initialize this structure.
pub inline fn init(v: struct {
next_in_chain: ?*const ChainedStruct = null,
label: ?[*:0]const u8 = null,
bind_group_layouts: ?[]const *BindGroupLayout = null,
}) Descriptor {
return .{
.next_in_chain = v.next_in_chain,
.label = v.label,
.bind_group_layout_count = if (v.bind_group_layouts) |e| e.len else 0,
.bind_group_layouts = if (v.bind_group_layouts) |e| e.ptr else null,
};
}
};
pub inline fn setLabel(pipeline_layout: *PipelineLayout, label: [*:0]const u8) void {
Impl.pipelineLayoutSetLabel(pipeline_layout, label);
}
pub inline fn reference(pipeline_layout: *PipelineLayout) void {
Impl.pipelineLayoutReference(pipeline_layout);
}
pub inline fn release(pipeline_layout: *PipelineLayout) void {
Impl.pipelineLayoutRelease(pipeline_layout);
}
};

View file

@ -0,0 +1,57 @@
const ChainedStruct = @import("main.zig").ChainedStruct;
const PipelineStatisticName = @import("main.zig").PipelineStatisticName;
const QueryType = @import("main.zig").QueryType;
const Impl = @import("interface.zig").Impl;
pub const QuerySet = opaque {
pub const Descriptor = extern struct {
next_in_chain: ?*const ChainedStruct = null,
label: ?[*:0]const u8 = null,
type: QueryType,
count: u32,
pipeline_statistics: ?[*]const PipelineStatisticName = null,
pipeline_statistics_count: usize = 0,
/// Provides a slightly friendlier Zig API to initialize this structure.
pub inline fn init(v: struct {
next_in_chain: ?*const ChainedStruct = null,
label: ?[*:0]const u8 = null,
type: QueryType,
count: u32,
pipeline_statistics: ?[]const PipelineStatisticName = null,
}) Descriptor {
return .{
.next_in_chain = v.next_in_chain,
.label = v.label,
.type = v.type,
.count = v.count,
.pipeline_statistics_count = if (v.pipeline_statistics) |e| e.len else 0,
.pipeline_statistics = if (v.pipeline_statistics) |e| e.ptr else null,
};
}
};
pub inline fn destroy(query_set: *QuerySet) void {
Impl.querySetDestroy(query_set);
}
pub inline fn getCount(query_set: *QuerySet) u32 {
return Impl.querySetGetCount(query_set);
}
pub inline fn getType(query_set: *QuerySet) QueryType {
return Impl.querySetGetType(query_set);
}
pub inline fn setLabel(query_set: *QuerySet, label: [*:0]const u8) void {
Impl.querySetSetLabel(query_set, label);
}
pub inline fn reference(query_set: *QuerySet) void {
Impl.querySetReference(query_set);
}
pub inline fn release(query_set: *QuerySet) void {
Impl.querySetRelease(query_set);
}
};

101
src/sysgpu/sysgpu/queue.zig Normal file
View file

@ -0,0 +1,101 @@
const std = @import("std");
const CommandBuffer = @import("command_buffer.zig").CommandBuffer;
const Buffer = @import("buffer.zig").Buffer;
const Texture = @import("texture.zig").Texture;
const ImageCopyTexture = @import("main.zig").ImageCopyTexture;
const ImageCopyExternalTexture = @import("main.zig").ImageCopyExternalTexture;
const ChainedStruct = @import("main.zig").ChainedStruct;
const Extent3D = @import("main.zig").Extent3D;
const CopyTextureForBrowserOptions = @import("main.zig").CopyTextureForBrowserOptions;
const Impl = @import("interface.zig").Impl;
pub const Queue = opaque {
pub const WorkDoneCallback = *const fn (
status: WorkDoneStatus,
userdata: ?*anyopaque,
) callconv(.C) void;
pub const WorkDoneStatus = enum(u32) {
success = 0x00000000,
err = 0x00000001,
unknown = 0x00000002,
device_lost = 0x00000003,
};
pub const Descriptor = extern struct {
next_in_chain: ?*const ChainedStruct = null,
label: ?[*:0]const u8 = null,
};
pub inline fn copyExternalTextureForBrowser(queue: *Queue, source: *const ImageCopyExternalTexture, destination: *const ImageCopyTexture, copy_size: *const Extent3D, options: *const CopyTextureForBrowserOptions) void {
Impl.queueCopyExternalTextureForBrowser(queue, source, destination, copy_size, options);
}
pub inline fn copyTextureForBrowser(queue: *Queue, source: *const ImageCopyTexture, destination: *const ImageCopyTexture, copy_size: *const Extent3D, options: *const CopyTextureForBrowserOptions) void {
Impl.queueCopyTextureForBrowser(queue, source, destination, copy_size, options);
}
// TODO: dawn: does not allow unsetting this callback to null
pub inline fn onSubmittedWorkDone(
queue: *Queue,
signal_value: u64,
context: anytype,
comptime callback: fn (ctx: @TypeOf(context), status: WorkDoneStatus) callconv(.Inline) void,
) void {
const Context = @TypeOf(context);
const Helper = struct {
pub fn cCallback(status: WorkDoneStatus, userdata: ?*anyopaque) callconv(.C) void {
callback(if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))), status);
}
};
Impl.queueOnSubmittedWorkDone(queue, signal_value, Helper.cCallback, if (Context == void) null else context);
}
pub inline fn setLabel(queue: *Queue, label: [*:0]const u8) void {
Impl.queueSetLabel(queue, label);
}
pub inline fn submit(queue: *Queue, commands: []const *const CommandBuffer) void {
Impl.queueSubmit(queue, commands.len, commands.ptr);
}
pub inline fn writeBuffer(
queue: *Queue,
buffer: *Buffer,
buffer_offset_bytes: u64,
data_slice: anytype,
) void {
Impl.queueWriteBuffer(
queue,
buffer,
buffer_offset_bytes,
@as(*const anyopaque, @ptrCast(std.mem.sliceAsBytes(data_slice).ptr)),
data_slice.len * @sizeOf(std.meta.Elem(@TypeOf(data_slice))),
);
}
pub inline fn writeTexture(
queue: *Queue,
destination: *const ImageCopyTexture,
data_layout: *const Texture.DataLayout,
write_size: *const Extent3D,
data_slice: anytype,
) void {
Impl.queueWriteTexture(
queue,
destination,
@as(*const anyopaque, @ptrCast(std.mem.sliceAsBytes(data_slice).ptr)),
@as(usize, @intCast(data_slice.len)) * @sizeOf(std.meta.Elem(@TypeOf(data_slice))),
data_layout,
write_size,
);
}
pub inline fn reference(queue: *Queue) void {
Impl.queueReference(queue);
}
pub inline fn release(queue: *Queue) void {
Impl.queueRelease(queue);
}
};

View file

@ -0,0 +1,21 @@
const ChainedStruct = @import("main.zig").ChainedStruct;
const Impl = @import("interface.zig").Impl;
pub const RenderBundle = opaque {
pub const Descriptor = extern struct {
next_in_chain: ?*const ChainedStruct = null,
label: ?[*:0]const u8 = null,
};
pub inline fn setLabel(render_bundle: *RenderBundle, label: [*:0]const u8) void {
Impl.renderBundleSetLabel(render_bundle, label);
}
pub inline fn reference(render_bundle: *RenderBundle) void {
Impl.renderBundleReference(render_bundle);
}
pub inline fn release(render_bundle: *RenderBundle) void {
Impl.renderBundleRelease(render_bundle);
}
};

View file

@ -0,0 +1,122 @@
const Texture = @import("texture.zig").Texture;
const Buffer = @import("buffer.zig").Buffer;
const BindGroup = @import("bind_group.zig").BindGroup;
const RenderPipeline = @import("render_pipeline.zig").RenderPipeline;
const RenderBundle = @import("render_bundle.zig").RenderBundle;
const Bool32 = @import("main.zig").Bool32;
const ChainedStruct = @import("main.zig").ChainedStruct;
const IndexFormat = @import("main.zig").IndexFormat;
const Impl = @import("interface.zig").Impl;
pub const RenderBundleEncoder = opaque {
pub const Descriptor = extern struct {
next_in_chain: ?*const ChainedStruct = null,
label: ?[*:0]const u8 = null,
color_formats_count: usize = 0,
color_formats: ?[*]const Texture.Format = null,
depth_stencil_format: Texture.Format = .undefined,
sample_count: u32 = 1,
depth_read_only: Bool32 = .false,
stencil_read_only: Bool32 = .false,
/// Provides a slightly friendlier Zig API to initialize this structure.
pub inline fn init(v: struct {
next_in_chain: ?*const ChainedStruct = null,
label: ?[*:0]const u8 = null,
color_formats: ?[]const Texture.Format = null,
depth_stencil_format: Texture.Format = .undefined,
sample_count: u32 = 1,
depth_read_only: bool = false,
stencil_read_only: bool = false,
}) Descriptor {
return .{
.next_in_chain = v.next_in_chain,
.label = v.label,
.color_formats_count = if (v.color_formats) |e| e.len else 0,
.color_formats = if (v.color_formats) |e| e.ptr else null,
.depth_stencil_format = v.depth_stencil_format,
.sample_count = v.sample_count,
.depth_read_only = Bool32.from(v.depth_read_only),
.stencil_read_only = Bool32.from(v.stencil_read_only),
};
}
};
/// Default `instance_count`: 1
/// Default `first_vertex`: 0
/// Default `first_instance`: 0
pub inline fn draw(render_bundle_encoder: *RenderBundleEncoder, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32) void {
Impl.renderBundleEncoderDraw(render_bundle_encoder, vertex_count, instance_count, first_vertex, first_instance);
}
/// Default `instance_count`: 1
/// Default `first_index`: 0
/// Default `base_vertex`: 0
/// Default `first_instance`: 0
pub inline fn drawIndexed(render_bundle_encoder: *RenderBundleEncoder, index_count: u32, instance_count: u32, first_index: u32, base_vertex: i32, first_instance: u32) void {
Impl.renderBundleEncoderDrawIndexed(render_bundle_encoder, index_count, instance_count, first_index, base_vertex, first_instance);
}
pub inline fn drawIndexedIndirect(render_bundle_encoder: *RenderBundleEncoder, indirect_buffer: *Buffer, indirect_offset: u64) void {
Impl.renderBundleEncoderDrawIndexedIndirect(render_bundle_encoder, indirect_buffer, indirect_offset);
}
pub inline fn drawIndirect(render_bundle_encoder: *RenderBundleEncoder, indirect_buffer: *Buffer, indirect_offset: u64) void {
Impl.renderBundleEncoderDrawIndirect(render_bundle_encoder, indirect_buffer, indirect_offset);
}
pub inline fn finish(render_bundle_encoder: *RenderBundleEncoder, descriptor: ?*const RenderBundle.Descriptor) *RenderBundle {
return Impl.renderBundleEncoderFinish(render_bundle_encoder, descriptor);
}
pub inline fn insertDebugMarker(render_bundle_encoder: *RenderBundleEncoder, marker_label: [*:0]const u8) void {
Impl.renderBundleEncoderInsertDebugMarker(render_bundle_encoder, marker_label);
}
pub inline fn popDebugGroup(render_bundle_encoder: *RenderBundleEncoder) void {
Impl.renderBundleEncoderPopDebugGroup(render_bundle_encoder);
}
pub inline fn pushDebugGroup(render_bundle_encoder: *RenderBundleEncoder, group_label: [*:0]const u8) void {
Impl.renderBundleEncoderPushDebugGroup(render_bundle_encoder, group_label);
}
/// Default `dynamic_offsets`: `null`
pub inline fn setBindGroup(render_bundle_encoder: *RenderBundleEncoder, group_index: u32, group: *BindGroup, dynamic_offsets: ?[]const u32) void {
Impl.renderBundleEncoderSetBindGroup(
render_bundle_encoder,
group_index,
group,
if (dynamic_offsets) |v| v.len else 0,
if (dynamic_offsets) |v| v.ptr else null,
);
}
/// Default `offset`: 0
/// Default `size`: `gpu.whole_size`
pub inline fn setIndexBuffer(render_bundle_encoder: *RenderBundleEncoder, buffer: *Buffer, format: IndexFormat, offset: u64, size: u64) void {
Impl.renderBundleEncoderSetIndexBuffer(render_bundle_encoder, buffer, format, offset, size);
}
pub inline fn setLabel(render_bundle_encoder: *RenderBundleEncoder, label: [*:0]const u8) void {
Impl.renderBundleEncoderSetLabel(render_bundle_encoder, label);
}
pub inline fn setPipeline(render_bundle_encoder: *RenderBundleEncoder, pipeline: *RenderPipeline) void {
Impl.renderBundleEncoderSetPipeline(render_bundle_encoder, pipeline);
}
/// Default `offset`: 0
/// Default `size`: `gpu.whole_size`
pub inline fn setVertexBuffer(render_bundle_encoder: *RenderBundleEncoder, slot: u32, buffer: *Buffer, offset: u64, size: u64) void {
Impl.renderBundleEncoderSetVertexBuffer(render_bundle_encoder, slot, buffer, offset, size);
}
pub inline fn reference(render_bundle_encoder: *RenderBundleEncoder) void {
Impl.renderBundleEncoderReference(render_bundle_encoder);
}
pub inline fn release(render_bundle_encoder: *RenderBundleEncoder) void {
Impl.renderBundleEncoderRelease(render_bundle_encoder);
}
};

View file

@ -0,0 +1,128 @@
const Buffer = @import("buffer.zig").Buffer;
const RenderBundle = @import("render_bundle.zig").RenderBundle;
const BindGroup = @import("bind_group.zig").BindGroup;
const RenderPipeline = @import("render_pipeline.zig").RenderPipeline;
const QuerySet = @import("query_set.zig").QuerySet;
const Color = @import("main.zig").Color;
const IndexFormat = @import("main.zig").IndexFormat;
const Impl = @import("interface.zig").Impl;
pub const RenderPassEncoder = opaque {
pub inline fn beginOcclusionQuery(render_pass_encoder: *RenderPassEncoder, query_index: u32) void {
Impl.renderPassEncoderBeginOcclusionQuery(render_pass_encoder, query_index);
}
/// Default `instance_count`: 1
/// Default `first_vertex`: 0
/// Default `first_instance`: 0
pub inline fn draw(render_pass_encoder: *RenderPassEncoder, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32) void {
Impl.renderPassEncoderDraw(render_pass_encoder, vertex_count, instance_count, first_vertex, first_instance);
}
/// Default `instance_count`: 1
/// Default `first_index`: 0
/// Default `base_vertex`: 0
/// Default `first_instance`: 0
pub inline fn drawIndexed(render_pass_encoder: *RenderPassEncoder, index_count: u32, instance_count: u32, first_index: u32, base_vertex: i32, first_instance: u32) void {
Impl.renderPassEncoderDrawIndexed(render_pass_encoder, index_count, instance_count, first_index, base_vertex, first_instance);
}
pub inline fn drawIndexedIndirect(render_pass_encoder: *RenderPassEncoder, indirect_buffer: *Buffer, indirect_offset: u64) void {
Impl.renderPassEncoderDrawIndexedIndirect(render_pass_encoder, indirect_buffer, indirect_offset);
}
pub inline fn drawIndirect(render_pass_encoder: *RenderPassEncoder, indirect_buffer: *Buffer, indirect_offset: u64) void {
Impl.renderPassEncoderDrawIndirect(render_pass_encoder, indirect_buffer, indirect_offset);
}
pub inline fn end(render_pass_encoder: *RenderPassEncoder) void {
Impl.renderPassEncoderEnd(render_pass_encoder);
}
pub inline fn endOcclusionQuery(render_pass_encoder: *RenderPassEncoder) void {
Impl.renderPassEncoderEndOcclusionQuery(render_pass_encoder);
}
pub inline fn executeBundles(
render_pass_encoder: *RenderPassEncoder,
bundles: []*const RenderBundle,
) void {
Impl.renderPassEncoderExecuteBundles(
render_pass_encoder,
bundles.len,
bundles.ptr,
);
}
pub inline fn insertDebugMarker(render_pass_encoder: *RenderPassEncoder, marker_label: [*:0]const u8) void {
Impl.renderPassEncoderInsertDebugMarker(render_pass_encoder, marker_label);
}
pub inline fn popDebugGroup(render_pass_encoder: *RenderPassEncoder) void {
Impl.renderPassEncoderPopDebugGroup(render_pass_encoder);
}
pub inline fn pushDebugGroup(render_pass_encoder: *RenderPassEncoder, group_label: [*:0]const u8) void {
Impl.renderPassEncoderPushDebugGroup(render_pass_encoder, group_label);
}
/// Default `dynamic_offsets_count`: 0
/// Default `dynamic_offsets`: `null`
pub inline fn setBindGroup(render_pass_encoder: *RenderPassEncoder, group_index: u32, group: *BindGroup, dynamic_offsets: ?[]const u32) void {
Impl.renderPassEncoderSetBindGroup(
render_pass_encoder,
group_index,
group,
if (dynamic_offsets) |v| v.len else 0,
if (dynamic_offsets) |v| v.ptr else null,
);
}
pub inline fn setBlendConstant(render_pass_encoder: *RenderPassEncoder, color: *const Color) void {
Impl.renderPassEncoderSetBlendConstant(render_pass_encoder, color);
}
/// Default `offset`: 0
/// Default `size`: `gpu.whole_size`
pub inline fn setIndexBuffer(render_pass_encoder: *RenderPassEncoder, buffer: *Buffer, format: IndexFormat, offset: u64, size: u64) void {
Impl.renderPassEncoderSetIndexBuffer(render_pass_encoder, buffer, format, offset, size);
}
pub inline fn setLabel(render_pass_encoder: *RenderPassEncoder, label: [*:0]const u8) void {
Impl.renderPassEncoderSetLabel(render_pass_encoder, label);
}
pub inline fn setPipeline(render_pass_encoder: *RenderPassEncoder, pipeline: *RenderPipeline) void {
Impl.renderPassEncoderSetPipeline(render_pass_encoder, pipeline);
}
pub inline fn setScissorRect(render_pass_encoder: *RenderPassEncoder, x: u32, y: u32, width: u32, height: u32) void {
Impl.renderPassEncoderSetScissorRect(render_pass_encoder, x, y, width, height);
}
pub inline fn setStencilReference(render_pass_encoder: *RenderPassEncoder, _reference: u32) void {
Impl.renderPassEncoderSetStencilReference(render_pass_encoder, _reference);
}
/// Default `offset`: 0
/// Default `size`: `gpu.whole_size`
pub inline fn setVertexBuffer(render_pass_encoder: *RenderPassEncoder, slot: u32, buffer: *Buffer, offset: u64, size: u64) void {
Impl.renderPassEncoderSetVertexBuffer(render_pass_encoder, slot, buffer, offset, size);
}
pub inline fn setViewport(render_pass_encoder: *RenderPassEncoder, x: f32, y: f32, width: f32, height: f32, min_depth: f32, max_depth: f32) void {
Impl.renderPassEncoderSetViewport(render_pass_encoder, x, y, width, height, min_depth, max_depth);
}
pub inline fn writeTimestamp(render_pass_encoder: *RenderPassEncoder, query_set: *QuerySet, query_index: u32) void {
Impl.renderPassEncoderWriteTimestamp(render_pass_encoder, query_set, query_index);
}
pub inline fn reference(render_pass_encoder: *RenderPassEncoder) void {
Impl.renderPassEncoderReference(render_pass_encoder);
}
pub inline fn release(render_pass_encoder: *RenderPassEncoder) void {
Impl.renderPassEncoderRelease(render_pass_encoder);
}
};

View file

@ -0,0 +1,38 @@
const ChainedStruct = @import("main.zig").ChainedStruct;
const DepthStencilState = @import("main.zig").DepthStencilState;
const MultisampleState = @import("main.zig").MultisampleState;
const VertexState = @import("main.zig").VertexState;
const PrimitiveState = @import("main.zig").PrimitiveState;
const FragmentState = @import("main.zig").FragmentState;
const PipelineLayout = @import("pipeline_layout.zig").PipelineLayout;
const BindGroupLayout = @import("bind_group_layout.zig").BindGroupLayout;
const Impl = @import("interface.zig").Impl;
pub const RenderPipeline = opaque {
pub const Descriptor = extern struct {
next_in_chain: ?*const ChainedStruct = null,
label: ?[*:0]const u8 = null,
layout: ?*PipelineLayout = null,
vertex: VertexState,
primitive: PrimitiveState = .{},
depth_stencil: ?*const DepthStencilState = null,
multisample: MultisampleState = .{},
fragment: ?*const FragmentState = null,
};
pub inline fn getBindGroupLayout(render_pipeline: *RenderPipeline, group_index: u32) *BindGroupLayout {
return Impl.renderPipelineGetBindGroupLayout(render_pipeline, group_index);
}
pub inline fn setLabel(render_pipeline: *RenderPipeline, label: [*:0]const u8) void {
Impl.renderPipelineSetLabel(render_pipeline, label);
}
pub inline fn reference(render_pipeline: *RenderPipeline) void {
Impl.renderPipelineReference(render_pipeline);
}
pub inline fn release(render_pipeline: *RenderPipeline) void {
Impl.renderPipelineRelease(render_pipeline);
}
};

View file

@ -0,0 +1,52 @@
const ChainedStruct = @import("main.zig").ChainedStruct;
const FilterMode = @import("main.zig").FilterMode;
const MipmapFilterMode = @import("main.zig").MipmapFilterMode;
const CompareFunction = @import("main.zig").CompareFunction;
const Impl = @import("interface.zig").Impl;
pub const Sampler = opaque {
pub const AddressMode = enum(u32) {
repeat = 0x00000000,
mirror_repeat = 0x00000001,
clamp_to_edge = 0x00000002,
};
pub const BindingType = enum(u32) {
undefined = 0x00000000,
filtering = 0x00000001,
non_filtering = 0x00000002,
comparison = 0x00000003,
};
pub const BindingLayout = extern struct {
next_in_chain: ?*const ChainedStruct = null,
type: BindingType = .undefined,
};
pub const Descriptor = extern struct {
next_in_chain: ?*const ChainedStruct = null,
label: ?[*:0]const u8 = null,
address_mode_u: AddressMode = .clamp_to_edge,
address_mode_v: AddressMode = .clamp_to_edge,
address_mode_w: AddressMode = .clamp_to_edge,
mag_filter: FilterMode = .nearest,
min_filter: FilterMode = .nearest,
mipmap_filter: MipmapFilterMode = .nearest,
lod_min_clamp: f32 = 0.0,
lod_max_clamp: f32 = 32.0,
compare: CompareFunction = .undefined,
max_anisotropy: u16 = 1,
};
pub inline fn setLabel(sampler: *Sampler, label: [*:0]const u8) void {
Impl.samplerSetLabel(sampler, label);
}
pub inline fn reference(sampler: *Sampler) void {
Impl.samplerReference(sampler);
}
pub inline fn release(sampler: *Sampler) void {
Impl.samplerRelease(sampler);
}
};

View file

@ -0,0 +1,86 @@
const ChainedStruct = @import("main.zig").ChainedStruct;
const CompilationInfoCallback = @import("main.zig").CompilationInfoCallback;
const CompilationInfoRequestStatus = @import("main.zig").CompilationInfoRequestStatus;
const CompilationInfo = @import("main.zig").CompilationInfo;
const Impl = @import("interface.zig").Impl;
const dawn = @import("dawn.zig");
pub const ShaderModule = opaque {
pub const Descriptor = extern struct {
pub const NextInChain = extern union {
generic: ?*const ChainedStruct,
spirv_descriptor: ?*const SPIRVDescriptor,
wgsl_descriptor: ?*const WGSLDescriptor,
hlsl_descriptor: ?*const HLSLDescriptor,
msl_descriptor: ?*const MSLDescriptor,
dawn_shader_module_spirv_options_descriptor: ?*const dawn.ShaderModuleSPIRVOptionsDescriptor,
};
next_in_chain: NextInChain = .{ .generic = null },
label: ?[*:0]const u8 = null,
};
pub const SPIRVDescriptor = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .shader_module_spirv_descriptor },
code_size: u32,
code: [*]const u32,
};
pub const WGSLDescriptor = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .shader_module_wgsl_descriptor },
code: [*:0]const u8,
};
pub const HLSLDescriptor = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .shader_module_hlsl_descriptor },
code: [*]const u8,
code_size: u32,
};
pub const MSLDescriptor = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .shader_module_msl_descriptor },
code: [*]const u8,
code_size: u32,
workgroup_size: WorkgroupSize,
};
pub const WorkgroupSize = extern struct { x: u32 = 1, y: u32 = 1, z: u32 = 1 };
pub inline fn getCompilationInfo(
shader_module: *ShaderModule,
context: anytype,
comptime callback: fn (
ctx: @TypeOf(context),
status: CompilationInfoRequestStatus,
compilation_info: *const CompilationInfo,
) callconv(.Inline) void,
) void {
const Context = @TypeOf(context);
const Helper = struct {
pub fn cCallback(
status: CompilationInfoRequestStatus,
compilation_info: *const CompilationInfo,
userdata: ?*anyopaque,
) callconv(.C) void {
callback(
if (Context == void) {} else @as(Context, @ptrCast(@alignCast(userdata))),
status,
compilation_info,
);
}
};
Impl.shaderModuleGetCompilationInfo(shader_module, Helper.cCallback, if (Context == void) null else context);
}
pub inline fn setLabel(shader_module: *ShaderModule, label: [*:0]const u8) void {
Impl.shaderModuleSetLabel(shader_module, label);
}
pub inline fn reference(shader_module: *ShaderModule) void {
Impl.shaderModuleReference(shader_module);
}
pub inline fn release(shader_module: *ShaderModule) void {
Impl.shaderModuleRelease(shader_module);
}
};

View file

@ -0,0 +1,91 @@
const ChainedStruct = @import("main.zig").ChainedStruct;
const ChainedStructOut = @import("main.zig").ChainedStructOut;
pub const SharedFence = opaque {
pub const Type = enum(u32) {
shared_fence_type_undefined = 0x00000000,
shared_fence_type_vk_semaphore_opaque_fd = 0x00000001,
shared_fence_type_vk_semaphore_sync_fd = 0x00000002,
shared_fence_type_vk_semaphore_zircon_handle = 0x00000003,
shared_fence_type_dxgi_shared_handle = 0x00000004,
shared_fence_type_mtl_shared_event = 0x00000005,
};
pub const Descriptor = extern struct {
pub const NextInChain = extern union {
generic: ?*const ChainedStruct,
vk_semaphore_opaque_fd_descriptor: *const VkSemaphoreOpaqueFDDescriptor,
vk_semaphore_sync_fd_descriptor: *const VkSemaphoreSyncFDDescriptor,
vk_semaphore_zircon_handle_descriptor: *const VkSemaphoreZirconHandleDescriptor,
dxgi_shared_handle_descriptor: *const DXGISharedHandleDescriptor,
mtl_shared_event_descriptor: *const MTLSharedEventDescriptor,
};
next_in_chain: NextInChain = .{ .generic = null },
label: ?[*]const u8,
};
pub const DXGISharedHandleDescriptor = extern struct {
chain: ChainedStruct,
handle: *anyopaque,
};
pub const DXGISharedHandleExportInfo = extern struct {
chain: ChainedStructOut,
handle: *anyopaque,
};
pub const ExportInfo = extern struct {
pub const NextInChain = extern union {
generic: ?*const ChainedStructOut,
dxgi_shared_handle_export_info: *const DXGISharedHandleExportInfo,
mtl_shared_event_export_info: *const MTLSharedEventExportInfo,
vk_semaphore_opaque_fd_export_info: *const VkSemaphoreOpaqueFDExportInfo,
vk_semaphore_sync_fd_export_info: *const VkSemaphoreSyncFDExportInfo,
vk_semaphore_zircon_handle_export_info: *const VkSemaphoreZirconHandleExportInfo,
};
next_in_chain: NextInChain = .{ .generic = null },
type: Type,
};
pub const MTLSharedEventDescriptor = extern struct {
chain: ChainedStruct,
shared_event: *anyopaque,
};
pub const MTLSharedEventExportInfo = extern struct {
chain: ChainedStructOut,
shared_event: *anyopaque,
};
pub const VkSemaphoreOpaqueFDDescriptor = extern struct {
chain: ChainedStruct,
handle: c_int,
};
pub const VkSemaphoreOpaqueFDExportInfo = extern struct {
chain: ChainedStructOut,
handle: c_int,
};
pub const VkSemaphoreSyncFDDescriptor = extern struct {
chain: ChainedStruct,
handle: c_int,
};
pub const VkSemaphoreSyncFDExportInfo = extern struct {
chain: ChainedStructOut,
handle: c_int,
};
pub const VkSemaphoreZirconHandleDescriptor = extern struct {
chain: ChainedStruct,
handle: u32,
};
pub const VkSemaphoreZirconHandleExportInfo = extern struct {
chain: ChainedStructOut,
handle: u32,
};
};

View file

@ -0,0 +1,124 @@
const Texture = @import("texture.zig").Texture;
const Bool32 = @import("main.zig").Bool32;
const Extent3D = @import("main.zig").Extent3D;
const SharedFence = @import("shared_fence.zig").SharedFence;
const ChainedStruct = @import("main.zig").ChainedStruct;
const ChainedStructOut = @import("main.zig").ChainedStructOut;
pub const SharedTextureMemory = opaque {
pub const Properties = extern struct {
next_in_chain: *const ChainedStruct,
usage: Texture.UsageFlags,
size: Extent3D,
format: Texture.Format,
};
pub const VkImageDescriptor = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_vk_image_descriptor },
vk_format: i32,
vk_usage_flags: Texture.UsageFlags,
vk_extent3D: Extent3D,
};
pub const AHardwareBufferDescriptor = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_a_hardware_buffer_descriptor },
handle: *anyopaque,
};
pub const BeginAccessDescriptor = extern struct {
pub const NextInChain = extern union {
generic: ?*const ChainedStruct,
vk_image_layout_begin_state: *const VkImageLayoutBeginState,
};
next_in_chain: NextInChain = .{ .generic = null },
initialized: Bool32,
fence_count: usize,
fences: *const SharedFence,
signaled_values: *const u64,
};
pub const Descriptor = extern struct {
pub const NextInChain = extern union {
generic: ?*const ChainedStruct,
a_hardware_buffer_descriptor: *const AHardwareBufferDescriptor,
dma_buf_descriptor: *const DmaBufDescriptor,
dxgi_shared_handle_descriptor: *const DXGISharedHandleDescriptor,
egl_image_descriptor: *const EGLImageDescriptor,
io_surface_descriptor: *const IOSurfaceDescriptor,
opaque_fd_descriptor: *const OpaqueFDDescriptor,
vk_dedicated_allocation_descriptor: *const VkDedicatedAllocationDescriptor,
zircon_handle_descriptor: *const ZirconHandleDescriptor,
};
next_in_chain: NextInChain = .{ .generic = null },
label: ?[*]const u8,
};
pub const DmaBufDescriptor = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_dma_buf_descriptor },
memory_fd: c_int,
allocation_size: u64,
drm_modifier: u64,
plane_count: usize,
plane_offsets: *const u64,
plane_strides: *const u32,
};
pub const DXGISharedHandleDescriptor = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_dxgi_shared_handle_descriptor },
handle: *anyopaque,
};
pub const EGLImageDescriptor = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_egl_image_descriptor },
image: *anyopaque,
};
pub const EndAccessState = extern struct {
pub const NextInChain = extern union {
generic: ?*const ChainedStruct,
vk_image_layout_end_state: *const VkImageLayoutEndState,
};
next_in_chain: NextInChain = .{ .generic = null },
initialized: Bool32,
fence_count: usize,
fences: *const SharedFence,
signaled_values: *const u64,
};
pub const IOSurfaceDescriptor = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_io_surface_descriptor },
ioSurface: *anyopaque,
};
pub const OpaqueFDDescriptor = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_opaque_fd_descriptor },
memory_fd: c_int,
allocation_size: u64,
};
pub const VkDedicatedAllocationDescriptor = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_vk_dedicated_allocation_descriptor },
dedicated_allocation: Bool32,
};
pub const VkImageLayoutBeginState = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_vk_image_layout_begin_state },
old_layout: i32,
new_layout: i32,
};
pub const VkImageLayoutEndState = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_vk_image_layout_end_state },
old_layout: i32,
new_layout: i32,
};
pub const ZirconHandleDescriptor = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .shared_texture_memory_zircon_handle_descriptor },
memory_fd: u32,
allocation_size: u64,
};
};

View file

@ -0,0 +1,72 @@
const ChainedStruct = @import("main.zig").ChainedStruct;
const Impl = @import("interface.zig").Impl;
pub const Surface = opaque {
pub const Descriptor = extern struct {
pub const NextInChain = extern union {
generic: ?*const ChainedStruct,
from_android_native_window: *const DescriptorFromAndroidNativeWindow,
from_canvas_html_selector: *const DescriptorFromCanvasHTMLSelector,
from_metal_layer: *const DescriptorFromMetalLayer,
from_wayland_surface: *const DescriptorFromWaylandSurface,
from_windows_core_window: *const DescriptorFromWindowsCoreWindow,
from_windows_hwnd: *const DescriptorFromWindowsHWND,
from_windows_swap_chain_panel: *const DescriptorFromWindowsSwapChainPanel,
from_xlib_window: *const DescriptorFromXlibWindow,
};
next_in_chain: NextInChain = .{ .generic = null },
label: ?[*:0]const u8 = null,
};
pub const DescriptorFromAndroidNativeWindow = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .surface_descriptor_from_android_native_window },
window: *anyopaque,
};
pub const DescriptorFromCanvasHTMLSelector = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .surface_descriptor_from_canvas_html_selector },
selector: [*:0]const u8,
};
pub const DescriptorFromMetalLayer = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .surface_descriptor_from_metal_layer },
layer: *anyopaque,
};
pub const DescriptorFromWaylandSurface = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .surface_descriptor_from_wayland_surface },
display: *anyopaque,
surface: *anyopaque,
};
pub const DescriptorFromWindowsCoreWindow = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .surface_descriptor_from_windows_core_window },
core_window: *anyopaque,
};
pub const DescriptorFromWindowsHWND = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .surface_descriptor_from_windows_hwnd },
hinstance: *anyopaque,
hwnd: *anyopaque,
};
pub const DescriptorFromWindowsSwapChainPanel = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .surface_descriptor_from_windows_swap_chain_panel },
swap_chain_panel: *anyopaque,
};
pub const DescriptorFromXlibWindow = extern struct {
chain: ChainedStruct = .{ .next = null, .s_type = .surface_descriptor_from_xlib_window },
display: *anyopaque,
window: u32,
};
pub inline fn reference(surface: *Surface) void {
Impl.surfaceReference(surface);
}
pub inline fn release(surface: *Surface) void {
Impl.surfaceRelease(surface);
}
};

View file

@ -0,0 +1,37 @@
const ChainedStruct = @import("main.zig").ChainedStruct;
const PresentMode = @import("main.zig").PresentMode;
const Texture = @import("texture.zig").Texture;
const TextureView = @import("texture_view.zig").TextureView;
const Impl = @import("interface.zig").Impl;
pub const SwapChain = opaque {
pub const Descriptor = extern struct {
next_in_chain: ?*const ChainedStruct = null,
label: ?[*:0]const u8 = null,
usage: Texture.UsageFlags,
format: Texture.Format,
width: u32,
height: u32,
present_mode: PresentMode,
};
pub inline fn getCurrentTexture(swap_chain: *SwapChain) ?*Texture {
return Impl.swapChainGetCurrentTexture(swap_chain);
}
pub inline fn getCurrentTextureView(swap_chain: *SwapChain) ?*TextureView {
return Impl.swapChainGetCurrentTextureView(swap_chain);
}
pub inline fn present(swap_chain: *SwapChain) void {
Impl.swapChainPresent(swap_chain);
}
pub inline fn reference(swap_chain: *SwapChain) void {
Impl.swapChainReference(swap_chain);
}
pub inline fn release(swap_chain: *SwapChain) void {
Impl.swapChainRelease(swap_chain);
}
};

View file

@ -0,0 +1,266 @@
const std = @import("std");
const Bool32 = @import("main.zig").Bool32;
const ChainedStruct = @import("main.zig").ChainedStruct;
const TextureView = @import("texture_view.zig").TextureView;
const Extent3D = @import("main.zig").Extent3D;
const Impl = @import("interface.zig").Impl;
const types = @import("main.zig");
const dawn = @import("dawn.zig");
pub const Texture = opaque {
pub const Aspect = enum(u32) {
all = 0x00000000,
stencil_only = 0x00000001,
depth_only = 0x00000002,
plane0_only = 0x00000003,
plane1_only = 0x00000004,
};
pub const Dimension = enum(u32) {
dimension_1d = 0x00000000,
dimension_2d = 0x00000001,
dimension_3d = 0x00000002,
};
pub const Format = enum(u32) {
undefined = 0x00000000,
r8_unorm = 0x00000001,
r8_snorm = 0x00000002,
r8_uint = 0x00000003,
r8_sint = 0x00000004,
r16_uint = 0x00000005,
r16_sint = 0x00000006,
r16_float = 0x00000007,
rg8_unorm = 0x00000008,
rg8_snorm = 0x00000009,
rg8_uint = 0x0000000a,
rg8_sint = 0x0000000b,
r32_float = 0x0000000c,
r32_uint = 0x0000000d,
r32_sint = 0x0000000e,
rg16_uint = 0x0000000f,
rg16_sint = 0x00000010,
rg16_float = 0x00000011,
rgba8_unorm = 0x00000012,
rgba8_unorm_srgb = 0x00000013,
rgba8_snorm = 0x00000014,
rgba8_uint = 0x00000015,
rgba8_sint = 0x00000016,
bgra8_unorm = 0x00000017,
bgra8_unorm_srgb = 0x00000018,
rgb10_a2_unorm = 0x00000019,
rg11_b10_ufloat = 0x0000001a,
rgb9_e5_ufloat = 0x0000001b,
rg32_float = 0x0000001c,
rg32_uint = 0x0000001d,
rg32_sint = 0x0000001e,
rgba16_uint = 0x0000001f,
rgba16_sint = 0x00000020,
rgba16_float = 0x00000021,
rgba32_float = 0x00000022,
rgba32_uint = 0x00000023,
rgba32_sint = 0x00000024,
stencil8 = 0x00000025,
depth16_unorm = 0x00000026,
depth24_plus = 0x00000027,
depth24_plus_stencil8 = 0x00000028,
depth32_float = 0x00000029,
depth32_float_stencil8 = 0x0000002a,
bc1_rgba_unorm = 0x0000002b,
bc1_rgba_unorm_srgb = 0x0000002c,
bc2_rgba_unorm = 0x0000002d,
bc2_rgba_unorm_srgb = 0x0000002e,
bc3_rgba_unorm = 0x0000002f,
bc3_rgba_unorm_srgb = 0x00000030,
bc4_runorm = 0x00000031,
bc4_rsnorm = 0x00000032,
bc5_rg_unorm = 0x00000033,
bc5_rg_snorm = 0x00000034,
bc6_hrgb_ufloat = 0x00000035,
bc6_hrgb_float = 0x00000036,
bc7_rgba_unorm = 0x00000037,
bc7_rgba_unorm_srgb = 0x00000038,
etc2_rgb8_unorm = 0x00000039,
etc2_rgb8_unorm_srgb = 0x0000003a,
etc2_rgb8_a1_unorm = 0x0000003b,
etc2_rgb8_a1_unorm_srgb = 0x0000003c,
etc2_rgba8_unorm = 0x0000003d,
etc2_rgba8_unorm_srgb = 0x0000003e,
eacr11_unorm = 0x0000003f,
eacr11_snorm = 0x00000040,
eacrg11_unorm = 0x00000041,
eacrg11_snorm = 0x00000042,
astc4x4_unorm = 0x00000043,
astc4x4_unorm_srgb = 0x00000044,
astc5x4_unorm = 0x00000045,
astc5x4_unorm_srgb = 0x00000046,
astc5x5_unorm = 0x00000047,
astc5x5_unorm_srgb = 0x00000048,
astc6x5_unorm = 0x00000049,
astc6x5_unorm_srgb = 0x0000004a,
astc6x6_unorm = 0x0000004b,
astc6x6_unorm_srgb = 0x0000004c,
astc8x5_unorm = 0x0000004d,
astc8x5_unorm_srgb = 0x0000004e,
astc8x6_unorm = 0x0000004f,
astc8x6_unorm_srgb = 0x00000050,
astc8x8_unorm = 0x00000051,
astc8x8_unorm_srgb = 0x00000052,
astc10x5_unorm = 0x00000053,
astc10x5_unorm_srgb = 0x00000054,
astc10x6_unorm = 0x00000055,
astc10x6_unorm_srgb = 0x00000056,
astc10x8_unorm = 0x00000057,
astc10x8_unorm_srgb = 0x00000058,
astc10x10_unorm = 0x00000059,
astc10x10_unorm_srgb = 0x0000005a,
astc12x10_unorm = 0x0000005b,
astc12x10_unorm_srgb = 0x0000005c,
astc12x12_unorm = 0x0000005d,
astc12x12_unorm_srgb = 0x0000005e,
r8_bg8_biplanar420_unorm = 0x0000005f,
};
pub const SampleType = enum(u32) {
undefined = 0x00000000,
float = 0x00000001,
unfilterable_float = 0x00000002,
depth = 0x00000003,
sint = 0x00000004,
uint = 0x00000005,
};
pub const UsageFlags = packed struct(u32) {
copy_src: bool = false,
copy_dst: bool = false,
texture_binding: bool = false,
storage_binding: bool = false,
render_attachment: bool = false,
transient_attachment: bool = false,
_padding: u26 = 0,
comptime {
std.debug.assert(
@sizeOf(@This()) == @sizeOf(u32) and
@bitSizeOf(@This()) == @bitSizeOf(u32),
);
}
pub const none = UsageFlags{};
pub fn equal(a: UsageFlags, b: UsageFlags) bool {
return @as(u6, @truncate(@as(u32, @bitCast(a)))) == @as(u6, @truncate(@as(u32, @bitCast(b))));
}
};
pub const BindingLayout = extern struct {
next_in_chain: ?*const ChainedStruct = null,
sample_type: SampleType = .undefined,
view_dimension: TextureView.Dimension = .dimension_undefined,
multisampled: Bool32 = .false,
};
pub const DataLayout = extern struct {
next_in_chain: ?*const ChainedStruct = null,
offset: u64 = 0,
bytes_per_row: u32 = types.copy_stride_undefined,
rows_per_image: u32 = types.copy_stride_undefined,
};
pub const Descriptor = extern struct {
pub const NextInChain = extern union {
generic: ?*const ChainedStruct,
dawn_texture_internal_usage_descriptor: *const dawn.TextureInternalUsageDescriptor,
};
next_in_chain: NextInChain = .{ .generic = null },
label: ?[*:0]const u8 = null,
usage: UsageFlags,
dimension: Dimension = .dimension_2d,
size: Extent3D,
format: Format,
mip_level_count: u32 = 1,
sample_count: u32 = 1,
view_format_count: usize = 0,
view_formats: ?[*]const Format = null,
/// Provides a slightly friendlier Zig API to initialize this structure.
pub inline fn init(v: struct {
next_in_chain: NextInChain = .{ .generic = null },
label: ?[*:0]const u8 = null,
usage: UsageFlags,
dimension: Dimension = .dimension_2d,
size: Extent3D,
format: Format,
mip_level_count: u32 = 1,
sample_count: u32 = 1,
view_formats: ?[]const Format = null,
}) Descriptor {
return .{
.next_in_chain = v.next_in_chain,
.label = v.label,
.usage = v.usage,
.dimension = v.dimension,
.size = v.size,
.format = v.format,
.mip_level_count = v.mip_level_count,
.sample_count = v.sample_count,
.view_format_count = if (v.view_formats) |e| e.len else 0,
.view_formats = if (v.view_formats) |e| e.ptr else null,
};
}
};
pub inline fn createView(texture: *Texture, descriptor: ?*const TextureView.Descriptor) *TextureView {
return Impl.textureCreateView(texture, descriptor);
}
pub inline fn destroy(texture: *Texture) void {
Impl.textureDestroy(texture);
}
pub inline fn getDepthOrArrayLayers(texture: *Texture) u32 {
return Impl.textureGetDepthOrArrayLayers(texture);
}
pub inline fn getDimension(texture: *Texture) Dimension {
return Impl.textureGetDimension(texture);
}
pub inline fn getFormat(texture: *Texture) Format {
return Impl.textureGetFormat(texture);
}
pub inline fn getHeight(texture: *Texture) u32 {
return Impl.textureGetHeight(texture);
}
pub inline fn getMipLevelCount(texture: *Texture) u32 {
return Impl.textureGetMipLevelCount(texture);
}
pub inline fn getSampleCount(texture: *Texture) u32 {
return Impl.textureGetSampleCount(texture);
}
pub inline fn getUsage(texture: *Texture) UsageFlags {
return Impl.textureGetUsage(texture);
}
pub inline fn getWidth(texture: *Texture) u32 {
return Impl.textureGetWidth(texture);
}
pub inline fn setLabel(texture: *Texture, label: [*:0]const u8) void {
Impl.textureSetLabel(texture, label);
}
pub inline fn reference(texture: *Texture) void {
Impl.textureReference(texture);
}
pub inline fn release(texture: *Texture) void {
Impl.textureRelease(texture);
}
};

View file

@ -0,0 +1,40 @@
const ChainedStruct = @import("main.zig").ChainedStruct;
const Texture = @import("texture.zig").Texture;
const Impl = @import("interface.zig").Impl;
const types = @import("main.zig");
pub const TextureView = opaque {
pub const Dimension = enum(u32) {
dimension_undefined = 0x00000000,
dimension_1d = 0x00000001,
dimension_2d = 0x00000002,
dimension_2d_array = 0x00000003,
dimension_cube = 0x00000004,
dimension_cube_array = 0x00000005,
dimension_3d = 0x00000006,
};
pub const Descriptor = extern struct {
next_in_chain: ?*const ChainedStruct = null,
label: ?[*:0]const u8 = null,
format: Texture.Format = .undefined,
dimension: Dimension = .dimension_undefined,
base_mip_level: u32 = 0,
mip_level_count: u32 = types.mip_level_count_undefined,
base_array_layer: u32 = 0,
array_layer_count: u32 = types.array_layer_count_undefined,
aspect: Texture.Aspect = .all,
};
pub inline fn setLabel(texture_view: *TextureView, label: [*:0]const u8) void {
Impl.textureViewSetLabel(texture_view, label);
}
pub inline fn reference(texture_view: *TextureView) void {
Impl.textureViewReference(texture_view);
}
pub inline fn release(texture_view: *TextureView) void {
Impl.textureViewRelease(texture_view);
}
};

View file

@ -0,0 +1,696 @@
//! Borrowed from Zig compiler codebase with changes.
//! Licensed under LICENSE-ZIG
const std = @import("std");
const g = @import("spirv/grammar.zig");
const Allocator = std.mem.Allocator;
const ExtendedStructSet = std.StringHashMap(void);
pub fn main() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = arena.allocator();
const args = try std.process.argsAlloc(allocator);
if (args.len != 2) {
usageAndExit(std.io.getStdErr(), args[0], 1);
}
const spec_path = args[1];
const spec = try std.fs.cwd().readFileAlloc(allocator, spec_path, std.math.maxInt(usize));
// Required for json parsing.
@setEvalBranchQuota(10000);
var scanner = std.json.Scanner.initCompleteInput(allocator, spec);
var diagnostics = std.json.Diagnostics{};
scanner.enableDiagnostics(&diagnostics);
const parsed = std.json.parseFromTokenSource(g.CoreRegistry, allocator, &scanner, .{}) catch |err| {
std.debug.print("line,col: {},{}\n", .{ diagnostics.getLine(), diagnostics.getColumn() });
return err;
};
var bw = std.io.bufferedWriter(std.io.getStdOut().writer());
try render(bw.writer(), allocator, parsed.value);
try bw.flush();
}
/// Returns a set with types that require an extra struct for the `Instruction` interface
/// to the spir-v spec, or whether the original type can be used.
fn extendedStructs(
arena: Allocator,
kinds: []const g.OperandKind,
) !ExtendedStructSet {
var map = ExtendedStructSet.init(arena);
try map.ensureTotalCapacity(@intCast(kinds.len));
for (kinds) |kind| {
const enumerants = kind.enumerants orelse continue;
for (enumerants) |enumerant| {
if (enumerant.parameters.len > 0) {
break;
}
} else continue;
map.putAssumeCapacity(kind.kind, {});
}
return map;
}
// Return a score for a particular priority. Duplicate instruction/operand enum values are
// removed by picking the tag with the lowest score to keep, and by making an alias for the
// other. Note that the tag does not need to be just a tag at this point, in which case it
// gets the lowest score automatically anyway.
fn tagPriorityScore(tag: []const u8) usize {
if (tag.len == 0) {
return 1;
} else if (std.mem.eql(u8, tag, "EXT")) {
return 2;
} else if (std.mem.eql(u8, tag, "KHR")) {
return 3;
} else {
return 4;
}
}
fn render(writer: anytype, allocator: Allocator, registry: g.CoreRegistry) !void {
try writer.writeAll(
\\//! Borrowed from Zig compiler codebase with changes.
\\//! Licensed under LICENSE-ZIG
\\//!
\\//! This file is auto-generated by tools/gen_spirv_spec.zig.
\\
\\pub const Version = packed struct(Word) {
\\ padding: u8 = 0,
\\ minor: u8,
\\ major: u8,
\\ padding0: u8 = 0,
\\
\\ pub fn toWord(self: @This()) Word {
\\ return @bitCast(self);
\\ }
\\};
\\
\\pub const Word = u32;
\\pub const IdResult = struct{
\\ id: Word,
\\};
\\pub const IdResultType = IdResult;
\\pub const IdRef = IdResult;
\\
\\pub const IdMemorySemantics = IdRef;
\\pub const IdScope = IdRef;
\\
\\pub const LiteralInteger = Word;
\\pub const LiteralString = []const u8;
\\pub const LiteralContextDependentNumber = union(enum) {
\\ int32: i32,
\\ uint32: u32,
\\ int64: i64,
\\ uint64: u64,
\\ float32: f32,
\\ float64: f64,
\\};
\\pub const LiteralExtInstInteger = struct{ inst: Word };
\\pub const LiteralSpecConstantOpInteger = struct { opcode: Opcode };
\\pub const PairLiteralIntegerIdRef = struct { value: LiteralInteger, label: IdRef };
\\pub const PairIdRefLiteralInteger = struct { target: IdRef, member: LiteralInteger };
\\pub const PairIdRefIdRef = [2]IdRef;
\\
\\pub const Quantifier = enum {
\\ required,
\\ optional,
\\ variadic,
\\};
\\
\\pub const Operand = struct {
\\ kind: OperandKind,
\\ quantifier: Quantifier,
\\};
\\
\\pub const OperandCategory = enum {
\\ bit_enum,
\\ value_enum,
\\ id,
\\ literal,
\\ composite,
\\};
\\
\\pub const Enumerant = struct {
\\ name: []const u8,
\\ value: Word,
\\ parameters: []const OperandKind,
\\};
\\
\\
);
try writer.print(
\\pub const version = Version{{ .major = {}, .minor = {}, .patch = {} }};
\\pub const magic_number: Word = {s};
\\
\\
,
.{ registry.major_version, registry.minor_version, registry.revision, registry.magic_number },
);
const extended_structs = try extendedStructs(allocator, registry.operand_kinds);
try renderClass(writer, allocator, registry.instructions);
try renderOperandKind(writer, registry.operand_kinds);
try renderOpcodes(writer, allocator, registry.instructions, extended_structs);
try renderOperandKinds(writer, allocator, registry.operand_kinds, extended_structs);
}
fn renderClass(writer: anytype, allocator: Allocator, instructions: []const g.Instruction) !void {
var class_map = std.StringArrayHashMap(void).init(allocator);
for (instructions) |inst| {
if (std.mem.eql(u8, inst.class.?, "@exclude")) {
continue;
}
try class_map.put(inst.class.?, {});
}
try writer.writeAll("pub const Class = enum {\n");
for (class_map.keys()) |class| {
try renderInstructionClass(writer, class);
try writer.writeAll(",\n");
}
try writer.writeAll("};\n");
}
fn renderInstructionClass(writer: anytype, class: []const u8) !void {
// Just assume that these wont clobber zig builtin types.
var prev_was_sep = true;
for (class) |c| {
switch (c) {
'-', '_' => prev_was_sep = true,
else => if (prev_was_sep) {
try writer.writeByte(std.ascii.toUpper(c));
prev_was_sep = false;
} else {
try writer.writeByte(std.ascii.toLower(c));
},
}
}
}
fn renderOperandKind(writer: anytype, operands: []const g.OperandKind) !void {
try writer.writeAll("pub const OperandKind = enum {\n");
for (operands) |operand| {
try writer.print("{},\n", .{std.zig.fmtId(operand.kind)});
}
try writer.writeAll(
\\
\\pub fn category(self: OperandKind) OperandCategory {
\\return switch (self) {
\\
);
for (operands) |operand| {
const cat = switch (operand.category) {
.BitEnum => "bit_enum",
.ValueEnum => "value_enum",
.Id => "id",
.Literal => "literal",
.Composite => "composite",
};
try writer.print(".{} => .{s},\n", .{ std.zig.fmtId(operand.kind), cat });
}
try writer.writeAll(
\\};
\\}
\\pub fn enumerants(self: OperandKind) []const Enumerant {
\\return switch (self) {
\\
);
for (operands) |operand| {
switch (operand.category) {
.BitEnum, .ValueEnum => {},
else => {
try writer.print(".{} => unreachable,\n", .{std.zig.fmtId(operand.kind)});
continue;
},
}
try writer.print(".{} => &[_]Enumerant{{", .{std.zig.fmtId(operand.kind)});
for (operand.enumerants.?) |enumerant| {
if (enumerant.value == .bitflag and std.mem.eql(u8, enumerant.enumerant, "None")) {
continue;
}
try renderEnumerant(writer, enumerant);
try writer.writeAll(",");
}
try writer.writeAll("},\n");
}
try writer.writeAll("};\n}\n};\n");
}
fn renderEnumerant(writer: anytype, enumerant: g.Enumerant) !void {
try writer.print(".{{.name = \"{s}\", .value = ", .{enumerant.enumerant});
switch (enumerant.value) {
.bitflag => |flag| try writer.writeAll(flag),
.int => |int| try writer.print("{}", .{int}),
}
try writer.writeAll(", .parameters = &[_]OperandKind{");
for (enumerant.parameters, 0..) |param, i| {
if (i != 0)
try writer.writeAll(", ");
// Note, param.quantifier will always be one.
try writer.print(".{}", .{std.zig.fmtId(param.kind)});
}
try writer.writeAll("}}");
}
fn renderOpcodes(
writer: anytype,
allocator: Allocator,
instructions: []const g.Instruction,
extended_structs: ExtendedStructSet,
) !void {
var inst_map = std.AutoArrayHashMap(u32, usize).init(allocator);
try inst_map.ensureTotalCapacity(instructions.len);
var aliases = std.ArrayList(struct { inst: usize, alias: usize }).init(allocator);
try aliases.ensureTotalCapacity(instructions.len);
for (instructions, 0..) |inst, i| {
if (std.mem.eql(u8, inst.class.?, "@exclude")) {
continue;
}
const result = inst_map.getOrPutAssumeCapacity(inst.opcode);
if (!result.found_existing) {
result.value_ptr.* = i;
continue;
}
const existing = instructions[result.value_ptr.*];
const tag_index = std.mem.indexOfDiff(u8, inst.opname, existing.opname).?;
const inst_priority = tagPriorityScore(inst.opname[tag_index..]);
const existing_priority = tagPriorityScore(existing.opname[tag_index..]);
if (inst_priority < existing_priority) {
aliases.appendAssumeCapacity(.{ .inst = result.value_ptr.*, .alias = i });
result.value_ptr.* = i;
} else {
aliases.appendAssumeCapacity(.{ .inst = i, .alias = result.value_ptr.* });
}
}
const instructions_indices = inst_map.values();
try writer.writeAll("pub const Opcode = enum(u16) {\n");
for (instructions_indices) |i| {
const inst = instructions[i];
try writer.print("{} = {},\n", .{ std.zig.fmtId(inst.opname), inst.opcode });
}
try writer.writeByte('\n');
for (aliases.items) |alias| {
try writer.print("pub const {} = Opcode.{};\n", .{
std.zig.fmtId(instructions[alias.inst].opname),
std.zig.fmtId(instructions[alias.alias].opname),
});
}
try writer.writeAll(
\\
\\pub fn Operands(comptime self: Opcode) type {
\\return switch (self) {
\\
);
for (instructions_indices) |i| {
const inst = instructions[i];
try renderOperand(writer, .instruction, inst.opname, inst.operands, extended_structs);
}
try writer.writeAll(
\\};
\\}
\\pub fn operands(self: Opcode) []const Operand {
\\return switch (self) {
\\
);
for (instructions_indices) |i| {
const inst = instructions[i];
try writer.print(".{} => &[_]Operand{{", .{std.zig.fmtId(inst.opname)});
for (inst.operands) |operand| {
const quantifier = if (operand.quantifier) |q|
switch (q) {
.@"?" => "optional",
.@"*" => "variadic",
}
else
"required";
try writer.print(".{{.kind = .{s}, .quantifier = .{s}}},", .{ operand.kind, quantifier });
}
try writer.writeAll("},\n");
}
try writer.writeAll(
\\};
\\}
\\pub fn class(self: Opcode) Class {
\\return switch (self) {
\\
);
for (instructions_indices) |i| {
const inst = instructions[i];
try writer.print(".{} => .", .{std.zig.fmtId(inst.opname)});
try renderInstructionClass(writer, inst.class.?);
try writer.writeAll(",\n");
}
try writer.writeAll("};\n}\n};\n");
}
fn renderOperandKinds(
writer: anytype,
allocator: Allocator,
kinds: []const g.OperandKind,
extended_structs: ExtendedStructSet,
) !void {
for (kinds) |kind| {
switch (kind.category) {
.ValueEnum => try renderValueEnum(writer, allocator, kind, extended_structs),
.BitEnum => try renderBitEnum(writer, allocator, kind, extended_structs),
else => {},
}
}
}
fn renderValueEnum(
writer: anytype,
allocator: Allocator,
enumeration: g.OperandKind,
extended_structs: ExtendedStructSet,
) !void {
const enumerants = enumeration.enumerants orelse return error.InvalidRegistry;
var enum_map = std.AutoArrayHashMap(u32, usize).init(allocator);
try enum_map.ensureTotalCapacity(enumerants.len);
var aliases = std.ArrayList(struct { enumerant: usize, alias: usize }).init(allocator);
try aliases.ensureTotalCapacity(enumerants.len);
for (enumerants, 0..) |enumerant, i| {
const result = enum_map.getOrPutAssumeCapacity(enumerant.value.int);
if (!result.found_existing) {
result.value_ptr.* = i;
continue;
}
const existing = enumerants[result.value_ptr.*];
const tag_index = std.mem.indexOfDiff(u8, enumerant.enumerant, existing.enumerant).?;
const enum_priority = tagPriorityScore(enumerant.enumerant[tag_index..]);
const existing_priority = tagPriorityScore(existing.enumerant[tag_index..]);
if (enum_priority < existing_priority) {
aliases.appendAssumeCapacity(.{ .enumerant = result.value_ptr.*, .alias = i });
result.value_ptr.* = i;
} else {
aliases.appendAssumeCapacity(.{ .enumerant = i, .alias = result.value_ptr.* });
}
}
const enum_indices = enum_map.values();
try writer.print("pub const {s} = enum(u32) {{\n", .{std.zig.fmtId(enumeration.kind)});
for (enum_indices) |i| {
const enumerant = enumerants[i];
if (enumerant.value != .int) return error.InvalidRegistry;
try writer.print("{} = {},\n", .{ std.zig.fmtId(enumerant.enumerant), enumerant.value.int });
}
try writer.writeByte('\n');
for (aliases.items) |alias| {
try writer.print("pub const {} = {}.{};\n", .{
std.zig.fmtId(enumerants[alias.enumerant].enumerant),
std.zig.fmtId(enumeration.kind),
std.zig.fmtId(enumerants[alias.alias].enumerant),
});
}
if (!extended_structs.contains(enumeration.kind)) {
try writer.writeAll("};\n");
return;
}
try writer.print("\npub const Extended = union({}) {{\n", .{std.zig.fmtId(enumeration.kind)});
for (enum_indices) |i| {
const enumerant = enumerants[i];
try renderOperand(writer, .@"union", enumerant.enumerant, enumerant.parameters, extended_structs);
}
try writer.writeAll("};\n};\n");
}
fn renderBitEnum(
writer: anytype,
allocator: Allocator,
enumeration: g.OperandKind,
extended_structs: ExtendedStructSet,
) !void {
try writer.print("pub const {s} = packed struct {{\n", .{std.zig.fmtId(enumeration.kind)});
var flags_by_bitpos = [_]?usize{null} ** 32;
const enumerants = enumeration.enumerants orelse return error.InvalidRegistry;
var aliases = std.ArrayList(struct { flag: usize, alias: u5 }).init(allocator);
try aliases.ensureTotalCapacity(enumerants.len);
for (enumerants, 0..) |enumerant, i| {
if (enumerant.value != .bitflag) return error.InvalidRegistry;
const value = try parseHexInt(enumerant.value.bitflag);
if (value == 0) {
continue; // Skip 'none' items
}
std.debug.assert(@popCount(value) == 1);
const bitpos = std.math.log2_int(u32, value);
if (flags_by_bitpos[bitpos]) |*existing| {
const tag_index = std.mem.indexOfDiff(u8, enumerant.enumerant, enumerants[existing.*].enumerant).?;
const enum_priority = tagPriorityScore(enumerant.enumerant[tag_index..]);
const existing_priority = tagPriorityScore(enumerants[existing.*].enumerant[tag_index..]);
if (enum_priority < existing_priority) {
aliases.appendAssumeCapacity(.{ .flag = existing.*, .alias = bitpos });
existing.* = i;
} else {
aliases.appendAssumeCapacity(.{ .flag = i, .alias = bitpos });
}
} else {
flags_by_bitpos[bitpos] = i;
}
}
for (flags_by_bitpos, 0..) |maybe_flag_index, bitpos| {
if (maybe_flag_index) |flag_index| {
try writer.print("{}", .{std.zig.fmtId(enumerants[flag_index].enumerant)});
} else {
try writer.print("_reserved_bit_{}", .{bitpos});
}
try writer.writeAll(": bool = false,\n");
}
try writer.writeByte('\n');
for (aliases.items) |alias| {
try writer.print("pub const {}: {} = .{{.{} = true}};\n", .{
std.zig.fmtId(enumerants[alias.flag].enumerant),
std.zig.fmtId(enumeration.kind),
std.zig.fmtId(enumerants[flags_by_bitpos[alias.alias].?].enumerant),
});
}
if (!extended_structs.contains(enumeration.kind)) {
try writer.writeAll("};\n");
return;
}
try writer.print("\npub const Extended = struct {{\n", .{});
for (flags_by_bitpos, 0..) |maybe_flag_index, bitpos| {
const flag_index = maybe_flag_index orelse {
try writer.print("_reserved_bit_{}: bool = false,\n", .{bitpos});
continue;
};
const enumerant = enumerants[flag_index];
try renderOperand(writer, .mask, enumerant.enumerant, enumerant.parameters, extended_structs);
}
try writer.writeAll("};\n};\n");
}
fn renderOperand(
writer: anytype,
kind: enum {
@"union",
instruction,
mask,
},
field_name: []const u8,
parameters: []const g.Operand,
extended_structs: ExtendedStructSet,
) !void {
if (kind == .instruction) {
try writer.writeByte('.');
}
try writer.print("{}", .{std.zig.fmtId(field_name)});
if (parameters.len == 0) {
switch (kind) {
.@"union" => try writer.writeAll(",\n"),
.instruction => try writer.writeAll(" => void,\n"),
.mask => try writer.writeAll(": bool = false,\n"),
}
return;
}
if (kind == .instruction) {
try writer.writeAll(" => ");
} else {
try writer.writeAll(": ");
}
if (kind == .mask) {
try writer.writeByte('?');
}
try writer.writeAll("struct{");
for (parameters, 0..) |param, j| {
if (j != 0) {
try writer.writeAll(", ");
}
try renderFieldName(writer, parameters, j);
try writer.writeAll(": ");
if (param.quantifier) |q| {
switch (q) {
.@"?" => try writer.writeByte('?'),
.@"*" => try writer.writeAll("[]const "),
}
}
try writer.print("{}", .{std.zig.fmtId(param.kind)});
if (extended_structs.contains(param.kind)) {
try writer.writeAll(".Extended");
}
if (param.quantifier) |q| {
switch (q) {
.@"?" => try writer.writeAll(" = null"),
.@"*" => try writer.writeAll(" = &.{}"),
}
}
}
try writer.writeAll("}");
if (kind == .mask) {
try writer.writeAll(" = null");
}
try writer.writeAll(",\n");
}
fn renderFieldName(writer: anytype, operands: []const g.Operand, field_index: usize) !void {
const operand = operands[field_index];
// Should be enough for all names - adjust as needed.
var name_buffer = std.BoundedArray(u8, 64){
.buffer = undefined,
};
derive_from_kind: {
// Operand names are often in the json encoded as "'Name'" (with two sets of quotes).
// Additionally, some operands have ~ in them at the end (D~ref~).
const name = std.mem.trim(u8, operand.name, "'~");
if (name.len == 0) {
break :derive_from_kind;
}
// Some names have weird characters in them (like newlines) - skip any such ones.
// Use the same loop to transform to snake-case.
for (name) |c| {
switch (c) {
'a'...'z', '0'...'9' => try name_buffer.append(c),
'A'...'Z' => try name_buffer.append(std.ascii.toLower(c)),
' ', '~' => try name_buffer.append('_'),
else => break :derive_from_kind,
}
}
// Assume there are no duplicate 'name' fields.
try writer.print("{}", .{std.zig.fmtId(name_buffer.slice())});
return;
}
// Translate to snake case.
name_buffer.len = 0;
for (operand.kind, 0..) |c, i| {
switch (c) {
'a'...'z', '0'...'9' => try name_buffer.append(c),
'A'...'Z' => if (i > 0 and std.ascii.isLower(operand.kind[i - 1])) {
try name_buffer.appendSlice(&[_]u8{ '_', std.ascii.toLower(c) });
} else {
try name_buffer.append(std.ascii.toLower(c));
},
else => unreachable, // Assume that the name is valid C-syntax (and contains no underscores).
}
}
try writer.print("{}", .{std.zig.fmtId(name_buffer.slice())});
// For fields derived from type name, there could be any amount.
// Simply check against all other fields, and if another similar one exists, add a number.
const need_extra_index = for (operands, 0..) |other_operand, i| {
if (i != field_index and std.mem.eql(u8, operand.kind, other_operand.kind)) {
break true;
}
} else false;
if (need_extra_index) {
try writer.print("_{}", .{field_index});
}
}
fn parseHexInt(text: []const u8) !u31 {
const prefix = "0x";
if (!std.mem.startsWith(u8, text, prefix))
return error.InvalidHexInt;
return try std.fmt.parseInt(u31, text[prefix.len..], 16);
}
fn usageAndExit(file: std.fs.File, arg0: []const u8, code: u8) noreturn {
file.writer().print(
\\Usage: {s} <spirv json spec>
\\
\\Generates Zig bindings for a SPIR-V specification .json (either core or
\\extinst versions). The result, printed to stdout, should be used to update
\\files in src/codegen/spirv. Don't forget to format the output.
\\
\\The relevant specifications can be obtained from the SPIR-V registry:
\\https://github.com/KhronosGroup/SPIRV-Headers/blob/master/include/spirv/unified1/
\\
, .{arg0}) catch std.process.exit(1);
std.process.exit(code);
}

View file

@ -0,0 +1,110 @@
//! Borrowed from Zig compiler codebase with changes.
//! Licensed under LICENSE-ZIG
//!
//! See https://www.khronos.org/registry/spir-v/specs/unified1/MachineReadableGrammar.html
//! and the files in https://github.com/KhronosGroup/SPIRV-Headers/blob/master/include/spirv/unified1/
//! Note: Non-canonical casing in these structs used to match SPIR-V spec json.
const std = @import("std");
pub const Registry = union(enum) {
core: CoreRegistry,
extension: ExtensionRegistry,
};
pub const CoreRegistry = struct {
copyright: [][]const u8,
/// Hexadecimal representation of the magic number
magic_number: []const u8,
major_version: u32,
minor_version: u32,
revision: u32,
instruction_printing_class: []InstructionPrintingClass,
instructions: []Instruction,
operand_kinds: []OperandKind,
};
pub const ExtensionRegistry = struct {
copyright: [][]const u8,
version: u32,
revision: u32,
instructions: []Instruction,
operand_kinds: []OperandKind = &[_]OperandKind{},
};
pub const InstructionPrintingClass = struct {
tag: []const u8,
heading: ?[]const u8 = null,
};
pub const Instruction = struct {
opname: []const u8,
class: ?[]const u8 = null, // Note: Only available in the core registry.
opcode: u32,
operands: []Operand = &[_]Operand{},
capabilities: [][]const u8 = &[_][]const u8{},
extensions: [][]const u8 = &[_][]const u8{},
version: ?[]const u8 = null,
lastVersion: ?[]const u8 = null,
};
pub const Operand = struct {
kind: []const u8,
/// If this field is 'null', the operand is only expected once.
quantifier: ?Quantifier = null,
name: []const u8 = "",
};
pub const Quantifier = enum {
/// zero or once
@"?",
/// zero or more
@"*",
};
pub const OperandCategory = enum {
BitEnum,
ValueEnum,
Id,
Literal,
Composite,
};
pub const OperandKind = struct {
category: OperandCategory,
/// The name
kind: []const u8,
doc: ?[]const u8 = null,
enumerants: ?[]Enumerant = null,
bases: ?[]const []const u8 = null,
};
pub const Enumerant = struct {
enumerant: []const u8,
value: union(enum) {
bitflag: []const u8, // Hexadecimal representation of the value
int: u31,
pub fn jsonParse(
allocator: std.mem.Allocator,
source: anytype,
options: std.json.ParseOptions,
) std.json.ParseError(@TypeOf(source.*))!@This() {
_ = options;
switch (try source.nextAlloc(allocator, .alloc_if_needed)) {
inline .string, .allocated_string => |s| return @This(){ .bitflag = s },
inline .number, .allocated_number => |s| return @This(){ .int = try std.fmt.parseInt(u31, s, 10) },
else => return error.UnexpectedToken,
}
}
pub const jsonStringify = @compileError("not supported");
},
capabilities: [][]const u8 = &[_][]const u8{},
/// Valid for .ValueEnum and .BitEnum
extensions: [][]const u8 = &[_][]const u8{},
/// `quantifier` will always be `null`.
parameters: []Operand = &[_]Operand{},
version: ?[]const u8 = null,
lastVersion: ?[]const u8 = null,
};

View file

@ -0,0 +1,4 @@
ls zig-out/spirv/ | while read -r file
do
spirv-val zig-out/spirv/$file
done

419
src/sysgpu/utils.zig Normal file
View file

@ -0,0 +1,419 @@
const std = @import("std");
const limits = @import("limits.zig");
const shader = @import("shader.zig");
const sysgpu = @import("sysgpu/main.zig");
pub fn Manager(comptime T: type) type {
return struct {
count: u32 = 1,
pub fn reference(manager: *@This()) void {
_ = @atomicRmw(u32, &manager.count, .Add, 1, .Monotonic);
}
pub fn release(manager: *@This()) void {
if (@atomicRmw(u32, &manager.count, .Sub, 1, .Release) == 1) {
@fence(.Acquire);
const parent = @fieldParentPtr(T, "manager", manager);
parent.deinit();
}
}
};
}
pub fn findChained(comptime T: type, next_in_chain: ?*const sysgpu.ChainedStruct) ?*const T {
const search = @as(*align(1) const sysgpu.ChainedStruct, @ptrCast(std.meta.fieldInfo(T, .chain).default_value.?));
var chain = next_in_chain;
while (chain) |c| {
if (c.s_type == search.s_type) {
return @as(*const T, @ptrCast(c));
}
chain = c.next;
}
return null;
}
pub fn alignUp(x: usize, a: usize) usize {
return (x + a - 1) / a * a;
}
pub const FormatType = enum {
float,
unorm,
unorm_srgb,
snorm,
uint,
sint,
depth,
stencil,
depth_stencil,
};
pub fn vertexFormatType(format: sysgpu.VertexFormat) FormatType {
return switch (format) {
.undefined => unreachable,
.uint8x2 => .uint,
.uint8x4 => .uint,
.sint8x2 => .sint,
.sint8x4 => .sint,
.unorm8x2 => .unorm,
.unorm8x4 => .unorm,
.snorm8x2 => .snorm,
.snorm8x4 => .snorm,
.uint16x2 => .uint,
.uint16x4 => .uint,
.sint16x2 => .sint,
.sint16x4 => .sint,
.unorm16x2 => .unorm,
.unorm16x4 => .unorm,
.snorm16x2 => .snorm,
.snorm16x4 => .snorm,
.float16x2 => .float,
.float16x4 => .float,
.float32 => .float,
.float32x2 => .float,
.float32x3 => .float,
.float32x4 => .float,
.uint32 => .uint,
.uint32x2 => .uint,
.uint32x3 => .uint,
.uint32x4 => .uint,
.sint32 => .sint,
.sint32x2 => .sint,
.sint32x3 => .sint,
.sint32x4 => .sint,
};
}
pub fn textureFormatType(format: sysgpu.Texture.Format) FormatType {
return switch (format) {
.undefined => unreachable,
.r8_unorm => .unorm,
.r8_snorm => .snorm,
.r8_uint => .uint,
.r8_sint => .sint,
.r16_uint => .uint,
.r16_sint => .sint,
.r16_float => .float,
.rg8_unorm => .unorm,
.rg8_snorm => .snorm,
.rg8_uint => .uint,
.rg8_sint => .sint,
.r32_float => .float,
.r32_uint => .uint,
.r32_sint => .sint,
.rg16_uint => .uint,
.rg16_sint => .sint,
.rg16_float => .float,
.rgba8_unorm => .unorm,
.rgba8_unorm_srgb => .unorm_srgb,
.rgba8_snorm => .snorm,
.rgba8_uint => .uint,
.rgba8_sint => .sint,
.bgra8_unorm => .unorm,
.bgra8_unorm_srgb => .unorm_srgb,
.rgb10_a2_unorm => .unorm,
.rg11_b10_ufloat => .float,
.rgb9_e5_ufloat => .float,
.rg32_float => .float,
.rg32_uint => .uint,
.rg32_sint => .sint,
.rgba16_uint => .uint,
.rgba16_sint => .sint,
.rgba16_float => .float,
.rgba32_float => .float,
.rgba32_uint => .uint,
.rgba32_sint => .sint,
.stencil8 => .stencil,
.depth16_unorm => .depth,
.depth24_plus => .depth,
.depth24_plus_stencil8 => .depth_stencil,
.depth32_float => .depth,
.depth32_float_stencil8 => .depth_stencil,
.bc1_rgba_unorm => .unorm,
.bc1_rgba_unorm_srgb => .unorm_srgb,
.bc2_rgba_unorm => .unorm,
.bc2_rgba_unorm_srgb => .unorm_srgb,
.bc3_rgba_unorm => .unorm,
.bc3_rgba_unorm_srgb => .unorm_srgb,
.bc4_runorm => .unorm,
.bc4_rsnorm => .snorm,
.bc5_rg_unorm => .unorm,
.bc5_rg_snorm => .snorm,
.bc6_hrgb_ufloat => .float,
.bc6_hrgb_float => .float,
.bc7_rgba_unorm => .unorm,
.bc7_rgba_unorm_srgb => .snorm,
.etc2_rgb8_unorm => .unorm,
.etc2_rgb8_unorm_srgb => .unorm_srgb,
.etc2_rgb8_a1_unorm => .unorm_srgb,
.etc2_rgb8_a1_unorm_srgb => .unorm,
.etc2_rgba8_unorm => .unorm,
.etc2_rgba8_unorm_srgb => .unorm_srgb,
.eacr11_unorm => .unorm,
.eacr11_snorm => .snorm,
.eacrg11_unorm => .unorm,
.eacrg11_snorm => .snorm,
.astc4x4_unorm => .unorm,
.astc4x4_unorm_srgb => .unorm_srgb,
.astc5x4_unorm => .unorm,
.astc5x4_unorm_srgb => .unorm_srgb,
.astc5x5_unorm => .unorm,
.astc5x5_unorm_srgb => .unorm_srgb,
.astc6x5_unorm => .unorm,
.astc6x5_unorm_srgb => .unorm_srgb,
.astc6x6_unorm => .unorm,
.astc6x6_unorm_srgb => .unorm_srgb,
.astc8x5_unorm => .unorm,
.astc8x5_unorm_srgb => .unorm_srgb,
.astc8x6_unorm => .unorm,
.astc8x6_unorm_srgb => .unorm_srgb,
.astc8x8_unorm => .unorm,
.astc8x8_unorm_srgb => .unorm_srgb,
.astc10x5_unorm => .unorm,
.astc10x5_unorm_srgb => .unorm_srgb,
.astc10x6_unorm => .unorm,
.astc10x6_unorm_srgb => .unorm_srgb,
.astc10x8_unorm => .unorm,
.astc10x8_unorm_srgb => .unorm_srgb,
.astc10x10_unorm => .unorm,
.astc10x10_unorm_srgb => .unorm_srgb,
.astc12x10_unorm => .unorm,
.astc12x10_unorm_srgb => .unorm_srgb,
.astc12x12_unorm => .unorm,
.astc12x12_unorm_srgb => .unorm_srgb,
.r8_bg8_biplanar420_unorm => .unorm,
};
}
pub fn formatHasDepthOrStencil(format: sysgpu.Texture.Format) bool {
return switch (textureFormatType(format)) {
.depth, .stencil, .depth_stencil => true,
else => false,
};
}
pub fn calcOrigin(dimension: sysgpu.Texture.Dimension, origin: sysgpu.Origin3D) struct {
x: u32,
y: u32,
z: u32,
array_slice: u32,
} {
return .{
.x = origin.x,
.y = origin.y,
.z = if (dimension == .dimension_3d) origin.z else 0,
.array_slice = if (dimension == .dimension_3d) 0 else origin.z,
};
}
pub fn calcExtent(dimension: sysgpu.Texture.Dimension, extent: sysgpu.Extent3D) struct {
width: u32,
height: u32,
depth: u32,
array_count: u32,
} {
return .{
.width = extent.width,
.height = extent.height,
.depth = if (dimension == .dimension_3d) extent.depth_or_array_layers else 1,
.array_count = if (dimension == .dimension_3d) 0 else extent.depth_or_array_layers,
};
}
pub const DefaultPipelineLayoutDescriptor = struct {
pub const Group = std.ArrayListUnmanaged(sysgpu.BindGroupLayout.Entry);
allocator: std.mem.Allocator,
groups: std.BoundedArray(Group, limits.max_bind_groups) = .{},
pub fn init(allocator: std.mem.Allocator) DefaultPipelineLayoutDescriptor {
return .{ .allocator = allocator };
}
pub fn deinit(desc: *DefaultPipelineLayoutDescriptor) void {
for (desc.groups.slice()) |*group| {
group.deinit(desc.allocator);
}
}
pub fn addFunction(
desc: *DefaultPipelineLayoutDescriptor,
air: *const shader.Air,
stage: sysgpu.ShaderStageFlags,
entry_point: [*:0]const u8,
) !void {
if (air.findFunction(std.mem.span(entry_point))) |fn_inst| {
const global_var_ref_list = air.refToList(fn_inst.global_var_refs);
for (global_var_ref_list) |global_var_inst_idx| {
const var_inst = air.getInst(global_var_inst_idx).@"var";
if (var_inst.addr_space == .workgroup)
continue;
const var_type = air.getInst(var_inst.type);
const group: u32 = @intCast(air.resolveInt(var_inst.group) orelse return error.ConstExpr);
const binding: u32 = @intCast(air.resolveInt(var_inst.binding) orelse return error.ConstExpr);
var entry: sysgpu.BindGroupLayout.Entry = .{ .binding = binding, .visibility = stage };
switch (var_type) {
.sampler_type => entry.sampler.type = .filtering,
.comparison_sampler_type => entry.sampler.type = .comparison,
.texture_type => |texture| {
switch (texture.kind) {
.storage_1d,
.storage_2d,
.storage_2d_array,
.storage_3d,
=> {
entry.storage_texture.access = .undefined; // TODO - write_only
entry.storage_texture.format = switch (texture.texel_format) {
.none => unreachable,
.rgba8unorm => .rgba8_unorm,
.rgba8snorm => .rgba8_snorm,
.bgra8unorm => .bgra8_unorm,
.rgba16float => .rgba16_float,
.r32float => .r32_float,
.rg32float => .rg32_float,
.rgba32float => .rgba32_float,
.rgba8uint => .rgba8_uint,
.rgba16uint => .rgba16_uint,
.r32uint => .r32_uint,
.rg32uint => .rg32_uint,
.rgba32uint => .rgba32_uint,
.rgba8sint => .rgba8_sint,
.rgba16sint => .rgba16_sint,
.r32sint => .r32_sint,
.rg32sint => .rg32_sint,
.rgba32sint => .rgba32_sint,
};
entry.storage_texture.view_dimension = switch (texture.kind) {
.storage_1d => .dimension_1d,
.storage_2d => .dimension_2d,
.storage_2d_array => .dimension_2d_array,
.storage_3d => .dimension_3d,
else => unreachable,
};
},
else => {
// sample_type
entry.texture.sample_type =
switch (texture.kind) {
.depth_2d,
.depth_2d_array,
.depth_cube,
.depth_cube_array,
=> .depth,
else => switch (texture.texel_format) {
.none => .float, // TODO - is this right?
.rgba8unorm,
.rgba8snorm,
.bgra8unorm,
.rgba16float,
.r32float,
.rg32float,
.rgba32float,
=> .float, // TODO - unfilterable
.rgba8uint,
.rgba16uint,
.r32uint,
.rg32uint,
.rgba32uint,
=> .uint,
.rgba8sint,
.rgba16sint,
.r32sint,
.rg32sint,
.rgba32sint,
=> .sint,
},
};
entry.texture.view_dimension = switch (texture.kind) {
.sampled_1d,
.storage_1d,
=> .dimension_1d,
.sampled_2d,
.multisampled_2d,
.multisampled_depth_2d,
.storage_2d,
.depth_2d,
=> .dimension_2d,
.sampled_2d_array,
.storage_2d_array,
.depth_2d_array,
=> .dimension_2d_array,
.sampled_3d,
.storage_3d,
=> .dimension_3d,
.sampled_cube,
.depth_cube,
=> .dimension_cube,
.sampled_cube_array,
.depth_cube_array,
=> .dimension_cube_array,
};
entry.texture.multisampled = switch (texture.kind) {
.multisampled_2d,
.multisampled_depth_2d,
=> .true,
else => .false,
};
},
}
},
else => {
switch (var_inst.addr_space) {
.uniform => entry.buffer.type = .uniform,
.storage => {
if (var_inst.access_mode == .read) {
entry.buffer.type = .read_only_storage;
} else {
entry.buffer.type = .storage;
}
},
else => std.debug.panic("unhandled addr_space\n", .{}),
}
},
}
while (desc.groups.len <= group) {
desc.groups.appendAssumeCapacity(.{});
}
var append = true;
var group_entries = &desc.groups.buffer[group];
for (group_entries.items) |*previous_entry| {
if (previous_entry.binding == binding) {
// TODO - bitfield or?
if (entry.visibility.vertex)
previous_entry.visibility.vertex = true;
if (entry.visibility.fragment)
previous_entry.visibility.fragment = true;
if (entry.visibility.compute)
previous_entry.visibility.compute = true;
if (previous_entry.buffer.min_binding_size < entry.buffer.min_binding_size) {
previous_entry.buffer.min_binding_size = entry.buffer.min_binding_size;
}
if (previous_entry.texture.sample_type != entry.texture.sample_type) {
if (previous_entry.texture.sample_type == .unfilterable_float and entry.texture.sample_type == .float) {
previous_entry.texture.sample_type = .float;
} else if (previous_entry.texture.sample_type == .float and entry.texture.sample_type == .unfilterable_float) {
// ignore
} else {
return error.IncompatibleEntries;
}
}
// TODO - any other differences return error
append = false;
break;
}
}
if (append)
try group_entries.append(desc.allocator, entry);
}
}
}
};

3612
src/sysgpu/vulkan.zig Normal file

File diff suppressed because it is too large Load diff

511
src/sysgpu/vulkan/conv.zig Normal file
View file

@ -0,0 +1,511 @@
const vk = @import("vulkan");
const sysgpu = @import("../sysgpu/main.zig");
const vulkan = @import("../vulkan.zig");
const utils = @import("../utils.zig");
pub fn stencilEnable(stencil: sysgpu.StencilFaceState) bool {
return stencil.compare != .always or stencil.fail_op != .keep or stencil.depth_fail_op != .keep or stencil.pass_op != .keep;
}
pub fn sysgpuAdapterType(device_type: vk.PhysicalDeviceType) sysgpu.Adapter.Type {
return switch (device_type) {
.integrated_gpu => .integrated_gpu,
.discrete_gpu => .discrete_gpu,
.cpu => .cpu,
else => .unknown,
};
}
pub fn vulkanAccessFlagsForBufferRead(usage: sysgpu.Buffer.UsageFlags) vk.AccessFlags {
return .{
.indirect_command_read_bit = usage.indirect,
.index_read_bit = usage.index,
.vertex_attribute_read_bit = usage.vertex,
.uniform_read_bit = usage.uniform,
.shader_read_bit = usage.storage,
.host_read_bit = usage.map_read,
};
}
pub fn vulkanAccessFlagsForImageRead(usage: sysgpu.Texture.UsageFlags, format: sysgpu.Texture.Format) vk.AccessFlags {
return .{
.shader_read_bit = usage.texture_binding or usage.storage_binding,
.color_attachment_read_bit = usage.render_attachment and !utils.formatHasDepthOrStencil(format),
.depth_stencil_attachment_read_bit = usage.render_attachment and utils.formatHasDepthOrStencil(format),
};
}
pub fn vulkanBlendOp(op: sysgpu.BlendOperation) vk.BlendOp {
return switch (op) {
.add => .add,
.subtract => .subtract,
.reverse_subtract => .reverse_subtract,
.min => .min,
.max => .max,
};
}
pub fn vulkanBlendFactor(op: sysgpu.BlendFactor, color: bool) vk.BlendFactor {
return switch (op) {
.zero => .zero,
.one => .one,
.src => .src_color,
.one_minus_src => .one_minus_src_color,
.src_alpha => .src_alpha,
.one_minus_src_alpha => .one_minus_src_alpha,
.dst => .dst_color,
.one_minus_dst => .one_minus_dst_color,
.dst_alpha => .dst_alpha,
.one_minus_dst_alpha => .one_minus_dst_alpha,
.src_alpha_saturated => .src_alpha_saturate,
.constant => if (color) .constant_color else .constant_alpha,
.one_minus_constant => if (color) .one_minus_constant_color else .one_minus_constant_alpha,
.src1 => .src1_color,
.one_minus_src1 => .one_minus_src1_color,
.src1_alpha => .src1_alpha,
.one_minus_src1_alpha => .one_minus_src1_alpha,
};
}
pub fn vulkanBufferUsageFlags(flags: sysgpu.Buffer.UsageFlags) vk.BufferUsageFlags {
return .{
.transfer_src_bit = flags.copy_src,
.transfer_dst_bit = flags.copy_dst or flags.query_resolve,
.uniform_buffer_bit = flags.uniform,
.storage_buffer_bit = flags.storage,
.index_buffer_bit = flags.index,
.vertex_buffer_bit = flags.vertex,
.indirect_buffer_bit = flags.indirect,
};
}
pub fn vulkanCompareOp(op: sysgpu.CompareFunction) vk.CompareOp {
return switch (op) {
.never => .never,
.less => .less,
.less_equal => .less_or_equal,
.greater => .greater,
.greater_equal => .greater_or_equal,
.equal => .equal,
.not_equal => .not_equal,
.always => .always,
.undefined => unreachable,
};
}
pub fn vulkanCullMode(cull_mode: sysgpu.CullMode) vk.CullModeFlags {
return .{
.front_bit = cull_mode == .front,
.back_bit = cull_mode == .back,
};
}
pub fn vulkanDepthBias(ds: ?*const sysgpu.DepthStencilState) f32 {
if (ds == null) return 0;
return @floatFromInt(ds.?.depth_bias);
}
pub fn vulkanDepthBiasClamp(ds: ?*const sysgpu.DepthStencilState) f32 {
if (ds == null) return 0;
return ds.?.depth_bias_clamp;
}
pub fn vulkanDepthBiasSlopeScale(ds: ?*const sysgpu.DepthStencilState) f32 {
if (ds == null) return 0;
return ds.?.depth_bias_slope_scale;
}
pub fn vulkanDescriptorType(entry: sysgpu.BindGroupLayout.Entry) vk.DescriptorType {
switch (entry.buffer.type) {
.undefined => {},
.uniform => if (entry.buffer.has_dynamic_offset == .true) {
return .uniform_buffer_dynamic;
} else {
return .uniform_buffer;
},
.storage,
.read_only_storage,
=> if (entry.buffer.has_dynamic_offset == .true) {
return .storage_buffer_dynamic;
} else {
return .storage_buffer;
},
}
switch (entry.sampler.type) {
.undefined => {},
else => return .sampler,
}
switch (entry.texture.sample_type) {
.undefined => {},
else => return .sampled_image,
}
switch (entry.storage_texture.format) {
.undefined => {},
else => return .storage_image,
}
unreachable;
}
pub fn vulkanFilter(filter: sysgpu.FilterMode) vk.Filter {
return switch (filter) {
.nearest => .nearest,
.linear => .linear,
};
}
pub fn vulkanFormat(device: *const vulkan.Device, format: sysgpu.Texture.Format) vk.Format {
return switch (format) {
.r8_unorm => .r8_unorm,
.r8_snorm => .r8_snorm,
.r8_uint => .r8_uint,
.r8_sint => .r8_sint,
.r16_uint => .r16_uint,
.r16_sint => .r16_sint,
.r16_float => .r16_sfloat,
.rg8_unorm => .r8g8_unorm,
.rg8_snorm => .r8g8_snorm,
.rg8_uint => .r8g8_uint,
.rg8_sint => .r8g8_sint,
.r32_float => .r32_sfloat,
.r32_uint => .r32_uint,
.r32_sint => .r32_sint,
.rg16_uint => .r16g16_uint,
.rg16_sint => .r16g16_sint,
.rg16_float => .r16g16_sfloat,
.rgba8_unorm => .r8g8b8a8_unorm,
.rgba8_unorm_srgb => .r8g8b8a8_srgb,
.rgba8_snorm => .r8g8b8a8_snorm,
.rgba8_uint => .r8g8b8a8_uint,
.rgba8_sint => .r8g8b8a8_sint,
.bgra8_unorm => .b8g8r8a8_unorm,
.bgra8_unorm_srgb => .b8g8r8a8_srgb,
.rgb10_a2_unorm => .a2r10g10b10_unorm_pack32,
.rg11_b10_ufloat => .b10g11r11_ufloat_pack32,
.rgb9_e5_ufloat => .e5b9g9r9_ufloat_pack32,
.rg32_float => .r32g32_sfloat,
.rg32_uint => .r32g32_uint,
.rg32_sint => .r32g32_sint,
.rgba16_uint => .r16g16b16a16_uint,
.rgba16_sint => .r16g16b16a16_sint,
.rgba16_float => .r16g16b16a16_sfloat,
.rgba32_float => .r32g32b32a32_sfloat,
.rgba32_uint => .r32g32b32a32_uint,
.rgba32_sint => .r32g32b32a32_sint,
.stencil8 => if (device.supported_ds_formats.get(.s8_uint) != null) {
return .s8_uint;
} else {
return vulkanFormat(device, .depth24_plus_stencil8);
},
.depth16_unorm => .d16_unorm,
.depth24_plus => .d32_sfloat,
.depth24_plus_stencil8 => if (device.supported_ds_formats.get(.d24_unorm_s8_uint) != null) {
return .d24_unorm_s8_uint;
} else {
return .d32_sfloat_s8_uint;
},
.depth32_float => .d32_sfloat,
.depth32_float_stencil8 => .d32_sfloat_s8_uint,
.bc1_rgba_unorm => .bc1_rgba_unorm_block,
.bc1_rgba_unorm_srgb => .bc1_rgba_srgb_block,
.bc2_rgba_unorm => .bc2_unorm_block,
.bc2_rgba_unorm_srgb => .bc2_srgb_block,
.bc3_rgba_unorm => .bc3_unorm_block,
.bc3_rgba_unorm_srgb => .bc3_srgb_block,
.bc4_runorm => .bc4_unorm_block,
.bc4_rsnorm => .bc4_snorm_block,
.bc5_rg_unorm => .bc5_unorm_block,
.bc5_rg_snorm => .bc5_snorm_block,
.bc6_hrgb_ufloat => .bc6h_ufloat_block,
.bc6_hrgb_float => .bc6h_sfloat_block,
.bc7_rgba_unorm => .bc7_unorm_block,
.bc7_rgba_unorm_srgb => .bc7_srgb_block,
.etc2_rgb8_unorm => .etc2_r8g8b8_unorm_block,
.etc2_rgb8_unorm_srgb => .etc2_r8g8b8_srgb_block,
.etc2_rgb8_a1_unorm => .etc2_r8g8b8a1_unorm_block,
.etc2_rgb8_a1_unorm_srgb => .etc2_r8g8b8a1_srgb_block,
.etc2_rgba8_unorm => .etc2_r8g8b8a8_unorm_block,
.etc2_rgba8_unorm_srgb => .etc2_r8g8b8a8_srgb_block,
.eacr11_unorm => .eac_r11_unorm_block,
.eacr11_snorm => .eac_r11_snorm_block,
.eacrg11_unorm => .eac_r11g11_unorm_block,
.eacrg11_snorm => .eac_r11g11_snorm_block,
.astc4x4_unorm => .astc_4x_4_unorm_block,
.astc4x4_unorm_srgb => .astc_4x_4_srgb_block,
.astc5x4_unorm => .astc_5x_4_unorm_block,
.astc5x4_unorm_srgb => .astc_5x_4_srgb_block,
.astc5x5_unorm => .astc_5x_5_unorm_block,
.astc5x5_unorm_srgb => .astc_5x_5_srgb_block,
.astc6x5_unorm => .astc_6x_5_unorm_block,
.astc6x5_unorm_srgb => .astc_6x_5_srgb_block,
.astc6x6_unorm => .astc_6x_6_unorm_block,
.astc6x6_unorm_srgb => .astc_6x_6_srgb_block,
.astc8x5_unorm => .astc_8x_5_unorm_block,
.astc8x5_unorm_srgb => .astc_8x_5_srgb_block,
.astc8x6_unorm => .astc_8x_6_unorm_block,
.astc8x6_unorm_srgb => .astc_8x_6_srgb_block,
.astc8x8_unorm => .astc_8x_8_unorm_block,
.astc8x8_unorm_srgb => .astc_8x_8_srgb_block,
.astc10x5_unorm => .astc_1_0x_5_unorm_block,
.astc10x5_unorm_srgb => .astc_1_0x_5_srgb_block,
.astc10x6_unorm => .astc_1_0x_6_unorm_block,
.astc10x6_unorm_srgb => .astc_1_0x_6_srgb_block,
.astc10x8_unorm => .astc_1_0x_8_unorm_block,
.astc10x8_unorm_srgb => .astc_1_0x_8_srgb_block,
.astc10x10_unorm => .astc_1_0x_10_unorm_block,
.astc10x10_unorm_srgb => .astc_1_0x_10_srgb_block,
.astc12x10_unorm => .astc_1_2x_10_unorm_block,
.astc12x10_unorm_srgb => .astc_1_2x_10_srgb_block,
.astc12x12_unorm => .astc_1_2x_12_unorm_block,
.astc12x12_unorm_srgb => .astc_1_2x_12_srgb_block,
.r8_bg8_biplanar420_unorm => .g8_b8r8_2plane_420_unorm,
.undefined => unreachable,
};
}
pub fn vulkanFrontFace(front_face: sysgpu.FrontFace) vk.FrontFace {
return switch (front_face) {
.ccw => vk.FrontFace.counter_clockwise,
.cw => vk.FrontFace.clockwise,
};
}
pub fn vulkanImageAspectFlags(aspect: sysgpu.Texture.Aspect, format: sysgpu.Texture.Format) vk.ImageAspectFlags {
return switch (aspect) {
.all => vulkanImageAspectFlagsForFormat(format),
.stencil_only => .{ .stencil_bit = true },
.depth_only => .{ .depth_bit = true },
.plane0_only => .{ .plane_0_bit = true },
.plane1_only => .{ .plane_1_bit = true },
};
}
pub fn vulkanImageAspectFlagsForFormat(format: sysgpu.Texture.Format) vk.ImageAspectFlags {
return switch (format) {
.stencil8 => .{ .stencil_bit = true },
.depth16_unorm, .depth24_plus, .depth32_float => .{ .depth_bit = true },
.depth24_plus_stencil8, .depth32_float_stencil8 => .{ .depth_bit = true, .stencil_bit = true },
.r8_bg8_biplanar420_unorm => .{ .plane_0_bit = true, .plane_1_bit = true },
else => .{ .color_bit = true },
};
}
pub fn vulkanImageCreateFlags(cube_compatible: bool, view_format_count: usize) vk.ImageCreateFlags {
return .{
.mutable_format_bit = view_format_count > 0,
.cube_compatible_bit = cube_compatible,
};
}
pub fn vulkanImageLayoutForRead(usage: sysgpu.Texture.UsageFlags, format: sysgpu.Texture.Format) vk.ImageLayout {
// In case where we do not read, use an appropriate write state to avoid unnecessary layout changes
return if (usage.texture_binding)
.shader_read_only_optimal
else if (usage.render_attachment and utils.formatHasDepthOrStencil(format))
.depth_stencil_attachment_optimal
else if (usage.render_attachment)
.color_attachment_optimal
else
.general;
}
pub fn vulkanImageLayoutForTextureBinding(sample_type: sysgpu.Texture.SampleType) vk.ImageLayout {
return switch (sample_type) {
.undefined => .general,
else => .shader_read_only_optimal,
};
}
pub fn vulkanImageType(dimension: sysgpu.Texture.Dimension) vk.ImageType {
return switch (dimension) {
.dimension_1d => .@"1d",
.dimension_2d => .@"2d",
.dimension_3d => .@"3d",
};
}
pub fn vulkanImageUsageFlags(usage: sysgpu.Texture.UsageFlags, format: sysgpu.Texture.Format) vk.ImageUsageFlags {
return .{
.transfer_src_bit = usage.copy_src,
.transfer_dst_bit = usage.copy_dst,
.sampled_bit = usage.texture_binding,
.storage_bit = usage.storage_binding,
.color_attachment_bit = usage.render_attachment and !utils.formatHasDepthOrStencil(format),
.transient_attachment_bit = usage.transient_attachment,
.depth_stencil_attachment_bit = usage.render_attachment and utils.formatHasDepthOrStencil(format),
};
}
pub fn vulkanImageViewType(dimension: sysgpu.TextureView.Dimension) vk.ImageViewType {
return switch (dimension) {
.dimension_undefined => unreachable,
.dimension_1d => .@"1d",
.dimension_2d => .@"2d",
.dimension_2d_array => .@"2d_array",
.dimension_cube => .cube,
.dimension_cube_array => .cube_array,
.dimension_3d => .@"3d",
};
}
pub fn vulkanIndexType(format: sysgpu.IndexFormat) vk.IndexType {
return switch (format) {
.undefined => unreachable,
.uint16 => .uint16,
.uint32 => .uint32,
};
}
pub fn vulkanLoadOp(op: sysgpu.LoadOp) vk.AttachmentLoadOp {
return switch (op) {
.load => .load,
.clear => .clear,
.undefined => .dont_care,
};
}
pub fn vulkanPipelineStageFlagsForBufferRead(usage: sysgpu.Buffer.UsageFlags) vk.PipelineStageFlags {
return .{
.draw_indirect_bit = usage.indirect,
.vertex_input_bit = usage.index or usage.vertex,
.vertex_shader_bit = usage.uniform or usage.storage,
.fragment_shader_bit = usage.uniform or usage.storage,
.compute_shader_bit = usage.uniform or usage.storage,
.host_bit = usage.map_read,
};
}
pub fn vulkanPipelineStageFlagsForImageRead(usage: sysgpu.Texture.UsageFlags, format: sysgpu.Texture.Format) vk.PipelineStageFlags {
return .{
.vertex_shader_bit = usage.texture_binding or usage.storage_binding,
.fragment_shader_bit = usage.texture_binding or usage.storage_binding,
.early_fragment_tests_bit = usage.render_attachment and utils.formatHasDepthOrStencil(format),
.late_fragment_tests_bit = usage.render_attachment and utils.formatHasDepthOrStencil(format),
.color_attachment_output_bit = usage.render_attachment and !utils.formatHasDepthOrStencil(format),
.compute_shader_bit = usage.texture_binding or usage.storage_binding,
};
}
pub fn vulkanPrimitiveTopology(topology: sysgpu.PrimitiveTopology) vk.PrimitiveTopology {
return switch (topology) {
.point_list => .point_list,
.line_list => .line_list,
.line_strip => .line_strip,
.triangle_list => .triangle_list,
.triangle_strip => .triangle_strip,
};
}
pub fn vulkanPresentMode(present_mode: sysgpu.PresentMode) vk.PresentModeKHR {
return switch (present_mode) {
.immediate => .immediate_khr,
.fifo => .fifo_khr,
.mailbox => .mailbox_khr,
};
}
pub fn vulkanSampleCount(samples: u32) vk.SampleCountFlags {
// TODO: https://github.com/Snektron/vulkan-zig/issues/27
return switch (samples) {
1 => .{ .@"1_bit" = true },
2 => .{ .@"2_bit" = true },
4 => .{ .@"4_bit" = true },
8 => .{ .@"8_bit" = true },
16 => .{ .@"16_bit" = true },
32 => .{ .@"32_bit" = true },
else => unreachable,
};
}
pub fn vulkanSamplerAddressMode(address_mode: sysgpu.Sampler.AddressMode) vk.SamplerAddressMode {
return switch (address_mode) {
.repeat => .repeat,
.mirror_repeat => .mirrored_repeat,
.clamp_to_edge => .clamp_to_edge,
};
}
pub fn vulkanSamplerMipmapMode(filter: sysgpu.MipmapFilterMode) vk.SamplerMipmapMode {
return switch (filter) {
.nearest => .nearest,
.linear => .linear,
};
}
pub fn vulkanShaderStageFlags(flags: sysgpu.ShaderStageFlags) vk.ShaderStageFlags {
return .{
.vertex_bit = flags.vertex,
.fragment_bit = flags.fragment,
.compute_bit = flags.compute,
};
}
pub fn vulkanStencilOp(op: sysgpu.StencilOperation) vk.StencilOp {
return switch (op) {
.keep => .keep,
.zero => .zero,
.replace => .replace,
.invert => .invert,
.increment_clamp => .increment_and_clamp,
.decrement_clamp => .decrement_and_clamp,
.increment_wrap => .increment_and_wrap,
.decrement_wrap => .decrement_and_wrap,
};
}
pub fn vulkanStoreOp(op: sysgpu.StoreOp) vk.AttachmentStoreOp {
return switch (op) {
.store => .store,
.discard => .dont_care,
.undefined => .dont_care,
};
}
pub fn vulkanVertexFormat(format: sysgpu.VertexFormat) vk.Format {
return switch (format) {
.uint8x2 => .r8g8_uint,
.uint8x4 => .r8g8b8a8_uint,
.sint8x2 => .r8g8_sint,
.sint8x4 => .r8g8b8a8_sint,
.unorm8x2 => .r8g8_unorm,
.unorm8x4 => .r8g8b8a8_unorm,
.snorm8x2 => .r8g8_snorm,
.snorm8x4 => .r8g8b8a8_snorm,
.uint16x2 => .r16g16_uint,
.uint16x4 => .r16g16b16a16_uint,
.sint16x2 => .r16g16_sint,
.sint16x4 => .r16g16b16a16_sint,
.unorm16x2 => .r16g16_unorm,
.unorm16x4 => .r16g16b16a16_unorm,
.snorm16x2 => .r16g16_snorm,
.snorm16x4 => .r16g16b16a16_snorm,
.float16x2 => .r16g16_sfloat,
.float16x4 => .r16g16b16a16_sfloat,
.float32 => .r32_sfloat,
.float32x2 => .r32g32_sfloat,
.float32x3 => .r32g32b32_sfloat,
.float32x4 => .r32g32b32a32_sfloat,
.uint32 => .r32_uint,
.uint32x2 => .r32g32_uint,
.uint32x3 => .r32g32b32_uint,
.uint32x4 => .r32g32b32a32_uint,
.sint32 => .r32_sint,
.sint32x2 => .r32g32_sint,
.sint32x3 => .r32g32b32_sint,
.sint32x4 => .r32g32b32a32_sint,
.undefined => unreachable,
};
}
pub fn vulkanVertexInputRate(step_mode: sysgpu.VertexStepMode) vk.VertexInputRate {
return switch (step_mode) {
.vertex => .vertex,
.instance => .instance,
.vertex_buffer_not_used => unreachable,
};
}

122
src/sysgpu/vulkan/proc.zig Normal file
View file

@ -0,0 +1,122 @@
const std = @import("std");
const builtin = @import("builtin");
const vk = @import("vulkan");
pub const BaseFunctions = vk.BaseWrapper(.{
.createInstance = true,
.enumerateInstanceExtensionProperties = true,
.enumerateInstanceLayerProperties = true,
.getInstanceProcAddr = true,
});
pub const InstanceFunctions = vk.InstanceWrapper(.{
.createDevice = true,
// TODO: renderdoc will not work with wayland
// .createWaylandSurfaceKHR = builtin.target.os.tag == .linux,
.createWin32SurfaceKHR = builtin.target.os.tag == .windows,
.createXlibSurfaceKHR = builtin.target.os.tag == .linux,
.destroyInstance = true,
.destroySurfaceKHR = true,
.enumerateDeviceExtensionProperties = true,
.enumerateDeviceLayerProperties = true,
.enumeratePhysicalDevices = true,
.getDeviceProcAddr = true,
.getPhysicalDeviceFeatures = true,
.getPhysicalDeviceFormatProperties = true,
.getPhysicalDeviceProperties = true,
.getPhysicalDeviceMemoryProperties = true,
.getPhysicalDeviceQueueFamilyProperties = true,
.getPhysicalDeviceSurfaceCapabilitiesKHR = true,
.getPhysicalDeviceSurfaceFormatsKHR = true,
});
pub const DeviceFunctions = vk.DeviceWrapper(.{
.acquireNextImageKHR = true,
.allocateCommandBuffers = true,
.allocateDescriptorSets = true,
.allocateMemory = true,
.beginCommandBuffer = true,
.bindBufferMemory = true,
.bindImageMemory = true,
.cmdBeginRenderPass = true,
.cmdBindDescriptorSets = true,
.cmdBindIndexBuffer = true,
.cmdBindPipeline = true,
.cmdBindVertexBuffers = true,
.cmdCopyBuffer = true,
.cmdCopyBufferToImage = true,
.cmdCopyImage = true,
.cmdDispatch = true,
.cmdDraw = true,
.cmdDrawIndexed = true,
.cmdEndRenderPass = true,
.cmdPipelineBarrier = true,
.cmdSetScissor = true,
.cmdSetStencilReference = true,
.cmdSetViewport = true,
.createBuffer = true,
.createCommandPool = true,
.createComputePipelines = true,
.createDescriptorPool = true,
.createDescriptorSetLayout = true,
.createFence = true,
.createFramebuffer = true,
.createGraphicsPipelines = true,
.createImage = true,
.createImageView = true,
.createPipelineLayout = true,
.createRenderPass = true,
.createSampler = true,
.createSemaphore = true,
.createShaderModule = true,
.createSwapchainKHR = true,
.destroyBuffer = true,
.destroyCommandPool = true,
.destroyDescriptorPool = true,
.destroyDescriptorSetLayout = true,
.destroyDevice = true,
.destroyFence = true,
.destroyFramebuffer = true,
.destroyImage = true,
.destroyImageView = true,
.destroyPipeline = true,
.destroyPipelineLayout = true,
.destroyRenderPass = true,
.destroySampler = true,
.destroySemaphore = true,
.destroyShaderModule = true,
.destroySwapchainKHR = true,
.deviceWaitIdle = true,
.endCommandBuffer = true,
.freeCommandBuffers = true,
.freeDescriptorSets = true,
.freeMemory = true,
.getBufferMemoryRequirements = true,
.getDeviceQueue = true,
.getFenceStatus = true,
.getImageMemoryRequirements = true,
.getSwapchainImagesKHR = true,
.mapMemory = true,
.queuePresentKHR = true,
.queueSubmit = true,
.queueWaitIdle = true,
.resetCommandBuffer = true,
.resetFences = true,
.unmapMemory = true,
.updateDescriptorSets = true,
.waitForFences = true,
});
pub const BaseLoader = *const fn (vk.Instance, [*:0]const u8) vk.PfnVoidFunction;
pub fn loadBase(baseLoader: BaseLoader) !BaseFunctions {
return BaseFunctions.load(baseLoader) catch return error.ProcLoadingFailed;
}
pub fn loadInstance(instance: vk.Instance, instanceLoader: vk.PfnGetInstanceProcAddr) !InstanceFunctions {
return InstanceFunctions.load(instance, instanceLoader) catch return error.ProcLoadingFailed;
}
pub fn loadDevice(device: vk.Device, deviceLoader: vk.PfnGetDeviceProcAddr) !DeviceFunctions {
return DeviceFunctions.load(device, deviceLoader) catch return error.ProcLoadingFailed;
}