[General] First triangle setup (not rendering yet)
This commit is contained in:
parent
6d66391e83
commit
f1e3a25f3a
3 changed files with 315 additions and 6 deletions
|
|
@ -2,8 +2,8 @@ cmake_minimum_required(VERSION 4.0)
|
|||
|
||||
project(wgpu-wasm LANGUAGES C CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
||||
set(GLM_BUILD_LIBRARY FALSE)
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/contrib/glm)
|
||||
|
||||
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)
|
||||
|
|
@ -11,9 +11,12 @@ add_custom_target(${CMAKE_PROJECT_NAME}_html COMMAND ${CMAKE_COMMAND} -E copy ${
|
|||
add_executable(${CMAKE_PROJECT_NAME} src/main.cpp)
|
||||
add_dependencies(${CMAKE_PROJECT_NAME} ${CMAKE_PROJECT_NAME}_html)
|
||||
|
||||
target_compile_features(${CMAKE_PROJECT_NAME} PRIVATE cxx_std_17)
|
||||
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)
|
||||
target_link_options(${CMAKE_PROJECT_NAME} PRIVATE --use-port=emdawnwebgpu -sASYNCIFY=1)
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE glm::glm)
|
||||
target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE -DGLM_FORCE_DEPTH_ZERO_TO_ONE -DGLM_FORCE_LEFT_HANDED)
|
||||
|
||||
function(compile_and_package_shader SHADER_NAME TARGET)
|
||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/shaders/${SHADER_NAME}.wgsl
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ struct STDCamera {
|
|||
};
|
||||
|
||||
[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);
|
||||
VertexOut vsMain(uint vertex_id: SV_VertexID, uint instance_id: SV_InstanceID, StructuredBuffer<STDVertex> vertices, uniform STDCamera camera) {
|
||||
return VertexOut(mul(camera.mvp, float4(vertices[vertex_id].pos, 1.0)), vertices[vertex_id].col);
|
||||
}
|
||||
|
||||
[shader("pixel")]
|
||||
|
|
|
|||
308
src/main.cpp
308
src/main.cpp
|
|
@ -1,8 +1,22 @@
|
|||
#include "glm/ext/matrix_clip_space.hpp"
|
||||
#include "glm/trigonometric.hpp"
|
||||
#include "webgpu/webgpu_cpp.h"
|
||||
#ifdef EMSCRIPTEN
|
||||
#include "emscripten/emscripten.h"
|
||||
#endif
|
||||
#define GLM_FORCE_ALIGNED_GENTYPES
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
#define SHADERS_ROOT "/shaders/"
|
||||
#else
|
||||
#define SHADERS_ROOT "shaders/"
|
||||
#endif
|
||||
|
||||
enum LoadStatus {
|
||||
None,
|
||||
|
|
@ -16,17 +30,66 @@ enum LoadStatus {
|
|||
|
||||
LoadStatus load_status;
|
||||
|
||||
struct STDVertex {
|
||||
union {
|
||||
glm::vec3 pos;
|
||||
float __pos_raw[4];
|
||||
};
|
||||
glm::vec4 col;
|
||||
};
|
||||
|
||||
std::array<STDVertex, 3> sample_triangle = {{
|
||||
{
|
||||
{ glm::vec3(-0.5f, -0.5f, -0.5f) },
|
||||
glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)
|
||||
},
|
||||
{
|
||||
{ glm::vec3(0.5f, -0.5f, -0.5f) },
|
||||
glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)
|
||||
},
|
||||
{
|
||||
{ glm::vec3(0.0f, 0.5f, -0.5f) },
|
||||
glm::vec4(0.0f, 0.0f, 1.0f, 1.0f)
|
||||
}
|
||||
}};
|
||||
|
||||
struct STDCamera {
|
||||
glm::mat4 mvp;
|
||||
union {
|
||||
glm::vec3 pos;
|
||||
float __pos_raw[4];
|
||||
};
|
||||
union {
|
||||
glm::vec3 dir;
|
||||
float __dir_raw[4];
|
||||
};
|
||||
};
|
||||
|
||||
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::Texture target_depth_texture;
|
||||
wgpu::Extent3D target_size = {};
|
||||
wgpu::Buffer staging_buffer;
|
||||
wgpu::Buffer vertex_buffer;
|
||||
wgpu::Buffer uniform_buffer;
|
||||
wgpu::ShaderModule basic_shader_module;
|
||||
wgpu::BindGroupLayout basic_bind_group_layout;
|
||||
wgpu::PipelineLayout basic_bind_layout;
|
||||
wgpu::RenderPipeline basic_render_pipeline;
|
||||
wgpu::BindGroup basic_bind_group;
|
||||
|
||||
void request_adapter() {
|
||||
std::cout << "[WGPU] request_adapter()" << std::endl;
|
||||
gpu_instance = wgpu::CreateInstance();
|
||||
|
||||
wgpu::InstanceDescriptor instance_desc{};
|
||||
instance_desc.capabilities.timedWaitAnyEnable = true;
|
||||
instance_desc.capabilities.timedWaitAnyMaxCount = 1;
|
||||
|
||||
gpu_instance = wgpu::CreateInstance(&instance_desc);
|
||||
|
||||
load_status = RequestedAdapter;
|
||||
|
||||
|
|
@ -41,6 +104,8 @@ void request_adapter() {
|
|||
gpu_adapter = adapter;
|
||||
load_status = GotAdapter;
|
||||
});
|
||||
|
||||
//gpu_instance.WaitAny(f, -1);
|
||||
}
|
||||
|
||||
void request_device() {
|
||||
|
|
@ -110,6 +175,197 @@ void configure_surface() {
|
|||
}
|
||||
|
||||
target_texture = gpu_device.CreateTexture(&target_texture_desc);
|
||||
|
||||
wgpu::TextureDescriptor depth_desc{};
|
||||
depth_desc.format = wgpu::TextureFormat::Depth16Unorm;
|
||||
depth_desc.usage = wgpu::TextureUsage::RenderAttachment;
|
||||
depth_desc.dimension = wgpu::TextureDimension::e2D;
|
||||
depth_desc.mipLevelCount = 1;
|
||||
depth_desc.sampleCount = 1;
|
||||
depth_desc.size.width = target_size.width;
|
||||
depth_desc.size.height = target_size.height;
|
||||
depth_desc.size.depthOrArrayLayers = 1;
|
||||
|
||||
if (target_depth_texture) {
|
||||
target_depth_texture.Destroy();
|
||||
}
|
||||
|
||||
target_depth_texture = gpu_device.CreateTexture(&depth_desc);
|
||||
}
|
||||
|
||||
std::string load_shader_source(const std::string_view& shader_name) {
|
||||
auto path = std::string(SHADERS_ROOT) + std::string(shader_name) + ".wgsl";
|
||||
if (!std::filesystem::exists(path)) {
|
||||
throw std::runtime_error("Shader not found.");
|
||||
}
|
||||
|
||||
std::ifstream stream(path);
|
||||
std::istream_iterator<char> start(stream), end{};
|
||||
|
||||
return std::string(start, end);
|
||||
}
|
||||
|
||||
wgpu::ShaderModule load_shader(const std::string_view& shader_name) {
|
||||
std::cout << "[WGPU] load_shader(\"" << shader_name << "\")" << std::endl;
|
||||
auto source = load_shader_source(shader_name);
|
||||
|
||||
wgpu::ShaderSourceWGSL source_desc{};
|
||||
source_desc.code = std::string_view(source);
|
||||
source_desc.sType = wgpu::SType::ShaderSourceWGSL;
|
||||
|
||||
wgpu::ShaderModuleDescriptor descriptor{};
|
||||
descriptor.nextInChain = &source_desc;
|
||||
|
||||
return gpu_device.CreateShaderModule(&descriptor);
|
||||
}
|
||||
|
||||
void load_buffers() {
|
||||
std::cout << "[WGPU] load_buffers()" << std::endl;
|
||||
|
||||
wgpu::BufferDescriptor staging_buffer_desc{};
|
||||
staging_buffer_desc.size = sizeof(float) * 1024;
|
||||
staging_buffer_desc.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc;
|
||||
staging_buffer_desc.label = "STAGING_BUFER_1K";
|
||||
|
||||
staging_buffer = gpu_device.CreateBuffer(&staging_buffer_desc);
|
||||
|
||||
wgpu::BufferDescriptor vertex_buffer_desc{};
|
||||
vertex_buffer_desc.size = sizeof(float) * 8 * 2048; //2048 STDVertex entries
|
||||
vertex_buffer_desc.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopyDst;
|
||||
vertex_buffer_desc.label = "BASIC_VERTEX_BUFFER";
|
||||
|
||||
vertex_buffer = gpu_device.CreateBuffer(&vertex_buffer_desc);
|
||||
|
||||
wgpu::BufferDescriptor uniform_buffer_desc{};
|
||||
uniform_buffer_desc.size = sizeof(float) * 24;
|
||||
uniform_buffer_desc.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst;
|
||||
uniform_buffer_desc.label = "CAMERA_BUFFER";
|
||||
|
||||
uniform_buffer = gpu_device.CreateBuffer(&uniform_buffer_desc);
|
||||
}
|
||||
|
||||
wgpu::PipelineLayout load_basic_pipeline_layout() {
|
||||
std::cout << "[WGPU] load_basic_pipeline_layout" << std::endl;
|
||||
std::array<wgpu::BindGroupLayoutEntry, 2> layout_entries{};
|
||||
layout_entries[0].binding = 1;
|
||||
layout_entries[0].buffer.minBindingSize = sizeof(float) * 8 * 3; //At least 3 STDVertex entries
|
||||
layout_entries[0].buffer.type = wgpu::BufferBindingType::ReadOnlyStorage;
|
||||
layout_entries[1].binding = 0;
|
||||
layout_entries[1].buffer.minBindingSize = sizeof(float) * 24; //1 STDCamera entry
|
||||
layout_entries[1].buffer.type = wgpu::BufferBindingType::Uniform;
|
||||
|
||||
wgpu::BindGroupLayoutDescriptor group_desc{};
|
||||
group_desc.entryCount = 2;
|
||||
group_desc.entries = layout_entries.data();
|
||||
|
||||
basic_bind_group_layout = gpu_device.CreateBindGroupLayout(&group_desc);
|
||||
|
||||
wgpu::PipelineLayoutDescriptor layout_desc{};
|
||||
layout_desc.bindGroupLayoutCount = 1;
|
||||
layout_desc.bindGroupLayouts = &basic_bind_group_layout;
|
||||
|
||||
return gpu_device.CreatePipelineLayout(&layout_desc);
|
||||
}
|
||||
|
||||
wgpu::RenderPipeline load_basic_render_pipeline() {
|
||||
std::cout << "[WGPU] load_basic_render_pipeline()" << std::endl;
|
||||
wgpu::BlendState fragment_blend{};
|
||||
fragment_blend.alpha.operation = wgpu::BlendOperation::Add;
|
||||
fragment_blend.alpha.srcFactor = wgpu::BlendFactor::SrcAlpha;
|
||||
fragment_blend.alpha.dstFactor = wgpu::BlendFactor::OneMinusSrcAlpha;
|
||||
fragment_blend.color.operation = wgpu::BlendOperation::Add;
|
||||
fragment_blend.color.srcFactor = wgpu::BlendFactor::SrcAlpha;
|
||||
fragment_blend.color.dstFactor = wgpu::BlendFactor::OneMinusSrcAlpha;
|
||||
|
||||
wgpu::ColorTargetState fragment_target{};
|
||||
fragment_target.format = target_format;
|
||||
fragment_target.blend = &fragment_blend;
|
||||
fragment_target.writeMask = wgpu::ColorWriteMask::All;
|
||||
|
||||
wgpu::FragmentState fragment_state{};
|
||||
fragment_state.module = basic_shader_module;
|
||||
fragment_state.entryPoint = "fsMain";
|
||||
fragment_state.constantCount = 0;
|
||||
fragment_state.targetCount = 1;
|
||||
fragment_state.targets = &fragment_target;
|
||||
|
||||
wgpu::DepthStencilState depth_stencil{};
|
||||
depth_stencil.format = target_depth_texture.GetFormat();
|
||||
depth_stencil.depthCompare = wgpu::CompareFunction::LessEqual;
|
||||
depth_stencil.depthWriteEnabled = true;
|
||||
|
||||
wgpu::RenderPipelineDescriptor final_descriptor{};
|
||||
final_descriptor.fragment = &fragment_state;
|
||||
final_descriptor.vertex.module = basic_shader_module;
|
||||
final_descriptor.vertex.entryPoint = "vsMain";
|
||||
final_descriptor.vertex.constantCount = 0;
|
||||
final_descriptor.vertex.bufferCount = 0;
|
||||
final_descriptor.layout = basic_bind_layout;
|
||||
final_descriptor.primitive.topology = wgpu::PrimitiveTopology::TriangleList;
|
||||
final_descriptor.primitive.cullMode = wgpu::CullMode::Back;
|
||||
final_descriptor.primitive.frontFace = wgpu::FrontFace::CCW;
|
||||
//final_descriptor.primitive.stripIndexFormat = wgpu::IndexFormat::Uint16;
|
||||
final_descriptor.depthStencil = &depth_stencil;
|
||||
|
||||
return gpu_device.CreateRenderPipeline(&final_descriptor);
|
||||
}
|
||||
|
||||
wgpu::BindGroup load_basic_bind_group() {
|
||||
std::array<wgpu::BindGroupEntry, 2> entries;
|
||||
entries[0].size = vertex_buffer.GetSize();
|
||||
entries[0].binding = 1;
|
||||
entries[0].buffer = vertex_buffer;
|
||||
entries[0].offset = 0;
|
||||
entries[1].size = uniform_buffer.GetSize();
|
||||
entries[1].binding = 0;
|
||||
entries[1].buffer = uniform_buffer;
|
||||
|
||||
wgpu::BindGroupDescriptor final_desc{};
|
||||
final_desc.layout = basic_bind_group_layout;
|
||||
final_desc.entryCount = entries.size();
|
||||
final_desc.entries = entries.data();
|
||||
|
||||
return gpu_device.CreateBindGroup(&final_desc);
|
||||
}
|
||||
|
||||
void fill_buffers() {
|
||||
std::cout << "[WGPU] fill_buffers()" << std::endl;
|
||||
|
||||
auto map = staging_buffer.MapAsync(wgpu::MapMode::Write, 0, (sizeof(STDVertex) * 3) + sizeof(STDCamera), wgpu::CallbackMode::WaitAnyOnly, [](wgpu::MapAsyncStatus status, std::string_view error) -> void {
|
||||
if (status == wgpu::MapAsyncStatus::Success) {
|
||||
std::cout << "[WGPU] Map Ok. " << std::endl;
|
||||
staging_buffer.WriteMappedRange(0, sample_triangle.data(), sample_triangle.size() * sizeof(STDVertex));
|
||||
|
||||
STDCamera cam{};
|
||||
//cam.mvp = glm::perspectiveFov(glm::radians(90.0f), 1280.0f, 720.0f, 0.001f, 100.0f);
|
||||
cam.mvp = glm::mat4(1.0f);
|
||||
|
||||
staging_buffer.WriteMappedRange(sizeof(STDVertex) * 3, &cam, sizeof(STDCamera));
|
||||
staging_buffer.Unmap();
|
||||
} else {
|
||||
std::cerr << "Mapping error: " << error << std::endl;
|
||||
throw std::runtime_error("WAaaaaaAAAaa");
|
||||
}
|
||||
});
|
||||
|
||||
gpu_instance.WaitAny(map, -1);
|
||||
|
||||
auto enc = gpu_device.CreateCommandEncoder();
|
||||
enc.CopyBufferToBuffer(staging_buffer, 0, vertex_buffer, 0, sizeof(STDVertex) * 3);
|
||||
enc.CopyBufferToBuffer(staging_buffer, sizeof(STDVertex) * 3, uniform_buffer, 0, sizeof(STDCamera));
|
||||
auto cmd = enc.Finish();
|
||||
|
||||
gpu_device.GetQueue().Submit(1, &cmd);
|
||||
|
||||
auto f = gpu_device.GetQueue().OnSubmittedWorkDone(wgpu::CallbackMode::WaitAnyOnly, [](wgpu::QueueWorkDoneStatus status) -> void {
|
||||
if (status != wgpu::QueueWorkDoneStatus::Success) {
|
||||
std::cout << "WAAAA x2" << std::endl;
|
||||
}
|
||||
});
|
||||
|
||||
gpu_instance.WaitAny(f, -1);
|
||||
|
||||
std::cout << "[WGPU] Copy Ok." << std::endl;
|
||||
}
|
||||
|
||||
void load_resources() {
|
||||
|
|
@ -118,6 +374,12 @@ void load_resources() {
|
|||
target_size.width = 1280;
|
||||
target_size.height = 720;
|
||||
configure_surface();
|
||||
basic_shader_module = load_shader("shaders");
|
||||
load_buffers();
|
||||
basic_bind_layout = load_basic_pipeline_layout();
|
||||
basic_render_pipeline = load_basic_render_pipeline();
|
||||
basic_bind_group = load_basic_bind_group();
|
||||
fill_buffers();
|
||||
|
||||
load_status = Ready;
|
||||
}
|
||||
|
|
@ -161,6 +423,35 @@ EM_JS(void, resize_canvas, (const char* canvas_id, int width, int height), {
|
|||
canvas.height = height;
|
||||
});
|
||||
|
||||
void update_camera(int width, int height) {
|
||||
auto map_future = staging_buffer.MapAsync(wgpu::MapMode::Write, 0, sizeof(STDCamera), wgpu::CallbackMode::WaitAnyOnly, [width, height](wgpu::MapAsyncStatus status, std::string_view error) -> void {
|
||||
if (status != wgpu::MapAsyncStatus::Success) {
|
||||
std::cerr << "[WGPU] Map failure: " << error << std::endl;
|
||||
throw std::runtime_error("Staging buffer map failure");
|
||||
}
|
||||
STDCamera camera{};
|
||||
camera.mvp = glm::perspectiveFov(glm::radians(90.0f), static_cast<float>(width), static_cast<float>(height), 0.001f, 100.0f);
|
||||
camera.mvp = glm::mat4(1.0f);
|
||||
|
||||
staging_buffer.WriteMappedRange(0, &camera, sizeof(STDCamera));
|
||||
staging_buffer.Unmap();
|
||||
});
|
||||
|
||||
gpu_instance.WaitAny(map_future, -1);
|
||||
|
||||
auto enc = gpu_device.CreateCommandEncoder();
|
||||
enc.CopyBufferToBuffer(staging_buffer, 0, uniform_buffer, 0, sizeof(STDCamera));
|
||||
auto cmd = enc.Finish();
|
||||
|
||||
gpu_device.GetQueue().Submit(1, &cmd);
|
||||
|
||||
auto copy_future = gpu_device.GetQueue().OnSubmittedWorkDone(wgpu::CallbackMode::WaitAnyOnly, [](wgpu::QueueWorkDoneStatus) -> void {
|
||||
|
||||
});
|
||||
|
||||
gpu_instance.WaitAny(copy_future, -1);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void main_loop() {
|
||||
|
|
@ -180,6 +471,9 @@ void main_loop() {
|
|||
target_size.height = page_height;
|
||||
resize_canvas("wgpu-canvas", page_width, page_height);
|
||||
configure_surface();
|
||||
if (staging_buffer && uniform_buffer) {
|
||||
update_camera(page_width, page_height);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -203,11 +497,23 @@ void main_loop() {
|
|||
target_attachment.storeOp = wgpu::StoreOp::Store;
|
||||
target_attachment.view = target_texture.CreateView();
|
||||
|
||||
wgpu::RenderPassDepthStencilAttachment depth_attachment = {};
|
||||
depth_attachment.view = target_depth_texture.CreateView();
|
||||
depth_attachment.depthLoadOp = wgpu::LoadOp::Clear;
|
||||
depth_attachment.depthStoreOp = wgpu::StoreOp::Store;
|
||||
depth_attachment.depthClearValue = 1.0f;
|
||||
|
||||
wgpu::RenderPassDescriptor pass_desc = {};
|
||||
pass_desc.colorAttachmentCount = 1;
|
||||
pass_desc.colorAttachments = &target_attachment;
|
||||
pass_desc.depthStencilAttachment = &depth_attachment;
|
||||
|
||||
auto pass = command_encoder.BeginRenderPass(&pass_desc);
|
||||
pass.SetViewport(0, 0, page_width, page_height, 0.0f, 1.0f);
|
||||
pass.SetPipeline(basic_render_pipeline);
|
||||
pass.SetBindGroup(0, basic_bind_group);
|
||||
|
||||
pass.Draw(3);
|
||||
|
||||
pass.End();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue