1 // Copyright 2021 The Dawn Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef DAWN_NODE_BINDING_CONVERTER_H_ 16 #define DAWN_NODE_BINDING_CONVERTER_H_ 17 18 #include <functional> 19 #include <type_traits> 20 21 #include "dawn/webgpu_cpp.h" 22 #include "dawn_native/DawnNative.h" 23 #include "napi.h" 24 #include "src/dawn_node/binding/Errors.h" 25 #include "src/dawn_node/interop/WebGPU.h" 26 27 namespace wgpu { namespace binding { 28 29 // ImplOfTraits is a traits helper that is used to associate the interop interface type to the 30 // binding implementation type. 31 template <typename T> 32 struct ImplOfTraits {}; 33 34 // DECLARE_IMPL() is a macro that declares a specialization of ImplOfTraits so that 35 // `typename ImplOfTraits<interop::NAME>::type` is equivalent to `binding::NAME`. 36 #define DECLARE_IMPL(NAME) \ 37 class NAME; \ 38 template <> \ 39 struct ImplOfTraits<interop::NAME> { \ 40 using type = binding::NAME; \ 41 } 42 43 // Declare the interop interface to binding implementations 44 DECLARE_IMPL(GPUBindGroup); 45 DECLARE_IMPL(GPUBindGroupLayout); 46 DECLARE_IMPL(GPUBuffer); 47 DECLARE_IMPL(GPUPipelineLayout); 48 DECLARE_IMPL(GPUQuerySet); 49 DECLARE_IMPL(GPURenderBundle); 50 DECLARE_IMPL(GPURenderPipeline); 51 DECLARE_IMPL(GPUSampler); 52 DECLARE_IMPL(GPUShaderModule); 53 DECLARE_IMPL(GPUTexture); 54 DECLARE_IMPL(GPUTextureView); 55 #undef DECLARE_IMPL 56 57 // Helper for obtaining the binding implementation type from the interop interface type 58 template <typename T> 59 using ImplOf = typename ImplOfTraits<T>::type; 60 61 // Converter is a utility class for converting IDL generated interop types into Dawn types. 62 // As the Dawn C++ API uses raw C pointers for a number of its interfaces, Converter performs 63 // heap allocations for conversions of vector or optional types. These pointers are 64 // automatically freed when the Converter is destructed. 65 class Converter { 66 public: Converter(Napi::Env e)67 Converter(Napi::Env e) : env(e) { 68 } 69 ~Converter(); 70 71 // Conversion function. Converts the interop type IN to the Dawn type OUT. 72 // Returns true on success, false on failure. 73 template <typename OUT, typename IN> operator()74 [[nodiscard]] inline bool operator()(OUT&& out, IN&& in) { 75 return Convert(std::forward<OUT>(out), std::forward<IN>(in)); 76 } 77 78 // Vector conversion function. Converts the vector of interop type IN to a pointer of 79 // elements of Dawn type OUT, which is assigned to 'out_els'. 80 // out_count is assigned the number of elements in 'in'. 81 // Returns true on success, false on failure. 82 // The pointer assigned to 'out_els' is valid until the Converter is destructed. 83 template <typename OUT, typename IN> operator()84 [[nodiscard]] inline bool operator()(OUT*& out_els, 85 uint32_t& out_count, 86 const std::vector<IN>& in) { 87 return Convert(out_els, out_count, in); 88 } 89 90 // Returns the Env that this Converter was constructed with. Env()91 inline Napi::Env Env() const { 92 return env; 93 } 94 95 // BufferSource is the converted type of interop::BufferSource. 96 struct BufferSource { 97 void* data; 98 size_t size; 99 }; 100 101 private: 102 // Below are the various overloads of Convert() used to convert the interop -> Dawn types. 103 [[nodiscard]] bool Convert(wgpu::Extent3D& out, const interop::GPUExtent3D& in); 104 105 [[nodiscard]] bool Convert(wgpu::Origin3D& out, const interop::GPUOrigin3DDict& in); 106 107 [[nodiscard]] bool Convert(wgpu::Color& out, const interop::GPUColor& in); 108 109 [[nodiscard]] bool Convert(wgpu::Origin3D& out, 110 const std::vector<interop::GPUIntegerCoordinate>& in); 111 112 [[nodiscard]] bool Convert(wgpu::TextureAspect& out, const interop::GPUTextureAspect& in); 113 114 [[nodiscard]] bool Convert(wgpu::ImageCopyTexture& out, 115 const interop::GPUImageCopyTexture& in); 116 117 [[nodiscard]] bool Convert(wgpu::ImageCopyBuffer& out, 118 const interop::GPUImageCopyBuffer& in); 119 120 [[nodiscard]] bool Convert(BufferSource& out, interop::BufferSource in); 121 122 [[nodiscard]] bool Convert(wgpu::TextureDataLayout& out, 123 const interop::GPUImageDataLayout& in); 124 125 [[nodiscard]] bool Convert(wgpu::TextureFormat& out, const interop::GPUTextureFormat& in); 126 127 [[nodiscard]] bool Convert(wgpu::TextureUsage& out, 128 const interop::GPUTextureUsageFlags& in); 129 130 [[nodiscard]] bool Convert(wgpu::ColorWriteMask& out, 131 const interop::GPUColorWriteFlags& in); 132 133 [[nodiscard]] bool Convert(wgpu::BufferUsage& out, const interop::GPUBufferUsageFlags& in); 134 135 [[nodiscard]] bool Convert(wgpu::MapMode& out, const interop::GPUMapModeFlags& in); 136 137 [[nodiscard]] bool Convert(wgpu::ShaderStage& out, const interop::GPUShaderStageFlags& in); 138 139 [[nodiscard]] bool Convert(wgpu::TextureDimension& out, 140 const interop::GPUTextureDimension& in); 141 142 [[nodiscard]] bool Convert(wgpu::TextureViewDimension& out, 143 const interop::GPUTextureViewDimension& in); 144 145 [[nodiscard]] bool Convert(wgpu::ProgrammableStageDescriptor& out, 146 const interop::GPUProgrammableStage& in); 147 148 [[nodiscard]] bool Convert(wgpu::ConstantEntry& out, 149 const std::string& in_name, 150 wgpu::interop::GPUPipelineConstantValue in_value); 151 152 [[nodiscard]] bool Convert(wgpu::BlendComponent& out, const interop::GPUBlendComponent& in); 153 154 [[nodiscard]] bool Convert(wgpu::BlendFactor& out, const interop::GPUBlendFactor& in); 155 156 [[nodiscard]] bool Convert(wgpu::BlendOperation& out, const interop::GPUBlendOperation& in); 157 158 [[nodiscard]] bool Convert(wgpu::BlendState& out, const interop::GPUBlendState& in); 159 160 [[nodiscard]] bool Convert(wgpu::PrimitiveState& out, const interop::GPUPrimitiveState& in); 161 162 [[nodiscard]] bool Convert(wgpu::ColorTargetState& out, 163 const interop::GPUColorTargetState& in); 164 165 [[nodiscard]] bool Convert(wgpu::DepthStencilState& out, 166 const interop::GPUDepthStencilState& in); 167 168 [[nodiscard]] bool Convert(wgpu::MultisampleState& out, 169 const interop::GPUMultisampleState& in); 170 171 [[nodiscard]] bool Convert(wgpu::FragmentState& out, const interop::GPUFragmentState& in); 172 173 [[nodiscard]] bool Convert(wgpu::PrimitiveTopology& out, 174 const interop::GPUPrimitiveTopology& in); 175 176 [[nodiscard]] bool Convert(wgpu::FrontFace& out, const interop::GPUFrontFace& in); 177 178 [[nodiscard]] bool Convert(wgpu::CullMode& out, const interop::GPUCullMode& in); 179 180 [[nodiscard]] bool Convert(wgpu::CompareFunction& out, 181 const interop::GPUCompareFunction& in); 182 183 [[nodiscard]] bool Convert(wgpu::IndexFormat& out, const interop::GPUIndexFormat& in); 184 185 [[nodiscard]] bool Convert(wgpu::StencilOperation& out, 186 const interop::GPUStencilOperation& in); 187 188 [[nodiscard]] bool Convert(wgpu::StencilFaceState& out, 189 const interop::GPUStencilFaceState& in); 190 191 [[nodiscard]] bool Convert(wgpu::VertexState& out, const interop::GPUVertexState& in); 192 193 [[nodiscard]] bool Convert(wgpu::VertexBufferLayout& out, 194 const interop::GPUVertexBufferLayout& in); 195 196 [[nodiscard]] bool Convert(wgpu::VertexStepMode& out, const interop::GPUVertexStepMode& in); 197 198 [[nodiscard]] bool Convert(wgpu::VertexAttribute& out, 199 const interop::GPUVertexAttribute& in); 200 201 [[nodiscard]] bool Convert(wgpu::VertexFormat& out, const interop::GPUVertexFormat& in); 202 203 [[nodiscard]] bool Convert(wgpu::RenderPassColorAttachment& out, 204 const interop::GPURenderPassColorAttachment& in); 205 206 [[nodiscard]] bool Convert(wgpu::RenderPassDepthStencilAttachment& out, 207 const interop::GPURenderPassDepthStencilAttachment& in); 208 209 [[nodiscard]] bool Convert(wgpu::LoadOp& out, const interop::GPULoadOp& in); 210 211 [[nodiscard]] bool Convert(wgpu::StoreOp& out, const interop::GPUStoreOp& in); 212 213 [[nodiscard]] bool Convert(wgpu::BindGroupEntry& out, const interop::GPUBindGroupEntry& in); 214 215 [[nodiscard]] bool Convert(wgpu::BindGroupLayoutEntry& out, 216 const interop::GPUBindGroupLayoutEntry& in); 217 218 [[nodiscard]] bool Convert(wgpu::BufferBindingLayout& out, 219 const interop::GPUBufferBindingLayout& in); 220 221 [[nodiscard]] bool Convert(wgpu::SamplerBindingLayout& out, 222 const interop::GPUSamplerBindingLayout& in); 223 224 [[nodiscard]] bool Convert(wgpu::TextureBindingLayout& out, 225 const interop::GPUTextureBindingLayout& in); 226 227 [[nodiscard]] bool Convert(wgpu::StorageTextureBindingLayout& out, 228 const interop::GPUStorageTextureBindingLayout& in); 229 230 [[nodiscard]] bool Convert(wgpu::BufferBindingType& out, 231 const interop::GPUBufferBindingType& in); 232 233 [[nodiscard]] bool Convert(wgpu::SamplerBindingType& out, 234 const interop::GPUSamplerBindingType& in); 235 236 [[nodiscard]] bool Convert(wgpu::TextureSampleType& out, 237 const interop::GPUTextureSampleType& in); 238 239 [[nodiscard]] bool Convert(wgpu::StorageTextureAccess& out, 240 const interop::GPUStorageTextureAccess& in); 241 242 [[nodiscard]] bool Convert(wgpu::QueryType& out, const interop::GPUQueryType& in); 243 244 [[nodiscard]] bool Convert(wgpu::PipelineStatisticName& out, 245 const interop::GPUPipelineStatisticName& in); 246 247 [[nodiscard]] bool Convert(wgpu::AddressMode& out, const interop::GPUAddressMode& in); 248 249 [[nodiscard]] bool Convert(wgpu::FilterMode& out, const interop::GPUFilterMode& in); 250 251 [[nodiscard]] bool Convert(wgpu::ComputePipelineDescriptor& out, 252 const interop::GPUComputePipelineDescriptor& in); 253 254 [[nodiscard]] bool Convert(wgpu::RenderPipelineDescriptor& out, 255 const interop::GPURenderPipelineDescriptor& in); 256 257 // std::string to C string Convert(const char * & out,const std::string & in)258 inline bool Convert(const char*& out, const std::string& in) { 259 out = in.c_str(); 260 return true; 261 } 262 263 // Pass-through (no conversion) 264 template <typename T> Convert(T & out,const T & in)265 inline bool Convert(T& out, const T& in) { 266 out = in; 267 return true; 268 } 269 270 // Integral number conversion, with dynamic limit checking 271 template <typename OUT, 272 typename IN, 273 typename = std::enable_if_t<std::is_integral_v<IN> && std::is_integral_v<OUT>>> Convert(OUT & out,const IN & in)274 inline bool Convert(OUT& out, const IN& in) { 275 out = static_cast<OUT>(in); 276 if (static_cast<IN>(out) != in) { 277 Napi::Error::New(env, "Integer value (" + std::to_string(in) + 278 ") cannot be converted to the Dawn data type without " 279 "truncation of the value") 280 .ThrowAsJavaScriptException(); 281 return false; 282 } 283 return true; 284 } 285 286 template <typename OUT, typename... IN_TYPES> Convert(OUT & out,const std::variant<IN_TYPES...> & in)287 inline bool Convert(OUT& out, const std::variant<IN_TYPES...>& in) { 288 return std::visit([&](auto&& i) { return Convert(out, i); }, in); 289 } 290 291 // If the std::optional does not have a value, then Convert() simply returns true and 'out' 292 // is not assigned a new value. 293 template <typename OUT, typename IN> Convert(OUT & out,const std::optional<IN> & in)294 inline bool Convert(OUT& out, const std::optional<IN>& in) { 295 if (in.has_value()) { 296 return Convert(out, in.value()); 297 } 298 return true; 299 } 300 301 // std::optional -> T* 302 // OUT* is assigned either a pointer to the converted value, or nullptr, depending on 303 // whether 'in' has a value. 304 template <typename OUT, 305 typename IN, 306 typename _ = std::enable_if_t<!std::is_same_v<IN, std::string>>> Convert(OUT * & out,const std::optional<IN> & in)307 inline bool Convert(OUT*& out, const std::optional<IN>& in) { 308 if (in.has_value()) { 309 auto* el = Allocate<std::remove_const_t<OUT>>(); 310 if (!Convert(*el, in.value())) { 311 return false; 312 } 313 out = el; 314 } else { 315 out = nullptr; 316 } 317 return true; 318 } 319 320 // interop::Interface -> Dawn object 321 template <typename OUT, typename IN> Convert(OUT & out,const interop::Interface<IN> & in)322 inline bool Convert(OUT& out, const interop::Interface<IN>& in) { 323 using Impl = ImplOf<IN>; 324 out = *in.template As<Impl>(); 325 if (!out) { 326 LOG("Dawn object has been destroyed. This should not happen"); 327 return false; 328 } 329 return true; 330 } 331 332 // vector -> raw pointer + count 333 template <typename OUT, typename IN> Convert(OUT * & out_els,uint32_t & out_count,const std::vector<IN> & in)334 inline bool Convert(OUT*& out_els, uint32_t& out_count, const std::vector<IN>& in) { 335 if (in.size() == 0) { 336 out_els = nullptr; 337 out_count = 0; 338 return true; 339 } 340 auto* els = Allocate<std::remove_const_t<OUT>>(in.size()); 341 for (size_t i = 0; i < in.size(); i++) { 342 if (!Convert(els[i], in[i])) { 343 return false; 344 } 345 } 346 out_els = els; 347 return Convert(out_count, in.size()); 348 } 349 350 // unordered_map -> raw pointer + count 351 template <typename OUT, typename IN_KEY, typename IN_VALUE> Convert(OUT * & out_els,uint32_t & out_count,const std::unordered_map<IN_KEY,IN_VALUE> & in)352 inline bool Convert(OUT*& out_els, 353 uint32_t& out_count, 354 const std::unordered_map<IN_KEY, IN_VALUE>& in) { 355 if (in.size() == 0) { 356 out_els = nullptr; 357 out_count = 0; 358 return true; 359 } 360 auto* els = Allocate<std::remove_const_t<OUT>>(in.size()); 361 size_t i = 0; 362 for (auto& it : in) { 363 if (!Convert(els[i++], it.first, it.second)) { 364 return false; 365 } 366 } 367 out_els = els; 368 return Convert(out_count, in.size()); 369 } 370 371 // std::optional<T> -> raw pointer + count 372 template <typename OUT, typename IN> Convert(OUT * & out_els,uint32_t & out_count,const std::optional<IN> & in)373 inline bool Convert(OUT*& out_els, uint32_t& out_count, const std::optional<IN>& in) { 374 if (!in.has_value()) { 375 out_els = nullptr; 376 out_count = 0; 377 return true; 378 } 379 return Convert(out_els, out_count, in.value()); 380 } 381 382 Napi::Env env; 383 384 // Allocate() allocates and constructs an array of 'n' elements, and returns a pointer to 385 // the first element. The array is freed when the Converter is destructed. 386 template <typename T> 387 T* Allocate(size_t n = 1) { 388 auto* ptr = new T[n]{}; 389 free_.emplace_back([ptr] { delete[] ptr; }); 390 return ptr; 391 } 392 393 std::vector<std::function<void()>> free_; 394 }; 395 396 }} // namespace wgpu::binding 397 398 #endif // DAWN_NODE_BINDING_CONVERTER_H_ 399