diff --git a/Source/Engine/Graphics/Textures/StreamingTexture.cpp b/Source/Engine/Graphics/Textures/StreamingTexture.cpp index ec0241cc7..93cfb4565 100644 --- a/Source/Engine/Graphics/Textures/StreamingTexture.cpp +++ b/Source/Engine/Graphics/Textures/StreamingTexture.cpp @@ -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(dataSource, dataPerSlice), rowPitch, slicePitch, _streamingTexture->GetHeader()->Format)) + if (TextureTool::UpdateTexture(context->GPU, texture, arrayIndex, _mipIndex, Span(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; diff --git a/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.cpp b/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.cpp index fac265832..4365b8c7e 100644 --- a/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.cpp +++ b/Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.cpp @@ -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); diff --git a/Source/Engine/GraphicsDevice/WebGPU/GPUDeviceWebGPU.cpp b/Source/Engine/GraphicsDevice/WebGPU/GPUDeviceWebGPU.cpp index 025db0d3f..4eb807f18 100644 --- a/Source/Engine/GraphicsDevice/WebGPU/GPUDeviceWebGPU.cpp +++ b/Source/Engine/GraphicsDevice/WebGPU/GPUDeviceWebGPU.cpp @@ -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; diff --git a/Source/Engine/GraphicsDevice/WebGPU/GraphicsDeviceWebGPU.Build.cs b/Source/Engine/GraphicsDevice/WebGPU/GraphicsDeviceWebGPU.Build.cs index 878dd4ecd..0a56770a4 100644 --- a/Source/Engine/GraphicsDevice/WebGPU/GraphicsDeviceWebGPU.Build.cs +++ b/Source/Engine/GraphicsDevice/WebGPU/GraphicsDeviceWebGPU.Build.cs @@ -16,7 +16,7 @@ public class GraphicsDeviceWebGPU : GraphicsDeviceBaseModule /// 1 - via Asyncify (causes the WASM to be much larger)
/// 2 - via JSPI (experimental)
/// - public int WithAsyncify = 2; + public static int WithAsyncify = 2; /// public override void Setup(BuildOptions options) diff --git a/Source/Engine/Main/Main.Build.cs b/Source/Engine/Main/Main.Build.cs index 73f39657e..89aa997fc 100644 --- a/Source/Engine/Main/Main.Build.cs +++ b/Source/Engine/Main/Main.Build.cs @@ -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); } diff --git a/Source/Engine/Main/Web/main.cpp b/Source/Engine/Main/Web/main.cpp index 53aef16f8..5e9e1ba42 100644 --- a/Source/Engine/Main/Web/main.cpp +++ b/Source/Engine/Main/Web/main.cpp @@ -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); diff --git a/Source/Engine/Tools/TextureTool/TextureTool.cpp b/Source/Engine/Tools/TextureTool/TextureTool.cpp index f0d5c071d..d19c24b4d 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.cpp @@ -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) &&