diff --git a/.gitmodules b/.gitmodules index 92b6928627..babb56760c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,6 @@ [submodule "ThirdParty/xxHash"] path = ThirdParty/xxHash url = https://github.com/DiligentGraphics/xxHash +[submodule "ThirdParty/webgpu"] + path = ThirdParty/webgpu + url = https://github.com/eliemichel/WebGPU-distribution diff --git a/BuildUtils.cmake b/BuildUtils.cmake index 34c3cdf6ac..313843d282 100644 --- a/BuildUtils.cmake +++ b/BuildUtils.cmake @@ -16,6 +16,9 @@ if(PLATFORM_WIN32 OR PLATFORM_UNIVERSAL_WINDOWS) if(METAL_SUPPORTED) list(APPEND ENGINE_DLLS Diligent-GraphicsEngineMetal-shared) endif() + if(WEBGPU_SUPPORTED) + list(APPEND ENGINE_DLLS Diligent-GraphicsEngineWebGPU-shared) + endif() if(TARGET Diligent-Archiver-shared) list(APPEND ENGINE_DLLS Diligent-Archiver-shared) endif() @@ -264,6 +267,10 @@ function(get_supported_backends _TARGETS) if(METAL_SUPPORTED) list(APPEND BACKENDS Diligent-GraphicsEngineMetal-${LIB_TYPE}) endif() + if(WEBGPU_SUPPORTED) + list(APPEND BACKENDS Diligent-GraphicsEngineWebGPU-${LIB_TYPE}) + endif() + # ${_TARGETS} == ENGINE_LIBRARIES # ${${_TARGETS}} == ${ENGINE_LIBRARIES} set(${_TARGETS} ${${_TARGETS}} ${BACKENDS} PARENT_SCOPE) diff --git a/CMakeLists.txt b/CMakeLists.txt index 74662771bc..ba6f943726 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,7 @@ set(GL_SUPPORTED FALSE CACHE INTERNAL "GL is not supported") set(GLES_SUPPORTED FALSE CACHE INTERNAL "GLES is not supported") set(VULKAN_SUPPORTED FALSE CACHE INTERNAL "Vulkan is not supported") set(METAL_SUPPORTED FALSE CACHE INTERNAL "Metal is not supported") +set(WEBGPU_SUPPORTED FALSE CACHE INTERNAL "WebGPU is not supported") set(ARCHIVER_SUPPORTED FALSE CACHE INTERNAL "Archiver is not supported") set(DILIGENT_CORE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CACHE INTERNAL "DiligentCore module source directory") @@ -156,6 +157,7 @@ if(PLATFORM_WIN32) set(GL_SUPPORTED TRUE CACHE INTERNAL "OpenGL is supported on Win32 platform") set(VULKAN_SUPPORTED TRUE CACHE INTERNAL "Vulkan is supported on Win32 platform") + set(WEBGPU_SUPPORTED TRUE CACHE INTERNAL "WebGPU is supported on Win32 platform") set(ARCHIVER_SUPPORTED TRUE CACHE INTERNAL "Archiver is supported on Win32 platform") target_compile_definitions(Diligent-PublicBuildSettings INTERFACE PLATFORM_WIN32=1) elseif(PLATFORM_UNIVERSAL_WINDOWS) @@ -265,6 +267,7 @@ option(DILIGENT_NO_DIRECT3D12 "Disable Direct3D12 backend" OFF) option(DILIGENT_NO_OPENGL "Disable OpenGL/GLES backend" OFF) option(DILIGENT_NO_VULKAN "Disable Vulkan backend" OFF) option(DILIGENT_NO_METAL "Disable Metal backend" OFF) +option(DILIGENT_NO_WEBGPU "Disable WebGPU backend" OFF) option(DILIGENT_NO_ARCHIVER "Do not build archiver" OFF) if(${DILIGENT_NO_DIRECT3D11}) set(D3D11_SUPPORTED FALSE CACHE INTERNAL "D3D11 backend is forcibly disabled") @@ -282,11 +285,14 @@ endif() if(${DILIGENT_NO_METAL}) set(METAL_SUPPORTED FALSE CACHE INTERNAL "Metal backend is forcibly disabled") endif() +if(${DILIGENT_NO_WEBGPU}) + set(WEBGPU_SUPPORTED FALSE CACHE INTERNAL "WebGPU backend is forcibly disabled") +endif() if(${DILIGENT_NO_ARCHIVER}) set(ARCHIVER_SUPPORTED FALSE CACHE INTERNAL "Archiver is forcibly disabled") endif() -if(NOT (${D3D11_SUPPORTED} OR ${D3D12_SUPPORTED} OR ${GL_SUPPORTED} OR ${GLES_SUPPORTED} OR ${VULKAN_SUPPORTED} OR ${METAL_SUPPORTED})) +if(NOT (${D3D11_SUPPORTED} OR ${D3D12_SUPPORTED} OR ${GL_SUPPORTED} OR ${GLES_SUPPORTED} OR ${VULKAN_SUPPORTED} OR ${METAL_SUPPORTED} OR ${WEBGPU_SUPPORTED})) message(FATAL_ERROR "No rendering backends are select to build") endif() @@ -297,6 +303,7 @@ message("GL_SUPPORTED: " ${GL_SUPPORTED}) message("GLES_SUPPORTED: " ${GLES_SUPPORTED}) message("VULKAN_SUPPORTED: " ${VULKAN_SUPPORTED}) message("METAL_SUPPORTED: " ${METAL_SUPPORTED}) +message("WEBGPU_SUPPORTED: " ${WEBGPU_SUPPORTED}) target_compile_definitions(Diligent-PublicBuildSettings INTERFACE @@ -306,6 +313,7 @@ INTERFACE GLES_SUPPORTED=$ VULKAN_SUPPORTED=$ METAL_SUPPORTED=$ + WEBGPU_SUPPORTED=$ ) foreach(DBG_CONFIG ${DEBUG_CONFIGURATIONS}) diff --git a/Graphics/CMakeLists.txt b/Graphics/CMakeLists.txt index 5b3363b752..49bbb9ad71 100644 --- a/Graphics/CMakeLists.txt +++ b/Graphics/CMakeLists.txt @@ -47,6 +47,9 @@ if(GL_SUPPORTED OR GLES_SUPPORTED) add_subdirectory(GraphicsEngineOpenGL) endif() +if (WEBGPU_SUPPORTED) + add_subdirectory(GraphicsEngineWebGPU) +endif() if(ARCHIVER_SUPPORTED) add_subdirectory(Archiver) diff --git a/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp b/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp index 710ad13726..30c3f313b2 100644 --- a/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp +++ b/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp @@ -1248,7 +1248,7 @@ const char* GetDeviceFeatureStateString(DEVICE_FEATURE_STATE State, bool bGetFul const char* GetRenderDeviceTypeString(RENDER_DEVICE_TYPE DeviceType, bool bGetEnumString) { - static_assert(RENDER_DEVICE_TYPE_COUNT == 7, "Did you add a new device type? Please update the switch below."); + static_assert(RENDER_DEVICE_TYPE_COUNT == 8, "Did you add a new device type? Please update the switch below."); switch (DeviceType) { // clang-format off @@ -1259,6 +1259,7 @@ const char* GetRenderDeviceTypeString(RENDER_DEVICE_TYPE DeviceType, bool bGetEn case RENDER_DEVICE_TYPE_GLES: return bGetEnumString ? "RENDER_DEVICE_TYPE_GLES" : "OpenGLES"; break; case RENDER_DEVICE_TYPE_VULKAN: return bGetEnumString ? "RENDER_DEVICE_TYPE_VULKAN" : "Vulkan"; break; case RENDER_DEVICE_TYPE_METAL: return bGetEnumString ? "RENDER_DEVICE_TYPE_METAL" : "Metal"; break; + case RENDER_DEVICE_TYPE_WEBGPU: return bGetEnumString ? "RENDER_DEVICe_TYPE_WEBGPU" : "WebGPU"; break; // clang-format on default: UNEXPECTED("Unknown/unsupported device type"); return "UNKNOWN"; } @@ -1266,7 +1267,7 @@ const char* GetRenderDeviceTypeString(RENDER_DEVICE_TYPE DeviceType, bool bGetEn const char* GetRenderDeviceTypeShortString(RENDER_DEVICE_TYPE DeviceType, bool Capital) { - static_assert(RENDER_DEVICE_TYPE_COUNT == 7, "Did you add a new device type? Please update the switch below."); + static_assert(RENDER_DEVICE_TYPE_COUNT == 8, "Did you add a new device type? Please update the switch below."); switch (DeviceType) { // clang-format off @@ -1277,6 +1278,7 @@ const char* GetRenderDeviceTypeShortString(RENDER_DEVICE_TYPE DeviceType, bool C case RENDER_DEVICE_TYPE_GLES: return Capital ? "GLES" : "gles"; break; case RENDER_DEVICE_TYPE_VULKAN: return Capital ? "VK" : "vk"; break; case RENDER_DEVICE_TYPE_METAL: return Capital ? "MTL" : "mtl"; break; + case RENDER_DEVICE_TYPE_WEBGPU: return Capital ? "WEBGPU" : "webgpu"; break; // clang-format on default: UNEXPECTED("Unknown/unsupported device type"); return "UNKNOWN"; } diff --git a/Graphics/GraphicsEngine/interface/GraphicsTypes.h b/Graphics/GraphicsEngine/interface/GraphicsTypes.h index e863ac3120..bea20d96dd 100644 --- a/Graphics/GraphicsEngine/interface/GraphicsTypes.h +++ b/Graphics/GraphicsEngine/interface/GraphicsTypes.h @@ -1576,6 +1576,7 @@ enum RENDER_DEVICE_TYPE RENDER_DEVICE_TYPE_GLES, ///< OpenGLES device RENDER_DEVICE_TYPE_VULKAN, ///< Vulkan device RENDER_DEVICE_TYPE_METAL, ///< Metal device + RENDER_DEVICE_TYPE_WEBGPU, ///< WebGPU device RENDER_DEVICE_TYPE_COUNT ///< The total number of device types }; @@ -3846,6 +3847,26 @@ struct EngineMtlCreateInfo DILIGENT_DERIVE(EngineCreateInfo) }; typedef struct EngineMtlCreateInfo EngineMtlCreateInfo; +/// Attributes of the WebGPU-based engine implementation +struct EngineWebGPUCreateInfo DILIGENT_DERIVE(EngineCreateInfo) + + /// + Uint32 QueueSignalPoolSize DEFAULT_INITIALIZER(32); + + /// + Uint32 DynamicHeapPageSize DEFAULT_INITIALIZER(4 << 20); + +#if DILIGENT_CPP_INTERFACE + EngineWebGPUCreateInfo() noexcept : + EngineWebGPUCreateInfo{EngineCreateInfo{}} + {} + + explicit EngineWebGPUCreateInfo(const EngineCreateInfo &EngineCI) noexcept : + EngineCreateInfo{EngineCI} + {} +#endif +}; +typedef struct EngineWebGPUCreateInfo EngineWebGPUCreateInfo; /// Box struct Box diff --git a/Graphics/GraphicsEngine/src/DeviceObjectArchive.cpp b/Graphics/GraphicsEngine/src/DeviceObjectArchive.cpp index e6a91824c0..ef668f4e4b 100644 --- a/Graphics/GraphicsEngine/src/DeviceObjectArchive.cpp +++ b/Graphics/GraphicsEngine/src/DeviceObjectArchive.cpp @@ -39,7 +39,7 @@ namespace Diligent DeviceObjectArchive::DeviceType RenderDeviceTypeToArchiveDeviceType(RENDER_DEVICE_TYPE Type) { - static_assert(RENDER_DEVICE_TYPE_COUNT == 7, "Did you add a new render device type? Please handle it here."); + static_assert(RENDER_DEVICE_TYPE_COUNT == 8, "Did you add a new render device type? Please handle it here."); switch (Type) { // clang-format off diff --git a/Graphics/GraphicsEngineWebGPU/CMakeLists.txt b/Graphics/GraphicsEngineWebGPU/CMakeLists.txt new file mode 100644 index 0000000000..6207c06018 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/CMakeLists.txt @@ -0,0 +1,142 @@ +cmake_minimum_required (VERSION 3.10) + +project(Diligent-GraphicsEngineWebGPU CXX) + +set(INCLUDE + include/AttachmentCleanerWebGPU.hpp + include/BufferViewWebGPUImpl.hpp + include/BufferWebGPUImpl.hpp + include/DeviceContextWebGPUImpl.hpp + include/EngineWebGPUImplTraits.hpp + include/FenceWebGPUImpl.hpp + include/FramebufferWebGPUImpl.hpp + include/pch.h + include/PipelineResourceAttribsWebGPU.hpp + include/PipelineResourceSignatureWebGPUImpl.hpp + include/PipelineStateWebGPUImpl.hpp + include/QueryManagerWebGPU.hpp + include/QueryWebGPUImpl.hpp + include/QueueSignalPoolWebGPU.hpp + include/RenderDeviceWebGPUImpl.hpp + include/RenderPassWebGPUImpl.hpp + include/SamplerWebGPUImpl.hpp + include/ShaderResourceBindingWebGPUImpl.hpp + include/ShaderResourceCacheWebGPU.hpp + include/ShaderVariableManagerWebGPU.hpp + include/ShaderWebGPUImpl.hpp + include/SharedMemoryManagerWebGPU.hpp + include/SwapChainWebGPUImpl.hpp + include/TextureViewWebGPUImpl.hpp + include/TextureWebGPUImpl.hpp + include/WebGPUObjectWrappers.hpp + include/WebGPUTypeConversions.hpp +) + +set(INTERFACE + interface/BufferWebGPU.h + interface/DeviceContextWebGPU.h + interface/EngineFactoryWebGPU.h + interface/RenderDeviceWebGPU.h + interface/SamplerWebGPU.h + interface/SwapChainWebGPU.h + interface/TextureViewWebGPU.h + interface/TextureWebGPU.h +) + +set(SRC + src/AttachmentCleanerWebGPU.cpp + src/BufferViewWebGPUImpl.cpp + src/BufferWebGPUImpl.cpp + src/DeviceContextWebGPUImpl.cpp + src/EngineFactoryWebGPU.cpp + src/FenceWebGPUImpl.cpp + src/FramebufferWebGPUImpl.cpp + src/QueryManagerWebGPU.cpp + src/QueryWebGPUImpl.cpp + src/RenderDeviceWebGPUImpl.cpp + src/RenderPassWebGPUImpl.cpp + src/SamplerWebGPUImpl.cpp + src/SharedMemoryManagerWebGPU.cpp + src/SwapChainWebGPUImpl.cpp + src/TextureViewWebGPUImpl.cpp + src/TextureWebGPUImpl.cpp + src/WebGPUTypeConversions.cpp +) + +set(DLL_SRC + src/DLLMain.cpp + src/GraphicsEngineWebGPU.def +) + +add_library(Diligent-GraphicsEngineWebGPUInterface INTERFACE) +target_link_libraries (Diligent-GraphicsEngineWebGPUInterface INTERFACE Diligent-GraphicsEngineInterface) +target_include_directories(Diligent-GraphicsEngineWebGPUInterface INTERFACE interface) + +add_library(Diligent-GraphicsEngineWebGPU-static STATIC + ${SRC} ${INTERFACE} ${INCLUDE} + readme.md +) + +target_precompile_headers(Diligent-GraphicsEngineWebGPU-static PRIVATE include/pch.h) + +add_library(Diligent-GraphicsEngineWebGPU-shared SHARED + readme.md +) + +if(MSVC) + target_sources(Diligent-GraphicsEngineWebGPU-shared PRIVATE ${DLL_SRC}) +endif() + +set_dll_output_name(Diligent-GraphicsEngineWebGPU-shared GraphicsEngineWebGPU) + +set_common_target_properties(Diligent-GraphicsEngineWebGPU-shared) +set_common_target_properties(Diligent-GraphicsEngineWebGPU-static) + +target_include_directories(Diligent-GraphicsEngineWebGPU-static +PRIVATE + include +) + +target_link_libraries(Diligent-GraphicsEngineWebGPU-static +PRIVATE + Diligent-BuildSettings + Diligent-TargetPlatform + Diligent-Common + Diligent-GraphicsEngine + Diligent-GraphicsEngineNextGenBase + Diligent-ShaderTools + webgpu +PUBLIC + Diligent-GraphicsEngineWebGPUInterface +) + +target_link_libraries(Diligent-GraphicsEngineWebGPU-shared +PRIVATE + Diligent-BuildSettings + Diligent-GraphicsEngineWebGPU-static +PUBLIC + Diligent-GraphicsEngineWebGPUInterface +) +target_compile_definitions(Diligent-GraphicsEngineWebGPU-shared PUBLIC ENGINE_DLL=1) +target_copy_webgpu_binaries(Diligent-GraphicsEngineWebGPU-shared) + +source_group("src" FILES ${SRC}) +source_group("dll" FILES ${DLL_SRC}) +source_group("include" FILES ${INCLUDE}) +source_group("interface" FILES ${INTERFACE}) + +set_target_properties(Diligent-GraphicsEngineWebGPU-static PROPERTIES + FOLDER DiligentCore/Graphics +) +set_target_properties(Diligent-GraphicsEngineWebGPU-shared PROPERTIES + FOLDER DiligentCore/Graphics +) + +set_source_files_properties( + readme.md PROPERTIES HEADER_FILE_ONLY TRUE +) + +if(DILIGENT_INSTALL_CORE) + install_core_lib(Diligent-GraphicsEngineWebGPU-shared) + install_core_lib(Diligent-GraphicsEngineWebGPU-static) +endif() diff --git a/Graphics/GraphicsEngineWebGPU/include/AttachmentCleanerWebGPU.hpp b/Graphics/GraphicsEngineWebGPU/include/AttachmentCleanerWebGPU.hpp new file mode 100644 index 0000000000..08939c5bf2 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/AttachmentCleanerWebGPU.hpp @@ -0,0 +1,107 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Declaration of Diligent::AttachmentCleanerWebGPU class + +#include "EngineWebGPUImplTraits.hpp" +#include "GraphicsTypes.h" +#include "DeviceContext.h" +#include "WebGPUObjectWrappers.hpp" + +namespace Diligent +{ +class AttachmentCleanerWebGPU +{ +public: + struct RenderPassInfo + { + using RTVFormatArray = std::array; + + bool operator==(const RenderPassInfo& rhs) const; + + size_t GetHash() const; + + Uint32 NumRenderTargets = 0; + Uint8 SampleCount = 1; + TEXTURE_FORMAT DSVFormat = TEX_FORMAT_UNKNOWN; + RTVFormatArray RTVFormats = {}; + }; + + AttachmentCleanerWebGPU(); + + void Initialize(WGPUDevice wgpuDevice); + + void ClearColor(WGPURenderPassEncoder wgpuCmdEncoder, + const RenderPassInfo& RPInfo, + COLOR_MASK ColorMask, + Uint32 RTIndex, + const float Color[]); + + void ClearDepthStencil(WGPURenderPassEncoder wgpuCmdEncoder, + const RenderPassInfo& RPInfo, + CLEAR_DEPTH_STENCIL_FLAGS Flags, + float Depth, + Uint8 Stencil); + +private: + struct ClearPSOHashKey + { + struct Hasher + { + size_t operator()(const ClearPSOHashKey& Key) const; + }; + + bool operator==(const ClearPSOHashKey& rhs) const; + + RenderPassInfo RPInfo = {}; + COLOR_MASK ColorMask = COLOR_MASK_ALL; + Int32 RTIndex = 0; // -1 for depth + WGPUDepthStencilState DepthState = {}; + mutable size_t PSOHash = 0; + }; + + using ClearPSOCache = std::unordered_map; + + WebGPURenderPipelineWrapper CreatePSO(const ClearPSOHashKey& Key) const; + + void ClearAttachment(WGPURenderPassEncoder wgpuCmdEncoder, + const ClearPSOHashKey& Key, + std::array& ClearData); + +private: + WGPUDevice m_wgpuDevice = nullptr; + ClearPSOCache m_PSOCache; + + WGPUDepthStencilState m_wgpuDisableDepth = {}; + WGPUDepthStencilState m_wgpuWriteDepth = {}; + WGPUDepthStencilState m_wgpuWriteStencil = {}; + WGPUDepthStencilState m_wgpuWriteDepthStencil = {}; +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/BufferViewWebGPUImpl.hpp b/Graphics/GraphicsEngineWebGPU/include/BufferViewWebGPUImpl.hpp new file mode 100644 index 0000000000..51a5715529 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/BufferViewWebGPUImpl.hpp @@ -0,0 +1,52 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Declaration of Diligent::BufferViewWebGPUImpl class + +#include "EngineWebGPUImplTraits.hpp" +#include "BufferViewBase.hpp" + +namespace Diligent +{ + +/// Buffer view implementation in WebGPU backend. +class BufferViewWebGPUImpl final : public BufferViewBase +{ +public: + using TBufferViewBase = BufferViewBase; + + BufferViewWebGPUImpl(IReferenceCounters* pRefCounters, + RenderDeviceWebGPUImpl* pDevice, + const BufferViewDesc& Desc, + IBuffer* pBuffer, + bool IsDefaultView); +}; + + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/BufferWebGPUImpl.hpp b/Graphics/GraphicsEngineWebGPU/include/BufferWebGPUImpl.hpp new file mode 100644 index 0000000000..995b133e17 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/BufferWebGPUImpl.hpp @@ -0,0 +1,107 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Declaration of Diligent::BufferWebGPUImpl class + +#include "EngineWebGPUImplTraits.hpp" +#include "BufferBase.hpp" +#include "BufferViewWebGPUImpl.hpp" // Required by BufferBase +#include "WebGPUObjectWrappers.hpp" +#include "IndexWrapper.hpp" +#include "SharedMemoryManagerWebGPU.hpp" + +namespace Diligent +{ + +/// Buffer implementation in WebGPU backend. +class BufferWebGPUImpl final : public BufferBase +{ +public: + using TBufferBase = BufferBase; + + BufferWebGPUImpl(IReferenceCounters* pRefCounters, + FixedBlockMemoryAllocator& BuffViewObjMemAllocator, + RenderDeviceWebGPUImpl* pDevice, + const BufferDesc& Desc, + const BufferData* pInitData = nullptr); + + // Attaches to an existing WebGPU resource + BufferWebGPUImpl(IReferenceCounters* pRefCounters, + FixedBlockMemoryAllocator& BuffViewObjMemAllocator, + RenderDeviceWebGPUImpl* pDevice, + const BufferDesc& Desc, + RESOURCE_STATE InitialState, + WGPUBuffer wgpuBuffer); + + + /// Implementation of IBuffer::GetNativeHandle(). + Uint64 DILIGENT_CALL_TYPE GetNativeHandle() override { return BitCast(m_wgpuBuffer.Get()); } + + /// Implementation of IBuffer::GetSparseProperties(). + SparseBufferProperties DILIGENT_CALL_TYPE GetSparseProperties() const override; + + /// Implementation of IBufferWebGPU::GetWebGPUBuffer(). + WGPUBuffer DILIGENT_CALL_TYPE GetWebGPUBuffer() const override { return m_wgpuBuffer.Get(); } + + void Map(MAP_TYPE MapType, Uint32 MapFlags, PVoid& pMappedData); + + void Unmap(MAP_TYPE MapType); + + Uint64 GetAlignment(); + + const SharedMemoryManagerWebGPU::Allocation& GetDynamicAllocation(DeviceContextIndex CtxId) const; + + void SetDynamicAllocation(DeviceContextIndex CtxId, SharedMemoryManagerWebGPU::Allocation&& Allocation); + +private: + void CreateViewInternal(const BufferViewDesc& ViewDesc, IBufferView** ppView, bool IsDefaultView) override; + +private: + // Use 64-byte alignment to avoid cache issues + static constexpr size_t CacheLineSize = 64; + struct alignas(64) DynamicAllocation : SharedMemoryManagerWebGPU::Allocation + { + DynamicAllocation& operator=(const Allocation& Allocation) + { + *static_cast(this) = Allocation; + return *this; + } + Uint8 Padding[CacheLineSize - sizeof(Allocation)] = {}; + }; + static_assert(sizeof(DynamicAllocation) == CacheLineSize, "Unexpected sizeof(DynamicAllocation)"); + + using DynamicAllocationList = std::vector>; + + WebGPUBufferWrapper m_wgpuBuffer; + std::vector m_MappedData; + DynamicAllocationList m_DynamicAllocations; + Uint64 m_Alignment; +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/DeviceContextWebGPUImpl.hpp b/Graphics/GraphicsEngineWebGPU/include/DeviceContextWebGPUImpl.hpp new file mode 100644 index 0000000000..7633e08282 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/DeviceContextWebGPUImpl.hpp @@ -0,0 +1,467 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Declaration of Diligent::DeviceContextWebGPUImpl class + +#include "EngineWebGPUImplTraits.hpp" +#include "DeviceContextBase.hpp" +#include "TextureWebGPUImpl.hpp" +#include "BufferWebGPUImpl.hpp" +#include "PipelineStateWebGPUImpl.hpp" +#include "ShaderWebGPUImpl.hpp" +#include "FramebufferWebGPUImpl.hpp" +#include "RenderPassWebGPUImpl.hpp" +#include "SharedMemoryManagerWebGPU.hpp" + +namespace Diligent +{ + +class QueryManagerWebGPU; + +/// Device context implementation in WebGPU backend. +class DeviceContextWebGPUImpl final : public DeviceContextBase +{ +public: + using TDeviceContextBase = DeviceContextBase; + + DeviceContextWebGPUImpl(IReferenceCounters* pRefCounters, + RenderDeviceWebGPUImpl* pDevice, + const EngineWebGPUCreateInfo& EngineCI, + const DeviceContextDesc& Desc); + + void DILIGENT_CALL_TYPE QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) override; + + /// Implementation of IDeviceContext::Begin() in WebGPU backend. + void DILIGENT_CALL_TYPE Begin(Uint32 ImmediateContextId) override; + + /// Implementation of IDeviceContext::SetPipelineState() in WebGPU backend. + void DILIGENT_CALL_TYPE SetPipelineState(IPipelineState* pPipelineState) override; + + /// Implementation of IDeviceContext::TransitionShaderResources() in WebGPU backend. + void DILIGENT_CALL_TYPE TransitionShaderResources(IPipelineState* pPipelineState, + IShaderResourceBinding* pShaderResourceBinding) override; + + /// Implementation of IDeviceContext::CommitShaderResources() in WebGPU backend. + void DILIGENT_CALL_TYPE CommitShaderResources(IShaderResourceBinding* pShaderResourceBinding, + RESOURCE_STATE_TRANSITION_MODE StateTransitionMode) override; + + /// Implementation of IDeviceContext::SetStencilRef() in WebGPU backend. + void DILIGENT_CALL_TYPE SetStencilRef(Uint32 StencilRef) override; + + /// Implementation of IDeviceContext::SetBlendFactors() in WebGPU backend. + void DILIGENT_CALL_TYPE SetBlendFactors(const float* pBlendFactors = nullptr) override; + + /// Implementation of IDeviceContext::SetVertexBuffers() in WebGPU backend. + void DILIGENT_CALL_TYPE SetVertexBuffers(Uint32 StartSlot, + Uint32 NumBuffersSet, + IBuffer** ppBuffers, + const Uint64* pOffsets, + RESOURCE_STATE_TRANSITION_MODE StateTransitionMode, + SET_VERTEX_BUFFERS_FLAGS Flags) override; + + /// Implementation of IDeviceContext::InvalidateState() in WebGPU backend. + void DILIGENT_CALL_TYPE InvalidateState() override; + + /// Implementation of IDeviceContext::SetIndexBuffer() in WebGPU backend. + void DILIGENT_CALL_TYPE SetIndexBuffer(IBuffer* pIndexBuffer, + Uint64 ByteOffset, + RESOURCE_STATE_TRANSITION_MODE StateTransitionMode) override; + + /// Implementation of IDeviceContext::SetViewports() in WebGPU backend. + void DILIGENT_CALL_TYPE SetViewports(Uint32 NumViewports, + const Viewport* pViewports, + Uint32 RTWidth, + Uint32 RTHeight) override; + + /// Implementation of IDeviceContext::SetScissorRects() in WebGPU backend. + void DILIGENT_CALL_TYPE SetScissorRects(Uint32 NumRects, + const Rect* pRects, + Uint32 RTWidth, + Uint32 RTHeight) override; + + /// Implementation of IDeviceContext::SetRenderTargetsExt() in WebGPU backend. + void DILIGENT_CALL_TYPE SetRenderTargetsExt(const SetRenderTargetsAttribs& Attribs) override; + + /// Implementation of IDeviceContext::BeginRenderPass() in WebGPU backend. + void DILIGENT_CALL_TYPE BeginRenderPass(const BeginRenderPassAttribs& Attribs) override; + + /// Implementation of IDeviceContext::NextSubpass() in WebGPU backend. + void DILIGENT_CALL_TYPE NextSubpass() override; + + /// Implementation of IDeviceContext::EndRenderPass() in WebGPU backend. + void DILIGENT_CALL_TYPE EndRenderPass() override; + + /// Implementation of IDeviceContext::Draw() in WebGPU backend. + void DILIGENT_CALL_TYPE Draw(const DrawAttribs& Attribs) override; + + /// Implementation of IDeviceContext::DrawIndexed() in WebGPU backend. + void DILIGENT_CALL_TYPE DrawIndexed(const DrawIndexedAttribs& Attribs) override; + + /// Implementation of IDeviceContext::DrawIndirect() in WebGPU backend. + void DILIGENT_CALL_TYPE DrawIndirect(const DrawIndirectAttribs& Attribs) override; + + /// Implementation of IDeviceContext::DrawIndexedIndirect() in WebGPU backend. + void DILIGENT_CALL_TYPE DrawIndexedIndirect(const DrawIndexedIndirectAttribs& Attribs) override; + + /// Implementation of IDeviceContext::DrawMesh() in WebGPU backend. + void DILIGENT_CALL_TYPE DrawMesh(const DrawMeshAttribs& Attribs) override; + + /// Implementation of IDeviceContext::DrawMeshIndirect() in WebGPU backend. + void DILIGENT_CALL_TYPE DrawMeshIndirect(const DrawMeshIndirectAttribs& Attribs) override; + + /// Implementation of IDeviceContext::DispatchCompute() in WebGPU backend. + void DILIGENT_CALL_TYPE DispatchCompute(const DispatchComputeAttribs& Attribs) override; + + /// Implementation of IDeviceContext::DispatchComputeIndirect() in WebGPU backend. + void DILIGENT_CALL_TYPE DispatchComputeIndirect(const DispatchComputeIndirectAttribs& Attribs) override; + + /// Implementation of IDeviceContext::GetTileSize() in WebGPU backend. + void DILIGENT_CALL_TYPE GetTileSize(Uint32& TileSizeX, Uint32& TileSizeY) override; + + /// Implementation of IDeviceContext::ClearDepthStencil() in WebGPU backend. + void DILIGENT_CALL_TYPE ClearDepthStencil(ITextureView* pView, + CLEAR_DEPTH_STENCIL_FLAGS ClearFlags, + float fDepth, + Uint8 Stencil, + RESOURCE_STATE_TRANSITION_MODE StateTransitionMode) override; + + /// Implementation of IDeviceContext::ClearRenderTarget() in WebGPU backend. + void DILIGENT_CALL_TYPE ClearRenderTarget(ITextureView* pView, + const float* RGBA, + RESOURCE_STATE_TRANSITION_MODE StateTransitionMode) override; + + /// Implementation of IDeviceContext::UpdateBuffer() in WebGPU backend. + void DILIGENT_CALL_TYPE UpdateBuffer(IBuffer* pBuffer, + Uint64 Offset, + Uint64 Size, + const void* pData, + RESOURCE_STATE_TRANSITION_MODE StateTransitionMode) override; + + /// Implementation of IDeviceContext::CopyBuffer() in WebGPU backend. + void DILIGENT_CALL_TYPE CopyBuffer(IBuffer* pSrcBuffer, + Uint64 SrcOffset, + RESOURCE_STATE_TRANSITION_MODE SrcBufferTransitionMode, + IBuffer* pDstBuffer, + Uint64 DstOffset, + Uint64 Size, + RESOURCE_STATE_TRANSITION_MODE DstBufferTransitionMode) override; + + /// Implementation of IDeviceContext::MapBuffer() in WebGPU backend. + void DILIGENT_CALL_TYPE MapBuffer(IBuffer* pBuffer, + MAP_TYPE MapType, + MAP_FLAGS MapFlags, + PVoid& pMappedData) override; + + /// Implementation of IDeviceContext::UnmapBuffer() in WebGPU backend. + void DILIGENT_CALL_TYPE UnmapBuffer(IBuffer* pBuffer, MAP_TYPE MapType) override; + + /// Implementation of IDeviceContext::UpdateTexture() in WebGPU backend. + void DILIGENT_CALL_TYPE UpdateTexture(ITexture* pTexture, + Uint32 MipLevel, + Uint32 Slice, + const Box& DstBox, + const TextureSubResData& SubresData, + RESOURCE_STATE_TRANSITION_MODE SrcBufferStateTransitionMode, + RESOURCE_STATE_TRANSITION_MODE TextureStateTransitionMode) override; + + /// Implementation of IDeviceContext::CopyTexture() in WebGPU backend. + void DILIGENT_CALL_TYPE CopyTexture(const CopyTextureAttribs& CopyAttribs) override; + + /// Implementation of IDeviceContext::MapTextureSubresource() in WebGPU backend. + void DILIGENT_CALL_TYPE MapTextureSubresource(ITexture* pTexture, + Uint32 MipLevel, + Uint32 ArraySlice, + MAP_TYPE MapType, + MAP_FLAGS MapFlags, + const Box* pMapRegion, + MappedTextureSubresource& MappedData) override; + + /// Implementation of IDeviceContext::UnmapTextureSubresource() in WebGPU backend. + void DILIGENT_CALL_TYPE UnmapTextureSubresource(ITexture* pTexture, Uint32 MipLevel, Uint32 ArraySlice) override; + + /// Implementation of IDeviceContext::FinishCommandList() in WebGPU backend. + void DILIGENT_CALL_TYPE FinishCommandList(ICommandList** ppCommandList) override; + + /// Implementation of IDeviceContext::ExecuteCommandLists() in WebGPU backend. + void DILIGENT_CALL_TYPE ExecuteCommandLists(Uint32 NumCommandLists, + ICommandList* const* ppCommandLists) override; + + /// Implementation of IDeviceContext::EnqueueSignal() in WebGPU backend. + void DILIGENT_CALL_TYPE EnqueueSignal(IFence* pFence, Uint64 Value) override; + + /// Implementation of IDeviceContext::DeviceWaitForFence() in WebGPU backend. + void DILIGENT_CALL_TYPE DeviceWaitForFence(IFence* pFence, Uint64 Value) override; + + /// Implementation of IDeviceContext::WaitForIdle() in WebGPU backend. + void DILIGENT_CALL_TYPE WaitForIdle() override; + + /// Implementation of IDeviceContext::BeginQuery() in WebGPU backend. + void DILIGENT_CALL_TYPE BeginQuery(IQuery* pQuery) override; + + /// Implementation of IDeviceContext::EndQuery() in WebGPU backend. + void DILIGENT_CALL_TYPE EndQuery(IQuery* pQuery) override; + + /// Implementation of IDeviceContext::Flush() in WebGPU backend. + void DILIGENT_CALL_TYPE Flush() override; + + /// Implementation of IDeviceContext::BuildBLAS() in WebGPU backend. + void DILIGENT_CALL_TYPE BuildBLAS(const BuildBLASAttribs& Attribs) override; + + /// Implementation of IDeviceContext::BuildTLAS() in WebGPU backend. + void DILIGENT_CALL_TYPE BuildTLAS(const BuildTLASAttribs& Attribs) override; + + /// Implementation of IDeviceContext::CopyBLAS() in WebGPU backend. + void DILIGENT_CALL_TYPE CopyBLAS(const CopyBLASAttribs& Attribs) override; + + /// Implementation of IDeviceContext::CopyTLAS() in WebGPU backend. + void DILIGENT_CALL_TYPE CopyTLAS(const CopyTLASAttribs& Attribs) override; + + /// Implementation of IDeviceContext::WriteBLASCompactedSize() in WebGPU backend. + void DILIGENT_CALL_TYPE WriteBLASCompactedSize(const WriteBLASCompactedSizeAttribs& Attribs) override; + + /// Implementation of IDeviceContext::WriteTLASCompactedSize() in WebGPU backend. + void DILIGENT_CALL_TYPE WriteTLASCompactedSize(const WriteTLASCompactedSizeAttribs& Attribs) override; + + /// Implementation of IDeviceContext::TraceRays() in WebGPU backend. + void DILIGENT_CALL_TYPE TraceRays(const TraceRaysAttribs& Attribs) override; + + /// Implementation of IDeviceContext::TraceRaysIndirect() in WebGPU backend. + void DILIGENT_CALL_TYPE TraceRaysIndirect(const TraceRaysIndirectAttribs& Attribs) override; + + /// Implementation of IDeviceContext::UpdateSBT() in WebGPU backend. + void DILIGENT_CALL_TYPE UpdateSBT(IShaderBindingTable* pSBT, const UpdateIndirectRTBufferAttribs* pUpdateIndirectBufferAttribs) override; + + /// Implementation of IDeviceContext::BeginDebugGroup() in WebGPU backend. + void DILIGENT_CALL_TYPE BeginDebugGroup(const Char* Name, const float* pColor) override; + + /// Implementation of IDeviceContext::EndDebugGroup() in WebGPU backend. + void DILIGENT_CALL_TYPE EndDebugGroup() override; + + /// Implementation of IDeviceContext::InsertDebugLabel() in WebGPU backend. + void DILIGENT_CALL_TYPE InsertDebugLabel(const Char* Label, const float* pColor) override; + + /// Implementation of IDeviceContext::SetShadingRate() in WebGPU backend. + void DILIGENT_CALL_TYPE SetShadingRate(SHADING_RATE BaseRate, + SHADING_RATE_COMBINER PrimitiveCombiner, + SHADING_RATE_COMBINER TextureCombiner) override; + + /// Implementation of IDeviceContext::BindSparseResourceMemory() in WebGPU backend. + void DILIGENT_CALL_TYPE BindSparseResourceMemory(const BindSparseResourceMemoryAttribs& Attribs) override; + + /// Implementation of IDeviceContext::GenerateMips() in WebGPU backend. + void DILIGENT_CALL_TYPE GenerateMips(ITextureView* pTexView) override; + + /// Implementation of IDeviceContext::FinishFrame() in WebGPU backend. + void DILIGENT_CALL_TYPE FinishFrame() override; + + /// Implementation of IDeviceContext::TransitionResourceStates() in WebGPU backend. + void DILIGENT_CALL_TYPE TransitionResourceStates(Uint32 BarrierCount, const StateTransitionDesc* pResourceBarriers) override; + + /// Implementation of IDeviceContext::LockCommandQueue() in WebGPU backend. + ICommandQueue* DILIGENT_CALL_TYPE LockCommandQueue() override; + + /// Implementation of IDeviceContext::UnlockCommandQueue() in WebGPU backend. + void DILIGENT_CALL_TYPE UnlockCommandQueue() override; + + /// Implementation of IDeviceContext::ResolveTextureSubresource() in WebGPU backend. + void DILIGENT_CALL_TYPE ResolveTextureSubresource(ITexture* pSrcTexture, + ITexture* pDstTexture, + const ResolveTextureSubresourceAttribs& ResolveAttribs) override; + + /// Implementation of IDeviceContextWebGPU::GetWebGPUQueue() in WebGPU backend. + WGPUQueue DILIGENT_CALL_TYPE GetWebGPUQueue() override; + + + + QueryManagerWebGPU& GetQueryManager(); + +private: + enum COMMAND_ENCODER_FLAGS : Uint32 + { + COMMAND_ENCODER_FLAG_NONE = 0u, + COMMAND_ENCODER_FLAG_RENDER = 1u << 0, + COMMAND_ENCODER_FLAG_COMPUTE = 1u << 1, + + COMMAND_ENCODER_FLAG_ALL = + COMMAND_ENCODER_FLAG_RENDER | + COMMAND_ENCODER_FLAG_COMPUTE + }; + + WGPUCommandEncoder GetCommandEncoder(); + + WGPURenderPassEncoder GetRenderPassCommandEncoder(); + + WGPUComputePassEncoder GetComputePassCommandEncoder(); + + void EndCommandEncoders(Uint32 EncoderFlags = COMMAND_ENCODER_FLAG_ALL); + + void CommitRenderTargets(); + + void BeginSubpass(); + + void ClearEncoderState(); + + void ClearAttachment(Int32 RTIndex, + COLOR_MASK ColorMask, + CLEAR_DEPTH_STENCIL_FLAGS DSFlags, + const float ClearData[], + Uint8 Stencil); + + SharedMemoryManagerWebGPU::Allocation AllocateSharedMemory(Uint64 Size, Uint64 Alignment = 16); + +private: + struct WebGPUEncoderState + { + enum CMD_ENCODER_STATE_FLAGS : Uint32 + { + CMD_ENCODER_STATE_NONE = 0, + CMD_ENCODER_STATE_PIPELINE_STATE = 1 << 0, + CMD_ENCODER_STATE_INDEX_BUFFER = 1 << 1, + CMD_ENCODER_STATE_VERTEX_BUFFERS = 1 << 2, + CMD_ENCODER_STATE_VIEWPORTS = 1 << 3, + CMD_ENCODER_STATE_SCISSOR_RECTS = 1 << 4, + CMD_ENCODER_STATE_BLEND_FACTORS = 1 << 5, + CMD_ENCODER_STATE_STENCIL_REF = 1 << 6 + }; + + bool IsUpToDate(CMD_ENCODER_STATE_FLAGS StateFlag) const + { + return (CmdEncoderUpToDateStates & StateFlag) != 0; + } + + void SetUpToDate(CMD_ENCODER_STATE_FLAGS StateFlag) + { + CmdEncoderUpToDateStates |= StateFlag; + } + + void Invalidate(CMD_ENCODER_STATE_FLAGS StateFlag) + { + CmdEncoderUpToDateStates &= ~StateFlag; + } + + void Clear() + { + *this = WebGPUEncoderState{}; + } + + Uint32 CmdEncoderUpToDateStates = CMD_ENCODER_STATE_NONE; + bool HasDynamicVertexBuffers = false; + + std::array Viewports = {}; + std::array ScissorRects = {}; + + } m_EncoderState; + + struct PendingClears + { + using RenderTargetClearColors = std::array; + + void SetColor(Uint32 RTIndex, const float Color[]) + { + for (int i = 0; i < 4; ++i) + Colors[RTIndex][i] = Color[i]; + Flags |= RT0Flag << RTIndex; + } + void SetDepth(float _Depth) + { + Depth = _Depth; + Flags |= DepthFlag; + } + void SetStencil(Uint8 _Stencil) + { + Stencil = _Stencil; + Flags |= StencilFlag; + } + + bool ColorPending(Uint32 RTIndex) const + { + return Flags & (RT0Flag << RTIndex); + } + bool DepthPending() const + { + return Flags & DepthFlag; + } + bool StencilPending() const + { + return Flags & StencilFlag; + } + bool AnyPending() const + { + return Flags != 0; + } + + void ResetFlags() + { + Flags = 0; + } + + void Clear() + { + *this = PendingClears{}; + } + + RenderTargetClearColors Colors = {}; + float Depth = 0; + Uint8 Stencil = 0; + + private: + static constexpr Uint32 RT0Flag = 1U; + static constexpr Uint32 DepthFlag = 1U << MAX_RENDER_TARGETS; + static constexpr Uint32 StencilFlag = 1U << (MAX_RENDER_TARGETS + 1); + + Uint32 Flags = 0; + } m_PendingClears; + + struct PendingQuery + { + QueryWebGPUImpl* const pQuery; + const bool IsBegin; + }; + + using PendingFenceList = std::vector>>; + using PendingQueryList = std::vector; + using AttachmentClearList = std::vector; + using SharedMemoryPageList = std::vector; + + WGPUQueue m_wgpuQueue = nullptr; + WGPUCommandEncoder m_wgpuCommandEncoder = nullptr; + WGPURenderPassEncoder m_wgpuRenderPassEncoder = nullptr; + WGPUComputePassEncoder m_wgpuComputePassEncoder = nullptr; + PendingFenceList m_SignalFences; + PendingFenceList m_WaitFences; + AttachmentClearList m_AttachmentClearValues; + PendingQueryList m_PendingTimeQueries; + SharedMemoryPageList m_SharedMemPages; + + QueryManagerWebGPU* m_pQueryMgr = nullptr; + Int32 m_ActiveQueriesCounter = 0; +}; + + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/EngineWebGPUImplTraits.hpp b/Graphics/GraphicsEngineWebGPU/include/EngineWebGPUImplTraits.hpp new file mode 100644 index 0000000000..905e83e26b --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/EngineWebGPUImplTraits.hpp @@ -0,0 +1,130 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Declaration of Diligent::EngineWebGPUImplTraits struct + +#include "RenderPass.h" +#include "Framebuffer.h" +#include "CommandList.h" +#include "PipelineResourceSignature.h" + +#include "BufferWebGPU.h" +#include "RenderDeviceWebGPU.h" +#include "DeviceContextWebGPU.h" +#include "TextureWebGPU.h" +#include "TextureViewWebGPU.h" +#include "SamplerWebGPU.h" + + +namespace Diligent +{ + +class RenderDeviceWebGPUImpl; +class DeviceContextWebGPUImpl; +class PipelineStateWebGPUImpl; +class ShaderResourceBindingWebGPUImpl; +class BufferWebGPUImpl; +class BufferViewWebGPUImpl; +class TextureWebGPUImpl; +class TextureViewWebGPUImpl; +class ShaderWebGPUImpl; +class SamplerWebGPUImpl; +class FenceWebGPUImpl; +class QueryWebGPUImpl; +class RenderPassWebGPUImpl; +class FramebufferWebGPUImpl; +class CommandListWebGPUImpl; +class BottomLevelASWebGPUImpl; +class TopLevelASWebGPUImpl; +class ShaderBindingTableWebGPUImpl; +class PipelineResourceSignatureWebGPUImpl; +class DeviceMemoryWebGPUImpl; +class PipelineStateCacheWebGPUImpl{}; + +class FixedBlockMemoryAllocator; + +class ShaderResourceCacheWebGPU; +class ShaderVariableManagerWebGPU; + +struct PipelineResourceAttribsWebGPU; + +struct EngineWebGPUImplTraits +{ + static constexpr auto DeviceType = RENDER_DEVICE_TYPE_WEBGPU; + + using RenderDeviceInterface = IRenderDeviceWebGPU; + using DeviceContextInterface = IDeviceContextWebGPU; + using PipelineStateInterface = IPipelineState; + using ShaderResourceBindingInterface = IShaderResourceBinding; + using BufferInterface = IBufferWebGPU; + using BufferViewInterface = IBufferView; + using TextureInterface = ITextureWebGPU; + using TextureViewInterface = ITextureViewWebGPU; + using ShaderInterface = IShader; + using SamplerInterface = ISamplerWebGPU; + using FenceInterface = IFence; + using QueryInterface = IQuery; + using RenderPassInterface = IRenderPass; + using FramebufferInterface = IFramebuffer; + using CommandListInterface = ICommandList; + using PipelineResourceSignatureInterface = IPipelineResourceSignature; + using DeviceMemoryInterface = IDeviceMemory; + + using RenderDeviceImplType = RenderDeviceWebGPUImpl; + using DeviceContextImplType = DeviceContextWebGPUImpl; + using PipelineStateImplType = PipelineStateWebGPUImpl; + using ShaderResourceBindingImplType = ShaderResourceBindingWebGPUImpl; + using BufferImplType = BufferWebGPUImpl; + using BufferViewImplType = BufferViewWebGPUImpl; + using TextureImplType = TextureWebGPUImpl; + using TextureViewImplType = TextureViewWebGPUImpl; + using ShaderImplType = ShaderWebGPUImpl; + using SamplerImplType = SamplerWebGPUImpl; + using FenceImplType = FenceWebGPUImpl; + using QueryImplType = QueryWebGPUImpl; + using RenderPassImplType = RenderPassWebGPUImpl; + using FramebufferImplType = FramebufferWebGPUImpl; + using CommandListImplType = CommandListWebGPUImpl; + using BottomLevelASImplType = BottomLevelASWebGPUImpl; + using TopLevelASImplType = TopLevelASWebGPUImpl; + using ShaderBindingTableImplType = ShaderBindingTableWebGPUImpl; + using PipelineResourceSignatureImplType = PipelineResourceSignatureWebGPUImpl; + using DeviceMemoryImplType = DeviceMemoryWebGPUImpl; + using PipelineStateCacheImplType = PipelineStateCacheWebGPUImpl; + + using BuffViewObjAllocatorType = FixedBlockMemoryAllocator; + using TexViewObjAllocatorType = FixedBlockMemoryAllocator; + + using ShaderResourceCacheImplType = ShaderResourceCacheWebGPU; + using ShaderVariableManagerImplType = ShaderVariableManagerWebGPU; + + using PipelineResourceAttribsType = PipelineResourceAttribsWebGPU; +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/FenceWebGPUImpl.hpp b/Graphics/GraphicsEngineWebGPU/include/FenceWebGPUImpl.hpp new file mode 100644 index 0000000000..10f89ffb6f --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/FenceWebGPUImpl.hpp @@ -0,0 +1,78 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Declaration of Diligent::FenceWebGPUImpl class + +#include "EngineWebGPUImplTraits.hpp" +#include "FenceBase.hpp" + +namespace Diligent +{ + +/// Fence object implementation in WebGPU backend. +class FenceWebGPUImpl final : public FenceBase +{ +public: + using TFenceBase = FenceBase; + + FenceWebGPUImpl(IReferenceCounters* pRefCounters, + RenderDeviceWebGPUImpl* pDevice, + const FenceDesc& Desc); + + /// Implementation of IFence::GetCompletedValue() in WebGPU backend. + Uint64 DILIGENT_CALL_TYPE GetCompletedValue() override; + + /// Implementation of IFence::Signal() in WebGPU backend. + void DILIGENT_CALL_TYPE Signal(Uint64 Value) override; + + /// Implementation of IFence::Wait() in WebGPU backend. + void DILIGENT_CALL_TYPE Wait(Uint64 Value) override; + + void AddPendingSignal(WGPUCommandEncoder wgpuCmdEncoder, Uint64 Value); + +private: + struct PendingFenceData + { + const Uint64 Value; + const Uint64 LastTimestamp; + const Uint32 QueryIdx; + + PendingFenceData(Uint64 _Value, Uint64 _LastTimestamp, Uint32 _QueryIdx) : + // clang-format off + Value {_Value}, + LastTimestamp {_LastTimestamp}, + QueryIdx {_QueryIdx} + + // clang-format on + {} + }; + std::deque m_PendingSignals; +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/FramebufferWebGPUImpl.hpp b/Graphics/GraphicsEngineWebGPU/include/FramebufferWebGPUImpl.hpp new file mode 100644 index 0000000000..2574c08769 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/FramebufferWebGPUImpl.hpp @@ -0,0 +1,51 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Declaration of Diligent::FramebufferWebGPUImpl class + +#include "EngineWebGPUImplTraits.hpp" +#include "FramebufferBase.hpp" + +namespace Diligent +{ + +/// Render pass implementation in WebGPU backend. +class FramebufferWebGPUImpl final : public FramebufferBase +{ +public: + using TFramebufferBase = FramebufferBase; + + FramebufferWebGPUImpl(IReferenceCounters* pRefCounters, + RenderDeviceWebGPUImpl* pDevice, + const FramebufferDesc& Desc); + + ~FramebufferWebGPUImpl() override; +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/PipelineResourceAttribsWebGPU.hpp b/Graphics/GraphicsEngineWebGPU/include/PipelineResourceAttribsWebGPU.hpp new file mode 100644 index 0000000000..1ef7727daa --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/PipelineResourceAttribsWebGPU.hpp @@ -0,0 +1,90 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once +/// \file +/// Declaration of Diligent::PipelineResourceAttribsWebGPU struct + +#include "HashUtils.hpp" + +namespace Diligent +{ + +struct PipelineResourceAttribsWebGPU +{ +private: + static constexpr Uint32 _SamplerIndBits = 31; + static constexpr Uint32 _SamplerAssignedBits = 1; + +public: + static constexpr Uint32 InvalidSamplerInd = (1u << _SamplerIndBits) - 1; + + // clang-format off + const Uint32 SamplerInd : _SamplerIndBits; + const Uint32 ImtblSamplerAssigned : _SamplerAssignedBits; + // clang-format on + + + PipelineResourceAttribsWebGPU(Uint32 _SamplerInd, + bool _ImtblSamplerAssigned) noexcept : + // clang-format off + SamplerInd {_SamplerInd }, + ImtblSamplerAssigned{_ImtblSamplerAssigned ? 1u : 0u} + // clang-format on + { + VERIFY(SamplerInd == _SamplerInd, "Sampler index (", _SamplerInd, ") exceeds maximum representable value."); + } + + // Only for serialization + PipelineResourceAttribsWebGPU() noexcept : + PipelineResourceAttribsWebGPU{0, false} + {} + + bool IsSamplerAssigned() const + { + return SamplerInd != InvalidSamplerInd; + } + + bool IsImmutableSamplerAssigned() const + { + return ImtblSamplerAssigned != 0; + } + + bool IsCompatibleWith(const PipelineResourceAttribsWebGPU& RHS) const + { + // Ignore assigned sampler index. + // clang-format off + return IsImmutableSamplerAssigned() == RHS.IsImmutableSamplerAssigned(); + // clang-format on + } + + size_t GetHash() const + { + return ComputeHash(IsImmutableSamplerAssigned()); + } +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/PipelineResourceSignatureWebGPUImpl.hpp b/Graphics/GraphicsEngineWebGPU/include/PipelineResourceSignatureWebGPUImpl.hpp new file mode 100644 index 0000000000..ea191256cc --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/PipelineResourceSignatureWebGPUImpl.hpp @@ -0,0 +1,64 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Declaration of Diligent::PipelineResourceSignatureWebGPUImpl class + +#include "EngineWebGPUImplTraits.hpp" +#include "PipelineResourceAttribsWebGPU.hpp" +#include "PipelineResourceSignatureBase.hpp" +#include "ShaderResourceBindingWebGPUImpl.hpp" +#include "ShaderVariableManagerWebGPU.hpp" + +namespace Diligent +{ +/// Implementation of the Diligent::PipelineResourceSignatureWebGPUImpl class +class PipelineResourceSignatureWebGPUImpl final : public PipelineResourceSignatureBase +{ +public: + using TPipelineResourceSignatureBase = PipelineResourceSignatureBase; + + PipelineResourceSignatureWebGPUImpl(IReferenceCounters* pRefCounters, + RenderDeviceWebGPUImpl* pDevice, + const PipelineResourceSignatureDesc& Desc, + SHADER_TYPE ShaderStages = SHADER_TYPE_UNKNOWN, + bool bIsDeviceInternal = false); + /* + PipelineResourceSignatureWebGPUImpl(IReferenceCounters* pRefCounters, + RenderDeviceWebGPUImpl* pDevice, + const PipelineResourceSignatureDesc& Desc, + const PipelineResourceSignatureInternalDataD3D11& InternalData); + */ + + // Copies static resources from the static resource cache to the destination cache + void CopyStaticResources(ShaderResourceCacheWebGPU& ResourceCache) const; + // Make the base class method visible + using TPipelineResourceSignatureBase::CopyStaticResources; +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/PipelineStateWebGPUImpl.hpp b/Graphics/GraphicsEngineWebGPU/include/PipelineStateWebGPUImpl.hpp new file mode 100644 index 0000000000..6232969444 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/PipelineStateWebGPUImpl.hpp @@ -0,0 +1,58 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Declaration of Diligent::PipelineStateWebGPUImpl class + +#include "EngineWebGPUImplTraits.hpp" +#include "PipelineStateBase.hpp" +#include "PipelineResourceSignatureWebGPUImpl.hpp" // Required by PipelineStateBase + +namespace Diligent +{ + +/// Pipeline state object implementation in WebGPU backend. +class PipelineStateWebGPUImpl final : public PipelineStateBase +{ +public: + using TPipelineStateBase = PipelineStateBase; + + //TODO + static constexpr INTERFACE_ID IID_InternalImpl = + {0xe432f9ec, 0xe60e, 0x4e14, {0xbc, 0xe0, 0x18, 0x81, 0x2f, 0x52, 0x32, 0x43}}; + + PipelineStateWebGPUImpl(IReferenceCounters* pRefCounters, + RenderDeviceWebGPUImpl* pDevice, + const GraphicsPipelineStateCreateInfo& CreateInfo); + + PipelineStateWebGPUImpl(IReferenceCounters* pRefCounters, + RenderDeviceWebGPUImpl* pDevice, + const ComputePipelineStateCreateInfo& CreateInfo); +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/QueryManagerWebGPU.hpp b/Graphics/GraphicsEngineWebGPU/include/QueryManagerWebGPU.hpp new file mode 100644 index 0000000000..22b3d75025 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/QueryManagerWebGPU.hpp @@ -0,0 +1,109 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Declaration of Diligent::QueryManagerWebGPU class + +#include "EngineWebGPUImplTraits.hpp" +#include "RenderDeviceWebGPUImpl.hpp" +#include "DeviceContextWebGPUImpl.hpp" + +namespace Diligent +{ + +class QueryManagerWebGPU +{ +public: + QueryManagerWebGPU(RenderDeviceWebGPUImpl* pRenderDeviceWebGPU, + const Uint32 QueryHeapSizes[]); + + ~QueryManagerWebGPU(); + + // clang-format off + QueryManagerWebGPU (const QueryManagerWebGPU&) = delete; + QueryManagerWebGPU ( QueryManagerWebGPU&&) = delete; + QueryManagerWebGPU& operator = (const QueryManagerWebGPU&) = delete; + QueryManagerWebGPU& operator = ( QueryManagerWebGPU&&) = delete; + // clang-format on + + static constexpr Uint32 InvalidIndex = static_cast(-1); + + Uint32 AllocateQuery(QUERY_TYPE Type); + + void ReleaseQuery(QUERY_TYPE Type, Uint32 Index); + + WGPUQuerySet GetQuerySet(QUERY_TYPE Type) const; + +private: + class QuerySetInfo + { + public: + QuerySetInfo() = default; + + ~QuerySetInfo(); + + // clang-format off + QuerySetInfo (const QuerySetInfo&) = delete; + QuerySetInfo ( QuerySetInfo&&) = delete; + QuerySetInfo& operator = (const QuerySetInfo&) = delete; + QuerySetInfo& operator = ( QuerySetInfo&&) = delete; + // clang-format on + + void Init(WGPUDevice wgpuDevice, + const WGPUQuerySetDescriptor& wgpuQuerySetDesc, + QUERY_TYPE Type); + + Uint32 Allocate(); + + void Release(Uint32 Index); + + QUERY_TYPE GetType() const; + + Uint32 GetQueryCount() const; + + WGPUQuerySet GetWebGPUQuerySet() const; + + Uint32 GetMaxAllocatedQueries() const; + + bool IsNull() const; + + private: + WebGPUQuerySetWrapper m_wgpuQuerySet; + WebGPUBufferWrapper m_wgpuResolveBuffer; + WebGPUBufferWrapper m_wgpuStagingBuffer; + std::vector m_AvailableQueries; + + QUERY_TYPE m_Type = QUERY_TYPE_UNDEFINED; + Uint32 m_QueryCount = 0; + Uint32 m_MaxAllocatedQueries = 0; + }; + + std::array m_QuerySets; +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/QueryWebGPUImpl.hpp b/Graphics/GraphicsEngineWebGPU/include/QueryWebGPUImpl.hpp new file mode 100644 index 0000000000..e6442e60b3 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/QueryWebGPUImpl.hpp @@ -0,0 +1,71 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Declaration of Diligent::QueryWebGPUImpl class + +#include "EngineWebGPUImplTraits.hpp" +#include "QueryBase.hpp" +#include "QueryManagerWebGPU.hpp" + +namespace Diligent +{ + +/// Query implementation in WebGPU backend. +class QueryWebGPUImpl final : public QueryBase +{ +public: + using TQueryBase = QueryBase; + + QueryWebGPUImpl(IReferenceCounters* pRefCounters, + RenderDeviceWebGPUImpl* pDevice, + const QueryDesc& Desc); + + ~QueryWebGPUImpl() override; + + /// Implementation of IQuery::GetData(). + bool DILIGENT_CALL_TYPE GetData(void* pData, Uint32 DataSize, bool AutoInvalidate) override; + + /// Implementation of IQuery::Invalidate(). + void DILIGENT_CALL_TYPE Invalidate() override; + + bool OnBeginQuery(DeviceContextWebGPUImpl* pContext); + + bool OnEndQuery(DeviceContextWebGPUImpl* pContext); + +private: + bool AllocateQueries(); + + void ReleaseQueries(); + +private: + std::array m_QuerySetIndex = {QueryManagerWebGPU::InvalidIndex, QueryManagerWebGPU::InvalidIndex}; + QueryManagerWebGPU* m_pQueryMgr = nullptr; +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/QueueSignalPoolWebGPU.hpp b/Graphics/GraphicsEngineWebGPU/include/QueueSignalPoolWebGPU.hpp new file mode 100644 index 0000000000..e281b8ea10 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/QueueSignalPoolWebGPU.hpp @@ -0,0 +1,144 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Declaration of Diligent::QueueSignalPoolWebGPU class + +#include "EngineWebGPUImplTraits.hpp" +#include "RenderDeviceWebGPUImpl.hpp" +#include "DeviceContextWebGPUImpl.hpp" + +namespace Diligent +{ + +class QueueSignalPoolWebGPU +{ +public: + QueueSignalPoolWebGPU(RenderDeviceWebGPUImpl* pDevice, Uint32 QueryCount) : + m_QueryCount(QueryCount) + { + WGPUBufferDescriptor wgpuQueryBufferDesc{}; + wgpuQueryBufferDesc.usage = WGPUBufferUsage_CopySrc | WGPUBufferUsage_QueryResolve; + wgpuQueryBufferDesc.size = sizeof(Uint64) * m_QueryCount; + m_wgpuQueryBuffer.Reset(wgpuDeviceCreateBuffer(pDevice->GetWebGPUDevice(), &wgpuQueryBufferDesc)); + if (!m_wgpuQueryBuffer) + LOG_ERROR_AND_THROW("Failed to create query buffer"); + + WGPUBufferDescriptor wgpuStagingBufferDesc{}; + wgpuStagingBufferDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_MapRead; + wgpuStagingBufferDesc.size = sizeof(Uint64) * m_QueryCount; + m_wgpuStagingBuffer.Reset(wgpuDeviceCreateBuffer(pDevice->GetWebGPUDevice(), &wgpuStagingBufferDesc)); + if (!m_wgpuStagingBuffer) + LOG_ERROR_AND_THROW("Failed to create staging buffer"); + + WGPUQuerySetDescriptor wgpuQuerySetDesc{}; + wgpuQuerySetDesc.type = WGPUQueryType_Timestamp; + wgpuQuerySetDesc.count = m_QueryCount; + m_wgpuQuerySet.Reset(wgpuDeviceCreateQuerySet(pDevice->GetWebGPUDevice(), &wgpuQuerySetDesc)); + if (!m_wgpuQuerySet) + LOG_ERROR_AND_THROW("Failed to create query set"); + + const std::vector BufferFillZero(QueryCount, 0); + wgpuQueueWriteBuffer(wgpuDeviceGetQueue(pDevice->GetWebGPUDevice()), m_wgpuStagingBuffer.Get(), 0, BufferFillZero.data(), BufferFillZero.size()); + } + + Uint32 AllocateQuery() + { + for (Uint32 QueryIdx = 0; QueryIdx < m_QueryStatus.size(); ++QueryIdx) + if (!m_QueryStatus[QueryIdx]) + return QueryIdx; + + LOG_ERROR_MESSAGE("Failed to find available query.Increase QueryCount"); + return UINT32_MAX; + } + + void ReleaseQuery(Uint32 QueryIdx) + { + DEV_CHECK_ERR(QueryIdx < m_QueryCount, "Query index should be less than the size of the query set"); + m_QueryStatus[QueryIdx] = false; + } + + void WriteTimestamp(WGPUCommandEncoder wgpuCmdEncoder, Uint32 QueryIdx) + { + DEV_CHECK_ERR(QueryIdx < m_QueryCount, "Query index should be less than the size of the query set"); + wgpuCommandEncoderWriteTimestamp(wgpuCmdEncoder, m_wgpuQuerySet.Get(), QueryIdx); + } + + void ResolveQuery(WGPUCommandEncoder wgpuCmdEncoder, Uint32 QueryIdx) + { + const Uint64 CopyOffset = QueryIdx * sizeof(Uint64); + wgpuCommandEncoderResolveQuerySet(wgpuCmdEncoder, m_wgpuQuerySet.Get(), QueryIdx, 1, m_wgpuQueryBuffer.Get(), CopyOffset); + wgpuCommandEncoderCopyBufferToBuffer(wgpuCmdEncoder, m_wgpuQueryBuffer.Get(), CopyOffset, m_wgpuStagingBuffer.Get(), CopyOffset, sizeof(Uint64)); + } + + Uint64 GetQueryTimestamp(WGPUDevice wgpuDevice, Uint32 QueryIdx) + { + DEV_CHECK_ERR(QueryIdx < m_QueryCount, "Query index should be less than the size of the query set"); + + struct CallbackCaptureData + { + QueueSignalPoolWebGPU* pQueueSignalPool; + Uint64 QueryTimestamp; + Uint32 QueryIdx; + } CallbackCapture{this, 0, QueryIdx}; + + auto MapAsyncCallback = [](WGPUBufferMapAsyncStatus MapStatus, void* pUserData) { + if (MapStatus == WGPUBufferMapAsyncStatus_Success) + { + auto* pCaptureData = static_cast(pUserData); + auto* pQueueSignalSet = pCaptureData->pQueueSignalPool; + + const auto BufferOffset = pCaptureData->QueryIdx * sizeof(Uint64); + + const auto* pQueryData = static_cast(wgpuBufferGetConstMappedRange(pQueueSignalSet->m_wgpuStagingBuffer.Get(), BufferOffset, sizeof(Uint64))); + VERIFY_EXPR(pUserData != nullptr); + pCaptureData->QueryTimestamp = *pQueryData; + wgpuBufferUnmap(pQueueSignalSet->m_wgpuStagingBuffer.Get()); + } + else + { + DEV_ERROR("Unexpected error"); + } + }; + + wgpuBufferMapAsync(m_wgpuStagingBuffer.Get(), WGPUMapMode_Read, QueryIdx * sizeof(Uint64), sizeof(Uint64), MapAsyncCallback, &CallbackCapture); +#ifndef PLATFORM_EMSCRIPTEN + wgpuQueueSubmit(wgpuDeviceGetQueue(wgpuDevice), 0, nullptr); +#endif + return CallbackCapture.QueryTimestamp; + } + +private: + WebGPUQuerySetWrapper m_wgpuQuerySet; + WebGPUBufferWrapper m_wgpuQueryBuffer; + WebGPUBufferWrapper m_wgpuStagingBuffer; + std::vector m_QueryStatus; + Uint32 m_QueryCount; +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/RenderDeviceWebGPUImpl.hpp b/Graphics/GraphicsEngineWebGPU/include/RenderDeviceWebGPUImpl.hpp new file mode 100644 index 0000000000..9cd56ab7e9 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/RenderDeviceWebGPUImpl.hpp @@ -0,0 +1,204 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Declaration of Diligent::RenderDeviceWebGPUImpl class + +#include "EngineWebGPUImplTraits.hpp" +#include "RenderDeviceBase.hpp" +#include "RenderDeviceWebGPU.h" +#include "WebGPUObjectWrappers.hpp" +#include "ShaderWebGPUImpl.hpp" +#include "SharedMemoryManagerWebGPU.hpp" + +namespace Diligent +{ + +using QueueSignalPoolWebGPUPtr = std::unique_ptr; +using QueryManagerWebGPUPtr = std::unique_ptr; +using AttachmentCleanerWebGPUPtr = std::unique_ptr; +using SharedMemoryManagerWebGPUPtr = std::unique_ptr; + +/// Render device implementation in WebGPU backend. +class RenderDeviceWebGPUImpl final : public RenderDeviceBase +{ +public: + using TRenderDeviceBase = RenderDeviceBase; + + RenderDeviceWebGPUImpl(IReferenceCounters* pRefCounters, + IMemoryAllocator& RawMemAllocator, + IEngineFactory* pEngineFactory, + const EngineWebGPUCreateInfo& EngineCI, + const GraphicsAdapterInfo& AdapterInfo, + WGPUInstance wgpuInstance, + WGPUAdapter wgpuAdapter, + WGPUDevice wgpuDevice) noexcept(false); + + ~RenderDeviceWebGPUImpl() override; + + void DILIGENT_CALL_TYPE QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) override; + + /// Implementation of IRenderDevice::CreateBuffer() in WebGPU backend. + void DILIGENT_CALL_TYPE CreateBuffer(const BufferDesc& BuffDesc, + const BufferData* pBuffData, + IBuffer** ppBuffer) override; + + /// Implementation of IRenderDevice::CreateShader() in WebGPU backend. + void DILIGENT_CALL_TYPE CreateShader(const ShaderCreateInfo& ShaderCI, + IShader** ppShader) override; + + /// Implementation of IRenderDevice::CreateTexture() in WebGPU backend. + void DILIGENT_CALL_TYPE CreateTexture(const TextureDesc& TexDesc, + const TextureData* pData, + ITexture** ppTexture) override; + + /// Implementation of IRenderDevice::CreateSampler() in WebGPU backend. + void DILIGENT_CALL_TYPE CreateSampler(const SamplerDesc& SamplerDesc, + ISampler** ppSampler) override; + + /// Implementation of IRenderDevice::CreateGraphicsPipelineState() in WebGPU backend. + void DILIGENT_CALL_TYPE CreateGraphicsPipelineState(const GraphicsPipelineStateCreateInfo& PSOCreateInfo, + IPipelineState** ppPipelineState) override; + + /// Implementation of IRenderDevice::CreateComputePipelineState() in WebGPU backend. + void DILIGENT_CALL_TYPE CreateComputePipelineState(const ComputePipelineStateCreateInfo& PSOCreateInfo, + IPipelineState** ppPipelineState) override; + + /// Implementation of IRenderDevice::CreateRayTracingPipelineState() in WebGPU backend. + void DILIGENT_CALL_TYPE CreateRayTracingPipelineState(const RayTracingPipelineStateCreateInfo& PSOCreateInfo, + IPipelineState** ppPipelineState) override; + + /// Implementation of IRenderDevice::CreateFence() in WebGPU backend. + void DILIGENT_CALL_TYPE CreateFence(const FenceDesc& Desc, + IFence** ppFence) override; + + /// Implementation of IRenderDevice::CreateQuery() in WebGPU backend. + void DILIGENT_CALL_TYPE CreateQuery(const QueryDesc& Desc, + IQuery** ppQuery) override; + + /// Implementation of IRenderDevice::CreateRenderPass() in WebGPU backend. + void DILIGENT_CALL_TYPE CreateRenderPass(const RenderPassDesc& Desc, + IRenderPass** ppRenderPass) override; + + /// Implementation of IRenderDevice::CreateFramebuffer() in WebGPU backend. + void DILIGENT_CALL_TYPE CreateFramebuffer(const FramebufferDesc& Desc, + IFramebuffer** ppFramebuffer) override; + + /// Implementation of IRenderDevice::CreateBLAS() in WebGPU backend. + void DILIGENT_CALL_TYPE CreateBLAS(const BottomLevelASDesc& Desc, + IBottomLevelAS** ppBLAS) override; + + /// Implementation of IRenderDevice::CreateTLAS() in WebGPU backend. + void DILIGENT_CALL_TYPE CreateTLAS(const TopLevelASDesc& Desc, + ITopLevelAS** ppTLAS) override; + + /// Implementation of IRenderDevice::CreateSBT() in WebGPU backend. + void DILIGENT_CALL_TYPE CreateSBT(const ShaderBindingTableDesc& Desc, + IShaderBindingTable** ppSBT) override; + + /// Implementation of IRenderDevice::CreatePipelineResourceSignature() in WebGPU backend. + void DILIGENT_CALL_TYPE CreatePipelineResourceSignature(const PipelineResourceSignatureDesc& Desc, + IPipelineResourceSignature** ppSignature) override; + + /// Implementation of IRenderDevice::CreateDeviceMemory() in WebGPU backend. + void DILIGENT_CALL_TYPE CreateDeviceMemory(const DeviceMemoryCreateInfo& CreateInfo, + IDeviceMemory** ppMemory) override; + + /// Implementation of IRenderDevice::CreatePipelineStateCache() in WebGPU backend. + void DILIGENT_CALL_TYPE CreatePipelineStateCache(const PipelineStateCacheCreateInfo& CreateInfo, + IPipelineStateCache** ppPSOCache) override; + + /// Implementation of IRenderDevice::ReleaseStaleResources() in WebGPU backend. + void DILIGENT_CALL_TYPE ReleaseStaleResources(bool ForceRelease = false) override {} + + /// Implementation of IRenderDevice::IdleGPU() in WebGPU backend. + void DILIGENT_CALL_TYPE IdleGPU() override; + + /// Implementation of IRenderDevice::GetSparseTextureFormatInfo() in WebGPU backend. + SparseTextureFormatInfo DILIGENT_CALL_TYPE GetSparseTextureFormatInfo(TEXTURE_FORMAT TexFormat, + RESOURCE_DIMENSION Dimension, + Uint32 SampleCount) const override; + + /// Implementation of IRenderDeviceWebGPU::GetWebGPUInstance() in WebGPU backend. + WGPUInstance DILIGENT_CALL_TYPE GetWebGPUInstance() const override { return m_wgpuInstance.Get(); } + + /// Implementation of IRenderDeviceWebGPU::GetWebGPUAdapter() in WebGPU backend. + WGPUAdapter DILIGENT_CALL_TYPE GetWebGPUAdapter() const override { return m_wgpuAdapter.Get(); } + + /// Implementation of IRenderDeviceWebGPU::GetWebGPUDevice() in WebGPU backend. + WGPUDevice DILIGENT_CALL_TYPE GetWebGPUDevice() const override { return m_wgpuDevice.Get(); } + + /// Implementation of IRenderDeviceWebGPU::CreateTextureFromWebGPUTexture() in WebGPU backend. + void DILIGENT_CALL_TYPE CreateTextureFromWebGPUTexture(WGPUTexture wgpuTexture, + const TextureDesc& TexDesc, + RESOURCE_STATE InitialState, + ITexture** ppTexture) override; + + /// Implementation of IRenderDeviceWebGPU::CreateTextureFromWebGPUBuffer() in WebGPU backend. + void DILIGENT_CALL_TYPE CreateTextureFromWebGPUBuffer(WGPUBuffer wgpuBuffer, + const BufferDesc& BuffDesc, + RESOURCE_STATE InitialState, + IBuffer** ppBuffer) override; + +public: + void TransitionResource(TextureWebGPUImpl& Texture, + RESOURCE_STATE NewState, + RESOURCE_STATE OldState = RESOURCE_STATE_UNKNOWN, + bool UpdateResourceState = true); + + void TransitionResource(BufferWebGPUImpl& Buffer, + RESOURCE_STATE NewState, + RESOURCE_STATE OldState = RESOURCE_STATE_UNKNOWN, + bool UpdateResourceState = true); + + Uint64 GetCommandQueueCount() const { return 1; } + + Uint64 GetCommandQueueMask() const { return 1; } + + QueueSignalPoolWebGPU& GetQueueSignalPool() const; + + AttachmentCleanerWebGPU& GetAttachmentCleaner() const; + + SharedMemoryManagerWebGPU::Page GetSharedMemoryPage(Uint64 Size); + +private: + void TestTextureFormat(TEXTURE_FORMAT TexFormat) override; + +private: + WebGPUInstanceWrapper m_wgpuInstance; + WebGPUAdapterWrapper m_wgpuAdapter; + WebGPUDeviceWrapper m_wgpuDevice; + QueueSignalPoolWebGPUPtr m_pQueueSignalPool; + AttachmentCleanerWebGPUPtr m_pAttachmentCleaner; + SharedMemoryManagerWebGPUPtr m_pMemoryManager; + + std::vector m_QueryMgrs; +}; + + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/RenderPassWebGPUImpl.hpp b/Graphics/GraphicsEngineWebGPU/include/RenderPassWebGPUImpl.hpp new file mode 100644 index 0000000000..ebace7805f --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/RenderPassWebGPUImpl.hpp @@ -0,0 +1,53 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Declaration of Diligent::RenderPassWebGPUImpl class + +#include "EngineWebGPUImplTraits.hpp" +#include "RenderPassBase.hpp" + +namespace Diligent +{ + +/// Render pass implementation in WebGPU backend. +class RenderPassWebGPUImpl final : public RenderPassBase +{ +public: + using TRenderPassBase = RenderPassBase; + + RenderPassWebGPUImpl(IReferenceCounters* pRefCounters, + RenderDeviceWebGPUImpl* pDevice, + const RenderPassDesc& Desc); + + ~RenderPassWebGPUImpl() override; +}; + + + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/SamplerWebGPUImpl.hpp b/Graphics/GraphicsEngineWebGPU/include/SamplerWebGPUImpl.hpp new file mode 100644 index 0000000000..0da763b13b --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/SamplerWebGPUImpl.hpp @@ -0,0 +1,52 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include "EngineWebGPUImplTraits.hpp" +#include "WebGPUObjectWrappers.hpp" +#include "SamplerBase.hpp" + +namespace Diligent +{ + +/// Sampler implementation in WebGPU backend. +class SamplerWebGPUImpl final : public SamplerBase +{ +public: + using TSamplerBase = SamplerBase; + + SamplerWebGPUImpl(IReferenceCounters* pRefCounters, + RenderDeviceWebGPUImpl* pDevice, + const SamplerDesc& Desc); + + WGPUSampler GetWebGPUSampler() const override { return m_wgpuSampler.Get(); } + +private: + WebGPUSamplerWrapper m_wgpuSampler; +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/ShaderResourceBindingWebGPUImpl.hpp b/Graphics/GraphicsEngineWebGPU/include/ShaderResourceBindingWebGPUImpl.hpp new file mode 100644 index 0000000000..0fd776cd4e --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/ShaderResourceBindingWebGPUImpl.hpp @@ -0,0 +1,49 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Declaration of Diligent::PipelineResourceSignatureWebGPUImpl class + +#include "EngineWebGPUImplTraits.hpp" +#include "ShaderResourceBindingBase.hpp" +#include "ShaderResourceCacheWebGPU.hpp" + +namespace Diligent +{ + +/// Shader resource binding object implementation in OpenGL backend. +class ShaderResourceBindingWebGPUImpl final : public ShaderResourceBindingBase +{ +public: + using TBase = ShaderResourceBindingBase; + + ShaderResourceBindingWebGPUImpl(IReferenceCounters* pRefCounters, + PipelineResourceSignatureWebGPUImpl* pPRS); +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/ShaderResourceCacheWebGPU.hpp b/Graphics/GraphicsEngineWebGPU/include/ShaderResourceCacheWebGPU.hpp new file mode 100644 index 0000000000..1ad604d6f7 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/ShaderResourceCacheWebGPU.hpp @@ -0,0 +1,60 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Declaration of Diligent::ShaderResourceCacheWebGPU class + +#include "ShaderResourceCacheCommon.hpp" + +namespace Diligent +{ + +class ShaderResourceCacheWebGPU : public ShaderResourceCacheBase +{ +public: + ShaderResourceCacheWebGPU(ResourceCacheContentType ContentType) noexcept : + m_ContentType{ContentType} + {} + + ~ShaderResourceCacheWebGPU(); + + // clang-format off + ShaderResourceCacheWebGPU (const ShaderResourceCacheWebGPU&) = delete; + + ShaderResourceCacheWebGPU& operator = (const ShaderResourceCacheWebGPU&) = delete; + + ShaderResourceCacheWebGPU (ShaderResourceCacheWebGPU&&) = delete; + + ShaderResourceCacheWebGPU& operator = (ShaderResourceCacheWebGPU&&) = delete; + // clang-format on + +private: + const ResourceCacheContentType m_ContentType; +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/ShaderVariableManagerWebGPU.hpp b/Graphics/GraphicsEngineWebGPU/include/ShaderVariableManagerWebGPU.hpp new file mode 100644 index 0000000000..b41fa53c73 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/ShaderVariableManagerWebGPU.hpp @@ -0,0 +1,112 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Declaration of Diligent::ShaderVariableManagerWebGPU class + +#include "EngineWebGPUImplTraits.hpp" +#include "ShaderResourceVariableBase.hpp" + +namespace Diligent +{ +class ShaderVariableManagerWebGPU; + +/// Diligent::ShaderVariableManagerWebGPU class +class ShaderVariableManagerWebGPU : ShaderVariableManagerBase +{ +public: + using TBase = ShaderVariableManagerBase; + + ShaderVariableManagerWebGPU(IObject& Owner, + ShaderResourceCacheWebGPU& ResourceCache) noexcept : + TBase{Owner, ResourceCache} + { + } + + // clang-format off + // No copies, only moves are allowed + ShaderVariableManagerWebGPU (const ShaderVariableManagerWebGPU&) = delete; + + ShaderVariableManagerWebGPU& operator = (const ShaderVariableManagerWebGPU&) = delete; + + ShaderVariableManagerWebGPU ( ShaderVariableManagerWebGPU&&) = default; + + ShaderVariableManagerWebGPU& operator = ( ShaderVariableManagerWebGPU&&) = delete; + // clang-format on + + void Initialize(const PipelineResourceSignatureWebGPUImpl& Signature, + IMemoryAllocator& Allocator, + const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, + Uint32 NumAllowedTypes, + SHADER_TYPE ShaderType); + + void Destroy(IMemoryAllocator& Allocator); + + static size_t GetRequiredMemorySize(const PipelineResourceSignatureWebGPUImpl& Signature, + const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, + Uint32 NumAllowedTypes, + SHADER_TYPE ShaderType); + + using ResourceAttribs = PipelineResourceAttribsWebGPU; + + const PipelineResourceDesc& GetResourceDesc(Uint32 Index) const; + + const ResourceAttribs& GetResourceAttribs(Uint32 Index) const; + + void BindResources(IResourceMapping* pResourceMapping, BIND_SHADER_RESOURCES_FLAGS Flags); + + void CheckResources(IResourceMapping* pResourceMapping, + BIND_SHADER_RESOURCES_FLAGS Flags, + SHADER_RESOURCE_VARIABLE_TYPE_FLAGS& StaleVarTypes) const; + + IShaderResourceVariable* GetVariable(const Char* Name) const; + + IShaderResourceVariable* GetVariable(Uint32 Index) const; + + IObject& GetOwner() { return m_Owner; } + + Uint32 GetVariableCount() const; + + Uint32 GetVariableIndex(const IShaderResourceVariable& Variable) const; + + Uint32 GetNumCBs() const; + + Uint32 GetNumTexSRVs() const; + + Uint32 GetNumTexUAVs() const; + + Uint32 GetNumBufSRVs() const; + + Uint32 GetNumBufUAVs() const; + + Uint32 GetNumSamplers() const; + +private: +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/ShaderWebGPUImpl.hpp b/Graphics/GraphicsEngineWebGPU/include/ShaderWebGPUImpl.hpp new file mode 100644 index 0000000000..0a94d3e3e0 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/ShaderWebGPUImpl.hpp @@ -0,0 +1,83 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include "EngineWebGPUImplTraits.hpp" +#include "ShaderBase.hpp" + +namespace Diligent +{ + +/// Shader object implementation in WebGPU backend. +class ShaderWebGPUImpl final : public ShaderBase +{ +public: + using TShaderBase = ShaderBase; + + //TODO + static constexpr INTERFACE_ID IID_InternalImpl = + {0xa62b7e6a, 0x566b, 0x4c8d, {0xbd, 0xe0, 0x2f, 0x63, 0xcf, 0xca, 0x78, 0xc8}}; + + + ShaderWebGPUImpl(IReferenceCounters* pRefCounters, + RenderDeviceWebGPUImpl* pDeviceWebGPU, + const ShaderCreateInfo& ShaderCI); + + /* + virtual void DILIGENT_CALL_TYPE QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) override final; + + /// Implementation of IShader::GetResourceCount() in OpenGL backend. + virtual Uint32 DILIGENT_CALL_TYPE GetResourceCount() const override final; + + /// Implementation of IShader::GetResource() in OpenGL backend. + virtual void DILIGENT_CALL_TYPE GetResourceDesc(Uint32 Index, ShaderResourceDesc& ResourceDesc) const override final; + + /// Implementation of IShader::GetConstantBufferDesc() in OpenGL backend. + virtual const ShaderCodeBufferDesc* DILIGENT_CALL_TYPE GetConstantBufferDesc(Uint32 Index) const override final; + + static GLObjectWrappers::GLProgramObj LinkProgram(ShaderGLImpl* const* ppShaders, Uint32 NumShaders, bool IsSeparableProgram); + + const std::shared_ptr& GetShaderResources() const { return m_pShaderResources; } + + SHADER_SOURCE_LANGUAGE GetSourceLanguage() const { return m_SourceLanguage; } + + virtual void DILIGENT_CALL_TYPE GetBytecode(const void** ppData, + Uint64& DataSize) const override final + { + *ppData = !m_GLSLSourceString.empty() ? m_GLSLSourceString.c_str() : nullptr; + DataSize = m_GLSLSourceString.length(); + } + +private: + SHADER_SOURCE_LANGUAGE m_SourceLanguage = SHADER_SOURCE_LANGUAGE_DEFAULT; + std::string m_GLSLSourceString; + GLObjectWrappers::GLShaderObj m_GLShaderObj; + std::shared_ptr m_pShaderResources; + */ +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/SharedMemoryManagerWebGPU.hpp b/Graphics/GraphicsEngineWebGPU/include/SharedMemoryManagerWebGPU.hpp new file mode 100644 index 0000000000..cda01b4a35 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/SharedMemoryManagerWebGPU.hpp @@ -0,0 +1,97 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Declaration of Diligent::SharedMemoryManagerWebGPU class + +#include "WebGPUObjectWrappers.hpp" +#include "BasicTypes.h" + +namespace Diligent +{ + +class SharedMemoryManagerWebGPU +{ +public: + struct Allocation + { + bool IsEmpty() const; + + WGPUBuffer wgpuBuffer = nullptr; + Uint64 Offset = 0; + Uint64 Size = 0; + Uint8* pData = nullptr; + }; + + struct Page + { + Page() noexcept = default; + Page(SharedMemoryManagerWebGPU* pMgr, Uint64 Size); + + Page(const Page&) = delete; + Page& operator=(const Page&) = delete; + + Page(Page&& RHS) noexcept; + Page& operator=(Page&& RHS) noexcept; + + ~Page(); + + Allocation Allocate(Uint64 Size, Uint64 Alignment = 16); + + void Recycle(); + + bool IsEmpty() const; + + SharedMemoryManagerWebGPU* pMgr = nullptr; + WebGPUBufferWrapper wgpuBuffer; + std::vector MappedData; + + Uint64 PageSize = 0; + Uint64 CurrOffset = 0; + Uint8* pData = nullptr; + }; + + SharedMemoryManagerWebGPU(WGPUDevice wgpuDevice, Uint64 PageSize); + + ~SharedMemoryManagerWebGPU(); + + Page GetPage(Uint64 Size); + +private: + void RecyclePage(Page&& page); + +private: + const Uint64 m_PageSize; + WGPUDevice m_wgpuDevice; + std::vector m_AvailablePages; +#if DILIGENT_DEBUG + Uint32 m_DbgPageCounter{0}; +#endif +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/SwapChainWebGPUImpl.hpp b/Graphics/GraphicsEngineWebGPU/include/SwapChainWebGPUImpl.hpp new file mode 100644 index 0000000000..c5f06c315d --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/SwapChainWebGPUImpl.hpp @@ -0,0 +1,100 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Declaration of Diligent::SwapChainWebGPUImpl class + +#include "SwapChainBase.hpp" +#include "SwapChainWebGPU.h" +#include "EngineWebGPUImplTraits.hpp" +#include "WebGPUObjectWrappers.hpp" + +namespace Diligent +{ + +class WebGPUSwapChainPresentCommand; + +/// Swap chain implementation in WebGPU backend. +class SwapChainWebGPUImpl final : public SwapChainBase +{ +public: + using TSwapChainBase = SwapChainBase; + + SwapChainWebGPUImpl(IReferenceCounters* pRefCounters, + const SwapChainDesc& SCDesc, + RenderDeviceWebGPUImpl* pDevice, + DeviceContextWebGPUImpl* pDeviceContext, + const NativeWindow& Window); + + ~SwapChainWebGPUImpl(); + + void DILIGENT_CALL_TYPE QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) override; + + /// Implementation of ISwapChain::Present() in Direct3D11 backend. + void DILIGENT_CALL_TYPE Present(Uint32 SyncInterval) override; + + /// Implementation of ISwapChain::Resize() in Direct3D11 backend. + void DILIGENT_CALL_TYPE Resize(Uint32 NewWidth, Uint32 NewHeight, SURFACE_TRANSFORM NewPreTransform) override; + + /// Implementation of ISwapChain::SetFullscreenMode() in WebGPU backend. + void DILIGENT_CALL_TYPE SetFullscreenMode(const DisplayModeAttribs& DisplayMode) override; + + /// Implementation of ISwapChain::SetWindowedMode() in WebGPU backend. + void DILIGENT_CALL_TYPE SetWindowedMode() override; + + /// Implementation of ISwapChainWebGPU::GetCurrentBackBufferRTV() in WebGPU backend. + ITextureViewWebGPU* DILIGENT_CALL_TYPE GetCurrentBackBufferRTV() override { return m_pBackBufferRTV; } + + /// Implementation of ISwapChainWebGPU::GetDepthBufferDSV() in WebGPU backend. + ITextureViewWebGPU* DILIGENT_CALL_TYPE GetDepthBufferDSV() override { return m_pDepthBufferDSV; } + + /// Implementation of ISwapChainWebGPU::GetWebGPUSwapChain() in WebGPU backend. + WGPUSwapChain DILIGENT_CALL_TYPE GetWebGPUSwapChain() override { return m_wgpuSwapChain.Get(); } + +private: + void CreateSurface(); + + void CreateSwapChain(); + + void CreateBuffersAndViews(); + + void ReleaseSwapChainResources(); + + void RecreateSwapChain(); + + NativeWindow m_NativeWindow; + WebGPUSurfaceWrapper m_wgpuSurface; + WebGPUSwapChainWrapper m_wgpuSwapChain; + RefCntAutoPtr m_pBackBufferRTV; + RefCntAutoPtr m_pBackBufferSRV; + RefCntAutoPtr m_pDepthBufferDSV; + std::unique_ptr m_pCmdPresent; + bool m_VSyncEnabled = true; +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/TextureViewWebGPUImpl.hpp b/Graphics/GraphicsEngineWebGPU/include/TextureViewWebGPUImpl.hpp new file mode 100644 index 0000000000..2e333982e0 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/TextureViewWebGPUImpl.hpp @@ -0,0 +1,59 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Declaration of Diligent::TextureViewWebGPUImpl class + +#include "EngineWebGPUImplTraits.hpp" +#include "TextureViewBase.hpp" +#include "WebGPUObjectWrappers.hpp" + +namespace Diligent +{ + +/// Texture view implementation in WebGPU backend. +class TextureViewWebGPUImpl final : public TextureViewBase +{ +public: + using TTextureViewBase = TextureViewBase; + + TextureViewWebGPUImpl(IReferenceCounters* pRefCounters, + RenderDeviceWebGPUImpl* pDevice, + const TextureViewDesc& ViewDesc, + ITexture* pTexture, + WGPUTextureView wgpuTextureView, + bool bIsDefaultView); + + /// Implementation of ITextureViewWebGPU::GetWebGPUTextureView() in WebGPU backend. + WGPUTextureView GetWebGPUTextureView() const override { return m_wgpuTextureView.Get(); } + +private: + WebGPUTextureViewWrapper m_wgpuTextureView; +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/TextureWebGPUImpl.hpp b/Graphics/GraphicsEngineWebGPU/include/TextureWebGPUImpl.hpp new file mode 100644 index 0000000000..e4fb649163 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/TextureWebGPUImpl.hpp @@ -0,0 +1,81 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Declaration of Diligent::TextureWebGPUImpl class + +#include "EngineWebGPUImplTraits.hpp" +#include "TextureBase.hpp" +#include "TextureViewWebGPUImpl.hpp" // Required by TextureBase +#include "WebGPUObjectWrappers.hpp" + +namespace Diligent +{ + +/// Texture implementation in WebGPU backend. +class TextureWebGPUImpl final : public TextureBase +{ +public: + using TTextureBase = TextureBase; + + TextureWebGPUImpl(IReferenceCounters* pRefCounters, + FixedBlockMemoryAllocator& TexViewObjAllocator, + RenderDeviceWebGPUImpl* pDevice, + const TextureDesc& Desc, + const TextureData* pInitData = nullptr); + + // Attaches to an existing WebGPU resource + TextureWebGPUImpl(IReferenceCounters* pRefCounters, + FixedBlockMemoryAllocator& TexViewObjAllocator, + RenderDeviceWebGPUImpl* pDevice, + const TextureDesc& Desc, + RESOURCE_STATE InitialState, + WGPUTexture wgpuTextureHandle); + + /// Implementation of ITexture::GetNativeHandle() in WebGPU backend. + Uint64 DILIGENT_CALL_TYPE GetNativeHandle() override { return reinterpret_cast(GetWebGPUTexture()); } + + /// Implementation of ITextureWebGPU::GetWebGPUTexture() in WebGPU backend. + WGPUTexture DILIGENT_CALL_TYPE GetWebGPUTexture() const override { return m_wgpuTexture.Get(); } + + WGPUBuffer GetWebGPUStagingBuffer() const { return m_wgpuStagingBuffer.Get(); } + + static constexpr Uint32 StagingDataAlignment = 16; + + static constexpr Uint64 CopyTextureRawStride = 256; + +private: + void DILIGENT_CALL_TYPE CreateViewInternal(const TextureViewDesc& ViewDesc, + ITextureView** ppView, + bool bIsDefaultView) override; + + WebGPUTextureWrapper m_wgpuTexture; + WebGPUBufferWrapper m_wgpuStagingBuffer; +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/WebGPUObjectWrappers.hpp b/Graphics/GraphicsEngineWebGPU/include/WebGPUObjectWrappers.hpp new file mode 100644 index 0000000000..1d2a8a6643 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/WebGPUObjectWrappers.hpp @@ -0,0 +1,149 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Declaration of Diligent::WebGPUObjectWrapper class + +namespace Diligent +{ + +template +class WebGPUObjectWrapper +{ +public: + WebGPUObjectWrapper() : + m_ObjectHandle{}, m_ObjectDeleter{} {} + + ~WebGPUObjectWrapper() + { + if (m_ObjectHandle) + m_ObjectDeleter(m_ObjectHandle); + } + + explicit WebGPUObjectWrapper(WebGPUObjectType ObjectHandle, WebGPUObjectDeleter ObjectDeleter = WebGPUObjectDeleter{}) : + m_ObjectHandle(ObjectHandle), m_ObjectDeleter(ObjectDeleter) {} + + WebGPUObjectWrapper(const WebGPUObjectWrapper&) = delete; + + WebGPUObjectWrapper& operator=(const WebGPUObjectWrapper&) = delete; + + WebGPUObjectWrapper(WebGPUObjectWrapper&& RHS) noexcept : + m_ObjectHandle(RHS.Release()), m_ObjectDeleter(std::move(RHS.m_ObjectDeleter)) + { + } + + WebGPUObjectWrapper& operator=(WebGPUObjectWrapper&& RHS) noexcept + { + Reset(RHS.Release()); + m_ObjectDeleter = std::move(RHS.m_ObjectDeleter); + return *this; + } + + const WebGPUObjectType& Get() const + { + return m_ObjectHandle; + } + + WebGPUObjectType& Get() + { + return m_ObjectHandle; + } + + void Reset(WebGPUObjectType Handle = nullptr) + { + if (m_ObjectHandle != Handle) + { + if (m_ObjectHandle) + m_ObjectDeleter(m_ObjectHandle); + m_ObjectHandle = Handle; + } + } + + WebGPUObjectType Release() + { + WebGPUObjectType ReleaseHandle = m_ObjectHandle; + m_ObjectHandle = nullptr; + return ReleaseHandle; + } + + explicit operator bool() const + { + return m_ObjectHandle != nullptr; + } + +private: + WebGPUObjectType m_ObjectHandle; + WebGPUObjectDeleter m_ObjectDeleter; +}; + +#ifdef PLATFORM_EMSCRIPTEN +# define DECLARE_WEBGPU_WRAPPER(HandleName, TypeName, FunctionName) \ + struct HandleName##Deleter \ + { \ + void operator()(TypeName Handle) const \ + { \ + if (!IsShared) \ + FunctionName##Release(Handle); \ + } \ + \ + bool IsShared = false; \ + }; \ + using HandleName##Wrapper = WebGPUObjectWrapper; + +#else +# define DECLARE_WEBGPU_WRAPPER(HandleName, TypeName, FunctionName) \ + struct HandleName##Deleter \ + { \ + void operator()(TypeName Handle) const \ + { \ + if (!IsShared) \ + FunctionName##Drop(Handle); \ + } \ + \ + bool IsShared = false; \ + }; \ + using HandleName##Wrapper = WebGPUObjectWrapper; +#endif + +DECLARE_WEBGPU_WRAPPER(WebGPUInstance, WGPUInstance, wgpuInstance) +DECLARE_WEBGPU_WRAPPER(WebGPUAdapter, WGPUAdapter, wgpuAdapter) +DECLARE_WEBGPU_WRAPPER(WebGPUDevice, WGPUDevice, wgpuDevice) +DECLARE_WEBGPU_WRAPPER(WebGPUSwapChain, WGPUSwapChain, wgpuSwapChain) +DECLARE_WEBGPU_WRAPPER(WebGPUSurface, WGPUSurface, wgpuSurface) +DECLARE_WEBGPU_WRAPPER(WebGPUTexture, WGPUTexture, wgpuTexture) +DECLARE_WEBGPU_WRAPPER(WebGPUTextureView, WGPUTextureView, wgpuTextureView) +DECLARE_WEBGPU_WRAPPER(WebGPUBuffer, WGPUBuffer, wgpuBuffer); +DECLARE_WEBGPU_WRAPPER(WebGPUSampler, WGPUSampler, wgpuSampler); +DECLARE_WEBGPU_WRAPPER(WebGPUShaderModule, WGPUShaderModule, wgpuShaderModule); +DECLARE_WEBGPU_WRAPPER(WebGPUBindGroupLayout, WGPUBindGroupLayout, wgpuBindGroupLayout); +DECLARE_WEBGPU_WRAPPER(WebGPUPipelineLayout, WGPUPipelineLayout, wgpuPipelineLayout); +DECLARE_WEBGPU_WRAPPER(WebGPURenderPipeline, WGPURenderPipeline, wgpuRenderPipeline); +DECLARE_WEBGPU_WRAPPER(WebGPUBindGroup, WGPUBindGroup, wgpuBindGroup); +DECLARE_WEBGPU_WRAPPER(WebGPUQuerySet, WGPUQuerySet, wgpuQuerySet); + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/WebGPUTypeConversions.hpp b/Graphics/GraphicsEngineWebGPU/include/WebGPUTypeConversions.hpp new file mode 100644 index 0000000000..90c576d07e --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/WebGPUTypeConversions.hpp @@ -0,0 +1,58 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Type conversion routines + +#include "GraphicsTypes.h" +#include "BlendState.h" + +namespace Diligent +{ + +WGPUTextureFormat TexFormatToWGPUFormat(TEXTURE_FORMAT TexFmt); + +WGPUTextureFormat BufferFormatToWGPUTextureFormat(const struct BufferFormat& BuffFmt); + +WGPUVertexFormat VertexFormatAttribsToWGPUVertexFormat(VALUE_TYPE ValueType, Uint32 NumComponents, bool IsNormalized); + +WGPUTextureViewDimension ResourceDimensionToWGPUTextureViewDimension(RESOURCE_DIMENSION ResourceDim); + +WGPUAddressMode TexAddressModeToWGPUAddressMode(TEXTURE_ADDRESS_MODE Mode); + +WGPUFilterMode FilterTypeToWGPUFilterMode(FILTER_TYPE FilterType); + +WGPUMipmapFilterMode FilterTypeToWGPUMipMapMode(FILTER_TYPE FilterType); + +WGPUCompareFunction ComparisonFuncToWGPUCompareFunction(COMPARISON_FUNCTION CmpFunc); + +WGPUQueryType QueryTypeToWGPUQueryType(QUERY_TYPE QueryType); + +WGPUColorWriteMaskFlags ColorMaskToWGPUColorWriteMask(COLOR_MASK ColorMask); + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/include/pch.h b/Graphics/GraphicsEngineWebGPU/include/pch.h new file mode 100644 index 0000000000..5890cdf9d1 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/include/pch.h @@ -0,0 +1,43 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include +#include +#include +#include + +#include + +#ifndef PLATFORM_EMSCRIPTEN +# include +#endif diff --git a/Graphics/GraphicsEngineWebGPU/interface/BufferWebGPU.h b/Graphics/GraphicsEngineWebGPU/interface/BufferWebGPU.h new file mode 100644 index 0000000000..93f13ff133 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/interface/BufferWebGPU.h @@ -0,0 +1,67 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Definition of the Diligent::IBufferWebGPU interface + +#include "../../GraphicsEngine/interface/Buffer.h" + +DILIGENT_BEGIN_NAMESPACE(Diligent) + +// {A3AB9014-8B47-4A13-AA96-F4975BFD13CA} +static const INTERFACE_ID IID_BufferWebGPU = + {0xA3AB9014, 0x8B47, 0x4A13, {0xAA, 0x96, 0xF4, 0x97, 0x5B, 0xFD, 0x13, 0xCA}}; + +#define DILIGENT_INTERFACE_NAME IBufferWebGPU +#include "../../../Primitives/interface/DefineInterfaceHelperMacros.h" + +#define IBufferWebGPUInclusiveMethods \ + IBufferInclusiveMethods; \ + IBufferWebGPUMethods BufferWebGPU + +// clang-format off + +/// Exposes WebGPU-specific functionality of a buffer object. +DILIGENT_BEGIN_INTERFACE(IBufferWebGPU, IBuffer) +{ + /// The application must not destroy the returned buffer + /// Returns a WebGPU handle of the internal buffer object. + VIRTUAL WGPUBuffer METHOD(GetWebGPUBuffer)(THIS) CONST PURE; + +}; +DILIGENT_END_INTERFACE + +#include "../../../Primitives/interface/UndefInterfaceHelperMacros.h" + +#if DILIGENT_C_INTERFACE + +# define IBufferWebGPU_GetWebGPUBuffer(This) CALL_IFACE_METHOD(BufferWebGPU, GetWebGPUBuffer, This) + +#endif + +DILIGENT_END_NAMESPACE // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/interface/DeviceContextWebGPU.h b/Graphics/GraphicsEngineWebGPU/interface/DeviceContextWebGPU.h new file mode 100644 index 0000000000..b0ac2dc6b8 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/interface/DeviceContextWebGPU.h @@ -0,0 +1,66 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Definition of the Diligent::IDeviceContextVk interface + +#include "../../GraphicsEngine/interface/DeviceContext.h" + +DILIGENT_BEGIN_NAMESPACE(Diligent) + +// {FC2A8031-0EDA-4115-B00E-DFB1FA8AD5C8} +static const INTERFACE_ID IID_DeviceContextWebGPU= + {0xFC2A8031, 0x0EDA, 0x4115, {0xB0, 0x0E, 0xDF, 0xB1, 0xFA, 0x8A, 0xD5, 0xC8}}; + +#define DILIGENT_INTERFACE_NAME IDeviceContextWebGPU +#include "../../../Primitives/interface/DefineInterfaceHelperMacros.h" + +#define IDeviceContextWebGPUInclusiveMethods \ + IDeviceContextInclusiveMethods; \ + IDeviceContextWebGPUMethods DeviceContextWebGPU + +// clang-format off + +/// Exposes WebGPU-specific functionality of a device context. +DILIGENT_BEGIN_INTERFACE(IDeviceContextWebGPU, IDeviceContext) +{ + VIRTUAL WGPUQueue METHOD(GetWebGPUQueue)(THIS) PURE; +}; +DILIGENT_END_INTERFACE + +#include "../../../Primitives/interface/UndefInterfaceHelperMacros.h" + +#if DILIGENT_C_INTERFACE + +// clang-format off +//# define IRenderDeviceWebGPU_GetWebGPUQueue(This) CALL_IFACE_METHOD(DeviceContextWebGPU, GetWebGPUQueue, This) +// clang-format on + +#endif + +DILIGENT_END_NAMESPACE // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/interface/EngineFactoryWebGPU.h b/Graphics/GraphicsEngineWebGPU/interface/EngineFactoryWebGPU.h new file mode 100644 index 0000000000..5dc8855838 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/interface/EngineFactoryWebGPU.h @@ -0,0 +1,125 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Declaration of functions that initialize WebGPU-based engine implementation + +#include "../../GraphicsEngine/interface/EngineFactory.h" +#include "../../GraphicsEngine/interface/RenderDevice.h" +#include "../../GraphicsEngine/interface/DeviceContext.h" +#include "../../GraphicsEngine/interface/SwapChain.h" + +#if ENGINE_DLL +# include "../../GraphicsEngine/interface/LoadEngineDll.h" +#endif + +DILIGENT_BEGIN_NAMESPACE(Diligent) + +// {CF7F4278-4EA7-491A-8575-161A5F3D95EC} +static const struct INTERFACE_ID IID_EngineFactoryWebGPU = + {0xCF7F4278, 0x4EA7, 0x491A, {0x85, 0x75, 0x16, 0x1A, 0x5F, 0x3D, 0x95, 0xEC}}; + +#define DILIGENT_INTERFACE_NAME IEngineFactoryWebGPU +#include "../../../Primitives/interface/DefineInterfaceHelperMacros.h" + +#define IEngineFactoryWebGPUInclusiveMethods \ + IEngineFactoryInclusiveMethods; \ + IEngineFactoryWebGPUMethods EngineFactoryWebGPU + +// clang-format off + +/// Engine factory for WebGPU rendering backend. +DILIGENT_BEGIN_INTERFACE(IEngineFactoryWebGPU, IEngineFactory) +{ + /// Creates a render device and device contexts for WebGPU-based engine implementation. + + /// \param [in] EngineCI - Engine creation info. + /// \param [out] ppDevice - Address of the memory location where pointer to + /// the created device will be written. + /// \param [out] ppContexts - Address of the memory location where pointers to + /// the contexts will be written. Immediate context goes at + /// position 0. If EngineCI.NumDeferredContexts > 0, + /// pointers to deferred contexts are written afterwards. + VIRTUAL void METHOD(CreateDeviceAndContextsWebGPU)(THIS_ + const EngineWebGPUCreateInfo REF EngineCI, + IRenderDevice** ppDevice, + IDeviceContext** ppContexts) PURE; + + /// Creates a swap chain for WebGPU-based engine implementation. + + /// \param [in] pDevice - Pointer to the render device. + /// \param [in] pImmediateContext - Pointer to the immediate device context. + /// \param [in] SCDesc - Swap chain description. + /// \param [in] Window - Platform-specific native window description that + /// the swap chain will be associated with: + /// * On Win32 platform, this is the window handle (HWND) + /// * On Universal Windows Platform, this is the reference + /// to the core window (Windows::UI::Core::CoreWindow) + /// + /// \param [out] ppSwapChain - Address of the memory location where pointer to the new + /// swap chain will be written. + VIRTUAL void METHOD(CreateSwapChainWebGPU)(THIS_ + IRenderDevice* pDevice, + IDeviceContext* pImmediateContext, + const SwapChainDesc REF SCDesc, + const NativeWindow REF Window, + ISwapChain** ppSwapChain) PURE; +}; +DILIGENT_END_INTERFACE + +#include "../../../Primitives/interface/UndefInterfaceHelperMacros.h" + +#if DILIGENT_C_INTERFACE + +// clang-format off + +# define IEngineFactoryWebGPU_CreateDeviceAndContextsWebGPU(This, ...) CALL_IFACE_METHOD(EngineFactoryWebGPU, CreateDeviceAndContextsWebGPU, This, __VA_ARGS__) +# define IEngineFactoryWebGPU_CreateSwapChainWebGPU(This, ...) CALL_IFACE_METHOD(EngineFactoryWebGPU, CreateSwapChainWebGPU, This, __VA_ARGS__) +# define IEngineFactoryWebGPU_AttachToWebGPUDevice(This, ...) CALL_IFACE_METHOD(EngineFactoryWebGPU, AttachToWebGPUDevice, This, __VA_ARGS__) + +// clang-format on + +#endif + + +#if ENGINE_DLL + +typedef struct IEngineFactoryWebGPU* (*GetEngineFactoryWebGPUType)(); + +inline GetEngineFactoryWebGPUType DILIGENT_GLOBAL_FUNCTION(LoadGraphicsEngineWebGPU)() +{ + return (GetEngineFactoryWebGPUType)LoadEngineDll("GraphicsEngineWebGPU", "GetEngineFactoryWebGPU"); +} + +#else + +struct IEngineFactoryWebGPU* DILIGENT_GLOBAL_FUNCTION(GetEngineFactoryWebGPU)(); + +#endif + +DILIGENT_END_NAMESPACE // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/interface/RenderDeviceWebGPU.h b/Graphics/GraphicsEngineWebGPU/interface/RenderDeviceWebGPU.h new file mode 100644 index 0000000000..5576c4825b --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/interface/RenderDeviceWebGPU.h @@ -0,0 +1,113 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Definition of the Diligent::IRenderDeviceWebGPU interface + +#include "../../GraphicsEngine/interface/RenderDevice.h" + +DILIGENT_BEGIN_NAMESPACE(Diligent) + +// {BB1F1488-C10D-493F-8139-3B9010598B16} +static const INTERFACE_ID IID_RenderDeviceWebGPU = + {0xBB1F1488, 0xC10D, 0x493F, {0x81, 0x39, 0x3B, 0x90, 0x10, 0x59, 0x8B, 0x16}}; + +#define DILIGENT_INTERFACE_NAME IRenderDeviceWebGPU +#include "../../../Primitives/interface/DefineInterfaceHelperMacros.h" + +#define IRenderDeviceWebGPUInclusiveMethods \ + IRenderDeviceInclusiveMethods; \ + IRenderDeviceWebGPUMethods RenderDeviceWebGPU + +// clang-format off + +/// Exposes WebGPU-specific functionality of a render device. +DILIGENT_BEGIN_INTERFACE(IRenderDeviceWebGPU, IRenderDevice) +{ + /// Returns WebGPU instance + VIRTUAL WGPUInstance METHOD(GetWebGPUInstance)(THIS) CONST PURE; + + /// Returns WebGPU adapter + VIRTUAL WGPUAdapter METHOD(GetWebGPUAdapter)(THIS) CONST PURE; + + /// Returns WebGPU device + VIRTUAL WGPUDevice METHOD(GetWebGPUDevice)(THIS) CONST PURE; + + /// Creates a texture object from native WebGPU texture + + /// \param [in] wgpuTexture - WebGPU texture handle + /// \param [in] TexDesc - Texture description. WebGPU provides no means to retrieve any + /// texture properties from the texture handle, so complete texture + /// description must be provided + /// \param [in] InitialState - Initial texture state. See Diligent::RESOURCE_STATE. + /// \param [out] ppTexture - Address of the memory location where the pointer to the + /// texture interface will be stored. + /// The function calls AddRef(), so that the new object will contain + /// one reference. + /// \note Created texture object does not take ownership of the WebGPU texture and will not + /// destroy it once released. The application must not destroy the image while it is + /// in use by the engine. + VIRTUAL void METHOD(CreateTextureFromWebGPUTexture)(THIS_ + WGPUTexture wgpuTexture, + const TextureDesc REF TexDesc, + RESOURCE_STATE InitialState, + ITexture** ppTexture) PURE; + + /// Creates a buffer object from native WebGPU buffer + + /// \param [in] wgpuBuffer - WebGPU buffer handle + /// \param [in] BuffDesc - Buffer description. WebGPU provides no means to retrieve any + /// buffer properties from the buffer handle, so complete buffer + /// description must be provided + /// \param [in] InitialState - Initial buffer state. See Diligent::RESOURCE_STATE. + /// \param [out] ppBuffer - Address of the memory location where the pointer to the + /// buffer interface will be stored. + /// The function calls AddRef(), so that the new object will contain + /// one reference. + /// \note Created buffer object does not take ownership of the WebGPU buffer and will not + /// destroy it once released. The application must not destroy Vulkan buffer while it is + /// in use by the engine. + VIRTUAL void METHOD(CreateTextureFromWebGPUBuffer)(THIS_ + WGPUBuffer wgpuBuffer, + const BufferDesc REF BuffDesc, + RESOURCE_STATE InitialState, + IBuffer** ppBuffer) PURE; +}; +DILIGENT_END_INTERFACE + +#include "../../../Primitives/interface/UndefInterfaceHelperMacros.h" + +#if DILIGENT_C_INTERFACE + +// clang-format off + +//# define IRenderDeviceWebGPU_GetWebGPUDevice(This) CALL_IFACE_METHOD(RenderDeviceWebGPU, GetWebGPUDevice, This) + +// clang-format on + +#endif + +DILIGENT_END_NAMESPACE // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/interface/SamplerWebGPU.h b/Graphics/GraphicsEngineWebGPU/interface/SamplerWebGPU.h new file mode 100644 index 0000000000..2cbf45d0e6 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/interface/SamplerWebGPU.h @@ -0,0 +1,63 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Definition of the Diligent::ISamplerWebGPU interface + +#include "../../GraphicsEngine/interface/Sampler.h" + +DILIGENT_BEGIN_NAMESPACE(Diligent) + +// {5425A1DD-7C75-43D1-9171-30ED61B3F9A2} +static const INTERFACE_ID IID_SamplerWebGPU = + {0x5425A1DD, 0x7C75, 0x43D1, {0x91, 0x71, 0x30, 0xED, 0x61, 0xB3, 0xF9, 0xA2}}; + +#define DILIGENT_INTERFACE_NAME ISamplerWebGPU +#include "../../../Primitives/interface/DefineInterfaceHelperMacros.h" + +#define ISamplerWebGPUInclusiveMethods \ + ISamplerInclusiveMethods; \ + ISamplerWebGPUMethods SamplerWebGPU + +/// Exposes WebGPU-specific functionality of a sampler object. +DILIGENT_BEGIN_INTERFACE(ISamplerWebGPU, ISampler) +{ + /// Returns a WebGPU handle of the internal sampler object. + VIRTUAL WGPUSampler METHOD(GetWebGPUSampler)() CONST PURE; +}; +DILIGENT_END_INTERFACE + +#include "../../../Primitives/interface/UndefInterfaceHelperMacros.h" + +#if DILIGENT_C_INTERFACE + +# define ISamplerWebGPU_GetWebGPUSampler(This) CALL_IFACE_METHOD(SamplerWebGPU, GetWebGPUSampler, This) + +#endif + +DILIGENT_END_NAMESPACE // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/interface/SwapChainWebGPU.h b/Graphics/GraphicsEngineWebGPU/interface/SwapChainWebGPU.h new file mode 100644 index 0000000000..170ba1e2af --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/interface/SwapChainWebGPU.h @@ -0,0 +1,67 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Definition of the Diligent::ISwapChainWebGPU interface + +#include "../../GraphicsEngine/interface/SwapChain.h" + +DILIGENT_BEGIN_NAMESPACE(Diligent) + +// {C298E077-D10D-4704-A50E-3A1598B09595} +static const INTERFACE_ID IID_SwapChainWebGPU = + {0xC298E077, 0xD10D, 0x4704, {0xA5, 0x0E, 0x3A, 0x15, 0x98, 0xB0, 0x95, 0x95}}; + +#define DILIGENT_INTERFACE_NAME ISwapChainWebGPU +#include "../../../Primitives/interface/DefineInterfaceHelperMacros.h" + +#define ISwapChainWebGPUInclusiveMethods \ + ISwapChainInclusiveMethods; \ + ISwapChainWebGPUMethods SwapChainWebGPU + +/// Exposes WebGPU-specific functionality of a swap chain. +DILIGENT_BEGIN_INTERFACE(ISwapChainWebGPU, ISwapChain) +{ + /// Returns a WebGPU handle to the internal swap chain object. + VIRTUAL WGPUSwapChain METHOD(GetWebGPUSwapChain)(THIS) PURE; +}; +DILIGENT_END_INTERFACE + +#include "../../../Primitives/interface/UndefInterfaceHelperMacros.h" + +#if DILIGENT_C_INTERFACE + +// clang-format off + +# define ISwapChainWebGPU_GetWebGPUSwapChain(This) CALL_IFACE_METHOD(SwapChainWebGPU, GetWebGPUSwapChain, This) + +// clang-format on + +#endif + +DILIGENT_END_NAMESPACE // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/interface/TextureViewWebGPU.h b/Graphics/GraphicsEngineWebGPU/interface/TextureViewWebGPU.h new file mode 100644 index 0000000000..52a7c79bc8 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/interface/TextureViewWebGPU.h @@ -0,0 +1,69 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Definition of the Diligent::ITextureViewWebGPU interface + +#include "../../GraphicsEngine/interface/TextureView.h" + +DILIGENT_BEGIN_NAMESPACE(Diligent) + +// {241F05D0-70B5-4722-AF35-43B875822BD5} +static const INTERFACE_ID IID_TextureViewWebGPU = + {0x241F05D0, 0x70B5, 0x4722, {0xAF, 0x35, 0x43, 0xB8, 0x75, 0x82, 0x2D, 0xD5}}; + +#define DILIGENT_INTERFACE_NAME ITextureViewWebGPU +#include "../../../Primitives/interface/DefineInterfaceHelperMacros.h" + +#define ITextureViewWebGPUInclusiveMethods \ + ITextureViewInclusiveMethods; \ + ITextureViewWebGPUMethods TextureViewWebGPU + +// clang-format off + +/// Exposes WebGPU-specific functionality of a texture view object. +DILIGENT_BEGIN_INTERFACE(ITextureViewWebGPU, ITextureView) +{ + /// Returns WebGPU texture view handle + VIRTUAL WGPUTextureView METHOD(GetWebGPUTextureView)(THIS) CONST PURE; +}; +DILIGENT_END_INTERFACE + +#include "../../../Primitives/interface/UndefInterfaceHelperMacros.h" + +#if DILIGENT_C_INTERFACE + +// clang-format off + +# define ITextureViewWebGPU_GetWebGPUTextureView(This) CALL_IFACE_METHOD(TextureViewWebGPU, GetWebGPUTextureView, This) + +// clang-format on + +#endif + +DILIGENT_END_NAMESPACE // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/interface/TextureWebGPU.h b/Graphics/GraphicsEngineWebGPU/interface/TextureWebGPU.h new file mode 100644 index 0000000000..b631d1283f --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/interface/TextureWebGPU.h @@ -0,0 +1,71 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Definition of the Diligent::ITextureWebGPU interface + +#include "../../GraphicsEngine/interface/Texture.h" + +DILIGENT_BEGIN_NAMESPACE(Diligent) + +// {A362522B-F9F8-4A5A-967F-DAB2EE2F9C26} +static const INTERFACE_ID IID_TextureWebGPU = + {0xA362522B, 0xF9F8, 0x4A5A, {0x96, 0x7F, 0xDA, 0xB2, 0xEE, 0x2F, 0x9C, 0x26}}; + +#define DILIGENT_INTERFACE_NAME ITextureWebGPU +#include "../../../Primitives/interface/DefineInterfaceHelperMacros.h" + +#define ITextureWebGPUInclusiveMethods \ + ITextureInclusiveMethods; \ + ITextureWebGPUMethods TextureWebGPU + +// clang-format off + +/// Exposes WebGPU-specific functionality of a texture object. +DILIGENT_BEGIN_INTERFACE(ITextureWebGPU, ITexture) +{ + /// Returns WebGPU texture handle. + /// The application must not destroy the returned texture + VIRTUAL WGPUTexture METHOD(GetWebGPUTexture)(THIS) CONST PURE; + +}; +DILIGENT_END_INTERFACE + +#include "../../../Primitives/interface/UndefInterfaceHelperMacros.h" + +#if DILIGENT_C_INTERFACE + +// clang-format off + +# define ITextureWebGPU_GetWebGPUTexture(This) CALL_IFACE_METHOD(TextureWebGPU, GetWebGPUTexture,This) + +// clang-format on + +#endif + +DILIGENT_END_NAMESPACE // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/readme.md b/Graphics/GraphicsEngineWebGPU/readme.md new file mode 100644 index 0000000000..031d061fb4 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/readme.md @@ -0,0 +1,3 @@ +# GraphicsEngineWebGPU + +Implementation of WebGPU backend diff --git a/Graphics/GraphicsEngineWebGPU/src/AttachmentCleanerWebGPU.cpp b/Graphics/GraphicsEngineWebGPU/src/AttachmentCleanerWebGPU.cpp new file mode 100644 index 0000000000..ea22f333f9 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/src/AttachmentCleanerWebGPU.cpp @@ -0,0 +1,297 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" + +#include "AttachmentCleanerWebGPU.hpp" +#include "DebugUtilities.hpp" +#include "WebGPUTypeConversions.hpp" +#include "HashUtils.hpp" + +namespace Diligent +{ + +static constexpr char ShaderSource[] = R"( +override RTVIndex : u32; + +struct ClearConstants +{ + Color: vec4f; + Depth: f32; + Padding0: f32; + Padding1: f32; + Padding2: f32; +}; + +var PushConstants: ClearConstants; + +struct VertexOutput +{ + @builtin(position) Position: vec4f; + @location(0) Color: vec4f; +}; + +@vertex +fn VSMain(@builtin(vertex_index) VertexId : u32) -> VertexOutput +{ + let Texcoord: vec2f = vec2f((VertexId << 1u) & 2.0f, VertexId & 2.0f); + let Position: vec4f = vec4f(Texcoord * vec2f(2.0f, -2.0f) + vec2f(-1.0f, 1.0f), PushConstants.Depth, 1.0f); + + var Output: VertexOutput; + Output.Position = Position; + Output.Color = PushConstants.Color; + return Output; +} + +@fragment +fn PSMain(Input: VertexOutput) -> @location(RTVIndex) vec4f { + return Input.Color; +} +)"; + +static bool operator==(const WGPUStencilFaceState& LHS, const WGPUStencilFaceState& RHS) +{ + // clang-format off + return LHS.compare == RHS.compare && + LHS.depthFailOp == RHS.depthFailOp && + LHS.failOp == RHS.failOp && + LHS.passOp == RHS.passOp; + // clang-format on +} + +static bool operator==(const WGPUDepthStencilState& LHS, const WGPUDepthStencilState& RHS) +{ + // clang-format off + return LHS.format == RHS.format && + LHS.depthWriteEnabled == RHS.depthWriteEnabled && + LHS.depthCompare == RHS.depthCompare && + LHS.stencilFront == RHS.stencilFront && + LHS.stencilBack == RHS.stencilFront && + LHS.stencilReadMask == RHS.stencilReadMask && + LHS.stencilWriteMask == RHS.stencilWriteMask && + LHS.depthBias == RHS.depthBias && + LHS.depthBiasSlopeScale == RHS.depthBiasSlopeScale && + LHS.depthBiasClamp == RHS.depthBiasClamp; + // clang-format on +} + +bool AttachmentCleanerWebGPU::RenderPassInfo::operator==(const RenderPassInfo& rhs) const +{ + // clang-format off + if (NumRenderTargets != rhs.NumRenderTargets || + SampleCount != rhs.SampleCount || + DSVFormat != rhs.DSVFormat) + return false; + // clang-format on + + for (Uint32 RTIndex = 0; RTIndex < NumRenderTargets; ++RTIndex) + if (RTVFormats[RTIndex] != rhs.RTVFormats[RTIndex]) + return false; + + return true; +} + +size_t AttachmentCleanerWebGPU::RenderPassInfo::GetHash() const +{ + size_t Hash = ComputeHash(NumRenderTargets, Uint32{DSVFormat}, Uint32{SampleCount}); + for (Uint32 RTIndex = 0; RTIndex < NumRenderTargets; ++RTIndex) + HashCombine(Hash, Uint32{RTVFormats[RTIndex]}); + return Hash; +} + +bool AttachmentCleanerWebGPU::ClearPSOHashKey::operator==(const ClearPSOHashKey& rhs) const +{ + if (PSOHash != rhs.PSOHash) + return false; + // clang-format off + return RPInfo == rhs.RPInfo && + ColorMask == rhs.ColorMask && + RTIndex == rhs.RTIndex && + DepthState == rhs.DepthState; + // clang-format on +} + +size_t AttachmentCleanerWebGPU::ClearPSOHashKey::Hasher::operator()(const ClearPSOHashKey& Key) const +{ + if (Key.PSOHash == 0) + Key.PSOHash = ComputeHash(Key.RPInfo.GetHash(), static_cast(Key.ColorMask), Key.RTIndex, ComputeHashRaw(&Key.DepthState, sizeof(WGPUDepthStencilState))); + + return Key.PSOHash; +} + +AttachmentCleanerWebGPU::AttachmentCleanerWebGPU() = default; + +void AttachmentCleanerWebGPU::Initialize(WGPUDevice wgpuDevice) +{ + m_wgpuDevice = wgpuDevice; + + m_wgpuDisableDepth.depthCompare = WGPUCompareFunction_Always; + m_wgpuDisableDepth.depthWriteEnabled = false; + + m_wgpuWriteDepth.depthCompare = WGPUCompareFunction_Always; + m_wgpuWriteDepth.depthWriteEnabled = true; + + m_wgpuWriteStencil.depthCompare = WGPUCompareFunction_Never; + m_wgpuWriteStencil.depthWriteEnabled = true; + m_wgpuWriteStencil.stencilFront.compare = WGPUCompareFunction_Always; + m_wgpuWriteStencil.stencilFront.depthFailOp = WGPUStencilOperation_Replace; + m_wgpuWriteStencil.stencilFront.failOp = WGPUStencilOperation_Replace; + m_wgpuWriteStencil.stencilFront.passOp = WGPUStencilOperation_Replace; + m_wgpuWriteStencil.stencilBack.compare = WGPUCompareFunction_Always; + m_wgpuWriteStencil.stencilBack.depthFailOp = WGPUStencilOperation_Replace; + m_wgpuWriteStencil.stencilBack.failOp = WGPUStencilOperation_Replace; + m_wgpuWriteStencil.stencilBack.passOp = WGPUStencilOperation_Replace; + + m_wgpuWriteDepthStencil.depthCompare = WGPUCompareFunction_Always; + m_wgpuWriteDepthStencil.depthWriteEnabled = true; + m_wgpuWriteDepthStencil.stencilFront = m_wgpuWriteStencil.stencilFront; + m_wgpuWriteDepthStencil.stencilBack = m_wgpuWriteStencil.stencilBack; +} + +void AttachmentCleanerWebGPU::ClearColor(WGPURenderPassEncoder wgpuCmdEncoder, + const RenderPassInfo& RPInfo, + COLOR_MASK ColorMask, + Uint32 RTIndex, + const float Color[]) +{ + ClearPSOHashKey Key; + Key.RPInfo = RPInfo; + Key.ColorMask = ColorMask; + Key.RTIndex = static_cast(RTIndex); + Key.DepthState = m_wgpuDisableDepth; + + std::array ClearData = {Color[0], Color[1], Color[2], Color[3]}; + ClearAttachment(wgpuCmdEncoder, Key, ClearData); +} + +void AttachmentCleanerWebGPU::ClearDepthStencil(WGPURenderPassEncoder wgpuCmdEncoder, + const RenderPassInfo& RPInfo, + CLEAR_DEPTH_STENCIL_FLAGS Flags, + float Depth, + Uint8 Stencil) +{ + ClearPSOHashKey Key{}; + Key.RPInfo = RPInfo; + Key.RTIndex = -1; + + if ((Flags & CLEAR_STENCIL_FLAG) != 0) + { + wgpuRenderPassEncoderSetStencilReference(wgpuCmdEncoder, Stencil); + Key.DepthState = (Flags & CLEAR_DEPTH_FLAG) != 0 ? m_wgpuWriteDepthStencil : m_wgpuWriteStencil; + } + else + { + VERIFY((Flags & CLEAR_DEPTH_FLAG) != 0, "At least one of CLEAR_DEPTH_FLAG or CLEAR_STENCIL_FLAG flags should be set"); + Key.DepthState = m_wgpuWriteDepth; + } + + std::array ClearData = {0, 0, 0, 0, Depth}; + ClearAttachment(wgpuCmdEncoder, Key, ClearData); +} + +WebGPURenderPipelineWrapper AttachmentCleanerWebGPU::CreatePSO(const ClearPSOHashKey& Key) const +{ + WebGPURenderPipelineWrapper wgpuPipeline; + try + { + WGPUShaderModuleWGSLDescriptor wgpuShaderCodeDesc{}; + wgpuShaderCodeDesc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor; + wgpuShaderCodeDesc.code = ShaderSource; + + WGPUShaderModuleDescriptor wgpuShaderModuleDesc{}; + wgpuShaderModuleDesc.nextInChain = reinterpret_cast(&wgpuShaderCodeDesc); + WebGPUShaderModuleWrapper wgpuShaderModule{wgpuDeviceCreateShaderModule(m_wgpuDevice, &wgpuShaderModuleDesc)}; + if (!wgpuShaderModule) + LOG_ERROR_AND_THROW("Failed to create shader module"); + + const auto& RPInfo = Key.RPInfo; + + WGPUColorTargetState wgpuColorTargetState[MAX_RENDER_TARGETS]{}; + for (Uint32 RTIndex = 0; RTIndex < RPInfo.NumRenderTargets; ++RTIndex) + { + wgpuColorTargetState[RTIndex].format = TexFormatToWGPUFormat(RPInfo.RTVFormats[RTIndex]); + wgpuColorTargetState[RTIndex].writeMask = ColorMaskToWGPUColorWriteMask(Key.ColorMask); + } + + WGPUDepthStencilState wgpuDepthStencilState = Key.DepthState; + wgpuDepthStencilState.format = TexFormatToWGPUFormat(RPInfo.DSVFormat); + + WGPUConstantEntry wgpuConstantEntry{}; + wgpuConstantEntry.key = "RTVIndex"; + wgpuConstantEntry.value = Key.RTIndex < 0 ? 0 : Key.RTIndex; + + WGPUFragmentState wgpuFragmentState; + wgpuFragmentState.module = wgpuShaderModule.Get(); + wgpuFragmentState.entryPoint = "PMain"; + wgpuFragmentState.targetCount = RPInfo.NumRenderTargets; + wgpuFragmentState.targets = wgpuColorTargetState; + wgpuFragmentState.constantCount = 1; + wgpuFragmentState.constants = &wgpuConstantEntry; + + WGPURenderPipelineDescriptor wgpuRenderPipelineDesc{}; + wgpuRenderPipelineDesc.label = "AttachmentCleanerPSO"; + wgpuRenderPipelineDesc.layout = nullptr; + wgpuRenderPipelineDesc.primitive.topology = WGPUPrimitiveTopology_TriangleList; + wgpuRenderPipelineDesc.vertex.module = wgpuShaderModule.Get(); + wgpuRenderPipelineDesc.vertex.entryPoint = "VSMain"; + wgpuRenderPipelineDesc.fragment = Key.RTIndex < 0 ? nullptr : &wgpuFragmentState; + wgpuRenderPipelineDesc.depthStencil = &wgpuDepthStencilState; + wgpuRenderPipelineDesc.multisample.count = RPInfo.SampleCount; + + wgpuPipeline.Reset(wgpuDeviceCreateRenderPipeline(m_wgpuDevice, &wgpuRenderPipelineDesc)); + + if (!wgpuPipeline) + LOG_ERROR_AND_THROW("Failed to create clear attachment render pipeline"); + } + catch (...) + { + } + + return wgpuPipeline; +} + +void AttachmentCleanerWebGPU::ClearAttachment(WGPURenderPassEncoder wgpuCmdEncoder, + const ClearPSOHashKey& Key, + std::array& ClearData) +{ + auto Iter = m_PSOCache.find(Key); + if (Iter == m_PSOCache.end()) + Iter = m_PSOCache.emplace(Key, CreatePSO(Key)).first; + + WGPURenderPipeline wgpuPipelineState = Iter->second.Get(); + if (wgpuPipelineState == nullptr) + { + UNEXPECTED("Clear attachment PSO is null"); + return; + } + + //TODO for Emscripten + wgpuRenderPassEncoderSetPipeline(wgpuCmdEncoder, wgpuPipelineState); + wgpuRenderPassEncoderSetPushConstants(wgpuCmdEncoder, WGPUShaderStage_Fragment, 0, static_cast(ClearData.size() * sizeof(float)), ClearData.data()); + wgpuRenderPassEncoderDraw(wgpuCmdEncoder, 3, 1, 0, 0); +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/src/BufferViewWebGPUImpl.cpp b/Graphics/GraphicsEngineWebGPU/src/BufferViewWebGPUImpl.cpp new file mode 100644 index 0000000000..4621f43c17 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/src/BufferViewWebGPUImpl.cpp @@ -0,0 +1,53 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" + +#include "BufferViewWebGPUImpl.hpp" +#include "RenderDeviceWebGPUImpl.hpp" + +namespace Diligent +{ + +BufferViewWebGPUImpl::BufferViewWebGPUImpl(IReferenceCounters* pRefCounters, + RenderDeviceWebGPUImpl* pDevice, + const BufferViewDesc& Desc, + IBuffer* pBuffer, + bool IsDefaultView) : + // clang-format off + TBufferViewBase + { + pRefCounters, + pDevice, + Desc, + pBuffer, + IsDefaultView + } + // clang-format on +{ +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/src/BufferWebGPUImpl.cpp b/Graphics/GraphicsEngineWebGPU/src/BufferWebGPUImpl.cpp new file mode 100644 index 0000000000..6fe3b98a87 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/src/BufferWebGPUImpl.cpp @@ -0,0 +1,300 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" + +#include "BufferWebGPUImpl.hpp" +#include "RenderDeviceWebGPUImpl.hpp" +#include "DeviceContextWebGPUImpl.hpp" +#include "WebGPUTypeConversions.hpp" + +namespace Diligent +{ + +BufferWebGPUImpl::BufferWebGPUImpl(IReferenceCounters* pRefCounters, + FixedBlockMemoryAllocator& BuffViewObjMemAllocator, + RenderDeviceWebGPUImpl* pDevice, + const BufferDesc& Desc, + const BufferData* pInitData) : + // clang-format off + TBufferBase + { + pRefCounters, + BuffViewObjMemAllocator, + pDevice, + Desc, + false + }, + m_DynamicAllocations(STD_ALLOCATOR_RAW_MEM(DynamicAllocation, GetRawAllocator(), "Allocator for vector")) +// clang-format on +{ + ValidateBufferInitData(m_Desc, pInitData); + + if (m_Desc.Usage == USAGE_UNIFIED) + LOG_ERROR_AND_THROW("Unified resources are not supported in WebGPU"); + + if (m_Desc.Usage == USAGE_SPARSE) + LOG_ERROR_AND_THROW("Sparse resources are not supported in WebGPU"); + + if (m_Desc.Mode == BUFFER_MODE_FORMATTED) + LOG_ERROR_AND_THROW("Formatted buffers are not supported in WebGPU"); + + m_Alignment = 16; + if (m_Desc.BindFlags & BIND_UNIFORM_BUFFER) + m_Alignment = pDevice->GetAdapterInfo().Buffer.ConstantBufferOffsetAlignment; + + if (m_Desc.BindFlags & BIND_UNORDERED_ACCESS) + m_Alignment = pDevice->GetAdapterInfo().Buffer.StructuredBufferOffsetAlignment; + + m_Desc.Size = AlignUp(m_Desc.Size, m_Alignment); + + WGPUBufferDescriptor wgpuBufferDesc{}; + wgpuBufferDesc.label = m_Desc.Name; + wgpuBufferDesc.size = m_Desc.Size; + + if (m_Desc.Usage == USAGE_STAGING) + { + if (m_Desc.CPUAccessFlags & CPU_ACCESS_READ) + { + wgpuBufferDesc.usage |= WGPUMapMode_Read; + wgpuBufferDesc.usage |= WGPUBufferUsage_CopyDst; + } + + if (m_Desc.CPUAccessFlags & CPU_ACCESS_WRITE) + { + wgpuBufferDesc.usage |= WGPUMapMode_Write; + wgpuBufferDesc.usage |= WGPUBufferUsage_CopySrc; + } + } + else if (m_Desc.Usage == USAGE_DYNAMIC && (m_Desc.BindFlags & (BIND_UNORDERED_ACCESS | BIND_SHADER_RESOURCE)) == 0) + { + auto CtxCount = pDevice->GetNumImmediateContexts() + pDevice->GetNumDeferredContexts(); + m_DynamicAllocations.resize(CtxCount); + return; + } + else + { + wgpuBufferDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_CopySrc; + + for (auto BindFlags = m_Desc.BindFlags; BindFlags != 0;) + { + const auto BindFlag = ExtractLSB(BindFlags); + switch (BindFlag) + { + case BIND_UNIFORM_BUFFER: + wgpuBufferDesc.usage |= WGPUBufferUsage_Uniform; + break; + case BIND_SHADER_RESOURCE: + case BIND_UNORDERED_ACCESS: + wgpuBufferDesc.usage |= WGPUBufferUsage_Storage; + break; + case BIND_VERTEX_BUFFER: + wgpuBufferDesc.usage |= WGPUBufferUsage_Vertex; + break; + case BIND_INDEX_BUFFER: + wgpuBufferDesc.usage |= WGPUBufferUsage_Index; + break; + case BIND_INDIRECT_DRAW_ARGS: + wgpuBufferDesc.usage |= WGPUBufferUsage_Indirect; + break; + default: + UNEXPECTED("unsupported buffer usage type"); + break; + } + } + } + + const auto IsInitializeBuffer = (pInitData != nullptr && pInitData->pData != nullptr); + m_wgpuBuffer.Reset(wgpuDeviceCreateBuffer(pDevice->GetWebGPUDevice(), &wgpuBufferDesc)); + if (!m_wgpuBuffer) + LOG_ERROR_AND_THROW("Failed to create WebGPU buffer ", " '", m_Desc.Name ? m_Desc.Name : "", '\''); + + if (IsInitializeBuffer) + wgpuQueueWriteBuffer(wgpuDeviceGetQueue(pDevice->GetWebGPUDevice()), m_wgpuBuffer.Get(), 0, pInitData->pData, pInitData->DataSize); + + if (m_Desc.CPUAccessFlags & (CPU_ACCESS_READ | CPU_ACCESS_WRITE)) + m_MappedData.resize(m_Desc.Size); + + SetState(RESOURCE_STATE_UNDEFINED); + m_MemoryProperties = MEMORY_PROPERTY_HOST_COHERENT; +} + +BufferWebGPUImpl::BufferWebGPUImpl(IReferenceCounters* pRefCounters, + FixedBlockMemoryAllocator& BuffViewObjMemAllocator, + RenderDeviceWebGPUImpl* pDevice, + const BufferDesc& Desc, + RESOURCE_STATE InitialState, + WGPUBuffer wgpuBuffer) : + // clang-format off + TBufferBase + { + pRefCounters, + BuffViewObjMemAllocator, + pDevice, + Desc, + false + }, + m_wgpuBuffer{wgpuBuffer, {true}}, + m_DynamicAllocations(STD_ALLOCATOR_RAW_MEM(DynamicAllocation, GetRawAllocator(), "Allocator for vector")) +// clang-format on +{ + if (m_Desc.CPUAccessFlags & (CPU_ACCESS_READ | CPU_ACCESS_WRITE)) + m_MappedData.resize(m_Desc.Size); + SetState(InitialState); +} + +SparseBufferProperties BufferWebGPUImpl::GetSparseProperties() const +{ + DEV_ERROR("IBuffer::GetSparseProperties() is not supported in WebGPU"); + return {}; +} + +void BufferWebGPUImpl::Map(MAP_TYPE MapType, Uint32 MapFlags, PVoid& pMappedData) +{ + VERIFY(m_Desc.Usage == USAGE_STAGING, "Map working only for staging buffers"); + + if (MapType == MAP_READ) + { + auto MapAsyncCallback = [](WGPUBufferMapAsyncStatus MapStatus, void* pUserData) { + if (MapStatus == WGPUBufferMapAsyncStatus_Success) + { + auto* pThis = static_cast(pUserData); + const auto* pData = static_cast(wgpuBufferGetMappedRange(pThis->m_wgpuBuffer.Get(), 0, pThis->m_Desc.Size)); + VERIFY_EXPR(pUserData != nullptr); + memcpy(pThis->m_MappedData.data(), pData, pThis->m_Desc.Size); + wgpuBufferUnmap(pThis->m_wgpuBuffer.Get()); + } + else + { + DEV_ERROR("Failed wgpuBufferMapAsync: ", MapStatus); + } + }; + + wgpuBufferMapAsync(m_wgpuBuffer.Get(), WGPUMapMode_Read, 0, m_Desc.Size, MapAsyncCallback, this); +#ifndef PLATFORM_EMSCRIPTEN + wgpuQueueSubmit(wgpuDeviceGetQueue(m_pDevice->GetWebGPUDevice()), 0, nullptr); +#endif + pMappedData = m_MappedData.data(); + } + else if (MapType == MAP_WRITE) + { + pMappedData = m_MappedData.data(); + } + else if (MapType == MAP_READ_WRITE) + { + LOG_ERROR("MAP_READ_WRITE is not supported in WebGPU backend"); + } + else + { + UNEXPECTED("Unknown map type"); + } +} + +void BufferWebGPUImpl::Unmap(MAP_TYPE MapType) +{ + VERIFY(m_Desc.Usage == USAGE_STAGING, "Map working only for staging buffers"); + + if (MapType == MAP_READ) + { + } + else if (MapType == MAP_WRITE) + { + auto MapAsyncCallback = [](WGPUBufferMapAsyncStatus MapStatus, void* pUserData) { + if (MapStatus == WGPUBufferMapAsyncStatus_Success) + { + auto* pThis = static_cast(pUserData); + auto* pData = static_cast(wgpuBufferGetMappedRange(pThis->m_wgpuBuffer.Get(), 0, pThis->m_Desc.Size)); + VERIFY_EXPR(pUserData != nullptr); + memcpy(pData, pThis->m_MappedData.data(), pThis->m_Desc.Size); + wgpuBufferUnmap(pThis->m_wgpuBuffer.Get()); + } + else + { + DEV_ERROR("Failed wgpuBufferMapAsync: ", MapStatus); + } + }; + + wgpuBufferMapAsync(m_wgpuBuffer.Get(), WGPUMapMode_Write, 0, m_Desc.Size, MapAsyncCallback, this); +#ifndef PLATFORM_EMSCRIPTEN + wgpuQueueSubmit(wgpuDeviceGetQueue(m_pDevice->GetWebGPUDevice()), 0, nullptr); +#endif + } + else if (MapType == MAP_READ_WRITE) + { + LOG_ERROR("MAP_READ_WRITE is not supported in WebGPU backend"); + } + else + { + UNEXPECTED("Unknown map type"); + } +} + +Uint64 BufferWebGPUImpl::GetAlignment() +{ + return m_Alignment; +} + +const SharedMemoryManagerWebGPU::Allocation& BufferWebGPUImpl::GetDynamicAllocation(DeviceContextIndex CtxId) const +{ + return m_DynamicAllocations[CtxId]; +} + +void BufferWebGPUImpl::SetDynamicAllocation(DeviceContextIndex CtxId, SharedMemoryManagerWebGPU::Allocation&& Allocation) +{ + m_DynamicAllocations[CtxId] = std::move(Allocation); +} + +void BufferWebGPUImpl::CreateViewInternal( + const BufferViewDesc& ViewDesc, + IBufferView** ppView, + bool IsDefaultView) +{ + VERIFY(ppView != nullptr, "Null pointer provided"); + if (!ppView) return; + VERIFY(*ppView == nullptr, "Overwriting reference to existing object may cause memory leaks"); + + *ppView = nullptr; + + try + { + auto* pDeviceWebGPU = GetDevice(); + auto& BuffViewAllocator = pDeviceWebGPU->GetBuffViewObjAllocator(); + VERIFY(&BuffViewAllocator == &m_dbgBuffViewAllocator, "Buffer view allocator does not match allocator provided at buffer initialization"); + + if (ViewDesc.ViewType == BUFFER_VIEW_UNORDERED_ACCESS || ViewDesc.ViewType == BUFFER_VIEW_SHADER_RESOURCE) + *ppView = NEW_RC_OBJ(BuffViewAllocator, "BufferViewWebGPUImpl instance", BufferViewWebGPUImpl, IsDefaultView ? this : nullptr)(pDeviceWebGPU, ViewDesc, this, IsDefaultView); + + if (!IsDefaultView && *ppView) + (*ppView)->AddRef(); + } + catch (const std::runtime_error&) + { + const auto* ViewTypeName = GetBufferViewTypeLiteralName(ViewDesc.ViewType); + LOG_ERROR("Failed to create view \"", ViewDesc.Name ? ViewDesc.Name : "", "\" (", ViewTypeName, ") for buffer \"", m_Desc.Name, "\""); + } +} + +} // namespace Diligent \ No newline at end of file diff --git a/Graphics/GraphicsEngineWebGPU/src/DLLMain.cpp b/Graphics/GraphicsEngineWebGPU/src/DLLMain.cpp new file mode 100644 index 0000000000..efe88cdb7d --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/src/DLLMain.cpp @@ -0,0 +1,53 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include +#include + +BOOL APIENTRY DllMain(HANDLE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: +#if defined(_DEBUG) || defined(DEBUG) + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); +#endif + break; + + case DLL_THREAD_ATTACH: + break; + + case DLL_THREAD_DETACH: + break; + + case DLL_PROCESS_DETACH: + break; + } + + return TRUE; +} diff --git a/Graphics/GraphicsEngineWebGPU/src/DeviceContextWebGPUImpl.cpp b/Graphics/GraphicsEngineWebGPU/src/DeviceContextWebGPUImpl.cpp new file mode 100644 index 0000000000..a89eb7ce38 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/src/DeviceContextWebGPUImpl.cpp @@ -0,0 +1,1046 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" + +#include "DeviceContextWebGPUImpl.hpp" +#include "RenderDeviceWebGPUImpl.hpp" +#include "TextureWebGPUImpl.hpp" +#include "TextureViewWebGPUImpl.hpp" +#include "BufferWebGPUImpl.hpp" +#include "PipelineStateWebGPUImpl.hpp" +#include "PipelineResourceSignatureWebGPUImpl.hpp" +#include "PipelineResourceAttribsWebGPU.hpp" +#include "ShaderResourceCacheWebGPU.hpp" +#include "RenderPassWebGPUImpl.hpp" +#include "FramebufferWebGPUImpl.hpp" +#include "FenceWebGPUImpl.hpp" +#include "QueueSignalPoolWebGPU.hpp" +#include "QueryManagerWebGPU.hpp" +#include "QueryWebGPUImpl.hpp" +#include "AttachmentCleanerWebGPU.hpp" + +namespace Diligent +{ + +// TODO: Compare float by epsilon +static bool operator!=(const Viewport& LHS, const Viewport& RHS) +{ + // clang-format off + return LHS.TopLeftX != RHS.TopLeftX || + LHS.TopLeftY != RHS.TopLeftY || + LHS.Width != RHS.Width || + LHS.Height != RHS.Height || + LHS.MinDepth != RHS.MinDepth || + LHS.MaxDepth != RHS.MaxDepth; + // clang-format on +} + +static bool operator!=(const Rect& LHS, const Rect& RHS) +{ + // clang-format off + return LHS.left != RHS.left || + LHS.top != RHS.top || + LHS.right != RHS.right || + LHS.bottom != RHS.bottom; + // clang-format on +} + +DeviceContextWebGPUImpl::DeviceContextWebGPUImpl(IReferenceCounters* pRefCounters, + RenderDeviceWebGPUImpl* pDevice, + const EngineWebGPUCreateInfo& EngineCI, + const DeviceContextDesc& Desc) : + // clang-format off + TDeviceContextBase + { + pRefCounters, + pDevice, + Desc + } +// clang-format on +{ + m_wgpuQueue = wgpuDeviceGetQueue(pDevice->GetWebGPUDevice()); +} + +IMPLEMENT_QUERY_INTERFACE(DeviceContextWebGPUImpl, IID_DeviceContextWebGPU, TDeviceContextBase) + +void DeviceContextWebGPUImpl::Begin(Uint32 ImmediateContextId) +{ + DEV_CHECK_ERR(ImmediateContextId == 0, "WebGPU supports only one immediate context"); + TDeviceContextBase::Begin(DeviceContextIndex{ImmediateContextId}, COMMAND_QUEUE_TYPE_GRAPHICS); +} + +void DeviceContextWebGPUImpl::SetPipelineState(IPipelineState* pPipelineState) {} + +void DeviceContextWebGPUImpl::TransitionShaderResources(IPipelineState* pPipelineState, + IShaderResourceBinding* pShaderResourceBinding) +{ + DEV_CHECK_ERR(pPipelineState != nullptr, "Pipeline state must not be null"); + DEV_CHECK_ERR(pShaderResourceBinding != nullptr, "Shader resource binding must not be null"); +} + +void DeviceContextWebGPUImpl::CommitShaderResources(IShaderResourceBinding* pShaderResourceBinding, + RESOURCE_STATE_TRANSITION_MODE StateTransitionMode) {} + +void DeviceContextWebGPUImpl::InvalidateState() +{ + TDeviceContextBase::InvalidateState(); + m_PendingClears.Clear(); + m_EncoderState.Clear(); +} + +void DeviceContextWebGPUImpl::SetStencilRef(Uint32 StencilRef) +{ + if (TDeviceContextBase::SetStencilRef(StencilRef, 0)) + m_EncoderState.Invalidate(WebGPUEncoderState::CMD_ENCODER_STATE_STENCIL_REF); +} + +void DeviceContextWebGPUImpl::SetBlendFactors(const float* pBlendFactors) +{ + if (TDeviceContextBase::SetBlendFactors(pBlendFactors, 0)) + m_EncoderState.Invalidate(WebGPUEncoderState::CMD_ENCODER_STATE_BLEND_FACTORS); +} + +void DeviceContextWebGPUImpl::SetVertexBuffers(Uint32 StartSlot, + Uint32 NumBuffersSet, + IBuffer** ppBuffers, + const Uint64* pOffsets, + RESOURCE_STATE_TRANSITION_MODE StateTransitionMode, + SET_VERTEX_BUFFERS_FLAGS Flags) +{ + TDeviceContextBase::SetVertexBuffers(StartSlot, NumBuffersSet, ppBuffers, pOffsets, StateTransitionMode, Flags); + m_EncoderState.Invalidate(WebGPUEncoderState::CMD_ENCODER_STATE_VERTEX_BUFFERS); +} + + +void DeviceContextWebGPUImpl::SetIndexBuffer(IBuffer* pIndexBuffer, + Uint64 ByteOffset, + RESOURCE_STATE_TRANSITION_MODE StateTransitionMode) +{ + TDeviceContextBase::SetIndexBuffer(pIndexBuffer, ByteOffset, StateTransitionMode); + m_EncoderState.Invalidate(WebGPUEncoderState::CMD_ENCODER_STATE_INDEX_BUFFER); +} + +void DeviceContextWebGPUImpl::SetViewports(Uint32 NumViewports, + const Viewport* pViewports, + Uint32 RTWidth, + Uint32 RTHeight) +{ + TDeviceContextBase::SetViewports(NumViewports, pViewports, RTWidth, RTHeight); + m_EncoderState.Invalidate(WebGPUEncoderState::CMD_ENCODER_STATE_VIEWPORTS); +} + +void DeviceContextWebGPUImpl::SetScissorRects(Uint32 NumRects, const Rect* pRects, Uint32 RTWidth, Uint32 RTHeight) +{ + TDeviceContextBase::SetScissorRects(NumRects, pRects, RTWidth, RTHeight); + m_EncoderState.Invalidate(WebGPUEncoderState::CMD_ENCODER_STATE_SCISSOR_RECTS); +} + +void DeviceContextWebGPUImpl::SetRenderTargetsExt(const SetRenderTargetsAttribs& Attribs) +{ + if (m_PendingClears.AnyPending()) + { + bool RTChanged = + Attribs.NumRenderTargets != m_NumBoundRenderTargets || + Attribs.pDepthStencil != m_pBoundDepthStencil || + Attribs.pShadingRateMap != m_pBoundShadingRateMap; + for (Uint32 RTIndex = 0; RTIndex < m_NumBoundRenderTargets && !RTChanged; ++RTIndex) + RTChanged = m_pBoundRenderTargets[RTIndex] != Attribs.ppRenderTargets[RTIndex]; + + if (RTChanged) + { + VERIFY(!m_wgpuRenderPassEncoder, "There should be no active render command encoder when pending clears mask is not zero"); + EndCommandEncoders(COMMAND_ENCODER_FLAG_ALL & ~COMMAND_ENCODER_FLAG_RENDER); + CommitRenderTargets(); + } + } + + if (TDeviceContextBase::SetRenderTargets(Attribs)) + { + EndCommandEncoders(COMMAND_ENCODER_FLAG_RENDER); + SetViewports(1, nullptr, 0, 0); + } +} + +void DeviceContextWebGPUImpl::BeginRenderPass(const BeginRenderPassAttribs& Attribs) +{ + TDeviceContextBase::BeginRenderPass(Attribs); + + m_AttachmentClearValues.resize(Attribs.ClearValueCount); + for (Uint32 RTIndex = 0; RTIndex < Attribs.ClearValueCount; ++RTIndex) + m_AttachmentClearValues[RTIndex] = Attribs.pClearValues[RTIndex]; + + SetViewports(1, nullptr, 0, 0); + BeginSubpass(); +} + +void DeviceContextWebGPUImpl::NextSubpass() +{ + TDeviceContextBase::NextSubpass(); + BeginSubpass(); +} + +void DeviceContextWebGPUImpl::EndRenderPass() +{ + TDeviceContextBase::EndRenderPass(); + m_AttachmentClearValues.clear(); + VERIFY(m_wgpuRenderPassEncoder, "There is no active render command encoder. Did you begin the render pass?"); + EndCommandEncoders(); +} + +void DeviceContextWebGPUImpl::Draw(const DrawAttribs& Attribs) {} + +void DeviceContextWebGPUImpl::DrawIndexed(const DrawIndexedAttribs& Attribs) {} + +void DeviceContextWebGPUImpl::DrawIndirect(const DrawIndirectAttribs& Attribs) {} + +void DeviceContextWebGPUImpl::DrawIndexedIndirect(const DrawIndexedIndirectAttribs& Attribs) {} + +void DeviceContextWebGPUImpl::DrawMesh(const DrawMeshAttribs& Attribs) +{ + UNSUPPORTED("DrawMesh is not supported in WebGPU"); +} + +void DeviceContextWebGPUImpl::DrawMeshIndirect(const DrawMeshIndirectAttribs& Attribs) +{ + UNSUPPORTED("DrawMeshIndirect is not supported in WebGPU"); +} + +void DeviceContextWebGPUImpl::DispatchCompute(const DispatchComputeAttribs& Attribs) {} + +void DeviceContextWebGPUImpl::DispatchComputeIndirect(const DispatchComputeIndirectAttribs& Attribs) {} + +void DeviceContextWebGPUImpl::GetTileSize(Uint32& TileSizeX, Uint32& TileSizeY) +{ + TDeviceContextBase::GetTileSize(TileSizeX, TileSizeY); +} + +void DeviceContextWebGPUImpl::ClearDepthStencil(ITextureView* pView, + CLEAR_DEPTH_STENCIL_FLAGS ClearFlags, + float Depth, + Uint8 Stencil, + RESOURCE_STATE_TRANSITION_MODE StateTransitionMode) +{ + TDeviceContextBase::ClearDepthStencil(pView); + + if (pView != m_pBoundDepthStencil) + { + LOG_ERROR_MESSAGE("Depth stencil buffer must be bound to the context to be cleared in WebGPU backend"); + return; + } + + if (m_wgpuRenderPassEncoder) + ClearAttachment(-1, COLOR_MASK_NONE, ClearFlags, &Depth, Stencil); + else + { + if (ClearFlags & CLEAR_DEPTH_FLAG) + m_PendingClears.SetDepth(Depth); + + if (ClearFlags & CLEAR_STENCIL_FLAG) + m_PendingClears.SetStencil(Stencil); + } +} + +void DeviceContextWebGPUImpl::ClearRenderTarget(ITextureView* pView, + const float* RGBA, + RESOURCE_STATE_TRANSITION_MODE StateTransitionMode) +{ + TDeviceContextBase::ClearRenderTarget(pView); + + Int32 RTIndex = -1; + for (Uint32 Index = 0; Index < m_NumBoundRenderTargets; ++Index) + { + if (m_pBoundRenderTargets[Index] == pView) + { + RTIndex = static_cast(Index); + break; + } + } + + if (RTIndex == -1) + { + LOG_ERROR_MESSAGE("Render target must be bound to the context to be cleared in WebGPU backend"); + return; + } + + if (m_wgpuRenderPassEncoder) + ClearAttachment(RTIndex, COLOR_MASK_ALL, CLEAR_DEPTH_FLAG_NONE, RGBA, 0); + else + m_PendingClears.SetColor(RTIndex, RGBA); +} + +void DeviceContextWebGPUImpl::UpdateBuffer(IBuffer* pBuffer, + Uint64 Offset, + Uint64 Size, + const void* pData, + RESOURCE_STATE_TRANSITION_MODE StateTransitionMode) +{ + TDeviceContextBase::UpdateBuffer(pBuffer, Offset, Size, pData, StateTransitionMode); + + auto* pBufferWebGPU = ClassPtrCast(pBuffer); + const auto& BuffDesc = pBufferWebGPU->GetDesc(); + if (BuffDesc.Usage == USAGE_DEFAULT) + { + EndCommandEncoders(); + ClearEncoderState(); + + const auto DynAllocation = AllocateSharedMemory(Size); + memcpy(DynAllocation.pData, pData, Size); + wgpuCommandEncoderCopyBufferToBuffer(GetCommandEncoder(), DynAllocation.wgpuBuffer, DynAllocation.Offset, + pBufferWebGPU->GetWebGPUBuffer(), Offset, Size); + } + else if (BuffDesc.Usage == USAGE_STAGING) + { + wgpuQueueWriteBuffer(m_wgpuQueue, pBufferWebGPU->GetWebGPUBuffer(), Offset, pData, Size); + } + else + { + LOG_ERROR_MESSAGE(GetUsageString(BuffDesc.Usage), " buffers can't be updated using UpdateBuffer method"); + } +} + +void DeviceContextWebGPUImpl::CopyBuffer(IBuffer* pSrcBuffer, + Uint64 SrcOffset, + RESOURCE_STATE_TRANSITION_MODE SrcBufferTransitionMode, + IBuffer* pDstBuffer, + Uint64 DstOffset, + Uint64 Size, + RESOURCE_STATE_TRANSITION_MODE DstBufferTransitionMode) +{ + TDeviceContextBase::CopyBuffer(pSrcBuffer, SrcOffset, SrcBufferTransitionMode, pDstBuffer, DstOffset, Size, DstBufferTransitionMode); + + EndCommandEncoders(); + ClearEncoderState(); + + const auto* pSrcBufferWebGPU = ClassPtrCast(pSrcBuffer); + const auto* pDstBufferWebGPU = ClassPtrCast(pDstBuffer); + + auto wgpuSrcBuffer = pSrcBufferWebGPU->GetWebGPUBuffer(); + auto wgpuDstBuffer = pDstBufferWebGPU->GetWebGPUBuffer(); + + if (wgpuSrcBuffer == nullptr) + { + VERIFY_EXPR(pSrcBufferWebGPU->GetDesc().Usage == USAGE_DYNAMIC); + const auto& DynAlloc = pSrcBufferWebGPU->GetDynamicAllocation(GetContextId()); + wgpuSrcBuffer = DynAlloc.wgpuBuffer; + SrcOffset += DynAlloc.Offset; + } + DEV_CHECK_ERR(wgpuDstBuffer != nullptr, "Copying to dynamic buffers is not supported"); + + wgpuCommandEncoderCopyBufferToBuffer(GetCommandEncoder(), wgpuSrcBuffer, SrcOffset, wgpuDstBuffer, DstOffset, Size); +} + +void DeviceContextWebGPUImpl::MapBuffer(IBuffer* pBuffer, + MAP_TYPE MapType, + MAP_FLAGS MapFlags, + PVoid& pMappedData) +{ + TDeviceContextBase::MapBuffer(pBuffer, MapType, MapFlags, pMappedData); + + auto* pBufferWebGPU = ClassPtrCast(pBuffer); + auto& BuffDesc = pBufferWebGPU->GetDesc(); + + if (MapType == MAP_READ) + { + pBufferWebGPU->Map(MapType, MapFlags, pMappedData); + } + else if (MapType == MAP_WRITE) + { + if (BuffDesc.Usage == USAGE_STAGING) + { + pBufferWebGPU->Map(MapType, MapFlags, pMappedData); + } + else if (BuffDesc.Usage == USAGE_DYNAMIC) + { + const auto& DynAllocation = pBufferWebGPU->GetDynamicAllocation(GetContextId()); + if ((MapFlags & MAP_FLAG_DISCARD) != 0 || DynAllocation.IsEmpty()) + { + auto Allocation = AllocateSharedMemory(BuffDesc.Size, pBufferWebGPU->GetAlignment()); + pMappedData = Allocation.pData; + pBufferWebGPU->SetDynamicAllocation(GetContextId(), std::move(Allocation)); + } + else + { + if (pBufferWebGPU->GetWebGPUBuffer() != nullptr) + { + LOG_ERROR("Formatted or structured buffers require actual WebGPU backing resource and cannot be suballocated " + "from dynamic heap. In current implementation, the entire contents of the backing buffer is updated when the buffer is unmapped. " + "As a consequence, the buffer cannot be mapped with MAP_FLAG_NO_OVERWRITE flag because updating the whole " + "buffer will overwrite regions that may still be in use by the GPU."); + return; + } + + pMappedData = DynAllocation.pData; + } + } + else + { + LOG_ERROR("Only USAGE_DYNAMIC, USAGE_STAGING WebGPU buffers can be mapped for writing"); + } + } + else if (MapType == MAP_READ_WRITE) + { + LOG_ERROR("MAP_READ_WRITE is not supported in WebGPU backend"); + } + else + { + UNEXPECTED("Unknown map type"); + } +} + +void DeviceContextWebGPUImpl::UnmapBuffer(IBuffer* pBuffer, MAP_TYPE MapType) +{ + TDeviceContextBase::UnmapBuffer(pBuffer, MapType); + + auto* pBufferWebGPU = ClassPtrCast(pBuffer); + auto& BuffDesc = pBufferWebGPU->GetDesc(); + + if (MapType == MAP_READ) + { + pBufferWebGPU->Unmap(MapType); + } + else if (MapType == MAP_WRITE) + { + if (BuffDesc.Usage == USAGE_STAGING) + { + pBufferWebGPU->Unmap(MapType); + } + else if (BuffDesc.Usage == USAGE_DYNAMIC) + { + auto wgpuBuffer = pBufferWebGPU->GetWebGPUBuffer(); + if (wgpuBuffer != nullptr) + { + DEV_CHECK_ERR(!m_pActiveRenderPass, + "Unmapping dynamic buffer with backing WebGPU resource requires " + "copying the data from shared memory to private storage. This can only be " + "done by blit encoder outside of render pass."); + + const auto& DynAlloc = pBufferWebGPU->GetDynamicAllocation(GetContextId()); + + EndCommandEncoders(); + ClearEncoderState(); + wgpuCommandEncoderCopyBufferToBuffer(GetCommandEncoder(), DynAlloc.wgpuBuffer, DynAlloc.Offset, wgpuBuffer, 0, BuffDesc.Size); + } + } + else + { + LOG_ERROR("Only USAGE_DYNAMIC, USAGE_STAGING WebGPU buffers can be mapped for writing"); + } + } +} + +void DeviceContextWebGPUImpl::UpdateTexture(ITexture* pTexture, + Uint32 MipLevel, + Uint32 Slice, + const Box& DstBox, + const TextureSubResData& SubresData, + RESOURCE_STATE_TRANSITION_MODE SrcBufferStateTransitionMode, + RESOURCE_STATE_TRANSITION_MODE DstTextureStateTransitionMode) +{ + TDeviceContextBase::UpdateTexture(pTexture, MipLevel, Slice, DstBox, SubresData, SrcBufferStateTransitionMode, DstTextureStateTransitionMode); + + //TODO + /* + auto* pTextureWebGPU = ClassPtrCast(pTexture); + + WGPUImageCopyTexture wgpuImageCopyTexture{}; + WGPUTextureDataLayout wgpuTextureDataLayout{}; + WGPUExtent3D wgpuCopySize{}; + + wgpuQueueWriteTexture(m_wgpuQueue, &wgpuImageCopyTexture, nullptr, 0, &wgpuTextureDataLayout, &wgpuCopySize); + */ +} + +void DeviceContextWebGPUImpl::CopyTexture(const CopyTextureAttribs& CopyAttribs) +{ + TDeviceContextBase::CopyTexture(CopyAttribs); + + EndCommandEncoders(); + ClearEncoderState(); + + auto* pSrcTexWebGPU = ClassPtrCast(CopyAttribs.pSrcTexture); + auto* pDstTexWebGPU = ClassPtrCast(CopyAttribs.pDstTexture); + + const auto& SrcTexDesc = pSrcTexWebGPU->GetDesc(); + const auto& DstTexDesc = pDstTexWebGPU->GetDesc(); + + auto wgpuCmdEncoder = GetCommandEncoder(); + + auto FullMipBox = Box{}; + auto* pSrcBox = CopyAttribs.pSrcBox; + + if (pSrcBox == nullptr) + { + auto MipLevelAttribs = GetMipLevelProperties(SrcTexDesc, CopyAttribs.SrcMipLevel); + FullMipBox.MaxX = MipLevelAttribs.LogicalWidth; + FullMipBox.MaxY = MipLevelAttribs.LogicalHeight; + FullMipBox.MaxZ = MipLevelAttribs.Depth; + pSrcBox = &FullMipBox; + } + + if (SrcTexDesc.Usage != USAGE_STAGING && DstTexDesc.Usage != USAGE_STAGING) + { + const auto& DstFmtAttribs = GetTextureFormatAttribs(DstTexDesc.Format); + + WGPUTextureAspect wgpuAspectMask = {}; + if (DstFmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH) + wgpuAspectMask = WGPUTextureAspect_DepthOnly; + else + wgpuAspectMask = WGPUTextureAspect_All; + + WGPUImageCopyTexture wgpuImageCopySrc{}; + wgpuImageCopySrc.texture = pDstTexWebGPU->GetWebGPUTexture(); + wgpuImageCopySrc.aspect = wgpuAspectMask; + wgpuImageCopySrc.origin.x = pSrcBox->MinX; + wgpuImageCopySrc.origin.y = pSrcBox->MinY; + wgpuImageCopySrc.origin.z = pSrcBox->MinZ; + wgpuImageCopySrc.mipLevel = CopyAttribs.SrcMipLevel; + + WGPUImageCopyTexture wgpuImageCopyDst{}; + wgpuImageCopyDst.texture = pDstTexWebGPU->GetWebGPUTexture(); + wgpuImageCopyDst.aspect = wgpuAspectMask; + wgpuImageCopyDst.origin.x = CopyAttribs.DstX; + wgpuImageCopyDst.origin.y = CopyAttribs.DstY; + wgpuImageCopyDst.origin.z = CopyAttribs.DstZ; + wgpuImageCopyDst.mipLevel = CopyAttribs.DstMipLevel; + + WGPUExtent3D wgpuCopySize{}; + wgpuCopySize.width = std::max(pSrcBox->Width(), 1u); + wgpuCopySize.height = std::max(pSrcBox->Height(), 1u); + wgpuCopySize.depthOrArrayLayers = std::max(pSrcBox->Depth(), 1u); + + wgpuCommandEncoderCopyTextureToTexture(wgpuCmdEncoder, &wgpuImageCopySrc, &wgpuImageCopyDst, &wgpuCopySize); + } + else if (SrcTexDesc.Usage == USAGE_STAGING && DstTexDesc.Usage != USAGE_STAGING) + { + const auto SrcBufferOffset = GetStagingTextureLocationOffset(SrcTexDesc, CopyAttribs.SrcSlice, CopyAttribs.SrcMipLevel, TextureWebGPUImpl::StagingDataAlignment, pSrcBox->MinX, pSrcBox->MinY, pSrcBox->MinZ); + + const auto SrcMipLevelAttribs = GetMipLevelProperties(SrcTexDesc, CopyAttribs.SrcMipLevel); + + const auto& DstFmtAttribs = GetTextureFormatAttribs(DstTexDesc.Format); + + WGPUTextureAspect wgpuAspectMask = {}; + if (DstFmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH) + wgpuAspectMask = WGPUTextureAspect_DepthOnly; + else + wgpuAspectMask = WGPUTextureAspect_All; + + WGPUImageCopyBuffer wgpuImageCopySrc{}; + wgpuImageCopySrc.buffer = pSrcTexWebGPU->GetWebGPUStagingBuffer(); + wgpuImageCopySrc.layout.offset = SrcBufferOffset; + wgpuImageCopySrc.layout.bytesPerRow = static_cast(SrcMipLevelAttribs.RowSize); + wgpuImageCopySrc.layout.rowsPerImage = SrcMipLevelAttribs.StorageHeight / DstFmtAttribs.BlockHeight; + + WGPUImageCopyTexture wgpuImageCopyDst{}; + wgpuImageCopyDst.texture = pDstTexWebGPU->GetWebGPUTexture(); + wgpuImageCopyDst.aspect = wgpuAspectMask; + wgpuImageCopyDst.origin.x = CopyAttribs.DstX; + wgpuImageCopyDst.origin.y = CopyAttribs.DstY; + wgpuImageCopyDst.origin.z = CopyAttribs.DstZ; + wgpuImageCopyDst.mipLevel = CopyAttribs.DstMipLevel; + + WGPUExtent3D wgpuCopySize{}; + wgpuCopySize.width = std::max(pSrcBox->Width(), 1u); + wgpuCopySize.height = std::max(pSrcBox->Height(), 1u); + wgpuCopySize.depthOrArrayLayers = std::max(pSrcBox->Depth(), 1u); + + wgpuCommandEncoderCopyBufferToTexture(wgpuCmdEncoder, &wgpuImageCopySrc, &wgpuImageCopyDst, &wgpuCopySize); + } + else if (SrcTexDesc.Usage != USAGE_STAGING && DstTexDesc.Usage == USAGE_STAGING) + { + const auto DstBufferOffset = GetStagingTextureLocationOffset(DstTexDesc, CopyAttribs.DstSlice, CopyAttribs.DstMipLevel, TextureWebGPUImpl::StagingDataAlignment, CopyAttribs.DstX, CopyAttribs.DstY, CopyAttribs.DstZ); + + const auto DstMipLevelAttribs = GetMipLevelProperties(DstTexDesc, CopyAttribs.DstMipLevel); + + const auto& SrcFmtAttribs = GetTextureFormatAttribs(SrcTexDesc.Format); + + WGPUTextureAspect wgpuAspectMask = {}; + if (SrcFmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH) + wgpuAspectMask = WGPUTextureAspect_DepthOnly; + else + wgpuAspectMask = WGPUTextureAspect_All; + + WGPUImageCopyTexture wgpuImageCopySrc{}; + wgpuImageCopySrc.texture = pSrcTexWebGPU->GetWebGPUTexture(); + wgpuImageCopySrc.aspect = wgpuAspectMask; + wgpuImageCopySrc.origin.x = pSrcBox->MinX; + wgpuImageCopySrc.origin.y = pSrcBox->MinY; + wgpuImageCopySrc.origin.z = pSrcBox->MinZ; + wgpuImageCopySrc.mipLevel = CopyAttribs.SrcMipLevel; + + WGPUImageCopyBuffer wgpuImageCopyDst{}; + wgpuImageCopyDst.buffer = pDstTexWebGPU->GetWebGPUStagingBuffer(); + wgpuImageCopyDst.layout.offset = DstBufferOffset; + wgpuImageCopyDst.layout.bytesPerRow = static_cast(DstMipLevelAttribs.RowSize); + wgpuImageCopyDst.layout.rowsPerImage = DstMipLevelAttribs.StorageHeight / SrcFmtAttribs.BlockHeight; + + WGPUExtent3D wgpuCopySize{}; + wgpuCopySize.width = std::max(pSrcBox->Width(), 1u); + wgpuCopySize.height = std::max(pSrcBox->Height(), 1u); + wgpuCopySize.depthOrArrayLayers = std::max(pSrcBox->Depth(), 1u); + + wgpuCommandEncoderCopyTextureToBuffer(wgpuCmdEncoder, &wgpuImageCopySrc, &wgpuImageCopyDst, &wgpuCopySize); + } + else + { + UNSUPPORTED("Copying data between staging textures is not supported and is likely not want you really want to do"); + } +} + +void DeviceContextWebGPUImpl::MapTextureSubresource(ITexture* pTexture, + Uint32 MipLevel, + Uint32 ArraySlice, + MAP_TYPE MapType, + MAP_FLAGS MapFlags, + const Box* pMapRegion, + MappedTextureSubresource& MappedData) +{ + TDeviceContextBase::MapTextureSubresource(pTexture, MipLevel, ArraySlice, MapType, MapFlags, pMapRegion, MappedData); + //TODO +} + +void DeviceContextWebGPUImpl::UnmapTextureSubresource(ITexture* pTexture, Uint32 MipLevel, Uint32 ArraySlice) +{ + TDeviceContextBase::UnmapTextureSubresource(pTexture, MipLevel, ArraySlice); + //TODO +} + +void DeviceContextWebGPUImpl::FinishCommandList(ICommandList** ppCommandList) +{ + LOG_ERROR("Deferred contexts are not supported in WebGPU"); +} + +void DeviceContextWebGPUImpl::ExecuteCommandLists(Uint32 NumCommandLists, ICommandList* const* ppCommandLists) +{ + LOG_ERROR("Deferred contexts are not supported in WebGPU"); +} + +void DeviceContextWebGPUImpl::EnqueueSignal(IFence* pFence, Uint64 Value) +{ + TDeviceContextBase::EnqueueSignal(pFence, Value, 0); + m_SignalFences.emplace_back(std::make_pair(Value, ClassPtrCast(pFence))); +} + +void DeviceContextWebGPUImpl::DeviceWaitForFence(IFence* pFence, Uint64 Value) +{ + DEV_ERROR("DeviceWaitForFence() is not supported in WebGPU"); +} + +void DeviceContextWebGPUImpl::WaitForIdle() +{ + Flush(); + //TODO +} + +void DeviceContextWebGPUImpl::BeginQuery(IQuery* pQuery) +{ + TDeviceContextBase::BeginQuery(pQuery, 0); + + // auto* pQueryWebGPUImpl = ClassPtrCast(pQuery); + // auto QueryType = pQueryWebGPUImpl->GetDesc().Type; + + //TODO +} + +void DeviceContextWebGPUImpl::EndQuery(IQuery* pQuery) +{ + TDeviceContextBase::EndQuery(pQuery, 0); + + // auto* pQueryWebGPUImpl = ClassPtrCast(pQuery); + // auto QueryType = pQueryWebGPUImpl->GetDesc().Type; + + //TODO +} + +void DeviceContextWebGPUImpl::Flush() +{ + EndCommandEncoders(); + + for (auto& Page : m_SharedMemPages) + wgpuQueueWriteBuffer(m_wgpuQueue, Page.wgpuBuffer.Get(), 0, Page.pData, Page.PageSize); + + if (m_wgpuCommandEncoder || !m_SignalFences.empty()) + { + for (auto& SignalItem : m_SignalFences) + { + auto* pFence = SignalItem.second.RawPtr(); + pFence->AddPendingSignal(GetCommandEncoder(), SignalItem.first); + } + m_SignalFences.clear(); + + WGPUCommandBufferDescriptor wgpuCmdBufferDesc{}; + WGPUCommandBuffer wgpuCmdBuffer = wgpuCommandEncoderFinish(GetCommandEncoder(), &wgpuCmdBufferDesc); + DEV_CHECK_ERR(wgpuCmdBuffer != nullptr, "Failed to finish command encoder"); + wgpuQueueSubmit(m_wgpuQueue, 1, &wgpuCmdBuffer); + m_wgpuCommandEncoder = nullptr; + } +} + +void DeviceContextWebGPUImpl::BuildBLAS(const BuildBLASAttribs& Attribs) +{ + UNSUPPORTED("BuildBLAS is not supported in WebGPU"); +} + +void DeviceContextWebGPUImpl::BuildTLAS(const BuildTLASAttribs& Attribs) +{ + UNSUPPORTED("BuildTLAS is not supported in WebGPU"); +} + +void DeviceContextWebGPUImpl::CopyBLAS(const CopyBLASAttribs& Attribs) +{ + UNSUPPORTED("CopyBLAS is not supported in WebGPU"); +} + +void DeviceContextWebGPUImpl::CopyTLAS(const CopyTLASAttribs& Attribs) +{ + UNSUPPORTED("CopyTLAS is not supported in WebGPU"); +} + +void DeviceContextWebGPUImpl::WriteBLASCompactedSize(const WriteBLASCompactedSizeAttribs& Attribs) +{ + UNSUPPORTED("WriteBLASCompactedSize is not supported in WebGPU"); +} + +void DeviceContextWebGPUImpl::WriteTLASCompactedSize(const WriteTLASCompactedSizeAttribs& Attribs) +{ + UNSUPPORTED("WriteTLASCompactedSize is not supported in WebGPU"); +} + +void DeviceContextWebGPUImpl::TraceRays(const TraceRaysAttribs& Attribs) +{ + UNSUPPORTED("TraceRays is not supported in WebGPU"); +} + +void DeviceContextWebGPUImpl::TraceRaysIndirect(const TraceRaysIndirectAttribs& Attribs) +{ + UNSUPPORTED("TraceRaysIndirect is not supported in WebGPU"); +} + +void DeviceContextWebGPUImpl::UpdateSBT(IShaderBindingTable* pSBT, + const UpdateIndirectRTBufferAttribs* pUpdateIndirectBufferAttribs) +{ + UNSUPPORTED("UpdateSBT is not supported in WebGPU"); +} + +void DeviceContextWebGPUImpl::SetShadingRate(SHADING_RATE BaseRate, + SHADING_RATE_COMBINER PrimitiveCombiner, + SHADING_RATE_COMBINER TextureCombiner) +{ + UNSUPPORTED("SetShadingRate is not supported in WebGPU"); +} + +void DeviceContextWebGPUImpl::BindSparseResourceMemory(const BindSparseResourceMemoryAttribs& Attribs) +{ + UNSUPPORTED("BindSparseResourceMemory is not supported in WebGPU"); +} + +void DeviceContextWebGPUImpl::BeginDebugGroup(const Char* Name, const float* pColor) +{ + VERIFY(!m_wgpuRenderPassEncoder && !m_wgpuComputePassEncoder, "Another command encoder is currently active"); + TDeviceContextBase::BeginDebugGroup(Name, pColor, 0); + + if (m_wgpuRenderPassEncoder) + wgpuRenderPassEncoderPushDebugGroup(GetRenderPassCommandEncoder(), Name); + else if (m_wgpuComputePassEncoder) + wgpuComputePassEncoderPushDebugGroup(GetComputePassCommandEncoder(), Name); + else + wgpuCommandEncoderPushDebugGroup(GetCommandEncoder(), Name); +} + +void DeviceContextWebGPUImpl::EndDebugGroup() +{ + VERIFY(!m_wgpuRenderPassEncoder && !m_wgpuComputePassEncoder, "Another command encoder is currently active"); + TDeviceContextBase::EndDebugGroup(0); + + if (m_wgpuRenderPassEncoder) + wgpuRenderPassEncoderPopDebugGroup(GetRenderPassCommandEncoder()); + else if (m_wgpuComputePassEncoder) + wgpuComputePassEncoderPopDebugGroup(GetComputePassCommandEncoder()); + else + wgpuCommandEncoderPopDebugGroup(GetCommandEncoder()); +} + +void DeviceContextWebGPUImpl::InsertDebugLabel(const Char* Label, const float* pColor) +{ + VERIFY(!m_wgpuRenderPassEncoder && !m_wgpuComputePassEncoder, "Another command encoder is currently active"); + TDeviceContextBase::InsertDebugLabel(Label, pColor, 0); + + if (m_wgpuRenderPassEncoder) + wgpuRenderPassEncoderInsertDebugMarker(GetRenderPassCommandEncoder(), Label); + else if (m_wgpuComputePassEncoder) + wgpuComputePassEncoderInsertDebugMarker(GetComputePassCommandEncoder(), Label); + else + wgpuCommandEncoderInsertDebugMarker(GetCommandEncoder(), Label); +} + +void DeviceContextWebGPUImpl::GenerateMips(ITextureView* pTexView) {} + +void DeviceContextWebGPUImpl::FinishFrame() {} + +void DeviceContextWebGPUImpl::TransitionResourceStates(Uint32 BarrierCount, const StateTransitionDesc* pResourceBarriers) {} + +ICommandQueue* DeviceContextWebGPUImpl::LockCommandQueue() { return nullptr; } + +void DeviceContextWebGPUImpl::UnlockCommandQueue() {} + +void DeviceContextWebGPUImpl::ResolveTextureSubresource(ITexture* pSrcTexture, + ITexture* pDstTexture, + const ResolveTextureSubresourceAttribs& ResolveAttribs) +{ + TDeviceContextBase::ResolveTextureSubresource(pSrcTexture, pDstTexture, ResolveAttribs); + +#ifdef DILIGENT_DEVELOPMENT + LOG_WARNING_MESSAGE_ONCE("ResolveTextureSubresource is suboptimal in WebGPU. Use render pass resolve attachments instead"); +#endif + + EndCommandEncoders(); + + const auto& SrcTexDesc = pSrcTexture->GetDesc(); + const auto& FmtAttribs = GetTextureFormatAttribs(SrcTexDesc.Format); + + if (FmtAttribs.ComponentType != COMPONENT_TYPE_DEPTH && FmtAttribs.ComponentType != COMPONENT_TYPE_DEPTH_STENCIL) + { + const auto* pSrcRTVWebGPU = ClassPtrCast(pSrcTexture->GetDefaultView(TEXTURE_VIEW_RENDER_TARGET)); + const auto* pDstRTVWebGPU = ClassPtrCast(pDstTexture->GetDefaultView(TEXTURE_VIEW_RENDER_TARGET)); + + WGPURenderPassDescriptor wgpuRenderPassDesc{}; + WGPURenderPassColorAttachment wgpuRenderPassColorAttachment{}; + + wgpuRenderPassColorAttachment.loadOp = WGPULoadOp_Load; + wgpuRenderPassColorAttachment.storeOp = WGPUStoreOp_Discard; + wgpuRenderPassColorAttachment.view = pSrcRTVWebGPU->GetWebGPUTextureView(); + wgpuRenderPassColorAttachment.resolveTarget = pDstRTVWebGPU->GetWebGPUTextureView(); + + wgpuRenderPassDesc.colorAttachmentCount = 1; + wgpuRenderPassDesc.colorAttachments = &wgpuRenderPassColorAttachment; + + WGPURenderPassEncoder wgpuRenderPassEncoder = wgpuCommandEncoderBeginRenderPass(GetCommandEncoder(), &wgpuRenderPassDesc); + DEV_CHECK_ERR(wgpuRenderPassEncoder != nullptr, "Failed to begin render pass"); + wgpuRenderPassEncoderEnd(wgpuRenderPassEncoder); + } + else + { + LOG_ERROR_MESSAGE("ResolveTextureSubresource is not supported for the depth attachment"); + } +} + +WGPUQueue DeviceContextWebGPUImpl::GetWebGPUQueue() +{ + return m_wgpuQueue; +} + +WGPUCommandEncoder DeviceContextWebGPUImpl::GetCommandEncoder() +{ + if (!m_wgpuCommandEncoder) + { + WGPUCommandEncoderDescriptor wgpuCommandEncoderDesc{}; + m_wgpuCommandEncoder = wgpuDeviceCreateCommandEncoder(m_pDevice->GetWebGPUDevice(), &wgpuCommandEncoderDesc); + DEV_CHECK_ERR(m_wgpuCommandEncoder != nullptr, "Failed wgpuDeviceCreateCommandEncoder"); + } + + return m_wgpuCommandEncoder; +} + +WGPURenderPassEncoder DeviceContextWebGPUImpl::GetRenderPassCommandEncoder() +{ + if (!m_wgpuRenderPassEncoder) + EndCommandEncoders(COMMAND_ENCODER_FLAG_ALL & ~COMMAND_ENCODER_FLAG_RENDER); + + return m_wgpuRenderPassEncoder; +} + +WGPUComputePassEncoder DeviceContextWebGPUImpl::GetComputePassCommandEncoder() +{ + if (!m_wgpuComputePassEncoder) + EndCommandEncoders(COMMAND_ENCODER_FLAG_ALL & ~COMMAND_ENCODER_FLAG_COMPUTE); + + return m_wgpuComputePassEncoder; +} + +void DeviceContextWebGPUImpl::EndCommandEncoders(Uint32 EncoderFlags) +{ + if ((EncoderFlags & COMMAND_ENCODER_FLAG_RENDER) != 0) + { + if (m_PendingClears.AnyPending()) + { + VERIFY(!m_wgpuRenderPassEncoder, "There should be no active render command encoder when pending clears mask is not zero"); + VERIFY(!m_pActiveRenderPass, "There should be no pending clears inside a render pass"); + CommitRenderTargets(); + } + + if (m_wgpuRenderPassEncoder) + { + wgpuRenderPassEncoderEnd(m_wgpuRenderPassEncoder); + m_wgpuRenderPassEncoder = nullptr; + ClearEncoderState(); + } + } + + if ((EncoderFlags & COMMAND_ENCODER_FLAG_COMPUTE) != 0) + { + if (m_wgpuComputePassEncoder) + { + wgpuComputePassEncoderEnd(m_wgpuComputePassEncoder); + m_wgpuComputePassEncoder = nullptr; + ClearEncoderState(); + } + } +} + +void DeviceContextWebGPUImpl::CommitRenderTargets() +{ + VERIFY(!m_wgpuRenderPassEncoder && !m_wgpuComputePassEncoder, "Another command encoder is currently active"); + + WGPURenderPassDescriptor wgpuRenderPassDesc{}; + WGPURenderPassColorAttachment wgpuRenderPassColorAttachments[MAX_RENDER_TARGETS]{}; + WGPURenderPassDepthStencilAttachment wgpuRenderPassDepthStencilAttachment{}; + for (Uint32 RTIndex = 0; RTIndex < m_NumBoundRenderTargets; ++RTIndex) + { + if (auto* pRTV = m_pBoundRenderTargets[RTIndex].RawPtr()) + { + const auto& ClearColor = m_PendingClears.Colors[RTIndex]; + + wgpuRenderPassColorAttachments[RTIndex].view = pRTV->GetWebGPUTextureView(); + wgpuRenderPassColorAttachments[RTIndex].storeOp = WGPUStoreOp_Store; + wgpuRenderPassColorAttachments[RTIndex].loadOp = m_PendingClears.ColorPending(RTIndex) ? WGPULoadOp_Clear : WGPULoadOp_Load; + wgpuRenderPassColorAttachments[RTIndex].clearValue = WGPUColor{ClearColor[0], ClearColor[1], ClearColor[2], ClearColor[3]}; + } + + wgpuRenderPassDesc.colorAttachments = wgpuRenderPassColorAttachments; + wgpuRenderPassDesc.colorAttachmentCount = m_NumBoundRenderTargets; + } + + if (m_pBoundDepthStencil) + { + wgpuRenderPassDepthStencilAttachment.view = m_pBoundDepthStencil->GetWebGPUTextureView(); + wgpuRenderPassDepthStencilAttachment.depthLoadOp = m_PendingClears.DepthPending() ? WGPULoadOp_Clear : WGPULoadOp_Load; + wgpuRenderPassDepthStencilAttachment.depthStoreOp = WGPUStoreOp_Store; + wgpuRenderPassDepthStencilAttachment.depthClearValue = m_PendingClears.Depth; + + wgpuRenderPassDepthStencilAttachment.stencilLoadOp = m_PendingClears.StencilPending() ? WGPULoadOp_Clear : WGPULoadOp_Load; + wgpuRenderPassDepthStencilAttachment.stencilStoreOp = WGPUStoreOp_Store; + wgpuRenderPassDepthStencilAttachment.stencilClearValue = m_PendingClears.Stencil; + + wgpuRenderPassDesc.depthStencilAttachment = &wgpuRenderPassDepthStencilAttachment; + } + + m_wgpuRenderPassEncoder = wgpuCommandEncoderBeginRenderPass(GetCommandEncoder(), &wgpuRenderPassDesc); + DEV_CHECK_ERR(m_wgpuRenderPassEncoder != nullptr, "Failed to begin render pass"); + m_PendingClears.ResetFlags(); +} + +void DeviceContextWebGPUImpl::BeginSubpass() +{ + //TODO + /* + VERIFY_EXPR(m_pActiveRenderPass); + VERIFY_EXPR(m_pBoundFramebuffer); + + Uint32 FirstSubpass = m_SubpassIndex; + Uint32 NumMergedSubpasses = 1; + m_pActiveRenderPass->GetSubpass(m_SubpassIndex, FirstSubpass, NumMergedSubpasses); + if (m_SubpassIndex != FirstSubpass) + return; // Inside a merged subpass + + EndCommandEncoders(); + + const auto& RPDesc = m_pActiveRenderPass->GetDesc(); + VERIFY_EXPR(m_SubpassIndex < RPDesc.SubpassCount); + */ +} + +void DeviceContextWebGPUImpl::ClearEncoderState() +{ + m_EncoderState.Clear(); +} + +void DeviceContextWebGPUImpl::ClearAttachment(Int32 RTIndex, + COLOR_MASK ColorMask, + CLEAR_DEPTH_STENCIL_FLAGS DSFlags, + const float ClearData[], + Uint8 Stencil) +{ + VERIFY_EXPR(m_wgpuRenderPassEncoder); + + auto& AttachmentCleaner = m_pDevice->GetAttachmentCleaner(); + + AttachmentCleanerWebGPU::RenderPassInfo RPInfo{}; + RPInfo.NumRenderTargets = m_NumBoundRenderTargets; + RPInfo.SampleCount = static_cast(m_FramebufferSamples); + for (Uint32 RTIdx = 0; RTIdx < RPInfo.NumRenderTargets; ++RTIdx) + RPInfo.RTVFormats[RTIdx] = m_pBoundRenderTargets[RTIdx] ? m_pBoundRenderTargets[RTIndex]->GetDesc().Format : TEX_FORMAT_UNKNOWN; + RPInfo.DSVFormat = m_pBoundDepthStencil ? m_pBoundDepthStencil->GetDesc().Format : TEX_FORMAT_UNKNOWN; + + const Viewport VP{0, 0, static_cast(m_FramebufferWidth), static_cast(m_FramebufferHeight), 0, 1}; + if (VP != m_EncoderState.Viewports[0]) + { + m_EncoderState.Viewports[0] = VP; + wgpuRenderPassEncoderSetViewport(m_wgpuRenderPassEncoder, VP.TopLeftX, VP.TopLeftY, VP.Width, VP.Height, VP.MinDepth, VP.MaxDepth); + m_EncoderState.Invalidate(WebGPUEncoderState::CMD_ENCODER_STATE_VIEWPORTS); + } + + const Rect SR{0, 0, static_cast(m_FramebufferWidth), static_cast(m_FramebufferHeight)}; + if (SR != m_EncoderState.ScissorRects[0]) + { + m_EncoderState.ScissorRects[0] = SR; + wgpuRenderPassEncoderSetScissorRect(m_wgpuRenderPassEncoder, SR.left, SR.top, SR.right, SR.bottom); + m_EncoderState.Invalidate(WebGPUEncoderState::CMD_ENCODER_STATE_SCISSOR_RECTS); + } + + if (RTIndex >= 0) + AttachmentCleaner.ClearColor(m_wgpuRenderPassEncoder, RPInfo, ColorMask, RTIndex, ClearData); + else + { + AttachmentCleaner.ClearDepthStencil(m_wgpuRenderPassEncoder, RPInfo, DSFlags, ClearData[0], Stencil); + if ((DSFlags & CLEAR_STENCIL_FLAG) != 0) + m_EncoderState.Invalidate(WebGPUEncoderState::CMD_ENCODER_STATE_STENCIL_REF); + } + + m_EncoderState.Invalidate(WebGPUEncoderState::CMD_ENCODER_STATE_PIPELINE_STATE); +} + +SharedMemoryManagerWebGPU::Allocation DeviceContextWebGPUImpl::AllocateSharedMemory(Uint64 Size, Uint64 Alignment) +{ + SharedMemoryManagerWebGPU::Allocation Alloc; + if (!m_SharedMemPages.empty()) + Alloc = m_SharedMemPages.back().Allocate(Size, Alignment); + + if (Alloc.IsEmpty()) + { + m_SharedMemPages.emplace_back(m_pDevice->GetSharedMemoryPage(Size)); + Alloc = m_SharedMemPages.back().Allocate(Size, Alignment); + } + + VERIFY_EXPR(!Alloc.IsEmpty()); + return Alloc; +} + +QueryManagerWebGPU& DeviceContextWebGPUImpl::GetQueryManager() +{ + return *m_pQueryMgr; +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/src/EngineFactoryWebGPU.cpp b/Graphics/GraphicsEngineWebGPU/src/EngineFactoryWebGPU.cpp new file mode 100644 index 0000000000..16f7d47647 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/src/EngineFactoryWebGPU.cpp @@ -0,0 +1,526 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * You may not use this file except in compliance with the License (see License.txt). + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +/// \file +/// Routines that initialize WebGPU-based engine implementation + +#include "pch.h" + +#include "WebGPUObjectWrappers.hpp" + +#include "EngineFactoryBase.hpp" +#include "EngineFactoryWebGPU.h" + +#include "DeviceContextWebGPUImpl.hpp" +#include "RenderDeviceWebGPUImpl.hpp" +#include "SwapChainWebGPUImpl.hpp" +#include "RenderPassWebGPUImpl.hpp" +#include "TextureWebGPUImpl.hpp" +#include "PipelineStateWebGPUImpl.hpp" +#include "PipelineResourceSignatureWebGPUImpl.hpp" +#include "PipelineResourceAttribsWebGPU.hpp" +#include "ShaderResourceCacheWebGPU.hpp" +#include "FenceWebGPUImpl.hpp" + +#include "StringTools.hpp" +#include "GraphicsAccessories.hpp" + +namespace Diligent +{ + +/// Engine factory for WebGPU implementation +class EngineFactoryWebGPUImpl final : public EngineFactoryBase +{ +public: + static EngineFactoryWebGPUImpl* GetInstance() + { + static EngineFactoryWebGPUImpl TheFactory; + return &TheFactory; + } + + using TBase = EngineFactoryBase; + + EngineFactoryWebGPUImpl() : + TBase{IID_EngineFactoryWebGPU} + {} + + void DILIGENT_CALL_TYPE EnumerateAdapters(Version MinVersion, + Uint32& NumAdapters, + GraphicsAdapterInfo* Adapters) const override; + + void DILIGENT_CALL_TYPE CreateDearchiver(const DearchiverCreateInfo& CreateInfo, + IDearchiver** ppDearchiver) const override; + + void DILIGENT_CALL_TYPE CreateDeviceAndContextsWebGPU(const EngineWebGPUCreateInfo& EngineCI, + IRenderDevice** ppDevice, + IDeviceContext** ppContexts) override; + + void DILIGENT_CALL_TYPE CreateSwapChainWebGPU(IRenderDevice* pDevice, + IDeviceContext* pImmediateContext, + const SwapChainDesc& SCDesc, + const NativeWindow& Window, + ISwapChain** ppSwapChain) override; + + void DILIGENT_CALL_TYPE AttachToWebGPUDevice(WGPUInstance wgpuInstance, + WGPUAdapter wgpuAdapter, + WGPUDevice wgpuDevice, + const EngineWebGPUCreateInfo& EngineCI, + IRenderDevice** ppDevice, + IDeviceContext** ppContexts); +}; + +static std::vector FindCompatibleAdapters(WGPUInstance wgpuInstance, Version MinVersion) +{ + std::vector wgpuAdapters; + + struct CallbackUserData + { + WGPUAdapter Adapter = nullptr; + WGPURequestAdapterStatus RequestStatus = {}; + String Message = {}; + }; + + auto OnAdapterRequestEnded = [](WGPURequestAdapterStatus Status, WGPUAdapter Adapter, char const* Message, void* pCallbackUserData) { + auto* pUserData = static_cast(pCallbackUserData); + pUserData->Adapter = Adapter; + pUserData->RequestStatus = Status; + if (Message != nullptr) + pUserData->Message = Message; + }; + + WGPUPowerPreference PowerPreferences[] = { + WGPUPowerPreference_HighPerformance, + WGPUPowerPreference_LowPower}; + + for (const auto& powerPreference : PowerPreferences) + { + CallbackUserData UserData{}; + WGPURequestAdapterOptions Options{nullptr, nullptr, powerPreference, false}; + wgpuInstanceRequestAdapter( + wgpuInstance, + &Options, + OnAdapterRequestEnded, + &UserData); + + if (UserData.RequestStatus == WGPURequestAdapterStatus_Success) + { + auto IsFound = std::find_if(wgpuAdapters.begin(), wgpuAdapters.end(), + [&](const auto& wgpuAdapter) { return wgpuAdapter.Get() == UserData.Adapter; }); + + if (IsFound == wgpuAdapters.end()) + wgpuAdapters.emplace_back(UserData.Adapter); + } + else + { + LOG_WARNING_MESSAGE(UserData.Message); + } + } + + return wgpuAdapters; +} + +static WebGPUDeviceWrapper CreateDeviceForAdapter(EngineWebGPUCreateInfo const& EngineCI, WGPUAdapter Adapter) +{ + struct CallbackUserData + { + WGPUDevice Device = nullptr; + WGPURequestDeviceStatus RequestStatus = {}; + String Message = {}; + } UserData; + + auto OnDeviceRequestEnded = [](WGPURequestDeviceStatus Status, WGPUDevice Device, char const* Message, void* pCallbackUserData) { + auto* pUserData = static_cast(pCallbackUserData); + pUserData->Device = Device; + pUserData->RequestStatus = Status; + if (Message != nullptr) + pUserData->Message = Message; + }; + + WGPUSupportedLimits SupportedLimits{}; + wgpuAdapterGetLimits(Adapter, &SupportedLimits); + + std::vector Features{}; + { + if (EngineCI.Features.DepthBiasClamp) + Features.push_back(WGPUFeatureName_DepthClipControl); + + if (EngineCI.Features.TimestampQueries) + Features.push_back(WGPUFeatureName_TimestampQuery); + + if (EngineCI.Features.PipelineStatisticsQueries) + Features.push_back(WGPUFeatureName_PipelineStatisticsQuery); + + if (EngineCI.Features.TextureCompressionBC) + Features.push_back(WGPUFeatureName_TextureCompressionBC); + + if (EngineCI.Features.ShaderFloat16) + Features.push_back(WGPUFeatureName_ShaderF16); + } + + WGPURequiredLimits RequiredLimits{nullptr, SupportedLimits.limits}; + WGPUDeviceDescriptor DeviceDesc{}; + DeviceDesc.requiredLimits = &RequiredLimits; + DeviceDesc.requiredFeaturesCount = static_cast(Features.size()); + DeviceDesc.requiredFeatures = Features.data(); + + wgpuAdapterRequestDevice( + Adapter, + &DeviceDesc, + OnDeviceRequestEnded, + &UserData); + + if (UserData.RequestStatus != WGPURequestDeviceStatus_Success) + LOG_ERROR_AND_THROW(UserData.Message); + + return WebGPUDeviceWrapper{UserData.Device}; +} + +static GraphicsAdapterInfo GetGraphicsAdapterInfo(WGPUAdapter wgpuAdapter) +{ + WGPUAdapterProperties wgpuAdapterDesc{}; + wgpuAdapterGetProperties(wgpuAdapter, &wgpuAdapterDesc); + + GraphicsAdapterInfo AdapterInfo{}; + + // Set graphics adapter properties + { + auto ConvertWPUAdapterType = [](WGPUAdapterType Type) -> ADAPTER_TYPE { + switch (Type) + { + case WGPUAdapterType_CPU: + return ADAPTER_TYPE_SOFTWARE; + case WGPUAdapterType_DiscreteGPU: + return ADAPTER_TYPE_DISCRETE; + case WGPUAdapterType_IntegratedGPU: + return ADAPTER_TYPE_INTEGRATED; + default: + return ADAPTER_TYPE_UNKNOWN; + } + }; + + const auto DescriptorSize = std::min(_countof(AdapterInfo.Description), strlen(wgpuAdapterDesc.name)); + memcpy(AdapterInfo.Description, wgpuAdapterDesc.name, DescriptorSize); + AdapterInfo.Type = ConvertWPUAdapterType(wgpuAdapterDesc.adapterType); + AdapterInfo.Vendor = VendorIdToAdapterVendor(wgpuAdapterDesc.vendorID); + AdapterInfo.VendorId = wgpuAdapterDesc.vendorID; + AdapterInfo.DeviceId = wgpuAdapterDesc.deviceID; + AdapterInfo.NumOutputs = 0; + } + + // Enable features + { + //TODO + auto& Features{AdapterInfo.Features}; + Features.SeparablePrograms = DEVICE_FEATURE_STATE_ENABLED; + Features.ShaderResourceQueries = DEVICE_FEATURE_STATE_ENABLED; + Features.WireframeFill = DEVICE_FEATURE_STATE_ENABLED; + Features.ComputeShaders = DEVICE_FEATURE_STATE_ENABLED; + Features.OcclusionQueries = DEVICE_FEATURE_STATE_ENABLED; + Features.BinaryOcclusionQueries = DEVICE_FEATURE_STATE_ENABLED; + Features.DurationQueries = DEVICE_FEATURE_STATE_ENABLED; + Features.DepthBiasClamp = DEVICE_FEATURE_STATE_ENABLED; + Features.IndependentBlend = DEVICE_FEATURE_STATE_ENABLED; + Features.DualSourceBlend = DEVICE_FEATURE_STATE_ENABLED; + Features.MultiViewport = DEVICE_FEATURE_STATE_ENABLED; + Features.PixelUAVWritesAndAtomics = DEVICE_FEATURE_STATE_ENABLED; + Features.TextureUAVExtendedFormats = DEVICE_FEATURE_STATE_ENABLED; + Features.InstanceDataStepRate = DEVICE_FEATURE_STATE_ENABLED; + Features.DepthClamp = DEVICE_FEATURE_STATE_ENABLED; + + if (wgpuAdapterHasFeature(wgpuAdapter, WGPUFeatureName_DepthClipControl)) + Features.DepthBiasClamp = DEVICE_FEATURE_STATE_ENABLED; + + if (wgpuAdapterHasFeature(wgpuAdapter, WGPUFeatureName_TimestampQuery)) + Features.TimestampQueries = DEVICE_FEATURE_STATE_ENABLED; + + if (wgpuAdapterHasFeature(wgpuAdapter, WGPUFeatureName_PipelineStatisticsQuery)) + Features.PipelineStatisticsQueries = DEVICE_FEATURE_STATE_ENABLED; + + if (wgpuAdapterHasFeature(wgpuAdapter, WGPUFeatureName_TextureCompressionBC)) + Features.TextureCompressionBC = DEVICE_FEATURE_STATE_ENABLED; + + if (wgpuAdapterHasFeature(wgpuAdapter, WGPUFeatureName_ShaderF16)) + Features.ShaderFloat16 = DEVICE_FEATURE_STATE_ENABLED; + } + + WGPUSupportedLimits wgpuSupportedLimits{}; + wgpuAdapterGetLimits(wgpuAdapter, &wgpuSupportedLimits); + + // Set adapter memory info + { + auto& DrawCommandInfo = AdapterInfo.Memory; + DrawCommandInfo.UnifiedMemoryCPUAccess = CPU_ACCESS_NONE; + DrawCommandInfo.UnifiedMemory = 0; + } + + // Draw command properties + { + auto& DrawCommandInfo = AdapterInfo.DrawCommand; + DrawCommandInfo.MaxDrawIndirectCount = ~0u; + DrawCommandInfo.CapFlags = DRAW_COMMAND_CAP_FLAG_DRAW_INDIRECT; + + if (wgpuAdapterHasFeature(wgpuAdapter, WGPUFeatureName_IndirectFirstInstance)) + DrawCommandInfo.CapFlags |= DRAW_COMMAND_CAP_FLAG_DRAW_INDIRECT_FIRST_INSTANCE; + } + + // Set queue info + { + AdapterInfo.NumQueues = 1; + AdapterInfo.Queues[0].QueueType = COMMAND_QUEUE_TYPE_GRAPHICS; + AdapterInfo.Queues[0].MaxDeviceContexts = 1; + AdapterInfo.Queues[0].TextureCopyGranularity[0] = 1; + AdapterInfo.Queues[0].TextureCopyGranularity[1] = 1; + AdapterInfo.Queues[0].TextureCopyGranularity[2] = 1; + } + + // Set compute shader info + { + auto& ComputeShaderInfo = AdapterInfo.ComputeShader; + + ComputeShaderInfo.MaxThreadGroupSizeX = wgpuSupportedLimits.limits.maxComputeWorkgroupSizeX; + ComputeShaderInfo.MaxThreadGroupSizeY = wgpuSupportedLimits.limits.maxComputeWorkgroupSizeY; + ComputeShaderInfo.MaxThreadGroupSizeZ = wgpuSupportedLimits.limits.maxComputeWorkgroupSizeZ; + + ComputeShaderInfo.MaxThreadGroupCountX = wgpuSupportedLimits.limits.maxComputeWorkgroupsPerDimension; + ComputeShaderInfo.MaxThreadGroupCountY = wgpuSupportedLimits.limits.maxComputeWorkgroupsPerDimension; + ComputeShaderInfo.MaxThreadGroupCountZ = wgpuSupportedLimits.limits.maxComputeWorkgroupsPerDimension; + + ComputeShaderInfo.SharedMemorySize = wgpuSupportedLimits.limits.maxComputeWorkgroupStorageSize; + ComputeShaderInfo.MaxThreadGroupInvocations = wgpuSupportedLimits.limits.maxComputeInvocationsPerWorkgroup; + } + + // Set texture info + { + auto& TextureInfo = AdapterInfo.Texture; + + TextureInfo.MaxTexture1DDimension = wgpuSupportedLimits.limits.maxTextureDimension1D; + TextureInfo.MaxTexture2DDimension = wgpuSupportedLimits.limits.maxTextureDimension2D; + TextureInfo.MaxTexture3DDimension = wgpuSupportedLimits.limits.maxTextureDimension3D; + TextureInfo.MaxTexture1DArraySlices = 0; + TextureInfo.MaxTexture2DArraySlices = wgpuSupportedLimits.limits.maxTextureArrayLayers; + + TextureInfo.Texture2DMSSupported = True; + TextureInfo.Texture2DMSArraySupported = True; + TextureInfo.TextureViewSupported = True; + TextureInfo.CubemapArraysSupported = True; + TextureInfo.TextureView2DOn3DSupported = True; + } + + // Set buffer info + { + auto& BufferInfo = AdapterInfo.Buffer; + + BufferInfo.ConstantBufferOffsetAlignment = wgpuSupportedLimits.limits.minUniformBufferOffsetAlignment; + BufferInfo.StructuredBufferOffsetAlignment = wgpuSupportedLimits.limits.minStorageBufferOffsetAlignment; + } + + return AdapterInfo; +} + + +void EngineFactoryWebGPUImpl::EnumerateAdapters(Version MinVersion, + Uint32& NumAdapters, + GraphicsAdapterInfo* Adapters) const +{ + + WGPUInstanceDescriptor wgpuInstanceDesc = {}; + WebGPUInstanceWrapper wgpuInstance{wgpuCreateInstance(&wgpuInstanceDesc)}; + if (!wgpuInstance) + LOG_ERROR_AND_THROW("Failed to create WebGPU instance"); + + auto wgpuAdapters = FindCompatibleAdapters(wgpuInstance.Get(), MinVersion); + if (Adapters == nullptr) + NumAdapters = static_cast(wgpuAdapters.size()); + else + { + NumAdapters = (std::min)(NumAdapters, static_cast(wgpuAdapters.size())); + for (Uint32 AdapterId = 0; AdapterId < NumAdapters; ++AdapterId) + { + auto& wgpuAdapter = wgpuAdapters[AdapterId]; + Adapters[AdapterId] = GetGraphicsAdapterInfo(wgpuAdapter.Get()); + } + } +} + +void EngineFactoryWebGPUImpl::CreateDearchiver(const DearchiverCreateInfo& CreateInfo, + IDearchiver** ppDearchiver) const +{ + // TBase::CreateDearchiver(CreateInfo, ppDearchiver); +} + +void EngineFactoryWebGPUImpl::CreateDeviceAndContextsWebGPU(const EngineWebGPUCreateInfo& EngineCI, + IRenderDevice** ppDevice, + IDeviceContext** ppImmediateContext) +{ + DEV_CHECK_ERR(ppDevice && ppImmediateContext, "Null pointer provided"); + if (!ppDevice || !ppImmediateContext) + return; + + *ppDevice = nullptr; + *ppImmediateContext = nullptr; + + try + { + WGPUInstanceDescriptor wgpuInstanceDesc = {}; + WebGPUInstanceWrapper wgpuInstance{wgpuCreateInstance(&wgpuInstanceDesc)}; + if (!wgpuInstance) + LOG_ERROR_AND_THROW("Failed to create WebGPU instance"); + + auto Adapters = FindCompatibleAdapters(wgpuInstance.Get(), EngineCI.GraphicsAPIVersion); + + WebGPUAdapterWrapper SpecificAdapter{}; + if (EngineCI.AdapterId != DEFAULT_ADAPTER_ID) + { + if (EngineCI.AdapterId < Adapters.size()) + SpecificAdapter = std::move(Adapters[EngineCI.AdapterId]); + else + LOG_ERROR_AND_THROW(EngineCI.AdapterId, " is not a valid hardware adapter id. Total number of compatible adapters available on this system: ", Adapters.size()); + } + else + { + SpecificAdapter = std::move(Adapters[0]); + } + + WebGPUDeviceWrapper Device = CreateDeviceForAdapter(EngineCI, SpecificAdapter.Get()); + AttachToWebGPUDevice(wgpuInstance.Release(), SpecificAdapter.Release(), Device.Release(), EngineCI, ppDevice, ppImmediateContext); + } + catch (const std::runtime_error&) + { + } +} + +void EngineFactoryWebGPUImpl::CreateSwapChainWebGPU(IRenderDevice* pDevice, + IDeviceContext* pImmediateContext, + const SwapChainDesc& SCDesc, + const NativeWindow& Window, + ISwapChain** ppSwapChain) +{ + DEV_CHECK_ERR(ppSwapChain, "Null pointer provided"); + if (!ppSwapChain) + return; + + *ppSwapChain = nullptr; + + try + { + auto* pDeviceWebGPU = ClassPtrCast(pDevice); + auto* pDeviceContextWebGPU = ClassPtrCast(pImmediateContext); + auto& RawMemAllocator = GetRawAllocator(); + + auto* pSwapChainWebGPU = NEW_RC_OBJ(RawMemAllocator, "SwapChainWebGPUImpl instance", SwapChainWebGPUImpl)(SCDesc, pDeviceWebGPU, pDeviceContextWebGPU, Window); + pSwapChainWebGPU->QueryInterface(IID_SwapChain, reinterpret_cast(ppSwapChain)); + } + catch (const std::runtime_error&) + { + if (*ppSwapChain) + { + (*ppSwapChain)->Release(); + *ppSwapChain = nullptr; + } + + LOG_ERROR("Failed to create WebGPU-based swapchain"); + } +} + +void EngineFactoryWebGPUImpl::AttachToWebGPUDevice(WGPUInstance wgpuInstance, + WGPUAdapter wgpuAdapter, + WGPUDevice wgpuDevice, + const EngineWebGPUCreateInfo& EngineCI, + IRenderDevice** ppDevice, + IDeviceContext** ppImmediateContext) +{ + if (EngineCI.EngineAPIVersion != DILIGENT_API_VERSION) + { + LOG_ERROR_MESSAGE("Diligent Engine runtime (", DILIGENT_API_VERSION, ") is not compatible with the client API version (", EngineCI.EngineAPIVersion, ")"); + return; + } + + VERIFY(ppDevice && ppImmediateContext, "Null pointer provided"); + if (!ppDevice || !ppImmediateContext) + return; + + if (EngineCI.NumImmediateContexts > 1) + { + LOG_ERROR_MESSAGE("WebGPU backend doesn't support multiple immediate contexts"); + return; + } + + if (EngineCI.NumDeferredContexts > 0) + { + LOG_ERROR_MESSAGE("WebGPU backend doesn't support multiple deferred contexts"); + return; + } + + *ppDevice = nullptr; + *ppImmediateContext = nullptr; + + try + { + const auto AdapterInfo = GetGraphicsAdapterInfo(wgpuAdapter); + VerifyEngineCreateInfo(EngineCI, AdapterInfo); + + SetRawAllocator(EngineCI.pRawMemAllocator); + auto& RawMemAllocator = GetRawAllocator(); + + RenderDeviceWebGPUImpl* pRenderDeviceWebGPU{ + NEW_RC_OBJ(RawMemAllocator, "RenderDeviceWebGPUImpl instance", RenderDeviceWebGPUImpl)( + RawMemAllocator, this, EngineCI, AdapterInfo, wgpuInstance, wgpuAdapter, wgpuDevice)}; + pRenderDeviceWebGPU->QueryInterface(IID_RenderDevice, reinterpret_cast(ppDevice)); + + DeviceContextWebGPUImpl* pDeviceContextWebGPU{ + NEW_RC_OBJ(RawMemAllocator, "DeviceContextWebGPUImpl instance", DeviceContextWebGPUImpl)( + pRenderDeviceWebGPU, EngineCI, + DeviceContextDesc{ + EngineCI.pImmediateContextInfo ? EngineCI.pImmediateContextInfo[0].Name : nullptr, + pRenderDeviceWebGPU->GetAdapterInfo().Queues[0].QueueType, + False, // IsDeferred + 0, // Context id + 0 // Queue id + })}; + pDeviceContextWebGPU->QueryInterface(IID_DeviceContext, reinterpret_cast(ppImmediateContext)); + } + catch (const std::runtime_error&) + { + if (*ppDevice) + { + (*ppDevice)->Release(); + *ppDevice = nullptr; + } + + if (*ppImmediateContext != nullptr) + { + (*ppImmediateContext)->Release(); + *ppImmediateContext = nullptr; + } + + LOG_ERROR("Failed to create WebGPU-based render device and context"); + } +} + +IEngineFactoryWebGPU* GetEngineFactoryWebGPU() +{ + return EngineFactoryWebGPUImpl::GetInstance(); +} + +} // namespace Diligent + +extern "C" +{ + Diligent::IEngineFactoryWebGPU* Diligent_GetEngineFactoryWebGPU() + { + return Diligent::GetEngineFactoryWebGPU(); + } +} diff --git a/Graphics/GraphicsEngineWebGPU/src/FenceWebGPUImpl.cpp b/Graphics/GraphicsEngineWebGPU/src/FenceWebGPUImpl.cpp new file mode 100644 index 0000000000..c8b4f61076 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/src/FenceWebGPUImpl.cpp @@ -0,0 +1,107 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" + +#include "FenceWebGPUImpl.hpp" +#include "GraphicsAccessories.hpp" +#include "QueueSignalPoolWebGPU.hpp" +#include "RenderDeviceWebGPUImpl.hpp" + +namespace Diligent +{ + +FenceWebGPUImpl::FenceWebGPUImpl(IReferenceCounters* pRefCounters, + RenderDeviceWebGPUImpl* pDevice, + const FenceDesc& Desc) : + TFenceBase{pRefCounters, pDevice, Desc} +{ + if (m_Desc.Type != FENCE_TYPE_CPU_WAIT_ONLY) + LOG_ERROR_AND_THROW("Description of Fence '", m_Desc.Name, "' is invalid: ", GetFenceTypeString(m_Desc.Type), " is not supported in WebGPU."); +} + +Uint64 FenceWebGPUImpl::GetCompletedValue() +{ + auto& SignalPoolWebGPU = m_pDevice->GetQueueSignalPool(); + + while (!m_PendingSignals.empty()) + { + const auto& QueryData = m_PendingSignals.front(); + + // Timestamp values are implementation defined and may not increase monotonically. + // The physical device may reset the timestamp counter occasionally, + // which can result in unexpected values such as negative deltas between timestamps that logically should be monotonically increasing. + if (SignalPoolWebGPU.GetQueryTimestamp(m_pDevice->GetWebGPUDevice(), QueryData.QueryIdx) != QueryData.LastTimestamp) + { + SignalPoolWebGPU.ReleaseQuery(QueryData.QueryIdx); + UpdateLastCompletedFenceValue(QueryData.Value); + m_PendingSignals.pop_front(); + } + else + { + break; + } + } + + return m_LastCompletedFenceValue.load(); +} + +void FenceWebGPUImpl::Signal(Uint64 Value) +{ + DEV_ERROR("Signal() is not supported in WebGPU backend"); +} + +void FenceWebGPUImpl::Wait(Uint64 Value) +{ + auto& SignalPoolWebGPU = m_pDevice->GetQueueSignalPool(); + + while (!m_PendingSignals.empty()) + { + const auto& QueryData = m_PendingSignals.front(); + if (QueryData.Value > Value) + break; + + while (SignalPoolWebGPU.GetQueryTimestamp(m_pDevice->GetWebGPUDevice(), QueryData.QueryIdx) != QueryData.LastTimestamp) + std::this_thread::sleep_for(std::chrono::microseconds{1}); + + SignalPoolWebGPU.ReleaseQuery(QueryData.QueryIdx); + UpdateLastCompletedFenceValue(QueryData.Value); + m_PendingSignals.pop_front(); + } +} + +void FenceWebGPUImpl::AddPendingSignal(WGPUCommandEncoder wgpuCmdEncoder, Uint64 Value) +{ + auto& SignalPoolWebGPU = m_pDevice->GetQueueSignalPool(); + const Uint32 QueryIdx = SignalPoolWebGPU.AllocateQuery(); + const Uint64 QueryTimestamp = SignalPoolWebGPU.GetQueryTimestamp(m_pDevice->GetWebGPUDevice(), QueryIdx); + SignalPoolWebGPU.WriteTimestamp(wgpuCmdEncoder, QueryIdx); + SignalPoolWebGPU.ResolveQuery(wgpuCmdEncoder, QueryIdx); + m_PendingSignals.emplace_back(Value, QueryTimestamp, QueryIdx); + DvpSignal(Value); +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/src/FramebufferWebGPUImpl.cpp b/Graphics/GraphicsEngineWebGPU/src/FramebufferWebGPUImpl.cpp new file mode 100644 index 0000000000..cfb4715d10 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/src/FramebufferWebGPUImpl.cpp @@ -0,0 +1,45 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" + +#include "RenderDeviceWebGPUImpl.hpp" +#include "FramebufferWebGPUImpl.hpp" + +namespace Diligent +{ + +FramebufferWebGPUImpl::FramebufferWebGPUImpl(IReferenceCounters* pRefCounters, + RenderDeviceWebGPUImpl* pDevice, + const FramebufferDesc& Desc) : + TFramebufferBase{pRefCounters, pDevice, Desc} +{ + +} + +FramebufferWebGPUImpl::~FramebufferWebGPUImpl() {} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/src/GraphicsEngineWebGPU.def b/Graphics/GraphicsEngineWebGPU/src/GraphicsEngineWebGPU.def new file mode 100644 index 0000000000..86e8c67049 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/src/GraphicsEngineWebGPU.def @@ -0,0 +1,2 @@ +EXPORTS + GetEngineFactoryWebGPU diff --git a/Graphics/GraphicsEngineWebGPU/src/QueryManagerWebGPU.cpp b/Graphics/GraphicsEngineWebGPU/src/QueryManagerWebGPU.cpp new file mode 100644 index 0000000000..e7ca54b134 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/src/QueryManagerWebGPU.cpp @@ -0,0 +1,217 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" + +#include "QueryManagerWebGPU.hpp" +#include "WebGPUTypeConversions.hpp" + +namespace Diligent +{ + +static Uint32 GetQueryDataSize(QUERY_TYPE QueryType) +{ + static_assert(QUERY_TYPE_NUM_TYPES == 6, "Not all QUERY_TYPE enum values are tested"); + + // clang-format off + switch (QueryType) + { + case QUERY_TYPE_OCCLUSION: + case QUERY_TYPE_BINARY_OCCLUSION: + case QUERY_TYPE_TIMESTAMP: + case QUERY_TYPE_DURATION: + return sizeof(Uint64); + case QUERY_TYPE_PIPELINE_STATISTICS: + return (WGPUPipelineStatisticName_ComputeShaderInvocations + 1) * sizeof(Uint64); + default: + UNEXPECTED("Unexpected query type"); + return 0; + } + // clang-format on +} + +QueryManagerWebGPU::QueryManagerWebGPU(RenderDeviceWebGPUImpl* pRenderDeviceWebGPU, const Uint32 QueryHeapSizes[]) +{ + const auto& DevInfo = pRenderDeviceWebGPU->GetDeviceInfo(); + + // clang-format off + static_assert(QUERY_TYPE_OCCLUSION == 1, "Unexpected value of QUERY_TYPE_OCCLUSION. EngineWebGPUCreateInfo::QueryPoolSizes must be updated"); + static_assert(QUERY_TYPE_BINARY_OCCLUSION == 2, "Unexpected value of QUERY_TYPE_BINARY_OCCLUSION. EngineWebGPUCreateInfo::QueryPoolSizes must be updated"); + static_assert(QUERY_TYPE_TIMESTAMP == 3, "Unexpected value of QUERY_TYPE_TIMESTAMP. EngineWebGPUCreateInfo::QueryPoolSizes must be updated"); + static_assert(QUERY_TYPE_PIPELINE_STATISTICS== 4, "Unexpected value of QUERY_TYPE_PIPELINE_STATISTICS. EngineWebGPUCreateInfo::QueryPoolSizes must be updated"); + static_assert(QUERY_TYPE_DURATION == 5, "Unexpected value of QUERY_TYPE_DURATION. EngineWebGPUCreateInfo::QueryPoolSizes must be updated"); + static_assert(QUERY_TYPE_NUM_TYPES == 6, "Unexpected value of QUERY_TYPE_NUM_TYPES. EngineWebGPUCreateInfo::QueryPoolSizes must be updated"); + // clang-format on + + for (Uint32 QueryTypeIdx = QUERY_TYPE_UNDEFINED + 1; QueryTypeIdx < QUERY_TYPE_NUM_TYPES; ++QueryTypeIdx) + { + const auto QueryType = static_cast(QueryTypeIdx); + + // clang-format off + if (QueryType == QUERY_TYPE_OCCLUSION && !DevInfo.Features.OcclusionQueries || + QueryType == QUERY_TYPE_BINARY_OCCLUSION && !DevInfo.Features.BinaryOcclusionQueries || + QueryType == QUERY_TYPE_TIMESTAMP && !DevInfo.Features.TimestampQueries || + QueryType == QUERY_TYPE_PIPELINE_STATISTICS && !DevInfo.Features.PipelineStatisticsQueries || + QueryType == QUERY_TYPE_DURATION && !DevInfo.Features.DurationQueries) + continue; + // clang-format on + + String QuerySetName = String{"QueryManagerWebGPU: Query set ["} + GetQueryTypeString(QueryType) + "]"; + + WGPUQuerySetDescriptor wgpuQuerySetDesc{}; + wgpuQuerySetDesc.type = QueryTypeToWGPUQueryType(QueryType); + wgpuQuerySetDesc.count = QueryHeapSizes[QueryType]; + wgpuQuerySetDesc.label = QuerySetName.c_str(); + + if (QueryType == QUERY_TYPE_DURATION) + wgpuQuerySetDesc.count *= 2; + + std::vector wgpuPipelineStatisticNames{ + WGPUPipelineStatisticName_VertexShaderInvocations, + WGPUPipelineStatisticName_ClipperInvocations, + WGPUPipelineStatisticName_ClipperPrimitivesOut, + WGPUPipelineStatisticName_FragmentShaderInvocations, + WGPUPipelineStatisticName_ComputeShaderInvocations}; + + if (QueryType == QUERY_TYPE_PIPELINE_STATISTICS) + { + wgpuQuerySetDesc.pipelineStatistics = wgpuPipelineStatisticNames.data(); + wgpuQuerySetDesc.pipelineStatisticsCount = static_cast(wgpuPipelineStatisticNames.size()); + } + + auto& QuerySetInfo = m_QuerySets[QueryType]; + QuerySetInfo.Init(pRenderDeviceWebGPU->GetWebGPUDevice(), wgpuQuerySetDesc, QueryType); + VERIFY_EXPR(!QuerySetInfo.IsNull() && QuerySetInfo.GetQueryCount() == wgpuQuerySetDesc.count && QuerySetInfo.GetType() == QueryType); + } +} + +QueryManagerWebGPU::~QueryManagerWebGPU() +{ + std::stringstream QueryUsageSS; + QueryUsageSS << "WebGPU query manager peak usage:"; + for (Uint32 QueryType = QUERY_TYPE_UNDEFINED + 1; QueryType < QUERY_TYPE_NUM_TYPES; ++QueryType) + { + auto& QuerySetInfo = m_QuerySets[QueryType]; + if (QuerySetInfo.IsNull()) + continue; + + QueryUsageSS << std::endl + << std::setw(30) << std::left << GetQueryTypeString(static_cast(QueryType)) << ": " + << std::setw(4) << std::right << QuerySetInfo.GetMaxAllocatedQueries() + << '/' << std::setw(4) << QuerySetInfo.GetQueryCount(); + } + LOG_INFO_MESSAGE(QueryUsageSS.str()); +} + +Uint32 QueryManagerWebGPU::AllocateQuery(QUERY_TYPE Type) +{ + return m_QuerySets[Type].Allocate(); +} + +void QueryManagerWebGPU::ReleaseQuery(QUERY_TYPE Type, Uint32 Index) +{ + return m_QuerySets[Type].Release(Index); +} + +WGPUQuerySet QueryManagerWebGPU::GetQuerySet(QUERY_TYPE Type) const +{ + return m_QuerySets[Type].GetWebGPUQuerySet(); +} + +QueryManagerWebGPU::QuerySetInfo::~QuerySetInfo() +{ + + if (m_AvailableQueries.size() != m_QueryCount) + { + const auto OutstandingQueries = m_QueryCount - m_AvailableQueries.size(); + if (OutstandingQueries == 1) + { + LOG_ERROR_MESSAGE("One query of type ", GetQueryTypeString(m_Type), + " has not been returned to the query manager"); + } + else + { + LOG_ERROR_MESSAGE(OutstandingQueries, " queries of type ", GetQueryTypeString(m_Type), + " have not been returned to the query manager"); + } + } +} + +void QueryManagerWebGPU::QuerySetInfo::Init(WGPUDevice wgpuDevice, const WGPUQuerySetDescriptor& wgpuQuerySetDesc, QUERY_TYPE Type) +{ + m_Type = Type; + m_QueryCount = wgpuQuerySetDesc.count; + m_wgpuQuerySet.Reset(wgpuDeviceCreateQuerySet(wgpuDevice, &wgpuQuerySetDesc)); + if (!m_wgpuQuerySet) + LOG_ERROR_AND_THROW("Failed to create '", wgpuQuerySetDesc.label, "'"); + + WGPUBufferDescriptor wgpuResolveBufferDesc{}; + wgpuResolveBufferDesc.usage = WGPUBufferUsage_QueryResolve | WGPUBufferUsage_CopySrc; + wgpuResolveBufferDesc.size = GetQueryDataSize(Type); + m_wgpuResolveBuffer.Reset(wgpuDeviceCreateBuffer(wgpuDevice, &wgpuResolveBufferDesc)); + if (!m_wgpuResolveBuffer) + LOG_ERROR_AND_THROW("Failed to create resolve buffer for '", wgpuQuerySetDesc.label, "'"); + + WGPUBufferDescriptor wgpuStagingBufferDesc{}; + wgpuStagingBufferDesc.usage = WGPUBufferUsage_MapRead | WGPUBufferUsage_CopyDst; + wgpuStagingBufferDesc.size = GetQueryDataSize(Type); + m_wgpuStagingBuffer.Reset(wgpuDeviceCreateBuffer(wgpuDevice, &wgpuStagingBufferDesc)); + if (!m_wgpuResolveBuffer) + LOG_ERROR_AND_THROW("Failed to create staging buffer for '", wgpuQuerySetDesc.label, "'"); +} + +Uint32 QueryManagerWebGPU::QuerySetInfo::Allocate() +{ + Uint32 Index = InvalidIndex; + + if (!m_AvailableQueries.empty()) + { + Index = m_AvailableQueries.back(); + m_AvailableQueries.pop_back(); + m_MaxAllocatedQueries = std::max(m_MaxAllocatedQueries, m_QueryCount - static_cast(m_AvailableQueries.size())); + } + return Index; +} + +void QueryManagerWebGPU::QuerySetInfo::Release(Uint32 Index) +{ + VERIFY(Index < m_QueryCount, "Query index ", Index, " is out of range"); + VERIFY(std::find(m_AvailableQueries.begin(), m_AvailableQueries.end(), Index) == m_AvailableQueries.end(), + "Index ", Index, " already present in available queries list"); + m_AvailableQueries.push_back(Index); +} + +QUERY_TYPE QueryManagerWebGPU::QuerySetInfo::GetType() const { return m_Type; } + +Uint32 QueryManagerWebGPU::QuerySetInfo::GetQueryCount() const { return m_QueryCount; } + +WGPUQuerySet QueryManagerWebGPU::QuerySetInfo::GetWebGPUQuerySet() const { return m_wgpuQuerySet.Get(); } + +Uint32 QueryManagerWebGPU::QuerySetInfo::GetMaxAllocatedQueries() const { return m_MaxAllocatedQueries; } + +bool QueryManagerWebGPU::QuerySetInfo::IsNull() const { return m_wgpuQuerySet.Get() == nullptr; } + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/src/QueryWebGPUImpl.cpp b/Graphics/GraphicsEngineWebGPU/src/QueryWebGPUImpl.cpp new file mode 100644 index 0000000000..bb6193b4c3 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/src/QueryWebGPUImpl.cpp @@ -0,0 +1,122 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" + +#include "QueryWebGPUImpl.hpp" + +namespace Diligent +{ +QueryWebGPUImpl::QueryWebGPUImpl(IReferenceCounters* pRefCounters, + RenderDeviceWebGPUImpl* pDevice, + const QueryDesc& Desc) : + // clang-format off + TQueryBase + { + pRefCounters, + pDevice, + Desc + } +// clang-format on +{ +} + +QueryWebGPUImpl::~QueryWebGPUImpl() +{ + ReleaseQueries(); +} + +bool QueryWebGPUImpl::AllocateQueries() +{ + ReleaseQueries(); + VERIFY_EXPR(m_pContext != nullptr); + m_pQueryMgr = &m_pContext->GetQueryManager(); + for (Uint32 i = 0; i < (m_Desc.Type == QUERY_TYPE_DURATION ? Uint32{2} : Uint32{1}); ++i) + { + m_QuerySetIndex[i] = m_pQueryMgr->AllocateQuery(m_Desc.Type); + if (m_QuerySetIndex[i] == QueryManagerWebGPU::InvalidIndex) + { + LOG_ERROR_MESSAGE("Failed to allocate WebGPU query for type ", GetQueryTypeString(m_Desc.Type), + ". Increase the query pool size in EngineWebGPUCreateInfo."); + ReleaseQueries(); + return false; + } + } + return true; +} + +void QueryWebGPUImpl::ReleaseQueries() +{ + for (const auto& SetIdx : m_QuerySetIndex) + { + if (SetIdx != QueryManagerWebGPU::InvalidIndex) + { + VERIFY_EXPR(m_pQueryMgr != nullptr); + m_pQueryMgr->ReleaseQuery(m_Desc.Type, SetIdx); + } + } + m_pQueryMgr = nullptr; +} + +bool QueryWebGPUImpl::GetData(void* pData, Uint32 DataSize, bool AutoInvalidate) +{ + return false; +} + +void QueryWebGPUImpl::Invalidate() +{ + ReleaseQueries(); + TQueryBase::Invalidate(); +} + +bool QueryWebGPUImpl::OnBeginQuery(DeviceContextWebGPUImpl* pContext) +{ + TQueryBase::OnBeginQuery(pContext); + return AllocateQueries(); +} + +bool QueryWebGPUImpl::OnEndQuery(DeviceContextWebGPUImpl* pContext) +{ + TQueryBase::OnEndQuery(pContext); + + if (m_Desc.Type == QUERY_TYPE_TIMESTAMP) + { + if (!AllocateQueries()) + return false; + } + + if (m_QuerySetIndex[0] == QueryManagerWebGPU::InvalidIndex || (m_Desc.Type == QUERY_TYPE_DURATION && m_QuerySetIndex[1] == QueryManagerWebGPU::InvalidIndex)) + { + LOG_ERROR_MESSAGE("Query '", m_Desc.Name, "' is invalid: WebGPU query allocation failed"); + return false; + } + + VERIFY_EXPR(m_pQueryMgr != nullptr); + + return true; +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/src/RenderDeviceWebGPUImpl.cpp b/Graphics/GraphicsEngineWebGPU/src/RenderDeviceWebGPUImpl.cpp new file mode 100644 index 0000000000..fafc5661ea --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/src/RenderDeviceWebGPUImpl.cpp @@ -0,0 +1,270 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" + +#include "RenderDeviceWebGPUImpl.hpp" +#include "DeviceContextWebGPUImpl.hpp" +#include "RenderPassWebGPUImpl.hpp" +#include "TextureWebGPUImpl.hpp" +#include "BufferWebGPUImpl.hpp" +#include "PipelineStateWebGPUImpl.hpp" +#include "PipelineResourceSignatureWebGPUImpl.hpp" +#include "PipelineResourceAttribsWebGPU.hpp" +#include "ShaderResourceCacheWebGPU.hpp" +#include "RenderPassWebGPUImpl.hpp" +#include "FramebufferWebGPUImpl.hpp" +#include "SamplerWebGPUImpl.hpp" +#include "FenceWebGPUImpl.hpp" +#include "QueryWebGPUImpl.hpp" +#include "QueueSignalPoolWebGPU.hpp" +#include "AttachmentCleanerWebGPU.hpp" + +namespace Diligent +{ + +class BottomLevelASWebGPUImpl +{}; + +class TopLevelASWebGPUImpl +{}; + +class ShaderBindingTableWebGPUImpl +{}; + +class DeviceMemoryWebGPUImpl +{}; + +static void DebugMessengerCallback(WGPUErrorType MessageType, const char* Message, void* pUserData) +{ + if (Message != nullptr) + LOG_DEBUG_MESSAGE(DEBUG_MESSAGE_SEVERITY_ERROR, "WebGPU: ", Message); +} + +RenderDeviceWebGPUImpl::RenderDeviceWebGPUImpl(IReferenceCounters* pRefCounters, + IMemoryAllocator& RawMemAllocator, + IEngineFactory* pEngineFactory, + const EngineWebGPUCreateInfo& EngineCI, + const GraphicsAdapterInfo& AdapterInfo, + WGPUInstance wgpuInstance, + WGPUAdapter wgpuAdapter, + WGPUDevice wgpuDevice) : + + // clang-format off + TRenderDeviceBase + { + pRefCounters, + RawMemAllocator, + pEngineFactory, + EngineCI, + AdapterInfo + }, + m_wgpuInstance(wgpuInstance), + m_wgpuAdapter{wgpuAdapter}, + m_wgpuDevice{wgpuDevice} +// clang-format on +{ + wgpuDeviceSetUncapturedErrorCallback(m_wgpuDevice.Get(), DebugMessengerCallback, nullptr); + + m_DeviceInfo.Type = RENDER_DEVICE_TYPE_WEBGPU; + m_DeviceInfo.Features = EnableDeviceFeatures(m_AdapterInfo.Features, EngineCI.Features); + m_pQueueSignalPool.reset(new QueueSignalPoolWebGPU{this, EngineCI.QueueSignalPoolSize}); + m_pMemoryManager.reset(new SharedMemoryManagerWebGPU{m_wgpuDevice.Get(), EngineCI.DynamicHeapPageSize}); +} + +RenderDeviceWebGPUImpl::~RenderDeviceWebGPUImpl() = default; + +IMPLEMENT_QUERY_INTERFACE(RenderDeviceWebGPUImpl, IID_RenderDeviceWebGPU, TRenderDeviceBase) + +void RenderDeviceWebGPUImpl::CreateBuffer(const BufferDesc& BuffDesc, + const BufferData* pBuffData, + IBuffer** ppBuffer) +{ + CreateBufferImpl(ppBuffer, BuffDesc, pBuffData); +} + +void RenderDeviceWebGPUImpl::CreateTexture(const TextureDesc& TexDesc, + const TextureData* pData, + ITexture** ppTexture) +{ + CreateTextureImpl(ppTexture, TexDesc, pData); +} + +void RenderDeviceWebGPUImpl::CreateSampler(const SamplerDesc& SamplerDesc, + ISampler** ppSampler) +{ + CreateSamplerImpl(ppSampler, SamplerDesc); +} + +void RenderDeviceWebGPUImpl::CreateShader(const ShaderCreateInfo& ShaderCI, + IShader** ppShader) +{ +} + +void RenderDeviceWebGPUImpl::CreatePipelineResourceSignature(const PipelineResourceSignatureDesc& Desc, + IPipelineResourceSignature** ppSignature) +{ +} + +void RenderDeviceWebGPUImpl::CreateGraphicsPipelineState(const GraphicsPipelineStateCreateInfo& PSOCreateInfo, + IPipelineState** ppPipelineState) +{ +} + +void RenderDeviceWebGPUImpl::CreateComputePipelineState(const ComputePipelineStateCreateInfo& PSOCreateInfo, + IPipelineState** ppPipelineState) +{ +} + +void RenderDeviceWebGPUImpl::CreateRayTracingPipelineState(const RayTracingPipelineStateCreateInfo& PSOCreateInfo, + IPipelineState** ppPipelineState) +{ + UNSUPPORTED("Ray tracing is not supported in WebGPU"); + *ppPipelineState = nullptr; +} + +void RenderDeviceWebGPUImpl::CreateFence(const FenceDesc& Desc, + IFence** ppFence) +{ + CreateFenceImpl(ppFence, Desc); +} + +void RenderDeviceWebGPUImpl::CreateQuery(const QueryDesc& Desc, + IQuery** ppQuery) +{ + CreateQueryImpl(ppQuery, Desc); +} + +void RenderDeviceWebGPUImpl::CreateRenderPass(const RenderPassDesc& Desc, + IRenderPass** ppRenderPass) +{ + CreateRenderPassImpl(ppRenderPass, Desc); +} + +void RenderDeviceWebGPUImpl::CreateFramebuffer(const FramebufferDesc& Desc, + IFramebuffer** ppFramebuffer) +{ + CreateFramebufferImpl(ppFramebuffer, Desc); +} + +void RenderDeviceWebGPUImpl::CreateBLAS(const BottomLevelASDesc& Desc, + IBottomLevelAS** ppBLAS) +{ + UNSUPPORTED("CreateBLAS is not supported in WebGPU"); + *ppBLAS = nullptr; +} + +void RenderDeviceWebGPUImpl::CreateTLAS(const TopLevelASDesc& Desc, + ITopLevelAS** ppTLAS) +{ + UNSUPPORTED("CreateTLAS is not supported in WebGPU"); + *ppTLAS = nullptr; +} + +void RenderDeviceWebGPUImpl::CreateSBT(const ShaderBindingTableDesc& Desc, + IShaderBindingTable** ppSBT) +{ + UNSUPPORTED("CreateSBT is not supported in WebGPU"); + *ppSBT = nullptr; +} + +void RenderDeviceWebGPUImpl::CreateDeviceMemory(const DeviceMemoryCreateInfo& CreateInfo, + IDeviceMemory** ppMemory) +{ + UNSUPPORTED("CreateDeviceMemory is not supported in WebGPU"); + *ppMemory = nullptr; +} + +void RenderDeviceWebGPUImpl::CreatePipelineStateCache(const PipelineStateCacheCreateInfo& CreateInfo, + IPipelineStateCache** ppPSOCache) +{ + UNSUPPORTED("CreatePipelineStateCache is not supported in WebGPU"); + *ppPSOCache = nullptr; +} + +SparseTextureFormatInfo RenderDeviceWebGPUImpl::GetSparseTextureFormatInfo(TEXTURE_FORMAT TexFormat, + RESOURCE_DIMENSION Dimension, + Uint32 SampleCount) const +{ + UNSUPPORTED("GetSparseTextureFormatInfo is not supported in WebGPU"); + return {}; +} + +void RenderDeviceWebGPUImpl::IdleGPU() +{ +} + +void RenderDeviceWebGPUImpl::CreateTextureFromWebGPUTexture(WGPUTexture wgpuTexture, + const TextureDesc& TexDesc, + RESOURCE_STATE InitialState, + ITexture** ppTexture) +{ + CreateDeviceObject( + "texture", TexDesc, ppTexture, + [&]() // + { + TextureWebGPUImpl* pTextureWebGPU = NEW_RC_OBJ(m_TexObjAllocator, "TextureWebGPUImpl instance", TextureWebGPUImpl)(m_TexViewObjAllocator, this, TexDesc, InitialState, wgpuTexture); + pTextureWebGPU->QueryInterface(IID_TextureWebGPU, reinterpret_cast(ppTexture)); + } // + ); +} + +void RenderDeviceWebGPUImpl::CreateTextureFromWebGPUBuffer(WGPUBuffer wgpuBuffer, + const BufferDesc& BuffDesc, + RESOURCE_STATE InitialState, + IBuffer** ppBuffer) +{ + CreateDeviceObject( + "buffer", BuffDesc, ppBuffer, + [&]() // + { + BufferWebGPUImpl* pBufferWebGPU = NEW_RC_OBJ(m_TexObjAllocator, "BufferWebGPUImpl instance", BufferWebGPUImpl)(m_BuffViewObjAllocator, this, BuffDesc, InitialState, wgpuBuffer); + pBufferWebGPU->QueryInterface(IID_BufferWebGPU, reinterpret_cast(ppBuffer)); + } // + ); +} + +QueueSignalPoolWebGPU& RenderDeviceWebGPUImpl::GetQueueSignalPool() const +{ + return *m_pQueueSignalPool; +} + +AttachmentCleanerWebGPU& RenderDeviceWebGPUImpl::GetAttachmentCleaner() const +{ + return *m_pAttachmentCleaner; +} + +SharedMemoryManagerWebGPU::Page RenderDeviceWebGPUImpl::GetSharedMemoryPage(Uint64 Size) +{ + return m_pMemoryManager->GetPage(Size); +} + +void RenderDeviceWebGPUImpl::TestTextureFormat(TEXTURE_FORMAT TexFormat) +{ + UNSUPPORTED("TestTextureFormat is not supported in WebGPU"); +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/src/RenderPassWebGPUImpl.cpp b/Graphics/GraphicsEngineWebGPU/src/RenderPassWebGPUImpl.cpp new file mode 100644 index 0000000000..160a056dae --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/src/RenderPassWebGPUImpl.cpp @@ -0,0 +1,44 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" + +#include "RenderDeviceWebGPUImpl.hpp" +#include "RenderPassWebGPUImpl.hpp" + +namespace Diligent +{ + +RenderPassWebGPUImpl::RenderPassWebGPUImpl(IReferenceCounters* pRefCounters, + RenderDeviceWebGPUImpl* pDevice, + const RenderPassDesc& Desc) : + TRenderPassBase{pRefCounters, pDevice, Desc} +{ +} + +RenderPassWebGPUImpl::~RenderPassWebGPUImpl() {} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/src/SamplerWebGPUImpl.cpp b/Graphics/GraphicsEngineWebGPU/src/SamplerWebGPUImpl.cpp new file mode 100644 index 0000000000..42b262b25e --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/src/SamplerWebGPUImpl.cpp @@ -0,0 +1,73 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" + +#include "SamplerWebGPUImpl.hpp" +#include "RenderDeviceWebGPUImpl.hpp" +#include "WebGPUTypeConversions.hpp" + +namespace Diligent +{ + +static WGPUSamplerDescriptor SamplerDescToWGPUSamplerDescriptor(const SamplerDesc& Desc) noexcept +{ + + + WGPUSamplerDescriptor wgpuSamplerDesc{}; + wgpuSamplerDesc.label = Desc.Name; + wgpuSamplerDesc.addressModeU = TexAddressModeToWGPUAddressMode(Desc.AddressU); + wgpuSamplerDesc.addressModeV = TexAddressModeToWGPUAddressMode(Desc.AddressV); + wgpuSamplerDesc.addressModeW = TexAddressModeToWGPUAddressMode(Desc.AddressW); + wgpuSamplerDesc.magFilter = FilterTypeToWGPUFilterMode(Desc.MagFilter); + wgpuSamplerDesc.minFilter = FilterTypeToWGPUFilterMode(Desc.MinFilter); + wgpuSamplerDesc.mipmapFilter = FilterTypeToWGPUMipMapMode(Desc.MipFilter); + wgpuSamplerDesc.lodMinClamp = Desc.MinLOD; + wgpuSamplerDesc.lodMaxClamp = Desc.MaxLOD; + wgpuSamplerDesc.compare = ComparisonFuncToWGPUCompareFunction(Desc.ComparisonFunc); + wgpuSamplerDesc.maxAnisotropy = static_cast(IsAnisotropicFilter(Desc.MinFilter) ? Desc.MaxAnisotropy : 1); + return wgpuSamplerDesc; +} + +SamplerWebGPUImpl::SamplerWebGPUImpl(IReferenceCounters* pRefCounters, + RenderDeviceWebGPUImpl* pDevice, + const SamplerDesc& Desc) : + // clang-format off + TSamplerBase + { + pRefCounters, + pDevice, + Desc + } +// clang-format on +{ + WGPUSamplerDescriptor wgpuSamplerDesc = SamplerDescToWGPUSamplerDescriptor(m_Desc); + m_wgpuSampler.Reset(wgpuDeviceCreateSampler(pDevice->GetWebGPUDevice(), &wgpuSamplerDesc)); + if (!m_wgpuSampler) + LOG_ERROR_AND_THROW("Failed to create WebGPU sampler ", " '", m_Desc.Name ? m_Desc.Name : "", '\''); +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/src/SharedMemoryManagerWebGPU.cpp b/Graphics/GraphicsEngineWebGPU/src/SharedMemoryManagerWebGPU.cpp new file mode 100644 index 0000000000..8caea47e5d --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/src/SharedMemoryManagerWebGPU.cpp @@ -0,0 +1,180 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" + +#include "Align.hpp" +#include "SharedMemoryManagerWebGPU.hpp" +#include "DebugUtilities.hpp" + +namespace Diligent +{ + +bool SharedMemoryManagerWebGPU::Allocation::IsEmpty() const +{ + return wgpuBuffer == nullptr; +} + +SharedMemoryManagerWebGPU::Page::Page(SharedMemoryManagerWebGPU* _pMgr, Uint64 _Size) : + pMgr{_pMgr}, + PageSize{_Size} +{ + WGPUBufferDescriptor wgpuBufferDesc{}; + wgpuBufferDesc.label = "Shared memory page"; + wgpuBufferDesc.size = _Size; + wgpuBufferDesc.usage = + WGPUBufferUsage_CopyDst | + WGPUBufferUsage_CopySrc | + WGPUBufferUsage_Uniform | + WGPUBufferUsage_Storage | + WGPUBufferUsage_Vertex | + WGPUBufferUsage_Index | + WGPUBufferUsage_Indirect; + wgpuBuffer.Reset(wgpuDeviceCreateBuffer(pMgr->m_wgpuDevice, &wgpuBufferDesc)); + MappedData.resize(_Size); + pData = MappedData.data(); + LOG_INFO_MESSAGE("Created a new shared memory page, size: ", PageSize >> 10, " KB"); +} + +SharedMemoryManagerWebGPU::Page::Page(Page&& RHS) noexcept : + //clang-format off + pMgr{RHS.pMgr}, + wgpuBuffer{std::move(RHS.wgpuBuffer)}, + MappedData{std::move(RHS.MappedData)}, + PageSize{RHS.PageSize}, + CurrOffset{RHS.CurrOffset}, + pData{RHS.pData} +// clang-format on +{ + RHS = Page{}; +} + +SharedMemoryManagerWebGPU::Page& SharedMemoryManagerWebGPU::Page::operator=(Page&& RHS) noexcept +{ + if (&RHS == this) + return *this; + + pMgr = RHS.pMgr; + wgpuBuffer = std::move(RHS.wgpuBuffer); + MappedData = std::move(RHS.MappedData); + PageSize = RHS.PageSize; + CurrOffset = RHS.CurrOffset; + pData = RHS.pData; + + RHS.pMgr = nullptr; + RHS.PageSize = 0; + RHS.CurrOffset = 0; + RHS.pData = nullptr; + + return *this; +} + +SharedMemoryManagerWebGPU::Page::~Page() +{ + VERIFY(CurrOffset == 0, "Destroying a page that has not been recycled"); +} + +SharedMemoryManagerWebGPU::Allocation SharedMemoryManagerWebGPU::Page::Allocate(Uint64 Size, Uint64 Alignment) +{ + VERIFY(IsPowerOfTwo(Alignment), "Alignment size must be a power of two"); + Allocation Alloc; + Alloc.Offset = AlignUp(CurrOffset, Alignment); + Alloc.Size = AlignUp(Size, Alignment); + if (Alloc.Offset + Alloc.Size <= PageSize) + { + Alloc.wgpuBuffer = wgpuBuffer.Get(); + Alloc.pData = pData + Alloc.Offset; + CurrOffset = Alloc.Offset + Alloc.Size; + return Alloc; + } + return Allocation{}; +} + +void SharedMemoryManagerWebGPU::Page::Recycle() +{ + if (pMgr == nullptr) + { + UNEXPECTED("The page is empty."); + return; + } + + pMgr->RecyclePage(std::move(*this)); +} + +bool SharedMemoryManagerWebGPU::Page::IsEmpty() const +{ + return wgpuBuffer.Get() == nullptr; +} + +SharedMemoryManagerWebGPU::SharedMemoryManagerWebGPU(WGPUDevice wgpuDevice, Uint64 PageSize) : + m_PageSize{PageSize}, + m_wgpuDevice{wgpuDevice} +{ + VERIFY(IsPowerOfTwo(m_PageSize), "Page size must be power of two"); +} + +SharedMemoryManagerWebGPU::~SharedMemoryManagerWebGPU() +{ + VERIFY(m_DbgPageCounter == m_AvailablePages.size(), + "Not all pages have been recycled. This may result in a crash if the page is recycled later."); + Uint64 TotalSize = 0; + for (const auto& page : m_AvailablePages) + TotalSize += page.PageSize; + LOG_INFO_MESSAGE("SharedMemoryManagerMtl: total allocated memory: ", TotalSize >> 10, " KB"); +} + +SharedMemoryManagerWebGPU::Page SharedMemoryManagerWebGPU::GetPage(Uint64 Size) +{ + auto PageSize = m_PageSize; + while (PageSize < Size) + PageSize *= 2; + + auto Iter = m_AvailablePages.begin(); + while (Iter != m_AvailablePages.end()) + { + if (PageSize <= Iter->PageSize) + { + auto Result = std::move(*Iter); + m_AvailablePages.erase(Iter); + return Result; + } + ++Iter; + } + +#if DILIGENT_DEBUG + m_DbgPageCounter++; +#endif + + return Page{this, PageSize}; +} + +void SharedMemoryManagerWebGPU::RecyclePage(Page&& page) +{ + page.CurrOffset = 0; + m_AvailablePages.emplace_back(std::move(page)); +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/src/SwapChainWebGPUImpl.cpp b/Graphics/GraphicsEngineWebGPU/src/SwapChainWebGPUImpl.cpp new file mode 100644 index 0000000000..dcc97749e9 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/src/SwapChainWebGPUImpl.cpp @@ -0,0 +1,435 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" + +#include "EngineWebGPUImplTraits.hpp" +#include "SwapChainWebGPUImpl.hpp" +#include "RenderDeviceWebGPUImpl.hpp" +#include "DeviceContextWebGPUImpl.hpp" +#include "RenderPassWebGPUImpl.hpp" +#include "FramebufferWebGPUImpl.hpp" +#include "TextureViewWebGPUImpl.hpp" +#include "PipelineStateWebGPUImpl.hpp" +#include "PipelineResourceAttribsWebGPU.hpp" +#include "ShaderResourceCacheWebGPU.hpp" +#include "BufferWebGPUImpl.hpp" +#include "BufferViewWebGPUImpl.hpp" +#include "SamplerWebGPUImpl.hpp" +#include "WebGPUTypeConversions.hpp" + +#ifdef PLATFORM_WIN32 +# include +#endif + +namespace Diligent +{ + +static constexpr char ShaderSource[] = R"( +@group(0) @binding(0) var TextureSrc: texture_2d; +@group(0) @binding(1) var SamplerPoint: sampler; + +struct VertexOutput +{ + @builtin(position) Position: vec4f; + @location(0) Texcoord: vec2f; +}; + +@vertex +fn VSMain(@builtin(vertex_index) VertexId : u32) -> VertexOutput +{ + let Texcoord: vec2f = vec2f((VertexId << 1u) & 2.0f, VertexId & 2.0f); + let Position: vec4f = vec4f(Texcoord * vec2f(2.0f, -2.0f) + vec2f(-1.0f, 1.0f), 1.0f, 1.0f); + + var Output : VertexOutput; + output.Position = Position; + output.Texcoord = Texcoord; + return Output; +} + +@fragment +fn PSMain(Input : VertexOutput) -> @location(0) vec4f +{ + return textureLoad(SamplerPoint, Input.Texcoord, 0); +} +)"; + +class WebGPUSwapChainPresentCommand +{ +public: + WebGPUSwapChainPresentCommand(IRenderDeviceWebGPU* pRenderDevice) : + m_pRenderDevice{pRenderDevice} + { + WGPUShaderModuleWGSLDescriptor wgpuShaderCodeDesc{}; + wgpuShaderCodeDesc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor; + wgpuShaderCodeDesc.code = ShaderSource; + + WGPUShaderModuleDescriptor wgpuShaderModuleDesc{}; + wgpuShaderModuleDesc.nextInChain = reinterpret_cast(&wgpuShaderCodeDesc); + WebGPUShaderModuleWrapper wgpuShaderModule{wgpuDeviceCreateShaderModule(m_pRenderDevice->GetWebGPUDevice(), &wgpuShaderModuleDesc)}; + if (!wgpuShaderModule) + LOG_ERROR_AND_THROW("Failed to create shader module"); + + WGPUBindGroupLayoutEntry wgpuBindGroupLayoutEntries[2]{}; + wgpuBindGroupLayoutEntries[0].binding = 0; + wgpuBindGroupLayoutEntries[0].visibility = WGPUShaderStage_Fragment; + wgpuBindGroupLayoutEntries[0].texture.sampleType = WGPUTextureSampleType_Float; + wgpuBindGroupLayoutEntries[0].texture.viewDimension = WGPUTextureViewDimension_2D; + + wgpuBindGroupLayoutEntries[1].binding = 1; + wgpuBindGroupLayoutEntries[1].visibility = WGPUShaderStage_Fragment; + wgpuBindGroupLayoutEntries[1].sampler.type = WGPUSamplerBindingType_Filtering; + + WGPUBindGroupLayoutDescriptor wgpuBindGroupLayoutDesc{}; + wgpuBindGroupLayoutDesc.entryCount = _countof(wgpuBindGroupLayoutEntries); + wgpuBindGroupLayoutDesc.entries = wgpuBindGroupLayoutEntries; + m_wgpuBindGroupLayout.Reset(wgpuDeviceCreateBindGroupLayout(m_pRenderDevice->GetWebGPUDevice(), &wgpuBindGroupLayoutDesc)); + if (!m_wgpuBindGroupLayout) + LOG_ERROR_AND_THROW("Failed to create bind group layout"); + + WGPUPipelineLayoutDescriptor wgpuPipelineLayoutDesc{}; + wgpuPipelineLayoutDesc.bindGroupLayoutCount = 1; + wgpuPipelineLayoutDesc.bindGroupLayouts = &m_wgpuBindGroupLayout.Get(); + m_wgpuPipelineLayout.Reset(wgpuDeviceCreatePipelineLayout(m_pRenderDevice->GetWebGPUDevice(), &wgpuPipelineLayoutDesc)); + if (!m_wgpuPipelineLayout) + LOG_ERROR_AND_THROW("Failed to create pipeline layout"); + + WGPUFragmentState wgpuFragmentState; + wgpuFragmentState.module = wgpuShaderModule.Get(); + wgpuFragmentState.entryPoint = "PMain"; + + WGPURenderPipelineDescriptor wgpuRenderPipelineDesc{}; + wgpuRenderPipelineDesc.label = "SwapChainPresentPSO"; + wgpuRenderPipelineDesc.layout = m_wgpuPipelineLayout.Get(); + wgpuRenderPipelineDesc.primitive.topology = WGPUPrimitiveTopology_TriangleList; + wgpuRenderPipelineDesc.vertex.module = wgpuShaderModule.Get(); + wgpuRenderPipelineDesc.vertex.entryPoint = "VSMain"; + wgpuRenderPipelineDesc.fragment = &wgpuFragmentState; + m_wgpuRenderPipeline.Reset(wgpuDeviceCreateRenderPipeline(m_pRenderDevice->GetWebGPUDevice(), &wgpuRenderPipelineDesc)); + if (!m_wgpuPipelineLayout) + LOG_ERROR_AND_THROW("Failed to create render pipeline"); + + SamplerDesc Desc{}; + Desc.Name = "Sampler SwapChainPresent"; + Desc.MinFilter = FILTER_TYPE_POINT; + Desc.MagFilter = FILTER_TYPE_POINT; + Desc.MipFilter = FILTER_TYPE_POINT; + m_pRenderDevice->CreateSampler(Desc, &m_pPointSampler); + } + + void Execute(ITextureViewWebGPU* pTexture, ISwapChainWebGPU* pSwapChain) + { + WebGPUTextureViewWrapper wgpuTextureView{wgpuSwapChainGetCurrentTextureView(pSwapChain->GetWebGPUSwapChain())}; + if (!wgpuTextureView) + LOG_ERROR_MESSAGE("Failed to acquire next frame"); + + WGPUBindGroupEntry wgpuBindGroupEntries[2]{}; + wgpuBindGroupEntries[0].binding = 0; + wgpuBindGroupEntries[0].textureView = pTexture->GetWebGPUTextureView(); + + wgpuBindGroupEntries[1].binding = 1; + wgpuBindGroupEntries[1].sampler = m_pPointSampler.RawPtr()->GetWebGPUSampler(); + + WGPUBindGroupDescriptor wgpuBindGroupDesc{}; + wgpuBindGroupDesc.entries = wgpuBindGroupEntries; + wgpuBindGroupDesc.entryCount = _countof(wgpuBindGroupEntries); + wgpuBindGroupDesc.layout = m_wgpuBindGroupLayout.Get(); + + WebGPUBindGroupWrapper wgpuBindGroup{wgpuDeviceCreateBindGroup(m_pRenderDevice->GetWebGPUDevice(), &wgpuBindGroupDesc)}; + + WGPUCommandEncoderDescriptor wgpuCmdEncoderDesc{}; + WGPUCommandEncoder wgpuCmdEncoder = wgpuDeviceCreateCommandEncoder(m_pRenderDevice->GetWebGPUDevice(), &wgpuCmdEncoderDesc); + + WGPURenderPassColorAttachment Attachments[] = { + {wgpuTextureView.Get(), nullptr, WGPULoadOp_Undefined, WGPUStoreOp_Store, {}}}; + + WGPURenderPassDescriptor wgpuRenderPassDesc{}; + wgpuRenderPassDesc.colorAttachmentCount = _countof(Attachments); + wgpuRenderPassDesc.colorAttachments = Attachments; + WGPURenderPassEncoder wgpuRenderPassEncoder = wgpuCommandEncoderBeginRenderPass(wgpuCmdEncoder, &wgpuRenderPassDesc); + + wgpuRenderPassEncoderSetPipeline(wgpuRenderPassEncoder, m_wgpuRenderPipeline.Get()); + wgpuRenderPassEncoderSetBindGroup(wgpuRenderPassEncoder, 0, wgpuBindGroup.Get(), 0, nullptr); + wgpuRenderPassEncoderDraw(wgpuRenderPassEncoder, 3, 1, 0, 0); + wgpuRenderPassEncoderEnd(wgpuRenderPassEncoder); + + WGPUCommandBufferDescriptor wgpuCmdBufferDesc{}; + WGPUCommandBuffer wgpuCmdBuffer = wgpuCommandEncoderFinish(wgpuCmdEncoder, &wgpuCmdBufferDesc); + wgpuQueueSubmit(wgpuDeviceGetQueue(m_pRenderDevice->GetWebGPUDevice()), 1, &wgpuCmdBuffer); + wgpuSwapChainPresent(pSwapChain->GetWebGPUSwapChain()); + } + +private: + RefCntAutoPtr m_pRenderDevice; + RefCntAutoPtr m_pPointSampler; + WebGPUBindGroupLayoutWrapper m_wgpuBindGroupLayout; + WebGPUPipelineLayoutWrapper m_wgpuPipelineLayout; + WebGPURenderPipelineWrapper m_wgpuRenderPipeline; +}; + +SwapChainWebGPUImpl::SwapChainWebGPUImpl(IReferenceCounters* pRefCounters, + const SwapChainDesc& SCDesc, + RenderDeviceWebGPUImpl* pRenderDevice, + DeviceContextWebGPUImpl* pDeviceContext, + const NativeWindow& Window) : + // clang-format off + TSwapChainBase + { + pRefCounters, + pRenderDevice, + pDeviceContext, + SCDesc + }, + m_NativeWindow(Window), + m_pCmdPresent(std::make_unique(pRenderDevice)) +// clang-format on +{ + CreateSurface(); + CreateSwapChain(); + CreateBuffersAndViews(); +} + +SwapChainWebGPUImpl::~SwapChainWebGPUImpl() = default; + +IMPLEMENT_QUERY_INTERFACE(SwapChainWebGPUImpl, IID_SwapChainWebGPU, TSwapChainBase) + +void SwapChainWebGPUImpl::Present(Uint32 SyncInterval) +{ + if (SyncInterval != 0 && SyncInterval != 1) + LOG_WARNING_MESSAGE_ONCE("WebGPU only supports 0 and 1 present intervals"); + + auto pDeviceContext = m_wpDeviceContext.Lock(); + auto* pRenderDevice = m_pRenderDevice.RawPtr(); + if (!pDeviceContext) + { + LOG_ERROR_MESSAGE("Immediate context has been released"); + return; + } + + auto* pImmediateCtxWebGPU = pDeviceContext.RawPtr(); + + pImmediateCtxWebGPU->Flush(); + m_pCmdPresent->Execute(m_pBackBufferSRV, this); + + if (m_SwapChainDesc.IsPrimary) + { + pImmediateCtxWebGPU->FinishFrame(); + pRenderDevice->ReleaseStaleResources(); + } + + const bool EnableVSync = SyncInterval != 0; + if (m_VSyncEnabled != EnableVSync) + { + m_VSyncEnabled = EnableVSync; + RecreateSwapChain(); + } +} + +void SwapChainWebGPUImpl::Resize(Uint32 NewWidth, + Uint32 NewHeight, + SURFACE_TRANSFORM NewPreTransform) +{ + if (TSwapChainBase::Resize(NewWidth, NewHeight, NewPreTransform)) + RecreateSwapChain(); +} + +void SwapChainWebGPUImpl::SetFullscreenMode(const DisplayModeAttribs& DisplayMode) +{ + UNSUPPORTED("WebGPU does not support switching to the fullscreen mode"); +} + +void SwapChainWebGPUImpl::SetWindowedMode() +{ + UNSUPPORTED("WebGPU does not support switching to the windowed mode"); +} + +void SwapChainWebGPUImpl::CreateSurface() +{ + const auto* pRenderDeviceWebGPU = m_pRenderDevice.RawPtr(); + +#if PLATFORM_WIN32 + WGPUSurfaceDescriptorFromWindowsHWND wgpuSurfaceNativeDesc{}; + wgpuSurfaceNativeDesc.chain = {nullptr, WGPUSType_SurfaceDescriptorFromWindowsHWND}; + wgpuSurfaceNativeDesc.hwnd = m_NativeWindow.hWnd; + wgpuSurfaceNativeDesc.hinstance = GetModuleHandle(nullptr); +#elif PLATFORM_LINUX + WGPUSurfaceDescriptorFromXcbWindow wgpuSurfaceNativeDesc{}; + wgpuSurfaceNativeDesc.chain = {nullptr, WGPUSType_SurfaceDescriptorFromXcbWindow}; + wgpuSurfaceNativeDesc.connection = m_NativeWindow.pXCBConnection; + wgpuSurfaceNativeDesc.window = m_NativeWindow.WindowId; +#elif PLATFROM_MACOS + WGPUSurfaceDescriptorFromMetalLayer wgpuSurfaceNativeDesc{}; + wgpuSurfaceNativeDesc.chain = {nullptr, WGPUSType_SurfaceDescriptorFromMetalLayer}; + wgpuSurfaceNativeDesc.window = m_NativeWindow.MetalLayer; +#elif PLATFORM_EMSCRIPTEN + WGPUSurfaceDescriptorFromCanvasHTMLSelector wgpuSurfaceNativeDesc{}; + wgpuSurfaceNativeDesc.chain = {nullptr, WGPUSType_SurfaceDescriptorFromCanvasHTMLSelector}; + wgpuSurfaceNativeDesc.selector = m_NativeWindow.Selector; +#endif + + WGPUSurfaceDescriptor wgpuSurfaceDesc{}; + wgpuSurfaceDesc.nextInChain = reinterpret_cast(&wgpuSurfaceNativeDesc); + + m_wgpuSurface.Reset(wgpuInstanceCreateSurface(pRenderDeviceWebGPU->GetWebGPUInstance(), &wgpuSurfaceDesc)); + if (!m_wgpuSurface) + LOG_ERROR_AND_THROW("Failed to create OS-specific surface"); +} + +void SwapChainWebGPUImpl::CreateSwapChain() +{ + const auto* pRenderDeviceWebGPU = m_pRenderDevice.RawPtr(); + + WGPUSurfaceCapabilities wgpuSurfaceCapabilities{}; + wgpuSurfaceGetCapabilities(m_wgpuSurface.Get(), pRenderDeviceWebGPU->GetWebGPUAdapter(), &wgpuSurfaceCapabilities); + + std::vector Formats(wgpuSurfaceCapabilities.formatCount); + std::vector PresentModes(wgpuSurfaceCapabilities.presentModeCount); + std::vector CompositeAlphaModes(wgpuSurfaceCapabilities.alphaModeCount); + wgpuSurfaceCapabilities.formats = Formats.data(); + wgpuSurfaceCapabilities.presentModes = PresentModes.data(); + wgpuSurfaceCapabilities.alphaModes = CompositeAlphaModes.data(); + wgpuSurfaceGetCapabilities(m_wgpuSurface.Get(), pRenderDeviceWebGPU->GetWebGPUAdapter(), &wgpuSurfaceCapabilities); + + auto SelectPresentMode = [&]() -> WGPUPresentMode { + WGPUPresentMode Result = WGPUPresentMode_Fifo; + + std::vector PreferredPresentModes; + if (m_VSyncEnabled) + { + PreferredPresentModes.push_back(WGPUPresentMode_Fifo); + } + else + { + PreferredPresentModes.push_back(WGPUPresentMode_Mailbox); + PreferredPresentModes.push_back(WGPUPresentMode_Immediate); + PreferredPresentModes.push_back(WGPUPresentMode_Fifo); + } + + for (auto PreferredMode : PreferredPresentModes) + { + if (std::find(PresentModes.begin(), PresentModes.end(), PreferredMode) != PresentModes.end()) + { + Result = PreferredMode; + break; + } + } + + return Result; + }; + + // TODO + auto SelectUsage = [&]() -> WGPUTextureUsageFlags { + WGPUTextureUsageFlags Result = {}; + + DEV_CHECK_ERR(m_SwapChainDesc.Usage != 0, "No swap chain usage flags defined"); + static_assert(SWAP_CHAIN_USAGE_LAST == 8, "Please update this function to handle the new swap chain usage"); + + if (m_SwapChainDesc.Usage & SWAP_CHAIN_USAGE_RENDER_TARGET) + Result |= WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_CopyDst; + if (m_SwapChainDesc.Usage & SWAP_CHAIN_USAGE_SHADER_RESOURCE) + Result |= WGPUTextureUsage_TextureBinding; + if (m_SwapChainDesc.Usage & SWAP_CHAIN_USAGE_COPY_SOURCE) + Result |= WGPUTextureUsage_CopySrc; + + return Result; + }; + + WGPUSwapChainDescriptor wgpuSwapChainDesc{}; + wgpuSwapChainDesc.width = m_SwapChainDesc.Width; + wgpuSwapChainDesc.height = m_SwapChainDesc.Height; + wgpuSwapChainDesc.format = wgpuSurfaceGetPreferredFormat(m_wgpuSurface.Get(), pRenderDeviceWebGPU->GetWebGPUAdapter()); + wgpuSwapChainDesc.presentMode = SelectPresentMode(); + wgpuSwapChainDesc.usage = SelectUsage(); + + m_wgpuSwapChain.Reset(wgpuDeviceCreateSwapChain(pRenderDeviceWebGPU->GetWebGPUDevice(), m_wgpuSurface.Get(), &wgpuSwapChainDesc)); + if (!m_wgpuSwapChain) + LOG_ERROR_AND_THROW("Failed to create WebGPU swap chain"); +} + +void SwapChainWebGPUImpl::CreateBuffersAndViews() +{ + TextureDesc BackBufferDesc{}; + BackBufferDesc.Type = RESOURCE_DIM_TEX_2D; + BackBufferDesc.Width = m_SwapChainDesc.Width; + BackBufferDesc.Height = m_SwapChainDesc.Height; + BackBufferDesc.Format = m_SwapChainDesc.ColorBufferFormat; + BackBufferDesc.SampleCount = 1; + BackBufferDesc.Usage = USAGE_DEFAULT; + BackBufferDesc.BindFlags = BIND_SHADER_RESOURCE | BIND_RENDER_TARGET; + BackBufferDesc.Name = "Main back buffer"; + + RefCntAutoPtr pBackBufferTex; + m_pRenderDevice->CreateTexture(BackBufferDesc, nullptr, &pBackBufferTex); + m_pBackBufferRTV = RefCntAutoPtr(pBackBufferTex->GetDefaultView(TEXTURE_VIEW_RENDER_TARGET), IID_TextureWebGPU); + m_pBackBufferSRV = RefCntAutoPtr(pBackBufferTex->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE), IID_TextureWebGPU); + + if (m_SwapChainDesc.DepthBufferFormat != TEX_FORMAT_UNKNOWN) + { + TextureDesc DepthBufferDesc{}; + DepthBufferDesc.Type = RESOURCE_DIM_TEX_2D; + DepthBufferDesc.Width = m_SwapChainDesc.Width; + DepthBufferDesc.Height = m_SwapChainDesc.Height; + DepthBufferDesc.Format = m_SwapChainDesc.DepthBufferFormat; + DepthBufferDesc.SampleCount = 1; + DepthBufferDesc.Usage = USAGE_DEFAULT; + DepthBufferDesc.BindFlags = BIND_DEPTH_STENCIL; + + DepthBufferDesc.ClearValue.Format = DepthBufferDesc.Format; + DepthBufferDesc.ClearValue.DepthStencil.Depth = m_SwapChainDesc.DefaultDepthValue; + DepthBufferDesc.ClearValue.DepthStencil.Stencil = m_SwapChainDesc.DefaultStencilValue; + DepthBufferDesc.Name = "Main depth buffer"; + RefCntAutoPtr pDepthBufferTex; + m_pRenderDevice->CreateTexture(DepthBufferDesc, nullptr, &pDepthBufferTex); + m_pDepthBufferDSV = RefCntAutoPtr(pDepthBufferTex->GetDefaultView(TEXTURE_VIEW_DEPTH_STENCIL), IID_TextureWebGPU); + } +} + +void SwapChainWebGPUImpl::ReleaseSwapChainResources() +{ + if (!m_wgpuSwapChain) + return; + + m_pBackBufferSRV.Release(); + m_pBackBufferRTV.Release(); + m_pDepthBufferDSV.Release(); + m_wgpuSwapChain.Reset(); +} + +void SwapChainWebGPUImpl::RecreateSwapChain() +{ + try + { + ReleaseSwapChainResources(); + CreateSwapChain(); + CreateBuffersAndViews(); + } + catch (const std::runtime_error&) + { + LOG_ERROR("Failed to recreate the swap chain"); + } +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/src/TextureViewWebGPUImpl.cpp b/Graphics/GraphicsEngineWebGPU/src/TextureViewWebGPUImpl.cpp new file mode 100644 index 0000000000..5702d98e51 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/src/TextureViewWebGPUImpl.cpp @@ -0,0 +1,56 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" + +#include "TextureViewWebGPUImpl.hpp" +#include "RenderDeviceWebGPUImpl.hpp" +#include "DeviceContextWebGPUImpl.hpp" + +namespace Diligent +{ + +TextureViewWebGPUImpl::TextureViewWebGPUImpl(IReferenceCounters* pRefCounters, + RenderDeviceWebGPUImpl* pDevice, + const TextureViewDesc& ViewDesc, + ITexture* pTexture, + WGPUTextureView wgpuTextureView, + bool bIsDefaultView) : + // clang-format off + TTextureViewBase + { + pRefCounters, + pDevice, + ViewDesc, + pTexture, + bIsDefaultView + }, m_wgpuTextureView(wgpuTextureView) +// clang-format on +{ + +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/src/TextureWebGPUImpl.cpp b/Graphics/GraphicsEngineWebGPU/src/TextureWebGPUImpl.cpp new file mode 100644 index 0000000000..c2d41e97db --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/src/TextureWebGPUImpl.cpp @@ -0,0 +1,369 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" + +#include "TextureWebGPUImpl.hpp" +#include "RenderDeviceWebGPUImpl.hpp" +#include "DeviceContextWebGPUImpl.hpp" +#include "WebGPUTypeConversions.hpp" + +namespace Diligent +{ + +static WGPUTextureDescriptor TextureDescToWGPUTextureDescriptor(const TextureDesc& Desc, const RenderDeviceWebGPUImpl* pRenderDevice) noexcept +{ + WGPUTextureDescriptor wgpuTextureDesc{}; + + if (Desc.Type == RESOURCE_DIM_TEX_CUBE) + DEV_CHECK_ERR(Desc.ArraySize == 6, "Cube textures are expected to have exactly 6 array slices"); + if (Desc.Type == RESOURCE_DIM_TEX_CUBE_ARRAY) + DEV_CHECK_ERR(Desc.ArraySize % 6 == 0, "Cube texture arrays are expected to have a number of array slices that is a multiple of 6"); + + if (Desc.IsArray()) + wgpuTextureDesc.size.depthOrArrayLayers = Desc.ArraySize; + else if (Desc.Is3D()) + wgpuTextureDesc.size.depthOrArrayLayers = Desc.Depth; + else + wgpuTextureDesc.size.depthOrArrayLayers = 1; + + if (Desc.Is1D()) + wgpuTextureDesc.dimension = WGPUTextureDimension_1D; + else if (Desc.Is2D()) + wgpuTextureDesc.dimension = WGPUTextureDimension_2D; + else if (Desc.Is3D()) + wgpuTextureDesc.dimension = WGPUTextureDimension_3D; + else + LOG_ERROR_AND_THROW("Unknown texture type"); + + wgpuTextureDesc.usage = WGPUTextureUsage_CopyDst | WGPUTextureUsage_CopySrc; + if (Desc.BindFlags & (BIND_RENDER_TARGET | BIND_DEPTH_STENCIL)) + wgpuTextureDesc.usage |= WGPUTextureUsage_RenderAttachment; + if (Desc.BindFlags & BIND_UNORDERED_ACCESS) + wgpuTextureDesc.usage |= WGPUTextureUsage_StorageBinding; + if (Desc.BindFlags & BIND_SHADER_RESOURCE) + wgpuTextureDesc.usage |= WGPUTextureUsage_TextureBinding; + + wgpuTextureDesc.format = TexFormatToWGPUFormat(Desc.Format); + wgpuTextureDesc.mipLevelCount = Desc.MipLevels; + wgpuTextureDesc.sampleCount = Desc.SampleCount; + wgpuTextureDesc.size.width = Desc.GetWidth(); + wgpuTextureDesc.size.height = Desc.GetHeight(); + wgpuTextureDesc.label = Desc.Name; + + return wgpuTextureDesc; +} + +static WGPUTextureViewDescriptor TextureViewDescToWGPUTextureViewDescriptor(const TextureDesc& TexDesc, TextureViewDesc& ViewDesc, const RenderDeviceWebGPUImpl* pRenderDevice) noexcept +{ + if (ViewDesc.Format == TEX_FORMAT_UNKNOWN) + ViewDesc.Format = TexDesc.Format; + + WGPUTextureViewDescriptor wgpuTextureViewDesc{}; + wgpuTextureViewDesc.dimension = ResourceDimensionToWGPUTextureViewDimension(ViewDesc.TextureDim); + wgpuTextureViewDesc.baseMipLevel = ViewDesc.MostDetailedMip; + wgpuTextureViewDesc.mipLevelCount = ViewDesc.NumMipLevels; + + if (TexDesc.IsArray()) + { + wgpuTextureViewDesc.baseArrayLayer = ViewDesc.FirstArraySlice; + wgpuTextureViewDesc.arrayLayerCount = ViewDesc.NumArraySlices; + } + else + { + wgpuTextureViewDesc.baseArrayLayer = 0; + wgpuTextureViewDesc.arrayLayerCount = 1; + } + + const auto& FmtAttribs = GetTextureFormatAttribs(ViewDesc.Format); + + if (ViewDesc.ViewType == TEXTURE_VIEW_DEPTH_STENCIL) + { + if (FmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH) + wgpuTextureViewDesc.aspect = WGPUTextureAspect_DepthOnly; + else if (FmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH_STENCIL) + wgpuTextureViewDesc.aspect = WGPUTextureAspect_All; + else + UNEXPECTED("Unexpected component type for a depth-stencil view format"); + } + else + { + wgpuTextureViewDesc.aspect = WGPUTextureAspect_All; + } + + return wgpuTextureViewDesc; +} + + +static Uint64 WebGPUGetTextureLocationOffset(const TextureDesc& TexDesc, + Uint32 ArraySlice, + Uint32 MipLevel, + Uint32 BlockHeight, + Uint32 ByteRawStride) +{ + VERIFY_EXPR(TexDesc.MipLevels > 0 && TexDesc.GetArraySize() > 0 && TexDesc.Width > 0 && TexDesc.Height > 0 && TexDesc.Format != TEX_FORMAT_UNKNOWN); + VERIFY_EXPR(ArraySlice < TexDesc.GetArraySize() && MipLevel < TexDesc.MipLevels || ArraySlice == TexDesc.GetArraySize() && MipLevel == 0); + + Uint64 Offset = 0; + if (ArraySlice > 0) + { + Uint64 ArraySliceSize = 0; + for (Uint32 mip = 0; mip < TexDesc.MipLevels; ++mip) + { + const auto MipInfo = GetMipLevelProperties(TexDesc, mip); + const auto DepthSliceSize = AlignUp(MipInfo.RowSize, ByteRawStride) * (MipInfo.StorageHeight / BlockHeight); + ArraySliceSize += DepthSliceSize * MipInfo.Depth; + } + + Offset = ArraySliceSize; + if (TexDesc.IsArray()) + Offset *= ArraySlice; + } + + for (Uint32 mip = 0; mip < MipLevel; ++mip) + { + const auto MipInfo = GetMipLevelProperties(TexDesc, mip); + const auto DepthSliceSize = AlignUp(MipInfo.RowSize, ByteRawStride) * (MipInfo.StorageHeight / BlockHeight); + Offset += DepthSliceSize * MipInfo.Depth; + } + + return Offset; +} + + +TextureWebGPUImpl::TextureWebGPUImpl(IReferenceCounters* pRefCounters, + FixedBlockMemoryAllocator& TexViewObjAllocator, + RenderDeviceWebGPUImpl* pDevice, + const TextureDesc& Desc, + const TextureData* pInitData) : + // clang-format off + TTextureBase + { + pRefCounters, + TexViewObjAllocator, + pDevice, + Desc + } +// clang-format on +{ + + if (m_Desc.Usage == USAGE_IMMUTABLE && (pInitData == nullptr || pInitData->pSubResources == nullptr)) + LOG_ERROR_AND_THROW("Immutable textures must be initialized with data at creation time: pInitData can't be null"); + + const auto& FmtAttribs = GetTextureFormatAttribs(m_Desc.Format); + const auto IsInitializeTexture = (pInitData != nullptr && pInitData->pSubResources != nullptr && pInitData->NumSubresources > 0); + + if (m_Desc.Usage == USAGE_IMMUTABLE || m_Desc.Usage == USAGE_DEFAULT || m_Desc.Usage == USAGE_DYNAMIC) + { + WGPUTextureDescriptor wgpuTextureDesc = TextureDescToWGPUTextureDescriptor(m_Desc, pDevice); + m_wgpuTexture.Reset(wgpuDeviceCreateTexture(pDevice->GetWebGPUDevice(), &wgpuTextureDesc)); + if (!m_wgpuTexture) + LOG_ERROR_AND_THROW("Failed to create WebGPU texture ", " '", m_Desc.Name ? m_Desc.Name : "", '\''); + + if (IsInitializeTexture) + { + WGPUBufferDescriptor wgpuBufferDesc{}; + wgpuBufferDesc.usage = WGPUBufferUsage_MapWrite | WGPUBufferUsage_CopySrc; + wgpuBufferDesc.size = WebGPUGetTextureLocationOffset(m_Desc, m_Desc.GetArraySize(), 0, FmtAttribs.BlockHeight, CopyTextureRawStride); + wgpuBufferDesc.mappedAtCreation = true; + + WebGPUBufferWrapper wgpuUploadBuffer{wgpuDeviceCreateBuffer(pDevice->GetWebGPUDevice(), &wgpuBufferDesc)}; + if (!wgpuUploadBuffer) + LOG_ERROR_AND_THROW("Failed to create WebGPU texture upload buffer"); + + auto* pUploadData = static_cast(wgpuBufferGetMappedRange(wgpuUploadBuffer.Get(), 0, wgpuBufferDesc.size)); + + WGPUCommandEncoderDescriptor wgpuEncoderDesc{}; + WGPUCommandEncoder wgpuCmdEncoder = wgpuDeviceCreateCommandEncoder(pDevice->GetWebGPUDevice(), &wgpuEncoderDesc); + + Uint32 CurrSubRes = 0; + for (Uint32 layer = 0; layer < m_Desc.GetArraySize(); ++layer) + { + for (Uint32 mip = 0; mip < m_Desc.MipLevels; ++mip) + { + const auto MipProps = GetMipLevelProperties(m_Desc, mip); + const auto& SubResData = pInitData->pSubResources[CurrSubRes++]; + + const auto DstSubResOffset = WebGPUGetTextureLocationOffset(m_Desc, layer, mip, FmtAttribs.BlockHeight, CopyTextureRawStride); + const auto DstRawStride = AlignUp(MipProps.RowSize, CopyTextureRawStride); + const auto DstDepthStride = DstRawStride * (MipProps.StorageHeight / FmtAttribs.BlockHeight); + + CopyTextureSubresource(SubResData, + MipProps.StorageHeight / FmtAttribs.BlockHeight, // NumRows + MipProps.Depth, + MipProps.RowSize, + pUploadData + DstSubResOffset, + DstRawStride, // DstRowStride + DstDepthStride // DstDepthStride + ); + + WGPUImageCopyBuffer wgpuSourceCopyInfo{}; + wgpuSourceCopyInfo.layout.offset = DstSubResOffset; + wgpuSourceCopyInfo.layout.bytesPerRow = static_cast(DstRawStride); + wgpuSourceCopyInfo.layout.rowsPerImage = static_cast(DstDepthStride / DstRawStride); + wgpuSourceCopyInfo.buffer = wgpuUploadBuffer.Get(); + + WGPUImageCopyTexture wgpuDestinationCopyInfo{}; + wgpuDestinationCopyInfo.texture = m_wgpuTexture.Get(); + wgpuDestinationCopyInfo.mipLevel = mip; + wgpuDestinationCopyInfo.origin = {0, 0, layer}; + wgpuDestinationCopyInfo.aspect = WGPUTextureAspect_All; + + WGPUExtent3D wgpuCopySize{}; + wgpuCopySize.width = MipProps.LogicalWidth; + wgpuCopySize.height = MipProps.LogicalHeight; + wgpuCopySize.depthOrArrayLayers = MipProps.Depth; + + wgpuCommandEncoderCopyBufferToTexture(wgpuCmdEncoder, &wgpuSourceCopyInfo, &wgpuDestinationCopyInfo, &wgpuCopySize); + } + } + + wgpuBufferUnmap(wgpuUploadBuffer.Get()); + + WGPUCommandBufferDescriptor wgpuCmdBufferDesc{}; + WGPUCommandBuffer wgpuCmdBuffer = wgpuCommandEncoderFinish(wgpuCmdEncoder, &wgpuCmdBufferDesc); + wgpuQueueSubmit(wgpuDeviceGetQueue(pDevice->GetWebGPUDevice()), 1, &wgpuCmdBuffer); + } + } + else if (m_Desc.Usage == USAGE_STAGING) + { + String StagingBufferName = "Staging buffer for '"; + StagingBufferName += m_Desc.Name; + StagingBufferName += '\''; + + WGPUBufferDescriptor wgpuBufferDesc{}; + wgpuBufferDesc.size = GetStagingTextureSubresourceOffset(m_Desc, m_Desc.GetArraySize(), 0, StagingDataAlignment); + wgpuBufferDesc.label = StagingBufferName.c_str(); + + // clang-format off + DEV_CHECK_ERR((m_Desc.CPUAccessFlags & (CPU_ACCESS_READ | CPU_ACCESS_WRITE)) == CPU_ACCESS_READ || + (m_Desc.CPUAccessFlags & (CPU_ACCESS_READ | CPU_ACCESS_WRITE)) == CPU_ACCESS_WRITE, + "Exactly one of CPU_ACCESS_READ or CPU_ACCESS_WRITE flags must be specified"); + // clang-format on + + if (m_Desc.CPUAccessFlags & CPU_ACCESS_READ) + { + DEV_CHECK_ERR(!IsInitializeTexture, "Readback textures should not be initialized with data"); + wgpuBufferDesc.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_MapRead; + SetState(RESOURCE_STATE_COPY_DEST); + } + else if (m_Desc.CPUAccessFlags & CPU_ACCESS_WRITE) + { + wgpuBufferDesc.usage = WGPUBufferUsage_CopySrc | WGPUBufferUsage_MapWrite; + SetState(RESOURCE_STATE_COPY_SOURCE); + } + else + UNEXPECTED("Unexpected CPU access"); + + m_wgpuStagingBuffer.Reset(wgpuDeviceCreateBuffer(pDevice->GetWebGPUDevice(), &wgpuBufferDesc)); + if (!m_wgpuStagingBuffer) + LOG_ERROR_AND_THROW("Failed to create WebGPU buffer ", " '", StagingBufferName, '\''); + + if (IsInitializeTexture) + { + auto* const pStagingData = static_cast(wgpuBufferGetMappedRange(m_wgpuStagingBuffer.Get(), 0, wgpuBufferDesc.size)); + + Uint32 CurrSubRes = 0; + for (Uint32 layer = 0; layer < m_Desc.GetArraySize(); ++layer) + { + for (Uint32 mip = 0; mip < m_Desc.MipLevels; ++mip) + { + const auto MipProps = GetMipLevelProperties(m_Desc, mip); + const auto& SubResData = pInitData->pSubResources[CurrSubRes++]; + const auto DstSubResOffset = GetStagingTextureSubresourceOffset(m_Desc, layer, mip, StagingDataAlignment); + + CopyTextureSubresource(SubResData, + MipProps.StorageHeight / FmtAttribs.BlockHeight, // NumRows + MipProps.Depth, + MipProps.RowSize, + pStagingData + DstSubResOffset, + MipProps.RowSize, // DstRowStride + MipProps.DepthSliceSize // DstDepthStride + ); + } + } + + wgpuBufferUnmap(m_wgpuStagingBuffer.Get()); + } + } + else + { + UNSUPPORTED("Unsupported usage ", GetUsageString(m_Desc.Usage)); + } + + SetState(RESOURCE_STATE_UNDEFINED); +} + +TextureWebGPUImpl::TextureWebGPUImpl(IReferenceCounters* pRefCounters, + FixedBlockMemoryAllocator& TexViewObjAllocator, + RenderDeviceWebGPUImpl* pDevice, + const TextureDesc& Desc, + RESOURCE_STATE InitialState, + WGPUTexture wgpuTextureHandle) : + TTextureBase{pRefCounters, TexViewObjAllocator, pDevice, Desc}, + m_wgpuTexture{wgpuTextureHandle, {true}} +{ + SetState(InitialState); +} + +void TextureWebGPUImpl::CreateViewInternal(const TextureViewDesc& ViewDesc, ITextureView** ppView, bool bIsDefaultView) +{ + + VERIFY(ppView != nullptr, "View pointer address is null"); + if (!ppView) return; + VERIFY(*ppView == nullptr, "Overwriting reference to existing object may cause memory leaks"); + + *ppView = nullptr; + + try + { + auto& TexViewAllocator = m_pDevice->GetTexViewObjAllocator(); + VERIFY(&TexViewAllocator == &m_dbgTexViewObjAllocator, "Texture view allocator does not match allocator provided during texture initialization"); + + auto UpdatedViewDesc = ViewDesc; + ValidatedAndCorrectTextureViewDesc(m_Desc, UpdatedViewDesc); + auto wgpuTextureViewDesc = TextureViewDescToWGPUTextureViewDescriptor(m_Desc, UpdatedViewDesc, m_pDevice); + + WebGPUTextureViewWrapper wgpuTextureView{wgpuTextureCreateView(m_wgpuTexture.Get(), &wgpuTextureViewDesc)}; + if (!wgpuTextureView) + LOG_ERROR_AND_THROW("Failed to create WebGPU texture view ", " '", ViewDesc.Name ? ViewDesc.Name : "", '\''); + + auto pViewWebGPU = NEW_RC_OBJ(TexViewAllocator, "TextureViewWebGPUImpl instance", TextureViewWebGPUImpl, bIsDefaultView ? this : nullptr)(GetDevice(), UpdatedViewDesc, this, wgpuTextureView.Release(), bIsDefaultView); + VERIFY(pViewWebGPU->GetDesc().ViewType == ViewDesc.ViewType, "Incorrect view type"); + + if (bIsDefaultView) + *ppView = pViewWebGPU; + else + pViewWebGPU->QueryInterface(IID_TextureView, reinterpret_cast(ppView)); + } + catch (const std::runtime_error&) + { + const auto* ViewTypeName = GetTexViewTypeLiteralName(ViewDesc.ViewType); + LOG_ERROR("Failed to create view \"", ViewDesc.Name ? ViewDesc.Name : "", "\" (", ViewTypeName, ") for texture \"", m_Desc.Name ? m_Desc.Name : "", "\""); + } +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineWebGPU/src/WebGPUTypeConversions.cpp b/Graphics/GraphicsEngineWebGPU/src/WebGPUTypeConversions.cpp new file mode 100644 index 0000000000..10b56f0320 --- /dev/null +++ b/Graphics/GraphicsEngineWebGPU/src/WebGPUTypeConversions.cpp @@ -0,0 +1,597 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" + +#include "WebGPUTypeConversions.hpp" +#include "DebugUtilities.hpp" +#include "GraphicsAccessories.hpp" + +namespace Diligent +{ + +class TexFormatToWebGPUFormatMapper +{ +public: + TexFormatToWebGPUFormatMapper() + { + // clang-format off + m_FmtToWGPUFmtMap[TEX_FORMAT_UNKNOWN] = WGPUTextureFormat_Undefined; + + m_FmtToWGPUFmtMap[TEX_FORMAT_RGBA32_TYPELESS] = WGPUTextureFormat_RGBA32Float; + m_FmtToWGPUFmtMap[TEX_FORMAT_RGBA32_FLOAT] = WGPUTextureFormat_RGBA32Float; + m_FmtToWGPUFmtMap[TEX_FORMAT_RGBA32_UINT] = WGPUTextureFormat_RGBA32Uint; + m_FmtToWGPUFmtMap[TEX_FORMAT_RGBA32_SINT] = WGPUTextureFormat_RGBA32Sint; + + m_FmtToWGPUFmtMap[TEX_FORMAT_RGB32_TYPELESS] = WGPUTextureFormat_Undefined; + m_FmtToWGPUFmtMap[TEX_FORMAT_RGB32_FLOAT] = WGPUTextureFormat_Undefined; + m_FmtToWGPUFmtMap[TEX_FORMAT_RGB32_UINT] = WGPUTextureFormat_Undefined; + m_FmtToWGPUFmtMap[TEX_FORMAT_RGB32_SINT] = WGPUTextureFormat_Undefined; + + m_FmtToWGPUFmtMap[TEX_FORMAT_RGBA16_TYPELESS] = WGPUTextureFormat_RGBA16Float; + m_FmtToWGPUFmtMap[TEX_FORMAT_RGBA16_FLOAT] = WGPUTextureFormat_RGBA16Float; + m_FmtToWGPUFmtMap[TEX_FORMAT_RGBA16_UNORM] = WGPUTextureFormat_Undefined; + m_FmtToWGPUFmtMap[TEX_FORMAT_RGBA16_UINT] = WGPUTextureFormat_RGBA16Uint; + m_FmtToWGPUFmtMap[TEX_FORMAT_RGBA16_SNORM] = WGPUTextureFormat_Undefined; + m_FmtToWGPUFmtMap[TEX_FORMAT_RGBA16_SINT] = WGPUTextureFormat_RGBA16Sint; + + m_FmtToWGPUFmtMap[TEX_FORMAT_RG32_TYPELESS] = WGPUTextureFormat_RG32Float; + m_FmtToWGPUFmtMap[TEX_FORMAT_RG32_FLOAT] = WGPUTextureFormat_RG32Float; + m_FmtToWGPUFmtMap[TEX_FORMAT_RG32_UINT] = WGPUTextureFormat_RG32Uint; + m_FmtToWGPUFmtMap[TEX_FORMAT_RG32_SINT] = WGPUTextureFormat_RG32Sint; + + m_FmtToWGPUFmtMap[TEX_FORMAT_R32G8X24_TYPELESS] = WGPUTextureFormat_Depth32FloatStencil8; + m_FmtToWGPUFmtMap[TEX_FORMAT_D32_FLOAT_S8X24_UINT] = WGPUTextureFormat_Depth32FloatStencil8; + m_FmtToWGPUFmtMap[TEX_FORMAT_R32_FLOAT_X8X24_TYPELESS] = WGPUTextureFormat_Depth32FloatStencil8; + m_FmtToWGPUFmtMap[TEX_FORMAT_X32_TYPELESS_G8X24_UINT] = WGPUTextureFormat_Undefined; + + m_FmtToWGPUFmtMap[TEX_FORMAT_RGB10A2_TYPELESS] = WGPUTextureFormat_RGB10A2Unorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_RGB10A2_UNORM] = WGPUTextureFormat_RGB10A2Unorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_RGB10A2_UINT] = WGPUTextureFormat_Undefined; + m_FmtToWGPUFmtMap[TEX_FORMAT_R11G11B10_FLOAT] = WGPUTextureFormat_RG11B10Ufloat; + + m_FmtToWGPUFmtMap[TEX_FORMAT_RGBA8_TYPELESS] = WGPUTextureFormat_RGBA8Unorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_RGBA8_UNORM] = WGPUTextureFormat_RGBA8Unorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_RGBA8_UNORM_SRGB] = WGPUTextureFormat_RGBA8UnormSrgb; + m_FmtToWGPUFmtMap[TEX_FORMAT_RGBA8_UINT] = WGPUTextureFormat_RGBA8Uint; + m_FmtToWGPUFmtMap[TEX_FORMAT_RGBA8_SNORM] = WGPUTextureFormat_RGBA8Snorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_RGBA8_SINT] = WGPUTextureFormat_RGBA8Sint; + + m_FmtToWGPUFmtMap[TEX_FORMAT_RG16_TYPELESS] = WGPUTextureFormat_RG16Float; + m_FmtToWGPUFmtMap[TEX_FORMAT_RG16_FLOAT] = WGPUTextureFormat_RG16Float; + m_FmtToWGPUFmtMap[TEX_FORMAT_RG16_UNORM] = WGPUTextureFormat_Undefined; + m_FmtToWGPUFmtMap[TEX_FORMAT_RG16_UINT] = WGPUTextureFormat_RG16Uint; + m_FmtToWGPUFmtMap[TEX_FORMAT_RG16_SNORM] = WGPUTextureFormat_Undefined; + m_FmtToWGPUFmtMap[TEX_FORMAT_RG16_SINT] = WGPUTextureFormat_RG16Sint; + + m_FmtToWGPUFmtMap[TEX_FORMAT_R32_TYPELESS] = WGPUTextureFormat_R32Float; + m_FmtToWGPUFmtMap[TEX_FORMAT_D32_FLOAT] = WGPUTextureFormat_Depth32Float; + m_FmtToWGPUFmtMap[TEX_FORMAT_R32_FLOAT] = WGPUTextureFormat_R32Float; + m_FmtToWGPUFmtMap[TEX_FORMAT_R32_UINT] = WGPUTextureFormat_R32Uint; + m_FmtToWGPUFmtMap[TEX_FORMAT_R32_SINT] = WGPUTextureFormat_R32Sint; + + m_FmtToWGPUFmtMap[TEX_FORMAT_R24G8_TYPELESS] = WGPUTextureFormat_Depth24PlusStencil8; + m_FmtToWGPUFmtMap[TEX_FORMAT_D24_UNORM_S8_UINT] = WGPUTextureFormat_Depth24PlusStencil8; + m_FmtToWGPUFmtMap[TEX_FORMAT_R24_UNORM_X8_TYPELESS] = WGPUTextureFormat_Depth24PlusStencil8; + m_FmtToWGPUFmtMap[TEX_FORMAT_X24_TYPELESS_G8_UINT] = WGPUTextureFormat_Undefined; + + m_FmtToWGPUFmtMap[TEX_FORMAT_RG8_TYPELESS] = WGPUTextureFormat_RG8Unorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_RG8_UNORM] = WGPUTextureFormat_RG8Unorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_RG8_UINT] = WGPUTextureFormat_RG8Uint; + m_FmtToWGPUFmtMap[TEX_FORMAT_RG8_SNORM] = WGPUTextureFormat_RG8Snorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_RG8_SINT] = WGPUTextureFormat_RG8Sint; + + m_FmtToWGPUFmtMap[TEX_FORMAT_R16_TYPELESS] = WGPUTextureFormat_R16Float; + m_FmtToWGPUFmtMap[TEX_FORMAT_R16_FLOAT] = WGPUTextureFormat_R16Float; + m_FmtToWGPUFmtMap[TEX_FORMAT_D16_UNORM] = WGPUTextureFormat_Depth16Unorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_R16_UNORM] = WGPUTextureFormat_Undefined; + m_FmtToWGPUFmtMap[TEX_FORMAT_R16_UINT] = WGPUTextureFormat_R16Uint; + m_FmtToWGPUFmtMap[TEX_FORMAT_R16_SNORM] = WGPUTextureFormat_Undefined; + m_FmtToWGPUFmtMap[TEX_FORMAT_R16_SINT] = WGPUTextureFormat_R16Sint; + + m_FmtToWGPUFmtMap[TEX_FORMAT_R8_TYPELESS] = WGPUTextureFormat_R8Unorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_R8_UNORM] = WGPUTextureFormat_R8Unorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_R8_UINT] = WGPUTextureFormat_R8Uint; + m_FmtToWGPUFmtMap[TEX_FORMAT_R8_SNORM] = WGPUTextureFormat_R8Snorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_R8_SINT] = WGPUTextureFormat_R8Sint; + + m_FmtToWGPUFmtMap[TEX_FORMAT_A8_UNORM] = WGPUTextureFormat_R8Unorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_R1_UNORM] = WGPUTextureFormat_Undefined; + + m_FmtToWGPUFmtMap[TEX_FORMAT_RGB9E5_SHAREDEXP] = WGPUTextureFormat_RGB9E5Ufloat; + m_FmtToWGPUFmtMap[TEX_FORMAT_RG8_B8G8_UNORM] = WGPUTextureFormat_Undefined; + m_FmtToWGPUFmtMap[TEX_FORMAT_G8R8_G8B8_UNORM] = WGPUTextureFormat_Undefined; + + m_FmtToWGPUFmtMap[TEX_FORMAT_BC1_TYPELESS] = WGPUTextureFormat_BC1RGBAUnorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_BC1_UNORM] = WGPUTextureFormat_BC1RGBAUnorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_BC1_UNORM_SRGB] = WGPUTextureFormat_BC1RGBAUnormSrgb; + m_FmtToWGPUFmtMap[TEX_FORMAT_BC2_TYPELESS] = WGPUTextureFormat_BC2RGBAUnorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_BC2_UNORM] = WGPUTextureFormat_BC2RGBAUnorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_BC2_UNORM_SRGB] = WGPUTextureFormat_BC2RGBAUnormSrgb; + m_FmtToWGPUFmtMap[TEX_FORMAT_BC3_TYPELESS] = WGPUTextureFormat_BC3RGBAUnorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_BC3_UNORM] = WGPUTextureFormat_BC3RGBAUnorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_BC3_UNORM_SRGB] = WGPUTextureFormat_BC3RGBAUnormSrgb; + m_FmtToWGPUFmtMap[TEX_FORMAT_BC4_TYPELESS] = WGPUTextureFormat_BC4RUnorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_BC4_UNORM] = WGPUTextureFormat_BC4RUnorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_BC4_SNORM] = WGPUTextureFormat_BC4RSnorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_BC5_TYPELESS] = WGPUTextureFormat_BC5RGUnorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_BC5_UNORM] = WGPUTextureFormat_BC5RGUnorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_BC5_SNORM] = WGPUTextureFormat_BC5RGSnorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_B5G6R5_UNORM] = WGPUTextureFormat_Undefined; + m_FmtToWGPUFmtMap[TEX_FORMAT_B5G5R5A1_UNORM] = WGPUTextureFormat_Undefined; + + m_FmtToWGPUFmtMap[TEX_FORMAT_BGRA8_UNORM] = WGPUTextureFormat_BGRA8Unorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_BGRX8_UNORM] = WGPUTextureFormat_BGRA8Unorm; + + m_FmtToWGPUFmtMap[TEX_FORMAT_BGRA8_TYPELESS] = WGPUTextureFormat_BGRA8Unorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_BGRA8_UNORM_SRGB] = WGPUTextureFormat_BGRA8UnormSrgb; + m_FmtToWGPUFmtMap[TEX_FORMAT_BGRX8_TYPELESS] = WGPUTextureFormat_BGRA8Unorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_BGRX8_UNORM_SRGB] = WGPUTextureFormat_BGRA8UnormSrgb; + m_FmtToWGPUFmtMap[TEX_FORMAT_BC6H_TYPELESS] = WGPUTextureFormat_BC6HRGBUfloat; + m_FmtToWGPUFmtMap[TEX_FORMAT_BC6H_UF16] = WGPUTextureFormat_BC6HRGBUfloat; + m_FmtToWGPUFmtMap[TEX_FORMAT_BC6H_SF16] = WGPUTextureFormat_BC6HRGBFloat; + m_FmtToWGPUFmtMap[TEX_FORMAT_BC7_TYPELESS] = WGPUTextureFormat_BC7RGBAUnorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_BC7_UNORM] = WGPUTextureFormat_BC7RGBAUnorm; + m_FmtToWGPUFmtMap[TEX_FORMAT_BC7_UNORM_SRGB] = WGPUTextureFormat_BC7RGBAUnormSrgb; + + m_FmtToWGPUFmtMap[TEX_FORMAT_R10G10B10_XR_BIAS_A2_UNORM] = WGPUTextureFormat_Undefined; + // clang-format on + } + + WGPUTextureFormat operator[](TEXTURE_FORMAT TexFmt) const + { + VERIFY_EXPR(TexFmt < _countof(m_FmtToWGPUFmtMap)); + return m_FmtToWGPUFmtMap[TexFmt]; + } + +private: + WGPUTextureFormat m_FmtToWGPUFmtMap[TEX_FORMAT_NUM_FORMATS] = {}; +}; + +WGPUTextureFormat TexFormatToWGPUFormat(TEXTURE_FORMAT TexFmt) +{ + const static TexFormatToWebGPUFormatMapper FmtMapper; + return FmtMapper[TexFmt]; +} + +WGPUTextureViewDimension ResourceDimensionToWGPUTextureViewDimension(RESOURCE_DIMENSION ResourceDim) +{ + switch (ResourceDim) + { + // clang-format off + case RESOURCE_DIM_TEX_1D: return WGPUTextureViewDimension_1D; + case RESOURCE_DIM_TEX_2D: return WGPUTextureViewDimension_2D; + case RESOURCE_DIM_TEX_2D_ARRAY: return WGPUTextureViewDimension_2DArray; + case RESOURCE_DIM_TEX_3D: return WGPUTextureViewDimension_3D; + case RESOURCE_DIM_TEX_CUBE: return WGPUTextureViewDimension_Cube; + case RESOURCE_DIM_TEX_CUBE_ARRAY: return WGPUTextureViewDimension_CubeArray; + // clang-format on + default: + UNEXPECTED("Unexpected resource dimension"); + return WGPUTextureViewDimension_Undefined; + } + static_assert(RESOURCE_DIM_NUM_DIMENSIONS == 9, "Please update the switch above to handle the new resource dimension"); +} + +WGPUAddressMode TexAddressModeToWGPUAddressMode(TEXTURE_ADDRESS_MODE Mode) +{ + switch (Mode) + { + case TEXTURE_ADDRESS_UNKNOWN: + UNEXPECTED("Unknown address mode"); + return WGPUAddressMode_ClampToEdge; + + // clang-format off + case TEXTURE_ADDRESS_WRAP: return WGPUAddressMode_Repeat; + case TEXTURE_ADDRESS_MIRROR: return WGPUAddressMode_MirrorRepeat; + case TEXTURE_ADDRESS_CLAMP: return WGPUAddressMode_ClampToEdge; + // clang-format on + default: + UNEXPECTED("Unexpected texture address mode"); + return WGPUAddressMode_ClampToEdge; + } +} + +WGPUFilterMode FilterTypeToWGPUFilterMode(FILTER_TYPE FilterType) +{ + switch (FilterType) + { + case FILTER_TYPE_UNKNOWN: + UNEXPECTED("Unknown filter type"); + return WGPUFilterMode_Nearest; + + case FILTER_TYPE_POINT: + case FILTER_TYPE_COMPARISON_POINT: + case FILTER_TYPE_MINIMUM_POINT: + case FILTER_TYPE_MAXIMUM_POINT: + return WGPUFilterMode_Nearest; + + case FILTER_TYPE_LINEAR: + case FILTER_TYPE_ANISOTROPIC: + case FILTER_TYPE_COMPARISON_LINEAR: + case FILTER_TYPE_COMPARISON_ANISOTROPIC: + case FILTER_TYPE_MINIMUM_LINEAR: + case FILTER_TYPE_MINIMUM_ANISOTROPIC: + case FILTER_TYPE_MAXIMUM_LINEAR: + case FILTER_TYPE_MAXIMUM_ANISOTROPIC: + return WGPUFilterMode_Linear; + + default: + UNEXPECTED("Unexpected filter type"); + return WGPUFilterMode_Nearest; + } +} + +WGPUMipmapFilterMode FilterTypeToWGPUMipMapMode(FILTER_TYPE FilterType) +{ + switch (FilterType) + { + case FILTER_TYPE_UNKNOWN: + UNEXPECTED("Unknown filter type"); + return WGPUMipmapFilterMode_Nearest; + + case FILTER_TYPE_POINT: + case FILTER_TYPE_COMPARISON_POINT: + case FILTER_TYPE_MINIMUM_POINT: + case FILTER_TYPE_MAXIMUM_POINT: + return WGPUMipmapFilterMode_Nearest; + + case FILTER_TYPE_LINEAR: + case FILTER_TYPE_ANISOTROPIC: + case FILTER_TYPE_COMPARISON_LINEAR: + case FILTER_TYPE_COMPARISON_ANISOTROPIC: + case FILTER_TYPE_MINIMUM_LINEAR: + case FILTER_TYPE_MINIMUM_ANISOTROPIC: + case FILTER_TYPE_MAXIMUM_LINEAR: + case FILTER_TYPE_MAXIMUM_ANISOTROPIC: + return WGPUMipmapFilterMode_Linear; + + default: + UNEXPECTED("Only point and linear filter types are allowed for mipmap mode"); + return WGPUMipmapFilterMode_Nearest; + } +} + +WGPUCompareFunction ComparisonFuncToWGPUCompareFunction(COMPARISON_FUNCTION CmpFunc) +{ + switch (CmpFunc) + { + case COMPARISON_FUNC_UNKNOWN: + UNEXPECTED("Comparison function is not specified"); + return WGPUCompareFunction_Always; + + // clang-format off + case COMPARISON_FUNC_NEVER: return WGPUCompareFunction_Never; + case COMPARISON_FUNC_LESS: return WGPUCompareFunction_Less; + case COMPARISON_FUNC_EQUAL: return WGPUCompareFunction_Equal; + case COMPARISON_FUNC_LESS_EQUAL: return WGPUCompareFunction_LessEqual; + case COMPARISON_FUNC_GREATER: return WGPUCompareFunction_Greater; + case COMPARISON_FUNC_NOT_EQUAL: return WGPUCompareFunction_NotEqual; + case COMPARISON_FUNC_GREATER_EQUAL: return WGPUCompareFunction_GreaterEqual; + case COMPARISON_FUNC_ALWAYS: return WGPUCompareFunction_Always; + // clang-format on + + default: + UNEXPECTED("Unknown comparison function"); + return WGPUCompareFunction_Always; + } +} + +WGPUVertexFormat VertexFormatAttribsToWGPUVertexFormat(VALUE_TYPE ValueType, Uint32 NumComponents, bool IsNormalized) +{ + switch (ValueType) + { + case VT_FLOAT16: + { + VERIFY(!IsNormalized, "Floating point formats cannot be normalized"); + switch (NumComponents) + { + case 2: return WGPUVertexFormat_Float16x2; + case 4: return WGPUVertexFormat_Float16x4; + default: UNEXPECTED("Unusupported number of components"); return WGPUVertexFormat_Undefined; + } + } + case VT_FLOAT32: + { + VERIFY(!IsNormalized, "Floating point formats cannot be normalized"); + switch (NumComponents) + { + case 1: return WGPUVertexFormat_Float32; + case 2: return WGPUVertexFormat_Float32x2; + case 3: return WGPUVertexFormat_Float32x3; + case 4: return WGPUVertexFormat_Float32x4; + default: UNEXPECTED("Unusupported number of components"); return WGPUVertexFormat_Undefined; + } + } + case VT_INT32: + { + VERIFY(!IsNormalized, "32-bit UNORM formats are not supported. Use R32_FLOAT instead"); + switch (NumComponents) + { + case 1: return WGPUVertexFormat_Sint32; + case 2: return WGPUVertexFormat_Sint32x2; + case 3: return WGPUVertexFormat_Sint32x3; + case 4: return WGPUVertexFormat_Sint32x4; + default: UNEXPECTED("Unusupported number of components"); return WGPUVertexFormat_Undefined; + } + } + case VT_UINT32: + { + VERIFY(!IsNormalized, "32-bit UNORM formats are not supported. Use R32_FLOAT instead"); + switch (NumComponents) + { + case 1: return WGPUVertexFormat_Uint32; + case 2: return WGPUVertexFormat_Uint32x2; + case 3: return WGPUVertexFormat_Uint32x3; + case 4: return WGPUVertexFormat_Uint32x4; + default: UNEXPECTED("Unusupported number of components"); return WGPUVertexFormat_Undefined; + } + } + case VT_INT16: + { + if (IsNormalized) + { + switch (NumComponents) + { + case 2: return WGPUVertexFormat_Snorm16x2; + case 4: return WGPUVertexFormat_Snorm16x4; + default: UNEXPECTED("Unusupported number of components"); return WGPUVertexFormat_Undefined; + } + } + else + { + switch (NumComponents) + { + case 2: return WGPUVertexFormat_Sint16x2; + case 4: return WGPUVertexFormat_Sint16x4; + default: UNEXPECTED("Unusupported number of components"); return WGPUVertexFormat_Undefined; + } + } + } + case VT_UINT16: + { + if (IsNormalized) + { + switch (NumComponents) + { + case 2: return WGPUVertexFormat_Unorm16x2; + case 4: return WGPUVertexFormat_Unorm16x4; + default: UNEXPECTED("Unusupported number of components"); return WGPUVertexFormat_Undefined; + } + } + else + { + switch (NumComponents) + { + case 2: return WGPUVertexFormat_Uint16x2; + case 4: return WGPUVertexFormat_Uint16x4; + default: UNEXPECTED("Unusupported number of components"); return WGPUVertexFormat_Undefined; + } + } + } + case VT_INT8: + { + if (IsNormalized) + { + switch (NumComponents) + { + case 2: return WGPUVertexFormat_Snorm8x2; + case 4: return WGPUVertexFormat_Snorm8x4; + default: UNEXPECTED("Unusupported number of components"); return WGPUVertexFormat_Undefined; + } + } + else + { + switch (NumComponents) + { + case 2: return WGPUVertexFormat_Sint8x2; + case 4: return WGPUVertexFormat_Sint8x4; + default: UNEXPECTED("Unusupported number of components"); return WGPUVertexFormat_Undefined; + } + } + } + case VT_UINT8: + { + if (IsNormalized) + { + switch (NumComponents) + { + case 2: return WGPUVertexFormat_Unorm8x2; + case 4: return WGPUVertexFormat_Unorm8x4; + default: UNEXPECTED("Unusupported number of components"); return WGPUVertexFormat_Undefined; + } + } + else + { + switch (NumComponents) + { + case 2: return WGPUVertexFormat_Uint8x2; + case 4: return WGPUVertexFormat_Uint8x4; + default: UNEXPECTED("Unusupported number of components"); return WGPUVertexFormat_Undefined; + } + } + } + + default: UNEXPECTED("Unusupported format"); return WGPUVertexFormat_Undefined; + } +} + +WGPUTextureFormat BufferFormatToWGPUTextureFormat(const BufferFormat& BuffFmt) +{ + switch (BuffFmt.ValueType) + { + case VT_FLOAT16: + { + VERIFY(!BuffFmt.IsNormalized, "Floating point formats cannot be normalized"); + switch (BuffFmt.NumComponents) + { + case 1: return WGPUTextureFormat_R16Float; + case 2: return WGPUTextureFormat_RG16Float; + case 4: return WGPUTextureFormat_RGBA16Float; + default: UNEXPECTED("Unusupported number of components"); return WGPUTextureFormat_Undefined; + } + } + case VT_FLOAT32: + { + VERIFY(!BuffFmt.IsNormalized, "Floating point formats cannot be normalized"); + switch (BuffFmt.NumComponents) + { + case 1: return WGPUTextureFormat_R32Float; + case 2: return WGPUTextureFormat_RG32Float; + case 4: return WGPUTextureFormat_RGBA32Float; + default: UNEXPECTED("Unusupported number of components"); return WGPUTextureFormat_Undefined; + } + } + case VT_INT32: + { + VERIFY(!BuffFmt.IsNormalized, "32-bit UNORM formats are not supported. Use R32_FLOAT instead"); + switch (BuffFmt.NumComponents) + { + case 1: return WGPUTextureFormat_R32Sint; + case 2: return WGPUTextureFormat_RG32Sint; + case 4: return WGPUTextureFormat_RGBA32Sint; + default: UNEXPECTED("Unusupported number of components"); return WGPUTextureFormat_Undefined; + } + } + case VT_UINT32: + { + VERIFY(!BuffFmt.IsNormalized, "32-bit UNORM formats are not supported. Use R32_FLOAT instead"); + switch (BuffFmt.NumComponents) + { + case 1: return WGPUTextureFormat_R32Uint; + case 2: return WGPUTextureFormat_RG32Uint; + case 4: return WGPUTextureFormat_RGBA32Uint; + default: UNEXPECTED("Unusupported number of components"); return WGPUTextureFormat_Undefined; + } + } + case VT_INT16: + { + + VERIFY(!BuffFmt.IsNormalized, "16-bit UNORM formats are not supported. Use R16_FLOAT instead"); + switch (BuffFmt.NumComponents) + { + case 1: return WGPUTextureFormat_R16Sint; + case 2: return WGPUTextureFormat_RG16Sint; + case 4: return WGPUTextureFormat_RGBA16Sint; + default: UNEXPECTED("Unusupported number of components"); return WGPUTextureFormat_Undefined; + } + } + case VT_UINT16: + { + VERIFY(!BuffFmt.IsNormalized, "16-bit UNORM formats are not supported. Use R16_FLOAT instead"); + switch (BuffFmt.NumComponents) + { + case 1: return WGPUTextureFormat_R16Uint; + case 2: return WGPUTextureFormat_RG16Uint; + case 4: return WGPUTextureFormat_RGBA16Uint; + default: UNEXPECTED("Unusupported number of components"); return WGPUTextureFormat_Undefined; + } + } + case VT_INT8: + { + if (BuffFmt.IsNormalized) + { + switch (BuffFmt.NumComponents) + { + case 1: return WGPUTextureFormat_R8Snorm; + case 2: return WGPUTextureFormat_RG8Snorm; + case 4: return WGPUTextureFormat_RGBA8Snorm; + default: UNEXPECTED("Unusupported number of components"); return WGPUTextureFormat_Undefined; + } + } + else + { + switch (BuffFmt.NumComponents) + { + case 1: return WGPUTextureFormat_R8Sint; + case 2: return WGPUTextureFormat_RG8Sint; + case 4: return WGPUTextureFormat_RGBA8Sint; + default: UNEXPECTED("Unusupported number of components"); return WGPUTextureFormat_Undefined; + } + } + } + case VT_UINT8: + { + if (BuffFmt.IsNormalized) + { + switch (BuffFmt.NumComponents) + { + case 1: return WGPUTextureFormat_R8Unorm; + case 2: return WGPUTextureFormat_RG8Unorm; + case 4: return WGPUTextureFormat_RGBA8Unorm; + default: UNEXPECTED("Unusupported number of components"); return WGPUTextureFormat_Undefined; + } + } + else + { + switch (BuffFmt.NumComponents) + { + case 1: return WGPUTextureFormat_R8Uint; + case 2: return WGPUTextureFormat_RG8Uint; + case 4: return WGPUTextureFormat_RGBA8Uint; + default: UNEXPECTED("Unusupported number of components"); return WGPUTextureFormat_Undefined; + } + } + } + default: UNEXPECTED("Unusupported format"); return WGPUTextureFormat_Undefined; + } +} + +WGPUQueryType QueryTypeToWGPUQueryType(QUERY_TYPE QueryType) +{ + switch (QueryType) + { + case QUERY_TYPE_OCCLUSION: + return WGPUQueryType_Occlusion; + + case QUERY_TYPE_BINARY_OCCLUSION: + return WGPUQueryType_Occlusion; + + case QUERY_TYPE_PIPELINE_STATISTICS: + return WGPUQueryType_PipelineStatistics; + + case QUERY_TYPE_DURATION: + case QUERY_TYPE_TIMESTAMP: + return WGPUQueryType_Timestamp; + + static_assert(QUERY_TYPE_NUM_TYPES == 6, "Not all QUERY_TYPE enum values are handled"); + default: + UNEXPECTED("Unexpected query type"); + return static_cast(-1); + } +} + +WGPUColorWriteMaskFlags ColorMaskToWGPUColorWriteMask(COLOR_MASK ColorMask) +{ + // clang-format off + return ((ColorMask & COLOR_MASK_RED) ? WGPUColorWriteMask_Red : WGPUColorWriteMask_None) | + ((ColorMask & COLOR_MASK_GREEN) ? WGPUColorWriteMask_Green : WGPUColorWriteMask_None) | + ((ColorMask & COLOR_MASK_BLUE) ? WGPUColorWriteMask_Blue : WGPUColorWriteMask_None) | + ((ColorMask & COLOR_MASK_ALPHA) ? WGPUColorWriteMask_Alpha : WGPUColorWriteMask_None); + // clang-format off +} + +} // namespace Diligent diff --git a/Tests/DiligentCoreAPITest/CMakeLists.txt b/Tests/DiligentCoreAPITest/CMakeLists.txt index 6976a97bbe..3eed0d7ef7 100644 --- a/Tests/DiligentCoreAPITest/CMakeLists.txt +++ b/Tests/DiligentCoreAPITest/CMakeLists.txt @@ -70,6 +70,14 @@ if(GL_SUPPORTED OR GLES_SUPPORTED) list(APPEND SOURCE ${GL_SOURCE}) endif() +if(WEBGPU_SUPPORTED) + file(GLOB WEBGPU_SOURCE LIST_DIRECTORIES false src/WebGPU/*) + file(GLOB WEBGPU_INCLUDE LIST_DIRECTORIES false include/WebGPU/*) + list(APPEND INCLUDE ${WEBGPU_INCLUDE}) + list(APPEND SOURCE ${WEBGPU_SOURCE}) +endif() + + set(ALL_SOURCE ${SOURCE} ${INCLUDE} ${SHADERS} ${INLINE_SHADERS}) add_executable(DiligentCoreAPITest ${ALL_SOURCE}) set_common_target_properties(DiligentCoreAPITest) diff --git a/Tests/DiligentCoreAPITest/include/WebGPU/CreateObjFromNativeResWebGPU.hpp b/Tests/DiligentCoreAPITest/include/WebGPU/CreateObjFromNativeResWebGPU.hpp new file mode 100644 index 0000000000..63543dcdb1 --- /dev/null +++ b/Tests/DiligentCoreAPITest/include/WebGPU/CreateObjFromNativeResWebGPU.hpp @@ -0,0 +1,51 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include "CreateObjFromNativeResTestBase.hpp" + +namespace Diligent +{ + +namespace Testing +{ + +class TestCreateObjFromNativeResWebGPU final : public CreateObjFromNativeResTestBase +{ +public: + TestCreateObjFromNativeResWebGPU(IRenderDevice* pDevice) : + CreateObjFromNativeResTestBase(pDevice) + {} + + void CreateTexture(ITexture* pTexture) override; + + void CreateBuffer(IBuffer* pBuffer) override; +}; + +} // namespace Testing + +} // namespace Diligent diff --git a/Tests/DiligentCoreAPITest/src/BufferCreationTest.cpp b/Tests/DiligentCoreAPITest/src/BufferCreationTest.cpp index 6eb6998c19..b5fe20767e 100644 --- a/Tests/DiligentCoreAPITest/src/BufferCreationTest.cpp +++ b/Tests/DiligentCoreAPITest/src/BufferCreationTest.cpp @@ -45,6 +45,10 @@ # include "Metal/CreateObjFromNativeResMtl.hpp" #endif +#if WEBGPU_SUPPORTED +# include "WebGPU/CreateObjFromNativeResWebGPU.hpp" +#endif + #include "GraphicsAccessories.hpp" #include "GPUTestingEnvironment.hpp" @@ -105,6 +109,12 @@ class BufferCreationTest : public ::testing::Test pCreateObjFromNativeRes.reset(new TestCreateObjFromNativeResMtl(pDevice)); break; #endif +#if WEBGPU_SUPPORTED + case RENDER_DEVICE_TYPE_WEBGPU: + pCreateObjFromNativeRes.reset(new TestCreateObjFromNativeResWebGPU(pDevice)); + break; +#endif + default: UNEXPECTED("Unexpected device type"); } } diff --git a/Tests/DiligentCoreAPITest/src/ClearRenderTargetTest.cpp b/Tests/DiligentCoreAPITest/src/ClearRenderTargetTest.cpp index 8a98f804d7..92c8545f1f 100644 --- a/Tests/DiligentCoreAPITest/src/ClearRenderTargetTest.cpp +++ b/Tests/DiligentCoreAPITest/src/ClearRenderTargetTest.cpp @@ -61,6 +61,10 @@ void ClearRenderTargetReferenceVk(ISwapChain* pSwapChain, const float ClearColor void ClearRenderTargetReferenceMtl(ISwapChain* pSwapChain, const float ClearColor[]); #endif +#if WEBGPU_SUPPORTED +void ClearRenderTargetReferenceWebGPU(ISwapChain* pSwapChain, const float ClearColor[]); +#endif + } // namespace Testing } // namespace Diligent @@ -110,6 +114,12 @@ void ClearRenderTargetReference(IRenderDevice* pDevice, break; #endif +#if WEBGPU_SUPPORTED + case RENDER_DEVICE_TYPE_WEBGPU: + ClearRenderTargetReferenceWebGPU(pSwapChain, ClearColor); + break; +#endif + default: LOG_ERROR_AND_THROW("Unsupported device type"); } diff --git a/Tests/DiligentCoreAPITest/src/WebGPU/ClearRenderTargetReferenceWebGPU.cpp b/Tests/DiligentCoreAPITest/src/WebGPU/ClearRenderTargetReferenceWebGPU.cpp new file mode 100644 index 0000000000..24cd514d9c --- /dev/null +++ b/Tests/DiligentCoreAPITest/src/WebGPU/ClearRenderTargetReferenceWebGPU.cpp @@ -0,0 +1,62 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "WebGPU/TestingEnvironmentWebGPU.hpp" +#include "WebGPU/TestingSwapChainWebGPU.hpp" + +namespace Diligent +{ + +namespace Testing +{ + +void ClearRenderTargetReferenceWebGPU(ISwapChain* pSwapChain, const float ClearColor[]) +{ + auto* pEnvWebGPU = TestingEnvironmentWebGPU::GetInstance(); + auto* pTestingSwapChainWebGPU = ClassPtrCast(pSwapChain); + + auto wgpuCmdEncoder = pEnvWebGPU->CreateCommandEncoder(); + + WGPURenderPassColorAttachment wgpuRenderPassColorAttachment = {pTestingSwapChainWebGPU->GetWebGPUColorTextureView()}; + + WGPURenderPassDescriptor wgpuRenderPassDesc = {}; + wgpuRenderPassColorAttachment.resolveTarget = nullptr; + wgpuRenderPassColorAttachment.loadOp = WGPULoadOp_Clear; + wgpuRenderPassColorAttachment.storeOp = WGPUStoreOp_Store; + wgpuRenderPassColorAttachment.clearValue = WGPUColor{ClearColor[0], ClearColor[1], ClearColor[2], ClearColor[3]}; + + wgpuRenderPassDesc.colorAttachmentCount = 1; + wgpuRenderPassDesc.colorAttachments = &wgpuRenderPassColorAttachment; + + WGPURenderPassEncoder wgpuRenderPass = wgpuCommandEncoderBeginRenderPass(wgpuCmdEncoder, &wgpuRenderPassDesc); + wgpuRenderPassEncoderEnd(wgpuRenderPass); + + pEnvWebGPU->SubmitCommandEncoder(wgpuCmdEncoder); +} + +} // namespace Testing + +} // namespace Diligent diff --git a/Tests/DiligentCoreAPITest/src/WebGPU/CreateObjFromNativeResWebGPU.cpp b/Tests/DiligentCoreAPITest/src/WebGPU/CreateObjFromNativeResWebGPU.cpp new file mode 100644 index 0000000000..1c806cedfb --- /dev/null +++ b/Tests/DiligentCoreAPITest/src/WebGPU/CreateObjFromNativeResWebGPU.cpp @@ -0,0 +1,94 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include + +#include "RenderDeviceWebGPU.h" +#include "TextureWebGPU.h" +#include "BufferWebGPU.h" +#include "GPUTestingEnvironment.hpp" +#include "WebGPU/CreateObjFromNativeResWebGPU.hpp" + +#include "gtest/gtest.h" + +namespace Diligent +{ + +namespace Testing +{ + +void TestCreateObjFromNativeResWebGPU::CreateTexture(ITexture* pTexture) +{ + RefCntAutoPtr pDeviceWebGPU(m_pDevice, IID_RenderDeviceWebGPU); + RefCntAutoPtr pTextureWebGPU(pTexture, IID_TextureWebGPU); + ASSERT_NE(pDeviceWebGPU, nullptr); + ASSERT_NE(pTextureWebGPU, nullptr); + + const auto& SrcTexDesc = pTexture->GetDesc(); + const auto wgpuTextureHandle = pTextureWebGPU->GetWebGPUTexture(); + ASSERT_NE(wgpuTextureHandle, nullptr); + + RefCntAutoPtr pAttachedTexture; + pDeviceWebGPU->CreateTextureFromWebGPUTexture(wgpuTextureHandle, SrcTexDesc, RESOURCE_STATE_UNKNOWN, &pAttachedTexture); + ASSERT_NE(pAttachedTexture, nullptr); + + const auto& TestTexDesc = pAttachedTexture->GetDesc(); + EXPECT_EQ(TestTexDesc, SrcTexDesc); + + RefCntAutoPtr pAttachedTextureWebGPU(pAttachedTexture, IID_TextureWebGPU); + ASSERT_NE(pAttachedTextureWebGPU, nullptr); + EXPECT_EQ(pAttachedTextureWebGPU->GetWebGPUTexture(), wgpuTextureHandle); + EXPECT_EQ(BitCast(pAttachedTextureWebGPU->GetNativeHandle()), wgpuTextureHandle); +} + +void TestCreateObjFromNativeResWebGPU::CreateBuffer(IBuffer* pBuffer) +{ + RefCntAutoPtr pDeviceWebGPU(m_pDevice, IID_RenderDeviceWebGPU); + RefCntAutoPtr pBufferWebGPU(pBuffer, IID_BufferWebGPU); + ASSERT_NE(pDeviceWebGPU, nullptr); + ASSERT_NE(pBufferWebGPU, nullptr); + + const auto& SrcBufDesc = pBuffer->GetDesc(); + const auto wgpuBufferHandle = pBufferWebGPU->GetWebGPUBuffer(); + ASSERT_NE(wgpuBufferHandle, nullptr); + + RefCntAutoPtr pAttachedBuffer; + pDeviceWebGPU->CreateTextureFromWebGPUBuffer(wgpuBufferHandle, SrcBufDesc, RESOURCE_STATE_UNKNOWN, &pAttachedBuffer); + ASSERT_NE(pAttachedBuffer, nullptr); + + const auto& TestBufDesc = pAttachedBuffer->GetDesc(); + EXPECT_EQ(TestBufDesc, SrcBufDesc); + + RefCntAutoPtr pAttachedBufferWebGPU(pAttachedBuffer, IID_BufferWebGPU); + ASSERT_NE(pAttachedBufferWebGPU, nullptr); + EXPECT_EQ(pAttachedBufferWebGPU->GetWebGPUBuffer(), wgpuBufferHandle); + EXPECT_EQ(BitCast(pAttachedBufferWebGPU->GetNativeHandle()), wgpuBufferHandle); + +} + +} // namespace Testing + +} // namespace Diligent diff --git a/Tests/GPUTestFramework/CMakeLists.txt b/Tests/GPUTestFramework/CMakeLists.txt index c6dba34435..8f3ee29dd1 100644 --- a/Tests/GPUTestFramework/CMakeLists.txt +++ b/Tests/GPUTestFramework/CMakeLists.txt @@ -54,6 +54,13 @@ if(GL_SUPPORTED OR GLES_SUPPORTED) list(APPEND SOURCE ${GL_SOURCE}) endif() +if(WEBGPU_SUPPORTED) + file(GLOB WEBGPU_SOURCE LIST_DIRECTORIES false src/WebGPU/*) + file(GLOB WEBGPU_INCLUDE LIST_DIRECTORIES false include/WebGPU/*) + list(APPEND INCLUDE ${WEBGPU_INCLUDE}) + list(APPEND SOURCE ${WEBGPU_SOURCE}) +endif() + set(ALL_SOURCE ${SOURCE} ${INCLUDE}) add_library(Diligent-GPUTestFramework STATIC ${ALL_SOURCE}) set_common_target_properties(Diligent-GPUTestFramework) @@ -79,6 +86,11 @@ if(D3D12_SUPPORTED) target_link_libraries(Diligent-GPUTestFramework PUBLIC d3d12.lib) endif() +if(WEBGPU_SUPPORTED) + target_link_libraries(Diligent-GPUTestFramework PUBLIC webgpu) + +endif() + if(GL_SUPPORTED OR GLES_SUPPORTED) if(PLATFORM_WIN32) target_link_libraries(Diligent-GPUTestFramework PUBLIC GLEW::glew opengl32.lib) @@ -126,6 +138,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") endif() endif() + target_include_directories(Diligent-GPUTestFramework PUBLIC include diff --git a/Tests/GPUTestFramework/include/WebGPU/TestingEnvironmentWebGPU.hpp b/Tests/GPUTestFramework/include/WebGPU/TestingEnvironmentWebGPU.hpp new file mode 100644 index 0000000000..b7bf27b47b --- /dev/null +++ b/Tests/GPUTestFramework/include/WebGPU/TestingEnvironmentWebGPU.hpp @@ -0,0 +1,62 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include +#include "GPUTestingEnvironment.hpp" + +namespace Diligent +{ + +namespace Testing +{ + +class TestingEnvironmentWebGPU final : public GPUTestingEnvironment +{ +public: + using CreateInfo = GPUTestingEnvironment::CreateInfo; + + TestingEnvironmentWebGPU(const CreateInfo& CI, + const SwapChainDesc& SCDesc); + + ~TestingEnvironmentWebGPU(); + + static TestingEnvironmentWebGPU* GetInstance() { return ClassPtrCast(GPUTestingEnvironment::GetInstance()); } + + WGPUDevice GetWebGPUDevice() { return m_wgpuDevice; } + + WGPUCommandEncoder CreateCommandEncoder(); + + void SubmitCommandEncoder(WGPUCommandEncoder wgpuCmdEncoder); + +private: + WGPUDevice m_wgpuDevice = nullptr; +}; + +} // namespace Testing + +} // namespace Diligent diff --git a/Tests/GPUTestFramework/include/WebGPU/TestingSwapChainWebGPU.hpp b/Tests/GPUTestFramework/include/WebGPU/TestingSwapChainWebGPU.hpp new file mode 100644 index 0000000000..a07747d1c8 --- /dev/null +++ b/Tests/GPUTestFramework/include/WebGPU/TestingSwapChainWebGPU.hpp @@ -0,0 +1,85 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ +#pragma once + +#include + +#include "TestingSwapChainBase.hpp" + +namespace Diligent +{ + +namespace Testing +{ + +class TestingEnvironmentWebGPU; + +class TestingSwapChainWebGPU final : public TestingSwapChainBase +{ +public: + using TBase = TestingSwapChainBase; + + TestingSwapChainWebGPU(IReferenceCounters* pRefCounters, + TestingEnvironmentWebGPU* pEnv, + const SwapChainDesc& SCDesc); + + ~TestingSwapChainWebGPU(); + + void TakeSnapshot(ITexture* pCopyFrom) override; + + WGPUTexture GetWebGPUColorTexture() + { + return m_wgpuColorTexture; + } + + WGPUTextureView GetWebGPUColorTextureView() + { + return m_wgpuColorTextureView; + } + + WGPUTexture GetWebGPUDepthTexture() + { + return m_wgpuDepthTexture; + } + + WGPUTextureView GetWebGPUDepthTextureView() + { + return m_wgpuDepthTextureView; + } + +private: + WGPUDevice m_wgpuDevice = nullptr; + WGPUTexture m_wgpuColorTexture = nullptr; + WGPUTexture m_wgpuDepthTexture = nullptr; + WGPUTextureView m_wgpuColorTextureView = nullptr; + WGPUTextureView m_wgpuDepthTextureView = nullptr; + WGPUBuffer m_wgpuStagingBuffer = nullptr; + std::vector m_ReferenceData; +}; + +} // namespace Testing + +} // namespace Diligent diff --git a/Tests/GPUTestFramework/src/GPUTestingEnvironment.cpp b/Tests/GPUTestFramework/src/GPUTestingEnvironment.cpp index dc591c6a85..97d8f449f6 100644 --- a/Tests/GPUTestFramework/src/GPUTestingEnvironment.cpp +++ b/Tests/GPUTestFramework/src/GPUTestingEnvironment.cpp @@ -57,6 +57,10 @@ # include "EngineFactoryMtl.h" #endif +#if WEBGPU_SUPPORTED +# include "EngineFactoryWebGPU.h" +#endif + #if ARCHIVER_SUPPORTED # include "ArchiverFactoryLoader.h" #endif @@ -88,6 +92,10 @@ GPUTestingEnvironment* CreateTestingEnvironmentVk(const GPUTestingEnvironment::C GPUTestingEnvironment* CreateTestingEnvironmentMtl(const GPUTestingEnvironment::CreateInfo& CI, const SwapChainDesc& SCDesc); #endif +#if WEBGPU_SUPPORTED +GPUTestingEnvironment* CreateTestingEnvironmentWebGPU(const GPUTestingEnvironment::CreateInfo& CI, const SwapChainDesc& SCDesc); +#endif + Uint32 GPUTestingEnvironment::FindAdapter(const std::vector& Adapters, ADAPTER_TYPE AdapterType, Uint32 AdapterId) @@ -443,7 +451,25 @@ GPUTestingEnvironment::GPUTestingEnvironment(const CreateInfo& EnvCI, const Swap } break; #endif - +#if WEBGPU_SUPPORTED + case RENDER_DEVICE_TYPE_WEBGPU: + { +# if ENGINE_DLL + auto GetEngineFactoryWebGPU = LoadGraphicsEngineWebGPU(); + if (GetEngineFactoryWebGPU == nullptr) + { + LOG_ERROR_AND_THROW("Failed to load the engine"); + } +# endif + auto* pFactoryWGPU = GetEngineFactoryWebGPU(); + pFactoryWGPU->SetMessageCallback(MessageCallback); + EngineWebGPUCreateInfo EngineCI{}; + EngineCI.Features = EnvCI.Features; + ppContexts.resize(std::max(size_t{1}, ContextCI.size()) + NumDeferredCtx); + pFactoryWGPU->CreateDeviceAndContextsWebGPU(EngineCI, &m_pDevice, ppContexts.data()); + } +#endif + break; default: LOG_ERROR_AND_THROW("Unknown device type"); break; @@ -829,6 +855,10 @@ GPUTestingEnvironment* GPUTestingEnvironment::Initialize(int argc, char** argv) { TestEnvCI.deviceType = RENDER_DEVICE_TYPE_METAL; } + else if (strcmp(arg, "--mode=webgpu") == 0) + { + TestEnvCI.deviceType = RENDER_DEVICE_TYPE_WEBGPU; + } else if (AdapterArgName.compare(0, AdapterArgName.length(), arg, AdapterArgName.length()) == 0) { const auto* AdapterStr = arg + AdapterArgName.length(); @@ -903,6 +933,12 @@ GPUTestingEnvironment* GPUTestingEnvironment::Initialize(int argc, char** argv) break; #endif +#if WEBGPU_SUPPORTED + case RENDER_DEVICE_TYPE_WEBGPU: + pEnv = CreateTestingEnvironmentWebGPU(TestEnvCI, SCDesc); + break; +#endif + #if METAL_SUPPORTED case RENDER_DEVICE_TYPE_METAL: pEnv = CreateTestingEnvironmentMtl(TestEnvCI, SCDesc); diff --git a/Tests/GPUTestFramework/src/WebGPU/TestingEnvironmentWebGPU.cpp b/Tests/GPUTestFramework/src/WebGPU/TestingEnvironmentWebGPU.cpp new file mode 100644 index 0000000000..3e2877b562 --- /dev/null +++ b/Tests/GPUTestFramework/src/WebGPU/TestingEnvironmentWebGPU.cpp @@ -0,0 +1,87 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "WebGPU/TestingEnvironmentWebGPU.hpp" +#include "RenderDeviceWebGPU.h" +#include "DeviceContextWebGPU.h" + +namespace Diligent +{ + +namespace Testing +{ + +void CreateTestingSwapChainWebGPU(TestingEnvironmentWebGPU* pEnv, + const SwapChainDesc& SCDesc, + ISwapChain** ppSwapChain); + +TestingEnvironmentWebGPU::TestingEnvironmentWebGPU(const CreateInfo& CI, + const SwapChainDesc& SCDesc) : + GPUTestingEnvironment{CI, SCDesc} +{ + RefCntAutoPtr pRenderDeviceWebGPU{m_pDevice, IID_RenderDeviceWebGPU}; + RefCntAutoPtr pDeviceContextWebGPU{GetDeviceContext(), IID_DeviceContextWebGPU}; + + m_wgpuDevice = pRenderDeviceWebGPU->GetWebGPUDevice(); + + if (m_pSwapChain == nullptr) + CreateTestingSwapChainWebGPU(this, SCDesc, &m_pSwapChain); +} + +TestingEnvironmentWebGPU::~TestingEnvironmentWebGPU() +{ +} + +WGPUCommandEncoder TestingEnvironmentWebGPU::CreateCommandEncoder() +{ + WGPUCommandEncoderDescriptor wgpuCmdEncoderDesc{}; + WGPUCommandEncoder wgpuCmdEncoder = wgpuDeviceCreateCommandEncoder(m_wgpuDevice, &wgpuCmdEncoderDesc); + VERIFY_EXPR(wgpuCmdEncoder != nullptr); + return wgpuCmdEncoder; +} + + +void TestingEnvironmentWebGPU::SubmitCommandEncoder(WGPUCommandEncoder wgpuCmdEncoder) +{ + WGPUCommandBufferDescriptor wgpuCmdBufferDesc{}; + WGPUCommandBuffer wgpuCmdBuffer = wgpuCommandEncoderFinish(wgpuCmdEncoder, &wgpuCmdBufferDesc); + VERIFY_EXPR(wgpuCmdBuffer != nullptr); + + WGPUQueue wgpuCmdQueue = wgpuDeviceGetQueue(m_wgpuDevice); + VERIFY_EXPR(wgpuCmdQueue != nullptr); + + wgpuQueueSubmit(wgpuCmdQueue, 1, &wgpuCmdBuffer); +} + +GPUTestingEnvironment* CreateTestingEnvironmentWebGPU(const GPUTestingEnvironment::CreateInfo& CI, + const SwapChainDesc& SCDesc) +{ + return new TestingEnvironmentWebGPU{CI, SCDesc}; +} + +} // namespace Testing + +} // namespace Diligent diff --git a/Tests/GPUTestFramework/src/WebGPU/TestingSwapChainWebGPU.cpp b/Tests/GPUTestFramework/src/WebGPU/TestingSwapChainWebGPU.cpp new file mode 100644 index 0000000000..2dc60afad9 --- /dev/null +++ b/Tests/GPUTestFramework/src/WebGPU/TestingSwapChainWebGPU.cpp @@ -0,0 +1,221 @@ +/* + * Copyright 2019-2023 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + + +#include "WebGPU/TestingSwapChainWebGPU.hpp" +#include "WebGPU/TestingEnvironmentWebGPU.hpp" + +#include "RenderDeviceWebGPU.h" +#include "DeviceContextWebGPU.h" +#include "TextureWebGPU.h" +#include "webgpu/wgpu.h" + + +namespace Diligent +{ + +namespace Testing +{ + +TestingSwapChainWebGPU::TestingSwapChainWebGPU(IReferenceCounters* pRefCounters, + TestingEnvironmentWebGPU* pEnv, + const SwapChainDesc& SCDesc) : + TBase // + { + pRefCounters, + pEnv->GetDevice(), + pEnv->GetDeviceContext(), + SCDesc // + } +{ + RefCntAutoPtr pRenderDeviceWebGPU{m_pDevice, IID_RenderDeviceWebGPU}; + RefCntAutoPtr pContextWebGPU{m_pContext, IID_DeviceContextWebGPU}; + + m_wgpuDevice = pRenderDeviceWebGPU->GetWebGPUDevice(); + + WGPUTextureFormat ColorFormat = WGPUTextureFormat_Undefined; + WGPUTextureFormat DepthFormat = WGPUTextureFormat_Undefined; + + switch (m_SwapChainDesc.ColorBufferFormat) + { + case TEX_FORMAT_RGBA8_UNORM: + ColorFormat = WGPUTextureFormat_RGBA8Unorm; + break; + + default: + UNSUPPORTED("Texture format ", GetTextureFormatAttribs(m_SwapChainDesc.ColorBufferFormat).Name, " is not a supported color buffer format"); + } + + switch (m_SwapChainDesc.DepthBufferFormat) + { + case TEX_FORMAT_D32_FLOAT: + DepthFormat = WGPUTextureFormat_Depth32Float; + break; + + default: + UNSUPPORTED("Texture format ", GetTextureFormatAttribs(m_SwapChainDesc.DepthBufferFormat).Name, " is not a supported depth buffer format"); + } + + { + WGPUTextureDescriptor wgpuTextureDesc{}; + wgpuTextureDesc.dimension = WGPUTextureDimension_2D; + wgpuTextureDesc.size = {m_SwapChainDesc.Width, m_SwapChainDesc.Height, 1}; + wgpuTextureDesc.mipLevelCount = 1; + wgpuTextureDesc.format = ColorFormat; + wgpuTextureDesc.usage = WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_CopySrc; + wgpuTextureDesc.sampleCount = 1; + + m_wgpuColorTexture = wgpuDeviceCreateTexture(m_wgpuDevice, &wgpuTextureDesc); + } + + { + WGPUTextureDescriptor wgpuTextureDesc{}; + wgpuTextureDesc.dimension = WGPUTextureDimension_2D; + wgpuTextureDesc.size = {m_SwapChainDesc.Width, m_SwapChainDesc.Height, 1}; + wgpuTextureDesc.mipLevelCount = 1; + wgpuTextureDesc.format = DepthFormat; + wgpuTextureDesc.usage = WGPUTextureUsage_RenderAttachment; + wgpuTextureDesc.sampleCount = 1; + + m_wgpuDepthTexture = wgpuDeviceCreateTexture(m_wgpuDevice, &wgpuTextureDesc); + } + + { + WGPUBufferDescriptor wgpuStagingBufferDesc{}; + wgpuStagingBufferDesc.usage = WGPUBufferUsage_MapRead | WGPUBufferUsage_CopyDst; + wgpuStagingBufferDesc.size = m_SwapChainDesc.Width * m_SwapChainDesc.Height * 4; + + m_wgpuStagingBuffer = wgpuDeviceCreateBuffer(m_wgpuDevice, &wgpuStagingBufferDesc); + } + + { + WGPUTextureViewDescriptor wgpuTextureViewDesc{}; + wgpuTextureViewDesc.dimension = WGPUTextureViewDimension_2D; + wgpuTextureViewDesc.mipLevelCount = 1; + wgpuTextureViewDesc.arrayLayerCount = 1; + wgpuTextureViewDesc.format = ColorFormat; + + m_wgpuColorTextureView = wgpuTextureCreateView(m_wgpuColorTexture, &wgpuTextureViewDesc); + } + + { + WGPUTextureViewDescriptor wgpuTextureViewDesc{}; + wgpuTextureViewDesc.dimension = WGPUTextureViewDimension_2D; + wgpuTextureViewDesc.mipLevelCount = 1; + wgpuTextureViewDesc.arrayLayerCount = 1; + wgpuTextureViewDesc.format = DepthFormat; + + m_wgpuDepthTextureView = wgpuTextureCreateView(m_wgpuDepthTexture, &wgpuTextureViewDesc); + } +} + +TestingSwapChainWebGPU::~TestingSwapChainWebGPU() +{ + if (m_wgpuColorTexture != nullptr) + wgpuTextureDrop(m_wgpuColorTexture); + if (m_wgpuDepthTexture != nullptr) + wgpuTextureDrop(m_wgpuDepthTexture); + if (m_wgpuColorTextureView != nullptr) + wgpuTextureViewDrop(m_wgpuColorTextureView); + if (m_wgpuDepthTextureView != nullptr) + wgpuTextureViewDrop(m_wgpuDepthTextureView); + if (m_wgpuStagingBuffer != nullptr) + wgpuBufferDrop(m_wgpuStagingBuffer); +} + +void TestingSwapChainWebGPU::TakeSnapshot(ITexture* pCopyFrom) +{ + WGPUTexture wgpuSrcTexture = m_wgpuColorTexture; + if (pCopyFrom != nullptr) + { + RefCntAutoPtr pSrcTextureWebGPU{pCopyFrom, IID_TextureWebGPU}; + VERIFY_EXPR(pSrcTextureWebGPU->GetState() == RESOURCE_STATE_COPY_SOURCE); + VERIFY_EXPR(GetDesc().Width == pSrcTextureWebGPU->GetDesc().Width); + VERIFY_EXPR(GetDesc().Height == pSrcTextureWebGPU->GetDesc().Height); + VERIFY_EXPR(GetDesc().ColorBufferFormat == pSrcTextureWebGPU->GetDesc().Format); + wgpuSrcTexture = pSrcTextureWebGPU->GetWebGPUTexture(); + } + + WGPUCommandEncoderDescriptor wgpuCmdEncoderDesc{}; + WGPUCommandEncoder wgpuCmdEncoder = wgpuDeviceCreateCommandEncoder(m_wgpuDevice, &wgpuCmdEncoderDesc); + + WGPUImageCopyTexture wgpuImageCopySrc{}; + wgpuImageCopySrc.mipLevel = 0; + wgpuImageCopySrc.texture = wgpuSrcTexture; + wgpuImageCopySrc.origin = {0, 0, 0}; + + WGPUImageCopyBuffer wgpuImageCopyDst{}; + wgpuImageCopyDst.layout.offset = 0; + wgpuImageCopyDst.layout.bytesPerRow = 4 * m_SwapChainDesc.Width; + wgpuImageCopyDst.layout.rowsPerImage = m_SwapChainDesc.Height; + wgpuImageCopyDst.buffer = m_wgpuStagingBuffer; + + WGPUExtent3D wgpuCopySize{}; + wgpuCopySize.width = m_SwapChainDesc.Width; + wgpuCopySize.height = m_SwapChainDesc.Height; + wgpuCopySize.depthOrArrayLayers = 1; + + wgpuCommandEncoderCopyTextureToBuffer(wgpuCmdEncoder, &wgpuImageCopySrc, &wgpuImageCopyDst, &wgpuCopySize); + + WGPUCommandBufferDescriptor wgpuCmdBufferDesc{}; + WGPUCommandBuffer wgpuCmdBuffer = wgpuCommandEncoderFinish(wgpuCmdEncoder, &wgpuCmdBufferDesc); + + wgpuQueueSubmit(wgpuDeviceGetQueue(m_wgpuDevice), 1, &wgpuCmdBuffer); + + const size_t DataSize = 4 * m_SwapChainDesc.Width * m_SwapChainDesc.Height; + wgpuBufferMapAsync( + m_wgpuStagingBuffer, WGPUMapMode_Read, 0, DataSize, [](WGPUBufferMapAsyncStatus MapStatus, void* pUserData) { + if (MapStatus == WGPUBufferMapAsyncStatus_Success) + { + const auto pThis = static_cast(pUserData); + + const size_t DataSize = 4 * pThis->m_SwapChainDesc.Width * pThis->m_SwapChainDesc.Height; + const auto* pMappedData = static_cast(wgpuBufferGetConstMappedRange(pThis->m_wgpuStagingBuffer, 0, DataSize)); + VERIFY_EXPR(pMappedData != nullptr); + + pThis->m_ReferenceData.resize(DataSize); + memcpy(pThis->m_ReferenceData.data(), pMappedData, DataSize); + wgpuBufferUnmap(pThis->m_wgpuStagingBuffer); + } + else + { + ADD_FAILURE() << "Failing to map staging buffer"; + } + }, + this); +} + +void CreateTestingSwapChainWebGPU(TestingEnvironmentWebGPU* pEnv, + const SwapChainDesc& SCDesc, + ISwapChain** ppSwapChain) +{ + TestingSwapChainWebGPU* pTestingSC(MakeNewRCObj()(pEnv, SCDesc)); + pTestingSC->QueryInterface(IID_SwapChain, reinterpret_cast(ppSwapChain)); +} + +} // namespace Testing + +} // namespace Diligent diff --git a/ThirdParty/CMakeLists.txt b/ThirdParty/CMakeLists.txt index 88b89f840a..e96f7d42c9 100644 --- a/ThirdParty/CMakeLists.txt +++ b/ThirdParty/CMakeLists.txt @@ -142,6 +142,15 @@ if (VULKAN_SUPPORTED AND (PLATFORM_WIN32 OR PLATFORM_LINUX OR PLATFORM_MACOS OR install(FILES "${volk_SOURCE_DIR}/LICENSE.md" DESTINATION "Licenses/ThirdParty/${DILIGENT_CORE_DIR}" RENAME Volk-License.md) endif() +if (WEBGPU_SUPPORTED) + get_property(ARCH_CACHE_VALUE CACHE ARCH PROPERTY VALUE) + unset(ARCH CACHE) + set(WEBGPU_BACKEND WGPU) + add_subdirectory(webgpu) + set_directory_root_folder("webgpu" "DiligentCore/ThirdParty/webgpu") + set(ARCH "${ARCH_CACHE_VALUE}" CACHE INTERNAL "") +endif() + if ((${DILIGENT_BUILD_GOOGLE_TEST}) AND (NOT TARGET gtest)) set(INSTALL_GTEST OFF CACHE BOOL "Do not install googletest") set(BUILD_GMOCK OFF CACHE BOOL "Do not build gmock") diff --git a/ThirdParty/webgpu b/ThirdParty/webgpu new file mode 160000 index 0000000000..2165676d34 --- /dev/null +++ b/ThirdParty/webgpu @@ -0,0 +1 @@ +Subproject commit 2165676d347d32ca3b8a8baf949972ef0fb2b60c