Fixes for Web

This commit is contained in:
2026-04-03 16:45:16 +02:00
parent 93e748a981
commit 0e161c6ec1
7 changed files with 29 additions and 19 deletions
@@ -383,12 +383,17 @@ protected:
_streamingTexture->GetOwner()->GetMipData(absoluteMipIndex, data);
if (data.IsInvalid())
return Result::MissingData;
PixelFormat dataFormat = _streamingTexture->GetHeader()->Format;
// Cache data
const int32 arraySize = texture->ArraySize();
uint32 rowPitch, slicePitch;
if (!_streamingTexture->GetOwner()->GetMipDataCustomPitch(absoluteMipIndex, rowPitch, slicePitch))
texture->ComputePitch(_mipIndex, rowPitch, slicePitch);
{
int32 mipWidth, mipHeight;
texture->GetMipSize(_mipIndex, mipWidth, mipHeight);
RenderTools::ComputePitch(dataFormat, mipWidth, mipHeight, rowPitch, slicePitch);
}
_data.Link(data);
// Update all array slices
@@ -396,9 +401,9 @@ protected:
int32 dataPerSlice = data.Length() / arraySize; // In most cases it's a slice pitch, except when using transcoding (eg. Basis), then each slice has to use the same amount of memory
for (int32 arrayIndex = 0; arrayIndex < arraySize; arrayIndex++)
{
if (TextureTool::UpdateTexture(context->GPU, texture, arrayIndex, _mipIndex, Span<byte>(dataSource, dataPerSlice), rowPitch, slicePitch, _streamingTexture->GetHeader()->Format))
if (TextureTool::UpdateTexture(context->GPU, texture, arrayIndex, _mipIndex, Span<byte>(dataSource, dataPerSlice), rowPitch, slicePitch, dataFormat))
{
LOG(Error, "Cannot stream mip {} of texture {} (format: {})", _mipIndex, texture->ToString(), ScriptingEnum::ToString(_streamingTexture->GetHeader()->Format));
LOG(Error, "Cannot stream mip {} of texture {} (format: {})", _mipIndex, texture->ToString(), ScriptingEnum::ToString(dataFormat));
return Result::Failed;
}
dataSource += dataPerSlice;
@@ -1090,12 +1090,13 @@ void GPUContextWebGPU::FlushBindGroup()
{
auto descriptors = _pipelineState->BindGroupDescriptors[groupIndex];
key.Layout = _pipelineState->BindGroupLayouts[groupIndex];
if (!descriptors || !key.Layout)
continue;
// Build descriptors
uint32 dynamicOffsetsCount = 0;
BuildBindGroup(groupIndex, *descriptors, key, dynamicOffsets, dynamicOffsetsCount);
if (descriptors)
BuildBindGroup(groupIndex, *descriptors, key, dynamicOffsets, dynamicOffsetsCount);
else
key.EntriesCount = 0;
// Bind group
WGPUBindGroup bindGroup = _pipelineState->GetBindGroup(key);
@@ -302,12 +302,11 @@ bool GPUDeviceWebGPU::Init()
limits.maxComputeWorkgroupSizeY >= 256 &&
limits.maxComputeWorkgroupSizeZ >= 8 &&
limits.maxBufferSize >= 64 * 1024 * 1024; // 64MB
Limits.HasInstancing = true;
Limits.HasInstancing = features.Contains(WGPUFeatureName_PrimitiveIndex); // TODO: investigate why Firefox doesn't draw instanced draws (this check is kind of a hack)
Limits.HasDrawIndirect = true;
Limits.HasDepthAsSRV = true;
Limits.HasReadOnlyDepth = true;
Limits.HasDepthClip = features.Contains(WGPUFeatureName_DepthClipControl);
Limits.HasReadOnlyDepth = true;
Limits.MaximumSamplerAnisotropy = 4;
Limits.MaximumTexture1DSize = limits.maxTextureDimension1D;
Limits.MaximumTexture2DSize = limits.maxTextureDimension2D;
@@ -16,7 +16,7 @@ public class GraphicsDeviceWebGPU : GraphicsDeviceBaseModule
/// 1 - via Asyncify (causes the WASM to be much larger) <br/>
/// 2 - via JSPI (experimental) <br/>
/// </summary>
public int WithAsyncify = 2;
public static int WithAsyncify = 2;
/// <inheritdoc />
public override void Setup(BuildOptions options)
+1
View File
@@ -77,6 +77,7 @@ public class Main : EngineModule
break;
case TargetPlatform.Web:
options.SourcePaths.Add(Path.Combine(FolderPath, "Web"));
options.PrivateDefinitions.Add("WEB_LOOP_MODE=" + GraphicsDeviceWebGPU.WithAsyncify);
break;
default: throw new InvalidPlatformException(options.Platform.Target);
}
+9 -7
View File
@@ -7,17 +7,19 @@
// Reference: https://github.com/kainino0x/webgpu-cross-platform-demo/blob/f5c69c6fccbb2584c1b6f9e559f9a41a38a9b5ad/main.cpp#L692-L704
// Reference: https://github.com/kainino0x/webgpu-cross-platform-demo/blob/c26ea3e29ed9f73f9b39bddf7964b482ce3c6964/main.cpp#L737-L758
#define WEB_LOOP_MODE 2 // 0 - default, 1 - Asyncify, 2 - JSPI
#if WEB_LOOP_MODE != 0
#ifndef WEB_LOOP_MODE // 0 - default, 1 - Asyncify, 2 - JSPI
#error "Unknown WEB_LOOP_MODE"
#endif
#if WEB_LOOP_MODE == 2
// Workaround for JSPI not working in emscripten_set_main_loop. Loosely based on this code:
// https://github.com/emscripten-core/emscripten/issues/22493#issuecomment-2330275282
// This code only works with JSPI is enabled.
typedef bool (*FrameCallback)(); // If callback returns true, continues the loop.
EM_JS(void, requestAnimationFrameLoopWithJSPI, (FrameCallback callback), {
EM_JS(void, requestAnimationFrameLoopWithJSPI, (FrameCallback frame), {
#if WEB_LOOP_MODE == 2
var callback = WebAssembly.promising(getWasmTableEntry(callback));
var callback = WebAssembly.promising(getWasmTableEntry(frame));
#elif WEB_LOOP_MODE == 1
var callback = () = > globalThis['Module']['ccall']("callback", "boolean", [], [], { async: true });
var callback = () = > globalThis['Module']['ccall']("frame", "boolean", [], [], { async: true });
#endif
async function tick() {
// Start the frame callback. 'await' means we won't call
@@ -31,7 +33,7 @@ EM_JS(void, requestAnimationFrameLoopWithJSPI, (FrameCallback callback), {
class PlatformMain
{
#if WEB_LOOP_MODE == 0
#if WEB_LOOP_MODE != 2
static void Loop()
{
// Tick engine
@@ -75,7 +77,7 @@ public:
return result;
// Setup main loop to be called by Emscripten
#if WEB_LOOP_MODE == 0
#if WEB_LOOP_MODE != 2
emscripten_set_main_loop(Loop, -1, false);
#else
requestAnimationFrameLoopWithJSPI(Loop);
@@ -419,15 +419,15 @@ bool TextureTool::UpdateTexture(GPUContext* context, GPUTexture* texture, int32
auto textureSampler = PixelFormatSampler::Get(textureFormat);
if (!dataSampler || !textureSampler)
return true;
auto bytesPerPixel = PixelFormatExtensions::SizeInBytes(textureFormat);
int32 mipWidth, mipHeight, mipDepth;
texture->GetMipSize(mipIndex, mipWidth, mipHeight, mipDepth);
auto tempRowPitch = mipWidth * bytesPerPixel;
auto tempRowPitch = mipWidth * textureSampler->PixelSize;
auto tempSlicePitch = tempRowPitch * mipHeight;
tempData.Resize(tempSlicePitch * mipDepth);
ASSERT(data.Length() / rowPitch >= mipHeight);
for (int32 y = 0; y < mipHeight; y++)
{
for (int32 x = 0; x < mipWidth; x++)
@@ -514,7 +514,9 @@ PixelFormat TextureTool::GetTextureFormat(TextureFormatType textureType, PixelFo
EnumHasAllFlags(GPUDevice::Instance->GetFormatFeatures(PixelFormat::R32_Float).Support, minSupport) &&
PixelFormatSampler::Get(dataFormat))
return PixelFormat::R32_Float;
if (dataFormat == PixelFormat::R16G16_UNorm && EnumHasAllFlags(GPUDevice::Instance->GetFormatFeatures(PixelFormat::R32G32_UInt).Support, minSupport))
if (dataFormat == PixelFormat::R16G16_UNorm &&
EnumHasAllFlags(GPUDevice::Instance->GetFormatFeatures(PixelFormat::R32G32_Float).Support, minSupport) &&
PixelFormatSampler::Get(dataFormat))
return PixelFormat::R32G32_Float;
if ((dataFormat == PixelFormat::R16G16B16A16_UNorm || dataFormat == PixelFormat::R16G16B16A16_Float) &&
EnumHasAllFlags(GPUDevice::Instance->GetFormatFeatures(PixelFormat::R32G32B32A32_Float).Support, minSupport) &&