tetanes 0.14.0

A cross-platform NES Emulator written in Rust using wgpu
use crate::nes::renderer::painter::RenderState;
use egui::{TextureId, Vec2, load::SizedTexture};

#[derive(Debug)]
#[must_use]
pub struct Texture {
    pub label: Option<&'static str>,
    pub id: TextureId,
    pub texture: wgpu::Texture,
    pub size: Vec2,
    pub output_size: Vec2,
    pub view: wgpu::TextureView,
    pub aspect_ratio: f32,
}

impl Texture {
    pub fn new(
        render_state: &mut RenderState,
        size: Vec2,
        aspect_ratio: f32,
        label: Option<&'static str>,
    ) -> Self {
        let max_texture_side = render_state.max_texture_side() as f32;
        let texture = render_state
            .device
            .create_texture(&wgpu::TextureDescriptor {
                label,
                size: wgpu::Extent3d {
                    width: size.x.min(max_texture_side) as u32,
                    height: size.y.min(max_texture_side) as u32,
                    depth_or_array_layers: 1,
                },
                mip_level_count: 1,
                sample_count: 1,
                dimension: wgpu::TextureDimension::D2,
                format: wgpu::TextureFormat::Rgba8UnormSrgb,
                usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
                view_formats: &[],
            });

        let view = texture.create_view(&wgpu::TextureViewDescriptor {
            label,
            dimension: Some(wgpu::TextureViewDimension::D2),
            ..Default::default()
        });
        let sampler_descriptor = wgpu::SamplerDescriptor {
            label: Some("sampler"),
            address_mode_u: wgpu::AddressMode::ClampToEdge,
            address_mode_v: wgpu::AddressMode::ClampToEdge,
            address_mode_w: wgpu::AddressMode::ClampToEdge,
            mag_filter: wgpu::FilterMode::Nearest,
            min_filter: wgpu::FilterMode::Nearest,
            mipmap_filter: wgpu::MipmapFilterMode::Nearest,
            ..Default::default()
        };
        let id = render_state.register_texture(label, &view, sampler_descriptor);

        Self {
            label,
            texture,
            size,
            output_size: Vec2 {
                x: size.x * aspect_ratio,
                y: size.y,
            },
            view,
            aspect_ratio,
            id,
        }
    }

    pub fn resize(&mut self, render_state: &mut RenderState, size: Vec2, aspect_ratio: f32) {
        *self = Self::new(render_state, size, aspect_ratio, self.label);
    }

    pub fn sized(&self) -> SizedTexture {
        SizedTexture::new(self.id, self.output_size)
    }

    pub fn update(&self, queue: &wgpu::Queue, bytes: &[u8]) {
        self.update_partial(queue, bytes, Vec2::ZERO, self.size);
    }

    pub fn update_partial(&self, queue: &wgpu::Queue, bytes: &[u8], origin: Vec2, size: Vec2) {
        let size = wgpu::Extent3d {
            width: size.x as u32,
            height: size.y as u32,
            depth_or_array_layers: 1,
        };
        queue.write_texture(
            wgpu::TexelCopyTextureInfo {
                aspect: wgpu::TextureAspect::All,
                texture: &self.texture,
                mip_level: 0,
                origin: wgpu::Origin3d {
                    x: origin.x as u32,
                    y: origin.y as u32,
                    z: 0,
                },
            },
            bytes,
            wgpu::TexelCopyBufferLayout {
                offset: 0,
                bytes_per_row: Some(4 * size.width),
                rows_per_image: Some(size.height),
            },
            size,
        );
    }
}