Use Tint SingleEntryPoint transform in Vulkan/GL backends
Some Vulkan drivers don't handle multiple entrypoints well.
In addition, SPIRV-Cross translation can be wrong for
shader modules with multiple entrypoints. Always emit a single
SPIR-V entrypoint to workaround these issues.
This allows updating CopyTextureForBrowser to use a single
shader module, and it fixes some tests with multiple
entrypoints.
Fixed: dawn:948, dawn:959, dawn:1013
Change-Id: Ie129a32a54845316d11917331937ca44fba3d347
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/60640
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/dawn_native/CopyTextureForBrowserHelper.cpp b/src/dawn_native/CopyTextureForBrowserHelper.cpp
index 46da2c8..d64870c 100644
--- a/src/dawn_native/CopyTextureForBrowserHelper.cpp
+++ b/src/dawn_native/CopyTextureForBrowserHelper.cpp
@@ -33,17 +33,14 @@
namespace dawn_native {
namespace {
-// TODO(crbug.com/1221110): Remove this header macro by merging vertex and
-// fragment shaders into one shader source. Now it blocks by
-// crbug.com/dawn/947 and crbug.com/tint/915
-#define HEADER \
- " [[block]] struct Uniforms {\n" \
- " u_scale: vec2<f32>;\n" \
- " u_offset: vec2<f32>;\n" \
- " u_alphaOp: u32;\n" \
- " };\n"
- static const char sCopyTextureForBrowserVertex[] = HEADER R"(
+ static const char sCopyTextureForBrowserShader[] = R"(
+ [[block]] struct Uniforms {
+ u_scale: vec2<f32>;
+ u_offset: vec2<f32>;
+ u_alphaOp: u32;
+ };
+
[[binding(0), group(0)]] var<uniform> uniforms : Uniforms;
struct VertexOutputs {
@@ -51,7 +48,7 @@
[[builtin(position)]] position : vec4<f32>;
};
- [[stage(vertex)]] fn main(
+ [[stage(vertex)]] fn vs_main(
[[builtin(vertex_index)]] VertexIndex : u32
) -> VertexOutputs {
var texcoord = array<vec2<f32>, 3>(
@@ -84,14 +81,11 @@
return output;
}
- )";
- static const char sCopyTextureForBrowserFragment[] = HEADER R"(
- [[binding(0), group(0)]] var<uniform> uniforms : Uniforms;
[[binding(1), group(0)]] var mySampler: sampler;
[[binding(2), group(0)]] var myTexture: texture_2d<f32>;
- [[stage(fragment)]] fn main(
+ [[stage(fragment)]] fn fs_main(
[[location(0)]] texcoord : vec2<f32>
) -> [[location(0)]] vec4<f32> {
// Clamp the texcoord and discard the out-of-bound pixels.
@@ -226,39 +220,27 @@
if (GetCachedPipeline(store, dstFormat) == nullptr) {
// Create vertex shader module if not cached before.
- if (store->copyTextureForBrowserVS == nullptr) {
+ if (store->copyTextureForBrowser == nullptr) {
ShaderModuleDescriptor descriptor;
ShaderModuleWGSLDescriptor wgslDesc;
- wgslDesc.source = sCopyTextureForBrowserVertex;
+ wgslDesc.source = sCopyTextureForBrowserShader;
descriptor.nextInChain = reinterpret_cast<ChainedStruct*>(&wgslDesc);
- DAWN_TRY_ASSIGN(store->copyTextureForBrowserVS,
+ DAWN_TRY_ASSIGN(store->copyTextureForBrowser,
device->CreateShaderModule(&descriptor));
}
- ShaderModuleBase* vertexModule = store->copyTextureForBrowserVS.Get();
-
- // Create fragment shader module if not cached before.
- if (store->copyTextureForBrowserFS == nullptr) {
- ShaderModuleDescriptor descriptor;
- ShaderModuleWGSLDescriptor wgslDesc;
- wgslDesc.source = sCopyTextureForBrowserFragment;
- descriptor.nextInChain = reinterpret_cast<ChainedStruct*>(&wgslDesc);
- DAWN_TRY_ASSIGN(store->copyTextureForBrowserFS,
- device->CreateShaderModule(&descriptor));
- }
-
- ShaderModuleBase* fragmentModule = store->copyTextureForBrowserFS.Get();
+ ShaderModuleBase* shaderModule = store->copyTextureForBrowser.Get();
// Prepare vertex stage.
VertexState vertex = {};
- vertex.module = vertexModule;
- vertex.entryPoint = "main";
+ vertex.module = shaderModule;
+ vertex.entryPoint = "vs_main";
// Prepare frgament stage.
FragmentState fragment = {};
- fragment.module = fragmentModule;
- fragment.entryPoint = "main";
+ fragment.module = shaderModule;
+ fragment.entryPoint = "fs_main";
// Prepare color state.
ColorTargetState target = {};
diff --git a/src/dawn_native/InternalPipelineStore.h b/src/dawn_native/InternalPipelineStore.h
index 1d90159..3066a9a 100644
--- a/src/dawn_native/InternalPipelineStore.h
+++ b/src/dawn_native/InternalPipelineStore.h
@@ -28,8 +28,7 @@
std::unordered_map<wgpu::TextureFormat, Ref<RenderPipelineBase>>
copyTextureForBrowserPipelines;
- Ref<ShaderModuleBase> copyTextureForBrowserVS;
- Ref<ShaderModuleBase> copyTextureForBrowserFS;
+ Ref<ShaderModuleBase> copyTextureForBrowser;
Ref<ComputePipelineBase> timestampComputePipeline;
Ref<ShaderModuleBase> timestampCS;
diff --git a/src/dawn_native/opengl/ComputePipelineGL.cpp b/src/dawn_native/opengl/ComputePipelineGL.cpp
index 837b928..7680cee 100644
--- a/src/dawn_native/opengl/ComputePipelineGL.cpp
+++ b/src/dawn_native/opengl/ComputePipelineGL.cpp
@@ -18,12 +18,19 @@
namespace dawn_native { namespace opengl {
- ComputePipeline::ComputePipeline(Device* device, const ComputePipelineDescriptor* descriptor)
- : ComputePipelineBase(device, descriptor) {
- PerStage<const ShaderModule*> modules(nullptr);
- modules[SingleShaderStage::Compute] = ToBackend(descriptor->compute.module);
+ // static
+ ResultOrError<Ref<ComputePipeline>> ComputePipeline::Create(
+ Device* device,
+ const ComputePipelineDescriptor* descriptor) {
+ Ref<ComputePipeline> pipeline = AcquireRef(new ComputePipeline(device, descriptor));
+ DAWN_TRY(pipeline->Initialize(descriptor));
+ return pipeline;
+ }
- PipelineGL::Initialize(device->gl, ToBackend(descriptor->layout), GetAllStages());
+ MaybeError ComputePipeline::Initialize(const ComputePipelineDescriptor*) {
+ DAWN_TRY(
+ InitializeBase(ToBackend(GetDevice())->gl, ToBackend(GetLayout()), GetAllStages()));
+ return {};
}
void ComputePipeline::ApplyNow() {
diff --git a/src/dawn_native/opengl/ComputePipelineGL.h b/src/dawn_native/opengl/ComputePipelineGL.h
index 95f6db9..e84e366 100644
--- a/src/dawn_native/opengl/ComputePipelineGL.h
+++ b/src/dawn_native/opengl/ComputePipelineGL.h
@@ -27,12 +27,16 @@
class ComputePipeline final : public ComputePipelineBase, public PipelineGL {
public:
- ComputePipeline(Device* device, const ComputePipelineDescriptor* descriptor);
+ static ResultOrError<Ref<ComputePipeline>> Create(
+ Device* device,
+ const ComputePipelineDescriptor* descriptor);
void ApplyNow();
private:
+ using ComputePipelineBase::ComputePipelineBase;
~ComputePipeline() override = default;
+ MaybeError Initialize(const ComputePipelineDescriptor* descriptor) override;
};
}} // namespace dawn_native::opengl
diff --git a/src/dawn_native/opengl/DeviceGL.cpp b/src/dawn_native/opengl/DeviceGL.cpp
index dd38faa..fb7b4ea 100644
--- a/src/dawn_native/opengl/DeviceGL.cpp
+++ b/src/dawn_native/opengl/DeviceGL.cpp
@@ -129,7 +129,7 @@
}
ResultOrError<Ref<ComputePipelineBase>> Device::CreateComputePipelineImpl(
const ComputePipelineDescriptor* descriptor) {
- return AcquireRef(new ComputePipeline(this, descriptor));
+ return ComputePipeline::Create(this, descriptor);
}
ResultOrError<Ref<PipelineLayoutBase>> Device::CreatePipelineLayoutImpl(
const PipelineLayoutDescriptor* descriptor) {
@@ -141,7 +141,7 @@
}
ResultOrError<Ref<RenderPipelineBase>> Device::CreateRenderPipelineImpl(
const RenderPipelineDescriptor* descriptor) {
- return AcquireRef(new RenderPipeline(this, descriptor));
+ return RenderPipeline::Create(this, descriptor);
}
ResultOrError<Ref<SamplerBase>> Device::CreateSamplerImpl(const SamplerDescriptor* descriptor) {
return AcquireRef(new Sampler(this, descriptor));
diff --git a/src/dawn_native/opengl/PipelineGL.cpp b/src/dawn_native/opengl/PipelineGL.cpp
index 0110025..4541e65 100644
--- a/src/dawn_native/opengl/PipelineGL.cpp
+++ b/src/dawn_native/opengl/PipelineGL.cpp
@@ -15,7 +15,6 @@
#include "dawn_native/opengl/PipelineGL.h"
#include "common/BitSetIterator.h"
-#include "common/Log.h"
#include "dawn_native/BindGroupLayout.h"
#include "dawn_native/Device.h"
#include "dawn_native/Pipeline.h"
@@ -26,6 +25,7 @@
#include "dawn_native/opengl/ShaderModuleGL.h"
#include <set>
+#include <sstream>
namespace dawn_native { namespace opengl {
@@ -47,11 +47,11 @@
PipelineGL::PipelineGL() = default;
PipelineGL::~PipelineGL() = default;
- void PipelineGL::Initialize(const OpenGLFunctions& gl,
- const PipelineLayout* layout,
- const PerStage<ProgrammableStage>& stages) {
+ MaybeError PipelineGL::InitializeBase(const OpenGLFunctions& gl,
+ const PipelineLayout* layout,
+ const PerStage<ProgrammableStage>& stages) {
auto CreateShader = [](const OpenGLFunctions& gl, GLenum type,
- const char* source) -> GLuint {
+ const char* source) -> ResultOrError<GLuint> {
GLuint shader = gl.CreateShader(type);
gl.ShaderSource(shader, 1, &source, nullptr);
gl.CompileShader(shader);
@@ -65,8 +65,9 @@
if (infoLogLength > 1) {
std::vector<char> buffer(infoLogLength);
gl.GetShaderInfoLog(shader, infoLogLength, nullptr, &buffer[0]);
- dawn::ErrorLog() << source << "\nProgram compilation failed:\n"
- << buffer.data();
+ std::stringstream ss;
+ ss << source << "\nProgram compilation failed:\n" << buffer.data();
+ return DAWN_VALIDATION_ERROR(ss.str().c_str());
}
}
return shader;
@@ -87,10 +88,12 @@
bool needsDummySampler = false;
for (SingleShaderStage stage : IterateStages(activeStages)) {
const ShaderModule* module = ToBackend(stages[stage].module.Get());
- std::string glsl =
- module->TranslateToGLSL(stages[stage].entryPoint.c_str(), stage,
- &combinedSamplers[stage], layout, &needsDummySampler);
- GLuint shader = CreateShader(gl, GLShaderType(stage), glsl.c_str());
+ std::string glsl;
+ DAWN_TRY_ASSIGN(glsl, module->TranslateToGLSL(stages[stage].entryPoint.c_str(), stage,
+ &combinedSamplers[stage], layout,
+ &needsDummySampler));
+ GLuint shader;
+ DAWN_TRY_ASSIGN(shader, CreateShader(gl, GLShaderType(stage), glsl.c_str()));
gl.AttachShader(mProgram, shader);
}
@@ -115,7 +118,9 @@
if (infoLogLength > 1) {
std::vector<char> buffer(infoLogLength);
gl.GetProgramInfoLog(mProgram, infoLogLength, nullptr, &buffer[0]);
- dawn::ErrorLog() << "Program link failed:\n" << buffer.data();
+ std::stringstream ss;
+ ss << "Program link failed:\n" << buffer.data();
+ return DAWN_VALIDATION_ERROR(ss.str().c_str());
}
}
@@ -172,6 +177,7 @@
textureUnit++;
}
+ return {};
}
const std::vector<PipelineGL::SamplerUnit>& PipelineGL::GetTextureUnitsForSampler(
diff --git a/src/dawn_native/opengl/PipelineGL.h b/src/dawn_native/opengl/PipelineGL.h
index 33e5341..e210606 100644
--- a/src/dawn_native/opengl/PipelineGL.h
+++ b/src/dawn_native/opengl/PipelineGL.h
@@ -37,10 +37,6 @@
PipelineGL();
~PipelineGL();
- void Initialize(const OpenGLFunctions& gl,
- const PipelineLayout* layout,
- const PerStage<ProgrammableStage>& stages);
-
// For each unit a sampler is bound to we need to know if we should use filtering or not
// because int and uint texture are only complete without filtering.
struct SamplerUnit {
@@ -53,6 +49,11 @@
void ApplyNow(const OpenGLFunctions& gl);
+ protected:
+ MaybeError InitializeBase(const OpenGLFunctions& gl,
+ const PipelineLayout* layout,
+ const PerStage<ProgrammableStage>& stages);
+
private:
GLuint mProgram;
std::vector<std::vector<SamplerUnit>> mUnitsForSamplers;
diff --git a/src/dawn_native/opengl/RenderPipelineGL.cpp b/src/dawn_native/opengl/RenderPipelineGL.cpp
index aed8c01..5bc596f 100644
--- a/src/dawn_native/opengl/RenderPipelineGL.cpp
+++ b/src/dawn_native/opengl/RenderPipelineGL.cpp
@@ -219,16 +219,26 @@
} // anonymous namespace
+ // static
+ ResultOrError<Ref<RenderPipeline>> RenderPipeline::Create(
+ Device* device,
+ const RenderPipelineDescriptor* descriptor) {
+ Ref<RenderPipeline> pipeline = AcquireRef(new RenderPipeline(device, descriptor));
+ DAWN_TRY(pipeline->Initialize());
+ return pipeline;
+ }
+
RenderPipeline::RenderPipeline(Device* device, const RenderPipelineDescriptor* descriptor)
: RenderPipelineBase(device, descriptor),
mVertexArrayObject(0),
mGlPrimitiveTopology(GLPrimitiveTopology(GetPrimitiveTopology())) {
- PerStage<const ShaderModule*> modules(nullptr);
- modules[SingleShaderStage::Vertex] = ToBackend(descriptor->vertex.module);
- modules[SingleShaderStage::Fragment] = ToBackend(descriptor->fragment->module);
+ }
- PipelineGL::Initialize(device->gl, ToBackend(GetLayout()), GetAllStages());
+ MaybeError RenderPipeline::Initialize() {
+ DAWN_TRY(
+ InitializeBase(ToBackend(GetDevice())->gl, ToBackend(GetLayout()), GetAllStages()));
CreateVAOForVertexState();
+ return {};
}
RenderPipeline::~RenderPipeline() {
diff --git a/src/dawn_native/opengl/RenderPipelineGL.h b/src/dawn_native/opengl/RenderPipelineGL.h
index 960b50b..3c7a4d3 100644
--- a/src/dawn_native/opengl/RenderPipelineGL.h
+++ b/src/dawn_native/opengl/RenderPipelineGL.h
@@ -29,7 +29,9 @@
class RenderPipeline final : public RenderPipelineBase, public PipelineGL {
public:
- RenderPipeline(Device* device, const RenderPipelineDescriptor* descriptor);
+ static ResultOrError<Ref<RenderPipeline>> Create(
+ Device* device,
+ const RenderPipelineDescriptor* descriptor);
GLenum GetGLPrimitiveTopology() const;
ityp::bitset<VertexAttributeLocation, kMaxVertexAttributes> GetAttributesUsingVertexBuffer(
@@ -38,7 +40,10 @@
void ApplyNow(PersistentPipelineState& persistentPipelineState);
private:
+ RenderPipeline(Device* device, const RenderPipelineDescriptor* descriptor);
~RenderPipeline() override;
+ MaybeError Initialize();
+
void CreateVAOForVertexState();
// TODO(yunchao.he@intel.com): vao need to be deduplicated between pipelines.
diff --git a/src/dawn_native/opengl/ShaderModuleGL.cpp b/src/dawn_native/opengl/ShaderModuleGL.cpp
index a5bbcb9..6aac35de 100644
--- a/src/dawn_native/opengl/ShaderModuleGL.cpp
+++ b/src/dawn_native/opengl/ShaderModuleGL.cpp
@@ -94,18 +94,18 @@
return DAWN_VALIDATION_ERROR(errorStream.str().c_str());
}
- mGLSpirv = std::move(result.spirv);
- DAWN_TRY_ASSIGN(mGLEntryPoints, ReflectShaderUsingSPIRVCross(GetDevice(), mGLSpirv));
+ DAWN_TRY_ASSIGN(mGLEntryPoints,
+ ReflectShaderUsingSPIRVCross(GetDevice(), result.spirv));
}
return {};
}
- std::string ShaderModule::TranslateToGLSL(const char* entryPointName,
- SingleShaderStage stage,
- CombinedSamplerInfo* combinedSamplers,
- const PipelineLayout* layout,
- bool* needsDummySampler) const {
+ ResultOrError<std::string> ShaderModule::TranslateToGLSL(const char* entryPointName,
+ SingleShaderStage stage,
+ CombinedSamplerInfo* combinedSamplers,
+ const PipelineLayout* layout,
+ bool* needsDummySampler) const {
// If these options are changed, the values in DawnSPIRVCrossGLSLFastFuzzer.cpp need to
// be updated.
spirv_cross::CompilerGLSL::Options options;
@@ -125,8 +125,32 @@
options.es = version.IsES();
options.version = version.GetMajor() * 100 + version.GetMinor() * 10;
- spirv_cross::CompilerGLSL compiler(
- GetDevice()->IsToggleEnabled(Toggle::UseTintGenerator) ? mGLSpirv : GetSpirv());
+ std::vector<uint32_t> spirv;
+ if (GetDevice()->IsToggleEnabled(Toggle::UseTintGenerator)) {
+ tint::transform::SingleEntryPoint singleEntryPointTransform;
+
+ tint::transform::DataMap transformInputs;
+ transformInputs.Add<tint::transform::SingleEntryPoint::Config>(entryPointName);
+
+ tint::Program program;
+ DAWN_TRY_ASSIGN(program, RunTransforms(&singleEntryPointTransform, GetTintProgram(),
+ transformInputs, nullptr, nullptr));
+
+ tint::writer::spirv::Options options;
+ options.disable_workgroup_init =
+ GetDevice()->IsToggleEnabled(Toggle::DisableWorkgroupInit);
+ auto result = tint::writer::spirv::Generate(&program, options);
+ if (!result.success) {
+ std::ostringstream errorStream;
+ errorStream << "Generator: " << result.error << std::endl;
+ return DAWN_VALIDATION_ERROR(errorStream.str().c_str());
+ }
+
+ spirv = std::move(result.spirv);
+ } else {
+ spirv = GetSpirv();
+ }
+ spirv_cross::CompilerGLSL compiler(std::move(spirv));
compiler.set_common_options(options);
compiler.set_entry_point(entryPointName, ShaderStageToExecutionModel(stage));
diff --git a/src/dawn_native/opengl/ShaderModuleGL.h b/src/dawn_native/opengl/ShaderModuleGL.h
index 7f598b9..d6d9f74 100644
--- a/src/dawn_native/opengl/ShaderModuleGL.h
+++ b/src/dawn_native/opengl/ShaderModuleGL.h
@@ -50,18 +50,17 @@
const ShaderModuleDescriptor* descriptor,
ShaderModuleParseResult* parseResult);
- std::string TranslateToGLSL(const char* entryPointName,
- SingleShaderStage stage,
- CombinedSamplerInfo* combinedSamplers,
- const PipelineLayout* layout,
- bool* needsDummySampler) const;
+ ResultOrError<std::string> TranslateToGLSL(const char* entryPointName,
+ SingleShaderStage stage,
+ CombinedSamplerInfo* combinedSamplers,
+ const PipelineLayout* layout,
+ bool* needsDummySampler) const;
private:
ShaderModule(Device* device, const ShaderModuleDescriptor* descriptor);
~ShaderModule() override = default;
MaybeError Initialize(ShaderModuleParseResult* parseResult);
- std::vector<uint32_t> mGLSpirv;
EntryPointMetadataTable mGLEntryPoints;
};
diff --git a/src/dawn_native/vulkan/ShaderModuleVk.cpp b/src/dawn_native/vulkan/ShaderModuleVk.cpp
index 525dbb1..0144eef 100644
--- a/src/dawn_native/vulkan/ShaderModuleVk.cpp
+++ b/src/dawn_native/vulkan/ShaderModuleVk.cpp
@@ -104,7 +104,6 @@
DAWN_TRY_ASSIGN(program,
RunTransforms(&transformManager, parseResult->tintProgram.get(),
transformInputs, nullptr, nullptr));
- // We will miss the messages generated in this RunTransforms.
tint::writer::spirv::Options options;
options.emit_vertex_point_size = true;
@@ -203,11 +202,14 @@
tint::transform::Manager transformManager;
transformManager.append(std::make_unique<tint::transform::BindingRemapper>());
+ // Many Vulkan drivers can't handle multi-entrypoint shader modules.
+ transformManager.append(std::make_unique<tint::transform::SingleEntryPoint>());
tint::transform::DataMap transformInputs;
transformInputs.Add<BindingRemapper::Remappings>(std::move(bindingPoints),
std::move(accessControls),
/* mayCollide */ false);
+ transformInputs.Add<tint::transform::SingleEntryPoint::Config>(entryPointName);
tint::Program program;
DAWN_TRY_ASSIGN(program, RunTransforms(&transformManager, GetTintProgram(), transformInputs,
diff --git a/src/tests/end2end/CopyTextureForBrowserTests.cpp b/src/tests/end2end/CopyTextureForBrowserTests.cpp
index 1aca79d..768e36a 100644
--- a/src/tests/end2end/CopyTextureForBrowserTests.cpp
+++ b/src/tests/end2end/CopyTextureForBrowserTests.cpp
@@ -140,6 +140,10 @@
void SetUp() override {
DawnTest::SetUp();
+ // crbug.com/dawn/948: Tint required for multiple entrypoints in a module.
+ // CopyTextureForBrowser uses and internal pipeline with a multi-entrypoint
+ // shader module.
+ DAWN_TEST_UNSUPPORTED_IF(!HasToggleEnabled("use_tint_generator"));
testPipeline = MakeTestPipeline();
diff --git a/src/tests/end2end/ShaderTests.cpp b/src/tests/end2end/ShaderTests.cpp
index fa427aa..c635048 100644
--- a/src/tests/end2end/ShaderTests.cpp
+++ b/src/tests/end2end/ShaderTests.cpp
@@ -262,8 +262,8 @@
// Tests that shaders I/O structs can be shared between vertex and fragment shaders.
TEST_P(ShaderTests, WGSLSharedStructIO) {
- // TODO(tint:714): Not yet implemeneted in tint yet, but intended to work.
- DAWN_SUPPRESS_TEST_IF(IsD3D12() || IsVulkan() || IsMetal() || IsOpenGL() || IsOpenGLES());
+ // crbug.com/dawn/948: Tint required for multiple entrypoints in a module.
+ DAWN_TEST_UNSUPPORTED_IF(!HasToggleEnabled("use_tint_generator"));
std::string shader = R"(
struct VertexIn {