Add initial base implementation for WebGPU rendering backend
This commit is contained in:
@@ -75,6 +75,11 @@ API_ENUM() enum class RendererType
|
||||
/// </summary>
|
||||
PS5 = 12,
|
||||
|
||||
/// <summary>
|
||||
/// WebGPU
|
||||
/// </summary>
|
||||
WebGPU = 13,
|
||||
|
||||
API_ENUM(Attributes="HideInEditor")
|
||||
MAX
|
||||
};
|
||||
@@ -131,6 +136,11 @@ API_ENUM() enum class ShaderProfile
|
||||
/// </summary>
|
||||
PS5 = 8,
|
||||
|
||||
/// <summary>
|
||||
/// WebGPU
|
||||
/// </summary>
|
||||
WebGPU = 9,
|
||||
|
||||
API_ENUM(Attributes="HideInEditor")
|
||||
MAX
|
||||
};
|
||||
|
||||
@@ -97,8 +97,7 @@ public class Graphics : EngineModule
|
||||
Log.WarningOnce(string.Format("Building for {0} without Vulkan rendering backend (Vulkan SDK is missing)", options.Platform.Target), ref _logMissingVulkanSDK);
|
||||
break;
|
||||
case TargetPlatform.Web:
|
||||
options.PrivateDependencies.Add("GraphicsDeviceNull");
|
||||
// TODO: add WebGPU
|
||||
options.PrivateDependencies.Add("GraphicsDeviceWebGPU");
|
||||
break;
|
||||
default: throw new InvalidPlatformException(options.Platform.Target);
|
||||
}
|
||||
|
||||
@@ -175,6 +175,11 @@ bool GraphicsService::Init()
|
||||
extern GPUDevice* CreateGPUDevicePS5();
|
||||
if (!device)
|
||||
device = CreateGPUDevicePS5();
|
||||
#endif
|
||||
#if GRAPHICS_API_WEBGPU
|
||||
extern GPUDevice* CreateGPUDeviceWebGPU();
|
||||
if (!device)
|
||||
device = CreateGPUDeviceWebGPU();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,9 @@ const Char* ToString(RendererType value)
|
||||
case RendererType::PS5:
|
||||
result = TEXT("PS5");
|
||||
break;
|
||||
case RendererType::WebGPU:
|
||||
result = TEXT("WebGPU");
|
||||
break;
|
||||
default:
|
||||
result = TEXT("?");
|
||||
}
|
||||
@@ -96,6 +99,9 @@ const Char* ToString(ShaderProfile value)
|
||||
case ShaderProfile::PS5:
|
||||
result = TEXT("PS5");
|
||||
break;
|
||||
case ShaderProfile::WebGPU:
|
||||
result = TEXT("WebGPU");
|
||||
break;
|
||||
default:
|
||||
result = TEXT("?");
|
||||
}
|
||||
@@ -268,6 +274,7 @@ FeatureLevel RenderTools::GetFeatureLevel(ShaderProfile profile)
|
||||
case ShaderProfile::GLSL_440:
|
||||
case ShaderProfile::GLSL_410:
|
||||
case ShaderProfile::Unknown:
|
||||
case ShaderProfile::WebGPU:
|
||||
return FeatureLevel::ES2;
|
||||
default:
|
||||
return FeatureLevel::ES2;
|
||||
|
||||
@@ -27,6 +27,7 @@ const Char* ShaderProfileCacheDirNames[] =
|
||||
TEXT("PS4"), // PS4
|
||||
TEXT("DX_SM6"), // DirectX_SM6
|
||||
TEXT("PS5"), // PS5
|
||||
TEXT("WEB"), // WebGPU
|
||||
// @formatter:on
|
||||
};
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ void GPUSamplerDescription::Clear()
|
||||
{
|
||||
Platform::MemoryClear(this, sizeof(GPUSamplerDescription));
|
||||
MaxMipLevel = MAX_float;
|
||||
MaxAnisotropy = 1.0f;
|
||||
}
|
||||
|
||||
bool GPUSamplerDescription::Equals(const GPUSamplerDescription& other) const
|
||||
|
||||
56
Source/Engine/GraphicsDevice/WebGPU/GPUAdapterWebGPU.h
Normal file
56
Source/Engine/GraphicsDevice/WebGPU/GPUAdapterWebGPU.h
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if GRAPHICS_API_WEBGPU
|
||||
|
||||
#include "Engine/Graphics/GPUAdapter.h"
|
||||
#include "Engine/Utilities/StringConverter.h"
|
||||
#include "IncludeWebGPU.h"
|
||||
|
||||
/// <summary>
|
||||
/// Graphics Device adapter implementation for Web GPU backend.
|
||||
/// </summary>
|
||||
class GPUAdapterWebGPU : public GPUAdapter
|
||||
{
|
||||
public:
|
||||
WGPUAdapter Adapter;
|
||||
WGPUAdapterInfo Info;
|
||||
|
||||
GPUAdapterWebGPU(WGPUAdapter adapter)
|
||||
: Adapter(adapter)
|
||||
, Info(WGPU_ADAPTER_INFO_INIT)
|
||||
{
|
||||
wgpuAdapterGetInfo(adapter, &Info);
|
||||
}
|
||||
|
||||
~GPUAdapterWebGPU()
|
||||
{
|
||||
wgpuAdapterRelease(Adapter);
|
||||
}
|
||||
|
||||
public:
|
||||
// [GPUAdapter]
|
||||
bool IsValid() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
void* GetNativePtr() const override
|
||||
{
|
||||
return Adapter;
|
||||
}
|
||||
uint32 GetVendorId() const override
|
||||
{
|
||||
return Info.vendorID;
|
||||
}
|
||||
String GetDescription() const override
|
||||
{
|
||||
return String::Format(TEXT("{} {} {} {}"), WEBGPU_TO_STR(Info.vendor), WEBGPU_TO_STR(Info.architecture), WEBGPU_TO_STR(Info.device), WEBGPU_TO_STR(Info.description)).TrimTrailing();
|
||||
}
|
||||
Version GetDriverVersion() const override
|
||||
{
|
||||
return Platform::GetSystemVersion();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
127
Source/Engine/GraphicsDevice/WebGPU/GPUBufferWebGPU.cpp
Normal file
127
Source/Engine/GraphicsDevice/WebGPU/GPUBufferWebGPU.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#if GRAPHICS_API_WEBGPU
|
||||
|
||||
#include "GPUBufferWebGPU.h"
|
||||
#include "RenderToolsWebGPU.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
|
||||
GPUBufferWebGPU::GPUBufferWebGPU(GPUDeviceWebGPU* device, const StringView& name)
|
||||
: GPUResourceWebGPU(device, name)
|
||||
{
|
||||
}
|
||||
|
||||
GPUBufferView* GPUBufferWebGPU::View() const
|
||||
{
|
||||
return (GPUBufferView*)&_view;
|
||||
}
|
||||
|
||||
void* GPUBufferWebGPU::Map(GPUResourceMapMode mode)
|
||||
{
|
||||
ASSERT(!_mapped);
|
||||
WGPUMapMode mapMode = 0;
|
||||
if (EnumHasAnyFlags(mode, GPUResourceMapMode::Read))
|
||||
mapMode |= WGPUMapMode_Read;
|
||||
if (EnumHasAnyFlags(mode, GPUResourceMapMode::Write))
|
||||
mapMode |= WGPUMapMode_Write;
|
||||
AsyncCallbackWebGPU<WGPUBufferMapCallbackInfo> mapRequest(WGPU_BUFFER_MAP_CALLBACK_INFO_INIT);
|
||||
mapRequest.Info.callback = [](WGPUMapAsyncStatus status, WGPUStringView message, WGPU_NULLABLE void* userdata1, WGPU_NULLABLE void* userdata2)
|
||||
{
|
||||
AsyncCallbackDataWebGPU& userData = *reinterpret_cast<AsyncCallbackDataWebGPU*>(userdata1);
|
||||
userData.Call(status == WGPUMapAsyncStatus_Success, status, message);
|
||||
};
|
||||
wgpuBufferMapAsync(Buffer, mapMode, 0, _desc.Size, mapRequest.Info);
|
||||
auto mapRequestResult = mapRequest.Wait();
|
||||
if (mapRequestResult == WGPUWaitStatus_TimedOut)
|
||||
{
|
||||
LOG(Error, "WebGPU buffer map request has timed out after {}s", mapRequest.Data.WaitTime);
|
||||
return nullptr;
|
||||
}
|
||||
if (mapRequestResult == WGPUWaitStatus_Error)
|
||||
return nullptr;
|
||||
_mapped = true;
|
||||
if (EnumHasNoneFlags(mode, GPUResourceMapMode::Write))
|
||||
return (void*)wgpuBufferGetConstMappedRange(Buffer, 0, _desc.Size);
|
||||
return wgpuBufferGetMappedRange(Buffer, 0, _desc.Size);
|
||||
}
|
||||
|
||||
void GPUBufferWebGPU::Unmap()
|
||||
{
|
||||
ASSERT(_mapped);
|
||||
_mapped = false;
|
||||
wgpuBufferUnmap(Buffer);
|
||||
}
|
||||
|
||||
bool GPUBufferWebGPU::OnInit()
|
||||
{
|
||||
// Create buffer
|
||||
WGPUBufferDescriptor bufferDesc = WGPU_BUFFER_DESCRIPTOR_INIT;
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
_name.Set(_namePtr, _nameSize);
|
||||
bufferDesc.label = { _name.Get(), (size_t)_name.Length() };
|
||||
#endif
|
||||
if (EnumHasAllFlags(_desc.Flags, GPUBufferFlags::IndexBuffer))
|
||||
bufferDesc.usage |= WGPUBufferUsage_Index;
|
||||
else if (EnumHasAllFlags(_desc.Flags, GPUBufferFlags::VertexBuffer))
|
||||
bufferDesc.usage |= WGPUBufferUsage_Vertex;
|
||||
else if (EnumHasAllFlags(_desc.Flags, GPUBufferFlags::Argument))
|
||||
bufferDesc.usage |= WGPUBufferUsage_Indirect;
|
||||
if (IsUnorderedAccess())
|
||||
bufferDesc.usage |= WGPUBufferUsage_Storage;
|
||||
switch (_desc.Usage)
|
||||
{
|
||||
case GPUResourceUsage::Default:
|
||||
if (!_desc.InitData)
|
||||
bufferDesc.usage |= WGPUBufferUsage_CopyDst;
|
||||
break;
|
||||
case GPUResourceUsage::Dynamic:
|
||||
bufferDesc.usage |= WGPUBufferUsage_MapWrite;
|
||||
break;
|
||||
case GPUResourceUsage::StagingUpload:
|
||||
bufferDesc.usage |= WGPUBufferUsage_MapWrite | WGPUBufferUsage_CopySrc;
|
||||
break;
|
||||
case GPUResourceUsage::StagingReadback:
|
||||
bufferDesc.usage |= WGPUBufferUsage_MapRead;
|
||||
break;
|
||||
case GPUResourceUsage::Staging:
|
||||
bufferDesc.usage |= WGPUBufferUsage_MapRead | WGPUBufferUsage_MapWrite | WGPUBufferUsage_CopySrc;
|
||||
break;
|
||||
}
|
||||
bufferDesc.size = _desc.Size;
|
||||
bufferDesc.mappedAtCreation = _desc.InitData != nullptr;
|
||||
Buffer = wgpuDeviceCreateBuffer(_device->Device, &bufferDesc);
|
||||
if (!Buffer)
|
||||
return true;
|
||||
_memoryUsage = _desc.Size;
|
||||
|
||||
// Initialize with a data if provided
|
||||
if (_desc.InitData)
|
||||
{
|
||||
//wgpuBufferWriteMappedRange(Buffer, 0, _desc.InitData, _desc.Size);
|
||||
Platform::MemoryCopy(wgpuBufferGetMappedRange(Buffer, 0, _desc.Size), _desc.InitData, _desc.Size);
|
||||
wgpuBufferUnmap(Buffer);
|
||||
}
|
||||
|
||||
// Create view
|
||||
_view.Set(this, Buffer);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GPUBufferWebGPU::OnReleaseGPU()
|
||||
{
|
||||
if (Buffer)
|
||||
{
|
||||
wgpuBufferDestroy(Buffer);
|
||||
wgpuBufferRelease(Buffer);
|
||||
Buffer = nullptr;
|
||||
}
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
_name.Clear();
|
||||
#endif
|
||||
|
||||
// Base
|
||||
GPUBuffer::OnReleaseGPU();
|
||||
}
|
||||
|
||||
#endif
|
||||
68
Source/Engine/GraphicsDevice/WebGPU/GPUBufferWebGPU.h
Normal file
68
Source/Engine/GraphicsDevice/WebGPU/GPUBufferWebGPU.h
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if GRAPHICS_API_WEBGPU
|
||||
|
||||
#include "Engine/Graphics/GPUBuffer.h"
|
||||
#include "GPUDeviceWebGPU.h"
|
||||
|
||||
/// <summary>
|
||||
/// The buffer view for Web GPU backend.
|
||||
/// </summary>
|
||||
/// <seealso cref="GPUBufferView" />
|
||||
class GPUBufferViewWebGPU : public GPUBufferView
|
||||
{
|
||||
public:
|
||||
// Handle to the WebGPU buffer object.
|
||||
WGPUBuffer Buffer = nullptr;
|
||||
GPUResourceViewPtrWebGPU Ptr;
|
||||
|
||||
void Set(GPUBuffer* parent, WGPUBuffer buffer)
|
||||
{
|
||||
_parent = parent;
|
||||
Buffer = buffer;
|
||||
Ptr = { this, nullptr };
|
||||
}
|
||||
|
||||
public:
|
||||
// [GPUResourceView]
|
||||
void* GetNativePtr() const override
|
||||
{
|
||||
return (void*)&Ptr;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// GPU buffer for Web GPU backend.
|
||||
/// </summary>
|
||||
/// <seealso cref="GPUResourceWebGPU" />
|
||||
class GPUBufferWebGPU : public GPUResourceWebGPU<GPUBuffer>
|
||||
{
|
||||
private:
|
||||
GPUBufferViewWebGPU _view;
|
||||
bool _mapped = false;
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
StringAnsi _name;
|
||||
#endif
|
||||
|
||||
public:
|
||||
GPUBufferWebGPU(GPUDeviceWebGPU* device, const StringView& name);
|
||||
|
||||
public:
|
||||
// Handle to the WebGPU buffer object.
|
||||
WGPUBuffer Buffer = nullptr;
|
||||
|
||||
public:
|
||||
// [GPUBuffer]
|
||||
GPUBufferView* View() const override;
|
||||
void* Map(GPUResourceMapMode mode) override;
|
||||
void Unmap() override;
|
||||
|
||||
protected:
|
||||
// [GPUBuffer]
|
||||
bool OnInit() override;
|
||||
void OnReleaseGPU() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
817
Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.cpp
Normal file
817
Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.cpp
Normal file
@@ -0,0 +1,817 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#if GRAPHICS_API_WEBGPU
|
||||
|
||||
#include "GPUContextWebGPU.h"
|
||||
#include "GPUShaderWebGPU.h"
|
||||
#include "GPUShaderProgramWebGPU.h"
|
||||
#include "GPUPipelineStateWebGPU.h"
|
||||
#include "GPUTextureWebGPU.h"
|
||||
#include "GPUBufferWebGPU.h"
|
||||
#include "GPUSamplerWebGPU.h"
|
||||
#include "GPUVertexLayoutWebGPU.h"
|
||||
#include "RenderToolsWebGPU.h"
|
||||
#include "Engine/Core/Math/Viewport.h"
|
||||
#include "Engine/Core/Math/Rectangle.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/RenderStats.h"
|
||||
#include "Engine/Graphics/PixelFormatExtensions.h"
|
||||
|
||||
// Ensure to match the indirect commands arguments layout
|
||||
static_assert(sizeof(GPUDispatchIndirectArgs) == sizeof(uint32) * 3, "Wrong size of GPUDrawIndirectArgs.");
|
||||
static_assert(OFFSET_OF(GPUDispatchIndirectArgs, ThreadGroupCountX) == sizeof(uint32) * 0, "Wrong offset for GPUDrawIndirectArgs::ThreadGroupCountX");
|
||||
static_assert(OFFSET_OF(GPUDispatchIndirectArgs, ThreadGroupCountY) == sizeof(uint32) * 1, "Wrong offset for GPUDrawIndirectArgs::ThreadGroupCountY");
|
||||
static_assert(OFFSET_OF(GPUDispatchIndirectArgs, ThreadGroupCountZ) == sizeof(uint32) * 2, "Wrong offset for GPUDrawIndirectArgs::ThreadGroupCountZ");
|
||||
//
|
||||
static_assert(sizeof(GPUDrawIndirectArgs) == sizeof(uint32) * 4, "Wrong size of GPUDrawIndirectArgs.");
|
||||
static_assert(OFFSET_OF(GPUDrawIndirectArgs, VerticesCount) == sizeof(uint32) * 0, "Wrong offset for GPUDrawIndirectArgs::VerticesCount");
|
||||
static_assert(OFFSET_OF(GPUDrawIndirectArgs, InstanceCount) == sizeof(uint32) * 1, "Wrong offset for GPUDrawIndirectArgs::InstanceCount");
|
||||
static_assert(OFFSET_OF(GPUDrawIndirectArgs, StartVertex) == sizeof(uint32) * 2, "Wrong offset for GPUDrawIndirectArgs::StartVertex");
|
||||
static_assert(OFFSET_OF(GPUDrawIndirectArgs, StartInstance) == sizeof(uint32) * 3, "Wrong offset for GPUDrawIndirectArgs::StartInstance");
|
||||
//
|
||||
static_assert(sizeof(GPUDrawIndexedIndirectArgs) == sizeof(uint32) * 5, "Wrong size of GPUDrawIndexedIndirectArgs.");
|
||||
static_assert(OFFSET_OF(GPUDrawIndexedIndirectArgs, IndicesCount) == sizeof(uint32) * 0, "Wrong offset for GPUDrawIndexedIndirectArgs::IndicesCount");
|
||||
static_assert(OFFSET_OF(GPUDrawIndexedIndirectArgs, InstanceCount) == sizeof(uint32) * 1, "Wrong offset for GPUDrawIndexedIndirectArgs::InstanceCount");
|
||||
static_assert(OFFSET_OF(GPUDrawIndexedIndirectArgs, StartIndex) == sizeof(uint32) * 2, "Wrong offset for GPUDrawIndexedIndirectArgs::StartIndex");
|
||||
static_assert(OFFSET_OF(GPUDrawIndexedIndirectArgs, StartVertex) == sizeof(uint32) * 3, "Wrong offset for GPUDrawIndexedIndirectArgs::StartVertex");
|
||||
static_assert(OFFSET_OF(GPUDrawIndexedIndirectArgs, StartInstance) == sizeof(uint32) * 4, "Wrong offset for GPUDrawIndexedIndirectArgs::StartInstance");
|
||||
|
||||
GPUContextWebGPU::GPUContextWebGPU(GPUDeviceWebGPU* device)
|
||||
: GPUContext(device)
|
||||
, _device(device)
|
||||
{
|
||||
_vertexBufferNullLayout = WGPU_VERTEX_BUFFER_LAYOUT_INIT;
|
||||
}
|
||||
|
||||
GPUContextWebGPU::~GPUContextWebGPU()
|
||||
{
|
||||
CHECK(Encoder == nullptr);
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::FrameBegin()
|
||||
{
|
||||
// Base
|
||||
GPUContext::FrameBegin();
|
||||
|
||||
// Setup
|
||||
_renderPassDirty = false;
|
||||
_pipelineDirty = false;
|
||||
_bindGroupDirty = false;
|
||||
_indexBufferDirty = false;
|
||||
_vertexBufferDirty = false;
|
||||
_indexBuffer32Bit = false;
|
||||
_blendFactorDirty = false;
|
||||
_blendFactorSet = false;
|
||||
_renderTargetCount = 0;
|
||||
_vertexBufferCount = 0;
|
||||
_stencilRef = 0;
|
||||
_blendFactor = Float4::One;
|
||||
_viewport = Viewport(Float2::Zero);
|
||||
_scissorRect = Rectangle::Empty;
|
||||
_renderPass = nullptr;
|
||||
_depthStencil = nullptr;
|
||||
_pipelineState = nullptr;
|
||||
Platform::MemoryClear(&_pipelineKey, sizeof(_pipelineKey));
|
||||
Platform::MemoryClear(&_indexBuffer, sizeof(_indexBuffer));
|
||||
Platform::MemoryClear(&_vertexBuffers, sizeof(_vertexBuffers));
|
||||
Platform::MemoryClear(&_renderTargets, sizeof(_renderTargets));
|
||||
Platform::MemoryClear(&_constantBuffers, sizeof(_constantBuffers));
|
||||
Platform::MemoryClear(&_shaderResources, sizeof(_shaderResources));
|
||||
Platform::MemoryClear(&_storageResources, sizeof(_storageResources));
|
||||
_pendingClears.Clear();
|
||||
|
||||
// Create command encoder
|
||||
WGPUCommandEncoderDescriptor encoderDesc = WGPU_COMMAND_ENCODER_DESCRIPTOR_INIT;
|
||||
Encoder = wgpuDeviceCreateCommandEncoder(_device->Device, &encoderDesc);
|
||||
ASSERT(Encoder);
|
||||
|
||||
// Bind static samplers
|
||||
for (int32 i = 0; i < ARRAY_COUNT(_device->DefaultSamplers); i++)
|
||||
_samplers[i] = _device->DefaultSamplers[i];
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::FrameEnd()
|
||||
{
|
||||
// Base
|
||||
GPUContext::FrameEnd();
|
||||
|
||||
// Flush command encoder to the command buffer and submit them on a queue
|
||||
Flush();
|
||||
}
|
||||
|
||||
void* GPUContextWebGPU::GetNativePtr() const
|
||||
{
|
||||
return Encoder;
|
||||
}
|
||||
|
||||
bool GPUContextWebGPU::IsDepthBufferBinded()
|
||||
{
|
||||
return _depthStencil != nullptr;
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::Clear(GPUTextureView* rt, const Color& color)
|
||||
{
|
||||
auto& clear = _pendingClears.AddOne();
|
||||
clear.View = (GPUTextureViewWebGPU*)rt;
|
||||
Platform::MemoryCopy(clear.RGBA, color.Raw, sizeof(color.Raw));
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::ClearDepth(GPUTextureView* depthBuffer, float depthValue, uint8 stencilValue)
|
||||
{
|
||||
auto& clear = _pendingClears.AddOne();
|
||||
clear.View = (GPUTextureViewWebGPU*)depthBuffer;
|
||||
clear.Depth = depthValue;
|
||||
clear.Stencil = stencilValue;
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::ClearUA(GPUBuffer* buf, const Float4& value)
|
||||
{
|
||||
MISSING_CODE("GPUContextWebGPU::ClearUA");
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::ClearUA(GPUBuffer* buf, const uint32 value[4])
|
||||
{
|
||||
MISSING_CODE("GPUContextWebGPU::ClearUA");
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::ClearUA(GPUTexture* texture, const uint32 value[4])
|
||||
{
|
||||
MISSING_CODE("GPUContextWebGPU::ClearUA");
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::ClearUA(GPUTexture* texture, const Float4& value)
|
||||
{
|
||||
MISSING_CODE("GPUContextWebGPU::ClearUA");
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::ResetRenderTarget()
|
||||
{
|
||||
if (_renderTargetCount != 0 || _depthStencil)
|
||||
{
|
||||
_renderPassDirty = true;
|
||||
_renderTargetCount = 0;
|
||||
_depthStencil = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::SetRenderTarget(GPUTextureView* rt)
|
||||
{
|
||||
auto rtWebGPU = (GPUTextureViewWebGPU*)rt;
|
||||
int32 newRtCount = rtWebGPU ? 1 : 0;
|
||||
if (_renderTargetCount != newRtCount || _renderTargets[0] != rtWebGPU || _depthStencil != nullptr)
|
||||
{
|
||||
_renderPassDirty = true;
|
||||
_renderTargetCount = newRtCount;
|
||||
_depthStencil = nullptr;
|
||||
_renderTargets[0] = rtWebGPU;
|
||||
}
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::SetRenderTarget(GPUTextureView* depthBuffer, GPUTextureView* rt)
|
||||
{
|
||||
auto depthBufferGPU = (GPUTextureViewWebGPU*)depthBuffer;
|
||||
auto rtWebGPU = (GPUTextureViewWebGPU*)rt;
|
||||
int32 newRtCount = rtWebGPU ? 1 : 0;
|
||||
if (_renderTargetCount != newRtCount || _renderTargets[0] != rtWebGPU || _depthStencil != depthBufferGPU)
|
||||
{
|
||||
_renderPassDirty = true;
|
||||
_renderTargetCount = newRtCount;
|
||||
_depthStencil = depthBufferGPU;
|
||||
_renderTargets[0] = rtWebGPU;
|
||||
}
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::SetRenderTarget(GPUTextureView* depthBuffer, const Span<GPUTextureView*>& rts)
|
||||
{
|
||||
ASSERT(Math::IsInRange(rts.Length(), 1, GPU_MAX_RT_BINDED));
|
||||
auto depthBufferGPU = (GPUTextureViewWebGPU*)depthBuffer;
|
||||
if (_renderTargetCount != rts.Length() || _depthStencil != depthBufferGPU || Platform::MemoryCompare(_renderTargets, rts.Get(), rts.Length() * sizeof(void*)) != 0)
|
||||
{
|
||||
_renderPassDirty = true;
|
||||
_renderTargetCount = rts.Length();
|
||||
_depthStencil = depthBufferGPU;
|
||||
Platform::MemoryCopy(_renderTargets, rts.Get(), rts.Length() * sizeof(void*));
|
||||
}
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::SetBlendFactor(const Float4& value)
|
||||
{
|
||||
if (_blendFactor != value)
|
||||
{
|
||||
_blendFactorDirty = true;
|
||||
_blendFactor = value;
|
||||
_blendFactorSet = value != Float4::One;
|
||||
}
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::SetStencilRef(uint32 value)
|
||||
{
|
||||
if (_stencilRef != value)
|
||||
{
|
||||
_stencilRef = value;
|
||||
if (_renderPass)
|
||||
wgpuRenderPassEncoderSetStencilReference(_renderPass, value);
|
||||
}
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::ResetSR()
|
||||
{
|
||||
Platform::MemoryClear(_shaderResources, sizeof(_shaderResources));
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::ResetUA()
|
||||
{
|
||||
Platform::MemoryClear(_storageResources, sizeof(_storageResources));
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::ResetCB()
|
||||
{
|
||||
_bindGroupDirty = false;
|
||||
Platform::MemoryClear(_constantBuffers, sizeof(_constantBuffers));
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::BindCB(int32 slot, GPUConstantBuffer* cb)
|
||||
{
|
||||
ASSERT(slot >= 0 && slot < GPU_MAX_CB_BINDED);
|
||||
auto cbWebGPU = (GPUConstantBufferWebGPU*)cb;
|
||||
if (_constantBuffers[slot] != cbWebGPU)
|
||||
{
|
||||
_bindGroupDirty = true;
|
||||
_constantBuffers[slot] = cbWebGPU;
|
||||
}
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::BindSR(int32 slot, GPUResourceView* view)
|
||||
{
|
||||
ASSERT(slot >= 0 && slot < GPU_MAX_SR_BINDED);
|
||||
if (_shaderResources[slot] != view)
|
||||
{
|
||||
_bindGroupDirty = true;
|
||||
_shaderResources[slot] = view;
|
||||
if (view)
|
||||
*view->LastRenderTime = _lastRenderTime;
|
||||
}
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::BindUA(int32 slot, GPUResourceView* view)
|
||||
{
|
||||
ASSERT(slot >= 0 && slot < GPU_MAX_UA_BINDED);
|
||||
if (_storageResources[slot] != view)
|
||||
{
|
||||
_bindGroupDirty = true;
|
||||
_storageResources[slot] = view;
|
||||
if (view)
|
||||
*view->LastRenderTime = _lastRenderTime;
|
||||
}
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::BindVB(const Span<GPUBuffer*>& vertexBuffers, const uint32* vertexBuffersOffsets, GPUVertexLayout* vertexLayout)
|
||||
{
|
||||
ASSERT(vertexBuffers.Length() <= GPU_MAX_VB_BINDED);
|
||||
_vertexBufferDirty = true;
|
||||
_vertexBufferCount = vertexBuffers.Length();
|
||||
_pipelineKey.VertexBufferCount = vertexBuffers.Length();
|
||||
for (int32 i = 0; i < vertexBuffers.Length(); i++)
|
||||
{
|
||||
auto vbWebGPU = (GPUBufferWebGPU*)vertexBuffers.Get()[i];
|
||||
_vertexBuffers[i].Buffer = vbWebGPU ? vbWebGPU->Buffer : nullptr;
|
||||
_vertexBuffers[i].Offset = vertexBuffersOffsets ? vertexBuffersOffsets[i] : 0;
|
||||
_vertexBuffers[i].Size = vbWebGPU ? vbWebGPU->GetSize() : 0;
|
||||
_pipelineKey.VertexBuffers[i] = vbWebGPU && vbWebGPU->GetVertexLayout() ? &((GPUVertexLayoutWebGPU*)vbWebGPU->GetVertexLayout())->Layout : &_vertexBufferNullLayout;
|
||||
}
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::BindIB(GPUBuffer* indexBuffer)
|
||||
{
|
||||
auto ibWebGPU = (GPUBufferWebGPU*)indexBuffer;
|
||||
_indexBufferDirty = true;
|
||||
_indexBuffer32Bit = indexBuffer->GetFormat() == PixelFormat::R32_UInt;
|
||||
_indexBuffer.Buffer = ibWebGPU->Buffer;
|
||||
_indexBuffer.Offset = 0;
|
||||
_indexBuffer.Size = indexBuffer->GetSize();
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::BindSampler(int32 slot, GPUSampler* sampler)
|
||||
{
|
||||
ASSERT(slot >= 0 && slot < GPU_MAX_SAMPLER_BINDED);
|
||||
auto samplerWebGPU = (GPUSamplerWebGPU*)sampler;
|
||||
if (_samplers[slot] != samplerWebGPU)
|
||||
{
|
||||
_bindGroupDirty = true;
|
||||
_samplers[slot] = samplerWebGPU;
|
||||
}
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::UpdateCB(GPUConstantBuffer* cb, const void* data)
|
||||
{
|
||||
ASSERT(data && cb);
|
||||
auto cbWebGPU = static_cast<GPUConstantBufferWebGPU*>(cb);
|
||||
const uint32 size = cbWebGPU->GetSize();
|
||||
if (size != 0)
|
||||
{
|
||||
wgpuQueueWriteBuffer(_device->Queue, cbWebGPU->Buffer, 0, data, size);
|
||||
}
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::Dispatch(GPUShaderProgramCS* shader, uint32 threadGroupCountX, uint32 threadGroupCountY, uint32 threadGroupCountZ)
|
||||
{
|
||||
OnDispatch(shader);
|
||||
MISSING_CODE("GPUContextWebGPU::Dispatch");
|
||||
RENDER_STAT_DISPATCH_CALL();
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::DispatchIndirect(GPUShaderProgramCS* shader, GPUBuffer* bufferForArgs, uint32 offsetForArgs)
|
||||
{
|
||||
ASSERT(bufferForArgs && EnumHasAnyFlags(bufferForArgs->GetFlags(), GPUBufferFlags::Argument));
|
||||
auto bufferForArgsWebGPU = (GPUBufferWebGPU*)bufferForArgs;
|
||||
OnDispatch(shader);
|
||||
MISSING_CODE("GPUContextWebGPU::Dispatch");
|
||||
RENDER_STAT_DISPATCH_CALL();
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::ResolveMultisample(GPUTexture* sourceMultisampleTexture, GPUTexture* destTexture, int32 sourceSubResource, int32 destSubResource, PixelFormat format)
|
||||
{
|
||||
ASSERT(sourceMultisampleTexture && sourceMultisampleTexture->IsMultiSample());
|
||||
ASSERT(destTexture && !destTexture->IsMultiSample());
|
||||
|
||||
// TODO: do it via a render pass (see WGPURenderPassColorAttachment::resolveTarget)
|
||||
MISSING_CODE("GPUContextWebGPU::ResolveMultisample");
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::DrawInstanced(uint32 verticesCount, uint32 instanceCount, int32 startInstance, int32 startVertex)
|
||||
{
|
||||
OnDrawCall();
|
||||
wgpuRenderPassEncoderDraw(_renderPass, verticesCount, instanceCount, startVertex, startInstance);
|
||||
RENDER_STAT_DRAW_CALL(verticesCount * instanceCount, verticesCount * instanceCount / 3);
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::DrawIndexedInstanced(uint32 indicesCount, uint32 instanceCount, int32 startInstance, int32 startVertex, int32 startIndex)
|
||||
{
|
||||
OnDrawCall();
|
||||
wgpuRenderPassEncoderDrawIndexed(_renderPass, indicesCount, instanceCount, startIndex, startVertex, startInstance);
|
||||
RENDER_STAT_DRAW_CALL(indicesCount * instanceCount, indicesCount / 3 * instanceCount);
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::DrawInstancedIndirect(GPUBuffer* bufferForArgs, uint32 offsetForArgs)
|
||||
{
|
||||
ASSERT(bufferForArgs && EnumHasAnyFlags(bufferForArgs->GetFlags(), GPUBufferFlags::Argument));
|
||||
const auto bufferForArgsWebGPU = static_cast<GPUBufferWebGPU*>(bufferForArgs);
|
||||
OnDrawCall();
|
||||
wgpuRenderPassEncoderDrawIndirect(_renderPass, bufferForArgsWebGPU->Buffer, offsetForArgs);
|
||||
RENDER_STAT_DRAW_CALL(0, 0);
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::DrawIndexedInstancedIndirect(GPUBuffer* bufferForArgs, uint32 offsetForArgs)
|
||||
{
|
||||
ASSERT(bufferForArgs && EnumHasAnyFlags(bufferForArgs->GetFlags(), GPUBufferFlags::Argument));
|
||||
const auto bufferForArgsWebGPU = static_cast<GPUBufferWebGPU*>(bufferForArgs);
|
||||
OnDrawCall();
|
||||
wgpuRenderPassEncoderDrawIndexedIndirect(_renderPass, bufferForArgsWebGPU->Buffer, offsetForArgs);
|
||||
RENDER_STAT_DRAW_CALL(0, 0);
|
||||
}
|
||||
|
||||
uint64 GPUContextWebGPU::BeginQuery(GPUQueryType type)
|
||||
{
|
||||
// TODO: impl timer/occlusion queries
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::EndQuery(uint64 queryID)
|
||||
{
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::SetViewport(const Viewport& viewport)
|
||||
{
|
||||
_viewport = viewport;
|
||||
if (_renderPass)
|
||||
wgpuRenderPassEncoderSetViewport(_renderPass, viewport.X, viewport.Y, viewport.Width, viewport.Height, viewport.MinDepth, viewport.MaxDepth);
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::SetScissor(const Rectangle& scissorRect)
|
||||
{
|
||||
_scissorRect = scissorRect;
|
||||
if (_renderPass)
|
||||
wgpuRenderPassEncoderSetScissorRect(_renderPass, (uint32_t)scissorRect.GetX(), (uint32_t)scissorRect.GetY(), (uint32_t)scissorRect.GetWidth(), (uint32_t)scissorRect.GetHeight());
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::SetDepthBounds(float minDepth, float maxDepth)
|
||||
{
|
||||
}
|
||||
|
||||
GPUPipelineState* GPUContextWebGPU::GetState() const
|
||||
{
|
||||
return _pipelineState;
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::SetState(GPUPipelineState* state)
|
||||
{
|
||||
if (_pipelineState != state)
|
||||
{
|
||||
_pipelineState = (GPUPipelineStateWebGPU*)state;
|
||||
_pipelineDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::ResetState()
|
||||
{
|
||||
if (!Encoder)
|
||||
return;
|
||||
|
||||
ResetRenderTarget();
|
||||
ResetSR();
|
||||
ResetUA();
|
||||
ResetCB();
|
||||
SetState(nullptr);
|
||||
|
||||
FlushState();
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::FlushState()
|
||||
{
|
||||
// Flush pending clears
|
||||
for (auto& clear : _pendingClears)
|
||||
ManualClear(clear);
|
||||
_pendingClears.Clear();
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::Flush()
|
||||
{
|
||||
if (!Encoder)
|
||||
return;
|
||||
PROFILE_CPU();
|
||||
|
||||
WGPUCommandBufferDescriptor commandBufferDesc = WGPU_COMMAND_BUFFER_DESCRIPTOR_INIT;
|
||||
WGPUCommandBuffer commandBuffer = wgpuCommandEncoderFinish(Encoder, &commandBufferDesc);
|
||||
wgpuCommandEncoderRelease(Encoder);
|
||||
if (commandBuffer)
|
||||
{
|
||||
wgpuQueueSubmit(_device->Queue, 1, &commandBuffer);
|
||||
wgpuCommandBufferRelease(commandBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::UpdateBuffer(GPUBuffer* buffer, const void* data, uint32 size, uint32 offset)
|
||||
{
|
||||
ASSERT(data);
|
||||
ASSERT(buffer && buffer->GetSize() >= size);
|
||||
auto bufferWebGPU = (GPUBufferWebGPU*)buffer;
|
||||
wgpuQueueWriteBuffer(_device->Queue, bufferWebGPU->Buffer, offset, data, size);
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::CopyBuffer(GPUBuffer* dstBuffer, GPUBuffer* srcBuffer, uint32 size, uint32 dstOffset, uint32 srcOffset)
|
||||
{
|
||||
ASSERT(dstBuffer && srcBuffer);
|
||||
auto srcBufferWebGPU = (GPUBufferWebGPU*)srcBuffer;
|
||||
auto dstBufferWebGPU = (GPUBufferWebGPU*)dstBuffer;
|
||||
wgpuCommandEncoderCopyBufferToBuffer(Encoder, srcBufferWebGPU->Buffer, srcOffset, dstBufferWebGPU->Buffer, dstOffset, size);
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::UpdateTexture(GPUTexture* texture, int32 arrayIndex, int32 mipIndex, const void* data, uint32 rowPitch, uint32 slicePitch)
|
||||
{
|
||||
ASSERT(texture && texture->IsAllocated() && data);
|
||||
auto textureWebGPU = (GPUTextureWebGPU*)texture;
|
||||
ASSERT_LOW_LAYER(textureWebGPU->Texture && wgpuTextureGetUsage(textureWebGPU->Texture) & WGPUTextureUsage_CopyDst);
|
||||
ASSERT(!texture->IsVolume()); // TODO: impl uploading volume textures (handle write size properly)
|
||||
|
||||
int32 mipWidth, mipHeight, mipDepth;
|
||||
texture->GetMipSize(mipIndex, mipWidth, mipHeight, mipDepth);
|
||||
|
||||
WGPUTexelCopyTextureInfo copyInfo = WGPU_TEXEL_COPY_TEXTURE_INFO_INIT;
|
||||
copyInfo.texture = textureWebGPU->Texture;
|
||||
copyInfo.mipLevel = mipIndex;
|
||||
copyInfo.aspect = WGPUTextureAspect_All;
|
||||
WGPUTexelCopyBufferLayout dataLayout = WGPU_TEXEL_COPY_BUFFER_LAYOUT_INIT;
|
||||
dataLayout.bytesPerRow = rowPitch;
|
||||
dataLayout.rowsPerImage = mipHeight;
|
||||
WGPUExtent3D writeSize = { (uint32_t)mipWidth, (uint32_t)mipHeight, 1 };
|
||||
wgpuQueueWriteTexture(_device->Queue, ©Info, data, slicePitch, &dataLayout, &writeSize);
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::CopyTexture(GPUTexture* dstResource, uint32 dstSubresource, uint32 dstX, uint32 dstY, uint32 dstZ, GPUTexture* srcResource, uint32 srcSubresource)
|
||||
{
|
||||
ASSERT(dstResource && srcResource);
|
||||
auto srcTextureWebGPU = (GPUTextureWebGPU*)srcResource;
|
||||
auto dstTextureWebGPU = (GPUTextureWebGPU*)dstResource;
|
||||
ASSERT_LOW_LAYER(dstTextureWebGPU->Texture && wgpuTextureGetUsage(dstTextureWebGPU->Texture) & WGPUTextureUsage_CopyDst);
|
||||
ASSERT_LOW_LAYER(srcTextureWebGPU->Texture && wgpuTextureGetUsage(srcTextureWebGPU->Texture) & WGPUTextureUsage_CopySrc);
|
||||
|
||||
// TODO: handle array/depth slices
|
||||
const int32 srcMipIndex = srcSubresource % srcTextureWebGPU->MipLevels();
|
||||
const int32 dstMipIndex = dstSubresource % srcTextureWebGPU->MipLevels();
|
||||
|
||||
int32 srcMipWidth, srcMipHeight, srcMipDepth;
|
||||
srcTextureWebGPU->GetMipSize(srcMipIndex, srcMipWidth, srcMipHeight, srcMipDepth);
|
||||
|
||||
WGPUTexelCopyTextureInfo srcInfo = WGPU_TEXEL_COPY_TEXTURE_INFO_INIT;
|
||||
srcInfo.texture = srcTextureWebGPU->Texture;
|
||||
srcInfo.mipLevel = srcMipIndex;
|
||||
srcInfo.aspect = WGPUTextureAspect_All;
|
||||
WGPUTexelCopyTextureInfo dstInfo = WGPU_TEXEL_COPY_TEXTURE_INFO_INIT;
|
||||
dstInfo.texture = dstTextureWebGPU->Texture;
|
||||
dstInfo.mipLevel = dstMipIndex;
|
||||
dstInfo.origin = { dstX, dstY, dstZ };
|
||||
dstInfo.aspect = WGPUTextureAspect_All;
|
||||
WGPUExtent3D copySize = { (uint32_t)srcMipWidth, (uint32_t)srcMipHeight, (uint32_t)srcMipDepth };
|
||||
wgpuCommandEncoderCopyTextureToTexture(Encoder, &srcInfo, &dstInfo, ©Size);
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::ResetCounter(GPUBuffer* buffer)
|
||||
{
|
||||
MISSING_CODE("GPUContextWebGPU::ResetCounter");
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::CopyCounter(GPUBuffer* dstBuffer, uint32 dstOffset, GPUBuffer* srcBuffer)
|
||||
{
|
||||
MISSING_CODE("GPUContextWebGPU::CopyCounter");
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::CopyResource(GPUResource* dstResource, GPUResource* srcResource)
|
||||
{
|
||||
ASSERT(dstResource && srcResource);
|
||||
auto dstTexture = Cast<GPUTexture>(dstResource);
|
||||
auto srcTexture = Cast<GPUTexture>(srcResource);
|
||||
if (srcTexture && dstTexture)
|
||||
{
|
||||
// Texture -> Texture
|
||||
ASSERT(srcTexture->MipLevels() == dstTexture->MipLevels());
|
||||
ASSERT(srcTexture->ArraySize() == 1); // TODO: implement copying texture arrays
|
||||
for (int32 mipLevel = 0; mipLevel < srcTexture->MipLevels(); mipLevel++)
|
||||
CopyTexture(dstTexture, mipLevel, 0, 0, 0, srcTexture, mipLevel);
|
||||
}
|
||||
else if (srcTexture)
|
||||
{
|
||||
// Texture -> Buffer
|
||||
auto srcTextureWebGPU = (GPUTextureWebGPU*)srcResource;
|
||||
auto dstBufferWebGPU = (GPUBufferWebGPU*)dstResource;
|
||||
MISSING_CODE("GPUContextWebGPU::CopyResource: texture -> buffer"); // TODO: impl this
|
||||
}
|
||||
else if (dstTexture)
|
||||
{
|
||||
// Buffer -> Texture
|
||||
auto srcBufferWebGPU = (GPUBufferWebGPU*)srcResource;
|
||||
auto dstTextureWebGPU = (GPUTextureWebGPU*)dstResource;
|
||||
MISSING_CODE("GPUContextWebGPU::CopyResource: buffer -> texture"); // TODO: impl this
|
||||
}
|
||||
else
|
||||
{
|
||||
// Buffer -> Buffer
|
||||
auto srcBufferWebGPU = (GPUBufferWebGPU*)srcResource;
|
||||
auto dstBufferWebGPU = (GPUBufferWebGPU*)dstResource;
|
||||
uint64 size = Math::Min(srcBufferWebGPU->GetSize(), dstBufferWebGPU->GetSize());
|
||||
wgpuCommandEncoderCopyBufferToBuffer(Encoder, srcBufferWebGPU->Buffer, 0, dstBufferWebGPU->Buffer, 0, size);
|
||||
}
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::CopySubresource(GPUResource* dstResource, uint32 dstSubresource, GPUResource* srcResource, uint32 srcSubresource)
|
||||
{
|
||||
ASSERT(dstResource && srcResource);
|
||||
auto dstTexture = Cast<GPUTexture>(dstResource);
|
||||
auto srcTexture = Cast<GPUTexture>(srcResource);
|
||||
if (srcTexture && dstTexture)
|
||||
{
|
||||
// Texture -> Texture
|
||||
CopyTexture(dstTexture, dstSubresource, 0, 0, 0, srcTexture, srcSubresource);
|
||||
}
|
||||
else if (srcTexture)
|
||||
{
|
||||
// Texture -> Buffer
|
||||
auto srcTextureWebGPU = (GPUTextureWebGPU*)srcResource;
|
||||
auto dstBufferWebGPU = (GPUBufferWebGPU*)dstResource;
|
||||
MISSING_CODE("GPUContextWebGPU::CopyResource: texture -> buffer"); // TODO: impl this
|
||||
}
|
||||
else if (dstTexture)
|
||||
{
|
||||
// Buffer -> Texture
|
||||
auto srcBufferWebGPU = (GPUBufferWebGPU*)srcResource;
|
||||
auto dstTextureWebGPU = (GPUTextureWebGPU*)dstResource;
|
||||
MISSING_CODE("GPUContextWebGPU::CopyResource: buffer -> texture"); // TODO: impl this
|
||||
}
|
||||
else
|
||||
{
|
||||
// Buffer -> Buffer
|
||||
ASSERT(dstSubresource == 0 && srcSubresource == 0);
|
||||
auto srcBufferWebGPU = (GPUBufferWebGPU*)srcResource;
|
||||
auto dstBufferWebGPU = (GPUBufferWebGPU*)dstResource;
|
||||
uint64 size = Math::Min(srcBufferWebGPU->GetSize(), dstBufferWebGPU->GetSize());
|
||||
wgpuCommandEncoderCopyBufferToBuffer(Encoder, srcBufferWebGPU->Buffer, 0, dstBufferWebGPU->Buffer, 0, size);
|
||||
}
|
||||
}
|
||||
|
||||
bool GPUContextWebGPU::FindClear(const GPUTextureViewWebGPU* view, PendingClear& clear)
|
||||
{
|
||||
for (auto& e : _pendingClears)
|
||||
{
|
||||
if (e.View == view)
|
||||
{
|
||||
clear = e;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::ManualClear(const PendingClear& clear)
|
||||
{
|
||||
// End existing pass (if any)
|
||||
if (_renderPass)
|
||||
{
|
||||
wgpuRenderPassEncoderEnd(_renderPass);
|
||||
wgpuRenderPassEncoderRelease(_renderPass);
|
||||
_renderPass = nullptr;
|
||||
}
|
||||
|
||||
// Clear with a render pass
|
||||
WGPURenderPassColorAttachment colorAttachment;
|
||||
WGPURenderPassDepthStencilAttachment depthStencilAttachment;
|
||||
WGPURenderPassDescriptor renderPassDesc = WGPU_RENDER_PASS_DESCRIPTOR_INIT;
|
||||
if (((GPUTextureWebGPU*)clear.View->GetParent())->IsDepthStencil())
|
||||
{
|
||||
renderPassDesc.depthStencilAttachment = &depthStencilAttachment;
|
||||
depthStencilAttachment = WGPU_RENDER_PASS_DEPTH_STENCIL_ATTACHMENT_INIT;
|
||||
depthStencilAttachment.view = clear.View->View;
|
||||
depthStencilAttachment.depthLoadOp = WGPULoadOp_Clear;
|
||||
depthStencilAttachment.depthStoreOp = WGPUStoreOp_Store;
|
||||
depthStencilAttachment.depthClearValue = clear.Depth;
|
||||
depthStencilAttachment.stencilClearValue = clear.Stencil;
|
||||
}
|
||||
else
|
||||
{
|
||||
renderPassDesc.colorAttachmentCount = 1;
|
||||
renderPassDesc.colorAttachments = &colorAttachment;
|
||||
colorAttachment = WGPU_RENDER_PASS_COLOR_ATTACHMENT_INIT;
|
||||
colorAttachment.view = clear.View->View;
|
||||
colorAttachment.depthSlice = clear.View->DepthSlice;
|
||||
colorAttachment.loadOp = WGPULoadOp_Clear;
|
||||
colorAttachment.storeOp = WGPUStoreOp_Store;
|
||||
if (clear.View->HasStencil)
|
||||
{
|
||||
depthStencilAttachment.stencilLoadOp = WGPULoadOp_Clear;
|
||||
depthStencilAttachment.stencilStoreOp = WGPUStoreOp_Store;
|
||||
depthStencilAttachment.stencilClearValue = clear.Stencil;
|
||||
}
|
||||
colorAttachment.clearValue = { clear.RGBA[0], clear.RGBA[1], clear.RGBA[2], clear.RGBA[3] };
|
||||
}
|
||||
auto renderPass = wgpuCommandEncoderBeginRenderPass(Encoder, &renderPassDesc);
|
||||
wgpuRenderPassEncoderEnd(renderPass);
|
||||
wgpuRenderPassEncoderRelease(renderPass);
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::OnDrawCall()
|
||||
{
|
||||
// Clear textures that are not bind to the render pass
|
||||
auto renderTargets = ToSpan(_renderTargets, _renderTargetCount);
|
||||
for (int32 i = _pendingClears.Count() - 1; i >= 0; i--)
|
||||
{
|
||||
auto clear = _pendingClears[i];
|
||||
if (clear.View != _depthStencil && SpanContains(renderTargets, clear.View))
|
||||
{
|
||||
ManualClear(clear);
|
||||
_pendingClears.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if need to start a new render pass
|
||||
if (_renderPassDirty)
|
||||
{
|
||||
_renderPassDirty = false;
|
||||
|
||||
// End existing pass (if any)
|
||||
if (_renderPass)
|
||||
{
|
||||
wgpuRenderPassEncoderEnd(_renderPass);
|
||||
wgpuRenderPassEncoderRelease(_renderPass);
|
||||
}
|
||||
|
||||
// Start a new render pass
|
||||
WGPURenderPassColorAttachment colorAttachments[GPU_MAX_RT_BINDED];
|
||||
WGPURenderPassDepthStencilAttachment depthStencilAttachment = WGPU_RENDER_PASS_DEPTH_STENCIL_ATTACHMENT_INIT;
|
||||
WGPURenderPassDescriptor renderPassDesc = WGPU_RENDER_PASS_DESCRIPTOR_INIT;
|
||||
renderPassDesc.colorAttachmentCount = _renderTargetCount;
|
||||
renderPassDesc.colorAttachments = colorAttachments;
|
||||
PendingClear clear;
|
||||
_pipelineKey.MultiSampleCount = 1;
|
||||
_pipelineKey.RenderTargetCount = _renderTargetCount;
|
||||
for (int32 i = 0; i < renderPassDesc.colorAttachmentCount; i++)
|
||||
{
|
||||
auto& colorAttachment = colorAttachments[i];
|
||||
colorAttachment = WGPU_RENDER_PASS_COLOR_ATTACHMENT_INIT;
|
||||
auto renderTarget = _renderTargets[i];
|
||||
colorAttachment.view = renderTarget->View;
|
||||
colorAttachment.depthSlice = renderTarget->DepthSlice;
|
||||
colorAttachment.loadOp = WGPULoadOp_Load;
|
||||
colorAttachment.storeOp = WGPUStoreOp_Store;
|
||||
if (FindClear(_depthStencil, clear))
|
||||
{
|
||||
colorAttachment.loadOp = WGPULoadOp_Clear;
|
||||
colorAttachment.clearValue = { clear.RGBA[0], clear.RGBA[1], clear.RGBA[2], clear.RGBA[3] };
|
||||
}
|
||||
_pipelineKey.MultiSampleCount = (int32)renderTarget->GetMSAA();
|
||||
_pipelineKey.RenderTargetFormats[i] = RenderToolsWebGPU::ToTextureFormat(renderTarget->GetFormat());
|
||||
}
|
||||
if (_depthStencil)
|
||||
{
|
||||
renderPassDesc.depthStencilAttachment = &depthStencilAttachment;
|
||||
depthStencilAttachment.view = _depthStencil->View;
|
||||
depthStencilAttachment.depthLoadOp = WGPULoadOp_Load;
|
||||
depthStencilAttachment.depthStoreOp = _depthStencil->ReadOnly ? WGPUStoreOp_Discard : WGPUStoreOp_Store;
|
||||
depthStencilAttachment.depthReadOnly = _depthStencil->ReadOnly;
|
||||
if (_depthStencil->HasStencil)
|
||||
{
|
||||
depthStencilAttachment.stencilLoadOp = WGPULoadOp_Load;
|
||||
depthStencilAttachment.stencilStoreOp = _depthStencil->ReadOnly ? WGPUStoreOp_Discard : WGPUStoreOp_Store;
|
||||
depthStencilAttachment.depthReadOnly = _depthStencil->ReadOnly;
|
||||
}
|
||||
else
|
||||
{
|
||||
depthStencilAttachment.stencilClearValue = 0;
|
||||
depthStencilAttachment.stencilLoadOp = WGPULoadOp_Clear;
|
||||
depthStencilAttachment.stencilStoreOp = WGPUStoreOp_Discard;
|
||||
depthStencilAttachment.stencilReadOnly = true;
|
||||
}
|
||||
if (FindClear(_depthStencil, clear))
|
||||
{
|
||||
depthStencilAttachment.depthLoadOp = WGPULoadOp_Clear;
|
||||
depthStencilAttachment.depthClearValue = clear.Depth;
|
||||
if (_depthStencil->HasStencil)
|
||||
{
|
||||
depthStencilAttachment.stencilLoadOp = WGPULoadOp_Clear;
|
||||
depthStencilAttachment.stencilClearValue = clear.Stencil;
|
||||
}
|
||||
}
|
||||
_pipelineKey.DepthStencilFormat = RenderToolsWebGPU::ToTextureFormat(_depthStencil->GetFormat());
|
||||
}
|
||||
else
|
||||
{
|
||||
_pipelineKey.DepthStencilFormat = WGPUTextureFormat_Undefined;
|
||||
}
|
||||
_renderPass = wgpuCommandEncoderBeginRenderPass(Encoder, &renderPassDesc);
|
||||
ASSERT(_renderPass);
|
||||
|
||||
// Discard texture clears (done manually or via render pass)
|
||||
_pendingClears.Clear();
|
||||
|
||||
// Apply pending state
|
||||
if (_stencilRef != 0)
|
||||
{
|
||||
wgpuRenderPassEncoderSetStencilReference(_renderPass, _stencilRef);
|
||||
}
|
||||
auto scissorRect = _scissorRect;
|
||||
// TODO: skip calling this if scissorRect is default (0, 0, attachment width, attachment height)
|
||||
wgpuRenderPassEncoderSetScissorRect(_renderPass, (uint32_t)scissorRect.GetX(), (uint32_t)scissorRect.GetY(), (uint32_t)scissorRect.GetWidth(), (uint32_t)scissorRect.GetHeight());
|
||||
auto viewport = _viewport;
|
||||
// TODO: skip calling this if viewport is default (0, 0, attachment width, attachment height, 0, 1)
|
||||
wgpuRenderPassEncoderSetViewport(_renderPass, viewport.X, viewport.Y, viewport.Width, viewport.Height, viewport.MinDepth, viewport.MaxDepth);
|
||||
|
||||
// Auto-dirty pipeline when new render pass starts
|
||||
if (_pipelineState)
|
||||
_pipelineDirty = true;
|
||||
_indexBufferDirty = true;
|
||||
_vertexBufferDirty = true;
|
||||
_bindGroupDirty = true;
|
||||
if (_blendFactorSet)
|
||||
_blendFactorDirty = true;
|
||||
}
|
||||
|
||||
// Flush rendering states
|
||||
if (_pipelineDirty)
|
||||
{
|
||||
_pipelineDirty = false;
|
||||
WGPURenderPipeline pipeline = _pipelineState ? _pipelineState->GetPipeline(_pipelineKey) : nullptr;
|
||||
wgpuRenderPassEncoderSetPipeline(_renderPass, pipeline);
|
||||
RENDER_STAT_PS_STATE_CHANGE();
|
||||
}
|
||||
if (_indexBufferDirty)
|
||||
{
|
||||
_indexBufferDirty = false;
|
||||
wgpuRenderPassEncoderSetIndexBuffer(_renderPass, _indexBuffer.Buffer, _indexBuffer32Bit ? WGPUIndexFormat_Uint32 : WGPUIndexFormat_Uint16, _indexBuffer.Offset, _indexBuffer.Size);
|
||||
}
|
||||
if (_vertexBufferDirty)
|
||||
{
|
||||
_vertexBufferDirty = false;
|
||||
for (int32 i = 0; i < _vertexBufferCount; i++)
|
||||
{
|
||||
auto vb = _vertexBuffers[i];
|
||||
wgpuRenderPassEncoderSetVertexBuffer(_renderPass, i, vb.Buffer, vb.Offset, vb.Size);
|
||||
}
|
||||
}
|
||||
if (_blendFactorDirty)
|
||||
{
|
||||
_blendFactorDirty = false;
|
||||
WGPUColor color = { _blendFactor.X, _blendFactor.Y, _blendFactor.Z, _blendFactor.W };
|
||||
wgpuRenderPassEncoderSetBlendConstant(_renderPass, &color);
|
||||
}
|
||||
if (_bindGroupDirty)
|
||||
{
|
||||
_bindGroupDirty = false;
|
||||
// TODO: bind _samplers
|
||||
// TODO: bind _constantBuffers
|
||||
// TODO: bind _shaderResources
|
||||
}
|
||||
}
|
||||
|
||||
void GPUContextWebGPU::OnDispatch(GPUShaderProgramCS* shader)
|
||||
{
|
||||
// TODO: add compute shaders support
|
||||
}
|
||||
|
||||
#endif
|
||||
144
Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.h
Normal file
144
Source/Engine/GraphicsDevice/WebGPU/GPUContextWebGPU.h
Normal file
@@ -0,0 +1,144 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Graphics/GPUContext.h"
|
||||
#include "Engine/Core/Math/Vector4.h"
|
||||
#include "GPUDeviceWebGPU.h"
|
||||
#include "GPUPipelineStateWebGPU.h"
|
||||
|
||||
#if GRAPHICS_API_WEBGPU
|
||||
|
||||
class GPUBufferWebGPU;
|
||||
class GPUSamplerWebGPU;
|
||||
class GPUTextureViewWebGPU;
|
||||
class GPUVertexLayoutWebGPU;
|
||||
class GPUConstantBufferWebGPU;
|
||||
|
||||
/// <summary>
|
||||
/// GPU Context for Web GPU backend.
|
||||
/// </summary>
|
||||
class GPUContextWebGPU : public GPUContext
|
||||
{
|
||||
private:
|
||||
struct BufferBind
|
||||
{
|
||||
WGPUBuffer Buffer;
|
||||
uint32 Size, Offset;
|
||||
};
|
||||
|
||||
struct PendingClear
|
||||
{
|
||||
GPUTextureViewWebGPU* View;
|
||||
union
|
||||
{
|
||||
float RGBA[4];
|
||||
struct
|
||||
{
|
||||
float Depth;
|
||||
uint32 Stencil;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
GPUDeviceWebGPU* _device;
|
||||
WGPUVertexBufferLayout _vertexBufferNullLayout;
|
||||
|
||||
// State tracking
|
||||
int32 _renderPassDirty : 1;
|
||||
int32 _pipelineDirty : 1;
|
||||
int32 _bindGroupDirty : 1;
|
||||
int32 _vertexBufferDirty : 1;
|
||||
int32 _indexBufferDirty : 1;
|
||||
int32 _indexBuffer32Bit : 1;
|
||||
int32 _blendFactorDirty : 1;
|
||||
int32 _blendFactorSet : 1;
|
||||
int32 _renderTargetCount : 3;
|
||||
int32 _vertexBufferCount : 3;
|
||||
uint32 _stencilRef;
|
||||
Float4 _blendFactor;
|
||||
Viewport _viewport;
|
||||
Rectangle _scissorRect;
|
||||
WGPURenderPassEncoder _renderPass;
|
||||
GPUTextureViewWebGPU* _depthStencil;
|
||||
GPUTextureViewWebGPU* _renderTargets[GPU_MAX_RT_BINDED];
|
||||
GPUConstantBufferWebGPU* _constantBuffers[GPU_MAX_CB_BINDED];
|
||||
GPUSamplerWebGPU* _samplers[GPU_MAX_SAMPLER_BINDED];
|
||||
BufferBind _indexBuffer;
|
||||
BufferBind _vertexBuffers[GPU_MAX_VB_BINDED];
|
||||
GPUPipelineStateWebGPU* _pipelineState;
|
||||
GPUPipelineStateWebGPU::Key _pipelineKey;
|
||||
Array<PendingClear, FixedAllocation<16>> _pendingClears;
|
||||
GPUResourceView* _shaderResources[GPU_MAX_SR_BINDED];
|
||||
GPUResourceView* _storageResources[GPU_MAX_SR_BINDED];
|
||||
|
||||
public:
|
||||
GPUContextWebGPU(GPUDeviceWebGPU* device);
|
||||
~GPUContextWebGPU();
|
||||
|
||||
public:
|
||||
// Handle to the WebGPU command encoder object.
|
||||
WGPUCommandEncoder Encoder = nullptr;
|
||||
|
||||
private:
|
||||
bool FindClear(const GPUTextureViewWebGPU* view, PendingClear& clear);
|
||||
void ManualClear(const PendingClear& clear);
|
||||
void OnDrawCall();
|
||||
void OnDispatch(GPUShaderProgramCS* shader);
|
||||
|
||||
public:
|
||||
// [GPUContext]
|
||||
void FrameBegin() override;
|
||||
void FrameEnd() override;
|
||||
void* GetNativePtr() const override;
|
||||
bool IsDepthBufferBinded() override;
|
||||
void Clear(GPUTextureView* rt, const Color& color) override;
|
||||
void ClearDepth(GPUTextureView* depthBuffer, float depthValue, uint8 stencilValue) override;
|
||||
void ClearUA(GPUBuffer* buf, const Float4& value) override;
|
||||
void ClearUA(GPUBuffer* buf, const uint32 value[4]) override;
|
||||
void ClearUA(GPUTexture* texture, const uint32 value[4]) override;
|
||||
void ClearUA(GPUTexture* texture, const Float4& value) override;
|
||||
void ResetRenderTarget() override;
|
||||
void SetRenderTarget(GPUTextureView* rt) override;
|
||||
void SetRenderTarget(GPUTextureView* depthBuffer, GPUTextureView* rt) override;
|
||||
void SetRenderTarget(GPUTextureView* depthBuffer, const Span<GPUTextureView*>& rts) override;
|
||||
void SetBlendFactor(const Float4& value) override;
|
||||
void SetStencilRef(uint32 value) override;
|
||||
void ResetSR() override;
|
||||
void ResetUA() override;
|
||||
void ResetCB() override;
|
||||
void BindCB(int32 slot, GPUConstantBuffer* cb) override;
|
||||
void BindSR(int32 slot, GPUResourceView* view) override;
|
||||
void BindUA(int32 slot, GPUResourceView* view) override;
|
||||
void BindVB(const Span<GPUBuffer*>& vertexBuffers, const uint32* vertexBuffersOffsets = nullptr, GPUVertexLayout* vertexLayout = nullptr) override;
|
||||
void BindIB(GPUBuffer* indexBuffer) override;
|
||||
void BindSampler(int32 slot, GPUSampler* sampler) override;
|
||||
void UpdateCB(GPUConstantBuffer* cb, const void* data) override;
|
||||
void Dispatch(GPUShaderProgramCS* shader, uint32 threadGroupCountX, uint32 threadGroupCountY, uint32 threadGroupCountZ) override;
|
||||
void DispatchIndirect(GPUShaderProgramCS* shader, GPUBuffer* bufferForArgs, uint32 offsetForArgs) override;
|
||||
void ResolveMultisample(GPUTexture* sourceMultisampleTexture, GPUTexture* destTexture, int32 sourceSubResource, int32 destSubResource, PixelFormat format) override;
|
||||
void DrawInstanced(uint32 verticesCount, uint32 instanceCount, int32 startInstance, int32 startVertex) override;
|
||||
void DrawIndexedInstanced(uint32 indicesCount, uint32 instanceCount, int32 startInstance, int32 startVertex, int32 startIndex) override;
|
||||
void DrawInstancedIndirect(GPUBuffer* bufferForArgs, uint32 offsetForArgs) override;
|
||||
void DrawIndexedInstancedIndirect(GPUBuffer* bufferForArgs, uint32 offsetForArgs) override;
|
||||
uint64 BeginQuery(GPUQueryType type) override;
|
||||
void EndQuery(uint64 queryID) override;
|
||||
void SetViewport(const Viewport& viewport) override;
|
||||
void SetScissor(const Rectangle& scissorRect) override;
|
||||
void SetDepthBounds(float minDepth, float maxDepth) override;
|
||||
GPUPipelineState* GetState() const override;
|
||||
void SetState(GPUPipelineState* state) override;
|
||||
void ResetState() override;
|
||||
void FlushState() override;
|
||||
void Flush() override;
|
||||
void UpdateBuffer(GPUBuffer* buffer, const void* data, uint32 size, uint32 offset) override;
|
||||
void CopyBuffer(GPUBuffer* dstBuffer, GPUBuffer* srcBuffer, uint32 size, uint32 dstOffset, uint32 srcOffset) override;
|
||||
void UpdateTexture(GPUTexture* texture, int32 arrayIndex, int32 mipIndex, const void* data, uint32 rowPitch, uint32 slicePitch) override;
|
||||
void CopyTexture(GPUTexture* dstResource, uint32 dstSubresource, uint32 dstX, uint32 dstY, uint32 dstZ, GPUTexture* srcResource, uint32 srcSubresource) override;
|
||||
void ResetCounter(GPUBuffer* buffer) override;
|
||||
void CopyCounter(GPUBuffer* dstBuffer, uint32 dstOffset, GPUBuffer* srcBuffer) override;
|
||||
void CopyResource(GPUResource* dstResource, GPUResource* srcResource) override;
|
||||
void CopySubresource(GPUResource* dstResource, uint32 dstSubresource, GPUResource* srcResource, uint32 srcSubresource) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
581
Source/Engine/GraphicsDevice/WebGPU/GPUDeviceWebGPU.cpp
Normal file
581
Source/Engine/GraphicsDevice/WebGPU/GPUDeviceWebGPU.cpp
Normal file
@@ -0,0 +1,581 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#if GRAPHICS_API_WEBGPU
|
||||
|
||||
#include "GPUDeviceWebGPU.h"
|
||||
#include "GPUShaderWebGPU.h"
|
||||
#include "GPUContextWebGPU.h"
|
||||
#include "GPUPipelineStateWebGPU.h"
|
||||
#include "GPUTextureWebGPU.h"
|
||||
#include "GPUBufferWebGPU.h"
|
||||
#include "GPUSamplerWebGPU.h"
|
||||
#include "GPUAdapterWebGPU.h"
|
||||
#include "GPUVertexLayoutWebGPU.h"
|
||||
#include "GPUSwapChainWebGPU.h"
|
||||
#include "RenderToolsWebGPU.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Utilities.h"
|
||||
#if BUILD_DEBUG
|
||||
#include "Engine/Core/Collections/Sorting.h"
|
||||
#endif
|
||||
#include "Engine/Graphics/PixelFormatExtensions.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include <emscripten/emscripten.h>
|
||||
|
||||
GPUVertexLayoutWebGPU::GPUVertexLayoutWebGPU(GPUDeviceWebGPU* device, const Elements& elements, bool explicitOffsets)
|
||||
: GPUResourceBase<GPUDeviceWebGPU, GPUVertexLayout>(device, StringView::Empty)
|
||||
{
|
||||
SetElements(elements, explicitOffsets);
|
||||
Layout = WGPU_VERTEX_BUFFER_LAYOUT_INIT;
|
||||
Layout.stepMode = WGPUVertexStepMode_Vertex;
|
||||
Layout.arrayStride = GetStride();
|
||||
Layout.attributeCount = elements.Count();
|
||||
Layout.attributes = Attributes;
|
||||
const VertexElement* srcElements = GetElements().Get();
|
||||
for (int32 i = 0; i < elements.Count(); i++)
|
||||
{
|
||||
const VertexElement& src = srcElements[i];
|
||||
WGPUVertexAttribute& dst = Attributes[i];
|
||||
dst.nextInChain = nullptr;
|
||||
dst.format = RenderToolsWebGPU::ToVertexFormat(src.Format);
|
||||
dst.offset = src.Offset;
|
||||
dst.shaderLocation = src.Slot;
|
||||
if (src.PerInstance)
|
||||
Layout.stepMode = WGPUVertexStepMode_Instance;
|
||||
}
|
||||
}
|
||||
|
||||
GPUDeviceWebGPU::GPUDeviceWebGPU(WGPUInstance instance, GPUAdapterWebGPU* adapter)
|
||||
: GPUDevice(RendererType::WebGPU, ShaderProfile::WebGPU)
|
||||
, Adapter(adapter)
|
||||
, WebGPUInstance(instance)
|
||||
{
|
||||
}
|
||||
|
||||
bool GPUDeviceWebGPU::Init()
|
||||
{
|
||||
// Init device limits
|
||||
Array<WGPUFeatureName, FixedAllocation<32>> features;
|
||||
#define CHECK_FEATURE(feature) if (wgpuAdapterHasFeature(Adapter->Adapter, feature)) features.Add(feature)
|
||||
CHECK_FEATURE(WGPUFeatureName_CoreFeaturesAndLimits);
|
||||
CHECK_FEATURE(WGPUFeatureName_DepthClipControl);
|
||||
CHECK_FEATURE(WGPUFeatureName_Depth32FloatStencil8);
|
||||
CHECK_FEATURE(WGPUFeatureName_TextureCompressionBC);
|
||||
CHECK_FEATURE(WGPUFeatureName_TextureCompressionBCSliced3D);
|
||||
CHECK_FEATURE(WGPUFeatureName_TextureCompressionETC2);
|
||||
CHECK_FEATURE(WGPUFeatureName_TextureCompressionASTC);
|
||||
CHECK_FEATURE(WGPUFeatureName_TextureCompressionASTCSliced3D);
|
||||
CHECK_FEATURE(WGPUFeatureName_TimestampQuery);
|
||||
CHECK_FEATURE(WGPUFeatureName_IndirectFirstInstance);
|
||||
CHECK_FEATURE(WGPUFeatureName_RG11B10UfloatRenderable);
|
||||
CHECK_FEATURE(WGPUFeatureName_BGRA8UnormStorage);
|
||||
CHECK_FEATURE(WGPUFeatureName_Float32Filterable);
|
||||
CHECK_FEATURE(WGPUFeatureName_Float32Blendable);
|
||||
CHECK_FEATURE(WGPUFeatureName_ClipDistances);
|
||||
CHECK_FEATURE(WGPUFeatureName_TextureFormatsTier1);
|
||||
CHECK_FEATURE(WGPUFeatureName_TextureFormatsTier2);
|
||||
CHECK_FEATURE(WGPUFeatureName_PrimitiveIndex);
|
||||
CHECK_FEATURE(WGPUFeatureName_MultiDrawIndirect);
|
||||
#undef CHECK_FEATURE
|
||||
#if BUILD_DEBUG
|
||||
LOG(Info, "WebGPU features:");
|
||||
#define LOG_FEATURE(feature) if (features.Contains(feature)) LOG(Info, " > {}", TEXT(#feature))
|
||||
LOG_FEATURE(WGPUFeatureName_CoreFeaturesAndLimits);
|
||||
LOG_FEATURE(WGPUFeatureName_DepthClipControl);
|
||||
LOG_FEATURE(WGPUFeatureName_Depth32FloatStencil8);
|
||||
LOG_FEATURE(WGPUFeatureName_TextureCompressionBC);
|
||||
LOG_FEATURE(WGPUFeatureName_TextureCompressionBCSliced3D);
|
||||
LOG_FEATURE(WGPUFeatureName_TextureCompressionETC2);
|
||||
LOG_FEATURE(WGPUFeatureName_TextureCompressionASTC);
|
||||
LOG_FEATURE(WGPUFeatureName_TextureCompressionASTCSliced3D);
|
||||
LOG_FEATURE(WGPUFeatureName_TimestampQuery);
|
||||
LOG_FEATURE(WGPUFeatureName_IndirectFirstInstance);
|
||||
LOG_FEATURE(WGPUFeatureName_RG11B10UfloatRenderable);
|
||||
LOG_FEATURE(WGPUFeatureName_BGRA8UnormStorage);
|
||||
LOG_FEATURE(WGPUFeatureName_Float32Filterable);
|
||||
LOG_FEATURE(WGPUFeatureName_Float32Blendable);
|
||||
LOG_FEATURE(WGPUFeatureName_ClipDistances);
|
||||
LOG_FEATURE(WGPUFeatureName_TextureFormatsTier1);
|
||||
LOG_FEATURE(WGPUFeatureName_TextureFormatsTier2);
|
||||
LOG_FEATURE(WGPUFeatureName_PrimitiveIndex);
|
||||
LOG_FEATURE(WGPUFeatureName_MultiDrawIndirect);
|
||||
#undef LOG_FEATURE
|
||||
#endif
|
||||
for (int32 i = 0; i < (int32)PixelFormat::MAX; i++)
|
||||
FeaturesPerFormat[i] = FormatFeatures(MSAALevel::None, FormatSupport::None);
|
||||
WGPULimits limits = WGPU_LIMITS_INIT;
|
||||
if (wgpuAdapterGetLimits(Adapter->Adapter, &limits) == WGPUStatus_Success)
|
||||
{
|
||||
Limits.HasDepthClip = features.Contains(WGPUFeatureName_DepthClipControl);
|
||||
Limits.HasReadOnlyDepth = true;
|
||||
Limits.MaximumTexture1DSize = Math::Min<int32>(GPU_MAX_TEXTURE_SIZE, limits.maxTextureDimension1D);
|
||||
Limits.MaximumTexture2DSize = Math::Min<int32>(GPU_MAX_TEXTURE_SIZE, limits.maxTextureDimension2D);
|
||||
Limits.MaximumTexture3DSize = Math::Min<int32>(GPU_MAX_TEXTURE_SIZE, limits.maxTextureDimension3D);
|
||||
Limits.MaximumMipLevelsCount = Math::Min<int32>(GPU_MAX_TEXTURE_MIP_LEVELS, (int32)log2(limits.maxTextureDimension2D));
|
||||
Limits.MaximumTexture1DArraySize = Limits.MaximumTexture2DArraySize = Math::Min<int32>(GPU_MAX_TEXTURE_ARRAY_SIZE, limits.maxTextureArrayLayers);
|
||||
if (limits.maxTextureArrayLayers >= 6)
|
||||
Limits.MaximumTextureCubeSize = Limits.MaximumTexture2DSize;
|
||||
|
||||
// Formats support based on: https://gpuweb.github.io/gpuweb/#plain-color-formats
|
||||
auto supportsBuffer =
|
||||
FormatSupport::Buffer |
|
||||
FormatSupport::InputAssemblyIndexBuffer |
|
||||
FormatSupport::InputAssemblyVertexBuffer;
|
||||
auto supportsTexture =
|
||||
FormatSupport::Texture1D |
|
||||
FormatSupport::Texture2D |
|
||||
FormatSupport::Texture3D |
|
||||
FormatSupport::TextureCube |
|
||||
FormatSupport::ShaderLoad |
|
||||
FormatSupport::ShaderSample |
|
||||
FormatSupport::ShaderSampleComparison |
|
||||
FormatSupport::ShaderGather |
|
||||
FormatSupport::ShaderGatherComparison |
|
||||
FormatSupport::Mip;
|
||||
auto supportsRender =
|
||||
FormatSupport::RenderTarget |
|
||||
FormatSupport::Blendable;
|
||||
auto supportsDepth =
|
||||
FormatSupport::DepthStencil;
|
||||
auto supportsMultisampling =
|
||||
FormatSupport::MultisampleRenderTarget |
|
||||
FormatSupport::MultisampleLoad;
|
||||
auto supportsMSAA =
|
||||
FormatSupport::MultisampleRenderTarget |
|
||||
FormatSupport::MultisampleResolve |
|
||||
FormatSupport::MultisampleLoad;
|
||||
|
||||
#define ADD_FORMAT(pixelFormat, support)
|
||||
FeaturesPerFormat[(int32)PixelFormat::R8_UNorm].Support |= supportsBuffer | supportsTexture | supportsRender | supportsMSAA;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R8_UInt].Support |= supportsBuffer | supportsTexture | supportsRender;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R8_SInt].Support |= supportsBuffer | supportsTexture | supportsRender;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16_UInt].Support |= supportsBuffer | supportsTexture;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16_SInt].Support |= supportsBuffer | supportsTexture;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16_Float].Support |= supportsBuffer | supportsTexture | supportsRender | supportsMSAA;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R8G8_UNorm].Support |= supportsBuffer | supportsTexture;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R8G8_SNorm].Support |= supportsBuffer | supportsTexture;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R8G8_UInt].Support |= supportsBuffer | supportsTexture;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R8G8_SInt].Support |= supportsBuffer | supportsTexture;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R32_Float].Support |= supportsBuffer | supportsTexture | FormatSupport::RenderTarget;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R32_Float].Support & ~FormatSupport::ShaderSample;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R32_UInt].Support |= supportsBuffer | supportsTexture | FormatSupport::RenderTarget;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R32_SInt].Support |= supportsBuffer | supportsTexture | FormatSupport::RenderTarget;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16_UInt].Support |= supportsBuffer | supportsTexture;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16_SInt].Support |= supportsBuffer | supportsTexture;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16_Float].Support |= supportsBuffer | supportsTexture | supportsRender | supportsMSAA;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R8G8B8A8_UNorm].Support |= supportsBuffer | supportsTexture | supportsRender | supportsMSAA;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R8G8B8A8_UNorm_sRGB].Support |= supportsBuffer | supportsTexture | supportsRender | supportsMSAA;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R8G8B8A8_SNorm].Support |= supportsBuffer | supportsTexture;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R8G8B8A8_UInt].Support |= supportsBuffer | supportsTexture;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R8G8B8A8_SInt].Support |= supportsBuffer | supportsTexture;
|
||||
FeaturesPerFormat[(int32)PixelFormat::B8G8R8A8_UNorm].Support |= supportsBuffer | supportsTexture | supportsRender | supportsMSAA;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R10G10B10A2_UInt].Support |= supportsBuffer | supportsTexture;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R10G10B10A2_UNorm].Support |= supportsBuffer | supportsTexture | supportsRender | supportsMSAA;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R11G11B10_Float].Support |= supportsBuffer | supportsTexture;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R9G9B9E5_SharedExp].Support |= supportsBuffer | supportsTexture;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R32G32_Float].Support |= supportsBuffer | supportsTexture | FormatSupport::RenderTarget;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R32G32_Float].Support & ~FormatSupport::ShaderSample;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R32G32_UInt].Support |= supportsBuffer | supportsTexture | FormatSupport::RenderTarget;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R32G32_SInt].Support |= supportsBuffer | supportsTexture | FormatSupport::RenderTarget;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16B16A16_UInt].Support |= supportsBuffer | supportsTexture;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16B16A16_SInt].Support |= supportsBuffer | supportsTexture;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16B16A16_Float].Support |= supportsBuffer | supportsTexture | supportsRender;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R32G32B32A32_Float].Support |= supportsBuffer | supportsTexture | FormatSupport::RenderTarget;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R32G32B32A32_Float].Support & ~FormatSupport::ShaderSample;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R32G32B32A32_UInt].Support |= supportsBuffer | supportsTexture | FormatSupport::RenderTarget;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R32G32B32A32_SInt].Support |= supportsBuffer | supportsTexture | FormatSupport::RenderTarget;
|
||||
FeaturesPerFormat[(int32)PixelFormat::D16_UNorm].Support |= supportsBuffer | supportsTexture | supportsDepth;
|
||||
FeaturesPerFormat[(int32)PixelFormat::D24_UNorm_S8_UInt].Support |= supportsBuffer | supportsTexture | supportsDepth;
|
||||
FeaturesPerFormat[(int32)PixelFormat::D32_Float].Support |= supportsBuffer | supportsTexture | supportsDepth;
|
||||
if (features.Contains(WGPUFeatureName_CoreFeaturesAndLimits))
|
||||
{
|
||||
FeaturesPerFormat[(int32)PixelFormat::B8G8R8A8_UNorm_sRGB].Support |= supportsBuffer | supportsTexture | supportsRender | supportsMSAA;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R8_UInt].Support |= supportsMultisampling;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R8_SInt].Support |= supportsMultisampling;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R8G8_UInt].Support |= supportsMultisampling;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R8G8_SInt].Support |= supportsMultisampling;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R8G8B8A8_UInt].Support |= supportsMultisampling;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R8G8B8A8_SInt].Support |= supportsMultisampling;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16_UInt].Support |= supportsMultisampling;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16_SInt].Support |= supportsMultisampling;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16_UInt].Support |= supportsMultisampling;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16_SInt].Support |= supportsMultisampling;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16B16A16_UInt].Support |= supportsMultisampling;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16B16A16_SInt].Support |= supportsMultisampling;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16B16A16_Float].Support |= supportsMSAA;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R32_Float].Support |= supportsMultisampling;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R10G10B10A2_UInt].Support |= supportsMultisampling;
|
||||
}
|
||||
if (features.Contains(WGPUFeatureName_TextureFormatsTier1))
|
||||
{
|
||||
FeaturesPerFormat[(int32)PixelFormat::R8_SNorm].Support |= supportsBuffer | supportsTexture;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16_UNorm].Support |= supportsBuffer | supportsTexture | supportsRender | supportsMSAA;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16_SNorm].Support |= supportsBuffer | supportsTexture;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16_UNorm].Support |= supportsBuffer | supportsTexture | supportsRender | supportsMultisampling;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16_SNorm].Support |= supportsBuffer | supportsTexture | supportsRender | supportsMultisampling;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16B16A16_UNorm].Support |= supportsBuffer | supportsTexture | supportsRender | supportsMultisampling;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R16G16B16A16_SNorm].Support |= supportsBuffer | supportsTexture | supportsRender | supportsMultisampling;
|
||||
}
|
||||
if (features.Contains(WGPUFeatureName_TextureFormatsTier2))
|
||||
{
|
||||
}
|
||||
if (features.Contains(WGPUFeatureName_Depth32FloatStencil8))
|
||||
{
|
||||
FeaturesPerFormat[(int32)PixelFormat::D32_Float_S8X24_UInt].Support |= supportsBuffer | supportsTexture | supportsDepth;
|
||||
}
|
||||
if (features.Contains(WGPUFeatureName_Float32Blendable))
|
||||
{
|
||||
FeaturesPerFormat[(int32)PixelFormat::R32_Float].Support |= FormatSupport::Blendable;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R32G32_Float].Support |= FormatSupport::Blendable;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R32G32B32A32_Float].Support |= FormatSupport::Blendable;
|
||||
}
|
||||
if (features.Contains(WGPUFeatureName_Float32Filterable))
|
||||
{
|
||||
FeaturesPerFormat[(int32)PixelFormat::R32_Float].Support |= FormatSupport::ShaderSample;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R32G32_Float].Support |= FormatSupport::ShaderSample;
|
||||
FeaturesPerFormat[(int32)PixelFormat::R32G32B32A32_Float].Support |= FormatSupport::ShaderSample;
|
||||
}
|
||||
if (features.Contains(WGPUFeatureName_RG11B10UfloatRenderable))
|
||||
{
|
||||
FeaturesPerFormat[(int32)PixelFormat::R11G11B10_Float].Support |= supportsRender | supportsMSAA;
|
||||
}
|
||||
if (features.Contains(WGPUFeatureName_TextureCompressionBC))
|
||||
{
|
||||
auto supportBc =
|
||||
FormatSupport::Texture2D |
|
||||
FormatSupport::ShaderLoad |
|
||||
FormatSupport::ShaderSample |
|
||||
FormatSupport::Mip;
|
||||
if (features.Contains(WGPUFeatureName_TextureCompressionBCSliced3D))
|
||||
supportBc |= FormatSupport::Texture3D;
|
||||
FeaturesPerFormat[(int32)PixelFormat::BC1_UNorm].Support |= supportBc;
|
||||
FeaturesPerFormat[(int32)PixelFormat::BC1_UNorm_sRGB].Support |= supportBc;
|
||||
FeaturesPerFormat[(int32)PixelFormat::BC2_UNorm].Support |= supportBc;
|
||||
FeaturesPerFormat[(int32)PixelFormat::BC2_UNorm_sRGB].Support |= supportBc;
|
||||
FeaturesPerFormat[(int32)PixelFormat::BC3_UNorm].Support |= supportBc;
|
||||
FeaturesPerFormat[(int32)PixelFormat::BC3_UNorm_sRGB].Support |= supportBc;
|
||||
FeaturesPerFormat[(int32)PixelFormat::BC4_UNorm].Support |= supportBc;
|
||||
FeaturesPerFormat[(int32)PixelFormat::BC4_SNorm].Support |= supportBc;
|
||||
FeaturesPerFormat[(int32)PixelFormat::BC5_UNorm].Support |= supportBc;
|
||||
FeaturesPerFormat[(int32)PixelFormat::BC5_SNorm].Support |= supportBc;
|
||||
FeaturesPerFormat[(int32)PixelFormat::BC6H_Uf16].Support |= supportBc;
|
||||
FeaturesPerFormat[(int32)PixelFormat::BC6H_Sf16].Support |= supportBc;
|
||||
FeaturesPerFormat[(int32)PixelFormat::BC7_UNorm].Support |= supportBc;
|
||||
FeaturesPerFormat[(int32)PixelFormat::BC7_UNorm_sRGB].Support |= supportBc;
|
||||
}
|
||||
if (features.Contains(WGPUFeatureName_TextureCompressionASTC))
|
||||
{
|
||||
auto supportAstc =
|
||||
FormatSupport::Texture2D |
|
||||
FormatSupport::ShaderLoad |
|
||||
FormatSupport::ShaderSample |
|
||||
FormatSupport::Mip;
|
||||
if (features.Contains(WGPUFeatureName_TextureCompressionASTCSliced3D))
|
||||
supportAstc |= FormatSupport::Texture3D;
|
||||
FeaturesPerFormat[(int32)PixelFormat::ASTC_4x4_UNorm].Support |= supportAstc;
|
||||
FeaturesPerFormat[(int32)PixelFormat::ASTC_4x4_UNorm_sRGB].Support |= supportAstc;
|
||||
FeaturesPerFormat[(int32)PixelFormat::ASTC_6x6_UNorm].Support |= supportAstc;
|
||||
FeaturesPerFormat[(int32)PixelFormat::ASTC_6x6_UNorm_sRGB].Support |= supportAstc;
|
||||
FeaturesPerFormat[(int32)PixelFormat::ASTC_8x8_UNorm].Support |= supportAstc;
|
||||
FeaturesPerFormat[(int32)PixelFormat::ASTC_8x8_UNorm_sRGB].Support |= supportAstc;
|
||||
FeaturesPerFormat[(int32)PixelFormat::ASTC_10x10_UNorm].Support |= supportAstc;
|
||||
FeaturesPerFormat[(int32)PixelFormat::ASTC_10x10_UNorm_sRGB].Support |= supportAstc;
|
||||
}
|
||||
for (auto& e : FeaturesPerFormat)
|
||||
{
|
||||
if (EnumHasAllFlags(e.Support, FormatSupport::MultisampleRenderTarget))
|
||||
e.MSAALevelMax = MSAALevel::X4;
|
||||
}
|
||||
#undef ADD_FORMAT
|
||||
|
||||
#if BUILD_DEBUG
|
||||
LOG(Info, "WebGPU limits:");
|
||||
LOG(Info, " > maxTextureDimension1D = {}", limits.maxTextureDimension1D);
|
||||
LOG(Info, " > maxTextureDimension2D = {}", limits.maxTextureDimension2D);
|
||||
LOG(Info, " > maxTextureDimension3D = {}", limits.maxTextureDimension3D);
|
||||
LOG(Info, " > maxTextureArrayLayers = {}", limits.maxTextureArrayLayers);
|
||||
LOG(Info, " > maxBindGroups = {}", limits.maxBindGroups);
|
||||
LOG(Info, " > maxBindGroupsPlusVertexBuffers = {}", limits.maxBindGroupsPlusVertexBuffers);
|
||||
LOG(Info, " > maxBindingsPerBindGroup = {}", limits.maxBindingsPerBindGroup);
|
||||
LOG(Info, " > maxDynamicUniformBuffersPerPipelineLayout = {}", limits.maxDynamicUniformBuffersPerPipelineLayout);
|
||||
LOG(Info, " > maxDynamicStorageBuffersPerPipelineLayout = {}", limits.maxDynamicStorageBuffersPerPipelineLayout);
|
||||
LOG(Info, " > maxSampledTexturesPerShaderStage = {}", limits.maxSampledTexturesPerShaderStage);
|
||||
LOG(Info, " > maxSamplersPerShaderStage = {}", limits.maxSamplersPerShaderStage);
|
||||
LOG(Info, " > maxStorageBuffersPerShaderStage = {}", limits.maxStorageBuffersPerShaderStage);
|
||||
LOG(Info, " > maxStorageTexturesPerShaderStage = {}", limits.maxStorageTexturesPerShaderStage);
|
||||
LOG(Info, " > maxUniformBuffersPerShaderStage = {}", limits.maxUniformBuffersPerShaderStage);
|
||||
LOG(Info, " > maxUniformBufferBindingSize = {}", limits.maxUniformBufferBindingSize);
|
||||
LOG(Info, " > maxStorageBufferBindingSize = {}", limits.maxStorageBufferBindingSize);
|
||||
LOG(Info, " > minUniformBufferOffsetAlignment = {}", limits.minUniformBufferOffsetAlignment);
|
||||
LOG(Info, " > minStorageBufferOffsetAlignment = {}", limits.minStorageBufferOffsetAlignment);
|
||||
LOG(Info, " > maxVertexBuffers = {}", limits.maxVertexBuffers);
|
||||
LOG(Info, " > maxBufferSize = {}", limits.maxBufferSize);
|
||||
LOG(Info, " > maxVertexAttributes = {}", limits.maxVertexAttributes);
|
||||
LOG(Info, " > maxVertexBufferArrayStride = {}", limits.maxVertexBufferArrayStride);
|
||||
LOG(Info, " > maxInterStageShaderVariables = {}", limits.maxInterStageShaderVariables);
|
||||
LOG(Info, " > maxColorAttachments = {}", limits.maxColorAttachments);
|
||||
LOG(Info, " > maxComputeWorkgroupStorageSize = {}", limits.maxComputeWorkgroupStorageSize);
|
||||
LOG(Info, " > maxComputeInvocationsPerWorkgroup = {}", limits.maxComputeInvocationsPerWorkgroup);
|
||||
LOG(Info, " > maxComputeWorkgroupSize = {}x{}x{}", limits.maxComputeWorkgroupSizeX, limits.maxComputeWorkgroupSizeY, limits.maxComputeWorkgroupSizeZ);
|
||||
LOG(Info, " > maxComputeWorkgroupsPerDimension = {}", limits.maxComputeWorkgroupsPerDimension);
|
||||
LOG(Info, " > maxImmediateSize = {}", limits.maxImmediateSize);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Inti device options
|
||||
WGPUDeviceDescriptor deviceDesc = WGPU_DEVICE_DESCRIPTOR_INIT;
|
||||
deviceDesc.requiredLimits = &limits;
|
||||
deviceDesc.requiredFeatureCount = features.Count();
|
||||
deviceDesc.requiredFeatures = features.Get();
|
||||
deviceDesc.deviceLostCallbackInfo.mode = WGPUCallbackMode_AllowSpontaneous;
|
||||
deviceDesc.deviceLostCallbackInfo.callback = [](WGPUDevice const* device, WGPUDeviceLostReason reason, WGPUStringView message, WGPU_NULLABLE void* userdata1, WGPU_NULLABLE void* userdata2)
|
||||
{
|
||||
if (reason == WGPUDeviceLostReason_Destroyed ||
|
||||
reason == WGPUDeviceLostReason_FailedCreation ||
|
||||
reason == WGPUDeviceLostReason_CallbackCancelled)
|
||||
return;
|
||||
Platform::Fatal(String::Format(TEXT("WebGPU device lost! {}, 0x:{:x}"), WEBGPU_TO_STR(message), (uint32)reason), nullptr, FatalErrorType::GPUHang);
|
||||
};
|
||||
deviceDesc.uncapturedErrorCallbackInfo.callback = [](WGPUDevice const* device, WGPUErrorType type, WGPUStringView message, WGPU_NULLABLE void* userdata1, WGPU_NULLABLE void* userdata2)
|
||||
{
|
||||
if (type == WGPUErrorType_OutOfMemory)
|
||||
{
|
||||
Platform::Fatal(String::Format(TEXT("WebGPU device run out of memory! {}"), WEBGPU_TO_STR(message)), nullptr, FatalErrorType::GPUOutOfMemory);
|
||||
}
|
||||
#if !BUILD_RELEASE
|
||||
else if (type == WGPUErrorType_Validation)
|
||||
{
|
||||
LOG(Warning, "WebGPU Validation: {}", WEBGPU_TO_STR(message));
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(Info, "WebGPU: {}", WEBGPU_TO_STR(message));
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
// Create device
|
||||
struct DeviceUserData : AsyncCallbackDataWebGPU
|
||||
{
|
||||
WGPUDevice Device = nullptr;
|
||||
};
|
||||
AsyncCallbackWebGPU<WGPURequestDeviceCallbackInfo, DeviceUserData> deviceRequest(WGPU_REQUEST_DEVICE_CALLBACK_INFO_INIT);
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
deviceDesc.label = WEBGPU_STR("Flax Device");
|
||||
deviceDesc.defaultQueue.label = WEBGPU_STR("Flax Queue");
|
||||
#endif
|
||||
deviceRequest.Info.callback = [](WGPURequestDeviceStatus status, WGPUDevice device, WGPUStringView message, WGPU_NULLABLE void* userdata1, WGPU_NULLABLE void* userdata2)
|
||||
{
|
||||
DeviceUserData& userData = *reinterpret_cast<DeviceUserData*>(userdata1);
|
||||
userData.Device = device;
|
||||
userData.Call(status == WGPURequestDeviceStatus_Success, status, message);
|
||||
};
|
||||
wgpuAdapterRequestDevice(Adapter->Adapter, &deviceDesc, deviceRequest.Info);
|
||||
auto deviceRequestResult = deviceRequest.Wait();
|
||||
if (deviceRequestResult == WGPUWaitStatus_TimedOut)
|
||||
{
|
||||
LOG(Fatal, "WebGPU device request has timed out after {}s", deviceRequest.Data.WaitTime);
|
||||
return true;
|
||||
}
|
||||
if (deviceRequestResult == WGPUWaitStatus_Error)
|
||||
{
|
||||
LOG(Fatal, "Failed to get WebGPU device (browser might not support it). Error: {}, 0x{:x}", deviceRequest.Data.Message, deviceRequest.Data.Status);
|
||||
return true;
|
||||
}
|
||||
Device = deviceRequest.Data.Device;
|
||||
|
||||
TotalGraphicsMemory = 1024 * 1024 * 1024; // Dummy 1GB
|
||||
|
||||
// Create default samplers
|
||||
auto samplerDesc = GPUSamplerDescription::New();
|
||||
#define INIT_SAMPLER(slot, filter, addressMode, compare) \
|
||||
DefaultSamplers[slot] = New<GPUSamplerWebGPU>(this); \
|
||||
samplerDesc.Filter = filter; \
|
||||
samplerDesc.AddressU = samplerDesc.AddressV = samplerDesc.AddressW = addressMode; \
|
||||
samplerDesc.ComparisonFunction = compare; \
|
||||
DefaultSamplers[slot]->Init(samplerDesc)
|
||||
INIT_SAMPLER(0, GPUSamplerFilter::Trilinear, GPUSamplerAddressMode::Clamp, GPUSamplerCompareFunction::Never);
|
||||
INIT_SAMPLER(1, GPUSamplerFilter::Point, GPUSamplerAddressMode::Clamp, GPUSamplerCompareFunction::Never);
|
||||
INIT_SAMPLER(2, GPUSamplerFilter::Trilinear, GPUSamplerAddressMode::Wrap, GPUSamplerCompareFunction::Never);
|
||||
INIT_SAMPLER(3, GPUSamplerFilter::Point, GPUSamplerAddressMode::Wrap, GPUSamplerCompareFunction::Never);
|
||||
INIT_SAMPLER(4, GPUSamplerFilter::Point, GPUSamplerAddressMode::Clamp, GPUSamplerCompareFunction::Less);
|
||||
INIT_SAMPLER(5, GPUSamplerFilter::Trilinear, GPUSamplerAddressMode::Clamp, GPUSamplerCompareFunction::Less);
|
||||
#undef INIT_SAMPLER
|
||||
|
||||
// Setup commands processing
|
||||
Queue = wgpuDeviceGetQueue(Device);
|
||||
_mainContext = New<GPUContextWebGPU>(this);
|
||||
|
||||
_state = DeviceState::Ready;
|
||||
return GPUDevice::Init();
|
||||
}
|
||||
|
||||
GPUDeviceWebGPU::~GPUDeviceWebGPU()
|
||||
{
|
||||
// Ensure to be disposed
|
||||
GPUDeviceWebGPU::Dispose();
|
||||
}
|
||||
|
||||
GPUDevice* CreateGPUDeviceWebGPU()
|
||||
{
|
||||
// Create instance
|
||||
WGPUInstanceDescriptor instanceDesc = WGPU_INSTANCE_DESCRIPTOR_INIT;
|
||||
WGPUInstance instance = wgpuCreateInstance(&instanceDesc);
|
||||
if (!instance)
|
||||
{
|
||||
LOG(Error, "Failed to create instance for WebGPU (browser might not support it).");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Init adapter options
|
||||
WGPURequestAdapterOptions adapterOptions = WGPU_REQUEST_ADAPTER_OPTIONS_INIT;
|
||||
#if !PLATFORM_WEB
|
||||
adapterOptions.powerPreference = WGPUPowerPreference_HighPerformance;
|
||||
#endif
|
||||
|
||||
// Create adapter
|
||||
struct AdapterUserData : AsyncCallbackDataWebGPU
|
||||
{
|
||||
WGPUAdapter Adapter = nullptr;
|
||||
};
|
||||
AsyncCallbackWebGPU<WGPURequestAdapterCallbackInfo, AdapterUserData> adapterRequest(WGPU_REQUEST_ADAPTER_CALLBACK_INFO_INIT);
|
||||
adapterRequest.Info.callback = [](WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message, WGPU_NULLABLE void* userdata1, WGPU_NULLABLE void* userdata2)
|
||||
{
|
||||
AdapterUserData& userData = *reinterpret_cast<AdapterUserData*>(userdata1);
|
||||
userData.Adapter = adapter;
|
||||
userData.Call(status == WGPURequestAdapterStatus_Success, status, message);
|
||||
};
|
||||
wgpuInstanceRequestAdapter(instance, &adapterOptions, adapterRequest.Info);
|
||||
auto adapterRequestResult = adapterRequest.Wait();
|
||||
if (adapterRequestResult == WGPUWaitStatus_TimedOut)
|
||||
{
|
||||
LOG(Fatal, "WebGPU adapter request has timed out after {}s", adapterRequest.Data.WaitTime);
|
||||
return nullptr;
|
||||
}
|
||||
if (adapterRequestResult == WGPUWaitStatus_Error)
|
||||
{
|
||||
LOG(Fatal, "Failed to get WebGPU adapter (browser might not support it). Error: {}, 0x{:x}", adapterRequest.Data.Message, adapterRequest.Data.Status);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create device
|
||||
auto device = New<GPUDeviceWebGPU>(instance, New<GPUAdapterWebGPU>(adapterRequest.Data.Adapter));
|
||||
if (device->Init())
|
||||
{
|
||||
LOG(Warning, "Graphics Device init failed");
|
||||
Delete(device);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
void GPUDeviceWebGPU::Dispose()
|
||||
{
|
||||
GPUDeviceLock lock(this);
|
||||
if (_state == DeviceState::Disposed)
|
||||
return;
|
||||
|
||||
// Begin destruction
|
||||
_state = DeviceState::Disposing;
|
||||
WaitForGPU();
|
||||
preDispose();
|
||||
|
||||
// Clear device resources
|
||||
SAFE_DELETE_GPU_RESOURCES(DefaultSamplers);
|
||||
SAFE_DELETE(_mainContext);
|
||||
SAFE_DELETE(Adapter);
|
||||
if (Queue)
|
||||
{
|
||||
wgpuQueueRelease(Queue);
|
||||
Queue = nullptr;
|
||||
}
|
||||
if (Device)
|
||||
{
|
||||
wgpuDeviceRelease(Device);
|
||||
Device = nullptr;
|
||||
}
|
||||
wgpuInstanceRelease(WebGPUInstance);
|
||||
|
||||
// End destruction
|
||||
GPUDevice::Dispose();
|
||||
_state = DeviceState::Disposed;
|
||||
}
|
||||
|
||||
void GPUDeviceWebGPU::WaitForGPU()
|
||||
{
|
||||
}
|
||||
|
||||
bool GPUDeviceWebGPU::GetQueryResult(uint64 queryID, uint64& result, bool wait)
|
||||
{
|
||||
// TODO: impl queries
|
||||
return false;
|
||||
}
|
||||
|
||||
GPUTexture* GPUDeviceWebGPU::CreateTexture(const StringView& name)
|
||||
{
|
||||
PROFILE_MEM(GraphicsTextures);
|
||||
return New<GPUTextureWebGPU>(this, name);
|
||||
}
|
||||
|
||||
GPUShader* GPUDeviceWebGPU::CreateShader(const StringView& name)
|
||||
{
|
||||
PROFILE_MEM(GraphicsShaders);
|
||||
return New<GPUShaderWebGPU>(this, name);
|
||||
}
|
||||
|
||||
GPUPipelineState* GPUDeviceWebGPU::CreatePipelineState()
|
||||
{
|
||||
PROFILE_MEM(GraphicsCommands);
|
||||
return New<GPUPipelineStateWebGPU>(this);
|
||||
}
|
||||
|
||||
GPUTimerQuery* GPUDeviceWebGPU::CreateTimerQuery()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPUBuffer* GPUDeviceWebGPU::CreateBuffer(const StringView& name)
|
||||
{
|
||||
PROFILE_MEM(GraphicsBuffers);
|
||||
return New<GPUBufferWebGPU>(this, name);
|
||||
}
|
||||
|
||||
GPUSampler* GPUDeviceWebGPU::CreateSampler()
|
||||
{
|
||||
return New<GPUSamplerWebGPU>(this);
|
||||
}
|
||||
|
||||
GPUVertexLayout* GPUDeviceWebGPU::CreateVertexLayout(const VertexElements& elements, bool explicitOffsets)
|
||||
{
|
||||
return New<GPUVertexLayoutWebGPU>(this, elements, explicitOffsets);
|
||||
}
|
||||
|
||||
GPUSwapChain* GPUDeviceWebGPU::CreateSwapChain(Window* window)
|
||||
{
|
||||
return New<GPUSwapChainWebGPU>(this, window);
|
||||
}
|
||||
|
||||
GPUConstantBuffer* GPUDeviceWebGPU::CreateConstantBuffer(uint32 size, const StringView& name)
|
||||
{
|
||||
PROFILE_MEM(GraphicsShaders);
|
||||
WGPUBuffer buffer = nullptr;
|
||||
if (size)
|
||||
{
|
||||
WGPUBufferDescriptor desc = WGPU_BUFFER_DESCRIPTOR_INIT;
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
desc.label = WEBGPU_STR("Uniform");
|
||||
#endif
|
||||
desc.size = size;
|
||||
desc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform;
|
||||
buffer = wgpuDeviceCreateBuffer(Device, &desc);
|
||||
if (buffer == nullptr)
|
||||
{
|
||||
LOG(Error, "Failed to create uniform buffer '{}' of size {} bytes", name, size);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return New<GPUConstantBufferWebGPU>(this, size, buffer, name);
|
||||
}
|
||||
|
||||
#endif
|
||||
84
Source/Engine/GraphicsDevice/WebGPU/GPUDeviceWebGPU.h
Normal file
84
Source/Engine/GraphicsDevice/WebGPU/GPUDeviceWebGPU.h
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if GRAPHICS_API_WEBGPU
|
||||
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Graphics/GPUResource.h"
|
||||
#include "IncludeWebGPU.h"
|
||||
|
||||
class GPUContextWebGPU;
|
||||
class GPUAdapterWebGPU;
|
||||
class GPUSamplerWebGPU;
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of Graphics Device for Web GPU backend.
|
||||
/// </summary>
|
||||
class GPUDeviceWebGPU : public GPUDevice
|
||||
{
|
||||
private:
|
||||
GPUContextWebGPU* _mainContext = nullptr;
|
||||
|
||||
public:
|
||||
GPUDeviceWebGPU(WGPUInstance instance, GPUAdapterWebGPU* adapter);
|
||||
~GPUDeviceWebGPU();
|
||||
|
||||
public:
|
||||
GPUAdapterWebGPU* Adapter = nullptr;
|
||||
WGPUInstance WebGPUInstance;
|
||||
WGPUDevice Device = nullptr;
|
||||
WGPUQueue Queue = nullptr;
|
||||
GPUSamplerWebGPU* DefaultSamplers[6] = {};
|
||||
|
||||
public:
|
||||
// [GPUDeviceDX]
|
||||
GPUContext* GetMainContext() override
|
||||
{
|
||||
return (GPUContext*)_mainContext;
|
||||
}
|
||||
GPUAdapter* GetAdapter() const override
|
||||
{
|
||||
return (GPUAdapter*)Adapter;
|
||||
}
|
||||
void* GetNativePtr() const override
|
||||
{
|
||||
return Device;
|
||||
}
|
||||
bool Init() override;
|
||||
void Dispose() override;
|
||||
void WaitForGPU() override;
|
||||
bool GetQueryResult(uint64 queryID, uint64& result, bool wait = false) override;
|
||||
GPUTexture* CreateTexture(const StringView& name) override;
|
||||
GPUShader* CreateShader(const StringView& name) override;
|
||||
GPUPipelineState* CreatePipelineState() override;
|
||||
GPUTimerQuery* CreateTimerQuery() override;
|
||||
GPUBuffer* CreateBuffer(const StringView& name) override;
|
||||
GPUSampler* CreateSampler() override;
|
||||
GPUVertexLayout* CreateVertexLayout(const VertexElements& elements, bool explicitOffsets) override;
|
||||
GPUSwapChain* CreateSwapChain(Window* window) override;
|
||||
GPUConstantBuffer* CreateConstantBuffer(uint32 size, const StringView& name) override;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// GPU resource implementation for Web GPU backend.
|
||||
/// </summary>
|
||||
template<class BaseType>
|
||||
class GPUResourceWebGPU : public GPUResourceBase<GPUDeviceWebGPU, BaseType>
|
||||
{
|
||||
public:
|
||||
GPUResourceWebGPU(GPUDeviceWebGPU* device, const StringView& name) noexcept
|
||||
: GPUResourceBase<GPUDeviceWebGPU, BaseType>(device, name)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct GPUResourceViewPtrWebGPU
|
||||
{
|
||||
class GPUBufferViewWebGPU* BufferView;
|
||||
class GPUTextureViewWebGPU* TextureView;
|
||||
};
|
||||
|
||||
extern GPUDevice* CreateGPUDeviceWebGPU();
|
||||
|
||||
#endif
|
||||
276
Source/Engine/GraphicsDevice/WebGPU/GPUPipelineStateWebGPU.cpp
Normal file
276
Source/Engine/GraphicsDevice/WebGPU/GPUPipelineStateWebGPU.cpp
Normal file
@@ -0,0 +1,276 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#if GRAPHICS_API_WEBGPU
|
||||
|
||||
#include "GPUPipelineStateWebGPU.h"
|
||||
#include "GPUVertexLayoutWebGPU.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Math/Color32.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
|
||||
WGPUCompareFunction ToCompareFunction(ComparisonFunc value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case ComparisonFunc::Never:
|
||||
return WGPUCompareFunction_Never;
|
||||
case ComparisonFunc::Less:
|
||||
return WGPUCompareFunction_Less;
|
||||
case ComparisonFunc::Equal:
|
||||
return WGPUCompareFunction_Equal;
|
||||
case ComparisonFunc::LessEqual:
|
||||
return WGPUCompareFunction_LessEqual;
|
||||
case ComparisonFunc::Greater:
|
||||
return WGPUCompareFunction_Greater;
|
||||
case ComparisonFunc::NotEqual:
|
||||
return WGPUCompareFunction_NotEqual;
|
||||
case ComparisonFunc::GreaterEqual:
|
||||
return WGPUCompareFunction_GreaterEqual;
|
||||
case ComparisonFunc::Always:
|
||||
return WGPUCompareFunction_Always;
|
||||
default:
|
||||
return WGPUCompareFunction_Undefined;
|
||||
}
|
||||
}
|
||||
|
||||
WGPUStencilOperation ToStencilOperation(StencilOperation value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case StencilOperation::Keep:
|
||||
return WGPUStencilOperation_Keep;
|
||||
case StencilOperation::Zero:
|
||||
return WGPUStencilOperation_Zero;
|
||||
case StencilOperation::Replace:
|
||||
return WGPUStencilOperation_Replace;
|
||||
case StencilOperation::IncrementSaturated:
|
||||
return WGPUStencilOperation_IncrementClamp;
|
||||
case StencilOperation::DecrementSaturated:
|
||||
return WGPUStencilOperation_DecrementClamp;
|
||||
case StencilOperation::Invert:
|
||||
return WGPUStencilOperation_Invert;
|
||||
case StencilOperation::Increment:
|
||||
return WGPUStencilOperation_IncrementWrap;
|
||||
case StencilOperation::Decrement:
|
||||
return WGPUStencilOperation_DecrementWrap;
|
||||
default:
|
||||
return WGPUStencilOperation_Undefined;
|
||||
}
|
||||
}
|
||||
|
||||
WGPUBlendFactor ToBlendFactor(BlendingMode::Blend value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case BlendingMode::Blend::Zero:
|
||||
return WGPUBlendFactor_Zero;
|
||||
case BlendingMode::Blend::One:
|
||||
return WGPUBlendFactor_One;
|
||||
case BlendingMode::Blend::SrcColor:
|
||||
return WGPUBlendFactor_Src;
|
||||
case BlendingMode::Blend::InvSrcColor:
|
||||
return WGPUBlendFactor_OneMinusSrc;
|
||||
case BlendingMode::Blend::SrcAlpha:
|
||||
return WGPUBlendFactor_SrcAlpha;
|
||||
case BlendingMode::Blend::InvSrcAlpha:
|
||||
return WGPUBlendFactor_OneMinusSrcAlpha;
|
||||
case BlendingMode::Blend::DestAlpha:
|
||||
return WGPUBlendFactor_DstAlpha;
|
||||
case BlendingMode::Blend::InvDestAlpha:
|
||||
return WGPUBlendFactor_OneMinusDstAlpha;
|
||||
case BlendingMode::Blend::DestColor:
|
||||
return WGPUBlendFactor_Dst;
|
||||
case BlendingMode::Blend::InvDestColor:
|
||||
return WGPUBlendFactor_OneMinusDst;
|
||||
case BlendingMode::Blend::SrcAlphaSat:
|
||||
return WGPUBlendFactor_SrcAlphaSaturated;
|
||||
case BlendingMode::Blend::BlendFactor:
|
||||
return WGPUBlendFactor_Constant;
|
||||
case BlendingMode::Blend::BlendInvFactor:
|
||||
return WGPUBlendFactor_OneMinusConstant;
|
||||
case BlendingMode::Blend::Src1Color:
|
||||
return WGPUBlendFactor_Src1;
|
||||
case BlendingMode::Blend::InvSrc1Color:
|
||||
return WGPUBlendFactor_OneMinusSrc1;
|
||||
case BlendingMode::Blend::Src1Alpha:
|
||||
return WGPUBlendFactor_Src1Alpha;
|
||||
case BlendingMode::Blend::InvSrc1Alpha:
|
||||
return WGPUBlendFactor_OneMinusSrc1Alpha;
|
||||
default:
|
||||
return WGPUBlendFactor_Undefined;
|
||||
}
|
||||
}
|
||||
|
||||
WGPUBlendComponent ToBlendComponent(BlendingMode::Operation blendOp, BlendingMode::Blend srcBlend, BlendingMode::Blend dstBlend)
|
||||
{
|
||||
WGPUBlendComponent result;
|
||||
switch (blendOp)
|
||||
{
|
||||
case BlendingMode::Operation::Add:
|
||||
result.operation = WGPUBlendOperation_Add;
|
||||
break;
|
||||
case BlendingMode::Operation::Subtract:
|
||||
result.operation = WGPUBlendOperation_Subtract;
|
||||
break;
|
||||
case BlendingMode::Operation::RevSubtract:
|
||||
result.operation = WGPUBlendOperation_ReverseSubtract;
|
||||
break;
|
||||
case BlendingMode::Operation::Min:
|
||||
result.operation = WGPUBlendOperation_Min;
|
||||
break;
|
||||
case BlendingMode::Operation::Max:
|
||||
result.operation = WGPUBlendOperation_Max;
|
||||
break;
|
||||
default:
|
||||
result.operation = WGPUBlendOperation_Undefined;
|
||||
break;
|
||||
}
|
||||
result.srcFactor = ToBlendFactor(srcBlend);
|
||||
result.dstFactor = ToBlendFactor(dstBlend);
|
||||
return result;
|
||||
}
|
||||
|
||||
void GPUPipelineStateWebGPU::OnReleaseGPU()
|
||||
{
|
||||
VS = nullptr;
|
||||
PS = nullptr;
|
||||
for (auto& e : _pipelines)
|
||||
wgpuRenderPipelineRelease(e.Value);
|
||||
_pipelines.Clear();
|
||||
}
|
||||
|
||||
uint32 GetHash(const GPUPipelineStateWebGPU::Key& key)
|
||||
{
|
||||
static_assert(sizeof(GPUPipelineStateWebGPU::Key) == sizeof(uint64) * 4, "Invalid PSO key size.");
|
||||
uint32 hash = GetHash(key.Packed[0]);
|
||||
CombineHash(hash, GetHash(key.Packed[1]));
|
||||
return hash;
|
||||
}
|
||||
|
||||
WGPURenderPipeline GPUPipelineStateWebGPU::GetPipeline(const Key& key)
|
||||
{
|
||||
WGPURenderPipeline pipeline;
|
||||
if (_pipelines.TryGet(key, pipeline))
|
||||
return pipeline;
|
||||
PROFILE_CPU();
|
||||
PROFILE_MEM(GraphicsCommands);
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
ZoneText(_debugName.Get(), _debugName.Count() - 1);
|
||||
#endif
|
||||
|
||||
// Build final pipeline description
|
||||
_depthStencilDesc.format = (WGPUTextureFormat)key.DepthStencilFormat;
|
||||
PipelineDesc.multisample.count = key.MultiSampleCount;
|
||||
_fragmentDesc.targetCount = key.RenderTargetCount;
|
||||
for (int32 i = 0; i < _fragmentDesc.targetCount; i++)
|
||||
_colorTargets[i].format = (WGPUTextureFormat)key.RenderTargetFormats[i];
|
||||
WGPUVertexBufferLayout buffers[GPU_MAX_VB_BINDED];
|
||||
for (int32 i = 0; i < PipelineDesc.vertex.bufferCount; i++)
|
||||
buffers[i] = *key.VertexBuffers[i];
|
||||
PipelineDesc.vertex.buffers = buffers;
|
||||
|
||||
// Create object
|
||||
pipeline = wgpuDeviceCreateRenderPipeline(_device->Device, &PipelineDesc);
|
||||
if (!pipeline)
|
||||
{
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
LOG(Error, "wgpuDeviceCreateRenderPipeline failed for {}", String(_debugName.Get(), _debugName.Count() - 1));
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Cache it
|
||||
_pipelines.Add(key, pipeline);
|
||||
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
bool GPUPipelineStateWebGPU::IsValid() const
|
||||
{
|
||||
return _memoryUsage != 0;
|
||||
}
|
||||
|
||||
bool GPUPipelineStateWebGPU::Init(const Description& desc)
|
||||
{
|
||||
if (IsValid())
|
||||
OnReleaseGPU();
|
||||
|
||||
// Cache shaders
|
||||
VS = (GPUShaderProgramVSWebGPU*)desc.VS;
|
||||
PS = (GPUShaderProgramPSWebGPU*)desc.PS;
|
||||
|
||||
// Initialize description (without dynamic state from context such as render targets, vertex buffers, etc.)
|
||||
PipelineDesc = WGPU_RENDER_PIPELINE_DESCRIPTOR_INIT;
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
GetDebugName(_debugName);
|
||||
PipelineDesc.label = { _debugName.Get(), (size_t)_debugName.Count() - 1 };
|
||||
#endif
|
||||
PipelineDesc.primitive.topology = WGPUPrimitiveTopology_TriangleList;
|
||||
PipelineDesc.primitive.frontFace = WGPUFrontFace_CW;
|
||||
switch (desc.CullMode)
|
||||
{
|
||||
case CullMode::Normal:
|
||||
PipelineDesc.primitive.cullMode = WGPUCullMode_Back;
|
||||
break;
|
||||
case CullMode::Inverted:
|
||||
PipelineDesc.primitive.cullMode = WGPUCullMode_Front;
|
||||
break;
|
||||
case CullMode::TwoSided:
|
||||
PipelineDesc.primitive.cullMode = WGPUCullMode_None;
|
||||
break;
|
||||
}
|
||||
PipelineDesc.primitive.unclippedDepth = !desc.DepthClipEnable && _device->Limits.HasDepthClip;
|
||||
if (desc.DepthEnable || desc.StencilEnable)
|
||||
{
|
||||
PipelineDesc.depthStencil = &_depthStencilDesc;
|
||||
_depthStencilDesc = WGPU_DEPTH_STENCIL_STATE_INIT;
|
||||
_depthStencilDesc.depthWriteEnabled = desc.DepthEnable && desc.DepthWriteEnable ? WGPUOptionalBool_True : WGPUOptionalBool_False;
|
||||
_depthStencilDesc.depthCompare = ToCompareFunction(desc.DepthFunc);
|
||||
if (desc.StencilEnable)
|
||||
{
|
||||
_depthStencilDesc.stencilFront.compare = ToCompareFunction(desc.StencilFunc);
|
||||
_depthStencilDesc.stencilFront.failOp = ToStencilOperation(desc.StencilFailOp);
|
||||
_depthStencilDesc.stencilFront.depthFailOp = ToStencilOperation(desc.StencilDepthFailOp);
|
||||
_depthStencilDesc.stencilFront.passOp = ToStencilOperation(desc.StencilPassOp);
|
||||
_depthStencilDesc.stencilBack = _depthStencilDesc.stencilFront;
|
||||
_depthStencilDesc.stencilReadMask = desc.StencilReadMask;
|
||||
_depthStencilDesc.stencilWriteMask = desc.StencilWriteMask;
|
||||
}
|
||||
}
|
||||
PipelineDesc.multisample.alphaToCoverageEnabled = desc.BlendMode.AlphaToCoverageEnable;
|
||||
PipelineDesc.fragment = &_fragmentDesc;
|
||||
_fragmentDesc = WGPU_FRAGMENT_STATE_INIT;
|
||||
_fragmentDesc.targets = _colorTargets;
|
||||
_blendState = WGPU_BLEND_STATE_INIT;
|
||||
_blendState.color = ToBlendComponent(desc.BlendMode.BlendOp, desc.BlendMode.SrcBlend, desc.BlendMode.DestBlend);
|
||||
_blendState.alpha = ToBlendComponent(desc.BlendMode.BlendOpAlpha, desc.BlendMode.SrcBlendAlpha, desc.BlendMode.DestBlendAlpha);
|
||||
WGPUColorWriteMask writeMask = WGPUColorWriteMask_All;
|
||||
if (desc.BlendMode.RenderTargetWriteMask != BlendingMode::ColorWrite::All)
|
||||
{
|
||||
writeMask = 0;
|
||||
if (EnumHasAllFlags(desc.BlendMode.RenderTargetWriteMask, BlendingMode::ColorWrite::Red))
|
||||
writeMask |= WGPUColorWriteMask_Red;
|
||||
if (EnumHasAllFlags(desc.BlendMode.RenderTargetWriteMask, BlendingMode::ColorWrite::Green))
|
||||
writeMask |= WGPUColorWriteMask_Green;
|
||||
if (EnumHasAllFlags(desc.BlendMode.RenderTargetWriteMask, BlendingMode::ColorWrite::Blue))
|
||||
writeMask |= WGPUColorWriteMask_Blue;
|
||||
if (EnumHasAllFlags(desc.BlendMode.RenderTargetWriteMask, BlendingMode::ColorWrite::Alpha))
|
||||
writeMask |= WGPUColorWriteMask_Alpha;
|
||||
}
|
||||
for (auto& e : _colorTargets)
|
||||
{
|
||||
e = WGPU_COLOR_TARGET_STATE_INIT;
|
||||
e.blend = &_blendState;
|
||||
e.writeMask = writeMask;
|
||||
}
|
||||
|
||||
// TODO: set resources binding into PipelineDesc.layout
|
||||
// TODO: set vertex shader into PipelineDesc.vertex
|
||||
// TODO: set pixel shader into PipelineDesc.fragment
|
||||
|
||||
_memoryUsage = 1;
|
||||
return GPUPipelineState::Init(desc);
|
||||
}
|
||||
|
||||
#endif
|
||||
77
Source/Engine/GraphicsDevice/WebGPU/GPUPipelineStateWebGPU.h
Normal file
77
Source/Engine/GraphicsDevice/WebGPU/GPUPipelineStateWebGPU.h
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Graphics/GPUPipelineState.h"
|
||||
#include "GPUShaderProgramWebGPU.h"
|
||||
#include "GPUDeviceWebGPU.h"
|
||||
|
||||
#if GRAPHICS_API_WEBGPU
|
||||
|
||||
/// <summary>
|
||||
/// Graphics pipeline state object for Web GPU backend.
|
||||
/// </summary>
|
||||
class GPUPipelineStateWebGPU : public GPUResourceWebGPU<GPUPipelineState>
|
||||
{
|
||||
public:
|
||||
// Batches render context state for the pipeline state. Used as a key for caching created pipelines.
|
||||
struct Key
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint16 DepthStencilFormat : 7;
|
||||
uint16 MultiSampleCount : 3;
|
||||
uint16 RenderTargetCount : 3;
|
||||
uint16 VertexBufferCount : 3;
|
||||
uint8 RenderTargetFormats[GPU_MAX_RT_BINDED];
|
||||
struct WGPUVertexBufferLayout* VertexBuffers[GPU_MAX_VB_BINDED];
|
||||
};
|
||||
uint64 Packed[4];
|
||||
};
|
||||
|
||||
FORCE_INLINE bool operator==(const Key& other) const
|
||||
{
|
||||
return Platform::MemoryCompare(&Packed, &other.Packed, sizeof(Packed)) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
DebugName _debugName;
|
||||
#endif
|
||||
WGPUDepthStencilState _depthStencilDesc;
|
||||
WGPUFragmentState _fragmentDesc;
|
||||
WGPUBlendState _blendState;
|
||||
WGPUColorTargetState _colorTargets[GPU_MAX_RT_BINDED];
|
||||
WGPUVertexBufferLayout _vertexBuffers[GPU_MAX_VB_BINDED];
|
||||
Dictionary<Key, WGPURenderPipeline> _pipelines;
|
||||
|
||||
public:
|
||||
GPUShaderProgramVSWebGPU* VS = nullptr;
|
||||
GPUShaderProgramPSWebGPU* PS = nullptr;
|
||||
WGPURenderPipelineDescriptor PipelineDesc;
|
||||
|
||||
public:
|
||||
GPUPipelineStateWebGPU(GPUDeviceWebGPU* device)
|
||||
: GPUResourceWebGPU<GPUPipelineState>(device, StringView::Empty)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
WGPURenderPipeline GetPipeline(const Key& key);
|
||||
|
||||
public:
|
||||
// [GPUPipelineState]
|
||||
bool IsValid() const override;
|
||||
bool Init(const Description& desc) override;
|
||||
|
||||
protected:
|
||||
// [GPUResourceWebGPU]
|
||||
void OnReleaseGPU() final override;
|
||||
};
|
||||
|
||||
uint32 GetHash(const GPUPipelineStateWebGPU::Key& key);
|
||||
|
||||
#endif
|
||||
84
Source/Engine/GraphicsDevice/WebGPU/GPUSamplerWebGPU.cpp
Normal file
84
Source/Engine/GraphicsDevice/WebGPU/GPUSamplerWebGPU.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#if GRAPHICS_API_WEBGPU
|
||||
|
||||
#include "GPUSamplerWebGPU.h"
|
||||
|
||||
WGPUAddressMode ToAddressMode(GPUSamplerAddressMode value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case GPUSamplerAddressMode::Wrap:
|
||||
return WGPUAddressMode_Repeat;
|
||||
case GPUSamplerAddressMode::Clamp:
|
||||
return WGPUAddressMode_ClampToEdge;
|
||||
case GPUSamplerAddressMode::Mirror:
|
||||
return WGPUAddressMode_MirrorRepeat;
|
||||
default:
|
||||
return WGPUAddressMode_Undefined;
|
||||
}
|
||||
}
|
||||
|
||||
WGPUCompareFunction ToCompareFunction(GPUSamplerCompareFunction value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case GPUSamplerCompareFunction::Never:
|
||||
return WGPUCompareFunction_Never;
|
||||
case GPUSamplerCompareFunction::Less:
|
||||
return WGPUCompareFunction_Less;
|
||||
default:
|
||||
return WGPUCompareFunction_Undefined;
|
||||
}
|
||||
}
|
||||
|
||||
bool GPUSamplerWebGPU::OnInit()
|
||||
{
|
||||
WGPUSamplerDescriptor samplerDesc = WGPU_SAMPLER_DESCRIPTOR_INIT;
|
||||
samplerDesc.addressModeU = ToAddressMode(_desc.AddressU);
|
||||
samplerDesc.addressModeV = ToAddressMode(_desc.AddressV);
|
||||
samplerDesc.addressModeW = ToAddressMode(_desc.AddressW);
|
||||
switch (_desc.Filter)
|
||||
{
|
||||
case GPUSamplerFilter::Point:
|
||||
samplerDesc.magFilter = samplerDesc.magFilter = WGPUFilterMode_Nearest;
|
||||
samplerDesc.mipmapFilter = WGPUMipmapFilterMode_Nearest;
|
||||
break;
|
||||
case GPUSamplerFilter::Bilinear:
|
||||
samplerDesc.magFilter = samplerDesc.magFilter = WGPUFilterMode_Linear;
|
||||
samplerDesc.mipmapFilter = WGPUMipmapFilterMode_Nearest;
|
||||
break;
|
||||
case GPUSamplerFilter::Trilinear:
|
||||
samplerDesc.magFilter = samplerDesc.magFilter = WGPUFilterMode_Linear;
|
||||
samplerDesc.mipmapFilter = WGPUMipmapFilterMode_Linear;
|
||||
break;
|
||||
case GPUSamplerFilter::Anisotropic:
|
||||
samplerDesc.magFilter = samplerDesc.magFilter = WGPUFilterMode_Linear;
|
||||
samplerDesc.mipmapFilter = WGPUMipmapFilterMode_Linear;
|
||||
break;
|
||||
}
|
||||
samplerDesc.lodMinClamp = _desc.MinMipLevel;
|
||||
samplerDesc.lodMaxClamp = _desc.MaxMipLevel;
|
||||
samplerDesc.compare = ToCompareFunction(_desc.ComparisonFunction);
|
||||
samplerDesc.maxAnisotropy = _desc.MaxAnisotropy;
|
||||
Sampler = wgpuDeviceCreateSampler(_device->Device, &samplerDesc);
|
||||
if (Sampler == nullptr)
|
||||
return true;
|
||||
_memoryUsage = 100;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GPUSamplerWebGPU::OnReleaseGPU()
|
||||
{
|
||||
if (Sampler)
|
||||
{
|
||||
wgpuSamplerRelease(Sampler);
|
||||
Sampler = nullptr;
|
||||
}
|
||||
|
||||
// Base
|
||||
GPUSampler::OnReleaseGPU();
|
||||
}
|
||||
|
||||
#endif
|
||||
30
Source/Engine/GraphicsDevice/WebGPU/GPUSamplerWebGPU.h
Normal file
30
Source/Engine/GraphicsDevice/WebGPU/GPUSamplerWebGPU.h
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Graphics/Textures/GPUSampler.h"
|
||||
#include "GPUDeviceWebGPU.h"
|
||||
|
||||
#if GRAPHICS_API_WEBGPU
|
||||
|
||||
/// <summary>
|
||||
/// Sampler object for Web GPU backend.
|
||||
/// </summary>
|
||||
class GPUSamplerWebGPU : public GPUResourceBase<GPUDeviceWebGPU, GPUSampler>
|
||||
{
|
||||
public:
|
||||
GPUSamplerWebGPU(GPUDeviceWebGPU* device)
|
||||
: GPUResourceBase<GPUDeviceWebGPU, GPUSampler>(device, StringView::Empty)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
WGPUSampler Sampler = nullptr;
|
||||
|
||||
protected:
|
||||
// [GPUSampler]
|
||||
bool OnInit() override;
|
||||
void OnReleaseGPU() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
65
Source/Engine/GraphicsDevice/WebGPU/GPUShaderProgramWebGPU.h
Normal file
65
Source/Engine/GraphicsDevice/WebGPU/GPUShaderProgramWebGPU.h
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Graphics/Shaders/GPUShaderProgram.h"
|
||||
#include "Engine/Core/Types/DataContainer.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
|
||||
#if GRAPHICS_API_WEBGPU
|
||||
|
||||
/// <summary>
|
||||
/// Shaders base class for Web GPU backend.
|
||||
/// </summary>
|
||||
template<typename BaseType>
|
||||
class GPUShaderProgramWebGPU : public BaseType
|
||||
{
|
||||
public:
|
||||
GPUShaderProgramWebGPU(const GPUShaderProgramInitializer& initializer)
|
||||
{
|
||||
BaseType::Init(initializer);
|
||||
}
|
||||
|
||||
~GPUShaderProgramWebGPU()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
// [BaseType]
|
||||
uint32 GetBufferSize() const override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
void* GetBufferHandle() const override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Vertex Shader for Web GPU backend.
|
||||
/// </summary>
|
||||
class GPUShaderProgramVSWebGPU : public GPUShaderProgramWebGPU<GPUShaderProgramVS>
|
||||
{
|
||||
public:
|
||||
GPUShaderProgramVSWebGPU(const GPUShaderProgramInitializer& initializer, GPUVertexLayout* inputLayout, GPUVertexLayout* vertexLayout, Span<byte> bytecode)
|
||||
: GPUShaderProgramWebGPU(initializer)
|
||||
{
|
||||
InputLayout = inputLayout;
|
||||
Layout = vertexLayout;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Pixel Shader for Web GPU backend.
|
||||
/// </summary>
|
||||
class GPUShaderProgramPSWebGPU : public GPUShaderProgramWebGPU<GPUShaderProgramPS>
|
||||
{
|
||||
public:
|
||||
GPUShaderProgramPSWebGPU(const GPUShaderProgramInitializer& initializer)
|
||||
: GPUShaderProgramWebGPU(initializer)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
62
Source/Engine/GraphicsDevice/WebGPU/GPUShaderWebGPU.cpp
Normal file
62
Source/Engine/GraphicsDevice/WebGPU/GPUShaderWebGPU.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#if GRAPHICS_API_WEBGPU
|
||||
|
||||
#include "GPUShaderWebGPU.h"
|
||||
#include "GPUShaderProgramWebGPU.h"
|
||||
#include "GPUVertexLayoutWebGPU.h"
|
||||
#include "Engine/Serialization/MemoryReadStream.h"
|
||||
|
||||
GPUConstantBufferWebGPU::GPUConstantBufferWebGPU(GPUDeviceWebGPU* device, uint32 size, WGPUBuffer buffer, const StringView& name) noexcept
|
||||
: GPUResourceWebGPU(device, name)
|
||||
{
|
||||
_size = _memoryUsage = size;
|
||||
Buffer = buffer;
|
||||
}
|
||||
|
||||
GPUConstantBufferWebGPU::~GPUConstantBufferWebGPU()
|
||||
{
|
||||
if (Buffer)
|
||||
wgpuBufferRelease(Buffer);
|
||||
}
|
||||
|
||||
void GPUConstantBufferWebGPU::OnReleaseGPU()
|
||||
{
|
||||
if (Buffer)
|
||||
{
|
||||
wgpuBufferRelease(Buffer);
|
||||
Buffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
GPUShaderProgram* GPUShaderWebGPU::CreateGPUShaderProgram(ShaderStage type, const GPUShaderProgramInitializer& initializer, Span<byte> bytecode, MemoryReadStream& stream)
|
||||
{
|
||||
GPUShaderProgram* shader = nullptr;
|
||||
switch (type)
|
||||
{
|
||||
case ShaderStage::Vertex:
|
||||
{
|
||||
GPUVertexLayout* inputLayout, *vertexLayout;
|
||||
ReadVertexLayout(stream, inputLayout, vertexLayout);
|
||||
MISSING_CODE("create vertex shader");
|
||||
shader = New<GPUShaderProgramVSWebGPU>(initializer, inputLayout, vertexLayout, bytecode);
|
||||
break;
|
||||
}
|
||||
case ShaderStage::Pixel:
|
||||
{
|
||||
MISSING_CODE("create pixel shader");
|
||||
shader = New<GPUShaderProgramPSWebGPU>(initializer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
|
||||
void GPUShaderWebGPU::OnReleaseGPU()
|
||||
{
|
||||
_cbs.Clear();
|
||||
|
||||
GPUShader::OnReleaseGPU();
|
||||
}
|
||||
|
||||
#endif
|
||||
48
Source/Engine/GraphicsDevice/WebGPU/GPUShaderWebGPU.h
Normal file
48
Source/Engine/GraphicsDevice/WebGPU/GPUShaderWebGPU.h
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if GRAPHICS_API_WEBGPU
|
||||
|
||||
#include "Engine/Graphics/Shaders/GPUShader.h"
|
||||
#include "Engine/Graphics/Shaders/GPUConstantBuffer.h"
|
||||
#include "GPUDeviceWebGPU.h"
|
||||
|
||||
/// <summary>
|
||||
/// Constant Buffer for Web GPU backend.
|
||||
/// </summary>
|
||||
class GPUConstantBufferWebGPU : public GPUResourceWebGPU<GPUConstantBuffer>
|
||||
{
|
||||
public:
|
||||
GPUConstantBufferWebGPU(GPUDeviceWebGPU* device, uint32 size, WGPUBuffer buffer, const StringView& name) noexcept;
|
||||
~GPUConstantBufferWebGPU();
|
||||
|
||||
public:
|
||||
WGPUBuffer Buffer;
|
||||
|
||||
public:
|
||||
// [GPUResourceWebGPU]
|
||||
void OnReleaseGPU() final override;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Shader for Web GPU backend.
|
||||
/// </summary>
|
||||
class GPUShaderWebGPU : public GPUResourceWebGPU<GPUShader>
|
||||
{
|
||||
private:
|
||||
Array<GPUConstantBufferWebGPU, FixedAllocation<GPU_MAX_CB_BINDED>> _cbs;
|
||||
|
||||
public:
|
||||
GPUShaderWebGPU(GPUDeviceWebGPU* device, const StringView& name)
|
||||
: GPUResourceWebGPU(device, name)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
// [GPUShader]
|
||||
GPUShaderProgram* CreateGPUShaderProgram(ShaderStage type, const GPUShaderProgramInitializer& initializer, Span<byte> bytecode, MemoryReadStream& stream) override;
|
||||
void OnReleaseGPU() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
179
Source/Engine/GraphicsDevice/WebGPU/GPUSwapChainWebGPU.cpp
Normal file
179
Source/Engine/GraphicsDevice/WebGPU/GPUSwapChainWebGPU.cpp
Normal file
@@ -0,0 +1,179 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#if GRAPHICS_API_WEBGPU
|
||||
|
||||
#include "GPUSwapChainWebGPU.h"
|
||||
#include "GPUAdapterWebGPU.h"
|
||||
#include "RenderToolsWebGPU.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Types/Span.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Scripting/Enums.h"
|
||||
|
||||
GPUSwapChainWebGPU::GPUSwapChainWebGPU(GPUDeviceWebGPU* device, Window* window)
|
||||
: GPUResourceWebGPU(device, StringView::Empty)
|
||||
{
|
||||
_window = window;
|
||||
}
|
||||
|
||||
void GPUSwapChainWebGPU::OnReleaseGPU()
|
||||
{
|
||||
// Release data
|
||||
PROFILE_MEM_DEC(Graphics, _memoryUsage);
|
||||
auto surfaceTexture = _surfaceView.Texture;
|
||||
_surfaceView.Release();
|
||||
if (surfaceTexture)
|
||||
{
|
||||
wgpuTextureRelease(surfaceTexture);
|
||||
}
|
||||
if (Surface)
|
||||
{
|
||||
wgpuSurfaceRelease(Surface);
|
||||
Surface = nullptr;
|
||||
}
|
||||
_width = _height = 0;
|
||||
_memoryUsage = 0;
|
||||
}
|
||||
|
||||
bool GPUSwapChainWebGPU::IsFullscreen()
|
||||
{
|
||||
return _window->IsFullscreen();
|
||||
}
|
||||
|
||||
void GPUSwapChainWebGPU::SetFullscreen(bool isFullscreen)
|
||||
{
|
||||
_window->SetIsFullscreen(true);
|
||||
}
|
||||
|
||||
GPUTextureView* GPUSwapChainWebGPU::GetBackBufferView()
|
||||
{
|
||||
if (!_surfaceView.Texture)
|
||||
{
|
||||
// Get current texture for the surface
|
||||
WGPUSurfaceTexture surfaceTexture = WGPU_SURFACE_TEXTURE_INIT;
|
||||
wgpuSurfaceGetCurrentTexture(Surface, &surfaceTexture);
|
||||
bool hasSurfaceTexture = surfaceTexture.status == WGPUSurfaceGetCurrentTextureStatus_SuccessOptimal || surfaceTexture.status == WGPUSurfaceGetCurrentTextureStatus_SuccessSuboptimal;
|
||||
ASSERT(hasSurfaceTexture);
|
||||
_surfaceView.Texture = surfaceTexture.texture;
|
||||
|
||||
// Create view
|
||||
WGPUTextureViewDescriptor viewDesc = WGPU_TEXTURE_VIEW_DESCRIPTOR_INIT;
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
viewDesc.label = WEBGPU_STR("Flax Surface");
|
||||
#endif
|
||||
viewDesc.format = wgpuTextureGetFormat(surfaceTexture.texture);
|
||||
viewDesc.dimension = WGPUTextureViewDimension_2D;
|
||||
viewDesc.mipLevelCount = 1;
|
||||
viewDesc.arrayLayerCount = 1;
|
||||
viewDesc.aspect = WGPUTextureAspect_All;
|
||||
viewDesc.usage = wgpuTextureGetUsage(surfaceTexture.texture);
|
||||
_surfaceView.Create(surfaceTexture.texture, &viewDesc);
|
||||
}
|
||||
return &_surfaceView;
|
||||
}
|
||||
|
||||
void GPUSwapChainWebGPU::Present(bool vsync)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
ZoneColor(TracyWaitZoneColor);
|
||||
|
||||
#if !PLATFORM_WEB
|
||||
// Present frame
|
||||
wgpuSurfacePresent(Surface);
|
||||
#endif
|
||||
|
||||
// Release the texture
|
||||
auto surfaceTexture = _surfaceView.Texture;
|
||||
ASSERT(surfaceTexture);
|
||||
_surfaceView.Release();
|
||||
wgpuTextureRelease(surfaceTexture);
|
||||
|
||||
// Base
|
||||
GPUSwapChain::Present(vsync);
|
||||
}
|
||||
|
||||
bool GPUSwapChainWebGPU::Resize(int32 width, int32 height)
|
||||
{
|
||||
if (width == _width && height == _height)
|
||||
return false;
|
||||
_device->WaitForGPU();
|
||||
GPUDeviceLock lock(_device);
|
||||
#if GPU_ENABLE_DIAGNOSTICS
|
||||
LOG(Info, "Resizing WebGPU surface to: {}x{}", width, height);
|
||||
#endif
|
||||
|
||||
// Ensure to have a surface
|
||||
if (!Surface)
|
||||
{
|
||||
WGPUEmscriptenSurfaceSourceCanvasHTMLSelector canvasDesc = WGPU_EMSCRIPTEN_SURFACE_SOURCE_CANVAS_HTML_SELECTOR_INIT;
|
||||
canvasDesc.selector = WEBGPU_STR(WEB_CANVAS_ID);
|
||||
WGPUSurfaceDescriptor surfaceDesc = WGPU_SURFACE_DESCRIPTOR_INIT;
|
||||
surfaceDesc.nextInChain = &canvasDesc.chain;
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
surfaceDesc.label = WEBGPU_STR("Flax Surface");
|
||||
#endif
|
||||
Surface = wgpuInstanceCreateSurface(_device->WebGPUInstance, &surfaceDesc);
|
||||
if (!Surface)
|
||||
{
|
||||
LOG(Fatal, "Failed to create WebGPU surface for the HTML selector '{}'", TEXT(WEB_CANVAS_ID));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Setup surface configuration (based on capabilities)
|
||||
WGPUSurfaceCapabilities capabilities = WGPU_SURFACE_CAPABILITIES_INIT;
|
||||
wgpuSurfaceGetCapabilities(Surface, _device->Adapter->Adapter, &capabilities);
|
||||
auto formats = ToSpan(capabilities.formats, capabilities.formatCount);
|
||||
auto presentModes = ToSpan(capabilities.presentModes, capabilities.presentModeCount);
|
||||
auto alphaModes = ToSpan(capabilities.alphaModes, capabilities.alphaModeCount);
|
||||
WGPUSurfaceConfiguration configuration = WGPU_SURFACE_CONFIGURATION_INIT;
|
||||
configuration.device = _device->Device;
|
||||
configuration.usage = WGPUTextureUsage_RenderAttachment;
|
||||
#if GPU_USE_WINDOW_SRV
|
||||
configuration.usage |= WGPUTextureUsage_TextureBinding;
|
||||
#endif
|
||||
configuration.width = width;
|
||||
configuration.height = height;
|
||||
configuration.format = WGPUTextureFormat_RGBA8Unorm;
|
||||
configuration.viewFormats = &configuration.format;
|
||||
configuration.viewFormatCount = 1;
|
||||
_format = PixelFormat::R8G8B8A8_UNorm;
|
||||
if (SpanContains(formats, RenderToolsWebGPU::ToTextureFormat(GPU_BACK_BUFFER_PIXEL_FORMAT)))
|
||||
{
|
||||
_format = GPU_BACK_BUFFER_PIXEL_FORMAT;
|
||||
configuration.format = RenderToolsWebGPU::ToTextureFormat(_format);
|
||||
}
|
||||
else if (formats.Length() != 0)
|
||||
{
|
||||
configuration.format = formats[0];
|
||||
_format = RenderToolsWebGPU::ToPixelFormat(configuration.format);
|
||||
}
|
||||
if (_window->GetSettings().SupportsTransparency && SpanContains(alphaModes, WGPUCompositeAlphaMode_Premultiplied))
|
||||
configuration.alphaMode = WGPUCompositeAlphaMode_Premultiplied;
|
||||
else if (SpanContains(alphaModes, WGPUCompositeAlphaMode_Opaque))
|
||||
configuration.alphaMode = WGPUCompositeAlphaMode_Opaque;
|
||||
if (SpanContains(presentModes, WGPUPresentMode_Mailbox))
|
||||
configuration.presentMode = WGPUPresentMode_Mailbox;
|
||||
if (SpanContains(presentModes, WGPUPresentMode_Fifo))
|
||||
configuration.presentMode = WGPUPresentMode_Fifo;
|
||||
else if (presentModes.Length() != 0)
|
||||
configuration.presentMode = presentModes[0];
|
||||
wgpuSurfaceCapabilitiesFreeMembers(capabilities);
|
||||
|
||||
// Configure surface
|
||||
wgpuSurfaceConfigure(Surface, &configuration);
|
||||
|
||||
// Init
|
||||
_surfaceView.Init(this, _format, MSAALevel::None);
|
||||
_width = width;
|
||||
_height = height;
|
||||
_memoryUsage = RenderTools::CalculateTextureMemoryUsage(_format, _width, _height, 1);
|
||||
PROFILE_MEM_INC(Graphics, _memoryUsage);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
41
Source/Engine/GraphicsDevice/WebGPU/GPUSwapChainWebGPU.h
Normal file
41
Source/Engine/GraphicsDevice/WebGPU/GPUSwapChainWebGPU.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPUDeviceWebGPU.h"
|
||||
#include "GPUTextureWebGPU.h"
|
||||
#include "Engine/Graphics/GPUSwapChain.h"
|
||||
|
||||
#if GRAPHICS_API_WEBGPU
|
||||
|
||||
/// <summary>
|
||||
/// Graphics Device rendering output for Web GPU backend.
|
||||
/// </summary>
|
||||
class GPUSwapChainWebGPU : public GPUResourceWebGPU<GPUSwapChain>
|
||||
{
|
||||
friend class WindowsWindow;
|
||||
friend class GPUContextWebGPU;
|
||||
friend GPUDeviceWebGPU;
|
||||
private:
|
||||
GPUTextureViewWebGPU _surfaceView;
|
||||
|
||||
public:
|
||||
GPUSwapChainWebGPU(GPUDeviceWebGPU* device, Window* window);
|
||||
|
||||
public:
|
||||
WGPUSurface Surface = nullptr;
|
||||
|
||||
public:
|
||||
// [GPUSwapChain]
|
||||
bool IsFullscreen() override;
|
||||
void SetFullscreen(bool isFullscreen) override;
|
||||
GPUTextureView* GetBackBufferView() override;
|
||||
void Present(bool vsync) override;
|
||||
bool Resize(int32 width, int32 height) override;
|
||||
|
||||
protected:
|
||||
// [GPUResourceWebGPU]
|
||||
void OnReleaseGPU() final override;
|
||||
};
|
||||
|
||||
#endif
|
||||
286
Source/Engine/GraphicsDevice/WebGPU/GPUTextureWebGPU.cpp
Normal file
286
Source/Engine/GraphicsDevice/WebGPU/GPUTextureWebGPU.cpp
Normal file
@@ -0,0 +1,286 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#if GRAPHICS_API_WEBGPU
|
||||
|
||||
#include "GPUTextureWebGPU.h"
|
||||
#include "RenderToolsWebGPU.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Graphics/PixelFormatExtensions.h"
|
||||
|
||||
void GPUTextureViewWebGPU::Create(WGPUTexture texture, WGPUTextureViewDescriptor const* desc)
|
||||
{
|
||||
if (View)
|
||||
wgpuTextureViewRelease(View);
|
||||
Texture = texture;
|
||||
View = wgpuTextureCreateView(texture, desc);
|
||||
if (!View)
|
||||
{
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
LOG(Error, "Failed to create a view for texture '{}'", GetParent() ? GetParent()->GetName() : StringView::Empty);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void GPUTextureViewWebGPU::Release()
|
||||
{
|
||||
if (View)
|
||||
{
|
||||
wgpuTextureViewRelease(View);
|
||||
View = nullptr;
|
||||
}
|
||||
Texture = nullptr;
|
||||
}
|
||||
|
||||
bool GPUTextureWebGPU::OnInit()
|
||||
{
|
||||
// Create texture
|
||||
WGPUTextureDescriptor textureDesc = WGPU_TEXTURE_DESCRIPTOR_INIT;
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
_name.Set(_namePtr, _nameSize);
|
||||
textureDesc.label = { _name.Get(), (size_t)_name.Length() };
|
||||
#endif
|
||||
if (!IsDepthStencil())
|
||||
textureDesc.usage |= WGPUTextureUsage_CopyDst;
|
||||
if (IsStaging())
|
||||
textureDesc.usage |= WGPUTextureUsage_CopySrc | WGPUTextureUsage_CopyDst;
|
||||
if (IsShaderResource())
|
||||
textureDesc.usage |= WGPUTextureUsage_TextureBinding;
|
||||
if (IsUnorderedAccess())
|
||||
textureDesc.usage |= WGPUTextureUsage_StorageBinding;
|
||||
if (IsRenderTarget() || IsDepthStencil())
|
||||
textureDesc.usage |= WGPUTextureUsage_RenderAttachment;
|
||||
textureDesc.size.width = _desc.Width;
|
||||
textureDesc.size.height = _desc.Height;
|
||||
textureDesc.size.depthOrArrayLayers = _desc.Depth;
|
||||
switch (_desc.Dimensions)
|
||||
{
|
||||
case TextureDimensions::Texture:
|
||||
_viewDimension = IsArray() ? WGPUTextureViewDimension_2DArray : WGPUTextureViewDimension_2D;
|
||||
textureDesc.dimension = WGPUTextureDimension_2D;
|
||||
break;
|
||||
case TextureDimensions::VolumeTexture:
|
||||
_viewDimension = WGPUTextureViewDimension_3D;
|
||||
textureDesc.dimension = WGPUTextureDimension_3D;
|
||||
break;
|
||||
case TextureDimensions::CubeTexture:
|
||||
_viewDimension = IsArray() ? WGPUTextureViewDimension_CubeArray : WGPUTextureViewDimension_Cube;
|
||||
textureDesc.dimension = WGPUTextureDimension_2D;
|
||||
textureDesc.size.depthOrArrayLayers *= 6; // Each cubemap uses 6 array slices
|
||||
break;
|
||||
}
|
||||
textureDesc.format = RenderToolsWebGPU::ToTextureFormat(Format());
|
||||
textureDesc.mipLevelCount = _desc.MipLevels;
|
||||
textureDesc.sampleCount = (uint32_t)_desc.MultiSampleLevel;
|
||||
textureDesc.viewFormats = &textureDesc.format;
|
||||
textureDesc.viewFormatCount = 1;
|
||||
_format = textureDesc.format;
|
||||
_usage = textureDesc.usage;
|
||||
Texture = wgpuDeviceCreateTexture(_device->Device, &textureDesc);
|
||||
if (!Texture)
|
||||
return true;
|
||||
|
||||
// Update memory usage
|
||||
_memoryUsage = calculateMemoryUsage();
|
||||
|
||||
// Initialize handles to the resource
|
||||
if (IsRegularTexture())
|
||||
{
|
||||
// 'Regular' texture is using only one handle (texture/cubemap)
|
||||
_handlesPerSlice.Resize(1, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create all handles
|
||||
InitHandles();
|
||||
}
|
||||
|
||||
// Setup metadata for the views
|
||||
bool hasStencil = PixelFormatExtensions::HasStencil(Format());
|
||||
if (hasStencil)
|
||||
{
|
||||
_handleArray.HasStencil = hasStencil;
|
||||
_handleVolume.HasStencil = hasStencil;
|
||||
_handleReadOnlyDepth.HasStencil = hasStencil;
|
||||
_handleStencil.HasStencil = hasStencil;
|
||||
for (auto& e : _handlesPerSlice)
|
||||
e.HasStencil = hasStencil;
|
||||
for (auto& q : _handlesPerMip)
|
||||
for (auto& e : q)
|
||||
e.HasStencil = hasStencil;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GPUTextureWebGPU::OnResidentMipsChanged()
|
||||
{
|
||||
// Update the view to handle base mip level as highest resident mip
|
||||
WGPUTextureViewDescriptor viewDesc = WGPU_TEXTURE_VIEW_DESCRIPTOR_INIT;
|
||||
viewDesc.format = _format;
|
||||
viewDesc.usage = _usage;
|
||||
viewDesc.dimension = _viewDimension;
|
||||
viewDesc.baseMipLevel = MipLevels() - ResidentMipLevels();
|
||||
viewDesc.mipLevelCount = ResidentMipLevels();
|
||||
viewDesc.arrayLayerCount = ArraySize();
|
||||
viewDesc.aspect = WGPUTextureAspect_All;
|
||||
GPUTextureViewWebGPU& view = IsVolume() ? _handleVolume : _handlesPerSlice[0];
|
||||
if (view.GetParent() == nullptr)
|
||||
view.Init(this, _desc.Format, _desc.MultiSampleLevel);
|
||||
view.Create(Texture, &viewDesc);
|
||||
}
|
||||
|
||||
void GPUTextureWebGPU::OnReleaseGPU()
|
||||
{
|
||||
_handlesPerMip.Resize(0, false);
|
||||
_handlesPerSlice.Resize(0, false);
|
||||
_handleArray.Release();
|
||||
_handleVolume.Release();
|
||||
_handleReadOnlyDepth.Release();
|
||||
_handleStencil.Release();
|
||||
if (Texture)
|
||||
{
|
||||
wgpuTextureDestroy(Texture);
|
||||
wgpuTextureRelease(Texture);
|
||||
Texture = nullptr;
|
||||
}
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
_name.Clear();
|
||||
#endif
|
||||
|
||||
// Base
|
||||
GPUTexture::OnReleaseGPU();
|
||||
}
|
||||
|
||||
void GPUTextureWebGPU::InitHandles()
|
||||
{
|
||||
WGPUTextureViewDescriptor viewDesc = WGPU_TEXTURE_VIEW_DESCRIPTOR_INIT;
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
viewDesc.label = { _name.Get(), (size_t)_name.Length() };
|
||||
#endif
|
||||
viewDesc.format = _format;
|
||||
viewDesc.usage = _usage;
|
||||
viewDesc.dimension = _viewDimension;
|
||||
viewDesc.mipLevelCount = MipLevels();
|
||||
viewDesc.arrayLayerCount = ArraySize();
|
||||
viewDesc.aspect = WGPUTextureAspect_All;
|
||||
|
||||
auto format = Format();
|
||||
auto msaa = MultiSampleLevel();
|
||||
|
||||
if (IsVolume())
|
||||
{
|
||||
// Create handle for whole 3d texture
|
||||
{
|
||||
auto& view = _handleVolume;
|
||||
view.Init(this, format, msaa);
|
||||
view.Create(Texture, &viewDesc);
|
||||
}
|
||||
|
||||
// Init per slice views
|
||||
_handlesPerSlice.Resize(Depth(), false);
|
||||
viewDesc.dimension = WGPUTextureViewDimension_2D;
|
||||
if (_desc.HasPerSliceViews() && IsRenderTarget())
|
||||
{
|
||||
for (int32 sliceIndex = 0; sliceIndex < Depth(); sliceIndex++)
|
||||
{
|
||||
auto& view = _handlesPerSlice[sliceIndex];
|
||||
view.Init(this, format, msaa);
|
||||
view.Create(Texture, &viewDesc);
|
||||
view.DepthSlice = sliceIndex;
|
||||
}
|
||||
}
|
||||
viewDesc.dimension = _viewDimension;
|
||||
}
|
||||
else if (IsArray())
|
||||
{
|
||||
// Create whole array handle
|
||||
{
|
||||
auto& view = _handleArray;
|
||||
view.Init(this, format, msaa);
|
||||
view.Create(Texture, &viewDesc);
|
||||
}
|
||||
|
||||
// Create per array slice handles
|
||||
_handlesPerSlice.Resize(ArraySize(), false);
|
||||
viewDesc.dimension = WGPUTextureViewDimension_2D;
|
||||
for (int32 arrayIndex = 0; arrayIndex < _handlesPerSlice.Count(); arrayIndex++)
|
||||
{
|
||||
viewDesc.baseArrayLayer = arrayIndex;
|
||||
viewDesc.arrayLayerCount = 1;
|
||||
auto& view = _handlesPerSlice[arrayIndex];
|
||||
view.Init(this, format, msaa);
|
||||
view.Create(Texture, &viewDesc);
|
||||
}
|
||||
viewDesc.baseArrayLayer = 0;
|
||||
viewDesc.arrayLayerCount = MipLevels();
|
||||
viewDesc.dimension = _viewDimension;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create single handle for the whole texture
|
||||
_handlesPerSlice.Resize(1, false);
|
||||
auto& view = _handlesPerSlice[0];
|
||||
view.Init(this, format, msaa);
|
||||
view.Create(Texture, &viewDesc);
|
||||
}
|
||||
|
||||
// Init per mip map handles
|
||||
if (HasPerMipViews())
|
||||
{
|
||||
// Init handles
|
||||
_handlesPerMip.Resize(ArraySize(), false);
|
||||
viewDesc.dimension = WGPUTextureViewDimension_2D;
|
||||
for (int32 arrayIndex = 0; arrayIndex < _handlesPerMip.Count(); arrayIndex++)
|
||||
{
|
||||
auto& slice = _handlesPerMip[arrayIndex];
|
||||
slice.Resize(MipLevels(), false);
|
||||
viewDesc.baseArrayLayer = arrayIndex;
|
||||
viewDesc.arrayLayerCount = 1;
|
||||
viewDesc.mipLevelCount = 1;
|
||||
for (int32 mipIndex = 0; mipIndex < slice.Count(); mipIndex++)
|
||||
{
|
||||
auto& view = slice[mipIndex];
|
||||
viewDesc.baseMipLevel = mipIndex;
|
||||
view.Init(this, format, msaa);
|
||||
view.Create(Texture, &viewDesc);
|
||||
}
|
||||
}
|
||||
viewDesc.dimension = _viewDimension;
|
||||
}
|
||||
|
||||
// Read-only depth-stencil
|
||||
if (EnumHasAnyFlags(_desc.Flags, GPUTextureFlags::ReadOnlyDepthView))
|
||||
{
|
||||
auto& view = _handleReadOnlyDepth;
|
||||
view.Init(this, format, msaa);
|
||||
view.Create(Texture, &viewDesc);
|
||||
view.ReadOnly = true;
|
||||
}
|
||||
|
||||
// Stencil view
|
||||
if (IsDepthStencil() && IsShaderResource() && PixelFormatExtensions::HasStencil(format))
|
||||
{
|
||||
PixelFormat stencilFormat = format;
|
||||
switch (format)
|
||||
{
|
||||
case PixelFormat::D24_UNorm_S8_UInt:
|
||||
stencilFormat = PixelFormat::X24_Typeless_G8_UInt;
|
||||
break;
|
||||
case PixelFormat::D32_Float_S8X24_UInt:
|
||||
stencilFormat = PixelFormat::X32_Typeless_G8X24_UInt;
|
||||
break;
|
||||
}
|
||||
viewDesc.aspect = WGPUTextureAspect_StencilOnly;
|
||||
viewDesc.format = WGPUTextureFormat_Stencil8;
|
||||
_handleStencil.Init(this, stencilFormat, msaa);
|
||||
_handleStencil.Create(Texture, &viewDesc);
|
||||
}
|
||||
}
|
||||
|
||||
bool GPUTextureWebGPU::GetData(int32 arrayIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch)
|
||||
{
|
||||
// TODO: no implemented
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
127
Source/Engine/GraphicsDevice/WebGPU/GPUTextureWebGPU.h
Normal file
127
Source/Engine/GraphicsDevice/WebGPU/GPUTextureWebGPU.h
Normal file
@@ -0,0 +1,127 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Graphics/Textures/GPUTexture.h"
|
||||
#include "GPUDeviceWebGPU.h"
|
||||
#include "IncludeWebGPU.h"
|
||||
|
||||
#if GRAPHICS_API_WEBGPU
|
||||
|
||||
/// <summary>
|
||||
/// The texture view for Web GPU backend.
|
||||
/// </summary>
|
||||
/// <seealso cref="GPUTextureView" />
|
||||
class GPUTextureViewWebGPU : public GPUTextureView
|
||||
{
|
||||
public:
|
||||
GPUTextureViewWebGPU()
|
||||
{
|
||||
Ptr = { nullptr, this };
|
||||
}
|
||||
|
||||
~GPUTextureViewWebGPU()
|
||||
{
|
||||
Release();
|
||||
}
|
||||
|
||||
public:
|
||||
// Handle to the WebGPU texture object.
|
||||
WGPUTexture Texture = nullptr;
|
||||
// Handle to the WebGPU texture view object.
|
||||
WGPUTextureView View = nullptr;
|
||||
bool HasStencil = false;
|
||||
bool ReadOnly = false;
|
||||
uint32 DepthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
|
||||
GPUResourceViewPtrWebGPU Ptr;
|
||||
|
||||
public:
|
||||
using GPUTextureView::Init;
|
||||
void Create(WGPUTexture texture, WGPUTextureViewDescriptor const* desc = nullptr);
|
||||
void Release();
|
||||
|
||||
public:
|
||||
// [GPUResourceView]
|
||||
void* GetNativePtr() const override
|
||||
{
|
||||
return (void*)&Ptr;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Texture object for Web GPU backend.
|
||||
/// </summary>
|
||||
class GPUTextureWebGPU : public GPUResourceWebGPU<GPUTexture>
|
||||
{
|
||||
private:
|
||||
GPUTextureViewWebGPU _handleArray;
|
||||
GPUTextureViewWebGPU _handleVolume;
|
||||
GPUTextureViewWebGPU _handleReadOnlyDepth;
|
||||
GPUTextureViewWebGPU _handleStencil;
|
||||
Array<GPUTextureViewWebGPU> _handlesPerSlice; // [slice]
|
||||
Array<Array<GPUTextureViewWebGPU>> _handlesPerMip; // [slice][mip]
|
||||
#if GPU_ENABLE_RESOURCE_NAMING
|
||||
StringAnsi _name;
|
||||
#endif
|
||||
WGPUTextureFormat _format = WGPUTextureFormat_Undefined;
|
||||
WGPUTextureViewDimension _viewDimension = WGPUTextureViewDimension_Undefined;
|
||||
WGPUTextureUsage _usage = 0;
|
||||
|
||||
public:
|
||||
GPUTextureWebGPU(GPUDeviceWebGPU* device, const StringView& name)
|
||||
: GPUResourceWebGPU<GPUTexture>(device, name)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
// Handle to the WebGPU texture object.
|
||||
WGPUTexture Texture = nullptr;
|
||||
|
||||
public:
|
||||
// [GPUTexture]
|
||||
GPUTextureView* View(int32 arrayOrDepthIndex) const override
|
||||
{
|
||||
return (GPUTextureView*)&_handlesPerSlice[arrayOrDepthIndex];
|
||||
}
|
||||
GPUTextureView* View(int32 arrayOrDepthIndex, int32 mipMapIndex) const override
|
||||
{
|
||||
return (GPUTextureView*)&_handlesPerMip[arrayOrDepthIndex][mipMapIndex];
|
||||
}
|
||||
GPUTextureView* ViewArray() const override
|
||||
{
|
||||
ASSERT(ArraySize() > 1);
|
||||
return (GPUTextureView*)&_handleArray;
|
||||
}
|
||||
GPUTextureView* ViewVolume() const override
|
||||
{
|
||||
ASSERT(IsVolume());
|
||||
return (GPUTextureView*)&_handleVolume;
|
||||
}
|
||||
GPUTextureView* ViewReadOnlyDepth() const override
|
||||
{
|
||||
ASSERT(_desc.Flags & GPUTextureFlags::ReadOnlyDepthView);
|
||||
return (GPUTextureView*)&_handleReadOnlyDepth;
|
||||
}
|
||||
GPUTextureView* ViewStencil() const override
|
||||
{
|
||||
ASSERT(_desc.Flags & GPUTextureFlags::DepthStencil);
|
||||
return (GPUTextureView*)&_handleStencil;
|
||||
}
|
||||
void* GetNativePtr() const override
|
||||
{
|
||||
return Texture;
|
||||
}
|
||||
bool GetData(int32 arrayIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch) override;
|
||||
|
||||
protected:
|
||||
// [GPUTexture]
|
||||
bool OnInit() override;
|
||||
void OnResidentMipsChanged() override;
|
||||
void OnReleaseGPU() override;
|
||||
|
||||
private:
|
||||
void InitHandles();
|
||||
};
|
||||
|
||||
#endif
|
||||
23
Source/Engine/GraphicsDevice/WebGPU/GPUVertexLayoutWebGPU.h
Normal file
23
Source/Engine/GraphicsDevice/WebGPU/GPUVertexLayoutWebGPU.h
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if GRAPHICS_API_WEBGPU
|
||||
|
||||
#include "Engine/Graphics/Shaders/GPUVertexLayout.h"
|
||||
#include "GPUDeviceWebGPU.h"
|
||||
|
||||
/// <summary>
|
||||
/// Vertex layout object for Web GPU backend.
|
||||
/// </summary>
|
||||
class GPUVertexLayoutWebGPU : public GPUResourceBase<GPUDeviceWebGPU, GPUVertexLayout>
|
||||
{
|
||||
public:
|
||||
GPUVertexLayoutWebGPU(GPUDeviceWebGPU* device, const Elements& elements, bool explicitOffsets);
|
||||
|
||||
public:
|
||||
WGPUVertexBufferLayout Layout;
|
||||
WGPUVertexAttribute Attributes[GPU_MAX_VS_ELEMENTS];
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.IO;
|
||||
using Flax.Build.NativeCpp;
|
||||
using Flax.Build.Platforms;
|
||||
|
||||
/// <summary>
|
||||
/// WebGPU graphics backend module.
|
||||
/// </summary>
|
||||
public class GraphicsDeviceWebGPU : GraphicsDeviceBaseModule
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Setup(BuildOptions options)
|
||||
{
|
||||
base.Setup(options);
|
||||
|
||||
var port = "--use-port=emdawnwebgpu:cpp_bindings=false";
|
||||
options.CompileEnv.CustomArgs.Add(port);
|
||||
options.LinkEnv.CustomArgs.Add("-sASYNCIFY");
|
||||
options.OutputFiles.Add(port);
|
||||
options.PublicDefinitions.Add("GRAPHICS_API_WEBGPU");
|
||||
options.PrivateIncludePaths.Add(Path.Combine(EmscriptenSdk.Instance.EmscriptenPath, "emscripten/cache/ports/emdawnwebgpu/emdawnwebgpu_pkg/webgpu/include"));
|
||||
}
|
||||
}
|
||||
26
Source/Engine/GraphicsDevice/WebGPU/IncludeWebGPU.h
Normal file
26
Source/Engine/GraphicsDevice/WebGPU/IncludeWebGPU.h
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if GRAPHICS_API_WEBGPU
|
||||
|
||||
// Fix Intellisense errors when Emsicpten SDK is not visible
|
||||
#ifndef UINT32_C
|
||||
#define UINT32_C(x) (x ## U)
|
||||
#endif
|
||||
#ifndef __SIZE_MAX__
|
||||
#define __SIZE_MAX__ (static_cast<intptr>(-1))
|
||||
#endif
|
||||
#ifndef SIZE_MAX
|
||||
#define SIZE_MAX __SIZE_MAX__
|
||||
#endif
|
||||
|
||||
#include <webgpu/webgpu.h>
|
||||
|
||||
// Utiltiy macro to convert WGPUStringView into UTF-16 string (on stack)
|
||||
#define WEBGPU_TO_STR(strView) StringAsUTF16<>(strView.data, strView.data ? strView.length : 0).Get()
|
||||
|
||||
// Utiltiy macro to get WGPUStringView for a text constant
|
||||
#define WEBGPU_STR(str) { str, ARRAY_COUNT(str) - 1 }
|
||||
|
||||
#endif
|
||||
241
Source/Engine/GraphicsDevice/WebGPU/RenderToolsWebGPU.cpp
Normal file
241
Source/Engine/GraphicsDevice/WebGPU/RenderToolsWebGPU.cpp
Normal file
@@ -0,0 +1,241 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#if GRAPHICS_API_WEBGPU
|
||||
|
||||
#include "RenderToolsWebGPU.h"
|
||||
#include "Engine/Graphics/PixelFormat.h"
|
||||
|
||||
WGPUVertexFormat RenderToolsWebGPU::ToVertexFormat(PixelFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
// @formatter:off
|
||||
case PixelFormat::R8_UInt: return WGPUVertexFormat_Uint8;
|
||||
case PixelFormat::R8G8_UInt: return WGPUVertexFormat_Uint8x2;
|
||||
case PixelFormat::R8G8B8A8_UInt: return WGPUVertexFormat_Uint8x4;
|
||||
case PixelFormat::R8_SInt: return WGPUVertexFormat_Sint8;
|
||||
case PixelFormat::R8G8_SInt: return WGPUVertexFormat_Sint8x2;
|
||||
case PixelFormat::R8G8B8A8_SInt: return WGPUVertexFormat_Sint8x4;
|
||||
case PixelFormat::R8_UNorm: return WGPUVertexFormat_Unorm8;
|
||||
case PixelFormat::R8G8_UNorm: return WGPUVertexFormat_Unorm8x2;
|
||||
case PixelFormat::R8G8B8A8_UNorm: return WGPUVertexFormat_Unorm8x4;
|
||||
case PixelFormat::R8_SNorm: return WGPUVertexFormat_Snorm8;
|
||||
case PixelFormat::R8G8_SNorm: return WGPUVertexFormat_Snorm8x2;
|
||||
case PixelFormat::R8G8B8A8_SNorm: return WGPUVertexFormat_Snorm8x4;
|
||||
case PixelFormat::R16_UInt: return WGPUVertexFormat_Uint16;
|
||||
case PixelFormat::R16G16_UInt: return WGPUVertexFormat_Uint16x2;
|
||||
case PixelFormat::R16G16B16A16_UInt: return WGPUVertexFormat_Uint16x4;
|
||||
case PixelFormat::R16_SInt: return WGPUVertexFormat_Sint16;
|
||||
case PixelFormat::R16G16_SInt: return WGPUVertexFormat_Sint16x2;
|
||||
case PixelFormat::R16G16B16A16_SInt: return WGPUVertexFormat_Sint16x4;
|
||||
case PixelFormat::R16_UNorm: return WGPUVertexFormat_Unorm16;
|
||||
case PixelFormat::R16G16_UNorm: return WGPUVertexFormat_Unorm16x2;
|
||||
case PixelFormat::R16G16B16A16_UNorm: return WGPUVertexFormat_Unorm16x4;
|
||||
case PixelFormat::R16_SNorm: return WGPUVertexFormat_Snorm16;
|
||||
case PixelFormat::R16G16_SNorm: return WGPUVertexFormat_Snorm16x2;
|
||||
case PixelFormat::R16G16B16A16_SNorm: return WGPUVertexFormat_Snorm16x4;
|
||||
case PixelFormat::R16_Float: return WGPUVertexFormat_Float16;
|
||||
case PixelFormat::R16G16_Float: return WGPUVertexFormat_Float16x2;
|
||||
case PixelFormat::R16G16B16A16_Float: return WGPUVertexFormat_Float16x4;
|
||||
case PixelFormat::R32_Float: return WGPUVertexFormat_Float32;
|
||||
case PixelFormat::R32G32_Float: return WGPUVertexFormat_Float32x2;
|
||||
case PixelFormat::R32G32B32_Float: return WGPUVertexFormat_Float32x3;
|
||||
case PixelFormat::R32G32B32A32_Float: return WGPUVertexFormat_Float32x4;
|
||||
case PixelFormat::R32_UInt: return WGPUVertexFormat_Uint32;
|
||||
case PixelFormat::R32G32_UInt: return WGPUVertexFormat_Uint32x2;
|
||||
case PixelFormat::R32G32B32_UInt: return WGPUVertexFormat_Uint32x3;
|
||||
case PixelFormat::R32G32B32A32_UInt: return WGPUVertexFormat_Uint32x4;
|
||||
case PixelFormat::R32_SInt: return WGPUVertexFormat_Sint32;
|
||||
case PixelFormat::R32G32_SInt: return WGPUVertexFormat_Sint32x2;
|
||||
case PixelFormat::R32G32B32_SInt: return WGPUVertexFormat_Sint32x3;
|
||||
case PixelFormat::R32G32B32A32_SInt: return WGPUVertexFormat_Sint32x4;
|
||||
case PixelFormat::R10G10B10A2_UNorm: return WGPUVertexFormat_Unorm10_10_10_2;
|
||||
case PixelFormat::B8G8R8A8_UNorm: return WGPUVertexFormat_Unorm8x4BGRA;
|
||||
default: return WGPUVertexFormat_Force32;
|
||||
// @formatter:on
|
||||
}
|
||||
}
|
||||
|
||||
WGPUTextureFormat RenderToolsWebGPU::ToTextureFormat(PixelFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
// @formatter:off
|
||||
case PixelFormat::R32G32B32A32_Typeless:
|
||||
case PixelFormat::R32G32B32A32_Float: return WGPUTextureFormat_RGBA32Float;
|
||||
case PixelFormat::R32G32B32A32_UInt: return WGPUTextureFormat_RGBA32Uint;
|
||||
case PixelFormat::R32G32B32A32_SInt: return WGPUTextureFormat_RGBA32Sint;
|
||||
case PixelFormat::R16G16B16A16_Typeless:
|
||||
case PixelFormat::R16G16B16A16_Float: return WGPUTextureFormat_RGBA16Float;
|
||||
case PixelFormat::R16G16B16A16_UNorm: return WGPUTextureFormat_RGBA16Unorm;
|
||||
case PixelFormat::R16G16B16A16_UInt: return WGPUTextureFormat_RGBA16Uint;
|
||||
case PixelFormat::R16G16B16A16_SNorm: return WGPUTextureFormat_RGBA16Snorm;
|
||||
case PixelFormat::R16G16B16A16_SInt: return WGPUTextureFormat_RGBA16Sint;
|
||||
case PixelFormat::R32G32_Typeless:
|
||||
case PixelFormat::R32G32_Float: return WGPUTextureFormat_RG32Float;
|
||||
case PixelFormat::R32G32_UInt: return WGPUTextureFormat_RG32Uint;
|
||||
case PixelFormat::R32G32_SInt: return WGPUTextureFormat_RG32Sint;
|
||||
case PixelFormat::R32G8X24_Typeless:
|
||||
case PixelFormat::D32_Float_S8X24_UInt:
|
||||
case PixelFormat::R32_Float_X8X24_Typeless:
|
||||
case PixelFormat::X32_Typeless_G8X24_UInt: return WGPUTextureFormat_Depth32FloatStencil8;
|
||||
case PixelFormat::R10G10B10A2_Typeless:
|
||||
case PixelFormat::R10G10B10A2_UNorm: return WGPUTextureFormat_RGB10A2Unorm;
|
||||
case PixelFormat::R10G10B10A2_UInt: return WGPUTextureFormat_RGB10A2Uint;
|
||||
case PixelFormat::R11G11B10_Float: return WGPUTextureFormat_RG11B10Ufloat;
|
||||
case PixelFormat::R8G8B8A8_Typeless:
|
||||
case PixelFormat::R8G8B8A8_UNorm: return WGPUTextureFormat_RGBA8Unorm;
|
||||
case PixelFormat::R8G8B8A8_UNorm_sRGB: return WGPUTextureFormat_RGBA8UnormSrgb;
|
||||
case PixelFormat::R8G8B8A8_UInt: return WGPUTextureFormat_RGBA8Uint;
|
||||
case PixelFormat::R8G8B8A8_SNorm: return WGPUTextureFormat_RGBA8Snorm;
|
||||
case PixelFormat::R8G8B8A8_SInt: return WGPUTextureFormat_RGBA8Sint;
|
||||
case PixelFormat::R16G16_Typeless:
|
||||
case PixelFormat::R16G16_Float: return WGPUTextureFormat_RG16Float;
|
||||
case PixelFormat::R16G16_UNorm: return WGPUTextureFormat_RG16Unorm;
|
||||
case PixelFormat::R16G16_UInt: return WGPUTextureFormat_RG16Uint;
|
||||
case PixelFormat::R16G16_SNorm: return WGPUTextureFormat_RG16Snorm;
|
||||
case PixelFormat::R16G16_SInt: return WGPUTextureFormat_RG16Sint;
|
||||
case PixelFormat::D32_Float: return WGPUTextureFormat_Depth32Float;
|
||||
case PixelFormat::R32_Typeless:
|
||||
case PixelFormat::R32_Float: return WGPUTextureFormat_R32Float;
|
||||
case PixelFormat::R32_UInt: return WGPUTextureFormat_R32Uint;
|
||||
case PixelFormat::R32_SInt: return WGPUTextureFormat_R32Sint;
|
||||
case PixelFormat::R24G8_Typeless:
|
||||
case PixelFormat::D24_UNorm_S8_UInt:
|
||||
case PixelFormat::R24_UNorm_X8_Typeless:
|
||||
case PixelFormat::X24_Typeless_G8_UInt: return WGPUTextureFormat_Depth24PlusStencil8;
|
||||
case PixelFormat::R8G8_Typeless:
|
||||
case PixelFormat::R8G8_UNorm: return WGPUTextureFormat_RG8Unorm;
|
||||
case PixelFormat::R8G8_UInt: return WGPUTextureFormat_RG8Uint;
|
||||
case PixelFormat::R8G8_SNorm: return WGPUTextureFormat_RG8Snorm;
|
||||
case PixelFormat::R8G8_SInt: return WGPUTextureFormat_RG8Sint;
|
||||
case PixelFormat::R16_Typeless:
|
||||
case PixelFormat::R16_Float: return WGPUTextureFormat_R16Float;
|
||||
case PixelFormat::D16_UNorm: return WGPUTextureFormat_Depth16Unorm;
|
||||
case PixelFormat::R16_UNorm: return WGPUTextureFormat_R16Unorm;
|
||||
case PixelFormat::R16_UInt: return WGPUTextureFormat_R16Uint;
|
||||
case PixelFormat::R16_SNorm: return WGPUTextureFormat_R16Snorm;
|
||||
case PixelFormat::R16_SInt: return WGPUTextureFormat_R16Sint;
|
||||
case PixelFormat::R8_Typeless:
|
||||
case PixelFormat::R8_UNorm: return WGPUTextureFormat_R8Unorm;
|
||||
case PixelFormat::R8_UInt: return WGPUTextureFormat_R8Uint;
|
||||
case PixelFormat::R8_SNorm: return WGPUTextureFormat_R8Snorm;
|
||||
case PixelFormat::R8_SInt: return WGPUTextureFormat_R8Sint;
|
||||
case PixelFormat::R9G9B9E5_SharedExp: return WGPUTextureFormat_RGB9E5Ufloat;
|
||||
case PixelFormat::BC1_Typeless:
|
||||
case PixelFormat::BC1_UNorm: return WGPUTextureFormat_BC1RGBAUnorm;
|
||||
case PixelFormat::BC1_UNorm_sRGB: return WGPUTextureFormat_BC1RGBAUnormSrgb;
|
||||
case PixelFormat::BC2_Typeless:
|
||||
case PixelFormat::BC2_UNorm: return WGPUTextureFormat_BC2RGBAUnorm;
|
||||
case PixelFormat::BC2_UNorm_sRGB: return WGPUTextureFormat_BC2RGBAUnormSrgb;
|
||||
case PixelFormat::BC3_Typeless:
|
||||
case PixelFormat::BC3_UNorm: return WGPUTextureFormat_BC3RGBAUnorm;
|
||||
case PixelFormat::BC3_UNorm_sRGB: return WGPUTextureFormat_BC3RGBAUnormSrgb;
|
||||
case PixelFormat::BC4_Typeless:
|
||||
case PixelFormat::BC4_UNorm: return WGPUTextureFormat_BC4RUnorm;
|
||||
case PixelFormat::BC4_SNorm: return WGPUTextureFormat_BC4RSnorm;
|
||||
case PixelFormat::BC5_Typeless:
|
||||
case PixelFormat::BC5_UNorm: return WGPUTextureFormat_BC5RGUnorm;
|
||||
case PixelFormat::BC5_SNorm: return WGPUTextureFormat_BC5RGSnorm;
|
||||
case PixelFormat::B8G8R8A8_Typeless:
|
||||
case PixelFormat::B8G8R8X8_Typeless:
|
||||
case PixelFormat::B8G8R8A8_UNorm:
|
||||
case PixelFormat::B8G8R8X8_UNorm: return WGPUTextureFormat_BGRA8Unorm;
|
||||
case PixelFormat::B8G8R8A8_UNorm_sRGB:
|
||||
case PixelFormat::B8G8R8X8_UNorm_sRGB: return WGPUTextureFormat_BGRA8UnormSrgb;
|
||||
case PixelFormat::BC6H_Typeless:
|
||||
case PixelFormat::BC6H_Uf16: return WGPUTextureFormat_BC6HRGBUfloat;
|
||||
case PixelFormat::BC6H_Sf16: return WGPUTextureFormat_BC6HRGBFloat;
|
||||
case PixelFormat::BC7_Typeless:
|
||||
case PixelFormat::BC7_UNorm: return WGPUTextureFormat_BC7RGBAUnorm;
|
||||
case PixelFormat::BC7_UNorm_sRGB: return WGPUTextureFormat_BC7RGBAUnormSrgb;
|
||||
case PixelFormat::ASTC_4x4_UNorm: return WGPUTextureFormat_ASTC4x4Unorm;
|
||||
case PixelFormat::ASTC_4x4_UNorm_sRGB: return WGPUTextureFormat_ASTC4x4UnormSrgb;
|
||||
case PixelFormat::ASTC_6x6_UNorm: return WGPUTextureFormat_ASTC6x6Unorm;
|
||||
case PixelFormat::ASTC_6x6_UNorm_sRGB: return WGPUTextureFormat_ASTC6x6UnormSrgb;
|
||||
case PixelFormat::ASTC_8x8_UNorm: return WGPUTextureFormat_ASTC8x8Unorm;
|
||||
case PixelFormat::ASTC_8x8_UNorm_sRGB: return WGPUTextureFormat_ASTC8x8UnormSrgb;
|
||||
case PixelFormat::ASTC_10x10_UNorm: return WGPUTextureFormat_ASTC10x10Unorm;
|
||||
case PixelFormat::ASTC_10x10_UNorm_sRGB: return WGPUTextureFormat_ASTC10x10UnormSrgb;
|
||||
default: return WGPUTextureFormat_Undefined;
|
||||
// @formatter:on
|
||||
}
|
||||
}
|
||||
|
||||
PixelFormat RenderToolsWebGPU::ToPixelFormat(WGPUTextureFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
// @formatter:off
|
||||
case WGPUTextureFormat_R8Unorm: return PixelFormat::R8_UNorm;
|
||||
case WGPUTextureFormat_R8Snorm: return PixelFormat::R8_SNorm;
|
||||
case WGPUTextureFormat_R8Uint: return PixelFormat::R8_UInt;
|
||||
case WGPUTextureFormat_R8Sint: return PixelFormat::R8_SInt;
|
||||
case WGPUTextureFormat_R16Unorm: return PixelFormat::R16_UNorm;
|
||||
case WGPUTextureFormat_R16Snorm: return PixelFormat::R16_SNorm;
|
||||
case WGPUTextureFormat_R16Uint: return PixelFormat::R16_UInt;
|
||||
case WGPUTextureFormat_R16Sint: return PixelFormat::R16_SInt;
|
||||
case WGPUTextureFormat_R16Float: return PixelFormat::R16_Float;
|
||||
case WGPUTextureFormat_R32Float: return PixelFormat::R32_Float;
|
||||
case WGPUTextureFormat_R32Uint: return PixelFormat::R32_UInt;
|
||||
case WGPUTextureFormat_R32Sint: return PixelFormat::R32_SInt;
|
||||
case WGPUTextureFormat_RG16Unorm: return PixelFormat::R16G16_UNorm;
|
||||
case WGPUTextureFormat_RG16Snorm: return PixelFormat::R16G16_SNorm;
|
||||
case WGPUTextureFormat_RG16Uint: return PixelFormat::R16G16_UInt;
|
||||
case WGPUTextureFormat_RG16Sint: return PixelFormat::R16G16_SInt;
|
||||
case WGPUTextureFormat_RG16Float: return PixelFormat::R16G16_Float;
|
||||
case WGPUTextureFormat_RGBA8Unorm: return PixelFormat::R8G8B8A8_UNorm;
|
||||
case WGPUTextureFormat_RGBA8UnormSrgb: return PixelFormat::R8G8B8A8_UNorm_sRGB;
|
||||
case WGPUTextureFormat_RGBA8Snorm: return PixelFormat::R8G8B8A8_SNorm;
|
||||
case WGPUTextureFormat_RGBA8Uint: return PixelFormat::R8G8B8A8_UInt;
|
||||
case WGPUTextureFormat_RGBA8Sint: return PixelFormat::R8G8B8A8_SInt;
|
||||
case WGPUTextureFormat_BGRA8Unorm: return PixelFormat::B8G8R8A8_UNorm;
|
||||
case WGPUTextureFormat_BGRA8UnormSrgb: return PixelFormat::B8G8R8A8_UNorm_sRGB;
|
||||
case WGPUTextureFormat_RGB10A2Uint: return PixelFormat::R10G10B10A2_UInt;
|
||||
case WGPUTextureFormat_RGB10A2Unorm: return PixelFormat::R10G10B10A2_UNorm;
|
||||
case WGPUTextureFormat_RG11B10Ufloat: return PixelFormat::R11G11B10_Float;
|
||||
case WGPUTextureFormat_RGB9E5Ufloat: return PixelFormat::R9G9B9E5_SharedExp;
|
||||
case WGPUTextureFormat_RG32Float: return PixelFormat::R32G32_Float;
|
||||
case WGPUTextureFormat_RG32Uint: return PixelFormat::R32G32_UInt;
|
||||
case WGPUTextureFormat_RG32Sint: return PixelFormat::R32G32_SInt;
|
||||
case WGPUTextureFormat_RGBA16Unorm: return PixelFormat::R16G16B16A16_UNorm;
|
||||
case WGPUTextureFormat_RGBA16Snorm: return PixelFormat::R16G16B16A16_SNorm;
|
||||
case WGPUTextureFormat_RGBA16Uint: return PixelFormat::R16G16B16A16_UInt;
|
||||
case WGPUTextureFormat_RGBA16Sint: return PixelFormat::R16G16B16A16_SInt;
|
||||
case WGPUTextureFormat_RGBA16Float: return PixelFormat::R16G16B16A16_Float;
|
||||
case WGPUTextureFormat_RGBA32Float: return PixelFormat::R32G32B32A32_Float;
|
||||
case WGPUTextureFormat_RGBA32Uint: return PixelFormat::R32G32B32A32_UInt;
|
||||
case WGPUTextureFormat_RGBA32Sint: return PixelFormat::R32G32B32A32_SInt;
|
||||
case WGPUTextureFormat_Depth16Unorm: return PixelFormat::D16_UNorm;
|
||||
case WGPUTextureFormat_Depth24Plus:
|
||||
case WGPUTextureFormat_Depth24PlusStencil8: return PixelFormat::D24_UNorm_S8_UInt;
|
||||
case WGPUTextureFormat_Depth32Float: return PixelFormat::D32_Float;
|
||||
case WGPUTextureFormat_Depth32FloatStencil8: return PixelFormat::D32_Float_S8X24_UInt;
|
||||
case WGPUTextureFormat_BC1RGBAUnorm: return PixelFormat::BC1_UNorm;
|
||||
case WGPUTextureFormat_BC1RGBAUnormSrgb: return PixelFormat::BC1_UNorm_sRGB;
|
||||
case WGPUTextureFormat_BC2RGBAUnorm: return PixelFormat::BC2_UNorm;
|
||||
case WGPUTextureFormat_BC2RGBAUnormSrgb: return PixelFormat::BC2_UNorm_sRGB;
|
||||
case WGPUTextureFormat_BC3RGBAUnorm: return PixelFormat::BC3_UNorm;
|
||||
case WGPUTextureFormat_BC3RGBAUnormSrgb: return PixelFormat::BC3_UNorm_sRGB;
|
||||
case WGPUTextureFormat_BC4RUnorm: return PixelFormat::BC4_UNorm;
|
||||
case WGPUTextureFormat_BC4RSnorm: return PixelFormat::BC4_SNorm;
|
||||
case WGPUTextureFormat_BC5RGUnorm: return PixelFormat::BC5_UNorm;
|
||||
case WGPUTextureFormat_BC5RGSnorm: return PixelFormat::BC5_SNorm;
|
||||
case WGPUTextureFormat_BC6HRGBUfloat: return PixelFormat::BC6H_Uf16;
|
||||
case WGPUTextureFormat_BC6HRGBFloat: return PixelFormat::BC6H_Sf16;
|
||||
case WGPUTextureFormat_BC7RGBAUnorm: return PixelFormat::BC7_UNorm;
|
||||
case WGPUTextureFormat_BC7RGBAUnormSrgb: return PixelFormat::BC7_UNorm_sRGB;
|
||||
case WGPUTextureFormat_ASTC4x4Unorm: return PixelFormat::ASTC_4x4_UNorm;
|
||||
case WGPUTextureFormat_ASTC4x4UnormSrgb: return PixelFormat::ASTC_4x4_UNorm_sRGB;
|
||||
case WGPUTextureFormat_ASTC6x6Unorm: return PixelFormat::ASTC_6x6_UNorm;
|
||||
case WGPUTextureFormat_ASTC6x6UnormSrgb: return PixelFormat::ASTC_6x6_UNorm_sRGB;
|
||||
case WGPUTextureFormat_ASTC8x8Unorm: return PixelFormat::ASTC_8x8_UNorm;
|
||||
case WGPUTextureFormat_ASTC8x8UnormSrgb: return PixelFormat::ASTC_8x8_UNorm_sRGB;
|
||||
case WGPUTextureFormat_ASTC10x10Unorm: return PixelFormat::ASTC_10x10_UNorm;
|
||||
case WGPUTextureFormat_ASTC10x10UnormSrgb: return PixelFormat::ASTC_10x10_UNorm_sRGB;
|
||||
default: return PixelFormat::Unknown;
|
||||
// @formatter:on
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
74
Source/Engine/GraphicsDevice/WebGPU/RenderToolsWebGPU.h
Normal file
74
Source/Engine/GraphicsDevice/WebGPU/RenderToolsWebGPU.h
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if GRAPHICS_API_WEBGPU
|
||||
|
||||
#include "Engine/Core/Types/String.h"
|
||||
#include "IncludeWebGPU.h"
|
||||
#include <emscripten/emscripten.h>
|
||||
|
||||
enum class PixelFormat : unsigned;
|
||||
|
||||
/// <summary>
|
||||
/// User data used by AsyncCallbackWebGPU to track state adn result.
|
||||
/// </summary>
|
||||
struct AsyncCallbackDataWebGPU
|
||||
{
|
||||
String Message;
|
||||
uint32 Status = 0;
|
||||
intptr Result = 0;
|
||||
double WaitTime = 0;
|
||||
|
||||
FORCE_INLINE void Call(bool success, uint32 status, WGPUStringView message)
|
||||
{
|
||||
Status = status;
|
||||
if (!success)
|
||||
Message.Set(message.data, message.data ? (int32)message.length : 0);
|
||||
Platform::AtomicStore(&Result, success ? 1 : 2);
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Helper utility to run WebGPU APIs that use async callback in sync by waiting on the spontaneous call back with an active-waiting loop.
|
||||
/// </summary>
|
||||
template<typename CallbackInfo, typename UserData = AsyncCallbackDataWebGPU>
|
||||
struct AsyncCallbackWebGPU
|
||||
{
|
||||
UserData Data;
|
||||
CallbackInfo Info;
|
||||
|
||||
AsyncCallbackWebGPU(CallbackInfo callbackDefault)
|
||||
: Info(callbackDefault)
|
||||
{
|
||||
Info.mode = WGPUCallbackMode_AllowSpontaneous;
|
||||
Info.userdata1 = &Data;
|
||||
}
|
||||
|
||||
WGPUWaitStatus Wait()
|
||||
{
|
||||
auto startTime = Platform::GetTimeSeconds();
|
||||
int32 ticksLeft = 500; // Wait max 5 second
|
||||
while (Platform::AtomicRead(&Data.Result) == 0 && ticksLeft-- > 0)
|
||||
emscripten_sleep(10);
|
||||
if (ticksLeft <= 0)
|
||||
{
|
||||
Data.WaitTime = Platform::GetTimeSeconds() - startTime;
|
||||
return WGPUWaitStatus_TimedOut;
|
||||
}
|
||||
return Data.Result == 1 ? WGPUWaitStatus_Success : WGPUWaitStatus_Error;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Set of utilities for rendering on Web GPU platform.
|
||||
/// </summary>
|
||||
class RenderToolsWebGPU
|
||||
{
|
||||
public:
|
||||
static WGPUVertexFormat ToVertexFormat(PixelFormat format);
|
||||
static WGPUTextureFormat ToTextureFormat(PixelFormat format);
|
||||
static PixelFormat ToPixelFormat(WGPUTextureFormat format);
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user