From 62f4d8ff04d13fc2a23157f6d1d6669150d5e2ac Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Mon, 20 Nov 2023 11:19:40 +0100 Subject: [PATCH 01/20] Bump wgpu-native --- wgpu/backends/wgpu_native/__init__.py | 4 +- wgpu/resources/webgpu.h | 220 ++++++++++++++------------ wgpu/resources/wgpu.h | 135 ++++++++++------ 3 files changed, 205 insertions(+), 154 deletions(-) diff --git a/wgpu/backends/wgpu_native/__init__.py b/wgpu/backends/wgpu_native/__init__.py index 6a407108..f9c710e9 100644 --- a/wgpu/backends/wgpu_native/__init__.py +++ b/wgpu/backends/wgpu_native/__init__.py @@ -9,8 +9,8 @@ # The wgpu-native version that we target/expect -__version__ = "0.17.2.1" -__commit_sha__ = "44d18911dc598104a9d611f8b6128e2620a5f145" +__version__ = "0.18.1.1" +__commit_sha__ = "118848fa951af384922d703315d3b245ed1adadf" version_info = tuple(map(int, __version__.split("."))) _check_expected_version(version_info) # produces a warning on mismatch diff --git a/wgpu/resources/webgpu.h b/wgpu/resources/webgpu.h index 3878705c..79c0bc03 100644 --- a/wgpu/resources/webgpu.h +++ b/wgpu/resources/webgpu.h @@ -66,17 +66,18 @@ #include #include -#include #define WGPU_ARRAY_LAYER_COUNT_UNDEFINED (0xffffffffUL) #define WGPU_COPY_STRIDE_UNDEFINED (0xffffffffUL) #define WGPU_LIMIT_U32_UNDEFINED (0xffffffffUL) #define WGPU_LIMIT_U64_UNDEFINED (0xffffffffffffffffULL) #define WGPU_MIP_LEVEL_COUNT_UNDEFINED (0xffffffffUL) +#define WGPU_QUERY_SET_INDEX_UNDEFINED (0xffffffffUL) #define WGPU_WHOLE_MAP_SIZE SIZE_MAX #define WGPU_WHOLE_SIZE (0xffffffffffffffffULL) typedef uint32_t WGPUFlags; +typedef uint32_t WGPUBool; typedef struct WGPUAdapterImpl* WGPUAdapter WGPU_OBJECT_ATTRIBUTE; typedef struct WGPUBindGroupImpl* WGPUBindGroup WGPU_OBJECT_ATTRIBUTE; @@ -98,7 +99,6 @@ typedef struct WGPURenderPipelineImpl* WGPURenderPipeline WGPU_OBJECT_ATTRIBUTE; typedef struct WGPUSamplerImpl* WGPUSampler WGPU_OBJECT_ATTRIBUTE; typedef struct WGPUShaderModuleImpl* WGPUShaderModule WGPU_OBJECT_ATTRIBUTE; typedef struct WGPUSurfaceImpl* WGPUSurface WGPU_OBJECT_ATTRIBUTE; -typedef struct WGPUSwapChainImpl* WGPUSwapChain WGPU_OBJECT_ATTRIBUTE; typedef struct WGPUTextureImpl* WGPUTexture WGPU_OBJECT_ATTRIBUTE; typedef struct WGPUTextureViewImpl* WGPUTextureView WGPU_OBJECT_ATTRIBUTE; @@ -112,7 +112,7 @@ struct WGPUColor; struct WGPUCommandBufferDescriptor; struct WGPUCommandEncoderDescriptor; struct WGPUCompilationMessage; -struct WGPUComputePassTimestampWrite; +struct WGPUComputePassTimestampWrites; struct WGPUConstantEntry; struct WGPUExtent3D; struct WGPUInstanceDescriptor; @@ -128,7 +128,7 @@ struct WGPURenderBundleDescriptor; struct WGPURenderBundleEncoderDescriptor; struct WGPURenderPassDepthStencilAttachment; struct WGPURenderPassDescriptorMaxDrawCount; -struct WGPURenderPassTimestampWrite; +struct WGPURenderPassTimestampWrites; struct WGPURequestAdapterOptions; struct WGPUSamplerBindingLayout; struct WGPUSamplerDescriptor; @@ -137,6 +137,8 @@ struct WGPUShaderModuleSPIRVDescriptor; struct WGPUShaderModuleWGSLDescriptor; struct WGPUStencilFaceState; struct WGPUStorageTextureBindingLayout; +struct WGPUSurfaceCapabilities; +struct WGPUSurfaceConfiguration; struct WGPUSurfaceDescriptor; struct WGPUSurfaceDescriptorFromAndroidNativeWindow; struct WGPUSurfaceDescriptorFromCanvasHTMLSelector; @@ -145,7 +147,7 @@ struct WGPUSurfaceDescriptorFromWaylandSurface; struct WGPUSurfaceDescriptorFromWindowsHWND; struct WGPUSurfaceDescriptorFromXcbWindow; struct WGPUSurfaceDescriptorFromXlibWindow; -struct WGPUSwapChainDescriptor; +struct WGPUSurfaceTexture; struct WGPUTextureBindingLayout; struct WGPUTextureDataLayout; struct WGPUTextureViewDescriptor; @@ -284,11 +286,14 @@ typedef enum WGPUCompilationMessageType { WGPUCompilationMessageType_Force32 = 0x7FFFFFFF } WGPUCompilationMessageType WGPU_ENUM_ATTRIBUTE; -typedef enum WGPUComputePassTimestampLocation { - WGPUComputePassTimestampLocation_Beginning = 0x00000000, - WGPUComputePassTimestampLocation_End = 0x00000001, - WGPUComputePassTimestampLocation_Force32 = 0x7FFFFFFF -} WGPUComputePassTimestampLocation WGPU_ENUM_ATTRIBUTE; +typedef enum WGPUCompositeAlphaMode { + WGPUCompositeAlphaMode_Auto = 0x00000000, + WGPUCompositeAlphaMode_Opaque = 0x00000001, + WGPUCompositeAlphaMode_Premultiplied = 0x00000002, + WGPUCompositeAlphaMode_Unpremultiplied = 0x00000003, + WGPUCompositeAlphaMode_Inherit = 0x00000004, + WGPUCompositeAlphaMode_Force32 = 0x7FFFFFFF +} WGPUCompositeAlphaMode WGPU_ENUM_ATTRIBUTE; typedef enum WGPUCreatePipelineAsyncStatus { WGPUCreatePipelineAsyncStatus_Success = 0x00000000, @@ -335,15 +340,14 @@ typedef enum WGPUFeatureName { WGPUFeatureName_DepthClipControl = 0x00000001, WGPUFeatureName_Depth32FloatStencil8 = 0x00000002, WGPUFeatureName_TimestampQuery = 0x00000003, - WGPUFeatureName_PipelineStatisticsQuery = 0x00000004, - WGPUFeatureName_TextureCompressionBC = 0x00000005, - WGPUFeatureName_TextureCompressionETC2 = 0x00000006, - WGPUFeatureName_TextureCompressionASTC = 0x00000007, - WGPUFeatureName_IndirectFirstInstance = 0x00000008, - WGPUFeatureName_ShaderF16 = 0x00000009, - WGPUFeatureName_RG11B10UfloatRenderable = 0x0000000A, - WGPUFeatureName_BGRA8UnormStorage = 0x0000000B, - WGPUFeatureName_Float32Filterable = 0x0000000C, + WGPUFeatureName_TextureCompressionBC = 0x00000004, + WGPUFeatureName_TextureCompressionETC2 = 0x00000005, + WGPUFeatureName_TextureCompressionASTC = 0x00000006, + WGPUFeatureName_IndirectFirstInstance = 0x00000007, + WGPUFeatureName_ShaderF16 = 0x00000008, + WGPUFeatureName_RG11B10UfloatRenderable = 0x00000009, + WGPUFeatureName_BGRA8UnormStorage = 0x0000000A, + WGPUFeatureName_Float32Filterable = 0x0000000B, WGPUFeatureName_Force32 = 0x7FFFFFFF } WGPUFeatureName WGPU_ENUM_ATTRIBUTE; @@ -379,15 +383,6 @@ typedef enum WGPUMipmapFilterMode { WGPUMipmapFilterMode_Force32 = 0x7FFFFFFF } WGPUMipmapFilterMode WGPU_ENUM_ATTRIBUTE; -typedef enum WGPUPipelineStatisticName { - WGPUPipelineStatisticName_VertexShaderInvocations = 0x00000000, - WGPUPipelineStatisticName_ClipperInvocations = 0x00000001, - WGPUPipelineStatisticName_ClipperPrimitivesOut = 0x00000002, - WGPUPipelineStatisticName_FragmentShaderInvocations = 0x00000003, - WGPUPipelineStatisticName_ComputeShaderInvocations = 0x00000004, - WGPUPipelineStatisticName_Force32 = 0x7FFFFFFF -} WGPUPipelineStatisticName WGPU_ENUM_ATTRIBUTE; - typedef enum WGPUPowerPreference { WGPUPowerPreference_Undefined = 0x00000000, WGPUPowerPreference_LowPower = 0x00000001, @@ -396,9 +391,10 @@ typedef enum WGPUPowerPreference { } WGPUPowerPreference WGPU_ENUM_ATTRIBUTE; typedef enum WGPUPresentMode { - WGPUPresentMode_Immediate = 0x00000000, - WGPUPresentMode_Mailbox = 0x00000001, - WGPUPresentMode_Fifo = 0x00000002, + WGPUPresentMode_Fifo = 0x00000000, + WGPUPresentMode_FifoRelaxed = 0x00000001, + WGPUPresentMode_Immediate = 0x00000002, + WGPUPresentMode_Mailbox = 0x00000003, WGPUPresentMode_Force32 = 0x7FFFFFFF } WGPUPresentMode WGPU_ENUM_ATTRIBUTE; @@ -413,8 +409,7 @@ typedef enum WGPUPrimitiveTopology { typedef enum WGPUQueryType { WGPUQueryType_Occlusion = 0x00000000, - WGPUQueryType_PipelineStatistics = 0x00000001, - WGPUQueryType_Timestamp = 0x00000002, + WGPUQueryType_Timestamp = 0x00000001, WGPUQueryType_Force32 = 0x7FFFFFFF } WGPUQueryType WGPU_ENUM_ATTRIBUTE; @@ -426,12 +421,6 @@ typedef enum WGPUQueueWorkDoneStatus { WGPUQueueWorkDoneStatus_Force32 = 0x7FFFFFFF } WGPUQueueWorkDoneStatus WGPU_ENUM_ATTRIBUTE; -typedef enum WGPURenderPassTimestampLocation { - WGPURenderPassTimestampLocation_Beginning = 0x00000000, - WGPURenderPassTimestampLocation_End = 0x00000001, - WGPURenderPassTimestampLocation_Force32 = 0x7FFFFFFF -} WGPURenderPassTimestampLocation WGPU_ENUM_ATTRIBUTE; - typedef enum WGPURequestAdapterStatus { WGPURequestAdapterStatus_Success = 0x00000000, WGPURequestAdapterStatus_Unavailable = 0x00000001, @@ -496,6 +485,16 @@ typedef enum WGPUStoreOp { WGPUStoreOp_Force32 = 0x7FFFFFFF } WGPUStoreOp WGPU_ENUM_ATTRIBUTE; +typedef enum WGPUSurfaceGetCurrentTextureStatus { + WGPUSurfaceGetCurrentTextureStatus_Success = 0x00000000, + WGPUSurfaceGetCurrentTextureStatus_Timeout = 0x00000001, + WGPUSurfaceGetCurrentTextureStatus_Outdated = 0x00000002, + WGPUSurfaceGetCurrentTextureStatus_Lost = 0x00000003, + WGPUSurfaceGetCurrentTextureStatus_OutOfMemory = 0x00000004, + WGPUSurfaceGetCurrentTextureStatus_DeviceLost = 0x00000005, + WGPUSurfaceGetCurrentTextureStatus_Force32 = 0x7FFFFFFF +} WGPUSurfaceGetCurrentTextureStatus WGPU_ENUM_ATTRIBUTE; + typedef enum WGPUTextureAspect { WGPUTextureAspect_All = 0x00000000, WGPUTextureAspect_StencilOnly = 0x00000001, @@ -779,7 +778,7 @@ typedef struct WGPUBlendComponent { typedef struct WGPUBufferBindingLayout { WGPUChainedStruct const * nextInChain; WGPUBufferBindingType type; - bool hasDynamicOffset; + WGPUBool hasDynamicOffset; uint64_t minBindingSize; } WGPUBufferBindingLayout WGPU_STRUCTURE_ATTRIBUTE; @@ -788,7 +787,7 @@ typedef struct WGPUBufferDescriptor { WGPU_NULLABLE char const * label; WGPUBufferUsageFlags usage; uint64_t size; - bool mappedAtCreation; + WGPUBool mappedAtCreation; } WGPUBufferDescriptor WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUColor { @@ -821,11 +820,11 @@ typedef struct WGPUCompilationMessage { uint64_t utf16Length; } WGPUCompilationMessage WGPU_STRUCTURE_ATTRIBUTE; -typedef struct WGPUComputePassTimestampWrite { +typedef struct WGPUComputePassTimestampWrites { WGPUQuerySet querySet; - uint32_t queryIndex; - WGPUComputePassTimestampLocation location; -} WGPUComputePassTimestampWrite WGPU_STRUCTURE_ATTRIBUTE; + uint32_t beginningOfPassWriteIndex; + uint32_t endOfPassWriteIndex; +} WGPUComputePassTimestampWrites WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUConstantEntry { WGPUChainedStruct const * nextInChain; @@ -849,6 +848,7 @@ typedef struct WGPULimits { uint32_t maxTextureDimension3D; uint32_t maxTextureArrayLayers; uint32_t maxBindGroups; + uint32_t maxBindGroupsPlusVertexBuffers; uint32_t maxBindingsPerBindGroup; uint32_t maxDynamicUniformBuffersPerPipelineLayout; uint32_t maxDynamicStorageBuffersPerPipelineLayout; @@ -881,7 +881,7 @@ typedef struct WGPUMultisampleState { WGPUChainedStruct const * nextInChain; uint32_t count; uint32_t mask; - bool alphaToCoverageEnabled; + WGPUBool alphaToCoverageEnabled; } WGPUMultisampleState WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUOrigin3D { @@ -900,7 +900,7 @@ typedef struct WGPUPipelineLayoutDescriptor { // Can be chained in WGPUPrimitiveState typedef struct WGPUPrimitiveDepthClipControl { WGPUChainedStruct chain; - bool unclippedDepth; + WGPUBool unclippedDepth; } WGPUPrimitiveDepthClipControl WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUPrimitiveState { @@ -916,8 +916,6 @@ typedef struct WGPUQuerySetDescriptor { WGPU_NULLABLE char const * label; WGPUQueryType type; uint32_t count; - WGPUPipelineStatisticName const * pipelineStatistics; - size_t pipelineStatisticsCount; } WGPUQuerySetDescriptor WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUQueueDescriptor { @@ -933,12 +931,12 @@ typedef struct WGPURenderBundleDescriptor { typedef struct WGPURenderBundleEncoderDescriptor { WGPUChainedStruct const * nextInChain; WGPU_NULLABLE char const * label; - size_t colorFormatsCount; + size_t colorFormatCount; WGPUTextureFormat const * colorFormats; WGPUTextureFormat depthStencilFormat; uint32_t sampleCount; - bool depthReadOnly; - bool stencilReadOnly; + WGPUBool depthReadOnly; + WGPUBool stencilReadOnly; } WGPURenderBundleEncoderDescriptor WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPURenderPassDepthStencilAttachment { @@ -946,11 +944,11 @@ typedef struct WGPURenderPassDepthStencilAttachment { WGPULoadOp depthLoadOp; WGPUStoreOp depthStoreOp; float depthClearValue; - bool depthReadOnly; + WGPUBool depthReadOnly; WGPULoadOp stencilLoadOp; WGPUStoreOp stencilStoreOp; uint32_t stencilClearValue; - bool stencilReadOnly; + WGPUBool stencilReadOnly; } WGPURenderPassDepthStencilAttachment WGPU_STRUCTURE_ATTRIBUTE; // Can be chained in WGPURenderPassDescriptor @@ -959,18 +957,18 @@ typedef struct WGPURenderPassDescriptorMaxDrawCount { uint64_t maxDrawCount; } WGPURenderPassDescriptorMaxDrawCount WGPU_STRUCTURE_ATTRIBUTE; -typedef struct WGPURenderPassTimestampWrite { +typedef struct WGPURenderPassTimestampWrites { WGPUQuerySet querySet; - uint32_t queryIndex; - WGPURenderPassTimestampLocation location; -} WGPURenderPassTimestampWrite WGPU_STRUCTURE_ATTRIBUTE; + uint32_t beginningOfPassWriteIndex; + uint32_t endOfPassWriteIndex; +} WGPURenderPassTimestampWrites WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPURequestAdapterOptions { WGPUChainedStruct const * nextInChain; WGPU_NULLABLE WGPUSurface compatibleSurface; WGPUPowerPreference powerPreference; WGPUBackendType backendType; - bool forceFallbackAdapter; + WGPUBool forceFallbackAdapter; } WGPURequestAdapterOptions WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUSamplerBindingLayout { @@ -1026,6 +1024,29 @@ typedef struct WGPUStorageTextureBindingLayout { WGPUTextureViewDimension viewDimension; } WGPUStorageTextureBindingLayout WGPU_STRUCTURE_ATTRIBUTE; +typedef struct WGPUSurfaceCapabilities { + WGPUChainedStructOut * nextInChain; + size_t formatCount; + WGPUTextureFormat * formats; + size_t presentModeCount; + WGPUPresentMode * presentModes; + size_t alphaModeCount; + WGPUCompositeAlphaMode * alphaModes; +} WGPUSurfaceCapabilities WGPU_STRUCTURE_ATTRIBUTE; + +typedef struct WGPUSurfaceConfiguration { + WGPUChainedStruct const * nextInChain; + WGPUDevice device; + WGPUTextureFormat format; + WGPUTextureUsageFlags usage; + size_t viewFormatCount; + WGPUTextureFormat const * viewFormats; + WGPUCompositeAlphaMode alphaMode; + uint32_t width; + uint32_t height; + WGPUPresentMode presentMode; +} WGPUSurfaceConfiguration WGPU_STRUCTURE_ATTRIBUTE; + typedef struct WGPUSurfaceDescriptor { WGPUChainedStruct const * nextInChain; WGPU_NULLABLE char const * label; @@ -1077,21 +1098,17 @@ typedef struct WGPUSurfaceDescriptorFromXlibWindow { uint32_t window; } WGPUSurfaceDescriptorFromXlibWindow WGPU_STRUCTURE_ATTRIBUTE; -typedef struct WGPUSwapChainDescriptor { - WGPUChainedStruct const * nextInChain; - WGPU_NULLABLE char const * label; - WGPUTextureUsageFlags usage; - WGPUTextureFormat format; - uint32_t width; - uint32_t height; - WGPUPresentMode presentMode; -} WGPUSwapChainDescriptor WGPU_STRUCTURE_ATTRIBUTE; +typedef struct WGPUSurfaceTexture { + WGPUTexture texture; + WGPUBool suboptimal; + WGPUSurfaceGetCurrentTextureStatus status; +} WGPUSurfaceTexture WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUTextureBindingLayout { WGPUChainedStruct const * nextInChain; WGPUTextureSampleType sampleType; WGPUTextureViewDimension viewDimension; - bool multisampled; + WGPUBool multisampled; } WGPUTextureBindingLayout WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUTextureDataLayout { @@ -1151,14 +1168,13 @@ typedef struct WGPUCompilationInfo { typedef struct WGPUComputePassDescriptor { WGPUChainedStruct const * nextInChain; WGPU_NULLABLE char const * label; - size_t timestampWriteCount; - WGPUComputePassTimestampWrite const * timestampWrites; + WGPU_NULLABLE WGPUComputePassTimestampWrites const * timestampWrites; } WGPUComputePassDescriptor WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUDepthStencilState { WGPUChainedStruct const * nextInChain; WGPUTextureFormat format; - bool depthWriteEnabled; + WGPUBool depthWriteEnabled; WGPUCompareFunction depthCompare; WGPUStencilFaceState stencilFront; WGPUStencilFaceState stencilBack; @@ -1192,6 +1208,7 @@ typedef struct WGPUProgrammableStageDescriptor { } WGPUProgrammableStageDescriptor WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPURenderPassColorAttachment { + WGPUChainedStruct const * nextInChain; WGPU_NULLABLE WGPUTextureView view; WGPU_NULLABLE WGPUTextureView resolveTarget; WGPULoadOp loadOp; @@ -1260,7 +1277,7 @@ typedef struct WGPUComputePipelineDescriptor { typedef struct WGPUDeviceDescriptor { WGPUChainedStruct const * nextInChain; WGPU_NULLABLE char const * label; - size_t requiredFeaturesCount; + size_t requiredFeatureCount; WGPUFeatureName const * requiredFeatures; WGPU_NULLABLE WGPURequiredLimits const * requiredLimits; WGPUQueueDescriptor defaultQueue; @@ -1275,8 +1292,7 @@ typedef struct WGPURenderPassDescriptor { WGPURenderPassColorAttachment const * colorAttachments; WGPU_NULLABLE WGPURenderPassDepthStencilAttachment const * depthStencilAttachment; WGPU_NULLABLE WGPUQuerySet occlusionQuerySet; - size_t timestampWriteCount; - WGPURenderPassTimestampWrite const * timestampWrites; + WGPU_NULLABLE WGPURenderPassTimestampWrites const * timestampWrites; } WGPURenderPassDescriptor WGPU_STRUCTURE_ATTRIBUTE; typedef struct WGPUVertexState { @@ -1316,14 +1332,14 @@ extern "C" { #if !defined(WGPU_SKIP_PROCS) -typedef WGPUInstance (*WGPUProcCreateInstance)(WGPUInstanceDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; +typedef WGPUInstance (*WGPUProcCreateInstance)(WGPU_NULLABLE WGPUInstanceDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; typedef WGPUProc (*WGPUProcGetProcAddress)(WGPUDevice device, char const * procName) WGPU_FUNCTION_ATTRIBUTE; // Procs of Adapter typedef size_t (*WGPUProcAdapterEnumerateFeatures)(WGPUAdapter adapter, WGPUFeatureName * features) WGPU_FUNCTION_ATTRIBUTE; -typedef bool (*WGPUProcAdapterGetLimits)(WGPUAdapter adapter, WGPUSupportedLimits * limits) WGPU_FUNCTION_ATTRIBUTE; +typedef WGPUBool (*WGPUProcAdapterGetLimits)(WGPUAdapter adapter, WGPUSupportedLimits * limits) WGPU_FUNCTION_ATTRIBUTE; typedef void (*WGPUProcAdapterGetProperties)(WGPUAdapter adapter, WGPUAdapterProperties * properties) WGPU_FUNCTION_ATTRIBUTE; -typedef bool (*WGPUProcAdapterHasFeature)(WGPUAdapter adapter, WGPUFeatureName feature) WGPU_FUNCTION_ATTRIBUTE; +typedef WGPUBool (*WGPUProcAdapterHasFeature)(WGPUAdapter adapter, WGPUFeatureName feature) WGPU_FUNCTION_ATTRIBUTE; typedef void (*WGPUProcAdapterRequestDevice)(WGPUAdapter adapter, WGPU_NULLABLE WGPUDeviceDescriptor const * descriptor, WGPURequestDeviceCallback callback, void * userdata) WGPU_FUNCTION_ATTRIBUTE; typedef void (*WGPUProcAdapterReference)(WGPUAdapter adapter) WGPU_FUNCTION_ATTRIBUTE; typedef void (*WGPUProcAdapterRelease)(WGPUAdapter adapter) WGPU_FUNCTION_ATTRIBUTE; @@ -1375,11 +1391,9 @@ typedef void (*WGPUProcCommandEncoderReference)(WGPUCommandEncoder commandEncode typedef void (*WGPUProcCommandEncoderRelease)(WGPUCommandEncoder commandEncoder) WGPU_FUNCTION_ATTRIBUTE; // Procs of ComputePassEncoder -typedef void (*WGPUProcComputePassEncoderBeginPipelineStatisticsQuery)(WGPUComputePassEncoder computePassEncoder, WGPUQuerySet querySet, uint32_t queryIndex) WGPU_FUNCTION_ATTRIBUTE; typedef void (*WGPUProcComputePassEncoderDispatchWorkgroups)(WGPUComputePassEncoder computePassEncoder, uint32_t workgroupCountX, uint32_t workgroupCountY, uint32_t workgroupCountZ) WGPU_FUNCTION_ATTRIBUTE; typedef void (*WGPUProcComputePassEncoderDispatchWorkgroupsIndirect)(WGPUComputePassEncoder computePassEncoder, WGPUBuffer indirectBuffer, uint64_t indirectOffset) WGPU_FUNCTION_ATTRIBUTE; typedef void (*WGPUProcComputePassEncoderEnd)(WGPUComputePassEncoder computePassEncoder) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcComputePassEncoderEndPipelineStatisticsQuery)(WGPUComputePassEncoder computePassEncoder) WGPU_FUNCTION_ATTRIBUTE; typedef void (*WGPUProcComputePassEncoderInsertDebugMarker)(WGPUComputePassEncoder computePassEncoder, char const * markerLabel) WGPU_FUNCTION_ATTRIBUTE; typedef void (*WGPUProcComputePassEncoderPopDebugGroup)(WGPUComputePassEncoder computePassEncoder) WGPU_FUNCTION_ATTRIBUTE; typedef void (*WGPUProcComputePassEncoderPushDebugGroup)(WGPUComputePassEncoder computePassEncoder, char const * groupLabel) WGPU_FUNCTION_ATTRIBUTE; @@ -1409,13 +1423,12 @@ typedef WGPURenderPipeline (*WGPUProcDeviceCreateRenderPipeline)(WGPUDevice devi typedef void (*WGPUProcDeviceCreateRenderPipelineAsync)(WGPUDevice device, WGPURenderPipelineDescriptor const * descriptor, WGPUCreateRenderPipelineAsyncCallback callback, void * userdata) WGPU_FUNCTION_ATTRIBUTE; typedef WGPUSampler (*WGPUProcDeviceCreateSampler)(WGPUDevice device, WGPU_NULLABLE WGPUSamplerDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; typedef WGPUShaderModule (*WGPUProcDeviceCreateShaderModule)(WGPUDevice device, WGPUShaderModuleDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; -typedef WGPUSwapChain (*WGPUProcDeviceCreateSwapChain)(WGPUDevice device, WGPUSurface surface, WGPUSwapChainDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; typedef WGPUTexture (*WGPUProcDeviceCreateTexture)(WGPUDevice device, WGPUTextureDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; typedef void (*WGPUProcDeviceDestroy)(WGPUDevice device) WGPU_FUNCTION_ATTRIBUTE; typedef size_t (*WGPUProcDeviceEnumerateFeatures)(WGPUDevice device, WGPUFeatureName * features) WGPU_FUNCTION_ATTRIBUTE; -typedef bool (*WGPUProcDeviceGetLimits)(WGPUDevice device, WGPUSupportedLimits * limits) WGPU_FUNCTION_ATTRIBUTE; +typedef WGPUBool (*WGPUProcDeviceGetLimits)(WGPUDevice device, WGPUSupportedLimits * limits) WGPU_FUNCTION_ATTRIBUTE; typedef WGPUQueue (*WGPUProcDeviceGetQueue)(WGPUDevice device) WGPU_FUNCTION_ATTRIBUTE; -typedef bool (*WGPUProcDeviceHasFeature)(WGPUDevice device, WGPUFeatureName feature) WGPU_FUNCTION_ATTRIBUTE; +typedef WGPUBool (*WGPUProcDeviceHasFeature)(WGPUDevice device, WGPUFeatureName feature) WGPU_FUNCTION_ATTRIBUTE; typedef void (*WGPUProcDevicePopErrorScope)(WGPUDevice device, WGPUErrorCallback callback, void * userdata) WGPU_FUNCTION_ATTRIBUTE; typedef void (*WGPUProcDevicePushErrorScope)(WGPUDevice device, WGPUErrorFilter filter) WGPU_FUNCTION_ATTRIBUTE; typedef void (*WGPUProcDeviceSetLabel)(WGPUDevice device, char const * label) WGPU_FUNCTION_ATTRIBUTE; @@ -1476,14 +1489,12 @@ typedef void (*WGPUProcRenderBundleEncoderRelease)(WGPURenderBundleEncoder rende // Procs of RenderPassEncoder typedef void (*WGPUProcRenderPassEncoderBeginOcclusionQuery)(WGPURenderPassEncoder renderPassEncoder, uint32_t queryIndex) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcRenderPassEncoderBeginPipelineStatisticsQuery)(WGPURenderPassEncoder renderPassEncoder, WGPUQuerySet querySet, uint32_t queryIndex) WGPU_FUNCTION_ATTRIBUTE; typedef void (*WGPUProcRenderPassEncoderDraw)(WGPURenderPassEncoder renderPassEncoder, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) WGPU_FUNCTION_ATTRIBUTE; typedef void (*WGPUProcRenderPassEncoderDrawIndexed)(WGPURenderPassEncoder renderPassEncoder, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t baseVertex, uint32_t firstInstance) WGPU_FUNCTION_ATTRIBUTE; typedef void (*WGPUProcRenderPassEncoderDrawIndexedIndirect)(WGPURenderPassEncoder renderPassEncoder, WGPUBuffer indirectBuffer, uint64_t indirectOffset) WGPU_FUNCTION_ATTRIBUTE; typedef void (*WGPUProcRenderPassEncoderDrawIndirect)(WGPURenderPassEncoder renderPassEncoder, WGPUBuffer indirectBuffer, uint64_t indirectOffset) WGPU_FUNCTION_ATTRIBUTE; typedef void (*WGPUProcRenderPassEncoderEnd)(WGPURenderPassEncoder renderPassEncoder) WGPU_FUNCTION_ATTRIBUTE; typedef void (*WGPUProcRenderPassEncoderEndOcclusionQuery)(WGPURenderPassEncoder renderPassEncoder) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcRenderPassEncoderEndPipelineStatisticsQuery)(WGPURenderPassEncoder renderPassEncoder) WGPU_FUNCTION_ATTRIBUTE; typedef void (*WGPUProcRenderPassEncoderExecuteBundles)(WGPURenderPassEncoder renderPassEncoder, size_t bundleCount, WGPURenderBundle const * bundles) WGPU_FUNCTION_ATTRIBUTE; typedef void (*WGPUProcRenderPassEncoderInsertDebugMarker)(WGPURenderPassEncoder renderPassEncoder, char const * markerLabel) WGPU_FUNCTION_ATTRIBUTE; typedef void (*WGPUProcRenderPassEncoderPopDebugGroup)(WGPURenderPassEncoder renderPassEncoder) WGPU_FUNCTION_ATTRIBUTE; @@ -1518,15 +1529,17 @@ typedef void (*WGPUProcShaderModuleReference)(WGPUShaderModule shaderModule) WGP typedef void (*WGPUProcShaderModuleRelease)(WGPUShaderModule shaderModule) WGPU_FUNCTION_ATTRIBUTE; // Procs of Surface +typedef void (*WGPUProcSurfaceConfigure)(WGPUSurface surface, WGPUSurfaceConfiguration const * config) WGPU_FUNCTION_ATTRIBUTE; +typedef void (*WGPUProcSurfaceGetCapabilities)(WGPUSurface surface, WGPUAdapter adapter, WGPUSurfaceCapabilities * capabilities) WGPU_FUNCTION_ATTRIBUTE; +typedef void (*WGPUProcSurfaceGetCurrentTexture)(WGPUSurface surface, WGPUSurfaceTexture * surfaceTexture) WGPU_FUNCTION_ATTRIBUTE; typedef WGPUTextureFormat (*WGPUProcSurfaceGetPreferredFormat)(WGPUSurface surface, WGPUAdapter adapter) WGPU_FUNCTION_ATTRIBUTE; +typedef void (*WGPUProcSurfacePresent)(WGPUSurface surface) WGPU_FUNCTION_ATTRIBUTE; +typedef void (*WGPUProcSurfaceUnconfigure)(WGPUSurface surface) WGPU_FUNCTION_ATTRIBUTE; typedef void (*WGPUProcSurfaceReference)(WGPUSurface surface) WGPU_FUNCTION_ATTRIBUTE; typedef void (*WGPUProcSurfaceRelease)(WGPUSurface surface) WGPU_FUNCTION_ATTRIBUTE; -// Procs of SwapChain -typedef WGPUTextureView (*WGPUProcSwapChainGetCurrentTextureView)(WGPUSwapChain swapChain) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcSwapChainPresent)(WGPUSwapChain swapChain) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcSwapChainReference)(WGPUSwapChain swapChain) WGPU_FUNCTION_ATTRIBUTE; -typedef void (*WGPUProcSwapChainRelease)(WGPUSwapChain swapChain) WGPU_FUNCTION_ATTRIBUTE; +// Procs of SurfaceCapabilities +typedef void (*WGPUProcSurfaceCapabilitiesFreeMembers)(WGPUSurfaceCapabilities capabilities) WGPU_FUNCTION_ATTRIBUTE; // Procs of Texture typedef WGPUTextureView (*WGPUProcTextureCreateView)(WGPUTexture texture, WGPU_NULLABLE WGPUTextureViewDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; @@ -1552,14 +1565,14 @@ typedef void (*WGPUProcTextureViewRelease)(WGPUTextureView textureView) WGPU_FUN #if !defined(WGPU_SKIP_DECLARATIONS) -WGPU_EXPORT WGPUInstance wgpuCreateInstance(WGPUInstanceDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT WGPUInstance wgpuCreateInstance(WGPU_NULLABLE WGPUInstanceDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT WGPUProc wgpuGetProcAddress(WGPUDevice device, char const * procName) WGPU_FUNCTION_ATTRIBUTE; // Methods of Adapter WGPU_EXPORT size_t wgpuAdapterEnumerateFeatures(WGPUAdapter adapter, WGPUFeatureName * features) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT bool wgpuAdapterGetLimits(WGPUAdapter adapter, WGPUSupportedLimits * limits) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT WGPUBool wgpuAdapterGetLimits(WGPUAdapter adapter, WGPUSupportedLimits * limits) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuAdapterGetProperties(WGPUAdapter adapter, WGPUAdapterProperties * properties) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT bool wgpuAdapterHasFeature(WGPUAdapter adapter, WGPUFeatureName feature) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT WGPUBool wgpuAdapterHasFeature(WGPUAdapter adapter, WGPUFeatureName feature) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuAdapterRequestDevice(WGPUAdapter adapter, WGPU_NULLABLE WGPUDeviceDescriptor const * descriptor, WGPURequestDeviceCallback callback, void * userdata) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuAdapterReference(WGPUAdapter adapter) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuAdapterRelease(WGPUAdapter adapter) WGPU_FUNCTION_ATTRIBUTE; @@ -1611,11 +1624,9 @@ WGPU_EXPORT void wgpuCommandEncoderReference(WGPUCommandEncoder commandEncoder) WGPU_EXPORT void wgpuCommandEncoderRelease(WGPUCommandEncoder commandEncoder) WGPU_FUNCTION_ATTRIBUTE; // Methods of ComputePassEncoder -WGPU_EXPORT void wgpuComputePassEncoderBeginPipelineStatisticsQuery(WGPUComputePassEncoder computePassEncoder, WGPUQuerySet querySet, uint32_t queryIndex) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuComputePassEncoderDispatchWorkgroups(WGPUComputePassEncoder computePassEncoder, uint32_t workgroupCountX, uint32_t workgroupCountY, uint32_t workgroupCountZ) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuComputePassEncoderDispatchWorkgroupsIndirect(WGPUComputePassEncoder computePassEncoder, WGPUBuffer indirectBuffer, uint64_t indirectOffset) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuComputePassEncoderEnd(WGPUComputePassEncoder computePassEncoder) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuComputePassEncoderEndPipelineStatisticsQuery(WGPUComputePassEncoder computePassEncoder) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuComputePassEncoderInsertDebugMarker(WGPUComputePassEncoder computePassEncoder, char const * markerLabel) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuComputePassEncoderPopDebugGroup(WGPUComputePassEncoder computePassEncoder) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuComputePassEncoderPushDebugGroup(WGPUComputePassEncoder computePassEncoder, char const * groupLabel) WGPU_FUNCTION_ATTRIBUTE; @@ -1645,13 +1656,12 @@ WGPU_EXPORT WGPURenderPipeline wgpuDeviceCreateRenderPipeline(WGPUDevice device, WGPU_EXPORT void wgpuDeviceCreateRenderPipelineAsync(WGPUDevice device, WGPURenderPipelineDescriptor const * descriptor, WGPUCreateRenderPipelineAsyncCallback callback, void * userdata) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT WGPUSampler wgpuDeviceCreateSampler(WGPUDevice device, WGPU_NULLABLE WGPUSamplerDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT WGPUShaderModule wgpuDeviceCreateShaderModule(WGPUDevice device, WGPUShaderModuleDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT WGPUSwapChain wgpuDeviceCreateSwapChain(WGPUDevice device, WGPUSurface surface, WGPUSwapChainDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT WGPUTexture wgpuDeviceCreateTexture(WGPUDevice device, WGPUTextureDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuDeviceDestroy(WGPUDevice device) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT size_t wgpuDeviceEnumerateFeatures(WGPUDevice device, WGPUFeatureName * features) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT bool wgpuDeviceGetLimits(WGPUDevice device, WGPUSupportedLimits * limits) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT WGPUBool wgpuDeviceGetLimits(WGPUDevice device, WGPUSupportedLimits * limits) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT WGPUQueue wgpuDeviceGetQueue(WGPUDevice device) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT bool wgpuDeviceHasFeature(WGPUDevice device, WGPUFeatureName feature) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT WGPUBool wgpuDeviceHasFeature(WGPUDevice device, WGPUFeatureName feature) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuDevicePopErrorScope(WGPUDevice device, WGPUErrorCallback callback, void * userdata) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuDevicePushErrorScope(WGPUDevice device, WGPUErrorFilter filter) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuDeviceSetLabel(WGPUDevice device, char const * label) WGPU_FUNCTION_ATTRIBUTE; @@ -1712,14 +1722,12 @@ WGPU_EXPORT void wgpuRenderBundleEncoderRelease(WGPURenderBundleEncoder renderBu // Methods of RenderPassEncoder WGPU_EXPORT void wgpuRenderPassEncoderBeginOcclusionQuery(WGPURenderPassEncoder renderPassEncoder, uint32_t queryIndex) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuRenderPassEncoderBeginPipelineStatisticsQuery(WGPURenderPassEncoder renderPassEncoder, WGPUQuerySet querySet, uint32_t queryIndex) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderPassEncoderDraw(WGPURenderPassEncoder renderPassEncoder, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderPassEncoderDrawIndexed(WGPURenderPassEncoder renderPassEncoder, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t baseVertex, uint32_t firstInstance) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderPassEncoderDrawIndexedIndirect(WGPURenderPassEncoder renderPassEncoder, WGPUBuffer indirectBuffer, uint64_t indirectOffset) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderPassEncoderDrawIndirect(WGPURenderPassEncoder renderPassEncoder, WGPUBuffer indirectBuffer, uint64_t indirectOffset) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderPassEncoderEnd(WGPURenderPassEncoder renderPassEncoder) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderPassEncoderEndOcclusionQuery(WGPURenderPassEncoder renderPassEncoder) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuRenderPassEncoderEndPipelineStatisticsQuery(WGPURenderPassEncoder renderPassEncoder) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderPassEncoderExecuteBundles(WGPURenderPassEncoder renderPassEncoder, size_t bundleCount, WGPURenderBundle const * bundles) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderPassEncoderInsertDebugMarker(WGPURenderPassEncoder renderPassEncoder, char const * markerLabel) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuRenderPassEncoderPopDebugGroup(WGPURenderPassEncoder renderPassEncoder) WGPU_FUNCTION_ATTRIBUTE; @@ -1754,15 +1762,17 @@ WGPU_EXPORT void wgpuShaderModuleReference(WGPUShaderModule shaderModule) WGPU_F WGPU_EXPORT void wgpuShaderModuleRelease(WGPUShaderModule shaderModule) WGPU_FUNCTION_ATTRIBUTE; // Methods of Surface +WGPU_EXPORT void wgpuSurfaceConfigure(WGPUSurface surface, WGPUSurfaceConfiguration const * config) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuSurfaceGetCapabilities(WGPUSurface surface, WGPUAdapter adapter, WGPUSurfaceCapabilities * capabilities) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuSurfaceGetCurrentTexture(WGPUSurface surface, WGPUSurfaceTexture * surfaceTexture) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT WGPUTextureFormat wgpuSurfaceGetPreferredFormat(WGPUSurface surface, WGPUAdapter adapter) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuSurfacePresent(WGPUSurface surface) WGPU_FUNCTION_ATTRIBUTE; +WGPU_EXPORT void wgpuSurfaceUnconfigure(WGPUSurface surface) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuSurfaceReference(WGPUSurface surface) WGPU_FUNCTION_ATTRIBUTE; WGPU_EXPORT void wgpuSurfaceRelease(WGPUSurface surface) WGPU_FUNCTION_ATTRIBUTE; -// Methods of SwapChain -WGPU_EXPORT WGPUTextureView wgpuSwapChainGetCurrentTextureView(WGPUSwapChain swapChain) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuSwapChainPresent(WGPUSwapChain swapChain) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuSwapChainReference(WGPUSwapChain swapChain) WGPU_FUNCTION_ATTRIBUTE; -WGPU_EXPORT void wgpuSwapChainRelease(WGPUSwapChain swapChain) WGPU_FUNCTION_ATTRIBUTE; +// Methods of SurfaceCapabilities +WGPU_EXPORT void wgpuSurfaceCapabilitiesFreeMembers(WGPUSurfaceCapabilities capabilities) WGPU_FUNCTION_ATTRIBUTE; // Methods of Texture WGPU_EXPORT WGPUTextureView wgpuTextureCreateView(WGPUTexture texture, WGPU_NULLABLE WGPUTextureViewDescriptor const * descriptor) WGPU_FUNCTION_ATTRIBUTE; diff --git a/wgpu/resources/wgpu.h b/wgpu/resources/wgpu.h index dd36031b..76bdb474 100644 --- a/wgpu/resources/wgpu.h +++ b/wgpu/resources/wgpu.h @@ -4,24 +4,28 @@ #include "webgpu.h" typedef enum WGPUNativeSType { - // Start at 6 to prevent collisions with webgpu STypes - WGPUSType_DeviceExtras = 0x60000001, - WGPUSType_AdapterExtras = 0x60000002, - WGPUSType_RequiredLimitsExtras = 0x60000003, - WGPUSType_PipelineLayoutExtras = 0x60000004, - WGPUSType_ShaderModuleGLSLDescriptor = 0x60000005, - WGPUSType_SupportedLimitsExtras = 0x60000003, - WGPUSType_InstanceExtras = 0x60000006, - WGPUSType_SwapChainDescriptorExtras = 0x60000007, + // Start at 0003 since that's allocated range for wgpu-native + WGPUSType_DeviceExtras = 0x00030001, + WGPUSType_RequiredLimitsExtras = 0x00030002, + WGPUSType_PipelineLayoutExtras = 0x00030003, + WGPUSType_ShaderModuleGLSLDescriptor = 0x00030004, + WGPUSType_SupportedLimitsExtras = 0x00030005, + WGPUSType_InstanceExtras = 0x00030006, + WGPUSType_BindGroupEntryExtras = 0x00030007, + WGPUSType_BindGroupLayoutEntryExtras = 0x00030008, + WGPUSType_QuerySetDescriptorExtras = 0x00030009, WGPUNativeSType_Force32 = 0x7FFFFFFF } WGPUNativeSType; typedef enum WGPUNativeFeature { - WGPUNativeFeature_PushConstants = 0x60000001, - WGPUNativeFeature_TextureAdapterSpecificFormatFeatures = 0x60000002, - WGPUNativeFeature_MultiDrawIndirect = 0x60000003, - WGPUNativeFeature_MultiDrawIndirectCount = 0x60000004, - WGPUNativeFeature_VertexWritableStorage = 0x60000005, + WGPUNativeFeature_PushConstants = 0x00030001, + WGPUNativeFeature_TextureAdapterSpecificFormatFeatures = 0x00030002, + WGPUNativeFeature_MultiDrawIndirect = 0x00030003, + WGPUNativeFeature_MultiDrawIndirectCount = 0x00030004, + WGPUNativeFeature_VertexWritableStorage = 0x00030005, + WGPUNativeFeature_TextureBindingArray = 0x00030006, + WGPUNativeFeature_SampledTextureAndStorageBufferArrayNonUniformIndexing = 0x00030007, + WGPUNativeFeature_PipelineStatisticsQuery = 0x00030008, WGPUNativeFeature_Force32 = 0x7FFFFFFF } WGPUNativeFeature; @@ -36,21 +40,30 @@ typedef enum WGPULogLevel { } WGPULogLevel; typedef enum WGPUInstanceBackend { - WGPUInstanceBackend_Vulkan = 1 << 1, - WGPUInstanceBackend_GL = 1 << 5, + WGPUInstanceBackend_All = 0x00000000, + WGPUInstanceBackend_Vulkan = 1 << 0, + WGPUInstanceBackend_GL = 1 << 1, WGPUInstanceBackend_Metal = 1 << 2, WGPUInstanceBackend_DX12 = 1 << 3, WGPUInstanceBackend_DX11 = 1 << 4, - WGPUInstanceBackend_BrowserWebGPU = 1 << 6, + WGPUInstanceBackend_BrowserWebGPU = 1 << 5, WGPUInstanceBackend_Primary = WGPUInstanceBackend_Vulkan | WGPUInstanceBackend_Metal | WGPUInstanceBackend_DX12 | WGPUInstanceBackend_BrowserWebGPU, WGPUInstanceBackend_Secondary = WGPUInstanceBackend_GL | WGPUInstanceBackend_DX11, - WGPUInstanceBackend_None = 0x00000000, WGPUInstanceBackend_Force32 = 0x7FFFFFFF } WGPUInstanceBackend; typedef WGPUFlags WGPUInstanceBackendFlags; +typedef enum WGPUInstanceFlag { + WGPUInstanceFlag_Default = 0x00000000, + WGPUInstanceFlag_Debug = 1 << 0, + WGPUInstanceFlag_Validation = 1 << 1, + WGPUInstanceFlag_DiscardHalLabels = 1 << 2, + WGPUInstanceFlag_Force32 = 0x7FFFFFFF +} WGPUInstanceFlag; +typedef WGPUFlags WGPUInstanceFlags; + typedef enum WGPUDx12Compiler { WGPUDx12Compiler_Undefined = 0x00000000, WGPUDx12Compiler_Fxc = 0x00000001, @@ -58,19 +71,34 @@ typedef enum WGPUDx12Compiler { WGPUDx12Compiler_Force32 = 0x7FFFFFFF } WGPUDx12Compiler; -typedef enum WGPUCompositeAlphaMode { - WGPUCompositeAlphaMode_Auto = 0x00000000, - WGPUCompositeAlphaMode_Opaque = 0x00000001, - WGPUCompositeAlphaMode_PreMultiplied = 0x00000002, - WGPUCompositeAlphaMode_PostMultiplied = 0x00000003, - WGPUCompositeAlphaMode_Inherit = 0x00000004, - WGPUCompositeAlphaMode_Force32 = 0x7FFFFFFF -} WGPUCompositeAlphaMode; +typedef enum WGPUGles3MinorVersion { + WGPUGles3MinorVersion_Automatic = 0x00000000, + WGPUGles3MinorVersion_Version0 = 0x00000001, + WGPUGles3MinorVersion_Version1 = 0x00000002, + WGPUGles3MinorVersion_Version2 = 0x00000003, + WGPUGles3MinorVersion_Force32 = 0x7FFFFFFF +} WGPUGles3MinorVersion; + +typedef enum WGPUPipelineStatisticName { + WGPUPipelineStatisticName_VertexShaderInvocations = 0x00000000, + WGPUPipelineStatisticName_ClipperInvocations = 0x00000001, + WGPUPipelineStatisticName_ClipperPrimitivesOut = 0x00000002, + WGPUPipelineStatisticName_FragmentShaderInvocations = 0x00000003, + WGPUPipelineStatisticName_ComputeShaderInvocations = 0x00000004, + WGPUPipelineStatisticName_Force32 = 0x7FFFFFFF +} WGPUPipelineStatisticName WGPU_ENUM_ATTRIBUTE; + +typedef enum WGPUNativeQueryType { + WGPUNativeQueryType_PipelineStatistics = 0x00030000, + WGPUNativeQueryType_Force32 = 0x7FFFFFFF +} WGPUNativeQueryType WGPU_ENUM_ATTRIBUTE; typedef struct WGPUInstanceExtras { WGPUChainedStruct chain; WGPUInstanceBackendFlags backends; + WGPUInstanceFlags flags; WGPUDx12Compiler dx12ShaderCompiler; + WGPUGles3MinorVersion gles3MinorVersion; const char * dxilPath; const char * dxcPath; } WGPUInstanceExtras; @@ -80,14 +108,19 @@ typedef struct WGPUDeviceExtras { const char * tracePath; } WGPUDeviceExtras; +typedef struct WGPUNativeLimits { + uint32_t maxPushConstantSize; + uint32_t maxNonSamplerBindings; +} WGPUNativeLimits; + typedef struct WGPURequiredLimitsExtras { WGPUChainedStruct chain; - uint32_t maxPushConstantSize; + WGPUNativeLimits limits; } WGPURequiredLimitsExtras; typedef struct WGPUSupportedLimitsExtras { WGPUChainedStructOut chain; - uint32_t maxPushConstantSize; + WGPUNativeLimits limits; } WGPUSupportedLimitsExtras; typedef struct WGPUPushConstantRange { @@ -157,27 +190,32 @@ typedef struct WGPUGlobalReport { WGPUHubReport gl; } WGPUGlobalReport; -typedef struct WGPUSurfaceCapabilities { - size_t formatCount; - WGPUTextureFormat * formats; - size_t presentModeCount; - WGPUPresentMode * presentModes; - size_t alphaModeCount; - WGPUCompositeAlphaMode * alphaModes; -} WGPUSurfaceCapabilities; - -typedef struct WGPUSwapChainDescriptorExtras { - WGPUChainedStruct chain; - WGPUCompositeAlphaMode alphaMode; - size_t viewFormatCount; - WGPUTextureFormat const * viewFormats; -} WGPUSwapChainDescriptorExtras; - typedef struct WGPUInstanceEnumerateAdapterOptions { WGPUChainedStruct const * nextInChain; WGPUInstanceBackendFlags backends; } WGPUInstanceEnumerateAdapterOptions; +typedef struct WGPUBindGroupEntryExtras { + WGPUChainedStruct chain; + WGPUBuffer const * buffers; + size_t bufferCount; + WGPUSampler const * samplers; + size_t samplerCount; + WGPUTextureView const * textureViews; + size_t textureViewCount; +} WGPUBindGroupEntryExtras; + +typedef struct WGPUBindGroupLayoutEntryExtras { + WGPUChainedStruct chain; + uint32_t count; +} WGPUBindGroupLayoutEntryExtras; + +typedef struct WGPUQuerySetDescriptorExtras { + WGPUChainedStruct chain; + WGPUPipelineStatisticName const * pipelineStatistics; + size_t pipelineStatisticCount; +} WGPUQuerySetDescriptorExtras WGPU_STRUCTURE_ATTRIBUTE; + typedef void (*WGPULogCallback)(WGPULogLevel level, char const * message, void * userdata); #ifdef __cplusplus @@ -190,7 +228,7 @@ size_t wgpuInstanceEnumerateAdapters(WGPUInstance instance, WGPUInstanceEnumerat WGPUSubmissionIndex wgpuQueueSubmitForIndex(WGPUQueue queue, size_t commandCount, WGPUCommandBuffer const * commands); // Returns true if the queue is empty, or false if there are more queue submissions still in flight. -bool wgpuDevicePoll(WGPUDevice device, bool wait, WGPUWrappedSubmissionIndex const * wrappedSubmissionIndex); +WGPUBool wgpuDevicePoll(WGPUDevice device, WGPUBool wait, WGPUWrappedSubmissionIndex const * wrappedSubmissionIndex); void wgpuSetLogCallback(WGPULogCallback callback, void * userdata); @@ -198,8 +236,6 @@ void wgpuSetLogLevel(WGPULogLevel level); uint32_t wgpuGetVersion(void); -void wgpuSurfaceGetCapabilities(WGPUSurface surface, WGPUAdapter adapter, WGPUSurfaceCapabilities * capabilities); - void wgpuRenderPassEncoderSetPushConstants(WGPURenderPassEncoder encoder, WGPUShaderStageFlags stages, uint32_t offset, uint32_t sizeBytes, void* const data); void wgpuRenderPassEncoderMultiDrawIndirect(WGPURenderPassEncoder encoder, WGPUBuffer buffer, uint64_t offset, uint32_t count); @@ -208,6 +244,11 @@ void wgpuRenderPassEncoderMultiDrawIndexedIndirect(WGPURenderPassEncoder encoder void wgpuRenderPassEncoderMultiDrawIndirectCount(WGPURenderPassEncoder encoder, WGPUBuffer buffer, uint64_t offset, WGPUBuffer count_buffer, uint64_t count_buffer_offset, uint32_t max_count); void wgpuRenderPassEncoderMultiDrawIndexedIndirectCount(WGPURenderPassEncoder encoder, WGPUBuffer buffer, uint64_t offset, WGPUBuffer count_buffer, uint64_t count_buffer_offset, uint32_t max_count); +void wgpuComputePassEncoderBeginPipelineStatisticsQuery(WGPUComputePassEncoder computePassEncoder, WGPUQuerySet querySet, uint32_t queryIndex); +void wgpuComputePassEncoderEndPipelineStatisticsQuery(WGPUComputePassEncoder computePassEncoder); +void wgpuRenderPassEncoderBeginPipelineStatisticsQuery(WGPURenderPassEncoder renderPassEncoder, WGPUQuerySet querySet, uint32_t queryIndex); +void wgpuRenderPassEncoderEndPipelineStatisticsQuery(WGPURenderPassEncoder renderPassEncoder); + #ifdef __cplusplus } // extern "C" #endif From f26a7168317f9df3682d7dc1f7e7387d847d8e5b Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Mon, 20 Nov 2023 11:58:51 +0100 Subject: [PATCH 02/20] Update codegen --- codegen/hparser.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/codegen/hparser.py b/codegen/hparser.py index 1797f957..740ba143 100644 --- a/codegen/hparser.py +++ b/codegen/hparser.py @@ -128,7 +128,9 @@ def _parse_from_h(self): name = parts[-1] if name.endswith("Flags"): assert name.startswith("WGPU") - name = name[4:-5] + name1 = name[4:-1] # xxFlags -> xxFlag + name2 = name[4:-5] # xxFlags -> xx + name = name1 if name1 in self.enums else name2 self.flags[name] = self.enums.pop(name) # Collect structs. This is relatively easy, since we only need the C code. From 6f0101a46510108f637dee5ee38de8fc49312c77 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Tue, 21 Nov 2023 09:08:25 +0100 Subject: [PATCH 03/20] Apply changes. canvas context still needs work tho --- codegen/wgpu_native_patcher.py | 21 +- tests/test_gui_glfw.py | 2 +- wgpu/_classes.py | 33 +-- wgpu/backends/wgpu_native/_api.py | 387 ++++++++++++++++--------- wgpu/backends/wgpu_native/_mappings.py | 132 ++++++++- wgpu/resources/codegen_report.md | 10 +- 6 files changed, 417 insertions(+), 168 deletions(-) diff --git a/codegen/wgpu_native_patcher.py b/codegen/wgpu_native_patcher.py index 73010d94..9cb4b533 100644 --- a/codegen/wgpu_native_patcher.py +++ b/codegen/wgpu_native_patcher.py @@ -129,13 +129,30 @@ def write_mappings(): pylines.append("}") # Write a few native-only mappings: int => key + # If possible, resolve to WebGPU names, otherwise use the native name. pylines.append("enum_int2str = {") - for name in ["BackendType", "AdapterType", "ErrorType", "DeviceLostReason"]: + for name in [ + "BackendType", + "AdapterType", + "ErrorType", + "DeviceLostReason", + "TextureFormat", + "PresentMode", + "CompositeAlphaMode", + ]: + webgpu_names = {} + if name in idl.enums: + webgpu_names = { + val.replace("-", ""): val for val in idl.enums[name].values() + } + if "unknown" in webgpu_names: + webgpu_names["undefined"] = "unknown" pylines.append(f' "{name}":' + " {") for key, val in hp.enums[name].items(): if key == "Force32": continue - pylines.append(f' {val}: "{key}",') + enum_val = webgpu_names.get(key.lower(), key) + pylines.append(f' {val}: "{enum_val}",') pylines.append(" },") pylines.append("}") diff --git a/tests/test_gui_glfw.py b/tests/test_gui_glfw.py index d2e49ece..a11f4df1 100644 --- a/tests/test_gui_glfw.py +++ b/tests/test_gui_glfw.py @@ -1,6 +1,6 @@ """ Test the canvas, and parts of the rendering that involves a canvas, -like the swap chain. +like the canvas context and surface texture. """ import os diff --git a/wgpu/_classes.py b/wgpu/_classes.py index 5dc19f4d..58f77a7c 100644 --- a/wgpu/_classes.py +++ b/wgpu/_classes.py @@ -174,31 +174,30 @@ def configure( ): """Configures the presentation context for the associated canvas. Destroys any textures produced with a previous configuration. + This clears the drawing buffer to transparent black. Arguments: - device (WgpuDevice): The GPU device object. - format (enums.TextureFormat): The texture format, e.g. "bgra8unorm-srgb". - Default uses the preferred_format. + device (WgpuDevice): The GPU device object to create compatible textures for. + format (enums.TextureFormat): The format that textures returned by + ``get_current_texture()`` will have. Must be one of the supported context + formats. An often used format is "bgra8unorm-srgb". usage (flags.TextureUsage): Default ``TextureUsage.OUTPUT_ATTACHMENT``. - color_space (PredefinedColorSpace): Default "srgb". - alpha_mode (enums.CanvasAlphaMode): Default opaque. + view_formats (List[enums.TextureFormat]): The formats that views created + from textures returned by ``get_current_texture()`` may use. + color_space (PredefinedColorSpace): The color space that values written + into textures returned by ``get_current_texture()`` should be displayed with. + Default "srgb". + alpha_mode (enums.CanvasAlphaMode): Determines the effect that alpha values + will have on the content of textures returned by ``get_current_texture()`` + when read, displayed, or used as an image source. Default "opaque". """ - self.unconfigure() - self._device = device - self._format = format or self.get_preferred_format(device.adapter) - self._usage = usage or flags.TextureUsage.RENDER_ATTACHMENT - self._color_space = color_space - self._alpha_mode = alpha_mode + raise NotImplementedError() # IDL: undefined unconfigure(); def unconfigure(self): """Removes the presentation context configuration. Destroys any textures produced while configured.""" - self._device = None - self._format = None - self._usage = None - self._color_space = None - self._alpha_mode = None + raise NotImplementedError() # IDL: GPUTexture getCurrentTexture(); def get_current_texture(self): @@ -219,7 +218,7 @@ def present(self): @apidiff.add("Better place to define the preferred format") def get_preferred_format(self, adapter): - """Get the preferred swap chain format.""" + """Get the preferred surface texture format.""" return "bgra8unorm-srgb" # seems to be a good default def __del__(self): diff --git a/wgpu/backends/wgpu_native/_api.py b/wgpu/backends/wgpu_native/_api.py index db1d77c5..25008e9a 100644 --- a/wgpu/backends/wgpu_native/_api.py +++ b/wgpu/backends/wgpu_native/_api.py @@ -196,12 +196,12 @@ def request_adapter( # Get surface id that the adapter must be compatible with. If we # don't pass a valid surface id, there is no guarantee we'll be - # able to create a swapchain for it (from this adapter). + # able to create a surface texture for it (from this adapter). surface_id = ffi.NULL if canvas is not None: window_id = canvas.get_window_id() if window_id is not None: # e.g. could be an off-screen canvas - surface_id = get_surface_id_from_canvas(canvas) + surface_id = canvas.context._get_surface_id() # ----- Select backend @@ -222,7 +222,7 @@ def request_adapter( # ----- Request adapter - # H: nextInChain: WGPUChainedStruct *, compatibleSurface: WGPUSurface, powerPreference: WGPUPowerPreference, backendType: WGPUBackendType, forceFallbackAdapter: bool + # H: nextInChain: WGPUChainedStruct *, compatibleSurface: WGPUSurface, powerPreference: WGPUPowerPreference, backendType: WGPUBackendType, forceFallbackAdapter: WGPUBool/int struct = new_struct_p( "WGPURequestAdapterOptions *", compatibleSurface=surface_id, @@ -306,7 +306,7 @@ def to_py_str(key): # not used: limits ) c_limits = c_supported_limits.limits - # H: bool f(WGPUAdapter adapter, WGPUSupportedLimits * limits) + # H: WGPUBool f(WGPUAdapter adapter, WGPUSupportedLimits * limits) libf.wgpuAdapterGetLimits(adapter_id, c_supported_limits) limits = {to_snake_case(k): getattr(c_limits, k) for k in sorted(dir(c_limits))} @@ -317,14 +317,14 @@ def to_py_str(key): for f in sorted(enums.FeatureName): key = f"FeatureName.{f}" i = enummap[key] - # H: bool f(WGPUAdapter adapter, WGPUFeatureName feature) + # H: WGPUBool f(WGPUAdapter adapter, WGPUFeatureName feature) if libf.wgpuAdapterHasFeature(adapter_id, i): features.add(f) # Native features for f in NATIVE_FEATURES: i = getattr(lib, f"WGPUNativeFeature_{f}") - # H: bool f(WGPUAdapter adapter, WGPUFeatureName feature) + # H: WGPUBool f(WGPUAdapter adapter, WGPUFeatureName feature) if libf.wgpuAdapterHasFeature(adapter_id, i): features.add(f) @@ -352,59 +352,101 @@ async def request_adapter_async( class GPUCanvasContext(classes.GPUCanvasContext): def __init__(self, canvas): super().__init__(canvas) - self._device = None - self._surface_size = (-1, -1) + self._device = None # set in configure() self._surface_id = None - self._internal = None - self._current_texture = None + self._config = None + self._psize = None - def get_current_texture(self): - if self._device is None: # pragma: no cover - raise RuntimeError( - "Preset context must be configured before get_current_texture()." - ) - if self._current_texture is None: - self._create_native_swap_chain_if_needed() - try: - # H: WGPUTextureView f(WGPUSwapChain swapChain) - view_id = libf.wgpuSwapChainGetCurrentTextureView(self._internal) - except Exception as err: - extra_msg = "\nThis may be caused by dragging the window to a monitor with different dpi. " - extra_msg += "Resize window to proceed.\n" - err.args = (err.args[0] + extra_msg,) + err.args[1:] - raise err from None - - size = self._surface_size[0], self._surface_size[1], 1 - self._current_texture = GPUTextureView( - "swap_chain", view_id, self._device, None, size - ) - return self._current_texture + def _get_surface_id(self): + if self._surface_id is None: + # get_surface_id_from_canvas calls wgpuInstanceCreateSurface + canvas = self._get_canvas() + self._surface_id = get_surface_id_from_canvas(canvas) + return self._surface_id - def present(self): - if ( - self._internal is not None - and lib is not None - and self._current_texture is not None - ): - # H: void f(WGPUSwapChain swapChain) - libf.wgpuSwapChainPresent(self._internal) - # Reset - always ask for a fresh texture (exactly once) on each draw - self._current_texture = None - - def _create_native_swap_chain_if_needed(self): + def configure( + self, + *, + device: "GPUDevice", + format: "enums.TextureFormat", + usage: "flags.TextureUsage" = 0x10, + view_formats: "List[enums.TextureFormat]" = [], + color_space: str = "srgb", + alpha_mode: "enums.CanvasAlphaMode" = "opaque", + ): + # Handle inputs + + # Store for later + self._device = device + # Handle usage + if isinstance(usage, str): + usage = str_flag_to_int(flags.TextureUsage, usage) + # View formats + c_view_formats = ffi.NULL + if view_formats: + view_formats_list = [enummap["TextureFormat." + x] for x in view_formats] + c_view_formats = ffi.new("WGPUTextureFormat []", view_formats_list) + # Lookup alpha mode, needs explicit conversion because enum names mismatch + c_alpha_mode = getattr(lib, f"WGPUCompositeAlphaMode_{alpha_mode.capitalize()}") + # The format is used as-is + format + # The color_space is not used for now + color_space + + # Prepare canvas = self._get_canvas() - psize = canvas.get_physical_size() + surface_id = self._get_surface_id() + + # Get what's supported + + # H: nextInChain: WGPUChainedStructOut *, formatCount: int, formats: WGPUTextureFormat *, presentModeCount: int, presentModes: WGPUPresentMode *, alphaModeCount: int, alphaModes: WGPUCompositeAlphaMode * + capabilities = new_struct_p( + "WGPUSurfaceCapabilities *", + # not used: formatCount + # not used: formats + # not used: presentModeCount + # not used: presentModes + # not used: alphaModeCount + # not used: alphaModes + # not used: nextInChain + ) + # H: void f(WGPUSurface surface, WGPUAdapter adapter, WGPUSurfaceCapabilities * capabilities) + libf.wgpuSurfaceGetCapabilities( + surface_id, self._device.adapter._internal, capabilities + ) - if psize == self._surface_size: - return - self._surface_size = psize + capable_formats = [] + for i in range(capabilities.formatCount): + int_val = capabilities.formats[i] + capable_formats.append(enum_int2str["TextureFormat"][int_val]) - if self._surface_id is None: - self._surface_id = get_surface_id_from_canvas(canvas) + capable_present_modes = [] + for i in range(capabilities.presentModeCount): + int_val = capabilities.presentModes[i] + str_val = enum_int2str["PresentMode"][int_val] + capable_present_modes.append(str_val.lower()) + + capable_alpha_modes = [] + for i in range(capabilities.alphaModeCount): + int_val = capabilities.alphaModes[i] + str_val = enum_int2str["CompositeAlphaMode"][int_val] + capable_alpha_modes.append(str_val.lower()) - # logger.info(str((psize, canvas.get_logical_size(), canvas.get_pixel_ratio()))) + # H: void f(WGPUSurfaceCapabilities capabilities) + libf.wgpuSurfaceCapabilitiesFreeMembers(capabilities[0]) - # Set the present mode to determine vsync behavior. + # Check if input is supported + + if format not in capable_formats: + raise ValueError( + f"Given format '{format}' is not in supported formats {capable_formats}" + ) + if alpha_mode not in capable_alpha_modes: + raise ValueError( + f"Given format '{alpha_mode}' is not in supported formats {capable_alpha_modes}" + ) + + # Select the present mode to determine vsync behavior. # # 0 Immediate: no waiting, with risk of tearing. # 1 Mailbox: submit without delay, but present on vsync. Not always available. @@ -419,77 +461,159 @@ def _create_native_swap_chain_if_needed(self): # * https://github.com/gfx-rs/wgpu/blob/e54a36ee/wgpu-types/src/lib.rs#L2663-L2678 # * https://github.com/pygfx/wgpu-py/issues/256 - pm_fifo = lib.WGPUPresentMode_Fifo - pm_immediate = lib.WGPUPresentMode_Immediate - present_mode = pm_fifo if getattr(canvas, "_vsync", True) else pm_immediate - - # H: nextInChain: WGPUChainedStruct *, label: char *, usage: WGPUTextureUsageFlags/int, format: WGPUTextureFormat, width: int, height: int, presentMode: WGPUPresentMode - struct = new_struct_p( - "WGPUSwapChainDescriptor *", - usage=self._usage, - format=self._format, - width=max(1, psize[0]), - height=max(1, psize[1]), - presentMode=present_mode, + if getattr(canvas, "_vsync", True): + present_mode_pref = ["fifo", "immediate"] + else: + present_mode_pref = ["immediate", "fifo"] + present_modes = [p for p in present_mode_pref if p in capable_present_modes] + present_mode = (present_modes or capable_present_modes)[0] + c_present_mode = getattr(lib, f"WGPUPresentMode_{present_mode.capitalize()}") + + # H: nextInChain: WGPUChainedStruct *, device: WGPUDevice, format: WGPUTextureFormat, usage: WGPUTextureUsageFlags/int, viewFormatCount: int, viewFormats: WGPUTextureFormat *, alphaMode: WGPUCompositeAlphaMode, width: int, height: int, presentMode: WGPUPresentMode + config = new_struct_p( + "WGPUSurfaceConfiguration *", + device=device._internal, + format=format, + usage=usage, + viewFormatCount=len(view_formats), + viewFormats=c_view_formats, + alphaMode=c_alpha_mode, + width=0, + height=0, + presentMode=c_present_mode, # not used: nextInChain - # not used: label ) + self._configure(config) - # Destroy old one - if self._internal is not None: - # H: void f(WGPUSwapChain swapChain) - libf.wgpuSwapChainRelease(self._internal) + def _configure(self, config): + width, height = self._get_canvas().get_physical_size() + config.width = width + config.height = height + if width <= 0 or height <= 0: + raise RuntimeError("Cannot configure canvas to size ({width}, {height}).") + libf.wgpuSurfaceConfigure(self._get_surface_id(), config) + self._config = config - # H: WGPUSwapChain f(WGPUDevice device, WGPUSurface surface, WGPUSwapChainDescriptor const * descriptor) - self._internal = libf.wgpuDeviceCreateSwapChain( - self._device._internal, self._surface_id, struct - ) + def _maybe_reconfigure(self): + config = self._config + width, height = self._get_canvas().get_physical_size() + if width != config.width or height != config.height: + self._configure(config) - def get_preferred_format(self, adapter): - if self._surface_id is None: - canvas = self._get_canvas() - self._surface_id = get_surface_id_from_canvas(canvas) + def unconfigure(self): + self._config = None + # H: void f(WGPUSurface surface) + libf.wgpuSurfaceUnconfigure(self._get_surface_id()) - # # The C-call - # c_count = ffi.new("size_t *") - # c_formats = libxx.wgpuSurfaceGetSupportedFormats( - # self._surface_id, adapter._internal, c_count + def get_current_texture(self): + # If the canvas has changed since the last configure, we need to re-configure + if not self._config: + raise RuntimeError( + "Canvas context must be configured before a surface texture can be obtained." + ) + self._maybe_reconfigure() + + # Try to obtain a texture + # H: texture: WGPUTexture, suboptimal: WGPUBool/int, status: WGPUSurfaceGetCurrentTextureStatus + surface_texture = new_struct_p( + "WGPUSurfaceTexture *", + # not used: texture + # not used: suboptimal + # not used: status + ) + + for iter in range(3): + libf.wgpuSurfaceGetCurrentTexture(self._get_surface_id(), surface_texture) + status = surface_texture.status + texture_id = surface_texture.texture + if status == lib.WGPUSurfaceGetCurrentTextureStatus_Success: + break # success + if texture_id: + libf.wgpuTextureRelease(texture_id) + if status in [ + lib.WGPUSurfaceGetCurrentTextureStatus_Timeout, + lib.WGPUSurfaceGetCurrentTextureStatus_Timeout, + lib.WGPUSurfaceGetCurrentTextureStatus_Outdated, + lib.WGPUSurfaceGetCurrentTextureStatus_Lost, + ]: + # Configure and try again + logger.warning("Re-configuring canvas context.") + self._configure(self._config) + continue + else: + # WGPUSurfaceGetCurrentTextureStatus_OutOfMemory + # WGPUSurfaceGetCurrentTextureStatus_DeviceLost + # catchall + raise RuntimeError(f"Cannot get surface texture ({status})") + + # I don't expect this to happen, but lets check just in case. + if not texture_id: + raise RuntimeError("Cannot get surface texture (no texture)") + + # Things look good, but texture may still be suboptimal, whatever that means + if surface_texture.suboptimal: + # todo: rate-limit this warning + logger.warning("The surface texture is suboptimal.") + + # Create the Python wrapper + + # H: uint32_t f(WGPUTexture texture) + width = libf.wgpuTextureGetWidth(texture_id) + # H: uint32_t f(WGPUTexture texture) + height = libf.wgpuTextureGetHeight(texture_id) + # H: uint32_t f(WGPUTexture texture) + depth = libf.wgpuTextureGetDepthOrArrayLayers(texture_id) + # H: uint32_t f(WGPUTexture texture) + sample_count = 1 # libf.wgpuTextureGetSampleCount(texture_id) + # H: WGPUTextureUsageFlags f(WGPUTexture texture) + usage = libf.wgpuTextureGetUsage(texture_id) + # H: WGPUTextureFormat f(WGPUTexture texture) + format = libf.wgpuTextureGetFormat(texture_id) + # H: WGPUTextureDimension f(WGPUTexture texture) + dim = ( + enums.TextureDimension.d2 + ) # libf.wgpuTextureGetDimension(texture_id) # -> to string + + label = "" + # Cannot yet set label, because it's not implemented in wgpu-native + # label = "surface-texture" + # libf.wgpuTextureSetLabel(texture_id, to_c_label(label)) + + tex_info = { + "size": (width, height, depth), + # H: uint32_t f(WGPUTexture texture) + "mip_level_count": 1, # libf.wgpuTextureGetMipLevelCount(texture_id) + "sample_count": sample_count, + "dimension": dim, + "format": format, + "usage": usage, + } + self._tex = GPUTexture(label, texture_id, self._device, tex_info) + return self._tex + # todo: this is now a texture, not a vierw! + + # todo: include these errors somehow? + # raise RuntimeError( + # "Preset context must be configured before get_current_texture()." # ) - # - # # Convert to string formats - # try: - # count = c_count[0] - # supported_format_ints = [c_formats[i] for i in range(count)] - # formats = [] - # for key in list(enums.TextureFormat): - # i = enummap[f"TextureFormat.{key}"] - # if i in supported_format_ints: - # formats.append(key) - # finally: - # t = ffi.typeof(c_formats) - # libxx.wgpuFree(c_formats, count * ffi.sizeof(t), ffi.alignof(t)) - - # There appears to be a bug in wgpuSurfaceGetSupportedFormats, see #341 - disabled it for now. - formats = [] - - # Select one - default = "bgra8unorm-srgb" # seems to be a good default - preferred = [f for f in formats if "srgb" in f] - if default in formats: - return default - elif preferred: - return preferred[0] - elif formats: - return formats[0] - else: - return default + # extra_msg = "\nThis may be caused by dragging the window to a monitor with different dpi. " + # extra_msg += "Resize window to proceed.\n" + # err.args = (err.args[0] + extra_msg,) + err.args[1:] + # raise err from None + + def present(self): + # H: void f(WGPUSurface surface) + libf.wgpuSurfacePresent(self._get_surface_id()) + + def get_preferred_format(self, adapter): + # H: WGPUTextureFormat f(WGPUSurface surface, WGPUAdapter adapter) + format = libf.wgpuSurfaceGetPreferredFormat( + self._get_surface_id(), adapter._internal + ) + return enum_int2str["TextureFormat"][format] def _destroy(self): - if self._internal is not None and libf is not None: - self._internal, internal = None, self._internal - # H: void f(WGPUSwapChain swapChain) - libf.wgpuSwapChainRelease(internal) - if self._surface_id is not None and lib is not None: + if self._surface_id is not None and libf is not None: self._surface_id, surface_id = None, self._surface_id # H: void f(WGPUSurface surface) libf.wgpuSurfaceRelease(surface_id) @@ -599,12 +723,12 @@ def device_lost_callback(c_reason, c_message, userdata): # ----- Request device - # H: nextInChain: WGPUChainedStruct *, label: char *, requiredFeaturesCount: int, requiredFeatures: WGPUFeatureName *, requiredLimits: WGPURequiredLimits *, defaultQueue: WGPUQueueDescriptor, deviceLostCallback: WGPUDeviceLostCallback, deviceLostUserdata: void * + # H: nextInChain: WGPUChainedStruct *, label: char *, requiredFeatureCount: int, requiredFeatures: WGPUFeatureName *, requiredLimits: WGPURequiredLimits *, defaultQueue: WGPUQueueDescriptor, deviceLostCallback: WGPUDeviceLostCallback, deviceLostUserdata: void * struct = new_struct_p( "WGPUDeviceDescriptor *", label=to_c_label(label), nextInChain=ffi.cast("WGPUChainedStruct * ", extras), - requiredFeaturesCount=len(c_features), + requiredFeatureCount=len(c_features), requiredFeatures=ffi.new("WGPUFeatureName []", c_features), requiredLimits=c_required_limits, defaultQueue=queue_struct, @@ -641,7 +765,7 @@ def callback(status, result, message, userdata): # not used: limits ) c_limits = c_supported_limits.limits - # H: bool f(WGPUDevice device, WGPUSupportedLimits * limits) + # H: WGPUBool f(WGPUDevice device, WGPUSupportedLimits * limits) libf.wgpuDeviceGetLimits(device_id, c_supported_limits) limits = {to_snake_case(k): getattr(c_limits, k) for k in dir(c_limits)} @@ -652,14 +776,14 @@ def callback(status, result, message, userdata): for f in sorted(enums.FeatureName): key = f"FeatureName.{f}" i = enummap[key] - # H: bool f(WGPUDevice device, WGPUFeatureName feature) + # H: WGPUBool f(WGPUDevice device, WGPUFeatureName feature) if libf.wgpuDeviceHasFeature(device_id, i): features.add(f) # Native features for f in NATIVE_FEATURES: i = getattr(lib, f"WGPUNativeFeature_{f}") - # H: bool f(WGPUDevice device, WGPUFeatureName feature) + # H: WGPUBool f(WGPUDevice device, WGPUFeatureName feature) if libf.wgpuDeviceHasFeature(device_id, i): features.add(f) @@ -716,7 +840,7 @@ def uncaptured_error_callback(c_type, c_message, userdata): def _poll(self): # Internal function if self._internal: - # H: bool f(WGPUDevice device, bool wait, WGPUWrappedSubmissionIndex const * wrappedSubmissionIndex) + # H: WGPUBool f(WGPUDevice device, WGPUBool wait, WGPUWrappedSubmissionIndex const * wrappedSubmissionIndex) libf.wgpuDevicePoll(self._internal, True, ffi.NULL) def create_buffer( @@ -733,7 +857,7 @@ def _create_buffer(self, label, size, usage, mapped_at_creation): # Create a buffer object if isinstance(usage, str): usage = str_flag_to_int(flags.BufferUsage, usage) - # H: nextInChain: WGPUChainedStruct *, label: char *, usage: WGPUBufferUsageFlags/int, size: int, mappedAtCreation: bool + # H: nextInChain: WGPUChainedStruct *, label: char *, usage: WGPUBufferUsageFlags/int, size: int, mappedAtCreation: WGPUBool/int struct = new_struct_p( "WGPUBufferDescriptor *", label=to_c_label(label), @@ -874,7 +998,7 @@ def create_bind_group_layout( min_binding_size = info.get("min_binding_size", None) if min_binding_size is None: min_binding_size = 0 # lib.WGPU_LIMIT_U64_UNDEFINED - # H: nextInChain: WGPUChainedStruct *, type: WGPUBufferBindingType, hasDynamicOffset: bool, minBindingSize: int + # H: nextInChain: WGPUChainedStruct *, type: WGPUBufferBindingType, hasDynamicOffset: WGPUBool/int, minBindingSize: int buffer = new_struct( "WGPUBufferBindingLayout", type=info["type"], @@ -894,7 +1018,7 @@ def create_bind_group_layout( elif entry.get("texture"): info = entry["texture"] check_struct("TextureBindingLayout", info) - # H: nextInChain: WGPUChainedStruct *, sampleType: WGPUTextureSampleType, viewDimension: WGPUTextureViewDimension, multisampled: bool + # H: nextInChain: WGPUChainedStruct *, sampleType: WGPUTextureSampleType, viewDimension: WGPUTextureViewDimension, multisampled: WGPUBool/int texture = new_struct( "WGPUTextureBindingLayout", sampleType=info.get("sample_type", "float"), @@ -1281,7 +1405,7 @@ def create_render_pipeline( depthFailOp=stencil_back.get("depth_fail_op", "keep"), passOp=stencil_back.get("pass_op", "keep"), ) - # H: nextInChain: WGPUChainedStruct *, format: WGPUTextureFormat, depthWriteEnabled: bool, depthCompare: WGPUCompareFunction, stencilFront: WGPUStencilFaceState, stencilBack: WGPUStencilFaceState, stencilReadMask: int, stencilWriteMask: int, depthBias: int, depthBiasSlopeScale: float, depthBiasClamp: float + # H: nextInChain: WGPUChainedStruct *, format: WGPUTextureFormat, depthWriteEnabled: WGPUBool/int, depthCompare: WGPUCompareFunction, stencilFront: WGPUStencilFaceState, stencilBack: WGPUStencilFaceState, stencilReadMask: int, stencilWriteMask: int, depthBias: int, depthBiasSlopeScale: float, depthBiasClamp: float c_depth_stencil_state = new_struct_p( "WGPUDepthStencilState *", format=depth_stencil["format"], @@ -1297,7 +1421,7 @@ def create_render_pipeline( # not used: nextInChain ) - # H: nextInChain: WGPUChainedStruct *, count: int, mask: int, alphaToCoverageEnabled: bool + # H: nextInChain: WGPUChainedStruct *, count: int, mask: int, alphaToCoverageEnabled: WGPUBool/int c_multisample_state = new_struct( "WGPUMultisampleState", count=multisample.get("count", 1), @@ -1965,13 +2089,12 @@ def begin_compute_pass( ): if timestamp_writes is not None: check_struct("ComputePassTimestampWrites", timestamp_writes) - # H: nextInChain: WGPUChainedStruct *, label: char *, timestampWriteCount: int, timestampWrites: WGPUComputePassTimestampWrite * + # H: nextInChain: WGPUChainedStruct *, label: char *, timestampWrites: WGPUComputePassTimestampWrites * struct = new_struct_p( "WGPUComputePassDescriptor *", label=to_c_label(label), - # not used: nextInChain - # not used: timestampWriteCount # not used: timestampWrites + # not used: nextInChain ) # H: WGPUComputePassEncoder f(WGPUCommandEncoder commandEncoder, WGPUComputePassDescriptor const * descriptor) raw_pass = libf.wgpuCommandEncoderBeginComputePass(self._internal, struct) @@ -2014,7 +2137,7 @@ def begin_render_pass( b=clear_value[2], a=clear_value[3], ) - # H: view: WGPUTextureView, resolveTarget: WGPUTextureView, loadOp: WGPULoadOp, storeOp: WGPUStoreOp, clearValue: WGPUColor + # H: nextInChain: WGPUChainedStruct *, view: WGPUTextureView, resolveTarget: WGPUTextureView, loadOp: WGPULoadOp, storeOp: WGPUStoreOp, clearValue: WGPUColor c_attachment = new_struct( "WGPURenderPassColorAttachment", view=texture_view_id, @@ -2023,6 +2146,7 @@ def begin_render_pass( storeOp=color_attachment["store_op"], clearValue=c_clear_value, # not used: resolveTarget + # not used: nextInChain ) c_color_attachments_list.append(c_attachment) c_color_attachments_array = ffi.new( @@ -2034,7 +2158,7 @@ def begin_render_pass( check_struct("RenderPassDepthStencilAttachment", depth_stencil_attachment) depth_clear_value = depth_stencil_attachment.get("depth_clear_value", 0) stencil_clear_value = depth_stencil_attachment.get("stencil_clear_value", 0) - # H: view: WGPUTextureView, depthLoadOp: WGPULoadOp, depthStoreOp: WGPUStoreOp, depthClearValue: float, depthReadOnly: bool, stencilLoadOp: WGPULoadOp, stencilStoreOp: WGPUStoreOp, stencilClearValue: int, stencilReadOnly: bool + # H: view: WGPUTextureView, depthLoadOp: WGPULoadOp, depthStoreOp: WGPUStoreOp, depthClearValue: float, depthReadOnly: WGPUBool/int, stencilLoadOp: WGPULoadOp, stencilStoreOp: WGPUStoreOp, stencilClearValue: int, stencilReadOnly: WGPUBool/int c_depth_stencil_attachment = new_struct_p( "WGPURenderPassDepthStencilAttachment *", view=depth_stencil_attachment["view"]._internal, @@ -2050,7 +2174,7 @@ def begin_render_pass( ), ) - # H: nextInChain: WGPUChainedStruct *, label: char *, colorAttachmentCount: int, colorAttachments: WGPURenderPassColorAttachment *, depthStencilAttachment: WGPURenderPassDepthStencilAttachment *, occlusionQuerySet: WGPUQuerySet, timestampWriteCount: int, timestampWrites: WGPURenderPassTimestampWrite * + # H: nextInChain: WGPUChainedStruct *, label: char *, colorAttachmentCount: int, colorAttachments: WGPURenderPassColorAttachment *, depthStencilAttachment: WGPURenderPassDepthStencilAttachment *, occlusionQuerySet: WGPUQuerySet, timestampWrites: WGPURenderPassTimestampWrites * struct = new_struct_p( "WGPURenderPassDescriptor *", label=to_c_label(label), @@ -2058,9 +2182,8 @@ def begin_render_pass( colorAttachmentCount=len(c_color_attachments_list), depthStencilAttachment=c_depth_stencil_attachment, # not used: occlusionQuerySet - # not used: nextInChain - # not used: timestampWriteCount # not used: timestampWrites + # not used: nextInChain ) # H: WGPURenderPassEncoder f(WGPUCommandEncoder commandEncoder, WGPURenderPassDescriptor const * descriptor) diff --git a/wgpu/backends/wgpu_native/_mappings.py b/wgpu/backends/wgpu_native/_mappings.py index de279200..9d8f083e 100644 --- a/wgpu/backends/wgpu_native/_mappings.py +++ b/wgpu/backends/wgpu_native/_mappings.py @@ -52,16 +52,16 @@ "ErrorFilter.internal": 2, "ErrorFilter.out-of-memory": 1, "ErrorFilter.validation": 0, - "FeatureName.bgra8unorm-storage": 11, + "FeatureName.bgra8unorm-storage": 10, "FeatureName.depth-clip-control": 1, "FeatureName.depth32float-stencil8": 2, - "FeatureName.float32-filterable": 12, - "FeatureName.indirect-first-instance": 8, - "FeatureName.rg11b10ufloat-renderable": 10, - "FeatureName.shader-f16": 9, - "FeatureName.texture-compression-astc": 7, - "FeatureName.texture-compression-bc": 5, - "FeatureName.texture-compression-etc2": 6, + "FeatureName.float32-filterable": 11, + "FeatureName.indirect-first-instance": 7, + "FeatureName.rg11b10ufloat-renderable": 9, + "FeatureName.shader-f16": 8, + "FeatureName.texture-compression-astc": 6, + "FeatureName.texture-compression-bc": 4, + "FeatureName.texture-compression-etc2": 5, "FeatureName.timestamp-query": 3, "FilterMode.linear": 1, "FilterMode.nearest": 0, @@ -81,7 +81,7 @@ "PrimitiveTopology.triangle-list": 3, "PrimitiveTopology.triangle-strip": 4, "QueryType.occlusion": 0, - "QueryType.timestamp": 2, + "QueryType.timestamp": 1, "SamplerBindingType.comparison": 3, "SamplerBindingType.filtering": 1, "SamplerBindingType.non-filtering": 2, @@ -281,7 +281,7 @@ "StorageTextureBindingLayout.access": "StorageTextureAccess", "StorageTextureBindingLayout.format": "TextureFormat", "StorageTextureBindingLayout.viewDimension": "TextureViewDimension", - "SwapChainDescriptor.format": "TextureFormat", + "SurfaceConfiguration.format": "TextureFormat", "TextureBindingLayout.sampleType": "TextureSampleType", "TextureBindingLayout.viewDimension": "TextureViewDimension", "TextureDescriptor.dimension": "TextureDimension", @@ -333,7 +333,117 @@ 5: "DeviceLost", }, "DeviceLostReason": { + 0: "unknown", + 1: "destroyed", + }, + "TextureFormat": { 0: "Undefined", - 1: "Destroyed", + 1: "r8unorm", + 2: "r8snorm", + 3: "r8uint", + 4: "r8sint", + 5: "r16uint", + 6: "r16sint", + 7: "r16float", + 8: "rg8unorm", + 9: "rg8snorm", + 10: "rg8uint", + 11: "rg8sint", + 12: "r32float", + 13: "r32uint", + 14: "r32sint", + 15: "rg16uint", + 16: "rg16sint", + 17: "rg16float", + 18: "rgba8unorm", + 19: "rgba8unorm-srgb", + 20: "rgba8snorm", + 21: "rgba8uint", + 22: "rgba8sint", + 23: "bgra8unorm", + 24: "bgra8unorm-srgb", + 25: "rgb10a2unorm", + 26: "rg11b10ufloat", + 27: "rgb9e5ufloat", + 28: "rg32float", + 29: "rg32uint", + 30: "rg32sint", + 31: "rgba16uint", + 32: "rgba16sint", + 33: "rgba16float", + 34: "rgba32float", + 35: "rgba32uint", + 36: "rgba32sint", + 37: "stencil8", + 38: "depth16unorm", + 39: "depth24plus", + 40: "depth24plus-stencil8", + 41: "depth32float", + 42: "depth32float-stencil8", + 43: "bc1-rgba-unorm", + 44: "bc1-rgba-unorm-srgb", + 45: "bc2-rgba-unorm", + 46: "bc2-rgba-unorm-srgb", + 47: "bc3-rgba-unorm", + 48: "bc3-rgba-unorm-srgb", + 49: "bc4-r-unorm", + 50: "bc4-r-snorm", + 51: "bc5-rg-unorm", + 52: "bc5-rg-snorm", + 53: "bc6h-rgb-ufloat", + 54: "bc6h-rgb-float", + 55: "bc7-rgba-unorm", + 56: "bc7-rgba-unorm-srgb", + 57: "etc2-rgb8unorm", + 58: "etc2-rgb8unorm-srgb", + 59: "etc2-rgb8a1unorm", + 60: "etc2-rgb8a1unorm-srgb", + 61: "etc2-rgba8unorm", + 62: "etc2-rgba8unorm-srgb", + 63: "eac-r11unorm", + 64: "eac-r11snorm", + 65: "eac-rg11unorm", + 66: "eac-rg11snorm", + 67: "astc-4x4-unorm", + 68: "astc-4x4-unorm-srgb", + 69: "astc-5x4-unorm", + 70: "astc-5x4-unorm-srgb", + 71: "astc-5x5-unorm", + 72: "astc-5x5-unorm-srgb", + 73: "astc-6x5-unorm", + 74: "astc-6x5-unorm-srgb", + 75: "astc-6x6-unorm", + 76: "astc-6x6-unorm-srgb", + 77: "astc-8x5-unorm", + 78: "astc-8x5-unorm-srgb", + 79: "astc-8x6-unorm", + 80: "astc-8x6-unorm-srgb", + 81: "astc-8x8-unorm", + 82: "astc-8x8-unorm-srgb", + 83: "astc-10x5-unorm", + 84: "astc-10x5-unorm-srgb", + 85: "astc-10x6-unorm", + 86: "astc-10x6-unorm-srgb", + 87: "astc-10x8-unorm", + 88: "astc-10x8-unorm-srgb", + 89: "astc-10x10-unorm", + 90: "astc-10x10-unorm-srgb", + 91: "astc-12x10-unorm", + 92: "astc-12x10-unorm-srgb", + 93: "astc-12x12-unorm", + 94: "astc-12x12-unorm-srgb", + }, + "PresentMode": { + 0: "Fifo", + 1: "FifoRelaxed", + 2: "Immediate", + 3: "Mailbox", + }, + "CompositeAlphaMode": { + 0: "Auto", + 1: "Opaque", + 2: "Premultiplied", + 3: "Unpremultiplied", + 4: "Inherit", }, } diff --git a/wgpu/resources/codegen_report.md b/wgpu/resources/codegen_report.md index 3f0d40f9..62489d67 100644 --- a/wgpu/resources/codegen_report.md +++ b/wgpu/resources/codegen_report.md @@ -3,7 +3,7 @@ * The webgpu.idl defines 37 classes with 76 functions * The webgpu.idl defines 5 flags, 33 enums, 59 structs * The wgpu.h defines 198 functions -* The wgpu.h defines 6 flags, 49 enums, 88 structs +* The wgpu.h defines 7 flags, 50 enums, 92 structs ## Updating API * Wrote 5 flags to flags.py * Wrote 33 enums to enums.py @@ -18,7 +18,7 @@ * Diffs for GPUQueue: add read_buffer, add read_texture, hide copy_external_image_to_texture * Validated 37 classes, 111 methods, 43 properties ### Patching API for backends/wgpu_native/_api.py -* Validated 37 classes, 100 methods, 0 properties +* Validated 37 classes, 102 methods, 0 properties ## Validating backends/wgpu_native/_api.py * Enum field TextureFormat.rgb10a2uint missing in wgpu.h * Enum field StorageTextureAccess.read-only missing in wgpu.h @@ -29,6 +29,6 @@ * Enum CanvasAlphaMode missing in wgpu.h * Enum field DeviceLostReason.unknown missing in wgpu.h * Wrote 232 enum mappings and 47 struct-field mappings to wgpu_native/_mappings.py -* Validated 89 C function calls -* Not using 113 C functions -* Validated 71 C structs +* Validated 99 C function calls +* Not using 102 C functions +* Validated 73 C structs From ca487dfe4881225a16ea280be2cc7356b0cb5f8d Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Tue, 21 Nov 2023 12:25:18 +0100 Subject: [PATCH 04/20] Closing on on canvas context --- codegen/wgpu_native_patcher.py | 1 + wgpu/_classes.py | 6 +- wgpu/backends/wgpu_native/_api.py | 122 ++++++++++++++++++------- wgpu/backends/wgpu_native/_mappings.py | 5 + wgpu/resources/codegen_report.md | 6 +- 5 files changed, 99 insertions(+), 41 deletions(-) diff --git a/codegen/wgpu_native_patcher.py b/codegen/wgpu_native_patcher.py index 9cb4b533..bc8110d0 100644 --- a/codegen/wgpu_native_patcher.py +++ b/codegen/wgpu_native_patcher.py @@ -137,6 +137,7 @@ def write_mappings(): "ErrorType", "DeviceLostReason", "TextureFormat", + "TextureDimension", "PresentMode", "CompositeAlphaMode", ]: diff --git a/wgpu/_classes.py b/wgpu/_classes.py index 58f77a7c..343e6862 100644 --- a/wgpu/_classes.py +++ b/wgpu/_classes.py @@ -201,10 +201,8 @@ def unconfigure(self): # IDL: GPUTexture getCurrentTexture(); def get_current_texture(self): - """Get the `GPUTexture` that will be composited to the canvas - by the context next. - - NOTE: for the time being, this could return a `GPUTextureView` instead. + """Get the `GPUTexture` that will be composited to the canvas next. + This method should be called exactly once during each draw event. """ raise NotImplementedError() diff --git a/wgpu/backends/wgpu_native/_api.py b/wgpu/backends/wgpu_native/_api.py index 25008e9a..5f816c69 100644 --- a/wgpu/backends/wgpu_native/_api.py +++ b/wgpu/backends/wgpu_native/_api.py @@ -350,12 +350,19 @@ async def request_adapter_async( class GPUCanvasContext(classes.GPUCanvasContext): + # The way this works, is that the context must first be configured. + # Then a texture can be obtained, which can be written to, and then it + # can be presented. The lifetime of the texture is between + # get_current_texture() and present(). We keep track of the texture so + # we can give meaningful errors/warnings on invalid use, rather than + # the more cryptic Rust panics. + def __init__(self, canvas): super().__init__(canvas) self._device = None # set in configure() self._surface_id = None self._config = None - self._psize = None + self._texture = None def _get_surface_id(self): if self._surface_id is None: @@ -469,6 +476,8 @@ def configure( present_mode = (present_modes or capable_present_modes)[0] c_present_mode = getattr(lib, f"WGPUPresentMode_{present_mode.capitalize()}") + # Prepare config object + # H: nextInChain: WGPUChainedStruct *, device: WGPUDevice, format: WGPUTextureFormat, usage: WGPUTextureUsageFlags/int, viewFormatCount: int, viewFormats: WGPUTextureFormat *, alphaMode: WGPUCompositeAlphaMode, width: int, height: int, presentMode: WGPUPresentMode config = new_struct_p( "WGPUSurfaceConfiguration *", @@ -483,14 +492,22 @@ def configure( presentMode=c_present_mode, # not used: nextInChain ) + + # Configure self._configure(config) def _configure(self, config): + # If a texture is still active, better destroy it first + self._destroy_texture() + # Set the size width, height = self._get_canvas().get_physical_size() config.width = width config.height = height if width <= 0 or height <= 0: + # todo: test this case, is this right thing to do? Ideally we should simply not draw raise RuntimeError("Cannot configure canvas to size ({width}, {height}).") + # Configure, and store the config if we did not error out + # H: void f(WGPUSurface surface, WGPUSurfaceConfiguration const * config) libf.wgpuSurfaceConfigure(self._get_surface_id(), config) self._config = config @@ -501,19 +518,40 @@ def _maybe_reconfigure(self): self._configure(config) def unconfigure(self): + self._destroy_texture() self._config = None # H: void f(WGPUSurface surface) libf.wgpuSurfaceUnconfigure(self._get_surface_id()) + def _destroy_texture(self): + if self._texture: + self._texture.destroy() + self._texture = None + def get_current_texture(self): - # If the canvas has changed since the last configure, we need to re-configure + # If the canvas has changed since the last configure, we need to re-configure it if not self._config: raise RuntimeError( - "Canvas context must be configured before a surface texture can be obtained." + "Canvas context must be configured before calling get_current_texture()." ) self._maybe_reconfigure() - # Try to obtain a texture + # When the texture is active right now, we could either: + # * return the existing texture + # * warn about it, and create a new one + # * raise an error + # Right now we do the warning, so things still (kinda) keep working + if self._texture: + self._destroy_texture() + logger.warning( + "get_current_texture() is called multiple times before pesent()." + ) + + # Try to obtain a texture. + # `If it fails, depending on status, we reconfure and try again. + + # todo: Look into drag-between-monitors issue again + # H: texture: WGPUTexture, suboptimal: WGPUBool/int, status: WGPUSurfaceGetCurrentTextureStatus surface_texture = new_struct_p( "WGPUSurfaceTexture *", @@ -522,15 +560,17 @@ def get_current_texture(self): # not used: status ) - for iter in range(3): + for attempt in [1, 2]: + # H: void f(WGPUSurface surface, WGPUSurfaceTexture * surfaceTexture) libf.wgpuSurfaceGetCurrentTexture(self._get_surface_id(), surface_texture) status = surface_texture.status texture_id = surface_texture.texture if status == lib.WGPUSurfaceGetCurrentTextureStatus_Success: break # success if texture_id: + # H: void f(WGPUTexture texture) libf.wgpuTextureRelease(texture_id) - if status in [ + if attempt == 1 and status in [ lib.WGPUSurfaceGetCurrentTextureStatus_Timeout, lib.WGPUSurfaceGetCurrentTextureStatus_Timeout, lib.WGPUSurfaceGetCurrentTextureStatus_Outdated, @@ -539,12 +579,11 @@ def get_current_texture(self): # Configure and try again logger.warning("Re-configuring canvas context.") self._configure(self._config) - continue else: # WGPUSurfaceGetCurrentTextureStatus_OutOfMemory # WGPUSurfaceGetCurrentTextureStatus_DeviceLost - # catchall - raise RuntimeError(f"Cannot get surface texture ({status})") + # Or if this is the second attempt. + raise RuntimeError(f"Cannot get surface texture ({status}).") # I don't expect this to happen, but lets check just in case. if not texture_id: @@ -552,11 +591,25 @@ def get_current_texture(self): # Things look good, but texture may still be suboptimal, whatever that means if surface_texture.suboptimal: - # todo: rate-limit this warning logger.warning("The surface texture is suboptimal.") + return self._create_python_texture(texture_id) + # todo: this is now a texture, not a view! + + def _create_python_texture(self, texture_id): # Create the Python wrapper + # We can derive texture props from the config and common sense: + # width = self._config.width + # height = self._config.height + # depth = 1 + # mip_level_count = 1 + # sample_count = 1 + # dimension = enums.TextureDimension.d2 + # format = enum_int2str["TextureFormat"][self._config.format] + # usage = self._config.usage + + # But we can also read them from the texture # H: uint32_t f(WGPUTexture texture) width = libf.wgpuTextureGetWidth(texture_id) # H: uint32_t f(WGPUTexture texture) @@ -564,46 +617,47 @@ def get_current_texture(self): # H: uint32_t f(WGPUTexture texture) depth = libf.wgpuTextureGetDepthOrArrayLayers(texture_id) # H: uint32_t f(WGPUTexture texture) - sample_count = 1 # libf.wgpuTextureGetSampleCount(texture_id) + mip_level_count = libf.wgpuTextureGetMipLevelCount(texture_id) + # H: uint32_t f(WGPUTexture texture) + sample_count = libf.wgpuTextureGetSampleCount(texture_id) + # H: WGPUTextureDimension f(WGPUTexture texture) + c_dim = libf.wgpuTextureGetDimension(texture_id) # -> to string + dimension = enum_int2str["TextureDimension"][c_dim] + # H: WGPUTextureFormat f(WGPUTexture texture) + c_format = libf.wgpuTextureGetFormat(texture_id) + format = enum_int2str["TextureFormat"][c_format] # H: WGPUTextureUsageFlags f(WGPUTexture texture) usage = libf.wgpuTextureGetUsage(texture_id) - # H: WGPUTextureFormat f(WGPUTexture texture) - format = libf.wgpuTextureGetFormat(texture_id) - # H: WGPUTextureDimension f(WGPUTexture texture) - dim = ( - enums.TextureDimension.d2 - ) # libf.wgpuTextureGetDimension(texture_id) # -> to string label = "" # Cannot yet set label, because it's not implemented in wgpu-native # label = "surface-texture" + # H: void f(WGPUTexture texture, char const * label) # libf.wgpuTextureSetLabel(texture_id, to_c_label(label)) tex_info = { "size": (width, height, depth), - # H: uint32_t f(WGPUTexture texture) - "mip_level_count": 1, # libf.wgpuTextureGetMipLevelCount(texture_id) + "mip_level_count": mip_level_count, "sample_count": sample_count, - "dimension": dim, + "dimension": dimension, "format": format, "usage": usage, } - self._tex = GPUTexture(label, texture_id, self._device, tex_info) - return self._tex - # todo: this is now a texture, not a vierw! - - # todo: include these errors somehow? - # raise RuntimeError( - # "Preset context must be configured before get_current_texture()." - # ) - # extra_msg = "\nThis may be caused by dragging the window to a monitor with different dpi. " - # extra_msg += "Resize window to proceed.\n" - # err.args = (err.args[0] + extra_msg,) + err.args[1:] - # raise err from None + + self._texture = GPUTexture(label, texture_id, self._device, tex_info) + return self._texture def present(self): - # H: void f(WGPUSurface surface) - libf.wgpuSurfacePresent(self._get_surface_id()) + if not self._texture: + msg = "present() is called without a preceeding call to " + msg += "get_current_texture(). Note that present() is usually " + msg += "called automatically after the draw function returns." + raise RuntimeError(msg) + else: + # Present the texture, then destroy it + # H: void f(WGPUSurface surface) + libf.wgpuSurfacePresent(self._get_surface_id()) + self._destroy_texture() def get_preferred_format(self, adapter): # H: WGPUTextureFormat f(WGPUSurface surface, WGPUAdapter adapter) diff --git a/wgpu/backends/wgpu_native/_mappings.py b/wgpu/backends/wgpu_native/_mappings.py index 9d8f083e..cde8a6f6 100644 --- a/wgpu/backends/wgpu_native/_mappings.py +++ b/wgpu/backends/wgpu_native/_mappings.py @@ -433,6 +433,11 @@ 93: "astc-12x12-unorm", 94: "astc-12x12-unorm-srgb", }, + "TextureDimension": { + 0: "1d", + 1: "2d", + 2: "3d", + }, "PresentMode": { 0: "Fifo", 1: "FifoRelaxed", diff --git a/wgpu/resources/codegen_report.md b/wgpu/resources/codegen_report.md index 62489d67..e91c658d 100644 --- a/wgpu/resources/codegen_report.md +++ b/wgpu/resources/codegen_report.md @@ -18,7 +18,7 @@ * Diffs for GPUQueue: add read_buffer, add read_texture, hide copy_external_image_to_texture * Validated 37 classes, 111 methods, 43 properties ### Patching API for backends/wgpu_native/_api.py -* Validated 37 classes, 102 methods, 0 properties +* Validated 37 classes, 106 methods, 0 properties ## Validating backends/wgpu_native/_api.py * Enum field TextureFormat.rgb10a2uint missing in wgpu.h * Enum field StorageTextureAccess.read-only missing in wgpu.h @@ -29,6 +29,6 @@ * Enum CanvasAlphaMode missing in wgpu.h * Enum field DeviceLostReason.unknown missing in wgpu.h * Wrote 232 enum mappings and 47 struct-field mappings to wgpu_native/_mappings.py -* Validated 99 C function calls -* Not using 102 C functions +* Validated 101 C function calls +* Not using 101 C functions * Validated 73 C structs From 13cde217f37d7342d5d82ad28f01ea5e9ff941c4 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Tue, 21 Nov 2023 12:47:01 +0100 Subject: [PATCH 05/20] check present modes --- wgpu/backends/wgpu_native/_api.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/wgpu/backends/wgpu_native/_api.py b/wgpu/backends/wgpu_native/_api.py index 5f816c69..2e035661 100644 --- a/wgpu/backends/wgpu_native/_api.py +++ b/wgpu/backends/wgpu_native/_api.py @@ -454,24 +454,23 @@ def configure( ) # Select the present mode to determine vsync behavior. + # * https://docs.rs/wgpu/latest/wgpu/enum.PresentMode.html + # * https://github.com/pygfx/wgpu-py/issues/256 # - # 0 Immediate: no waiting, with risk of tearing. - # 1 Mailbox: submit without delay, but present on vsync. Not always available. - # 2 Fifo: Wait for vsync. + # Fifo: Wait for vsync, with a queue of ± 3 frames. + # FifoRelaxed: Like fifo but less lag and more tearing? aka adaptive vsync. + # Mailbox: submit without queue, but present on vsync. Not always available. + # Immediate: no queue, no waiting, with risk of tearing, vsync off. # - # In general 2 gives the best result, but sometimes people want to + # In general Fifo gives the best result, but sometimes people want to # benchmark something and get the highest FPS possible. Note # that we've observed rate limiting regardless of setting this - # to 0, depending on OS or being on battery power. - # - # Also see: - # * https://github.com/gfx-rs/wgpu/blob/e54a36ee/wgpu-types/src/lib.rs#L2663-L2678 - # * https://github.com/pygfx/wgpu-py/issues/256 + # to Immediate, depending on OS or being on battery power. if getattr(canvas, "_vsync", True): - present_mode_pref = ["fifo", "immediate"] + present_mode_pref = ["fifo", "mailbox"] else: - present_mode_pref = ["immediate", "fifo"] + present_mode_pref = ["immediate", "mailbox", "fifo"] present_modes = [p for p in present_mode_pref if p in capable_present_modes] present_mode = (present_modes or capable_present_modes)[0] c_present_mode = getattr(lib, f"WGPUPresentMode_{present_mode.capitalize()}") From 43597e09b69f8fcff401eb1aa6bce0d158c5b40d Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Tue, 21 Nov 2023 14:17:09 +0100 Subject: [PATCH 06/20] GPU object __repr__ bit more conservative --- wgpu/_classes.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/wgpu/_classes.py b/wgpu/_classes.py index 343e6862..87aa028b 100644 --- a/wgpu/_classes.py +++ b/wgpu/_classes.py @@ -2064,10 +2064,13 @@ def _seed_object_counts(): def generic_repr(self): try: - module_name = "wgpu" - if "backends." in self.__module__: - backend_name = self.__module__.split("backends")[-1].split(".")[1] - module_name = f"wgpu.backends.{backend_name}" + module_name = self.__module__ + if module_name.startswith("wgpu"): + if module_name == "wgpu._classes": + module_name = "wgpu" + elif "backends." in module_name: + backend_name = self.__module__.split("backends")[-1].split(".")[1] + module_name = f"wgpu.backends.{backend_name}" object_str = "object" if isinstance(self, GPUObjectBase): object_str = f"object '{self.label}'" From da54590ded48deede078e97712e64c785381e92b Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Tue, 21 Nov 2023 14:20:15 +0100 Subject: [PATCH 07/20] Fixing tests and examples --- examples/cube.py | 2 +- examples/triangle.py | 2 +- tests/renderutils.py | 2 +- tests/test_gui_base.py | 10 +-- tests/test_gui_glfw.py | 2 +- wgpu/backends/wgpu_native/_api.py | 19 +++-- wgpu/gui/_offscreen.py | 113 +++++++++++++++++++----------- wgpu/gui/base.py | 8 +-- wgpu/gui/jupyter.py | 8 +-- wgpu/gui/offscreen.py | 8 +-- wgpu/utils/shadertoy.py | 5 +- 11 files changed, 110 insertions(+), 69 deletions(-) diff --git a/examples/cube.py b/examples/cube.py index e12a3e61..18dc4e15 100644 --- a/examples/cube.py +++ b/examples/cube.py @@ -356,7 +356,7 @@ def draw_frame(): tmp_buffer, 0, uniform_buffer, 0, uniform_data.nbytes ) - current_texture_view = present_context.get_current_texture() + current_texture_view = present_context.get_current_texture().create_view() render_pass = command_encoder.begin_render_pass( color_attachments=[ { diff --git a/examples/triangle.py b/examples/triangle.py index 17b3f035..2759ce8b 100644 --- a/examples/triangle.py +++ b/examples/triangle.py @@ -122,7 +122,7 @@ def _main(canvas, device): ) def draw_frame(): - current_texture_view = present_context.get_current_texture() + current_texture_view = present_context.get_current_texture().create_view() command_encoder = device.create_command_encoder() render_pass = command_encoder.begin_render_pass( diff --git a/tests/renderutils.py b/tests/renderutils.py index 0ed4693b..e88e8930 100644 --- a/tests/renderutils.py +++ b/tests/renderutils.py @@ -277,7 +277,7 @@ def render_to_screen( present_context.configure(device=device, format=None) def draw_frame(): - current_texture_view = present_context.get_current_texture() + current_texture_view = present_context.get_current_texture().create_view() command_encoder = device.create_command_encoder() ca = color_attachment or { diff --git a/tests/test_gui_base.py b/tests/test_gui_base.py index 6402b9b8..f8022c68 100644 --- a/tests/test_gui_base.py +++ b/tests/test_gui_base.py @@ -92,13 +92,13 @@ def _request_draw(self): # Note: this would normaly schedule a call in a later event loop iteration self._draw_frame_and_present() - def present(self, texture_view): - device = texture_view._device - size = texture_view.size + def present(self, texture): + device = texture._device + size = texture.size bytes_per_pixel = 4 data = device.queue.read_texture( { - "texture": texture_view.texture, + "texture": texture, "mip_level": 0, "origin": (0, 0, 0), }, @@ -121,7 +121,7 @@ def test_offscreen_canvas(): @canvas.request_draw def draw_frame(): - current_texture_view = present_context.get_current_texture() + current_texture_view = present_context.get_current_texture().create_view() command_encoder = device.create_command_encoder() render_pass = command_encoder.begin_render_pass( color_attachments=[ diff --git a/tests/test_gui_glfw.py b/tests/test_gui_glfw.py index a11f4df1..2ee8af01 100644 --- a/tests/test_gui_glfw.py +++ b/tests/test_gui_glfw.py @@ -269,7 +269,7 @@ def _get_draw_function(device, canvas): ) def draw_frame(): - current_texture_view = present_context.get_current_texture() + current_texture_view = present_context.get_current_texture().create_view() command_encoder = device.create_command_encoder() assert current_texture_view.size ca = { diff --git a/wgpu/backends/wgpu_native/_api.py b/wgpu/backends/wgpu_native/_api.py index 2e035661..5f92c9d4 100644 --- a/wgpu/backends/wgpu_native/_api.py +++ b/wgpu/backends/wgpu_native/_api.py @@ -200,8 +200,8 @@ def request_adapter( surface_id = ffi.NULL if canvas is not None: window_id = canvas.get_window_id() - if window_id is not None: # e.g. could be an off-screen canvas - surface_id = canvas.context._get_surface_id() + if window_id: # e.g. could be an off-screen canvas + surface_id = canvas.get_context()._get_surface_id() # ----- Select backend @@ -396,7 +396,8 @@ def configure( # Lookup alpha mode, needs explicit conversion because enum names mismatch c_alpha_mode = getattr(lib, f"WGPUCompositeAlphaMode_{alpha_mode.capitalize()}") # The format is used as-is - format + if format is None: + format = self.get_preferred_format(device.adapter) # The color_space is not used for now color_space @@ -2167,12 +2168,16 @@ def begin_render_pass( if timestamp_writes is not None: check_struct("RenderPassTimestampWrites", timestamp_writes) + objects_to_keep_alive = {} + c_color_attachments_list = [] for color_attachment in color_attachments: check_struct("RenderPassColorAttachment", color_attachment) - if not isinstance(color_attachment["view"], GPUTextureView): + texture_view = color_attachment["view"] + if not isinstance(texture_view, GPUTextureView): raise TypeError("Color attachement view must be a GPUTextureView.") - texture_view_id = color_attachment["view"]._internal + texture_view_id = texture_view._internal + objects_to_keep_alive[texture_view_id] = texture_view c_resolve_target = ( ffi.NULL if color_attachment.get("resolve_target", None) is None @@ -2241,7 +2246,9 @@ def begin_render_pass( # H: WGPURenderPassEncoder f(WGPUCommandEncoder commandEncoder, WGPURenderPassDescriptor const * descriptor) raw_pass = libf.wgpuCommandEncoderBeginRenderPass(self._internal, struct) - return GPURenderPassEncoder(label, raw_pass, self) + encoder = GPURenderPassEncoder(label, raw_pass, self) + encoder._objects_to_keep_alive = objects_to_keep_alive + return encoder def clear_buffer(self, buffer, offset=0, size=None): offset = int(offset) diff --git a/wgpu/gui/_offscreen.py b/wgpu/gui/_offscreen.py index 64f12965..090b492a 100644 --- a/wgpu/gui/_offscreen.py +++ b/wgpu/gui/_offscreen.py @@ -5,9 +5,9 @@ class WgpuOffscreenCanvas(WgpuCanvasBase): """Base class for off-screen canvases. - It provides a custom presentation context that renders to a tetxure - instead of a surface/screen. The resulting texture view is passed - to the `present()` method. + It provides a custom context that renders to a texture instead of + a surface/screen. The resulting texture is passed to the `present()` + method. """ def __init__(self, *args, **kwargs): @@ -17,18 +17,21 @@ def get_window_id(self): """This canvas does not correspond to an on-screen window.""" return None - def get_context(self, kind="gpupresent"): + def get_context(self, kind="webgpu"): """Get the GPUCanvasContext object to obtain a texture to render to.""" # Normally this creates a GPUCanvasContext object provided by - # the backend (e.g. rs), but here we use our own context. - assert kind == "gpupresent" + # the backend (e.g. wgpu-native), but here we use our own context. + assert kind == "webgpu" if self._canvas_context is None: self._canvas_context = GPUCanvasContext(self) return self._canvas_context - def present(self, texture_view): - """Method that gets called at the end of each draw event. Subclasses - should provide the approproate implementation. + def present(self, texture): + """Method that gets called at the end of each draw event. + The rendered image is represented by the texture argument. + Subclasses should overload this method and use the texture to + process the rendered image. Note that the texture is destroyed + soon after this method returns. """ pass @@ -48,44 +51,74 @@ class GPUCanvasContext(classes.GPUCanvasContext): def __init__(self, canvas): super().__init__(canvas) - self._surface_size = (-1, -1) + self._config = None self._texture = None - self._texture_view = None - def unconfigure(self): - super().unconfigure() - # todo: maybe destroy texture? (currently API is unclear about destroying) - self._texture = None + def _destroy_texture(self): + if self._texture: + self._texture.destroy() + self._texture = None + + def configure( + self, + *, + device, + format, + usage=flags.TextureUsage.RENDER_ATTACHMENT | flags.TextureUsage.COPY_SRC, + view_formats=[], + color_space="srgb", + alpha_mode="opaque" + ): + if format is None: + format = self.get_preferred_format(device.adapter) + self._destroy_texture() + self._config = { + "device": device, + "format": format, + "usage": usage, + "width": 0, + "height": 0, + # "view_formats": xx, + # "color_space": xx, + # "alpha_mode": xx, + } - def get_preferred_format(self, adapter): - canvas = self._get_canvas() - if canvas: - return canvas.get_preferred_format() - else: - return "rgba8unorm-srgb" + def unconfigure(self): + self._destroy_texture() def get_current_texture(self): - self._create_new_texture_if_needed() - # Technically a texture view, even though WebGPU says it must be a texture. - # Anyway, this API will change in a next version anyway. - return self._texture_view + if not self._config: + raise RuntimeError( + "Canvas context must be configured before calling get_current_texture()." + ) + + width, height = self._get_canvas().get_physical_size() + width, height = max(width, 1), max(height, 1) + + self._destroy_texture() + self._texture = self._config["device"].create_texture( + label="presentation-context", + size=(width, height, 1), + format=self._config["format"], + usage=self._config["usage"], + ) + return self._texture def present(self): - if self._texture_view is not None: + if not self._texture: + msg = "present() is called without a preceeding call to " + msg += "get_current_texture(). Note that present() is usually " + msg += "called automatically after the draw function returns." + raise RuntimeError(msg) + else: canvas = self._get_canvas() - return canvas.present(self._texture_view) + result = canvas.present(self._texture) + self._destroy_texture() + return result - def _create_new_texture_if_needed(self): + def get_preferred_format(self, adapter): canvas = self._get_canvas() - psize = canvas.get_physical_size() - if psize == self._surface_size: - return - self._surface_size = psize - - self._texture = self._device.create_texture( - label="presentation-context", - size=(max(psize[0], 1), max(psize[1], 1), 1), - format=self._format, - usage=self._usage | flags.TextureUsage.COPY_SRC, - ) - self._texture_view = self._texture.create_view() + if canvas: + return canvas.get_preferred_format() + else: + return "rgba8unorm-srgb" diff --git a/wgpu/gui/base.py b/wgpu/gui/base.py index ab03b932..9f6ed4d2 100644 --- a/wgpu/gui/base.py +++ b/wgpu/gui/base.py @@ -106,13 +106,13 @@ def get_physical_size(self): """Get the physical size of the canvas in integer pixels.""" raise NotImplementedError() - def get_context(self, kind="gpupresent"): + def get_context(self, kind="webgpu"): """Get the GPUCanvasContext object corresponding to this canvas, which can be used to e.g. obtain a texture to render to. """ - # Note that this function is analog to HtmlCanvas.get_context(), except - # here the only valid arg is 'gpupresent', which is also made the default. - assert kind == "gpupresent" + # Note that this function is analog to HtmlCanvas.getContext(), except + # here the only valid arg is 'webgpu', which is also made the default. + assert kind == "webgpu" if self._canvas_context is None: # Get the active wgpu backend module backend_module = sys.modules["wgpu"].gpu.__module__ diff --git a/wgpu/gui/jupyter.py b/wgpu/gui/jupyter.py index 5a84b844..15286909 100644 --- a/wgpu/gui/jupyter.py +++ b/wgpu/gui/jupyter.py @@ -90,14 +90,14 @@ def _request_draw(self): # Implementation needed for WgpuOffscreenCanvas - def present(self, texture_view): + def present(self, texture): # This gets called at the end of a draw pass via _offscreen.GPUCanvasContext - device = texture_view._device - size = texture_view.size + device = texture._device + size = texture.size bytes_per_pixel = 4 data = device.queue.read_texture( { - "texture": texture_view.texture, + "texture": texture, "mip_level": 0, "origin": (0, 0, 0), }, diff --git a/wgpu/gui/offscreen.py b/wgpu/gui/offscreen.py index b5a1985d..c10287d8 100644 --- a/wgpu/gui/offscreen.py +++ b/wgpu/gui/offscreen.py @@ -41,14 +41,14 @@ def _request_draw(self): # Deliberately a no-op, because people use .draw() instead. pass - def present(self, texture_view): + def present(self, texture): # This gets called at the end of a draw pass via _offscreen.GPUCanvasContext - device = texture_view._device - size = texture_view.size + device = texture._device + size = texture.size bytes_per_pixel = 4 data = device.queue.read_texture( { - "texture": texture_view.texture, + "texture": texture, "mip_level": 0, "origin": (0, 0, 0), }, diff --git a/wgpu/utils/shadertoy.py b/wgpu/utils/shadertoy.py index 997434ac..c20b52f0 100644 --- a/wgpu/utils/shadertoy.py +++ b/wgpu/utils/shadertoy.py @@ -445,12 +445,13 @@ def _draw_frame(self): command_encoder = self._device.create_command_encoder() - current_texture_view = self._present_context.get_current_texture() + # Get texture to render to. Keep a ref to the view to avoid premature destruction + current_texture = self._present_context.get_current_texture() render_pass = command_encoder.begin_render_pass( color_attachments=[ { - "view": current_texture_view, + "view": current_texture.create_view(), "resolve_target": None, "clear_value": (0, 0, 0, 1), "load_op": wgpu.LoadOp.clear, From 9ef69b4be38610d2d6d5b294a9361f5d784bb88f Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Tue, 21 Nov 2023 14:54:53 +0100 Subject: [PATCH 08/20] Fix offscreen canvas context and corresponding mem test --- tests_mem/test_gui_offscreen.py | 12 +++++------ wgpu/backends/wgpu_native/_api.py | 1 + wgpu/gui/_offscreen.py | 34 +++++++++++++++---------------- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/tests_mem/test_gui_offscreen.py b/tests_mem/test_gui_offscreen.py index 3c02b914..29d617bb 100644 --- a/tests_mem/test_gui_offscreen.py +++ b/tests_mem/test_gui_offscreen.py @@ -28,7 +28,7 @@ def make_draw_func_for_canvas(canvas): def draw(): ctx = canvas.get_context() command_encoder = DEVICE.create_command_encoder() - current_texture_view = ctx.get_current_texture() + current_texture_view = ctx.get_current_texture().create_view() render_pass = command_encoder.begin_render_pass( color_attachments=[ { @@ -42,7 +42,6 @@ def draw(): ) render_pass.end() DEVICE.queue.submit([command_encoder.finish()]) - ctx.present() return draw @@ -51,17 +50,16 @@ def draw(): def test_release_canvas_context(n): # Test with offscreen canvases. A context is created, but not a wgpu-native surface. - # Note: the offscreen canvas keeps the render-texture-view alive, since it - # is used to e.g. download the resulting image. That's why we also see - # Textures and TextureViews in the counts. + # Note: the offscreen canvas keeps the render-texture alive, since it + # is used to e.g. download the resulting image, and who knows how the + # user want to use the result. The context does drop its ref to the + # textures, which is why we don't see textures in the measurements. from wgpu.gui.offscreen import WgpuCanvas yield { "expected_counts_after_create": { "CanvasContext": (n, 0), - "Texture": (n, n), - "TextureView": (n, n), }, } diff --git a/wgpu/backends/wgpu_native/_api.py b/wgpu/backends/wgpu_native/_api.py index 5f92c9d4..8d7109f9 100644 --- a/wgpu/backends/wgpu_native/_api.py +++ b/wgpu/backends/wgpu_native/_api.py @@ -671,6 +671,7 @@ def _destroy(self): self._surface_id, surface_id = None, self._surface_id # H: void f(WGPUSurface surface) libf.wgpuSurfaceRelease(surface_id) + self._destroy_texture() class GPUObjectBase(classes.GPUObjectBase): diff --git a/wgpu/gui/_offscreen.py b/wgpu/gui/_offscreen.py index 090b492a..2db40560 100644 --- a/wgpu/gui/_offscreen.py +++ b/wgpu/gui/_offscreen.py @@ -27,11 +27,11 @@ def get_context(self, kind="webgpu"): return self._canvas_context def present(self, texture): - """Method that gets called at the end of each draw event. - The rendered image is represented by the texture argument. - Subclasses should overload this method and use the texture to - process the rendered image. Note that the texture is destroyed - soon after this method returns. + """Method that gets called at the end of each draw event. The + rendered image is represented by the texture argument. Subclasses + should overload this method and use the texture to process the + rendered image. The texture is not explicitly destroyed, so it can + be used e.g. as a texture binding (subject to set TextureUsage). """ pass @@ -49,16 +49,18 @@ def get_preferred_format(self): class GPUCanvasContext(classes.GPUCanvasContext): """Helper class for canvases that render to a texture.""" + # In this context implementation, we keep a ref to the texture, to keep + # it alive until at least until present() is called, and to be able to + # pass it to the canvas' present() method. Thereafter, the texture + # reference is removed. If there are no more references to it, it will + # be cleaned up. But if the offscreen canvas uses it for something, + # it'll simply stay alive longer. + def __init__(self, canvas): super().__init__(canvas) self._config = None self._texture = None - def _destroy_texture(self): - if self._texture: - self._texture.destroy() - self._texture = None - def configure( self, *, @@ -71,7 +73,6 @@ def configure( ): if format is None: format = self.get_preferred_format(device.adapter) - self._destroy_texture() self._config = { "device": device, "format": format, @@ -84,7 +85,8 @@ def configure( } def unconfigure(self): - self._destroy_texture() + self._texture = None + self._config = None def get_current_texture(self): if not self._config: @@ -95,7 +97,6 @@ def get_current_texture(self): width, height = self._get_canvas().get_physical_size() width, height = max(width, 1), max(height, 1) - self._destroy_texture() self._texture = self._config["device"].create_texture( label="presentation-context", size=(width, height, 1), @@ -111,10 +112,9 @@ def present(self): msg += "called automatically after the draw function returns." raise RuntimeError(msg) else: - canvas = self._get_canvas() - result = canvas.present(self._texture) - self._destroy_texture() - return result + texture = self._texture + self._texture = None + return self._get_canvas().present(texture) def get_preferred_format(self, adapter): canvas = self._get_canvas() From aa58e34e239d2e164acf37c428bbb7d8f8f8cc42 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Tue, 21 Nov 2023 16:41:47 +0100 Subject: [PATCH 09/20] Avoid canvas ref sticking around --- wgpu/backends/wgpu_native/_api.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/wgpu/backends/wgpu_native/_api.py b/wgpu/backends/wgpu_native/_api.py index 8d7109f9..1109f0b4 100644 --- a/wgpu/backends/wgpu_native/_api.py +++ b/wgpu/backends/wgpu_native/_api.py @@ -367,8 +367,7 @@ def __init__(self, canvas): def _get_surface_id(self): if self._surface_id is None: # get_surface_id_from_canvas calls wgpuInstanceCreateSurface - canvas = self._get_canvas() - self._surface_id = get_surface_id_from_canvas(canvas) + self._surface_id = get_surface_id_from_canvas(self._get_canvas()) return self._surface_id def configure( @@ -401,10 +400,6 @@ def configure( # The color_space is not used for now color_space - # Prepare - canvas = self._get_canvas() - surface_id = self._get_surface_id() - # Get what's supported # H: nextInChain: WGPUChainedStructOut *, formatCount: int, formats: WGPUTextureFormat *, presentModeCount: int, presentModes: WGPUPresentMode *, alphaModeCount: int, alphaModes: WGPUCompositeAlphaMode * @@ -420,7 +415,7 @@ def configure( ) # H: void f(WGPUSurface surface, WGPUAdapter adapter, WGPUSurfaceCapabilities * capabilities) libf.wgpuSurfaceGetCapabilities( - surface_id, self._device.adapter._internal, capabilities + self._get_surface_id(), self._device.adapter._internal, capabilities ) capable_formats = [] @@ -467,8 +462,7 @@ def configure( # benchmark something and get the highest FPS possible. Note # that we've observed rate limiting regardless of setting this # to Immediate, depending on OS or being on battery power. - - if getattr(canvas, "_vsync", True): + if getattr(self._get_canvas(), "_vsync", True): present_mode_pref = ["fifo", "mailbox"] else: present_mode_pref = ["immediate", "mailbox", "fifo"] @@ -667,11 +661,11 @@ def get_preferred_format(self, adapter): return enum_int2str["TextureFormat"][format] def _destroy(self): + self._destroy_texture() if self._surface_id is not None and libf is not None: self._surface_id, surface_id = None, self._surface_id # H: void f(WGPUSurface surface) libf.wgpuSurfaceRelease(surface_id) - self._destroy_texture() class GPUObjectBase(classes.GPUObjectBase): From cf286477e002b04ea49cba53f0acf0e1623603bf Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Tue, 21 Nov 2023 16:42:03 +0100 Subject: [PATCH 10/20] Fix memtest for glfw canvases --- tests_mem/test_gui_glfw.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests_mem/test_gui_glfw.py b/tests_mem/test_gui_glfw.py index cd31ec1d..5f4f9b69 100644 --- a/tests_mem/test_gui_glfw.py +++ b/tests_mem/test_gui_glfw.py @@ -52,9 +52,10 @@ def test_release_canvas_context(n): loop.run_until_complete(stub_event_loop()) gc.collect() loop.run_until_complete(stub_event_loop()) + gc.collect() # Check that the canvas objects are really deleted - assert not canvases + assert not canvases, f"Still {len(canvases)} canvases" if __name__ == "__main__": From bbe6a29cea900b61057f7d8f0c8039e68c7e03d4 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Tue, 21 Nov 2023 17:06:09 +0100 Subject: [PATCH 11/20] Finalize how offscreen canvas behaves wrt texture, and check in test --- tests/test_gui_base.py | 41 +++++++++++++++++++++++++++++++++++++++-- wgpu/gui/_offscreen.py | 19 ++++++++++++++----- 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/tests/test_gui_base.py b/tests/test_gui_base.py index f8022c68..e3060bc9 100644 --- a/tests/test_gui_base.py +++ b/tests/test_gui_base.py @@ -79,6 +79,11 @@ def test_canvas_logging(caplog): class MyOffscreenCanvas(wgpu.gui.WgpuOffscreenCanvas): + def __init__(self): + super().__init__() + self.textures = [] + self.physical_size = 100, 100 + def get_pixel_ratio(self): return 1 @@ -86,13 +91,14 @@ def get_logical_size(self): return self.get_physical_size() def get_physical_size(self): - return 100, 100 + return self.physical_size def _request_draw(self): # Note: this would normaly schedule a call in a later event loop iteration self._draw_frame_and_present() def present(self, texture): + self.textures.append(texture) device = texture._device size = texture.size bytes_per_pixel = 4 @@ -119,7 +125,6 @@ def test_offscreen_canvas(): present_context = canvas.get_context() present_context.configure(device=device, format=None) - @canvas.request_draw def draw_frame(): current_texture_view = present_context.get_current_texture().create_view() command_encoder = device.create_command_encoder() @@ -137,10 +142,42 @@ def draw_frame(): render_pass.end() device.queue.submit([command_encoder.finish()]) + assert len(canvas.textures) == 0 + + # Draw 1 + canvas.request_draw(draw_frame) assert canvas.array.shape == (100, 100, 4) assert np.all(canvas.array[:, :, 0] == 0) assert np.all(canvas.array[:, :, 1] == 255) + # Draw 2 + canvas.request_draw(draw_frame) + assert canvas.array.shape == (100, 100, 4) + assert np.all(canvas.array[:, :, 0] == 0) + assert np.all(canvas.array[:, :, 1] == 255) + + # Change resolution + canvas.physical_size = 120, 100 + + # Draw 3 + canvas.request_draw(draw_frame) + assert canvas.array.shape == (100, 120, 4) + assert np.all(canvas.array[:, :, 0] == 0) + assert np.all(canvas.array[:, :, 1] == 255) + + # Change resolution + canvas.physical_size = 120, 140 + + # Draw 4 + canvas.request_draw(draw_frame) + assert canvas.array.shape == (140, 120, 4) + assert np.all(canvas.array[:, :, 0] == 0) + assert np.all(canvas.array[:, :, 1] == 255) + + # We now have four unique texture objects + assert len(canvas.textures) == 4 + assert len(set(canvas.textures)) == 4 + def test_autogui_mixin(): c = wgpu.gui.WgpuAutoGui() diff --git a/wgpu/gui/_offscreen.py b/wgpu/gui/_offscreen.py index 2db40560..8a76b41f 100644 --- a/wgpu/gui/_offscreen.py +++ b/wgpu/gui/_offscreen.py @@ -27,12 +27,21 @@ def get_context(self, kind="webgpu"): return self._canvas_context def present(self, texture): - """Method that gets called at the end of each draw event. The - rendered image is represented by the texture argument. Subclasses - should overload this method and use the texture to process the - rendered image. The texture is not explicitly destroyed, so it can - be used e.g. as a texture binding (subject to set TextureUsage). + """Method that gets called at the end of each draw event. + + The rendered image is represented by the texture argument. + Subclasses should overload this method and use the texture to + process the rendered image. The texture is a new object at each + draw, but is not explicitly destroyed, so it can be used e.g. + as a texture binding (subject to set TextureUsage). """ + # Notes: Creating a new texture object for each draw is + # consistent with how real canvas contexts work, plus it avoids + # confusion of re-using the same texture except when the canvas + # changes size. For use-cases when you do want to render to the + # same texture one does not need the canvas API. E.g. in pygfx + # the renderer can also work with a target that is a (fixed + # size) texture. pass def get_preferred_format(self): From 37ad4b0c19f50ff04ff19e41c669250adcb2249d Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Tue, 21 Nov 2023 21:56:28 +0100 Subject: [PATCH 12/20] Prevent warning when moving window between monitors --- wgpu/backends/wgpu_native/_api.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/wgpu/backends/wgpu_native/_api.py b/wgpu/backends/wgpu_native/_api.py index 1109f0b4..f7be53ff 100644 --- a/wgpu/backends/wgpu_native/_api.py +++ b/wgpu/backends/wgpu_native/_api.py @@ -505,12 +505,6 @@ def _configure(self, config): libf.wgpuSurfaceConfigure(self._get_surface_id(), config) self._config = config - def _maybe_reconfigure(self): - config = self._config - width, height = self._get_canvas().get_physical_size() - if width != config.width or height != config.height: - self._configure(config) - def unconfigure(self): self._destroy_texture() self._config = None @@ -528,7 +522,6 @@ def get_current_texture(self): raise RuntimeError( "Canvas context must be configured before calling get_current_texture()." ) - self._maybe_reconfigure() # When the texture is active right now, we could either: # * return the existing texture @@ -544,8 +537,6 @@ def get_current_texture(self): # Try to obtain a texture. # `If it fails, depending on status, we reconfure and try again. - # todo: Look into drag-between-monitors issue again - # H: texture: WGPUTexture, suboptimal: WGPUBool/int, status: WGPUSurfaceGetCurrentTextureStatus surface_texture = new_struct_p( "WGPUSurfaceTexture *", @@ -570,8 +561,14 @@ def get_current_texture(self): lib.WGPUSurfaceGetCurrentTextureStatus_Outdated, lib.WGPUSurfaceGetCurrentTextureStatus_Lost, ]: - # Configure and try again - logger.warning("Re-configuring canvas context.") + # Configure and try again. + # This happens e.g. when the window has resized (status==Outdated), + # but also when moving the window from one monitor to another + # with different scale-factor. Pre-emptively re-configuring + # when the canvas size changes can cause errors being logged + # in the latter case (issue #352). So just letting it fail and + # then retry seems like the correct approach. + logger.info(f"Re-configuring canvas context ({status}).") self._configure(self._config) else: # WGPUSurfaceGetCurrentTextureStatus_OutOfMemory @@ -588,7 +585,6 @@ def get_current_texture(self): logger.warning("The surface texture is suboptimal.") return self._create_python_texture(texture_id) - # todo: this is now a texture, not a view! def _create_python_texture(self, texture_id): # Create the Python wrapper From 8197bdfd4b1c52c1a1be49b65b34f2caa64fe7a3 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Tue, 21 Nov 2023 22:19:47 +0100 Subject: [PATCH 13/20] Fix window resize on macos --- wgpu/backends/wgpu_native/_api.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/wgpu/backends/wgpu_native/_api.py b/wgpu/backends/wgpu_native/_api.py index f7be53ff..205c326a 100644 --- a/wgpu/backends/wgpu_native/_api.py +++ b/wgpu/backends/wgpu_native/_api.py @@ -498,8 +498,9 @@ def _configure(self, config): config.width = width config.height = height if width <= 0 or height <= 0: - # todo: test this case, is this right thing to do? Ideally we should simply not draw - raise RuntimeError("Cannot configure canvas to size ({width}, {height}).") + raise RuntimeError( + "Cannot configure canvas that has no pixels ({width}x{height})." + ) # Configure, and store the config if we did not error out # H: void f(WGPUSurface surface, WGPUSurfaceConfiguration const * config) libf.wgpuSurfaceConfigure(self._get_surface_id(), config) @@ -550,6 +551,11 @@ def get_current_texture(self): libf.wgpuSurfaceGetCurrentTexture(self._get_surface_id(), surface_texture) status = surface_texture.status texture_id = surface_texture.texture + # Check size too + old_size = (self._config.width, self._config.height) + new_size = tuple(self._get_canvas().get_physical_size()) + if old_size != new_size: + status = "resized" if status == lib.WGPUSurfaceGetCurrentTextureStatus_Success: break # success if texture_id: @@ -560,6 +566,7 @@ def get_current_texture(self): lib.WGPUSurfaceGetCurrentTextureStatus_Timeout, lib.WGPUSurfaceGetCurrentTextureStatus_Outdated, lib.WGPUSurfaceGetCurrentTextureStatus_Lost, + "resized", ]: # Configure and try again. # This happens e.g. when the window has resized (status==Outdated), From 592c3eb6424b9dbf5e8a9c27bda8eed068a0f8d8 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Tue, 21 Nov 2023 23:37:15 +0100 Subject: [PATCH 14/20] Fix from Windows again --- wgpu/backends/wgpu_native/_api.py | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/wgpu/backends/wgpu_native/_api.py b/wgpu/backends/wgpu_native/_api.py index 205c326a..cfc1909c 100644 --- a/wgpu/backends/wgpu_native/_api.py +++ b/wgpu/backends/wgpu_native/_api.py @@ -535,6 +535,21 @@ def get_current_texture(self): "get_current_texture() is called multiple times before pesent()." ) + # Reconfigure when the canvas has resized. + # On some systems (Windows+Qt) this is not necessary, because + # the texture status would be Outdated below, resulting in a + # reconfigure. But on others (e.g. glfwf) the texture size does + # not have to match the window size, apparently. The downside + # for doing this check on the former systems, is that errors + # get logged, which would not be there if we did not + # pre-emptively reconfigure. These log entries are harmless but + # anoying, and I currently don't know how to prevent them + # elegantly. See issue #352 + old_size = (self._config.width, self._config.height) + new_size = tuple(self._get_canvas().get_physical_size()) + if old_size != new_size: + self._configure(self._config) + # Try to obtain a texture. # `If it fails, depending on status, we reconfure and try again. @@ -551,11 +566,6 @@ def get_current_texture(self): libf.wgpuSurfaceGetCurrentTexture(self._get_surface_id(), surface_texture) status = surface_texture.status texture_id = surface_texture.texture - # Check size too - old_size = (self._config.width, self._config.height) - new_size = tuple(self._get_canvas().get_physical_size()) - if old_size != new_size: - status = "resized" if status == lib.WGPUSurfaceGetCurrentTextureStatus_Success: break # success if texture_id: @@ -566,15 +576,11 @@ def get_current_texture(self): lib.WGPUSurfaceGetCurrentTextureStatus_Timeout, lib.WGPUSurfaceGetCurrentTextureStatus_Outdated, lib.WGPUSurfaceGetCurrentTextureStatus_Lost, - "resized", ]: # Configure and try again. - # This happens e.g. when the window has resized (status==Outdated), - # but also when moving the window from one monitor to another - # with different scale-factor. Pre-emptively re-configuring - # when the canvas size changes can cause errors being logged - # in the latter case (issue #352). So just letting it fail and - # then retry seems like the correct approach. + # On Window+Qt this happens e.g. when the window has resized + # (status==Outdated), but also when moving the window from one + # monitor to another with different scale-factor. logger.info(f"Re-configuring canvas context ({status}).") self._configure(self._config) else: From e34dbc8bbe0137cfc465577a3336959daae94e5f Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Thu, 23 Nov 2023 10:30:22 +0100 Subject: [PATCH 15/20] Minor tweaks --- examples/triangle.py | 4 ++-- wgpu/gui/_offscreen.py | 2 +- wgpu/utils/shadertoy.py | 2 -- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/examples/triangle.py b/examples/triangle.py index 2759ce8b..80a14de9 100644 --- a/examples/triangle.py +++ b/examples/triangle.py @@ -122,13 +122,13 @@ def _main(canvas, device): ) def draw_frame(): - current_texture_view = present_context.get_current_texture().create_view() + current_texture = present_context.get_current_texture() command_encoder = device.create_command_encoder() render_pass = command_encoder.begin_render_pass( color_attachments=[ { - "view": current_texture_view, + "view": current_texture.create_view(), "resolve_target": None, "clear_value": (0, 0, 0, 1), "load_op": wgpu.LoadOp.clear, diff --git a/wgpu/gui/_offscreen.py b/wgpu/gui/_offscreen.py index 8a76b41f..b240236f 100644 --- a/wgpu/gui/_offscreen.py +++ b/wgpu/gui/_offscreen.py @@ -38,7 +38,7 @@ def present(self, texture): # Notes: Creating a new texture object for each draw is # consistent with how real canvas contexts work, plus it avoids # confusion of re-using the same texture except when the canvas - # changes size. For use-cases when you do want to render to the + # changes size. For use-cases where you do want to render to the # same texture one does not need the canvas API. E.g. in pygfx # the renderer can also work with a target that is a (fixed # size) texture. diff --git a/wgpu/utils/shadertoy.py b/wgpu/utils/shadertoy.py index bf3f6af2..c4918eb9 100644 --- a/wgpu/utils/shadertoy.py +++ b/wgpu/utils/shadertoy.py @@ -465,8 +465,6 @@ def _draw_frame(self): ) command_encoder = self._device.create_command_encoder() - - # Get texture to render to. Keep a ref to the view to avoid premature destruction current_texture = self._present_context.get_current_texture() render_pass = command_encoder.begin_render_pass( From 8a6d5f15999779ee8e7d413f59fd60347da43537 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Thu, 23 Nov 2023 12:31:36 +0100 Subject: [PATCH 16/20] codegen --- wgpu/resources/codegen_report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wgpu/resources/codegen_report.md b/wgpu/resources/codegen_report.md index e91c658d..a9dc8f96 100644 --- a/wgpu/resources/codegen_report.md +++ b/wgpu/resources/codegen_report.md @@ -18,7 +18,7 @@ * Diffs for GPUQueue: add read_buffer, add read_texture, hide copy_external_image_to_texture * Validated 37 classes, 111 methods, 43 properties ### Patching API for backends/wgpu_native/_api.py -* Validated 37 classes, 106 methods, 0 properties +* Validated 37 classes, 105 methods, 0 properties ## Validating backends/wgpu_native/_api.py * Enum field TextureFormat.rgb10a2uint missing in wgpu.h * Enum field StorageTextureAccess.read-only missing in wgpu.h From 098bd406889e007f4d6c80eddaf9e5f04f579d64 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Thu, 23 Nov 2023 12:31:44 +0100 Subject: [PATCH 17/20] Update changelog --- CHANGELOG.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28810bdc..94fef742 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,27 @@ Possible sections in each release: * Security: in case of vulnerabilities. +### [v0.13.0] - TBD + +IN this release we + +Changed: + +* Update to wgpu-native 0.18.1.1. +* `CanvasContext.get_current_texture()` now returns a `GPUTexture` instead of a `GPUTextureView`. +* `OffscreenCanvas.present()` now receives a `GPUTexture` instead of a `GPUTextureView`, + and this is a new texture on each draw (no re-use). +* `GPUCommandEncoder.begin_render_pass()` binds the lifetime of passed texture views to + the returned render pass object to prevent premature destruction when no reference to + a texture view is kept. + +Fixed: + +* Dragging a window between windows with different scale factor (with Qt on Windows) no longer + puts the window in an invalid state. A warning is still produced though. +* Fixed that the canonical class name in the `__repr__` of GPU classes was changed in cases where it shouldn't. + + ### [v0.12.0] - 15-11-2023 This is a big release that contains many improvements, but also multiple API changes. From 75a184eb69ac794c5f38d359ec0803cc6199d05d Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Thu, 23 Nov 2023 12:41:45 +0100 Subject: [PATCH 18/20] pypy needs more help cleaning up --- tests_mem/testutils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests_mem/testutils.py b/tests_mem/testutils.py index 36d75029..55e282e1 100644 --- a/tests_mem/testutils.py +++ b/tests_mem/testutils.py @@ -78,6 +78,7 @@ def clear_mem(): gc.collect() if is_pypy: gc.collect() + gc.collect() def get_counts(): From 4d32b62ddbd9dc22124f842891dda7367fda30c4 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Thu, 23 Nov 2023 13:08:46 +0100 Subject: [PATCH 19/20] That was the wrong place for extra collect --- tests_mem/test_gui_offscreen.py | 1 + tests_mem/testutils.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/tests_mem/test_gui_offscreen.py b/tests_mem/test_gui_offscreen.py index 29d617bb..0dd2e381 100644 --- a/tests_mem/test_gui_offscreen.py +++ b/tests_mem/test_gui_offscreen.py @@ -75,6 +75,7 @@ def test_release_canvas_context(n): gc.collect() if is_pypy: gc.collect() # Need a bit more on pypy :) + gc.collect() # Check that the canvas objects are really deleted assert not canvases diff --git a/tests_mem/testutils.py b/tests_mem/testutils.py index 55e282e1..36d75029 100644 --- a/tests_mem/testutils.py +++ b/tests_mem/testutils.py @@ -78,7 +78,6 @@ def clear_mem(): gc.collect() if is_pypy: gc.collect() - gc.collect() def get_counts(): From 2a963ecab6c741758d4e09e2fcfa8b43212dce19 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Thu, 23 Nov 2023 14:36:42 +0100 Subject: [PATCH 20/20] fix changelo --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94fef742..cbd6a61b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,8 +19,6 @@ Possible sections in each release: ### [v0.13.0] - TBD -IN this release we - Changed: * Update to wgpu-native 0.18.1.1.