Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8c647d3700 | |||
| c5a4d0026b |
@@ -0,0 +1,47 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## Branch `truecolor16` — 16-Bit-Truecolor (RGB565)
|
||||||
|
|
||||||
|
Abzweig von `master` (nach `2d81471 poll_event`). Ersetzt den 8-Bit-Paletten-Renderer
|
||||||
|
durch direktes RGB565-Truecolor. Beide Varianten bleiben unabhängig nutzbar:
|
||||||
|
`master` = 8 Bit indiziert, `truecolor16` = 16 Bit direkt.
|
||||||
|
|
||||||
|
### Geändert
|
||||||
|
|
||||||
|
- **Framebuffer**: `framebuffer_mut()` liefert `&mut [u16]` statt `&mut [u8]`.
|
||||||
|
Jeder Wert ist ein RGB565-Pixel (Bits `RRRRRGGGGGGBBBBB`), keine Palettenindizes mehr.
|
||||||
|
- **GPU-Pfad**: Die Pixel-Textur ist jetzt `R16Uint` (vorher `R8Uint`); der
|
||||||
|
Fragment-Shader dekodiert die 5/6/5-Bitfelder direkt, statt in einer
|
||||||
|
Paletten-Textur nachzuschlagen. Upload pro Frame: `w * h * 2` Byte.
|
||||||
|
- **Example** `starter.rs`: schreibt RGB565-Werte via `rgb565()`.
|
||||||
|
|
||||||
|
### Neu
|
||||||
|
|
||||||
|
- `pbio::rgb565(r: u8, g: u8, b: u8) -> u16` (const fn) — packt 8-Bit-Kanäle
|
||||||
|
in einen RGB565-Pixelwert.
|
||||||
|
|
||||||
|
### Entfernt
|
||||||
|
|
||||||
|
- `PlatformConfig::palette` sowie `palette()` / `palette_mut()` und das
|
||||||
|
Dirty-Flag-Handling in `present()`.
|
||||||
|
- Modul `pbio::palette` inkl. Preset `RGB332`.
|
||||||
|
- Paletten-Textur und deren Shader-Binding (`@binding(2)`).
|
||||||
|
- `generate_gimp_palette.py` (Paletten-Tooling, nur auf `master` relevant).
|
||||||
|
|
||||||
|
### Migration von `master`
|
||||||
|
|
||||||
|
| 8 Bit (`master`) | 16 Bit (`truecolor16`) |
|
||||||
|
|---|---|
|
||||||
|
| `fb[i] = index` | `fb[i] = rgb565(r, g, b)` |
|
||||||
|
| `p.palette_mut()[i] = [r, g, b]` | entfällt — Farbe direkt ins Pixel schreiben |
|
||||||
|
| `PlatformConfig { palette: …, .. }` | Feld entfernt |
|
||||||
|
|
||||||
|
Paletten-Effekte (Color-Cycling, Fades über `palette_mut()`) gibt es hier
|
||||||
|
nicht — die entsprechenden Farbwerte müssen pro Frame in den Framebuffer
|
||||||
|
geschrieben werden.
|
||||||
|
|
||||||
|
### Unverändert
|
||||||
|
|
||||||
|
Events (`poll_event`/`drain_events`), Letterbox/`aspect_ratio`,
|
||||||
|
Cursor-Modi (`set_mouse_visible`/`set_mouse_capture`), `mouse_delta()`,
|
||||||
|
VSync-Handling, Fenster-/Surface-Verwaltung.
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
# pbio: simple pixel buffer and I/O system
|
# pbio: simple pixel buffer and I/O system — 16-Bit-Truecolor-Variante
|
||||||
|
|
||||||
Pixelbuffer-basiertes Fenster mit Palette-Lookup-Renderer (winit + wgpu) und Maus/Tastatureingaben.
|
Pixelbuffer-basiertes Fenster mit RGB565-Truecolor-Renderer (winit + wgpu) und Maus/Tastatureingaben.
|
||||||
|
|
||||||
pbio nimmt einen Bytearray, interpretiert diesen als 8-Bit-Pixelbuffer und rendert ihn auf ein Fenster. Optional mit Letterbox für ein festes Seitenverhältnis.
|
pbio nimmt ein `u16`-Array, interpretiert jeden Wert als RGB565-Pixel (Bits `RRRRRGGGGGGBBBBB`) und rendert ihn auf ein Fenster. Optional mit Letterbox für ein festes Seitenverhältnis.
|
||||||
|
|
||||||
|
Dies ist der Branch `truecolor16`. Auf `master` liegt die 8-Bit-Variante mit Palette-Lookup — beide sind unabhängig voneinander nutzbar.
|
||||||
|
|
||||||
## Integration
|
## Integration
|
||||||
|
|
||||||
@@ -10,7 +12,7 @@ pbio ist nicht auf crates.io veröffentlicht. Das Crate kann direkt aus dem Git-
|
|||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
pbio = { git = "https://codeberg.org/irrlicht/rust-pbio.git" }
|
pbio = { git = "https://codeberg.org/irrlicht/rust-pbio.git", branch = "truecolor16" }
|
||||||
```
|
```
|
||||||
|
|
||||||
pbio braucht keine System-Libraries zum Bauen — winit lädt X11/Wayland zur Laufzeit per dlopen. Zur Laufzeit reichen aktuelle Grafiktreiber mit Vulkan-, Metal- oder DX12-Unterstützung für wgpu.
|
pbio braucht keine System-Libraries zum Bauen — winit lädt X11/Wayland zur Laufzeit per dlopen. Zur Laufzeit reichen aktuelle Grafiktreiber mit Vulkan-, Metal- oder DX12-Unterstützung für wgpu.
|
||||||
@@ -40,7 +42,8 @@ fn main() {
|
|||||||
let (w, _h) = p.framebuffer_size();
|
let (w, _h) = p.framebuffer_size();
|
||||||
let fb = p.framebuffer_mut();
|
let fb = p.framebuffer_mut();
|
||||||
for (i, px) in fb.iter_mut().enumerate() {
|
for (i, px) in fb.iter_mut().enumerate() {
|
||||||
*px = ((i as u32 % w) ^ (i as u32 / w)) as u8;
|
let v = ((i as u32 % w) ^ (i as u32 / w)) as u8;
|
||||||
|
*px = pbio::rgb565(v, v, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
p.present();
|
p.present();
|
||||||
@@ -51,21 +54,20 @@ fn main() {
|
|||||||
## API-Überblick
|
## API-Überblick
|
||||||
|
|
||||||
- `PlatformConfig` – Titel, Fenster-/Framebuffer-Größe, optionales
|
- `PlatformConfig` – Titel, Fenster-/Framebuffer-Größe, optionales
|
||||||
`aspect_ratio` (Letterbox), Palette, VSync, `mouse_visible`, `mouse_capture`.
|
`aspect_ratio` (Letterbox), VSync, `mouse_visible`, `mouse_capture`.
|
||||||
Default = 800×600 Fenster, 320×240 Framebuffer, 4:3, `palette::RGB332`.
|
Default = 800×600 Fenster, 320×240 Framebuffer, 4:3.
|
||||||
- `Platform::new(cfg)` – Fenster + GPU-Pipeline aufsetzen.
|
- `Platform::new(cfg)` – Fenster + GPU-Pipeline aufsetzen.
|
||||||
- `poll_events(timeout)` – winit-Events einlesen (`None` = blockierend).
|
- `poll_events(timeout)` – winit-Events einlesen (`None` = blockierend).
|
||||||
- `poll_event() -> Option<Event>` – ein Event aus der Queue holen. Gibt
|
- `poll_event() -> Option<Event>` – ein Event aus der Queue holen. Gibt
|
||||||
`&mut self` nach jedem Call frei, sodass im Loop direkt auf die Platform
|
`&mut self` nach jedem Call frei, sodass im Loop direkt auf die Platform
|
||||||
reagiert werden kann (`request_close`, `set_mouse_capture`, `palette_mut` …).
|
reagiert werden kann (`request_close`, `set_mouse_capture` …).
|
||||||
Empfohlen für die normale Event-Schleife.
|
Empfohlen für die normale Event-Schleife.
|
||||||
- `drain_events()` – Iterator über `Event` (`Key`, `Char`, `MouseMove`,
|
- `drain_events()` – Iterator über `Event` (`Key`, `Char`, `MouseMove`,
|
||||||
`MouseBtn`, `Resize`, `CloseRequested`). Borrowt `&mut self` bis der
|
`MouseBtn`, `Resize`, `CloseRequested`). Borrowt `&mut self` bis der
|
||||||
Iterator fertig ist — Reaktionen auf die Platform müssen dann nach dem
|
Iterator fertig ist — Reaktionen auf die Platform müssen dann nach dem
|
||||||
Loop angewandt werden. Nützlich für reines Loggen/Filtern.
|
Loop angewandt werden. Nützlich für reines Loggen/Filtern.
|
||||||
- `framebuffer_mut()` – `&mut [u8]` der Länge `w*h`, je Pixel ein Paletten-Index.
|
- `framebuffer_mut()` – `&mut [u16]` der Länge `w*h`, je Pixel ein RGB565-Wert.
|
||||||
- `palette_mut()` / `palette()` – `&mut [[u8; 3]; 256]` bzw. `&[...]`; Schreibzugriff
|
- `rgb565(r, g, b) -> u16` – packt 8-Bit-RGB in einen RGB565-Pixelwert (const fn).
|
||||||
markiert die Palette dirty, der nächste `present()` lädt sie hoch.
|
|
||||||
- `present()` – Frame auf das Fenster bringen.
|
- `present()` – Frame auf das Fenster bringen.
|
||||||
- `should_close()` / `request_close()`.
|
- `should_close()` / `request_close()`.
|
||||||
- `set_mouse_visible(bool)` – Cursor anzeigen oder verstecken (freie Bewegung).
|
- `set_mouse_visible(bool)` – Cursor anzeigen oder verstecken (freie Bewegung).
|
||||||
@@ -94,17 +96,13 @@ yaw += dx * sensitivity;
|
|||||||
pitch += dy * sensitivity;
|
pitch += dy * sensitivity;
|
||||||
```
|
```
|
||||||
|
|
||||||
## Paletten
|
## Pixelformat
|
||||||
|
|
||||||
`palette::RGB332` ist als Default eingebaut. Eigene Paletten sind ein
|
RGB565: 5 Bit Rot, 6 Bit Grün, 5 Bit Blau in einem `u16` (Bits
|
||||||
`[[u8; 3]; 256]` (RGB pro Index) und werden über `PlatformConfig::palette`
|
`RRRRRGGGGGGBBBBB`). Der Shader dekodiert die Bitfelder direkt — es gibt
|
||||||
übergeben.
|
keine Palette. Zum Packen von 8-Bit-Kanälen dient `pbio::rgb565`:
|
||||||
|
|
||||||
Zur Laufzeit kann die Palette über `palette_mut()` modifiziert werden — einzelne
|
|
||||||
Einträge oder die komplette Tabelle. Der Upload passiert automatisch im nächsten
|
|
||||||
`present()`:
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
p.palette_mut()[42] = [255, 0, 0]; // Slot 42 → rot
|
let orange = pbio::rgb565(255, 160, 0);
|
||||||
p.present();
|
p.framebuffer_mut()[0] = orange;
|
||||||
```
|
```
|
||||||
|
|||||||
+16
-14
@@ -3,18 +3,18 @@
|
|||||||
//! Ausführen: cargo run --example starter --release
|
//! Ausführen: cargo run --example starter --release
|
||||||
//!
|
//!
|
||||||
//! Zeigt: Game-Loop-Struktur, non-blocking Event-Polling, Tastatur (Esc beendet),
|
//! Zeigt: Game-Loop-Struktur, non-blocking Event-Polling, Tastatur (Esc beendet),
|
||||||
//! Maus, direktes Schreiben auf den indizierten Framebuffer.
|
//! Maus, direktes Schreiben auf den RGB565-Framebuffer.
|
||||||
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use pbio::{Event, Key, MouseButton, Platform, PlatformConfig};
|
use pbio::{rgb565, Event, Key, MouseButton, Platform, PlatformConfig};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut p = Platform::new(PlatformConfig {
|
let mut p = Platform::new(PlatformConfig {
|
||||||
title: "pbio starter".into(),
|
title: "pbio starter".into(),
|
||||||
window_size: (960, 720),
|
window_size: (1240, 960),
|
||||||
framebuffer_size: (320, 240),
|
framebuffer_size: (620, 480),
|
||||||
aspect_ratio: Some(4.0/3.0),
|
aspect_ratio: Some(620.0/480.0),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -29,8 +29,8 @@ fn main() {
|
|||||||
p.poll_events(Some(Duration::from_millis(0)));
|
p.poll_events(Some(Duration::from_millis(0)));
|
||||||
|
|
||||||
// poll_event() gibt &mut self nach jedem Call wieder frei → Aktionen
|
// poll_event() gibt &mut self nach jedem Call wieder frei → Aktionen
|
||||||
// auf p (request_close, set_mouse_capture, palette_mut …) sind direkt
|
// auf p (request_close, set_mouse_capture …) sind direkt im Loop
|
||||||
// im Loop erlaubt. drain_events() existiert weiter für den Fall, dass
|
// erlaubt. drain_events() existiert weiter für den Fall, dass
|
||||||
// nur gelesen wird.
|
// nur gelesen wird.
|
||||||
while let Some(ev) = p.poll_event() {
|
while let Some(ev) = p.poll_event() {
|
||||||
match ev {
|
match ev {
|
||||||
@@ -44,24 +44,26 @@ fn main() {
|
|||||||
let (w, h) = p.framebuffer_size();
|
let (w, h) = p.framebuffer_size();
|
||||||
let fb = p.framebuffer_mut();
|
let fb = p.framebuffer_mut();
|
||||||
|
|
||||||
// Diagonal wanderndes Gradientband. Jeder Pixel ist ein Palettenindex
|
// Diagonal wanderndes Farbverlauf-Band, direkt als RGB565-Truecolor.
|
||||||
// 0..=255 — Default-Palette ist RGB332, daher die sichtbaren Bänder.
|
let t = (frame / 2) as u8;
|
||||||
let t = (frame / 256) as u8;
|
|
||||||
for y in 0..h {
|
for y in 0..h {
|
||||||
for x in 0..w {
|
for x in 0..w {
|
||||||
fb[(y * w + x) as usize] = (x as u8).wrapping_add(y as u8).wrapping_add(t);
|
let r = (x as u8).wrapping_add(t);
|
||||||
|
let g = (y as u8).wrapping_add(t);
|
||||||
|
let b = (x as u8).wrapping_add(y as u8);
|
||||||
|
fb[(y * w + x) as usize] = rgb565(r, g, b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cursor: 3x3-Quadrat in Palettenindex 0xE0 (rot), beim Klick 0xFC (gelb).
|
// Cursor: 3x3-Quadrat, rot; beim Klick gelb.
|
||||||
let (mx, my) = (mouse.0 as i32, mouse.1 as i32);
|
let (mx, my) = (mouse.0 as i32, mouse.1 as i32);
|
||||||
let cursor_ix = if paint { 0xFC } else { 0xE0 };
|
let cursor_px = if paint { rgb565(255, 255, 0) } else { rgb565(255, 0, 0) };
|
||||||
for dy in -1..=1 {
|
for dy in -1..=1 {
|
||||||
for dx in -1..=1 {
|
for dx in -1..=1 {
|
||||||
let x = mx + dx;
|
let x = mx + dx;
|
||||||
let y = my + dy;
|
let y = my + dy;
|
||||||
if (0..w as i32).contains(&x) && (0..h as i32).contains(&y) {
|
if (0..w as i32).contains(&x) && (0..h as i32).contains(&y) {
|
||||||
fb[(y as u32 * w + x as u32) as usize] = cursor_ix;
|
fb[(y as u32 * w + x as u32) as usize] = cursor_px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
with open("rgb332.gpl", "w") as f:
|
|
||||||
f.write("GIMP Palette\nName: RGB332\nColumns: 16\n#\n")
|
|
||||||
for i in range(256):
|
|
||||||
r = ((i >> 5) * 255) // 7
|
|
||||||
g = (((i >> 2) & 0x7) * 255) // 7
|
|
||||||
b = ((i & 0x3) * 255) // 3
|
|
||||||
f.write(f"{r:3d} {g:3d} {b:3d} Index {i}\n")
|
|
||||||
+21
-65
@@ -1,4 +1,4 @@
|
|||||||
//! pbio — Pixelbuffer-basiertes Fenster mit Palette-Lookup-Renderer.
|
//! pbio — Pixelbuffer-basiertes Fenster mit RGB565-Truecolor-Renderer.
|
||||||
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -15,8 +15,6 @@ use winit::keyboard::{KeyCode, PhysicalKey};
|
|||||||
use winit::platform::pump_events::EventLoopExtPumpEvents;
|
use winit::platform::pump_events::EventLoopExtPumpEvents;
|
||||||
use winit::window::{CursorGrabMode, Window, WindowAttributes, WindowId};
|
use winit::window::{CursorGrabMode, Window, WindowAttributes, WindowId};
|
||||||
|
|
||||||
pub mod palette;
|
|
||||||
|
|
||||||
// --- Konfiguration ---------------------------------------------------------
|
// --- Konfiguration ---------------------------------------------------------
|
||||||
|
|
||||||
pub struct PlatformConfig {
|
pub struct PlatformConfig {
|
||||||
@@ -24,7 +22,6 @@ pub struct PlatformConfig {
|
|||||||
pub window_size: (u32, u32),
|
pub window_size: (u32, u32),
|
||||||
pub framebuffer_size: (u32, u32),
|
pub framebuffer_size: (u32, u32),
|
||||||
pub aspect_ratio: Option<f32>,
|
pub aspect_ratio: Option<f32>,
|
||||||
pub palette: [[u8; 3]; 256],
|
|
||||||
pub vsync: bool,
|
pub vsync: bool,
|
||||||
pub mouse_visible: bool,
|
pub mouse_visible: bool,
|
||||||
pub mouse_capture: bool,
|
pub mouse_capture: bool,
|
||||||
@@ -37,7 +34,6 @@ impl Default for PlatformConfig {
|
|||||||
window_size: (800, 600),
|
window_size: (800, 600),
|
||||||
framebuffer_size: (320, 240),
|
framebuffer_size: (320, 240),
|
||||||
aspect_ratio: Some(4.0 / 3.0),
|
aspect_ratio: Some(4.0 / 3.0),
|
||||||
palette: palette::RGB332,
|
|
||||||
vsync: true,
|
vsync: true,
|
||||||
mouse_visible: true,
|
mouse_visible: true,
|
||||||
mouse_capture: false,
|
mouse_capture: false,
|
||||||
@@ -45,6 +41,11 @@ impl Default for PlatformConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Packt 8-Bit-RGB in einen RGB565-Pixelwert (Bits `RRRRRGGGGGGBBBBB`).
|
||||||
|
pub const fn rgb565(r: u8, g: u8, b: u8) -> u16 {
|
||||||
|
((r as u16 >> 3) << 11) | ((g as u16 >> 2) << 5) | (b as u16 >> 3)
|
||||||
|
}
|
||||||
|
|
||||||
// --- Events ----------------------------------------------------------------
|
// --- Events ----------------------------------------------------------------
|
||||||
|
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
@@ -91,10 +92,7 @@ pub struct Platform {
|
|||||||
// GPU-Ressourcen vor Surface, Surface vor Window (Drop-Reihenfolge).
|
// GPU-Ressourcen vor Surface, Surface vor Window (Drop-Reihenfolge).
|
||||||
bind_group: wgpu::BindGroup,
|
bind_group: wgpu::BindGroup,
|
||||||
pipeline: wgpu::RenderPipeline,
|
pipeline: wgpu::RenderPipeline,
|
||||||
index_tex: wgpu::Texture,
|
pixel_tex: wgpu::Texture,
|
||||||
palette_tex: wgpu::Texture,
|
|
||||||
palette: [[u8; 3]; 256],
|
|
||||||
palette_dirty: bool,
|
|
||||||
params_buf: wgpu::Buffer,
|
params_buf: wgpu::Buffer,
|
||||||
params: Params,
|
params: Params,
|
||||||
surface: wgpu::Surface<'static>,
|
surface: wgpu::Surface<'static>,
|
||||||
@@ -106,7 +104,7 @@ pub struct Platform {
|
|||||||
window: Arc<Window>,
|
window: Arc<Window>,
|
||||||
event_loop: EventLoop<()>,
|
event_loop: EventLoop<()>,
|
||||||
|
|
||||||
framebuffer: Vec<u8>,
|
framebuffer: Vec<u16>,
|
||||||
framebuffer_size: (u32, u32),
|
framebuffer_size: (u32, u32),
|
||||||
aspect_ratio: f32, // 0.0 = keine Letterbox
|
aspect_ratio: f32, // 0.0 = keine Letterbox
|
||||||
content_rect: (u32, u32, u32, u32),
|
content_rect: (u32, u32, u32, u32),
|
||||||
@@ -189,21 +187,11 @@ impl Platform {
|
|||||||
surface.configure(&device, &surface_config);
|
surface.configure(&device, &surface_config);
|
||||||
|
|
||||||
// --- Texturen ---
|
// --- Texturen ---
|
||||||
|
// RGB565 hat kein natives wgpu-Format — die rohen u16 landen in einer
|
||||||
|
// R16Uint-Textur, der Shader dekodiert die Bitfelder.
|
||||||
let (src_w, src_h) = cfg.framebuffer_size;
|
let (src_w, src_h) = cfg.framebuffer_size;
|
||||||
let index_tex = make_tex(&device, "pbio index_tex", src_w, src_h, wgpu::TextureFormat::R8Uint);
|
let pixel_tex = make_tex(&device, "pbio pixel_tex", src_w, src_h, wgpu::TextureFormat::R16Uint);
|
||||||
let palette_tex = make_tex(&device, "pbio palette_tex", 256, 1, wgpu::TextureFormat::Rgba8Unorm);
|
let pixel_view = pixel_tex.create_view(&Default::default());
|
||||||
let index_view = index_tex.create_view(&Default::default());
|
|
||||||
let palette_view = palette_tex.create_view(&Default::default());
|
|
||||||
|
|
||||||
let palette_rgba: Vec<u8> = cfg.palette.iter()
|
|
||||||
.flat_map(|[r, g, b]| [*r, *g, *b, 255])
|
|
||||||
.collect();
|
|
||||||
queue.write_texture(
|
|
||||||
wgpu::TexelCopyTextureInfoBase { texture: &palette_tex, mip_level: 0, origin: wgpu::Origin3d::ZERO, aspect: wgpu::TextureAspect::All },
|
|
||||||
&palette_rgba,
|
|
||||||
wgpu::TexelCopyBufferLayout { offset: 0, bytes_per_row: Some(256 * 4), rows_per_image: None },
|
|
||||||
wgpu::Extent3d { width: 256, height: 1, depth_or_array_layers: 1 },
|
|
||||||
);
|
|
||||||
|
|
||||||
// --- Uniform ---
|
// --- Uniform ---
|
||||||
let aspect_ratio = cfg.aspect_ratio.unwrap_or(0.0);
|
let aspect_ratio = cfg.aspect_ratio.unwrap_or(0.0);
|
||||||
@@ -227,18 +215,16 @@ impl Platform {
|
|||||||
layout: &bgl,
|
layout: &bgl,
|
||||||
entries: &[
|
entries: &[
|
||||||
wgpu::BindGroupEntry { binding: 0, resource: params_buf.as_entire_binding() },
|
wgpu::BindGroupEntry { binding: 0, resource: params_buf.as_entire_binding() },
|
||||||
wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::TextureView(&index_view) },
|
wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::TextureView(&pixel_view) },
|
||||||
wgpu::BindGroupEntry { binding: 2, resource: wgpu::BindingResource::TextureView(&palette_view) },
|
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut platform = Self {
|
let mut platform = Self {
|
||||||
bind_group, pipeline, index_tex, palette_tex,
|
bind_group, pipeline, pixel_tex,
|
||||||
palette: cfg.palette, palette_dirty: false,
|
|
||||||
params_buf, params,
|
params_buf, params,
|
||||||
surface, config: surface_config, device, queue, instance,
|
surface, config: surface_config, device, queue, instance,
|
||||||
window, event_loop,
|
window, event_loop,
|
||||||
framebuffer: vec![0u8; (src_w * src_h) as usize],
|
framebuffer: vec![0u16; (src_w * src_h) as usize],
|
||||||
framebuffer_size: (src_w, src_h),
|
framebuffer_size: (src_w, src_h),
|
||||||
aspect_ratio, content_rect,
|
aspect_ratio, content_rect,
|
||||||
event_queue: VecDeque::new(),
|
event_queue: VecDeque::new(),
|
||||||
@@ -313,49 +299,20 @@ impl Platform {
|
|||||||
|
|
||||||
/// Holt ein einzelnes Event aus der Queue. Gibt `&mut self` nach jedem Call
|
/// Holt ein einzelnes Event aus der Queue. Gibt `&mut self` nach jedem Call
|
||||||
/// wieder frei, im Gegensatz zu `drain_events()` — sodass im Loop direkt
|
/// wieder frei, im Gegensatz zu `drain_events()` — sodass im Loop direkt
|
||||||
/// auf die Platform reagiert werden kann (`request_close`, `set_mouse_capture`,
|
/// auf die Platform reagiert werden kann (`request_close`, `set_mouse_capture` …).
|
||||||
/// `palette_mut` …).
|
|
||||||
pub fn poll_event(&mut self) -> Option<Event> {
|
pub fn poll_event(&mut self) -> Option<Event> {
|
||||||
self.event_queue.pop_front()
|
self.event_queue.pop_front()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn framebuffer_mut(&mut self) -> &mut [u8] { &mut self.framebuffer }
|
pub fn framebuffer_mut(&mut self) -> &mut [u16] { &mut self.framebuffer }
|
||||||
pub fn framebuffer_size(&self) -> (u32, u32) { self.framebuffer_size }
|
pub fn framebuffer_size(&self) -> (u32, u32) { self.framebuffer_size }
|
||||||
|
|
||||||
/// Liefert &mut auf die 256-Einträge-Palette. Setzt pessimistisch ein
|
|
||||||
/// Dirty-Flag — der nächste `present()` lädt die Palette neu hoch.
|
|
||||||
pub fn palette_mut(&mut self) -> &mut [[u8; 3]; 256] {
|
|
||||||
self.palette_dirty = true;
|
|
||||||
&mut self.palette
|
|
||||||
}
|
|
||||||
pub fn palette(&self) -> &[[u8; 3]; 256] { &self.palette }
|
|
||||||
|
|
||||||
pub fn present(&mut self) {
|
pub fn present(&mut self) {
|
||||||
if self.palette_dirty {
|
|
||||||
let rgba: [u8; 256 * 4] = {
|
|
||||||
let mut buf = [0u8; 256 * 4];
|
|
||||||
for (i, [r, g, b]) in self.palette.iter().enumerate() {
|
|
||||||
buf[i * 4] = *r;
|
|
||||||
buf[i * 4 + 1] = *g;
|
|
||||||
buf[i * 4 + 2] = *b;
|
|
||||||
buf[i * 4 + 3] = 255;
|
|
||||||
}
|
|
||||||
buf
|
|
||||||
};
|
|
||||||
self.queue.write_texture(
|
|
||||||
wgpu::TexelCopyTextureInfoBase { texture: &self.palette_tex, mip_level: 0, origin: wgpu::Origin3d::ZERO, aspect: wgpu::TextureAspect::All },
|
|
||||||
&rgba,
|
|
||||||
wgpu::TexelCopyBufferLayout { offset: 0, bytes_per_row: Some(256 * 4), rows_per_image: None },
|
|
||||||
wgpu::Extent3d { width: 256, height: 1, depth_or_array_layers: 1 },
|
|
||||||
);
|
|
||||||
self.palette_dirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let (w, h) = self.framebuffer_size;
|
let (w, h) = self.framebuffer_size;
|
||||||
self.queue.write_texture(
|
self.queue.write_texture(
|
||||||
wgpu::TexelCopyTextureInfoBase { texture: &self.index_tex, mip_level: 0, origin: wgpu::Origin3d::ZERO, aspect: wgpu::TextureAspect::All },
|
wgpu::TexelCopyTextureInfoBase { texture: &self.pixel_tex, mip_level: 0, origin: wgpu::Origin3d::ZERO, aspect: wgpu::TextureAspect::All },
|
||||||
&self.framebuffer,
|
bytemuck::cast_slice(&self.framebuffer),
|
||||||
wgpu::TexelCopyBufferLayout { offset: 0, bytes_per_row: Some(w), rows_per_image: None },
|
wgpu::TexelCopyBufferLayout { offset: 0, bytes_per_row: Some(w * 2), rows_per_image: None },
|
||||||
wgpu::Extent3d { width: w, height: h, depth_or_array_layers: 1 },
|
wgpu::Extent3d { width: w, height: h, depth_or_array_layers: 1 },
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -616,7 +573,6 @@ fn build_pipeline(
|
|||||||
count: None,
|
count: None,
|
||||||
},
|
},
|
||||||
tex_entry(1, wgpu::TextureSampleType::Uint),
|
tex_entry(1, wgpu::TextureSampleType::Uint),
|
||||||
tex_entry(2, wgpu::TextureSampleType::Float { filterable: false }),
|
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
//! Palette-Presets.
|
|
||||||
|
|
||||||
/// RGB332-Palette: 8 Levels Rot, 8 Levels Grün, 4 Levels Blau.
|
|
||||||
///
|
|
||||||
/// Index-Bits: `RRRGGGBB` — `r = idx / 32`, `g = (idx / 4) % 8`, `b = idx % 4`.
|
|
||||||
pub const RGB332: [[u8; 3]; 256] = {
|
|
||||||
let mut p = [[0u8; 3]; 256];
|
|
||||||
let mut i = 0;
|
|
||||||
while i < 256 {
|
|
||||||
let r = (i / 32) as u32; // 0..=7
|
|
||||||
let g = ((i / 4) % 8) as u32; // 0..=7
|
|
||||||
let b = (i % 4) as u32; // 0..=3
|
|
||||||
p[i][0] = ((r * 255) / 7) as u8;
|
|
||||||
p[i][1] = ((g * 255) / 7) as u8;
|
|
||||||
p[i][2] = ((b * 255) / 3) as u8;
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
p
|
|
||||||
};
|
|
||||||
+7
-5
@@ -5,8 +5,7 @@ struct Params {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@group(0) @binding(0) var<uniform> params: Params;
|
@group(0) @binding(0) var<uniform> params: Params;
|
||||||
@group(0) @binding(1) var src_indices: texture_2d<u32>;
|
@group(0) @binding(1) var src_pixels: texture_2d<u32>;
|
||||||
@group(0) @binding(2) var palette_tex: texture_2d<f32>;
|
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
fn vs_main(@builtin(vertex_index) vid: u32) -> @builtin(position) vec4<f32> {
|
fn vs_main(@builtin(vertex_index) vid: u32) -> @builtin(position) vec4<f32> {
|
||||||
@@ -25,8 +24,11 @@ fn fs_main(@builtin(position) frag_xy: vec4<f32>) -> @location(0) vec4<f32> {
|
|||||||
let sx = clamp(i32(floor(uv.x * params.src_size.x)), 0, i32(params.src_size.x) - 1);
|
let sx = clamp(i32(floor(uv.x * params.src_size.x)), 0, i32(params.src_size.x) - 1);
|
||||||
let sy = clamp(i32(floor(uv.y * params.src_size.y)), 0, i32(params.src_size.y) - 1);
|
let sy = clamp(i32(floor(uv.y * params.src_size.y)), 0, i32(params.src_size.y) - 1);
|
||||||
|
|
||||||
let idx: u32 = textureLoad(src_indices, vec2<i32>(sx, sy), 0).r & 0xFFu;
|
// RGB565: RRRRRGGGGGGBBBBB
|
||||||
let rgb = textureLoad(palette_tex, vec2<i32>(i32(idx), 0), 0).rgb;
|
let px: u32 = textureLoad(src_pixels, vec2<i32>(sx, sy), 0).r;
|
||||||
|
let r = f32((px >> 11u) & 31u) / 31.0;
|
||||||
|
let g = f32((px >> 5u) & 63u) / 63.0;
|
||||||
|
let b = f32( px & 31u) / 31.0;
|
||||||
|
|
||||||
return vec4<f32>(rgb, 1.0);
|
return vec4<f32>(r, g, b, 1.0);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user