First implementation of GrDawnProgramBuilder.

Conversion of SkSL to (Dawn-friendly) SPIR-V.
Conversion of GlBlendCoeff -> dawn::BlendFactor.
Conversion of GlBlendEquation -> dawn::BlendOperation.
Creation of dawn::ColorState from color format, write mask, blend state.
Building of basic BindGroupLayouts and BindGroups (the equivalent of
Vulkan's DescriptorSets).
Handling the RTAdjustment uniform, which handles the viewing transform.

Change-Id: If8aef19d71f81bc805defcf5fd7c4ebe3b9547db
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/231177
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Stephen White <senorblanco@chromium.org>
diff --git a/gn/gpu.gni b/gn/gpu.gni
index df91d44..dc03c12 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -718,6 +718,8 @@
   "$_src/gpu/dawn/GrDawnGpuCommandBuffer.h",
   "$_src/gpu/dawn/GrDawnProgramDataManager.cpp",
   "$_src/gpu/dawn/GrDawnProgramDataManager.h",
+  "$_src/gpu/dawn/GrDawnProgramBuilder.cpp",
+  "$_src/gpu/dawn/GrDawnProgramBuilder.h",
   "$_src/gpu/dawn/GrDawnRenderTarget.cpp",
   "$_src/gpu/dawn/GrDawnRenderTarget.h",
   "$_src/gpu/dawn/GrDawnUniformHandler.cpp",
diff --git a/src/gpu/dawn/GrDawnProgramBuilder.cpp b/src/gpu/dawn/GrDawnProgramBuilder.cpp
new file mode 100644
index 0000000..850b388
--- /dev/null
+++ b/src/gpu/dawn/GrDawnProgramBuilder.cpp
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2019 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/gpu/dawn/GrDawnProgramBuilder.h"
+
+#include "include/gpu/GrRenderTarget.h"
+#include "src/gpu/dawn/GrDawnGpu.h"
+#include "src/sksl/SkSLCompiler.h"
+
+#include "src/gpu/GrShaderUtils.h"
+
+static SkSL::String sksl_to_spirv(const GrDawnGpu* gpu, const char* shaderString,
+                                  SkSL::Program::Kind kind, SkSL::Program::Inputs* inputs) {
+    SkSL::Program::Settings settings;
+    std::unique_ptr<SkSL::Program> program = gpu->shaderCompiler()->convertProgram(
+        kind,
+        shaderString,
+        settings);
+    if (!program) {
+        SkDebugf("SkSL error:\n%s\n", gpu->shaderCompiler()->errorText().c_str());
+        SkASSERT(false);
+        return "";
+    }
+    *inputs = program->fInputs;
+    SkSL::String code;
+    if (!gpu->shaderCompiler()->toSPIRV(*program, &code)) {
+        return "";
+    }
+    return code;
+}
+
+static dawn::BlendFactor to_dawn_blend_factor(GrBlendCoeff coeff) {
+    switch (coeff) {
+    case kZero_GrBlendCoeff:
+        return dawn::BlendFactor::Zero;
+    case kOne_GrBlendCoeff:
+        return dawn::BlendFactor::One;
+    case kSC_GrBlendCoeff:
+        return dawn::BlendFactor::SrcColor;
+    case kISC_GrBlendCoeff:
+        return dawn::BlendFactor::OneMinusSrcColor;
+    case kDC_GrBlendCoeff:
+        return dawn::BlendFactor::DstColor;
+    case kIDC_GrBlendCoeff:
+        return dawn::BlendFactor::OneMinusDstColor;
+    case kSA_GrBlendCoeff:
+        return dawn::BlendFactor::SrcAlpha;
+    case kISA_GrBlendCoeff:
+        return dawn::BlendFactor::OneMinusSrcAlpha;
+    case kDA_GrBlendCoeff:
+        return dawn::BlendFactor::DstAlpha;
+    case kIDA_GrBlendCoeff:
+        return dawn::BlendFactor::OneMinusDstAlpha;
+    case kConstC_GrBlendCoeff:
+        return dawn::BlendFactor::BlendColor;
+    case kIConstC_GrBlendCoeff:
+        return dawn::BlendFactor::OneMinusBlendColor;
+    case kConstA_GrBlendCoeff:
+    case kIConstA_GrBlendCoeff:
+    case kS2C_GrBlendCoeff:
+    case kIS2C_GrBlendCoeff:
+    case kS2A_GrBlendCoeff:
+    case kIS2A_GrBlendCoeff:
+    default:
+        SkASSERT(!"unsupported blend coefficient");
+        return dawn::BlendFactor::One;
+    }
+}
+
+static dawn::BlendOperation to_dawn_blend_operation(GrBlendEquation equation) {
+    switch (equation) {
+    case kAdd_GrBlendEquation:
+        return dawn::BlendOperation::Add;
+    case kSubtract_GrBlendEquation:
+        return dawn::BlendOperation::Subtract;
+    default:
+        SkASSERT(!"unsupported blend equation");
+        return dawn::BlendOperation::Add;
+    }
+}
+
+static dawn::ColorStateDescriptor create_color_state(const GrDawnGpu* gpu,
+                                                     const GrPipeline& pipeline,
+                                                     dawn::TextureFormat colorFormat) {
+    GrXferProcessor::BlendInfo blendInfo = pipeline.getXferProcessor().getBlendInfo();
+    GrBlendEquation equation = blendInfo.fEquation;
+    GrBlendCoeff srcCoeff = blendInfo.fSrcBlend;
+    GrBlendCoeff dstCoeff = blendInfo.fDstBlend;
+
+    dawn::BlendFactor srcFactor = to_dawn_blend_factor(srcCoeff);
+    dawn::BlendFactor dstFactor = to_dawn_blend_factor(dstCoeff);
+    dawn::BlendOperation operation = to_dawn_blend_operation(equation);
+    auto mask = blendInfo.fWriteColor ? dawn::ColorWriteMask::All : dawn::ColorWriteMask::None;
+
+    dawn::BlendDescriptor colorDesc = {operation, srcFactor, dstFactor};
+    dawn::BlendDescriptor alphaDesc = {operation, srcFactor, dstFactor};
+
+    dawn::ColorStateDescriptor descriptor;
+    descriptor.format = colorFormat;
+    descriptor.alphaBlend = alphaDesc;
+    descriptor.colorBlend = colorDesc;
+    descriptor.nextInChain = nullptr;
+    descriptor.writeMask = mask;
+
+    return descriptor;
+}
+
+static dawn::BindGroupBinding make_bind_group_binding(uint32_t binding, const dawn::Buffer& buffer,
+                                                      uint32_t offset, uint32_t size, const
+                                                      dawn::Sampler& sampler,
+                                                      const dawn::TextureView& textureView) {
+    dawn::BindGroupBinding result;
+    result.binding = binding;
+    result.buffer = buffer;
+    result.offset = offset;
+    result.size = size;
+    result.sampler = sampler;
+    result.textureView = textureView;
+    return result;
+}
+
+static dawn::BindGroupBinding make_bind_group_binding(uint32_t binding, const dawn::Buffer& buffer,
+                                                      uint32_t offset, uint32_t size) {
+    return make_bind_group_binding(binding, buffer, offset, size, nullptr, nullptr);
+}
+
+sk_sp<GrDawnProgram> GrDawnProgramBuilder::Build(GrDawnGpu* gpu,
+                                                 GrRenderTarget* renderTarget,
+                                                 GrSurfaceOrigin origin,
+                                                 const GrPipeline& pipeline,
+                                                 const GrPrimitiveProcessor& primProc,
+                                                 const GrTextureProxy* const primProcProxies[],
+                                                 dawn::TextureFormat colorFormat,
+                                                 GrProgramDesc* desc) {
+    GrDawnProgramBuilder builder(gpu, renderTarget, origin, primProc, primProcProxies, pipeline,
+                                 desc);
+    if (!builder.emitAndInstallProcs()) {
+        return nullptr;
+    }
+
+    builder.fVS.extensions().appendf("#extension GL_ARB_separate_shader_objects : enable\n");
+    builder.fFS.extensions().appendf("#extension GL_ARB_separate_shader_objects : enable\n");
+    builder.fVS.extensions().appendf("#extension GL_ARB_shading_language_420pack : enable\n");
+    builder.fFS.extensions().appendf("#extension GL_ARB_shading_language_420pack : enable\n");
+
+    builder.finalizeShaders();
+
+    SkSL::Program::Inputs vertInputs, fragInputs;
+    GrDawnUniformHandler::UniformInfoArray& uniforms = builder.fUniformHandler.fUniforms;
+    uint32_t geometryUniformSize = builder.fUniformHandler.fCurrentGeometryUBOOffset;
+    uint32_t fragmentUniformSize = builder.fUniformHandler.fCurrentFragmentUBOOffset;
+    sk_sp<GrDawnProgram> result(
+        new GrDawnProgram(uniforms, geometryUniformSize, fragmentUniformSize));
+    result->fVSModule = builder.createShaderModule(builder.fVS, SkSL::Program::kVertex_Kind,
+                                                   &vertInputs);
+    result->fFSModule = builder.createShaderModule(builder.fFS, SkSL::Program::kFragment_Kind,
+                                                   &fragInputs);
+    result->fGeometryProcessor = std::move(builder.fGeometryProcessor);
+    result->fXferProcessor = std::move(builder.fXferProcessor);
+    result->fFragmentProcessors = std::move(builder.fFragmentProcessors);
+    result->fFragmentProcessorCnt = builder.fFragmentProcessorCnt;
+    std::vector<dawn::BindGroupLayoutBinding> layoutBindings;
+    std::vector<dawn::BindGroupBinding> bindings;
+
+    if (0 != geometryUniformSize) {
+        dawn::BufferDescriptor desc;
+        desc.usage = dawn::BufferUsageBit::Uniform | dawn::BufferUsageBit::CopyDst;
+        desc.size = geometryUniformSize;
+        result->fGeometryUniformBuffer = gpu->device().CreateBuffer(&desc);
+        bindings.push_back(make_bind_group_binding(0, result->fGeometryUniformBuffer, 0,
+                                                   geometryUniformSize));
+        layoutBindings.push_back({ 0, dawn::ShaderStageBit::Vertex,
+                                   dawn::BindingType::UniformBuffer});
+    }
+    if (0 != fragmentUniformSize) {
+        dawn::BufferDescriptor desc;
+        desc.usage = dawn::BufferUsageBit::Uniform | dawn::BufferUsageBit::CopyDst;
+        desc.size = fragmentUniformSize;
+        result->fFragmentUniformBuffer = gpu->device().CreateBuffer(&desc);
+        bindings.push_back(make_bind_group_binding(1, result->fFragmentUniformBuffer, 0,
+                                                   fragmentUniformSize));
+        layoutBindings.push_back({ 1, dawn::ShaderStageBit::Fragment,
+                                   dawn::BindingType::UniformBuffer});
+    }
+    dawn::BindGroupLayoutDescriptor bindGroupLayoutDesc;
+    bindGroupLayoutDesc.bindingCount = layoutBindings.size();
+    bindGroupLayoutDesc.bindings = layoutBindings.data();
+    auto bindGroupLayout = gpu->device().CreateBindGroupLayout(&bindGroupLayoutDesc);
+    dawn::BindGroupDescriptor descriptor;
+    descriptor.layout = bindGroupLayout;
+    descriptor.bindingCount = bindings.size();
+    descriptor.bindings = bindings.data();
+    result->fUniformBindGroup = gpu->device().CreateBindGroup(&descriptor);
+    dawn::PipelineLayoutDescriptor pipelineLayoutDesc;
+    pipelineLayoutDesc.bindGroupLayoutCount = 1;
+    pipelineLayoutDesc.bindGroupLayouts = &bindGroupLayout;
+    result->fPipelineLayout = gpu->device().CreatePipelineLayout(&pipelineLayoutDesc);
+    result->fBuiltinUniformHandles = builder.fUniformHandles;
+    result->fColorState = create_color_state(gpu, pipeline, colorFormat);
+    return result;
+}
+
+GrDawnProgramBuilder::GrDawnProgramBuilder(GrDawnGpu* gpu,
+                                           GrRenderTarget* renderTarget,
+                                           GrSurfaceOrigin origin,
+                                           const GrPrimitiveProcessor& primProc,
+                                           const GrTextureProxy* const primProcProxies[],
+                                           const GrPipeline& pipeline,
+                                           GrProgramDesc* desc)
+    : INHERITED(renderTarget, origin, primProc, primProcProxies, pipeline, desc)
+    , fGpu(gpu)
+    , fVaryingHandler(this)
+    , fUniformHandler(this) {
+}
+
+dawn::ShaderModule GrDawnProgramBuilder::createShaderModule(const GrGLSLShaderBuilder& builder,
+                                                            SkSL::Program::Kind kind,
+                                                            SkSL::Program::Inputs* inputs) {
+    dawn::Device device = fGpu->device();
+    SkString source(builder.fCompilerString.c_str());
+
+#if 0
+    SkSL::String sksl = GrShaderUtils::PrettyPrint(builder.fCompilerString);
+    printf("converting program:\n%s\n", sksl.c_str());
+#endif
+
+    SkSL::String spirvSource = sksl_to_spirv(fGpu, source.c_str(), kind, inputs);
+
+    dawn::ShaderModuleDescriptor desc;
+    desc.codeSize = spirvSource.size() / 4;
+    desc.code = reinterpret_cast<const uint32_t*>(spirvSource.c_str());
+
+    return device.CreateShaderModule(&desc);
+};
+
+const GrCaps* GrDawnProgramBuilder::caps() const {
+    return fGpu->caps();
+}
+
+void GrDawnProgram::setRenderTargetState(const GrRenderTarget* rt, GrSurfaceOrigin origin) {
+    // Load the RT height uniform if it is needed to y-flip gl_FragCoord.
+    if (fBuiltinUniformHandles.fRTHeightUni.isValid() &&
+        fRenderTargetState.fRenderTargetSize.fHeight != rt->height()) {
+        fDataManager.set1f(fBuiltinUniformHandles.fRTHeightUni, SkIntToScalar(rt->height()));
+    }
+
+    // set RT adjustment
+    SkISize size;
+    size.set(rt->width(), rt->height());
+    SkASSERT(fBuiltinUniformHandles.fRTAdjustmentUni.isValid());
+    if (fRenderTargetState.fRenderTargetOrigin != origin ||
+        fRenderTargetState.fRenderTargetSize != size) {
+        fRenderTargetState.fRenderTargetSize = size;
+        fRenderTargetState.fRenderTargetOrigin = origin;
+
+        float rtAdjustmentVec[4];
+        fRenderTargetState.getRTAdjustmentVec(rtAdjustmentVec);
+        fDataManager.set4fv(fBuiltinUniformHandles.fRTAdjustmentUni, 1, rtAdjustmentVec);
+    }
+}
+
+void GrDawnProgram::setData(const GrPrimitiveProcessor& primProc,
+                            const GrRenderTarget* renderTarget,
+                            GrSurfaceOrigin origin,
+                            const GrPipeline& pipeline) {
+    this->setRenderTargetState(renderTarget, origin);
+    fGeometryProcessor->setData(fDataManager, primProc,
+                                GrFragmentProcessor::CoordTransformIter(pipeline));
+    GrFragmentProcessor::Iter iter(pipeline);
+    GrGLSLFragmentProcessor::Iter glslIter(fFragmentProcessors.get(), fFragmentProcessorCnt);
+    const GrFragmentProcessor* fp = iter.next();
+    GrGLSLFragmentProcessor* glslFP = glslIter.next();
+    while (fp && glslFP) {
+        glslFP->setData(fDataManager, *fp);
+        fp = iter.next();
+        glslFP = glslIter.next();
+    }
+    fDataManager.uploadUniformBuffers(fGeometryUniformBuffer,
+                                      fFragmentUniformBuffer);
+}
diff --git a/src/gpu/dawn/GrDawnProgramBuilder.h b/src/gpu/dawn/GrDawnProgramBuilder.h
new file mode 100644
index 0000000..656ef51
--- /dev/null
+++ b/src/gpu/dawn/GrDawnProgramBuilder.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2019 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrDawnProgramBuilder_DEFINED
+#define GrDawnProgramBuilder_DEFINED
+
+#include "src/gpu/dawn/GrDawnProgramDataManager.h"
+#include "src/gpu/dawn/GrDawnUniformHandler.h"
+#include "src/gpu/dawn/GrDawnVaryingHandler.h"
+#include "src/sksl/SkSLCompiler.h"
+#include "dawn/dawncpp.h"
+#include "src/gpu/glsl/GrGLSLProgramBuilder.h"
+
+class GrPipeline;
+
+struct GrDawnProgram : public SkRefCnt {
+    struct RenderTargetState {
+        SkISize         fRenderTargetSize;
+        GrSurfaceOrigin fRenderTargetOrigin;
+
+        RenderTargetState() { this->invalidate(); }
+        void invalidate() {
+            fRenderTargetSize.fWidth = -1;
+            fRenderTargetSize.fHeight = -1;
+            fRenderTargetOrigin = (GrSurfaceOrigin) -1;
+        }
+
+        /**
+         * Gets a float4 that adjusts the position from Skia device coords to GL's normalized device
+         * coords. Assuming the transformed position, pos, is a homogeneous float3, the vec, v, is
+         * applied as such:
+         * pos.x = dot(v.xy, pos.xz)
+         * pos.y = dot(v.zw, pos.yz)
+         */
+        void getRTAdjustmentVec(float* destVec) {
+            destVec[0] = 2.f / fRenderTargetSize.fWidth;
+            destVec[1] = -1.f;
+            if (kBottomLeft_GrSurfaceOrigin == fRenderTargetOrigin) {
+                destVec[2] = -2.f / fRenderTargetSize.fHeight;
+                destVec[3] = 1.f;
+            } else {
+                destVec[2] = 2.f / fRenderTargetSize.fHeight;
+                destVec[3] = -1.f;
+            }
+        }
+    };
+    typedef GrGLSLBuiltinUniformHandles BuiltinUniformHandles;
+    GrDawnProgram(const GrDawnUniformHandler::UniformInfoArray& uniforms,
+                  uint32_t geometryUniformSize,
+                  uint32_t fragmentUniformSize)
+      : fDataManager(uniforms, geometryUniformSize, fragmentUniformSize) {
+    }
+    dawn::ShaderModule fVSModule;
+    dawn::ShaderModule fFSModule;
+    std::unique_ptr<GrGLSLPrimitiveProcessor> fGeometryProcessor;
+    std::unique_ptr<GrGLSLXferProcessor> fXferProcessor;
+    std::unique_ptr<std::unique_ptr<GrGLSLFragmentProcessor>[]> fFragmentProcessors;
+    int fFragmentProcessorCnt;
+    dawn::Buffer fGeometryUniformBuffer;
+    dawn::Buffer fFragmentUniformBuffer;
+    dawn::PipelineLayout fPipelineLayout;
+    dawn::BindGroup fUniformBindGroup;
+    dawn::ColorStateDescriptor fColorState;
+    GrDawnProgramDataManager fDataManager;
+    RenderTargetState fRenderTargetState;
+    BuiltinUniformHandles fBuiltinUniformHandles;
+
+    void setRenderTargetState(const GrRenderTarget*, GrSurfaceOrigin);
+    void setData(const GrPrimitiveProcessor&, const GrRenderTarget*, GrSurfaceOrigin,
+                 const GrPipeline&);
+};
+
+class GrDawnProgramBuilder : public GrGLSLProgramBuilder {
+public:
+    static sk_sp<GrDawnProgram> Build(GrDawnGpu*,
+                                      GrRenderTarget* renderTarget,
+                                      GrSurfaceOrigin origin,
+                                      const GrPipeline&,
+                                      const GrPrimitiveProcessor&,
+                                      const GrTextureProxy* const primProcProxies[],
+                                      dawn::TextureFormat colorFormat,
+                                      GrProgramDesc* desc);
+    const GrCaps* caps() const override;
+    GrGLSLUniformHandler* uniformHandler() override { return &fUniformHandler; }
+    const GrGLSLUniformHandler* uniformHandler() const override { return &fUniformHandler; }
+    GrGLSLVaryingHandler* varyingHandler() override { return &fVaryingHandler; }
+
+    GrDawnGpu* gpu() const { return fGpu; }
+
+private:
+    GrDawnProgramBuilder(GrDawnGpu*,
+                         GrRenderTarget*,
+                         GrSurfaceOrigin,
+                         const GrPrimitiveProcessor&,
+                         const GrTextureProxy* const primProcProxies[],
+                         const GrPipeline&,
+                         GrProgramDesc*);
+    dawn::ShaderModule createShaderModule(const GrGLSLShaderBuilder&, SkSL::Program::Kind,
+                                          SkSL::Program::Inputs* inputs);
+    GrDawnGpu*             fGpu;
+    GrDawnVaryingHandler   fVaryingHandler;
+    GrDawnUniformHandler   fUniformHandler;
+
+    typedef GrGLSLProgramBuilder INHERITED;
+};
+#endif
diff --git a/src/gpu/glsl/GrGLSLShaderBuilder.h b/src/gpu/glsl/GrGLSLShaderBuilder.h
index 959d24a..9d3cc48 100644
--- a/src/gpu/glsl/GrGLSLShaderBuilder.h
+++ b/src/gpu/glsl/GrGLSLShaderBuilder.h
@@ -248,6 +248,7 @@
     friend class GrCCCoverageProcessor; // to access code().
     friend class GrGLSLProgramBuilder;
     friend class GrGLProgramBuilder;
+    friend class GrDawnProgramBuilder;
     friend class GrGLSLVaryingHandler; // to access noperspective interpolation feature.
     friend class GrGLPathProgramBuilder; // to access fInputs.
     friend class GrVkPipelineStateBuilder;