diff --git a/README.md b/README.md
index cc742a467..6dd8622d8 100644
--- a/README.md
+++ b/README.md
@@ -43,6 +43,7 @@ Supported rendering backends:
* Vulkan
* WebGL 1.0
* WebGL 2.0
+ * WebGPU (Dawn Native only)
Supported platforms:
diff --git a/bindings/bf/bgfx.bf b/bindings/bf/bgfx.bf
index bc52e341c..ffbaa3578 100644
--- a/bindings/bf/bgfx.bf
+++ b/bindings/bf/bgfx.bf
@@ -1419,6 +1419,11 @@ public static class bgfx
///
Vulkan,
+ ///
+ /// WebGPU
+ ///
+ WebGPU,
+
Count
}
diff --git a/bindings/c3/bgfx.c3 b/bindings/c3/bgfx.c3
index adac38ade..a1f57d21f 100644
--- a/bindings/c3/bgfx.c3
+++ b/bindings/c3/bgfx.c3
@@ -896,6 +896,9 @@ enum RendererType : uint
// Vulkan
VULKAN,
+ // WebGPU
+ WEBGPU,
+
COUNT
}
diff --git a/bindings/cs/bgfx.cs b/bindings/cs/bgfx.cs
index 66c0c7023..5914a7f71 100644
--- a/bindings/cs/bgfx.cs
+++ b/bindings/cs/bgfx.cs
@@ -1416,6 +1416,11 @@ public static partial class bgfx
///
Vulkan,
+ ///
+ /// WebGPU
+ ///
+ WebGPU,
+
Count
}
diff --git a/bindings/d/impl.d b/bindings/d/impl.d
index e373d6790..9648e19e1 100644
--- a/bindings/d/impl.d
+++ b/bindings/d/impl.d
@@ -26,7 +26,7 @@ extern(C++, "bgfx") package final abstract class Fatal{
}
extern(C++, "bgfx") package final abstract class RendererType{
enum Enum{
- noop,agc,direct3D11,direct3D12,gnm,metal,nvn,openGLES,openGL,vulkan,count
+ noop,agc,direct3D11,direct3D12,gnm,metal,nvn,openGLES,openGL,vulkan,webGPU,count
}
}
extern(C++, "bgfx") package final abstract class Access{
diff --git a/bindings/d/package.d b/bindings/d/package.d
index 4b6e7e8df..32b9b08cd 100644
--- a/bindings/d/package.d
+++ b/bindings/d/package.d
@@ -599,6 +599,7 @@ enum RendererType: bgfx.impl.RendererType.Enum{
openGLES = bgfx.impl.RendererType.Enum.openGLES,
openGL = bgfx.impl.RendererType.Enum.openGL,
vulkan = bgfx.impl.RendererType.Enum.vulkan,
+ webGPU = bgfx.impl.RendererType.Enum.webGPU,
count = bgfx.impl.RendererType.Enum.count,
}
diff --git a/bindings/zig/bgfx.zig b/bindings/zig/bgfx.zig
index 31107d7d7..df800fa00 100644
--- a/bindings/zig/bgfx.zig
+++ b/bindings/zig/bgfx.zig
@@ -867,6 +867,9 @@ pub const RendererType = enum(c_int) {
/// Vulkan
Vulkan,
+ /// WebGPU
+ WebGPU,
+
Count
};
diff --git a/docs/overview.rst b/docs/overview.rst
index 5b7dad232..44c1dcc7c 100644
--- a/docs/overview.rst
+++ b/docs/overview.rst
@@ -28,6 +28,7 @@ Supported rendering backends
- Vulkan
- WebGL 1.0
- WebGL 2.0
+- WebGPU (Dawn Native only)
Supported Platforms
~~~~~~~~~~~~~~~~~~~
diff --git a/examples/common/args.h b/examples/common/args.h
index c41757522..a5096cf86 100644
--- a/examples/common/args.h
+++ b/examples/common/args.h
@@ -23,6 +23,10 @@ struct Args
{
m_type = bgfx::RendererType::Vulkan;
}
+ else if (cmdLine.hasArg("wgpu") )
+ {
+ m_type = bgfx::RendererType::WebGPU;
+ }
else if (cmdLine.hasArg("noop") )
{
m_type = bgfx::RendererType::Noop;
diff --git a/examples/common/bgfx_utils.cpp b/examples/common/bgfx_utils.cpp
index 944434d8f..862e4f1c1 100644
--- a/examples/common/bgfx_utils.cpp
+++ b/examples/common/bgfx_utils.cpp
@@ -112,6 +112,7 @@ static bgfx::ShaderHandle loadShader(bx::FileReaderI* _reader, const bx::StringV
case bgfx::RendererType::OpenGL: filePath.join("glsl"); break;
case bgfx::RendererType::OpenGLES: filePath.join("essl"); break;
case bgfx::RendererType::Vulkan: filePath.join("spirv"); break;
+ case bgfx::RendererType::WebGPU: filePath.join("wgsl"); break;
case bgfx::RendererType::Count:
BX_ASSERT(false, "You should not be here!");
diff --git a/include/bgfx/bgfx.h b/include/bgfx/bgfx.h
index ab59de611..cbd6bc206 100644
--- a/include/bgfx/bgfx.h
+++ b/include/bgfx/bgfx.h
@@ -63,6 +63,7 @@ namespace bgfx
OpenGLES, //!< OpenGL ES 2.0+
OpenGL, //!< OpenGL 2.1+
Vulkan, //!< Vulkan
+ WebGPU, //!< WebGPU
Count
};
diff --git a/include/bgfx/c99/bgfx.h b/include/bgfx/c99/bgfx.h
index 608c9531c..d92108de5 100644
--- a/include/bgfx/c99/bgfx.h
+++ b/include/bgfx/c99/bgfx.h
@@ -90,6 +90,7 @@ typedef enum bgfx_renderer_type
BGFX_RENDERER_TYPE_OPENGLES, /** ( 7) OpenGL ES 2.0+ */
BGFX_RENDERER_TYPE_OPENGL, /** ( 8) OpenGL 2.1+ */
BGFX_RENDERER_TYPE_VULKAN, /** ( 9) Vulkan */
+ BGFX_RENDERER_TYPE_WEBGPU, /** (10) WebGPU */
BGFX_RENDERER_TYPE_COUNT
diff --git a/include/bgfx/embedded_shader.h b/include/bgfx/embedded_shader.h
index 4c49bddd1..d64446c2a 100644
--- a/include/bgfx/embedded_shader.h
+++ b/include/bgfx/embedded_shader.h
@@ -81,7 +81,11 @@
#endif // BGFX_PLATFORM_SUPPORTS_SPIRV
#ifndef BGFX_PLATFORM_SUPPORTS_WGSL
-# define BGFX_PLATFORM_SUPPORTS_WGSL 0
+# define BGFX_PLATFORM_SUPPORTS_WGSL (0 \
+ || BX_PLATFORM_LINUX \
+ || BX_PLATFORM_WINDOWS \
+ || BX_PLATFORM_OSX \
+ )
#endif // BGFX_PLATFORM_SUPPORTS_WGSL
///
diff --git a/scripts/bgfx.idl b/scripts/bgfx.idl
index dc9c11857..8e33ad2d5 100644
--- a/scripts/bgfx.idl
+++ b/scripts/bgfx.idl
@@ -468,6 +468,7 @@ enum.RendererType { comment = "Renderer types:" }
.OpenGLES --- OpenGL ES 2.0+
.OpenGL --- OpenGL 2.1+
.Vulkan --- Vulkan
+ .WebGPU --- WebGPU
()
--- Access mode enum.
diff --git a/src/amalgamated.cpp b/src/amalgamated.cpp
index a0672dd35..a130f7cdc 100644
--- a/src/amalgamated.cpp
+++ b/src/amalgamated.cpp
@@ -18,6 +18,7 @@
#include "renderer_noop.cpp"
#include "renderer_nvn.cpp"
#include "renderer_vk.cpp"
+#include "renderer_webgpu.cpp"
#include "shader.cpp"
#include "shader_dxbc.cpp"
#include "shader_spirv.cpp"
diff --git a/src/bgfx.cpp b/src/bgfx.cpp
index bf0d39d15..c41662417 100644
--- a/src/bgfx.cpp
+++ b/src/bgfx.cpp
@@ -2727,6 +2727,7 @@ namespace bgfx
{ gl::rendererCreate, gl::rendererDestroy, BGFX_RENDERER_OPENGL_NAME, !!BGFX_CONFIG_RENDERER_OPENGLES }, // OpenGLES
{ gl::rendererCreate, gl::rendererDestroy, BGFX_RENDERER_OPENGL_NAME, !!BGFX_CONFIG_RENDERER_OPENGL }, // OpenGL
{ vk::rendererCreate, vk::rendererDestroy, BGFX_RENDERER_VULKAN_NAME, !!BGFX_CONFIG_RENDERER_VULKAN }, // Vulkan
+ { wgpu::rendererCreate, wgpu::rendererDestroy, BGFX_RENDERER_WEBGPU_NAME, !!BGFX_CONFIG_RENDERER_WEBGPU }, // WebGPU
};
static_assert(BX_COUNTOF(s_rendererCreator) == RendererType::Count);
diff --git a/src/bgfx_p.h b/src/bgfx_p.h
index 56992b297..db6139f36 100644
--- a/src/bgfx_p.h
+++ b/src/bgfx_p.h
@@ -240,6 +240,7 @@ namespace stl = std;
#define BGFX_RENDERER_METAL_NAME "Metal"
#define BGFX_RENDERER_NVN_NAME "NVN"
#define BGFX_RENDERER_VULKAN_NAME "Vulkan"
+#define BGFX_RENDERER_WEBGPU_NAME "WebGPU"
#if BGFX_CONFIG_RENDERER_OPENGL
# if BGFX_CONFIG_RENDERER_OPENGL >= 31 && BGFX_CONFIG_RENDERER_OPENGL <= 33
diff --git a/src/config.h b/src/config.h
index 4cb2cc047..8c22b8373 100644
--- a/src/config.h
+++ b/src/config.h
@@ -27,7 +27,8 @@
&& !defined(BGFX_CONFIG_RENDERER_NVN) \
&& !defined(BGFX_CONFIG_RENDERER_OPENGL) \
&& !defined(BGFX_CONFIG_RENDERER_OPENGLES) \
- && !defined(BGFX_CONFIG_RENDERER_VULKAN)
+ && !defined(BGFX_CONFIG_RENDERER_VULKAN) \
+ && !defined(BGFX_CONFIG_RENDERER_WEBGPU)
# ifndef BGFX_CONFIG_RENDERER_AGC
# define BGFX_CONFIG_RENDERER_AGC (0 \
@@ -109,6 +110,15 @@
? 1 : 0)
# endif // BGFX_CONFIG_RENDERER_VULKAN
+# ifndef BGFX_CONFIG_RENDERER_WEBGPU
+# define BGFX_CONFIG_RENDERER_WEBGPU (0 \
+ /*|| BX_PLATFORM_EMSCRIPTEN*/ \
+ || BX_PLATFORM_LINUX \
+ || BX_PLATFORM_OSX \
+ || BX_PLATFORM_WINDOWS \
+ ? 1 : 0)
+# endif // BGFX_CONFIG_RENDERER_WEBGPU
+
#else
# ifndef BGFX_CONFIG_RENDERER_AGC
# define BGFX_CONFIG_RENDERER_AGC 0
@@ -145,6 +155,10 @@
# ifndef BGFX_CONFIG_RENDERER_VULKAN
# define BGFX_CONFIG_RENDERER_VULKAN 0
# endif // BGFX_CONFIG_RENDERER_VULKAN
+
+# ifndef BGFX_CONFIG_RENDERER_WEBGPU
+# define BGFX_CONFIG_RENDERER_WEBGPU 0
+# endif // BGFX_CONFIG_RENDERER_WEBGPU
#endif // !defined...
#if BGFX_CONFIG_RENDERER_OPENGL && BGFX_CONFIG_RENDERER_OPENGL < 21
diff --git a/src/makefile b/src/makefile
index 541c66be5..ec27e5833 100644
--- a/src/makefile
+++ b/src/makefile
@@ -31,6 +31,8 @@ define shader-embedded
-@cat "$(SHADER_TMP)" >> $(@)
-$(SILENT) $(SHADERC) --type $(1) --platform linux -p spirv -f $(<) -o "$(SHADER_TMP)" --bin2c $(basename $(<))_spv
-@cat "$(SHADER_TMP)" >> $(@)
+ -$(SILENT) $(SHADERC) --type $(1) --platform linux -p wgsl -f $(<) -o "$(SHADER_TMP)" --bin2c $(basename $(<))_wgsl
+ -@cat "$(SHADER_TMP)" >> $(@)
-$(SILENT) $(SHADERC) --type $(1) --platform windows -p $(2) -O 3 -f $(<) -o "$(SHADER_TMP)" --bin2c $(basename $(<))_dx11
-@cat "$(SHADER_TMP)" >> $(@)
-$(SILENT) $(SHADERC) --type $(1) --platform ios -p metal -O 3 -f $(<) -o "$(SHADER_TMP)" --bin2c $(basename $(<))_mtl
diff --git a/src/renderer_webgpu.cpp b/src/renderer_webgpu.cpp
new file mode 100644
index 000000000..dc4123e75
--- /dev/null
+++ b/src/renderer_webgpu.cpp
@@ -0,0 +1,6301 @@
+/*
+ * Copyright 2011-2025 Branimir Karadzic. All rights reserved.
+ * License: https://github.com/bkaradzic/bgfx/blob/master/LICENSE
+ */
+
+#include "bgfx_p.h"
+
+#if BGFX_CONFIG_RENDERER_WEBGPU
+# if BX_PLATFORM_OSX
+# include
+# include
+# endif // BX_PLATFORM_OSX
+# include
+# include "renderer_webgpu.h"
+
+namespace bgfx { namespace wgpu
+{
+ static char s_viewName[BGFX_CONFIG_MAX_VIEWS][BGFX_CONFIG_MAX_VIEW_NAME];
+
+ inline void setViewType(ViewId _view, const bx::StringView _str)
+ {
+ if (BX_ENABLED(BGFX_CONFIG_DEBUG_ANNOTATION || BGFX_CONFIG_PROFILER) )
+ {
+ bx::memCopy(&s_viewName[_view][3], _str.getPtr(), _str.getLength() );
+ }
+ }
+
+ struct PrimInfo
+ {
+ WGPUPrimitiveTopology m_topology;
+ uint32_t m_min;
+ uint32_t m_div;
+ uint32_t m_sub;
+ WGPUIndexFormat m_stripIndexFormat[2];
+ };
+
+ static const PrimInfo s_primInfo[] =
+ {
+ { WGPUPrimitiveTopology_TriangleList, 3, 3, 0, { WGPUIndexFormat_Undefined, WGPUIndexFormat_Undefined } },
+ { WGPUPrimitiveTopology_TriangleStrip, 3, 1, 2, { WGPUIndexFormat_Uint16, WGPUIndexFormat_Uint32 } },
+ { WGPUPrimitiveTopology_LineList, 2, 2, 0, { WGPUIndexFormat_Undefined, WGPUIndexFormat_Undefined } },
+ { WGPUPrimitiveTopology_LineStrip, 2, 1, 1, { WGPUIndexFormat_Uint16, WGPUIndexFormat_Uint32 } },
+ { WGPUPrimitiveTopology_PointList, 1, 1, 0, { WGPUIndexFormat_Undefined, WGPUIndexFormat_Undefined } },
+ { WGPUPrimitiveTopology_Undefined, 0, 0, 0, { WGPUIndexFormat_Undefined, WGPUIndexFormat_Undefined } },
+ };
+ static_assert(Topology::Count == BX_COUNTOF(s_primInfo)-1);
+
+ static const uint32_t s_msaa[] =
+ {
+ 1,
+ 2,
+ 4,
+ 4,
+ 4,
+ };
+
+ static const WGPUVertexFormat s_attribType[][4][2] =
+ {
+ { // Uint8
+ { WGPUVertexFormat_Uint8, WGPUVertexFormat_Unorm8 },
+ { WGPUVertexFormat_Uint8x2, WGPUVertexFormat_Unorm8x2 },
+ { WGPUVertexFormat_Uint8x4, WGPUVertexFormat_Unorm8x4 },
+ { WGPUVertexFormat_Uint8x4, WGPUVertexFormat_Unorm8x4 },
+ },
+ { // Uint10
+ { WGPUVertexFormat_Uint16, WGPUVertexFormat_Unorm16 },
+ { WGPUVertexFormat_Uint16x2, WGPUVertexFormat_Unorm16x2 },
+ { WGPUVertexFormat_Uint16x4, WGPUVertexFormat_Unorm16x4 },
+ { WGPUVertexFormat_Uint16x4, WGPUVertexFormat_Unorm16x4 },
+ },
+ { // Int16
+ { WGPUVertexFormat_Sint16, WGPUVertexFormat_Snorm16 },
+ { WGPUVertexFormat_Sint16x2, WGPUVertexFormat_Snorm16x2 },
+ { WGPUVertexFormat_Sint16x4, WGPUVertexFormat_Snorm16x4 },
+ { WGPUVertexFormat_Sint16x4, WGPUVertexFormat_Snorm16x4 },
+ },
+ { // Half
+ { WGPUVertexFormat_Float16, WGPUVertexFormat_Float16 },
+ { WGPUVertexFormat_Float16x2, WGPUVertexFormat_Float16x2 },
+ { WGPUVertexFormat_Float16x4, WGPUVertexFormat_Float16x4 },
+ { WGPUVertexFormat_Float16x4, WGPUVertexFormat_Float16x4 },
+ },
+ { // Float
+ { WGPUVertexFormat_Float32, WGPUVertexFormat_Float32 },
+ { WGPUVertexFormat_Float32x2, WGPUVertexFormat_Float32x2 },
+ { WGPUVertexFormat_Float32x3, WGPUVertexFormat_Float32x3 },
+ { WGPUVertexFormat_Float32x4, WGPUVertexFormat_Float32x4 },
+ },
+ };
+ static_assert(AttribType::Count == BX_COUNTOF(s_attribType) );
+
+ static const WGPUCullMode s_cullMode[] =
+ {
+ WGPUCullMode_None,
+ WGPUCullMode_Front,
+ WGPUCullMode_Back,
+ };
+
+ static const WGPUBlendFactor s_blendFactor[][2] =
+ {
+ { WGPUBlendFactor_Undefined, WGPUBlendFactor_Undefined }, // ignored
+ { WGPUBlendFactor_Zero, WGPUBlendFactor_Zero }, // ZERO
+ { WGPUBlendFactor_One, WGPUBlendFactor_One }, // ONE
+ { WGPUBlendFactor_Src, WGPUBlendFactor_SrcAlpha }, // SRC_COLOR
+ { WGPUBlendFactor_OneMinusSrc, WGPUBlendFactor_OneMinusSrcAlpha }, // INV_SRC_COLOR
+ { WGPUBlendFactor_SrcAlpha, WGPUBlendFactor_SrcAlpha }, // SRC_ALPHA
+ { WGPUBlendFactor_OneMinusSrcAlpha, WGPUBlendFactor_OneMinusSrcAlpha }, // INV_SRC_ALPHA
+ { WGPUBlendFactor_DstAlpha, WGPUBlendFactor_DstAlpha }, // DST_ALPHA
+ { WGPUBlendFactor_OneMinusDstAlpha, WGPUBlendFactor_OneMinusDstAlpha }, // INV_DST_ALPHA
+ { WGPUBlendFactor_Dst, WGPUBlendFactor_DstAlpha }, // DST_COLOR
+ { WGPUBlendFactor_OneMinusDst, WGPUBlendFactor_OneMinusDstAlpha }, // INV_DST_COLOR
+ { WGPUBlendFactor_SrcAlphaSaturated, WGPUBlendFactor_One }, // SRC_ALPHA_SAT
+ { WGPUBlendFactor_Constant, WGPUBlendFactor_Constant }, // FACTOR
+ { WGPUBlendFactor_OneMinusConstant, WGPUBlendFactor_OneMinusConstant }, // INV_FACTOR
+ };
+
+ static const WGPUBlendOperation s_blendEquation[] =
+ {
+ WGPUBlendOperation_Add,
+ WGPUBlendOperation_Subtract,
+ WGPUBlendOperation_ReverseSubtract,
+ WGPUBlendOperation_Min,
+ WGPUBlendOperation_Max,
+ };
+
+ static const WGPUCompareFunction s_cmpFunc[] =
+ {
+ WGPUCompareFunction_Always, // ignored
+ WGPUCompareFunction_Less,
+ WGPUCompareFunction_LessEqual,
+ WGPUCompareFunction_Equal,
+ WGPUCompareFunction_GreaterEqual,
+ WGPUCompareFunction_Greater,
+ WGPUCompareFunction_NotEqual,
+ WGPUCompareFunction_Never,
+ WGPUCompareFunction_Always,
+ };
+
+ static const WGPUStencilOperation s_stencilOp[] =
+ {
+ WGPUStencilOperation_Zero,
+ WGPUStencilOperation_Keep,
+ WGPUStencilOperation_Replace,
+ WGPUStencilOperation_IncrementWrap,
+ WGPUStencilOperation_IncrementClamp,
+ WGPUStencilOperation_DecrementWrap,
+ WGPUStencilOperation_DecrementClamp,
+ WGPUStencilOperation_Invert,
+ };
+
+ static const WGPUStorageTextureAccess s_storageTextureAccess[] =
+ {
+ WGPUStorageTextureAccess_ReadOnly,
+ WGPUStorageTextureAccess_WriteOnly,
+ WGPUStorageTextureAccess_ReadWrite,
+ };
+ static_assert(BX_COUNTOF(s_storageTextureAccess) == Access::Count, "");
+
+ static const WGPUAddressMode s_textureAddress[] =
+ {
+ WGPUAddressMode_Repeat, // 0 - wrap
+ WGPUAddressMode_MirrorRepeat, // 1 - mirror
+ WGPUAddressMode_ClampToEdge, // 2 - clamp
+ WGPUAddressMode_ClampToEdge, // 3 - border
+ };
+ static_assert(BX_COUNTOF(s_textureAddress) == (BGFX_SAMPLER_U_MASK>>BGFX_SAMPLER_U_SHIFT)+1, "");
+
+ static const WGPUFilterMode s_textureFilterMinMag[] =
+ {
+ WGPUFilterMode_Linear, // 0 - linear
+ WGPUFilterMode_Nearest, // 1 - point
+ WGPUFilterMode_Linear, // 2 - anisotropic
+ WGPUFilterMode_Undefined,
+ };
+ static_assert(BX_COUNTOF(s_textureFilterMinMag) == (BGFX_SAMPLER_MAG_MASK>>BGFX_SAMPLER_MAG_SHIFT)+1, "");
+
+ static const WGPUMipmapFilterMode s_textureFilterMip[] =
+ {
+ WGPUMipmapFilterMode_Linear,
+ WGPUMipmapFilterMode_Nearest,
+ };
+ static_assert(BX_COUNTOF(s_textureFilterMip) == (BGFX_SAMPLER_MIP_MASK>>BGFX_SAMPLER_MIP_SHIFT)+1, "");
+
+ static const WGPUTextureSampleType s_textureComponentType[] =
+ {
+ WGPUTextureSampleType_Float,
+ WGPUTextureSampleType_Sint,
+ WGPUTextureSampleType_Uint,
+ WGPUTextureSampleType_Depth,
+ WGPUTextureSampleType_UnfilterableFloat,
+ };
+ static_assert(TextureComponentType::Count == BX_COUNTOF(s_textureComponentType) );
+
+ static const WGPUTextureViewDimension s_textureDimension[] =
+ {
+ WGPUTextureViewDimension_1D,
+ WGPUTextureViewDimension_2D,
+ WGPUTextureViewDimension_2DArray,
+ WGPUTextureViewDimension_Cube,
+ WGPUTextureViewDimension_CubeArray,
+ WGPUTextureViewDimension_3D,
+ };
+ static_assert(TextureDimension::Count == BX_COUNTOF(s_textureDimension) );
+
+ struct TextureFormatInfo
+ {
+ WGPUTextureFormat m_fmt;
+ WGPUTextureFormat m_fmtSrgb;
+ WGPUTextureSampleType m_samplerType;
+ bool m_blendable;
+ WGPUTextureComponentSwizzle m_mapping;
+ };
+
+ static const TextureFormatInfo s_textureFormat[] =
+ {
+#define $_ WGPUComponentSwizzle_Undefined
+#define $0 WGPUComponentSwizzle_Zero
+#define $1 WGPUComponentSwizzle_One
+#define $R WGPUComponentSwizzle_R
+#define $G WGPUComponentSwizzle_G
+#define $B WGPUComponentSwizzle_B
+#define $A WGPUComponentSwizzle_A
+ { WGPUTextureFormat_BC1RGBAUnorm, WGPUTextureFormat_BC1RGBAUnormSrgb, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // BC1
+ { WGPUTextureFormat_BC2RGBAUnorm, WGPUTextureFormat_BC2RGBAUnormSrgb, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // BC2
+ { WGPUTextureFormat_BC3RGBAUnorm, WGPUTextureFormat_BC3RGBAUnormSrgb, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // BC3
+ { WGPUTextureFormat_BC4RUnorm, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // BC4
+ { WGPUTextureFormat_BC5RGUnorm, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // BC5
+ { WGPUTextureFormat_BC6HRGBFloat, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // BC6H
+ { WGPUTextureFormat_BC7RGBAUnorm, WGPUTextureFormat_BC7RGBAUnormSrgb, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // BC7
+ { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // ETC1
+ { WGPUTextureFormat_ETC2RGB8Unorm, WGPUTextureFormat_ETC2RGB8UnormSrgb, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // ETC2
+ { WGPUTextureFormat_ETC2RGBA8Unorm, WGPUTextureFormat_ETC2RGBA8UnormSrgb, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // ETC2A
+ { WGPUTextureFormat_ETC2RGB8A1Unorm, WGPUTextureFormat_ETC2RGB8A1UnormSrgb, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // ETC2A1
+ { WGPUTextureFormat_EACR11Unorm, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // EACR11
+ { WGPUTextureFormat_EACR11Snorm, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // EACR11S
+ { WGPUTextureFormat_EACRG11Unorm, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // EACRG11
+ { WGPUTextureFormat_EACRG11Snorm, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // EACRG11S
+ { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // PTC12
+ { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // PTC14
+ { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // PTC12A
+ { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // PTC14A
+ { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // PTC22
+ { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // PTC24
+ { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // ATC
+ { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // ATCE
+ { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // ATCI
+ { WGPUTextureFormat_ASTC4x4Unorm, WGPUTextureFormat_ASTC4x4UnormSrgb, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // ASTC4x4
+ { WGPUTextureFormat_ASTC5x4Unorm, WGPUTextureFormat_ASTC5x4UnormSrgb, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // ASTC5x4
+ { WGPUTextureFormat_ASTC5x5Unorm, WGPUTextureFormat_ASTC5x5UnormSrgb, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // ASTC5x5
+ { WGPUTextureFormat_ASTC6x5Unorm, WGPUTextureFormat_ASTC6x5UnormSrgb, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // ASTC6x5
+ { WGPUTextureFormat_ASTC6x6Unorm, WGPUTextureFormat_ASTC6x6UnormSrgb, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // ASTC6x6
+ { WGPUTextureFormat_ASTC8x5Unorm, WGPUTextureFormat_ASTC8x5UnormSrgb, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // ASTC8x5
+ { WGPUTextureFormat_ASTC8x6Unorm, WGPUTextureFormat_ASTC8x6UnormSrgb, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // ASTC8x6
+ { WGPUTextureFormat_ASTC8x8Unorm, WGPUTextureFormat_ASTC8x8UnormSrgb, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // ASTC8x8
+ { WGPUTextureFormat_ASTC10x5Unorm, WGPUTextureFormat_ASTC10x5UnormSrgb, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // ASTC10x5
+ { WGPUTextureFormat_ASTC10x6Unorm, WGPUTextureFormat_ASTC10x6UnormSrgb, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // ASTC10x6
+ { WGPUTextureFormat_ASTC10x8Unorm, WGPUTextureFormat_ASTC10x8UnormSrgb, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // ASTC10x8
+ { WGPUTextureFormat_ASTC10x10Unorm, WGPUTextureFormat_ASTC10x10UnormSrgb, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // ASTC10x10
+ { WGPUTextureFormat_ASTC12x10Unorm, WGPUTextureFormat_ASTC12x10UnormSrgb, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // ASTC12x10
+ { WGPUTextureFormat_ASTC12x12Unorm, WGPUTextureFormat_ASTC12x12UnormSrgb, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // ASTC12x12
+ { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Undefined, false, { $_, $_, $_, $_ } }, // Unknown
+ { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // R1
+ { WGPUTextureFormat_R8Unorm, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $0, $0, $0, $R } }, // A8
+ { WGPUTextureFormat_R8Unorm, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // R8
+ { WGPUTextureFormat_R8Sint, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // R8I
+ { WGPUTextureFormat_R8Uint, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // R8U
+ { WGPUTextureFormat_R8Snorm, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // R8S
+ { WGPUTextureFormat_R16Unorm, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // R16
+ { WGPUTextureFormat_R16Sint, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // R16I
+ { WGPUTextureFormat_R16Uint, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // R16U
+ { WGPUTextureFormat_R16Float, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // R16F
+ { WGPUTextureFormat_R16Snorm, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // R16S
+ { WGPUTextureFormat_R32Sint, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // R32I
+ { WGPUTextureFormat_R32Uint, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // R32U
+ { WGPUTextureFormat_R32Float, WGPUTextureFormat_Undefined, WGPUTextureSampleType_UnfilterableFloat, false, { $_, $_, $_, $_ } }, // R32F
+ { WGPUTextureFormat_RG8Unorm, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RG8
+ { WGPUTextureFormat_RG8Sint, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RG8I
+ { WGPUTextureFormat_RG8Uint, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RG8U
+ { WGPUTextureFormat_RG8Snorm, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RG8S
+ { WGPUTextureFormat_RG16Unorm, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RG16
+ { WGPUTextureFormat_RG16Sint, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RG16I
+ { WGPUTextureFormat_RG16Uint, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RG16U
+ { WGPUTextureFormat_RG16Float, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RG16F
+ { WGPUTextureFormat_RG16Snorm, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RG16S
+ { WGPUTextureFormat_RG32Sint, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RG32I
+ { WGPUTextureFormat_RG32Uint, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RG32U
+ { WGPUTextureFormat_RG32Float, WGPUTextureFormat_Undefined, WGPUTextureSampleType_UnfilterableFloat, false, { $_, $_, $_, $_ } }, // RG32F
+ { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RGB8
+ { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RGB8I
+ { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RGB8U
+ { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RGB8S
+ { WGPUTextureFormat_RGB9E5Ufloat, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RGB9E5F
+ { WGPUTextureFormat_BGRA8Unorm, WGPUTextureFormat_BGRA8UnormSrgb, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // BGRA8
+ { WGPUTextureFormat_RGBA8Unorm, WGPUTextureFormat_RGBA8UnormSrgb, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RGBA8
+ { WGPUTextureFormat_RGBA8Sint, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RGBA8I
+ { WGPUTextureFormat_RGBA8Uint, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RGBA8U
+ { WGPUTextureFormat_RGBA8Snorm, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RGBA8S
+ { WGPUTextureFormat_RGBA16Unorm, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RGBA16
+ { WGPUTextureFormat_RGBA16Sint, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RGBA16I
+ { WGPUTextureFormat_RGBA16Uint, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RGBA16U
+ { WGPUTextureFormat_RGBA16Float, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RGBA16F
+ { WGPUTextureFormat_RGBA16Snorm, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RGBA16S
+ { WGPUTextureFormat_RGBA32Sint, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RGBA32I
+ { WGPUTextureFormat_RGBA32Uint, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RGBA32U
+ { WGPUTextureFormat_RGBA32Float, WGPUTextureFormat_Undefined, WGPUTextureSampleType_UnfilterableFloat, false, { $_, $_, $_, $_ } }, // RGBA32F
+ { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // B5G6R5
+ { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // R5G6B5
+ { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // BGRA4
+ { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RGBA4
+ { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // BGR5A1
+ { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RGB5A1
+ { WGPUTextureFormat_RGB10A2Unorm, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RGB10A2
+ { WGPUTextureFormat_RG11B10Ufloat, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Float, true, { $_, $_, $_, $_ } }, // RG11B10F
+ { WGPUTextureFormat_Undefined, WGPUTextureFormat_Undefined, WGPUTextureSampleType_Undefined, false, { $_, $_, $_, $_ } }, // UnknownDepth
+ { WGPUTextureFormat_Depth16Unorm, WGPUTextureFormat_Undefined, WGPUTextureSampleType_UnfilterableFloat, false, { $_, $_, $_, $_ } }, // D16
+ { WGPUTextureFormat_Depth24Plus, WGPUTextureFormat_Undefined, WGPUTextureSampleType_UnfilterableFloat, false, { $_, $_, $_, $_ } }, // D24
+ { WGPUTextureFormat_Depth24PlusStencil8, WGPUTextureFormat_Undefined, WGPUTextureSampleType_UnfilterableFloat, false, { $_, $_, $_, $_ } }, // D24S8
+ { WGPUTextureFormat_Depth32Float, WGPUTextureFormat_Undefined, WGPUTextureSampleType_UnfilterableFloat, false, { $_, $_, $_, $_ } }, // D32
+ { WGPUTextureFormat_Depth32Float, WGPUTextureFormat_Undefined, WGPUTextureSampleType_UnfilterableFloat, false, { $_, $_, $_, $_ } }, // D16F
+ { WGPUTextureFormat_Depth24Plus, WGPUTextureFormat_Undefined, WGPUTextureSampleType_UnfilterableFloat, false, { $_, $_, $_, $_ } }, // D24F
+ { WGPUTextureFormat_Depth32Float, WGPUTextureFormat_Undefined, WGPUTextureSampleType_UnfilterableFloat, false, { $_, $_, $_, $_ } }, // D32F
+ { WGPUTextureFormat_Stencil8, WGPUTextureFormat_Undefined, WGPUTextureSampleType_UnfilterableFloat, false, { $_, $_, $_, $_ } }, // D0S8
+#undef $_
+#undef $0
+#undef $1
+#undef $R
+#undef $G
+#undef $B
+#undef $A
+ };
+ static_assert(TextureFormat::Count == BX_COUNTOF(s_textureFormat) );
+
+ static const char* s_backendType[] =
+ {
+ "Undefined",
+ "Null",
+ "WebGPU",
+ "D3D11",
+ "D3D12",
+ "Metal",
+ "Vulkan",
+ "OpenGL",
+ "OpenGLES",
+ };
+
+ static const char* s_adapterType[] =
+ {
+ "DiscreteGPU",
+ "IntegratedGPU",
+ "CPU",
+ "Unknown",
+ };
+
+ template
+ struct FeatureT
+ {
+ Ty featureName;
+ const char* name;
+ bool optional;
+ bool supported;
+
+ static int32_t cmpFn(const void* _lhs, const void* _rhs)
+ {
+ const Ty lhs = ( (const FeatureT*)(_lhs) )->featureName;
+ const Ty rhs = ( (const FeatureT*)(_rhs) )->featureName;
+ return (lhs > rhs) - (lhs < rhs);
+ }
+ };
+
+ using LanguageFeature = FeatureT;
+
+ static LanguageFeature s_languageFeature[] =
+ {
+#define LANGUAGE_FEATURE(_name) WGPUWGSLLanguageFeatureName_##_name, #_name
+
+ { LANGUAGE_FEATURE(ReadonlyAndReadwriteStorageTextures), true, false },
+ { LANGUAGE_FEATURE(Packed4x8IntegerDotProduct), true, false },
+ { LANGUAGE_FEATURE(UnrestrictedPointerParameters), true, false },
+ { LANGUAGE_FEATURE(PointerCompositeAccess), true, false },
+ { LANGUAGE_FEATURE(UniformBufferStandardLayout), true, false },
+ { LANGUAGE_FEATURE(SubgroupId), true, false },
+ { LANGUAGE_FEATURE(ChromiumTestingUnimplemented), true, false },
+ { LANGUAGE_FEATURE(ChromiumTestingUnsafeExperimental), true, false },
+ { LANGUAGE_FEATURE(ChromiumTestingExperimental), true, false },
+ { LANGUAGE_FEATURE(ChromiumTestingShippedWithKillswitch), true, false },
+ { LANGUAGE_FEATURE(ChromiumTestingShipped), true, false },
+ { LANGUAGE_FEATURE(SizedBindingArray), true, false },
+ { LANGUAGE_FEATURE(TexelBuffers), true, false },
+ { LANGUAGE_FEATURE(ChromiumPrint), true, false },
+ { LANGUAGE_FEATURE(FragmentDepth), true, false },
+ { LANGUAGE_FEATURE(ImmediateAddressSpace), true, false },
+ { LANGUAGE_FEATURE(SubgroupUniformity), true, false },
+ { LANGUAGE_FEATURE(TextureAndSamplerLet), true, false },
+
+#undef LANGUAGE_FEATURE
+ };
+
+ using Feature = FeatureT;
+
+ static Feature s_feature[] =
+ {
+#define FEATURE(_name) WGPUFeatureName_##_name, #_name
+
+ { FEATURE(CoreFeaturesAndLimits), true, false },
+ { FEATURE(DepthClipControl), true, false },
+ { FEATURE(Depth32FloatStencil8), true, false },
+ { FEATURE(TextureCompressionBC), true, false },
+ { FEATURE(TextureCompressionBCSliced3D), true, false },
+ { FEATURE(TextureCompressionETC2), true, false },
+ { FEATURE(TextureCompressionASTC), true, false },
+ { FEATURE(TextureCompressionASTCSliced3D), true, false },
+ { FEATURE(TimestampQuery), true, false },
+ { FEATURE(IndirectFirstInstance), true, false },
+ { FEATURE(ShaderF16), true, false },
+ { FEATURE(RG11B10UfloatRenderable), true, false },
+ { FEATURE(BGRA8UnormStorage), true, false },
+ { FEATURE(Float32Filterable), true, false },
+ { FEATURE(Float32Blendable), true, false },
+ { FEATURE(ClipDistances), true, false },
+ { FEATURE(DualSourceBlending), true, false },
+ { FEATURE(Subgroups), true, false },
+ { FEATURE(TextureFormatsTier1), true, false },
+ { FEATURE(TextureFormatsTier2), true, false },
+ { FEATURE(PrimitiveIndex), true, false },
+ { FEATURE(TextureComponentSwizzle), true, false },
+ { FEATURE(DawnInternalUsages), true, false },
+ { FEATURE(DawnMultiPlanarFormats), true, false },
+ { FEATURE(DawnNative), true, false },
+ { FEATURE(ChromiumExperimentalTimestampQueryInsidePasses), true, false },
+ { FEATURE(ImplicitDeviceSynchronization), true, false },
+ { FEATURE(TransientAttachments), true, false },
+ { FEATURE(MSAARenderToSingleSampled), true, false },
+ { FEATURE(D3D11MultithreadProtected), true, false },
+ { FEATURE(ANGLETextureSharing), true, false },
+ { FEATURE(PixelLocalStorageCoherent), true, false },
+ { FEATURE(PixelLocalStorageNonCoherent), true, false },
+ { FEATURE(Unorm16TextureFormats), true, false },
+ { FEATURE(MultiPlanarFormatExtendedUsages), true, false },
+ { FEATURE(MultiPlanarFormatP010), true, false },
+ { FEATURE(HostMappedPointer), true, false },
+ { FEATURE(MultiPlanarRenderTargets), true, false },
+ { FEATURE(MultiPlanarFormatNv12a), true, false },
+ { FEATURE(FramebufferFetch), true, false },
+ { FEATURE(BufferMapExtendedUsages), true, false },
+ { FEATURE(AdapterPropertiesMemoryHeaps), true, false },
+ { FEATURE(AdapterPropertiesD3D), true, false },
+ { FEATURE(AdapterPropertiesVk), true, false },
+ { FEATURE(DawnFormatCapabilities), true, false },
+ { FEATURE(DawnDrmFormatCapabilities), true, false },
+ { FEATURE(MultiPlanarFormatNv16), true, false },
+ { FEATURE(MultiPlanarFormatNv24), true, false },
+ { FEATURE(MultiPlanarFormatP210), true, false },
+ { FEATURE(MultiPlanarFormatP410), true, false },
+ { FEATURE(SharedTextureMemoryVkDedicatedAllocation), true, false },
+ { FEATURE(SharedTextureMemoryAHardwareBuffer), true, false },
+ { FEATURE(SharedTextureMemoryDmaBuf), true, false },
+ { FEATURE(SharedTextureMemoryOpaqueFD), true, false },
+ { FEATURE(SharedTextureMemoryZirconHandle), true, false },
+ { FEATURE(SharedTextureMemoryDXGISharedHandle), true, false },
+ { FEATURE(SharedTextureMemoryD3D11Texture2D), true, false },
+ { FEATURE(SharedTextureMemoryIOSurface), true, false },
+ { FEATURE(SharedTextureMemoryEGLImage), true, false },
+ { FEATURE(SharedFenceVkSemaphoreOpaqueFD), true, false },
+ { FEATURE(SharedFenceSyncFD), true, false },
+ { FEATURE(SharedFenceVkSemaphoreZirconHandle), true, false },
+ { FEATURE(SharedFenceDXGISharedHandle), true, false },
+ { FEATURE(SharedFenceMTLSharedEvent), true, false },
+ { FEATURE(SharedBufferMemoryD3D12Resource), true, false },
+ { FEATURE(StaticSamplers), true, false },
+ { FEATURE(YCbCrVulkanSamplers), true, false },
+ { FEATURE(ShaderModuleCompilationOptions), true, false },
+ { FEATURE(DawnLoadResolveTexture), true, false },
+ { FEATURE(DawnPartialLoadResolveTexture), true, false },
+ { FEATURE(MultiDrawIndirect), true, false },
+ { FEATURE(DawnTexelCopyBufferRowAlignment), true, false },
+ { FEATURE(FlexibleTextureViews), true, false },
+ { FEATURE(ChromiumExperimentalSubgroupMatrix), true, false },
+ { FEATURE(SharedFenceEGLSync), true, false },
+ { FEATURE(DawnDeviceAllocatorControl), true, false },
+ { FEATURE(AdapterPropertiesWGPU), true, false },
+ { FEATURE(SharedBufferMemoryD3D12SharedMemoryFileMappingHandle), true, false },
+
+#undef FEATURE
+ };
+
+ static WGPUFeatureName ifSupported(WGPUFeatureName _featureName)
+ {
+ const int32_t idx = bx::binarySearch(_featureName, s_feature, BX_COUNTOF(s_feature), sizeof(Feature), Feature::cmpFn);
+
+ if (s_feature[idx].supported)
+ {
+ return _featureName;
+ }
+
+ return WGPUFeatureName_Force32;
+ }
+
+# if USE_WEBGPU_DYNAMIC_LIB
+
+# define WGPU_IGNORE_____(_optional, _func)
+# define WGPU_IMPORT_FUNC(_optional, _func) WGPUProc##_func wgpu##_func
+WGPU_IMPORT
+# undef WGPU_IGNORE_____
+# undef WGPU_IMPORT_FUNC
+
+# endif // USE_WEBGPU_DYNAMIC_LIB
+
+#define WGPU_RELEASE_FUNC(_name) \
+ inline void wgpuRelease(WGPU##_name& _obj) \
+ { \
+ if (NULL != _obj) \
+ { \
+ wgpu##_name##Release(_obj); \
+ _obj = NULL; \
+ } \
+ }
+
+ WGPU_RELEASE
+
+#undef WGPU_RELEASE_FUNC
+
+ inline constexpr bx::StringView toStringView(const WGPUStringView& _str)
+ {
+ return bx::StringView(_str.data, int32_t(_str.length) );
+ }
+
+ static void trace(const WGPUStringView& _message)
+ {
+ if (NULL != _message.data
+ && 0 != _message.length)
+ {
+ BX_TRACE("WGPU: `%.*s`", _message.length, _message.data);
+ }
+ }
+
+ static void deviceLostCb(
+ const WGPUDevice* _device
+ , WGPUDeviceLostReason _reason
+ , WGPUStringView _message
+ , void* _userdata1
+ , void* _userdata2
+ )
+ {
+ BX_UNUSED(_device, _reason, _message, _userdata1, _userdata2);
+
+ BX_TRACE("Reason: %d", _reason);
+
+ trace(_message);
+ }
+
+ static uint32_t s_uncapturedError = 0;
+
+ static bool wgpuErrorCheck()
+ {
+ BX_UNUSED(&wgpuErrorCheck);
+
+ if (0 < s_uncapturedError)
+ {
+ BX_WARN(1 == s_uncapturedError
+ , "Uncaptured error count is %d, which means that wgpu call that caused error wasn't wrapped with WGPU_CHECK macro!"
+ , s_uncapturedError
+ );
+
+ s_uncapturedError = 0;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ static void uncapturedErrorCb(
+ const WGPUDevice* _device
+ , WGPUErrorType _type
+ , WGPUStringView _message
+ , void* _userdata1
+ , void* _userdata2
+ )
+ {
+ BX_UNUSED(_device, _type, _message, _userdata1, _userdata2);
+
+ BX_TRACE("WGPU uncaptured error!\n\nErrorType: %d\n\n%.*s\n"
+ , _type
+ , _message.length
+ , _message.data
+ );
+
+ ++s_uncapturedError;
+ }
+
+ static void popErrorScopeCb(
+ WGPUPopErrorScopeStatus _status
+ , WGPUErrorType _type
+ , WGPUStringView _message
+ , void* _userdata1
+ , void* _userdata2
+ )
+ {
+ BX_UNUSED(_status, _type, _userdata1, _userdata2, &popErrorScopeCb);
+
+ trace(_message);
+ }
+
+ struct RendererContextWGPU : public RendererContextI
+ {
+ RendererContextWGPU()
+ : m_webgpuDll(NULL)
+ , m_renderDocDll(NULL)
+ , m_instance(NULL)
+ , m_adapter(NULL)
+ , m_device(NULL)
+ , m_maxAnisotropy(1)
+ , m_depthClamp(false)
+ , m_wireframe(false)
+ {
+ }
+
+ ~RendererContextWGPU()
+ {
+ }
+
+ static void requestAdapterCb(
+ WGPURequestAdapterStatus _status
+ , WGPUAdapter _adapter
+ , WGPUStringView _message
+ , void* _userdata1
+ , void* _userdata2
+ )
+ {
+ BX_UNUSED(_userdata2);
+
+ trace(_message);
+
+ if (WGPURequestAdapterStatus_Success != _status)
+ {
+ return;
+ }
+
+ RendererContextWGPU* renderCtx = (RendererContextWGPU*)_userdata1;
+ renderCtx->m_adapter = _adapter;
+ }
+
+ static void requestDeviceCb(
+ WGPURequestDeviceStatus _status
+ , WGPUDevice _device
+ , WGPUStringView _message
+ , void* _userdata1
+ , void* _userdata2
+ )
+ {
+ BX_UNUSED(_userdata2);
+
+ trace(_message);
+
+ if (WGPURequestDeviceStatus_Success != _status)
+ {
+ return;
+ }
+
+ RendererContextWGPU* renderCtx = (RendererContextWGPU*)_userdata1;
+ renderCtx->m_device = _device;
+ }
+
+ bool init(const Init& _init)
+ {
+ struct ErrorState
+ {
+ enum Enum
+ {
+ Default,
+ LoadedWebGPU,
+ InstanceCreated,
+ AdapterCreated,
+ DeviceCreated,
+ QueueCreated,
+ SwapChainCreated,
+ };
+ };
+
+ ErrorState::Enum errorState = ErrorState::Default;
+
+// m_fbh = BGFX_INVALID_HANDLE;
+ bx::memSet(m_uniforms, 0, sizeof(m_uniforms) );
+ bx::memSet(&m_resolution, 0, sizeof(m_resolution) );
+
+ if (_init.debug
+ || _init.profile)
+ {
+ m_renderDocDll = loadRenderDoc();
+ }
+
+ setGraphicsDebuggerPresent(false
+ || NULL != m_renderDocDll
+ );
+
+ const bool headless = NULL == g_platformData.nwh;
+
+ bool imported = true;
+
+ m_webgpuDll = bx::dlopen(
+#if BX_PLATFORM_WINDOWS
+ "webgpu_dawn.dll"
+// "wgpu_native.dll"
+#elif BX_PLATFORM_LINUX
+ "libwebgpu_dawn.so"
+// "libwgpu_native.so"
+#elif BX_PLATFORM_OSX
+ "libwebgpu_dawn.dylib"
+#else
+ "webgpu?"
+#endif //
+ );
+
+ if (NULL == m_webgpuDll)
+ {
+ BX_TRACE("Init error: Failed to load WebGPU dynamic library.");
+ goto error;
+ }
+
+ errorState = ErrorState::LoadedWebGPU;
+
+ BX_TRACE("Shared library functions:");
+
+#if USE_WEBGPU_DYNAMIC_LIB
+
+# define WGPU_IGNORE_____(_optional, _func)
+# define WGPU_IMPORT_FUNC(_optional, _func) \
+ wgpu##_func = (WGPUProc##_func)bx::dlsym(m_webgpuDll, "wgpu" #_func); \
+ BX_TRACE("\t%p wgpu" #_func, wgpu##_func); \
+ imported &= _optional || NULL != wgpu##_func
+
+WGPU_IMPORT
+
+# undef WGPU_IGNORE_____
+# undef WGPU_IMPORT_FUNC
+
+#endif // USE_WEBGPU_DYNAMIC_LIB
+
+ BX_TRACE("");
+
+ if (!imported)
+ {
+ BX_TRACE("Init error: Failed to load shared library functions.");
+ goto error;
+ }
+
+ {
+ {
+ WGPUInstanceFeatureName requiredFeatures[] =
+ {
+ WGPUInstanceFeatureName_TimedWaitAny,
+ WGPUInstanceFeatureName_ShaderSourceSPIRV,
+ };
+
+ WGPUInstanceDescriptor instanceDesc =
+ {
+ .nextInChain = NULL,
+ .requiredFeatureCount = BX_COUNTOF(requiredFeatures),
+ .requiredFeatures = requiredFeatures,
+ .requiredLimits = NULL,
+ };
+
+ m_instance = wgpuCreateInstance(&instanceDesc);
+
+ if (NULL == m_instance)
+ {
+ BX_TRACE("Failed to create instance!");
+ goto error;
+ }
+
+ errorState = ErrorState::InstanceCreated;
+ }
+
+ {
+ WGPURequestAdapterOptions rao =
+ {
+ .nextInChain = NULL,
+ .featureLevel = WGPUFeatureLevel_Undefined,
+ .powerPreference = WGPUPowerPreference_HighPerformance,
+ .forceFallbackAdapter = false,
+ .backendType = WGPUBackendType_Undefined,
+ .compatibleSurface = NULL,
+ };
+
+ WGPUFutureWaitInfo fwi =
+ {
+ .future = wgpuInstanceRequestAdapter(m_instance, &rao,
+ {
+ .nextInChain = NULL,
+ .mode = WGPUCallbackMode_WaitAnyOnly,
+ .callback = requestAdapterCb,
+ .userdata1 = this,
+ .userdata2 = NULL,
+ }),
+ .completed = false,
+ };
+
+ WGPUWaitStatus waitStatus = wgpuInstanceWaitAny(m_instance, 1, &fwi, UINT64_MAX);
+
+ if (WGPUWaitStatus_Success != waitStatus
+ || NULL == m_adapter)
+ {
+ goto error;
+ }
+
+ errorState = ErrorState::AdapterCreated;
+ }
+
+ {
+ WGPUSupportedWGSLLanguageFeatures supportedWGSLLanguageFeatures;
+ wgpuInstanceGetWGSLLanguageFeatures(m_instance, &supportedWGSLLanguageFeatures);
+
+ BX_ASSERT(bx::isSorted(s_languageFeature, BX_COUNTOF(s_languageFeature), LanguageFeature::cmpFn), "Feature table must be sorted!");
+
+ BX_TRACE("Supported WGSL language features (%d):", supportedWGSLLanguageFeatures.featureCount);
+
+ for (uint32_t ii = 0; ii < BX_COUNTOF(s_languageFeature); ++ii)
+ {
+ LanguageFeature& feature = s_languageFeature[ii];
+ feature.supported = false;
+ }
+
+ for (uint32_t ii = 0; ii < uint32_t(supportedWGSLLanguageFeatures.featureCount); ++ii)
+ {
+ const WGPUWGSLLanguageFeatureName featureName = supportedWGSLLanguageFeatures.features[ii];
+ const int32_t idx = bx::binarySearch(featureName, s_languageFeature, BX_COUNTOF(s_languageFeature), sizeof(LanguageFeature), LanguageFeature::cmpFn);
+
+ if (0 <= idx)
+ {
+ LanguageFeature& feature = s_languageFeature[idx];
+ BX_TRACE("\t%5x - %s", featureName, feature.name);
+ feature.supported = true;
+ }
+ else
+ {
+ BX_TRACE("\t%5x - ** Unknown?! **", featureName);
+ }
+ }
+
+ wgpuSupportedWGSLLanguageFeaturesFreeMembers(supportedWGSLLanguageFeatures);
+
+ BX_TRACE("");
+
+ WGPUSupportedFeatures supportedFeatures;
+ wgpuAdapterGetFeatures(m_adapter, &supportedFeatures);
+
+ BX_TRACE("Supported features (%d):", supportedFeatures.featureCount);
+
+ BX_ASSERT(bx::isSorted(s_feature, BX_COUNTOF(s_feature), Feature::cmpFn), "Feature table must be sorted!");
+
+ for (uint32_t ii = 0; ii < BX_COUNTOF(s_feature); ++ii)
+ {
+ Feature& feature = s_feature[ii];
+ feature.supported = false;
+ }
+
+ for (uint32_t ii = 0; ii < uint32_t(supportedFeatures.featureCount); ++ii)
+ {
+ const WGPUFeatureName featureName = supportedFeatures.features[ii];
+ const int32_t idx = bx::binarySearch(featureName, s_feature, BX_COUNTOF(s_feature), sizeof(Feature), Feature::cmpFn);
+
+ if (0 <= idx)
+ {
+ Feature& feature = s_feature[idx];
+ BX_TRACE("\t%5x - %s", featureName, feature.name);
+ feature.supported = true;
+ }
+ else
+ {
+ BX_TRACE("\t%5x - ** Unknown?! **", featureName);
+ }
+ }
+
+ BX_TRACE("");
+
+ wgpuSupportedFeaturesFreeMembers(supportedFeatures);
+
+ WGPUFeatureName requiredFeatures[] =
+ {
+ ifSupported(WGPUFeatureName_TimestampQuery),
+
+ ifSupported(WGPUFeatureName_DepthClipControl),
+
+ WGPUFeatureName_IndirectFirstInstance,
+
+ ifSupported(WGPUFeatureName_Unorm16TextureFormats),
+
+ ifSupported(WGPUFeatureName_TextureComponentSwizzle),
+ ifSupported(WGPUFeatureName_TextureFormatsTier1),
+ ifSupported(WGPUFeatureName_TextureFormatsTier2),
+
+ ifSupported(WGPUFeatureName_TextureCompressionBC),
+ ifSupported(WGPUFeatureName_TextureCompressionETC2),
+ ifSupported(WGPUFeatureName_TextureCompressionASTC),
+
+ ifSupported(WGPUFeatureName_RG11B10UfloatRenderable),
+
+ ifSupported(WGPUFeatureName_Depth32FloatStencil8),
+ };
+
+ bx::quickSort(requiredFeatures, BX_COUNTOF(requiredFeatures) );
+
+ uint32_t requiredFeatureCount = BX_COUNTOF(requiredFeatures);
+ for (uint32_t ii = 0; ii < BX_COUNTOF(requiredFeatures); ++ii)
+ {
+ if (WGPUFeatureName_Force32 == requiredFeatures[ii])
+ {
+ requiredFeatureCount = ii;
+ break;
+ }
+ }
+
+ BX_TRACE("Required features (%d / %d):", requiredFeatureCount, BX_COUNTOF(requiredFeatures) );
+
+ for (uint32_t ii = 0; ii < requiredFeatureCount; ++ii)
+ {
+ const WGPUFeatureName featureName = requiredFeatures[ii];
+
+ const int32_t idx = bx::binarySearch(featureName, s_feature, BX_COUNTOF(s_feature), sizeof(Feature), Feature::cmpFn);
+ BX_ASSERT(0 <= idx, "Feature listed in required features must be present in s_feature table!");
+
+ const Feature& feature = s_feature[idx];
+ BX_TRACE("\t%5x - %s%s", featureName, feature.name, feature.supported ? "" : " <- this feature is not optional, and it's not supported by WebGPU implementation!");
+ BX_UNUSED(feature);
+ }
+
+ BX_TRACE("");
+
+ WGPULimits requiredLimits = WGPU_LIMITS_INIT;
+ WGPUStatus status = wgpuAdapterGetLimits(m_adapter, &requiredLimits);
+
+ if (WGPUStatus_Success == status)
+ {
+ requiredLimits.maxComputeWorkgroupSizeX = 1024;
+ requiredLimits.maxComputeWorkgroupSizeY = 1024;
+ requiredLimits.maxComputeWorkgroupSizeZ = 64;
+ }
+
+ static constexpr uint32_t kMaxEnabledTogles = 10;
+ const char* enabledToggles[kMaxEnabledTogles];
+ uint32_t enabledTogglesCount = 0;
+
+ enabledToggles[enabledTogglesCount++] = "allow_unsafe_apis"; // TimestampWrite requires this.
+
+ if (_init.debug)
+ {
+// bx::setEnv("DAWN_DEBUG_BREAK_ON_ERROR", "1");
+
+ enabledToggles[enabledTogglesCount++] = "dump_shaders_on_failure";
+ enabledToggles[enabledTogglesCount++] = "use_user_defined_labels_in_backend";
+// enabledToggles[enabledTogglesCount++] = "dump_shaders";
+ }
+ else
+ {
+ enabledToggles[enabledTogglesCount++] = "skip_validation";
+ }
+
+ BX_ASSERT(enabledTogglesCount < kMaxEnabledTogles, "");
+
+ BX_TRACE("Dawn enabled toggles (%d):", enabledTogglesCount);
+
+ for (uint32_t ii = 0; ii < enabledTogglesCount; ++ii)
+ {
+ BX_TRACE("\t%d: %s", ii, enabledToggles[ii]);
+ }
+
+ BX_TRACE("");
+
+ WGPUDawnTogglesDescriptor dawnTogglesDescriptor =
+ {
+ .chain =
+ {
+ .next = NULL,
+ .sType = WGPUSType_DawnTogglesDescriptor,
+ },
+ .enabledToggleCount = enabledTogglesCount,
+ .enabledToggles = enabledToggles,
+ .disabledToggleCount = 0,
+ .disabledToggles = NULL,
+ };
+
+ WGPUDeviceDescriptor deviceDesc =
+ {
+ .nextInChain = &dawnTogglesDescriptor.chain,
+ .label = WGPU_STRING_VIEW_INIT,
+ .requiredFeatureCount = requiredFeatureCount,
+ .requiredFeatures = requiredFeatures,
+ .requiredLimits = &requiredLimits,
+ .defaultQueue = WGPU_QUEUE_DESCRIPTOR_INIT,
+ .deviceLostCallbackInfo =
+ {
+ .nextInChain = NULL,
+ .mode = WGPUCallbackMode_WaitAnyOnly,
+ .callback = deviceLostCb,
+ .userdata1 = this,
+ .userdata2 = NULL,
+ },
+ .uncapturedErrorCallbackInfo
+ {
+ .nextInChain = NULL,
+ .callback = uncapturedErrorCb,
+ .userdata1 = this,
+ .userdata2 = NULL,
+ },
+ };
+
+ WGPUFutureWaitInfo fwi =
+ {
+ .future = wgpuAdapterRequestDevice(m_adapter, &deviceDesc,
+ {
+ .nextInChain = NULL,
+ .mode = WGPUCallbackMode_WaitAnyOnly,
+ .callback = requestDeviceCb,
+ .userdata1 = this,
+ .userdata2 = NULL,
+ }),
+ .completed = false,
+ };
+
+ WGPUWaitStatus waitStatus = wgpuInstanceWaitAny(m_instance, 1, &fwi, UINT64_MAX);
+
+ if (WGPUWaitStatus_Success != waitStatus
+ || NULL == m_device)
+ {
+ goto error;
+ }
+
+ errorState = ErrorState::DeviceCreated;
+ }
+
+ {
+ WGPUStatus status;
+
+ WGPUAdapterInfo adapterInfo = WGPU_ADAPTER_INFO_INIT;
+ status = wgpuAdapterGetInfo(m_adapter, &adapterInfo);
+
+ if (WGPUStatus_Success == status)
+ {
+#define FORMAT(_str) _str.length, _str.data
+
+ BX_TRACE("Adapter info:");
+ BX_TRACE("\t Vendor: %.*s", FORMAT(adapterInfo.vendor) );
+ BX_TRACE("\tArchitecture: %.*s", FORMAT(adapterInfo.architecture) );
+ BX_TRACE("\t Device: %.*s", FORMAT(adapterInfo.device) );
+ BX_TRACE("\t Description: %.*s", FORMAT(adapterInfo.description) );
+#undef FORMAT
+
+ BX_TRACE("\t VendorId: %x", adapterInfo.vendorID);
+ BX_TRACE("\t DeviceId: %x", adapterInfo.deviceID);
+
+ BX_TRACE("\tBackend type (%x): %s"
+ , adapterInfo.backendType
+ , s_backendType[bx::min(adapterInfo.backendType, BX_COUNTOF(s_backendType)-1)]
+ );
+ BX_TRACE("\tAdapter type (%x): %s"
+ , adapterInfo.adapterType
+ , s_adapterType[bx::min(adapterInfo.adapterType, BX_COUNTOF(s_adapterType)-1)]
+ );
+
+ g_caps.vendorId = bx::narrowCast(adapterInfo.vendorID);
+ g_caps.deviceId = bx::narrowCast(adapterInfo.deviceID);
+ }
+
+ BX_TRACE("");
+
+ m_limits = WGPU_LIMITS_INIT;
+ status = wgpuAdapterGetLimits(m_adapter, &m_limits);
+
+ if (WGPUStatus_Success == status)
+ {
+ BX_TRACE("WGPU limits:");
+
+ BX_TRACE("\tmaxTextureDimension1D: %u", m_limits.maxTextureDimension1D);
+ BX_TRACE("\tmaxTextureDimension2D: %u", m_limits.maxTextureDimension2D);
+ BX_TRACE("\tmaxTextureDimension3D: %u", m_limits.maxTextureDimension3D);
+ BX_TRACE("\tmaxTextureArrayLayers: %u", m_limits.maxTextureArrayLayers);
+ BX_TRACE("\tmaxBindGroups: %u", m_limits.maxBindGroups);
+ BX_TRACE("\tmaxBindGroupsPlusVertexBuffers: %u", m_limits.maxBindGroupsPlusVertexBuffers);
+ BX_TRACE("\tmaxBindingsPerBindGroup: %u", m_limits.maxBindingsPerBindGroup);
+ BX_TRACE("\tmaxDynamicUniformBuffersPerPipelineLayout: %u", m_limits.maxDynamicUniformBuffersPerPipelineLayout);
+ BX_TRACE("\tmaxDynamicStorageBuffersPerPipelineLayout: %u", m_limits.maxDynamicStorageBuffersPerPipelineLayout);
+ BX_TRACE("\tmaxSampledTexturesPerShaderStage: %u", m_limits.maxSampledTexturesPerShaderStage);
+ BX_TRACE("\tmaxSamplersPerShaderStage: %u", m_limits.maxSamplersPerShaderStage);
+ BX_TRACE("\tmaxStorageBuffersPerShaderStage: %u", m_limits.maxStorageBuffersPerShaderStage);
+ BX_TRACE("\tmaxStorageTexturesPerShaderStage: %u", m_limits.maxStorageTexturesPerShaderStage);
+ BX_TRACE("\tmaxUniformBuffersPerShaderStage: %u", m_limits.maxUniformBuffersPerShaderStage);
+ BX_TRACE("\tmaxUniformBufferBindingSize: %u", m_limits.maxUniformBufferBindingSize);
+ BX_TRACE("\tmaxStorageBufferBindingSize: %u", m_limits.maxStorageBufferBindingSize);
+ BX_TRACE("\tminUniformBufferOffsetAlignment: %u", m_limits.minUniformBufferOffsetAlignment);
+ BX_TRACE("\tminStorageBufferOffsetAlignment: %u", m_limits.minStorageBufferOffsetAlignment);
+ BX_TRACE("\tmaxVertexBuffers: %u", m_limits.maxVertexBuffers);
+ BX_TRACE("\tmaxBufferSize: %u", m_limits.maxBufferSize);
+ BX_TRACE("\tmaxVertexAttributes: %u", m_limits.maxVertexAttributes);
+ BX_TRACE("\tmaxVertexBufferArrayStride: %u", m_limits.maxVertexBufferArrayStride);
+ BX_TRACE("\tmaxInterStageShaderVariables: %u", m_limits.maxInterStageShaderVariables);
+ BX_TRACE("\tmaxColorAttachments: %u", m_limits.maxColorAttachments);
+ BX_TRACE("\tmaxColorAttachmentBytesPerSample: %u", m_limits.maxColorAttachmentBytesPerSample);
+ BX_TRACE("\tmaxComputeWorkgroupStorageSize: %u", m_limits.maxComputeWorkgroupStorageSize);
+ BX_TRACE("\tmaxComputeInvocationsPerWorkgroup: %u", m_limits.maxComputeInvocationsPerWorkgroup);
+ BX_TRACE("\tmaxComputeWorkgroupSizeX: %u", m_limits.maxComputeWorkgroupSizeX);
+ BX_TRACE("\tmaxComputeWorkgroupSizeY: %u", m_limits.maxComputeWorkgroupSizeY);
+ BX_TRACE("\tmaxComputeWorkgroupSizeZ: %u", m_limits.maxComputeWorkgroupSizeZ);
+ BX_TRACE("\tmaxComputeWorkgroupsPerDimension: %u", m_limits.maxComputeWorkgroupsPerDimension);
+ BX_TRACE("\tmaxImmediateSize: %u", m_limits.maxImmediateSize);
+
+ g_caps.limits.maxTextureSize = m_limits.maxTextureDimension2D;
+ g_caps.limits.maxTextureLayers = m_limits.maxTextureArrayLayers;
+ g_caps.limits.maxTextureSamplers = bx::min(m_limits.maxSamplersPerShaderStage, BGFX_CONFIG_MAX_TEXTURE_SAMPLERS);
+ g_caps.limits.maxComputeBindings = BGFX_CONFIG_MAX_TEXTURE_SAMPLERS;
+ g_caps.limits.maxFBAttachments = bx::min(m_limits.maxColorAttachments, BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS);
+ g_caps.limits.maxVertexStreams = bx::min(m_limits.maxVertexBuffers, BGFX_CONFIG_MAX_VERTEX_STREAMS);
+
+ g_caps.supported = 0
+ | BGFX_CAPS_ALPHA_TO_COVERAGE
+ | BGFX_CAPS_BLEND_INDEPENDENT
+ | BGFX_CAPS_COMPUTE
+// | BGFX_CAPS_CONSERVATIVE_RASTER
+ | BGFX_CAPS_DRAW_INDIRECT
+// | BGFX_CAPS_DRAW_INDIRECT_COUNT
+ | BGFX_CAPS_FRAGMENT_DEPTH
+// | BGFX_CAPS_FRAGMENT_ORDERING
+// | BGFX_CAPS_HDR10
+// | BGFX_CAPS_HIDPI
+ | BGFX_CAPS_IMAGE_RW
+ | BGFX_CAPS_INDEX32
+ | BGFX_CAPS_INSTANCING
+ | BGFX_CAPS_OCCLUSION_QUERY
+ | BGFX_CAPS_PRIMITIVE_ID
+ | BGFX_CAPS_RENDERER_MULTITHREADED
+ | BGFX_CAPS_SWAP_CHAIN
+ | BGFX_CAPS_TEXTURE_2D_ARRAY
+ | BGFX_CAPS_TEXTURE_3D
+ | BGFX_CAPS_TEXTURE_BLIT
+ | BGFX_CAPS_TEXTURE_COMPARE_ALL
+ | BGFX_CAPS_TEXTURE_COMPARE_LEQUAL
+ | BGFX_CAPS_TEXTURE_CUBE_ARRAY
+ | BGFX_CAPS_TEXTURE_READ_BACK
+// | BGFX_CAPS_TRANSPARENT_BACKBUFFER
+ | BGFX_CAPS_VERTEX_ATTRIB_HALF
+ | BGFX_CAPS_VERTEX_ATTRIB_UINT10
+ | BGFX_CAPS_VERTEX_ID
+// | BGFX_CAPS_VIEWPORT_LAYER_ARRAY
+ ;
+
+ for (uint32_t formatIdx = 0; formatIdx < TextureFormat::Count; ++formatIdx)
+ {
+ g_caps.formats[formatIdx] = 0
+ | BGFX_CAPS_FORMAT_TEXTURE_NONE
+ | (WGPUTextureFormat_Undefined != s_textureFormat[formatIdx].m_fmt ? 0
+ | BGFX_CAPS_FORMAT_TEXTURE_2D
+ | BGFX_CAPS_FORMAT_TEXTURE_3D
+ | BGFX_CAPS_FORMAT_TEXTURE_CUBE
+ | 0
+ | (!bimg::isCompressed(bimg::TextureFormat::Enum(formatIdx) ) ? BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER : 0)
+ : 0)
+ | (WGPUTextureFormat_Undefined != s_textureFormat[formatIdx].m_fmtSrgb ? 0
+ | BGFX_CAPS_FORMAT_TEXTURE_2D_SRGB
+ | BGFX_CAPS_FORMAT_TEXTURE_3D_SRGB
+ | BGFX_CAPS_FORMAT_TEXTURE_CUBE_SRGB
+ : 0)
+// | BGFX_CAPS_FORMAT_TEXTURE_VERTEX
+// | BGFX_CAPS_FORMAT_TEXTURE_IMAGE_READ
+// | BGFX_CAPS_FORMAT_TEXTURE_IMAGE_WRITE
+// | BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER
+// | BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER_MSAA
+// | BGFX_CAPS_FORMAT_TEXTURE_MSAA
+// | BGFX_CAPS_FORMAT_TEXTURE_MIP_AUTOGEN
+ ;
+ }
+
+ g_caps.formats[TextureFormat::ETC1] = 0
+ | BGFX_CAPS_FORMAT_TEXTURE_2D_EMULATED
+ | BGFX_CAPS_FORMAT_TEXTURE_3D_EMULATED
+ | BGFX_CAPS_FORMAT_TEXTURE_CUBE_EMULATED
+ ;
+ g_caps.formats[TextureFormat::ETC2] = 0
+ | BGFX_CAPS_FORMAT_TEXTURE_2D_EMULATED
+ | BGFX_CAPS_FORMAT_TEXTURE_3D_EMULATED
+ | BGFX_CAPS_FORMAT_TEXTURE_CUBE_EMULATED
+ ;
+ g_caps.formats[TextureFormat::ASTC4x4 ] = 0;
+ g_caps.formats[TextureFormat::ASTC5x4 ] = 0;
+ g_caps.formats[TextureFormat::ASTC5x5 ] = 0;
+ g_caps.formats[TextureFormat::ASTC6x5 ] = 0;
+ g_caps.formats[TextureFormat::ASTC6x6 ] = 0;
+ g_caps.formats[TextureFormat::ASTC8x5 ] = 0;
+ g_caps.formats[TextureFormat::ASTC8x6 ] = 0;
+ g_caps.formats[TextureFormat::ASTC8x8 ] = 0;
+ g_caps.formats[TextureFormat::ASTC10x5 ] = 0;
+ g_caps.formats[TextureFormat::ASTC10x6 ] = 0;
+ g_caps.formats[TextureFormat::ASTC10x8 ] = 0;
+ g_caps.formats[TextureFormat::ASTC10x10] = 0;
+ g_caps.formats[TextureFormat::ASTC12x10] = 0;
+ g_caps.formats[TextureFormat::ASTC12x12] = 0;
+
+ g_caps.formats[TextureFormat::RGBA8] |= 0
+ | BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER
+ | BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER_MSAA
+ ;
+ g_caps.formats[TextureFormat::BGRA8] |= 0
+ | BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER
+ | BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER_MSAA
+ ;
+ g_caps.formats[TextureFormat::R16F] |= 0
+ | BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER
+ | BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER_MSAA
+ ;
+ g_caps.formats[TextureFormat::RG16F] |= 0
+ | BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER
+ | BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER_MSAA
+ ;
+ g_caps.formats[TextureFormat::R32F] |= 0
+ | BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER
+ | BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER_MSAA
+ ;
+ g_caps.formats[TextureFormat::RGBA16F] |= 0
+ | BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER
+ | BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER_MSAA
+ ;
+ g_caps.formats[TextureFormat::D16] |= 0
+ | BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER
+ | BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER_MSAA
+ ;
+ g_caps.formats[TextureFormat::D24S8] |= 0
+ | BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER
+ | BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER_MSAA
+ ;
+ g_caps.formats[TextureFormat::D32F] |= 0
+ | BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER
+ | BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER_MSAA
+ ;
+ }
+ }
+
+ BX_TRACE("");
+
+ {
+ m_maxFrameLatency = _init.resolution.maxFrameLatency == 0
+ ? BGFX_CONFIG_MAX_FRAME_LATENCY
+ : _init.resolution.maxFrameLatency
+ ;
+
+ m_cmd.init(m_device);
+
+ errorState = ErrorState::QueueCreated;
+ }
+
+ {
+ m_resolution = _init.resolution;
+ m_resolution.reset &= ~BGFX_RESET_INTERNAL_FORCE;
+
+ m_textVideoMem.resize(false, _init.resolution.width, _init.resolution.height);
+ m_textVideoMem.clear();
+
+ m_numWindows = 0;
+
+ if (!headless)
+ {
+ m_backBuffer.create(
+ UINT16_MAX
+ , g_platformData.nwh
+ , m_resolution.width
+ , m_resolution.height
+ , m_resolution.formatColor
+ );
+
+
+ m_windows[0] = BGFX_INVALID_HANDLE;
+ m_numWindows++;
+
+ postReset();
+ }
+
+ errorState = ErrorState::SwapChainCreated;
+ }
+
+ {
+ m_gpuTimer.init();
+ m_occlusionQuery.init();
+ m_uniformScratchBuffer.createUniform(1<<20, m_maxFrameLatency*2);
+ }
+ }
+
+ return true;
+
+ error:
+ BX_TRACE("errorState %d", errorState);
+ switch (errorState)
+ {
+ case ErrorState::SwapChainCreated:
+ [[fallthrough]];
+
+ case ErrorState::QueueCreated:
+ m_cmd.shutdown();
+ [[fallthrough]];
+
+ case ErrorState::DeviceCreated:
+ wgpuRelease(m_device);
+ [[fallthrough]];
+
+ case ErrorState::AdapterCreated:
+ wgpuRelease(m_adapter);
+ [[fallthrough]];
+
+ case ErrorState::InstanceCreated:
+ wgpuRelease(m_instance);
+ [[fallthrough]];
+
+ case ErrorState::LoadedWebGPU:
+ bx::dlclose(m_webgpuDll);
+ m_webgpuDll = NULL;
+ [[fallthrough]];
+
+ case ErrorState::Default:
+ unloadRenderDoc(m_renderDocDll);
+ break;
+ };
+
+ return false;
+ }
+
+ void shutdown()
+ {
+ preReset();
+
+ for (uint32_t ii = 0; ii < BX_COUNTOF(m_frameBuffers); ++ii)
+ {
+ m_frameBuffers[ii].destroy();
+ }
+
+ for (uint32_t ii = 0; ii < BX_COUNTOF(m_indexBuffers); ++ii)
+ {
+ m_indexBuffers[ii].destroy();
+ }
+
+ for (uint32_t ii = 0; ii < BX_COUNTOF(m_vertexBuffers); ++ii)
+ {
+ m_vertexBuffers[ii].destroy();
+ }
+
+ for (uint32_t ii = 0; ii < BX_COUNTOF(m_shaders); ++ii)
+ {
+ m_shaders[ii].destroy();
+ }
+
+ for (uint32_t ii = 0; ii < BX_COUNTOF(m_textures); ++ii)
+ {
+ m_textures[ii].destroy();
+ }
+
+ m_backBuffer.destroy();
+
+ m_gpuTimer.shutdown();
+ m_occlusionQuery.shutdown();
+ m_uniformScratchBuffer.destroy();
+
+ m_cmd.shutdown();
+
+ wgpuRelease(m_device);
+ wgpuRelease(m_adapter);
+ wgpuRelease(m_instance);
+
+ bx::dlclose(m_webgpuDll);
+ m_webgpuDll = NULL;
+
+ unloadRenderDoc(m_renderDocDll);
+ }
+
+ RendererType::Enum getRendererType() const override
+ {
+ return RendererType::WebGPU;
+ }
+
+ const char* getRendererName() const override
+ {
+ return BGFX_RENDERER_WEBGPU_NAME;
+ }
+
+ bool isDeviceRemoved() override
+ {
+ return false;
+ }
+
+ void flip() override
+ {
+ int64_t start = bx::getHPCounter();
+
+ for (uint16_t ii = 0; ii < m_numWindows; ++ii)
+ {
+ FrameBufferWGPU& fb = isValid(m_windows[ii])
+ ? m_frameBuffers[m_windows[ii].idx]
+ : m_backBuffer
+ ;
+
+ fb.present();
+ }
+
+ const int64_t now = bx::getHPCounter();
+
+ m_presentElapsed += now - start;
+ }
+
+ void createIndexBuffer(IndexBufferHandle _handle, const Memory* _mem, uint16_t _flags) override
+ {
+ m_indexBuffers[_handle.idx].create(_mem->size, _mem->data, _flags, false);
+ }
+
+ void destroyIndexBuffer(IndexBufferHandle _handle) override
+ {
+ m_indexBuffers[_handle.idx].destroy();
+ }
+
+ void createVertexLayout(VertexLayoutHandle _handle, const VertexLayout& _layout) override
+ {
+ VertexLayout& layout = m_vertexLayouts[_handle.idx];
+ bx::memCopy(&layout, &_layout, sizeof(VertexLayout) );
+ dump(layout);
+ }
+
+ void destroyVertexLayout(VertexLayoutHandle /*_handle*/) override
+ {
+ }
+
+ void createVertexBuffer(VertexBufferHandle _handle, const Memory* _mem, VertexLayoutHandle _layoutHandle, uint16_t _flags) override
+ {
+ m_vertexBuffers[_handle.idx].create(_mem->size, _mem->data, _layoutHandle, _flags);
+ }
+
+ void destroyVertexBuffer(VertexBufferHandle _handle) override
+ {
+ m_vertexBuffers[_handle.idx].destroy();
+ }
+
+ void createDynamicIndexBuffer(IndexBufferHandle _handle, uint32_t _size, uint16_t _flags) override
+ {
+ m_indexBuffers[_handle.idx].create(_size, NULL, _flags, false);
+ }
+
+ void updateDynamicIndexBuffer(IndexBufferHandle _handle, uint32_t _offset, uint32_t _size, const Memory* _mem) override
+ {
+ m_indexBuffers[_handle.idx].update(_offset, bx::min(_size, _mem->size), _mem->data);
+ }
+
+ void destroyDynamicIndexBuffer(IndexBufferHandle _handle) override
+ {
+ m_indexBuffers[_handle.idx].destroy();
+ }
+
+ void createDynamicVertexBuffer(VertexBufferHandle _handle, uint32_t _size, uint16_t _flags) override
+ {
+ VertexLayoutHandle layoutHandle = BGFX_INVALID_HANDLE;
+ m_vertexBuffers[_handle.idx].create(_size, NULL, layoutHandle, _flags);
+ }
+
+ void updateDynamicVertexBuffer(VertexBufferHandle _handle, uint32_t _offset, uint32_t _size, const Memory* _mem) override
+ {
+ m_vertexBuffers[_handle.idx].update(_offset, bx::min(_size, _mem->size), _mem->data);
+ }
+
+ void destroyDynamicVertexBuffer(VertexBufferHandle _handle) override
+ {
+ m_vertexBuffers[_handle.idx].destroy();
+ }
+
+ void createShader(ShaderHandle _handle, const Memory* _mem) override
+ {
+ m_shaders[_handle.idx].create(_mem);
+ }
+
+ void destroyShader(ShaderHandle _handle) override
+ {
+ m_shaders[_handle.idx].destroy();
+ }
+
+ void createProgram(ProgramHandle _handle, ShaderHandle _vsh, ShaderHandle _fsh) override
+ {
+ m_program[_handle.idx].create(&m_shaders[_vsh.idx], isValid(_fsh) ? &m_shaders[_fsh.idx] : NULL);
+ }
+
+ void destroyProgram(ProgramHandle _handle) override
+ {
+ m_program[_handle.idx].destroy();
+ }
+
+ void* createTexture(TextureHandle _handle, const Memory* _mem, uint64_t _flags, uint8_t _skip) override
+ {
+ m_textures[_handle.idx].create(_mem, _flags, _skip);
+ return NULL;
+ }
+
+ void updateTexture(TextureHandle _handle, uint8_t _side, uint8_t _mip, const Rect& _rect, uint16_t _z, uint16_t _depth, uint16_t _pitch, const Memory* _mem) override
+ {
+ m_textures[_handle.idx].update(_side, _mip, _rect, _z, _depth, _pitch, _mem);
+ }
+
+ bool s_done;
+
+ struct ReadTexture
+ {
+ WGPUBuffer buffer;
+ uint32_t size;
+ void* data;
+ uint32_t dataPitch;
+ uint32_t pitch;
+ uint32_t height;
+ };
+
+ static void readTextureCb(WGPUMapAsyncStatus _status, WGPUStringView _message, void* _userdata1, void* _userdata2)
+ {
+ BX_ASSERT(WGPUMapAsyncStatus_Success == _status, "%d", _status);
+
+ BX_UNUSED(_status, _message, _userdata2);
+
+ ReadTexture& readTexture = *(ReadTexture*)_userdata1;
+
+ const void* result = (const void*)WGPU_CHECK(wgpuBufferGetConstMappedRange(
+ readTexture.buffer
+ , 0
+ , readTexture.size
+ ) );
+
+ bx::gather(
+ readTexture.data
+ , result
+ , readTexture.pitch
+ , readTexture.dataPitch
+ , readTexture.height
+ );
+
+ WGPU_CHECK(wgpuBufferUnmap(readTexture.buffer) );
+
+ wgpuRelease(readTexture.buffer);
+
+ *(bool*)(_userdata2) = true;
+ }
+
+ void readTexture(TextureHandle _handle, void* _data, uint8_t _mip) override
+ {
+ const TextureWGPU& texture = m_textures[_handle.idx];
+
+ uint32_t srcWidth = bx::uint32_max(1, texture.m_width >>_mip);
+ uint32_t srcHeight = bx::uint32_max(1, texture.m_height>>_mip);
+
+ const uint8_t bpp = bimg::getBitsPerPixel(bimg::TextureFormat::Enum(texture.m_textureFormat) );
+ const uint32_t dstPitch = srcWidth*bpp/8;
+ const uint32_t dstBufferPitch = bx::alignUp(dstPitch, 256);
+
+ const uint32_t copySize = dstBufferPitch * srcHeight;
+
+ WGPUBufferDescriptor readTextureBufferDesc =
+ {
+ .nextInChain = NULL,
+ .label = toWGPUStringView("Read Texture Buffer"),
+ .usage = 0
+ | WGPUBufferUsage_MapRead
+ | WGPUBufferUsage_CopyDst
+ ,
+ .size = copySize,
+ .mappedAtCreation = false,
+ };
+
+ WGPUBuffer readTextureBuffer = WGPU_CHECK(wgpuDeviceCreateBuffer(m_device, &readTextureBufferDesc) );
+
+ s_done = false;
+
+ m_cmd.copyTextureToBuffer(
+ {
+ .texture = texture.m_texture,
+ .mipLevel = _mip,
+ .origin =
+ {
+ .x = 0,
+ .y = 0,
+ .z = 0,
+ },
+ .aspect = WGPUTextureAspect_All,
+ },
+ {
+ .layout =
+ {
+ .offset = 0,
+ .bytesPerRow = dstBufferPitch,
+ .rowsPerImage = srcHeight,
+ },
+ .buffer = readTextureBuffer,
+ },
+ {
+ .width = srcWidth,
+ .height = srcHeight,
+ .depthOrArrayLayers = 1,
+ }
+ );
+
+ m_cmd.kick();
+ m_cmd.wait();
+
+ ReadTexture readTexture =
+ {
+ .buffer = readTextureBuffer,
+ .size = copySize,
+ .data = _data,
+ .dataPitch = dstPitch,
+ .pitch = dstBufferPitch,
+ .height = srcHeight,
+ };
+
+ WGPU_CHECK(wgpuBufferMapAsync(
+ readTextureBuffer
+ , WGPUMapMode_Read
+ , 0
+ , copySize
+ , {
+ .nextInChain = NULL,
+ .mode = WGPUCallbackMode_AllowProcessEvents,
+ .callback = readTextureCb,
+ .userdata1 = &readTexture,
+ .userdata2 = &s_done,
+ }) );
+
+ while (!s_done)
+ {
+ wgpuInstanceProcessEvents(m_instance);
+ }
+ }
+
+ void resizeTexture(TextureHandle _handle, uint16_t _width, uint16_t _height, uint8_t _numMips, uint16_t _numLayers) override
+ {
+ TextureWGPU& texture = m_textures[_handle.idx];
+
+ uint32_t size = sizeof(uint32_t) + sizeof(TextureCreate);
+ const Memory* mem = alloc(size);
+
+ bx::StaticMemoryBlockWriter writer(mem->data, mem->size);
+ bx::write(&writer, kChunkMagicTex, bx::ErrorAssert{});
+
+ TextureCreate tc;
+ tc.m_width = _width;
+ tc.m_height = _height;
+ tc.m_depth = 0;
+ tc.m_numLayers = _numLayers;
+ tc.m_numMips = _numMips;
+ tc.m_format = TextureFormat::Enum(texture.m_requestedFormat);
+ tc.m_cubeMap = false;
+ tc.m_mem = NULL;
+ bx::write(&writer, tc, bx::ErrorAssert{});
+
+ texture.destroy();
+ texture.create(mem, texture.m_flags, 0);
+
+ release(mem);
+ }
+
+ void overrideInternal(TextureHandle /*_handle*/, uintptr_t /*_ptr*/, uint16_t /*_layerIndex*/) override
+ {
+ }
+
+ uintptr_t getInternal(TextureHandle _handle) override
+ {
+ setGraphicsDebuggerPresent(true);
+ return uintptr_t(m_textures[_handle.idx].m_texture);
+ }
+
+ void destroyTexture(TextureHandle _handle) override
+ {
+ m_textures[_handle.idx].destroy();
+ }
+
+ void createFrameBuffer(FrameBufferHandle _handle, uint8_t _num, const Attachment* _attachment) override
+ {
+ m_frameBuffers[_handle.idx].create(_num, _attachment);
+ }
+
+ void createFrameBuffer(FrameBufferHandle _handle, void* _nwh, uint32_t _width, uint32_t _height, TextureFormat::Enum _format, TextureFormat::Enum _depthFormat) override
+ {
+ for (uint32_t ii = 0, num = m_numWindows; ii < num; ++ii)
+ {
+ FrameBufferHandle handle = m_windows[ii];
+ if (isValid(handle)
+ && m_frameBuffers[handle.idx].m_swapChain.m_nwh == _nwh)
+ {
+ destroyFrameBuffer(handle);
+ }
+ }
+
+ uint16_t denseIdx = m_numWindows++;
+ m_windows[denseIdx] = _handle;
+ m_frameBuffers[_handle.idx].create(denseIdx, _nwh, _width, _height, _format, _depthFormat);
+ }
+
+ void destroyFrameBuffer(FrameBufferHandle _handle) override
+ {
+ FrameBufferWGPU& frameBuffer = m_frameBuffers[_handle.idx];
+
+ uint16_t denseIdx = frameBuffer.destroy();
+ if (UINT16_MAX != denseIdx)
+ {
+ --m_numWindows;
+ if (m_numWindows > 1)
+ {
+ FrameBufferHandle handle = m_windows[m_numWindows];
+ m_windows[m_numWindows] = {kInvalidHandle};
+ if (m_numWindows != denseIdx)
+ {
+ m_windows[denseIdx] = handle;
+ m_frameBuffers[handle.idx].m_denseIdx = denseIdx;
+ }
+ }
+ }
+ }
+
+ void createUniform(UniformHandle _handle, UniformType::Enum _type, uint16_t _num, const char* _name) override
+ {
+ if (NULL != m_uniforms[_handle.idx])
+ {
+ bx::free(g_allocator, m_uniforms[_handle.idx]);
+ }
+
+ uint32_t size = g_uniformTypeSize[_type]*_num;
+ void* data = bx::alloc(g_allocator, size);
+ bx::memSet(data, 0, size);
+ m_uniforms[_handle.idx] = data;
+ m_uniformReg.add(_handle, _name);
+ }
+
+ void destroyUniform(UniformHandle _handle) override
+ {
+ bx::free(g_allocator, m_uniforms[_handle.idx]);
+ m_uniforms[_handle.idx] = NULL;
+ m_uniformReg.remove(_handle);
+ }
+
+ void requestScreenShot(FrameBufferHandle /*_handle*/, const char* /*_filePath*/) override
+ {
+ }
+
+ void updateViewName(ViewId _id, const char* _name) override
+ {
+ bx::strCopy(
+ &s_viewName[_id][BGFX_CONFIG_MAX_VIEW_NAME_RESERVED]
+ , BX_COUNTOF(s_viewName[0])-BGFX_CONFIG_MAX_VIEW_NAME_RESERVED
+ , _name
+ );
+ }
+
+ void updateUniform(uint16_t _loc, const void* _data, uint32_t _size) override
+ {
+ bx::memCopy(m_uniforms[_loc], _data, _size);
+ }
+
+ void invalidateOcclusionQuery(OcclusionQueryHandle _handle) override
+ {
+ m_occlusionQuery.invalidate(_handle);
+ }
+
+ void setMarker(const char* _marker, uint16_t _len) override
+ {
+ BX_UNUSED(_marker, _len);
+ }
+
+ virtual void setName(Handle _handle, const char* _name, uint16_t _len) override
+ {
+ switch (_handle.type)
+ {
+ case Handle::IndexBuffer:
+ WGPU_CHECK(wgpuBufferSetLabel(m_indexBuffers[_handle.idx].m_buffer, { .data = _name, .length = _len }) );
+ break;
+
+ case Handle::Shader:
+ WGPU_CHECK(wgpuShaderModuleSetLabel(m_shaders[_handle.idx].m_module, { .data = _name, .length = _len }) );
+ break;
+
+ case Handle::Texture:
+ WGPU_CHECK(wgpuTextureSetLabel(m_textures[_handle.idx].m_texture, { .data = _name, .length = _len }) );
+ break;
+
+ case Handle::VertexBuffer:
+ WGPU_CHECK(wgpuBufferSetLabel(m_vertexBuffers[_handle.idx].m_buffer, { .data = _name, .length = _len }) );
+ break;
+
+ default:
+ BX_ASSERT(false, "Invalid handle type?! %d", _handle.type);
+ break;
+ }
+ }
+
+ void submitBlit(BlitState& _bs, uint16_t _view);
+
+ void submitUniformCache(UniformCacheState& _ucs, uint16_t _view);
+
+ void submit(Frame* _render, ClearQuad& _clearQuad, TextVideoMemBlitter& _textVideoMemBlitter) override;
+
+ void dbgTextRenderBegin(TextVideoMemBlitter& _blitter) override
+ {
+ const uint32_t width = m_backBuffer.m_width;
+ const uint32_t height = m_backBuffer.m_height;
+
+ float proj[16];
+ bx::mtxOrtho(proj, 0.0f, (float)width, (float)height, 0.0f, 0.0f, 1000.0f, 0.0f, false);
+
+ const ProgramWGPU& program = m_program[_blitter.m_program.idx];
+
+ const PredefinedUniform& predefined = program.m_predefined[0];
+ const uint8_t flags = predefined.m_type;
+ setShaderUniform4x4f(flags, predefined.m_loc, proj, 4);
+
+ ChunkedScratchBufferOffset sbo;
+ const uint32_t vsSize = program.m_vsh->m_size;
+ const uint32_t fsSize = NULL != program.m_fsh ? program.m_fsh->m_size : 0;
+ m_uniformScratchBuffer.write(sbo, m_vsScratch, vsSize, m_fsScratch, fsSize);
+
+ const uint64_t state = 0
+ | BGFX_STATE_WRITE_RGB
+ | BGFX_STATE_WRITE_A
+ | BGFX_STATE_DEPTH_TEST_ALWAYS
+ ;
+
+ const TextureWGPU& texture = m_textures[_blitter.m_texture.idx];
+ RenderBind renderBind;
+ renderBind.clear();
+ Binding& bind = renderBind.m_bind[0];
+ bind.m_idx = _blitter.m_texture.idx;
+ bind.m_type = uint8_t(Binding::Texture);
+ bind.m_samplerFlags = uint32_t(texture.m_flags & BGFX_SAMPLER_BITS_MASK);
+ bind.m_format = 0;
+ bind.m_access = 0;
+ bind.m_mip = 0;
+
+ const Stream stream =
+ {
+ .m_startVertex = 0,
+ .m_handle = _blitter.m_vb->handle,
+ .m_layoutHandle = _blitter.m_vb->layoutHandle,
+ };
+
+ const RenderPipeline& pipeline = *getPipeline(
+ _blitter.m_program
+ , BGFX_INVALID_HANDLE
+ , 1
+ , state
+ , packStencil(BGFX_STENCIL_DEFAULT, BGFX_STENCIL_DEFAULT)
+ , 1
+ , &stream
+ , 0
+ , true
+ , renderBind
+ , false
+ );
+
+ WGPURenderPassColorAttachment colorAttachment =
+ {
+ .nextInChain = NULL,
+ .view = m_backBuffer.m_swapChain.m_textureView,
+ .depthSlice = WGPU_DEPTH_SLICE_UNDEFINED,
+ .resolveTarget = NULL,
+ .loadOp = WGPULoadOp_Load,
+ .storeOp = WGPUStoreOp_Store,
+ .clearValue = {},
+ };
+
+ WGPURenderPassDescriptor renderPassDesc =
+ {
+ .nextInChain = NULL,
+ .label = WGPU_STRING_VIEW_INIT,
+ .colorAttachmentCount = 1,
+ .colorAttachments = &colorAttachment,
+ .depthStencilAttachment = NULL,
+ .occlusionQuerySet = NULL,
+ .timestampWrites = NULL,
+ };
+
+ WGPUCommandEncoder cmdEncoder = m_cmd.alloc();
+ WGPURenderPassEncoder blitRenderPassEncoder = WGPU_CHECK(wgpuCommandEncoderBeginRenderPass(cmdEncoder, &renderPassDesc) );
+ _blitter.m_usedData = uintptr_t(blitRenderPassEncoder);
+
+ BindGroup bindGroup = createBindGroup(pipeline.bindGroupLayout, program, renderBind, sbo, false);
+
+ WGPU_CHECK(wgpuRenderPassEncoderSetViewport(
+ blitRenderPassEncoder
+ , 0.0f
+ , 0.0f
+ , float(width)
+ , float(height)
+ , 0.0f
+ , 1.0f
+ ) );
+
+ WGPU_CHECK(wgpuRenderPassEncoderSetPipeline(blitRenderPassEncoder, pipeline.pipeline) );
+ WGPU_CHECK(wgpuRenderPassEncoderSetBindGroup(blitRenderPassEncoder, 0, bindGroup.bindGroup, bindGroup.numOffsets, sbo.offsets) );
+ release(bindGroup);
+ }
+
+ void dbgTextRender(TextVideoMemBlitter& _blitter, uint32_t _numIndices) override
+ {
+ WGPURenderPassEncoder blitRenderPassEncoder = WGPURenderPassEncoder(_blitter.m_usedData);
+
+ const uint32_t numVertices = _numIndices*4/6;
+
+ if (0 < numVertices)
+ {
+ const IndexBufferWGPU& ib = m_indexBuffers[_blitter.m_ib->handle.idx];
+ const uint32_t ibSize = _numIndices*2;
+ ib.update(0, ibSize, _blitter.m_ib->data);
+
+ const VertexBufferWGPU& vb = m_vertexBuffers[_blitter.m_vb->handle.idx];
+ const uint32_t vbSize = numVertices*_blitter.m_layout.m_stride;
+ vb.update(0, vbSize, _blitter.m_vb->data, true);
+
+ WGPU_CHECK(wgpuRenderPassEncoderSetVertexBuffer(blitRenderPassEncoder, 0, vb.m_buffer, 0, vbSize) );
+ WGPU_CHECK(wgpuRenderPassEncoderSetIndexBuffer(blitRenderPassEncoder, ib.m_buffer, WGPUIndexFormat_Uint16, 0, ibSize) );
+ WGPU_CHECK(wgpuRenderPassEncoderDrawIndexed(blitRenderPassEncoder, _numIndices, 1, 0, 0, 0) );
+ }
+ }
+
+ void dbgTextRenderEnd(TextVideoMemBlitter& _blitter) override
+ {
+ WGPURenderPassEncoder blitRenderPassEncoder = WGPURenderPassEncoder(_blitter.m_usedData);
+ _blitter.m_usedData = 0;
+ WGPU_CHECK(wgpuRenderPassEncoderEnd(blitRenderPassEncoder) );
+ wgpuRelease(blitRenderPassEncoder);
+ }
+
+ void clearQuad(WGPURenderPassEncoder _renderPassEncoder, FrameBufferHandle _fbh, uint32_t _msaaCount, const ClearQuad& _clearQuad, const Rect& _rect, const Clear& _clear, const float _palette[][4])
+ {
+ BX_UNUSED(_clearQuad, _rect, _clear, _palette);
+
+ uint64_t state = BGFX_STATE_PT_TRISTRIP;
+ state |= _clear.m_flags & BGFX_CLEAR_COLOR ? BGFX_STATE_WRITE_RGB|BGFX_STATE_WRITE_A : 0;
+ state |= _clear.m_flags & BGFX_CLEAR_DEPTH ? BGFX_STATE_DEPTH_TEST_ALWAYS|BGFX_STATE_WRITE_Z : 0;
+
+ uint64_t stencil = 0;
+ stencil |= _clear.m_flags & BGFX_CLEAR_STENCIL ? 0
+ | BGFX_STENCIL_TEST_ALWAYS
+ | BGFX_STENCIL_FUNC_REF(_clear.m_stencil)
+ | BGFX_STENCIL_FUNC_RMASK(0xff)
+ | BGFX_STENCIL_OP_FAIL_S_REPLACE
+ | BGFX_STENCIL_OP_FAIL_Z_REPLACE
+ | BGFX_STENCIL_OP_PASS_Z_REPLACE
+ : 0
+ ;
+
+ uint32_t numMrt = 1;
+ if (isValid(_fbh) )
+ {
+ const FrameBufferWGPU& fb = m_frameBuffers[_fbh.idx];
+ numMrt = bx::uint32_max(1, fb.m_numColorAttachments);
+ }
+
+ const VertexBufferWGPU& vb = m_vertexBuffers[_clearQuad.m_vb.idx];
+
+ const Stream stream = {
+ .m_startVertex = 0,
+ .m_handle = _clearQuad.m_vb,
+ .m_layoutHandle = _clearQuad.m_layout,
+ };
+
+ RenderBind renderBind;
+ renderBind.clear();
+
+ const RenderPipeline& pipeline = *getPipeline(
+ _clearQuad.m_program[numMrt-1]
+ , _fbh
+ , _msaaCount
+ , state
+ , stencil
+ , 1
+ , &stream
+ , 0
+ , true
+ , renderBind
+ , true
+ );
+
+ const ProgramWGPU& program = m_program[_clearQuad.m_program[numMrt-1].idx];
+
+ const float mrtClearDepth[4] = { _clear.m_depth };
+ float mrtClearColor[BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS][4];
+
+ if (BGFX_CLEAR_COLOR_USE_PALETTE & _clear.m_flags)
+ {
+ for (uint32_t ii = 0; ii < numMrt; ++ii)
+ {
+ uint8_t index = (uint8_t)bx::uint32_min(BGFX_CONFIG_MAX_COLOR_PALETTE-1, _clear.m_index[ii]);
+ bx::memCopy(mrtClearColor[ii], _palette[index], 16);
+ }
+ }
+ else
+ {
+ const float rgba[4] =
+ {
+ _clear.m_index[0]*1.0f/255.0f,
+ _clear.m_index[1]*1.0f/255.0f,
+ _clear.m_index[2]*1.0f/255.0f,
+ _clear.m_index[3]*1.0f/255.0f,
+ };
+
+ for (uint32_t ii = 0; ii < numMrt; ++ii)
+ {
+ bx::memCopy(mrtClearColor[ii], rgba, 16);
+ }
+ }
+
+ ChunkedScratchBufferOffset sbo;
+ m_uniformScratchBuffer.write(sbo, mrtClearDepth, sizeof(mrtClearDepth), mrtClearColor, sizeof(mrtClearColor) );
+
+ BindGroup bindGroup = createBindGroup(pipeline.bindGroupLayout, program, renderBind, sbo, false);
+
+ WGPU_CHECK(wgpuRenderPassEncoderSetPipeline(_renderPassEncoder, pipeline.pipeline) );
+ WGPU_CHECK(wgpuRenderPassEncoderSetBindGroup(_renderPassEncoder, 0, bindGroup.bindGroup, bindGroup.numOffsets, sbo.offsets) );
+ release(bindGroup);
+
+ WGPU_CHECK(wgpuRenderPassEncoderSetVertexBuffer(_renderPassEncoder, 0, vb.m_buffer, 0, vb.m_size) );
+ WGPU_CHECK(wgpuRenderPassEncoderDraw(_renderPassEncoder, 4, 1, 0, 0) );
+ }
+
+ void preReset()
+ {
+ m_renderPipelineCache.invalidate();
+
+ for (uint32_t ii = 0; ii < BX_COUNTOF(m_frameBuffers); ++ii)
+ {
+ m_frameBuffers[ii].preReset();
+ }
+
+ invalidateCache();
+ }
+
+ void postReset()
+ {
+ for (uint32_t ii = 0; ii < BX_COUNTOF(m_frameBuffers); ++ii)
+ {
+ m_frameBuffers[ii].postReset();
+ }
+
+ if (m_resolution.reset & BGFX_RESET_CAPTURE)
+ {
+ }
+ }
+
+ void invalidateCache()
+ {
+ m_computePipelineCache.invalidate();
+ m_renderPipelineCache.invalidate();
+ m_textureViewStateCache.invalidate();
+ m_samplerStateCache.invalidate();
+ }
+
+ bool updateResolution(const Resolution& _resolution)
+ {
+ const bool suspended = !!(_resolution.reset & BGFX_RESET_SUSPEND);
+
+ uint16_t maxAnisotropy = 1;
+ if (!!(_resolution.reset & BGFX_RESET_MAXANISOTROPY) )
+ {
+ maxAnisotropy = 16;
+ }
+
+ if (m_maxAnisotropy != maxAnisotropy)
+ {
+ m_maxAnisotropy = maxAnisotropy;
+ m_samplerStateCache.invalidate();
+ }
+
+ const bool depthClamp = !!(_resolution.reset & BGFX_RESET_DEPTH_CLAMP);
+
+ if (m_depthClamp != depthClamp)
+ {
+ m_depthClamp = depthClamp;
+ m_renderPipelineCache.invalidate();
+ }
+
+ if (!m_backBuffer.isSwapChain() )
+ {
+ return suspended;
+ }
+
+ uint32_t flags = _resolution.reset & ~(0
+ | BGFX_RESET_SUSPEND
+ | BGFX_RESET_MAXANISOTROPY
+ | BGFX_RESET_DEPTH_CLAMP
+ );
+
+ if (false
+ || m_resolution.formatColor != _resolution.formatColor
+ || m_resolution.formatDepthStencil != _resolution.formatDepthStencil
+ || m_resolution.width != _resolution.width
+ || m_resolution.height != _resolution.height
+ || m_resolution.reset != flags
+ )
+ {
+ flags &= ~BGFX_RESET_INTERNAL_FORCE;
+
+ if (m_backBuffer.m_swapChain.m_nwh != g_platformData.nwh)
+ {
+ m_backBuffer.m_swapChain.m_nwh = g_platformData.nwh;
+ }
+
+ m_resolution = _resolution;
+ m_resolution.reset = flags;
+
+ m_textVideoMem.resize(false, _resolution.width, _resolution.height);
+ m_textVideoMem.clear();
+
+ preReset();
+
+ m_backBuffer.update(m_resolution);
+
+ postReset();
+ }
+
+ return suspended;
+ }
+
+ void setShaderUniform(uint8_t _flags, uint32_t _regIndex, const void* _val, uint32_t _numRegs)
+ {
+ if (_flags & kUniformFragmentBit)
+ {
+ bx::memCopy(&m_fsScratch[_regIndex], _val, _numRegs*16);
+ }
+ else
+ {
+ bx::memCopy(&m_vsScratch[_regIndex], _val, _numRegs*16);
+ }
+ }
+
+ void setShaderUniform4f(uint8_t _flags, uint32_t _regIndex, const void* _val, uint32_t _numRegs)
+ {
+ setShaderUniform(_flags, _regIndex, _val, _numRegs);
+ }
+
+ void setShaderUniform4x4f(uint8_t _flags, uint32_t _regIndex, const void* _val, uint32_t _numRegs)
+ {
+ setShaderUniform(_flags, _regIndex, _val, _numRegs);
+ }
+
+ bool isVisible(Frame* _render, OcclusionQueryHandle _handle, bool _visible)
+ {
+ return _visible == (0 != _render->m_occlusion[_handle.idx]);
+ }
+
+ void commit(UniformBuffer& _uniformBuffer)
+ {
+ _uniformBuffer.reset();
+
+ for (;;)
+ {
+ uint32_t opcode = _uniformBuffer.read();
+
+ if (UniformType::End == opcode)
+ {
+ break;
+ }
+
+ uint8_t type;
+ uint16_t loc;
+ uint16_t num;
+ uint16_t copy;
+ UniformBuffer::decodeOpcode(opcode, type, loc, num, copy);
+
+ const char* data;
+ if (copy)
+ {
+ data = _uniformBuffer.read(g_uniformTypeSize[type]*num);
+ }
+ else
+ {
+ UniformHandle handle;
+ bx::memCopy(&handle, _uniformBuffer.read(sizeof(UniformHandle) ), sizeof(UniformHandle) );
+ data = (const char*)m_uniforms[handle.idx];
+ }
+
+ switch (type)
+ {
+ case UniformType::Mat3:
+ case UniformType::Mat3|kUniformFragmentBit:
+ {
+ float* value = (float*)data;
+ for (uint32_t ii = 0, count = num/3; ii < count; ++ii, loc += 3*16, value += 9)
+ {
+ Matrix4 mtx;
+ mtx.un.val[ 0] = value[0];
+ mtx.un.val[ 1] = value[1];
+ mtx.un.val[ 2] = value[2];
+ mtx.un.val[ 3] = 0.0f;
+ mtx.un.val[ 4] = value[3];
+ mtx.un.val[ 5] = value[4];
+ mtx.un.val[ 6] = value[5];
+ mtx.un.val[ 7] = 0.0f;
+ mtx.un.val[ 8] = value[6];
+ mtx.un.val[ 9] = value[7];
+ mtx.un.val[10] = value[8];
+ mtx.un.val[11] = 0.0f;
+ setShaderUniform(uint8_t(type), loc, &mtx.un.val[0], 3);
+ }
+ }
+ break;
+
+ case UniformType::Sampler:
+ case UniformType::Sampler|kUniformFragmentBit:
+ break;
+
+ case UniformType::Vec4:
+ case UniformType::Vec4 | kUniformFragmentBit:
+ setShaderUniform(uint8_t(type), loc, data, num);
+ break;
+
+ case UniformType::Mat4:
+ case UniformType::Mat4 | kUniformFragmentBit:
+ setShaderUniform4x4f(uint8_t(type), loc, data, num);
+ break;
+
+ case UniformType::End:
+ break;
+
+ default:
+ BX_TRACE("%4d: INVALID 0x%08x, t %d, l %d, n %d, c %d", _uniformBuffer.getPos(), opcode, type, loc, num, copy);
+ break;
+ }
+ }
+ }
+
+ void initBufferBinding(WGPUBindGroupLayoutEntry& _out, uint32_t _binding, WGPUShaderStage _visibility, WGPUBufferBindingType _type, bool _hasDynamicOffset)
+ {
+ _out =
+ {
+ .nextInChain = NULL,
+ .binding = _binding,
+ .visibility = _visibility,
+ .bindingArraySize = 0,
+ .buffer =
+ {
+ .nextInChain = NULL,
+ .type = _type,
+ .hasDynamicOffset = _hasDynamicOffset,
+ .minBindingSize = 0,
+ },
+ .sampler =
+ {
+ .nextInChain = NULL,
+ .type = WGPUSamplerBindingType_BindingNotUsed,
+ },
+ .texture =
+ {
+ .nextInChain = NULL,
+ .sampleType = WGPUTextureSampleType_BindingNotUsed,
+ .viewDimension = WGPUTextureViewDimension_Undefined,
+ .multisampled = false,
+ },
+ .storageTexture =
+ {
+ .nextInChain = NULL,
+ .access = WGPUStorageTextureAccess_BindingNotUsed,
+ .format = WGPUTextureFormat_Undefined,
+ .viewDimension = WGPUTextureViewDimension_Undefined,
+ },
+ };
+ }
+
+ void initSamplerBinding(WGPUBindGroupLayoutEntry& _out, uint32_t _binding, WGPUShaderStage _visibility, WGPUSamplerBindingType _type)
+ {
+ _out =
+ {
+ .nextInChain = NULL,
+ .binding = _binding,
+ .visibility = _visibility,
+ .bindingArraySize = 0,
+ .buffer =
+ {
+ .nextInChain = NULL,
+ .type = WGPUBufferBindingType_BindingNotUsed,
+ .hasDynamicOffset = false,
+ .minBindingSize = 0,
+ },
+ .sampler =
+ {
+ .nextInChain = NULL,
+ .type = _type,
+ },
+ .texture =
+ {
+ .nextInChain = NULL,
+ .sampleType = WGPUTextureSampleType_BindingNotUsed,
+ .viewDimension = WGPUTextureViewDimension_Undefined,
+ .multisampled = false,
+ },
+ .storageTexture =
+ {
+ .nextInChain = NULL,
+ .access = WGPUStorageTextureAccess_BindingNotUsed,
+ .format = WGPUTextureFormat_Undefined,
+ .viewDimension = WGPUTextureViewDimension_Undefined,
+ },
+ };
+ }
+
+ void initTextureBinding(WGPUBindGroupLayoutEntry& _out, uint32_t _binding, WGPUShaderStage _visibility, WGPUTextureSampleType _sampleType, WGPUTextureViewDimension _viewDimension)
+ {
+ _out =
+ {
+ .nextInChain = NULL,
+ .binding = _binding,
+ .visibility = _visibility,
+ .bindingArraySize = 0,
+ .buffer =
+ {
+ .nextInChain = NULL,
+ .type = WGPUBufferBindingType_BindingNotUsed,
+ .hasDynamicOffset = false,
+ .minBindingSize = 0,
+ },
+ .sampler =
+ {
+ .nextInChain = NULL,
+ .type = WGPUSamplerBindingType_BindingNotUsed,
+ },
+ .texture =
+ {
+ .nextInChain = NULL,
+ .sampleType = _sampleType,
+ .viewDimension = _viewDimension,
+ .multisampled = false,
+ },
+ .storageTexture =
+ {
+ .nextInChain = NULL,
+ .access = WGPUStorageTextureAccess_BindingNotUsed,
+ .format = WGPUTextureFormat_Undefined,
+ .viewDimension = WGPUTextureViewDimension_Undefined,
+ },
+ };
+ }
+
+ void initStorageTextureBinding(WGPUBindGroupLayoutEntry& _out, uint32_t _binding, WGPUShaderStage _visibility, WGPUStorageTextureAccess _access, WGPUTextureFormat _format, WGPUTextureViewDimension _viewDimension)
+ {
+ _out =
+ {
+ .nextInChain = NULL,
+ .binding = _binding,
+ .visibility = _visibility,
+ .bindingArraySize = 0,
+ .buffer =
+ {
+ .nextInChain = NULL,
+ .type = WGPUBufferBindingType_BindingNotUsed,
+ .hasDynamicOffset = false,
+ .minBindingSize = 0,
+ },
+ .sampler =
+ {
+ .nextInChain = NULL,
+ .type = WGPUSamplerBindingType_BindingNotUsed,
+ },
+ .texture =
+ {
+ .nextInChain = NULL,
+ .sampleType = WGPUTextureSampleType_BindingNotUsed,
+ .viewDimension = WGPUTextureViewDimension_Undefined,
+ .multisampled = false,
+ },
+ .storageTexture =
+ {
+ .nextInChain = NULL,
+ .access = _access,
+ .format = _format,
+ .viewDimension = _viewDimension,
+ },
+ };
+ }
+
+ uint8_t fillBindGroupLayoutEntry(WGPUBindGroupLayoutEntry* _entries, const ProgramWGPU& _program, const RenderBind& _renderBind, bool _isCompute)
+ {
+ uint8_t entryCount = 0;
+
+ const uint32_t vsSize = _program.m_vsh->m_size;
+ const uint32_t fsSize = NULL == _program.m_fsh ? 0 : _program.m_fsh->m_size;
+
+ if (0 < vsSize)
+ {
+ initBufferBinding(
+ _entries[entryCount++]
+ , 0
+ , _isCompute
+ ? WGPUShaderStage_Compute
+ : WGPUShaderStage_Vertex
+ , WGPUBufferBindingType_Uniform
+ , true
+ );
+ }
+
+ if (0 < fsSize)
+ {
+ initBufferBinding(
+ _entries[entryCount++]
+ , 1
+ , WGPUShaderStage_Fragment
+ , WGPUBufferBindingType_Uniform
+ , true
+ );
+ }
+
+ for (uint8_t stage = 0; stage < BGFX_CONFIG_MAX_TEXTURE_SAMPLERS; ++stage)
+ {
+ const ShaderBinding& shaderBind = _program.m_shaderBinding[stage];
+
+ if (!isValid(shaderBind.uniformHandle) )
+ {
+ continue;
+ }
+
+ const Binding& bind = _renderBind.m_bind[stage];
+
+ switch (shaderBind.type)
+ {
+ case ShaderBinding::Type::Buffer:
+ initBufferBinding(
+ _entries[entryCount++]
+ , shaderBind.binding
+ , shaderBind.shaderStage
+ , shaderBind.bufferBindingType
+ , false
+ );
+ break;
+
+ case ShaderBinding::Type::Image:
+ initStorageTextureBinding(
+ _entries[entryCount++]
+ , shaderBind.binding
+ , shaderBind.shaderStage
+ , s_storageTextureAccess[bind.m_access]
+ , s_textureFormat[m_textures[bind.m_idx].m_textureFormat].m_fmt
+ , shaderBind.viewDimension
+ );
+ break;
+
+ case ShaderBinding::Type::Sampler:
+ {
+ TextureWGPU& texture = m_textures[bind.m_idx];
+ WGPUTextureSampleType sampleType = WGPUTextureSampleType_Depth != shaderBind.sampleType
+ ? s_textureFormat[texture.m_textureFormat].m_samplerType
+ : shaderBind.sampleType
+ ;
+
+ WGPUSamplerBindingType samplerBindingType = WGPUSamplerBindingType_Filtering;
+ switch (sampleType)
+ {
+ case WGPUTextureSampleType_UnfilterableFloat: samplerBindingType = WGPUSamplerBindingType_NonFiltering; break;
+ case WGPUTextureSampleType_Depth: samplerBindingType = WGPUSamplerBindingType_Comparison; break;
+ default: break;
+ }
+
+ initTextureBinding(
+ _entries[entryCount++]
+ , shaderBind.binding
+ , shaderBind.shaderStage
+ , sampleType
+ , m_textures[bind.m_idx].m_viewDimension
+ );
+
+ initSamplerBinding(
+ _entries[entryCount++]
+ , shaderBind.samplerBinding
+ , shaderBind.shaderStage
+ , samplerBindingType
+ );
+ }
+ break;
+
+ case ShaderBinding::Type::Count:
+ break;
+ }
+ }
+
+ return entryCount;
+ }
+
+ ComputePipeline* getPipeline(ProgramHandle _program, const RenderBind& _renderBind)
+ {
+ const ProgramWGPU& program = m_program[_program.idx];
+
+ bx::HashMurmur3 murmur;
+ murmur.begin();
+ murmur.add(program.m_vsh->m_hash);
+ murmur.add(&_renderBind.m_bind, sizeof(_renderBind.m_bind) );
+ const uint32_t hash = murmur.end();
+
+ ComputePipeline* computePipeline = m_computePipelineCache.find(hash);
+
+ if (BX_LIKELY(NULL != computePipeline) )
+ {
+ return computePipeline;
+ }
+
+ WGPUBindGroupLayoutEntry entries[2 + BGFX_CONFIG_MAX_TEXTURE_SAMPLERS * 3];
+ const uint8_t entryCount = fillBindGroupLayoutEntry(entries, program, _renderBind, true);
+ BX_ASSERT(entryCount < BX_COUNTOF(entries), "");
+ BX_ASSERT(program.m_numBindings <= entryCount, "");
+
+ WGPUBindGroupLayoutDescriptor bindGroupLayoutDesc =
+ {
+ .nextInChain = NULL,
+ .label = WGPU_STRING_VIEW_INIT,
+ .entryCount = entryCount,
+ .entries = entries,
+ };
+
+ WGPUBindGroupLayout bindGroupLayout = WGPU_CHECK(wgpuDeviceCreateBindGroupLayout(m_device, &bindGroupLayoutDesc) );
+
+ WGPUPipelineLayoutDescriptor pipelineLayoutDesc =
+ {
+ .nextInChain = NULL,
+ .label = WGPU_STRING_VIEW_INIT,
+ .bindGroupLayoutCount = 1,
+ .bindGroupLayouts = &bindGroupLayout,
+ .immediateSize = 0,
+ };
+
+ WGPUPipelineLayout pipelineLayout = WGPU_CHECK(wgpuDeviceCreatePipelineLayout(m_device, &pipelineLayoutDesc) );
+
+ WGPUComputePipelineDescriptor computePipelineDesc =
+ {
+ .nextInChain = NULL,
+ .label = WGPU_STRING_VIEW_INIT,
+ .layout = pipelineLayout,
+ .compute =
+ {
+ .nextInChain = NULL,
+ .module = program.m_vsh->m_module,
+ .entryPoint = toWGPUStringView("main"),
+ .constantCount = 0,
+ .constants = NULL,
+ },
+ };
+
+ computePipeline = m_computePipelineCache.add(
+ hash
+ , {
+ .bindGroupLayout = bindGroupLayout,
+ .pipeline = wgpuDeviceCreateComputePipeline(m_device, &computePipelineDesc),
+ }
+ , 0
+ );
+ wgpuRelease(pipelineLayout);
+
+ return computePipeline;
+ }
+
+ static WGPUVertexAttribute* fillVertexLayout(const ShaderWGPU* _vsh, WGPUVertexAttribute* _out, const VertexLayout& _layout)
+ {
+ WGPUVertexAttribute* elem = _out;
+
+ for (uint32_t attr = 0; attr < Attrib::Count; ++attr)
+ {
+ if (UINT16_MAX != _layout.m_attributes[attr])
+ {
+ elem->nextInChain = NULL;
+ elem->shaderLocation = _vsh->m_attrRemap[attr];
+
+ if (0 != _layout.m_attributes[attr])
+ {
+ uint8_t num;
+ AttribType::Enum type;
+ bool normalized;
+ bool asInt;
+ _layout.decode(Attrib::Enum(attr), num, type, normalized, asInt);
+ elem->format = s_attribType[type][num-1][normalized];
+ elem->offset = 0 == _layout.m_attributes[attr]
+ ? 0
+ : _layout.m_offset[attr]
+ ;
+ }
+ else
+ {
+ elem->format = WGPUVertexFormat_Float32x3;
+ elem->offset = 0;
+ }
+
+ ++elem;
+ }
+ }
+
+ return elem;
+ }
+
+ RenderPipeline* getPipeline(
+ ProgramHandle _program
+ , FrameBufferHandle _fbh
+ , uint32_t _msaaCount
+ , uint64_t _state
+ , uint64_t _stencil
+ , uint8_t _streamMask
+ , const Stream* _stream
+ , uint8_t _numInstanceData
+ , bool _isIndex16
+ , const RenderBind& _renderBind
+ , bool _useDepthAttachment = true
+ )
+ {
+ const ProgramWGPU& program = m_program[_program.idx];
+
+ _state &= 0
+ | BGFX_STATE_WRITE_RGB
+ | BGFX_STATE_WRITE_A
+ | BGFX_STATE_WRITE_Z
+ | BGFX_STATE_DEPTH_TEST_MASK
+ | BGFX_STATE_BLEND_MASK
+ | BGFX_STATE_BLEND_EQUATION_MASK
+ | BGFX_STATE_BLEND_INDEPENDENT
+ | BGFX_STATE_BLEND_ALPHA_TO_COVERAGE
+ | BGFX_STATE_CULL_MASK
+ | BGFX_STATE_FRONT_CCW
+ | BGFX_STATE_MSAA
+ | BGFX_STATE_LINEAA
+ | BGFX_STATE_CONSERVATIVE_RASTER
+ | BGFX_STATE_PT_MASK
+ ;
+
+ _stencil &= kStencilNoRefMask;
+
+ const uint8_t numVertexStreams = bx::countBits(_streamMask);
+
+ VertexLayout layout;
+ if (0 < numVertexStreams)
+ {
+ const uint16_t layoutIdx = isValid(_stream[0].m_layoutHandle)
+ ? _stream[0].m_layoutHandle.idx
+ : m_vertexBuffers[_stream[0].m_handle.idx].m_layoutHandle.idx
+ ;
+
+ bx::memCopy(&layout, &m_vertexLayouts[layoutIdx], sizeof(VertexLayout) );
+ const uint16_t* attrMask = program.m_vsh->m_attrMask;
+
+ for (uint32_t ii = 0; ii < Attrib::Count; ++ii)
+ {
+ uint16_t mask = attrMask[ii];
+ uint16_t attr = (layout.m_attributes[ii] & mask);
+ layout.m_attributes[ii] = attr == 0 ? UINT16_MAX : attr == UINT16_MAX ? 0 : attr;
+ }
+ }
+
+ bx::HashMurmur2A murmur;
+ murmur.begin();
+ murmur.add(_state);
+ murmur.add(_stencil);
+ murmur.add(&_renderBind.m_bind, sizeof(_renderBind.m_bind) );
+ murmur.add(program.m_vsh->m_hash);
+ murmur.add(program.m_vsh->m_attrMask, sizeof(program.m_vsh->m_attrMask) );
+
+ if (NULL != program.m_fsh)
+ {
+ murmur.add(program.m_fsh->m_hash);
+ }
+
+ for (BitMaskToIndexIteratorT it(_streamMask); !it.isDone(); it.next() )
+ {
+ const uint8_t idx = it.idx;
+
+ uint16_t handle = _stream[idx].m_handle.idx;
+ const VertexBufferWGPU& vb = m_vertexBuffers[handle];
+ const uint16_t layoutIdx = isValid(_stream[idx].m_layoutHandle)
+ ? _stream[idx].m_layoutHandle.idx
+ : vb.m_layoutHandle.idx;
+
+ murmur.add(m_vertexLayouts[layoutIdx].m_hash);
+ }
+
+ murmur.add(layout.m_attributes, sizeof(layout.m_attributes) );
+ murmur.add(_fbh);
+ murmur.add(_msaaCount);
+ murmur.add(_numInstanceData);
+ const uint32_t hash = murmur.end();
+
+ RenderPipeline* renderPipeline = m_renderPipelineCache.find(hash);
+
+ if (BX_LIKELY(NULL != renderPipeline) )
+ {
+ return renderPipeline;
+ }
+
+ WGPUVertexBufferLayout vertexBufferLayout[BGFX_CONFIG_MAX_VERTEX_STREAMS];
+ WGPUVertexAttribute vertexAttribute[Attrib::Count+1+BGFX_CONFIG_MAX_INSTANCE_DATA_COUNT];
+
+ uint8_t numStreams = 0;
+
+ {
+ WGPUVertexAttribute* elem = vertexAttribute;
+
+ uint16_t attrMask[Attrib::Count];
+ bx::memCopy(attrMask, program.m_vsh->m_attrMask, sizeof(attrMask) );
+
+ uint32_t maxShaderLocation = 0;
+
+ for (uint32_t attr = 0; attr < Attrib::Count; ++attr)
+ {
+ maxShaderLocation = bx::max(maxShaderLocation, program.m_vsh->m_attrRemap[attr]);
+ }
+
+ if (UINT8_MAX != _streamMask)
+ {
+ for (BitMaskToIndexIteratorT it(_streamMask)
+ ; !it.isDone()
+ ; it.next(), numStreams++
+ )
+ {
+ const uint8_t idx = it.idx;
+
+ const uint16_t handle = _stream[idx].m_handle.idx;
+ const VertexBufferWGPU& vb = m_vertexBuffers[handle];
+ const uint16_t layoutIdx = isValid(_stream[idx].m_layoutHandle)
+ ? _stream[idx].m_layoutHandle.idx
+ : vb.m_layoutHandle.idx
+ ;
+
+ bx::memCopy(&layout, &m_vertexLayouts[layoutIdx], sizeof(VertexLayout) );
+
+ const bool lastStream = idx == uint32_t(numVertexStreams-1);
+
+ for (uint32_t ii = 0; ii < Attrib::Count; ++ii)
+ {
+ const uint16_t mask = attrMask[ii];
+ const uint16_t attr = (layout.m_attributes[ii] & mask);
+
+ if (0 == attr
+ || UINT16_MAX == attr)
+ {
+ layout.m_attributes[ii] = lastStream ? ~attr : UINT16_MAX;
+ }
+ else
+ {
+ attrMask[ii] = 0;
+ }
+ }
+
+ WGPUVertexAttribute* last = fillVertexLayout(program.m_vsh, elem, layout);
+
+ vertexBufferLayout[idx] =
+ {
+ .nextInChain = NULL,
+ .stepMode = WGPUVertexStepMode_Vertex,
+ .arrayStride = layout.m_stride,
+ .attributeCount = uint32_t(last - elem),
+ .attributes = elem,
+ };
+
+ elem = last;
+ }
+ }
+
+ if (0 < _numInstanceData)
+ {
+ maxShaderLocation += 0 < numStreams;
+
+ for (uint32_t ii = 0; ii < _numInstanceData; ++ii)
+ {
+ elem[ii] =
+ {
+ .nextInChain = 0,
+ .format = WGPUVertexFormat_Float32x4,
+ .offset = ii*16ull,
+ .shaderLocation = maxShaderLocation+ii,
+ };
+ }
+
+ vertexBufferLayout[numStreams] =
+ {
+ .nextInChain = NULL,
+ .stepMode = WGPUVertexStepMode_Instance,
+ .arrayStride = _numInstanceData*16ull,
+ .attributeCount = _numInstanceData,
+ .attributes = elem,
+ };
+
+ ++numStreams;
+ }
+ }
+
+ WGPUBindGroupLayoutEntry entries[2 + BGFX_CONFIG_MAX_TEXTURE_SAMPLERS * 3];
+ const uint8_t entryCount = fillBindGroupLayoutEntry(entries, program, _renderBind, false);
+ BX_ASSERT(entryCount < BX_COUNTOF(entries), "");
+ BX_ASSERT(program.m_numBindings <= entryCount, "");
+
+ WGPUBindGroupLayoutDescriptor bindGroupLayoutDesc =
+ {
+ .nextInChain = NULL,
+ .label = WGPU_STRING_VIEW_INIT,
+ .entryCount = entryCount,
+ .entries = entries,
+ };
+
+ WGPUBindGroupLayout bindGroupLayout = WGPU_CHECK(wgpuDeviceCreateBindGroupLayout(m_device, &bindGroupLayoutDesc) );
+
+ WGPUPipelineLayoutDescriptor pipelineLayoutDesc =
+ {
+ .nextInChain = NULL,
+ .label = WGPU_STRING_VIEW_INIT,
+ .bindGroupLayoutCount = 1,
+ .bindGroupLayouts = &bindGroupLayout,
+ .immediateSize = 0,
+ };
+
+ WGPUPipelineLayout pipelineLayout = WGPU_CHECK(wgpuDeviceCreatePipelineLayout(m_device, &pipelineLayoutDesc) );
+
+ WGPUBlendState blendState[BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS];
+ WGPUColorTargetState colorTragetState[BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS];
+ WGPUDepthStencilState depthStencilState;
+
+ const FrameBufferWGPU& fb = isValid(_fbh)
+ ? m_frameBuffers[_fbh.idx]
+ : m_backBuffer
+ ;
+
+ const WGPUTextureView depthStencilTextureView = _useDepthAttachment
+ ? fb.isSwapChain()
+ ? fb.m_swapChain.m_depthStencilView
+ : fb.m_depthStencilView
+ : NULL
+ ;
+
+ const TextureFormat::Enum formatDepthStencil = fb.isSwapChain()
+ ? fb.m_swapChain.m_resolution.formatDepthStencil
+ : TextureFormat::Enum(fb.m_formatDepthStencil)
+ ;
+
+ const bool hasFragmentShader = NULL != program.m_fsh;
+
+ const uint32_t targetCount = hasFragmentShader ? setColorTargetState(blendState, colorTragetState, fb, _state) : 0;
+
+ if (NULL != depthStencilTextureView)
+ {
+ setDepthStencilState(depthStencilState, formatDepthStencil, _state, _stencil);
+ }
+ else
+ {
+ depthStencilState.format = WGPUTextureFormat_Undefined;
+ }
+
+ WGPUFragmentState fragmentState =
+ {
+ .nextInChain = NULL,
+ .module = hasFragmentShader ? program.m_fsh->m_module : NULL,
+ .entryPoint = toWGPUStringView("main"),
+ .constantCount = 0,
+ .constants = NULL,
+ .targetCount = targetCount,
+ .targets = colorTragetState,
+ };
+
+ const uint32_t cull = (_state&BGFX_STATE_CULL_MASK) >> BGFX_STATE_CULL_SHIFT;
+ const PrimInfo& primInfo = s_primInfo[(_state&BGFX_STATE_PT_MASK) >> BGFX_STATE_PT_SHIFT];
+
+ WGPURenderPipelineDescriptor renderPipelineDesc =
+ {
+ .nextInChain = NULL,
+ .label = WGPU_STRING_VIEW_INIT,
+ .layout = pipelineLayout,
+ .vertex =
+ {
+ .nextInChain = NULL,
+ .module = program.m_vsh->m_module,
+ .entryPoint = toWGPUStringView("main"),
+ .constantCount = 0,
+ .constants = NULL,
+ .bufferCount = numStreams,
+ .buffers = vertexBufferLayout,
+ },
+ .primitive =
+ {
+ .nextInChain = NULL,
+ .topology = primInfo.m_topology,
+ .stripIndexFormat = primInfo.m_stripIndexFormat[!_isIndex16],
+ .frontFace = !!(_state&BGFX_STATE_FRONT_CCW) ? WGPUFrontFace_CCW : WGPUFrontFace_CW,
+ .cullMode = s_cullMode[cull],
+ .unclippedDepth = !m_depthClamp,
+ },
+ .depthStencil = WGPUTextureFormat_Undefined == depthStencilState.format
+ ? NULL
+ : &depthStencilState
+ ,
+ .multisample =
+ {
+ .nextInChain = NULL,
+ .count = _msaaCount,
+ .mask = 0xffffffff,
+ .alphaToCoverageEnabled = !!(BGFX_STATE_BLEND_ALPHA_TO_COVERAGE & _state),
+ },
+ .fragment = hasFragmentShader ? &fragmentState : NULL,
+ };
+
+ renderPipeline = m_renderPipelineCache.add(
+ hash
+ , {
+ .bindGroupLayout = bindGroupLayout,
+ .pipeline = wgpuDeviceCreateRenderPipeline(m_device, &renderPipelineDesc),
+ }
+ , 0
+ );
+ wgpuRelease(pipelineLayout);
+
+ return renderPipeline;
+ }
+
+ BindGroup createBindGroup(WGPUBindGroupLayout _bindGroupLayout, const ProgramWGPU& _program, const RenderBind& _renderBind, const ChunkedScratchBufferOffset& _sbo, bool _isCompute)
+ {
+ const uint32_t vsSize = _program.m_vsh->m_size;
+ const uint32_t fsSize = NULL == _program.m_fsh ? 0 : _program.m_fsh->m_size;
+
+ WGPUBindGroupEntry bindGroupEntry[2 + BGFX_CONFIG_MAX_TEXTURE_SAMPLERS*3];
+ uint32_t entryCount = 0;
+ uint32_t numOffsets = 0;
+
+ if (0 < vsSize)
+ {
+ bindGroupEntry[entryCount++] =
+ {
+ .nextInChain = NULL,
+ .binding = 0,
+ .buffer = _sbo.buffer,
+ .offset = 0,
+ .size = _program.m_vsh->m_blockSize,
+ .sampler = NULL,
+ .textureView = NULL,
+ };
+
+ ++numOffsets;
+ }
+
+ if (0 < fsSize)
+ {
+ bindGroupEntry[entryCount++] =
+ {
+ .nextInChain = NULL,
+ .binding = 1,
+ .buffer = _sbo.buffer,
+ .offset = 0,
+ .size = _program.m_fsh->m_blockSize,
+ .sampler = NULL,
+ .textureView = NULL,
+ };
+
+ ++numOffsets;
+ }
+
+ for (uint32_t stage = 0; stage < BGFX_CONFIG_MAX_TEXTURE_SAMPLERS; ++stage)
+ {
+ const Binding& bind = _renderBind.m_bind[stage];
+ const ShaderBinding& shaderBind = _program.m_shaderBinding[stage];
+
+ if (isValid(shaderBind.uniformHandle) )
+ {
+ switch (bind.m_type)
+ {
+ case Binding::Image:
+ case Binding::Texture:
+ {
+ const TextureWGPU& texture = m_textures[bind.m_idx];
+
+ bindGroupEntry[entryCount++] =
+ {
+ .nextInChain = NULL,
+ .binding = shaderBind.binding,
+ .buffer = NULL,
+ .offset = 0,
+ .size = 0,
+ .sampler = NULL,
+ .textureView = _isCompute
+ ? texture.getTextureView(bind.m_mip, 1, Binding::Image == bind.m_type)
+ : texture.getTextureView(0, UINT8_MAX, false)
+ ,
+ };
+
+ if (!_isCompute
+ || ShaderBinding::Type::Sampler == shaderBind.type)
+ {
+ bindGroupEntry[entryCount++] =
+ {
+ .nextInChain = NULL,
+ .binding = shaderBind.samplerBinding,
+ .buffer = NULL,
+ .offset = 0,
+ .size = 0,
+ .sampler = texture.getSamplerState(bind.m_samplerFlags),
+ .textureView = NULL,
+ };
+ }
+ }
+ break;
+
+ case Binding::IndexBuffer:
+ case Binding::VertexBuffer:
+ {
+ BufferWGPU& buffer = Binding::IndexBuffer == bind.m_type
+ ? m_indexBuffers[bind.m_idx]
+ : m_vertexBuffers[bind.m_idx]
+ ;
+
+ bindGroupEntry[entryCount++] =
+ {
+ .nextInChain = NULL,
+ .binding = shaderBind.binding,
+ .buffer = buffer.m_buffer,
+ .offset = 0,
+ .size = buffer.m_size,
+ .sampler = NULL,
+ .textureView = NULL,
+ };
+ }
+ break;
+ }
+ }
+ }
+
+ BX_ASSERT(_program.m_numBindings <= entryCount, "");
+
+ WGPUBindGroupDescriptor bindGroupDesc =
+ {
+ .nextInChain = NULL,
+ .label = WGPU_STRING_VIEW_INIT,
+ .layout = _bindGroupLayout,
+ .entryCount = entryCount,
+ .entries = bindGroupEntry,
+ };
+
+ WGPUBindGroup bindGroup = WGPU_CHECK(wgpuDeviceCreateBindGroup(m_device, &bindGroupDesc) );
+
+ return {
+ .bindGroup = bindGroup,
+ .numOffsets = numOffsets,
+ };
+ }
+
+ void setDebugWireframe(bool _wireframe)
+ {
+ if (m_wireframe != _wireframe)
+ {
+ m_wireframe = _wireframe;
+ m_renderPipelineCache.invalidate();
+ }
+ }
+
+ uint32_t setColorTargetState(WGPUBlendState* _outBlendState, WGPUColorTargetState* _outColorTargetState, const FrameBufferWGPU& _fb, uint64_t _state, uint32_t _rgba = 0)
+ {
+ BX_UNUSED(_rgba);
+
+ if (0 == _fb.m_numColorAttachments
+ && !_fb.isSwapChain() )
+ {
+ return 0;
+ }
+
+// const bool alphaToCoverageEnable = !!(BGFX_STATE_BLEND_ALPHA_TO_COVERAGE & _state);
+ const bool blendEnabled = !!(BGFX_STATE_BLEND_MASK & _state);
+ const bool independentBlendEnable = blendEnabled && !!(BGFX_STATE_BLEND_INDEPENDENT & _state);
+
+ if (blendEnabled)
+ {
+// constexpr uint32_t kBlendOne = (BGFX_STATE_BLEND_ONE & BGFX_STATE_BLEND_MASK) >> BGFX_STATE_BLEND_SHIFT;
+
+ const uint32_t blend = uint32_t( (_state & BGFX_STATE_BLEND_MASK ) >> BGFX_STATE_BLEND_SHIFT);
+ const uint32_t equation = uint32_t( (_state & BGFX_STATE_BLEND_EQUATION_MASK) >> BGFX_STATE_BLEND_EQUATION_SHIFT);
+
+ const uint32_t equRGB = (equation ) & 0x7;
+ const uint32_t equA = (equation >> 3) & 0x7;
+
+ const bool equRGBIsMinOrMax = 3 <= equRGB;
+ const bool equAIsMinOrMax = 3 <= equA;
+
+ const uint32_t srcRGB = equRGBIsMinOrMax ? 0 : (blend ) & 0xf;
+ const uint32_t dstRGB = equRGBIsMinOrMax ? 0 : (blend >> 4) & 0xf;
+ const uint32_t srcA = equAIsMinOrMax ? 0 : (blend >> 8) & 0xf;
+ const uint32_t dstA = equAIsMinOrMax ? 0 : (blend >> 12) & 0xf;
+
+ _outBlendState[0] =
+ {
+ .color =
+ {
+ .operation = s_blendEquation[equRGB],
+ .srcFactor = s_blendFactor[srcRGB][0],
+ .dstFactor = s_blendFactor[dstRGB][0],
+ },
+ .alpha =
+ {
+ .operation = s_blendEquation[equA],
+ .srcFactor = s_blendFactor[srcA][1],
+ .dstFactor = s_blendFactor[dstA][1],
+ },
+ };
+ }
+
+ if (independentBlendEnable)
+ {
+ for (uint32_t ii = 1, rgba = _rgba; ii < BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS; ++ii, rgba >>= 11)
+ {
+ const uint32_t src = (rgba ) & 0xf;
+ const uint32_t dst = (rgba >> 4) & 0xf;
+ const uint32_t equation = (rgba >> 8) & 0x7;
+
+ _outBlendState[ii] =
+ {
+ .color =
+ {
+ .operation = s_blendEquation[equation],
+ .srcFactor = s_blendFactor[src][0],
+ .dstFactor = s_blendFactor[dst][0],
+ },
+ .alpha =
+ {
+ .operation = s_blendEquation[equation],
+ .srcFactor = s_blendFactor[src][1],
+ .dstFactor = s_blendFactor[dst][1],
+ },
+ };
+ }
+ }
+
+ const bool isSwapChain = _fb.isSwapChain();
+ const uint32_t numAttachments = isSwapChain ? 1 : _fb.m_numColorAttachments;
+ const WGPUBlendState* blendState = blendEnabled ? &_outBlendState[0] : NULL;
+ const WGPUColorWriteMask writeMask = 0
+ | ( (_state & BGFX_STATE_WRITE_R) ? WGPUColorWriteMask_Red : 0)
+ | ( (_state & BGFX_STATE_WRITE_G) ? WGPUColorWriteMask_Green : 0)
+ | ( (_state & BGFX_STATE_WRITE_B) ? WGPUColorWriteMask_Blue : 0)
+ | ( (_state & BGFX_STATE_WRITE_A) ? WGPUColorWriteMask_Alpha : 0)
+ ;
+
+ for (uint32_t ii = 0; ii < numAttachments; ++ii)
+ {
+ TextureFormat::Enum textureFormat = isSwapChain
+ ? _fb.m_swapChain.m_resolution.formatColor
+ : TextureFormat::Enum(m_textures[_fb.m_texture[ii].idx].m_textureFormat)
+ ;
+
+ _outColorTargetState[ii] =
+ {
+ .nextInChain = NULL,
+ .format = s_textureFormat[textureFormat].m_fmt,
+ .blend = independentBlendEnable ? &_outBlendState[ii] : blendState,
+ .writeMask = writeMask,
+ };
+ }
+
+ return numAttachments;
+ }
+
+ bool hasStencil(TextureFormat::Enum _format)
+ {
+ return 0 < bimg::getBlockInfo(bimg::TextureFormat::Enum(_format) ).stencilBits;
+ }
+
+ void setDepthStencilState(WGPUDepthStencilState& _outDepthStencilState, TextureFormat::Enum _format, uint64_t _state, uint64_t _stencil)
+ {
+ _stencil = 0 == _stencil ? kStencilDisabled : _stencil;
+
+ const uint32_t fstencil = unpackStencil(0, _stencil);
+ uint32_t bstencil = unpackStencil(1, _stencil);
+ const uint32_t frontAndBack = bstencil != BGFX_STENCIL_NONE && bstencil != fstencil;
+ bstencil = frontAndBack ? bstencil : fstencil;
+
+ const uint32_t func = (_state&BGFX_STATE_DEPTH_TEST_MASK)>>BGFX_STATE_DEPTH_TEST_SHIFT;
+
+ _outDepthStencilState =
+ {
+ .nextInChain = NULL,
+ .format = s_textureFormat[_format].m_fmt,
+ .depthWriteEnabled = WGPUOptionalBool(!!(BGFX_STATE_WRITE_Z & _state) ),
+ .depthCompare = s_cmpFunc[func],
+ .stencilFront =
+ {
+ .compare = s_cmpFunc[(fstencil & BGFX_STENCIL_TEST_MASK) >> BGFX_STENCIL_TEST_SHIFT],
+ .failOp = s_stencilOp[(fstencil & BGFX_STENCIL_OP_FAIL_S_MASK) >> BGFX_STENCIL_OP_FAIL_S_SHIFT],
+ .depthFailOp = s_stencilOp[(fstencil & BGFX_STENCIL_OP_FAIL_Z_MASK) >> BGFX_STENCIL_OP_FAIL_Z_SHIFT],
+ .passOp = s_stencilOp[(fstencil & BGFX_STENCIL_OP_PASS_Z_MASK) >> BGFX_STENCIL_OP_PASS_Z_SHIFT],
+ },
+ .stencilBack =
+ {
+ .compare = s_cmpFunc[(bstencil&BGFX_STENCIL_TEST_MASK) >> BGFX_STENCIL_TEST_SHIFT],
+ .failOp = s_stencilOp[(bstencil & BGFX_STENCIL_OP_FAIL_S_MASK) >> BGFX_STENCIL_OP_FAIL_S_SHIFT],
+ .depthFailOp = s_stencilOp[(bstencil & BGFX_STENCIL_OP_FAIL_Z_MASK) >> BGFX_STENCIL_OP_FAIL_Z_SHIFT],
+ .passOp = s_stencilOp[(bstencil & BGFX_STENCIL_OP_PASS_Z_MASK) >> BGFX_STENCIL_OP_PASS_Z_SHIFT],
+ },
+ .stencilReadMask = (fstencil & BGFX_STENCIL_FUNC_RMASK_MASK) >> BGFX_STENCIL_FUNC_RMASK_SHIFT,
+ .stencilWriteMask = 0xff,
+ .depthBias = 0,
+ .depthBiasSlopeScale = 0.0f,
+ .depthBiasClamp = 0.0f,
+ };
+ }
+
+ void* m_webgpuDll;
+ void* m_renderDocDll;
+
+ WGPUInstance m_instance;
+ WGPUAdapter m_adapter;
+ WGPUDevice m_device;
+
+ TimerQueryWGPU m_gpuTimer;
+ OcclusionQueryWGPU m_occlusionQuery;
+ ChunkedScratchBufferWGPU m_uniformScratchBuffer;
+
+ WGPULimits m_limits;
+
+ uint32_t m_maxFrameLatency;
+ CommandQueueWGPU m_cmd;
+
+ Resolution m_resolution;
+ uint16_t m_maxAnisotropy;
+ bool m_depthClamp;
+ bool m_wireframe;
+
+ IndexBufferWGPU m_indexBuffers[BGFX_CONFIG_MAX_INDEX_BUFFERS];
+ VertexBufferWGPU m_vertexBuffers[BGFX_CONFIG_MAX_VERTEX_BUFFERS];
+ ShaderWGPU m_shaders[BGFX_CONFIG_MAX_SHADERS];
+ ProgramWGPU m_program[BGFX_CONFIG_MAX_PROGRAMS];
+ TextureWGPU m_textures[BGFX_CONFIG_MAX_TEXTURES];
+ VertexLayout m_vertexLayouts[BGFX_CONFIG_MAX_VERTEX_LAYOUTS];
+ FrameBufferWGPU m_frameBuffers[BGFX_CONFIG_MAX_FRAME_BUFFERS];
+
+ StateCacheLru m_computePipelineCache;
+ StateCacheLru m_renderPipelineCache;
+ StateCacheT m_textureViewStateCache;
+ StateCacheT m_samplerStateCache;
+
+ void* m_uniforms[BGFX_CONFIG_MAX_UNIFORMS];
+ Matrix4 m_predefinedUniforms[PredefinedUniform::Count];
+ UniformRegistry m_uniformReg;
+
+ FrameBufferWGPU m_backBuffer;
+
+ uint16_t m_numWindows;
+ FrameBufferHandle m_windows[BGFX_CONFIG_MAX_FRAME_BUFFERS];
+ int64_t m_presentElapsed;
+
+ TextVideoMem m_textVideoMem;
+
+ uint8_t m_fsScratch[64<<10];
+ uint8_t m_vsScratch[64<<10];
+ };
+
+ static RendererContextWGPU* s_renderWGPU;
+
+ RendererContextI* rendererCreate(const Init& _init)
+ {
+ BX_UNUSED(_init);
+ s_renderWGPU = BX_NEW(g_allocator, RendererContextWGPU);
+ if (!s_renderWGPU->init(_init) )
+ {
+ bx::deleteObject(g_allocator, s_renderWGPU);
+ s_renderWGPU = NULL;
+ }
+ return s_renderWGPU;
+ }
+
+ void rendererDestroy()
+ {
+ s_renderWGPU->shutdown();
+ bx::deleteObject(g_allocator, s_renderWGPU);
+ s_renderWGPU = NULL;
+ }
+
+ void stubRenderPassEncoderMultiDrawIndirect(WGPURenderPassEncoder _renderPassEncoder, WGPUBuffer _indirectBuffer, uint64_t _indirectOffset, uint32_t _maxDrawCount, WGPUBuffer _drawCountBuffer, uint64_t _drawCountBufferOffset)
+ {
+ BX_ASSERT(NULL == _drawCountBuffer, "stubRenderPassEncoderMultiDrawIndirect doesn't support count buffer.");
+ BX_UNUSED(_drawCountBuffer, _drawCountBufferOffset);
+
+ for (uint32_t ii = 0; ii < _maxDrawCount; ++ii)
+ {
+ wgpuRenderPassEncoderDrawIndirect(
+ _renderPassEncoder
+ , _indirectBuffer
+ , _indirectOffset
+ );
+
+ _indirectOffset += BGFX_CONFIG_DRAW_INDIRECT_STRIDE;
+ }
+ }
+
+ void stubRenderPassEncoderMultiDrawIndexedIndirect(WGPURenderPassEncoder _renderPassEncoder, WGPUBuffer _indirectBuffer, uint64_t _indirectOffset, uint32_t _maxDrawCount, WGPUBuffer _drawCountBuffer, uint64_t _drawCountBufferOffset)
+ {
+ BX_ASSERT(NULL == _drawCountBuffer, "stubRenderPassEncoderMultiDrawIndexedIndirect doesn't support count buffer.");
+ BX_UNUSED(_drawCountBuffer, _drawCountBufferOffset);
+
+ for (uint32_t ii = 0; ii < _maxDrawCount; ++ii)
+ {
+ wgpuRenderPassEncoderDrawIndexedIndirect(
+ _renderPassEncoder
+ , _indirectBuffer
+ , _indirectOffset
+ );
+
+ _indirectOffset += BGFX_CONFIG_DRAW_INDIRECT_STRIDE;
+ }
+ }
+
+ void ChunkedScratchBufferWGPU::create(uint32_t _chunkSize, uint32_t _numChunks, WGPUBufferUsage _usage, uint32_t _align)
+ {
+ const uint32_t chunkSize = bx::alignUp(_chunkSize, 1<<20);
+
+ m_chunkPos = 0;
+ m_chunkSize = chunkSize;
+ m_align = _align;
+ m_usage = _usage;
+
+ m_chunkControl.m_size = 0;
+ m_chunkControl.reset();
+
+ bx::memSet(m_consume, 0, sizeof(m_consume) );
+ m_totalUsed = 0;
+
+ for (uint32_t ii = 0; ii < _numChunks; ++ii)
+ {
+ addChunk();
+ }
+ }
+
+ void ChunkedScratchBufferWGPU::createUniform(uint32_t _chunkSize, uint32_t _numChunks)
+ {
+ const WGPULimits& limits = s_renderWGPU->m_limits;
+ const uint32_t align = uint32_t(limits.minUniformBufferOffsetAlignment);
+
+ create(_chunkSize, _numChunks, WGPUBufferUsage_Uniform, align);
+ }
+
+ void ChunkedScratchBufferWGPU::destroy()
+ {
+ for (Chunk& sbc : m_chunks)
+ {
+ wgpuRelease(sbc.buffer);
+ bx::free(g_allocator, sbc.data);
+ }
+ }
+
+ void ChunkedScratchBufferWGPU::addChunk(uint32_t _at)
+ {
+ Chunk sbc;
+
+ WGPUBufferDescriptor bufferDesc =
+ {
+ .nextInChain = NULL,
+ .label = toWGPUStringView("uniform buffer"),
+ .usage = 0
+ | m_usage
+ | WGPUBufferUsage_CopyDst
+ ,
+ .size = m_chunkSize,
+ .mappedAtCreation = false,
+ };
+
+ sbc.buffer = WGPU_CHECK(wgpuDeviceCreateBuffer(s_renderWGPU->m_device, &bufferDesc) );
+ sbc.data = (uint8_t*)bx::alloc(g_allocator, m_chunkSize);
+
+ const uint32_t lastChunk = bx::max(uint32_t(m_chunks.size()-1), 1);
+ const uint32_t at = UINT32_MAX == _at ? lastChunk : _at;
+ const uint32_t chunkIndex = at % bx::max(m_chunks.size(), 1);
+
+ m_chunkControl.resize(m_chunkSize);
+
+ m_chunks.insert(&m_chunks[chunkIndex], sbc);
+ }
+
+ ChunkedScratchBufferAlloc ChunkedScratchBufferWGPU::alloc(uint32_t _size)
+ {
+ BX_ASSERT(_size < m_chunkSize, "Size can't be larger than chunk size (size: %d, chunk size: %d)!", _size, m_chunkSize);
+
+ uint32_t offset = m_chunkPos;
+ uint32_t nextOffset = offset + _size;
+ uint32_t chunkIdx = m_chunkControl.m_write/m_chunkSize;
+
+ if (nextOffset >= m_chunkSize)
+ {
+ const uint32_t total = m_chunkSize - m_chunkPos + _size;
+ uint32_t reserved = m_chunkControl.reserve(total, true);
+
+ if (total != reserved)
+ {
+ addChunk(chunkIdx + 1);
+ reserved = m_chunkControl.reserve(total, true);
+ BX_ASSERT(total == reserved, "Failed to reserve chunk memory after adding chunk.");
+ }
+
+ m_chunkPos = 0;
+ offset = 0;
+ nextOffset = _size;
+ chunkIdx = m_chunkControl.m_write/m_chunkSize;
+ }
+ else
+ {
+ const uint32_t size = m_chunkControl.reserve(_size, true);
+ BX_ASSERT(size == _size, "Failed to reserve chunk memory.");
+ BX_UNUSED(size);
+ }
+
+ m_chunkPos = nextOffset;
+
+ return { .offset = offset, .chunkIdx = chunkIdx };
+ }
+
+ void ChunkedScratchBufferWGPU::write(ChunkedScratchBufferOffset& _outSbo, const void* _vsData, uint32_t _vsSize, const void* _fsData, uint32_t _fsSize)
+ {
+ const uint32_t vsSize = bx::strideAlign(_vsSize, m_align);
+ const uint32_t fsSize = bx::strideAlign(_fsSize, m_align);
+ const uint32_t size = vsSize + fsSize;
+
+ const ChunkedScratchBufferAlloc sba = alloc(size);
+
+ const uint32_t offset0 = sba.offset;
+ const uint32_t offset1 = offset0 + vsSize;
+
+ const Chunk& sbc = m_chunks[sba.chunkIdx];
+
+ _outSbo.buffer = sbc.buffer;
+ _outSbo.offsets[0] = offset0;
+ _outSbo.offsets[1] = offset1;
+
+ bx::memCopy(&sbc.data[offset0], _vsData, _vsSize);
+ bx::memCopy(&sbc.data[offset1], _fsData, _fsSize);
+ }
+
+ void ChunkedScratchBufferWGPU::begin()
+ {
+ BX_ASSERT(0 == m_chunkPos, "");
+ const uint32_t numConsumed = m_consume[s_renderWGPU->m_cmd.m_currentFrameInFlight];
+ m_chunkControl.consume(numConsumed);
+ }
+
+ void ChunkedScratchBufferWGPU::end()
+ {
+ uint32_t numFlush = m_chunkControl.getNumReserved();
+
+ if (0 != m_chunkPos)
+ {
+retry:
+ const uint32_t remainder = m_chunkSize - m_chunkPos;
+ const uint32_t rem = m_chunkControl.reserve(remainder, true);
+
+ if (rem != remainder)
+ {
+ const uint32_t chunkIdx = m_chunkControl.m_write/m_chunkSize;
+ addChunk(chunkIdx + 1);
+ goto retry;
+ }
+
+ m_chunkPos = 0;
+ }
+
+ const uint32_t numReserved = m_chunkControl.getNumReserved();
+ BX_ASSERT(0 == numReserved % m_chunkSize, "Number of reserved must always be aligned to chunk size!");
+
+ const uint32_t first = m_chunkControl.m_current / m_chunkSize;
+
+ for (uint32_t ii = first, end = numReserved / m_chunkSize + first; ii < end; ++ii)
+ {
+ const Chunk& chunk = m_chunks[ii % m_chunks.size()];
+
+ s_renderWGPU->m_cmd.writeBuffer(chunk.buffer, 0, chunk.data, bx::min(numFlush, m_chunkSize) );
+
+ m_chunkControl.commit(m_chunkSize);
+ numFlush = bx::uint32_satsub(numFlush, m_chunkSize);
+ }
+
+ m_consume[s_renderWGPU->m_cmd.m_currentFrameInFlight] = numReserved;
+
+ m_totalUsed = m_chunkControl.getNumUsed();
+ }
+
+ void ChunkedScratchBufferWGPU::flush()
+ {
+ end();
+ begin();
+ }
+
+ void BufferWGPU::create(uint32_t _size, void* _data, uint16_t _flags, bool _vertex, uint32_t _stride)
+ {
+ BX_UNUSED(_stride);
+
+ m_size = bx::alignUp(_size, 4);
+ m_flags = _flags;
+
+ const bool indirect = !!(m_flags & BGFX_BUFFER_DRAW_INDIRECT);
+ const bool storage = indirect || !!(m_flags & BGFX_BUFFER_COMPUTE_READ_WRITE);
+
+ WGPUBufferDescriptor bufferDesc =
+ {
+ .nextInChain = NULL,
+ .label = WGPU_STRING_VIEW_INIT,
+ .usage = 0
+ | (storage ? WGPUBufferUsage_Storage : 0)
+ | (indirect
+ ? WGPUBufferUsage_Indirect
+ : _vertex ? WGPUBufferUsage_Vertex : WGPUBufferUsage_Index
+ )
+ | WGPUBufferUsage_CopyDst
+ ,
+ .size = m_size,
+ .mappedAtCreation = false,
+ };
+
+ m_buffer = WGPU_CHECK(wgpuDeviceCreateBuffer(s_renderWGPU->m_device, &bufferDesc) );
+
+ if (NULL != _data)
+ {
+ s_renderWGPU->m_cmd.writeBuffer(m_buffer, 0, _data, m_size);
+ }
+ }
+
+ void BufferWGPU::update(uint32_t _offset, uint32_t _size, void* _data, bool _discard) const
+ {
+ BX_UNUSED(_discard);
+ s_renderWGPU->m_cmd.writeBuffer(m_buffer, _offset, _data, bx::alignUp(_size, 4) );
+ }
+
+ void BufferWGPU::destroy()
+ {
+ wgpuRelease(m_buffer);
+ }
+
+ void VertexBufferWGPU::create(uint32_t _size, void* _data, VertexLayoutHandle _layoutHandle, uint16_t _flags)
+ {
+ BufferWGPU::create(_size, _data, _flags, true);
+ m_layoutHandle = _layoutHandle;
+ }
+
+ void ShaderWGPU::create(const Memory* _mem)
+ {
+ bx::MemoryReader reader(_mem->data, _mem->size);
+
+ bx::ErrorAssert err;
+
+ uint32_t magic;
+ bx::read(&reader, magic, &err);
+
+ const bool fragment = isShaderType(magic, 'F');
+
+ uint32_t hashIn;
+ bx::read(&reader, hashIn, &err);
+
+ uint32_t hashOut;
+
+ if (isShaderVerLess(magic, 6) )
+ {
+ hashOut = hashIn;
+ }
+ else
+ {
+ bx::read(&reader, hashOut, &err);
+ }
+
+ uint16_t count;
+ bx::read(&reader, count, &err);
+
+ m_numPredefined = 0;
+ m_numUniforms = count;
+ m_numTextures = 0;
+
+ BX_TRACE("%s Shader consts %d"
+ , getShaderTypeName(magic)
+ , count
+ );
+
+ uint8_t fragmentBit = fragment ? kUniformFragmentBit : 0;
+
+ for (uint32_t ii = 0; ii < BGFX_CONFIG_MAX_TEXTURE_SAMPLERS; ++ii)
+ {
+ m_shaderBinding[ii].clear();
+ }
+
+ if (0 < count)
+ {
+ for (uint32_t ii = 0; ii < count; ++ii)
+ {
+ uint8_t nameSize = 0;
+ bx::read(&reader, nameSize, &err);
+
+ char name[256];
+ bx::read(&reader, &name, nameSize, &err);
+ name[nameSize] = '\0';
+
+ uint8_t type = 0;
+ bx::read(&reader, type, &err);
+
+ uint8_t num;
+ bx::read(&reader, num, &err);
+
+ uint16_t regIndex;
+ bx::read(&reader, regIndex, &err);
+
+ uint16_t regCount;
+ bx::read(&reader, regCount, &err);
+
+ const bool hasTexData = !isShaderVerLess(magic, 8);
+ const bool hasTexFormat = !isShaderVerLess(magic, 10);
+ uint8_t texComponent = 0;
+ uint8_t texDimension = 0;
+ uint16_t texFormat = 0;
+
+ if (hasTexData)
+ {
+ bx::read(&reader, texComponent, &err);
+ bx::read(&reader, texDimension, &err);
+ }
+
+ if (hasTexFormat)
+ {
+ bx::read(&reader, texFormat, &err);
+ }
+
+ const char* kind = "invalid";
+
+ BX_UNUSED(num, texComponent, texFormat);
+
+ if (UINT16_MAX != regIndex)
+ {
+ PredefinedUniform::Enum predefined = nameToPredefinedUniformEnum(name);
+
+ if (PredefinedUniform::Count != predefined)
+ {
+ kind = "predefined";
+ m_predefined[m_numPredefined].m_loc = regIndex;
+ m_predefined[m_numPredefined].m_count = regCount;
+ m_predefined[m_numPredefined].m_type = uint8_t(predefined|fragmentBit);
+ m_numPredefined++;
+ }
+ else if (UniformType::End == (~kUniformMask & type) )
+ {
+ const bool isBuffer = idToDescriptorType(regCount) == DescriptorType::StorageBuffer;
+
+ if (0 == regIndex)
+ {
+ continue;
+ }
+
+ const uint8_t reverseShift = kSpirvBindShift;
+
+ const uint16_t stage = regIndex - reverseShift; // regIndex is used for buffer binding index
+ ShaderBinding& shaderBind = m_shaderBinding[stage];
+
+ shaderBind.type = isBuffer ? ShaderBinding::Type::Buffer : ShaderBinding::Type::Image;
+ shaderBind.uniformHandle = { 0 };
+ shaderBind.binding = regIndex;
+
+ if (isBuffer)
+ {
+ shaderBind.bufferBindingType = 0 != (kUniformReadOnlyBit & type)
+ ? WGPUBufferBindingType_ReadOnlyStorage
+ : WGPUBufferBindingType_Storage
+ ;
+ }
+ else
+ {
+ shaderBind.bufferBindingType = WGPUBufferBindingType_BindingNotUsed;
+ }
+
+ if (!isBuffer
+ && hasTexData)
+ {
+ shaderBind.viewDimension = s_textureDimension[idToTextureDimension(texDimension)];
+ shaderBind.sampleType = s_textureComponentType[idToTextureComponentType(texComponent)];
+ }
+ else
+ {
+ shaderBind.viewDimension = WGPUTextureViewDimension_Undefined;
+ shaderBind.sampleType = WGPUTextureSampleType_Float;
+ }
+
+ kind = "storage";
+ }
+ else if (UniformType::Sampler == (~kUniformMask & type) )
+ {
+ const UniformRegInfo* info = s_renderWGPU->m_uniformReg.find(name);
+ BX_ASSERT(NULL != info, "User defined uniform '%s' is not found, it won't be set.", name);
+
+ const uint8_t reverseShift = kSpirvBindShift;
+
+ const uint16_t stage = regIndex - reverseShift; // regIndex is used for image/sampler binding index
+ ShaderBinding& shaderBind = m_shaderBinding[stage];
+
+ shaderBind.uniformHandle = info->m_handle;
+ shaderBind.type = ShaderBinding::Type::Sampler;
+ shaderBind.binding = regIndex;
+ shaderBind.samplerBinding = regIndex + kSpirvSamplerShift;
+
+ if (hasTexData)
+ {
+ shaderBind.viewDimension = s_textureDimension[idToTextureDimension(texDimension)];
+ shaderBind.sampleType = s_textureComponentType[idToTextureComponentType(texComponent)];
+ }
+ else
+ {
+ shaderBind.viewDimension = WGPUTextureViewDimension_Undefined;
+ shaderBind.sampleType = WGPUTextureSampleType_Float;
+ }
+
+ if (type & kUniformCompareBit)
+ {
+ shaderBind.sampleType = WGPUTextureSampleType_Depth;
+ }
+
+ kind = "sampler";
+ }
+ else
+ {
+ const UniformRegInfo* info = s_renderWGPU->m_uniformReg.find(name);
+ BX_ASSERT(NULL != info, "User defined uniform '%s' is not found, it won't be set.", name);
+
+ if (NULL != info)
+ {
+ if (NULL == m_constantBuffer)
+ {
+ m_constantBuffer = UniformBuffer::create(1024);
+ }
+
+ kind = "user";
+ m_constantBuffer->writeUniformHandle(type|fragmentBit, regIndex, info->m_handle, regCount);
+ }
+ }
+ }
+
+ BX_TRACE("\t%s: %s (%s), r.index %3d, r.count %2d, r.texComponent %1d, r.texDimension %1d"
+ , kind
+ , name
+ , getUniformTypeName(UniformType::Enum(type&~kUniformMask) )
+ , regIndex
+ , regCount
+ , texComponent
+ , texDimension
+ );
+ BX_UNUSED(kind);
+ }
+
+ if (NULL != m_constantBuffer)
+ {
+ m_constantBuffer->finish();
+ }
+ }
+
+ uint32_t shaderSize;
+ bx::read(&reader, shaderSize, &err);
+
+ const void* code = reader.getDataPtr();
+
+ bx::skip(&reader, shaderSize+1);
+
+ m_code = alloc(shaderSize);
+ bx::memCopy(m_code->data, code, shaderSize);
+
+ WGPUShaderSourceWGSL shaderSourceWgsl =
+ {
+ .chain =
+ {
+ .next = NULL,
+ .sType = WGPUSType_ShaderSourceWGSL,
+ },
+ .code =
+ {
+ .data = (const char*)m_code->data,
+ .length = m_code->size,
+ },
+ };
+
+ WGPUShaderModuleDescriptor shaderModuleDesc =
+ {
+ .nextInChain = &shaderSourceWgsl.chain,
+ .label = WGPU_STRING_VIEW_INIT,
+ };
+
+ m_module = WGPU_CHECK(wgpuDeviceCreateShaderModule(s_renderWGPU->m_device, &shaderModuleDesc) );
+ BX_ASSERT(NULL != m_module, "");
+
+ bx::memSet(m_attrMask, 0, sizeof(m_attrMask) );
+ bx::memSet(m_attrRemap, 0, sizeof(m_attrRemap) );
+
+ bx::read(&reader, m_numAttrs, &err);
+
+ for (uint8_t ii = 0; ii < m_numAttrs; ++ii)
+ {
+ uint16_t id;
+ bx::read(&reader, id, &err);
+
+ Attrib::Enum attr = idToAttrib(id);
+
+ if (Attrib::Count != attr)
+ {
+ m_attrMask[attr] = UINT16_MAX;
+ m_attrRemap[attr] = ii;
+ }
+ }
+
+ bx::HashMurmur3 murmur;
+ murmur.begin();
+ murmur.add(hashIn);
+ murmur.add(hashOut);
+ murmur.add(m_code->data, m_code->size);
+ murmur.add(m_numAttrs);
+ murmur.add(m_attrMask, m_numAttrs);
+ murmur.add(m_attrRemap, m_numAttrs);
+ m_hash = murmur.end();
+
+ bx::read(&reader, m_size, &err);
+ bx::read(&reader, m_blockSize, &err);
+ }
+
+ void ShaderWGPU::destroy()
+ {
+ if (NULL != m_constantBuffer)
+ {
+ UniformBuffer::destroy(m_constantBuffer);
+ m_constantBuffer = NULL;
+ }
+
+ m_numPredefined = 0;
+
+ if (NULL != m_code)
+ {
+ release(m_code);
+ m_code = NULL;
+ m_hash = 0;
+ }
+
+ wgpuRelease(m_module);
+ }
+
+ void ProgramWGPU::create(const ShaderWGPU* _vsh, const ShaderWGPU* _fsh)
+ {
+ BX_ASSERT(_vsh->m_module, "Vertex shader doesn't exist.");
+ m_vsh = _vsh;
+ m_fsh = _fsh;
+
+ const bool isCompute = NULL == m_fsh;
+
+ m_vsh = _vsh;
+ bx::memCopy(&m_predefined[0], _vsh->m_predefined, _vsh->m_numPredefined * sizeof(PredefinedUniform) );
+ m_numPredefined = _vsh->m_numPredefined;
+
+ if (NULL != _fsh)
+ {
+ m_fsh = _fsh;
+ bx::memCopy(&m_predefined[m_numPredefined], _fsh->m_predefined, _fsh->m_numPredefined * sizeof(PredefinedUniform) );
+ m_numPredefined += _fsh->m_numPredefined;
+ }
+
+ const uint32_t vsSize = m_vsh->m_size;
+ const uint32_t fsSize = NULL != m_fsh ? m_fsh->m_size : 0;
+
+ uint8_t numBindings = 0
+ + (0 < vsSize)
+ + (0 < fsSize)
+ ;
+
+ if (isCompute)
+ {
+ for (uint8_t stage = 0; stage < BGFX_CONFIG_MAX_TEXTURE_SAMPLERS; ++stage)
+ {
+ ShaderBinding& shaderBind = m_shaderBinding[stage];
+ shaderBind.clear();
+
+ if (isValid(m_vsh->m_shaderBinding[stage].uniformHandle) )
+ {
+ shaderBind = m_vsh->m_shaderBinding[stage];
+ shaderBind.shaderStage = WGPUShaderStage_Compute;
+ numBindings++;
+ }
+ }
+ }
+ else
+ {
+ for (uint8_t stage = 0; stage < BGFX_CONFIG_MAX_TEXTURE_SAMPLERS; ++stage)
+ {
+ ShaderBinding& shaderBind = m_shaderBinding[stage];
+ shaderBind.clear();
+
+ if (isValid(m_vsh->m_shaderBinding[stage].uniformHandle) )
+ {
+ shaderBind = m_vsh->m_shaderBinding[stage];
+ shaderBind.shaderStage = WGPUShaderStage_Vertex;
+ numBindings++;
+ }
+ else if (NULL != m_fsh && isValid(m_fsh->m_shaderBinding[stage].uniformHandle) )
+ {
+ shaderBind = m_fsh->m_shaderBinding[stage];
+ shaderBind.shaderStage = WGPUShaderStage_Fragment;
+ numBindings += 2;
+ }
+ }
+ }
+
+ m_numBindings = numBindings;
+ }
+
+ void ProgramWGPU::destroy()
+ {
+ m_numBindings = 0;
+ m_numPredefined = 0;
+ m_vsh = NULL;
+ m_fsh = NULL;
+ }
+
+ void TextureWGPU::create(const Memory* _mem, uint64_t _flags, uint8_t _skip)
+ {
+ bimg::ImageContainer imageContainer;
+
+ if (bimg::imageParse(imageContainer, _mem->data, _mem->size) )
+ {
+ const bimg::ImageBlockInfo& blockInfo = bimg::getBlockInfo(imageContainer.m_format);
+ const uint8_t startLod = bx::min(_skip, imageContainer.m_numMips-1);
+
+ bimg::TextureInfo ti;
+ bimg::imageGetSize(
+ &ti
+ , uint16_t(imageContainer.m_width >>startLod)
+ , uint16_t(imageContainer.m_height>>startLod)
+ , uint16_t(imageContainer.m_depth >>startLod)
+ , imageContainer.m_cubeMap
+ , 1 < imageContainer.m_numMips
+ , imageContainer.m_numLayers
+ , imageContainer.m_format
+ );
+ ti.numMips = bx::min(imageContainer.m_numMips-startLod, ti.numMips);
+
+ m_flags = _flags;
+ m_width = ti.width;
+ m_height = ti.height;
+ m_depth = ti.depth;
+ m_numLayers = ti.numLayers;
+ m_requestedFormat = uint8_t(imageContainer.m_format);
+ m_textureFormat = uint8_t(getViableTextureFormat(imageContainer) );
+ const bool convert = m_textureFormat != m_requestedFormat;
+ const uint8_t bpp = bimg::getBitsPerPixel(bimg::TextureFormat::Enum(m_textureFormat) );
+
+ WGPUTextureDimension dimension = WGPUTextureDimension_2D;
+ uint32_t depthOrArrayLayers = 1;
+
+ if (imageContainer.m_cubeMap)
+ {
+ m_type = TextureCube;
+ m_viewDimension = 1 < m_numLayers
+ ? WGPUTextureViewDimension_CubeArray
+ : WGPUTextureViewDimension_Cube
+ ;
+ depthOrArrayLayers = 6;
+ }
+ else if (imageContainer.m_depth > 1)
+ {
+ m_type = Texture3D;
+ m_viewDimension = WGPUTextureViewDimension_3D;
+
+ dimension = WGPUTextureDimension_3D;
+ depthOrArrayLayers = m_depth;
+ }
+ else
+ {
+ m_type = Texture2D;
+ m_viewDimension = 1 < m_numLayers
+ ? WGPUTextureViewDimension_2DArray
+ : WGPUTextureViewDimension_2D
+ ;
+ depthOrArrayLayers = m_numLayers;
+ }
+
+ m_numMips = ti.numMips;
+ const uint16_t numSides = ti.numLayers * (imageContainer.m_cubeMap ? 6 : 1);
+
+ const bool compressed = bimg::isCompressed(bimg::TextureFormat::Enum(m_textureFormat) );
+ const bool swizzle = TextureFormat::BGRA8 == m_textureFormat && 0 != (m_flags&BGFX_TEXTURE_COMPUTE_WRITE);
+
+ const bool writeOnly = 0 != (m_flags&BGFX_TEXTURE_RT_WRITE_ONLY);
+ const bool computeWrite = 0 != (m_flags&BGFX_TEXTURE_COMPUTE_WRITE);
+ const bool renderTarget = 0 != (m_flags&BGFX_TEXTURE_RT_MASK);
+ const bool blit = 0 != (m_flags&BGFX_TEXTURE_BLIT_DST);
+
+ const uint32_t msaaQuality = bx::uint32_satsub((m_flags & BGFX_TEXTURE_RT_MSAA_MASK) >> BGFX_TEXTURE_RT_MSAA_SHIFT, 1);
+ const uint32_t msaaCount = 1; //s_msaa[msaaQuality];
+
+ const bool needResolve = true
+ && 1 < msaaCount
+ && 0 == (m_flags & BGFX_TEXTURE_MSAA_SAMPLE)
+ && !writeOnly
+ ;
+
+ BX_TRACE("Texture %3d: %s (requested: %s), %dx%d%s RT[%c], BO[%c], CW[%c]%s."
+ , this - s_renderWGPU->m_textures
+ , getName( (TextureFormat::Enum)m_textureFormat)
+ , getName( (TextureFormat::Enum)m_requestedFormat)
+ , ti.width
+ , ti.height
+ , imageContainer.m_cubeMap ? "x6" : ""
+ , renderTarget ? 'x' : ' '
+ , writeOnly ? 'x' : ' '
+ , computeWrite ? 'x' : ' '
+ , swizzle ? " (swizzle BGRA8 -> RGBA8)" : ""
+ );
+ BX_UNUSED(swizzle);
+
+ WGPUTextureDescriptor textureDesc =
+ {
+ .nextInChain = NULL,
+ .label = WGPU_STRING_VIEW_INIT,
+ .usage = 0
+ | WGPUTextureUsage_TextureBinding
+ | WGPUTextureUsage_CopySrc
+ | (!writeOnly ? WGPUTextureUsage_CopyDst : 0)
+ | (blit ? WGPUTextureUsage_CopyDst : 0)
+ | (computeWrite ? WGPUTextureUsage_StorageBinding : 0)
+ | (renderTarget ? WGPUTextureUsage_RenderAttachment : 0)
+ ,
+ .dimension = dimension,
+ .size =
+ {
+ .width = m_width,
+ .height = m_height,
+ .depthOrArrayLayers = depthOrArrayLayers,
+ },
+ .format = s_textureFormat[m_textureFormat].m_fmt,
+ .mipLevelCount = m_numMips,
+ .sampleCount = msaaCount,
+ .viewFormatCount = 0,
+ .viewFormats = NULL,
+ };
+
+ m_texture = WGPU_CHECK(wgpuDeviceCreateTexture(s_renderWGPU->m_device, &textureDesc) );
+
+ if (needResolve)
+ {
+ textureDesc.sampleCount = 1;
+ m_textureResolve = WGPU_CHECK(wgpuDeviceCreateTexture(s_renderWGPU->m_device, &textureDesc) );
+ }
+
+ uint32_t totalSize = 0;
+
+ WGPUTexelCopyTextureInfo copyTextureDst =
+ {
+ .texture = m_texture,
+ .mipLevel = 0,
+ .origin =
+ {
+ .x = 0,
+ .y = 0,
+ .z = 0,
+ },
+ .aspect = WGPUTextureAspect_All,
+ };
+
+ uint8_t* temp = convert ? (uint8_t*)bx::alloc(g_allocator, m_width*m_height*bpp/8) : NULL;
+
+ for (uint16_t side = 0, kk = 0; side < numSides; ++side)
+ {
+ copyTextureDst.origin.z = side;
+
+ for (uint8_t lod = 0; lod < ti.numMips; ++lod)
+ {
+ copyTextureDst.mipLevel = lod;
+
+ bimg::ImageMip mip;
+ if (bimg::imageGetRawData(imageContainer, side, lod+startLod, _mem->data, _mem->size, mip) )
+ {
+ if (convert)
+ {
+ const uint32_t mipWidth = bx::max(mip.m_width, 4);
+ const uint32_t mipHeight = bx::max(mip.m_height, 4);
+ const uint32_t bytesPerRow = mipWidth*bpp/8;
+ const uint32_t width = bx::max(m_width >> lod, 1);
+ const uint32_t height = bx::max(m_height >> lod, 1);
+ const uint32_t size = bytesPerRow*height*mip.m_depth;
+
+ bimg::imageDecodeToBgra8(
+ g_allocator
+ , temp
+ , mip.m_data
+ , mipWidth
+ , mipHeight
+ , bytesPerRow
+ , bimg::TextureFormat::Enum(m_requestedFormat)
+ );
+
+ s_renderWGPU->m_cmd.writeTexture(
+ copyTextureDst
+ , temp
+ , size
+ , {
+ .offset = 0,
+ .bytesPerRow = bytesPerRow,
+ .rowsPerImage = height,
+ }
+ , {
+ .width = width,
+ .height = height,
+ .depthOrArrayLayers = mip.m_depth,
+ });
+
+ totalSize += size;
+ }
+ else if (compressed)
+ {
+ const uint32_t width = mip.m_width;
+ const uint32_t height = mip.m_height;
+ const uint32_t bytesPerRow = (mip.m_width/blockInfo.blockWidth)*mip.m_blockSize;
+ const uint32_t slice = (mip.m_height/blockInfo.blockHeight)*bytesPerRow;
+ const uint32_t size = slice*mip.m_depth;
+
+ s_renderWGPU->m_cmd.writeTexture(
+ copyTextureDst
+ , mip.m_data
+ , mip.m_size
+ , {
+ .offset = 0,
+ .bytesPerRow = bytesPerRow,
+ .rowsPerImage = height,
+ }
+ , {
+ .width = width,
+ .height = height,
+ .depthOrArrayLayers = mip.m_depth,
+ });
+
+ totalSize += size;
+ }
+ else
+ {
+ const uint32_t width = mip.m_width;
+ const uint32_t height = mip.m_height;
+ const uint32_t bytesPerRow = mip.m_width*mip.m_bpp / 8;
+ const uint32_t slice = mip.m_height*bytesPerRow;
+ const uint32_t size = slice*mip.m_depth;
+
+ s_renderWGPU->m_cmd.writeTexture(
+ copyTextureDst
+ , mip.m_data
+ , mip.m_size
+ , {
+ .offset = 0,
+ .bytesPerRow = bytesPerRow,
+ .rowsPerImage = height,
+ }
+ , {
+ .width = width,
+ .height = height,
+ .depthOrArrayLayers = mip.m_depth,
+ });
+
+ totalSize += size;
+ }
+ }
+
+ ++kk;
+ }
+ }
+
+ if (NULL != temp)
+ {
+ bx::free(g_allocator, temp);
+ }
+ }
+ }
+
+ void TextureWGPU::destroy()
+ {
+ wgpuRelease(m_texture);
+ wgpuRelease(m_textureResolve);
+ }
+
+ void TextureWGPU::update(uint8_t _side, uint8_t _mip, const Rect& _rect, uint16_t _z, uint16_t _depth, uint16_t _pitch, const Memory* _mem)
+ {
+ const uint32_t bpp = bimg::getBitsPerPixel(bimg::TextureFormat::Enum(m_textureFormat) );
+ uint32_t rectPitch = _rect.m_width*bpp/8;
+ const bimg::ImageBlockInfo& blockInfo = bimg::getBlockInfo(bimg::TextureFormat::Enum(m_textureFormat) );
+
+ if (bimg::isCompressed(bimg::TextureFormat::Enum(m_textureFormat) ) )
+ {
+ rectPitch = (_rect.m_width / blockInfo.blockWidth) * blockInfo.blockSize;
+ }
+
+ const uint32_t bytesPerRow = UINT16_MAX == _pitch ? rectPitch : _pitch;
+ const uint32_t slicePitch = rectPitch*_rect.m_height;
+
+ const bool convert = m_textureFormat != m_requestedFormat;
+
+ uint8_t* srcData = _mem->data;
+ uint8_t* temp = NULL;
+
+ if (convert)
+ {
+ temp = (uint8_t*)bx::alloc(g_allocator, slicePitch);
+ bimg::imageDecodeToBgra8(g_allocator, temp, srcData, _rect.m_width, _rect.m_height, bytesPerRow, bimg::TextureFormat::Enum(m_requestedFormat) );
+ srcData = temp;
+
+ }
+
+ const uint32_t width = bx::min(bx::max(1u, bx::alignUp(m_width >> _mip, blockInfo.blockWidth ) ), _rect.m_width);
+ const uint32_t height = bx::min(bx::max(1u, bx::alignUp(m_height >> _mip, blockInfo.blockHeight) ), _rect.m_height);
+ const uint32_t originZ = TextureWGPU::TextureCube == m_type ? _side : _z;
+
+ s_renderWGPU->m_cmd.writeTexture(
+ {
+ .texture = m_texture,
+ .mipLevel = _mip,
+ .origin =
+ {
+ .x = _rect.m_x,
+ .y = _rect.m_y,
+ .z = originZ,
+ },
+ .aspect = WGPUTextureAspect_All,
+ }
+ , srcData
+ , bytesPerRow*height
+ , {
+ .offset = 0,
+ .bytesPerRow = bytesPerRow,
+ .rowsPerImage = height,
+ }
+ , {
+ .width = width,
+ .height = height,
+ .depthOrArrayLayers = _depth,
+ });
+
+ if (NULL != temp)
+ {
+ bx::free(g_allocator, temp);
+ }
+ }
+
+ WGPUSampler TextureWGPU::getSamplerState(uint32_t _samplerFlags) const
+ {
+ uint32_t samplerFlags = (0 == (BGFX_SAMPLER_INTERNAL_DEFAULT & _samplerFlags)
+ ? _samplerFlags
+ : m_flags
+ ) & (BGFX_SAMPLER_BITS_MASK | BGFX_SAMPLER_BORDER_COLOR_MASK | BGFX_SAMPLER_COMPARE_MASK)
+ ;
+
+ if (WGPUTextureSampleType_UnfilterableFloat == s_textureFormat[m_textureFormat].m_samplerType)
+ {
+ samplerFlags &= ~(BGFX_SAMPLER_MIN_MASK |BGFX_SAMPLER_MAG_MASK |BGFX_SAMPLER_MIP_MASK);
+ samplerFlags |= (BGFX_SAMPLER_MIN_POINT|BGFX_SAMPLER_MAG_POINT|BGFX_SAMPLER_MIP_POINT);
+ }
+
+ samplerFlags &= BGFX_SAMPLER_BITS_MASK;
+ WGPUSampler sampler = s_renderWGPU->m_samplerStateCache.find(samplerFlags);
+
+ const bool disableAniso = true
+ && (BGFX_SAMPLER_MIN_POINT == (samplerFlags&BGFX_SAMPLER_MIN_POINT) )
+ && (BGFX_SAMPLER_MAG_POINT == (samplerFlags&BGFX_SAMPLER_MAG_POINT) )
+ ;
+
+ if (NULL == sampler)
+ {
+ const uint32_t cmpFunc = (samplerFlags&BGFX_SAMPLER_COMPARE_MASK)>>BGFX_SAMPLER_COMPARE_SHIFT;
+ WGPUSamplerDescriptor samplerDesc =
+ {
+ .nextInChain = NULL,
+ .label = WGPU_STRING_VIEW_INIT,
+ .addressModeU = s_textureAddress[(samplerFlags&BGFX_SAMPLER_U_MASK)>>BGFX_SAMPLER_U_SHIFT],
+ .addressModeV = s_textureAddress[(samplerFlags&BGFX_SAMPLER_V_MASK)>>BGFX_SAMPLER_V_SHIFT],
+ .addressModeW = s_textureAddress[(samplerFlags&BGFX_SAMPLER_W_MASK)>>BGFX_SAMPLER_W_SHIFT],
+ .magFilter = s_textureFilterMinMag[(samplerFlags&BGFX_SAMPLER_MAG_MASK)>>BGFX_SAMPLER_MAG_SHIFT],
+ .minFilter = s_textureFilterMinMag[(samplerFlags&BGFX_SAMPLER_MIN_MASK)>>BGFX_SAMPLER_MIN_SHIFT],
+ .mipmapFilter = s_textureFilterMip[(samplerFlags&BGFX_SAMPLER_MIP_MASK)>>BGFX_SAMPLER_MIP_SHIFT],
+ .lodMinClamp = 0,
+ .lodMaxClamp = bx::kFloatLargest,
+ .compare = 0 == cmpFunc ? WGPUCompareFunction_Undefined : s_cmpFunc[cmpFunc],
+ .maxAnisotropy = disableAniso ? uint16_t(1) : s_renderWGPU->m_maxAnisotropy,
+ };
+
+ sampler = WGPU_CHECK(wgpuDeviceCreateSampler(s_renderWGPU->m_device, &samplerDesc) );
+ s_renderWGPU->m_samplerStateCache.add(samplerFlags, sampler);
+ }
+
+ return sampler;
+ }
+
+ WGPUTextureView TextureWGPU::getTextureView(uint8_t _baseMipLevel, uint8_t _mipLevelCount, bool _storage) const
+ {
+ bx::HashMurmur3 murmur;
+ murmur.begin();
+ murmur.add(uintptr_t(this) );
+ murmur.add(_baseMipLevel);
+ murmur.add(_mipLevelCount);
+ murmur.add(_storage);
+ const uint32_t hash = murmur.end();
+
+ WGPUTextureView textureView = s_renderWGPU->m_textureViewStateCache.find(hash);
+
+ if (NULL == textureView)
+ {
+ WGPUTextureViewDimension tvd = m_viewDimension;
+ uint32_t arrayLayerCount = WGPU_ARRAY_LAYER_COUNT_UNDEFINED;
+
+ if (_storage)
+ {
+ if (WGPUTextureViewDimension_Cube == tvd)
+ {
+ tvd = WGPUTextureViewDimension_2DArray;
+ }
+ }
+
+ WGPUTextureViewDescriptor textureViewDesc =
+ {
+ .nextInChain = NULL,
+ .label = WGPU_STRING_VIEW_INIT,
+ .format = s_textureFormat[m_textureFormat].m_fmt,
+ .dimension = tvd,
+ .baseMipLevel = _baseMipLevel,
+ .mipLevelCount = UINT8_MAX == _mipLevelCount ? WGPU_MIP_LEVEL_COUNT_UNDEFINED : _mipLevelCount,
+ .baseArrayLayer = 0,
+ .arrayLayerCount = arrayLayerCount,
+ .aspect = WGPUTextureAspect_All,
+ .usage = WGPUTextureUsage_TextureBinding
+ | (_storage ? WGPUTextureUsage_StorageBinding : 0)
+ ,
+ };
+
+ textureView = WGPU_CHECK(wgpuTextureCreateView(m_texture, &textureViewDesc) );
+ s_renderWGPU->m_textureViewStateCache.add(hash, textureView);
+ }
+
+ return textureView;
+ }
+
+ struct SwapChainFormatRemap
+ {
+ WGPUTextureFormat requestedFormat;
+ WGPUTextureFormat alternativeFormat;
+ };
+
+ static const SwapChainFormatRemap s_swapChainFormatRemap[] =
+ {
+ { WGPUTextureFormat_RGBA8Unorm, WGPUTextureFormat_BGRA8Unorm },
+ { WGPUTextureFormat_RGBA8UnormSrgb, WGPUTextureFormat_BGRA8UnormSrgb },
+ };
+
+ WGPUTextureFormat findSurfaceCapsFormat(const WGPUSurfaceCapabilities& _surfaceCaps, WGPUTextureFormat _requestedFormat)
+ {
+ for (uint32_t ii = 0; ii < _surfaceCaps.formatCount; ++ii)
+ {
+ if (_requestedFormat == _surfaceCaps.formats[ii])
+ {
+ return _requestedFormat;
+ }
+ }
+
+ return WGPUTextureFormat_Undefined;
+ }
+
+ bool SwapChainWGPU::create(void* _nwh, const Resolution& _resolution)
+ {
+ if (NULL == _nwh
+ || !createSurface(_nwh) )
+ {
+ return false;
+ }
+
+ return configure(_resolution);
+ }
+
+ void SwapChainWGPU::destroy()
+ {
+ WGPU_CHECK(wgpuSurfaceUnconfigure(m_surface) );
+
+ wgpuRelease(m_surface);
+ wgpuRelease(m_textureView);
+ wgpuRelease(m_depthStencilView);
+
+ m_nwh = NULL;
+ }
+
+ bool SwapChainWGPU::configure(const Resolution& _resolution)
+ {
+ m_resolution = _resolution;
+
+ WGPUSurfaceCapabilities surfaceCaps;
+ WGPUStatus status = WGPU_CHECK(wgpuSurfaceGetCapabilities(m_surface, s_renderWGPU->m_adapter, &surfaceCaps) );
+
+ if (WGPUStatus_Success != status)
+ {
+ return false;
+ }
+
+ WGPUTextureFormat requestedFormat = s_textureFormat[m_resolution.formatColor].m_fmt;
+ WGPUTextureFormat format = findSurfaceCapsFormat(surfaceCaps, requestedFormat);
+
+ if (WGPUTextureFormat_Undefined == format)
+ {
+ for (uint32_t ii = 0; ii < BX_COUNTOF(s_swapChainFormatRemap); ++ii)
+ {
+ if (requestedFormat == s_swapChainFormatRemap[ii].requestedFormat)
+ {
+ format = findSurfaceCapsFormat(surfaceCaps, s_swapChainFormatRemap[ii].alternativeFormat);
+m_resolution.formatColor = TextureFormat::BGRA8;
+ break;
+ }
+ }
+ }
+
+ BX_ASSERT(WGPUTextureFormat_Undefined != format, "SwapChain surface format is not available!");
+
+ m_surfaceConfig =
+ {
+ .nextInChain = NULL,
+ .device = s_renderWGPU->m_device,
+ .format = format,
+ .usage = WGPUTextureUsage_RenderAttachment,
+ .width = m_resolution.width,
+ .height = m_resolution.height,
+ .viewFormatCount = 0,
+ .viewFormats = NULL,
+ .alphaMode = WGPUCompositeAlphaMode_Auto,
+ .presentMode = WGPUPresentMode_Fifo,
+ };
+
+ WGPU_CHECK(wgpuSurfaceConfigure(m_surface, &m_surfaceConfig) );
+
+ WGPUSurfaceTexture surfaceTexture = WGPU_SURFACE_TEXTURE_INIT;
+ WGPU_CHECK(wgpuSurfaceGetCurrentTexture(m_surface, &surfaceTexture) );
+ m_textureView = WGPU_CHECK(wgpuTextureCreateView(surfaceTexture.texture, NULL) );
+ wgpuRelease(surfaceTexture.texture);
+
+ const uint32_t msaa = s_msaa[(_resolution.reset&BGFX_RESET_MSAA_MASK)>>BGFX_RESET_MSAA_SHIFT];
+
+ if (bimg::isDepth(bimg::TextureFormat::Enum(m_resolution.formatDepthStencil) ) )
+ {
+ WGPUTextureDescriptor textureDesc =
+ {
+ .nextInChain = NULL,
+ .label = toWGPUStringView("SwapChain Depth/Stencil"),
+ .usage = 0
+ | WGPUTextureUsage_RenderAttachment
+ ,
+ .dimension = WGPUTextureDimension_2D,
+ .size =
+ {
+ .width = m_surfaceConfig.width,
+ .height = m_surfaceConfig.height,
+ .depthOrArrayLayers = 1,
+ },
+ .format = s_textureFormat[m_resolution.formatDepthStencil].m_fmt,
+ .mipLevelCount = 1,
+ .sampleCount = msaa,
+ .viewFormatCount = 0,
+ .viewFormats = NULL,
+ };
+
+ WGPUTexture texture = WGPU_CHECK(wgpuDeviceCreateTexture(s_renderWGPU->m_device, &textureDesc) );
+
+ WGPUTextureViewDescriptor textureViewDesc =
+ {
+ .nextInChain = NULL,
+ .label = textureDesc.label,
+ .format = textureDesc.format,
+ .dimension = WGPUTextureViewDimension_2D,
+ .baseMipLevel = 0,
+ .mipLevelCount = 1,
+ .baseArrayLayer = 0,
+ .arrayLayerCount = 1,
+ .aspect = WGPUTextureAspect_All,
+ .usage = textureDesc.usage,
+ };
+
+ m_depthStencilView = WGPU_CHECK(wgpuTextureCreateView(texture, &textureViewDesc) );
+ m_formatDepthStencil = uint8_t(m_resolution.formatDepthStencil);
+
+ wgpuRelease(texture);
+ }
+
+ if (1 < msaa)
+ {
+ WGPUTextureDescriptor textureDesc =
+ {
+ .nextInChain = NULL,
+ .label = toWGPUStringView("SwapChain MSAA"),
+ .usage = 0
+ | WGPUTextureUsage_RenderAttachment
+ ,
+ .dimension = WGPUTextureDimension_2D,
+ .size =
+ {
+ .width = m_surfaceConfig.width,
+ .height = m_surfaceConfig.height,
+ .depthOrArrayLayers = 1,
+ },
+ .format = format,
+ .mipLevelCount = 1,
+ .sampleCount = msaa,
+ .viewFormatCount = 0,
+ .viewFormats = NULL,
+ };
+
+ WGPUTexture texture = WGPU_CHECK(wgpuDeviceCreateTexture(s_renderWGPU->m_device, &textureDesc) );
+
+ WGPUTextureViewDescriptor textureViewDesc =
+ {
+ .nextInChain = NULL,
+ .label = textureDesc.label,
+ .format = textureDesc.format,
+ .dimension = WGPUTextureViewDimension_2D,
+ .baseMipLevel = 0,
+ .mipLevelCount = 1,
+ .baseArrayLayer = 0,
+ .arrayLayerCount = 1,
+ .aspect = WGPUTextureAspect_All,
+ .usage = textureDesc.usage,
+ };
+
+ m_msaaTextureView = WGPU_CHECK(wgpuTextureCreateView(texture, &textureViewDesc) );
+ }
+
+ return true;
+ }
+
+ void SwapChainWGPU::update(void* _nwh, const Resolution& _resolution)
+ {
+ BX_UNUSED(_nwh);
+
+ wgpuRelease(m_textureView);
+ wgpuRelease(m_msaaTextureView);
+ wgpuRelease(m_depthStencilView);
+ configure(_resolution);
+ }
+
+#if BX_PLATFORM_OSX || BX_PLATFORM_IOS || BX_PLATFORM_VISIONOS
+ CAMetalLayer* toMetalLayer(void* _nwh)
+ {
+ if (NULL == _nwh)
+ {
+ return NULL;
+ }
+
+ if (NULL != NSClassFromString(@"MTKView") )
+ {
+ MTKView* view = (MTKView*)_nwh;
+
+ if (NULL != view
+ && [view isKindOfClass:NSClassFromString(@"MTKView")])
+ {
+ return (CAMetalLayer*)view.layer;
+ }
+ }
+
+ if (NULL != NSClassFromString(@"CAMetalLayer") )
+ {
+ NSObject* nwh = (NSObject*)_nwh;
+
+ if ([nwh isKindOfClass:[CAMetalLayer class]])
+ {
+ return (CAMetalLayer*)nwh;
+ }
+ else
+ {
+# if BX_PLATFORM_OSX
+ __block NSView* contentView = NULL;
+ __block CAMetalLayer* metalLayer = NULL;
+
+ if ([nwh isKindOfClass:[NSView class]])
+ {
+ contentView = (NSView*)nwh;
+ }
+ else if ([nwh isKindOfClass:[NSWindow class]])
+ {
+ NSWindow* nsWindow = (NSWindow*)nwh;
+ contentView = [nsWindow contentView];
+ }
+ else
+ {
+ return NULL;
+ }
+
+ void (^setLayer)() =
+ ^{
+ CALayer* layer = contentView.layer;
+
+ if (NULL != layer
+ && [layer isKindOfClass:NSClassFromString(@"CAMetalLayer")])
+ {
+ metalLayer = (CAMetalLayer*)layer;
+ }
+ else
+ {
+ [contentView setWantsLayer: YES];
+ metalLayer = [CAMetalLayer layer];
+ [contentView setLayer:metalLayer];
+ }
+ };
+
+ if ([NSThread isMainThread])
+ {
+ setLayer();
+ }
+ else
+ {
+ bx::Semaphore semaphore;
+ bx::Semaphore* psemaphore = &semaphore;
+
+ CFRunLoopPerformBlock([[NSRunLoop mainRunLoop] getCFRunLoop], kCFRunLoopCommonModes,
+ ^{
+ setLayer();
+ psemaphore->post();
+ });
+
+ semaphore.wait();
+ }
+
+ return metalLayer;
+# endif // BX_PLATFORM_*
+ }
+ }
+
+ return NULL;
+ }
+#endif // BX_PLATFORM_OSX || BX_PLATFORM_IOS || BX_PLATFORM_TVOS || BX_PLATFORM_VISIONOS
+
+#if BX_PLATFORM_WINDOWS
+extern "C" void* __stdcall GetModuleHandleA(const char* _moduleName);
+#endif // BX_PLATFORM_WINDOWS
+
+ bool SwapChainWGPU::createSurface(void* _nwh)
+ {
+ m_nwh = _nwh;
+
+ WGPUSurfaceDescriptor surfaceDesc = WGPU_SURFACE_DESCRIPTOR_INIT;
+
+#if BX_PLATFORM_WINDOWS
+ WGPUSurfaceSourceWindowsHWND surfaceSource =
+ {
+ .chain =
+ {
+ .next = NULL,
+ .sType = WGPUSType_SurfaceSourceWindowsHWND,
+ },
+ .hinstance = GetModuleHandleA(NULL),
+ .hwnd = m_nwh,
+ };
+
+ surfaceDesc =
+ {
+ .nextInChain = &surfaceSource.chain,
+ .label = toWGPUStringView("SwapChainWGPU"),
+ };
+#elif BX_PLATFORM_LINUX
+ WGPUSurfaceSourceXlibWindow sufraceSourceXlib =
+ {
+ .chain =
+ {
+ .next = NULL,
+ .sType = WGPUSType_SurfaceSourceXlibWindow,
+ },
+ .display = g_platformData.ndt,
+ .window = uint64_t(m_nwh),
+ };
+
+ WGPUSurfaceSourceWaylandSurface sufraceSourceWayland =
+ {
+ .chain =
+ {
+ .next = NULL,
+ .sType = WGPUSType_SurfaceSourceWaylandSurface,
+ },
+ .display = g_platformData.ndt,
+ .surface = m_nwh,
+ };
+
+ surfaceDesc =
+ {
+ .nextInChain = g_platformData.type == bgfx::NativeWindowHandleType::Wayland
+ ? &sufraceSourceWayland.chain
+ : &sufraceSourceXlib.chain
+ ,
+ .label = toWGPUStringView("SwapChainWGPU"),
+ };
+#elif BX_PLATFORM_OSX
+ WGPUSurfaceSourceMetalLayer surfaceSource =
+ {
+ .chain =
+ {
+ .next = NULL,
+ .sType = WGPUSType_SurfaceSourceMetalLayer,
+ },
+ .layer = toMetalLayer(m_nwh),
+ };
+
+ surfaceDesc =
+ {
+ .nextInChain = &surfaceSource.chain,
+ .label = toWGPUStringView("SwapChainWGPU"),
+ };
+#else
+# error "Figure out WGPU surface..."
+#endif // BX_PLATFORM_*
+
+ m_surface = WGPU_CHECK(wgpuInstanceCreateSurface(s_renderWGPU->m_instance, &surfaceDesc) );
+
+ return NULL != m_surface;
+ }
+
+ void SwapChainWGPU::present()
+ {
+ wgpuRelease(m_textureView);
+ WGPU_CHECK(wgpuSurfacePresent(m_surface) );
+
+ WGPUSurfaceTexture surfaceTexture = WGPU_SURFACE_TEXTURE_INIT;
+ wgpuSurfaceGetCurrentTexture(m_surface, &surfaceTexture);
+
+ switch (surfaceTexture.status)
+ {
+ case WGPUSurfaceGetCurrentTextureStatus_SuccessOptimal:
+ case WGPUSurfaceGetCurrentTextureStatus_SuccessSuboptimal:
+ break;
+
+ case WGPUSurfaceGetCurrentTextureStatus_Timeout:
+ case WGPUSurfaceGetCurrentTextureStatus_Outdated:
+ case WGPUSurfaceGetCurrentTextureStatus_Lost:
+// wgpuTextureRelease(surfaceTexture.texture);
+// break;
+
+ case WGPUSurfaceGetCurrentTextureStatus_Error:
+// BX_ASSERT(false, "");
+ break;
+
+ default:
+ break;
+ }
+
+ m_textureView = WGPU_CHECK(wgpuTextureCreateView(surfaceTexture.texture, NULL) );
+ wgpuRelease(surfaceTexture.texture);
+ }
+
+ void FrameBufferWGPU::create(uint8_t _num, const Attachment* _attachment)
+ {
+ m_numAttachments = _num;
+ bx::memCopy(m_attachment, _attachment, sizeof(Attachment) * _num);
+
+ postReset();
+ }
+
+ bool FrameBufferWGPU::create(uint16_t _denseIdx, void* _nwh, uint32_t _width, uint32_t _height, TextureFormat::Enum _colorFormat, TextureFormat::Enum _depthFormat)
+ {
+ bool result = true;
+
+ Resolution resolution = s_renderWGPU->m_resolution;
+ resolution.formatColor = TextureFormat::Count == _colorFormat ? resolution.formatColor : _colorFormat;
+ resolution.formatDepthStencil = TextureFormat::Count == _depthFormat ? resolution.formatDepthStencil : _depthFormat;
+ resolution.width = _width;
+ resolution.height = _height;
+
+ m_width = bx::max(resolution.width, 1);
+ m_height = bx::max(resolution.height, 1);
+
+ if (_denseIdx != UINT16_MAX)
+ {
+ resolution.reset &= ~BGFX_RESET_MSAA_MASK;
+ }
+
+ result = m_swapChain.create(_nwh, resolution);
+ m_formatDepthStencil = m_swapChain.m_formatDepthStencil;
+
+ m_denseIdx = _denseIdx;
+
+ return result;
+ }
+
+ uint16_t FrameBufferWGPU::destroy()
+ {
+ preReset();
+
+ if (isSwapChain() )
+ {
+ m_swapChain.destroy();
+ m_needPresent = false;
+ }
+
+ m_numAttachments = 0;
+ m_numColorAttachments = 0;
+ m_depth = BGFX_INVALID_HANDLE;
+
+ m_needResolve = false;
+
+ uint16_t denseIdx = m_denseIdx;
+ m_denseIdx = UINT16_MAX;
+ return denseIdx;
+ }
+
+ void FrameBufferWGPU::preReset()
+ {
+ for (uint8_t ii = 0; ii < m_numColorAttachments; ++ii)
+ {
+ wgpuRelease(m_textureView[ii]);
+ }
+
+ wgpuRelease(m_depthStencilView);
+ }
+
+ void FrameBufferWGPU::postReset()
+ {
+ if (0 < m_numAttachments)
+ {
+ m_depth = BGFX_INVALID_HANDLE;
+ m_numColorAttachments = 0;
+
+ const TextureWGPU& firstTexture = s_renderWGPU->m_textures[m_attachment[0].handle.idx];
+ m_width = bx::max(firstTexture.m_width >> m_attachment[0].mip, 1);
+ m_height = bx::max(firstTexture.m_height >> m_attachment[0].mip, 1);
+
+ for (uint8_t ii = 0; ii < m_numAttachments; ++ii)
+ {
+ const Attachment& at = m_attachment[ii];
+ const TextureWGPU& texture = s_renderWGPU->m_textures[at.handle.idx];
+
+ WGPUTextureViewDescriptor textureViewDesc =
+ {
+ .nextInChain = NULL,
+ .label = WGPU_STRING_VIEW_INIT,
+ .format = s_textureFormat[texture.m_textureFormat].m_fmt,
+ .dimension = at.numLayers > 1 ? WGPUTextureViewDimension_2DArray : WGPUTextureViewDimension_2D,
+ .baseMipLevel = at.mip,
+ .mipLevelCount = 1,
+ .baseArrayLayer = at.layer,
+ .arrayLayerCount = at.numLayers,
+ .aspect = WGPUTextureAspect_All,
+ .usage = WGPUTextureUsage_RenderAttachment,
+ };
+
+ if (bimg::isDepth(bimg::TextureFormat::Enum(texture.m_textureFormat) ) )
+ {
+ m_depthStencilView = WGPU_CHECK(wgpuTextureCreateView(texture.m_texture, &textureViewDesc) );
+ m_formatDepthStencil = texture.m_textureFormat;
+ m_depth = at.handle;
+ }
+ else
+ {
+ m_textureView[m_numColorAttachments] = WGPU_CHECK(wgpuTextureCreateView(texture.m_texture, &textureViewDesc) );
+ m_texture[m_numColorAttachments] = at.handle;
+ m_numColorAttachments++;
+ }
+ }
+ }
+ }
+
+ void FrameBufferWGPU::update(const Resolution& _resolution)
+ {
+ m_swapChain.update(m_swapChain.m_nwh, _resolution);
+ m_width = _resolution.width;
+ m_height = _resolution.height;
+ m_formatDepthStencil = m_swapChain.m_formatDepthStencil;
+ }
+
+ void FrameBufferWGPU::present()
+ {
+ if (m_needPresent)
+ {
+ m_swapChain.present();
+ m_needPresent = false;
+ }
+ }
+
+ void CommandQueueWGPU::init(WGPUDevice _device)
+ {
+ m_currentFrameInFlight = 0;
+ m_queue = WGPU_CHECK(wgpuDeviceGetQueue(_device) );
+ m_commandEncoder = WGPU_CHECK(wgpuDeviceCreateCommandEncoder(_device, NULL) );
+ }
+
+ void CommandQueueWGPU::shutdown()
+ {
+ wgpuRelease(m_queue);
+ }
+
+ static void queueWorkDoneCb(
+ WGPUQueueWorkDoneStatus _status
+ , WGPUStringView _message
+ , void* _userdata1
+ , void* _userdata2
+ )
+ {
+// BX_ASSERT(WGPUQueueWorkDoneStatus_Success == _status, "%d", _status);
+ BX_UNUSED(_status, _message, _userdata1, _userdata2);
+ s_renderWGPU->m_cmd.m_counter--;
+ }
+
+ WGPUCommandEncoder CommandQueueWGPU::alloc()
+ {
+ s_renderWGPU->m_uniformScratchBuffer.flush();
+
+ kick();
+
+ return m_commandEncoder;
+ }
+
+ void CommandQueueWGPU::kick()
+ {
+ WGPUCommandBuffer commandBuffer = WGPU_CHECK(wgpuCommandEncoderFinish(m_commandEncoder, NULL) );
+
+ WGPU_CHECK(wgpuQueueSubmit(m_queue, 1, &commandBuffer) );
+ WGPU_CHECK(wgpuQueueOnSubmittedWorkDone(
+ m_queue
+ , {
+ .nextInChain = NULL,
+ .mode = WGPUCallbackMode_AllowProcessEvents,
+ .callback = queueWorkDoneCb,
+ .userdata1 = (void*)uintptr_t(m_counter),
+ .userdata2 = NULL,
+ }) );
+ wgpuRelease(commandBuffer);
+ wgpuRelease(m_commandEncoder);
+ ++m_counter;
+
+ WGPU_CHECK(wgpuInstanceProcessEvents(s_renderWGPU->m_instance) );
+
+ m_commandEncoder = WGPU_CHECK(wgpuDeviceCreateCommandEncoder(s_renderWGPU->m_device, NULL) );
+ }
+
+ void CommandQueueWGPU::wait()
+ {
+ while (0 < m_counter)
+ {
+ WGPU_CHECK(wgpuInstanceProcessEvents(s_renderWGPU->m_instance) );
+ }
+ }
+
+ void CommandQueueWGPU::frame()
+ {
+ kick();
+
+ m_currentFrameInFlight = (m_currentFrameInFlight + 1) % s_renderWGPU->m_maxFrameLatency;
+ }
+
+ void CommandQueueWGPU::writeBuffer(WGPUBuffer _buffer, uint64_t _bufferOffset, const void* _data, size_t _size) const
+ {
+ WGPU_CHECK(wgpuQueueWriteBuffer(m_queue, _buffer, _bufferOffset, _data, _size) );
+ }
+
+ void CommandQueueWGPU::writeTexture(const WGPUTexelCopyTextureInfo& _destination, const void* _data, size_t _size, const WGPUTexelCopyBufferLayout& _source, const WGPUExtent3D& _writeSize) const
+ {
+ WGPU_CHECK(wgpuQueueWriteTexture(m_queue, &_destination, _data, _size, &_source, &_writeSize) );
+ }
+
+ void CommandQueueWGPU::copyBufferToBuffer(WGPUBuffer _source, uint64_t _sourceOffset, WGPUBuffer _destination, uint64_t _destinationOffset, uint64_t _size)
+ {
+ WGPU_CHECK(wgpuCommandEncoderCopyBufferToBuffer(m_commandEncoder, _source, _sourceOffset, _destination, _destinationOffset, _size) );
+ }
+
+ void CommandQueueWGPU::copyBufferToTexture(const WGPUTexelCopyBufferInfo& _source, const WGPUTexelCopyTextureInfo& _destination, const WGPUExtent3D& _copySize)
+ {
+ WGPU_CHECK(wgpuCommandEncoderCopyBufferToTexture(m_commandEncoder, &_source, &_destination, &_copySize) );
+ }
+
+ void CommandQueueWGPU::copyTextureToBuffer(const WGPUTexelCopyTextureInfo& _source, const WGPUTexelCopyBufferInfo& _destination, const WGPUExtent3D& _copySize)
+ {
+ WGPU_CHECK(wgpuCommandEncoderCopyTextureToBuffer(m_commandEncoder, &_source, &_destination, &_copySize) );
+ }
+
+ void CommandQueueWGPU::copyTextureToTexture(const WGPUTexelCopyTextureInfo& _source, const WGPUTexelCopyTextureInfo& _destination, const WGPUExtent3D& _copySize)
+ {
+ WGPU_CHECK(wgpuCommandEncoderCopyTextureToTexture(m_commandEncoder, &_source, &_destination, &_copySize) );
+ }
+
+ void TimerQueryWGPU::init()
+ {
+ WGPUDevice device = s_renderWGPU->m_device;
+
+ static constexpr uint32_t kCount = BX_COUNTOF(m_query);
+
+ WGPUQuerySetDescriptor querySetDesc =
+ {
+ .nextInChain = NULL,
+ .label = toWGPUStringView("TimerQuery"),
+ .type = WGPUQueryType_Timestamp,
+ .count = kCount,
+ };
+
+ m_querySet = WGPU_CHECK(wgpuDeviceCreateQuerySet(device, &querySetDesc) );
+
+ static constexpr uint64_t kTimestampBufferSize = kCount * sizeof(uint64_t);
+
+ WGPUBufferDescriptor resolveBufferDesc =
+ {
+ .nextInChain = NULL,
+ .label = toWGPUStringView("TimerQuery - Resolve Buffer"),
+ .usage = 0
+ | WGPUBufferUsage_CopySrc
+ | WGPUBufferUsage_QueryResolve
+ ,
+ .size = kTimestampBufferSize,
+ .mappedAtCreation = false,
+ };
+
+ m_resolve = WGPU_CHECK(wgpuDeviceCreateBuffer(device, &resolveBufferDesc) );
+
+ WGPUBufferDescriptor readbackBufferDesc =
+ {
+ .nextInChain = NULL,
+ .label = toWGPUStringView("TimerQuery - Readback Buffer"),
+ .usage = 0
+ | WGPUBufferUsage_MapRead
+ | WGPUBufferUsage_CopyDst
+ ,
+ .size = kTimestampBufferSize,
+ .mappedAtCreation = false,
+ };
+
+ m_readback = WGPU_CHECK(wgpuDeviceCreateBuffer(device, &readbackBufferDesc) );
+ }
+
+ void TimerQueryWGPU::shutdown()
+ {
+ wgpuRelease(m_querySet);
+ wgpuRelease(m_resolve);
+ wgpuRelease(m_readback);
+ }
+
+ uint32_t TimerQueryWGPU::begin(uint32_t _resultIdx, uint32_t _frameNum)
+ {
+ const uint32_t reserved = m_control.reserve(1);
+
+ if (1 == reserved)
+ {
+ Result& result = m_result[_resultIdx];
+ ++result.m_pending;
+
+ const uint32_t idx = m_control.m_current;
+ Query& query = m_query[idx];
+ query.m_resultIdx = _resultIdx;
+ query.m_ready = false;
+ query.m_frameNum = _frameNum;
+
+ const uint32_t offset = idx * 2 + 0;
+ WGPU_CHECK(wgpuCommandEncoderWriteTimestamp(s_renderWGPU->m_cmd.m_commandEncoder, m_querySet, offset) );
+
+ return idx;
+ }
+
+ return UINT32_MAX;
+ }
+
+ void TimerQueryWGPU::end(uint32_t _idx)
+ {
+ m_control.commit(1);
+
+ Query& query = m_query[_idx];
+ query.m_ready = true;
+ query.m_fence = s_renderWGPU->m_cmd.m_counter;
+
+ const uint32_t offset = _idx * 2 + 1;
+ WGPU_CHECK(wgpuCommandEncoderWriteTimestamp(s_renderWGPU->m_cmd.m_commandEncoder, m_querySet, offset) );
+
+ m_control.consume(1);
+ }
+
+ void OcclusionQueryWGPU::init()
+ {
+ WGPUDevice device = s_renderWGPU->m_device;
+
+ static constexpr uint32_t kCount = BX_COUNTOF(m_handle);
+
+ WGPUQuerySetDescriptor querySetDesc =
+ {
+ .nextInChain = NULL,
+ .label = toWGPUStringView("OcclusionQuery"),
+ .type = WGPUQueryType_Occlusion,
+ .count = kCount,
+ };
+
+ m_querySet = WGPU_CHECK(wgpuDeviceCreateQuerySet(device, &querySetDesc) );
+
+ static constexpr uint64_t kOcclusionQueryBufferSize = kCount * sizeof(uint64_t);
+
+ WGPUBufferDescriptor resolveBufferDesc =
+ {
+ .nextInChain = NULL,
+ .label = toWGPUStringView("OcclusionQuery - Resolve Buffer"),
+ .usage = 0
+ | WGPUBufferUsage_CopySrc
+ | WGPUBufferUsage_QueryResolve
+ ,
+ .size = kOcclusionQueryBufferSize,
+ .mappedAtCreation = false,
+ };
+
+ m_resolve = WGPU_CHECK(wgpuDeviceCreateBuffer(device, &resolveBufferDesc) );
+
+ WGPUBufferDescriptor readbackBufferDesc =
+ {
+ .nextInChain = NULL,
+ .label = toWGPUStringView("OcclusionQuery - Readback Buffer"),
+ .usage = 0
+ | WGPUBufferUsage_MapRead
+ | WGPUBufferUsage_CopyDst
+ ,
+ .size = kOcclusionQueryBufferSize,
+ .mappedAtCreation = false,
+ };
+
+ m_readback = WGPU_CHECK(wgpuDeviceCreateBuffer(device, &readbackBufferDesc) );
+ }
+
+ void OcclusionQueryWGPU::shutdown()
+ {
+ wgpuRelease(m_querySet);
+ wgpuRelease(m_resolve);
+ wgpuRelease(m_readback);
+ }
+
+ void OcclusionQueryWGPU::begin(WGPURenderPassEncoder _renderPassEncoder, OcclusionQueryHandle _handle)
+ {
+ const uint32_t reserved = m_control.reserve(1);
+
+ if (1 == reserved)
+ {
+ m_handle[m_control.m_current] = _handle;
+ WGPU_CHECK(wgpuRenderPassEncoderBeginOcclusionQuery(_renderPassEncoder, _handle.idx) );
+ }
+ }
+
+ void OcclusionQueryWGPU::end(WGPURenderPassEncoder _renderPassEncoder)
+ {
+ if (1 == m_control.getNumReserved() )
+ {
+ WGPU_CHECK(wgpuRenderPassEncoderEndOcclusionQuery(_renderPassEncoder) );
+
+ m_control.commit(1);
+ }
+ }
+
+ void OcclusionQueryWGPU::resolve()
+ {
+ if (0 < m_control.getNumUsed() )
+ {
+ WGPUCommandEncoder commandEncoder = s_renderWGPU->m_cmd.m_commandEncoder;
+
+ constexpr uint64_t kOcclusionQueryBufferSize = BGFX_CONFIG_MAX_OCCLUSION_QUERIES * sizeof(uint64_t);
+
+ WGPU_CHECK(wgpuCommandEncoderResolveQuerySet(
+ commandEncoder
+ , m_querySet
+ , 0
+ , BGFX_CONFIG_MAX_OCCLUSION_QUERIES
+ , m_resolve
+ , 0
+ ) );
+
+ WGPU_CHECK(wgpuCommandEncoderCopyBufferToBuffer(
+ commandEncoder
+ , m_resolve
+ , 0
+ , m_readback
+ , 0
+ , kOcclusionQueryBufferSize
+ ) );
+ }
+ }
+
+ static void readQueryResultsCb(WGPUMapAsyncStatus _status, WGPUStringView _message, void* _userdata1, void* _userdata2)
+ {
+ BX_UNUSED(_status, _message);
+ OcclusionQueryWGPU& occlusionQuery = *(OcclusionQueryWGPU*)_userdata1;
+ occlusionQuery.consumeResults( (Frame*)_userdata2);
+ }
+
+ void OcclusionQueryWGPU::readResultsAsync(Frame* _frame)
+ {
+ if (0 < m_control.getNumUsed() )
+ {
+ constexpr uint64_t kOcclusionQueryBufferSize = BGFX_CONFIG_MAX_OCCLUSION_QUERIES * sizeof(uint64_t);
+
+ WGPU_CHECK(wgpuBufferMapAsync(
+ m_readback
+ , WGPUMapMode_Read
+ , 0
+ , kOcclusionQueryBufferSize
+ , {
+ .nextInChain = NULL,
+ .mode = WGPUCallbackMode_AllowProcessEvents,
+ .callback = readQueryResultsCb,
+ .userdata1 = this,
+ .userdata2 = _frame,
+ }) );
+ }
+ }
+
+ void OcclusionQueryWGPU::consumeResults(Frame* _frame)
+ {
+ constexpr uint64_t kOcclusionQueryBufferSize = BGFX_CONFIG_MAX_OCCLUSION_QUERIES * sizeof(uint64_t);
+
+ const uint64_t* result = (const uint64_t*)WGPU_CHECK(wgpuBufferGetConstMappedRange(
+ m_readback
+ , 0
+ , kOcclusionQueryBufferSize
+ ) );
+
+ while (0 < m_control.getNumUsed() )
+ {
+ OcclusionQueryHandle handle = m_handle[m_control.m_read];
+ if (isValid(handle) )
+ {
+ _frame->m_occlusion[handle.idx] = int32_t(result[handle.idx]);
+ }
+
+ m_control.consume(1);
+ }
+
+ WGPU_CHECK(wgpuBufferUnmap(m_readback) );
+ }
+
+ void OcclusionQueryWGPU::invalidate(OcclusionQueryHandle _handle)
+ {
+ const uint32_t size = m_control.m_size;
+
+ for (uint32_t ii = 0, num = m_control.getNumUsed(); ii < num; ++ii)
+ {
+ OcclusionQueryHandle& handle = m_handle[(m_control.m_read + ii) % size];
+ if (handle.idx == _handle.idx)
+ {
+ handle.idx = bgfx::kInvalidHandle;
+ }
+ }
+ }
+
+ void RendererContextWGPU::submitBlit(BlitState& _bs, uint16_t _view)
+ {
+ while (_bs.hasItem(_view) )
+ {
+ const BlitItem& blit = _bs.advance();
+
+ const TextureWGPU& src = m_textures[blit.m_src.idx];
+ const TextureWGPU& dst = m_textures[blit.m_dst.idx];
+
+ s_renderWGPU->m_cmd.copyTextureToTexture(
+ {
+ .texture = src.m_texture,
+ .mipLevel = blit.m_srcMip,
+ .origin =
+ {
+ .x = blit.m_srcX,
+ .y = blit.m_srcY,
+ .z = blit.m_srcZ,
+ },
+ .aspect = WGPUTextureAspect_All,
+ },
+ {
+ .texture = dst.m_texture,
+ .mipLevel = blit.m_dstMip,
+ .origin =
+ {
+ .x = blit.m_dstX,
+ .y = blit.m_dstY,
+ .z = blit.m_dstZ,
+ },
+ .aspect = WGPUTextureAspect_All,
+ },
+ {
+ .width = blit.m_width,
+ .height = blit.m_height,
+ .depthOrArrayLayers = bx::max(1, blit.m_depth),
+ }
+ );
+ }
+ }
+
+ void RendererContextWGPU::submitUniformCache(UniformCacheState& _ucs, uint16_t _view)
+ {
+ while (_ucs.hasItem(_view) )
+ {
+ const UniformCacheItem& uci = _ucs.advance();
+
+ bx::memCopy(m_uniforms[uci.m_handle], &_ucs.m_frame->m_uniformCacheFrame.m_data[uci.m_offset], uci.m_size);
+ }
+ }
+
+ void RendererContextWGPU::submit(Frame* _render, ClearQuad& _clearQuad, TextVideoMemBlitter& _textVideoMemBlitter)
+ {
+ m_occlusionQuery.readResultsAsync(_render);
+ WGPU_CHECK(wgpuInstanceProcessEvents(s_renderWGPU->m_instance) );
+
+ if (updateResolution(_render->m_resolution) )
+ {
+ return;
+ }
+
+ if (_render->m_capture)
+ {
+ renderDocTriggerCapture();
+ }
+
+ BGFX_WGPU_PROFILER_BEGIN_LITERAL("rendererSubmit", kColorFrame);
+
+ const int64_t timeBegin = bx::getHPCounter();
+ int64_t captureElapsed = 0;
+
+ uint32_t frameQueryIdx = UINT32_MAX;
+
+ frameQueryIdx = m_gpuTimer.begin(BGFX_CONFIG_MAX_VIEWS, _render->m_frameNum);
+
+ if (0 < _render->m_iboffset)
+ {
+ BGFX_PROFILER_SCOPE("bgfx/Update transient index buffer", kColorResource);
+
+ TransientIndexBuffer* ib = _render->m_transientIb;
+ m_indexBuffers[ib->handle.idx].update(0, _render->m_iboffset, ib->data);
+ }
+
+ if (0 < _render->m_vboffset)
+ {
+ BGFX_PROFILER_SCOPE("bgfx/Update transient vertex buffer", kColorResource);
+
+ TransientVertexBuffer* vb = _render->m_transientVb;
+ m_vertexBuffers[vb->handle.idx].update(0, _render->m_vboffset, vb->data);
+ }
+
+ _render->sort();
+
+ m_cmd.wait();
+
+ RenderDraw currentState;
+ currentState.clear();
+ currentState.m_stateFlags = BGFX_STATE_NONE;
+ currentState.m_stencil = packStencil(BGFX_STENCIL_NONE, BGFX_STENCIL_NONE);
+
+ uint32_t currentNumVertices = 0;
+
+ static ViewState viewState;
+ viewState.reset(_render);
+
+ const bool wireframe = !!(_render->m_debug&BGFX_DEBUG_WIREFRAME);
+ setDebugWireframe(wireframe);
+
+ ProgramHandle currentProgram = BGFX_INVALID_HANDLE;
+ bool hasPredefined = false;
+ SortKey key;
+ uint16_t view = UINT16_MAX;
+ FrameBufferHandle fbh = { BGFX_CONFIG_MAX_FRAME_BUFFERS };
+
+ UniformCacheState ucs(_render);
+ BlitState bs(_render);
+
+ uint64_t blendFactor = 0;
+
+ const uint64_t primType = _render->m_debug&BGFX_DEBUG_WIREFRAME ? BGFX_STATE_PT_LINES : 0;
+ uint8_t primIndex = uint8_t(primType >> BGFX_STATE_PT_SHIFT);
+ PrimInfo prim = s_primInfo[primIndex];
+
+ bool viewHasScissor = false;
+ bool restoreScissor = false;
+ Rect viewScissorRect;
+ viewScissorRect.clear();
+
+ bool isFrameBufferValid = true;
+
+ uint32_t statsNumPrimsSubmitted[BX_COUNTOF(s_primInfo)] = {};
+ uint32_t statsNumPrimsRendered[BX_COUNTOF(s_primInfo)] = {};
+ uint32_t statsNumInstances[BX_COUNTOF(s_primInfo)] = {};
+ uint32_t statsNumDrawIndirect[BX_COUNTOF(s_primInfo)] = {};
+ uint32_t statsNumIndices = 0;
+ uint32_t statsKeyType[2] = {};
+
+ m_uniformScratchBuffer.begin();
+
+ WGPURenderPassEncoder renderPassEncoder = NULL;
+ WGPUComputePassEncoder computePassEncoder = NULL;
+
+ WGPUBindGroupLayout bindGroupLayout = NULL;
+
+ Profiler profiler(
+ _render
+ , m_gpuTimer
+ , s_viewName
+ , true
+ );
+
+ StateCacheLru bindGroupLru;
+
+ uint32_t msaaCount = 1;
+
+ if (0 == (_render->m_debug&BGFX_DEBUG_IFH) )
+ {
+ viewState.m_rect = _render->m_view[0].m_rect;
+
+ int32_t numItems = _render->m_numRenderItems;
+ for (int32_t item = 0; item < numItems;)
+ {
+ const uint64_t encodedKey = _render->m_sortKeys[item];
+ const bool isCompute = key.decode(encodedKey, _render->m_viewRemap);
+ statsKeyType[isCompute]++;
+
+ const bool viewChanged = 0
+ || key.m_view != view
+ || item == numItems
+ ;
+
+ const uint32_t itemIdx = _render->m_sortValues[item];
+ const RenderItem& renderItem = _render->m_renderItem[itemIdx];
+ const RenderBind& renderBind = _render->m_renderItemBind[itemIdx];
+ ++item;
+
+ if (viewChanged)
+ {
+ view = key.m_view;
+ currentProgram = BGFX_INVALID_HANDLE;
+ currentState.clear();
+ hasPredefined = false;
+
+ if (_render->m_view[view].m_fbh.idx != fbh.idx)
+ {
+ if (NULL != renderPassEncoder)
+ {
+ WGPU_CHECK(wgpuRenderPassEncoderEnd(renderPassEncoder) );
+ wgpuRelease(renderPassEncoder);
+ }
+
+ fbh = _render->m_view[view].m_fbh;
+ }
+ }
+
+ if (!isCompute
+ && (viewChanged || NULL != computePassEncoder) )
+ {
+ if (NULL != computePassEncoder)
+ {
+ WGPU_CHECK(wgpuComputePassEncoderEnd(computePassEncoder) );
+ wgpuRelease(computePassEncoder);
+ }
+
+ if (NULL != renderPassEncoder)
+ {
+ WGPU_CHECK(wgpuRenderPassEncoderEnd(renderPassEncoder) );
+ wgpuRelease(renderPassEncoder);
+ }
+
+ if (item > 1)
+ {
+ profiler.end();
+ }
+
+ submitUniformCache(ucs, view);
+ submitBlit(bs, view);
+
+ BGFX_WGPU_PROFILER_END();
+ setViewType(view, " ");
+ BGFX_WGPU_PROFILER_BEGIN(view, kColorView);
+
+ profiler.begin(view);
+
+ FrameBufferWGPU& fb = isValid(fbh)
+ ? m_frameBuffers[fbh.idx]
+ : m_backBuffer
+ ;
+
+ const bool isSwapChain = fb.isSwapChain();
+
+ if (isSwapChain)
+ {
+ fb.m_needPresent = true;
+ }
+
+ WGPUTextureView depthStencilTextureView = isSwapChain
+ ? fb.m_swapChain.m_depthStencilView
+ : fb.m_depthStencilView
+ ;
+
+ viewState.m_rect = _render->m_view[view].m_rect;
+ Rect viewRect = _render->m_view[view].m_rect;
+ Rect scissorRect = _render->m_view[view].m_scissor;
+
+ const Rect fbRect(0, 0, bx::narrowCast(fb.m_width), bx::narrowCast(fb.m_height) );
+ viewRect.intersect(fbRect);
+ scissorRect.intersect(fbRect);
+
+ viewHasScissor = !scissorRect.isZero();
+ viewScissorRect = viewHasScissor ? scissorRect : viewRect;
+ restoreScissor = false;
+
+ const Clear& clr = _render->m_view[view].m_clear;
+
+ const bool needClear = BGFX_CLEAR_NONE != ( (BGFX_CLEAR_COLOR|BGFX_CLEAR_DEPTH|BGFX_CLEAR_STENCIL) & clr.m_flags);
+ const bool clearWhole = viewRect.isEqual(fbRect);
+
+ WGPURenderPassColorAttachment colorAttachment[BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS];
+
+ const uint32_t numColorAttachments = isSwapChain
+ ? 1
+ : fb.m_numColorAttachments
+ ;
+
+ for (uint32_t ii = 0; ii < numColorAttachments; ++ii)
+ {
+ WGPUTextureView colorTextureView = isSwapChain
+ ? fb.m_swapChain.m_textureView
+ : (0 < fb.m_numColorAttachments ? fb.m_textureView[ii] : NULL)
+ ;
+ WGPUTextureView msaaTextureView = isSwapChain
+ ? fb.m_swapChain.m_msaaTextureView
+ : NULL
+ ;
+
+ if (NULL != msaaTextureView)
+ {
+ bx::swap(colorTextureView, msaaTextureView);
+ msaaCount = 4;
+ }
+ else
+ {
+ msaaCount = 1;
+ }
+
+ colorAttachment[ii] =
+ {
+ .nextInChain = NULL,
+ .view = colorTextureView,
+ .depthSlice = WGPU_DEPTH_SLICE_UNDEFINED,
+ .resolveTarget = msaaTextureView,
+ .loadOp = clearWhole && (BGFX_CLEAR_COLOR & clr.m_flags)
+ ? WGPULoadOp_Clear
+ : WGPULoadOp_Load
+ ,
+ .storeOp = WGPUStoreOp_Store,
+ .clearValue = {},
+ };
+
+ if (0 != (BGFX_CLEAR_COLOR_USE_PALETTE & clr.m_flags) )
+ {
+ uint8_t index = (uint8_t)bx::uint32_min(BGFX_CONFIG_MAX_COLOR_PALETTE-1, clr.m_index[ii]);
+ const float* rgba = _render->m_colorPalette[index];
+ colorAttachment[ii].clearValue =
+ {
+ .r = rgba[0],
+ .g = rgba[1],
+ .b = rgba[2],
+ .a = rgba[3],
+ };
+ }
+ else
+ {
+ colorAttachment[ii].clearValue =
+ {
+ .r = clr.m_index[0] * 1.0/255.0,
+ .g = clr.m_index[1] * 1.0/255.0,
+ .b = clr.m_index[2] * 1.0/255.0,
+ .a = clr.m_index[3] * 1.0/255.0,
+ };
+ }
+ }
+
+ const bool stencilRw = hasStencil(TextureFormat::Enum(fb.m_formatDepthStencil) );
+
+ WGPURenderPassDepthStencilAttachment depthStencilAttachement =
+ {
+ .nextInChain = NULL,
+ .view = depthStencilTextureView,
+ .depthLoadOp = clearWhole && (BGFX_CLEAR_DEPTH & clr.m_flags)
+ ? WGPULoadOp_Clear
+ : WGPULoadOp_Load
+ ,
+ .depthStoreOp = WGPUStoreOp_Store,
+ .depthClearValue = clr.m_depth,
+ .depthReadOnly = false,
+ .stencilLoadOp = !stencilRw
+ ? WGPULoadOp_Undefined
+ : (clearWhole && (BGFX_CLEAR_STENCIL & clr.m_flags) ? WGPULoadOp_Clear : WGPULoadOp_Load)
+ ,
+ .stencilStoreOp = !stencilRw ? WGPUStoreOp_Undefined : WGPUStoreOp_Store,
+ .stencilClearValue = clr.m_stencil,
+ .stencilReadOnly = !stencilRw,
+ };
+
+ WGPURenderPassDescriptor renderPassDesc =
+ {
+ .nextInChain = NULL,
+ .label = toWGPUStringView(s_viewName[view]),
+ .colorAttachmentCount = numColorAttachments,
+ .colorAttachments = colorAttachment,
+ .depthStencilAttachment = NULL == depthStencilTextureView
+ ? NULL
+ : &depthStencilAttachement
+ ,
+ .occlusionQuerySet = m_occlusionQuery.m_querySet,
+ .timestampWrites = NULL,
+ };
+
+ WGPUCommandEncoder cmdEncoder = m_cmd.alloc();
+ renderPassEncoder = WGPU_CHECK(wgpuCommandEncoderBeginRenderPass(cmdEncoder, &renderPassDesc) );
+
+ wgpuRenderPassEncoderSetViewport(
+ renderPassEncoder
+ , float(viewRect.m_x)
+ , float(viewRect.m_y)
+ , float(viewRect.m_width)
+ , float(viewRect.m_height)
+ , 0.0f
+ , 1.0f
+ );
+
+ if (!clearWhole && needClear)
+ {
+ clearQuad(renderPassEncoder, fbh, msaaCount, _clearQuad, viewRect, clr, _render->m_colorPalette);
+ }
+
+ wgpuRenderPassEncoderSetScissorRect(
+ renderPassEncoder
+ , viewScissorRect.m_x
+ , viewScissorRect.m_y
+ , viewScissorRect.m_width
+ , viewScissorRect.m_height
+ );
+ restoreScissor = false;
+ }
+
+ if (isCompute)
+ {
+ if (NULL == computePassEncoder)
+ {
+ BGFX_WGPU_PROFILER_END();
+ setViewType(view, "C");
+ BGFX_WGPU_PROFILER_BEGIN(view, kColorCompute);
+
+ if (NULL != renderPassEncoder)
+ {
+ WGPU_CHECK(wgpuRenderPassEncoderEnd(renderPassEncoder) );
+ wgpuRelease(renderPassEncoder);
+ }
+
+ WGPUCommandEncoder cmdEncoder = m_cmd.alloc();
+ computePassEncoder = WGPU_CHECK(wgpuCommandEncoderBeginComputePass(cmdEncoder, NULL) );
+ }
+
+ const RenderCompute& compute = renderItem.compute;
+
+ bool programChanged = false;
+ bool constantsChanged = compute.m_uniformBegin < compute.m_uniformEnd;
+ rendererUpdateUniforms(this, _render->m_uniformBuffer[compute.m_uniformIdx], compute.m_uniformBegin, compute.m_uniformEnd);
+
+ if (key.m_program.idx != currentProgram.idx)
+ {
+ currentProgram = key.m_program;
+
+ programChanged =
+ constantsChanged = true;
+ }
+
+ const ProgramWGPU& program = m_program[currentProgram.idx];
+
+ if (constantsChanged)
+ {
+ UniformBuffer* vcb = program.m_vsh->m_constantBuffer;
+ if (NULL != vcb)
+ {
+ commit(*vcb);
+ }
+ }
+
+ ChunkedScratchBufferOffset sbo;
+ const uint32_t vsSize = program.m_vsh->m_size;
+
+ if (constantsChanged
+ || hasPredefined)
+ {
+ viewState.setPredefined<4>(this, view, program, _render, compute);
+ m_uniformScratchBuffer.write(sbo, m_vsScratch, vsSize);
+ }
+
+ if (programChanged)
+ {
+ const ComputePipeline& computePipeline = *getPipeline(key.m_program, renderBind);
+ bindGroupLayout = computePipeline.bindGroupLayout;
+ WGPU_CHECK(wgpuComputePassEncoderSetPipeline(computePassEncoder, computePipeline.pipeline) );
+ }
+
+ bx::HashMurmur3 murmur;
+ murmur.begin(0x434f4d50);
+ murmur.add(renderBind.m_bind, sizeof(renderBind.m_bind) );
+ murmur.add(sbo.buffer);
+ murmur.add(vsSize);
+ const uint32_t bindHash = murmur.end();
+
+ const BindGroup* bindGroupCached = bindGroupLru.find(bindHash);
+ if (NULL == bindGroupCached)
+ {
+ const BindGroup bindGroup = createBindGroup(bindGroupLayout, program, renderBind, sbo, true);
+ bindGroupCached = bindGroupLru.add(bindHash, bindGroup, 0);
+ }
+
+ WGPU_CHECK(wgpuComputePassEncoderSetBindGroup(computePassEncoder, 0, bindGroupCached->bindGroup, bindGroupCached->numOffsets, sbo.offsets) );
+
+ if (isValid(compute.m_indirectBuffer) )
+ {
+ const VertexBufferWGPU& indirect = m_vertexBuffers[compute.m_indirectBuffer.idx];
+ const WGPUBuffer buffer = indirect.m_buffer;
+
+ const uint32_t numDrawIndirect = UINT32_MAX == compute.m_numIndirect
+ ? indirect.m_size/BGFX_CONFIG_DRAW_INDIRECT_STRIDE
+ : compute.m_numIndirect
+ ;
+
+ uint32_t args = compute.m_startIndirect * BGFX_CONFIG_DRAW_INDIRECT_STRIDE;
+ for (uint32_t ii = 0; ii < numDrawIndirect; ++ii)
+ {
+ WGPU_CHECK(wgpuComputePassEncoderDispatchWorkgroupsIndirect(computePassEncoder, buffer, args) );
+ args += BGFX_CONFIG_DRAW_INDIRECT_STRIDE;
+ }
+ }
+ else
+ {
+ WGPU_CHECK(wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, compute.m_numX, compute.m_numY, compute.m_numZ) );
+ }
+
+ continue;
+ }
+
+ if (NULL != computePassEncoder)
+ {
+ WGPU_CHECK(wgpuComputePassEncoderEnd(computePassEncoder) );
+ wgpuRelease(computePassEncoder);
+
+ setViewType(view, " ");
+ BGFX_WGPU_PROFILER_END();
+ BGFX_WGPU_PROFILER_BEGIN(view, kColorDraw);
+ }
+
+ const RenderDraw& draw = renderItem.draw;
+
+ const bool hasOcclusionQuery = 0 != (draw.m_stateFlags & BGFX_STATE_INTERNAL_OCCLUSION_QUERY);
+ {
+ const bool occluded = true
+ && isValid(draw.m_occlusionQuery)
+ && !hasOcclusionQuery
+ && !isVisible(_render, draw.m_occlusionQuery, 0 != (draw.m_submitFlags & BGFX_SUBMIT_INTERNAL_OCCLUSION_VISIBLE) )
+ ;
+
+ if (occluded
+ || !isFrameBufferValid
+ || 0 == draw.m_streamMask
+ || _render->m_frameCache.isZeroArea(viewScissorRect, draw.m_scissor) )
+ {
+ continue;
+ }
+ }
+
+ const uint64_t newStencil = draw.m_stencil;
+ uint64_t changedStencil = currentState.m_stencil ^ draw.m_stencil;
+ currentState.m_stencil = newStencil;
+
+ bool programChanged = false;
+ bool constantsChanged = draw.m_uniformBegin < draw.m_uniformEnd;
+ rendererUpdateUniforms(this, _render->m_uniformBuffer[draw.m_uniformIdx], draw.m_uniformBegin, draw.m_uniformEnd);
+
+ currentNumVertices = draw.m_numVertices;
+
+ const uint64_t state = draw.m_stateFlags;
+
+ const RenderPipeline& renderPipeline = *getPipeline(
+ key.m_program
+ , fbh
+ , msaaCount
+ , draw.m_stateFlags
+ , draw.m_stencil
+ , draw.m_streamMask
+ , draw.m_stream
+ , uint8_t(draw.m_instanceDataStride/16)
+ , draw.isIndex16()
+ , renderBind
+ );
+ bindGroupLayout = renderPipeline.bindGroupLayout;
+ WGPU_CHECK(wgpuRenderPassEncoderSetPipeline(renderPassEncoder, renderPipeline.pipeline) );
+
+ const ProgramWGPU& program = m_program[key.m_program.idx];
+
+ if (constantsChanged
+ || currentProgram.idx != key.m_program.idx)
+ {
+ currentProgram = key.m_program;
+
+ UniformBuffer* vcb = program.m_vsh->m_constantBuffer;
+ if (NULL != vcb)
+ {
+ commit(*vcb);
+ }
+
+ if (NULL != program.m_fsh)
+ {
+ UniformBuffer* fcb = program.m_fsh->m_constantBuffer;
+ if (NULL != fcb)
+ {
+ commit(*fcb);
+ }
+ }
+
+ hasPredefined = 0 < program.m_numPredefined;
+ constantsChanged = true;
+ }
+
+ if (constantsChanged
+ || hasPredefined)
+ {
+ const uint32_t ref = (draw.m_stateFlags&BGFX_STATE_ALPHA_REF_MASK)>>BGFX_STATE_ALPHA_REF_SHIFT;
+ viewState.m_alphaRef = ref/255.0f;
+ viewState.setPredefined<4>(this, view, program, _render, draw);
+ }
+
+ ChunkedScratchBufferOffset sbo;
+ const uint32_t vsSize = program.m_vsh->m_size;
+ const uint32_t fsSize = NULL != program.m_fsh ? program.m_fsh->m_size : 0;
+ m_uniformScratchBuffer.write(sbo, m_vsScratch, vsSize, m_fsScratch, fsSize);
+
+ bx::HashMurmur3 murmur;
+ murmur.begin(0x44524157);
+ murmur.add(renderBind.m_bind, sizeof(renderBind.m_bind) );
+ murmur.add(sbo.buffer);
+ murmur.add(vsSize);
+ murmur.add(fsSize);
+ const uint32_t bindHash = murmur.end();
+
+ const BindGroup* bindGroupCached = bindGroupLru.find(bindHash);
+ if (NULL == bindGroupCached)
+ {
+ const BindGroup bind = createBindGroup(bindGroupLayout, program, renderBind, sbo, false);
+ bindGroupCached = bindGroupLru.add(bindHash, bind, 0);
+ }
+
+ WGPU_CHECK(wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupCached->bindGroup, bindGroupCached->numOffsets, sbo.offsets) );
+
+ if (0 != changedStencil)
+ {
+ const uint32_t fstencil = unpackStencil(0, draw.m_stencil);
+ const uint32_t ref = (fstencil&BGFX_STENCIL_FUNC_REF_MASK)>>BGFX_STENCIL_FUNC_REF_SHIFT;
+ WGPU_CHECK(wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, ref) );
+ }
+
+ constexpr uint64_t kF0 = BGFX_STATE_BLEND_FACTOR;
+ constexpr uint64_t kF1 = BGFX_STATE_BLEND_INV_FACTOR;
+ constexpr uint64_t kF2 = BGFX_STATE_BLEND_FACTOR<<4;
+ constexpr uint64_t kF3 = BGFX_STATE_BLEND_INV_FACTOR<<4;
+ bool hasFactor = 0
+ || kF0 == (state & kF0)
+ || kF1 == (state & kF1)
+ || kF2 == (state & kF2)
+ || kF3 == (state & kF3)
+ ;
+
+ if (hasFactor
+ && blendFactor != draw.m_rgba)
+ {
+ blendFactor = draw.m_rgba;
+
+ WGPUColor bf =
+ {
+ .r = ( (draw.m_rgba>>24) )/255.0f,
+ .g = ( (draw.m_rgba>>16)&0xff)/255.0f,
+ .b = ( (draw.m_rgba>> 8)&0xff)/255.0f,
+ .a = ( (draw.m_rgba )&0xff)/255.0f,
+ };
+
+ WGPU_CHECK(wgpuRenderPassEncoderSetBlendConstant(renderPassEncoder, &bf) );
+ }
+
+ const uint16_t scissor = draw.m_scissor;
+ if (currentState.m_scissor != scissor)
+ {
+ currentState.m_scissor = scissor;
+
+ if (UINT16_MAX == scissor)
+ {
+ if (restoreScissor
+ || viewHasScissor)
+ {
+ restoreScissor = false;
+
+ wgpuRenderPassEncoderSetScissorRect(
+ renderPassEncoder
+ , viewScissorRect.m_x
+ , viewScissorRect.m_y
+ , viewScissorRect.m_width
+ , viewScissorRect.m_height
+ );
+ }
+ }
+ else
+ {
+ restoreScissor = true;
+ Rect scissorRect;
+ scissorRect.setIntersect(viewScissorRect, _render->m_frameCache.m_rectCache.m_cache[scissor]);
+ if (scissorRect.isZeroArea() )
+ {
+ continue;
+ }
+
+ wgpuRenderPassEncoderSetScissorRect(
+ renderPassEncoder
+ , scissorRect.m_x
+ , scissorRect.m_y
+ , scissorRect.m_width
+ , scissorRect.m_height
+ );
+ }
+ }
+
+ bool vertexStreamChanged = programChanged || hasVertexStreamChanged(currentState, draw);
+
+ if (vertexStreamChanged)
+ {
+ currentState.m_streamMask = draw.m_streamMask;
+ currentState.m_instanceDataBuffer.idx = draw.m_instanceDataBuffer.idx;
+ currentState.m_instanceDataOffset = draw.m_instanceDataOffset;
+ currentState.m_instanceDataStride = draw.m_instanceDataStride;
+
+ WGPUBuffer buffers[BGFX_CONFIG_MAX_VERTEX_STREAMS + 1 /* instanced buffer */];
+ uint32_t offsets[BGFX_CONFIG_MAX_VERTEX_STREAMS + 1 /* instanced buffer */];
+ uint32_t sizes[BGFX_CONFIG_MAX_VERTEX_STREAMS + 1 /* instanced buffer */];
+
+ uint32_t numVertices = draw.m_numVertices;
+
+ uint32_t numStreams = 0;
+
+ if (UINT8_MAX != draw.m_streamMask)
+ {
+ for (BitMaskToIndexIteratorT it(draw.m_streamMask)
+ ; !it.isDone()
+ ; it.next(), numStreams++
+ )
+ {
+ const uint8_t idx = it.idx;
+
+ currentState.m_stream[idx].m_layoutHandle = draw.m_stream[idx].m_layoutHandle;
+ currentState.m_stream[idx].m_handle = draw.m_stream[idx].m_handle;
+ currentState.m_stream[idx].m_startVertex = draw.m_stream[idx].m_startVertex;
+
+ const uint16_t handle = draw.m_stream[idx].m_handle.idx;
+ const VertexBufferWGPU&vb = m_vertexBuffers[handle];
+ const uint16_t layoutIdx = isValid(draw.m_stream[idx].m_layoutHandle)
+ ? draw.m_stream[idx].m_layoutHandle.idx
+ : vb.m_layoutHandle.idx;
+ const VertexLayout& layout = m_vertexLayouts[layoutIdx];
+ const uint32_t stride = layout.m_stride;
+
+ buffers[numStreams] = vb.m_buffer;
+ offsets[numStreams] = draw.m_stream[idx].m_startVertex * stride;
+
+ numVertices = bx::uint32_min(UINT32_MAX == draw.m_numVertices
+ ? vb.m_size/stride
+ : draw.m_numVertices
+ , numVertices
+ );
+
+ sizes[numStreams] = stride * numVertices;
+ }
+
+ if (isValid(draw.m_instanceDataBuffer) )
+ {
+ const VertexBufferWGPU& inst = m_vertexBuffers[draw.m_instanceDataBuffer.idx];
+
+ buffers[numStreams] = inst.m_buffer;
+ offsets[numStreams] = draw.m_instanceDataOffset;
+ sizes[numStreams] = draw.m_instanceDataStride * draw.m_numInstances;
+
+ ++numStreams;
+ }
+ }
+
+ for (uint8_t ii = 0; ii < numStreams; ++ii)
+ {
+ WGPU_CHECK(wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, ii, buffers[ii], offsets[ii], sizes[ii]) );
+ }
+ }
+
+ if (currentState.m_indexBuffer.idx != draw.m_indexBuffer.idx
+ || currentState.isIndex16() != draw.isIndex16() )
+ {
+ currentState.m_indexBuffer = draw.m_indexBuffer;
+ currentState.m_submitFlags = draw.m_submitFlags;
+
+ uint16_t handle = draw.m_indexBuffer.idx;
+ if (kInvalidHandle != handle)
+ {
+ const IndexBufferWGPU& ib = m_indexBuffers[handle];
+ WGPU_CHECK(wgpuRenderPassEncoderSetIndexBuffer(
+ renderPassEncoder
+ , ib.m_buffer
+ , draw.isIndex16() ? WGPUIndexFormat_Uint16 : WGPUIndexFormat_Uint32
+ , 0
+ , WGPU_WHOLE_SIZE
+ ) );
+ }
+ }
+
+ if (0 != currentState.m_streamMask)
+ {
+ uint32_t numVertices = currentNumVertices;
+ uint32_t numIndices = 0;
+ uint32_t numPrimsSubmitted = 0;
+ uint32_t numInstances = 0;
+ uint32_t numPrimsRendered = 0;
+ uint32_t numDrawIndirect = 0;
+
+ if (hasOcclusionQuery)
+ {
+ m_occlusionQuery.begin(renderPassEncoder, draw.m_occlusionQuery);
+ }
+
+ if (isValid(draw.m_indirectBuffer) )
+ {
+ const VertexBufferWGPU& indirect = m_vertexBuffers[draw.m_indirectBuffer.idx];
+ numDrawIndirect = UINT32_MAX == draw.m_numIndirect
+ ? indirect.m_size/BGFX_CONFIG_DRAW_INDIRECT_STRIDE
+ : draw.m_numIndirect
+ ;
+
+ if (isValid(draw.m_indexBuffer) )
+ {
+ if (isValid(draw.m_numIndirectBuffer) )
+ {
+ const IndexBufferWGPU& numIndirect = m_indexBuffers[draw.m_numIndirectBuffer.idx];
+
+ WGPU_CHECK(stubRenderPassEncoderMultiDrawIndexedIndirect(
+ renderPassEncoder
+ , indirect.m_buffer
+ , draw.m_startIndirect * BGFX_CONFIG_DRAW_INDIRECT_STRIDE
+ , numDrawIndirect
+ , numIndirect.m_buffer
+ , draw.m_numIndirectIndex * sizeof(uint32_t)
+ ) );
+ }
+ else
+ {
+ WGPU_CHECK(stubRenderPassEncoderMultiDrawIndexedIndirect(
+ renderPassEncoder
+ , indirect.m_buffer
+ , draw.m_startIndirect * BGFX_CONFIG_DRAW_INDIRECT_STRIDE
+ , numDrawIndirect
+ , NULL
+ , 0
+ ) );
+ }
+ }
+ else
+ {
+ if (isValid(draw.m_numIndirectBuffer) )
+ {
+ const IndexBufferWGPU& numIndirect = m_indexBuffers[draw.m_numIndirectBuffer.idx];
+
+ WGPU_CHECK(stubRenderPassEncoderMultiDrawIndirect(
+ renderPassEncoder
+ , indirect.m_buffer
+ , draw.m_startIndirect * BGFX_CONFIG_DRAW_INDIRECT_STRIDE
+ , numDrawIndirect
+ , numIndirect.m_buffer
+ , draw.m_numIndirectIndex * sizeof(uint32_t)
+ ) );
+ }
+ else
+ {
+ WGPU_CHECK(stubRenderPassEncoderMultiDrawIndirect(
+ renderPassEncoder
+ , indirect.m_buffer
+ , draw.m_startIndirect * BGFX_CONFIG_DRAW_INDIRECT_STRIDE
+ , numDrawIndirect
+ , NULL
+ , 0
+ ) );
+ }
+ }
+ }
+ else
+ {
+ if (isValid(draw.m_indexBuffer) )
+ {
+ if (UINT32_MAX == draw.m_numIndices)
+ {
+ const IndexBufferWGPU& ib = m_indexBuffers[draw.m_indexBuffer.idx];
+ const uint32_t indexSize = 0 == (ib.m_flags & BGFX_BUFFER_INDEX32) ? 2 : 4;
+ numIndices = ib.m_size/indexSize;
+ numPrimsSubmitted = numIndices/prim.m_div - prim.m_sub;
+ numInstances = draw.m_numInstances;
+ numPrimsRendered = numPrimsSubmitted*draw.m_numInstances;
+
+ WGPU_CHECK(wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, numIndices, draw.m_numInstances, 0, 0, 0) );
+ }
+ else if (prim.m_min <= draw.m_numIndices)
+ {
+ numIndices = draw.m_numIndices;
+ numPrimsSubmitted = numIndices/prim.m_div - prim.m_sub;
+ numInstances = draw.m_numInstances;
+ numPrimsRendered = numPrimsSubmitted*draw.m_numInstances;
+
+ WGPU_CHECK(wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, numIndices, draw.m_numInstances, draw.m_startIndex, 0, 0) );
+ }
+ }
+ else
+ {
+ numPrimsSubmitted = numVertices/prim.m_div - prim.m_sub;
+ numInstances = draw.m_numInstances;
+ numPrimsRendered = numPrimsSubmitted*draw.m_numInstances;
+
+ WGPU_CHECK(wgpuRenderPassEncoderDraw(renderPassEncoder, numVertices, draw.m_numInstances, 0, 0) );
+ }
+ }
+
+ if (hasOcclusionQuery)
+ {
+ m_occlusionQuery.end(renderPassEncoder);
+ }
+
+ statsNumPrimsSubmitted[primIndex] += numPrimsSubmitted;
+ statsNumPrimsRendered[primIndex] += numPrimsRendered;
+ statsNumInstances[primIndex] += numInstances;
+ statsNumDrawIndirect[primIndex] += numDrawIndirect;
+ statsNumIndices += numIndices;
+ }
+ }
+
+ if (NULL != renderPassEncoder)
+ {
+ WGPU_CHECK(wgpuRenderPassEncoderEnd(renderPassEncoder) );
+ wgpuRelease(renderPassEncoder);
+ }
+
+ if (NULL != computePassEncoder)
+ {
+ WGPU_CHECK(wgpuComputePassEncoderEnd(computePassEncoder) );
+ wgpuRelease(computePassEncoder);
+
+ setViewType(view, "C");
+ BGFX_WGPU_PROFILER_END();
+ BGFX_WGPU_PROFILER_BEGIN(view, kColorCompute);
+ }
+
+ submitBlit(bs, BGFX_CONFIG_MAX_VIEWS);
+
+ m_occlusionQuery.resolve();
+
+ if (0 < _render->m_numRenderItems)
+ {
+ captureElapsed = -bx::getHPCounter();
+// capture();
+ captureElapsed += bx::getHPCounter();
+
+ profiler.end();
+ }
+ }
+
+ BGFX_WGPU_PROFILER_END();
+
+ int64_t timeEnd = bx::getHPCounter();
+ int64_t frameTime = timeEnd - timeBegin;
+
+ static int64_t min = frameTime;
+ static int64_t max = frameTime;
+ min = bx::min(min, frameTime);
+ max = bx::max(max, frameTime);
+
+ static uint32_t maxGpuLatency = 0;
+ static double maxGpuElapsed = 0.0f;
+ double elapsedGpuMs = 0.0;
+ BX_UNUSED(elapsedGpuMs);
+
+ static int64_t presentMin = m_presentElapsed;
+ static int64_t presentMax = m_presentElapsed;
+ presentMin = bx::min(presentMin, m_presentElapsed);
+ presentMax = bx::max(presentMax, m_presentElapsed);
+
+ if (UINT32_MAX != frameQueryIdx)
+ {
+ m_gpuTimer.end(frameQueryIdx);
+ }
+
+ const int64_t timerFreq = bx::getHPFrequency();
+
+ Stats& perfStats = _render->m_perfStats;
+ perfStats.cpuTimeBegin = timeBegin;
+ perfStats.cpuTimeEnd = timeBegin;
+ perfStats.cpuTimerFreq = timerFreq;
+
+ perfStats.gpuTimeBegin = 0;
+ perfStats.gpuTimeEnd = 0;
+ perfStats.gpuTimerFreq = 1000000000;
+ perfStats.gpuFrameNum = 0;
+
+ perfStats.numDraw = statsKeyType[0];
+ perfStats.numCompute = statsKeyType[1];
+ perfStats.numBlit = _render->m_numBlitItems;
+
+ perfStats.gpuMemoryMax = -INT64_MAX;
+ perfStats.gpuMemoryUsed = -INT64_MAX;
+
+
+ if (_render->m_debug & (BGFX_DEBUG_IFH|BGFX_DEBUG_STATS) )
+ {
+ TextVideoMem& tvm = m_textVideoMem;
+
+ static int64_t next = timeEnd;
+
+ if (timeEnd >= next)
+ {
+ next = timeEnd + timerFreq;
+
+ double freq = double(timerFreq);
+ double toMs = 1000.0 / freq;
+
+ tvm.clear();
+ uint16_t pos = 0;
+ tvm.printf(0, pos++, BGFX_CONFIG_DEBUG ? 0x8c : 0x8f
+ , " %s / " BX_COMPILER_NAME
+ " / " BX_CPU_NAME
+ " / " BX_ARCH_NAME
+ " / " BX_PLATFORM_NAME
+ " / Version 1.%d.%d (commit: " BGFX_REV_SHA1 ")"
+ , getRendererName()
+ , BGFX_API_VERSION
+ , BGFX_REV_NUMBER
+ );
+
+ pos = 10;
+ tvm.printf(10, pos++, 0x8b, " Frame: % 7.3f, % 7.3f \x1f, % 7.3f \x1e [ms] / % 6.2f FPS "
+ , double(frameTime)*toMs
+ , double(min)*toMs
+ , double(max)*toMs
+ , freq/frameTime
+ );
+ tvm.printf(10, pos++, 0x8b, " Present: % 7.3f, % 7.3f \x1f, % 7.3f \x1e [ms] "
+ , double(m_presentElapsed)*toMs
+ , double(presentMin)*toMs
+ , double(presentMax)*toMs
+ );
+
+ const uint32_t msaa = (m_resolution.reset&BGFX_RESET_MSAA_MASK)>>BGFX_RESET_MSAA_SHIFT;
+ tvm.printf(10, pos++, 0x8b, " Reset flags: [%c] vsync, [%c] MSAAx%d, [%c] MaxAnisotropy "
+ , !!(m_resolution.reset&BGFX_RESET_VSYNC) ? '\xfe' : ' '
+ , 0 != msaa ? '\xfe' : ' '
+ , 1<m_numRenderItems
+ , statsKeyType[0]
+ , statsKeyType[1]
+ , elapsedCpuMs
+ , elapsedCpuMs > maxGpuElapsed ? '>' : '<'
+ , maxGpuElapsed
+ , maxGpuLatency
+ );
+ maxGpuLatency = 0;
+ maxGpuElapsed = 0.0;
+
+ for (uint32_t ii = 0; ii < Topology::Count; ++ii)
+ {
+ tvm.printf(10, pos++, 0x8b, " %9s: %7d (#inst: %5d), submitted: %7d "
+ , getName(Topology::Enum(ii) )
+ , statsNumPrimsRendered[ii]
+ , statsNumInstances[ii]
+ , statsNumPrimsSubmitted[ii]
+ );
+ }
+
+ if (NULL != m_renderDocDll)
+ {
+ tvm.printf(tvm.m_width-27, 0, 0x4f, " [F11 - RenderDoc capture] ");
+ }
+
+ tvm.printf(10, pos++, 0x8b, " Indices: %7d ", statsNumIndices);
+ tvm.printf(10, pos++, 0x8b, " DVB size: %7d ", _render->m_vboffset);
+ tvm.printf(10, pos++, 0x8b, " DIB size: %7d ", _render->m_iboffset);
+ pos++;
+
+ double captureMs = double(captureElapsed)*toMs;
+ tvm.printf(10, pos++, 0x8b, " Capture: %7.4f [ms] ", captureMs);
+
+ uint8_t attr[2] = { 0x8c, 0x8a };
+ uint8_t attrIndex = _render->m_waitSubmit < _render->m_waitRender;
+
+ tvm.printf(10, pos++, attr[attrIndex&1], " Submit wait: %7.4f [ms] ", _render->m_waitSubmit*toMs);
+ tvm.printf(10, pos++, attr[(attrIndex+1)&1], " Render wait: %7.4f [ms] ", _render->m_waitRender*toMs);
+
+ min = frameTime;
+ max = frameTime;
+ presentMin = m_presentElapsed;
+ presentMax = m_presentElapsed;
+ }
+
+ dbgTextSubmit(this, _textVideoMemBlitter, tvm);
+ }
+ else if (_render->m_debug & BGFX_DEBUG_TEXT)
+ {
+ dbgTextSubmit(this, _textVideoMemBlitter, _render->m_textVideoMem);
+ }
+
+ m_presentElapsed = 0;
+
+ m_uniformScratchBuffer.end();
+
+ m_cmd.frame();
+ }
+
+
+} /* namespace wgpu */ } // namespace bgfx
+
+#else
+
+namespace bgfx { namespace wgpu
+{
+ RendererContextI* rendererCreate(const Init& _init)
+ {
+ BX_UNUSED(_init);
+ return NULL;
+ }
+
+ void rendererDestroy()
+ {
+ }
+} /* namespace wgpu */ } // namespace bgfx
+
+#endif // BGFX_CONFIG_RENDERER_WEBGPU
diff --git a/src/renderer_webgpu.h b/src/renderer_webgpu.h
new file mode 100644
index 000000000..cee495f83
--- /dev/null
+++ b/src/renderer_webgpu.h
@@ -0,0 +1,889 @@
+/*
+ * Copyright 2011-2025 Branimir Karadzic. All rights reserved.
+ * License: https://github.com/bkaradzic/bgfx/blob/master/LICENSE
+ */
+
+#ifndef BGFX_RENDERER_WEBGPU_H_HEADER_GUARD
+#define BGFX_RENDERER_WEBGPU_H_HEADER_GUARD
+
+#include "renderer.h"
+
+#define USE_WEBGPU_DYNAMIC_LIB (0 \
+ || BX_PLATFORM_LINUX \
+ || BX_PLATFORM_OSX \
+ || BX_PLATFORM_WINDOWS \
+ )
+
+#include "debug_renderdoc.h"
+#include "shader_spirv.h"
+
+#define _WGPU_CHECK(_call) _call; BX_ASSERT(!wgpuErrorCheck(), "" #_call " failed!")
+
+#if BGFX_CONFIG_DEBUG
+# define WGPU_CHECK(_call) _WGPU_CHECK(_call)
+#else
+# define WGPU_CHECK(_call) _call
+#endif // BGFX_CONFIG_DEBUG
+
+#define WGPU_SKIP_DECLARATIONS
+#include
+
+#if USE_WEBGPU_DYNAMIC_LIB
+# define WGPU_IMPORT \
+ /* instance */ \
+ WGPU_IMPORT_FUNC(false, CreateInstance); \
+ WGPU_IGNORE_____(false, GetInstanceFeatures); \
+ WGPU_IGNORE_____(false, GetInstanceLimits); \
+ WGPU_IGNORE_____(false, HasInstanceFeature); \
+ WGPU_IMPORT_FUNC(false, GetProcAddress); \
+ /* adapter */ \
+ WGPU_IGNORE_____(false, AdapterCreateDevice); \
+ WGPU_IMPORT_FUNC(false, AdapterGetFeatures); \
+ WGPU_IGNORE_____(false, AdapterGetFormatCapabilities); \
+ WGPU_IMPORT_FUNC(false, AdapterGetInfo); \
+ WGPU_IGNORE_____(false, AdapterGetInstance); \
+ WGPU_IMPORT_FUNC(false, AdapterGetLimits); \
+ WGPU_IMPORT_FUNC(false, AdapterHasFeature); \
+ WGPU_IMPORT_FUNC(false, AdapterRequestDevice); \
+ WGPU_IGNORE_____(false, AdapterAddRef); \
+ WGPU_IMPORT_FUNC(false, AdapterRelease); \
+ WGPU_IMPORT_FUNC(false, AdapterInfoFreeMembers); \
+ WGPU_IGNORE_____(false, AdapterPropertiesMemoryHeapsFreeMembers); \
+ WGPU_IGNORE_____(false, AdapterPropertiesSubgroupMatrixConfigsFreeMembers); \
+ /* bind group */ \
+ WGPU_IMPORT_FUNC(false, BindGroupSetLabel); \
+ WGPU_IGNORE_____(false, BindGroupAddRef); \
+ WGPU_IMPORT_FUNC(false, BindGroupRelease); \
+ WGPU_IMPORT_FUNC(false, BindGroupLayoutSetLabel); \
+ WGPU_IGNORE_____(false, BindGroupLayoutAddRef); \
+ WGPU_IMPORT_FUNC(false, BindGroupLayoutRelease); \
+ /* buffer */ \
+ WGPU_IGNORE_____(false, BufferCreateTexelView); \
+ WGPU_IMPORT_FUNC(false, BufferDestroy); \
+ WGPU_IMPORT_FUNC(false, BufferGetConstMappedRange); \
+ WGPU_IGNORE_____(false, BufferGetMappedRange); \
+ WGPU_IGNORE_____(false, BufferGetMapState); \
+ WGPU_IGNORE_____(false, BufferGetSize); \
+ WGPU_IGNORE_____(false, BufferGetUsage); \
+ WGPU_IMPORT_FUNC(false, BufferMapAsync); \
+ WGPU_IGNORE_____(false, BufferReadMappedRange); \
+ WGPU_IMPORT_FUNC(false, BufferSetLabel); \
+ WGPU_IMPORT_FUNC(false, BufferUnmap); \
+ WGPU_IGNORE_____(false, BufferWriteMappedRange); \
+ WGPU_IGNORE_____(false, BufferAddRef); \
+ WGPU_IMPORT_FUNC(false, BufferRelease); \
+ WGPU_IMPORT_FUNC(false, CommandBufferSetLabel); \
+ WGPU_IGNORE_____(false, CommandBufferAddRef); \
+ WGPU_IMPORT_FUNC(false, CommandBufferRelease); \
+ /* */ \
+ WGPU_IMPORT_FUNC(false, CommandEncoderBeginComputePass); \
+ WGPU_IMPORT_FUNC(false, CommandEncoderBeginRenderPass); \
+ WGPU_IMPORT_FUNC(false, CommandEncoderClearBuffer); \
+ WGPU_IMPORT_FUNC(false, CommandEncoderCopyBufferToBuffer); \
+ WGPU_IMPORT_FUNC(false, CommandEncoderCopyBufferToTexture); \
+ WGPU_IMPORT_FUNC(false, CommandEncoderCopyTextureToBuffer); \
+ WGPU_IMPORT_FUNC(false, CommandEncoderCopyTextureToTexture); \
+ WGPU_IMPORT_FUNC(false, CommandEncoderFinish); \
+ WGPU_IGNORE_____(false, CommandEncoderInjectValidationError); \
+ WGPU_IMPORT_FUNC(false, CommandEncoderInsertDebugMarker); \
+ WGPU_IMPORT_FUNC(false, CommandEncoderPopDebugGroup); \
+ WGPU_IMPORT_FUNC(false, CommandEncoderPushDebugGroup); \
+ WGPU_IMPORT_FUNC(false, CommandEncoderResolveQuerySet); \
+ WGPU_IMPORT_FUNC(false, CommandEncoderSetLabel); \
+ WGPU_IGNORE_____(false, CommandEncoderWriteBuffer); \
+ WGPU_IMPORT_FUNC(false, CommandEncoderWriteTimestamp); \
+ WGPU_IGNORE_____(false, CommandEncoderAddRef); \
+ WGPU_IMPORT_FUNC(false, CommandEncoderRelease); \
+ /* */ \
+ WGPU_IMPORT_FUNC(false, ComputePassEncoderDispatchWorkgroups); \
+ WGPU_IMPORT_FUNC(false, ComputePassEncoderDispatchWorkgroupsIndirect); \
+ WGPU_IMPORT_FUNC(false, ComputePassEncoderEnd); \
+ WGPU_IMPORT_FUNC(false, ComputePassEncoderInsertDebugMarker); \
+ WGPU_IMPORT_FUNC(false, ComputePassEncoderPopDebugGroup); \
+ WGPU_IMPORT_FUNC(false, ComputePassEncoderPushDebugGroup); \
+ WGPU_IMPORT_FUNC(false, ComputePassEncoderSetBindGroup); \
+ WGPU_IMPORT_FUNC(false, ComputePassEncoderSetLabel); \
+ WGPU_IMPORT_FUNC(false, ComputePassEncoderSetPipeline); \
+ WGPU_IMPORT_FUNC(false, ComputePassEncoderWriteTimestamp); \
+ WGPU_IGNORE_____(false, ComputePassEncoderAddRef); \
+ WGPU_IMPORT_FUNC(false, ComputePassEncoderRelease); \
+ /* */ \
+ WGPU_IMPORT_FUNC(false, ComputePipelineGetBindGroupLayout); \
+ WGPU_IMPORT_FUNC(false, ComputePipelineSetLabel); \
+ WGPU_IGNORE_____(false, ComputePipelineAddRef); \
+ WGPU_IMPORT_FUNC(false, ComputePipelineRelease); \
+ /* */ \
+ WGPU_IGNORE_____(false, DawnDrmFormatCapabilitiesFreeMembers); \
+ /* */ \
+ WGPU_IMPORT_FUNC(false, DeviceCreateBindGroup); \
+ WGPU_IMPORT_FUNC(false, DeviceCreateBindGroupLayout); \
+ WGPU_IMPORT_FUNC(false, DeviceCreateBuffer); \
+ WGPU_IMPORT_FUNC(false, DeviceCreateCommandEncoder); \
+ WGPU_IMPORT_FUNC(false, DeviceCreateComputePipeline); \
+ WGPU_IMPORT_FUNC(false, DeviceCreateComputePipelineAsync); \
+ WGPU_IGNORE_____(false, DeviceCreateErrorBuffer); \
+ WGPU_IGNORE_____(false, DeviceCreateErrorExternalTexture); \
+ WGPU_IGNORE_____(false, DeviceCreateErrorShaderModule); \
+ WGPU_IGNORE_____(false, DeviceCreateErrorTexture); \
+ WGPU_IGNORE_____(false, DeviceCreateExternalTexture); \
+ WGPU_IMPORT_FUNC(false, DeviceCreatePipelineLayout); \
+ WGPU_IMPORT_FUNC(false, DeviceCreateQuerySet); \
+ WGPU_IMPORT_FUNC(false, DeviceCreateRenderBundleEncoder); \
+ WGPU_IMPORT_FUNC(false, DeviceCreateRenderPipeline); \
+ WGPU_IMPORT_FUNC(false, DeviceCreateRenderPipelineAsync); \
+ WGPU_IMPORT_FUNC(false, DeviceCreateSampler); \
+ WGPU_IMPORT_FUNC(false, DeviceCreateShaderModule); \
+ WGPU_IMPORT_FUNC(false, DeviceCreateTexture); \
+ WGPU_IMPORT_FUNC(false, DeviceDestroy); \
+ WGPU_IGNORE_____(false, DeviceForceLoss); \
+ WGPU_IGNORE_____(false, DeviceGetAdapter); \
+ WGPU_IMPORT_FUNC(false, DeviceGetAdapterInfo); \
+ WGPU_IGNORE_____(false, DeviceGetAHardwareBufferProperties); \
+ WGPU_IMPORT_FUNC(false, DeviceGetFeatures); \
+ WGPU_IMPORT_FUNC(false, DeviceGetLimits); \
+ WGPU_IMPORT_FUNC(false, DeviceGetLostFuture); \
+ WGPU_IMPORT_FUNC(false, DeviceGetQueue); \
+ WGPU_IMPORT_FUNC(false, DeviceHasFeature); \
+ WGPU_IGNORE_____(false, DeviceImportSharedBufferMemory); \
+ WGPU_IGNORE_____(false, DeviceImportSharedFence); \
+ WGPU_IGNORE_____(false, DeviceImportSharedTextureMemory); \
+ WGPU_IGNORE_____(false, DeviceInjectError); \
+ WGPU_IMPORT_FUNC(false, DevicePopErrorScope); \
+ WGPU_IMPORT_FUNC(false, DevicePushErrorScope); \
+ WGPU_IMPORT_FUNC(false, DeviceSetLabel); \
+ WGPU_IGNORE_____(false, DeviceSetLoggingCallback); \
+ WGPU_IGNORE_____(false, DeviceTick); \
+ WGPU_IGNORE_____(false, DeviceValidateTextureDescriptor); \
+ WGPU_IGNORE_____(false, DeviceAddRef); \
+ WGPU_IMPORT_FUNC(false, DeviceRelease); \
+ /* */ \
+ WGPU_IGNORE_____(false, ExternalTextureDestroy); \
+ WGPU_IGNORE_____(false, ExternalTextureExpire); \
+ WGPU_IGNORE_____(false, ExternalTextureRefresh); \
+ WGPU_IGNORE_____(false, ExternalTextureSetLabel); \
+ WGPU_IGNORE_____(false, ExternalTextureAddRef); \
+ WGPU_IGNORE_____(false, ExternalTextureRelease); \
+ /* */ \
+ WGPU_IMPORT_FUNC(false, InstanceCreateSurface); \
+ WGPU_IMPORT_FUNC(false, InstanceGetWGSLLanguageFeatures); \
+ WGPU_IMPORT_FUNC(false, InstanceHasWGSLLanguageFeature); \
+ WGPU_IMPORT_FUNC(false, InstanceProcessEvents); \
+ WGPU_IMPORT_FUNC(false, InstanceRequestAdapter); \
+ WGPU_IMPORT_FUNC(false, InstanceWaitAny); \
+ WGPU_IGNORE_____(false, InstanceAddRef); \
+ WGPU_IMPORT_FUNC(false, InstanceRelease); \
+ /* */ \
+ WGPU_IMPORT_FUNC(false, PipelineLayoutSetLabel); \
+ WGPU_IGNORE_____(false, PipelineLayoutAddRef); \
+ WGPU_IMPORT_FUNC(false, PipelineLayoutRelease); \
+ /* */ \
+ WGPU_IMPORT_FUNC(false, QuerySetDestroy); \
+ WGPU_IMPORT_FUNC(false, QuerySetGetCount); \
+ WGPU_IMPORT_FUNC(false, QuerySetGetType); \
+ WGPU_IMPORT_FUNC(false, QuerySetSetLabel); \
+ WGPU_IGNORE_____(false, QuerySetAddRef); \
+ WGPU_IMPORT_FUNC(false, QuerySetRelease); \
+ /* */ \
+ WGPU_IGNORE_____(false, QueueCopyExternalTextureForBrowser); \
+ WGPU_IGNORE_____(false, QueueCopyTextureForBrowser); \
+ WGPU_IMPORT_FUNC(false, QueueOnSubmittedWorkDone); \
+ WGPU_IMPORT_FUNC(false, QueueSetLabel); \
+ WGPU_IMPORT_FUNC(false, QueueSubmit); \
+ WGPU_IMPORT_FUNC(false, QueueWriteBuffer); \
+ WGPU_IMPORT_FUNC(false, QueueWriteTexture); \
+ WGPU_IGNORE_____(false, QueueAddRef); \
+ WGPU_IMPORT_FUNC(false, QueueRelease); \
+ /* */ \
+ WGPU_IGNORE_____(false, RenderBundleSetLabel); \
+ WGPU_IGNORE_____(false, RenderBundleAddRef); \
+ WGPU_IGNORE_____(false, RenderBundleRelease); \
+ /* */ \
+ WGPU_IGNORE_____(false, RenderBundleEncoderDraw); \
+ WGPU_IGNORE_____(false, RenderBundleEncoderDrawIndexed); \
+ WGPU_IGNORE_____(false, RenderBundleEncoderDrawIndexedIndirect); \
+ WGPU_IGNORE_____(false, RenderBundleEncoderDrawIndirect); \
+ WGPU_IGNORE_____(false, RenderBundleEncoderFinish); \
+ WGPU_IGNORE_____(false, RenderBundleEncoderInsertDebugMarker); \
+ WGPU_IGNORE_____(false, RenderBundleEncoderPopDebugGroup); \
+ WGPU_IGNORE_____(false, RenderBundleEncoderPushDebugGroup); \
+ WGPU_IGNORE_____(false, RenderBundleEncoderSetBindGroup); \
+ WGPU_IGNORE_____(false, RenderBundleEncoderSetIndexBuffer); \
+ WGPU_IGNORE_____(false, RenderBundleEncoderSetLabel); \
+ WGPU_IGNORE_____(false, RenderBundleEncoderSetPipeline); \
+ WGPU_IGNORE_____(false, RenderBundleEncoderSetVertexBuffer); \
+ WGPU_IGNORE_____(false, RenderBundleEncoderAddRef); \
+ WGPU_IGNORE_____(false, RenderBundleEncoderRelease); \
+ /* */ \
+ WGPU_IMPORT_FUNC(false, RenderPassEncoderBeginOcclusionQuery); \
+ WGPU_IMPORT_FUNC(false, RenderPassEncoderDraw); \
+ WGPU_IMPORT_FUNC(false, RenderPassEncoderDrawIndexed); \
+ WGPU_IMPORT_FUNC(false, RenderPassEncoderDrawIndexedIndirect); \
+ WGPU_IMPORT_FUNC(false, RenderPassEncoderDrawIndirect); \
+ WGPU_IMPORT_FUNC(false, RenderPassEncoderEnd); \
+ WGPU_IMPORT_FUNC(false, RenderPassEncoderEndOcclusionQuery); \
+ WGPU_IGNORE_____(false, RenderPassEncoderExecuteBundles); \
+ WGPU_IMPORT_FUNC(false, RenderPassEncoderInsertDebugMarker); \
+ WGPU_IMPORT_FUNC(false, RenderPassEncoderMultiDrawIndexedIndirect); \
+ WGPU_IMPORT_FUNC(false, RenderPassEncoderMultiDrawIndirect); \
+ WGPU_IGNORE_____(false, RenderPassEncoderPixelLocalStorageBarrier); \
+ WGPU_IMPORT_FUNC(false, RenderPassEncoderPopDebugGroup); \
+ WGPU_IMPORT_FUNC(false, RenderPassEncoderPushDebugGroup); \
+ WGPU_IMPORT_FUNC(false, RenderPassEncoderSetBindGroup); \
+ WGPU_IMPORT_FUNC(false, RenderPassEncoderSetBlendConstant); \
+ WGPU_IMPORT_FUNC(false, RenderPassEncoderSetIndexBuffer); \
+ WGPU_IMPORT_FUNC(false, RenderPassEncoderSetLabel); \
+ WGPU_IMPORT_FUNC(false, RenderPassEncoderSetPipeline); \
+ WGPU_IMPORT_FUNC(false, RenderPassEncoderSetScissorRect); \
+ WGPU_IMPORT_FUNC(false, RenderPassEncoderSetStencilReference); \
+ WGPU_IMPORT_FUNC(false, RenderPassEncoderSetVertexBuffer); \
+ WGPU_IMPORT_FUNC(false, RenderPassEncoderSetViewport); \
+ WGPU_IMPORT_FUNC(false, RenderPassEncoderWriteTimestamp); \
+ WGPU_IGNORE_____(false, RenderPassEncoderAddRef); \
+ WGPU_IMPORT_FUNC(false, RenderPassEncoderRelease); \
+ /* */ \
+ WGPU_IMPORT_FUNC(false, RenderPipelineGetBindGroupLayout); \
+ WGPU_IMPORT_FUNC(false, RenderPipelineSetLabel); \
+ WGPU_IMPORT_FUNC(false, RenderPipelineAddRef); \
+ WGPU_IMPORT_FUNC(false, RenderPipelineRelease); \
+ /* */ \
+ WGPU_IMPORT_FUNC(false, SamplerSetLabel); \
+ WGPU_IGNORE_____(false, SamplerAddRef); \
+ WGPU_IMPORT_FUNC(false, SamplerRelease); \
+ /* */ \
+ WGPU_IMPORT_FUNC(false, ShaderModuleGetCompilationInfo); \
+ WGPU_IMPORT_FUNC(false, ShaderModuleSetLabel); \
+ WGPU_IGNORE_____(false, ShaderModuleAddRef); \
+ WGPU_IMPORT_FUNC(false, ShaderModuleRelease); \
+ /* */ \
+ WGPU_IGNORE_____(false, SharedBufferMemoryBeginAccess); \
+ WGPU_IGNORE_____(false, SharedBufferMemoryCreateBuffer); \
+ WGPU_IGNORE_____(false, SharedBufferMemoryEndAccess); \
+ WGPU_IGNORE_____(false, SharedBufferMemoryGetProperties); \
+ WGPU_IGNORE_____(false, SharedBufferMemoryIsDeviceLost); \
+ WGPU_IGNORE_____(false, SharedBufferMemorySetLabel); \
+ WGPU_IGNORE_____(false, SharedBufferMemoryAddRef); \
+ WGPU_IGNORE_____(false, SharedBufferMemoryRelease); \
+ /* */ \
+ WGPU_IGNORE_____(false, SharedBufferMemoryEndAccessStateFreeMembers); \
+ /* */ \
+ WGPU_IGNORE_____(false, SharedFenceExportInfo); \
+ WGPU_IGNORE_____(false, SharedFenceAddRef); \
+ WGPU_IGNORE_____(false, SharedFenceRelease); \
+ /* */ \
+ WGPU_IGNORE_____(false, SharedTextureMemoryBeginAccess); \
+ WGPU_IGNORE_____(false, SharedTextureMemoryCreateTexture); \
+ WGPU_IGNORE_____(false, SharedTextureMemoryEndAccess); \
+ WGPU_IGNORE_____(false, SharedTextureMemoryGetProperties); \
+ WGPU_IGNORE_____(false, SharedTextureMemoryIsDeviceLost); \
+ WGPU_IGNORE_____(false, SharedTextureMemorySetLabel); \
+ WGPU_IGNORE_____(false, SharedTextureMemoryAddRef); \
+ WGPU_IGNORE_____(false, SharedTextureMemoryRelease); \
+ /* */ \
+ WGPU_IGNORE_____(false, SharedTextureMemoryEndAccessStateFreeMembers); \
+ /* */ \
+ WGPU_IMPORT_FUNC(false, SupportedFeaturesFreeMembers); \
+ /* */ \
+ WGPU_IGNORE_____(false, SupportedInstanceFeaturesFreeMembers); \
+ /* */ \
+ WGPU_IMPORT_FUNC(false, SupportedWGSLLanguageFeaturesFreeMembers); \
+ /* */ \
+ WGPU_IMPORT_FUNC(false, SurfaceConfigure); \
+ WGPU_IMPORT_FUNC(false, SurfaceGetCapabilities); \
+ WGPU_IMPORT_FUNC(false, SurfaceGetCurrentTexture); \
+ WGPU_IMPORT_FUNC(false, SurfacePresent); \
+ WGPU_IMPORT_FUNC(false, SurfaceSetLabel); \
+ WGPU_IMPORT_FUNC(false, SurfaceUnconfigure); \
+ WGPU_IGNORE_____(false, SurfaceAddRef); \
+ WGPU_IMPORT_FUNC(false, SurfaceRelease); \
+ /* */ \
+ WGPU_IMPORT_FUNC(false, SurfaceCapabilitiesFreeMembers); \
+ /* */ \
+ WGPU_IGNORE_____(false, TexelBufferViewSetLabel); \
+ WGPU_IGNORE_____(false, TexelBufferViewAddRef); \
+ WGPU_IGNORE_____(false, TexelBufferViewRelease); \
+ /* */ \
+ WGPU_IGNORE_____(false, TextureCreateErrorView); \
+ WGPU_IMPORT_FUNC(false, TextureCreateView); \
+ WGPU_IMPORT_FUNC(false, TextureDestroy); \
+ WGPU_IGNORE_____(false, TextureGetDepthOrArrayLayers); \
+ WGPU_IGNORE_____(false, TextureGetDimension); \
+ WGPU_IGNORE_____(false, TextureGetFormat); \
+ WGPU_IGNORE_____(false, TextureGetHeight); \
+ WGPU_IGNORE_____(false, TextureGetMipLevelCount); \
+ WGPU_IGNORE_____(false, TextureGetSampleCount); \
+ WGPU_IGNORE_____(false, TextureGetUsage); \
+ WGPU_IGNORE_____(false, TextureGetWidth); \
+ WGPU_IGNORE_____(false, TexturePin); \
+ WGPU_IMPORT_FUNC(false, TextureSetLabel); \
+ WGPU_IGNORE_____(false, TextureUnpin); \
+ WGPU_IGNORE_____(false, TextureAddRef); \
+ WGPU_IMPORT_FUNC(false, TextureRelease); \
+ /* */ \
+ WGPU_IMPORT_FUNC(false, TextureViewSetLabel); \
+ WGPU_IGNORE_____(false, TextureViewAddRef); \
+ WGPU_IMPORT_FUNC(false, TextureViewRelease); \
+ /* end */
+
+#endif // USE_WEBGPU_DYNAMIC_LIB
+
+#define WGPU_RELEASE \
+ WGPU_RELEASE_FUNC(Adapter); \
+ WGPU_RELEASE_FUNC(Device); \
+ WGPU_RELEASE_FUNC(Instance); \
+ WGPU_RELEASE_FUNC(Surface); \
+ WGPU_RELEASE_FUNC(Queue); \
+ WGPU_RELEASE_FUNC(Texture); \
+ WGPU_RELEASE_FUNC(TextureView); \
+ WGPU_RELEASE_FUNC(CommandEncoder); \
+ WGPU_RELEASE_FUNC(ComputePassEncoder); \
+ WGPU_RELEASE_FUNC(RenderPassEncoder); \
+ WGPU_RELEASE_FUNC(CommandBuffer); \
+ WGPU_RELEASE_FUNC(BindGroup); \
+ WGPU_RELEASE_FUNC(BindGroupLayout); \
+ WGPU_RELEASE_FUNC(Buffer); \
+ WGPU_RELEASE_FUNC(ComputePipeline); \
+ /*WGPU_RELEASE_FUNC(ExternalTexture);*/ \
+ WGPU_RELEASE_FUNC(PipelineLayout); \
+ WGPU_RELEASE_FUNC(QuerySet); \
+ /*WGPU_RELEASE_FUNC(RenderBundle);*/ \
+ /*WGPU_RELEASE_FUNC(RenderBundleEncoder);*/ \
+ WGPU_RELEASE_FUNC(RenderPipeline); \
+ WGPU_RELEASE_FUNC(Sampler); \
+ WGPU_RELEASE_FUNC(ShaderModule); \
+ /*WGPU_RELEASE_FUNC(SharedBufferMemory);*/ \
+ /*WGPU_RELEASE_FUNC(SharedFence);*/ \
+ /*WGPU_RELEASE_FUNC(SharedTextureMemory);*/ \
+ /*WGPU_RELEASE_FUNC(TexelBufferView);*/ \
+ /* end */
+
+#define BGFX_WGPU_PROFILER_BEGIN(_view, _abgr) \
+ BX_MACRO_BLOCK_BEGIN \
+ BGFX_PROFILER_BEGIN(s_viewName[view], _abgr); \
+ BX_MACRO_BLOCK_END
+
+#define BGFX_WGPU_PROFILER_BEGIN_LITERAL(_name, _abgr) \
+ BX_MACRO_BLOCK_BEGIN \
+ BGFX_PROFILER_BEGIN_LITERAL("" _name, _abgr); \
+ BX_MACRO_BLOCK_END
+
+#define BGFX_WGPU_PROFILER_END() \
+ BX_MACRO_BLOCK_BEGIN \
+ BGFX_PROFILER_END(); \
+ BX_MACRO_BLOCK_END
+
+namespace bgfx { namespace wgpu
+{
+#define WGPU_RELEASE_FUNC(_name) void wgpuRelease(WGPU##_name& _obj)
+
+ WGPU_RELEASE
+
+#undef WGPU_RELEASE_FUNC
+
+ template
+ class StateCacheT
+ {
+ public:
+ void add(uint64_t _key, Ty _value)
+ {
+ invalidate(_key);
+ m_hashMap.insert(stl::make_pair(_key, _value) );
+ }
+
+ Ty find(uint64_t _key)
+ {
+ typename HashMap::iterator it = m_hashMap.find(_key);
+ if (it != m_hashMap.end() )
+ {
+ return it->second;
+ }
+
+ return 0;
+ }
+
+ void invalidate(uint64_t _key)
+ {
+ typename HashMap::iterator it = m_hashMap.find(_key);
+ if (it != m_hashMap.end() )
+ {
+ wgpuRelease(it->second);
+ m_hashMap.erase(it);
+ }
+ }
+
+ void invalidate()
+ {
+ for (typename HashMap::iterator it = m_hashMap.begin(), itEnd = m_hashMap.end(); it != itEnd; ++it)
+ {
+ wgpuRelease(it->second);
+ }
+
+ m_hashMap.clear();
+ }
+
+ uint32_t getCount() const
+ {
+ return uint32_t(m_hashMap.size() );
+ }
+
+ private:
+ typedef stl::unordered_map HashMap;
+ HashMap m_hashMap;
+ };
+
+ inline constexpr WGPUStringView toWGPUStringView(const bx::StringView& _str)
+ {
+ return { .data = _str.getPtr(), .length = size_t(_str.getLength() ) };
+ }
+
+ struct ChunkedScratchBufferOffset
+ {
+ WGPUBuffer buffer;
+ uint32_t offsets[2];
+ };
+
+ struct ChunkedScratchBufferAlloc
+ {
+ uint32_t offset;
+ uint32_t chunkIdx;
+ };
+
+ struct ChunkedScratchBufferWGPU
+ {
+ ChunkedScratchBufferWGPU()
+ : m_chunkControl(0)
+ {
+ }
+
+ void create(uint32_t _chunkSize, uint32_t _numChunks, WGPUBufferUsage _usage, uint32_t _align);
+ void createUniform(uint32_t _chunkSize, uint32_t _numChunks);
+ void destroy();
+
+ void addChunk(uint32_t _at = UINT32_MAX);
+ ChunkedScratchBufferAlloc alloc(uint32_t _size);
+
+ void write(ChunkedScratchBufferOffset& _outSbo, const void* _vsData, uint32_t _vsSize, const void* _fsData = NULL, uint32_t _fsSize = 0);
+
+ void begin();
+ void end();
+ void flush();
+
+ struct Chunk
+ {
+ WGPUBuffer buffer;
+ uint8_t* data;
+ };
+
+ using ScratchBufferChunksArray = stl::vector;
+
+ ScratchBufferChunksArray m_chunks;
+ bx::RingBufferControl m_chunkControl;
+
+ uint32_t m_chunkPos;
+ uint32_t m_chunkSize;
+ uint32_t m_align;
+ WGPUBufferUsage m_usage;
+
+ uint32_t m_consume[BGFX_CONFIG_MAX_FRAME_LATENCY];
+ uint32_t m_totalUsed;
+ };
+
+ struct BufferWGPU
+ {
+ BufferWGPU()
+ : m_buffer(NULL)
+ , m_size(0)
+ , m_flags(BGFX_BUFFER_NONE)
+ {
+ }
+
+ void create(uint32_t _size, void* _data, uint16_t _flags, bool _vertex, uint32_t _stride = 0);
+ void update(uint32_t _offset, uint32_t _size, void* _data, bool _discard = false) const;
+ void destroy();
+
+ WGPUBuffer m_buffer;
+ uint32_t m_size;
+ uint16_t m_flags;
+ };
+
+ using IndexBufferWGPU = BufferWGPU;
+
+ struct VertexBufferWGPU : public BufferWGPU
+ {
+ void create(uint32_t _size, void* _data, VertexLayoutHandle _layoutHandle, uint16_t _flags);
+
+ VertexLayoutHandle m_layoutHandle;
+ };
+
+ struct ShaderBinding
+ {
+ struct Type
+ {
+ enum Enum
+ {
+ Buffer,
+ Image,
+ Sampler,
+
+ Count
+ };
+ };
+
+ UniformHandle uniformHandle = BGFX_INVALID_HANDLE;
+ Type::Enum type;
+ uint32_t binding;
+ uint32_t samplerBinding;
+ uint32_t index;
+ WGPUBufferBindingType bufferBindingType;
+ WGPUTextureSampleType sampleType;
+ WGPUTextureViewDimension viewDimension;
+ WGPUShaderStage shaderStage;
+
+ void clear()
+ {
+ uniformHandle = BGFX_INVALID_HANDLE;
+ type = ShaderBinding::Type::Count;
+ binding = 0;
+ samplerBinding = 0;
+ index = UINT32_MAX;
+ bufferBindingType = WGPUBufferBindingType_Undefined;
+ sampleType = WGPUTextureSampleType_Undefined;
+ viewDimension = WGPUTextureViewDimension_Undefined;
+ shaderStage = shaderStage;
+ }
+ };
+
+ struct ShaderWGPU
+ {
+ ShaderWGPU()
+ : m_code(NULL)
+ , m_module(NULL)
+ , m_constantBuffer(NULL)
+ , m_hash(0)
+ , m_numUniforms(0)
+ , m_numPredefined(0)
+ {
+ }
+
+ void create(const Memory* _mem);
+ void destroy();
+
+ const Memory* m_code;
+ WGPUShaderModule m_module;
+ UniformBuffer* m_constantBuffer;
+
+ PredefinedUniform m_predefined[PredefinedUniform::Count];
+ uint16_t m_attrMask[Attrib::Count];
+ uint8_t m_attrRemap[Attrib::Count];
+
+ uint32_t m_hash;
+ uint16_t m_numUniforms;
+ uint16_t m_size;
+ uint16_t m_blockSize;
+ uint8_t m_numPredefined;
+ uint8_t m_numAttrs;
+
+ ShaderBinding m_shaderBinding[BGFX_CONFIG_MAX_TEXTURE_SAMPLERS];
+
+ uint8_t m_numTextures;
+ };
+
+ struct BindGroup
+ {
+ void invalidate()
+ {
+ wgpuRelease(bindGroup);
+ }
+
+ WGPUBindGroup bindGroup;
+ uint32_t numOffsets;
+ };
+
+ inline void release(BindGroup& _bindGroup)
+ {
+ _bindGroup.invalidate();
+ }
+
+ struct ComputePipeline
+ {
+ void invalidate()
+ {
+ wgpuRelease(bindGroupLayout);
+ wgpuRelease(pipeline);
+ }
+
+ WGPUBindGroupLayout bindGroupLayout;
+ WGPUComputePipeline pipeline;
+ };
+
+ inline void release(ComputePipeline& _computePipeline)
+ {
+ _computePipeline.invalidate();
+ }
+
+ struct RenderPipeline
+ {
+ void invalidate()
+ {
+ wgpuRelease(bindGroupLayout);
+ wgpuRelease(pipeline);
+ }
+
+ WGPUBindGroupLayout bindGroupLayout;
+ WGPURenderPipeline pipeline;
+ };
+
+ inline void release(RenderPipeline& _renderPipeline)
+ {
+ _renderPipeline.invalidate();
+ }
+
+ struct ProgramWGPU
+ {
+ ProgramWGPU()
+ : m_vsh(NULL)
+ , m_fsh(NULL)
+ {
+ }
+
+ void create(const ShaderWGPU* _vsh, const ShaderWGPU* _fsh);
+ void destroy();
+
+ const ShaderWGPU* m_vsh;
+ const ShaderWGPU* m_fsh;
+
+ PredefinedUniform m_predefined[PredefinedUniform::Count * 2];
+ uint8_t m_numPredefined;
+
+ ShaderBinding m_shaderBinding[BGFX_CONFIG_MAX_TEXTURE_SAMPLERS];
+
+ uint32_t m_numBindings;
+ };
+
+ struct TextureWGPU
+ {
+ enum Enum
+ {
+ Texture2D,
+ Texture3D,
+ TextureCube,
+ };
+
+ TextureWGPU()
+ : m_texture(NULL)
+ , m_textureResolve(NULL)
+ , m_type(Texture2D)
+ {
+ }
+
+ void create(const Memory* _mem, uint64_t _flags, uint8_t _skip);
+ void destroy();
+ void update(uint8_t _side, uint8_t _mip, const Rect& _rect, uint16_t _z, uint16_t _depth, uint16_t _pitch, const Memory* _mem);
+
+ WGPUSampler getSamplerState(uint32_t _samplerFlags) const;
+ WGPUTextureView getTextureView(uint8_t _baseMipLevel, uint8_t _mipLevelCount, bool _storage) const;
+
+ WGPUTexture m_texture;
+ WGPUTexture m_textureResolve;
+ WGPUTextureViewDimension m_viewDimension;
+
+ uint64_t m_flags;
+ uint32_t m_width;
+ uint32_t m_height;
+ uint32_t m_depth;
+ uint32_t m_numLayers;
+ uint32_t m_numSides;
+ uint8_t m_type;
+ uint8_t m_requestedFormat;
+ uint8_t m_textureFormat;
+ uint8_t m_numMips;
+ };
+
+ struct SwapChainWGPU
+ {
+ SwapChainWGPU()
+ : m_nwh(NULL)
+ , m_surface(NULL)
+ , m_textureView(NULL)
+ , m_msaaTextureView(NULL)
+ , m_depthStencilView(NULL)
+ {
+ }
+
+ bool create(void* _nwh, const Resolution& _resolution);
+ void destroy();
+ void update(void* _nwh, const Resolution& _resolution);
+
+ bool createSurface(void* _nwh);
+
+ bool configure(const Resolution& _resolution);
+ void present();
+
+ void* m_nwh;
+ Resolution m_resolution;
+ WGPUSurfaceConfiguration m_surfaceConfig;
+
+ WGPUSurface m_surface;
+ WGPUTextureView m_textureView;
+ WGPUTextureView m_msaaTextureView;
+ WGPUTextureView m_depthStencilView;
+
+ uint8_t m_formatDepthStencil;
+ };
+
+ struct FrameBufferWGPU
+ {
+ FrameBufferWGPU()
+ : m_depth({ kInvalidHandle })
+ , m_depthStencilView(NULL)
+ , m_denseIdx(kInvalidHandle)
+ , m_numColorAttachments(0)
+ , m_numAttachments(0)
+ , m_needPresent(false)
+ {
+ }
+
+ void create(uint8_t _num, const Attachment* _attachment);
+ bool create(uint16_t _denseIdx, void* _nwh, uint32_t _width, uint32_t _height, TextureFormat::Enum _colorFormat, TextureFormat::Enum _depthFormat = TextureFormat::Count);
+ uint16_t destroy();
+
+ void preReset();
+ void postReset();
+
+ void update(const Resolution& _resolution);
+
+ void present();
+
+ bool isSwapChain() const
+ {
+ return m_swapChain.m_nwh;
+ }
+
+ TextureHandle m_texture[BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS];
+ TextureHandle m_depth;
+
+ Attachment m_attachment[BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS];
+ WGPUTextureView m_textureView[BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS];
+ WGPUTextureView m_depthStencilView;
+ uint8_t m_formatDepthStencil;
+
+ uint16_t m_denseIdx;
+ uint8_t m_numColorAttachments;
+ uint8_t m_numAttachments;
+
+ uint32_t m_width;
+ uint32_t m_height;
+
+ SwapChainWGPU m_swapChain;
+ bool m_needPresent;
+ bool m_needResolve;
+ };
+
+ struct CommandQueueWGPU
+ {
+ CommandQueueWGPU()
+ : m_queue(NULL)
+ , m_commandEncoder(NULL)
+ , m_counter(0)
+ {
+ }
+
+ void init(WGPUDevice _device);
+ void shutdown();
+
+ WGPUCommandEncoder alloc();
+ void kick();
+ void wait();
+ void frame();
+
+ void writeBuffer(WGPUBuffer _buffer, uint64_t _bufferOffset, const void* _data, size_t _size) const;
+ void writeTexture(const WGPUTexelCopyTextureInfo& _destination, const void* _data, size_t _size, const WGPUTexelCopyBufferLayout& _source, const WGPUExtent3D& _writeSize) const;
+
+ void copyBufferToBuffer(WGPUBuffer _source, uint64_t _sourceOffset, WGPUBuffer _destination, uint64_t _destinationOffset, uint64_t _size);
+ void copyBufferToTexture(const WGPUTexelCopyBufferInfo& _source, const WGPUTexelCopyTextureInfo& _destination, const WGPUExtent3D& _copySize);
+ void copyTextureToBuffer(const WGPUTexelCopyTextureInfo& source, const WGPUTexelCopyBufferInfo& destination, const WGPUExtent3D& copySize);
+ void copyTextureToTexture(const WGPUTexelCopyTextureInfo& _source, const WGPUTexelCopyTextureInfo& _destination, const WGPUExtent3D& _copySize);
+
+ WGPUQueue m_queue;
+ WGPUCommandEncoder m_commandEncoder;
+ uint32_t m_currentFrameInFlight;
+ uint32_t m_counter;
+ };
+
+ struct TimerQueryWGPU
+ {
+ TimerQueryWGPU()
+ : m_control(BX_COUNTOF(m_result) )
+ {
+ }
+
+ void init();
+ void shutdown();
+ uint32_t begin(uint32_t _resultIdx, uint32_t _frameNum);
+ void end(uint32_t _idx);
+
+ struct Query
+ {
+ uint32_t m_resultIdx;
+ uint32_t m_frameNum;
+ uint64_t m_fence;
+ bool m_ready;
+ };
+
+ struct Result
+ {
+ void reset()
+ {
+ m_begin = 0;
+ m_end = 0;
+ m_pending = 0;
+ m_frameNum = 0;
+ }
+
+ uint64_t m_begin;
+ uint64_t m_end;
+ uint32_t m_pending;
+ uint32_t m_frameNum;
+ };
+
+ uint64_t m_frequency;
+
+ Result m_result[BGFX_CONFIG_MAX_VIEWS+1];
+ Query m_query[BGFX_CONFIG_MAX_VIEWS*4];
+
+ WGPUQuerySet m_querySet;
+ WGPUBuffer m_resolve;
+ WGPUBuffer m_readback;
+
+ bx::RingBufferControl m_control;
+ };
+
+ struct OcclusionQueryWGPU
+ {
+ OcclusionQueryWGPU()
+ : m_querySet(NULL)
+ , m_resolve(NULL)
+ , m_readback(NULL)
+ , m_control(BX_COUNTOF(m_handle) )
+ {
+ }
+
+ void init();
+ void shutdown();
+ void begin(WGPURenderPassEncoder _renderPassEncoder, OcclusionQueryHandle _handle);
+ void end(WGPURenderPassEncoder _renderPassEncoder);
+ void resolve();
+ void readResultsAsync(Frame* _frame);
+ void consumeResults(Frame* _frame);
+ void invalidate(OcclusionQueryHandle _handle);
+
+ WGPUQuerySet m_querySet;
+ WGPUBuffer m_resolve;
+ WGPUBuffer m_readback;
+
+ OcclusionQueryHandle m_handle[BGFX_CONFIG_MAX_OCCLUSION_QUERIES];
+ bx::RingBufferControl m_control;
+ };
+
+} /* namespace bgfx */ } // namespace wgpu
+
+#endif // BGFX_RENDERER_WEBGPU_H_HEADER_GUARD
diff --git a/src/vertexlayout.cpp b/src/vertexlayout.cpp
index ad5729ed4..0532908b0 100644
--- a/src/vertexlayout.cpp
+++ b/src/vertexlayout.cpp
@@ -44,6 +44,7 @@ namespace bgfx
&s_attribTypeSizeGl, // OpenGLES
&s_attribTypeSizeGl, // OpenGL
&s_attribTypeSizeD3D1x, // Vulkan
+ &s_attribTypeSizeD3D1x, // WebGPU
&s_attribTypeSizeD3D1x, // Count
};
static_assert(BX_COUNTOF(s_attribTypeSize) == RendererType::Count+1);