@@ -334,10 +334,14 @@ def _get_limits(id: int, device: bool = False, adapter: bool = False):
334334 # not used: maxPushConstantSize
335335 # not used: maxNonSamplerBindings
336336 )
337+
338+ # Note that the object returned by ffi.cast() does not own the memory, so we must keep a ref to the uncast object, until wgpu-native has consumed it.
339+ c_limit_next_in_chain = ffi .addressof (c_limits_native , "chain" )
340+
337341 # H: nextInChain: WGPUChainedStructOut *, maxTextureDimension1D: int, maxTextureDimension2D: int, maxTextureDimension3D: int, maxTextureArrayLayers: int, maxBindGroups: int, maxBindGroupsPlusVertexBuffers: int, maxBindingsPerBindGroup: int, maxDynamicUniformBuffersPerPipelineLayout: int, maxDynamicStorageBuffersPerPipelineLayout: int, maxSampledTexturesPerShaderStage: int, maxSamplersPerShaderStage: int, maxStorageBuffersPerShaderStage: int, maxStorageTexturesPerShaderStage: int, maxUniformBuffersPerShaderStage: int, maxUniformBufferBindingSize: int, maxStorageBufferBindingSize: int, minUniformBufferOffsetAlignment: int, minStorageBufferOffsetAlignment: int, maxVertexBuffers: int, maxBufferSize: int, maxVertexAttributes: int, maxVertexBufferArrayStride: int, maxInterStageShaderVariables: int, maxColorAttachments: int, maxColorAttachmentBytesPerSample: int, maxComputeWorkgroupStorageSize: int, maxComputeInvocationsPerWorkgroup: int, maxComputeWorkgroupSizeX: int, maxComputeWorkgroupSizeY: int, maxComputeWorkgroupSizeZ: int, maxComputeWorkgroupsPerDimension: int
338342 c_limits = new_struct_p (
339343 "WGPULimits *" ,
340- nextInChain = ffi . addressof ( c_limits_native , "chain" ) ,
344+ nextInChain = c_limit_next_in_chain ,
341345 # not used: maxTextureDimension1D
342346 # not used: maxTextureDimension2D
343347 # not used: maxTextureDimension3D
@@ -1214,12 +1218,15 @@ def canonicalize_limit_name(name):
12141218 c_trace_path = to_c_string_view (trace_path if trace_path else None )
12151219
12161220 # H: chain: WGPUChainedStruct, tracePath: WGPUStringView
1217- extras = new_struct_p (
1221+ c_device_extras = new_struct_p (
12181222 "WGPUDeviceExtras *" ,
12191223 tracePath = c_trace_path ,
12201224 # not used: chain
12211225 )
1222- extras .chain .sType = lib .WGPUSType_DeviceExtras
1226+ c_device_extras .chain .sType = lib .WGPUSType_DeviceExtras
1227+
1228+ # Note that the object returned by ffi.cast() does not own the memory, so we must keep a ref to the uncast object, until wgpu-native has consumed it.
1229+ c_device_next_in_chain = ffi .cast ("WGPUChainedStruct * " , c_device_extras )
12231230
12241231 # ----- Device lost
12251232
@@ -1277,7 +1284,7 @@ def uncaptured_error_callback(
12771284 # H: nextInChain: WGPUChainedStruct *, label: WGPUStringView, requiredFeatureCount: int, requiredFeatures: WGPUFeatureName *, requiredLimits: WGPULimits *, defaultQueue: WGPUQueueDescriptor, deviceLostCallbackInfo: WGPUDeviceLostCallbackInfo, uncapturedErrorCallbackInfo: WGPUUncapturedErrorCallbackInfo
12781285 struct = new_struct_p (
12791286 "WGPUDeviceDescriptor *" ,
1280- nextInChain = ffi . cast ( "WGPUChainedStruct * " , extras ) ,
1287+ nextInChain = c_device_next_in_chain ,
12811288 label = to_c_string_view (label ),
12821289 requiredFeatureCount = len (c_features ),
12831290 requiredFeatures = new_array ("WGPUFeatureName[]" , c_features ),
@@ -1708,7 +1715,7 @@ def _create_pipeline_layout(
17081715 bind_group_layouts_ids = [x ._internal for x in bind_group_layouts ]
17091716 c_layout_array = new_array ("WGPUBindGroupLayout[]" , bind_group_layouts_ids )
17101717
1711- next_in_chain = ffi .NULL
1718+ c_pipeline_layout_next_in_chain = ffi .NULL
17121719 if push_constant_layouts :
17131720 count = len (push_constant_layouts )
17141721 c_push_constant_ranges = new_array ("WGPUPushConstantRange[]" , count )
@@ -1730,12 +1737,15 @@ def _create_pipeline_layout(
17301737 # not used: chain
17311738 )
17321739 c_pipeline_layout_extras .chain .sType = lib .WGPUSType_PipelineLayoutExtras
1733- next_in_chain = ffi .cast ("WGPUChainedStruct *" , c_pipeline_layout_extras )
1740+ # Note that the object returned by ffi.cast() does not own the memory, so we must keep a ref to the uncast object, until wgpu-native has consumed it.
1741+ c_pipeline_layout_next_in_chain = ffi .cast (
1742+ "WGPUChainedStruct *" , c_pipeline_layout_extras
1743+ )
17341744
17351745 # H: nextInChain: WGPUChainedStruct *, label: WGPUStringView, bindGroupLayoutCount: int, bindGroupLayouts: WGPUBindGroupLayout *
17361746 struct = new_struct_p (
17371747 "WGPUPipelineLayoutDescriptor *" ,
1738- nextInChain = next_in_chain ,
1748+ nextInChain = c_pipeline_layout_next_in_chain ,
17391749 label = to_c_string_view (label ),
17401750 bindGroupLayouts = c_layout_array ,
17411751 bindGroupLayoutCount = len (bind_group_layouts ),
@@ -1829,10 +1839,13 @@ def create_shader_module(
18291839 "Shader code must be str for WGSL or GLSL, or bytes for SpirV."
18301840 )
18311841
1842+ # Note that the object returned by ffi.cast() does not own the memory, so we must keep a ref to the uncast object, until wgpu-native has consumed it.
1843+ c_shader_module_next_in_chain = ffi .cast ("WGPUChainedStruct *" , source_struct )
1844+
18321845 # H: nextInChain: WGPUChainedStruct *, label: WGPUStringView
18331846 struct = new_struct_p (
18341847 "WGPUShaderModuleDescriptor *" ,
1835- nextInChain = ffi . cast ( "WGPUChainedStruct *" , source_struct ) ,
1848+ nextInChain = c_shader_module_next_in_chain ,
18361849 label = to_c_string_view (label ),
18371850 )
18381851 # H: WGPUShaderModule f(WGPUDevice device, WGPUShaderModuleDescriptor const * descriptor)
@@ -1964,7 +1977,7 @@ def create_render_pipeline(
19641977 ) -> GPURenderPipeline :
19651978 primitive = {} if primitive is None else primitive
19661979 multisample = {} if multisample is None else multisample
1967- descriptor = self ._create_render_pipeline_descriptor (
1980+ descriptor , _keep_alive = self ._create_render_pipeline_descriptor (
19681981 label , layout , vertex , primitive , depth_stencil , multisample , fragment
19691982 )
19701983 # H: WGPURenderPipeline f(WGPUDevice device, WGPURenderPipelineDescriptor const * descriptor)
@@ -1985,7 +1998,7 @@ def create_render_pipeline_async(
19851998 primitive = {} if primitive is None else primitive
19861999 multisample = {} if multisample is None else multisample
19872000 # TODO: wgpuDeviceCreateRenderPipelineAsync is not yet implemented in wgpu-native
1988- descriptor = self ._create_render_pipeline_descriptor (
2001+ descriptor , _keep_alive = self ._create_render_pipeline_descriptor (
19892002 label , layout , vertex , primitive , depth_stencil , multisample , fragment
19902003 )
19912004
@@ -2051,6 +2064,9 @@ def _create_render_pipeline_descriptor(
20512064 multisample : structs .MultisampleState ,
20522065 fragment : structs .FragmentState ,
20532066 ):
2067+ # We need to keep some objects alive until the struct is consumed by wgpu-native
2068+ keep_alive = []
2069+
20542070 depth_stencil = depth_stencil or {}
20552071 multisample = multisample or {}
20562072 primitive = primitive or {}
@@ -2098,12 +2114,17 @@ def _create_render_pipeline_descriptor(
20982114 conservative = primitive_extras .get ("conservative" , False ),
20992115 )
21002116 c_primitive_state_extras .chain .sType = lib .WGPUSType_PrimitiveStateExtras
2101- next_in_chain = ffi .cast ("WGPUChainedStruct *" , c_primitive_state_extras )
2117+
2118+ # Note that the object returned by ffi.cast() does not own the memory, so we must keep a ref to the uncast object, until wgpu-native has consumed it.
2119+ c_primitive_state_next_in_chain = ffi .cast (
2120+ "WGPUChainedStruct *" , c_primitive_state_extras
2121+ )
2122+ keep_alive .append (c_primitive_state_extras )
21022123
21032124 # H: nextInChain: WGPUChainedStruct *, topology: WGPUPrimitiveTopology, stripIndexFormat: WGPUIndexFormat, frontFace: WGPUFrontFace, cullMode: WGPUCullMode, unclippedDepth: WGPUBool/int
21042125 c_primitive_state = new_struct (
21052126 "WGPUPrimitiveState" ,
2106- nextInChain = next_in_chain ,
2127+ nextInChain = c_primitive_state_next_in_chain ,
21072128 topology = primitive .get ("topology" , "triangle-list" ),
21082129 stripIndexFormat = primitive .get ("strip_index_format" , 0 ),
21092130 frontFace = primitive .get ("front_face" , "ccw" ),
@@ -2170,7 +2191,7 @@ def _create_render_pipeline_descriptor(
21702191 multisample = c_multisample_state ,
21712192 fragment = c_fragment_state ,
21722193 )
2173- return struct
2194+ return struct , keep_alive
21742195
21752196 def _create_color_target_state (self , target ):
21762197 if not target .get ("blend" , None ):
@@ -2332,27 +2353,30 @@ def _create_statistics_query_set(self, label, count, statistics):
23322353 )
23332354
23342355 def _create_query_set (self , label , type , count , statistics ):
2335- next_in_chain = ffi .NULL
2356+ c_query_set_next_in_chain = ffi .NULL
23362357 if statistics :
23372358 c_statistics = new_array ("WGPUPipelineStatisticName[]" , statistics )
23382359 # H: chain: WGPUChainedStruct, pipelineStatistics: WGPUPipelineStatisticName *, pipelineStatisticCount: int
2339- query_set_descriptor_extras = new_struct_p (
2360+ c_query_set_descriptor_extras = new_struct_p (
23402361 "WGPUQuerySetDescriptorExtras *" ,
23412362 pipelineStatisticCount = len (statistics ),
23422363 pipelineStatistics = ffi .cast (
23432364 "WGPUPipelineStatisticName const *" , c_statistics
23442365 ),
23452366 # not used: chain
23462367 )
2347- query_set_descriptor_extras .chain .sType = (
2368+ c_query_set_descriptor_extras .chain .sType = (
23482369 lib .WGPUSType_QuerySetDescriptorExtras
23492370 )
2350- next_in_chain = ffi .cast ("WGPUChainedStruct *" , query_set_descriptor_extras )
2371+ # Note that the object returned by ffi.cast() does not own the memory, so we must keep a ref to the uncast object, until wgpu-native has consumed it.
2372+ c_query_set_next_in_chain = ffi .cast (
2373+ "WGPUChainedStruct *" , c_query_set_descriptor_extras
2374+ )
23512375
23522376 # H: nextInChain: WGPUChainedStruct *, label: WGPUStringView, type: WGPUQueryType, count: int
23532377 query_set_descriptor = new_struct_p (
23542378 "WGPUQuerySetDescriptor *" ,
2355- nextInChain = next_in_chain ,
2379+ nextInChain = c_query_set_next_in_chain ,
23562380 label = to_c_string_view (label ),
23572381 type = type ,
23582382 count = count ,
0 commit comments