Initial commit
This commit is contained in:
commit
cfd32501b6
5 changed files with 329 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
.cache/
|
||||||
|
build/
|
||||||
|
tmp/
|
||||||
|
.clangd
|
||||||
17
CMakeLists.txt
Normal file
17
CMakeLists.txt
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
cmake_minimum_required(VERSION 4.0)
|
||||||
|
|
||||||
|
project(wgpu-wasm LANGUAGES C CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
||||||
|
|
||||||
|
add_custom_target(${CMAKE_PROJECT_NAME}_html COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/assets/index.html ${CMAKE_CURRENT_BINARY_DIR}/index.html
|
||||||
|
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/assets/index.html)
|
||||||
|
|
||||||
|
add_executable(${CMAKE_PROJECT_NAME} src/main.cpp)
|
||||||
|
|
||||||
|
add_dependencies(${CMAKE_PROJECT_NAME} ${CMAKE_PROJECT_NAME}_html)
|
||||||
|
|
||||||
|
target_compile_options(${CMAKE_PROJECT_NAME} PRIVATE $<$<CXX_COMPILER_ID:Msvc>:/W4 /WX> $<$<NOT:$<CXX_COMPILER_ID:Msvc>>:-Wall -Wextra -Wpedantic -Werror>)
|
||||||
|
target_compile_options(${CMAKE_PROJECT_NAME} PRIVATE --use-port=emdawnwebgpu)
|
||||||
|
target_link_options(${CMAKE_PROJECT_NAME} PRIVATE --use-port=emdawnwebgpu)
|
||||||
30
assets/index.html
Normal file
30
assets/index.html
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>WGPU ASM Test</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
background-color: rgb(0.25, 0.25, 0.25);
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wgpu-canvas {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<canvas id="wgpu-canvas" width="1280" height="720">
|
||||||
|
</canvas>
|
||||||
|
<script defer src="wgpu-wasm.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
28
assets/shaders/shaders.slang
Normal file
28
assets/shaders/shaders.slang
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
struct STDVertex {
|
||||||
|
float3 pos;
|
||||||
|
float4 col;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VertexOut {
|
||||||
|
float4 pos : SV_Position;
|
||||||
|
float4 col;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct STDCamera {
|
||||||
|
float4x4 mvp;
|
||||||
|
float3 position;
|
||||||
|
float3 direction;
|
||||||
|
};
|
||||||
|
|
||||||
|
[shader("vertex")]
|
||||||
|
VertexOut vsMain(uint vertex_id: SV_VertexID, uint instance_id: SV_InstanceID, StructuredBuffer<STDVertex> vertices, uniform StructuredBuffer<STDCamera> camera) {
|
||||||
|
return VertexOut(mul(float4(vertices[vertex_id].pos, 1.0), camera[0].mvp), vertices[vertex_id].col);
|
||||||
|
}
|
||||||
|
|
||||||
|
[shader("pixel")]
|
||||||
|
float4 fsMain(VertexOut fsIn){
|
||||||
|
return fsIn.col;
|
||||||
|
}
|
||||||
|
|
||||||
250
src/main.cpp
Normal file
250
src/main.cpp
Normal file
|
|
@ -0,0 +1,250 @@
|
||||||
|
#include "webgpu/webgpu_cpp.h"
|
||||||
|
#ifdef EMSCRIPTEN
|
||||||
|
#include "emscripten/emscripten.h"
|
||||||
|
#endif
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
enum LoadStatus {
|
||||||
|
None,
|
||||||
|
RequestedAdapter,
|
||||||
|
GotAdapter,
|
||||||
|
RequestedDevice,
|
||||||
|
GotDevice,
|
||||||
|
Ready,
|
||||||
|
Error
|
||||||
|
};
|
||||||
|
|
||||||
|
LoadStatus load_status;
|
||||||
|
|
||||||
|
wgpu::Instance gpu_instance;
|
||||||
|
wgpu::Adapter gpu_adapter;
|
||||||
|
wgpu::Device gpu_device;
|
||||||
|
wgpu::Surface gpu_surface;
|
||||||
|
wgpu::TextureFormat target_format;
|
||||||
|
wgpu::Texture target_texture;
|
||||||
|
wgpu::Extent3D target_size = {};
|
||||||
|
|
||||||
|
void request_adapter() {
|
||||||
|
std::cout << "[WGPU] request_adapter()" << std::endl;
|
||||||
|
gpu_instance = wgpu::CreateInstance();
|
||||||
|
|
||||||
|
load_status = RequestedAdapter;
|
||||||
|
|
||||||
|
wgpu::RequestAdapterOptions options = {};
|
||||||
|
|
||||||
|
gpu_instance.RequestAdapter(&options, wgpu::CallbackMode::AllowProcessEvents, [](wgpu::RequestAdapterStatus status, wgpu::Adapter adapter, std::string_view error) -> void {
|
||||||
|
if (status != wgpu::RequestAdapterStatus::Success) {
|
||||||
|
std::cout << "[WGPU] RequestAdapter error: " << error << std::endl;
|
||||||
|
load_status = Error;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gpu_adapter = adapter;
|
||||||
|
load_status = GotAdapter;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void request_device() {
|
||||||
|
std::cout << "[WGPU] request_device()" << std::endl;
|
||||||
|
load_status = RequestedDevice;
|
||||||
|
|
||||||
|
wgpu::DeviceDescriptor descriptor = {};
|
||||||
|
|
||||||
|
gpu_adapter.RequestDevice(&descriptor, wgpu::CallbackMode::AllowProcessEvents, [](wgpu::RequestDeviceStatus status, wgpu::Device device, std::string_view error) -> void {
|
||||||
|
if (status != wgpu::RequestDeviceStatus::Success) {
|
||||||
|
std::cout << "[WGPU] RequestDevice error: " << error << std::endl;
|
||||||
|
load_status = Error;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpu_device = device;
|
||||||
|
load_status = GotDevice;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void configure_surface() {
|
||||||
|
if (!gpu_surface) {
|
||||||
|
|
||||||
|
wgpu::SurfaceDescriptor surface_descriptor = {};
|
||||||
|
#ifdef EMSCRIPTEN
|
||||||
|
wgpu::EmscriptenSurfaceSourceCanvasHTMLSelector selector = {};
|
||||||
|
selector.sType = wgpu::SType::EmscriptenSurfaceSourceCanvasHTMLSelector;
|
||||||
|
selector.selector = "#wgpu-canvas";
|
||||||
|
surface_descriptor.nextInChain = &selector;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
gpu_surface = gpu_instance.CreateSurface(&surface_descriptor);
|
||||||
|
} else {
|
||||||
|
gpu_surface.Unconfigure();
|
||||||
|
}
|
||||||
|
|
||||||
|
wgpu::SurfaceCapabilities surface_caps = {};
|
||||||
|
|
||||||
|
gpu_surface.GetCapabilities(gpu_adapter, &surface_caps);
|
||||||
|
|
||||||
|
target_format = surface_caps.formatCount > 0 ? surface_caps.formats[0] : wgpu::TextureFormat::RGBA8Unorm;
|
||||||
|
|
||||||
|
wgpu::SurfaceConfiguration surface_config = {};
|
||||||
|
surface_config.device = gpu_device;
|
||||||
|
surface_config.alphaMode = surface_caps.alphaModeCount > 0 ? surface_caps.alphaModes[0] : wgpu::CompositeAlphaMode::Premultiplied;
|
||||||
|
surface_config.presentMode = surface_caps.presentModeCount > 0 ? surface_caps.presentModes[0] : wgpu::PresentMode::Undefined;
|
||||||
|
surface_config.usage = wgpu::TextureUsage::CopyDst;
|
||||||
|
surface_config.width = target_size.width;
|
||||||
|
surface_config.height = target_size.height;
|
||||||
|
surface_config.viewFormatCount = 0;
|
||||||
|
surface_config.format = target_format;
|
||||||
|
|
||||||
|
gpu_surface.Configure(&surface_config);
|
||||||
|
|
||||||
|
wgpu::TextureDescriptor target_texture_desc = {};
|
||||||
|
target_texture_desc.format = target_format;
|
||||||
|
target_texture_desc.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment;
|
||||||
|
target_texture_desc.dimension = wgpu::TextureDimension::e2D;
|
||||||
|
target_texture_desc.mipLevelCount = 1;
|
||||||
|
target_texture_desc.sampleCount = 1;
|
||||||
|
target_texture_desc.size.width = target_size.width;
|
||||||
|
target_texture_desc.size.height = target_size.height;;
|
||||||
|
target_texture_desc.size.depthOrArrayLayers = 1;
|
||||||
|
|
||||||
|
if (target_texture) {
|
||||||
|
target_texture.Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
target_texture = gpu_device.CreateTexture(&target_texture_desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_resources() {
|
||||||
|
std::cout << "[WGPU] load_resources()" << std::endl;
|
||||||
|
|
||||||
|
target_size.width = 1280;
|
||||||
|
target_size.height = 720;
|
||||||
|
configure_surface();
|
||||||
|
|
||||||
|
load_status = Ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
void load() {
|
||||||
|
switch(load_status) {
|
||||||
|
case LoadStatus::None:
|
||||||
|
request_adapter();
|
||||||
|
return;
|
||||||
|
case LoadStatus::GotAdapter:
|
||||||
|
request_device();
|
||||||
|
return;
|
||||||
|
case LoadStatus::GotDevice:
|
||||||
|
load_resources();
|
||||||
|
return;
|
||||||
|
case LoadStatus::Error:
|
||||||
|
std::cerr << "[WGPU] An error occurred during load. Aborting." << std::endl;
|
||||||
|
emscripten_cancel_main_loop();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef EMSCRIPTEN
|
||||||
|
EM_JS(int, get_page_width, (), {
|
||||||
|
return document.documentElement.offsetWidth;
|
||||||
|
});
|
||||||
|
|
||||||
|
EM_JS(int, get_page_height, (), {
|
||||||
|
return document.documentElement.offsetHeight;
|
||||||
|
});
|
||||||
|
EM_JS(void, resize_canvas, (const char* canvas_id, int width, int height), {
|
||||||
|
const canvas = document.getElementById(canvas_id);
|
||||||
|
|
||||||
|
if (!canvas) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
});
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void main_loop() {
|
||||||
|
if (load_status != LoadStatus::Ready) {
|
||||||
|
load();
|
||||||
|
gpu_instance.ProcessEvents();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef EMSCRIPTEN
|
||||||
|
auto page_width = static_cast<uint32_t>(get_page_width());
|
||||||
|
auto page_height = static_cast<uint32_t>(get_page_height());
|
||||||
|
|
||||||
|
if (page_width != target_size.width || page_height != target_size.height) {
|
||||||
|
std::cout << "[WGPU] Resizing to: " << page_width << "x" << page_height << std::endl;
|
||||||
|
target_size.width = page_width;
|
||||||
|
target_size.height = page_height;
|
||||||
|
resize_canvas("wgpu-canvas", page_width, page_height);
|
||||||
|
configure_surface();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
wgpu::SurfaceTexture display_texture = {};
|
||||||
|
|
||||||
|
gpu_surface.GetCurrentTexture(&display_texture);
|
||||||
|
|
||||||
|
if (display_texture.status != wgpu::SurfaceGetCurrentTextureStatus::SuccessOptimal &&display_texture.status != wgpu::SurfaceGetCurrentTextureStatus::SuccessSuboptimal) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto command_encoder = gpu_device.CreateCommandEncoder();
|
||||||
|
|
||||||
|
wgpu::RenderPassColorAttachment target_attachment = {};
|
||||||
|
target_attachment.clearValue.r = 0.0f;
|
||||||
|
target_attachment.clearValue.g = 0.0f;
|
||||||
|
target_attachment.clearValue.b = 1.0f;
|
||||||
|
target_attachment.clearValue.a = 1.0f;
|
||||||
|
target_attachment.loadOp = wgpu::LoadOp::Clear;
|
||||||
|
target_attachment.storeOp = wgpu::StoreOp::Store;
|
||||||
|
target_attachment.view = target_texture.CreateView();
|
||||||
|
|
||||||
|
wgpu::RenderPassDescriptor pass_desc = {};
|
||||||
|
pass_desc.colorAttachmentCount = 1;
|
||||||
|
pass_desc.colorAttachments = &target_attachment;
|
||||||
|
|
||||||
|
auto pass = command_encoder.BeginRenderPass(&pass_desc);
|
||||||
|
|
||||||
|
pass.End();
|
||||||
|
|
||||||
|
wgpu::TexelCopyTextureInfo src_info = {};
|
||||||
|
src_info.texture = target_texture;
|
||||||
|
src_info.aspect = wgpu::TextureAspect::All;
|
||||||
|
src_info.mipLevel = 0;
|
||||||
|
|
||||||
|
wgpu::TexelCopyTextureInfo dst_info = {};
|
||||||
|
dst_info.texture = display_texture.texture;
|
||||||
|
dst_info.aspect = wgpu::TextureAspect::All;
|
||||||
|
dst_info.mipLevel = 0;
|
||||||
|
|
||||||
|
wgpu::Extent3D copy_size = {};
|
||||||
|
copy_size.width = target_size.width;
|
||||||
|
copy_size.height = target_size.height;
|
||||||
|
copy_size.depthOrArrayLayers = 1;
|
||||||
|
|
||||||
|
command_encoder.CopyTextureToTexture(&src_info, &dst_info, ©_size);
|
||||||
|
|
||||||
|
auto cmd_buffer = command_encoder.Finish();
|
||||||
|
|
||||||
|
gpu_device.GetQueue().Submit(1, &cmd_buffer);
|
||||||
|
|
||||||
|
#ifndef EMSCRIPTEN
|
||||||
|
//TODO: Run gpu_srface.Present() when queued work is done
|
||||||
|
#endif
|
||||||
|
gpu_instance.ProcessEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int, char**) {
|
||||||
|
#ifdef EMSCRIPTEN
|
||||||
|
emscripten_set_main_loop(main_loop, 0, true);
|
||||||
|
#else
|
||||||
|
while(true) {
|
||||||
|
main_loop();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue