Diff
Modified: trunk/Source/WebGPU/ChangeLog (290266 => 290267)
--- trunk/Source/WebGPU/ChangeLog 2022-02-21 21:32:31 UTC (rev 290266)
+++ trunk/Source/WebGPU/ChangeLog 2022-02-21 21:34:13 UTC (rev 290267)
@@ -1,5 +1,43 @@
2022-02-21 Myles C. Maxfield <[email protected]>
+ [WebGPU] Tracer bullet part 11: Implement shader creation methods
+ https://bugs.webkit.org/show_bug.cgi?id=236933
+
+ Reviewed by Dean Jackson.
+
+ This implements Device::createShaderModule() and Device::createComputePipeline(). The async versions (neither
+ the WebGPU nor the Metal flavor) aren't implemented yet; this is just the first basic implementation.
+
+ * WebGPU/ComputePipeline.mm:
+ (WebGPU::createLibrary):
+ (WebGPU::createConstantValues):
+ (WebGPU::createFunction):
+ (WebGPU::createComputePipelineState):
+ (WebGPU::Device::createComputePipeline):
+ (WebGPU::Device::createComputePipelineAsync):
+ * WebGPU/PipelineLayout.h:
+ * WebGPU/PipelineLayout.mm:
+ (WebGPU::PipelineLayout::operator== const):
+ (WebGPU::PipelineLayout::operator!= const):
+ * WebGPU/ShaderModule.h:
+ (WebGPU::ShaderModule::create):
+ * WebGPU/ShaderModule.mm:
+ (WebGPU::findShaderModuleParameters):
+ (WebGPU::ShaderModule::createLibrary):
+ (WebGPU::earlyCompileShaderModule):
+ (WebGPU::Device::createShaderModule):
+ (WebGPU::ShaderModule::ShaderModule):
+ (WebGPU::CompilationMessageData::CompilationMessageData):
+ (WebGPU::convertMessages):
+ (WebGPU::ShaderModule::getCompilationInfo):
+ (WebGPU::ShaderModule::setLabel):
+ (WebGPU::ShaderModule::convertPipelineLayout):
+ (WebGPU::ShaderModule::ast const):
+ (WebGPU::ShaderModule::pipelineLayoutHint const):
+ (WebGPU::ShaderModule::entryPointInformation const):
+
+2022-02-21 Myles C. Maxfield <[email protected]>
+
[WebGPU] Tracer bullet part 10: Implement setLabel() and Metal accessors
https://bugs.webkit.org/show_bug.cgi?id=236910
Modified: trunk/Source/WebGPU/WebGPU/ComputePipeline.mm (290266 => 290267)
--- trunk/Source/WebGPU/WebGPU/ComputePipeline.mm 2022-02-21 21:32:31 UTC (rev 290266)
+++ trunk/Source/WebGPU/WebGPU/ComputePipeline.mm 2022-02-21 21:34:13 UTC (rev 290267)
@@ -29,17 +29,150 @@
#import "BindGroupLayout.h"
#import "Device.h"
+#import "PipelineLayout.h"
+#import "ShaderModule.h"
namespace WebGPU {
+struct LibraryCreationResult {
+ id <MTLLibrary> library;
+ WGSL::Reflection::EntryPointInformation entryPointInformation; // FIXME: This is big. Don't copy this around.
+};
+
+static std::optional<LibraryCreationResult> createLibrary(id <MTLDevice> device, const ShaderModule& shaderModule, const PipelineLayout& pipelineLayout, const String& entryPoint, NSString *label)
+{
+ if (shaderModule.library()) {
+ if (const auto* pipelineLayoutHint = shaderModule.pipelineLayoutHint(entryPoint)) {
+ if (*pipelineLayoutHint == pipelineLayout) {
+ if (const auto* entryPointInformation = shaderModule.entryPointInformation(entryPoint))
+ return { { shaderModule.library(), *entryPointInformation } };
+ }
+ }
+ }
+
+ const auto* ast = shaderModule.ast();
+ if (!ast)
+ return std::nullopt;
+
+ auto prepareResult = WGSL::prepare(*ast, entryPoint, ShaderModule::convertPipelineLayout(pipelineLayout));
+
+ auto library = ShaderModule::createLibrary(device, prepareResult.msl, label);
+
+ auto iterator = prepareResult.entryPoints.find(entryPoint);
+ if (iterator == prepareResult.entryPoints.end())
+ return std::nullopt;
+ const auto& entryPointInformation = iterator->value;
+
+ return { { library, entryPointInformation } };
+}
+
+static MTLFunctionConstantValues *createConstantValues(uint32_t constantCount, const WGPUConstantEntry* constants, const WGSL::Reflection::EntryPointInformation& entryPointInformation)
+{
+ auto constantValues = [MTLFunctionConstantValues new];
+ for (uint32_t i = 0; i < constantCount; ++i) {
+ const auto& entry = constants[i];
+ auto nameIterator = entryPointInformation.specializationConstantIndices.find(entry.key);
+ if (nameIterator == entryPointInformation.specializationConstantIndices.end())
+ return nullptr;
+ auto specializationConstantIndex = nameIterator->value;
+ auto indexIterator = entryPointInformation.specializationConstants.find(specializationConstantIndex);
+ if (indexIterator == entryPointInformation.specializationConstants.end())
+ return nullptr;
+ const auto& specializationConstant = indexIterator->value;
+ switch (specializationConstant.type) {
+ case WGSL::Reflection::SpecializationConstantType::Boolean: {
+ bool value = entry.value;
+ [constantValues setConstantValue:&value type:MTLDataTypeBool withName:specializationConstant.mangledName];
+ break;
+ }
+ case WGSL::Reflection::SpecializationConstantType::Float: {
+ float value = entry.value;
+ [constantValues setConstantValue:&value type:MTLDataTypeFloat withName:specializationConstant.mangledName];
+ break;
+ }
+ case WGSL::Reflection::SpecializationConstantType::Int: {
+ int value = entry.value;
+ [constantValues setConstantValue:&value type:MTLDataTypeInt withName:specializationConstant.mangledName];
+ break;
+ }
+ case WGSL::Reflection::SpecializationConstantType::Unsigned: {
+ unsigned value = entry.value;
+ [constantValues setConstantValue:&value type:MTLDataTypeUInt withName:specializationConstant.mangledName];
+ break;
+ }
+ default:
+ return nullptr;
+ }
+ }
+ return constantValues;
+}
+
+static id <MTLFunction> createFunction(id <MTLLibrary> library, const WGSL::Reflection::EntryPointInformation& entryPointInformation, const WGPUProgrammableStageDescriptor& compute, NSString *label)
+{
+ auto functionDescriptor = [MTLFunctionDescriptor new];
+ functionDescriptor.name = entryPointInformation.mangledName;
+ if (compute.constantCount) {
+ auto constantValues = createConstantValues(compute.constantCount, compute.constants, entryPointInformation);
+ if (!constantValues)
+ return nullptr;
+ functionDescriptor.constantValues = constantValues;
+ }
+ NSError *error = nil;
+ id <MTLFunction> function = [library newFunctionWithDescriptor:functionDescriptor error:&error];
+ if (error)
+ WTFLogAlways("Function creation error: %@", error);
+ function.label = label;
+ return function;
+}
+
+static id <MTLComputePipelineState> createComputePipelineState(id <MTLDevice> device, id <MTLFunction> function, const PipelineLayout& pipelineLayout, const WGSL::Reflection::Compute& computeInformation, NSString *label)
+{
+ auto computePipelineDescriptor = [MTLComputePipelineDescriptor new];
+ computePipelineDescriptor.computeFunction = function;
+ // FIXME: check this calculation for overflow
+ computePipelineDescriptor.maxTotalThreadsPerThreadgroup = computeInformation.workgroupSize.width * computeInformation.workgroupSize.height * computeInformation.workgroupSize.depth;
+ for (size_t i = 0; i < pipelineLayout.numberOfBindGroupLayouts(); ++i)
+ computePipelineDescriptor.buffers[i].mutability = MTLMutabilityImmutable; // Argument buffers are always immutable in WebGPU.
+ computePipelineDescriptor.supportIndirectCommandBuffers = YES;
+ computePipelineDescriptor.label = label;
+ NSError *error = nil;
+ // FIXME: Run the asynchronous version of this
+ id <MTLComputePipelineState> computePipelineState = [device newComputePipelineStateWithDescriptor:computePipelineDescriptor options:MTLPipelineOptionNone reflection:nil error:&error];
+ if (error)
+ WTFLogAlways("Pipeline state creation error: %@", error);
+ return computePipelineState;
+}
+
RefPtr<ComputePipeline> Device::createComputePipeline(const WGPUComputePipelineDescriptor* descriptor)
{
- UNUSED_PARAM(descriptor);
- return ComputePipeline::create(nil);
+ if (descriptor->nextInChain || descriptor->compute.nextInChain)
+ return nullptr;
+
+ const ShaderModule& shaderModule = descriptor->compute.module->shaderModule;
+ const PipelineLayout& pipelineLayout = descriptor->layout->pipelineLayout;
+ auto label = [NSString stringWithCString:descriptor->label encoding:NSUTF8StringEncoding];
+
+ auto libraryCreationResult = createLibrary(m_device, shaderModule, pipelineLayout, descriptor->compute.entryPoint, label);
+ if (!libraryCreationResult)
+ return nullptr;
+
+ auto library = libraryCreationResult->library;
+ const auto& entryPointInformation = libraryCreationResult->entryPointInformation;
+
+ if (!std::holds_alternative<WGSL::Reflection::Compute>(entryPointInformation.typedEntryPoint))
+ return nullptr;
+ const auto& computeInformation = std::get<WGSL::Reflection::Compute>(entryPointInformation.typedEntryPoint);
+
+ auto function = createFunction(library, entryPointInformation, descriptor->compute, label);
+
+ auto computePipelineState = createComputePipelineState(m_device, function, pipelineLayout, computeInformation, label);
+
+ return ComputePipeline::create(computePipelineState);
}
void Device::createComputePipelineAsync(const WGPUComputePipelineDescriptor* descriptor, WTF::Function<void(WGPUCreatePipelineAsyncStatus, RefPtr<ComputePipeline>&&, const char* message)>&& callback)
{
+ // FIXME: Implement this
UNUSED_PARAM(descriptor);
UNUSED_PARAM(callback);
}
Modified: trunk/Source/WebGPU/WebGPU/PipelineLayout.h (290266 => 290267)
--- trunk/Source/WebGPU/WebGPU/PipelineLayout.h 2022-02-21 21:32:31 UTC (rev 290266)
+++ trunk/Source/WebGPU/WebGPU/PipelineLayout.h 2022-02-21 21:34:13 UTC (rev 290267)
@@ -46,6 +46,9 @@
void setLabel(const char*);
+ bool operator==(const PipelineLayout&) const;
+ bool operator!=(const PipelineLayout&) const;
+
size_t numberOfBindGroupLayouts() const { return m_bindGroupLayouts.size(); }
const BindGroupLayout& bindGroupLayout(size_t i) const { return m_bindGroupLayouts[i]; }
Modified: trunk/Source/WebGPU/WebGPU/PipelineLayout.mm (290266 => 290267)
--- trunk/Source/WebGPU/WebGPU/PipelineLayout.mm 2022-02-21 21:32:31 UTC (rev 290266)
+++ trunk/Source/WebGPU/WebGPU/PipelineLayout.mm 2022-02-21 21:34:13 UTC (rev 290267)
@@ -57,8 +57,20 @@
// There is no Metal object that represents a pipeline layout.
}
+bool PipelineLayout::operator==(const PipelineLayout& other) const
+{
+ UNUSED_PARAM(other);
+ // FIXME: Implement this
+ return false;
}
+bool PipelineLayout::operator!=(const PipelineLayout& other) const
+{
+ return !(*this == other);
+}
+
+}
+
void wgpuPipelineLayoutRelease(WGPUPipelineLayout pipelineLayout)
{
delete pipelineLayout;
Modified: trunk/Source/WebGPU/WebGPU/ShaderModule.h (290266 => 290267)
--- trunk/Source/WebGPU/WebGPU/ShaderModule.h 2022-02-21 21:32:31 UTC (rev 290266)
+++ trunk/Source/WebGPU/WebGPU/ShaderModule.h 2022-02-21 21:34:13 UTC (rev 290267)
@@ -25,18 +25,23 @@
#pragma once
+#import "WGSL.h"
#import <wtf/FastMalloc.h>
#import <wtf/Ref.h>
#import <wtf/RefCounted.h>
+#import <wtf/text/StringHash.h>
+#import <wtf/text/WTFString.h>
namespace WebGPU {
+class PipelineLayout;
+
class ShaderModule : public RefCounted<ShaderModule> {
WTF_MAKE_FAST_ALLOCATED;
public:
- static Ref<ShaderModule> create(id <MTLLibrary> library)
+ static Ref<ShaderModule> create(std::variant<WGSL::SuccessfulCheck, WGSL::FailedCheck>&& checkResult, HashMap<String, Ref<PipelineLayout>>&& pipelineLayoutHints, HashMap<String, WGSL::Reflection::EntryPointInformation>&& entryPointInformation, id <MTLLibrary> library)
{
- return adoptRef(*new ShaderModule(library));
+ return adoptRef(*new ShaderModule(WTFMove(checkResult), WTFMove(pipelineLayoutHints), WTFMove(entryPointInformation), library));
}
~ShaderModule();
@@ -44,12 +49,22 @@
void getCompilationInfo(WTF::Function<void(WGPUCompilationInfoRequestStatus, const WGPUCompilationInfo*)>&& callback);
void setLabel(const char*);
+ static WGSL::PipelineLayout convertPipelineLayout(const PipelineLayout&);
+ static id <MTLLibrary> createLibrary(id <MTLDevice>, const String& msl, NSString *label);
+
+ const WGSL::AST* ast() const;
+
+ const PipelineLayout* pipelineLayoutHint(const String&) const;
+ const WGSL::Reflection::EntryPointInformation* entryPointInformation(const String&) const;
id <MTLLibrary> library() const { return m_library; }
private:
- ShaderModule(id <MTLLibrary>);
+ ShaderModule(std::variant<WGSL::SuccessfulCheck, WGSL::FailedCheck>&&, HashMap<String, Ref<PipelineLayout>>&&, HashMap<String, WGSL::Reflection::EntryPointInformation>&&, id <MTLLibrary>);
- id <MTLLibrary> m_library { nil };
+ const std::variant<WGSL::SuccessfulCheck, WGSL::FailedCheck> m_checkResult;
+ const HashMap<String, Ref<PipelineLayout>> m_pipelineLayoutHints;
+ const HashMap<String, WGSL::Reflection::EntryPointInformation> m_entryPointInformation;
+ id <MTLLibrary> m_library { nil }; // This is only non-null if we could compile the module early.
};
} // namespace WebGPU
Modified: trunk/Source/WebGPU/WebGPU/ShaderModule.mm (290266 => 290267)
--- trunk/Source/WebGPU/WebGPU/ShaderModule.mm 2022-02-21 21:32:31 UTC (rev 290266)
+++ trunk/Source/WebGPU/WebGPU/ShaderModule.mm 2022-02-21 21:34:13 UTC (rev 290267)
@@ -27,32 +27,222 @@
#import "ShaderModule.h"
#import "Device.h"
+#import "PipelineLayout.h"
namespace WebGPU {
+struct ShaderModuleParameters {
+ const WGPUShaderModuleWGSLDescriptor& wgsl;
+ const WGPUShaderModuleDescriptorHints* hints;
+};
+
+static std::optional<ShaderModuleParameters> findShaderModuleParameters(const WGPUShaderModuleDescriptor* descriptor)
+{
+ const WGPUShaderModuleWGSLDescriptor* wgsl = nullptr;
+ const WGPUShaderModuleDescriptorHints* hints = nullptr;
+
+ for (const WGPUChainedStruct* ptr = descriptor->nextInChain; ptr; ptr = ptr->next) {
+ auto type = ptr->sType;
+
+ switch (static_cast<int>(type)) {
+ case WGPUSType_ShaderModuleWGSLDescriptor:
+ if (wgsl)
+ return std::nullopt;
+ wgsl = reinterpret_cast<const WGPUShaderModuleWGSLDescriptor*>(ptr);
+ break;
+ case WGPUSTypeExtended_ShaderModuleDescriptorHints:
+ if (hints)
+ return std::nullopt;
+ hints = reinterpret_cast<const WGPUShaderModuleDescriptorHints*>(ptr);
+ break;
+ default:
+ return std::nullopt;
+ }
+ }
+
+ if (!wgsl)
+ return std::nullopt;
+
+ return { { *wgsl, hints } };
+}
+
+id <MTLLibrary> ShaderModule::createLibrary(id <MTLDevice> device, const String& msl, NSString *label)
+{
+ auto options = [MTLCompileOptions new];
+ options.fastMathEnabled = YES;
+ NSError *error = nil;
+ // FIXME: Run the asynchronous version of this
+ id <MTLLibrary> library = [device newLibraryWithSource:msl options:options error:&error];
+ if (error)
+ WTFLogAlways("MSL compilation error: %@", error);
+ library.label = label;
+ return library;
+}
+
+static RefPtr<ShaderModule> earlyCompileShaderModule(id <MTLDevice> device, std::variant<WGSL::SuccessfulCheck, WGSL::FailedCheck>&& checkResult, const WGPUShaderModuleDescriptorHints& suppliedHints, NSString *label)
+{
+ HashMap<String, Ref<PipelineLayout>> hints;
+ HashMap<String, WGSL::PipelineLayout> wgslHints;
+ for (uint32_t i = 0; i < suppliedHints.hintsCount; ++i) {
+ const auto& hint = suppliedHints.hints[i];
+ if (hint.nextInChain)
+ return nullptr;
+ hints.add(hint.key, hint.hint.layout->pipelineLayout);
+ auto convertedPipelineLayout = ShaderModule::convertPipelineLayout(hint.hint.layout->pipelineLayout);
+ wgslHints.add(hint.key, WTFMove(convertedPipelineLayout));
+ }
+ auto prepareResult = WGSL::prepare(std::get<WGSL::SuccessfulCheck>(checkResult).ast, wgslHints);
+ auto library = ShaderModule::createLibrary(device, prepareResult.msl, label);
+ if (!library)
+ return nullptr;
+ return ShaderModule::create(WTFMove(checkResult), WTFMove(hints), WTFMove(prepareResult.entryPoints), library);
+}
+
RefPtr<ShaderModule> Device::createShaderModule(const WGPUShaderModuleDescriptor* descriptor)
{
- UNUSED_PARAM(descriptor);
- return ShaderModule::create(nil);
+ if (!descriptor->nextInChain)
+ return nullptr;
+
+ auto shaderModuleParameters = findShaderModuleParameters(descriptor);
+ if (!shaderModuleParameters)
+ return nullptr;
+
+ auto checkResult = WGSL::staticCheck(String(shaderModuleParameters->wgsl.code), std::nullopt);
+
+ if (std::holds_alternative<WGSL::SuccessfulCheck>(checkResult) && shaderModuleParameters->hints && shaderModuleParameters->hints->hintsCount) {
+ if (auto result = earlyCompileShaderModule(m_device, WTFMove(checkResult), *shaderModuleParameters->hints, [NSString stringWithCString:descriptor->label encoding:NSUTF8StringEncoding]))
+ return result;
+ }
+
+ return ShaderModule::create(WTFMove(checkResult), { }, { }, nil);
}
-ShaderModule::ShaderModule(id <MTLLibrary> library)
- : m_library(library)
+ShaderModule::ShaderModule(std::variant<WGSL::SuccessfulCheck, WGSL::FailedCheck>&& checkResult, HashMap<String, Ref<PipelineLayout>>&& pipelineLayoutHints, HashMap<String, WGSL::Reflection::EntryPointInformation>&& entryPointInformation, id <MTLLibrary> library)
+ : m_checkResult(WTFMove(checkResult))
+ , m_pipelineLayoutHints(WTFMove(pipelineLayoutHints))
+ , m_entryPointInformation(WTFMove(entryPointInformation))
+ , m_library(library)
{
}
ShaderModule::~ShaderModule() = default;
+struct Messages {
+ const Vector<WGSL::CompilationMessage>& messages;
+ WGPUCompilationMessageType type;
+};
+
+struct CompilationMessageData {
+ CompilationMessageData(Vector<WGPUCompilationMessage>&& compilationMessages, Vector<CString>&& messages)
+ : compilationMessages(WTFMove(compilationMessages))
+ , messages(WTFMove(messages))
+ {
+ }
+
+ CompilationMessageData(const CompilationMessageData&) = delete;
+ CompilationMessageData(CompilationMessageData&&) = default;
+
+ Vector<WGPUCompilationMessage> compilationMessages;
+ Vector<CString> messages;
+};
+
+static CompilationMessageData convertMessages(const Messages& messages1, const std::optional<Messages>& messages2 = std::nullopt)
+{
+ Vector<WGPUCompilationMessage> flattenedCompilationMessages;
+ Vector<CString> flattenedMessages;
+
+ auto populateMessages = [&] (const Messages& compilationMessages) {
+ for (const auto& compilationMessage : compilationMessages.messages)
+ flattenedMessages.append(compilationMessage.message.utf8());
+ };
+
+ populateMessages(messages1);
+ if (messages2)
+ populateMessages(*messages2);
+
+ auto populateCompilationMessages = [&] (const Messages& compilationMessages, size_t base) {
+ for (size_t i = 0; i < compilationMessages.messages.size(); ++i) {
+ const auto& compilationMessage = compilationMessages.messages[i];
+ flattenedCompilationMessages.append({
+ nullptr,
+ flattenedMessages[i + base].data(),
+ compilationMessages.type,
+ compilationMessage.lineNumber,
+ compilationMessage.linePosition,
+ compilationMessage.offset,
+ compilationMessage.length,
+ });
+ }
+ };
+
+ populateCompilationMessages(messages1, 0);
+ if (messages2)
+ populateCompilationMessages(*messages2, messages1.messages.size());
+
+ return { WTFMove(flattenedCompilationMessages), WTFMove(flattenedMessages) };
+}
+
void ShaderModule::getCompilationInfo(WTF::Function<void(WGPUCompilationInfoRequestStatus, const WGPUCompilationInfo*)>&& callback)
{
- UNUSED_PARAM(callback);
+ WTF::switchOn(m_checkResult, [&] (const WGSL::SuccessfulCheck& successfulCheck) {
+ auto compilationMessageData(convertMessages({ successfulCheck.warnings, WGPUCompilationMessageType_Warning }));
+ WGPUCompilationInfo compilationInfo {
+ nullptr,
+ static_cast<uint32_t>(compilationMessageData.compilationMessages.size()),
+ compilationMessageData.compilationMessages.data(),
+ };
+ callback(WGPUCompilationInfoRequestStatus_Success, &compilationInfo);
+ }, [&] (const WGSL::FailedCheck& failedCheck) {
+ auto compilationMessageData(convertMessages(
+ { failedCheck.errors, WGPUCompilationMessageType_Error },
+ { { failedCheck.warnings, WGPUCompilationMessageType_Warning } }));
+ WGPUCompilationInfo compilationInfo {
+ nullptr,
+ static_cast<uint32_t>(compilationMessageData.compilationMessages.size()),
+ compilationMessageData.compilationMessages.data(),
+ };
+ callback(WGPUCompilationInfoRequestStatus_Error, &compilationInfo);
+ });
}
void ShaderModule::setLabel(const char* label)
{
- m_library.label = [NSString stringWithCString:label encoding:NSUTF8StringEncoding];
+ if (m_library)
+ m_library.label = [NSString stringWithCString:label encoding:NSUTF8StringEncoding];
}
+WGSL::PipelineLayout ShaderModule::convertPipelineLayout(const PipelineLayout& pipelineLayout)
+{
+ UNUSED_PARAM(pipelineLayout);
+ // FIXME: Implement this
+ return { { } };
+}
+
+const WGSL::AST* ShaderModule::ast() const
+{
+ return WTF::switchOn(m_checkResult, [&] (const WGSL::SuccessfulCheck& successfulCheck) -> const WGSL::AST* {
+ return successfulCheck.ast.ptr();
+ }, [&] (const WGSL::FailedCheck&) -> const WGSL::AST* {
+ return nullptr;
+ });
+}
+
+const PipelineLayout* ShaderModule::pipelineLayoutHint(const String& name) const
+{
+ auto iterator = m_pipelineLayoutHints.find(name);
+ if (iterator == m_pipelineLayoutHints.end())
+ return nullptr;
+ return iterator->value.ptr();
+}
+
+const WGSL::Reflection::EntryPointInformation* ShaderModule::entryPointInformation(const String& name) const
+{
+ auto iterator = m_entryPointInformation.find(name);
+ if (iterator == m_entryPointInformation.end())
+ return nullptr;
+ return &iterator->value;
+}
+
} // namespace WebGPU
void wgpuShaderModuleRelease(WGPUShaderModule shaderModule)