blob: db06427cdadb1d574e1d63cd724bd41a8934b1fa [file] [log] [blame]
/*
* Copyright 2021 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/gpu/graphite/ContextUtils.h"
#include <string>
#include "include/private/SkSLString.h"
#include "src/core/SkBlenderBase.h"
#include "src/gpu/graphite/Caps.h"
#include "src/gpu/graphite/GraphicsPipelineDesc.h"
#include "src/gpu/graphite/KeyContext.h"
#include "src/gpu/graphite/PaintParams.h"
#include "src/gpu/graphite/PipelineData.h"
#include "src/gpu/graphite/RecorderPriv.h"
#include "src/gpu/graphite/Renderer.h"
#include "src/gpu/graphite/ResourceProvider.h"
#include "src/gpu/graphite/ShaderCodeDictionary.h"
#include "src/gpu/graphite/UniformManager.h"
#include "src/gpu/graphite/UniquePaintParamsID.h"
namespace skgpu::graphite {
std::tuple<UniquePaintParamsID, const UniformDataBlock*, const TextureDataBlock*>
ExtractPaintData(Recorder* recorder,
PipelineDataGatherer* gatherer,
PaintParamsKeyBuilder* builder,
const Layout layout,
const SkM44& local2Dev,
const PaintParams& p,
const SkColorInfo& targetColorInfo) {
SkDEBUGCODE(builder->checkReset());
gatherer->resetWithNewLayout(layout);
KeyContext keyContext(recorder, local2Dev, targetColorInfo);
p.toKey(keyContext, builder, gatherer);
auto dict = recorder->priv().shaderCodeDictionary();
UniformDataCache* uniformDataCache = recorder->priv().uniformDataCache();
TextureDataCache* textureDataCache = recorder->priv().textureDataCache();
auto entry = dict->findOrCreate(builder);
const UniformDataBlock* uniforms =
gatherer->hasUniforms() ? uniformDataCache->insert(gatherer->finishUniformDataBlock())
: nullptr;
const TextureDataBlock* textures =
gatherer->hasTextures() ? textureDataCache->insert(gatherer->textureDataBlock())
: nullptr;
return { entry->uniqueID(), uniforms, textures };
}
std::tuple<const UniformDataBlock*, const TextureDataBlock*> ExtractRenderStepData(
UniformDataCache* uniformDataCache,
TextureDataCache* textureDataCache,
PipelineDataGatherer* gatherer,
const Layout layout,
const RenderStep* step,
const DrawParams& params) {
gatherer->resetWithNewLayout(layout);
step->writeUniformsAndTextures(params, gatherer);
const UniformDataBlock* uniforms =
gatherer->hasUniforms() ? uniformDataCache->insert(gatherer->finishUniformDataBlock())
: nullptr;
const TextureDataBlock* textures =
gatherer->hasTextures() ? textureDataCache->insert(gatherer->textureDataBlock())
: nullptr;
return { uniforms, textures };
}
namespace {
std::string get_uniform_header(int bufferID, const char* name) {
std::string result;
SkSL::String::appendf(&result, "layout (binding=%d) uniform %sUniforms {\n", bufferID, name);
return result;
}
std::string get_uniforms(Layout layout,
SkSpan<const Uniform> uniforms,
int* offset,
int manglingSuffix) {
std::string result;
UniformOffsetCalculator offsetter(layout, *offset);
for (const Uniform& u : uniforms) {
SkSL::String::appendf(&result,
" layout(offset=%zu) %s %s",
offsetter.advanceOffset(u.type(), u.count()),
SkSLTypeString(u.type()),
u.name());
if (manglingSuffix >= 0) {
result.append("_");
result.append(std::to_string(manglingSuffix));
}
if (u.count()) {
result.append("[");
result.append(std::to_string(u.count()));
result.append("]");
}
result.append(";\n");
}
*offset = offsetter.size();
return result;
}
} // anonymous namespace
std::string EmitPaintParamsUniforms(int bufferID,
const char* name,
const Layout layout,
const std::vector<PaintParamsKey::BlockReader>& readers) {
int offset = 0;
std::string result = get_uniform_header(bufferID, name);
for (int i = 0; i < (int) readers.size(); ++i) {
SkSpan<const Uniform> uniforms = readers[i].entry()->fUniforms;
if (!uniforms.empty()) {
SkSL::String::appendf(&result, "// %s uniforms\n", readers[i].entry()->fName);
result += get_uniforms(layout, uniforms, &offset, i);
}
}
result.append("};\n\n");
return result;
}
std::string EmitRenderStepUniforms(int bufferID,
const char* name,
const Layout layout,
SkSpan<const Uniform> uniforms) {
int offset = 0;
std::string result = get_uniform_header(bufferID, name);
result += get_uniforms(layout, uniforms, &offset, -1);
result.append("};\n\n");
return result;
}
std::string EmitPaintParamsStorageBuffer(
int bufferID,
const char* bufferTypePrefix,
const char* bufferNamePrefix,
const std::vector<PaintParamsKey::BlockReader>& readers) {
std::string result;
SkSL::String::appendf(&result, "struct %sUniformData {\n", bufferTypePrefix);
for (int i = 0; i < (int)readers.size(); ++i) {
SkSpan<const Uniform> uniforms = readers[i].entry()->fUniforms;
if (uniforms.empty()) {
continue;
}
SkSL::String::appendf(&result, "// %s uniforms\n", readers[i].entry()->fName);
int manglingSuffix = i;
for (const Uniform& u : uniforms) {
SkSL::String::appendf(
&result, " %s %s_%d", SkSLTypeString(u.type()), u.name(), manglingSuffix);
if (u.count()) {
SkSL::String::appendf(&result, "[%u]", u.count());
}
result.append(";\n");
}
}
result.append("};\n\n");
SkSL::String::appendf(&result,
"layout (binding=%d) buffer %sUniforms {\n"
" %sUniformData %sUniformData[];\n"
"};\n",
bufferID,
bufferTypePrefix,
bufferTypePrefix,
bufferNamePrefix);
return result;
}
std::string EmitStorageBufferAccess(const char* bufferNamePrefix,
const char* ssboIndex,
const char* uniformName) {
return SkSL::String::printf("%sUniformData[%s].%s", bufferNamePrefix, ssboIndex, uniformName);
}
std::string EmitTexturesAndSamplers(const ResourceBindingRequirements& bindingReqs,
const std::vector<PaintParamsKey::BlockReader>& readers,
int* binding) {
std::string result;
for (int i = 0; i < (int) readers.size(); ++i) {
SkSpan<const TextureAndSampler> samplers = readers[i].entry()->fTexturesAndSamplers;
if (!samplers.empty()) {
SkSL::String::appendf(&result, "// %s samplers\n", readers[i].entry()->fName);
for (const TextureAndSampler& t : samplers) {
result += EmitSamplerLayout(bindingReqs, binding);
SkSL::String::appendf(&result, " uniform sampler2D %s_%d;\n", t.name(), i);
}
}
}
return result;
}
std::string EmitSamplerLayout(const ResourceBindingRequirements& bindingReqs, int* binding) {
std::string result;
// If fDistinctIndexRanges is false, then texture and sampler indices may clash with other
// resource indices. Graphite assumes that they will be placed in descriptor set (Vulkan) and
// bind group (Dawn) index 1.
if (bindingReqs.fSeparateTextureAndSamplerBinding) {
int samplerIndex = (*binding)++;
int textureIndex = (*binding)++;
SkSL::String::appendf(&result,
"layout(wgsl, %ssampler=%d, texture=%d)",
bindingReqs.fDistinctIndexRanges ? "" : "set=1, ",
samplerIndex,
textureIndex);
} else {
SkSL::String::appendf(&result,
"layout(%sbinding=%d)",
bindingReqs.fDistinctIndexRanges ? "" : "set=1, ",
*binding);
(*binding)++;
}
return result;
}
namespace {
std::string emit_attributes(SkSpan<const Attribute> vertexAttrs,
SkSpan<const Attribute> instanceAttrs) {
std::string result;
int attr = 0;
auto add_attrs = [&](SkSpan<const Attribute> attrs) {
for (auto a : attrs) {
SkSL::String::appendf(&result, " layout(location=%d) in ", attr++);
result.append(SkSLTypeString(a.gpuType()));
SkSL::String::appendf(&result, " %s;\n", a.name());
}
};
if (!vertexAttrs.empty()) {
result.append("// vertex attrs\n");
add_attrs(vertexAttrs);
}
if (!instanceAttrs.empty()) {
result.append("// instance attrs\n");
add_attrs(instanceAttrs);
}
return result;
}
} // anonymous namespace
std::string EmitVaryings(const RenderStep* step,
const char* direction,
bool emitShadingSsboIndexVarying,
bool emitLocalCoordsVarying) {
std::string result;
int location = 0;
if (emitShadingSsboIndexVarying) {
SkSL::String::appendf(&result,
" layout(location=%d) %s int shadingSsboIndexVar;\n",
location++,
direction);
}
if (emitLocalCoordsVarying) {
SkSL::String::appendf(&result, " layout(location=%d) %s ", location++, direction);
result.append(SkSLTypeString(SkSLType::kFloat2));
SkSL::String::appendf(&result, " localCoordsVar;\n");
}
for (auto v : step->varyings()) {
SkSL::String::appendf(&result, " layout(location=%d) %s ", location++, direction);
result.append(SkSLTypeString(v.fType));
SkSL::String::appendf(&result, " %s;\n", v.fName);
}
return result;
}
std::string GetSkSLVS(const ResourceBindingRequirements& bindingReqs,
const RenderStep* step,
bool defineShadingSsboIndexVarying,
bool defineLocalCoordsVarying) {
// TODO: To more completely support end-to-end rendering, this will need to be updated so that
// the RenderStep shader snippet can produce a device coord, a local coord, and depth.
// If the paint combination doesn't need the local coord it can be ignored, otherwise we need
// a varying for it. The fragment function's output will need to be updated to have a color and
// the depth, or when there's no combination, just the depth. Lastly, we also should add the
// static/intrinsic uniform binding point so that we can handle normalizing the device position
// produced by the RenderStep automatically.
// Fixed program header
std::string sksl =
"layout (binding=0) uniform intrinsicUniforms {\n"
" layout(offset=0) float4 rtAdjust;\n"
"};\n"
"\n";
if (step->numVertexAttributes() > 0 || step->numInstanceAttributes() > 0) {
sksl += emit_attributes(step->vertexAttributes(), step->instanceAttributes());
}
// Uniforms needed by RenderStep
// The uniforms are mangled by having their index in 'fEntries' as a suffix (i.e., "_%d")
// TODO: replace hard-coded bufferID with the backend's renderstep uniform-buffer index.
if (step->numUniforms() > 0) {
sksl += EmitRenderStepUniforms(
1, "Step", bindingReqs.fUniformBufferLayout, step->uniforms());
}
// Varyings needed by RenderStep
sksl += EmitVaryings(step, "out", defineShadingSsboIndexVarying, defineLocalCoordsVarying);
// Vertex shader function declaration
sksl += "void main() {";
// Create stepLocalCoords which render steps can write to.
sksl += "float2 stepLocalCoords = float2(0);";
// Vertex shader body
sksl += step->vertexSkSL();
sksl += "sk_Position = float4(devPosition.xy * rtAdjust.xy + devPosition.ww * rtAdjust.zw,"
"devPosition.zw);";
if (defineShadingSsboIndexVarying) {
// Assign SSBO index value to the SSBO index varying
SkSL::String::appendf(&sksl, "shadingSsboIndexVar = %s;", step->ssboIndex());
}
if (defineLocalCoordsVarying) {
// Assign Render Step's stepLocalCoords to the localCoordsVar varying.
sksl += "localCoordsVar = stepLocalCoords;";
}
sksl += "}";
return sksl;
}
FragSkSLInfo GetSkSLFS(const ResourceBindingRequirements& bindingReqs,
const ShaderCodeDictionary* dict,
const RuntimeEffectDictionary* rteDict,
const RenderStep* step,
UniquePaintParamsID paintID,
bool useStorageBuffers) {
if (!paintID.isValid()) {
// TODO: we should return the error shader code here
return {};
}
FragSkSLInfo result;
const char* shadingSsboIndexVar = useStorageBuffers ? "shadingSsboIndexVar" : nullptr;
ShaderInfo shaderInfo(rteDict, shadingSsboIndexVar);
dict->getShaderInfo(paintID, &shaderInfo);
result.fBlendInfo = shaderInfo.blendInfo();
result.fRequiresLocalCoords = shaderInfo.needsLocalCoords();
// Extra RenderStep uniforms are always backed by a UBO. Uniforms for the PaintParams are either
// UBO or SSBO backed based on `useStorageBuffers`.
result.fSkSL =
shaderInfo.toSkSL(bindingReqs,
step,
useStorageBuffers,
/*defineLocalCoordsVarying=*/result.fRequiresLocalCoords,
/*numTexturesAndSamplersUsed=*/&result.fNumTexturesAndSamplers);
return result;
}
} // namespace skgpu::graphite