WGPU BACKEND!!! almost works!!

This commit is contained in:
Alex Janka 2024-02-09 10:20:00 +11:00
parent 43b800ff80
commit 5a2229728a
16 changed files with 523 additions and 210 deletions

112
Cargo.lock generated
View file

@ -683,15 +683,6 @@ dependencies = [
"gb-emu-lib",
]
[[package]]
name = "cmake"
version = "0.1.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130"
dependencies = [
"cc",
]
[[package]]
name = "cocoa"
version = "0.20.2"
@ -1556,6 +1547,7 @@ dependencies = [
"ron",
"serde",
"serde_with",
"thiserror",
"wgpu",
]
@ -1623,7 +1615,7 @@ dependencies = [
"vec_map",
"wasm-bindgen",
"web-sys",
"windows 0.52.0",
"windows 0.44.0",
]
[[package]]
@ -1661,6 +1653,28 @@ dependencies = [
"web-sys",
]
[[package]]
name = "glslang"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5df6491e5d4c222a6373b892c1bea8d697fca0c087890f64f0e2975b3f8bb48"
dependencies = [
"glslang-sys",
"once_cell",
"rustc-hash",
"thiserror",
]
[[package]]
name = "glslang-sys"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d76f2d75ad6e8a12c26e77ed6443a9369ef7957daf361e751c3489a97f8b816"
dependencies = [
"cc",
"glob",
]
[[package]]
name = "glutin_wgl_sys"
version = "0.5.0"
@ -2150,8 +2164,8 @@ dependencies = [
[[package]]
name = "librashader"
version = "0.2.0-beta.5"
source = "git+https://git.alexjanka.com/alex/librashader#446b052657fee7cc364ae33ba2644fafc65a1587"
version = "0.2.0-beta.7"
source = "git+https://git.alexjanka.com/alex/librashader#0974254c0e067e4fa8f43f1311a7ac343cec3f2e"
dependencies = [
"ash",
"librashader-cache",
@ -2163,12 +2177,13 @@ dependencies = [
"librashader-runtime-vk",
"librashader-runtime-wgpu",
"wgpu",
"wgpu-types",
]
[[package]]
name = "librashader-cache"
version = "0.2.0-beta.5"
source = "git+https://git.alexjanka.com/alex/librashader#446b052657fee7cc364ae33ba2644fafc65a1587"
version = "0.2.0-beta.7"
source = "git+https://git.alexjanka.com/alex/librashader#0974254c0e067e4fa8f43f1311a7ac343cec3f2e"
dependencies = [
"bincode",
"blake3",
@ -2183,8 +2198,8 @@ dependencies = [
[[package]]
name = "librashader-common"
version = "0.2.0-beta.5"
source = "git+https://git.alexjanka.com/alex/librashader#446b052657fee7cc364ae33ba2644fafc65a1587"
version = "0.2.0-beta.7"
source = "git+https://git.alexjanka.com/alex/librashader#0974254c0e067e4fa8f43f1311a7ac343cec3f2e"
dependencies = [
"ash",
"num-traits",
@ -2193,8 +2208,8 @@ dependencies = [
[[package]]
name = "librashader-preprocess"
version = "0.2.0-beta.5"
source = "git+https://git.alexjanka.com/alex/librashader#446b052657fee7cc364ae33ba2644fafc65a1587"
version = "0.2.0-beta.7"
source = "git+https://git.alexjanka.com/alex/librashader#0974254c0e067e4fa8f43f1311a7ac343cec3f2e"
dependencies = [
"encoding_rs",
"librashader-common",
@ -2205,8 +2220,8 @@ dependencies = [
[[package]]
name = "librashader-presets"
version = "0.2.0-beta.5"
source = "git+https://git.alexjanka.com/alex/librashader#446b052657fee7cc364ae33ba2644fafc65a1587"
version = "0.2.0-beta.7"
source = "git+https://git.alexjanka.com/alex/librashader#0974254c0e067e4fa8f43f1311a7ac343cec3f2e"
dependencies = [
"librashader-common",
"nom",
@ -2217,11 +2232,12 @@ dependencies = [
[[package]]
name = "librashader-reflect"
version = "0.2.0-beta.5"
source = "git+https://git.alexjanka.com/alex/librashader#446b052657fee7cc364ae33ba2644fafc65a1587"
version = "0.2.0-beta.7"
source = "git+https://git.alexjanka.com/alex/librashader#0974254c0e067e4fa8f43f1311a7ac343cec3f2e"
dependencies = [
"bitflags 1.3.2",
"bitflags 2.4.2",
"bytemuck",
"glslang",
"indexmap 2.1.0",
"librashader-common",
"librashader-preprocess",
@ -2232,15 +2248,14 @@ dependencies = [
"rspirv",
"rustc-hash",
"serde",
"shaderc",
"spirv",
"thiserror",
]
[[package]]
name = "librashader-runtime"
version = "0.2.0-beta.5"
source = "git+https://git.alexjanka.com/alex/librashader#446b052657fee7cc364ae33ba2644fafc65a1587"
version = "0.2.0-beta.7"
source = "git+https://git.alexjanka.com/alex/librashader#0974254c0e067e4fa8f43f1311a7ac343cec3f2e"
dependencies = [
"bytemuck",
"image",
@ -2254,8 +2269,8 @@ dependencies = [
[[package]]
name = "librashader-runtime-vk"
version = "0.2.0-beta.5"
source = "git+https://git.alexjanka.com/alex/librashader#446b052657fee7cc364ae33ba2644fafc65a1587"
version = "0.2.0-beta.7"
source = "git+https://git.alexjanka.com/alex/librashader#0974254c0e067e4fa8f43f1311a7ac343cec3f2e"
dependencies = [
"ash",
"bytemuck",
@ -2275,8 +2290,8 @@ dependencies = [
[[package]]
name = "librashader-runtime-wgpu"
version = "0.2.0-beta.5"
source = "git+https://git.alexjanka.com/alex/librashader#446b052657fee7cc364ae33ba2644fafc65a1587"
version = "0.2.0-beta.7"
source = "git+https://git.alexjanka.com/alex/librashader#0974254c0e067e4fa8f43f1311a7ac343cec3f2e"
dependencies = [
"array-concat",
"bytemuck",
@ -3445,15 +3460,6 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "roxmltree"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b"
dependencies = [
"xmlparser",
]
[[package]]
name = "rspirv"
version = "0.12.0+sdk-1.3.268.0"
@ -3662,27 +3668,6 @@ dependencies = [
"syn 2.0.48",
]
[[package]]
name = "shaderc"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27e07913ada18607bb60d12431cbe3358d3bbebbe95948e1618851dc01e63b7b"
dependencies = [
"libc",
"shaderc-sys",
]
[[package]]
name = "shaderc-sys"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73120d240fe22196300f39ca8547ca2d014960f27b19b47b21288b396272f7f7"
dependencies = [
"cmake",
"libc",
"roxmltree",
]
[[package]]
name = "shlex"
version = "1.3.0"
@ -4011,7 +3996,6 @@ dependencies = [
"nih_plug",
"raw-window-handle 0.6.0",
"serde",
"thiserror",
]
[[package]]
@ -4951,12 +4935,6 @@ version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a"
[[package]]
name = "xmlparser"
version = "0.13.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4"
[[package]]
name = "xtask"
version = "0.1.0"

View file

@ -8,7 +8,7 @@ description = "Frontend common library for TWINC Game Boy (CGB/DMG) emulator"
identifier = "com.alexjanka.TWINC"
[features]
default = ["wgpu", "pixels", "vulkan-static"]
default = ["wgpu"]
pixels = ["gb-emu-lib/pixels-renderer"]
vulkan = ["gb-emu-lib/vulkan-renderer", "gb-emu-lib/vulkan-debug"]
vulkan-static = ["vulkan", "gb-emu-lib/vulkan-static"]

View file

@ -11,6 +11,7 @@ use cpal::Stream;
use gb_emu_lib::{
connect::{EmulatorMessage, JoypadState, RendererMessage, ResolutionData},
renderer::{RendererBackend, RendererBackendManager},
util::PrintErrors,
};
use gilrs::{Button, Gilrs};
use image::ImageBuffer;
@ -295,12 +296,20 @@ where
recording: Option<RecordInfo>,
}
#[derive(Default)]
struct QueuedBuf {
buf: Vec<[u8; 4]>,
displayed: bool,
}
impl Default for QueuedBuf {
fn default() -> Self {
Self {
buf: Default::default(),
displayed: true,
}
}
}
impl QueuedBuf {
fn update(&mut self, new: Vec<[u8; 4]>) {
self.buf = new;
@ -336,7 +345,7 @@ where
scaled_height: inner_size.height / real_factor,
};
let renderer = RendererBackend::new(resolutions, &window, shader_path, manager);
let renderer = RendererBackend::new(resolutions, &window, shader_path, manager).unwrap();
let recording = if record {
let configs = access_config();
@ -374,19 +383,23 @@ where
fn render(&mut self, manager: &Backend::RendererBackendManager) {
let inner_size = self.window.inner_size();
if !self.queued_buf.displayed {
self.renderer.new_frame(&self.queued_buf.buf);
self.renderer
.new_frame(&self.queued_buf.buf)
.some_or_print();
self.queued_buf.displayed = true;
}
self.renderer.render(
ResolutionData {
real_width: inner_size.width,
real_height: inner_size.height,
scaled_width: self.width as u32,
scaled_height: self.height as u32,
},
manager,
);
self.renderer
.render(
ResolutionData {
real_width: inner_size.width,
real_height: inner_size.height,
scaled_width: self.width as u32,
scaled_height: self.height as u32,
},
manager,
)
.some_or_print();
}
fn process(&mut self) {
@ -426,7 +439,9 @@ where
scaled_height: self.height as u32,
};
self.renderer.resize(resolutions, &self.window);
self.renderer
.resize(resolutions, &self.window)
.some_or_print();
self.window.request_redraw();
}

View file

@ -7,7 +7,7 @@ edition = "2021"
crate-type = ["cdylib", "rlib"]
[features]
default = ["plugin", "wgpu", "pixels", "vulkan-static"]
default = ["plugin", "wgpu"]
pixels = ["gb-emu-lib/pixels-renderer"]
vulkan = ["gb-emu-lib/vulkan-renderer"]
vulkan-static = ["vulkan", "gb-emu-lib/vulkan-static"]
@ -32,4 +32,3 @@ futures = { version = "0.3", optional = true }
keyboard-types = { version = "0.6.2", optional = true }
raw-window-handle = { workspace = true }
serde = { version = "1.0", features = ["derive"] }
thiserror = { workspace = true }

View file

@ -6,6 +6,7 @@ use baseview::{
use gb_emu_lib::{
connect::{JoypadButtons, JoypadState, RendererMessage, ResolutionData, HEIGHT, WIDTH},
renderer::{ActiveBackend, RendererBackend, RendererBackendManager},
util::PrintErrors,
};
use keyboard_types::{Code, KeyState};
use nih_plug::prelude::*;
@ -135,7 +136,7 @@ impl TwincEditorWindow {
};
let renderer =
RendererBackend::new(current_resolution, window, shader_path, manager.clone());
RendererBackend::new(current_resolution, window, shader_path, manager.clone()).unwrap();
Self {
renderer,
@ -178,10 +179,12 @@ impl WindowHandler for TwincEditorWindow {
== (self.current_resolution.scaled_height * self.current_resolution.scaled_width)
as usize
{
self.renderer.new_frame(&self.latest_buf);
self.renderer.new_frame(&self.latest_buf).some_or_print();
}
self.renderer.render(self.current_resolution, &self.manager);
self.renderer
.render(self.current_resolution, &self.manager)
.some_or_print();
}
fn on_event(&mut self, window: &mut Window, event: baseview::Event) -> EventStatus {
@ -194,7 +197,9 @@ impl WindowHandler for TwincEditorWindow {
scaled_width: WIDTH as u32,
scaled_height: HEIGHT as u32,
};
self.renderer.resize(self.current_resolution, window);
self.renderer
.resize(self.current_resolution, window)
.some_or_print();
EventStatus::Captured
}
Event::Keyboard(event) => {

View file

@ -24,6 +24,7 @@ use frontend_common::{
use gb_emu_lib::{
connect::{EmulatorMessage, JoypadButtons, JoypadState, RendererMessage, ResolutionData},
renderer::{ActiveBackend, RendererBackend, RendererBackendManager},
util::PrintErrors,
};
use objc::{
class, msg_send, msg_send_id,
@ -141,9 +142,10 @@ const ALL_BUTTONS: [JoypadButtons; 8] = [
fn get_buttons(event: &Event) -> Option<JoypadButtons> {
let characters = event.characters();
if characters.len() != 1 {
panic!("ok that assumption was wrong lol. event characters CAN be != 1");
}
// i have no idea why i left this check in? what was i looking for?
// if characters.len() != 1 {
// panic!("ok that assumption was wrong lol. event characters CAN be != 1");
// }
if event.current_modifier_flags().is_empty() {
for button in ALL_BUTTONS {
@ -478,8 +480,10 @@ impl CacaoWindow {
fn display(&mut self, buffer: Vec<[u8; 4]>) {
if let Some(backend) = self.backend.as_mut() {
backend.render(self.resolutions, &self.backend_manager);
backend.new_frame(&buffer);
backend
.render(self.resolutions, &self.backend_manager)
.some_or_print();
backend.new_frame(&buffer).some_or_print();
}
}
}
@ -488,12 +492,13 @@ impl WindowDelegate for CacaoWindow {
const NAME: &'static str = "EmulatorWindow";
fn did_load(&mut self, window: Window) {
self.backend = Some(RendererBackend::new(
self.backend = RendererBackend::new(
self.resolutions,
&WindowHandleWrapper(&window),
self.shader_path.clone(),
self.backend_manager.clone(),
));
)
.some_or_print();
}
fn will_close(&self) {

View file

@ -6,14 +6,17 @@ edition = "2021"
[features]
default = []
clocked-serial = []
vulkan-renderer = [
"renderer",
"dep:ash",
"dep:ash-window",
"dep:naga",
librashader = [
"dep:librashader",
"dep:librashader-presets",
"dep:librashader-common",
]
vulkan-renderer = [
"renderer",
"librashader",
"dep:ash",
"dep:ash-window",
"dep:naga",
"librashader/runtime-vk",
]
vulkan-static = ["dep:ash-molten", "vulkan-renderer"]
@ -21,7 +24,7 @@ vulkan-debug = []
pixels-renderer = ["renderer", "dep:pixels"]
wgpu-renderer = [
"renderer",
"dep:librashader",
"librashader",
"librashader/runtime-wgpu",
"dep:wgpu",
]
@ -41,7 +44,6 @@ num-traits = "0.2"
pixels = { git = "https://git.alexjanka.com/alex/pixels", optional = true }
ash = { workspace = true, features = ["linked"], optional = true }
ash-window = { workspace = true, optional = true }
raw-window-handle = { workspace = true }
librashader = { workspace = true, optional = true }
librashader-presets = { workspace = true, optional = true }
@ -50,6 +52,7 @@ directories = { version = "5.0", optional = true }
ron = { version = "0.8", optional = true }
lazy_static = "1.4"
wgpu = { version = "0.19", optional = true }
thiserror = { workspace = true }
[build-dependencies]
naga = { version = "0.19", optional = true, features = ["wgsl-in", "spv-out"] }

View file

@ -1,35 +0,0 @@
use raw_window_handle::{DisplayHandle, HasDisplayHandle, HasWindowHandle};
pub struct DummyHandle<'a> {
pub window: raw_window_handle::WindowHandle<'a>,
pub display: raw_window_handle::DisplayHandle<'a>,
}
impl<'a> DummyHandle<'a> {
pub fn new<T>(value: &'a T) -> Option<Self>
where
T: HasWindowHandle + HasDisplayHandle + 'a,
{
Some(Self {
window: value.window_handle().ok()?,
display: value.display_handle().ok()?,
})
}
}
unsafe impl<'a> Sync for DummyHandle<'a> {}
unsafe impl<'a> Send for DummyHandle<'a> {}
impl<'a> HasWindowHandle for DummyHandle<'a> {
fn window_handle(
&self,
) -> Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
Ok(self.window)
}
}
impl<'a> HasDisplayHandle for DummyHandle<'a> {
fn display_handle(&self) -> Result<DisplayHandle<'_>, raw_window_handle::HandleError> {
Ok(self.display)
}
}

View file

@ -21,24 +21,32 @@ pub mod vulkan;
#[cfg(feature = "wgpu-renderer")]
pub mod wgpu;
mod compat;
#[cfg(feature = "librashader")]
mod shaders;
pub trait RendererBackend {
type RendererBackendManager: RendererBackendManager;
type RendererError: std::error::Error;
fn new<W: HasDisplayHandle + HasWindowHandle>(
resolutions: ResolutionData,
window: &W,
shader_path: Option<PathBuf>,
manager: Arc<Self::RendererBackendManager>,
) -> Self;
) -> Result<Self, Self::RendererError>
where
Self: std::marker::Sized;
fn resize<W: HasDisplayHandle + HasWindowHandle>(
&mut self,
resolutions: ResolutionData,
window: &W,
);
fn new_frame(&mut self, buffer: &[[u8; 4]]);
fn render(&mut self, resolutions: ResolutionData, manager: &Self::RendererBackendManager);
) -> Result<(), Self::RendererError>;
fn new_frame(&mut self, buffer: &[[u8; 4]]) -> Result<(), Self::RendererError>;
fn render(
&mut self,
resolutions: ResolutionData,
manager: &Self::RendererBackendManager,
) -> Result<(), Self::RendererError>;
}
pub trait RendererBackendManager {

View file

@ -2,10 +2,11 @@ use std::{path::PathBuf, sync::Arc};
use pixels::{Pixels, SurfaceTexture};
use raw_window_handle::{DisplayHandle, HasDisplayHandle, HasWindowHandle};
use thiserror::Error;
use crate::connect::ResolutionData;
use super::{compat::DummyHandle, RendererBackend, RendererBackendManager};
use super::{RendererBackend, RendererBackendManager};
pub struct PixelsBackendManager {}
@ -21,43 +22,51 @@ pub struct PixelsBackend {
impl RendererBackend for PixelsBackend {
type RendererBackendManager = PixelsBackendManager;
type RendererError = PixelsError;
fn new<W: HasDisplayHandle + HasWindowHandle>(
resolutions: ResolutionData,
window: &W,
_: Option<PathBuf>,
_: Arc<Self::RendererBackendManager>,
) -> Self {
Self {
pixels: new_pixels(resolutions, window),
}
) -> Result<Self, Self::RendererError> {
Ok(Self {
pixels: new_pixels(resolutions, window)?,
})
}
fn resize<W: HasDisplayHandle + HasWindowHandle>(
&mut self,
resolutions: ResolutionData,
window: &W,
) {
self.pixels = new_pixels(resolutions, window);
) -> Result<(), Self::RendererError> {
self.pixels = new_pixels(resolutions, window)?;
Ok(())
}
fn new_frame(&mut self, buffer: &[[u8; 4]]) {
fn new_frame(&mut self, buffer: &[[u8; 4]]) -> Result<(), Self::RendererError> {
if !buffer.is_empty() {
self.pixels
.frame_mut()
.copy_from_slice(bytemuck::cast_slice(buffer));
}
Ok(())
}
fn render(&mut self, _: ResolutionData, _: &PixelsBackendManager) {
self.pixels.render().unwrap();
fn render(
&mut self,
_: ResolutionData,
_: &PixelsBackendManager,
) -> Result<(), Self::RendererError> {
self.pixels.render()?;
Ok(())
}
}
fn new_pixels<W: HasDisplayHandle + HasWindowHandle>(
resolutions: ResolutionData,
window: &W,
) -> Pixels {
) -> Result<Pixels, pixels::Error> {
let dummy = DummyHandle::new(window).unwrap();
let surface_texture: SurfaceTexture<'_, DummyHandle> =
SurfaceTexture::new(resolutions.real_width, resolutions.real_height, &dummy);
@ -71,5 +80,44 @@ fn new_pixels<W: HasDisplayHandle + HasWindowHandle>(
..pixels::wgpu::RequestAdapterOptionsBase::default()
})
.build()
.unwrap()
}
#[derive(Debug, Error)]
pub enum PixelsError {
#[error("pixels error")]
Pixels(#[from] pixels::Error),
}
struct DummyHandle<'a> {
window: raw_window_handle::WindowHandle<'a>,
display: raw_window_handle::DisplayHandle<'a>,
}
impl<'a> DummyHandle<'a> {
fn new<T>(value: &'a T) -> Option<Self>
where
T: HasWindowHandle + HasDisplayHandle + 'a,
{
Some(Self {
window: value.window_handle().ok()?,
display: value.display_handle().ok()?,
})
}
}
unsafe impl<'a> Sync for DummyHandle<'a> {}
unsafe impl<'a> Send for DummyHandle<'a> {}
impl<'a> HasWindowHandle for DummyHandle<'a> {
fn window_handle(
&self,
) -> Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
Ok(self.window)
}
}
impl<'a> HasDisplayHandle for DummyHandle<'a> {
fn display_handle(&self) -> Result<DisplayHandle<'_>, raw_window_handle::HandleError> {
Ok(self.display)
}
}

View file

@ -0,0 +1,33 @@
use librashader_presets::ShaderPreset;
pub fn default_preset() -> ShaderPreset {
ShaderPreset {
shader_count: 1,
shaders: vec![librashader_presets::ShaderPassConfig {
id: 0,
name: librashader_common::ShaderStorage::String(
include_str!("./stock.slang").to_string(),
),
alias: None,
filter: librashader::FilterMode::Nearest,
wrap_mode: librashader::WrapMode::ClampToBorder,
frame_count_mod: 0,
srgb_framebuffer: false,
float_framebuffer: false,
mipmap_input: false,
scaling: librashader_presets::Scale2D {
valid: false,
x: librashader_presets::Scaling {
scale_type: librashader_presets::ScaleType::Input,
factor: librashader_presets::ScaleFactor::Float(1.0),
},
y: librashader_presets::Scaling {
scale_type: librashader_presets::ScaleType::Input,
factor: librashader_presets::ScaleFactor::Float(1.0),
},
},
}],
textures: vec![],
parameters: vec![],
}
}

View file

@ -7,14 +7,17 @@ use std::{mem::ManuallyDrop, path::PathBuf, sync::Arc};
use crate::connect::ResolutionData;
use self::{
types::{FramebufferData, SurfaceData, SwapchainData, Vertex, VulkanData, SHADER_INPUT_FORMAT},
types::{
FramebufferData, SurfaceData, SwapchainData, Vertex, VulkanData, VulkanError,
SHADER_INPUT_FORMAT,
},
utils::{
begin_commandbuffer, find_memorytype_index, record_submit_commandbuffer,
submit_commandbuffer,
},
};
use super::{RendererBackend, RendererBackendManager};
use super::{shaders::default_preset, RendererBackend, RendererBackendManager};
#[cfg(all(debug_assertions, feature = "vulkan-debug"))]
mod debug;
@ -119,14 +122,15 @@ pub struct VulkanWindowOptions {
impl RendererBackend for VulkanBackend {
type RendererBackendManager = VulkanBackendManager;
type RendererError = VulkanError;
fn new<W: HasDisplayHandle + HasWindowHandle>(
resolutions: ResolutionData,
window: &W,
shader_path: Option<PathBuf>,
manager: Arc<VulkanBackendManager>,
) -> Self {
let inner = unsafe { VulkanWindowInner::new(resolutions, window, manager.as_ref()) };
) -> Result<Self, Self::RendererError> {
let inner = unsafe { VulkanWindowInner::new(resolutions, &window, manager.as_ref()) };
let filter_chain_options = FilterChainOptions {
frames_in_flight: 0,
@ -148,64 +152,44 @@ impl RendererBackend for VulkanBackend {
.unwrap()
}
None => unsafe {
let preset = librashader_presets::ShaderPreset {
shader_count: 1,
shaders: vec![librashader_presets::ShaderPassConfig {
id: 0,
name: librashader_common::ShaderStorage::String(
include_str!("./shaders/stock.slang").to_string(),
),
alias: None,
filter: librashader::FilterMode::Nearest,
wrap_mode: librashader::WrapMode::ClampToBorder,
frame_count_mod: 0,
srgb_framebuffer: false,
float_framebuffer: false,
mipmap_input: false,
scaling: librashader_presets::Scale2D {
valid: false,
x: librashader_presets::Scaling {
scale_type: librashader_presets::ScaleType::Input,
factor: librashader_presets::ScaleFactor::Float(1.0),
},
y: librashader_presets::Scaling {
scale_type: librashader_presets::ScaleType::Input,
factor: librashader_presets::ScaleFactor::Float(1.0),
},
},
}],
textures: vec![],
parameters: vec![],
};
FilterChain::load_from_preset(preset, vulkan, Some(&filter_chain_options))
FilterChain::load_from_preset(default_preset(), vulkan, Some(&filter_chain_options))
}
.unwrap(),
};
Self {
// TODO - don't unwrap
Ok(Self {
inner: ManuallyDrop::new(inner),
filter_chain: ManuallyDrop::new(filter_chain),
manager,
}
})
}
fn resize<W: HasDisplayHandle + HasWindowHandle>(
&mut self,
resolutions: ResolutionData,
_window: &W,
) {
) -> Result<(), Self::RendererError> {
unsafe { self.inner.resize(resolutions, self.manager.as_ref()) };
// TODO - make inner return a result
Ok(())
}
fn new_frame(&mut self, buffer: &[[u8; 4]]) {
unsafe { self.inner.new_frame(buffer) };
fn new_frame(&mut self, buffer: &[[u8; 4]]) -> Result<(), Self::RendererError> {
unsafe { self.inner.new_frame(buffer) }; // TODO - make inner return a result
Ok(())
}
fn render(&mut self, resolutions: ResolutionData, manager: &VulkanBackendManager) {
fn render(
&mut self,
resolutions: ResolutionData,
manager: &VulkanBackendManager,
) -> Result<(), Self::RendererError> {
unsafe {
self.inner
.render(&mut self.filter_chain, resolutions, manager)
};
}; // TODO - make inner return a result
Ok(())
}
}

View file

@ -4,6 +4,7 @@ use ash::{
};
#[allow(deprecated)]
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
use thiserror::Error;
use crate::connect::ResolutionData;
@ -12,6 +13,12 @@ use super::{
VulkanBackendManager,
};
#[derive(Debug, Error)]
pub enum VulkanError {
#[error("vulkan error")]
Vulkan, //(#[from] vk::Error),
}
pub(super) const SHADER_INPUT_FORMAT: vk::Format = vk::Format::R8G8B8A8_UNORM;
#[derive(Clone, Debug, Copy)]

View file

@ -1,45 +1,274 @@
use std::sync::Arc;
use futures::executor::block_on;
use librashader::runtime::wgpu::{FilterChain, FilterChainOptions};
use raw_window_handle::{DisplayHandle, HasDisplayHandle, HasWindowHandle};
use thiserror::Error;
use wgpu::{Device, Queue, Surface, SurfaceConfiguration};
use crate::{
connect::ResolutionData,
renderer::{RendererBackend, RendererBackendManager},
};
pub struct WgpuBackend {}
use super::shaders::default_preset;
const SHADER_INPUT_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8Unorm;
pub struct WgpuBackendManager {
instance: wgpu::Instance,
}
impl RendererBackendManager for WgpuBackendManager {
fn new(_display_handle: DisplayHandle) -> Self {
let instance = wgpu::Instance::default();
Self { instance }
}
}
pub struct WgpuBackend {
device: Arc<Device>,
queue: Arc<Queue>,
surface: Surface<'static>,
resolutions: ResolutionData,
format: wgpu::TextureFormat,
alpha_mode: wgpu::CompositeAlphaMode,
filter_chain: FilterChain,
gb_framebuffer: Arc<wgpu::Texture>,
frame_num: usize,
}
impl ResolutionData {
fn get_config(
&self,
format: wgpu::TextureFormat,
alpha_mode: wgpu::CompositeAlphaMode,
) -> SurfaceConfiguration {
wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST,
format,
width: self.real_width,
height: self.real_height,
present_mode: wgpu::PresentMode::Fifo,
desired_maximum_frame_latency: 2,
alpha_mode,
view_formats: vec![],
}
}
fn scaled_as_extent(&self) -> wgpu::Extent3d {
wgpu::Extent3d {
width: self.scaled_width,
height: self.scaled_height,
depth_or_array_layers: 1,
}
}
}
impl RendererBackend for WgpuBackend {
type RendererBackendManager = WgpuBackendManager;
type RendererError = WgpuError;
fn new<W: HasDisplayHandle + HasWindowHandle>(
resolutions: ResolutionData,
window: &W,
shader_path: Option<std::path::PathBuf>,
manager: std::sync::Arc<Self::RendererBackendManager>,
) -> Self {
todo!()
) -> Result<Self, Self::RendererError> {
let surface = unsafe {
manager
.instance
.create_surface_unsafe(wgpu::SurfaceTargetUnsafe::from_window(window)?)
}?;
let adapter = manager
.instance
.enumerate_adapters(wgpu::Backends::all())
.into_iter()
.find(|adapter| adapter.is_surface_supported(&surface))
.ok_or(Self::RendererError::NoAdapter)?;
let (device, queue) = block_on(adapter.request_device(
&wgpu::DeviceDescriptor {
required_features: wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER,
required_limits: wgpu::Limits::default(),
label: None,
},
None,
))?;
let (device, queue) = (Arc::new(device), Arc::new(queue));
let capabilities = surface.get_capabilities(&adapter);
let format = capabilities
.formats
.into_iter()
.find(|v| !v.is_srgb())
.ok_or(Self::RendererError::NoTextureFormat)?;
let alpha_mode = *capabilities
.alpha_modes
.first()
.ok_or(Self::RendererError::NoTextureFormat)?;
let filter_chain = {
let filter_chain_options = FilterChainOptions {
force_no_mipmaps: false,
};
match shader_path {
Some(path) => FilterChain::load_from_path(
path,
device.clone(),
queue.clone(),
Some(&filter_chain_options),
)
.unwrap(), //?,
None => {
FilterChain::load_from_preset(
default_preset(),
device.clone(),
queue.clone(),
Some(&filter_chain_options),
)
}?,
}
};
let gb_framebuffer = Arc::new(device.create_texture(&wgpu::TextureDescriptor {
label: Some("rendertexture"),
size: resolutions.scaled_as_extent(),
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: SHADER_INPUT_FORMAT,
usage: wgpu::TextureUsages::TEXTURE_BINDING
| wgpu::TextureUsages::RENDER_ATTACHMENT
| wgpu::TextureUsages::COPY_DST
| wgpu::TextureUsages::COPY_SRC,
view_formats: &[SHADER_INPUT_FORMAT],
}));
Ok(Self {
device,
queue,
surface,
resolutions,
format,
alpha_mode,
filter_chain,
gb_framebuffer,
frame_num: 0,
})
}
fn resize<W: HasDisplayHandle + HasWindowHandle>(
&mut self,
resolutions: ResolutionData,
window: &W,
) {
todo!()
_window: &W,
) -> Result<(), Self::RendererError> {
self.resolutions = resolutions;
self.surface.configure(
&self.device,
&self.resolutions.get_config(self.format, self.alpha_mode),
);
self.gb_framebuffer = Arc::new(self.device.create_texture(&wgpu::TextureDescriptor {
label: Some("rendertexture"),
size: resolutions.scaled_as_extent(),
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: SHADER_INPUT_FORMAT,
usage: wgpu::TextureUsages::TEXTURE_BINDING
| wgpu::TextureUsages::RENDER_ATTACHMENT
| wgpu::TextureUsages::COPY_DST
| wgpu::TextureUsages::COPY_SRC,
view_formats: &[SHADER_INPUT_FORMAT],
}));
Ok(())
}
fn new_frame(&mut self, buffer: &[[u8; 4]]) {
todo!()
fn new_frame(&mut self, buffer: &[[u8; 4]]) -> Result<(), Self::RendererError> {
self.queue.write_texture(
self.gb_framebuffer.as_image_copy(),
bytemuck::cast_slice(buffer),
wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: Some(self.resolutions.scaled_width * 4),
rows_per_image: None,
},
self.resolutions.scaled_as_extent(),
);
Ok(())
}
fn render(&mut self, resolutions: ResolutionData, manager: &Self::RendererBackendManager) {
todo!()
fn render(
&mut self,
_resolutions: ResolutionData,
_manager: &Self::RendererBackendManager,
) -> Result<(), Self::RendererError> {
let output = self.surface.get_current_texture()?;
let filter_output = Arc::new(self.device.create_texture(&wgpu::TextureDescriptor {
label: Some("filteroutput"),
size: output.texture.size(),
mip_level_count: output.texture.mip_level_count(),
sample_count: output.texture.sample_count(),
dimension: output.texture.dimension(),
format: output.texture.format(),
usage: wgpu::TextureUsages::TEXTURE_BINDING
| wgpu::TextureUsages::RENDER_ATTACHMENT
| wgpu::TextureUsages::COPY_DST
| wgpu::TextureUsages::COPY_SRC,
view_formats: &[output.texture.format()],
}));
let filter_view = filter_output.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
self.filter_chain.frame(
self.gb_framebuffer.clone(),
&librashader_common::Viewport {
x: 0.0,
y: 0.0,
mvp: None,
output: librashader::runtime::wgpu::WgpuOutputView::new_from_raw(
&filter_view,
filter_output.size().into(),
filter_output.format(),
),
},
&mut encoder,
self.frame_num,
None,
)?;
encoder.copy_texture_to_texture(
filter_output.as_image_copy(),
output.texture.as_image_copy(),
output.texture.size(),
);
self.queue.submit(std::iter::once(encoder.finish()));
output.present();
self.frame_num += 1;
Ok(())
}
}
pub struct WgpuBackendManager {}
impl RendererBackendManager for WgpuBackendManager {
fn new(display_handle: DisplayHandle) -> Self {
todo!()
}
#[derive(Debug, Error)]
pub enum WgpuError {
#[error("no adapter")]
NoAdapter,
#[error("no texture format")]
NoTextureFormat,
#[error("rwh error")]
RawWindowHandle(#[from] raw_window_handle::HandleError),
#[error("create surface error")]
CreateSurface(#[from] wgpu::CreateSurfaceError),
#[error("wgpu surface error")]
Surface(#[from] wgpu::SurfaceError),
#[error("request device error")]
RequestDevice(#[from] wgpu::RequestDeviceError),
#[error("librashader filterchain error")]
FilterChain(#[from] librashader::runtime::wgpu::error::FilterChainError),
}

View file

@ -3,6 +3,40 @@ use num_traits::{PrimInt, Unsigned};
use crate::processor::{memory::mmio::gpu::Colour, Direction};
use std::mem::transmute;
pub trait PrintErrors {
type Inner;
fn some_or_print(self) -> Option<Self::Inner>;
fn some_or_print_with_context(self, context: &str) -> Option<Self::Inner>;
}
impl<T, E> PrintErrors for Result<T, E>
where
E: std::error::Error,
{
type Inner = T;
fn some_or_print(self) -> Option<Self::Inner> {
match self {
Ok(val) => Some(val),
Err(e) => {
eprintln!("{e:?}");
None
}
}
}
fn some_or_print_with_context(self, context: &str) -> Option<Self::Inner> {
match self {
Ok(val) => Some(val),
Err(e) => {
eprintln!("{context}: {e:?}");
None
}
}
}
}
pub(crate) fn as_signed(unsigned: u8) -> i8 {
unsafe { transmute(unsigned) }
}