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>
This commit is contained in:
Austin Eng 2021-08-03 19:07:29 +00:00 committed by Dawn LUCI CQ
parent c5989a9042
commit 8e957160b1
14 changed files with 127 additions and 84 deletions

View File

@ -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 @@ namespace dawn_native {
[[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 @@ namespace dawn_native {
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 @@ namespace dawn_native {
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 = {};

View File

@ -28,8 +28,7 @@ namespace dawn_native {
std::unordered_map<wgpu::TextureFormat, Ref<RenderPipelineBase>>
copyTextureForBrowserPipelines;
Ref<ShaderModuleBase> copyTextureForBrowserVS;
Ref<ShaderModuleBase> copyTextureForBrowserFS;
Ref<ShaderModuleBase> copyTextureForBrowser;
Ref<ComputePipelineBase> timestampComputePipeline;
Ref<ShaderModuleBase> timestampCS;

View File

@ -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() {

View File

@ -27,12 +27,16 @@ namespace dawn_native { namespace opengl {
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

View File

@ -129,7 +129,7 @@ namespace dawn_native { namespace opengl {
}
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 @@ namespace dawn_native { namespace opengl {
}
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));

View File

@ -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 @@ namespace dawn_native { namespace opengl {
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 @@ namespace dawn_native { namespace opengl {
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 @@ namespace dawn_native { namespace opengl {
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 @@ namespace dawn_native { namespace opengl {
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 @@ namespace dawn_native { namespace opengl {
textureUnit++;
}
return {};
}
const std::vector<PipelineGL::SamplerUnit>& PipelineGL::GetTextureUnitsForSampler(

View File

@ -37,10 +37,6 @@ namespace dawn_native { namespace opengl {
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 @@ namespace dawn_native { namespace opengl {
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;

View File

@ -219,16 +219,26 @@ namespace dawn_native { namespace opengl {
} // 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() {

View File

@ -29,7 +29,9 @@ namespace dawn_native { namespace opengl {
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 @@ namespace dawn_native { namespace opengl {
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.

View File

@ -94,18 +94,18 @@ namespace dawn_native { namespace opengl {
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 @@ namespace dawn_native { namespace opengl {
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));

View File

@ -50,18 +50,17 @@ namespace dawn_native { namespace opengl {
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;
};

View File

@ -104,7 +104,6 @@ namespace dawn_native { namespace vulkan {
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 @@ namespace dawn_native { namespace vulkan {
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,

View File

@ -140,6 +140,10 @@ class CopyTextureForBrowserTests : public DawnTest {
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();

View File

@ -262,8 +262,8 @@ fn main(input : FragmentIn) -> [[location(0)]] vec4<f32> {
// 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 {