WebGPU: enable emscripten support (#3780)

This commit is contained in:
trollyjolly
2026-06-25 02:12:27 -03:00
committed by GitHub
parent bb86d0a221
commit 6777023bfe
4 changed files with 96 additions and 15 deletions

View File

@@ -27,10 +27,6 @@
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifdef __EMSCRIPTEN__
#error "Do not include this header. Emscripten already provides headers needed for WebGPU."
#endif
#ifndef WEBGPU_H_
#define WEBGPU_H_

View File

@@ -90,6 +90,7 @@
#ifndef BGFX_PLATFORM_SUPPORTS_WGSL
# define BGFX_PLATFORM_SUPPORTS_WGSL (0 \
|| BX_PLATFORM_EMSCRIPTEN \
|| BX_PLATFORM_LINUX \
|| BX_PLATFORM_WINDOWS \
|| BX_PLATFORM_OSX \

View File

@@ -887,6 +887,12 @@ WGPU_IMPORT
renderCtx->m_device = _device;
}
WGPUWaitStatus waitForFuture(WGPUFutureWaitInfo& _fwi)
{
wgpuInstanceProcessEvents(m_instance);
return wgpuInstanceWaitAny(m_instance, 1, &_fwi, UINT64_MAX);
}
bool init(const Init& _init)
{
struct ErrorState
@@ -923,6 +929,7 @@ WGPU_IMPORT
bool imported = true;
#if !BX_PLATFORM_EMSCRIPTEN
m_webgpuDll = bx::dlopen(
#if BX_PLATFORM_WINDOWS
"webgpu_dawn.dll"
@@ -970,12 +977,21 @@ WGPU_IMPORT
goto error;
}
#else // BX_PLATFORM_EMSCRIPTEN
BX_TRACE("Emscripten: Using statically linked WebGPU.");
m_webgpuDll = NULL;
errorState = ErrorState::LoadedWebGPU;
#endif // !BX_PLATFORM_EMSCRIPTEN
{
{
WGPUInstanceFeatureName requiredFeatures[] =
{
WGPUInstanceFeatureName_TimedWaitAny,
#if !BX_PLATFORM_EMSCRIPTEN
WGPUInstanceFeatureName_ShaderSourceSPIRV,
#endif // !BX_PLATFORM_EMSCRIPTEN
};
WGPUInstanceDescriptor instanceDesc =
@@ -1021,7 +1037,7 @@ WGPU_IMPORT
.completed = false,
};
WGPUWaitStatus waitStatus = wgpuInstanceWaitAny(m_instance, 1, &fwi, UINT64_MAX);
WGPUWaitStatus waitStatus = waitForFuture(fwi);
if (WGPUWaitStatus_Success != waitStatus
|| NULL == m_adapter)
@@ -1157,15 +1173,18 @@ WGPU_IMPORT
if (WGPUStatus_Success == status)
{
#if !BX_PLATFORM_EMSCRIPTEN
requiredLimits.maxComputeWorkgroupSizeX = 1024;
requiredLimits.maxComputeWorkgroupSizeY = 1024;
requiredLimits.maxComputeWorkgroupSizeZ = 64;
#endif // !BX_PLATFORM_EMSCRIPTEN
}
static constexpr uint32_t kMaxEnabledTogles = 10;
const char* enabledToggles[kMaxEnabledTogles];
uint32_t enabledTogglesCount = 0;
#if !BX_PLATFORM_EMSCRIPTEN
enabledToggles[enabledTogglesCount++] = "allow_unsafe_apis"; // TimestampWrite requires this.
if (_init.debug)
@@ -1207,10 +1226,15 @@ WGPU_IMPORT
.disabledToggleCount = 0,
.disabledToggles = NULL,
};
#endif // !BX_PLATFORM_EMSCRIPTEN
WGPUDeviceDescriptor deviceDesc =
{
#if !BX_PLATFORM_EMSCRIPTEN
.nextInChain = &dawnTogglesDescriptor.chain,
#else
.nextInChain = NULL,
#endif
.label = WGPU_STRING_VIEW_INIT,
.requiredFeatureCount = requiredFeatureCount,
.requiredFeatures = requiredFeatures,
@@ -1219,7 +1243,7 @@ WGPU_IMPORT
.deviceLostCallbackInfo =
{
.nextInChain = NULL,
.mode = WGPUCallbackMode_WaitAnyOnly,
.mode = WGPUCallbackMode_AllowSpontaneous,
.callback = deviceLostCb,
.userdata1 = this,
.userdata2 = NULL,
@@ -1246,7 +1270,7 @@ WGPU_IMPORT
.completed = false,
};
WGPUWaitStatus waitStatus = wgpuInstanceWaitAny(m_instance, 1, &fwi, UINT64_MAX);
WGPUWaitStatus waitStatus = waitForFuture(fwi);
if (WGPUWaitStatus_Success != waitStatus
|| NULL == m_device)
@@ -4337,7 +4361,7 @@ WGPU_IMPORT
{
m_resolution = _resolution;
WGPUSurfaceCapabilities surfaceCaps;
WGPUSurfaceCapabilities surfaceCaps = WGPU_SURFACE_CAPABILITIES_INIT;
WGPUStatus status = WGPU_CHECK(wgpuSurfaceGetCapabilities(m_surface, s_renderWGPU->m_adapter, &surfaceCaps) );
if (WGPUStatus_Success != status)
@@ -4361,6 +4385,24 @@ m_resolution.formatColor = TextureFormat::BGRA8;
}
}
#if BX_PLATFORM_EMSCRIPTEN
if (WGPUTextureFormat_Undefined != format
&& surfaceCaps.formatCount > 0
&& format != surfaceCaps.formats[0])
{
format = surfaceCaps.formats[0];
if (WGPUTextureFormat_BGRA8Unorm == format)
{
m_resolution.formatColor = TextureFormat::BGRA8;
}
else if (WGPUTextureFormat_RGBA8Unorm == format)
{
m_resolution.formatColor = TextureFormat::RGBA8;
}
}
#endif // BX_PLATFORM_EMSCRIPTEN
BX_ASSERT(WGPUTextureFormat_Undefined != format, "SwapChain surface format is not available!");
m_surfaceConfig =
@@ -4386,6 +4428,9 @@ m_resolution.formatColor = TextureFormat::BGRA8;
const uint32_t msaa = s_msaa[(_resolution.reset&BGFX_RESET_MSAA_MASK)>>BGFX_RESET_MSAA_SHIFT];
wgpuRelease(m_depthStencilView);
wgpuRelease(m_msaaTextureView);
if (bimg::isDepth(bimg::TextureFormat::Enum(m_resolution.formatDepthStencil) ) )
{
WGPUTextureDescriptor textureDesc =
@@ -4471,6 +4516,7 @@ m_resolution.formatColor = TextureFormat::BGRA8;
};
m_msaaTextureView = WGPU_CHECK(wgpuTextureCreateView(texture, &textureViewDesc) );
wgpuRelease(texture);
}
return true;
@@ -4642,6 +4688,26 @@ m_resolution.formatColor = TextureFormat::BGRA8;
.layer = toMetalLayer(m_nwh),
};
surfaceDesc =
{
.nextInChain = &surfaceSource.chain,
.label = toWGPUStringView("SwapChainWGPU"),
};
#elif BX_PLATFORM_EMSCRIPTEN
WGPUEmscriptenSurfaceSourceCanvasHTMLSelector surfaceSource =
{
.chain =
{
.next = NULL,
.sType = WGPUSType_EmscriptenSurfaceSourceCanvasHTMLSelector,
},
.selector =
{
.data = static_cast<const char*>(m_nwh),
.length = uint32_t(bx::strLen(static_cast<const char*>(m_nwh))),
},
};
surfaceDesc =
{
.nextInChain = &surfaceSource.chain,
@@ -4659,7 +4725,9 @@ m_resolution.formatColor = TextureFormat::BGRA8;
void SwapChainWGPU::present()
{
wgpuRelease(m_textureView);
#if !BX_PLATFORM_EMSCRIPTEN
WGPU_CHECK(wgpuSurfacePresent(m_surface) );
#endif // !BX_PLATFORM_EMSCRIPTEN
WGPUSurfaceTexture surfaceTexture = WGPU_SURFACE_TEXTURE_INIT;
wgpuSurfaceGetCurrentTexture(m_surface, &surfaceTexture);
@@ -4874,30 +4942,42 @@ m_resolution.formatColor = TextureFormat::BGRA8;
WGPUCommandBuffer commandBuffer = WGPU_CHECK(wgpuCommandEncoderFinish(m_commandEncoder, NULL) );
WGPU_CHECK(wgpuQueueSubmit(m_queue, 1, &commandBuffer) );
WGPU_CHECK(wgpuQueueOnSubmittedWorkDone(
m_queue
m_future = wgpuQueueOnSubmittedWorkDone(
m_queue
, {
.nextInChain = NULL,
.mode = WGPUCallbackMode_AllowProcessEvents,
.callback = queueWorkDoneCb,
.userdata1 = (void*)uintptr_t(m_counter),
.userdata2 = NULL,
}) );
});
wgpuRelease(commandBuffer);
wgpuRelease(m_commandEncoder);
++m_counter;
#if !BX_PLATFORM_EMSCRIPTEN
WGPU_CHECK(wgpuInstanceProcessEvents(s_renderWGPU->m_instance) );
#endif // !BX_PLATFORM_EMSCRIPTEN
m_commandEncoder = WGPU_CHECK(wgpuDeviceCreateCommandEncoder(s_renderWGPU->m_device, NULL) );
}
void CommandQueueWGPU::wait()
{
#if BX_PLATFORM_EMSCRIPTEN
wgpuInstanceProcessEvents(s_renderWGPU->m_instance);
#else
while (0 < m_counter)
{
WGPU_CHECK(wgpuInstanceProcessEvents(s_renderWGPU->m_instance) );
WGPUFutureWaitInfo fwi =
{
.future = m_future,
.completed = false,
};
wgpuInstanceWaitAny(s_renderWGPU->m_instance, 1, &fwi, 1000000);
}
#endif // BX_PLATFORM_EMSCRIPTEN
}
void CommandQueueWGPU::frame()

View File

@@ -24,7 +24,9 @@
# define WGPU_CHECK(_call) _call
#endif // BGFX_CONFIG_DEBUG
#define WGPU_SKIP_DECLARATIONS
#if !BX_PLATFORM_EMSCRIPTEN
# define WGPU_SKIP_DECLARATIONS
#endif // !BX_PLATFORM_EMSCRIPTEN
#include <dawn/include/webgpu/webgpu.h>
#if USE_WEBGPU_DYNAMIC_LIB
@@ -307,11 +309,11 @@
WGPU_IGNORE_____(false, TextureGetDepthOrArrayLayers); \
WGPU_IGNORE_____(false, TextureGetDimension); \
WGPU_IGNORE_____(false, TextureGetFormat); \
WGPU_IGNORE_____(false, TextureGetHeight); \
WGPU_IMPORT_FUNC(false, TextureGetHeight); \
WGPU_IGNORE_____(false, TextureGetMipLevelCount); \
WGPU_IGNORE_____(false, TextureGetSampleCount); \
WGPU_IGNORE_____(false, TextureGetUsage); \
WGPU_IGNORE_____(false, TextureGetWidth); \
WGPU_IMPORT_FUNC(false, TextureGetWidth); \
WGPU_IGNORE_____(false, TexturePin); \
WGPU_IMPORT_FUNC(false, TextureSetLabel); \
WGPU_IGNORE_____(false, TextureUnpin); \
@@ -744,6 +746,7 @@ namespace wgpu {
: m_queue(NULL)
, m_commandEncoder(NULL)
, m_counter(0)
, m_future(WGPU_FUTURE_INIT)
{
}
@@ -767,6 +770,7 @@ namespace wgpu {
WGPUCommandEncoder m_commandEncoder;
uint32_t m_currentFrameInFlight;
uint32_t m_counter;
WGPUFuture m_future;
};
struct TimerQueryWGPU