| /* |
| * Copyright 2022 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/Renderer.h" |
| |
| #include "src/gpu/graphite/DrawParams.h" |
| #include "src/gpu/graphite/UniformManager.h" |
| |
| namespace skgpu::graphite { |
| |
| RenderStep::RenderStep(Layout layout, |
| RenderStepID renderStepID, |
| SkEnumBitMask<Flags> flags, |
| std::initializer_list<Uniform> uniforms, |
| PrimitiveType primitiveType, |
| DepthStencilSettings depthStencilSettings, |
| SkSpan<const Attribute> staticAttrs, |
| SkSpan<const Attribute> appendAttrs, |
| SkSpan<const Uniform> storageUniforms, |
| SkSpan<const Varying> varyings) |
| : fRenderStepID(renderStepID) |
| , fFlags(flags) |
| , fStorageBufferStages( |
| (SkToBool(flags & Flags::kVsUsesStorage) ? PipelineStageFlags::kVertexShader |
| : PipelineStageFlags{}) | |
| (SkToBool(flags & Flags::kFsUsesStorage) ? PipelineStageFlags::kFragmentShader |
| : PipelineStageFlags{})) |
| , fPrimitiveType(primitiveType) |
| , fDepthStencilSettings(depthStencilSettings) |
| , fUniforms(uniforms) |
| , fStaticAttrs(staticAttrs.begin(), staticAttrs.end()) |
| , fAppendAttrs(appendAttrs.begin(), appendAttrs.end()) |
| , fStorageUniforms(storageUniforms.begin(), storageUniforms.end()) |
| , fVaryings(varyings.begin(), varyings.end()) |
| , fUniformAlignment(0) |
| , fStaticDataStride(0) |
| , fAppendDataStride(0) |
| , fStorageUniformStride(0) |
| , fStorageUniformAlignment(1) { |
| // If declaring storage uniforms, the flags must specify their visibility |
| SkASSERT(fStorageUniforms.empty() || |
| SkToBool(fStorageBufferStages & (PipelineStageFlags::kVertexShader | |
| PipelineStageFlags::kFragmentShader))); |
| |
| // kAllowSelfIntersection and kNoSelfIntersection are mutually exclusive |
| SkASSERT(!(SkToBool(fFlags & Flags::kAllowsSelfIntersection) && |
| SkToBool(fFlags & Flags::kNoSelfIntersections))); |
| |
| // Ensure that the depth-related flags are consistent with the depth settings provided |
| #if defined(SK_DEBUG) |
| if (this->requiresDepth()) { |
| // This mode requires the depth buffer to prevent double blending, so writes need to be |
| // enabled and the test needs to be LESS to fail subsequent pixel touches. |
| SkASSERT(depthStencilSettings.fDepthWriteEnabled && |
| depthStencilSettings.fDepthCompareOp == CompareOp::kLess); |
| } else if (this->useNonAAInnerFill()) { |
| // Currently, inner fill draws use the same z value as the main draw, so a render step |
| // opting into this feature needs to use LESS even if otherwise could use LEQUAL. This |
| // could be relaxed by changing how Device assigns z values but there's no pressing need. |
| // Opting into a non-aa inner fill does not require depth writes, since this step is being |
| // rendered after the inner fill (which would write depth). |
| SkASSERT(depthStencilSettings.fDepthCompareOp == CompareOp::kLess); |
| } else { |
| // These flags have the same effective behavior, which is that the test is LEQUAL |
| // (either necessary to show self-intersections, or desired to highlight bugs that indicate |
| // a violation of kNoSelfIntersection). They do not mandate depth writes being enabled. |
| // For now, also allow kLess to support scenarios where multiple recordDraws() depend on |
| // the Z value being the same. |
| SkASSERT(depthStencilSettings.fDepthCompareOp == CompareOp::kLEqual || |
| depthStencilSettings.fDepthCompareOp == CompareOp::kLess); |
| } |
| |
| // Depth writing should be consistent with the shading policy; every Renderer needs to have |
| // at least one render step that performs shading, these are responsible for writing depth. The |
| // non-shading steps should only be testing (if required for the renderpass). |
| SkASSERT(depthStencilSettings.fDepthWriteEnabled == this->performsShading()); |
| #endif |
| |
| for (auto v : this->staticAttributes()) { |
| fStaticDataStride += v.sizeAlign4(); |
| } |
| for (auto i : this->appendAttributes()) { |
| fAppendDataStride += i.sizeAlign4(); |
| } |
| |
| if (!fStorageUniforms.empty()) { |
| // Storage uniforms are backed by a storage buffer or emulated via a fallback texture. |
| // In both cases the struct layout follows Std430. |
| UniformOffsetCalculator calculator = UniformOffsetCalculator::ForStruct(Layout::kStd430); |
| for (const auto& u : fStorageUniforms) { |
| calculator.advanceOffset(u.type(), u.count()); |
| } |
| fStorageUniformAlignment = calculator.requiredAlignment(); |
| fStorageUniformStride = SkAlignTo<size_t>(calculator.size(), fStorageUniformAlignment); |
| } |
| |
| UniformOffsetCalculator calculator = UniformOffsetCalculator::ForTopLevel(layout); |
| for (const auto& u : fUniforms) { |
| calculator.advanceOffset(u.type(), u.count()); |
| } |
| fUniformAlignment = calculator.requiredAlignment(); |
| } |
| |
| std::optional<SkIRect> RenderStep::getScissor(const DrawParams& params, |
| SkIRect currentScissor, |
| SkIRect deviceBounds) const { |
| if (currentScissor == params.scissor()) { |
| return {}; // Trivially no change in scissor state is required |
| } |
| |
| Rect drawBounds = params.drawBounds(); |
| if (params.geometry().isShape() && params.geometry().shape().inverted()) { |
| // For inverse filled shapes, the scissor is able to be handled in unique ways. |
| if (fFlags & Flags::kInverseFillsScissor) { |
| // In this case, the RenderStep geometrically respects the scissor so as long as the |
| // current scissor doesn't interfere, we don't need a state change. |
| if (currentScissor.contains(params.scissor())) { |
| return {}; |
| } else { |
| // This draw doesn't need a scissor at all, so return the device bounds. It is |
| // expected that this will generally lead to fewer scissor state changes (for |
| // instance when applying the cover steps for a lot of inverse-filled intersect |
| // clip depth-only draws). However, it could lead to a redundant scissor change if |
| // the next draw would have used this draw's original scissor. |
| return deviceBounds; |
| } |
| } |
| |
| if (fFlags & Flags::kIgnoreInverseFill) { |
| // In this case params.drawBounds() fills the scissor from the inverse fill rule, |
| // but we want to apply the scissor as if it were a regular fill. |
| drawBounds = params.transformedShapeBounds(); // this ignores fill rule |
| drawBounds.intersect(params.scissor()); |
| } // Else leave drawBounds filling the original scissor |
| |
| // Fall through to regular scissor state checking with the possibly-updated bounds |
| } |
| |
| // Draws that are unaffected by a clip stack will have a scissor matching the device's bounds. |
| // If their transformed shape bounds clipped to the current scissor are no different than their |
| // draw bounds (clipped to the original scissor), then no state change is required. |
| Rect currentClippedBounds = params.transformedShapeBounds(); |
| currentClippedBounds.intersect(currentScissor); |
| if (currentClippedBounds == drawBounds) { |
| return {}; |
| } |
| |
| if (drawBounds == params.transformedShapeBounds()) { |
| // We need to change the scissor, but the registered scissor is a no-op so |
| // use the device bounds as a canonical scissor. |
| return deviceBounds; |
| } |
| |
| return params.scissor(); |
| } |
| |
| Coverage RenderStep::GetCoverage(SkEnumBitMask<Flags> flags) { |
| return !(flags & Flags::kEmitsCoverage) ? Coverage::kNone |
| : (flags & Flags::kLCDCoverage) ? Coverage::kLCD |
| : Coverage::kSingleChannel; |
| } |
| |
| const char* RenderStep::RenderStepName(RenderStepID id) { |
| #define CASE1(BaseName) case RenderStepID::k##BaseName: return #BaseName "RenderStep"; |
| #define CASE2(BaseName, VariantName) \ |
| case RenderStepID::k##BaseName##_##VariantName: return #BaseName "RenderStep[" #VariantName "]"; |
| |
| switch (id) { |
| SKGPU_RENDERSTEP_TYPES(CASE1, CASE2) |
| } |
| #undef CASE1 |
| #undef CASE2 |
| |
| SkUNREACHABLE; |
| } |
| |
| bool RenderStep::IsValidRenderStepID(uint32_t renderStepID) { |
| return renderStepID > (int) RenderStep::RenderStepID::kInvalid && |
| renderStepID < RenderStep::kNumRenderSteps; |
| } |
| |
| } // namespace skgpu::graphite |