BVE-Reborn / rend3

MAINTENCE MODE ---- Easy to use, customizable, efficient 3D renderer library built on wgpu.
https://rend3.rs
Apache License 2.0
1.05k stars 59 forks source link

Enable full aliasing of wgpu texture inside rend3 texture/material. #552

Open pillowtrucker opened 6 months ago

pillowtrucker commented 6 months ago

Sometimes, a third party library will write directly to a wgpu texture. E.g. to draw a 2d animation and/or 2d UI elements. Currently, the only way that I could find to display this texture on a rend3 material/texture is to copy the data with a separate instruction for the encoder for each frame. As mentioned on Discord - it should not be necessary to take ownership of the wgpu texture to link it to a rend3 texture. Currently, the code below kind of works, but my_texture_wgpu is consumed by my_texture_wgpu_internal. Because of that, I am no longer able to create further views of it or to directly access it with the third-party non-rend3-aware pure-wgpu renderer - which really only works if the texture is generated once, not for an animated texture/material. (or interactive UI element)

        let my_texture_descriptor = wgpu::TextureDescriptor {
            size: texture_size,
            mip_level_count: 1,
            sample_count: 1,
            dimension: wgpu::TextureDimension::D2,
            format,
            usage: wgpu::TextureUsages::COPY_SRC | wgpu::TextureUsages::RENDER_ATTACHMENT,
            label: Some("my texture"),
            view_formats: &[wgpu::TextureFormat::Bgra8Unorm],
        };

        let my_texture_wgpu = renderer.device.create_texture(&texture_descriptor);

        let texture_wgpu_view = my_texture_wgpu.create_view(&wgpu::TextureViewDescriptor::default());

        let my_texture_rend3 = rend3::types::Texture {
            label: Some("my texture but rend3".to_owned()),
            format,
            size: texture_size_uvec2,
            mip_count: MipmapCount::Specific(NonZeroU32::new(1).unwrap()),
            mip_source: rend3::types::MipmapSource::Uploaded,
            data: vec![0; (texture_size_uvec2.x * texture_size_uvec2.y * 4) as usize],
        };
        let my_texture_rend3_handle = renderer.add_texture_2d(my_texture_rend3).unwrap();

        let my_texture_rend3_raw_handle = my_texture_rend3_handle.get_raw();

        let  my_texture_wgpu_internal = InternalTexture {
            texture: my_texture_wgpu,
            view: my_texture_wgpu_view,
            desc: my_texture_descriptor,
        };
        {
             renderer
                .data_core
                .lock()
                 .d2_texture_manager
                 .fill(my_texture_rend3_raw_handle, my_texture_wgpu_internal);
        }

        // Create mesh and calculate smooth normals based on vertices
        let sprite_mesh = create_quad(300.0);
        // Add mesh to renderer's world.
        //
        // All handles are refcounted, so we only need to hang onto the handle until we
        // make an object.
        let sprite_mesh_handle = renderer.add_mesh(sprite_mesh).unwrap();
        let sprite_material = rend3_routine::pbr::PbrMaterial {
            albedo: rend3_routine::pbr::AlbedoComponent::Texture(my_texture_rend3_handle.clone()),
            transparency: rend3_routine::pbr::Transparency::Blend,
            ..Default::default()
        };

        let sprite_material_handle = renderer.add_material(sprite_material);
        // Combine the mesh and the material with a location to give an object.
        let sprite_object = rend3::types::Object {
            mesh_kind: rend3::types::ObjectMeshKind::Static(sprite_mesh_handle),
            material: sprite_material_handle.clone(),
            transform: glam::Mat4::from_scale_rotation_translation(
                glam::Vec3::new(1.0, 1.0, 1.0),
                glam::Quat::from_euler(glam::EulerRot::XYZ, 0.0, 0.0, 0.0),
                glam::Vec3::new(0.0, 0.0, 0.0),
            ),
        };

As a bit of a side note / maybe material for another request: The other approach I've tried in my attempts to achieve this - namely, by reading an existing internal wgpu::Texture from a rend3::types::Texture's handle, presents a different problem: The rend3::Texture's internal texture doesn't have the RENDER_ATTACHMENT usage on it and that field is read-only. I have tried to re-fill such a texture with a raw wgpu::Texture with the RENDER_ATTACHMENT usage enabled, but it doesn't seem to become aware of it either way. This means I can't use the external non-rend3 renderer on that texture.