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