blob: a33e9bbeb7ed900687db479fcead693140a4325a [file] [log] [blame]
/*
* 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 "src/gpu/GrRenderTarget.h"
#include "src/gpu/GrShaderUtils.h"
#include "src/gpu/GrStencilSettings.h"
#include "src/gpu/dawn/GrDawnGpu.h"
#include "src/gpu/dawn/GrDawnTexture.h"
#include "src/sksl/SkSLCompiler.h"
static SkSL::String sksl_to_spirv(const GrDawnGpu* gpu, const char* shaderString,
SkSL::Program::Kind kind, bool flipY,
SkSL::Program::Inputs* inputs) {
SkSL::Program::Settings settings;
settings.fCaps = gpu->caps()->shaderCaps();
settings.fFlipY = flipY;
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::BlendFactor to_dawn_blend_factor_for_alpha(GrBlendCoeff coeff) {
switch (coeff) {
// Force all srcColor used in alpha slot to alpha version.
case kSC_GrBlendCoeff:
return dawn::BlendFactor::SrcAlpha;
case kISC_GrBlendCoeff:
return dawn::BlendFactor::OneMinusSrcAlpha;
case kDC_GrBlendCoeff:
return dawn::BlendFactor::DstAlpha;
case kIDC_GrBlendCoeff:
return dawn::BlendFactor::OneMinusDstAlpha;
default:
return to_dawn_blend_factor(coeff);
}
}
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;
case kReverseSubtract_GrBlendEquation:
return dawn::BlendOperation::ReverseSubtract;
default:
SkASSERT(!"unsupported blend equation");
return dawn::BlendOperation::Add;
}
}
static dawn::CompareFunction to_dawn_compare_function(GrStencilTest test) {
switch (test) {
case GrStencilTest::kAlways:
return dawn::CompareFunction::Always;
case GrStencilTest::kNever:
return dawn::CompareFunction::Never;
case GrStencilTest::kGreater:
return dawn::CompareFunction::Greater;
case GrStencilTest::kGEqual:
return dawn::CompareFunction::GreaterEqual;
case GrStencilTest::kLess:
return dawn::CompareFunction::Less;
case GrStencilTest::kLEqual:
return dawn::CompareFunction::LessEqual;
case GrStencilTest::kEqual:
return dawn::CompareFunction::Equal;
case GrStencilTest::kNotEqual:
return dawn::CompareFunction::NotEqual;
default:
SkASSERT(!"unsupported stencil test");
return dawn::CompareFunction::Always;
}
}
static dawn::StencilOperation to_dawn_stencil_operation(GrStencilOp op) {
switch (op) {
case GrStencilOp::kKeep:
return dawn::StencilOperation::Keep;
case GrStencilOp::kZero:
return dawn::StencilOperation::Zero;
case GrStencilOp::kReplace:
return dawn::StencilOperation::Replace;
case GrStencilOp::kInvert:
return dawn::StencilOperation::Invert;
case GrStencilOp::kIncClamp:
return dawn::StencilOperation::IncrementClamp;
case GrStencilOp::kDecClamp:
return dawn::StencilOperation::DecrementClamp;
case GrStencilOp::kIncWrap:
return dawn::StencilOperation::IncrementWrap;
case GrStencilOp::kDecWrap:
return dawn::StencilOperation::DecrementWrap;
default:
SkASSERT(!"unsupported stencil function");
return dawn::StencilOperation::Keep;
}
}
static dawn::PrimitiveTopology to_dawn_primitive_topology(GrPrimitiveType primitiveType) {
switch (primitiveType) {
case GrPrimitiveType::kTriangles:
return dawn::PrimitiveTopology::TriangleList;
case GrPrimitiveType::kTriangleStrip:
return dawn::PrimitiveTopology::TriangleStrip;
case GrPrimitiveType::kPoints:
return dawn::PrimitiveTopology::PointList;
case GrPrimitiveType::kLines:
return dawn::PrimitiveTopology::LineList;
case GrPrimitiveType::kLineStrip:
return dawn::PrimitiveTopology::LineStrip;
case GrPrimitiveType::kPath:
default:
SkASSERT(!"unsupported primitive topology");
return dawn::PrimitiveTopology::TriangleList;
}
}
static dawn::VertexFormat to_dawn_vertex_format(GrVertexAttribType type) {
switch (type) {
case kFloat_GrVertexAttribType:
case kHalf_GrVertexAttribType:
return dawn::VertexFormat::Float;
case kFloat2_GrVertexAttribType:
case kHalf2_GrVertexAttribType:
return dawn::VertexFormat::Float2;
case kFloat3_GrVertexAttribType:
case kHalf3_GrVertexAttribType:
return dawn::VertexFormat::Float3;
case kFloat4_GrVertexAttribType:
case kHalf4_GrVertexAttribType:
return dawn::VertexFormat::Float4;
case kUShort2_GrVertexAttribType:
return dawn::VertexFormat::UShort2;
case kInt_GrVertexAttribType:
return dawn::VertexFormat::Int;
case kUByte4_norm_GrVertexAttribType:
return dawn::VertexFormat::UChar4Norm;
default:
SkASSERT(!"unsupported vertex format");
return dawn::VertexFormat::Float4;
}
}
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::BlendFactor srcFactorAlpha = to_dawn_blend_factor_for_alpha(srcCoeff);
dawn::BlendFactor dstFactorAlpha = to_dawn_blend_factor_for_alpha(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, srcFactorAlpha, dstFactorAlpha};
dawn::ColorStateDescriptor descriptor;
descriptor.format = colorFormat;
descriptor.alphaBlend = alphaDesc;
descriptor.colorBlend = colorDesc;
descriptor.nextInChain = nullptr;
descriptor.writeMask = mask;
return descriptor;
}
static dawn::StencilStateFaceDescriptor to_stencil_state_face(const GrStencilSettings::Face& face) {
dawn::StencilStateFaceDescriptor desc;
desc.compare = to_dawn_compare_function(face.fTest);
desc.failOp = desc.depthFailOp = to_dawn_stencil_operation(face.fFailOp);
desc.passOp = to_dawn_stencil_operation(face.fPassOp);
return desc;
}
static dawn::DepthStencilStateDescriptor create_depth_stencil_state(
const GrStencilSettings& stencilSettings,
dawn::TextureFormat depthStencilFormat,
GrSurfaceOrigin origin) {
dawn::DepthStencilStateDescriptor state;
state.format = depthStencilFormat;
if (!stencilSettings.isDisabled()) {
const GrStencilSettings::Face& front = stencilSettings.front(origin);
state.stencilReadMask = front.fTestMask;
state.stencilWriteMask = front.fWriteMask;
state.stencilFront = to_stencil_state_face(stencilSettings.front(origin));
if (stencilSettings.isTwoSided()) {
state.stencilBack = to_stencil_state_face(stencilSettings.back(origin));
} else {
state.stencilBack = state.stencilFront;
}
}
return state;
}
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);
}
static dawn::BindGroupBinding make_bind_group_binding(uint32_t binding,
const dawn::Sampler& sampler) {
return make_bind_group_binding(binding, nullptr, 0, 0, sampler, nullptr);
}
static dawn::BindGroupBinding make_bind_group_binding(uint32_t binding,
const dawn::TextureView& textureView) {
return make_bind_group_binding(binding, nullptr, 0, 0, nullptr, textureView);
}
sk_sp<GrDawnProgram> GrDawnProgramBuilder::Build(GrDawnGpu* gpu,
GrRenderTarget* renderTarget,
const GrProgramInfo& programInfo,
GrPrimitiveType primitiveType,
dawn::TextureFormat colorFormat,
bool hasDepthStencil,
dawn::TextureFormat depthStencilFormat,
GrProgramDesc* desc) {
GrDawnProgramBuilder builder(gpu, renderTarget, programInfo, 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));
bool flipY = programInfo.origin() != kTopLeft_GrSurfaceOrigin;
auto vsModule = builder.createShaderModule(builder.fVS, SkSL::Program::kVertex_Kind, flipY,
&vertInputs);
auto fsModule = builder.createShaderModule(builder.fFS, SkSL::Program::kFragment_Kind, flipY,
&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;
if (0 != geometryUniformSize) {
layoutBindings.push_back({ GrDawnUniformHandler::kGeometryBinding,
dawn::ShaderStage::Vertex,
dawn::BindingType::UniformBuffer});
}
if (0 != fragmentUniformSize) {
layoutBindings.push_back({ GrDawnUniformHandler::kFragBinding,
dawn::ShaderStage::Fragment,
dawn::BindingType::UniformBuffer});
}
uint32_t binding = GrDawnUniformHandler::kSamplerBindingBase;
for (int i = 0; i < builder.fUniformHandler.fSamplers.count(); ++i) {
layoutBindings.push_back({ binding++, dawn::ShaderStage::Fragment,
dawn::BindingType::Sampler});
layoutBindings.push_back({ binding++, dawn::ShaderStage::Fragment,
dawn::BindingType::SampledTexture});
}
dawn::BindGroupLayoutDescriptor bindGroupLayoutDesc;
bindGroupLayoutDesc.bindingCount = layoutBindings.size();
bindGroupLayoutDesc.bindings = layoutBindings.data();
result->fBindGroupLayout = gpu->device().CreateBindGroupLayout(&bindGroupLayoutDesc);
dawn::PipelineLayoutDescriptor pipelineLayoutDesc;
pipelineLayoutDesc.bindGroupLayoutCount = 1;
pipelineLayoutDesc.bindGroupLayouts = &result->fBindGroupLayout;
auto pipelineLayout = gpu->device().CreatePipelineLayout(&pipelineLayoutDesc);
result->fBuiltinUniformHandles = builder.fUniformHandles;
const GrPipeline& pipeline = programInfo.pipeline();
auto colorState = create_color_state(gpu, pipeline, colorFormat);
dawn::DepthStencilStateDescriptor depthStencilState;
GrStencilSettings stencil;
if (pipeline.isStencilEnabled()) {
int numStencilBits = renderTarget->renderTargetPriv().numStencilBits();
stencil.reset(*pipeline.getUserStencil(), pipeline.hasStencilClip(), numStencilBits);
}
depthStencilState = create_depth_stencil_state(stencil, depthStencilFormat,
programInfo.origin());
std::vector<dawn::VertexBufferDescriptor> inputs;
std::vector<dawn::VertexAttributeDescriptor> vertexAttributes;
const GrPrimitiveProcessor& primProc = programInfo.primProc();
if (primProc.numVertexAttributes() > 0) {
size_t offset = 0;
int i = 0;
for (const auto& attrib : primProc.vertexAttributes()) {
dawn::VertexAttributeDescriptor attribute;
attribute.shaderLocation = i;
attribute.offset = offset;
attribute.format = to_dawn_vertex_format(attrib.cpuType());
vertexAttributes.push_back(attribute);
offset += attrib.sizeAlign4();
i++;
}
dawn::VertexBufferDescriptor input;
input.stride = offset;
input.stepMode = dawn::InputStepMode::Vertex;
input.attributeCount = vertexAttributes.size();
input.attributes = &vertexAttributes.front();
inputs.push_back(input);
}
std::vector<dawn::VertexAttributeDescriptor> instanceAttributes;
if (primProc.numInstanceAttributes() > 0) {
size_t offset = 0;
int i = 0;
for (const auto& attrib : primProc.instanceAttributes()) {
dawn::VertexAttributeDescriptor attribute;
attribute.shaderLocation = i;
attribute.offset = offset;
attribute.format = to_dawn_vertex_format(attrib.cpuType());
instanceAttributes.push_back(attribute);
offset += attrib.sizeAlign4();
i++;
}
dawn::VertexBufferDescriptor input;
input.stride = offset;
input.stepMode = dawn::InputStepMode::Instance;
input.attributeCount = instanceAttributes.size();
input.attributes = &instanceAttributes.front();
inputs.push_back(input);
}
dawn::VertexInputDescriptor vertexInput;
vertexInput.indexFormat = dawn::IndexFormat::Uint16;
vertexInput.bufferCount = inputs.size();
vertexInput.buffers = &inputs.front();
dawn::ProgrammableStageDescriptor vsDesc;
vsDesc.module = vsModule;
vsDesc.entryPoint = "main";
dawn::ProgrammableStageDescriptor fsDesc;
fsDesc.module = fsModule;
fsDesc.entryPoint = "main";
dawn::RenderPipelineDescriptor rpDesc;
rpDesc.layout = pipelineLayout;
rpDesc.vertexStage = vsDesc;
rpDesc.fragmentStage = &fsDesc;
rpDesc.vertexInput = &vertexInput;
rpDesc.primitiveTopology = to_dawn_primitive_topology(primitiveType);
if (hasDepthStencil) {
rpDesc.depthStencilState = &depthStencilState;
}
rpDesc.colorStateCount = 1;
rpDesc.colorStates = &colorState;
result->fRenderPipeline = gpu->device().CreateRenderPipeline(&rpDesc);
return result;
}
GrDawnProgramBuilder::GrDawnProgramBuilder(GrDawnGpu* gpu,
GrRenderTarget* renderTarget,
const GrProgramInfo& programInfo,
GrProgramDesc* desc)
: INHERITED(renderTarget, programInfo, desc)
, fGpu(gpu)
, fVaryingHandler(this)
, fUniformHandler(this) {
}
dawn::ShaderModule GrDawnProgramBuilder::createShaderModule(const GrGLSLShaderBuilder& builder,
SkSL::Program::Kind kind,
bool flipY,
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, flipY, 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);
}
}
static void setTexture(GrDawnGpu* gpu, const GrSamplerState& state, GrTexture* texture,
std::vector<dawn::BindGroupBinding> *bindings, int* binding) {
// FIXME: could probably cache samplers in GrDawnProgram
dawn::Sampler sampler = gpu->getOrCreateSampler(state);
bindings->push_back(make_bind_group_binding((*binding)++, sampler));
GrDawnTexture* tex = static_cast<GrDawnTexture*>(texture);
dawn::TextureView textureView = tex->textureView();
bindings->push_back(make_bind_group_binding((*binding)++, textureView));
}
dawn::BindGroup GrDawnProgram::setData(GrDawnGpu* gpu, const GrRenderTarget* renderTarget,
const GrProgramInfo& programInfo) {
std::vector<dawn::BindGroupBinding> bindings;
GrDawnRingBuffer::Slice geom, frag;
uint32_t geometryUniformSize = fDataManager.geometryUniformSize();
uint32_t fragmentUniformSize = fDataManager.fragmentUniformSize();
if (0 != geometryUniformSize) {
geom = gpu->allocateUniformRingBufferSlice(geometryUniformSize);
bindings.push_back(make_bind_group_binding(GrDawnUniformHandler::kGeometryBinding,
geom.fBuffer, geom.fOffset,
geometryUniformSize));
}
if (0 != fragmentUniformSize) {
frag = gpu->allocateUniformRingBufferSlice(fragmentUniformSize);
bindings.push_back(make_bind_group_binding(GrDawnUniformHandler::kFragBinding,
frag.fBuffer, frag.fOffset,
fragmentUniformSize));
}
this->setRenderTargetState(renderTarget, programInfo.origin());
const GrPipeline& pipeline = programInfo.pipeline();
const GrPrimitiveProcessor& primProc = programInfo.primProc();
fGeometryProcessor->setData(fDataManager, primProc,
GrFragmentProcessor::CoordTransformIter(pipeline));
int binding = GrDawnUniformHandler::kSamplerBindingBase;
auto primProcTextures = programInfo.hasFixedPrimProcTextures() ?
programInfo.fixedPrimProcTextures() : nullptr;
for (int i = 0; i < primProc.numTextureSamplers(); ++i) {
auto& sampler = primProc.textureSampler(i);
setTexture(gpu, sampler.samplerState(), primProcTextures[i]->peekTexture(), &bindings,
&binding);
}
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);
for (int i = 0; i < fp->numTextureSamplers(); ++i) {
auto& s = fp->textureSampler(i);
setTexture(gpu, s.samplerState(), s.peekTexture(), &bindings, &binding);
}
fp = iter.next();
glslFP = glslIter.next();
}
SkIPoint offset;
GrTexture* dstTexture = pipeline.peekDstTexture(&offset);
fXferProcessor->setData(fDataManager, pipeline.getXferProcessor(), dstTexture, offset);
if (GrTextureProxy* proxy = pipeline.dstTextureProxy()) {
GrFragmentProcessor::TextureSampler sampler(sk_ref_sp(proxy));
setTexture(gpu, sampler.samplerState(), sampler.peekTexture(), &bindings, &binding);
}
fDataManager.uploadUniformBuffers(gpu, geom, frag);
dawn::BindGroupDescriptor descriptor;
descriptor.layout = fBindGroupLayout;
descriptor.bindingCount = bindings.size();
descriptor.bindings = bindings.data();
return gpu->device().CreateBindGroup(&descriptor);
}