#include #include #include #include #include #include namespace psemek::wgpu { queue device::get_queue() { return queue(wgpuDeviceGetQueue((WGPUDevice)get())); } bind_group device::create_bind_group(bind_group::descriptor const & desc) { std::vector entries; for (auto const & entry_in : desc.entries) { auto & entry_out = entries.emplace_back(); entry_out.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(entry_in.chain); entry_out.binding = entry_in.binding; entry_out.buffer = (WGPUBuffer)entry_in.buffer.get(); entry_out.offset = entry_in.offset; entry_out.size = entry_in.size; entry_out.sampler = (WGPUSampler)entry_in.sampler.get(); entry_out.textureView = (WGPUTextureView)entry_in.texture_view.get(); } WGPUBindGroupDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); descriptor.label = detail::to_string_view(desc.label); descriptor.layout = (WGPUBindGroupLayout)desc.layout.get(); descriptor.entryCount = entries.size(); descriptor.entries = entries.data(); return bind_group(wgpuDeviceCreateBindGroup((WGPUDevice)get(), &descriptor)); } bind_group_layout device::create_bind_group_layout(bind_group_layout::descriptor const & desc) { std::vector entries; for (auto const & entry_in : desc.entries) { auto & entry_out = entries.emplace_back(); entry_out.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(entry_in.chain); entry_out.binding = entry_in.binding; entry_out.visibility = (WGPUShaderStage)entry_in.visibility; entry_out.buffer.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(entry_in.buffer.chain); entry_out.buffer.type = (WGPUBufferBindingType)entry_in.buffer.type; entry_out.buffer.hasDynamicOffset = entry_in.buffer.has_dynamic_offset; entry_out.buffer.minBindingSize = entry_in.buffer.min_binding_size; entry_out.sampler.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(entry_in.sampler.chain); entry_out.sampler.type = (WGPUSamplerBindingType)entry_in.sampler.type; entry_out.texture.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(entry_in.texture.chain); entry_out.texture.sampleType = (WGPUTextureSampleType)entry_in.texture.sample_type; entry_out.texture.viewDimension = (WGPUTextureViewDimension)entry_in.texture.view_dimension; entry_out.texture.multisampled = entry_in.texture.multisampled; entry_out.storageTexture.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(entry_in.storage_texture.chain); entry_out.storageTexture.access = (WGPUStorageTextureAccess)entry_in.storage_texture.access; entry_out.storageTexture.format = (WGPUTextureFormat)entry_in.storage_texture.format; entry_out.storageTexture.viewDimension = (WGPUTextureViewDimension)entry_in.storage_texture.view_dimension; } WGPUBindGroupLayoutDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); descriptor.label = detail::to_string_view(desc.label); descriptor.entryCount = entries.size(); descriptor.entries = entries.data(); return bind_group_layout(wgpuDeviceCreateBindGroupLayout((WGPUDevice)get(), &descriptor)); } buffer device::create_buffer(buffer::descriptor const & desc) { WGPUBufferDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); descriptor.label = detail::to_string_view(desc.label); descriptor.usage = (WGPUBufferUsage)desc.usage; descriptor.size = desc.size; descriptor.mappedAtCreation = desc.mapped_at_creation; return buffer(wgpuDeviceCreateBuffer((WGPUDevice)get(), &descriptor)); } command_encoder device::create_command_encoder(command_encoder::descriptor const & desc) { WGPUCommandEncoderDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); descriptor.label = detail::to_string_view(desc.label); return command_encoder(wgpuDeviceCreateCommandEncoder((WGPUDevice)get(), &descriptor)); } namespace { void fill_compute_pipeline_descriptor(compute_pipeline::descriptor const & desc, std::vector & constants, WGPUComputePipelineDescriptor & descriptor) { for (auto const & constant_in : desc.compute.constants) { auto & constant_out = constants.emplace_back(); constant_out.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(constant_in.chain); constant_out.key = detail::to_string_view(constant_in.key); constant_out.value = constant_in.value; } descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); descriptor.label = detail::to_string_view(desc.label); descriptor.layout = (WGPUPipelineLayout)desc.layout.get(); descriptor.compute.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.compute.chain); descriptor.compute.module = (WGPUShaderModule)desc.compute.module.get(); descriptor.compute.entryPoint = detail::to_string_view(desc.compute.entry_point); descriptor.compute.constantCount = constants.size(); descriptor.compute.constants = constants.data(); } } compute_pipeline device::create_compute_pipeline(compute_pipeline::descriptor const & desc) { std::vector constants; WGPUComputePipelineDescriptor descriptor = {}; fill_compute_pipeline_descriptor(desc, constants, descriptor); return compute_pipeline(wgpuDeviceCreateComputePipeline((WGPUDevice)get(), &descriptor)); } void device::create_compute_pipeline_async(callback_mode mode, compute_pipeline::descriptor const & desc, create_compute_pipeline_async_callback const & callback) { std::vector constants; WGPUComputePipelineDescriptor descriptor = {}; fill_compute_pipeline_descriptor(desc, constants, descriptor); WGPUCreateComputePipelineAsyncCallbackInfo callback_info = {}; callback_info.mode = (WGPUCallbackMode)mode; callback_info.callback = [](WGPUCreatePipelineAsyncStatus status, WGPUComputePipeline pipeline, WGPUStringView message, void * userdata, void *) { std::unique_ptr callback((create_compute_pipeline_async_callback *)userdata); if (*callback) (*callback)((create_pipeline_async_status)status, compute_pipeline(pipeline), std::string_view(message.data, message.length)); }; callback_info.userdata1 = new create_compute_pipeline_async_callback(callback); wgpuDeviceCreateComputePipelineAsync((WGPUDevice)get(), &descriptor, callback_info); } pipeline_layout device::create_pipeline_layout(pipeline_layout::descriptor const & desc) { WGPUPipelineLayoutDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); descriptor.label = detail::to_string_view(desc.label); descriptor.bindGroupLayoutCount = desc.layouts.size(); static_assert(sizeof(WGPUBindGroupLayout) == sizeof(bind_group_layout)); descriptor.bindGroupLayouts = (WGPUBindGroupLayout const *)desc.layouts.data(); return pipeline_layout(wgpuDeviceCreatePipelineLayout((WGPUDevice)get(), &descriptor)); } query_set device::create_query_set(query_set::descriptor const & desc) { WGPUQuerySetDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); descriptor.label = detail::to_string_view(desc.label); descriptor.type = (WGPUQueryType)desc.type; descriptor.count = desc.count; return query_set(wgpuDeviceCreateQuerySet((WGPUDevice)get(), &descriptor)); } namespace { WGPUOptionalBool to_optional_bool(std::optional value) { return value ? (*value ? WGPUOptionalBool_True : WGPUOptionalBool_False) : WGPUOptionalBool_Undefined; } void fill_render_pipeline_descriptor(render_pipeline::descriptor const & desc, WGPURenderPipelineDescriptor & descriptor, std::vector & vertex_constants, std::vector & vertex_buffers, WGPUDepthStencilState & depth_stencil_state, WGPUFragmentState & fragment_state, std::vector & fragment_constants, std::vector & color_targets) { descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); descriptor.label = detail::to_string_view(desc.label); descriptor.layout = (WGPUPipelineLayout)desc.layout.get(); for (auto const & constant_in : desc.vertex.constants) { auto & constant_out = vertex_constants.emplace_back(); constant_out.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(constant_in.chain); constant_out.key = detail::to_string_view(constant_in.key); constant_out.value = constant_in.value; } for (auto const & buffer_in : desc.vertex.buffers) { auto & buffer_out = vertex_buffers.emplace_back(); buffer_out.arrayStride = buffer_in.array_stride; buffer_out.stepMode = (WGPUVertexStepMode)buffer_in.step_mode; buffer_out.attributeCount = buffer_in.attributes.size(); static_assert(sizeof(WGPUVertexAttribute) == sizeof(vertex_attribute)); buffer_out.attributes = (WGPUVertexAttribute const *)buffer_in.attributes.data(); } descriptor.vertex.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.vertex.chain); descriptor.vertex.module = (WGPUShaderModule)desc.vertex.module.get(); descriptor.vertex.entryPoint = detail::to_string_view(desc.vertex.entry_point); descriptor.vertex.constantCount = vertex_constants.size(); descriptor.vertex.constants = vertex_constants.data(); descriptor.vertex.bufferCount = vertex_buffers.size(); descriptor.vertex.buffers = vertex_buffers.data(); descriptor.primitive.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.primitive.chain); descriptor.primitive.topology = (WGPUPrimitiveTopology)desc.primitive.topology; descriptor.primitive.stripIndexFormat = (WGPUIndexFormat)desc.primitive.strip_index_format; descriptor.primitive.frontFace = (WGPUFrontFace)desc.primitive.front_face; descriptor.primitive.cullMode = (WGPUCullMode)desc.primitive.cull_mode; descriptor.primitive.unclippedDepth = desc.primitive.clip_depth ? 0 : 1; if (desc.depth_stencil) { depth_stencil_state.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.depth_stencil->chain); depth_stencil_state.format = (WGPUTextureFormat)desc.depth_stencil->format; depth_stencil_state.depthWriteEnabled = to_optional_bool(desc.depth_stencil->depth_write); depth_stencil_state.depthCompare = (WGPUCompareFunction)desc.depth_stencil->depth_compare; depth_stencil_state.stencilFront.compare = (WGPUCompareFunction)desc.depth_stencil->stencil_front.compare; depth_stencil_state.stencilFront.failOp = (WGPUStencilOperation)desc.depth_stencil->stencil_front.fail_op; depth_stencil_state.stencilFront.depthFailOp = (WGPUStencilOperation)desc.depth_stencil->stencil_front.depth_fail_op; depth_stencil_state.stencilFront.passOp = (WGPUStencilOperation)desc.depth_stencil->stencil_front.pass_op; depth_stencil_state.stencilBack.compare = (WGPUCompareFunction)desc.depth_stencil->stencil_back.compare; depth_stencil_state.stencilBack.failOp = (WGPUStencilOperation)desc.depth_stencil->stencil_back.fail_op; depth_stencil_state.stencilBack.depthFailOp = (WGPUStencilOperation)desc.depth_stencil->stencil_back.depth_fail_op; depth_stencil_state.stencilBack.passOp = (WGPUStencilOperation)desc.depth_stencil->stencil_back.pass_op; depth_stencil_state.stencilReadMask = desc.depth_stencil->stencil_read_mask; depth_stencil_state.stencilWriteMask = desc.depth_stencil->stencil_write_mask; depth_stencil_state.depthBias = desc.depth_stencil->depth_bias; depth_stencil_state.depthBiasSlopeScale = desc.depth_stencil->depth_bias_slope_scale; depth_stencil_state.depthBiasClamp = desc.depth_stencil->depth_bias_clamp; descriptor.depthStencil = &depth_stencil_state; } descriptor.multisample.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.multisample.chain); descriptor.multisample.count = desc.multisample.count; descriptor.multisample.mask = desc.multisample.mask; descriptor.multisample.alphaToCoverageEnabled = desc.multisample.alpha_to_coverage; if (desc.fragment) { for (auto const & constant_in : desc.fragment->constants) { auto & constant_out = fragment_constants.emplace_back(); constant_out.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(constant_in.chain); constant_out.key = detail::to_string_view(constant_in.key); constant_out.value = constant_in.value; } for (auto const & target_in : desc.fragment->targets) { auto & target_out = color_targets.emplace_back(); target_out.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(target_in.chain); target_out.format = (WGPUTextureFormat)target_in.format; static_assert(sizeof(WGPUBlendState) == sizeof(blend_state)); target_out.blend = (WGPUBlendState *)(target_in.blend ? &(*target_in.blend) : nullptr); target_out.writeMask = (WGPUColorWriteMask)target_in.write_mask; } fragment_state.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.fragment->chain); fragment_state.module = (WGPUShaderModule)desc.fragment->module.get(); fragment_state.entryPoint = detail::to_string_view(desc.fragment->entry_point); fragment_state.constantCount = fragment_constants.size(); fragment_state.constants = fragment_constants.data(); fragment_state.targetCount = color_targets.size(); fragment_state.targets = color_targets.data(); descriptor.fragment = &fragment_state; } } } render_pipeline device::create_render_pipeline(render_pipeline::descriptor const & desc) { WGPURenderPipelineDescriptor descriptor = {}; std::vector vertex_constants; std::vector vertex_buffers; WGPUDepthStencilState depth_stencil_state = {}; WGPUFragmentState fragment_state = {}; std::vector fragment_constants; std::vector color_targets; fill_render_pipeline_descriptor(desc, descriptor, vertex_constants, vertex_buffers, depth_stencil_state, fragment_state, fragment_constants, color_targets); return render_pipeline(wgpuDeviceCreateRenderPipeline((WGPUDevice)get(), &descriptor)); } void device::create_render_pipeline_async(callback_mode mode, render_pipeline::descriptor const & desc, create_render_pipeline_async_callback const & callback) { WGPURenderPipelineDescriptor descriptor = {}; std::vector vertex_constants; std::vector vertex_buffers; WGPUDepthStencilState depth_stencil_state = {}; WGPUFragmentState fragment_state = {}; std::vector fragment_constants; std::vector color_targets; fill_render_pipeline_descriptor(desc, descriptor, vertex_constants, vertex_buffers, depth_stencil_state, fragment_state, fragment_constants, color_targets); WGPUCreateRenderPipelineAsyncCallbackInfo callback_info = {}; callback_info.mode = (WGPUCallbackMode)mode; callback_info.callback = [](WGPUCreatePipelineAsyncStatus status, WGPURenderPipeline pipeline, WGPUStringView message, void * userdata, void *) { std::unique_ptr callback((create_render_pipeline_async_callback *)userdata); if (*callback) (*callback)((create_pipeline_async_status)status, render_pipeline(pipeline), std::string_view(message.data, message.length)); }; callback_info.userdata1 = new create_render_pipeline_async_callback(callback); wgpuDeviceCreateRenderPipelineAsync((WGPUDevice)get(), &descriptor, callback_info); } render_bundle_encoder device::create_render_bundle_encoder(render_bundle_encoder::descriptor const & desc) { WGPURenderBundleEncoderDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); descriptor.label = detail::to_string_view(desc.label); descriptor.colorFormatCount = desc.color_formats.size(); descriptor.colorFormats = (WGPUTextureFormat const *)desc.color_formats.data(); descriptor.depthStencilFormat = (WGPUTextureFormat)desc.depth_stencil_format; descriptor.sampleCount = desc.sample_count; descriptor.depthReadOnly = desc.depth_read_only; descriptor.stencilReadOnly = desc.stencil_read_only; return render_bundle_encoder(wgpuDeviceCreateRenderBundleEncoder((WGPUDevice)get(), &descriptor)); } sampler device::create_sampler(sampler::descriptor const & desc) { WGPUSamplerDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); descriptor.label = detail::to_string_view(desc.label); descriptor.addressModeU = (WGPUAddressMode)desc.address_mode_u; descriptor.addressModeV = (WGPUAddressMode)desc.address_mode_v; descriptor.addressModeW = (WGPUAddressMode)desc.address_mode_w; descriptor.magFilter = (WGPUFilterMode)desc.mag_filter; descriptor.minFilter = (WGPUFilterMode)desc.min_filter; descriptor.mipmapFilter = (WGPUMipmapFilterMode)desc.mipmap_filter; descriptor.lodMinClamp = desc.lod_clamp.min; descriptor.lodMaxClamp = desc.lod_clamp.max; descriptor.compare = (WGPUCompareFunction)desc.compare; descriptor.maxAnisotropy = desc.max_anisotropy; return sampler(wgpuDeviceCreateSampler((WGPUDevice)get(), &descriptor)); } shader_module device::create_shader_module(shader_module::descriptor const & desc) { WGPUShaderModuleDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); descriptor.label = detail::to_string_view(desc.label); return shader_module(wgpuDeviceCreateShaderModule((WGPUDevice)get(), &descriptor)); } texture device::create_texture(texture::descriptor const & desc) { WGPUTextureDescriptor descriptor = {}; descriptor.nextInChain = (WGPUChainedStruct const *)detail::fill_chain(desc.chain); descriptor.label = detail::to_string_view(desc.label); descriptor.usage = (WGPUTextureUsage)desc.usage; descriptor.dimension = (WGPUTextureDimension)desc.dimension; descriptor.size = {desc.size[0], desc.size[1], desc.size[2]}; descriptor.format = (WGPUTextureFormat)desc.format; descriptor.mipLevelCount = desc.mip_level_count; descriptor.sampleCount = desc.sample_count; descriptor.viewFormatCount = desc.view_formats.size(); descriptor.viewFormats = (WGPUTextureFormat *)desc.view_formats.data(); return texture(wgpuDeviceCreateTexture((WGPUDevice)get(), &descriptor)); } void device::destroy() { wgpuDeviceDestroy((WGPUDevice)get()); } std::vector device::enumerate_features() { WGPUSupportedFeatures supported_features; wgpuDeviceGetFeatures((WGPUDevice)get(), &supported_features); std::vector result((feature const *)supported_features.features, (feature const *)(supported_features.features + supported_features.featureCount)); wgpuSupportedFeaturesFreeMembers(supported_features); return result; } limits device::get_limits() { WGPULimits limits = {}; detail::check_status("wgpuDeviceGetLimits", wgpuDeviceGetLimits((WGPUDevice)get(), &limits)); // TODO: support out chain wgpu::limits result; result.max_texture_dimension_1D = limits.maxTextureDimension1D; result.max_texture_dimension_2D = limits.maxTextureDimension2D; result.max_texture_dimension_3D = limits.maxTextureDimension3D; result.max_texture_array_layers = limits.maxTextureArrayLayers; result.max_bind_groups = limits.maxBindGroups; result.max_bind_groups_plus_vertex_buffers = limits.maxBindGroupsPlusVertexBuffers; result.max_bindings_per_bind_group = limits.maxBindingsPerBindGroup; result.max_dynamic_uniform_buffers_per_pipeline_layout = limits.maxDynamicUniformBuffersPerPipelineLayout; result.max_dynamic_storage_buffers_per_pipeline_layout = limits.maxDynamicStorageBuffersPerPipelineLayout; result.max_sampled_textures_per_shader_stage = limits.maxSampledTexturesPerShaderStage; result.max_samplers_per_shader_stage = limits.maxSamplersPerShaderStage; result.max_storage_buffers_per_shader_stage = limits.maxStorageBuffersPerShaderStage; result.max_storage_textures_per_shader_stage = limits.maxStorageTexturesPerShaderStage; result.max_uniform_buffers_per_shader_stage = limits.maxUniformBuffersPerShaderStage; result.max_uniform_buffer_binding_size = limits.maxUniformBufferBindingSize; result.max_storage_buffer_binding_size = limits.maxStorageBufferBindingSize; result.min_uniform_buffer_offset_alignment = limits.minUniformBufferOffsetAlignment; result.min_storage_buffer_offset_alignment = limits.minStorageBufferOffsetAlignment; result.max_vertex_buffers = limits.maxVertexBuffers; result.max_buffer_size = limits.maxBufferSize; result.max_vertex_attributes = limits.maxVertexAttributes; result.max_vertex_buffer_array_stride = limits.maxVertexBufferArrayStride; result.max_inter_stage_shader_variables = limits.maxInterStageShaderVariables; result.max_color_attachments = limits.maxColorAttachments; result.max_color_attachment_bytes_per_sample = limits.maxColorAttachmentBytesPerSample; result.max_compute_workgroup_storage_size = limits.maxComputeWorkgroupStorageSize; result.max_compute_invocations_per_workgroup = limits.maxComputeInvocationsPerWorkgroup; result.max_compute_workgroup_size_x = limits.maxComputeWorkgroupSizeX; result.max_compute_workgroup_size_y = limits.maxComputeWorkgroupSizeY; result.max_compute_workgroup_size_z = limits.maxComputeWorkgroupSizeZ; result.max_compute_workgroups_per_dimension = limits.maxComputeWorkgroupsPerDimension; return result; } bool device::has_feature(feature feature) { return wgpuDeviceHasFeature((WGPUDevice)get(), (WGPUFeatureName)feature); } void device::push_error_scope(error_filter filter) { wgpuDevicePushErrorScope((WGPUDevice)get(), (WGPUErrorFilter)filter); } void device::pop_error_scope(callback_mode mode, pop_error_callback const & callback) { WGPUPopErrorScopeCallbackInfo callback_info = {}; callback_info.mode = (WGPUCallbackMode)mode; callback_info.callback = [](WGPUPopErrorScopeStatus status, WGPUErrorType type, WGPUStringView message, void * userdata, void *) { std::unique_ptr callback((pop_error_callback *)userdata); if (*callback) (*callback)((pop_error_scope_status)status, (error_type)type, std::string_view(message.data, message.length)); }; callback_info.userdata1 = new pop_error_callback(callback); wgpuDevicePopErrorScope((WGPUDevice)get(), callback_info); } void device::set_label(std::string const & label) { wgpuDeviceSetLabel((WGPUDevice)get(), detail::to_string_view(label)); } void device::reference(void * ptr) { wgpuDeviceAddRef((WGPUDevice)ptr); } void device::release(void * ptr) { wgpuDeviceRelease((WGPUDevice)ptr); } detail::chained_struct_ptr to_chained_struct(native_limits && value) { WGPUNativeLimits chained = {}; chained.chain.sType = (WGPUSType)WGPUSType_NativeLimits; chained.maxPushConstantSize = value.max_push_constant_size; chained.maxNonSamplerBindings = value.max_non_sampler_bindings; return detail::make_chained_struct(chained); } }