mirror of
https://github.com/encounter/aurora.git
synced 2025-12-16 16:37:04 +00:00
Transition back to webgpu_cpp; initial emscripten support
This commit is contained in:
@@ -6,140 +6,149 @@
|
||||
#include "../internal.hpp"
|
||||
|
||||
#include <SDL.h>
|
||||
#include <dawn/native/DawnNative.h>
|
||||
#include <magic_enum.hpp>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef WEBGPU_DAWN
|
||||
#include <dawn/native/DawnNative.h>
|
||||
#include "../dawn/BackendBinding.hpp"
|
||||
#endif
|
||||
|
||||
namespace aurora::webgpu {
|
||||
static Module Log("aurora::gpu");
|
||||
|
||||
WGPUDevice g_device;
|
||||
WGPUQueue g_queue;
|
||||
WGPUSwapChain g_swapChain;
|
||||
WGPUBackendType g_backendType;
|
||||
wgpu::Device g_device;
|
||||
wgpu::Queue g_queue;
|
||||
wgpu::SwapChain g_swapChain;
|
||||
wgpu::BackendType g_backendType;
|
||||
GraphicsConfig g_graphicsConfig;
|
||||
TextureWithSampler g_frameBuffer;
|
||||
TextureWithSampler g_frameBufferResolved;
|
||||
TextureWithSampler g_depthBuffer;
|
||||
|
||||
// EFB -> XFB copy pipeline
|
||||
static WGPUBindGroupLayout g_CopyBindGroupLayout;
|
||||
WGPURenderPipeline g_CopyPipeline;
|
||||
WGPUBindGroup g_CopyBindGroup;
|
||||
static wgpu::BindGroupLayout g_CopyBindGroupLayout;
|
||||
wgpu::RenderPipeline g_CopyPipeline;
|
||||
wgpu::BindGroup g_CopyBindGroup;
|
||||
|
||||
static std::unique_ptr<dawn::native::Instance> g_Instance;
|
||||
static dawn::native::Adapter g_Adapter;
|
||||
static WGPUAdapterProperties g_AdapterProperties;
|
||||
static std::unique_ptr<utils::BackendBinding> g_BackendBinding;
|
||||
#ifdef WEBGPU_DAWN
|
||||
static std::unique_ptr<dawn::native::Instance> g_dawnInstance;
|
||||
static dawn::native::Adapter g_adapter;
|
||||
static std::unique_ptr<utils::BackendBinding> g_backendBinding;
|
||||
#else
|
||||
wgpu::Instance g_instance;
|
||||
static wgpu::Adapter g_adapter;
|
||||
#endif
|
||||
static wgpu::Surface g_surface;
|
||||
static wgpu::AdapterProperties g_adapterProperties;
|
||||
|
||||
TextureWithSampler create_render_texture(bool multisampled) {
|
||||
const WGPUExtent3D size{
|
||||
.width = g_graphicsConfig.width,
|
||||
.height = g_graphicsConfig.height,
|
||||
const wgpu::Extent3D size{
|
||||
.width = g_graphicsConfig.swapChainDescriptor.width,
|
||||
.height = g_graphicsConfig.swapChainDescriptor.height,
|
||||
.depthOrArrayLayers = 1,
|
||||
};
|
||||
const auto format = g_graphicsConfig.colorFormat;
|
||||
const auto format = g_graphicsConfig.swapChainDescriptor.format;
|
||||
uint32_t sampleCount = 1;
|
||||
if (multisampled) {
|
||||
sampleCount = g_graphicsConfig.msaaSamples;
|
||||
}
|
||||
const WGPUTextureDescriptor textureDescriptor{
|
||||
const wgpu::TextureDescriptor textureDescriptor{
|
||||
.label = "Render texture",
|
||||
.usage = WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopySrc |
|
||||
WGPUTextureUsage_CopyDst,
|
||||
.dimension = WGPUTextureDimension_2D,
|
||||
.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopySrc |
|
||||
wgpu::TextureUsage::CopyDst,
|
||||
.dimension = wgpu::TextureDimension::e2D,
|
||||
.size = size,
|
||||
.format = format,
|
||||
.mipLevelCount = 1,
|
||||
.sampleCount = sampleCount,
|
||||
};
|
||||
auto texture = wgpuDeviceCreateTexture(g_device, &textureDescriptor);
|
||||
auto texture = g_device.CreateTexture(&textureDescriptor);
|
||||
|
||||
const WGPUTextureViewDescriptor viewDescriptor{
|
||||
.dimension = WGPUTextureViewDimension_2D,
|
||||
const wgpu::TextureViewDescriptor viewDescriptor{
|
||||
.label = "Render texture view",
|
||||
.dimension = wgpu::TextureViewDimension::e2D,
|
||||
.mipLevelCount = WGPU_MIP_LEVEL_COUNT_UNDEFINED,
|
||||
.arrayLayerCount = WGPU_ARRAY_LAYER_COUNT_UNDEFINED,
|
||||
};
|
||||
auto view = wgpuTextureCreateView(texture, &viewDescriptor);
|
||||
auto view = texture.CreateView(&viewDescriptor);
|
||||
|
||||
const WGPUSamplerDescriptor samplerDescriptor{
|
||||
const wgpu::SamplerDescriptor samplerDescriptor{
|
||||
.label = "Render sampler",
|
||||
.addressModeU = WGPUAddressMode_ClampToEdge,
|
||||
.addressModeV = WGPUAddressMode_ClampToEdge,
|
||||
.addressModeW = WGPUAddressMode_ClampToEdge,
|
||||
.magFilter = WGPUFilterMode_Linear,
|
||||
.minFilter = WGPUFilterMode_Linear,
|
||||
.mipmapFilter = WGPUFilterMode_Linear,
|
||||
.addressModeU = wgpu::AddressMode::ClampToEdge,
|
||||
.addressModeV = wgpu::AddressMode::ClampToEdge,
|
||||
.addressModeW = wgpu::AddressMode::ClampToEdge,
|
||||
.magFilter = wgpu::FilterMode::Linear,
|
||||
.minFilter = wgpu::FilterMode::Linear,
|
||||
.mipmapFilter = wgpu::FilterMode::Linear,
|
||||
.lodMinClamp = 0.f,
|
||||
.lodMaxClamp = 1000.f,
|
||||
.maxAnisotropy = 1,
|
||||
};
|
||||
auto sampler = wgpuDeviceCreateSampler(g_device, &samplerDescriptor);
|
||||
auto sampler = g_device.CreateSampler(&samplerDescriptor);
|
||||
|
||||
return {
|
||||
.texture{texture},
|
||||
.view{view},
|
||||
.texture = std::move(texture),
|
||||
.view = std::move(view),
|
||||
.size = size,
|
||||
.format = format,
|
||||
.sampler{sampler},
|
||||
.sampler = std::move(sampler),
|
||||
};
|
||||
}
|
||||
|
||||
static TextureWithSampler create_depth_texture() {
|
||||
const WGPUExtent3D size{
|
||||
.width = g_graphicsConfig.width,
|
||||
.height = g_graphicsConfig.height,
|
||||
const wgpu::Extent3D size{
|
||||
.width = g_graphicsConfig.swapChainDescriptor.width,
|
||||
.height = g_graphicsConfig.swapChainDescriptor.height,
|
||||
.depthOrArrayLayers = 1,
|
||||
};
|
||||
const auto format = g_graphicsConfig.depthFormat;
|
||||
const WGPUTextureDescriptor textureDescriptor{
|
||||
const wgpu::TextureDescriptor textureDescriptor{
|
||||
.label = "Depth texture",
|
||||
.usage = WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding,
|
||||
.dimension = WGPUTextureDimension_2D,
|
||||
.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding,
|
||||
.dimension = wgpu::TextureDimension::e2D,
|
||||
.size = size,
|
||||
.format = format,
|
||||
.mipLevelCount = 1,
|
||||
.sampleCount = g_graphicsConfig.msaaSamples,
|
||||
};
|
||||
auto texture = wgpuDeviceCreateTexture(g_device, &textureDescriptor);
|
||||
auto texture = g_device.CreateTexture(&textureDescriptor);
|
||||
|
||||
const WGPUTextureViewDescriptor viewDescriptor{
|
||||
.dimension = WGPUTextureViewDimension_2D,
|
||||
const wgpu::TextureViewDescriptor viewDescriptor{
|
||||
.label = "Depth texture view",
|
||||
.dimension = wgpu::TextureViewDimension::e2D,
|
||||
.mipLevelCount = WGPU_MIP_LEVEL_COUNT_UNDEFINED,
|
||||
.arrayLayerCount = WGPU_ARRAY_LAYER_COUNT_UNDEFINED,
|
||||
};
|
||||
auto view = wgpuTextureCreateView(texture, &viewDescriptor);
|
||||
auto view = texture.CreateView(&viewDescriptor);
|
||||
|
||||
const WGPUSamplerDescriptor samplerDescriptor{
|
||||
const wgpu::SamplerDescriptor samplerDescriptor{
|
||||
.label = "Depth sampler",
|
||||
.addressModeU = WGPUAddressMode_ClampToEdge,
|
||||
.addressModeV = WGPUAddressMode_ClampToEdge,
|
||||
.addressModeW = WGPUAddressMode_ClampToEdge,
|
||||
.magFilter = WGPUFilterMode_Linear,
|
||||
.minFilter = WGPUFilterMode_Linear,
|
||||
.mipmapFilter = WGPUFilterMode_Linear,
|
||||
.addressModeU = wgpu::AddressMode::ClampToEdge,
|
||||
.addressModeV = wgpu::AddressMode::ClampToEdge,
|
||||
.addressModeW = wgpu::AddressMode::ClampToEdge,
|
||||
.magFilter = wgpu::FilterMode::Linear,
|
||||
.minFilter = wgpu::FilterMode::Linear,
|
||||
.mipmapFilter = wgpu::FilterMode::Linear,
|
||||
.lodMinClamp = 0.f,
|
||||
.lodMaxClamp = 1000.f,
|
||||
.maxAnisotropy = 1,
|
||||
};
|
||||
auto sampler = wgpuDeviceCreateSampler(g_device, &samplerDescriptor);
|
||||
auto sampler = g_device.CreateSampler(&samplerDescriptor);
|
||||
|
||||
return {
|
||||
.texture{texture},
|
||||
.view{view},
|
||||
.texture = std::move(texture),
|
||||
.view = std::move(view),
|
||||
.size = size,
|
||||
.format = format,
|
||||
.sampler{sampler},
|
||||
.sampler = std::move(sampler),
|
||||
};
|
||||
}
|
||||
|
||||
void create_copy_pipeline() {
|
||||
WGPUShaderModuleWGSLDescriptor sourceDescriptor{
|
||||
.chain = {.sType = WGPUSType_ShaderModuleWGSLDescriptor},
|
||||
.source = R"""(
|
||||
wgpu::ShaderModuleWGSLDescriptor sourceDescriptor{};
|
||||
sourceDescriptor.source = R"""(
|
||||
@group(0) @binding(0)
|
||||
var efb_sampler: sampler;
|
||||
@group(0) @binding(1)
|
||||
@@ -173,179 +182,237 @@ fn vs_main(@builtin(vertex_index) vtxIdx: u32) -> VertexOutput {
|
||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
return textureSample(efb_texture, efb_sampler, in.uv);
|
||||
}
|
||||
)""",
|
||||
};
|
||||
const WGPUShaderModuleDescriptor moduleDescriptor{
|
||||
.nextInChain = &sourceDescriptor.chain,
|
||||
)""";
|
||||
const wgpu::ShaderModuleDescriptor moduleDescriptor{
|
||||
.nextInChain = &sourceDescriptor,
|
||||
.label = "XFB Copy Module",
|
||||
};
|
||||
auto module = wgpuDeviceCreateShaderModule(g_device, &moduleDescriptor);
|
||||
const std::array colorTargets{WGPUColorTargetState{
|
||||
.format = g_graphicsConfig.colorFormat,
|
||||
.writeMask = WGPUColorWriteMask_All,
|
||||
auto module = g_device.CreateShaderModule(&moduleDescriptor);
|
||||
const std::array colorTargets{wgpu::ColorTargetState{
|
||||
.format = g_graphicsConfig.swapChainDescriptor.format,
|
||||
.writeMask = wgpu::ColorWriteMask::All,
|
||||
}};
|
||||
const WGPUFragmentState fragmentState{
|
||||
const wgpu::FragmentState fragmentState{
|
||||
.module = module,
|
||||
.entryPoint = "fs_main",
|
||||
.targetCount = colorTargets.size(),
|
||||
.targets = colorTargets.data(),
|
||||
};
|
||||
const std::array bindGroupLayoutEntries{
|
||||
WGPUBindGroupLayoutEntry{
|
||||
wgpu::BindGroupLayoutEntry{
|
||||
.binding = 0,
|
||||
.visibility = WGPUShaderStage_Fragment,
|
||||
.visibility = wgpu::ShaderStage::Fragment,
|
||||
.sampler =
|
||||
WGPUSamplerBindingLayout{
|
||||
.type = WGPUSamplerBindingType_Filtering,
|
||||
wgpu::SamplerBindingLayout{
|
||||
.type = wgpu::SamplerBindingType::Filtering,
|
||||
},
|
||||
},
|
||||
WGPUBindGroupLayoutEntry{
|
||||
wgpu::BindGroupLayoutEntry{
|
||||
.binding = 1,
|
||||
.visibility = WGPUShaderStage_Fragment,
|
||||
.visibility = wgpu::ShaderStage::Fragment,
|
||||
.texture =
|
||||
WGPUTextureBindingLayout{
|
||||
.sampleType = WGPUTextureSampleType_Float,
|
||||
.viewDimension = WGPUTextureViewDimension_2D,
|
||||
wgpu::TextureBindingLayout{
|
||||
.sampleType = wgpu::TextureSampleType::Float,
|
||||
.viewDimension = wgpu::TextureViewDimension::e2D,
|
||||
},
|
||||
},
|
||||
};
|
||||
const WGPUBindGroupLayoutDescriptor bindGroupLayoutDescriptor{
|
||||
const wgpu::BindGroupLayoutDescriptor bindGroupLayoutDescriptor{
|
||||
.entryCount = bindGroupLayoutEntries.size(),
|
||||
.entries = bindGroupLayoutEntries.data(),
|
||||
};
|
||||
g_CopyBindGroupLayout = wgpuDeviceCreateBindGroupLayout(g_device, &bindGroupLayoutDescriptor);
|
||||
const WGPUPipelineLayoutDescriptor layoutDescriptor{
|
||||
g_CopyBindGroupLayout = g_device.CreateBindGroupLayout(&bindGroupLayoutDescriptor);
|
||||
const wgpu::PipelineLayoutDescriptor layoutDescriptor{
|
||||
.bindGroupLayoutCount = 1,
|
||||
.bindGroupLayouts = &g_CopyBindGroupLayout,
|
||||
};
|
||||
auto pipelineLayout = wgpuDeviceCreatePipelineLayout(g_device, &layoutDescriptor);
|
||||
const WGPURenderPipelineDescriptor pipelineDescriptor{
|
||||
auto pipelineLayout = g_device.CreatePipelineLayout(&layoutDescriptor);
|
||||
const wgpu::RenderPipelineDescriptor pipelineDescriptor{
|
||||
.layout = pipelineLayout,
|
||||
.vertex =
|
||||
WGPUVertexState{
|
||||
wgpu::VertexState{
|
||||
.module = module,
|
||||
.entryPoint = "vs_main",
|
||||
},
|
||||
.primitive =
|
||||
WGPUPrimitiveState{
|
||||
.topology = WGPUPrimitiveTopology_TriangleList,
|
||||
wgpu::PrimitiveState{
|
||||
.topology = wgpu::PrimitiveTopology::TriangleList,
|
||||
},
|
||||
.multisample =
|
||||
WGPUMultisampleState{
|
||||
wgpu::MultisampleState{
|
||||
.count = 1,
|
||||
.mask = UINT32_MAX,
|
||||
},
|
||||
.fragment = &fragmentState,
|
||||
};
|
||||
g_CopyPipeline = wgpuDeviceCreateRenderPipeline(g_device, &pipelineDescriptor);
|
||||
wgpuPipelineLayoutRelease(pipelineLayout);
|
||||
g_CopyPipeline = g_device.CreateRenderPipeline(&pipelineDescriptor);
|
||||
}
|
||||
|
||||
void create_copy_bind_group() {
|
||||
const std::array bindGroupEntries{
|
||||
WGPUBindGroupEntry{
|
||||
wgpu::BindGroupEntry{
|
||||
.binding = 0,
|
||||
.sampler = g_graphicsConfig.msaaSamples > 1 ? g_frameBufferResolved.sampler : g_frameBuffer.sampler,
|
||||
},
|
||||
WGPUBindGroupEntry{
|
||||
wgpu::BindGroupEntry{
|
||||
.binding = 1,
|
||||
.textureView = g_graphicsConfig.msaaSamples > 1 ? g_frameBufferResolved.view : g_frameBuffer.view,
|
||||
},
|
||||
};
|
||||
const WGPUBindGroupDescriptor bindGroupDescriptor{
|
||||
const wgpu::BindGroupDescriptor bindGroupDescriptor{
|
||||
.layout = g_CopyBindGroupLayout,
|
||||
.entryCount = bindGroupEntries.size(),
|
||||
.entries = bindGroupEntries.data(),
|
||||
};
|
||||
g_CopyBindGroup = wgpuDeviceCreateBindGroup(g_device, &bindGroupDescriptor);
|
||||
g_CopyBindGroup = g_device.CreateBindGroup(&bindGroupDescriptor);
|
||||
}
|
||||
|
||||
static void error_callback(WGPUErrorType type, char const* message, void* userdata) {
|
||||
Log.report(LOG_FATAL, FMT_STRING("Dawn error {}: {}"), magic_enum::enum_name(static_cast<WGPUErrorType>(type)),
|
||||
Log.report(LOG_FATAL, FMT_STRING("WebGPU error {}: {}"), magic_enum::enum_name(static_cast<WGPUErrorType>(type)),
|
||||
message);
|
||||
}
|
||||
|
||||
#ifndef WEBGPU_DAWN
|
||||
static void adapter_callback(WGPURequestAdapterStatus status, WGPUAdapter adapter, char const* message,
|
||||
void* userdata) {
|
||||
if (status == WGPURequestAdapterStatus_Success) {
|
||||
g_adapter = wgpu::Adapter::Acquire(adapter);
|
||||
} else {
|
||||
Log.report(LOG_WARNING, FMT_STRING("Adapter request failed with message: {}"), message);
|
||||
}
|
||||
*static_cast<bool*>(userdata) = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void device_callback(WGPURequestDeviceStatus status, WGPUDevice device, char const* message, void* userdata) {
|
||||
if (status == WGPURequestDeviceStatus_Success) {
|
||||
g_device = device;
|
||||
g_device = wgpu::Device::Acquire(device);
|
||||
} else {
|
||||
Log.report(LOG_WARNING, FMT_STRING("Device request failed with message: {}"), message);
|
||||
}
|
||||
*static_cast<bool*>(userdata) = true;
|
||||
}
|
||||
|
||||
static WGPUBackendType to_wgpu_backend(AuroraBackend backend) {
|
||||
static wgpu::BackendType to_wgpu_backend(AuroraBackend backend) {
|
||||
switch (backend) {
|
||||
case BACKEND_WEBGPU:
|
||||
return WGPUBackendType_WebGPU;
|
||||
return wgpu::BackendType::WebGPU;
|
||||
case BACKEND_D3D12:
|
||||
return WGPUBackendType_D3D12;
|
||||
return wgpu::BackendType::D3D12;
|
||||
case BACKEND_METAL:
|
||||
return WGPUBackendType_Metal;
|
||||
return wgpu::BackendType::Metal;
|
||||
case BACKEND_VULKAN:
|
||||
return WGPUBackendType_Vulkan;
|
||||
return wgpu::BackendType::Vulkan;
|
||||
case BACKEND_OPENGL:
|
||||
return WGPUBackendType_OpenGL;
|
||||
return wgpu::BackendType::OpenGL;
|
||||
case BACKEND_OPENGLES:
|
||||
return WGPUBackendType_OpenGLES;
|
||||
return wgpu::BackendType::OpenGLES;
|
||||
default:
|
||||
return WGPUBackendType_Null;
|
||||
return wgpu::BackendType::Null;
|
||||
}
|
||||
}
|
||||
|
||||
bool initialize(AuroraBackend auroraBackend) {
|
||||
if (!g_Instance) {
|
||||
#ifdef WEBGPU_DAWN
|
||||
if (!g_dawnInstance) {
|
||||
Log.report(LOG_INFO, FMT_STRING("Creating Dawn instance"));
|
||||
g_Instance = std::make_unique<dawn::native::Instance>();
|
||||
g_dawnInstance = std::make_unique<dawn::native::Instance>();
|
||||
}
|
||||
WGPUBackendType backend = to_wgpu_backend(auroraBackend);
|
||||
#else
|
||||
if (!g_instance) {
|
||||
const wgpu::InstanceDescriptor instanceDescriptor{};
|
||||
g_instance = {}; // TODO use wgpuCreateInstance when supported
|
||||
}
|
||||
#endif
|
||||
wgpu::BackendType backend = to_wgpu_backend(auroraBackend);
|
||||
#ifdef EMSCRIPTEN
|
||||
if (backend != wgpu::BackendType::WebGPU) {
|
||||
Log.report(LOG_WARNING, FMT_STRING("Backend type {} unsupported"), magic_enum::enum_name(backend));
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
Log.report(LOG_INFO, FMT_STRING("Attempting to initialize {}"), magic_enum::enum_name(backend));
|
||||
#if 0
|
||||
// D3D12's debug layer is very slow
|
||||
g_Instance->EnableBackendValidation(backend != WGPUBackendType::D3D12);
|
||||
g_dawnInstance->EnableBackendValidation(backend != WGPUBackendType::D3D12);
|
||||
#endif
|
||||
|
||||
#ifdef WEBGPU_DAWN
|
||||
SDL_Window* window = window::get_sdl_window();
|
||||
if (!utils::DiscoverAdapter(g_Instance.get(), window, backend)) {
|
||||
if (!utils::DiscoverAdapter(g_dawnInstance.get(), window, backend)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<dawn::native::Adapter> adapters = g_Instance->GetAdapters();
|
||||
std::vector<dawn::native::Adapter> adapters = g_dawnInstance->GetAdapters();
|
||||
std::sort(adapters.begin(), adapters.end(), [&](const auto& a, const auto& b) {
|
||||
WGPUAdapterProperties propertiesA;
|
||||
WGPUAdapterProperties propertiesB;
|
||||
wgpu::AdapterProperties propertiesA;
|
||||
wgpu::AdapterProperties propertiesB;
|
||||
a.GetProperties(&propertiesA);
|
||||
b.GetProperties(&propertiesB);
|
||||
constexpr std::array PreferredTypeOrder{
|
||||
WGPUAdapterType_DiscreteGPU,
|
||||
WGPUAdapterType_IntegratedGPU,
|
||||
WGPUAdapterType_CPU,
|
||||
wgpu::AdapterType::DiscreteGPU,
|
||||
wgpu::AdapterType::IntegratedGPU,
|
||||
wgpu::AdapterType::CPU,
|
||||
};
|
||||
const auto typeItA = std::find(PreferredTypeOrder.begin(), PreferredTypeOrder.end(), propertiesA.adapterType);
|
||||
const auto typeItB = std::find(PreferredTypeOrder.begin(), PreferredTypeOrder.end(), propertiesB.adapterType);
|
||||
return typeItA < typeItB;
|
||||
});
|
||||
const auto adapterIt = std::find_if(adapters.begin(), adapters.end(), [=](const auto& adapter) -> bool {
|
||||
WGPUAdapterProperties properties;
|
||||
wgpu::AdapterProperties properties;
|
||||
adapter.GetProperties(&properties);
|
||||
return properties.backendType == backend;
|
||||
});
|
||||
if (adapterIt == adapters.end()) {
|
||||
return false;
|
||||
}
|
||||
g_Adapter = *adapterIt;
|
||||
g_adapter = *adapterIt;
|
||||
}
|
||||
g_Adapter.GetProperties(&g_AdapterProperties);
|
||||
g_backendType = g_AdapterProperties.backendType;
|
||||
#else
|
||||
const WGPUSurfaceDescriptorFromCanvasHTMLSelector canvasDescriptor{
|
||||
.chain = {.sType = WGPUSType_SurfaceDescriptorFromCanvasHTMLSelector},
|
||||
.selector = "#canvas",
|
||||
};
|
||||
const WGPUSurfaceDescriptor surfaceDescriptor{
|
||||
.nextInChain = &canvasDescriptor.chain,
|
||||
.label = "Surface",
|
||||
};
|
||||
g_surface = wgpu::Surface::Acquire(wgpuInstanceCreateSurface(g_instance.Get(), &surfaceDescriptor));
|
||||
if (!g_surface) {
|
||||
Log.report(LOG_FATAL, FMT_STRING("Failed to initialize surface"));
|
||||
}
|
||||
const WGPURequestAdapterOptions options{
|
||||
.compatibleSurface = g_surface.Get(),
|
||||
.powerPreference = WGPUPowerPreference_HighPerformance,
|
||||
.forceFallbackAdapter = false,
|
||||
};
|
||||
bool adapterCallbackRecieved = false;
|
||||
wgpuInstanceRequestAdapter(g_instance.Get(), &options, adapter_callback, &adapterCallbackRecieved);
|
||||
while (!adapterCallbackRecieved) {
|
||||
emscripten_log(EM_LOG_CONSOLE, "Waiting for adapter...\n");
|
||||
emscripten_sleep(100);
|
||||
}
|
||||
#endif
|
||||
g_adapter.GetProperties(&g_adapterProperties);
|
||||
g_backendType = g_adapterProperties.backendType;
|
||||
const auto backendName = magic_enum::enum_name(g_backendType);
|
||||
const char* adapterName = g_adapterProperties.name;
|
||||
if (adapterName == nullptr) {
|
||||
adapterName = "Unknown";
|
||||
}
|
||||
const char* driverDescription = g_adapterProperties.driverDescription;
|
||||
if (driverDescription == nullptr) {
|
||||
driverDescription = "Unknown";
|
||||
}
|
||||
Log.report(LOG_INFO, FMT_STRING("Graphics adapter information\n API: {}\n Device: {} ({})\n Driver: {}"),
|
||||
backendName, g_AdapterProperties.name, magic_enum::enum_name(g_AdapterProperties.adapterType),
|
||||
g_AdapterProperties.driverDescription);
|
||||
backendName, adapterName, magic_enum::enum_name(g_adapterProperties.adapterType), driverDescription);
|
||||
|
||||
{
|
||||
// TODO: emscripten doesn't implement wgpuAdapterGetLimits
|
||||
#ifdef WEBGPU_DAWN
|
||||
WGPUSupportedLimits supportedLimits{};
|
||||
g_Adapter.GetLimits(&supportedLimits);
|
||||
const WGPURequiredLimits requiredLimits{
|
||||
g_adapter.GetLimits(&supportedLimits);
|
||||
const wgpu::RequiredLimits requiredLimits{
|
||||
.limits =
|
||||
{
|
||||
// Use "best" supported alignments
|
||||
@@ -357,13 +424,27 @@ bool initialize(AuroraBackend auroraBackend) {
|
||||
: supportedLimits.limits.minStorageBufferOffsetAlignment,
|
||||
},
|
||||
};
|
||||
std::vector<WGPUFeatureName> features;
|
||||
const auto supportedFeatures = g_Adapter.GetSupportedFeatures();
|
||||
#endif
|
||||
std::vector<wgpu::FeatureName> features;
|
||||
#ifdef WEBGPU_DAWN
|
||||
const auto supportedFeatures = g_adapter.GetSupportedFeatures();
|
||||
for (const auto* const feature : supportedFeatures) {
|
||||
if (strcmp(feature, "texture-compression-bc") == 0) {
|
||||
features.push_back(WGPUFeatureName_TextureCompressionBC);
|
||||
features.push_back(wgpu::FeatureName::TextureCompressionBC);
|
||||
}
|
||||
}
|
||||
#else
|
||||
std::vector<wgpu::FeatureName> supportedFeatures;
|
||||
size_t featureCount = g_adapter.EnumerateFeatures(nullptr);
|
||||
supportedFeatures.resize(featureCount);
|
||||
g_adapter.EnumerateFeatures(supportedFeatures.data());
|
||||
for (const auto& feature : supportedFeatures) {
|
||||
if (feature == wgpu::FeatureName::TextureCompressionBC) {
|
||||
features.push_back(wgpu::FeatureName::TextureCompressionBC);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef WEBGPU_DAWN
|
||||
const std::array enableToggles {
|
||||
/* clang-format off */
|
||||
#if _WIN32
|
||||
@@ -377,87 +458,110 @@ bool initialize(AuroraBackend auroraBackend) {
|
||||
"disable_symbol_renaming",
|
||||
/* clang-format on */
|
||||
};
|
||||
const WGPUDawnTogglesDeviceDescriptor togglesDescriptor{
|
||||
.chain = {.sType = WGPUSType_DawnTogglesDeviceDescriptor},
|
||||
.forceEnabledTogglesCount = enableToggles.size(),
|
||||
.forceEnabledToggles = enableToggles.data(),
|
||||
};
|
||||
const WGPUDeviceDescriptor deviceDescriptor{
|
||||
.nextInChain = &togglesDescriptor.chain,
|
||||
wgpu::DawnTogglesDeviceDescriptor togglesDescriptor{};
|
||||
togglesDescriptor.forceEnabledTogglesCount = enableToggles.size();
|
||||
togglesDescriptor.forceEnabledToggles = enableToggles.data();
|
||||
#endif
|
||||
const wgpu::DeviceDescriptor deviceDescriptor{
|
||||
#ifdef WEBGPU_DAWN
|
||||
.nextInChain = &togglesDescriptor,
|
||||
#endif
|
||||
.requiredFeaturesCount = static_cast<uint32_t>(features.size()),
|
||||
.requiredFeatures = features.data(),
|
||||
#ifdef WEBGPU_DAWN
|
||||
.requiredLimits = &requiredLimits,
|
||||
#endif
|
||||
};
|
||||
bool deviceCallbackReceived = false;
|
||||
g_Adapter.RequestDevice(&deviceDescriptor, &device_callback, &deviceCallbackReceived);
|
||||
// while (!deviceCallbackReceived) {
|
||||
// TODO wgpuInstanceProcessEvents
|
||||
// }
|
||||
g_adapter.RequestDevice(&deviceDescriptor, device_callback, &deviceCallbackReceived);
|
||||
#ifdef EMSCRIPTEN
|
||||
while (!deviceCallbackReceived) {
|
||||
emscripten_log(EM_LOG_CONSOLE, "Waiting for device...\n");
|
||||
emscripten_sleep(100);
|
||||
}
|
||||
#endif
|
||||
if (!g_device) {
|
||||
return false;
|
||||
}
|
||||
wgpuDeviceSetUncapturedErrorCallback(g_device, &error_callback, nullptr);
|
||||
g_device.SetUncapturedErrorCallback(&error_callback, nullptr);
|
||||
}
|
||||
wgpuDeviceSetDeviceLostCallback(g_device, nullptr, nullptr);
|
||||
g_queue = wgpuDeviceGetQueue(g_device);
|
||||
g_device.SetDeviceLostCallback(nullptr, nullptr);
|
||||
g_queue = g_device.GetQueue();
|
||||
|
||||
g_BackendBinding = std::unique_ptr<utils::BackendBinding>(utils::CreateBinding(g_backendType, window, g_device));
|
||||
if (!g_BackendBinding) {
|
||||
#if WEBGPU_DAWN
|
||||
g_backendBinding =
|
||||
std::unique_ptr<utils::BackendBinding>(utils::CreateBinding(g_backendType, window, g_device.Get()));
|
||||
if (!g_backendBinding) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto swapChainFormat = static_cast<WGPUTextureFormat>(g_BackendBinding->GetPreferredSwapChainTextureFormat());
|
||||
if (swapChainFormat == WGPUTextureFormat_RGBA8UnormSrgb) {
|
||||
swapChainFormat = WGPUTextureFormat_RGBA8Unorm;
|
||||
} else if (swapChainFormat == WGPUTextureFormat_BGRA8UnormSrgb) {
|
||||
swapChainFormat = WGPUTextureFormat_BGRA8Unorm;
|
||||
auto swapChainFormat = static_cast<wgpu::TextureFormat>(g_backendBinding->GetPreferredSwapChainTextureFormat());
|
||||
#else
|
||||
auto swapChainFormat = g_surface.GetPreferredFormat(g_adapter);
|
||||
#endif
|
||||
if (swapChainFormat == wgpu::TextureFormat::RGBA8UnormSrgb) {
|
||||
swapChainFormat = wgpu::TextureFormat::RGBA8Unorm;
|
||||
} else if (swapChainFormat == wgpu::TextureFormat::BGRA8UnormSrgb) {
|
||||
swapChainFormat = wgpu::TextureFormat::BGRA8Unorm;
|
||||
}
|
||||
Log.report(LOG_INFO, FMT_STRING("Using swapchain format {}"), magic_enum::enum_name(swapChainFormat));
|
||||
{
|
||||
const WGPUSwapChainDescriptor descriptor{
|
||||
.format = swapChainFormat,
|
||||
.implementation = g_BackendBinding->GetSwapChainImplementation(),
|
||||
};
|
||||
g_swapChain = wgpuDeviceCreateSwapChain(g_device, nullptr, &descriptor);
|
||||
}
|
||||
{
|
||||
const auto size = window::get_window_size();
|
||||
g_graphicsConfig = GraphicsConfig{
|
||||
.width = size.fb_width,
|
||||
.height = size.fb_height,
|
||||
.colorFormat = swapChainFormat,
|
||||
.depthFormat = WGPUTextureFormat_Depth32Float,
|
||||
.msaaSamples = g_config.msaa,
|
||||
.textureAnisotropy = g_config.maxTextureAnisotropy,
|
||||
};
|
||||
create_copy_pipeline();
|
||||
resize_swapchain(size.fb_width, size.fb_height, true);
|
||||
// g_windowSize = size;
|
||||
}
|
||||
const auto size = window::get_window_size();
|
||||
g_graphicsConfig = GraphicsConfig{
|
||||
.swapChainDescriptor =
|
||||
wgpu::SwapChainDescriptor{
|
||||
.usage = wgpu::TextureUsage::RenderAttachment,
|
||||
.format = swapChainFormat,
|
||||
.width = size.fb_width,
|
||||
.height = size.fb_height,
|
||||
.presentMode = wgpu::PresentMode::Fifo,
|
||||
#ifdef WEBGPU_DAWN
|
||||
.implementation = g_backendBinding->GetSwapChainImplementation(),
|
||||
#endif
|
||||
},
|
||||
.depthFormat = wgpu::TextureFormat::Depth32Float,
|
||||
.msaaSamples = g_config.msaa,
|
||||
.textureAnisotropy = g_config.maxTextureAnisotropy,
|
||||
};
|
||||
create_copy_pipeline();
|
||||
resize_swapchain(size.fb_width, size.fb_height, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
wgpuBindGroupLayoutRelease(g_CopyBindGroupLayout);
|
||||
wgpuRenderPipelineRelease(g_CopyPipeline);
|
||||
wgpuBindGroupRelease(g_CopyBindGroup);
|
||||
g_CopyBindGroupLayout = {};
|
||||
g_CopyPipeline = {};
|
||||
g_CopyBindGroup = {};
|
||||
g_frameBuffer = {};
|
||||
g_frameBufferResolved = {};
|
||||
g_depthBuffer = {};
|
||||
wgpuSwapChainRelease(g_swapChain);
|
||||
wgpuQueueRelease(g_queue);
|
||||
g_BackendBinding.reset();
|
||||
wgpuDeviceDestroy(g_device);
|
||||
g_Instance.reset();
|
||||
wgpuSwapChainRelease(g_swapChain.Release());
|
||||
wgpuQueueRelease(g_queue.Release());
|
||||
wgpuDeviceDestroy(g_device.Release());
|
||||
g_adapter = {};
|
||||
#ifdef WEBGPU_DAWN
|
||||
g_backendBinding.reset();
|
||||
g_dawnInstance.reset();
|
||||
#else
|
||||
g_surface = {};
|
||||
g_instance = {};
|
||||
#endif
|
||||
}
|
||||
|
||||
void resize_swapchain(uint32_t width, uint32_t height, bool force) {
|
||||
if (!force && g_graphicsConfig.width == width && g_graphicsConfig.height == height) {
|
||||
if (!force && g_graphicsConfig.swapChainDescriptor.width == width &&
|
||||
g_graphicsConfig.swapChainDescriptor.height == height) {
|
||||
return;
|
||||
}
|
||||
g_graphicsConfig.width = width;
|
||||
g_graphicsConfig.height = height;
|
||||
wgpuSwapChainConfigure(g_swapChain, g_graphicsConfig.colorFormat, WGPUTextureUsage_RenderAttachment, width, height);
|
||||
g_graphicsConfig.swapChainDescriptor.width = width;
|
||||
g_graphicsConfig.swapChainDescriptor.height = height;
|
||||
#ifdef WEBGPU_DAWN
|
||||
if (!g_swapChain) {
|
||||
g_swapChain = g_device.CreateSwapChain(g_surface, &g_graphicsConfig.swapChainDescriptor);
|
||||
}
|
||||
g_swapChain.Configure(g_graphicsConfig.swapChainDescriptor.format, g_graphicsConfig.swapChainDescriptor.usage, width,
|
||||
height);
|
||||
#else
|
||||
g_swapChain = g_device.CreateSwapChain(g_surface, &g_graphicsConfig.swapChainDescriptor);
|
||||
#endif
|
||||
g_frameBuffer = create_render_texture(true);
|
||||
g_frameBufferResolved = create_render_texture(false);
|
||||
g_depthBuffer = create_depth_texture();
|
||||
|
||||
@@ -11,75 +11,30 @@ struct SDL_Window;
|
||||
|
||||
namespace aurora::webgpu {
|
||||
struct GraphicsConfig {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
WGPUTextureFormat colorFormat;
|
||||
WGPUTextureFormat depthFormat;
|
||||
wgpu::SwapChainDescriptor swapChainDescriptor;
|
||||
wgpu::TextureFormat depthFormat;
|
||||
uint32_t msaaSamples;
|
||||
uint16_t textureAnisotropy;
|
||||
};
|
||||
struct TextureWithSampler {
|
||||
wgpu::Texture texture;
|
||||
wgpu::TextureView view;
|
||||
WGPUExtent3D size;
|
||||
WGPUTextureFormat format;
|
||||
wgpu::Extent3D size;
|
||||
wgpu::TextureFormat format;
|
||||
wgpu::Sampler sampler;
|
||||
|
||||
// TextureWithSampler() = default;
|
||||
// TextureWithSampler(WGPUTexture texture, WGPUTextureView view, WGPUExtent3D size, WGPUTextureFormat format,
|
||||
// WGPUSampler sampler) noexcept
|
||||
// : texture(texture), view(view), size(size), format(format), sampler(sampler) {}
|
||||
// TextureWithSampler(const TextureWithSampler& rhs) noexcept
|
||||
// : texture(rhs.texture), view(rhs.view), size(rhs.size), format(rhs.format), sampler(rhs.sampler) {
|
||||
// wgpuTextureReference(texture);
|
||||
// wgpuTextureViewReference(view);
|
||||
// wgpuSamplerReference(sampler);
|
||||
// }
|
||||
// TextureWithSampler(TextureWithSampler&& rhs) noexcept
|
||||
// : texture(rhs.texture), view(rhs.view), size(rhs.size), format(rhs.format), sampler(rhs.sampler) {
|
||||
// rhs.texture = nullptr;
|
||||
// rhs.view = nullptr;
|
||||
// rhs.sampler = nullptr;
|
||||
// }
|
||||
// ~TextureWithSampler() { reset(); }
|
||||
// TextureWithSampler& operator=(const TextureWithSampler& rhs) noexcept {
|
||||
// reset();
|
||||
// texture = rhs.texture;
|
||||
// view = rhs.view;
|
||||
// size = rhs.size;
|
||||
// format = rhs.format;
|
||||
// sampler = rhs.sampler;
|
||||
// wgpuTextureReference(texture);
|
||||
// wgpuTextureViewReference(view);
|
||||
// wgpuSamplerReference(sampler);
|
||||
// return *this;
|
||||
// }
|
||||
// void reset() {
|
||||
// if (texture != nullptr) {
|
||||
// wgpuTextureRelease(texture);
|
||||
// texture = nullptr;
|
||||
// }
|
||||
// if (view != nullptr) {
|
||||
// wgpuTextureViewRelease(view);
|
||||
// view = nullptr;
|
||||
// }
|
||||
// if (sampler != nullptr) {
|
||||
// wgpuSamplerRelease(sampler);
|
||||
// sampler = nullptr;
|
||||
// }
|
||||
// }
|
||||
};
|
||||
|
||||
extern WGPUDevice g_device;
|
||||
extern WGPUQueue g_queue;
|
||||
extern WGPUSwapChain g_swapChain;
|
||||
extern WGPUBackendType g_backendType;
|
||||
extern wgpu::Device g_device;
|
||||
extern wgpu::Queue g_queue;
|
||||
extern wgpu::SwapChain g_swapChain;
|
||||
extern wgpu::BackendType g_backendType;
|
||||
extern GraphicsConfig g_graphicsConfig;
|
||||
extern TextureWithSampler g_frameBuffer;
|
||||
extern TextureWithSampler g_frameBufferResolved;
|
||||
extern TextureWithSampler g_depthBuffer;
|
||||
extern WGPURenderPipeline g_CopyPipeline;
|
||||
extern WGPUBindGroup g_CopyBindGroup;
|
||||
extern wgpu::RenderPipeline g_CopyPipeline;
|
||||
extern wgpu::BindGroup g_CopyBindGroup;
|
||||
extern wgpu::Instance g_instance;
|
||||
|
||||
bool initialize(AuroraBackend backend);
|
||||
void shutdown();
|
||||
|
||||
@@ -1,102 +1,4 @@
|
||||
#include <webgpu/webgpu.h>
|
||||
|
||||
namespace wgpu {
|
||||
template <typename Derived, typename CType>
|
||||
struct ObjectBase {
|
||||
ObjectBase() = default;
|
||||
ObjectBase(CType handle) : mHandle(handle) {}
|
||||
~ObjectBase() { Reset(); }
|
||||
|
||||
ObjectBase(ObjectBase const& other) : ObjectBase(other.Get()) {}
|
||||
Derived& operator=(ObjectBase const& other) {
|
||||
if (&other != this) {
|
||||
if (mHandle) {
|
||||
Derived::WGPURelease(mHandle);
|
||||
}
|
||||
mHandle = other.mHandle;
|
||||
if (mHandle) {
|
||||
Derived::WGPUReference(mHandle);
|
||||
}
|
||||
}
|
||||
return static_cast<Derived&>(*this);
|
||||
}
|
||||
|
||||
ObjectBase(ObjectBase&& other) noexcept {
|
||||
mHandle = other.mHandle;
|
||||
other.mHandle = 0;
|
||||
}
|
||||
Derived& operator=(ObjectBase&& other) noexcept {
|
||||
if (&other != this) {
|
||||
if (mHandle) {
|
||||
Derived::WGPURelease(mHandle);
|
||||
}
|
||||
mHandle = other.mHandle;
|
||||
other.mHandle = nullptr;
|
||||
}
|
||||
return static_cast<Derived&>(*this);
|
||||
}
|
||||
|
||||
ObjectBase(std::nullptr_t) {}
|
||||
Derived& operator=(std::nullptr_t) {
|
||||
if (mHandle != nullptr) {
|
||||
Derived::WGPURelease(mHandle);
|
||||
mHandle = nullptr;
|
||||
}
|
||||
return static_cast<Derived&>(*this);
|
||||
}
|
||||
|
||||
bool operator==(std::nullptr_t) const { return mHandle == nullptr; }
|
||||
bool operator!=(std::nullptr_t) const { return mHandle != nullptr; }
|
||||
|
||||
explicit operator bool() const { return mHandle != nullptr; }
|
||||
operator CType() { return mHandle; }
|
||||
[[nodiscard]] CType Get() const { return mHandle; }
|
||||
CType Release() {
|
||||
CType result = mHandle;
|
||||
mHandle = 0;
|
||||
return result;
|
||||
}
|
||||
void Reset() {
|
||||
if (mHandle) {
|
||||
Derived::WGPURelease(mHandle);
|
||||
mHandle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
CType mHandle = nullptr;
|
||||
};
|
||||
|
||||
class Texture : public ObjectBase<Texture, WGPUTexture> {
|
||||
public:
|
||||
using ObjectBase::ObjectBase;
|
||||
using ObjectBase::operator=;
|
||||
|
||||
private:
|
||||
friend ObjectBase<Texture, WGPUTexture>;
|
||||
static void WGPUReference(WGPUTexture handle) { wgpuTextureReference(handle); }
|
||||
static void WGPURelease(WGPUTexture handle) { wgpuTextureRelease(handle); }
|
||||
};
|
||||
|
||||
class TextureView : public ObjectBase<TextureView, WGPUTextureView> {
|
||||
public:
|
||||
using ObjectBase::ObjectBase;
|
||||
using ObjectBase::operator=;
|
||||
|
||||
private:
|
||||
friend ObjectBase<TextureView, WGPUTextureView>;
|
||||
static void WGPUReference(WGPUTextureView handle) { wgpuTextureViewReference(handle); }
|
||||
static void WGPURelease(WGPUTextureView handle) { wgpuTextureViewRelease(handle); }
|
||||
};
|
||||
|
||||
class Sampler : public ObjectBase<Sampler, WGPUSampler> {
|
||||
public:
|
||||
using ObjectBase::ObjectBase;
|
||||
using ObjectBase::operator=;
|
||||
|
||||
private:
|
||||
friend ObjectBase<Sampler, WGPUSampler>;
|
||||
static void WGPUReference(WGPUSampler handle) { wgpuSamplerReference(handle); }
|
||||
static void WGPURelease(WGPUSampler handle) { wgpuSamplerRelease(handle); }
|
||||
};
|
||||
} // namespace wgpu
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
#ifdef EMSCRIPTEN
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user