mirror of https://github.com/AxioDL/metaforce.git
Start wiring up wgpu+winit
This commit is contained in:
parent
5491fd75cf
commit
e48435f11e
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
// Resolved by CMake Tools:
|
||||
"program": "${command:cmake.launchTargetPath}",
|
||||
"args": [],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}",
|
||||
"environment": [
|
||||
{
|
||||
// add the directory where our target was built to the PATHs
|
||||
// it gets resolved by CMake Tools:
|
||||
"name": "PATH",
|
||||
"value": "$PATH:${command:cmake.launchTargetDirectory}"
|
||||
},
|
||||
{
|
||||
"name": "OTHER_VALUE",
|
||||
"value": "Something something"
|
||||
}
|
||||
],
|
||||
"externalConsole": true,
|
||||
"MIMode": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -469,7 +469,7 @@ function(shaderc out)
|
|||
endfunction()
|
||||
|
||||
include(hecl/ApplicationTools.cmake)
|
||||
add_subdirectory(Shaders)
|
||||
#add_subdirectory(Shaders)
|
||||
add_subdirectory(imgui)
|
||||
add_subdirectory(extern/libSquish EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(extern/libpng EXCLUDE_FROM_ALL)
|
||||
|
@ -481,21 +481,21 @@ target_include_directories(hecl-light PRIVATE ${CMAKE_SOURCE_DIR})
|
|||
target_link_libraries(hecl-full PRIVATE zeus nod)
|
||||
target_link_libraries(hecl-light PRIVATE zeus nod)
|
||||
|
||||
bintoc(CModelShaders.common.glsl.cpp Shaders/CModelShaders.common.glsl CMODELSHADERS_COMMON_GLSL)
|
||||
bintoc(CModelShaders.vert.glsl.cpp Shaders/CModelShaders.vert.glsl CMODELSHADERS_VERT_GLSL)
|
||||
bintoc(CModelShaders.frag.glsl.cpp Shaders/CModelShaders.frag.glsl CMODELSHADERS_FRAG_GLSL)
|
||||
bintoc(CModelShaders.common.hlsl.cpp Shaders/CModelShaders.common.hlsl CMODELSHADERS_COMMON_HLSL)
|
||||
bintoc(CModelShaders.vert.hlsl.cpp Shaders/CModelShaders.vert.hlsl CMODELSHADERS_VERT_HLSL)
|
||||
bintoc(CModelShaders.frag.hlsl.cpp Shaders/CModelShaders.frag.hlsl CMODELSHADERS_FRAG_HLSL)
|
||||
bintoc(CModelShaders.common.metal.cpp Shaders/CModelShaders.common.metal CMODELSHADERS_COMMON_METAL)
|
||||
bintoc(CModelShaders.vert.metal.cpp Shaders/CModelShaders.vert.metal CMODELSHADERS_VERT_METAL)
|
||||
bintoc(CModelShaders.frag.metal.cpp Shaders/CModelShaders.frag.metal CMODELSHADERS_FRAG_METAL)
|
||||
add_library(CModelShaders
|
||||
CModelShaders.common.glsl.cpp CModelShaders.vert.glsl.cpp CModelShaders.frag.glsl.cpp
|
||||
CModelShaders.common.hlsl.cpp CModelShaders.vert.hlsl.cpp CModelShaders.frag.hlsl.cpp
|
||||
CModelShaders.common.metal.cpp CModelShaders.vert.metal.cpp CModelShaders.frag.metal.cpp)
|
||||
target_link_libraries(CModelShaders PUBLIC zeus)
|
||||
target_link_libraries(shader_CModelShaders PUBLIC CModelShaders)
|
||||
#bintoc(CModelShaders.common.glsl.cpp Shaders/CModelShaders.common.glsl CMODELSHADERS_COMMON_GLSL)
|
||||
#bintoc(CModelShaders.vert.glsl.cpp Shaders/CModelShaders.vert.glsl CMODELSHADERS_VERT_GLSL)
|
||||
#bintoc(CModelShaders.frag.glsl.cpp Shaders/CModelShaders.frag.glsl CMODELSHADERS_FRAG_GLSL)
|
||||
#bintoc(CModelShaders.common.hlsl.cpp Shaders/CModelShaders.common.hlsl CMODELSHADERS_COMMON_HLSL)
|
||||
#bintoc(CModelShaders.vert.hlsl.cpp Shaders/CModelShaders.vert.hlsl CMODELSHADERS_VERT_HLSL)
|
||||
#bintoc(CModelShaders.frag.hlsl.cpp Shaders/CModelShaders.frag.hlsl CMODELSHADERS_FRAG_HLSL)
|
||||
#bintoc(CModelShaders.common.metal.cpp Shaders/CModelShaders.common.metal CMODELSHADERS_COMMON_METAL)
|
||||
#bintoc(CModelShaders.vert.metal.cpp Shaders/CModelShaders.vert.metal CMODELSHADERS_VERT_METAL)
|
||||
#bintoc(CModelShaders.frag.metal.cpp Shaders/CModelShaders.frag.metal CMODELSHADERS_FRAG_METAL)
|
||||
#add_library(CModelShaders
|
||||
# CModelShaders.common.glsl.cpp CModelShaders.vert.glsl.cpp CModelShaders.frag.glsl.cpp
|
||||
# CModelShaders.common.hlsl.cpp CModelShaders.vert.hlsl.cpp CModelShaders.frag.hlsl.cpp
|
||||
# CModelShaders.common.metal.cpp CModelShaders.vert.metal.cpp CModelShaders.frag.metal.cpp)
|
||||
#target_link_libraries(CModelShaders PUBLIC zeus)
|
||||
#target_link_libraries(shader_CModelShaders PUBLIC CModelShaders)
|
||||
|
||||
if(NOT TARGET atdna)
|
||||
# Import native atdna if cross-compiling
|
||||
|
@ -517,12 +517,13 @@ add_subdirectory(extern/jbus EXCLUDE_FROM_ALL)
|
|||
set(JBUS_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/extern/jbus/include)
|
||||
|
||||
add_subdirectory(NESEmulator EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(Graphics)
|
||||
add_subdirectory(Runtime)
|
||||
add_subdirectory(mpcksum EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(gbalink EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(visigen)
|
||||
#add_subdirectory(visigen)
|
||||
|
||||
add_dependencies(hecl visigen)
|
||||
#add_dependencies(hecl visigen)
|
||||
|
||||
if (NOT WINDOWS_STORE AND NOT NX)
|
||||
if (APPLE AND EXISTS /opt/local/libexec/qt5)
|
||||
|
@ -552,7 +553,7 @@ endif()
|
|||
configure_file(${CMAKE_SOURCE_DIR}/version.h.in ${CMAKE_BINARY_DIR}/version.h)
|
||||
|
||||
# Packaging logic
|
||||
list(APPEND BINARY_TARGETS metaforce hecl visigen)
|
||||
list(APPEND BINARY_TARGETS metaforce hecl) # visigen
|
||||
set(DSYM_ONLY_TARGETS "")
|
||||
if (TARGET crashpad_handler)
|
||||
list(APPEND BINARY_TARGETS crashpad_handler)
|
||||
|
|
|
@ -74,7 +74,7 @@ add_library(AssetNameMapNull
|
|||
|
||||
get_target_property(HECL_INCLUDES hecl-full INCLUDE_DIRECTORIES)
|
||||
target_include_directories(RetroDataSpec PUBLIC ${HECL_INCLUDES} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR})
|
||||
target_link_libraries(RetroDataSpec PUBLIC amuse zeus nod squish ${PNG_LIBRARIES} ${ZLIB_LIBRARIES} lzokay logvisor)
|
||||
target_link_libraries(RetroDataSpec PUBLIC amuse zeus nod squish ${PNG_LIBRARIES} ${ZLIB_LIBRARIES} lzokay logvisor aurora)
|
||||
if (COMMAND add_sanitizers)
|
||||
add_sanitizers(RetroDataSpec)
|
||||
endif ()
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# Rust
|
||||
target/
|
||||
**/*.rs.bk
|
||||
generated/
|
||||
|
||||
# cargo-mobile
|
||||
.cargo/
|
||||
/gen
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
# JetBrains
|
||||
.idea
|
||||
|
||||
# Generated SPIR-V
|
||||
*.spv
|
|
@ -0,0 +1,42 @@
|
|||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(CARGO_CMD cargo build --verbose)
|
||||
set(TARGET_DIR "debug")
|
||||
else ()
|
||||
set(CARGO_CMD cargo build --release --verbose)
|
||||
set(TARGET_DIR "release")
|
||||
endif ()
|
||||
|
||||
if(USE_LTO)
|
||||
set(RUST_FLAGS "-Clinker-plugin-lto" "-Clinker=clang" "-Clink-arg=-fuse-ld=lld")
|
||||
endif()
|
||||
|
||||
set(AURORA_LIB "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_DIR}/libaurora.a")
|
||||
|
||||
set(AURORA_CXX "${CMAKE_CURRENT_BINARY_DIR}/aurora.cpp")
|
||||
set(AURORA_SHADERS_CXX "${CMAKE_CURRENT_BINARY_DIR}/aurora_shaders.cpp")
|
||||
set(AURORA_IMGUI_CXX "${CMAKE_CURRENT_BINARY_DIR}/aurora_imgui.cpp")
|
||||
add_library(aurora STATIC ${AURORA_CXX} ${AURORA_SHADERS_CXX})
|
||||
target_include_directories(aurora PRIVATE ../Runtime)
|
||||
target_include_directories(aurora PUBLIC include ${CMAKE_CURRENT_BINARY_DIR})
|
||||
add_custom_command(
|
||||
OUTPUT ${AURORA_CXX} ${AURORA_SHADERS_CXX} ${AURORA_IMGUI_CXX} ${AURORA_LIB} #__no_exist
|
||||
COMMAND CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR} RUSTFLAGS="${RUST_FLAGS}" ${CARGO_CMD}
|
||||
COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/cxxbridge/aurora/src/lib.rs.cc ${AURORA_CXX}
|
||||
COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/cxxbridge/aurora/src/lib.rs.h ${CMAKE_CURRENT_BINARY_DIR}/aurora.h
|
||||
COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/cxxbridge/aurora/src/shaders/mod.rs.cc ${AURORA_SHADERS_CXX}
|
||||
COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/cxxbridge/aurora/src/shaders/mod.rs.h ${CMAKE_CURRENT_BINARY_DIR}/aurora_shaders.h
|
||||
COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/cxxbridge/aurora/src/imgui.rs.cc ${AURORA_IMGUI_CXX}
|
||||
COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/cxxbridge/aurora/src/imgui.rs.h ${CMAKE_CURRENT_BINARY_DIR}/aurora_imgui.h
|
||||
DEPENDS src/lib.rs
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
add_custom_target(
|
||||
always_run ALL
|
||||
DEPENDS __no_exist
|
||||
)
|
||||
|
||||
target_link_libraries(aurora pthread dl ${AURORA_LIB} zeus)
|
||||
|
||||
add_test(NAME aurora_test
|
||||
COMMAND cargo test
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,75 @@
|
|||
[package]
|
||||
name = "aurora"
|
||||
version = "0.0.1"
|
||||
authors = ["Luke Street <luke@street.dev>"]
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
#[[bin]]
|
||||
#name = "metaforce"
|
||||
#path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
cxx = "1.0.60"
|
||||
web-sys = "0.3.55"
|
||||
env_logger = "0.9.0"
|
||||
mobile-entry-point = "0.1.1"
|
||||
pollster = "0.2.4"
|
||||
binread = { version = "2.2.0", features = ['const_generics'] }
|
||||
modular-bitfield = "0.11.2"
|
||||
num-traits = "0.2.14"
|
||||
num_enum = "0.5.5"
|
||||
bytemuck = "1.7.3"
|
||||
bytemuck_derive = "1.0.1"
|
||||
log = "0.4.14"
|
||||
log-panics = "2.0.0"
|
||||
cgmath = "0.18.0"
|
||||
smallvec = "1.7.0"
|
||||
scopeguard = "1.1.0"
|
||||
twox-hash = "1.6.2"
|
||||
winit = "0.26.0"
|
||||
|
||||
[dependencies.imgui]
|
||||
git = "https://github.com/imgui-rs/imgui-rs"
|
||||
rev = "55a76370466953d5c044afc123330d0262b50e17"
|
||||
[dependencies.imgui-winit-support]
|
||||
git = "https://github.com/imgui-rs/imgui-rs"
|
||||
rev = "55a76370466953d5c044afc123330d0262b50e17"
|
||||
default-features = false
|
||||
features = ["winit-26"]
|
||||
[patch.'https://github.com/imgui-rs/imgui-rs']
|
||||
imgui-sys = { path = "../imgui-sys" }
|
||||
[patch.crates-io]
|
||||
imgui-sys = { path = "../imgui-sys" }
|
||||
|
||||
[dependencies.wgpu]
|
||||
#path = "../../wgpu/wgpu"
|
||||
git = "https://github.com/gfx-rs/wgpu"
|
||||
rev = "39b7a8a202c21da43035ec8dd5570a58cf4e8ef1"
|
||||
features = ["spirv"]
|
||||
|
||||
#[patch.'https://github.com/gfx-rs/naga']
|
||||
#naga = { path = "../../naga" }
|
||||
|
||||
[build-dependencies]
|
||||
#naga = { path = "../../naga", features = ['spv-out', 'wgsl-in'] }
|
||||
naga = { git = "https://github.com/gfx-rs/naga", rev = "8df5421", features = ['spv-out', 'wgsl-in'] }
|
||||
bytemuck = "1.7.3"
|
||||
cxx-build = "1.0.60"
|
||||
bindgen = "0.59.2"
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
android_logger = "0.10.1"
|
||||
ndk-glue = "0.5.0"
|
||||
|
||||
[target.'cfg(not(target_os = "android"))'.dependencies]
|
||||
wgpu-subscriber = "0.1.0"
|
||||
|
||||
[target.'cfg(target_family = "wasm")'.dependencies]
|
||||
console_error_panic_hook = "0.1.7"
|
||||
console_log = "0.2.0"
|
||||
wasm-bindgen-futures = "0.4.28"
|
||||
#features = ["web-sys"]
|
|
@ -0,0 +1,47 @@
|
|||
fn main() {
|
||||
let zeus_include = std::path::PathBuf::from(format!(
|
||||
"{}/../extern/zeus/include",
|
||||
std::env::var("CARGO_MANIFEST_DIR").unwrap()
|
||||
))
|
||||
.canonicalize()
|
||||
.unwrap();
|
||||
let imgui_include = std::path::PathBuf::from(format!(
|
||||
"{}/../extern/imgui",
|
||||
std::env::var("CARGO_MANIFEST_DIR").unwrap()
|
||||
))
|
||||
.canonicalize()
|
||||
.unwrap();
|
||||
let imgui_engine_include = std::path::PathBuf::from(format!(
|
||||
"{}/../imgui",
|
||||
std::env::var("CARGO_MANIFEST_DIR").unwrap()
|
||||
))
|
||||
.canonicalize()
|
||||
.unwrap();
|
||||
// let include_dir = include_path.to_string_lossy();
|
||||
// let bindings = bindgen::Builder::default()
|
||||
// .header(format!("{}/zeus/CVector3f.hpp", include_dir))
|
||||
// .clang_arg(format!("-I{}", include_dir))
|
||||
// .parse_callbacks(Box::new(bindgen::CargoCallbacks))
|
||||
// .generate()
|
||||
// .expect("Unable to generate bindings");
|
||||
// let out_path = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap());
|
||||
// bindings.write_to_file(out_path.join("zeus.rs")).expect("Couldn't write bindings!");
|
||||
|
||||
cxx_build::bridge("src/lib.rs")
|
||||
.include("include")
|
||||
.include(zeus_include.clone())
|
||||
.compile("aurora");
|
||||
println!("cargo:rerun-if-changed=src/lib.rs");
|
||||
cxx_build::bridge("src/shaders/mod.rs")
|
||||
.include("include")
|
||||
.include(zeus_include.clone())
|
||||
.compile("aurora_shaders");
|
||||
println!("cargo:rerun-if-changed=src/shaders/mod.rs");
|
||||
cxx_build::bridge("src/imgui.rs")
|
||||
.include("include")
|
||||
.include(zeus_include.clone())
|
||||
.include(imgui_include.clone())
|
||||
.include(imgui_engine_include.clone())
|
||||
.compile("aurora_imgui");
|
||||
println!("cargo:rerun-if-changed=src/imgui.rs");
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace aurora {
|
||||
struct WindowSize;
|
||||
|
||||
struct App;
|
||||
struct AppDelegate {
|
||||
AppDelegate() = default;
|
||||
virtual ~AppDelegate() noexcept = default;
|
||||
AppDelegate(const AppDelegate&) = delete;
|
||||
AppDelegate& operator=(const AppDelegate&) = delete;
|
||||
AppDelegate(AppDelegate&&) = delete;
|
||||
AppDelegate& operator=(AppDelegate&&) = delete;
|
||||
|
||||
virtual void onAppLaunched() noexcept = 0;
|
||||
virtual bool onAppIdle(float dt) noexcept = 0;
|
||||
virtual void onAppDraw() noexcept = 0;
|
||||
virtual void onAppPostDraw() noexcept = 0;
|
||||
virtual void onAppExiting() noexcept = 0;
|
||||
|
||||
// virtual void resized([[maybe_unused]] const WindowSize& rect, [[maybe_unused]] bool sync) noexcept {}
|
||||
// virtual void mouseDown([[maybe_unused]] const SWindowCoord& coord, [[maybe_unused]] EMouseButton button,
|
||||
// [[maybe_unused]] EModifierKey mods) noexcept {}
|
||||
// virtual void mouseUp([[maybe_unused]] const SWindowCoord& coord, [[maybe_unused]] EMouseButton button,
|
||||
// [[maybe_unused]] EModifierKey mods) noexcept {}
|
||||
// virtual void mouseMove([[maybe_unused]] const SWindowCoord& coord) noexcept {}
|
||||
// virtual void mouseEnter([[maybe_unused]] const SWindowCoord& coord) noexcept {}
|
||||
// virtual void mouseLeave([[maybe_unused]] const SWindowCoord& coord) noexcept {}
|
||||
// virtual void scroll([[maybe_unused]] const SWindowCoord& coord, [[maybe_unused]] const SScrollDelta& scroll) noexcept {}
|
||||
//
|
||||
// virtual void touchDown([[maybe_unused]] const STouchCoord& coord, [[maybe_unused]] uintptr_t tid) noexcept {}
|
||||
// virtual void touchUp([[maybe_unused]] const STouchCoord& coord, [[maybe_unused]] uintptr_t tid) noexcept {}
|
||||
// virtual void touchMove([[maybe_unused]] const STouchCoord& coord, [[maybe_unused]] uintptr_t tid) noexcept {}
|
||||
//
|
||||
// virtual void charKeyDown([[maybe_unused]] unsigned long charCode, [[maybe_unused]] EModifierKey mods,
|
||||
// [[maybe_unused]] bool isRepeat) noexcept {}
|
||||
// virtual void charKeyUp([[maybe_unused]] unsigned long charCode, [[maybe_unused]] EModifierKey mods) noexcept {}
|
||||
// virtual void specialKeyDown([[maybe_unused]] ESpecialKey key, [[maybe_unused]] EModifierKey mods,
|
||||
// [[maybe_unused]] bool isRepeat) noexcept {}
|
||||
// virtual void specialKeyUp([[maybe_unused]] ESpecialKey key, [[maybe_unused]] EModifierKey mods) noexcept {}
|
||||
// virtual void modKeyDown([[maybe_unused]] EModifierKey mod, [[maybe_unused]] bool isRepeat) noexcept {}
|
||||
// virtual void modKeyUp([[maybe_unused]] EModifierKey mod) noexcept {}
|
||||
};
|
||||
} // namespace aurora
|
|
@ -0,0 +1,9 @@
|
|||
#include "aurora.hpp"
|
||||
|
||||
namespace aurora {
|
||||
void App_onAppLaunched(AppDelegate& cb) noexcept;
|
||||
bool App_onAppIdle(AppDelegate& cb, float dt) noexcept;
|
||||
void App_onAppDraw(AppDelegate& cb) noexcept;
|
||||
void App_onAppPostDraw(AppDelegate& cb) noexcept;
|
||||
void App_onAppExiting(AppDelegate& cb) noexcept;
|
||||
} // namespace aurora
|
|
@ -0,0 +1,9 @@
|
|||
#include "include/lib.hpp"
|
||||
|
||||
namespace aurora {
|
||||
void App_onAppLaunched(AppDelegate& cb) noexcept { return cb.onAppLaunched(); }
|
||||
bool App_onAppIdle(AppDelegate& cb, float dt) noexcept { return cb.onAppIdle(dt); }
|
||||
void App_onAppDraw(AppDelegate& cb) noexcept { cb.onAppDraw(); }
|
||||
void App_onAppPostDraw(AppDelegate& cb) noexcept { cb.onAppPostDraw(); }
|
||||
void App_onAppExiting(AppDelegate& cb) noexcept { cb.onAppExiting(); }
|
||||
} // namespace aurora
|
|
@ -0,0 +1,8 @@
|
|||
fn_single_line = true
|
||||
group_imports = "StdExternalCrate"
|
||||
imports_granularity = "Crate"
|
||||
overflow_delimited_expr = true
|
||||
reorder_impl_items = true
|
||||
use_field_init_shorthand = true
|
||||
use_small_heuristics = "Max"
|
||||
where_single_line = true
|
|
@ -0,0 +1,194 @@
|
|||
use std::{num::NonZeroU8, sync::Arc};
|
||||
|
||||
use pollster::block_on;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct GraphicsConfig {
|
||||
pub(crate) color_format: wgpu::TextureFormat,
|
||||
pub(crate) depth_format: wgpu::TextureFormat,
|
||||
pub(crate) texture_anistropy: Option<NonZeroU8>,
|
||||
pub(crate) msaa_samples: u32,
|
||||
}
|
||||
|
||||
pub struct DeviceHolder {
|
||||
pub(crate) instance: wgpu::Instance,
|
||||
pub(crate) surface: wgpu::Surface,
|
||||
pub(crate) adapter: wgpu::Adapter,
|
||||
pub(crate) backend: wgpu::Backend,
|
||||
pub(crate) device: Arc<wgpu::Device>,
|
||||
pub(crate) queue: Arc<wgpu::Queue>,
|
||||
pub(crate) config: GraphicsConfig,
|
||||
pub(crate) surface_config: wgpu::SurfaceConfiguration,
|
||||
pub(crate) framebuffer: TextureWithSampler,
|
||||
pub(crate) depth: TextureWithSampler,
|
||||
}
|
||||
|
||||
pub(crate) struct TextureWithSampler {
|
||||
pub(crate) texture: wgpu::Texture,
|
||||
pub(crate) view: wgpu::TextureView,
|
||||
pub(crate) sampler: wgpu::Sampler,
|
||||
}
|
||||
|
||||
pub(crate) fn create_render_texture(
|
||||
device: &wgpu::Device,
|
||||
config: &wgpu::SurfaceConfiguration,
|
||||
graphics_config: &GraphicsConfig,
|
||||
) -> TextureWithSampler {
|
||||
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some("Render texture"),
|
||||
size: wgpu::Extent3d {
|
||||
width: config.width,
|
||||
height: config.height,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: graphics_config.msaa_samples,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: graphics_config.color_format,
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
});
|
||||
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
label: Some("Render sampler"),
|
||||
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||
mag_filter: wgpu::FilterMode::Linear,
|
||||
min_filter: wgpu::FilterMode::Linear,
|
||||
mipmap_filter: wgpu::FilterMode::Linear,
|
||||
lod_min_clamp: 0.0,
|
||||
lod_max_clamp: f32::MAX,
|
||||
compare: None,
|
||||
anisotropy_clamp: None,
|
||||
border_color: None,
|
||||
});
|
||||
TextureWithSampler { texture, view, sampler }
|
||||
}
|
||||
|
||||
pub(crate) fn create_depth_texture(
|
||||
device: &wgpu::Device,
|
||||
config: &wgpu::SurfaceConfiguration,
|
||||
graphics_config: &GraphicsConfig,
|
||||
) -> TextureWithSampler {
|
||||
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some("Depth texture"),
|
||||
size: wgpu::Extent3d {
|
||||
width: config.width,
|
||||
height: config.height,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: graphics_config.msaa_samples,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: graphics_config.depth_format,
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
});
|
||||
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
label: Some("Depth sampler"),
|
||||
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||
mag_filter: wgpu::FilterMode::Linear,
|
||||
min_filter: wgpu::FilterMode::Linear,
|
||||
mipmap_filter: wgpu::FilterMode::Linear,
|
||||
lod_min_clamp: 0.0,
|
||||
lod_max_clamp: f32::MAX,
|
||||
compare: None,
|
||||
anisotropy_clamp: None,
|
||||
border_color: None,
|
||||
});
|
||||
TextureWithSampler { texture, view, sampler }
|
||||
}
|
||||
|
||||
pub(crate) fn initialize_gpu(window: &winit::window::Window) -> DeviceHolder {
|
||||
log::info!("Creating wgpu instance");
|
||||
let instance = wgpu::Instance::new(wgpu::Backends::all());
|
||||
let surface = unsafe { instance.create_surface(window) };
|
||||
let adapter = block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
|
||||
power_preference: wgpu::PowerPreference::HighPerformance,
|
||||
compatible_surface: Some(&surface),
|
||||
force_fallback_adapter: false,
|
||||
}))
|
||||
.expect("Failed to find an appropriate adapter");
|
||||
let best_limits = adapter.limits();
|
||||
let backend = adapter.get_info().backend;
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
let needs_denorm = true;
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
let _needs_denorm = backend == wgpu::Backend::Gl;
|
||||
|
||||
let required_features = if backend == wgpu::Backend::Vulkan {
|
||||
wgpu::Features::SPIRV_SHADER_PASSTHROUGH
|
||||
} else {
|
||||
wgpu::Features::empty()
|
||||
};
|
||||
let optional_features = wgpu::Features::TEXTURE_COMPRESSION_BC
|
||||
| wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER
|
||||
| required_features;
|
||||
log::info!("Requesting device with features: {:?}", optional_features);
|
||||
let limits = wgpu::Limits::downlevel_defaults()
|
||||
.using_resolution(best_limits.clone())
|
||||
.using_alignment(best_limits);
|
||||
let (device, queue) = block_on(adapter.request_device(
|
||||
&wgpu::DeviceDescriptor {
|
||||
label: Some("Device with optional features"),
|
||||
features: optional_features,
|
||||
limits: limits.clone(),
|
||||
},
|
||||
None,
|
||||
))
|
||||
.unwrap_or_else(|_| {
|
||||
log::warn!("Failed... requesting device with no features");
|
||||
block_on(adapter.request_device(
|
||||
&wgpu::DeviceDescriptor {
|
||||
label: Some("Device without optional features"),
|
||||
features: required_features,
|
||||
limits,
|
||||
},
|
||||
None,
|
||||
))
|
||||
.expect("Failed to create device")
|
||||
});
|
||||
log::info!("Successfully created device");
|
||||
|
||||
let mut swapchain_format = surface.get_preferred_format(&adapter).unwrap();
|
||||
if backend != wgpu::Backend::Gl {
|
||||
swapchain_format = match swapchain_format {
|
||||
wgpu::TextureFormat::Rgba8UnormSrgb => wgpu::TextureFormat::Rgba8Unorm,
|
||||
wgpu::TextureFormat::Bgra8UnormSrgb => wgpu::TextureFormat::Bgra8Unorm,
|
||||
_ => swapchain_format,
|
||||
};
|
||||
}
|
||||
let graphics_config = GraphicsConfig {
|
||||
color_format: swapchain_format,
|
||||
depth_format: wgpu::TextureFormat::Depth32Float,
|
||||
texture_anistropy: NonZeroU8::new(16),
|
||||
msaa_samples: 4,
|
||||
};
|
||||
|
||||
let size = window.inner_size();
|
||||
let surface_config = wgpu::SurfaceConfiguration {
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
format: swapchain_format,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
present_mode: wgpu::PresentMode::Fifo,
|
||||
};
|
||||
surface.configure(&device, &surface_config);
|
||||
let depth = create_depth_texture(&device, &surface_config, &graphics_config);
|
||||
let framebuffer = create_render_texture(&device, &surface_config, &graphics_config);
|
||||
|
||||
DeviceHolder {
|
||||
instance,
|
||||
surface,
|
||||
adapter,
|
||||
backend,
|
||||
device: Arc::new(device),
|
||||
queue: Arc::new(queue),
|
||||
config: graphics_config,
|
||||
surface_config,
|
||||
framebuffer,
|
||||
depth,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
use crate::{gpu::DeviceHolder, imgui_backend};
|
||||
|
||||
#[cxx::bridge(namespace = "metaforce")]
|
||||
mod ffi {
|
||||
unsafe extern "C++" {
|
||||
include!("ImGuiEngine.hpp");
|
||||
pub(crate) fn ImGuiEngine_Initialize(scale: f32);
|
||||
pub(crate) fn ImGuiEngine_AddTextures(state: &mut ImGuiState, device: &DeviceHolder);
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
type ImGuiState;
|
||||
type DeviceHolder;
|
||||
fn ImGuiEngine_AddTexture(
|
||||
state: &mut ImGuiState,
|
||||
device: &DeviceHolder,
|
||||
width: u32,
|
||||
height: u32,
|
||||
data: &[u8],
|
||||
) -> usize;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ImGuiState {
|
||||
pub(crate) context: imgui::Context,
|
||||
pub(crate) platform: imgui_winit_support::WinitPlatform,
|
||||
pub(crate) backend: imgui_backend::Renderer,
|
||||
}
|
||||
|
||||
pub(crate) fn initialize_imgui(window: &winit::window::Window, gpu: &DeviceHolder) -> ImGuiState {
|
||||
let hidpi_factor = window.scale_factor();
|
||||
let mut imgui = imgui::Context::create();
|
||||
let mut platform = imgui_winit_support::WinitPlatform::init(&mut imgui);
|
||||
platform.attach_window(imgui.io_mut(), &window, imgui_winit_support::HiDpiMode::Default);
|
||||
imgui.set_ini_filename(None);
|
||||
imgui.io_mut().font_global_scale = (1.0 / hidpi_factor) as f32;
|
||||
|
||||
unsafe {
|
||||
ffi::ImGuiEngine_Initialize(hidpi_factor as f32);
|
||||
}
|
||||
|
||||
let renderer_config = if gpu.backend == wgpu::Backend::Vulkan {
|
||||
imgui_backend::RendererConfig::new_spv_srgb()
|
||||
} else {
|
||||
imgui_backend::RendererConfig::new_srgb()
|
||||
};
|
||||
let imgui_backend = imgui_backend::Renderer::new(
|
||||
&mut imgui,
|
||||
&gpu.device,
|
||||
&gpu.queue,
|
||||
imgui_backend::RendererConfig {
|
||||
texture_format: gpu.config.color_format,
|
||||
depth_format: Some(gpu.config.depth_format),
|
||||
sample_count: gpu.config.msaa_samples,
|
||||
..renderer_config
|
||||
},
|
||||
);
|
||||
let mut state = ImGuiState { context: imgui, platform, backend: imgui_backend };
|
||||
unsafe {
|
||||
ffi::ImGuiEngine_AddTextures(&mut state, &gpu);
|
||||
}
|
||||
state
|
||||
}
|
||||
|
||||
fn ImGuiEngine_AddTexture(
|
||||
state: &mut ImGuiState,
|
||||
gpu: &DeviceHolder,
|
||||
width: u32,
|
||||
height: u32,
|
||||
data: &[u8],
|
||||
) -> usize {
|
||||
let texture =
|
||||
imgui_backend::Texture::new(&gpu.device, &state.backend, imgui_backend::TextureConfig {
|
||||
size: wgpu::Extent3d { width, height, depth_or_array_layers: 1 },
|
||||
format: Some(wgpu::TextureFormat::Rgba8Unorm),
|
||||
..Default::default()
|
||||
});
|
||||
texture.write(&gpu.queue, data, width, height);
|
||||
state.backend.textures.insert(texture).id()
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
struct Uniforms {
|
||||
u_Matrix: mat4x4<f32>;
|
||||
};
|
||||
|
||||
struct VertexInput {
|
||||
[[location(0)]] a_Pos: vec2<f32>;
|
||||
[[location(1)]] a_UV: vec2<f32>;
|
||||
[[location(2)]] a_Color: vec4<f32>;
|
||||
};
|
||||
|
||||
struct VertexOutput {
|
||||
[[location(0)]] v_UV: vec2<f32>;
|
||||
[[location(1)]] v_Color: vec4<f32>;
|
||||
[[builtin(position)]] v_Position: vec4<f32>;
|
||||
};
|
||||
|
||||
[[group(0), binding(0)]]
|
||||
var<uniform> uniforms: Uniforms;
|
||||
|
||||
[[stage(vertex)]]
|
||||
fn vs_main(in: VertexInput) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
out.v_UV = in.a_UV;
|
||||
out.v_Color = in.a_Color;
|
||||
out.v_Position = uniforms.u_Matrix * vec4<f32>(in.a_Pos.xy, 0.0, 1.0);
|
||||
return out;
|
||||
}
|
||||
|
||||
[[group(1), binding(0)]]
|
||||
var u_Texture: texture_2d<f32>;
|
||||
[[group(1), binding(1)]]
|
||||
var u_Sampler: sampler;
|
||||
|
||||
fn srgb_to_linear(srgb: vec4<f32>) -> vec4<f32> {
|
||||
let color_srgb = srgb.rgb;
|
||||
let selector = ceil(color_srgb - 0.04045); // 0 if under value, 1 if over
|
||||
let under = color_srgb / 12.92;
|
||||
let over = pow((color_srgb + 0.055) / 1.055, vec3<f32>(2.4));
|
||||
let result = mix(under, over, selector);
|
||||
return vec4<f32>(result, srgb.a);
|
||||
}
|
||||
|
||||
[[stage(fragment)]]
|
||||
fn fs_main_linear(in: VertexOutput) -> [[location(0)]] vec4<f32> {
|
||||
let color = srgb_to_linear(in.v_Color);
|
||||
return color * textureSample(u_Texture, u_Sampler, in.v_UV);
|
||||
}
|
||||
|
||||
[[stage(fragment)]]
|
||||
fn fs_main_srgb(in: VertexOutput) -> [[location(0)]] vec4<f32> {
|
||||
let color = in.v_Color;
|
||||
return color * textureSample(u_Texture, u_Sampler, in.v_UV);
|
||||
}
|
|
@ -0,0 +1,659 @@
|
|||
use std::{borrow::BorrowMut, error::Error, fmt, mem::size_of, num::NonZeroU32};
|
||||
|
||||
use imgui::{
|
||||
Context, DrawCmd::Elements, DrawData, DrawIdx, DrawList, DrawVert, TextureId, Textures,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use wgpu::{
|
||||
util::{BufferInitDescriptor, DeviceExt},
|
||||
*,
|
||||
};
|
||||
|
||||
static VS_ENTRY_POINT: &str = "vs_main";
|
||||
static FS_ENTRY_POINT_LINEAR: &str = "fs_main_linear";
|
||||
static FS_ENTRY_POINT_SRGB: &str = "fs_main_srgb";
|
||||
|
||||
pub type RendererResult<T> = Result<T, RendererError>;
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
struct DrawVertPod(DrawVert);
|
||||
|
||||
unsafe impl bytemuck::Zeroable for DrawVertPod {}
|
||||
|
||||
unsafe impl bytemuck::Pod for DrawVertPod {}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum RendererError {
|
||||
BadTexture(TextureId),
|
||||
}
|
||||
|
||||
impl fmt::Display for RendererError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
RendererError::BadTexture(id) => {
|
||||
write!(f, "imgui render error: bad texture id '{}'", id.id())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for RendererError {}
|
||||
|
||||
#[allow(dead_code)]
|
||||
enum ShaderStage {
|
||||
Vertex,
|
||||
Fragment,
|
||||
Compute,
|
||||
}
|
||||
|
||||
/// Config for creating a texture.
|
||||
///
|
||||
/// Uses the builder pattern.
|
||||
#[derive(Clone)]
|
||||
pub struct TextureConfig<'a> {
|
||||
/// The size of the texture.
|
||||
pub size: Extent3d,
|
||||
/// An optional label for the texture used for debugging.
|
||||
pub label: Option<&'a str>,
|
||||
/// The format of the texture, if not set uses the format from the renderer.
|
||||
pub format: Option<TextureFormat>,
|
||||
/// The usage of the texture.
|
||||
pub usage: TextureUsages,
|
||||
/// The mip level of the texture.
|
||||
pub mip_level_count: u32,
|
||||
/// The sample count of the texture.
|
||||
pub sample_count: u32,
|
||||
/// The dimension of the texture.
|
||||
pub dimension: TextureDimension,
|
||||
}
|
||||
|
||||
impl<'a> Default for TextureConfig<'a> {
|
||||
/// Create a new texture config.
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
size: Extent3d { width: 0, height: 0, depth_or_array_layers: 1 },
|
||||
label: None,
|
||||
format: None,
|
||||
usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: TextureDimension::D2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A container for a bindable texture.
|
||||
pub struct Texture {
|
||||
texture: wgpu::Texture,
|
||||
view: wgpu::TextureView,
|
||||
bind_group: BindGroup,
|
||||
size: Extent3d,
|
||||
}
|
||||
|
||||
impl Texture {
|
||||
/// Create a `Texture` from its raw parts.
|
||||
pub fn from_raw_parts(
|
||||
texture: wgpu::Texture,
|
||||
view: wgpu::TextureView,
|
||||
bind_group: BindGroup,
|
||||
size: Extent3d,
|
||||
) -> Self {
|
||||
Self { texture, view, bind_group, size }
|
||||
}
|
||||
|
||||
/// Create a new GPU texture width the specified `config`.
|
||||
pub fn new(device: &Device, renderer: &Renderer, config: TextureConfig) -> Self {
|
||||
// Create the wgpu texture.
|
||||
let texture = device.create_texture(&TextureDescriptor {
|
||||
label: config.label,
|
||||
size: config.size,
|
||||
mip_level_count: config.mip_level_count,
|
||||
sample_count: config.sample_count,
|
||||
dimension: config.dimension,
|
||||
format: config.format.unwrap_or(renderer.config.texture_format),
|
||||
usage: config.usage,
|
||||
});
|
||||
|
||||
// Extract the texture view.
|
||||
let view = texture.create_view(&TextureViewDescriptor::default());
|
||||
|
||||
// Create the texture sampler.
|
||||
let sampler = device.create_sampler(&SamplerDescriptor {
|
||||
label: Some("imgui-wgpu sampler"),
|
||||
address_mode_u: AddressMode::ClampToEdge,
|
||||
address_mode_v: AddressMode::ClampToEdge,
|
||||
address_mode_w: AddressMode::ClampToEdge,
|
||||
mag_filter: FilterMode::Linear,
|
||||
min_filter: FilterMode::Linear,
|
||||
mipmap_filter: FilterMode::Linear,
|
||||
lod_min_clamp: 0.0,
|
||||
lod_max_clamp: f32::MAX,
|
||||
compare: None,
|
||||
anisotropy_clamp: None,
|
||||
border_color: None,
|
||||
});
|
||||
|
||||
// Create the texture bind group from the layout.
|
||||
let bind_group = device.create_bind_group(&BindGroupDescriptor {
|
||||
label: config.label,
|
||||
layout: &renderer.texture_layout,
|
||||
entries: &[
|
||||
BindGroupEntry { binding: 0, resource: BindingResource::TextureView(&view) },
|
||||
BindGroupEntry { binding: 1, resource: BindingResource::Sampler(&sampler) },
|
||||
],
|
||||
});
|
||||
|
||||
Self { texture, view, bind_group, size: config.size }
|
||||
}
|
||||
|
||||
/// Write `data` to the texture.
|
||||
///
|
||||
/// - `data`: 32-bit RGBA bitmap data.
|
||||
/// - `width`: The width of the source bitmap (`data`) in pixels.
|
||||
/// - `height`: The height of the source bitmap (`data`) in pixels.
|
||||
pub fn write(&self, queue: &Queue, data: &[u8], width: u32, height: u32) {
|
||||
queue.write_texture(
|
||||
// destination (sub)texture
|
||||
ImageCopyTexture {
|
||||
texture: &self.texture,
|
||||
mip_level: 0,
|
||||
origin: Origin3d { x: 0, y: 0, z: 0 },
|
||||
aspect: TextureAspect::All,
|
||||
},
|
||||
// source bitmap data
|
||||
data,
|
||||
// layout of the source bitmap
|
||||
ImageDataLayout {
|
||||
offset: 0,
|
||||
bytes_per_row: NonZeroU32::new(width * 4),
|
||||
rows_per_image: NonZeroU32::new(height),
|
||||
},
|
||||
// size of the source bitmap
|
||||
Extent3d { width, height, depth_or_array_layers: 1 },
|
||||
);
|
||||
}
|
||||
|
||||
/// The width of the texture in pixels.
|
||||
pub fn width(&self) -> u32 { self.size.width }
|
||||
|
||||
/// The height of the texture in pixels.
|
||||
pub fn height(&self) -> u32 { self.size.height }
|
||||
|
||||
/// The depth of the texture.
|
||||
pub fn depth(&self) -> u32 { self.size.depth_or_array_layers }
|
||||
|
||||
/// The size of the texture in pixels.
|
||||
pub fn size(&self) -> Extent3d { self.size }
|
||||
|
||||
/// The underlying `wgpu::Texture`.
|
||||
pub fn texture(&self) -> &wgpu::Texture { &self.texture }
|
||||
|
||||
/// The `wgpu::TextureView` of the underlying texture.
|
||||
pub fn view(&self) -> &wgpu::TextureView { &self.view }
|
||||
}
|
||||
|
||||
pub struct CombinedShader<'s> {
|
||||
pub shader: ShaderModuleDescriptor<'s>,
|
||||
}
|
||||
|
||||
pub struct SeparateShaders<'s> {
|
||||
pub vertex_shader: ShaderModuleDescriptorSpirV<'s>,
|
||||
pub fragment_shader: ShaderModuleDescriptorSpirV<'s>,
|
||||
}
|
||||
|
||||
pub enum ShaderType<'s> {
|
||||
None,
|
||||
Combined(CombinedShader<'s>),
|
||||
Separate(SeparateShaders<'s>),
|
||||
}
|
||||
|
||||
/// Configuration for the renderer.
|
||||
pub struct RendererConfig<'s> {
|
||||
pub texture_format: TextureFormat,
|
||||
pub depth_format: Option<TextureFormat>,
|
||||
pub sample_count: u32,
|
||||
pub shader: ShaderType<'s>,
|
||||
pub vertex_shader_entry_point: String,
|
||||
pub fragment_shader_entry_point: String,
|
||||
}
|
||||
|
||||
impl RendererConfig<'_> {
|
||||
/// Create a new renderer config with custom shaders.
|
||||
pub fn with_combined_shaders<'s>(
|
||||
shader: ShaderModuleDescriptor<'s>,
|
||||
fragment_shader_entry_point: &str,
|
||||
) -> RendererConfig<'s> {
|
||||
RendererConfig {
|
||||
texture_format: TextureFormat::Rgba8Unorm,
|
||||
depth_format: None,
|
||||
sample_count: 1,
|
||||
shader: ShaderType::Combined(CombinedShader { shader }),
|
||||
vertex_shader_entry_point: VS_ENTRY_POINT.to_string(),
|
||||
fragment_shader_entry_point: fragment_shader_entry_point.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new renderer config with custom shaders.
|
||||
pub fn with_separate_shaders<'s>(
|
||||
vertex_shader: ShaderModuleDescriptorSpirV<'s>,
|
||||
fragment_shader: ShaderModuleDescriptorSpirV<'s>,
|
||||
fragment_shader_entry_point: &str,
|
||||
) -> RendererConfig<'s> {
|
||||
RendererConfig {
|
||||
texture_format: TextureFormat::Rgba8Unorm,
|
||||
depth_format: None,
|
||||
sample_count: 1,
|
||||
shader: ShaderType::Separate(SeparateShaders { vertex_shader, fragment_shader }),
|
||||
vertex_shader_entry_point: VS_ENTRY_POINT.to_string(),
|
||||
fragment_shader_entry_point: fragment_shader_entry_point.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for RendererConfig<'_> {
|
||||
/// Create a new renderer config with precompiled default shaders outputting linear color.
|
||||
///
|
||||
/// If you write to a Bgra8UnormSrgb framebuffer, this is what you want.
|
||||
fn default() -> Self { Self::new() }
|
||||
}
|
||||
|
||||
impl RendererConfig<'_> {
|
||||
/// Create a new renderer config with precompiled default shaders outputting linear color.
|
||||
///
|
||||
/// If you write to a Bgra8UnormSrgb framebuffer, this is what you want.
|
||||
pub fn new() -> Self {
|
||||
Self::with_combined_shaders(include_wgsl!("imgui.wgsl"), FS_ENTRY_POINT_LINEAR)
|
||||
}
|
||||
|
||||
/// Create a new renderer config with precompiled default shaders outputting srgb color.
|
||||
///
|
||||
/// If you write to a Bgra8Unorm framebuffer, this is what you want.
|
||||
pub fn new_srgb() -> Self {
|
||||
Self::with_combined_shaders(include_wgsl!("imgui.wgsl"), FS_ENTRY_POINT_SRGB)
|
||||
}
|
||||
|
||||
/// Create a new renderer config with precompiled default shaders outputting linear color.
|
||||
///
|
||||
/// If you write to a Bgra8UnormSrgb framebuffer, this is what you want.
|
||||
pub fn new_spv() -> Self {
|
||||
Self::with_separate_shaders(
|
||||
include_spirv_raw!("imgui_vs_main.spv"),
|
||||
include_spirv_raw!("imgui_fs_main_linear.spv"),
|
||||
FS_ENTRY_POINT_LINEAR,
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a new renderer config with precompiled default shaders outputting srgb color.
|
||||
///
|
||||
/// If you write to a Bgra8Unorm framebuffer, this is what you want.
|
||||
pub fn new_spv_srgb() -> Self {
|
||||
Self::with_separate_shaders(
|
||||
include_spirv_raw!("imgui_vs_main.spv"),
|
||||
include_spirv_raw!("imgui_fs_main_srgb.spv"),
|
||||
FS_ENTRY_POINT_SRGB,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Renderer {
|
||||
pipeline: RenderPipeline,
|
||||
uniform_buffer: Buffer,
|
||||
uniform_bind_group: BindGroup,
|
||||
/// Textures of the font atlas and all images.
|
||||
pub textures: Textures<Texture>,
|
||||
texture_layout: BindGroupLayout,
|
||||
index_buffers: SmallVec<[Buffer; 4]>,
|
||||
vertex_buffers: SmallVec<[Buffer; 4]>,
|
||||
config: RendererConfig<'static>,
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
/// Create an entirely new imgui wgpu renderer.
|
||||
pub fn new(
|
||||
imgui: &mut Context,
|
||||
device: &Device,
|
||||
queue: &Queue,
|
||||
config: RendererConfig,
|
||||
) -> Self {
|
||||
let RendererConfig {
|
||||
texture_format,
|
||||
depth_format,
|
||||
sample_count,
|
||||
shader,
|
||||
vertex_shader_entry_point,
|
||||
fragment_shader_entry_point,
|
||||
} = config;
|
||||
|
||||
// Load shaders.
|
||||
let shader1: Option<wgpu::ShaderModule>;
|
||||
let shader2: Option<wgpu::ShaderModule>;
|
||||
let (vs, fs) = match shader {
|
||||
ShaderType::Combined(d) => {
|
||||
shader1 = Some(device.create_shader_module(&d.shader));
|
||||
let r = shader1.as_ref().unwrap();
|
||||
(r, r)
|
||||
}
|
||||
ShaderType::Separate(d) => unsafe {
|
||||
shader1 = Some(device.create_shader_module_spirv(&d.vertex_shader));
|
||||
shader2 = Some(device.create_shader_module_spirv(&d.fragment_shader));
|
||||
(shader1.as_ref().unwrap(), shader2.as_ref().unwrap())
|
||||
},
|
||||
ShaderType::None => panic!(),
|
||||
};
|
||||
|
||||
// Create the uniform matrix buffer.
|
||||
let size = 64;
|
||||
let uniform_buffer = device.create_buffer(&BufferDescriptor {
|
||||
label: Some("imgui-wgpu uniform buffer"),
|
||||
size,
|
||||
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
|
||||
// Create the uniform matrix buffer bind group layout.
|
||||
let uniform_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||
label: None,
|
||||
entries: &[BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::VERTEX,
|
||||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
}],
|
||||
});
|
||||
|
||||
// Create the uniform matrix buffer bind group.
|
||||
let uniform_bind_group = device.create_bind_group(&BindGroupDescriptor {
|
||||
label: Some("imgui-wgpu bind group"),
|
||||
layout: &uniform_layout,
|
||||
entries: &[BindGroupEntry { binding: 0, resource: uniform_buffer.as_entire_binding() }],
|
||||
});
|
||||
|
||||
// Create the texture layout for further usage.
|
||||
let texture_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||
label: Some("imgui-wgpu bind group layout"),
|
||||
entries: &[
|
||||
BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: BindingType::Texture {
|
||||
multisampled: false,
|
||||
sample_type: TextureSampleType::Float { filterable: true },
|
||||
view_dimension: TextureViewDimension::D2,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Create the render pipeline layout.
|
||||
let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
|
||||
label: Some("imgui-wgpu pipeline layout"),
|
||||
bind_group_layouts: &[&uniform_layout, &texture_layout],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
// Create the render pipeline.
|
||||
// Create the render pipeline.
|
||||
let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
|
||||
label: Some("imgui-wgpu pipeline"),
|
||||
layout: Some(&pipeline_layout),
|
||||
vertex: VertexState {
|
||||
module: vs,
|
||||
entry_point: &vertex_shader_entry_point,
|
||||
buffers: &[VertexBufferLayout {
|
||||
array_stride: size_of::<DrawVert>() as BufferAddress,
|
||||
step_mode: VertexStepMode::Vertex,
|
||||
attributes: &vertex_attr_array![0 => Float32x2, 1 => Float32x2, 2 => Unorm8x4],
|
||||
}],
|
||||
},
|
||||
primitive: PrimitiveState {
|
||||
topology: PrimitiveTopology::TriangleList,
|
||||
strip_index_format: None,
|
||||
front_face: FrontFace::Cw,
|
||||
cull_mode: None,
|
||||
polygon_mode: PolygonMode::Fill,
|
||||
unclipped_depth: false,
|
||||
conservative: false,
|
||||
},
|
||||
depth_stencil: depth_format.map(|format| wgpu::DepthStencilState {
|
||||
format,
|
||||
depth_write_enabled: false,
|
||||
depth_compare: wgpu::CompareFunction::Always,
|
||||
stencil: wgpu::StencilState::default(),
|
||||
bias: DepthBiasState::default(),
|
||||
}),
|
||||
multisample: MultisampleState { count: sample_count, ..Default::default() },
|
||||
fragment: Some(FragmentState {
|
||||
module: fs,
|
||||
entry_point: &fragment_shader_entry_point,
|
||||
targets: &[ColorTargetState {
|
||||
format: texture_format,
|
||||
blend: Some(BlendState {
|
||||
color: BlendComponent {
|
||||
src_factor: BlendFactor::SrcAlpha,
|
||||
dst_factor: BlendFactor::OneMinusSrcAlpha,
|
||||
operation: BlendOperation::Add,
|
||||
},
|
||||
alpha: BlendComponent {
|
||||
src_factor: BlendFactor::OneMinusDstAlpha,
|
||||
dst_factor: BlendFactor::One,
|
||||
operation: BlendOperation::Add,
|
||||
},
|
||||
}),
|
||||
write_mask: ColorWrites::ALL,
|
||||
}],
|
||||
}),
|
||||
multiview: None,
|
||||
});
|
||||
|
||||
let mut renderer = Self {
|
||||
pipeline,
|
||||
uniform_buffer,
|
||||
uniform_bind_group,
|
||||
textures: Textures::new(),
|
||||
texture_layout,
|
||||
vertex_buffers: SmallVec::new(),
|
||||
index_buffers: SmallVec::new(),
|
||||
config: RendererConfig {
|
||||
texture_format,
|
||||
depth_format,
|
||||
sample_count,
|
||||
shader: ShaderType::None,
|
||||
vertex_shader_entry_point,
|
||||
fragment_shader_entry_point,
|
||||
},
|
||||
};
|
||||
|
||||
// Immediately load the font texture to the GPU.
|
||||
renderer.reload_font_texture(imgui, device, queue);
|
||||
|
||||
renderer
|
||||
}
|
||||
|
||||
/// Render the current imgui frame.
|
||||
pub fn render<'r>(
|
||||
&'r mut self,
|
||||
draw_data: &DrawData,
|
||||
queue: &Queue,
|
||||
device: &Device,
|
||||
rpass: &mut RenderPass<'r>,
|
||||
) -> RendererResult<()> {
|
||||
rpass.push_debug_group("imgui-wgpu");
|
||||
let mut rpass = scopeguard::guard(rpass, |rpass| {
|
||||
rpass.pop_debug_group();
|
||||
});
|
||||
|
||||
let fb_width = draw_data.display_size[0] * draw_data.framebuffer_scale[0];
|
||||
let fb_height = draw_data.display_size[1] * draw_data.framebuffer_scale[1];
|
||||
|
||||
// If the render area is <= 0, exit here and now.
|
||||
if !(fb_width > 0.0 && fb_height > 0.0) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let width = draw_data.display_size[0];
|
||||
let height = draw_data.display_size[1];
|
||||
|
||||
let offset_x = draw_data.display_pos[0] / width;
|
||||
let offset_y = draw_data.display_pos[1] / height;
|
||||
|
||||
// Create and update the transform matrix for the current frame.
|
||||
// This is required to adapt to vulkan coordinates.
|
||||
// let matrix = [
|
||||
// [2.0 / width, 0.0, 0.0, 0.0],
|
||||
// [0.0, 2.0 / height as f32, 0.0, 0.0],
|
||||
// [0.0, 0.0, -1.0, 0.0],
|
||||
// [-1.0, -1.0, 0.0, 1.0],
|
||||
// ];
|
||||
let matrix = [
|
||||
[2.0 / width, 0.0, 0.0, 0.0],
|
||||
[0.0, 2.0 / -height as f32, 0.0, 0.0],
|
||||
[0.0, 0.0, 1.0, 0.0],
|
||||
[-1.0 - offset_x * 2.0, 1.0 + offset_y * 2.0, 0.0, 1.0],
|
||||
];
|
||||
self.update_uniform_buffer(queue, &matrix);
|
||||
|
||||
rpass.set_pipeline(&self.pipeline);
|
||||
rpass.set_bind_group(0, &self.uniform_bind_group, &[]);
|
||||
|
||||
self.vertex_buffers.clear();
|
||||
self.index_buffers.clear();
|
||||
|
||||
for draw_list in draw_data.draw_lists() {
|
||||
self.vertex_buffers.push(self.upload_vertex_buffer(device, draw_list.vtx_buffer()));
|
||||
self.index_buffers.push(self.upload_index_buffer(device, draw_list.idx_buffer()));
|
||||
}
|
||||
|
||||
// Execute all the imgui render work.
|
||||
for (draw_list_buffers_index, draw_list) in draw_data.draw_lists().enumerate() {
|
||||
self.render_draw_list(
|
||||
rpass.borrow_mut(),
|
||||
draw_list,
|
||||
draw_data.display_pos,
|
||||
draw_data.framebuffer_scale,
|
||||
draw_list_buffers_index,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Render a given `DrawList` from imgui onto a wgpu frame.
|
||||
fn render_draw_list<'render>(
|
||||
&'render self,
|
||||
rpass: &mut RenderPass<'render>,
|
||||
draw_list: &DrawList,
|
||||
clip_off: [f32; 2],
|
||||
clip_scale: [f32; 2],
|
||||
draw_list_buffers_index: usize,
|
||||
) -> RendererResult<()> {
|
||||
let mut start = 0;
|
||||
|
||||
let index_buffer = &self.index_buffers[draw_list_buffers_index];
|
||||
let vertex_buffer = &self.vertex_buffers[draw_list_buffers_index];
|
||||
|
||||
// Make sure the current buffers are attached to the render pass.
|
||||
rpass.set_index_buffer(index_buffer.slice(..), IndexFormat::Uint16);
|
||||
rpass.set_vertex_buffer(0, vertex_buffer.slice(..));
|
||||
|
||||
for cmd in draw_list.commands() {
|
||||
if let Elements { count, cmd_params } = cmd {
|
||||
let clip_rect = [
|
||||
(cmd_params.clip_rect[0] - clip_off[0]) * clip_scale[0],
|
||||
(cmd_params.clip_rect[1] - clip_off[1]) * clip_scale[1],
|
||||
(cmd_params.clip_rect[2] - clip_off[0]) * clip_scale[0],
|
||||
(cmd_params.clip_rect[3] - clip_off[1]) * clip_scale[1],
|
||||
];
|
||||
|
||||
// Set the current texture bind group on the renderpass.
|
||||
let texture_id = cmd_params.texture_id;
|
||||
let tex =
|
||||
self.textures.get(texture_id).ok_or(RendererError::BadTexture(texture_id))?;
|
||||
rpass.set_bind_group(1, &tex.bind_group, &[]);
|
||||
|
||||
// Set scissors on the renderpass.
|
||||
let scissors = (
|
||||
clip_rect[0].max(0.0).floor() as u32,
|
||||
clip_rect[1].max(0.0).floor() as u32,
|
||||
(clip_rect[2] - clip_rect[0]).abs().ceil() as u32,
|
||||
(clip_rect[3] - clip_rect[1]).abs().ceil() as u32,
|
||||
);
|
||||
rpass.set_scissor_rect(scissors.0, scissors.1, scissors.2, scissors.3);
|
||||
|
||||
// Draw the current batch of vertices with the renderpass.
|
||||
let end = start + count as u32;
|
||||
rpass.draw_indexed(start..end, 0, 0..1);
|
||||
start = end;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Updates the current uniform buffer containing the transform matrix.
|
||||
fn update_uniform_buffer(&mut self, queue: &Queue, matrix: &[[f32; 4]; 4]) {
|
||||
let data = bytemuck::bytes_of(matrix);
|
||||
|
||||
queue.write_buffer(&self.uniform_buffer, 0, data);
|
||||
}
|
||||
|
||||
/// Upload the vertex buffer to the GPU.
|
||||
fn upload_vertex_buffer(&self, device: &Device, vertices: &[DrawVert]) -> Buffer {
|
||||
// Safety: DrawVertPod is #[repr(transparent)] over DrawVert and DrawVert _should_ be Pod.
|
||||
let vertices = unsafe {
|
||||
std::slice::from_raw_parts(vertices.as_ptr() as *mut DrawVertPod, vertices.len())
|
||||
};
|
||||
|
||||
let data = bytemuck::cast_slice(vertices);
|
||||
device.create_buffer_init(&BufferInitDescriptor {
|
||||
label: Some("imgui-wgpu vertex buffer"),
|
||||
contents: data,
|
||||
usage: BufferUsages::VERTEX,
|
||||
})
|
||||
}
|
||||