blob: a7cdef07b8efb803a708d0da4de1a3820978a0d1 [file] [log] [blame] [edit]
/*
* 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/DrawWriter.h"
#include "src/gpu/graphite/Buffer.h"
#include "src/gpu/graphite/DrawCommands.h"
namespace skgpu::graphite {
DrawWriter::DrawWriter(DrawPassCommands::List* commandList, DrawBufferManager* bufferManager)
: fCommandList(commandList)
, fManager(bufferManager)
, fRenderState(RenderStateFlags::kNone)
, fPrimitiveType(PrimitiveType::kTriangles)
, fStaticStride(0)
, fAppendStride(0)
, fAppend({})
, fStatic({})
, fIndices({})
, fBoundAppend({})
, fBoundStatic({})
, fBoundIndices({})
, fTemplateCount(0)
, fPendingCount(0) {
SkASSERT(commandList && bufferManager);
}
void DrawWriter::flushInternal(BindBufferInfo appendData) {
// Skip flush if no items appended, or dynamic instances resolved to zero count.
if (fPendingCount == 0 ||
((fRenderState & RenderStateFlags::kAppendDynamicInstances) && fTemplateCount == 0)) {
return;
}
// How much to advance fAppend.fOffset when the flush is completed
uint32_t advanceOffset = fPendingCount;
// ARM hardware (b/399631317): Unreferenced vertices in sequential indexes of 4 will be
// speculatively executed. To work around this, we pad the buffer by requesting additional
// space, and then ensure valid, minimally deleterious data by memsetting the padding to zero.
if (fRenderState & RenderStateFlags::kAppendVertices) {
const uint32_t countDiff = (SkAlign4(fPendingCount) - fPendingCount);
if (countDiff) {
BufferWriter zWriter = fCurrentBuffer.appendMappedWithStride(countDiff);
SkASSERT(zWriter); // The buffer should have been sized to hold an aligned total
zWriter.zeroBytes(countDiff * fAppendStride);
advanceOffset += countDiff;
}
}
// Calculate base offsets from buffer info for the draw commands.
// - If a valid bufferFoo exists and matches the current stride, use pendingBaseFoo as a
// pseudo-alias for offset and reset the offset and size before assigning to boundBufferFoo.
// - If a valid bufferFoo does not exist, or is not stride aligned, then draw from the start of
// the offset with pendingBaseFoo = 0 and assign the current buffer.
auto bind = [](const BindBufferInfo& buffer, uint32_t stride, BindBufferInfo& boundBuffer,
uint32_t& pendingBase) -> bool {
bool shouldBind = false;
if (buffer.fBuffer) {
BindBufferInfo newBinding = buffer;
if (buffer.fOffset % stride == 0) {
pendingBase = buffer.fOffset / stride;
newBinding = {buffer.fBuffer, 0, SkTo<uint32_t>(buffer.fBuffer->size())};
}
shouldBind = boundBuffer != newBinding;
boundBuffer = newBinding;
}
return shouldBind;
};
uint32_t pendingBaseAppend = 0;
uint32_t pendingBaseStatic = 0;
uint32_t pendingBaseIndices = 0;
if (bind(appendData, fAppendStride, fBoundAppend, pendingBaseAppend)) {
fCommandList->bindAppendDataBuffer(fBoundAppend);
}
if (bind(fStatic, fStaticStride, fBoundStatic, pendingBaseStatic)) {
fCommandList->bindStaticDataBuffer(fBoundStatic);
}
if (bind(fIndices, sizeof(uint16_t), fBoundIndices, pendingBaseIndices)) {
fCommandList->bindIndexBuffer(fBoundIndices);
}
// Before any draw commands are added, check if the DrawWriter has an assigned barrier type
// to issue prior to draw calls.
if (fBarrierToIssueBeforeDraws != BarrierType::kNone) {
fCommandList->addBarrier(fBarrierToIssueBeforeDraws);
}
// Issue the appropriate draw call (instanced vs. non-instanced) based on the current
// fTemplateCount. Because of the initial AppendDynamicInstances && fTemplateCount check, any
// DynamicInstance render step must have valid (non-zero) templateCount data at this point.
if (fTemplateCount) {
SkASSERT((pendingBaseAppend + fPendingCount)*fAppendStride <= fBoundAppend.fSize);
if (fIndices) {
// It's not possible to validate that the indices stored in fIndices access only valid
// data within fVertices. Simply validate that fIndices holds enough data for the
// vertex count that's drawn.
SkASSERT(fTemplateCount*sizeof(uint16_t) <= fIndices.fSize);
fCommandList->drawIndexedInstanced(fPrimitiveType,
pendingBaseIndices,
fTemplateCount,
pendingBaseStatic,
pendingBaseAppend,
fPendingCount);
} else {
SkASSERT(fTemplateCount*fStaticStride <= fStatic.fSize);
fCommandList->drawInstanced(fPrimitiveType, pendingBaseStatic, fTemplateCount,
pendingBaseAppend, fPendingCount);
}
// Clear instancing template state after the draw is recorded for non-Fixed RenderState
if (fRenderState & RenderStateFlags::kAppendDynamicInstances) {
fTemplateCount = 0;
}
} else {
// Issue non-instanced draw call (indexed or non-indexed).
if (fIndices) {
// As before, just validate there is sufficient index data
SkASSERT(fPendingCount*sizeof(uint16_t) <= fIndices.fSize);
fCommandList->drawIndexed(fPrimitiveType,
pendingBaseIndices,
fPendingCount,
pendingBaseAppend);
} else {
SkASSERT((pendingBaseAppend + fPendingCount)*fStaticStride <= fBoundAppend.fSize);
fCommandList->draw(fPrimitiveType, pendingBaseAppend, fPendingCount);
}
}
// Mark all appended items as drawn, advance base offset which is normally set as part of a new
// pipeline state or flushing for a new buffer during appending data. But if we flushed because
// of other dynamic state changes, there might not be interaction with the BufferManager.
fAppend.fOffset += advanceOffset * fAppendStride;
fPendingCount = 0;
}
} // namespace skgpu::graphite