blob: f0fe75979ba9a96f82bb58512d98900154fca151 [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, SkSL::Program::Inputs* inputs) {
SkSL::Program::Settings settings;
settings.fCaps = gpu->caps()->shaderCaps();
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::FilterMode to_dawn_filter_mode(GrSamplerState::Filter filter) {
switch (filter) {
case GrSamplerState::Filter::kNearest:
return dawn::FilterMode::Nearest;
case GrSamplerState::Filter::kBilerp:
case GrSamplerState::Filter::kMipMap:
return dawn::FilterMode::Linear;
default:
SkASSERT(!"unsupported filter mode");
return dawn::FilterMode::Nearest;
}
}
static dawn::AddressMode to_dawn_address_mode(GrSamplerState::WrapMode wrapMode) {
switch (wrapMode) {
case GrSamplerState::WrapMode::kClamp:
return dawn::AddressMode::ClampToEdge;
case GrSamplerState::WrapMode::kClampToBorder:
// TODO: unsupported
return dawn::AddressMode::ClampToEdge;
case GrSamplerState::WrapMode::kRepeat:
return dawn::AddressMode::Repeat;
case GrSamplerState::WrapMode::kMirrorRepeat:
return dawn::AddressMode::MirrorRepeat;
}
SkASSERT(!"unsupported address mode");
return dawn::AddressMode::ClampToEdge;
}
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;
state.depthWriteEnabled = false;
state.depthCompare = dawn::CompareFunction::Always;
if (stencilSettings.isDisabled()) {
dawn::StencilStateFaceDescriptor stencilFace;
stencilFace.compare = dawn::CompareFunction::Always;
stencilFace.failOp = dawn::StencilOperation::Keep;
stencilFace.depthFailOp = dawn::StencilOperation::Keep;
stencilFace.passOp = dawn::StencilOperation::Keep;
state.stencilReadMask = state.stencilWriteMask = 0x0;
state.stencilBack = state.stencilFront = stencilFace;
} else {
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::Sampler create_sampler(const GrDawnGpu* gpu, const GrSamplerState& samplerState) {
dawn::SamplerDescriptor desc;
desc.addressModeU = to_dawn_address_mode(samplerState.wrapModeX());
desc.addressModeV = to_dawn_address_mode(samplerState.wrapModeY());
desc.addressModeW = dawn::AddressMode::ClampToEdge;
desc.magFilter = desc.minFilter = to_dawn_filter_mode(samplerState.filter());
desc.mipmapFilter = dawn::FilterMode::Linear;
desc.lodMinClamp = 0.0f;
desc.lodMaxClamp = 1000.0f;
desc.compare = dawn::CompareFunction::Never;
return gpu->device().CreateSampler(&desc);
}
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,
GrSurfaceOrigin origin,
const GrPipeline& pipeline,
const GrPrimitiveProcessor& primProc,
const GrTextureProxy* const primProcProxies[],
dawn::TextureFormat colorFormat,
bool hasDepthStencil,
dawn::TextureFormat depthStencilFormat,
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) {
layoutBindings.push_back({ GrDawnUniformHandler::kGeometryBinding,
dawn::ShaderStageBit::Vertex,
dawn::BindingType::UniformBuffer});
}
if (0 != fragmentUniformSize) {
layoutBindings.push_back({ GrDawnUniformHandler::kFragBinding,
dawn::ShaderStageBit::Fragment,
dawn::BindingType::UniformBuffer});
}
uint32_t binding = GrDawnUniformHandler::kSamplerBindingBase;
for (int i = 0; i < builder.fUniformHandler.fSamplers.count(); ++i) {
layoutBindings.push_back({ binding++, dawn::ShaderStageBit::Fragment,
dawn::BindingType::Sampler});
layoutBindings.push_back({ binding++, dawn::ShaderStageBit::Fragment,
dawn::BindingType::SampledTexture});
}
dawn::BindGroupLayoutDescriptor bindGroupLayoutDesc;
bindGroupLayoutDesc.bindingCount = layoutBindings.size();
bindGroupLayoutDesc.bindings = layoutBindings.data();
auto bindGroupLayout = gpu->device().CreateBindGroupLayout(&bindGroupLayoutDesc);
dawn::PipelineLayoutDescriptor pipelineLayoutDesc;
pipelineLayoutDesc.bindGroupLayoutCount = 1;
pipelineLayoutDesc.bindGroupLayouts = &bindGroupLayout;
result->fPipelineLayout = gpu->device().CreatePipelineLayout(&pipelineLayoutDesc);
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(GrDawnUniformHandler::kGeometryBinding,
result->fGeometryUniformBuffer,
0, geometryUniformSize));
}
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(GrDawnUniformHandler::kFragBinding,
result->fFragmentUniformBuffer,
0, fragmentUniformSize));
}
binding = GrDawnUniformHandler::kSamplerBindingBase;
for (int i = 0; i < primProc.numTextureSamplers(); ++i) {
dawn::Sampler sampler = create_sampler(gpu, primProc.textureSampler(i).samplerState());
bindings.push_back(make_bind_group_binding(binding++, sampler));
GrDawnTexture* tex = static_cast<GrDawnTexture*>(primProcProxies[i]->peekTexture());
dawn::TextureView textureView = tex->textureView();
bindings.push_back(make_bind_group_binding(binding++, textureView));
}
GrFragmentProcessor::Iter iter(pipeline);
const GrFragmentProcessor* fp = iter.next();
while (fp) {
for (int i = 0; i < fp->numTextureSamplers(); ++i) {
dawn::Sampler sampler = create_sampler(gpu, fp->textureSampler(i).samplerState());
bindings.push_back(make_bind_group_binding(binding++, sampler));
GrDawnTexture* tex = static_cast<GrDawnTexture*>(fp->textureSampler(i).peekTexture());
dawn::TextureView textureView = tex->textureView();
bindings.push_back(make_bind_group_binding(binding++, textureView));
}
fp = iter.next();
}
dawn::BindGroupDescriptor bindGroupDescriptor;
bindGroupDescriptor.layout = bindGroupLayout;
bindGroupDescriptor.bindingCount = bindings.size();
bindGroupDescriptor.bindings = bindings.data();
result->fBindGroup = gpu->device().CreateBindGroup(&bindGroupDescriptor);
result->fBuiltinUniformHandles = builder.fUniformHandles;
result->fColorState = create_color_state(gpu, pipeline, colorFormat);
GrStencilSettings stencil;
if (pipeline.isStencilEnabled()) {
int numStencilBits = renderTarget->renderTargetPriv().numStencilBits();
stencil.reset(*pipeline.getUserStencil(), pipeline.hasStencilClip(), numStencilBits);
}
result->fDepthStencilState = create_depth_stencil_state(stencil, depthStencilFormat, origin);
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);
}