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/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