webgpu 编译并集成到Qt中
Dawn库编译
dawn 开源库编译 https://dawn.googlesource.com/dawn
set http_proxy=http://127.0.0.1:1080
set https_proxy=http://127.0.0.1:1080
下载gclient https://storage.googleapis.com/chrome-infra/depot_tools.zip
# 引导 gclient 配置
cp scripts/standalone.gclient .gclient
# 使用 gclient 拉取外部依赖与工具链
gclient sync
使用cmake 编译
一般来说我们要编译的是动态库,通过cmake 传入参数 -DBUILD_SHARED_LIBS=1
,但是在编译dawn_common
库时会出现,这个库只能是静态库,需要手动修改配置(可以用cmake-gui生成vs工程再修改dawn_common为静态库)。
mkdir build1
cd build1
cmake ..
make -j8
编译结果
dawn20241113.7z
集成到Qt程序中
初始化webgpu 相关信息;
//初始化instance dawnProcSetProcs这个函数必须写,否则启动会报错。
dawnProcSetProcs(&dawn::native::GetProcs());
wgpu::InstanceDescriptor instanceDescriptor = {};
instanceDescriptor.features.timedWaitAnyEnable = true;
var _instance = wgpu::CreateInstance(&instanceDescriptor);
//获取适配器和设备;
wgpu::RequestAdapterOptions options;
//默认设置为独显;
options.powerPreference = wgpu::PowerPreference::HighPerformance;
//设置后端程序为D3D12
options.backendType = wgpu::BackendType::D3D12;
//获取适配器;
wgpu::Adapter _adapter
_instance.WaitAny(
_instance.RequestAdapter(
&options, wgpu::CallbackMode::WaitAnyOnly,
[this](wgpu::RequestAdapterStatus status, wgpu::Adapter adapter, wgpu::StringView message) {
if (status != wgpu::RequestAdapterStatus::Success) {
return;
}
_adapter = std::move(adapter);
}),
UINT64_MAX);
wgpu::ChainedStruct* togglesChain = nullptr;
//获取设备;
wgpu::DeviceDescriptor deviceDesc = {};
//设置错误回调;
deviceDesc.SetUncapturedErrorCallback(
[](const wgpu::Device&, wgpu::ErrorType type, wgpu::StringView message) {
});
_instance.WaitAny(
_adapter.RequestDevice(
&deviceDesc, wgpu::CallbackMode::WaitAnyOnly,
[this](wgpu::RequestDeviceStatus status, wgpu::Device device, wgpu::StringView message) {
if (status != wgpu::RequestDeviceStatus::Success) {
return;
}
_device= std::move(device);
}),
UINT64_MAX);
}
将Qt窗口绑定到webgpu的surface中
std::unique_ptr<wgpu::ChainedStruct, void (*)(wgpu::ChainedStruct*)>
SetupWindowAndGetSurfaceDescriptor(void* window) {
wgpu::SurfaceSourceWindowsHWND* desc = new wgpu::SurfaceSourceWindowsHWND();
desc->hwnd = window;
desc->hinstance = GetModuleHandle(nullptr);
return { desc, [](wgpu::ChainedStruct* desc) {
delete reinterpret_cast<wgpu::SurfaceSourceWindowsHWND*>(desc);
} };
}
//指定Qt窗口的句柄;
wgpu::Surface CreateSurfaceForWindow(wgpu::Instance instance, void* window) {
wgpu::SurfaceDescriptor temp_descriptor;
auto desc=SetupWindowAndGetSurfaceDescriptor(window);
temp_descriptor.nextInChain = desc.get();
wgpu::Surface surface = instance.CreateSurface(&temp_descriptor);
return surface;
}
窗口大小调整响应
void Renderer::onResize()
{
RECT rect;
GetWindowRect((HWND)_impl->_window_handle, &rect);
int width= rect.right - rect.left;
int height= rect.bottom - rect.top;
wgpu::SurfaceConfiguration config;
config.width = width;
config.height = height;
config.format = wgpu::TextureFormat::BGRA8Unorm;
config.device =_device;
config.presentMode = wgpu::PresentMode::Fifo;
config.usage = wgpu::TextureUsage::RenderAttachment;
_impl->_surface.Configure(&config);
}
主帧渲染函数
auto device = GlobalInstance::instance()->device();
wgpu::RenderPassColorAttachment attachment = {};
if (v == 0)
{
int time = clock();
_impl->_surface.GetCurrentTexture(&surface_texture);
v = 0;
int end=clock()-time;
std::cout << "texture time:" << end << std::endl;
}
text_View = surface_texture.texture.CreateView();
attachment.view = text_View;
attachment.loadOp = wgpu::LoadOp::Clear;
attachment.storeOp = wgpu::StoreOp::Store;
//设置背景色;
attachment.clearValue=wgpu::Color{0.0f,0.0f,0.0f,1.0f};
attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
wgpu::RenderPassDescriptor renderpass;
renderpass.colorAttachmentCount = 1;
renderpass.colorAttachments = &attachment;
float* mat = glm::value_ptr(_mat);
device.GetQueue().WriteBuffer(_batch->impl()->_matrix_buffer, 0, mat, sizeof(glm::mat4));
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderpass);
//pass.setViewport(0, 0, 500, 500,0,1 );
pass.SetPipeline(_batch->impl()->_pipeline);
pass.SetViewport(0, 0, _impl->_width, _impl->_height, 0, 1);
pass.SetScissorRect(0, 0, _impl->_width, _impl->_height);
pass.SetBindGroup(0, _batch->impl()->_bind_group);
pass.SetVertexBuffer(0, _batch->impl()->_vertex_buffer,0, _batch->impl()->_vertex_buffer.GetSize());
pass.SetVertexBuffer(1, _batch->impl()->_color_buffer);
pass.SetIndexBuffer(_batch->impl()->_index_buffer, wgpu::IndexFormat::Uint32);
pass.DrawIndexed(_batch->impl()->_index_buffer.GetSize()/4);
pass.End();
wgpu::CommandBuffer commands = encoder.Finish();
device.GetQueue().Submit(1,&commands);
_impl->_surface.Present();
QWidget 窗口设置
class CenterWidget :public QWidget
{
public:
CenterWidget()
{
this->setAttribute(Qt::WA_PaintOnScreen, true);
this->setAttribute(Qt::WA_NativeWindow, true);
this->setUpdatesEnabled(false);
}
virtual QPaintEngine* paintEngine() const override
{
return nullptr;
}
void resizeEvent(QResizeEvent* event)
{
if (_renderer != nullptr)
{
_renderer->onResize();
}
}
void frame()
{
QTime tm;
tm.start();
//调用的render的渲染方法,具体如何渲染不在本文章中.
_renderer->render();
int aa = tm.elapsed();
qApp->processEvents();
std::cout << "frame time:" << aa << std::endl;
}
}
测试
重复渲染100万个三角形,3060独立显卡,将所有数据放在一个顶点缓冲中,并使用索引绘制的结果。基本上需要耗时60ms。