Skip to content

Commit

Permalink
feat(webgpu): texture
Browse files Browse the repository at this point in the history
  • Loading branch information
triniwiz committed Jun 21, 2024
1 parent 5819bfb commit 4bc0c6b
Show file tree
Hide file tree
Showing 91 changed files with 10,704 additions and 3,974 deletions.
1 change: 1 addition & 0 deletions crates/canvas-c/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ log = { version = "0.4.20"}
wgpu-core = {version = "0.21.0", features = ["wgsl", "metal"]}
wgpu-types = "0.20.0"
futures = "0.3"
raw-window-handle = "0.5.2"

[target.'cfg(target_vendor="apple")'.dependencies]
display-link = "0.2.0"
Expand Down
8 changes: 7 additions & 1 deletion crates/canvas-c/cbindgen.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,20 @@ language = "C"
namespace = "ffi"
include_guard = "CANVAS_C_H"

[parse]
parse_deps = true
include = ["wgpu_types"]


[parse.expand]
features = ["2d", "webgl"]
crates = ["wgpu_types"]

[export]
include = ["GLConstants", "InvalidateState"]
include = ["GLConstants", "InvalidateState","CanvasGPUTextureFormat"]
exclude = ["AChoreographer", "AChoreographer_frameCallback", "AChoreographer_frameCallback64", "AChoreographer_getInstance", "AChoreographer_postFrameCallback", "AChoreographer_postFrameCallbackDelayed", "AChoreographer_postFrameCallback64", "AChoreographer_postFrameCallbackDelayed64"]


[defines]
"target_os = ios" = "TARGET_OS_IOS"
"target_os = macos" = "TARGET_OS_MACOS"
Expand Down
551 changes: 551 additions & 0 deletions crates/canvas-c/src/webgpu/enums.rs

Large diffs are not rendered by default.

241 changes: 241 additions & 0 deletions crates/canvas-c/src/webgpu/gpu_canvas_context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
use std::os::raw::c_void;

use super::{
enums::CanvasGPUTextureFormat, gpu::CanvasWebGPUInstance, gpu_device::CanvasGPUDevice,
gpu_texture::CanvasGPUTexture,
};
use raw_window_handle::{
AppKitDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle,
};
pub struct CanvasGPUCanvasContext {
pub(crate) instance: CanvasWebGPUInstance,
pub(crate) surface: wgpu_core::id::SurfaceId,
pub(crate) width: u32,
pub(crate) height: u32,
pub(crate) format: wgpu_types::TextureFormat,
pub(crate) usage: u32,
}

#[no_mangle]
pub unsafe extern "C" fn canvas_native_webgpu_context_create(
instance: *mut CanvasWebGPUInstance,
view: *mut c_void,
width: u32,
height: u32,
) -> *mut CanvasGPUCanvasContext {
if instance.is_null() {
return std::ptr::null_mut();
}
let instance = unsafe { &*instance };
let global = &instance.0;

match global.instance_create_surface_metal(view, None) {
Ok(surface_id) => {
let ctx = CanvasGPUCanvasContext {
instance: instance.clone(),
surface: surface_id,
width,
height,
format: wgpu_types::TextureFormat::Bgra8Unorm,
usage: wgpu_types::TextureUsages::RENDER_ATTACHMENT.bits(),
};

Box::into_raw(Box::new(ctx))
}
Err(_) => {
// todo handle error
std::ptr::null_mut()
}
}
}

#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CanvasGPUSurfaceAlphaMode {
Auto = 0,
Opaque = 1,
PreMultiplied = 2,
PostMultiplied = 3,
Inherit = 4,
}

impl From<wgpu_types::CompositeAlphaMode> for CanvasGPUSurfaceAlphaMode {
fn from(value: wgpu_types::CompositeAlphaMode) -> Self {
match value {
wgpu_types::CompositeAlphaMode::Auto => Self::Auto,
wgpu_types::CompositeAlphaMode::Opaque => Self::Opaque,
wgpu_types::CompositeAlphaMode::PreMultiplied => Self::PreMultiplied,
wgpu_types::CompositeAlphaMode::PostMultiplied => Self::PostMultiplied,
wgpu_types::CompositeAlphaMode::Inherit => Self::Inherit,
}
}
}

impl Into<wgpu_types::CompositeAlphaMode> for CanvasGPUSurfaceAlphaMode {
fn into(self) -> wgpu_types::CompositeAlphaMode {
match self {
CanvasGPUSurfaceAlphaMode::Auto => wgpu_types::CompositeAlphaMode::Auto,
CanvasGPUSurfaceAlphaMode::Opaque => wgpu_types::CompositeAlphaMode::Opaque,
CanvasGPUSurfaceAlphaMode::PreMultiplied => {
wgpu_types::CompositeAlphaMode::PreMultiplied
}
CanvasGPUSurfaceAlphaMode::PostMultiplied => {
wgpu_types::CompositeAlphaMode::PostMultiplied
}
CanvasGPUSurfaceAlphaMode::Inherit => wgpu_types::CompositeAlphaMode::Inherit,
}
}
}

#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CanvasGPUPresentMode {
AutoVsync = 0,
AutoNoVsync = 1,
Fifo = 2,
FifoRelaxed = 3,
Immediate = 4,
Mailbox = 5,
}

impl Into<wgpu_types::PresentMode> for CanvasGPUPresentMode {
fn into(self) -> wgpu_types::PresentMode {
match self {
CanvasGPUPresentMode::AutoVsync => wgpu_types::PresentMode::AutoVsync,
CanvasGPUPresentMode::AutoNoVsync => wgpu_types::PresentMode::AutoNoVsync,
CanvasGPUPresentMode::Fifo => wgpu_types::PresentMode::Fifo,
CanvasGPUPresentMode::FifoRelaxed => wgpu_types::PresentMode::FifoRelaxed,
CanvasGPUPresentMode::Immediate => wgpu_types::PresentMode::Immediate,
CanvasGPUPresentMode::Mailbox => wgpu_types::PresentMode::Mailbox,
}
}
}

impl From<wgpu_types::PresentMode> for CanvasGPUPresentMode {
fn from(value: wgpu_types::PresentMode) -> Self {
match value {
wgpu_types::PresentMode::AutoVsync => Self::AutoVsync,
wgpu_types::PresentMode::AutoNoVsync => Self::AutoNoVsync,
wgpu_types::PresentMode::Fifo => Self::Fifo,
wgpu_types::PresentMode::FifoRelaxed => Self::FifoRelaxed,
wgpu_types::PresentMode::Immediate => Self::Immediate,
wgpu_types::PresentMode::Mailbox => Self::Mailbox,
}
}
}

#[repr(C)]
#[derive(Debug)]
pub struct CanvasGPUSurfaceConfiguration {
alphaMode: CanvasGPUSurfaceAlphaMode,
usage: u32,
presentMode: CanvasGPUPresentMode,
view_formats: *const CanvasGPUTextureFormat,
view_formats_size: usize,
}

#[no_mangle]
pub extern "C" fn canvas_native_webgpu_context_configure(
context: *mut CanvasGPUCanvasContext,
device: *mut CanvasGPUDevice,
config: *const CanvasGPUSurfaceConfiguration,
) {
if context.is_null() || device.is_null() {
return;
}
let context = unsafe { &*context };
let surface_id = context.surface;
let global = &context.instance.0;
let device = unsafe { &*device };
let device_id = device.device;

let config = unsafe { &*config };

println!("config {:?}", config);

let view_formats = if !config.view_formats.is_null() && config.view_formats_size > 0 {
unsafe {
std::slice::from_raw_parts(config.view_formats, config.view_formats_size)
.to_vec()
.into_iter()
.map(|v| v.into())
.collect::<Vec<wgpu_types::TextureFormat>>()
}
} else {
vec![]
};

print!("view_formats {:?}", view_formats.as_slice());

let config = wgpu_types::SurfaceConfiguration::<Vec<wgpu_types::TextureFormat>> {
desired_maximum_frame_latency: 2,
usage: wgpu_types::TextureUsages::from_bits_truncate(config.usage),
format: context.format,
width: context.height,
height: context.width,
present_mode: config.presentMode.into(),
alpha_mode: config.alphaMode.into(),
view_formats: view_formats,
};

print!("SurfaceConfiguration {:?}", &config);

// todo handle error
if let Some(_) =
gfx_select!(device_id => global.surface_configure(surface_id, device_id, &config))
{}
}

#[no_mangle]
pub unsafe extern "C" fn canvas_native_webgpu_context_unconfigure(
context: *mut CanvasGPUCanvasContext,
device: *mut CanvasGPUDevice,
config: *const CanvasGPUSurfaceConfiguration,
) {
if context.is_null() {
return;
}
let context = &*context;
let surface_id = context.surface;
let global = &context.instance.0;
let device = &*device;
let device_id = device.device;
let config = &*config;
}

#[no_mangle]
pub extern "C" fn canvas_native_webgpu_context_get_current_texture(
context: *mut CanvasGPUCanvasContext,
) -> *mut CanvasGPUTexture {
if context.is_null() {
return std::ptr::null_mut();
}
let context = unsafe { &*context };
let surface_id = context.surface;
let global = &context.instance.0;

match gfx_select!(surface_id => global.surface_get_current_texture(surface_id, None)) {
Ok(texture) => match texture.status {
wgpu_types::SurfaceStatus::Good | wgpu_types::SurfaceStatus::Suboptimal => {
Box::into_raw(Box::new(CanvasGPUTexture {
instance: context.instance.clone(),
texture: texture.texture_id.unwrap(),
owned: false,
depth_or_array_layers: 1,
dimension: super::enums::CanvasTextureDimension::D2,
format: context.format.into(),
mipLevelCount: 1,
sampleCount: 1,
width: context.width,
height: context.height,
usage: context.usage,
}))
}
_ => std::ptr::null_mut(),
},
Err(_) => {
// todo handle error
std::ptr::null_mut()
}
}
}
102 changes: 97 additions & 5 deletions crates/canvas-c/src/webgpu/gpu_device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ use std::{
os::raw::{c_char, c_void},
};


use crate::buffers::StringBuffer;

use super::{
gpu::CanvasWebGPUInstance, gpu_buffer::CanvasGPUBuffer,
gpu_command_encoder::CanvasGPUCommandEncoder, gpu_queue::CanvasGPUQueue,
gpu_shader_module::CanvasGPUShaderModule, gpu_supported_limits::CanvasGPUSupportedLimits,
enums::{CanvasGPUTextureFormat, CanvasTextureDimension},
gpu::CanvasWebGPUInstance,
gpu_buffer::CanvasGPUBuffer,
gpu_command_encoder::CanvasGPUCommandEncoder,
gpu_queue::CanvasGPUQueue,
gpu_shader_module::CanvasGPUShaderModule,
gpu_supported_limits::CanvasGPUSupportedLimits,
gpu_texture::CanvasGPUTexture,
prelude::*,
};

Expand Down Expand Up @@ -65,7 +69,7 @@ impl CanvasGPUDevice {
let (buffer, err) =
gfx_select!(device_id => global.device_create_buffer(device_id, &desc, None));

// todo handle error
// todo handle error
if let Some(_) = err {
error = CString::new("usage is not valid").unwrap().into_raw();
std::ptr::null_mut()
Expand Down Expand Up @@ -280,3 +284,91 @@ pub extern "C" fn canvas_native_webgpu_device_create_buffer(

device.create_buffer(label, size, usage, mapped_at_creation, error)
}

#[repr(C)]
pub struct CanvasCreateTextureDescriptor {
label: *const c_char,
dimension: CanvasTextureDimension,
format: CanvasGPUTextureFormat,
mipLevelCount: u32,
sampleCount: u32,
width: u32,
height: u32,
depthOrArrayLayers: u32,
usage: u32,
view_formats: *const CanvasGPUTextureFormat,
view_formats_size: usize,
}

#[no_mangle]
pub extern "C" fn canvas_native_webgpu_device_create_texture(
device: *const CanvasGPUDevice,
descriptor: *const CanvasCreateTextureDescriptor,
mut error: *mut c_char,
) -> *mut CanvasGPUTexture {
if device.is_null() || descriptor.is_null() {
return std::ptr::null_mut();
}

let device = unsafe { &*device };
let descriptor = unsafe { &*descriptor };
let device_id = device.device;

let global = &device.instance.0;

let label = if !descriptor.label.is_null() {
Some(unsafe { CStr::from_ptr(descriptor.label).to_string_lossy() })
} else {
None
};

let view_formats = if !descriptor.view_formats.is_null() && descriptor.view_formats_size > 0 {
unsafe {
std::slice::from_raw_parts(descriptor.view_formats, descriptor.view_formats_size)
.to_vec()
.into_iter()
.map(|v| v.into())
.collect::<Vec<wgpu_types::TextureFormat>>()
}
} else {
vec![]
};

let desc = wgpu_core::resource::TextureDescriptor {
label,
format: descriptor.format.into(),
size: wgpu_types::Extent3d {
width: descriptor.width,
height: descriptor.height,
depth_or_array_layers: descriptor.width,
},
mip_level_count: descriptor.mipLevelCount,
sample_count: descriptor.sampleCount,
dimension: descriptor.dimension.into(),
usage: wgpu_types::TextureUsages::from_bits_truncate(descriptor.usage),
view_formats,
};

let (texture_id, err) =
gfx_select!(device_id => global.device_create_texture(device_id, &desc, None));

if let Some(err) = err {
let err = err.to_string();
error = CString::new(err).unwrap().into_raw();
return std::ptr::null_mut();
}

Box::into_raw(Box::new(CanvasGPUTexture {
instance: device.instance.clone(),
texture: texture_id,
owned: true,
depth_or_array_layers: descriptor.depthOrArrayLayers,
dimension: descriptor.dimension,
format: descriptor.format,
mipLevelCount: descriptor.mipLevelCount,
sampleCount: descriptor.sampleCount,
width: descriptor.width,
height: descriptor.height,
usage: descriptor.usage,
}))
}
Loading

0 comments on commit 4bc0c6b

Please sign in to comment.