How to share a texture across compute and render pipeline in wgpu?

Hi, I am trying to get this working but I ran into this error:

wgpu error: Validation Error

Caused by:
In ComputePass::end
In a dispatch command, indirect:false
Attempted to use Texture with 'Output texture' label (mips 0..1 layers 0..1) with conflicting usages. Current usage TextureUses(STORAGE_READ_WRITE) and new usage TextureUses(RESOURCE). TextureUses(STORAGE_READ_WRITE) is an exclusive usage and cannot be used with any other usages within the usage scope (renderpass or compute dispatch).

The thing I defined the usage in shared bind group, so it is either write or read.
This is the texture:

let output_texture = device.create_texture(&TextureDescriptor {
            label: Some("Output texture"),
            size: Extent3d { width: surface_config.width, height: surface_config.height, depth_or_array_layers: 1},
            mip_level_count: 1,
            sample_count: 1,
            dimension: wgpu::TextureDimension::D2,
            format: TextureFormat::Rgba8Unorm,
            usage: TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING,
            view_formats: &[],
        });

        let output_view = output_texture.create_view(&TextureViewDescriptor::default());

The bind group:

let bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
            label: Some("Shared bindgroup layout"),
            entries: &[
                BindGroupLayoutEntry {
                    binding: 0,
                    visibility: ShaderStages::COMPUTE,
                    ty: wgpu::BindingType::StorageTexture {
                        access: StorageTextureAccess::WriteOnly,
                        format: TextureFormat::Rgba8Unorm,
                        view_dimension: wgpu::TextureViewDimension::D2
                    },
                    count: None,
                }, 
                BindGroupLayoutEntry {
                    binding: 1,
                    visibility: ShaderStages::FRAGMENT,
                    ty: wgpu::BindingType::Texture {
                        sample_type: wgpu::TextureSampleType::Float { filterable: false },
                        view_dimension: wgpu::TextureViewDimension::D2,
                        multisampled: false
                    },
                    count: None,
                },
            ],
        });

        let bind_group = device.create_bind_group(&BindGroupDescriptor {
            label: Some("Shared bindgroup"),
            layout: &bind_group_layout,
            entries: &[
                BindGroupEntry {
                    binding: 0,
                    resource: BindingResource::TextureView(&output_view),
                },
                BindGroupEntry {
                    binding: 1,
                    resource: BindingResource::TextureView(&output_view),
                }
            ]
        });

And the render function:

let mut encoder = self.device.create_command_encoder(&CommandEncoderDescriptor { 
            label: Some("Compute encoder"),
        });
        {
            let mut compute_pass = encoder.begin_compute_pass(&ComputePassDescriptor {
                label: Some("Compute pass"),
                timestamp_writes: None,
            });
            
            compute_pass.set_pipeline(&self.compute_pipeline);
            compute_pass.set_bind_group(0, &self.bind_group, &[]);
            compute_pass.dispatch_workgroups(
                (self.surface_config.width + 7) / 8, 
                (self.surface_config.height + 7) / 8, 
                1,
            );
        }

        {
            let frame = self.surface.get_current_texture().unwrap();
            let view = frame.texture.create_view(&TextureViewDescriptor::default());

            let mut render_pass = encoder.begin_render_pass(&RenderPassDescriptor {
                label: Some("Render pass"),
                color_attachments: &[Some(RenderPassColorAttachment {
                    view: &view,
                    resolve_target: None,
                    ops: Operations {
                        load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
                        store: wgpu::StoreOp::Discard,
                    }
                })],
                depth_stencil_attachment: None,
                timestamp_writes: None,
                occlusion_query_set: None,
            });

            render_pass.set_pipeline(&self.render_pipeline);
            render_pass.set_bind_group(0, &self.bind_group, &[]);
            render_pass.draw(0..6, 0..1);
        }

        self.queue.submit(Some(encoder.finish()));

I do not really know how to tackle this issue as I am learning the API, help appreciated.

Without reproducing the issue, it looks like it's that you're using one bind-group that binds the same texture with two different usages. So either have two bind groups (better), or use the same storage binding for both (e.g. use textureLoad() instead of textureSample() in WGSL to load without sampling).

The problem as I understand it is that the driver/hardware can and will overlap the compute and render passes if it can, and without separate bind groups WGPU can't insert the "memory barrier" between them that tells the hardware that writes from the compute pass will be read by the render pass.