/* * 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/GrAutoLocaleSetter.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/gpu/effects/GrTextureEffect.h" static wgpu::BlendFactor to_dawn_blend_factor(GrBlendCoeff coeff) { switch (coeff) { case kZero_GrBlendCoeff: return wgpu::BlendFactor::Zero; case kOne_GrBlendCoeff: return wgpu::BlendFactor::One; case kSC_GrBlendCoeff: return wgpu::BlendFactor::Src; case kISC_GrBlendCoeff: return wgpu::BlendFactor::OneMinusSrc; case kDC_GrBlendCoeff: return wgpu::BlendFactor::Dst; case kIDC_GrBlendCoeff: return wgpu::BlendFactor::OneMinusDst; case kSA_GrBlendCoeff: return wgpu::BlendFactor::SrcAlpha; case kISA_GrBlendCoeff: return wgpu::BlendFactor::OneMinusSrcAlpha; case kDA_GrBlendCoeff: return wgpu::BlendFactor::DstAlpha; case kIDA_GrBlendCoeff: return wgpu::BlendFactor::OneMinusDstAlpha; case kConstC_GrBlendCoeff: return wgpu::BlendFactor::Constant; case kIConstC_GrBlendCoeff: return wgpu::BlendFactor::OneMinusConstant; case kS2C_GrBlendCoeff: case kIS2C_GrBlendCoeff: case kS2A_GrBlendCoeff: case kIS2A_GrBlendCoeff: default: SkASSERT(!"unsupported blend coefficient"); return wgpu::BlendFactor::One; } } static wgpu::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 wgpu::BlendFactor::SrcAlpha; case kISC_GrBlendCoeff: return wgpu::BlendFactor::OneMinusSrcAlpha; case kDC_GrBlendCoeff: return wgpu::BlendFactor::DstAlpha; case kIDC_GrBlendCoeff: return wgpu::BlendFactor::OneMinusDstAlpha; default: return to_dawn_blend_factor(coeff); } } static wgpu::BlendOperation to_dawn_blend_operation(GrBlendEquation equation) { switch (equation) { case kAdd_GrBlendEquation: return wgpu::BlendOperation::Add; case kSubtract_GrBlendEquation: return wgpu::BlendOperation::Subtract; case kReverseSubtract_GrBlendEquation: return wgpu::BlendOperation::ReverseSubtract; default: SkASSERT(!"unsupported blend equation"); return wgpu::BlendOperation::Add; } } static wgpu::CompareFunction to_dawn_compare_function(GrStencilTest test) { switch (test) { case GrStencilTest::kAlways: return wgpu::CompareFunction::Always; case GrStencilTest::kNever: return wgpu::CompareFunction::Never; case GrStencilTest::kGreater: return wgpu::CompareFunction::Greater; case GrStencilTest::kGEqual: return wgpu::CompareFunction::GreaterEqual; case GrStencilTest::kLess: return wgpu::CompareFunction::Less; case GrStencilTest::kLEqual: return wgpu::CompareFunction::LessEqual; case GrStencilTest::kEqual: return wgpu::CompareFunction::Equal; case GrStencilTest::kNotEqual: return wgpu::CompareFunction::NotEqual; default: SkASSERT(!"unsupported stencil test"); return wgpu::CompareFunction::Always; } } static wgpu::StencilOperation to_dawn_stencil_operation(GrStencilOp op) { switch (op) { case GrStencilOp::kKeep: return wgpu::StencilOperation::Keep; case GrStencilOp::kZero: return wgpu::StencilOperation::Zero; case GrStencilOp::kReplace: return wgpu::StencilOperation::Replace; case GrStencilOp::kInvert: return wgpu::StencilOperation::Invert; case GrStencilOp::kIncClamp: return wgpu::StencilOperation::IncrementClamp; case GrStencilOp::kDecClamp: return wgpu::StencilOperation::DecrementClamp; case GrStencilOp::kIncWrap: return wgpu::StencilOperation::IncrementWrap; case GrStencilOp::kDecWrap: return wgpu::StencilOperation::DecrementWrap; default: SkASSERT(!"unsupported stencil function"); return wgpu::StencilOperation::Keep; } } static wgpu::PrimitiveTopology to_dawn_primitive_topology(GrPrimitiveType primitiveType) { switch (primitiveType) { case GrPrimitiveType::kTriangles: return wgpu::PrimitiveTopology::TriangleList; case GrPrimitiveType::kTriangleStrip: return wgpu::PrimitiveTopology::TriangleStrip; case GrPrimitiveType::kPoints: return wgpu::PrimitiveTopology::PointList; case GrPrimitiveType::kLines: return wgpu::PrimitiveTopology::LineList; case GrPrimitiveType::kLineStrip: return wgpu::PrimitiveTopology::LineStrip; case GrPrimitiveType::kPath: default: SkASSERT(!"unsupported primitive topology"); return wgpu::PrimitiveTopology::TriangleList; } } static wgpu::VertexFormat to_dawn_vertex_format(GrVertexAttribType type) { switch (type) { case kFloat_GrVertexAttribType: case kHalf_GrVertexAttribType: return wgpu::VertexFormat::Float32; case kFloat2_GrVertexAttribType: case kHalf2_GrVertexAttribType: return wgpu::VertexFormat::Float32x2; case kFloat3_GrVertexAttribType: return wgpu::VertexFormat::Float32x3; case kFloat4_GrVertexAttribType: case kHalf4_GrVertexAttribType: return wgpu::VertexFormat::Float32x4; case kUShort2_GrVertexAttribType: return wgpu::VertexFormat::Uint16x2; case kInt_GrVertexAttribType: return wgpu::VertexFormat::Sint32; case kUByte4_norm_GrVertexAttribType: return wgpu::VertexFormat::Unorm8x4; default: SkASSERT(!"unsupported vertex format"); return wgpu::VertexFormat::Float32x4; } } static wgpu::BlendState create_blend_state(const GrDawnGpu* gpu, const GrPipeline& pipeline) { GrXferProcessor::BlendInfo blendInfo = pipeline.getXferProcessor().getBlendInfo(); GrBlendEquation equation = blendInfo.fEquation; GrBlendCoeff srcCoeff = blendInfo.fSrcBlend; GrBlendCoeff dstCoeff = blendInfo.fDstBlend; wgpu::BlendFactor srcFactor = to_dawn_blend_factor(srcCoeff); wgpu::BlendFactor dstFactor = to_dawn_blend_factor(dstCoeff); wgpu::BlendFactor srcFactorAlpha = to_dawn_blend_factor_for_alpha(srcCoeff); wgpu::BlendFactor dstFactorAlpha = to_dawn_blend_factor_for_alpha(dstCoeff); wgpu::BlendOperation operation = to_dawn_blend_operation(equation); wgpu::BlendState blendState; blendState.color = {operation, srcFactor, dstFactor}; blendState.alpha = {operation, srcFactorAlpha, dstFactorAlpha}; return blendState; } static wgpu::StencilFaceState to_stencil_state_face(const GrStencilSettings::Face& face) { wgpu::StencilFaceState 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 wgpu::DepthStencilState create_depth_stencil_state( const GrProgramInfo& programInfo, wgpu::TextureFormat depthStencilFormat) { GrStencilSettings stencilSettings = programInfo.nonGLStencilSettings(); GrSurfaceOrigin origin = programInfo.origin(); wgpu::DepthStencilState state; state.format = depthStencilFormat; if (!stencilSettings.isDisabled()) { if (stencilSettings.isTwoSided()) { auto front = stencilSettings.postOriginCCWFace(origin); auto back = stencilSettings.postOriginCWFace(origin); state.stencilFront = to_stencil_state_face(front); state.stencilBack = to_stencil_state_face(back); state.stencilReadMask = front.fTestMask; state.stencilWriteMask = front.fWriteMask; } else { auto frontAndBack = stencilSettings.singleSidedFace(); state.stencilBack = state.stencilFront = to_stencil_state_face(frontAndBack); state.stencilReadMask = frontAndBack.fTestMask; state.stencilWriteMask = frontAndBack.fWriteMask; } } return state; } static wgpu::BindGroupEntry make_bind_group_entry(uint32_t binding, const wgpu::Sampler& sampler, const wgpu::TextureView& textureView) { wgpu::BindGroupEntry result; result.binding = binding; result.buffer = nullptr; result.offset = 0; result.size = 0; result.sampler = sampler; result.textureView = textureView; return result; } static wgpu::BindGroupEntry make_bind_group_entry(uint32_t binding, const wgpu::Sampler& sampler) { return make_bind_group_entry(binding, sampler, nullptr); } static wgpu::BindGroupEntry make_bind_group_entry(uint32_t binding, const wgpu::TextureView& textureView) { return make_bind_group_entry(binding, nullptr, textureView); } sk_sp GrDawnProgramBuilder::Build(GrDawnGpu* gpu, GrRenderTarget* renderTarget, const GrProgramInfo& programInfo, wgpu::TextureFormat colorFormat, bool hasDepthStencil, wgpu::TextureFormat depthStencilFormat, GrProgramDesc* desc) { GrAutoLocaleSetter als("C"); GrDawnProgramBuilder builder(gpu, programInfo, desc); if (!builder.emitAndInstallProcs()) { return nullptr; } builder.finalizeShaders(); SkSL::Program::Inputs vertInputs, fragInputs; bool flipY = programInfo.origin() != kTopLeft_GrSurfaceOrigin; auto vsModule = builder.createShaderModule(builder.fVS, SkSL::ProgramKind::kVertex, flipY, &vertInputs); auto fsModule = builder.createShaderModule(builder.fFS, SkSL::ProgramKind::kFragment, flipY, &fragInputs); GrSPIRVUniformHandler::UniformInfoArray& uniforms = builder.fUniformHandler.fUniforms; uint32_t uniformBufferSize = builder.fUniformHandler.fCurrentUBOOffset; sk_sp result(new GrDawnProgram(uniforms, uniformBufferSize)); result->fGPImpl = std::move(builder.fGPImpl); result->fXPImpl = std::move(builder.fXPImpl); result->fFPImpls = std::move(builder.fFPImpls); std::vector uniformLayoutEntries; if (0 != uniformBufferSize) { wgpu::BindGroupLayoutEntry entry; entry.binding = GrSPIRVUniformHandler::kUniformBinding; entry.visibility = wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment; entry.buffer.type = wgpu::BufferBindingType::Uniform; uniformLayoutEntries.push_back(std::move(entry)); } wgpu::BindGroupLayoutDescriptor uniformBindGroupLayoutDesc; uniformBindGroupLayoutDesc.entryCount = uniformLayoutEntries.size(); uniformBindGroupLayoutDesc.entries = uniformLayoutEntries.data(); result->fBindGroupLayouts.push_back( gpu->device().CreateBindGroupLayout(&uniformBindGroupLayoutDesc)); uint32_t binding = 0; std::vector textureLayoutEntries; int textureCount = builder.fUniformHandler.fSamplers.count(); if (textureCount > 0) { for (int i = 0; i < textureCount; ++i) { { wgpu::BindGroupLayoutEntry entry; entry.binding = binding++; entry.visibility = wgpu::ShaderStage::Fragment; entry.sampler.type = wgpu::SamplerBindingType::Filtering; textureLayoutEntries.push_back(std::move(entry)); } { wgpu::BindGroupLayoutEntry entry; entry.binding = binding++; entry.visibility = wgpu::ShaderStage::Fragment; entry.texture.sampleType = wgpu::TextureSampleType::Float; entry.texture.viewDimension = wgpu::TextureViewDimension::e2D; textureLayoutEntries.push_back(std::move(entry)); } } wgpu::BindGroupLayoutDescriptor textureBindGroupLayoutDesc; textureBindGroupLayoutDesc.entryCount = textureLayoutEntries.size(); textureBindGroupLayoutDesc.entries = textureLayoutEntries.data(); result->fBindGroupLayouts.push_back( gpu->device().CreateBindGroupLayout(&textureBindGroupLayoutDesc)); } wgpu::PipelineLayoutDescriptor pipelineLayoutDesc; pipelineLayoutDesc.bindGroupLayoutCount = result->fBindGroupLayouts.size(); pipelineLayoutDesc.bindGroupLayouts = result->fBindGroupLayouts.data(); auto pipelineLayout = gpu->device().CreatePipelineLayout(&pipelineLayoutDesc); result->fBuiltinUniformHandles = builder.fUniformHandles; const GrPipeline& pipeline = programInfo.pipeline(); wgpu::DepthStencilState depthStencilState; #ifdef SK_DEBUG if (programInfo.isStencilEnabled()) { SkASSERT(renderTarget->numStencilBits(renderTarget->numSamples() > 1) == 8); } #endif depthStencilState = create_depth_stencil_state(programInfo, depthStencilFormat); std::vector inputs; std::vector vertexAttributes; const GrGeometryProcessor& geomProc = programInfo.geomProc(); int i = 0; if (geomProc.numVertexAttributes() > 0) { size_t offset = 0; for (const auto& attrib : geomProc.vertexAttributes()) { wgpu::VertexAttribute attribute; attribute.shaderLocation = i; attribute.offset = offset; attribute.format = to_dawn_vertex_format(attrib.cpuType()); vertexAttributes.push_back(attribute); offset += attrib.sizeAlign4(); i++; } wgpu::VertexBufferLayout input; input.arrayStride = offset; input.stepMode = wgpu::VertexStepMode::Vertex; input.attributeCount = vertexAttributes.size(); input.attributes = &vertexAttributes.front(); inputs.push_back(input); } std::vector instanceAttributes; if (geomProc.numInstanceAttributes() > 0) { size_t offset = 0; for (const auto& attrib : geomProc.instanceAttributes()) { wgpu::VertexAttribute attribute; attribute.shaderLocation = i; attribute.offset = offset; attribute.format = to_dawn_vertex_format(attrib.cpuType()); instanceAttributes.push_back(attribute); offset += attrib.sizeAlign4(); i++; } wgpu::VertexBufferLayout input; input.arrayStride = offset; input.stepMode = wgpu::VertexStepMode::Instance; input.attributeCount = instanceAttributes.size(); input.attributes = &instanceAttributes.front(); inputs.push_back(input); } wgpu::VertexState vertexState; vertexState.module = vsModule; vertexState.entryPoint = "main"; vertexState.bufferCount = inputs.size(); vertexState.buffers = &inputs.front(); wgpu::BlendState blendState = create_blend_state(gpu, pipeline); wgpu::ColorTargetState colorTargetState; colorTargetState.format = colorFormat; colorTargetState.blend = &blendState; bool writeColor = pipeline.getXferProcessor().getBlendInfo().fWriteColor; colorTargetState.writeMask = writeColor ? wgpu::ColorWriteMask::All : wgpu::ColorWriteMask::None; wgpu::FragmentState fragmentState; fragmentState.module = fsModule; fragmentState.entryPoint = "main"; fragmentState.targetCount = 1; fragmentState.targets = &colorTargetState; wgpu::RenderPipelineDescriptor rpDesc; rpDesc.layout = pipelineLayout; rpDesc.vertex = vertexState; rpDesc.primitive.topology = to_dawn_primitive_topology(programInfo.primitiveType()); GrPrimitiveType primitiveType = programInfo.primitiveType(); if (primitiveType == GrPrimitiveType::kTriangleStrip || primitiveType == GrPrimitiveType::kLineStrip) { rpDesc.primitive.stripIndexFormat = wgpu::IndexFormat::Uint16; } if (hasDepthStencil) { rpDesc.depthStencil = &depthStencilState; } rpDesc.fragment = &fragmentState; result->fRenderPipeline = gpu->device().CreateRenderPipeline(&rpDesc); return result; } GrDawnProgramBuilder::GrDawnProgramBuilder(GrDawnGpu* gpu, const GrProgramInfo& programInfo, GrProgramDesc* desc) : INHERITED(*desc, programInfo) , fGpu(gpu) , fVaryingHandler(this) , fUniformHandler(this) { } wgpu::ShaderModule GrDawnProgramBuilder::createShaderModule(const GrGLSLShaderBuilder& builder, SkSL::ProgramKind kind, bool flipY, SkSL::Program::Inputs* inputs) { wgpu::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 = fGpu->SkSLToSPIRV(source.c_str(), kind, fUniformHandler.getRTFlipOffset(), inputs); if (inputs->fUseFlipRTUniform) { this->addRTFlipUniform(SKSL_RTFLIP_NAME); } return fGpu->createShaderModule(spirvSource); }; const GrCaps* GrDawnProgramBuilder::caps() const { return fGpu->caps(); } SkSL::Compiler* GrDawnProgramBuilder::shaderCompiler() const { return fGpu->shaderCompiler(); } void GrDawnProgram::setRenderTargetState(const GrRenderTarget* rt, GrSurfaceOrigin origin) { // Set RT adjustment and RT flip SkISize dimensions = rt->dimensions(); SkASSERT(fBuiltinUniformHandles.fRTAdjustmentUni.isValid()); if (fRenderTargetState.fRenderTargetOrigin != origin || fRenderTargetState.fRenderTargetSize != dimensions) { fRenderTargetState.fRenderTargetSize = dimensions; fRenderTargetState.fRenderTargetOrigin = origin; // The client will mark a swap buffer as kTopLeft when making a SkSurface because // Dawn's framebuffer space has (0, 0) at the top left. This agrees with Skia's device // coords. However, in NDC (-1, -1) is the bottom left. So we flip when origin is kTopLeft. bool flip = (origin == kTopLeft_GrSurfaceOrigin); std::array v = SkSL::Compiler::GetRTAdjustVector(dimensions, flip); fDataManager.set4fv(fBuiltinUniformHandles.fRTAdjustmentUni, 1, v.data()); if (fBuiltinUniformHandles.fRTFlipUni.isValid()) { // Note above that framebuffer space has origin top left. So we need !flip here. std::array d = SkSL::Compiler::GetRTFlipVector(rt->height(), !flip); fDataManager.set2fv(fBuiltinUniformHandles.fRTFlipUni, 1, d.data()); } } } static void set_texture(GrDawnGpu* gpu, GrSamplerState state, GrTexture* texture, std::vector* bindings, int* binding) { // FIXME: could probably cache samplers in GrDawnProgram wgpu::Sampler sampler = gpu->getOrCreateSampler(state); bindings->push_back(make_bind_group_entry((*binding)++, sampler)); GrDawnTexture* tex = static_cast(texture); wgpu::TextureViewDescriptor viewDesc; // Note that a mipLevelCount == WGPU_MIP_LEVEL_COUNT_UNDEFINED here means to expose all // available levels. viewDesc.mipLevelCount = GrSamplerState::MipmapMode::kNone == state.mipmapMode() ? 1 : WGPU_MIP_LEVEL_COUNT_UNDEFINED; wgpu::TextureView textureView = tex->texture().CreateView(&viewDesc); bindings->push_back(make_bind_group_entry((*binding)++, textureView)); } wgpu::BindGroup GrDawnProgram::setUniformData(GrDawnGpu* gpu, const GrRenderTarget* renderTarget, const GrProgramInfo& programInfo) { if (0 == fDataManager.uniformBufferSize()) { return nullptr; } this->setRenderTargetState(renderTarget, programInfo.origin()); const GrPipeline& pipeline = programInfo.pipeline(); const GrGeometryProcessor& geomProc = programInfo.geomProc(); fGPImpl->setData(fDataManager, *gpu->caps()->shaderCaps(), geomProc); for (int i = 0; i < programInfo.pipeline().numFragmentProcessors(); ++i) { const auto& fp = programInfo.pipeline().getFragmentProcessor(i); fp.visitWithImpls([&](const GrFragmentProcessor& fp, GrFragmentProcessor::ProgramImpl& impl) { impl.setData(fDataManager, fp); }, *fFPImpls[i]); } programInfo.pipeline().setDstTextureUniforms(fDataManager, &fBuiltinUniformHandles); fXPImpl->setData(fDataManager, pipeline.getXferProcessor()); return fDataManager.uploadUniformBuffers(gpu, fBindGroupLayouts[0]); } wgpu::BindGroup GrDawnProgram::setTextures(GrDawnGpu* gpu, const GrGeometryProcessor& geomProc, const GrPipeline& pipeline, const GrSurfaceProxy* const geomProcTextures[]) { if (fBindGroupLayouts.size() < 2) { return nullptr; } std::vector bindings; int binding = 0; if (geomProcTextures) { for (int i = 0; i < geomProc.numTextureSamplers(); ++i) { SkASSERT(geomProcTextures[i]->asTextureProxy()); auto& sampler = geomProc.textureSampler(i); set_texture(gpu, sampler.samplerState(), geomProcTextures[i]->peekTexture(), &bindings, &binding); } } if (GrTexture* dstTexture = pipeline.peekDstTexture()) { set_texture(gpu, GrSamplerState::Filter::kNearest, dstTexture, &bindings, &binding); } pipeline.visitTextureEffects([&](const GrTextureEffect& te) { set_texture(gpu, te.samplerState(), te.texture(), &bindings, &binding); }); wgpu::BindGroupDescriptor descriptor; descriptor.layout = fBindGroupLayouts[1]; descriptor.entryCount = bindings.size(); descriptor.entries = bindings.data(); return gpu->device().CreateBindGroup(&descriptor); }